chore: gitignore release build artifacts and local Claude state
Some checks are pending
ci / check (push) Waiting to run
Some checks are pending
ci / check (push) Waiting to run
dist/v*/ holds tarballs/SHAs we upload to Forgejo releases, not source. .claude/settings.local.json is per-machine allow-list state that shouldn't travel with the repo. Removes the artifacts that slipped into the previous commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
65420678d9
commit
61cfd85254
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -6,3 +6,9 @@ Cargo.lock.bak
|
||||||
.vscode/
|
.vscode/
|
||||||
*.swp
|
*.swp
|
||||||
*.swo
|
*.swo
|
||||||
|
|
||||||
|
# Release build outputs (uploaded to Forgejo releases, never tracked)
|
||||||
|
/dist/v*/
|
||||||
|
|
||||||
|
# Local Claude / agent state
|
||||||
|
.claude/settings.local.json
|
||||||
|
|
|
||||||
2
dist/v0.1.0/SHA256SUMS
vendored
2
dist/v0.1.0/SHA256SUMS
vendored
|
|
@ -1,2 +0,0 @@
|
||||||
eb5fd0206a5194179f6d1aecee7fe4a4a5434e1f44ed75a525f7f41d18670061 fj-v0.1.0-darwin-aarch64.tar.gz
|
|
||||||
d7102095f051f4af7af1d9dbf34678df85142fdfece71dcab0cc111aeb942c2c fj-v0.1.0-darwin-x86_64.tar.gz
|
|
||||||
BIN
dist/v0.1.0/fj-v0.1.0-darwin-aarch64.tar.gz
vendored
BIN
dist/v0.1.0/fj-v0.1.0-darwin-aarch64.tar.gz
vendored
Binary file not shown.
121
dist/v0.1.0/fj-v0.1.0-darwin-aarch64/CHANGELOG.md
vendored
121
dist/v0.1.0/fj-v0.1.0-darwin-aarch64/CHANGELOG.md
vendored
|
|
@ -1,121 +0,0 @@
|
||||||
# Changelog
|
|
||||||
|
|
||||||
All notable changes will be recorded here. The format follows
|
|
||||||
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versions follow
|
|
||||||
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
||||||
|
|
||||||
## [Unreleased]
|
|
||||||
|
|
||||||
### Added (distribution + open-source ready)
|
|
||||||
|
|
||||||
- `LICENSE` (MIT) at the repo root. Cargo.toml had always declared MIT;
|
|
||||||
the file was just missing.
|
|
||||||
- `.forgejo/workflows/release.yml`: on `v*` tags, builds for
|
|
||||||
`darwin-aarch64`, `darwin-x86_64`, `linux-x86_64`; uploads tarballs,
|
|
||||||
`SHA256SUMS`, and a pre-rendered `fj.rb` to the Forgejo release.
|
|
||||||
- Homebrew formula template at `dist/homebrew/fj.rb.tmpl` and
|
|
||||||
`scripts/render-homebrew-formula.sh` to fill SHA256s post-release.
|
|
||||||
Publishes into the existing `rasterandstate/homebrew-tap`.
|
|
||||||
- `.forgejo/issue_template/{bug,feature,api-gap}.md` and
|
|
||||||
`.forgejo/pull_request_template.md` to keep triage cheap.
|
|
||||||
- `scripts/record-demo.sh` + `scripts/_demo-session.sh` to record an
|
|
||||||
asciinema demo; README has a placeholder embed for the v0.1.0 cast.
|
|
||||||
- `docs/compatibility.md`: tested Forgejo versions, caveats for older
|
|
||||||
Gitea, and Forgejo-only endpoints.
|
|
||||||
- One-shot version probe during `fj auth login` that warns when the
|
|
||||||
server reports a pre-7.x version (with tests for the parser).
|
|
||||||
|
|
||||||
### Added (agent-focused Forgejo gaps)
|
|
||||||
|
|
||||||
- `fj issue edit-comment` / `delete-comment`. Lets an agent (or you)
|
|
||||||
fix or remove a wrong comment after the fact.
|
|
||||||
- `fj search code "..."` (and `-R owner/name` to scope to one repo).
|
|
||||||
Powered by Forgejo's `/repos/search/code` endpoint.
|
|
||||||
- `fj pr request-review N user1 user2,user3` and
|
|
||||||
`fj pr unrequest-review N user1`. Distinct from `pr review`, which
|
|
||||||
submits your own review.
|
|
||||||
- `fj repo watch` / `unwatch` / `star` / `unstar` / `starred`.
|
|
||||||
- `fj milestone {list,view,create,edit,close,reopen,delete,assign}`.
|
|
||||||
Includes `fj milestone assign N --milestone ID|none` to attach an
|
|
||||||
issue or PR to a milestone.
|
|
||||||
|
|
||||||
### Added (UX + stability)
|
|
||||||
|
|
||||||
- `--json-fields field1,field2` global flag. gh-style projection on top
|
|
||||||
of any `--json` output, with dotted-path support
|
|
||||||
(`--json-fields owner.login,id`).
|
|
||||||
- 429 / Retry-After honored in the retry loop with a 30 s cap. Wiremock
|
|
||||||
test added.
|
|
||||||
- `did you mean` suggestions on typo'd subcommands via clap's
|
|
||||||
`suggestions` feature.
|
|
||||||
- `fj auth token` and `fj auth status --show-token` now refuse to write
|
|
||||||
to a TTY (use `--force` to override). Avoids accidental shoulder-
|
|
||||||
surfing or capture in shell history.
|
|
||||||
- `tokio::signal::ctrl_c()` race in `cli::run` so the pager guard drops
|
|
||||||
cleanly on SIGINT.
|
|
||||||
- 10 wiremock-backed HTTP client integration tests covering retry
|
|
||||||
behavior (5xx, 429), header forwarding, pagination, and panic-free
|
|
||||||
error paths.
|
|
||||||
- `Client::for_base_url` test constructor pointing at an arbitrary URL.
|
|
||||||
- `.forgejo/workflows/ci.yml` runs the same gate as the pre-push hook
|
|
||||||
on every push and PR.
|
|
||||||
|
|
||||||
### Added (docs)
|
|
||||||
|
|
||||||
- `SECURITY.md` covering threat model, known sharp edges, and reporting.
|
|
||||||
- `docs/gh-to-fj.md` — complete command-by-command mapping.
|
|
||||||
- `docs/faq.md` — common questions about tokens, hosts, debug,
|
|
||||||
scripting, plugins.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Trimmed dependencies (no more `indicatif`, `futures-util`,
|
|
||||||
`is-terminal`, `textwrap`, `tempfile` in prod). Dropped reqwest
|
|
||||||
features we don't use (`stream`, `brotli`). Release profile uses
|
|
||||||
`lto = "fat"` and `panic = "abort"`.
|
|
||||||
- HTTP retry loop builds the request once and clones via
|
|
||||||
`reqwest::Request::try_clone` per attempt.
|
|
||||||
- Binary size: 5.94 MB → 4.15 MB stripped (-30%).
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- **Shell injection** in `fj auth setup-git`. The hostname now must
|
|
||||||
match a strict DNS pattern before being interpolated into the
|
|
||||||
credential-helper string, and we call `git config` directly with
|
|
||||||
separate args instead of going through `sh -c`.
|
|
||||||
- **Pager won't compile on Windows**. The libc-based `dup2` redirect
|
|
||||||
now lives behind `#[cfg(unix)]`; non-Unix gets a no-op stub that
|
|
||||||
returns `None` from `maybe_start`.
|
|
||||||
- Removed the unsafe `std::env::set_var("FJ_NO_PAGER")` from dispatch.
|
|
||||||
`--no-pager` is now threaded into `pager::maybe_start(force_disabled)`.
|
|
||||||
- Replaced the panicking `.expect("token contains invalid header chars")`
|
|
||||||
in `auth_headers` with a typed error.
|
|
||||||
|
|
||||||
## 0.1.0 — 2026-05-13
|
|
||||||
|
|
||||||
Initial release. Multi-host Forgejo CLI with feature parity to `gh`
|
|
||||||
across the surface Forgejo exposes. Commands:
|
|
||||||
|
|
||||||
- `auth`: login, status, logout, list, switch, token, refresh, setup-git
|
|
||||||
- `repo`: list, view, clone, create, fork, sync, edit, rename, archive,
|
|
||||||
unarchive, delete, branches, topics, mirror, mirror-sync
|
|
||||||
- `issue`: list, view, create, edit, close, reopen, comment, develop
|
|
||||||
- `pr`: list, view, create, edit, diff, commits, files, checks, ready,
|
|
||||||
review, status, checkout, merge, close, reopen
|
|
||||||
- `release`: list, view, create, edit, delete, upload, download
|
|
||||||
- `label`, `run`, `secret`, `variable`, `search`, `browse`, `status`,
|
|
||||||
`org`, `ssh-key`, `gpg-key`, `alias`, `config`, `protect`, `hook`,
|
|
||||||
`extension`, `gist`, `api`, `completion`, `man`
|
|
||||||
|
|
||||||
Other highlights:
|
|
||||||
|
|
||||||
- Repo auto-detection from `upstream` / `origin` git remote.
|
|
||||||
- `--web` flag on all list/view subcommands.
|
|
||||||
- `$EDITOR` integration for body inputs.
|
|
||||||
- `fj api` with `-H`, `-X`, `-f`, `-F`, `--paginate`, `--include`,
|
|
||||||
`--silent`, `--jq` (dot-paths, `[N]`/`[-N]`, pipes).
|
|
||||||
- `--debug` / `FJ_DEBUG` request logging.
|
|
||||||
- Tokens in the OS keychain.
|
|
||||||
- Pager via `dup2` redirect to `$FJ_PAGER` / `$PAGER` / `less -FRX`.
|
|
||||||
- Pre-push hook running fmt, clippy `-D warnings`, tests, and release
|
|
||||||
build before any push. Live API smoke gated on `FJ_E2E=1`.
|
|
||||||
21
dist/v0.1.0/fj-v0.1.0-darwin-aarch64/LICENSE
vendored
21
dist/v0.1.0/fj-v0.1.0-darwin-aarch64/LICENSE
vendored
|
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2026 Stephen Way
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
149
dist/v0.1.0/fj-v0.1.0-darwin-aarch64/README.md
vendored
149
dist/v0.1.0/fj-v0.1.0-darwin-aarch64/README.md
vendored
|
|
@ -1,149 +0,0 @@
|
||||||
# fj
|
|
||||||
|
|
||||||
A command-line tool for [Forgejo](https://forgejo.org) instances, in the spirit of GitHub's `gh`.
|
|
||||||
|
|
||||||
Multi-host from day one. Tokens are stored in your OS keychain.
|
|
||||||
|
|
||||||
<!--
|
|
||||||
TODO: replace the line below with an asciinema embed once the v0.1.0
|
|
||||||
release is tagged. Run `./scripts/record-demo.sh` then
|
|
||||||
`asciinema upload dist/demo.cast` and paste the returned URL.
|
|
||||||
-->
|
|
||||||
<!-- [](https://asciinema.org/a/REPLACE) -->
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# Homebrew (macOS + Linux):
|
|
||||||
brew tap rasterandstate/tap
|
|
||||||
brew install fj
|
|
||||||
|
|
||||||
# From source:
|
|
||||||
cargo install --path .
|
|
||||||
# or
|
|
||||||
cargo build --release && cp target/release/fj ~/.local/bin/fj
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick start
|
|
||||||
|
|
||||||
```sh
|
|
||||||
fj auth login # add a host and token
|
|
||||||
fj auth status # show signed-in hosts
|
|
||||||
fj repo list # repos you own
|
|
||||||
fj repo view owner/name # repo overview
|
|
||||||
fj issue list -R owner/name # issues
|
|
||||||
fj pr list -R owner/name --state open # pull requests
|
|
||||||
fj api /repos/search -X GET -f q=foo # raw API escape hatch
|
|
||||||
```
|
|
||||||
|
|
||||||
`-R/--repo` is optional inside a git clone. fj detects the upstream from
|
|
||||||
`upstream`, then `origin`, then any other remote.
|
|
||||||
|
|
||||||
## Commands
|
|
||||||
|
|
||||||
| Group | Common operations |
|
|
||||||
| ---------- | --------------------------------------------------------------------------------- |
|
|
||||||
| `auth` | login, status, logout, list, switch, token, refresh, setup-git |
|
|
||||||
| `repo` | list, view, clone, create, fork, sync, edit, rename, archive, unarchive, delete, branches, topics, mirror, mirror-sync, watch, unwatch, star, unstar, starred |
|
|
||||||
| `issue` | list, view, create, edit, close, reopen, comment, edit-comment, delete-comment, develop |
|
|
||||||
| `pr` | list, view, create, edit, diff, commits, files, checks, ready, review, request-review, unrequest-review, status, checkout, merge, close, reopen |
|
|
||||||
| `release` | list, view, create, edit, delete, upload, download |
|
|
||||||
| `label` | list, create, edit, delete |
|
|
||||||
| `milestone`| list, view, create, edit, close, reopen, delete, assign |
|
|
||||||
| `run` | list, view, rerun, cancel (Forgejo Actions workflow runs) |
|
|
||||||
| `secret` | list, set, delete (Actions secrets) |
|
|
||||||
| `variable` | list, set, delete (Actions variables) |
|
|
||||||
| `search` | repos, issues, prs, users, code |
|
|
||||||
| `browse` | open the current repo (or a path within it) in your browser |
|
|
||||||
| `status` | notifications inbox + `--mark-read` |
|
|
||||||
| `org` | list, view, teams |
|
|
||||||
| `ssh-key` | list, add, delete |
|
|
||||||
| `gpg-key` | list, add, delete |
|
|
||||||
| `alias` | list, set, delete |
|
|
||||||
| `config` | get, set, list, path |
|
|
||||||
| `protect` | list, view, set, delete branch protection rules |
|
|
||||||
| `hook` | list, create, delete, test webhooks |
|
|
||||||
| `gist` | list, create (thin wrapper over `gist-*` repos) |
|
|
||||||
| `extension`| list, run discovered `fj-<name>` plugins on PATH |
|
|
||||||
| `api` | raw HTTP with `-X`, `-f`, `-F`, `-H`, `-q` (jq-ish), `--paginate`, `--include` |
|
|
||||||
| `completion`| Print shell completions (bash, zsh, fish, powershell, elvish) |
|
|
||||||
| `man` | Generate man pages into a directory |
|
|
||||||
|
|
||||||
## Global flags
|
|
||||||
|
|
||||||
- `--host <hostname>` (or `FJ_HOST`): pick the host explicitly.
|
|
||||||
- `--debug` (or `FJ_DEBUG=1`): log every HTTP request to stderr.
|
|
||||||
- `--no-pager` (or `FJ_NO_PAGER=1`): skip the pager.
|
|
||||||
- `--json-fields foo,bar`: gh-style projection on top of any `--json`
|
|
||||||
output. Dotted paths supported (`--json-fields owner.login,id`).
|
|
||||||
- `--web` on most list/view subcommands: open the relevant page in
|
|
||||||
your default browser.
|
|
||||||
|
|
||||||
## Config
|
|
||||||
|
|
||||||
- Hosts and the current host live in `$XDG_CONFIG_HOME/fj/hosts.toml`
|
|
||||||
(`~/Library/Application Support/fj/hosts.toml` on macOS).
|
|
||||||
- Aliases in `aliases.toml`. Preferences in `config.toml`.
|
|
||||||
- Tokens live in the OS keychain under service `fj`, keyed by hostname.
|
|
||||||
|
|
||||||
## Aliases
|
|
||||||
|
|
||||||
```sh
|
|
||||||
fj alias set co "pr checkout"
|
|
||||||
fj co 42 # equivalent to: fj pr checkout 42
|
|
||||||
```
|
|
||||||
|
|
||||||
## Extensions
|
|
||||||
|
|
||||||
Any executable on `$PATH` named `fj-<name>` is invokable as `fj <name> [args]`.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
fj extension list # show discovered plugins
|
|
||||||
fj my-plugin some-arg # invokes `fj-my-plugin some-arg`
|
|
||||||
```
|
|
||||||
|
|
||||||
## Plugin-style usage of `fj api`
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# Auto-paginate a list endpoint into a single JSON array:
|
|
||||||
fj api /repos/search -f q=fj --paginate -q .
|
|
||||||
|
|
||||||
# Pass a custom request header:
|
|
||||||
fj api /user -H "X-Trace-Id: foo"
|
|
||||||
|
|
||||||
# Show response headers along with the body:
|
|
||||||
fj api /version --include
|
|
||||||
|
|
||||||
# Send a literal JSON body:
|
|
||||||
fj api /repos/migrate --input '{"clone_addr":"...","repo_name":"x","repo_owner":"y"}'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Hooks
|
|
||||||
|
|
||||||
- `hooks/pre-push` runs `cargo fmt --check`, `cargo clippy -D warnings`,
|
|
||||||
`cargo test`, and a release build before any push.
|
|
||||||
- With `FJ_E2E=1`, the hook also runs `scripts/e2e-smoke.sh` against the
|
|
||||||
configured host.
|
|
||||||
- Install via `./scripts/install-hooks.sh`.
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cargo build --release # ~4 MB stripped binary at target/release/fj
|
|
||||||
./target/release/fj completion zsh > ~/.zfunc/_fj
|
|
||||||
./target/release/fj man -o ~/man/man1
|
|
||||||
```
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
- [`docs/architecture.md`](docs/architecture.md) — module graph, HTTP
|
|
||||||
funnel, pager + SIGINT, repo resolution
|
|
||||||
- [`docs/jq.md`](docs/jq.md) — `fj api --jq` projection syntax
|
|
||||||
- [`docs/troubleshooting.md`](docs/troubleshooting.md) — keychain
|
|
||||||
prompts, 401s, hook bypass, pager opt-out, alias precedence
|
|
||||||
- [`CONTRIBUTING.md`](CONTRIBUTING.md) — build / test / release workflow
|
|
||||||
- [`CHANGELOG.md`](CHANGELOG.md) — release notes
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
MIT
|
|
||||||
BIN
dist/v0.1.0/fj-v0.1.0-darwin-aarch64/fj
vendored
BIN
dist/v0.1.0/fj-v0.1.0-darwin-aarch64/fj
vendored
Binary file not shown.
BIN
dist/v0.1.0/fj-v0.1.0-darwin-x86_64.tar.gz
vendored
BIN
dist/v0.1.0/fj-v0.1.0-darwin-x86_64.tar.gz
vendored
Binary file not shown.
121
dist/v0.1.0/fj-v0.1.0-darwin-x86_64/CHANGELOG.md
vendored
121
dist/v0.1.0/fj-v0.1.0-darwin-x86_64/CHANGELOG.md
vendored
|
|
@ -1,121 +0,0 @@
|
||||||
# Changelog
|
|
||||||
|
|
||||||
All notable changes will be recorded here. The format follows
|
|
||||||
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versions follow
|
|
||||||
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
||||||
|
|
||||||
## [Unreleased]
|
|
||||||
|
|
||||||
### Added (distribution + open-source ready)
|
|
||||||
|
|
||||||
- `LICENSE` (MIT) at the repo root. Cargo.toml had always declared MIT;
|
|
||||||
the file was just missing.
|
|
||||||
- `.forgejo/workflows/release.yml`: on `v*` tags, builds for
|
|
||||||
`darwin-aarch64`, `darwin-x86_64`, `linux-x86_64`; uploads tarballs,
|
|
||||||
`SHA256SUMS`, and a pre-rendered `fj.rb` to the Forgejo release.
|
|
||||||
- Homebrew formula template at `dist/homebrew/fj.rb.tmpl` and
|
|
||||||
`scripts/render-homebrew-formula.sh` to fill SHA256s post-release.
|
|
||||||
Publishes into the existing `rasterandstate/homebrew-tap`.
|
|
||||||
- `.forgejo/issue_template/{bug,feature,api-gap}.md` and
|
|
||||||
`.forgejo/pull_request_template.md` to keep triage cheap.
|
|
||||||
- `scripts/record-demo.sh` + `scripts/_demo-session.sh` to record an
|
|
||||||
asciinema demo; README has a placeholder embed for the v0.1.0 cast.
|
|
||||||
- `docs/compatibility.md`: tested Forgejo versions, caveats for older
|
|
||||||
Gitea, and Forgejo-only endpoints.
|
|
||||||
- One-shot version probe during `fj auth login` that warns when the
|
|
||||||
server reports a pre-7.x version (with tests for the parser).
|
|
||||||
|
|
||||||
### Added (agent-focused Forgejo gaps)
|
|
||||||
|
|
||||||
- `fj issue edit-comment` / `delete-comment`. Lets an agent (or you)
|
|
||||||
fix or remove a wrong comment after the fact.
|
|
||||||
- `fj search code "..."` (and `-R owner/name` to scope to one repo).
|
|
||||||
Powered by Forgejo's `/repos/search/code` endpoint.
|
|
||||||
- `fj pr request-review N user1 user2,user3` and
|
|
||||||
`fj pr unrequest-review N user1`. Distinct from `pr review`, which
|
|
||||||
submits your own review.
|
|
||||||
- `fj repo watch` / `unwatch` / `star` / `unstar` / `starred`.
|
|
||||||
- `fj milestone {list,view,create,edit,close,reopen,delete,assign}`.
|
|
||||||
Includes `fj milestone assign N --milestone ID|none` to attach an
|
|
||||||
issue or PR to a milestone.
|
|
||||||
|
|
||||||
### Added (UX + stability)
|
|
||||||
|
|
||||||
- `--json-fields field1,field2` global flag. gh-style projection on top
|
|
||||||
of any `--json` output, with dotted-path support
|
|
||||||
(`--json-fields owner.login,id`).
|
|
||||||
- 429 / Retry-After honored in the retry loop with a 30 s cap. Wiremock
|
|
||||||
test added.
|
|
||||||
- `did you mean` suggestions on typo'd subcommands via clap's
|
|
||||||
`suggestions` feature.
|
|
||||||
- `fj auth token` and `fj auth status --show-token` now refuse to write
|
|
||||||
to a TTY (use `--force` to override). Avoids accidental shoulder-
|
|
||||||
surfing or capture in shell history.
|
|
||||||
- `tokio::signal::ctrl_c()` race in `cli::run` so the pager guard drops
|
|
||||||
cleanly on SIGINT.
|
|
||||||
- 10 wiremock-backed HTTP client integration tests covering retry
|
|
||||||
behavior (5xx, 429), header forwarding, pagination, and panic-free
|
|
||||||
error paths.
|
|
||||||
- `Client::for_base_url` test constructor pointing at an arbitrary URL.
|
|
||||||
- `.forgejo/workflows/ci.yml` runs the same gate as the pre-push hook
|
|
||||||
on every push and PR.
|
|
||||||
|
|
||||||
### Added (docs)
|
|
||||||
|
|
||||||
- `SECURITY.md` covering threat model, known sharp edges, and reporting.
|
|
||||||
- `docs/gh-to-fj.md` — complete command-by-command mapping.
|
|
||||||
- `docs/faq.md` — common questions about tokens, hosts, debug,
|
|
||||||
scripting, plugins.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Trimmed dependencies (no more `indicatif`, `futures-util`,
|
|
||||||
`is-terminal`, `textwrap`, `tempfile` in prod). Dropped reqwest
|
|
||||||
features we don't use (`stream`, `brotli`). Release profile uses
|
|
||||||
`lto = "fat"` and `panic = "abort"`.
|
|
||||||
- HTTP retry loop builds the request once and clones via
|
|
||||||
`reqwest::Request::try_clone` per attempt.
|
|
||||||
- Binary size: 5.94 MB → 4.15 MB stripped (-30%).
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- **Shell injection** in `fj auth setup-git`. The hostname now must
|
|
||||||
match a strict DNS pattern before being interpolated into the
|
|
||||||
credential-helper string, and we call `git config` directly with
|
|
||||||
separate args instead of going through `sh -c`.
|
|
||||||
- **Pager won't compile on Windows**. The libc-based `dup2` redirect
|
|
||||||
now lives behind `#[cfg(unix)]`; non-Unix gets a no-op stub that
|
|
||||||
returns `None` from `maybe_start`.
|
|
||||||
- Removed the unsafe `std::env::set_var("FJ_NO_PAGER")` from dispatch.
|
|
||||||
`--no-pager` is now threaded into `pager::maybe_start(force_disabled)`.
|
|
||||||
- Replaced the panicking `.expect("token contains invalid header chars")`
|
|
||||||
in `auth_headers` with a typed error.
|
|
||||||
|
|
||||||
## 0.1.0 — 2026-05-13
|
|
||||||
|
|
||||||
Initial release. Multi-host Forgejo CLI with feature parity to `gh`
|
|
||||||
across the surface Forgejo exposes. Commands:
|
|
||||||
|
|
||||||
- `auth`: login, status, logout, list, switch, token, refresh, setup-git
|
|
||||||
- `repo`: list, view, clone, create, fork, sync, edit, rename, archive,
|
|
||||||
unarchive, delete, branches, topics, mirror, mirror-sync
|
|
||||||
- `issue`: list, view, create, edit, close, reopen, comment, develop
|
|
||||||
- `pr`: list, view, create, edit, diff, commits, files, checks, ready,
|
|
||||||
review, status, checkout, merge, close, reopen
|
|
||||||
- `release`: list, view, create, edit, delete, upload, download
|
|
||||||
- `label`, `run`, `secret`, `variable`, `search`, `browse`, `status`,
|
|
||||||
`org`, `ssh-key`, `gpg-key`, `alias`, `config`, `protect`, `hook`,
|
|
||||||
`extension`, `gist`, `api`, `completion`, `man`
|
|
||||||
|
|
||||||
Other highlights:
|
|
||||||
|
|
||||||
- Repo auto-detection from `upstream` / `origin` git remote.
|
|
||||||
- `--web` flag on all list/view subcommands.
|
|
||||||
- `$EDITOR` integration for body inputs.
|
|
||||||
- `fj api` with `-H`, `-X`, `-f`, `-F`, `--paginate`, `--include`,
|
|
||||||
`--silent`, `--jq` (dot-paths, `[N]`/`[-N]`, pipes).
|
|
||||||
- `--debug` / `FJ_DEBUG` request logging.
|
|
||||||
- Tokens in the OS keychain.
|
|
||||||
- Pager via `dup2` redirect to `$FJ_PAGER` / `$PAGER` / `less -FRX`.
|
|
||||||
- Pre-push hook running fmt, clippy `-D warnings`, tests, and release
|
|
||||||
build before any push. Live API smoke gated on `FJ_E2E=1`.
|
|
||||||
21
dist/v0.1.0/fj-v0.1.0-darwin-x86_64/LICENSE
vendored
21
dist/v0.1.0/fj-v0.1.0-darwin-x86_64/LICENSE
vendored
|
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2026 Stephen Way
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
149
dist/v0.1.0/fj-v0.1.0-darwin-x86_64/README.md
vendored
149
dist/v0.1.0/fj-v0.1.0-darwin-x86_64/README.md
vendored
|
|
@ -1,149 +0,0 @@
|
||||||
# fj
|
|
||||||
|
|
||||||
A command-line tool for [Forgejo](https://forgejo.org) instances, in the spirit of GitHub's `gh`.
|
|
||||||
|
|
||||||
Multi-host from day one. Tokens are stored in your OS keychain.
|
|
||||||
|
|
||||||
<!--
|
|
||||||
TODO: replace the line below with an asciinema embed once the v0.1.0
|
|
||||||
release is tagged. Run `./scripts/record-demo.sh` then
|
|
||||||
`asciinema upload dist/demo.cast` and paste the returned URL.
|
|
||||||
-->
|
|
||||||
<!-- [](https://asciinema.org/a/REPLACE) -->
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# Homebrew (macOS + Linux):
|
|
||||||
brew tap rasterandstate/tap
|
|
||||||
brew install fj
|
|
||||||
|
|
||||||
# From source:
|
|
||||||
cargo install --path .
|
|
||||||
# or
|
|
||||||
cargo build --release && cp target/release/fj ~/.local/bin/fj
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick start
|
|
||||||
|
|
||||||
```sh
|
|
||||||
fj auth login # add a host and token
|
|
||||||
fj auth status # show signed-in hosts
|
|
||||||
fj repo list # repos you own
|
|
||||||
fj repo view owner/name # repo overview
|
|
||||||
fj issue list -R owner/name # issues
|
|
||||||
fj pr list -R owner/name --state open # pull requests
|
|
||||||
fj api /repos/search -X GET -f q=foo # raw API escape hatch
|
|
||||||
```
|
|
||||||
|
|
||||||
`-R/--repo` is optional inside a git clone. fj detects the upstream from
|
|
||||||
`upstream`, then `origin`, then any other remote.
|
|
||||||
|
|
||||||
## Commands
|
|
||||||
|
|
||||||
| Group | Common operations |
|
|
||||||
| ---------- | --------------------------------------------------------------------------------- |
|
|
||||||
| `auth` | login, status, logout, list, switch, token, refresh, setup-git |
|
|
||||||
| `repo` | list, view, clone, create, fork, sync, edit, rename, archive, unarchive, delete, branches, topics, mirror, mirror-sync, watch, unwatch, star, unstar, starred |
|
|
||||||
| `issue` | list, view, create, edit, close, reopen, comment, edit-comment, delete-comment, develop |
|
|
||||||
| `pr` | list, view, create, edit, diff, commits, files, checks, ready, review, request-review, unrequest-review, status, checkout, merge, close, reopen |
|
|
||||||
| `release` | list, view, create, edit, delete, upload, download |
|
|
||||||
| `label` | list, create, edit, delete |
|
|
||||||
| `milestone`| list, view, create, edit, close, reopen, delete, assign |
|
|
||||||
| `run` | list, view, rerun, cancel (Forgejo Actions workflow runs) |
|
|
||||||
| `secret` | list, set, delete (Actions secrets) |
|
|
||||||
| `variable` | list, set, delete (Actions variables) |
|
|
||||||
| `search` | repos, issues, prs, users, code |
|
|
||||||
| `browse` | open the current repo (or a path within it) in your browser |
|
|
||||||
| `status` | notifications inbox + `--mark-read` |
|
|
||||||
| `org` | list, view, teams |
|
|
||||||
| `ssh-key` | list, add, delete |
|
|
||||||
| `gpg-key` | list, add, delete |
|
|
||||||
| `alias` | list, set, delete |
|
|
||||||
| `config` | get, set, list, path |
|
|
||||||
| `protect` | list, view, set, delete branch protection rules |
|
|
||||||
| `hook` | list, create, delete, test webhooks |
|
|
||||||
| `gist` | list, create (thin wrapper over `gist-*` repos) |
|
|
||||||
| `extension`| list, run discovered `fj-<name>` plugins on PATH |
|
|
||||||
| `api` | raw HTTP with `-X`, `-f`, `-F`, `-H`, `-q` (jq-ish), `--paginate`, `--include` |
|
|
||||||
| `completion`| Print shell completions (bash, zsh, fish, powershell, elvish) |
|
|
||||||
| `man` | Generate man pages into a directory |
|
|
||||||
|
|
||||||
## Global flags
|
|
||||||
|
|
||||||
- `--host <hostname>` (or `FJ_HOST`): pick the host explicitly.
|
|
||||||
- `--debug` (or `FJ_DEBUG=1`): log every HTTP request to stderr.
|
|
||||||
- `--no-pager` (or `FJ_NO_PAGER=1`): skip the pager.
|
|
||||||
- `--json-fields foo,bar`: gh-style projection on top of any `--json`
|
|
||||||
output. Dotted paths supported (`--json-fields owner.login,id`).
|
|
||||||
- `--web` on most list/view subcommands: open the relevant page in
|
|
||||||
your default browser.
|
|
||||||
|
|
||||||
## Config
|
|
||||||
|
|
||||||
- Hosts and the current host live in `$XDG_CONFIG_HOME/fj/hosts.toml`
|
|
||||||
(`~/Library/Application Support/fj/hosts.toml` on macOS).
|
|
||||||
- Aliases in `aliases.toml`. Preferences in `config.toml`.
|
|
||||||
- Tokens live in the OS keychain under service `fj`, keyed by hostname.
|
|
||||||
|
|
||||||
## Aliases
|
|
||||||
|
|
||||||
```sh
|
|
||||||
fj alias set co "pr checkout"
|
|
||||||
fj co 42 # equivalent to: fj pr checkout 42
|
|
||||||
```
|
|
||||||
|
|
||||||
## Extensions
|
|
||||||
|
|
||||||
Any executable on `$PATH` named `fj-<name>` is invokable as `fj <name> [args]`.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
fj extension list # show discovered plugins
|
|
||||||
fj my-plugin some-arg # invokes `fj-my-plugin some-arg`
|
|
||||||
```
|
|
||||||
|
|
||||||
## Plugin-style usage of `fj api`
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# Auto-paginate a list endpoint into a single JSON array:
|
|
||||||
fj api /repos/search -f q=fj --paginate -q .
|
|
||||||
|
|
||||||
# Pass a custom request header:
|
|
||||||
fj api /user -H "X-Trace-Id: foo"
|
|
||||||
|
|
||||||
# Show response headers along with the body:
|
|
||||||
fj api /version --include
|
|
||||||
|
|
||||||
# Send a literal JSON body:
|
|
||||||
fj api /repos/migrate --input '{"clone_addr":"...","repo_name":"x","repo_owner":"y"}'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Hooks
|
|
||||||
|
|
||||||
- `hooks/pre-push` runs `cargo fmt --check`, `cargo clippy -D warnings`,
|
|
||||||
`cargo test`, and a release build before any push.
|
|
||||||
- With `FJ_E2E=1`, the hook also runs `scripts/e2e-smoke.sh` against the
|
|
||||||
configured host.
|
|
||||||
- Install via `./scripts/install-hooks.sh`.
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cargo build --release # ~4 MB stripped binary at target/release/fj
|
|
||||||
./target/release/fj completion zsh > ~/.zfunc/_fj
|
|
||||||
./target/release/fj man -o ~/man/man1
|
|
||||||
```
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
- [`docs/architecture.md`](docs/architecture.md) — module graph, HTTP
|
|
||||||
funnel, pager + SIGINT, repo resolution
|
|
||||||
- [`docs/jq.md`](docs/jq.md) — `fj api --jq` projection syntax
|
|
||||||
- [`docs/troubleshooting.md`](docs/troubleshooting.md) — keychain
|
|
||||||
prompts, 401s, hook bypass, pager opt-out, alias precedence
|
|
||||||
- [`CONTRIBUTING.md`](CONTRIBUTING.md) — build / test / release workflow
|
|
||||||
- [`CHANGELOG.md`](CHANGELOG.md) — release notes
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
MIT
|
|
||||||
BIN
dist/v0.1.0/fj-v0.1.0-darwin-x86_64/fj
vendored
BIN
dist/v0.1.0/fj-v0.1.0-darwin-x86_64/fj
vendored
Binary file not shown.
Loading…
Reference in a new issue