fizzy-cli 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eb20ca66466e8b1749b5b02f88107fa31ca0a9cb4cffb51cfeca3e712eb6effa
4
- data.tar.gz: 63b5c87f28cc3b915f77836f2147e8b1f747e6791c4f1918694df9290940ac16
3
+ metadata.gz: 1684a8ac9b61399c368c8ff3b5828f00f965d65cfedadaa03c23eefe002f4661
4
+ data.tar.gz: 31615887c4f1c5bbdc7b33c08e216690a1b440b8551e8328ecbefcb455e2fbe4
5
5
  SHA512:
6
- metadata.gz: 99464b430132e9ea62b098fc031112b8a6cebd8cf2ee350693b67cd189c850ee6f7ad7e8cc2ae6b9898e7377b273cf77db3e64bac1de5e406da70e996a843986
7
- data.tar.gz: ebf5fae49836fa68aac920a8e309dbf8731002d48fcbf8415fbbeabcf3a1e9be8fc586c4b09115a7f0772d064e4ce176fd92321d429d6a1f78c02da7d12b70fb
6
+ metadata.gz: 262b4e467ca32aaa6e202bb21358d0111870ca449983112abdd0d45cfe8e87229b904f0b28185c7ab4246848f69222d9e363aadae9bb49a25af4314a4e96bc5e
7
+ data.tar.gz: c6d180d3c263bbf9eefe2bcdd28de99809d825d8af974044e64a8d91c66ac45d7cd35ef07e5dad3204b59187fa6e1c62d4aed303655585ffe5693cd20b95268c
data/CHANGELOG.md CHANGED
@@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
+ ## [0.6.0] - 2026-02-23
9
+
10
+ ### Added
11
+ - Custom URL support for self-hosted Fizzy instances
12
+ - `fizzy auth login --url URL` stores instance URL per-account in tokens.yml
13
+ - `url` key in `.fizzy.yml` for per-project instance URL
14
+ - `FIZZY_URL` environment variable for instance URL override
15
+ - URL resolution priority: `FIZZY_URL` env > `.fizzy.yml` > tokens.yml per-account > default
16
+ - URL validation in Client rejects non-http(s) URLs with clear error message
17
+ - `auth status` displays URL when using a non-default instance
18
+ - HTTP scheme detection for SSL (supports `http://` for local development)
19
+
20
+ ## [0.5.0] - 2026-02-22
21
+
22
+ ### Added
23
+ - Cache boards in `.fizzy.yml` as `boards: { id: name }` hash for quick lookups without API calls
24
+ - `fizzy boards sync` command to refresh the cached boards list from the API
25
+ - `fizzy init` now automatically fetches and caches all boards during setup
26
+ - `ProjectConfig#boards` accessor for reading cached board data
27
+
8
28
  ## [0.4.1] - 2026-02-22
9
29
 
10
30
  ### Fixed
data/README.md CHANGED
@@ -43,7 +43,13 @@ fizzy skill uninstall # Remove skill file
43
43
  fizzy auth login --token YOUR_TOKEN
44
44
  ```
45
45
 
46
- Tokens are stored at `~/.config/fizzy-cli/tokens.yml`. You can also set `FIZZY_TOKEN` as an environment variable with `--account` to skip the file.
46
+ For self-hosted Fizzy instances, pass `--url`:
47
+
48
+ ```sh
49
+ fizzy auth login --token YOUR_TOKEN --url https://fizzy.mycompany.com
50
+ ```
51
+
52
+ The URL is stored per-account in `~/.config/fizzy-cli/tokens.yml`. You can also set `FIZZY_TOKEN` as an environment variable with `--account` to skip the file.
47
53
 
48
54
  ```sh
49
55
  fizzy auth status # Show current auth
@@ -66,6 +72,14 @@ account: acme
66
72
  board: b1
67
73
  ```
68
74
 
75
+ For self-hosted instances, add a `url` key:
76
+
77
+ ```yaml
78
+ account: acme
79
+ board: b1
80
+ url: https://fizzy.mycompany.com
81
+ ```
82
+
69
83
  **Resolution priority** (highest wins):
70
84
  1. CLI flag (`--account` / `--board`)
71
85
  2. `.fizzy.yml` (nearest ancestor directory)
@@ -210,9 +224,16 @@ end
210
224
 
211
225
  | Source | Purpose |
212
226
  |--------|---------|
213
- | `.fizzy.yml` | Per-project account and board defaults |
214
- | `~/.config/fizzy-cli/tokens.yml` | Stored auth tokens and default account |
227
+ | `.fizzy.yml` | Per-project account, board, and URL defaults |
228
+ | `~/.config/fizzy-cli/tokens.yml` | Stored auth tokens, default account, and per-account URL |
215
229
  | `FIZZY_TOKEN` env var | Token override (requires `--account`) |
230
+ | `FIZZY_URL` env var | Instance URL override (default: `https://app.fizzy.do`) |
231
+
232
+ **URL resolution priority** (highest wins):
233
+ 1. `FIZZY_URL` environment variable
234
+ 2. `url` in `.fizzy.yml`
235
+ 3. Per-account `url` from `tokens.yml` (set via `fizzy auth login --url`)
236
+ 4. Default: `https://app.fizzy.do`
216
237
 
217
238
  ## Development
218
239
 
@@ -24,11 +24,15 @@ module Fizzy
24
24
 
25
25
  desc "login", "Authenticate with a Personal Access Token"
26
26
  option :token, required: true, desc: "Personal Access Token"
27
+ option :url, type: :string, desc: "Custom Fizzy instance URL (default: #{Client::DEFAULT_BASE_URL})"
27
28
  def login
28
29
  token = options[:token]
30
+ custom_url = options[:url]
29
31
 
30
32
  # Verify token by fetching identity
31
- c = Client.new(token: token, account_slug: "")
33
+ client_opts = { token: token, account_slug: "" }
34
+ client_opts[:base_url] = custom_url if custom_url
35
+ c = Client.new(**client_opts)
32
36
  resp = c.get("/my/identity")
33
37
  accounts = resp.body["accounts"]
34
38
 
@@ -36,7 +40,7 @@ module Fizzy
36
40
 
37
41
  # Build tokens data
38
42
  token_accounts = accounts.map do |a|
39
- {
43
+ acct = {
40
44
  "account_slug" => Auth.normalize_slug(a["slug"]),
41
45
  "account_name" => a["name"],
42
46
  "account_id" => a["id"],
@@ -49,6 +53,8 @@ module Fizzy
49
53
  },
50
54
  "created_at" => Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%3NZ")
51
55
  }
56
+ acct["url"] = custom_url if custom_url
57
+ acct
52
58
  end
53
59
 
54
60
  data = {
@@ -69,13 +75,12 @@ module Fizzy
69
75
 
70
76
  desc "status", "Show current auth status"
71
77
  def status
72
- acct = Auth.resolve
73
- c = Client.new(token: acct["access_token"], account_slug: acct["account_slug"])
74
- resp = c.get("/my/identity")
78
+ resp = client.get("/my/identity")
75
79
 
80
+ puts "URL: #{client.base_url}" unless base_url == Client::DEFAULT_BASE_URL
76
81
  puts "Token: #{Auth::TOKEN_FILE}"
77
- puts "Account: #{acct["account_name"]} (#{acct["account_slug"]})"
78
- puts "User: #{acct.dig("user", "name")} <#{acct.dig("user", "email_address")}>"
82
+ puts "Account: #{account["account_name"]} (#{account["account_slug"]})"
83
+ puts "User: #{account.dig("user", "name")} <#{account.dig("user", "email_address")}>"
79
84
 
80
85
  accounts_count = resp.body["accounts"].size
81
86
  puts "Accounts: #{accounts_count}" if accounts_count > 1
@@ -37,10 +37,15 @@ module Fizzy
37
37
  "No value provided for option '--board'. Set via --board, .fizzy.yml, or: fizzy init")
38
38
  end
39
39
 
40
+ def base_url
41
+ ENV["FIZZY_URL"] || project_config.url || account["url"] || Client::DEFAULT_BASE_URL
42
+ end
43
+
40
44
  def client
41
45
  @client ||= Client.new(
42
46
  token: account["access_token"],
43
- account_slug: account["account_slug"]
47
+ account_slug: account["account_slug"],
48
+ base_url: base_url
44
49
  )
45
50
  end
46
51
 
@@ -56,6 +56,21 @@ module Fizzy
56
56
  client.delete("boards/#{board_id}")
57
57
  puts "Board #{board_id} deleted."
58
58
  end
59
+
60
+ desc "sync", "Refresh boards cache in .fizzy.yml"
61
+ def sync
62
+ raise Thor::Error, "No .fizzy.yml found. Run: fizzy init" unless project_config.found?
63
+
64
+ boards = paginator.all("boards")
65
+ boards_hash = boards.to_h { |b| [b["id"], b["name"]] }
66
+
67
+ config = YAML.safe_load_file(project_config.path)
68
+ config = {} unless config.is_a?(Hash)
69
+ config["boards"] = boards_hash
70
+
71
+ File.write(project_config.path, YAML.dump(config))
72
+ say "Synced #{boards_hash.size} board(s) to #{project_config.path}"
73
+ end
59
74
  end
60
75
  end
61
76
  end
data/lib/fizzy/cli.rb CHANGED
@@ -32,7 +32,10 @@ module Fizzy
32
32
 
33
33
  selected = pick_account
34
34
  config = { "account" => selected["account_slug"] }
35
- config["board"] = pick_board(selected) if yes?("Set a default board?")
35
+
36
+ boards = fetch_boards(selected)
37
+ config["boards"] = boards.to_h { |b| [b["id"], b["name"]] } if boards.any?
38
+ config["board"] = pick_board(boards) if boards.any? && yes?("Set a default board?")
36
39
 
37
40
  File.write(config_path, YAML.dump(config))
38
41
  say "Wrote #{config_path}"
@@ -97,15 +100,15 @@ module Fizzy
97
100
  accounts[idx]
98
101
  end
99
102
 
100
- def pick_board(account)
101
- c = Client.new(token: account["access_token"], account_slug: account["account_slug"])
103
+ def fetch_boards(account)
104
+ url = ENV["FIZZY_URL"] || account["url"] || Client::DEFAULT_BASE_URL
105
+ c = Client.new(token: account["access_token"], account_slug: account["account_slug"], base_url: url)
102
106
  boards = c.get("boards").body
107
+ say "No boards found." if boards.empty?
108
+ boards
109
+ end
103
110
 
104
- if boards.empty?
105
- say "No boards found."
106
- return nil
107
- end
108
-
111
+ def pick_board(boards)
109
112
  say "Boards:"
110
113
  boards.each_with_index do |b, i|
111
114
  say " #{i + 1}. #{b["name"]} (#{b["id"]})"
data/lib/fizzy/client.rb CHANGED
@@ -4,11 +4,15 @@ module Fizzy
4
4
  Response = Data.define(:body, :headers, :status)
5
5
 
6
6
  class Client
7
- BASE_URL = "https://app.fizzy.do"
7
+ DEFAULT_BASE_URL = "https://app.fizzy.do"
8
8
 
9
- def initialize(token:, account_slug:)
9
+ attr_reader :base_url
10
+
11
+ def initialize(token:, account_slug:, base_url: DEFAULT_BASE_URL)
10
12
  @token = token
11
13
  @account_slug = account_slug
14
+ @base_url = base_url.chomp("/")
15
+ raise ArgumentError, "Invalid URL (must be http or https): #{@base_url}" unless @base_url.match?(%r{\Ahttps?://})
12
16
  end
13
17
 
14
18
  def get(path, params: {})
@@ -31,9 +35,9 @@ module Fizzy
31
35
 
32
36
  def connection
33
37
  @connection ||= begin
34
- uri = URI(BASE_URL)
38
+ uri = URI(@base_url)
35
39
  http = Net::HTTP.new(uri.host, uri.port)
36
- http.use_ssl = true
40
+ http.use_ssl = uri.scheme == "https"
37
41
  http.open_timeout = 5
38
42
  http.read_timeout = 30
39
43
  http.start
@@ -48,7 +52,7 @@ module Fizzy
48
52
 
49
53
  def request(method, path, body: nil, params: {})
50
54
  full_path = path.start_with?("/") ? path : "/#{@account_slug}/#{path}"
51
- uri = URI("#{BASE_URL}#{full_path}")
55
+ uri = URI("#{@base_url}#{full_path}")
52
56
  uri.query = URI.encode_www_form(params) unless params.empty?
53
57
 
54
58
  req = build_request(method, uri)
@@ -20,6 +20,10 @@ module Fizzy
20
20
 
21
21
  def board = @data["board"]
22
22
 
23
+ def url = @data["url"]
24
+
25
+ def boards = @data["boards"] || {}
26
+
23
27
  private
24
28
 
25
29
  def find_config(dir)
data/lib/fizzy/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fizzy
4
- VERSION = "0.4.1"
4
+ VERSION = "0.6.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fizzy-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Paluy