rename fgj to fj

Module path, binary name, config dir, help text, and docs
all updated from fgj-sid/fgj to fj.
This commit is contained in:
sid 2026-04-26 08:16:52 -06:00
parent a6cf9a7096
commit bc43f6e5a5
34 changed files with 479 additions and 479 deletions

View file

@ -72,7 +72,7 @@ jobs:
- name: Build production binary - name: Build production binary
run: | run: |
make build make build
echo "Binary built at: $(pwd)/bin/fgj" echo "Binary built at: $(pwd)/bin/fj"
- name: Run functional tests - name: Run functional tests
run: go test -v -race -tags=functional ./tests/functional/... run: go test -v -race -tags=functional ./tests/functional/...

View file

@ -24,7 +24,7 @@ jobs:
- name: Build production binary - name: Build production binary
run: | run: |
make build make build
echo "Binary built at: $(pwd)/bin/fgj" echo "Binary built at: $(pwd)/bin/fj"
- name: Run functional tests - name: Run functional tests
run: go test -v -race -tags=functional ./tests/functional/... run: go test -v -race -tags=functional ./tests/functional/...

View file

@ -10,45 +10,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
#### Label Management #### Label Management
- `fgj label list` - List repository labels - `fj label list` - List repository labels
- `fgj label create` - Create a label with color and description - `fj label create` - Create a label with color and description
- `fgj label edit` - Edit label name, color, or description - `fj label edit` - Edit label name, color, or description
- `fgj label delete` - Delete a label - `fj label delete` - Delete a label
#### Milestone Management #### Milestone Management
- `fgj milestone list` - List milestones with state filtering - `fj milestone list` - List milestones with state filtering
- `fgj milestone view` - View milestone details - `fj milestone view` - View milestone details
- `fgj milestone create` - Create a milestone with description and due date - `fj milestone create` - Create a milestone with description and due date
- `fgj milestone edit` - Edit milestone title, description, due date, or state - `fj milestone edit` - Edit milestone title, description, due date, or state
- `fgj milestone delete` - Delete a milestone - `fj milestone delete` - Delete a milestone
#### Wiki Management #### Wiki Management
- `fgj wiki list` - List wiki pages - `fj wiki list` - List wiki pages
- `fgj wiki view` - View wiki page content - `fj wiki view` - View wiki page content
- `fgj wiki create` - Create a wiki page from flag or file - `fj wiki create` - Create a wiki page from flag or file
- `fgj wiki edit` - Edit a wiki page - `fj wiki edit` - Edit a wiki page
- `fgj wiki delete` - Delete a wiki page - `fj wiki delete` - Delete a wiki page
#### Issue Dependencies #### Issue Dependencies
- `fgj issue edit --add-dependency <number>` - Add issue dependency - `fj issue edit --add-dependency <number>` - Add issue dependency
- `fgj issue edit --remove-dependency <number>` - Remove issue dependency - `fj issue edit --remove-dependency <number>` - Remove issue dependency
## [0.3.0b] - 2026-03-21 ## [0.3.0b] - 2026-03-21
### Added ### Added
#### Repository Management #### Repository Management
- `fgj repo edit` - Edit repository settings (visibility, description, homepage, default branch) - `fj repo edit` - Edit repository settings (visibility, description, homepage, default branch)
### Fixed ### Fixed
- `fgj repo create --public` flag was defined but never read; now properly wired up - `fj repo create --public` flag was defined but never read; now properly wired up
## [0.3.0a] - 2026-03-21 ## [0.3.0a] - 2026-03-21
### Added ### Added
#### Raw API Access #### Raw API Access
- `fgj api <endpoint>` - Make authenticated REST API requests to any Forgejo/Gitea endpoint - `fj api <endpoint>` - Make authenticated REST API requests to any Forgejo/Gitea endpoint
- HTTP method selection (`--method`/`-X`), auto-switches to POST when fields are provided - HTTP method selection (`--method`/`-X`), auto-switches to POST when fields are provided
- JSON field assembly (`--field`/`-f`) with type inference (bool, int, float, null, string) - JSON field assembly (`--field`/`-f`) with type inference (bool, int, float, null, string)
- Raw string fields (`--raw-field`/`-F`) - Raw string fields (`--raw-field`/`-F`)
@ -58,14 +58,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Response header display (`--include`/`-i`) - Response header display (`--include`/`-i`)
#### Pull Request Management #### Pull Request Management
- `fgj pr diff <number>` - View the diff for a pull request - `fj pr diff <number>` - View the diff for a pull request
- Colorized output (`--color auto/always/never`) - Colorized output (`--color auto/always/never`)
- Changed file names only (`--name-only`) - Changed file names only (`--name-only`)
- Diffstat summary (`--stat`) - Diffstat summary (`--stat`)
- `fgj pr comment <number>` - Add a comment to a pull request - `fj pr comment <number>` - Add a comment to a pull request
- Body from flag (`--body`/`-b`) or file (`--body-file`, `-` for stdin) - Body from flag (`--body`/`-b`) or file (`--body-file`, `-` for stdin)
- JSON output (`--json`) - JSON output (`--json`)
- `fgj pr review <number>` - Submit a review on a pull request - `fj pr review <number>` - Submit a review on a pull request
- Approve (`--approve`/`-a`), request changes (`--request-changes`/`-r`), or comment (`--comment`/`-c`) - Approve (`--approve`/`-a`), request changes (`--request-changes`/`-r`), or comment (`--comment`/`-c`)
- Body from flag or file - Body from flag or file
- JSON output (`--json`) - JSON output (`--json`)
@ -81,30 +81,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
#### Forgejo Actions #### Forgejo Actions
- `fgj actions run watch <run-id>` - Poll a run until completion - `fj actions run watch <run-id>` - Poll a run until completion
- `fgj actions run rerun <run-id>` - Trigger a rerun of a workflow run - `fj actions run rerun <run-id>` - Trigger a rerun of a workflow run
- `fgj actions run cancel <run-id>` - Cancel an in-progress workflow run - `fj actions run cancel <run-id>` - Cancel an in-progress workflow run
- `fgj actions workflow enable <workflow>` - Enable a workflow - `fj actions workflow enable <workflow>` - Enable a workflow
- `fgj actions workflow disable <workflow>` - Disable a workflow - `fj actions workflow disable <workflow>` - Disable a workflow
#### Repository Management #### Repository Management
- `fgj repo create <name>` - Create a new repository with full option set: `--private`/`--public`, `--description`, `--add-readme`, `--gitignore`, `--license`, `--homepage`, `--clone`, `--team` - `fj repo create <name>` - Create a new repository with full option set: `--private`/`--public`, `--description`, `--add-readme`, `--gitignore`, `--license`, `--homepage`, `--clone`, `--team`
#### Issue Management #### Issue Management
- `fgj issue create -l <label>` - Assign labels when creating an issue - `fj issue create -l <label>` - Assign labels when creating an issue
- `fgj issue edit --add-label` / `--remove-label` - Add or remove labels on existing issues - `fj issue edit --add-label` / `--remove-label` - Add or remove labels on existing issues
- `fgj issue close -c <comment>` - Close an issue with an optional comment - `fj issue close -c <comment>` - Close an issue with an optional comment
#### Workflow Management #### Workflow Management
- `fgj actions workflow list/view/run` - List, view, and trigger workflows - `fj actions workflow list/view/run` - List, view, and trigger workflows
#### Auth Helpers #### Auth Helpers
- `fgj auth token` - Print the stored token for the current host - `fj auth token` - Print the stored token for the current host
- `fgj auth logout` - Remove authentication for a host - `fj auth logout` - Remove authentication for a host
#### Shell Completions and Man Pages #### Shell Completions and Man Pages
- `fgj completion [bash|zsh|fish|powershell]` - Generate shell completion scripts - `fj completion [bash|zsh|fish|powershell]` - Generate shell completion scripts
- `fgj manpages --dir <path>` - Generate man pages for all commands - `fj manpages --dir <path>` - Generate man pages for all commands
#### JSON Output #### JSON Output
- `--json` flag for all list and view commands: PRs, issues, releases, workflow runs, workflows - `--json` flag for all list and view commands: PRs, issues, releases, workflow runs, workflows
@ -117,17 +117,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
#### Release Management #### Release Management
- `fgj release list` - List releases for a repository - `fj release list` - List releases for a repository
- `fgj release view` - View details of a specific release (supports "latest" keyword) - `fj release view` - View details of a specific release (supports "latest" keyword)
- `fgj release create` - Create new releases with optional asset uploads - `fj release create` - Create new releases with optional asset uploads
- `fgj release upload` - Upload assets to existing releases with optional clobber support - `fj release upload` - Upload assets to existing releases with optional clobber support
- `fgj release delete` - Delete releases (preserves Git tags) - `fj release delete` - Delete releases (preserves Git tags)
#### Issue Management #### Issue Management
- `fgj issue edit` - Edit existing issues with support for updating title, body, and labels - `fj issue edit` - Edit existing issues with support for updating title, body, and labels
#### Pull Request Management #### Pull Request Management
- `fgj pr create --assignee` - Assign users when creating pull requests - `fj pr create --assignee` - Assign users when creating pull requests
#### Repository Detection #### Repository Detection
- Automatic hostname detection from git remote URLs - Automatic hostname detection from git remote URLs
@ -148,47 +148,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
#### Core Features #### Core Features
- Initial release of fgj - Forgejo CLI tool - Initial release of fj - Forgejo CLI tool
- Multi-instance support for any Forgejo/Gitea instance - Multi-instance support for any Forgejo/Gitea instance
- Automatic repository detection from git context (optional `-R` flag) - Automatic repository detection from git context (optional `-R` flag)
- Secure authentication with personal access tokens - Secure authentication with personal access tokens
- Configuration management via `~/.config/fgj/config.yaml` - Configuration management via `~/.config/fj/config.yaml`
#### Pull Request Management #### Pull Request Management
- `fgj pr list` - List pull requests with filtering by state - `fj pr list` - List pull requests with filtering by state
- `fgj pr view` - View detailed pull request information - `fj pr view` - View detailed pull request information
- `fgj pr create` - Create new pull requests - `fj pr create` - Create new pull requests
- `fgj pr merge` - Merge pull requests with configurable merge methods - `fj pr merge` - Merge pull requests with configurable merge methods
#### Issue Management #### Issue Management
- `fgj issue list` - List issues with state filtering - `fj issue list` - List issues with state filtering
- `fgj issue view` - View detailed issue information - `fj issue view` - View detailed issue information
- `fgj issue create` - Create new issues - `fj issue create` - Create new issues
- `fgj issue comment` - Add comments to issues - `fj issue comment` - Add comments to issues
- `fgj issue close` - Close issues - `fj issue close` - Close issues
#### Repository Operations #### Repository Operations
- `fgj repo view` - View repository details - `fj repo view` - View repository details
- `fgj repo list` - List user repositories - `fj repo list` - List user repositories
- `fgj repo clone` - Clone repositories with protocol selection (HTTPS/SSH) - `fj repo clone` - Clone repositories with protocol selection (HTTPS/SSH)
- `fgj repo fork` - Fork repositories - `fj repo fork` - Fork repositories
#### Forgejo Actions Support #### Forgejo Actions Support
- `fgj actions run list` - List workflow runs with status and metadata - `fj actions run list` - List workflow runs with status and metadata
- `fgj actions run view` - View detailed run information, jobs, and logs - `fj actions run view` - View detailed run information, jobs, and logs
- Support for `--verbose`, `--log`, `--log-failed`, and `--job` flags - Support for `--verbose`, `--log`, `--log-failed`, and `--job` flags
- `fgj actions secret list` - List repository secrets - `fj actions secret list` - List repository secrets
- `fgj actions secret create` - Create repository secrets - `fj actions secret create` - Create repository secrets
- `fgj actions secret delete` - Delete repository secrets - `fj actions secret delete` - Delete repository secrets
- `fgj actions variable list` - List repository variables - `fj actions variable list` - List repository variables
- `fgj actions variable get` - Get variable values - `fj actions variable get` - Get variable values
- `fgj actions variable create` - Create repository variables - `fj actions variable create` - Create repository variables
- `fgj actions variable update` - Update repository variables - `fj actions variable update` - Update repository variables
- `fgj actions variable delete` - Delete repository variables - `fj actions variable delete` - Delete repository variables
#### Authentication #### Authentication
- `fgj auth login` - Interactive authentication with Forgejo instances - `fj auth login` - Interactive authentication with Forgejo instances
- `fgj auth status` - Check authentication status - `fj auth status` - Check authentication status
- Environment variable support (`FGJ_HOST`, `FGJ_TOKEN`) - Environment variable support (`FGJ_HOST`, `FGJ_TOKEN`)
#### Development #### Development
@ -203,9 +203,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Cobra framework for CLI structure - Cobra framework for CLI structure
- Viper for configuration management - Viper for configuration management
[0.3.0c]: https://forgejo.zerova.net/public/fgj-sid/releases/tag/v0.3.0c [0.3.0c]: https://forgejo.zerova.net/public/fj/releases/tag/v0.3.0c
[0.3.0b]: https://forgejo.zerova.net/public/fgj-sid/releases/tag/v0.3.0b [0.3.0b]: https://forgejo.zerova.net/public/fj/releases/tag/v0.3.0b
[0.3.0a]: https://forgejo.zerova.net/public/fgj-sid/releases/tag/v0.3.0a [0.3.0a]: https://forgejo.zerova.net/public/fj/releases/tag/v0.3.0a
[0.3.0]: https://codeberg.org/romaintb/fgj/releases/tag/v0.3.0 [0.3.0]: https://codeberg.org/romaintb/fj/releases/tag/v0.3.0
[0.2.0]: https://codeberg.org/romaintb/fgj/releases/tag/v0.2.0 [0.2.0]: https://codeberg.org/romaintb/fj/releases/tag/v0.2.0
[0.1.0]: https://codeberg.org/romaintb/fgj/releases/tag/v0.1.0 [0.1.0]: https://codeberg.org/romaintb/fj/releases/tag/v0.1.0

View file

@ -11,10 +11,10 @@ help:
@echo " make clean - Clean build artifacts" @echo " make clean - Clean build artifacts"
build: build:
go build -o bin/fgj . go build -o bin/fj .
install: build install: build
install -Dm755 bin/fgj /usr/bin/fgj install -Dm755 bin/fj /usr/bin/fj
run: run:
go run . go run .

286
README.md
View file

@ -1,11 +1,11 @@
# fgj - Forgejo/Gitea CLI Tool # fj - Forgejo/Gitea CLI Tool
[![Go Version](https://img.shields.io/badge/Go-1.23+-00ADD8?style=flat-square&logo=go)](https://golang.org) [![Go Version](https://img.shields.io/badge/Go-1.23+-00ADD8?style=flat-square&logo=go)](https://golang.org)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE)
`fgj` is a command-line tool for working with Forgejo and Gitea instances. It brings pull requests, issues, and other forge concepts to the terminal, similar to what `gh` does for GitHub. This fork adds agentic dev features — raw API access, PR review workflows, structured error output, and machine-readable I/O for AI coding agents. `fj` is a command-line tool for working with Forgejo and Gitea instances. It brings pull requests, issues, and other forge concepts to the terminal, similar to what `gh` does for GitHub. This fork adds agentic dev features — raw API access, PR review workflows, structured error output, and machine-readable I/O for AI coding agents.
> Forked from [codeberg.org/romaintb/fgj](https://codeberg.org/romaintb/fgj) and hosted at [forgejo.zerova.net/public/fgj-sid](https://forgejo.zerova.net/public/fgj-sid). > Forked from [codeberg.org/romaintb/fj](https://codeberg.org/romaintb/fj) and hosted at [forgejo.zerova.net/public/fj](https://forgejo.zerova.net/public/fj).
## Features ## Features
@ -19,7 +19,7 @@
- Issue dependencies (`--add-dependency`, `--remove-dependency`) - Issue dependencies (`--add-dependency`, `--remove-dependency`)
- Forgejo Actions (workflow runs, watch/rerun/cancel, enable/disable, secrets, variables) - Forgejo Actions (workflow runs, watch/rerun/cancel, enable/disable, secrets, variables)
- Releases (create, upload, delete) - Releases (create, upload, delete)
- Raw API access (`fgj api`) for arbitrary REST calls - Raw API access (`fj api`) for arbitrary REST calls
- Shell completions (bash, zsh, fish, PowerShell) and man pages - Shell completions (bash, zsh, fish, PowerShell) and man pages
- JSON output (`--json`) for all list/view commands - JSON output (`--json`) for all list/view commands
- Structured JSON error output (`--json-errors`) for machine consumption - Structured JSON error output (`--json-errors`) for machine consumption
@ -33,22 +33,22 @@
### macOS (Homebrew) ### macOS (Homebrew)
```bash ```bash
brew tap sid/fgj-sid https://forgejo.zerova.net/sid/homebrew-fgj-sid.git brew tap sid/fj https://forgejo.zerova.net/sid/homebrew-fj.git
brew install fgj brew install fj
``` ```
### Using Go Install ### Using Go Install
```bash ```bash
go install forgejo.zerova.net/public/fgj-sid@latest go install forgejo.zerova.net/public/fj@latest
``` ```
### From Source ### From Source
```bash ```bash
git clone https://forgejo.zerova.net/public/fgj-sid.git git clone https://forgejo.zerova.net/public/fj.git
cd fgj-sid cd fj
go build -o fgj . go build -o fj .
``` ```
## Quick Start ## Quick Start
@ -58,7 +58,7 @@ go build -o fgj .
First, authenticate with your Forgejo or Gitea instance: First, authenticate with your Forgejo or Gitea instance:
```bash ```bash
fgj auth login fj auth login
``` ```
You'll be prompted for: You'll be prompted for:
@ -74,34 +74,34 @@ To create a personal access token:
### 2. Check Authentication Status ### 2. Check Authentication Status
```bash ```bash
fgj auth status fj auth status
``` ```
### Auth Helpers ### Auth Helpers
```bash ```bash
# Print the stored token for the current host # Print the stored token for the current host
fgj auth token fj auth token
# Remove authentication for a host # Remove authentication for a host
fgj auth logout fj auth logout
``` ```
## Usage ## Usage
### Repository Detection ### Repository Detection
`fgj` automatically detects the repository from your git context, similar to `gh`: `fj` automatically detects the repository from your git context, similar to `gh`:
```bash ```bash
# When inside a git repository, no -R flag needed! # When inside a git repository, no -R flag needed!
cd /path/to/your/repo cd /path/to/your/repo
fgj pr list # Automatically uses current repo fj pr list # Automatically uses current repo
fgj issue list # Automatically uses current repo fj issue list # Automatically uses current repo
fgj pr view 123 # Automatically uses current repo fj pr view 123 # Automatically uses current repo
# Or explicitly specify a repository with -R # Or explicitly specify a repository with -R
fgj pr list -R owner/repo fj pr list -R owner/repo
``` ```
The tool reads `.git/config` to find the origin remote and extract both the owner/repo information and the instance hostname. If you're not in a git repository, you'll need to use the `-R` flag. The tool reads `.git/config` to find the origin remote and extract both the owner/repo information and the instance hostname. If you're not in a git repository, you'll need to use the `-R` flag.
@ -110,307 +110,307 @@ The tool reads `.git/config` to find the origin remote and extract both the owne
```bash ```bash
# List pull requests (auto-detects repo and hostname from git) # List pull requests (auto-detects repo and hostname from git)
fgj pr list fj pr list
# Or specify explicitly # Or specify explicitly
fgj pr list -R owner/repo fj pr list -R owner/repo
# Filter by state # Filter by state
fgj pr list --state closed fj pr list --state closed
# View a specific pull request # View a specific pull request
fgj pr view 123 fj pr view 123
# Create a pull request # Create a pull request
fgj pr create -t "PR Title" -b "PR Description" -H feature-branch -B main fj pr create -t "PR Title" -b "PR Description" -H feature-branch -B main
# Merge a pull request # Merge a pull request
fgj pr merge 123 --merge-method squash fj pr merge 123 --merge-method squash
# View PR diff # View PR diff
fgj pr diff 123 fj pr diff 123
# View diff with color # View diff with color
fgj pr diff 123 --color always fj pr diff 123 --color always
# Show only changed file names # Show only changed file names
fgj pr diff 123 --name-only fj pr diff 123 --name-only
# Show diffstat summary # Show diffstat summary
fgj pr diff 123 --stat fj pr diff 123 --stat
# Comment on a pull request # Comment on a pull request
fgj pr comment 123 -b "Looks good, minor nit on line 42" fj pr comment 123 -b "Looks good, minor nit on line 42"
# Comment from a file # Comment from a file
fgj pr comment 123 --body-file review-notes.md fj pr comment 123 --body-file review-notes.md
# Approve a pull request # Approve a pull request
fgj pr review 123 --approve -b "LGTM" fj pr review 123 --approve -b "LGTM"
# Request changes # Request changes
fgj pr review 123 --request-changes -b "Please fix the error handling" fj pr review 123 --request-changes -b "Please fix the error handling"
# Submit a review comment (neither approve nor request changes) # Submit a review comment (neither approve nor request changes)
fgj pr review 123 --comment -b "Some observations" fj pr review 123 --comment -b "Some observations"
``` ```
### Issues ### Issues
```bash ```bash
# List issues (auto-detects repo and hostname from git) # List issues (auto-detects repo and hostname from git)
fgj issue list fj issue list
# Or specify explicitly # Or specify explicitly
fgj issue list -R owner/repo fj issue list -R owner/repo
# Filter by state # Filter by state
fgj issue list --state all fj issue list --state all
# View an issue # View an issue
fgj issue view 456 fj issue view 456
# Create an issue # Create an issue
fgj issue create -t "Issue Title" -b "Issue Description" fj issue create -t "Issue Title" -b "Issue Description"
# Create an issue with labels # Create an issue with labels
fgj issue create -t "Issue Title" -b "Issue Description" -l bug -l enhancement fj issue create -t "Issue Title" -b "Issue Description" -l bug -l enhancement
# Comment on an issue # Comment on an issue
fgj issue comment 456 -b "My comment" fj issue comment 456 -b "My comment"
# Close an issue # Close an issue
fgj issue close 456 fj issue close 456
# Close an issue with a comment # Close an issue with a comment
fgj issue close 456 -c "Fixed in v2.0" fj issue close 456 -c "Fixed in v2.0"
# Edit an issue (title, body, state, labels) # Edit an issue (title, body, state, labels)
fgj issue edit 456 -t "New Title" fj issue edit 456 -t "New Title"
fgj issue edit 456 --add-label priority --remove-label bug fj issue edit 456 --add-label priority --remove-label bug
# Manage issue dependencies # Manage issue dependencies
fgj issue edit 456 --add-dependency 123 fj issue edit 456 --add-dependency 123
fgj issue edit 456 --remove-dependency 123 fj issue edit 456 --remove-dependency 123
``` ```
### Labels ### Labels
```bash ```bash
# List labels # List labels
fgj label list fj label list
# Create a label # Create a label
fgj label create bug --color ff0000 -d "Something isn't working" fj label create bug --color ff0000 -d "Something isn't working"
# Edit a label # Edit a label
fgj label edit bug --name bugfix --color ee0000 fj label edit bug --name bugfix --color ee0000
# Delete a label # Delete a label
fgj label delete bug fj label delete bug
``` ```
### Milestones ### Milestones
```bash ```bash
# List milestones # List milestones
fgj milestone list fj milestone list
fgj milestone list --state all fj milestone list --state all
# View a milestone # View a milestone
fgj milestone view "v1.0" fj milestone view "v1.0"
# Create a milestone with due date # Create a milestone with due date
fgj milestone create "v2.0" -d "Next major release" --due 2026-06-01 fj milestone create "v2.0" -d "Next major release" --due 2026-06-01
# Edit a milestone # Edit a milestone
fgj milestone edit "v2.0" --title "v2.0-rc1" --state closed fj milestone edit "v2.0" --title "v2.0-rc1" --state closed
# Delete a milestone # Delete a milestone
fgj milestone delete "v2.0" fj milestone delete "v2.0"
``` ```
### Wiki ### Wiki
```bash ```bash
# List wiki pages # List wiki pages
fgj wiki list fj wiki list
# View a wiki page # View a wiki page
fgj wiki view "Home" fj wiki view "Home"
# Create a wiki page # Create a wiki page
fgj wiki create "Setup Guide" -b "# Setup\n\nFollow these steps..." fj wiki create "Setup Guide" -b "# Setup\n\nFollow these steps..."
# Create from file # Create from file
fgj wiki create "API Docs" --body-file docs/api.md fj wiki create "API Docs" --body-file docs/api.md
# Edit a wiki page # Edit a wiki page
fgj wiki edit "Home" -b "Updated content" fj wiki edit "Home" -b "Updated content"
# Delete a wiki page # Delete a wiki page
fgj wiki delete "Old Page" fj wiki delete "Old Page"
``` ```
### Repositories ### Repositories
```bash ```bash
# View repository details # View repository details
fgj repo view owner/repo fj repo view owner/repo
# List your repositories # List your repositories
fgj repo list fj repo list
# Create a repository # Create a repository
fgj repo create my-repo fj repo create my-repo
fgj repo create my-repo -d "My project" --private --add-readme -g Go -l MIT fj repo create my-repo -d "My project" --private --add-readme -g Go -l MIT
# Clone a repository # Clone a repository
fgj repo clone owner/repo fj repo clone owner/repo
# Clone via SSH # Clone via SSH
fgj repo clone owner/repo -p ssh fj repo clone owner/repo -p ssh
# Fork a repository # Fork a repository
fgj repo fork owner/repo fj repo fork owner/repo
# Edit repository settings # Edit repository settings
fgj repo edit owner/repo --public fj repo edit owner/repo --public
fgj repo edit owner/repo --private fj repo edit owner/repo --private
fgj repo edit owner/repo -d "New description" --homepage https://example.com fj repo edit owner/repo -d "New description" --homepage https://example.com
fgj repo edit --default-branch develop fj repo edit --default-branch develop
fgj repo edit owner/repo --name new-name fj repo edit owner/repo --name new-name
# Rename a repository (shorthand) # Rename a repository (shorthand)
fgj repo rename new-name fj repo rename new-name
fgj repo rename new-name -R owner/old-name fj repo rename new-name -R owner/old-name
``` ```
### Releases ### Releases
```bash ```bash
# List releases # List releases
fgj release list fj release list
# View a release (or use "latest") # View a release (or use "latest")
fgj release view v1.2.3 fj release view v1.2.3
# Create a release with notes and optional assets # Create a release with notes and optional assets
fgj release create v1.2.3 -t "v1.2.3" -n "Release notes" ./dist/app.tar.gz fj release create v1.2.3 -t "v1.2.3" -n "Release notes" ./dist/app.tar.gz
# Upload assets to an existing release # Upload assets to an existing release
fgj release upload v1.2.3 ./dist/app.tar.gz --clobber fj release upload v1.2.3 ./dist/app.tar.gz --clobber
# Delete a release (keeps the Git tag) # Delete a release (keeps the Git tag)
fgj release delete v1.2.3 fj release delete v1.2.3
``` ```
### Forgejo Actions ### Forgejo Actions
```bash ```bash
# List workflows # List workflows
fgj actions workflow list fj actions workflow list
# View a workflow # View a workflow
fgj actions workflow view ci.yml fj actions workflow view ci.yml
# Run a workflow (trigger workflow_dispatch) # Run a workflow (trigger workflow_dispatch)
fgj actions workflow run deploy.yml fj actions workflow run deploy.yml
# Run a workflow with inputs # Run a workflow with inputs
fgj actions workflow run deploy.yml -f environment=production -f version=1.2.3 fj actions workflow run deploy.yml -f environment=production -f version=1.2.3
# Run a workflow on a specific branch # Run a workflow on a specific branch
fgj actions workflow run deploy.yml -r feature-branch fj actions workflow run deploy.yml -r feature-branch
# Enable or disable a workflow # Enable or disable a workflow
fgj actions workflow enable ci.yml fj actions workflow enable ci.yml
fgj actions workflow disable ci.yml fj actions workflow disable ci.yml
# List workflow runs # List workflow runs
fgj actions run list fj actions run list
# View a specific run # View a specific run
fgj actions run view 123 fj actions run view 123
# View run with job details # View run with job details
fgj actions run view 123 --verbose fj actions run view 123 --verbose
# View run logs # View run logs
fgj actions run view 123 --log fj actions run view 123 --log
# View specific job logs # View specific job logs
fgj actions run view 123 --job 456 --log fj actions run view 123 --job 456 --log
# Watch a run until completion # Watch a run until completion
fgj actions run watch 123 fj actions run watch 123
# Rerun a workflow run # Rerun a workflow run
fgj actions run rerun 123 fj actions run rerun 123
# Cancel a running workflow # Cancel a running workflow
fgj actions run cancel 123 fj actions run cancel 123
# List secrets # List secrets
fgj actions secret list fj actions secret list
# Create a secret # Create a secret
fgj actions secret create MY_SECRET fj actions secret create MY_SECRET
# Delete a secret # Delete a secret
fgj actions secret delete MY_SECRET fj actions secret delete MY_SECRET
# List variables # List variables
fgj actions variable list fj actions variable list
# Get a variable # Get a variable
fgj actions variable get MY_VAR fj actions variable get MY_VAR
# Create a variable # Create a variable
fgj actions variable create MY_VAR "value" fj actions variable create MY_VAR "value"
# Update a variable # Update a variable
fgj actions variable update MY_VAR "new value" fj actions variable update MY_VAR "new value"
# Delete a variable # Delete a variable
fgj actions variable delete MY_VAR fj actions variable delete MY_VAR
``` ```
### Raw API Access ### Raw API Access
```bash ```bash
# GET request (auto-detects owner/repo from git context) # GET request (auto-detects owner/repo from git context)
fgj api /repos/{owner}/{repo}/pulls fj api /repos/{owner}/{repo}/pulls
# POST with fields # POST with fields
fgj api /repos/{owner}/{repo}/issues -X POST -f title="Bug report" -f body="Description" fj api /repos/{owner}/{repo}/issues -X POST -f title="Bug report" -f body="Description"
# Explicit method and hostname # Explicit method and hostname
fgj api /repos/myorg/myrepo/labels --hostname my-forgejo.example.com fj api /repos/myorg/myrepo/labels --hostname my-forgejo.example.com
# Read request body from file # Read request body from file
fgj api /repos/{owner}/{repo}/issues -X POST --input issue.json fj api /repos/{owner}/{repo}/issues -X POST --input issue.json
# Read from stdin # Read from stdin
echo '{"title":"test"}' | fgj api /repos/{owner}/{repo}/issues -X POST --input - echo '{"title":"test"}' | fj api /repos/{owner}/{repo}/issues -X POST --input -
# Include response headers # Include response headers
fgj api /repos/{owner}/{repo} -i fj api /repos/{owner}/{repo} -i
# Suppress output (useful for DELETE) # Suppress output (useful for DELETE)
fgj api /repos/{owner}/{repo}/issues/123 -X DELETE --silent fj api /repos/{owner}/{repo}/issues/123 -X DELETE --silent
``` ```
## Shell Completions and Man Pages ## Shell Completions and Man Pages
```bash ```bash
# Generate shell completion scripts # Generate shell completion scripts
fgj completion bash > /etc/bash_completion.d/fgj fj completion bash > /etc/bash_completion.d/fj
fgj completion zsh > "${fpath[1]}/_fgj" fj completion zsh > "${fpath[1]}/_fj"
fgj completion fish > ~/.config/fish/completions/fgj.fish fj completion fish > ~/.config/fish/completions/fj.fish
# Generate man pages to a directory # Generate man pages to a directory
fgj manpages --dir ~/.local/share/man/man1 fj manpages --dir ~/.local/share/man/man1
``` ```
## JSON Output ## JSON Output
@ -418,15 +418,15 @@ fgj manpages --dir ~/.local/share/man/man1
Most list and view commands support `--json` for machine-readable output: Most list and view commands support `--json` for machine-readable output:
```bash ```bash
fgj pr list --json fj pr list --json
fgj issue view 456 --json fj issue view 456 --json
fgj release list --json fj release list --json
fgj actions run list --json fj actions run list --json
fgj actions workflow view ci.yml --json fj actions workflow view ci.yml --json
# Get JSON output from PR comment/review # Get JSON output from PR comment/review
fgj pr comment 123 -b "LGTM" --json fj pr comment 123 -b "LGTM" --json
fgj pr review 123 --approve -b "Ship it" --json fj pr review 123 --approve -b "Ship it" --json
``` ```
### Structured Error Output ### Structured Error Output
@ -435,16 +435,16 @@ For machine consumption (ideal for AI agents and scripts), use `--json-errors` t
```bash ```bash
# Errors are written to stderr as JSON # Errors are written to stderr as JSON
fgj pr view 9999 --json-errors fj pr view 9999 --json-errors
# stderr: {"error":{"code":"not_found","message":"...","status":404}} # stderr: {"error":{"code":"not_found","message":"...","status":404}}
# Combine with --json for fully machine-readable I/O # Combine with --json for fully machine-readable I/O
fgj pr list --json --json-errors fj pr list --json --json-errors
``` ```
## Configuration ## Configuration
Configuration is stored in `~/.config/fgj/config.yaml`: Configuration is stored in `~/.config/fj/config.yaml`:
```yaml ```yaml
hosts: hosts:
@ -466,9 +466,9 @@ hosts:
### Directory-Based Host Selection (`match_dirs`) ### Directory-Based Host Selection (`match_dirs`)
When you work with multiple Forgejo/Gitea instances, `fgj` can automatically select the right host based on your current working directory — no `--hostname` flag needed. When you work with multiple Forgejo/Gitea instances, `fj` can automatically select the right host based on your current working directory — no `--hostname` flag needed.
Each host entry supports a `match_dirs` list of directory paths. When `fgj` can't determine the host from a git remote, it finds the host whose `match_dirs` entry is the **longest prefix match** for your current directory. Each host entry supports a `match_dirs` list of directory paths. When `fj` can't determine the host from a git remote, it finds the host whose `match_dirs` entry is the **longest prefix match** for your current directory.
```yaml ```yaml
hosts: hosts:
@ -509,15 +509,15 @@ Hostname is resolved in this priority order:
- `--hostname`: Specify instance for a command (overrides auto-detection and environment variables) - `--hostname`: Specify instance for a command (overrides auto-detection and environment variables)
- `--config`: Use a custom config file - `--config`: Use a custom config file
When working in a git repository, `fgj` automatically detects the instance from your origin remote URL, so you typically don't need to specify `--hostname` unless working with multiple instances. When working in a git repository, `fj` automatically detects the instance from your origin remote URL, so you typically don't need to specify `--hostname` unless working with multiple instances.
## Use with AI Coding Agents ## Use with AI Coding Agents
`fgj` is designed to work seamlessly with AI coding agents like Claude Code. Use `--json` and `--json-errors` for fully machine-readable I/O: `fj` is designed to work seamlessly with AI coding agents like Claude Code. Use `--json` and `--json-errors` for fully machine-readable I/O:
```bash ```bash
# Create PR from agent's changes # Create PR from agent's changes
fgj pr create -R owner/repo -t "feat: add new feature" -b "$(cat <<EOF fj pr create -R owner/repo -t "feat: add new feature" -b "$(cat <<EOF
## Summary ## Summary
- Added new feature X - Added new feature X
- Fixed bug Y - Fixed bug Y
@ -527,29 +527,29 @@ EOF
)" --json )" --json
# Check PR status during development # Check PR status during development
fgj pr list -R owner/repo --state open --json fj pr list -R owner/repo --state open --json
# Review a PR diff, then approve # Review a PR diff, then approve
fgj pr diff 123 fj pr diff 123
fgj pr review 123 --approve -b "LGTM" --json fj pr review 123 --approve -b "LGTM" --json
# Post review feedback # Post review feedback
fgj pr comment 123 -b "Consider using a map here for O(1) lookup" --json fj pr comment 123 -b "Consider using a map here for O(1) lookup" --json
# Request changes with detailed feedback # Request changes with detailed feedback
fgj pr review 123 --request-changes --body-file feedback.md --json fj pr review 123 --request-changes --body-file feedback.md --json
# Use raw API for anything not covered by commands # Use raw API for anything not covered by commands
fgj api /repos/{owner}/{repo}/topics --json-errors fj api /repos/{owner}/{repo}/topics --json-errors
fgj api /repos/{owner}/{repo}/labels -X POST -f name=agent-reviewed -f color="#00ff00" fj api /repos/{owner}/{repo}/labels -X POST -f name=agent-reviewed -f color="#00ff00"
# Fully machine-readable error handling # Fully machine-readable error handling
fgj pr view 9999 --json --json-errors 2>errors.json fj pr view 9999 --json --json-errors 2>errors.json
``` ```
## Supported Instances ## Supported Instances
`fgj` works with any Forgejo or Gitea instance, including: `fj` works with any Forgejo or Gitea instance, including:
- Self-hosted Forgejo instances - Self-hosted Forgejo instances
- Self-hosted Gitea instances - Self-hosted Gitea instances
@ -557,11 +557,11 @@ fgj pr view 9999 --json --json-errors 2>errors.json
## Contributing ## Contributing
Contributions are welcome! Please feel free to submit a Pull Request at [forgejo.zerova.net/public/fgj-sid](https://forgejo.zerova.net/public/fgj-sid). Contributions are welcome! Please feel free to submit a Pull Request at [forgejo.zerova.net/public/fj](https://forgejo.zerova.net/public/fj).
## Missing Features / Roadmap ## Missing Features / Roadmap
`fgj` aims to be a drop-in replacement for `gh` when working with Forgejo and Gitea instances. While we've implemented the core features, some `gh` commands are not yet available: `fj` aims to be a drop-in replacement for `gh` when working with Forgejo and Gitea instances. While we've implemented the core features, some `gh` commands are not yet available:
**Not Yet Implemented:** **Not Yet Implemented:**
- `run delete` - Delete a workflow run - `run delete` - Delete a workflow run
@ -576,7 +576,7 @@ We welcome contributions to implement any of these features!
## Acknowledgments ## Acknowledgments
Based on [fgj by romaintb](https://codeberg.org/romaintb/fgj). Enhanced with agentic dev features for AI-assisted workflows. Based on [fj by romaintb](https://codeberg.org/romaintb/fj). Enhanced with agentic dev features for AI-assisted workflows.
## License ## License

BIN
bin/fj Executable file

Binary file not shown.

View file

@ -10,8 +10,8 @@ import (
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"forgejo.zerova.net/public/fgj-sid/internal/api" "forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config" "forgejo.zerova.net/public/fj/internal/config"
) )
// ActionRun represents a workflow run // ActionRun represents a workflow run
@ -99,13 +99,13 @@ var runListCmd = &cobra.Command{
Short: "List recent workflow runs", Short: "List recent workflow runs",
Long: "List recent workflow runs for a repository.", Long: "List recent workflow runs for a repository.",
Example: ` # List recent workflow runs Example: ` # List recent workflow runs
fgj actions run list fj actions run list
# List runs with a custom limit # List runs with a custom limit
fgj actions run list -L 50 fj actions run list -L 50
# Output as JSON # Output as JSON
fgj actions run list --json`, fj actions run list --json`,
RunE: runRunList, RunE: runRunList,
} }
@ -114,16 +114,16 @@ var runViewCmd = &cobra.Command{
Short: "View a workflow run", Short: "View a workflow run",
Long: "View details about a specific workflow run.", Long: "View details about a specific workflow run.",
Example: ` # View a workflow run Example: ` # View a workflow run
fgj actions run view 123 fj actions run view 123
# View with job details # View with job details
fgj actions run view 123 -v fj actions run view 123 -v
# View logs for a specific job # View logs for a specific job
fgj actions run view 123 --job 456 --log fj actions run view 123 --job 456 --log
# View only failed logs # View only failed logs
fgj actions run view 123 --log-failed`, fj actions run view 123 --log-failed`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runRunView, RunE: runRunView,
} }
@ -133,10 +133,10 @@ var runWatchCmd = &cobra.Command{
Short: "Watch a workflow run", Short: "Watch a workflow run",
Long: "Poll a workflow run until it completes.", Long: "Poll a workflow run until it completes.",
Example: ` # Watch a run until it completes Example: ` # Watch a run until it completes
fgj actions run watch 123 fj actions run watch 123
# Watch with a custom polling interval # Watch with a custom polling interval
fgj actions run watch 123 -i 10s`, fj actions run watch 123 -i 10s`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runRunWatch, RunE: runRunWatch,
} }
@ -146,7 +146,7 @@ var runRerunCmd = &cobra.Command{
Short: "Rerun a workflow run", Short: "Rerun a workflow run",
Long: "Trigger a rerun for a specific workflow run.", Long: "Trigger a rerun for a specific workflow run.",
Example: ` # Rerun a failed workflow run Example: ` # Rerun a failed workflow run
fgj actions run rerun 123`, fj actions run rerun 123`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runRunRerun, RunE: runRunRerun,
} }
@ -156,7 +156,7 @@ var runCancelCmd = &cobra.Command{
Short: "Cancel a workflow run", Short: "Cancel a workflow run",
Long: "Cancel a running workflow run.", Long: "Cancel a running workflow run.",
Example: ` # Cancel a running workflow Example: ` # Cancel a running workflow
fgj actions run cancel 123`, fj actions run cancel 123`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runRunCancel, RunE: runRunCancel,
} }
@ -173,13 +173,13 @@ var workflowListCmd = &cobra.Command{
Short: "List workflows", Short: "List workflows",
Long: "List all workflows in a repository.", Long: "List all workflows in a repository.",
Example: ` # List all workflows Example: ` # List all workflows
fgj actions workflow list fj actions workflow list
# List workflows as JSON # List workflows as JSON
fgj actions workflow list --json fj actions workflow list --json
# List workflows for a specific repo # List workflows for a specific repo
fgj actions workflow list -R owner/repo`, fj actions workflow list -R owner/repo`,
RunE: runWorkflowList, RunE: runWorkflowList,
} }
@ -188,10 +188,10 @@ var workflowViewCmd = &cobra.Command{
Short: "View a workflow", Short: "View a workflow",
Long: "View details about a specific workflow. You can specify the workflow by name or filename.", Long: "View details about a specific workflow. You can specify the workflow by name or filename.",
Example: ` # View a workflow by filename Example: ` # View a workflow by filename
fgj actions workflow view ci.yml fj actions workflow view ci.yml
# View as JSON # View as JSON
fgj actions workflow view ci.yml --json`, fj actions workflow view ci.yml --json`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runWorkflowView, RunE: runWorkflowView,
} }
@ -201,10 +201,10 @@ var workflowRunCmd = &cobra.Command{
Short: "Run a workflow", Short: "Run a workflow",
Long: "Trigger a workflow_dispatch event for a workflow. The workflow must support the workflow_dispatch trigger.", Long: "Trigger a workflow_dispatch event for a workflow. The workflow must support the workflow_dispatch trigger.",
Example: ` # Trigger a workflow on the default branch Example: ` # Trigger a workflow on the default branch
fgj actions workflow run deploy.yml fj actions workflow run deploy.yml
# Trigger on a specific branch with input parameters # Trigger on a specific branch with input parameters
fgj actions workflow run deploy.yml -r staging -f environment=staging -f version=1.2.3`, fj actions workflow run deploy.yml -r staging -f environment=staging -f version=1.2.3`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runWorkflowRun, RunE: runWorkflowRun,
} }
@ -214,7 +214,7 @@ var workflowEnableCmd = &cobra.Command{
Short: "Enable a workflow", Short: "Enable a workflow",
Long: "Enable a workflow so it can be triggered.\n\nNote: This feature requires Forgejo 15.0+ or Gitea 1.24+.\nFor older versions, use the web UI to enable workflows.", Long: "Enable a workflow so it can be triggered.\n\nNote: This feature requires Forgejo 15.0+ or Gitea 1.24+.\nFor older versions, use the web UI to enable workflows.",
Example: ` # Enable a workflow Example: ` # Enable a workflow
fgj actions workflow enable ci.yml`, fj actions workflow enable ci.yml`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runWorkflowEnable, RunE: runWorkflowEnable,
} }
@ -224,7 +224,7 @@ var workflowDisableCmd = &cobra.Command{
Short: "Disable a workflow", Short: "Disable a workflow",
Long: "Disable a workflow so it cannot be triggered.\n\nNote: This feature requires Forgejo 15.0+ or Gitea 1.24+.\nFor older versions, use the web UI to disable workflows.", Long: "Disable a workflow so it cannot be triggered.\n\nNote: This feature requires Forgejo 15.0+ or Gitea 1.24+.\nFor older versions, use the web UI to disable workflows.",
Example: ` # Disable a workflow Example: ` # Disable a workflow
fgj actions workflow disable ci.yml`, fj actions workflow disable ci.yml`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runWorkflowDisable, RunE: runWorkflowDisable,
} }
@ -241,10 +241,10 @@ var actionsSecretListCmd = &cobra.Command{
Short: "List repository secrets", Short: "List repository secrets",
Long: "List all secrets for a repository.", Long: "List all secrets for a repository.",
Example: ` # List all secrets Example: ` # List all secrets
fgj actions secret list fj actions secret list
# List secrets for a specific repo # List secrets for a specific repo
fgj actions secret list -R owner/repo`, fj actions secret list -R owner/repo`,
RunE: runActionsSecretList, RunE: runActionsSecretList,
} }
@ -253,10 +253,10 @@ var actionsSecretCreateCmd = &cobra.Command{
Short: "Create or update a repository secret", Short: "Create or update a repository secret",
Long: "Create or update a secret for Forgejo Actions. The secret value will be read from stdin.", Long: "Create or update a secret for Forgejo Actions. The secret value will be read from stdin.",
Example: ` # Create a secret (will prompt for value) Example: ` # Create a secret (will prompt for value)
fgj actions secret create DEPLOY_TOKEN fj actions secret create DEPLOY_TOKEN
# Create a secret for a specific repo # Create a secret for a specific repo
fgj actions secret create API_KEY -R owner/repo`, fj actions secret create API_KEY -R owner/repo`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runActionsSecretCreate, RunE: runActionsSecretCreate,
} }
@ -266,7 +266,7 @@ var actionsSecretDeleteCmd = &cobra.Command{
Short: "Delete a repository secret", Short: "Delete a repository secret",
Long: "Delete a secret from Forgejo Actions.", Long: "Delete a secret from Forgejo Actions.",
Example: ` # Delete a secret Example: ` # Delete a secret
fgj actions secret delete DEPLOY_TOKEN`, fj actions secret delete DEPLOY_TOKEN`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runActionsSecretDelete, RunE: runActionsSecretDelete,
} }
@ -283,10 +283,10 @@ var actionsVariableListCmd = &cobra.Command{
Short: "List repository variables", Short: "List repository variables",
Long: "List all variables for a repository.", Long: "List all variables for a repository.",
Example: ` # List all variables Example: ` # List all variables
fgj actions variable list fj actions variable list
# List variables for a specific repo # List variables for a specific repo
fgj actions variable list -R owner/repo`, fj actions variable list -R owner/repo`,
RunE: runActionsVariableList, RunE: runActionsVariableList,
} }
@ -295,7 +295,7 @@ var actionsVariableGetCmd = &cobra.Command{
Short: "Get a repository variable", Short: "Get a repository variable",
Long: "Get the value of a specific repository variable.", Long: "Get the value of a specific repository variable.",
Example: ` # Get a variable value Example: ` # Get a variable value
fgj actions variable get ENVIRONMENT`, fj actions variable get ENVIRONMENT`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runActionsVariableGet, RunE: runActionsVariableGet,
} }
@ -305,10 +305,10 @@ var actionsVariableCreateCmd = &cobra.Command{
Short: "Create a repository variable", Short: "Create a repository variable",
Long: "Create a new variable for Forgejo Actions.", Long: "Create a new variable for Forgejo Actions.",
Example: ` # Create a variable Example: ` # Create a variable
fgj actions variable create ENVIRONMENT production fj actions variable create ENVIRONMENT production
# Create a variable for a specific repo # Create a variable for a specific repo
fgj actions variable create NODE_VERSION 20 -R owner/repo`, fj actions variable create NODE_VERSION 20 -R owner/repo`,
Args: cobra.ExactArgs(2), Args: cobra.ExactArgs(2),
RunE: runActionsVariableCreate, RunE: runActionsVariableCreate,
} }
@ -318,7 +318,7 @@ var actionsVariableUpdateCmd = &cobra.Command{
Short: "Update a repository variable", Short: "Update a repository variable",
Long: "Update an existing variable for Forgejo Actions.", Long: "Update an existing variable for Forgejo Actions.",
Example: ` # Update a variable Example: ` # Update a variable
fgj actions variable update ENVIRONMENT staging`, fj actions variable update ENVIRONMENT staging`,
Args: cobra.ExactArgs(2), Args: cobra.ExactArgs(2),
RunE: runActionsVariableUpdate, RunE: runActionsVariableUpdate,
} }
@ -328,7 +328,7 @@ var actionsVariableDeleteCmd = &cobra.Command{
Short: "Delete a repository variable", Short: "Delete a repository variable",
Long: "Delete a variable from Forgejo Actions.", Long: "Delete a variable from Forgejo Actions.",
Example: ` # Delete a variable Example: ` # Delete a variable
fgj actions variable delete ENVIRONMENT`, fj actions variable delete ENVIRONMENT`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runActionsVariableDelete, RunE: runActionsVariableDelete,
} }

View file

@ -7,7 +7,7 @@ import (
) )
// Top-level aliases for "actions run" and "actions workflow" commands, // Top-level aliases for "actions run" and "actions workflow" commands,
// matching gh CLI's command structure (e.g., "fgj run list" instead of "fgj actions run list"). // matching gh CLI's command structure (e.g., "fj run list" instead of "fj actions run list").
func init() { func init() {
// --- run alias --- // --- run alias ---

View file

@ -10,8 +10,8 @@ import (
"strconv" "strconv"
"strings" "strings"
"forgejo.zerova.net/public/fgj-sid/internal/config" "forgejo.zerova.net/public/fj/internal/config"
"forgejo.zerova.net/public/fgj-sid/internal/git" "forgejo.zerova.net/public/fj/internal/git"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -26,16 +26,16 @@ detected from the current git repository.
If --field is used and no --method is specified, the method defaults to POST.`, If --field is used and no --method is specified, the method defaults to POST.`,
Example: ` # List pull requests for the current repository Example: ` # List pull requests for the current repository
fgj api /repos/{owner}/{repo}/pulls fj api /repos/{owner}/{repo}/pulls
# Create an issue # Create an issue
fgj api /repos/{owner}/{repo}/issues --method POST --field title=Bug --field body="It broke" fj api /repos/{owner}/{repo}/issues --method POST --field title=Bug --field body="It broke"
# Get a specific user # Get a specific user
fgj api /users/johndoe fj api /users/johndoe
# Use raw body from stdin # Use raw body from stdin
echo '{"title":"test"}' | fgj api /repos/{owner}/{repo}/issues --input -`, echo '{"title":"test"}' | fj api /repos/{owner}/{repo}/issues --input -`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runAPI, RunE: runAPI,
} }

View file

@ -7,8 +7,8 @@ import (
"strings" "strings"
"syscall" "syscall"
"forgejo.zerova.net/public/fgj-sid/internal/api" "forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config" "forgejo.zerova.net/public/fj/internal/config"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
"golang.org/x/term" "golang.org/x/term"
@ -16,7 +16,7 @@ import (
var authCmd = &cobra.Command{ var authCmd = &cobra.Command{
Use: "auth", Use: "auth",
Short: "Authenticate fgj with a Forgejo instance", Short: "Authenticate fj with a Forgejo instance",
Long: "Manage authentication state for Forgejo instances.", Long: "Manage authentication state for Forgejo instances.",
} }
@ -132,7 +132,7 @@ func runAuthStatus(cmd *cobra.Command, args []string) error {
if len(cfg.Hosts) == 0 { if len(cfg.Hosts) == 0 {
fmt.Fprintln(ios.Out, "Not authenticated with any Forgejo instances") fmt.Fprintln(ios.Out, "Not authenticated with any Forgejo instances")
fmt.Fprintln(ios.Out, "Run 'fgj auth login' to authenticate") fmt.Fprintln(ios.Out, "Run 'fj auth login' to authenticate")
return nil return nil
} }

View file

@ -11,7 +11,7 @@ import (
var completionCmd = &cobra.Command{ var completionCmd = &cobra.Command{
Use: "completion [bash|zsh|fish|powershell]", Use: "completion [bash|zsh|fish|powershell]",
Short: "Generate shell completion scripts", Short: "Generate shell completion scripts",
Long: "Generate shell completion scripts for fgj.", Long: "Generate shell completion scripts for fj.",
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,

View file

@ -6,7 +6,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"forgejo.zerova.net/public/fgj-sid/internal/api" "forgejo.zerova.net/public/fj/internal/api"
) )
// Error codes for structured error output. // Error codes for structured error output.
@ -54,7 +54,7 @@ func ContextualError(err error) error {
if errors.As(err, &apiErr) { if errors.As(err, &apiErr) {
switch { switch {
case apiErr.StatusCode == 401 || apiErr.StatusCode == 403: case apiErr.StatusCode == 401 || apiErr.StatusCode == 403:
return fmt.Errorf("%w\nHint: Try authenticating with: fgj auth login", err) return fmt.Errorf("%w\nHint: Try authenticating with: fj auth login", err)
case apiErr.StatusCode == 404: case apiErr.StatusCode == 404:
return fmt.Errorf("%w\nHint: Resource not found. Check the repository and number are correct.", err) return fmt.Errorf("%w\nHint: Resource not found. Check the repository and number are correct.", err)
} }
@ -73,7 +73,7 @@ func ContextualError(err error) error {
switch { switch {
case strings.Contains(msg, "401") || strings.Contains(msg, "403"): case strings.Contains(msg, "401") || strings.Contains(msg, "403"):
if strings.Contains(msg, "authentication") || strings.Contains(msg, "unauthorized") || strings.Contains(msg, "forbidden") { if strings.Contains(msg, "authentication") || strings.Contains(msg, "unauthorized") || strings.Contains(msg, "forbidden") {
return fmt.Errorf("%w\nHint: Try authenticating with: fgj auth login", err) return fmt.Errorf("%w\nHint: Try authenticating with: fj auth login", err)
} }
} }

View file

@ -1,5 +1,5 @@
package cmd package cmd
import "forgejo.zerova.net/public/fgj-sid/internal/iostreams" import "forgejo.zerova.net/public/fj/internal/iostreams"
var ios = iostreams.New() var ios = iostreams.New()

View file

@ -6,9 +6,9 @@ import (
"strings" "strings"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"forgejo.zerova.net/public/fgj-sid/internal/api" "forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config" "forgejo.zerova.net/public/fj/internal/config"
"forgejo.zerova.net/public/fgj-sid/internal/text" "forgejo.zerova.net/public/fj/internal/text"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -23,13 +23,13 @@ var issueListCmd = &cobra.Command{
Short: "List issues", Short: "List issues",
Long: "List issues in a repository.", Long: "List issues in a repository.",
Example: ` # List open issues Example: ` # List open issues
fgj issue list fj issue list
# List closed issues for a specific repo # List closed issues for a specific repo
fgj issue list -s closed -R owner/repo fj issue list -s closed -R owner/repo
# Output as JSON # Output as JSON
fgj issue list --json`, fj issue list --json`,
RunE: runIssueList, RunE: runIssueList,
} }
@ -38,16 +38,16 @@ var issueViewCmd = &cobra.Command{
Short: "View an issue", Short: "View an issue",
Long: "Display detailed information about an issue.", Long: "Display detailed information about an issue.",
Example: ` # View issue #42 Example: ` # View issue #42
fgj issue view 42 fj issue view 42
# View using URL # View using URL
fgj issue view https://codeberg.org/owner/repo/issues/42 fj issue view https://codeberg.org/owner/repo/issues/42
# Open in browser # Open in browser
fgj issue view 42 --web fj issue view 42 --web
# View an issue from a specific repo as JSON # View an issue from a specific repo as JSON
fgj issue view 42 -R owner/repo --json`, fj issue view 42 -R owner/repo --json`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runIssueView, RunE: runIssueView,
} }
@ -57,10 +57,10 @@ var issueCreateCmd = &cobra.Command{
Short: "Create an issue", Short: "Create an issue",
Long: "Create a new issue.", Long: "Create a new issue.",
Example: ` # Create an issue with a title Example: ` # Create an issue with a title
fgj issue create -t "Fix login bug" fj issue create -t "Fix login bug"
# Create an issue with title, body, and labels # Create an issue with title, body, and labels
fgj issue create -t "Add dark mode" -b "We need a dark theme" -l feature -l ui`, fj issue create -t "Add dark mode" -b "We need a dark theme" -l feature -l ui`,
RunE: runIssueCreate, RunE: runIssueCreate,
} }
@ -69,10 +69,10 @@ var issueCommentCmd = &cobra.Command{
Short: "Add a comment to an issue", Short: "Add a comment to an issue",
Long: "Add a comment to an existing issue.", Long: "Add a comment to an existing issue.",
Example: ` # Add a comment to issue #42 Example: ` # Add a comment to issue #42
fgj issue comment 42 -b "This is fixed in the latest release" fj issue comment 42 -b "This is fixed in the latest release"
# Comment on an issue in a specific repo # Comment on an issue in a specific repo
fgj issue comment 10 -b "Confirmed on my end" -R owner/repo`, fj issue comment 10 -b "Confirmed on my end" -R owner/repo`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runIssueComment, RunE: runIssueComment,
} }
@ -82,10 +82,10 @@ var issueCloseCmd = &cobra.Command{
Short: "Close an issue", Short: "Close an issue",
Long: "Close an existing issue.", Long: "Close an existing issue.",
Example: ` # Close issue #42 Example: ` # Close issue #42
fgj issue close 42 fj issue close 42
# Close with a comment # Close with a comment
fgj issue close 42 -c "Fixed in commit abc1234"`, fj issue close 42 -c "Fixed in commit abc1234"`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runIssueClose, RunE: runIssueClose,
} }
@ -95,7 +95,7 @@ var issueReopenCmd = &cobra.Command{
Short: "Reopen an issue", Short: "Reopen an issue",
Long: "Reopen a closed issue.", Long: "Reopen a closed issue.",
Example: ` # Reopen issue #42 Example: ` # Reopen issue #42
fgj issue reopen 42`, fj issue reopen 42`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runIssueReopen, RunE: runIssueReopen,
} }
@ -105,10 +105,10 @@ var issueDeleteCmd = &cobra.Command{
Short: "Delete an issue", Short: "Delete an issue",
Long: "Delete an issue permanently.", Long: "Delete an issue permanently.",
Example: ` # Delete issue #42 Example: ` # Delete issue #42
fgj issue delete 42 fj issue delete 42
# Delete without confirmation # Delete without confirmation
fgj issue delete 42 -y`, fj issue delete 42 -y`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runIssueDelete, RunE: runIssueDelete,
} }
@ -118,16 +118,16 @@ var issueEditCmd = &cobra.Command{
Short: "Edit an issue", Short: "Edit an issue",
Long: "Edit an existing issue's title, body, or state.", Long: "Edit an existing issue's title, body, or state.",
Example: ` # Update the title of issue #42 Example: ` # Update the title of issue #42
fgj issue edit 42 -t "Updated title" fj issue edit 42 -t "Updated title"
# Reopen a closed issue # Reopen a closed issue
fgj issue edit 42 -s open fj issue edit 42 -s open
# Add and remove labels # Add and remove labels
fgj issue edit 42 --add-label bug --remove-label wontfix fj issue edit 42 --add-label bug --remove-label wontfix
# Add a dependency # Add a dependency
fgj issue edit 42 --add-dependency 10`, fj issue edit 42 --add-dependency 10`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runIssueEdit, RunE: runIssueEdit,
} }

View file

@ -5,8 +5,8 @@ import (
"strings" "strings"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"forgejo.zerova.net/public/fgj-sid/internal/api" "forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config" "forgejo.zerova.net/public/fj/internal/config"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -21,13 +21,13 @@ var labelListCmd = &cobra.Command{
Short: "List labels for a repository", Short: "List labels for a repository",
Long: "List all labels defined in a repository.", Long: "List all labels defined in a repository.",
Example: ` # List labels for the current repository Example: ` # List labels for the current repository
fgj label list fj label list
# List labels for a specific repository # List labels for a specific repository
fgj label list -R owner/repo fj label list -R owner/repo
# Output as JSON # Output as JSON
fgj label list --json`, fj label list --json`,
RunE: runLabelList, RunE: runLabelList,
} }
@ -36,13 +36,13 @@ var labelCreateCmd = &cobra.Command{
Short: "Create a label", Short: "Create a label",
Long: "Create a new label in a repository.", Long: "Create a new label in a repository.",
Example: ` # Create a label with a color Example: ` # Create a label with a color
fgj label create bug -c ff0000 fj label create bug -c ff0000
# Create a label with color and description # Create a label with color and description
fgj label create feature -c 00ff00 -d "New feature request" fj label create feature -c 00ff00 -d "New feature request"
# Create a label in a specific repository # Create a label in a specific repository
fgj label create urgent -c ff0000 -R owner/repo`, fj label create urgent -c ff0000 -R owner/repo`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runLabelCreate, RunE: runLabelCreate,
} }
@ -52,13 +52,13 @@ var labelEditCmd = &cobra.Command{
Short: "Edit a label", Short: "Edit a label",
Long: "Edit an existing label in a repository.", Long: "Edit an existing label in a repository.",
Example: ` # Rename a label Example: ` # Rename a label
fgj label edit bug --name bugfix fj label edit bug --name bugfix
# Change the color of a label # Change the color of a label
fgj label edit bug -c 00ff00 fj label edit bug -c 00ff00
# Update description # Update description
fgj label edit bug -d "Something is broken"`, fj label edit bug -d "Something is broken"`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runLabelEdit, RunE: runLabelEdit,
} }
@ -68,13 +68,13 @@ var labelDeleteCmd = &cobra.Command{
Short: "Delete a label", Short: "Delete a label",
Long: "Delete a label from a repository.", Long: "Delete a label from a repository.",
Example: ` # Delete a label Example: ` # Delete a label
fgj label delete bug fj label delete bug
# Delete without confirmation # Delete without confirmation
fgj label delete bug -y fj label delete bug -y
# Delete a label from a specific repository # Delete a label from a specific repository
fgj label delete bug -R owner/repo`, fj label delete bug -R owner/repo`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runLabelDelete, RunE: runLabelDelete,
} }

View file

@ -12,7 +12,7 @@ import (
var manpagesCmd = &cobra.Command{ var manpagesCmd = &cobra.Command{
Use: "manpages", Use: "manpages",
Short: "Generate manpages", Short: "Generate manpages",
Long: "Generate manpages for fgj commands.", Long: "Generate manpages for fj commands.",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
dir, _ := cmd.Flags().GetString("dir") dir, _ := cmd.Flags().GetString("dir")
if dir == "" { if dir == "" {

View file

@ -7,9 +7,9 @@ import (
"time" "time"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"forgejo.zerova.net/public/fgj-sid/internal/api" "forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config" "forgejo.zerova.net/public/fj/internal/config"
"forgejo.zerova.net/public/fgj-sid/internal/text" "forgejo.zerova.net/public/fj/internal/text"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -24,13 +24,13 @@ var milestoneListCmd = &cobra.Command{
Short: "List milestones", Short: "List milestones",
Long: "List milestones in a repository.", Long: "List milestones in a repository.",
Example: ` # List open milestones Example: ` # List open milestones
fgj milestone list fj milestone list
# List all milestones for a specific repo # List all milestones for a specific repo
fgj milestone list -R owner/repo --state all fj milestone list -R owner/repo --state all
# Output as JSON # Output as JSON
fgj milestone list --json`, fj milestone list --json`,
RunE: runMilestoneList, RunE: runMilestoneList,
} }
@ -39,16 +39,16 @@ var milestoneViewCmd = &cobra.Command{
Short: "View a milestone", Short: "View a milestone",
Long: "Display detailed information about a milestone.", Long: "Display detailed information about a milestone.",
Example: ` # View by ID Example: ` # View by ID
fgj milestone view 1 fj milestone view 1
# View by title # View by title
fgj milestone view "v1.0" fj milestone view "v1.0"
# Open in browser # Open in browser
fgj milestone view "v1.0" --web fj milestone view "v1.0" --web
# Output as JSON # Output as JSON
fgj milestone view "v1.0" --json`, fj milestone view "v1.0" --json`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runMilestoneView, RunE: runMilestoneView,
} }
@ -58,13 +58,13 @@ var milestoneCreateCmd = &cobra.Command{
Short: "Create a milestone", Short: "Create a milestone",
Long: "Create a new milestone.", Long: "Create a new milestone.",
Example: ` # Create a simple milestone Example: ` # Create a simple milestone
fgj milestone create "v1.0" fj milestone create "v1.0"
# Create with description and due date # Create with description and due date
fgj milestone create "v2.0" -d "Second release" --due 2026-06-01 fj milestone create "v2.0" -d "Second release" --due 2026-06-01
# Output as JSON # Output as JSON
fgj milestone create "v1.0" --json`, fj milestone create "v1.0" --json`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runMilestoneCreate, RunE: runMilestoneCreate,
} }
@ -74,13 +74,13 @@ var milestoneEditCmd = &cobra.Command{
Short: "Edit a milestone", Short: "Edit a milestone",
Long: "Edit an existing milestone's title, description, due date, or state.", Long: "Edit an existing milestone's title, description, due date, or state.",
Example: ` # Rename a milestone Example: ` # Rename a milestone
fgj milestone edit "v1.0" --title "v1.1" fj milestone edit "v1.0" --title "v1.1"
# Close a milestone # Close a milestone
fgj milestone edit "v1.0" --state closed fj milestone edit "v1.0" --state closed
# Update due date # Update due date
fgj milestone edit 1 --due 2026-12-31`, fj milestone edit 1 --due 2026-12-31`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runMilestoneEdit, RunE: runMilestoneEdit,
} }
@ -90,13 +90,13 @@ var milestoneDeleteCmd = &cobra.Command{
Short: "Delete a milestone", Short: "Delete a milestone",
Long: "Delete an existing milestone.", Long: "Delete an existing milestone.",
Example: ` # Delete by title Example: ` # Delete by title
fgj milestone delete "v1.0" fj milestone delete "v1.0"
# Delete by ID # Delete by ID
fgj milestone delete 1 fj milestone delete 1
# Delete without confirmation # Delete without confirmation
fgj milestone delete "v1.0" -y`, fj milestone delete "v1.0" -y`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runMilestoneDelete, RunE: runMilestoneDelete,
} }

View file

@ -7,10 +7,10 @@ import (
"strings" "strings"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"forgejo.zerova.net/public/fgj-sid/internal/api" "forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config" "forgejo.zerova.net/public/fj/internal/config"
gitpkg "forgejo.zerova.net/public/fgj-sid/internal/git" gitpkg "forgejo.zerova.net/public/fj/internal/git"
"forgejo.zerova.net/public/fgj-sid/internal/text" "forgejo.zerova.net/public/fj/internal/text"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -26,13 +26,13 @@ var prListCmd = &cobra.Command{
Short: "List pull requests", Short: "List pull requests",
Long: "List pull requests in a repository.", Long: "List pull requests in a repository.",
Example: ` # List open pull requests Example: ` # List open pull requests
fgj pr list fj pr list
# List all pull requests for a specific repo # List all pull requests for a specific repo
fgj pr list -s all -R owner/repo fj pr list -s all -R owner/repo
# Output as JSON # Output as JSON
fgj pr list --json`, fj pr list --json`,
RunE: runPRList, RunE: runPRList,
} }
@ -41,19 +41,19 @@ var prViewCmd = &cobra.Command{
Short: "View a pull request", Short: "View a pull request",
Long: "Display detailed information about a pull request.", Long: "Display detailed information about a pull request.",
Example: ` # View pull request #5 Example: ` # View pull request #5
fgj pr view 5 fj pr view 5
# View using URL # View using URL
fgj pr view https://codeberg.org/owner/repo/pulls/5 fj pr view https://codeberg.org/owner/repo/pulls/5
# View PR for current branch # View PR for current branch
fgj pr view fj pr view
# Open in browser # Open in browser
fgj pr view 5 --web fj pr view 5 --web
# View as JSON # View as JSON
fgj pr view 5 --json`, fj pr view 5 --json`,
Args: cobra.MaximumNArgs(1), Args: cobra.MaximumNArgs(1),
RunE: runPRView, RunE: runPRView,
} }
@ -63,13 +63,13 @@ var prCreateCmd = &cobra.Command{
Short: "Create a pull request", Short: "Create a pull request",
Long: "Create a new pull request.", Long: "Create a new pull request.",
Example: ` # Create a pull request from feature branch to main Example: ` # Create a pull request from feature branch to main
fgj pr create -t "Add login page" -H feature/login fj pr create -t "Add login page" -H feature/login
# Create with body and custom base branch # Create with body and custom base branch
fgj pr create -t "Fix bug" -b "Closes #42" -H fix/bug -B develop fj pr create -t "Fix bug" -b "Closes #42" -H fix/bug -B develop
# Create and self-assign # Create and self-assign
fgj pr create -t "Update docs" -H docs/update -a @me`, fj pr create -t "Update docs" -H docs/update -a @me`,
RunE: runPRCreate, RunE: runPRCreate,
} }
@ -78,16 +78,16 @@ var prMergeCmd = &cobra.Command{
Short: "Merge a pull request", Short: "Merge a pull request",
Long: "Merge a pull request.", Long: "Merge a pull request.",
Example: ` # Merge pull request #5 Example: ` # Merge pull request #5
fgj pr merge 5 fj pr merge 5
# Squash merge # Squash merge
fgj pr merge 5 --merge-method squash fj pr merge 5 --merge-method squash
# Rebase merge # Rebase merge
fgj pr merge 5 --merge-method rebase fj pr merge 5 --merge-method rebase
# Merge without confirmation # Merge without confirmation
fgj pr merge 5 -y`, fj pr merge 5 -y`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runPRMerge, RunE: runPRMerge,
} }
@ -97,10 +97,10 @@ var prCloseCmd = &cobra.Command{
Short: "Close a pull request", Short: "Close a pull request",
Long: "Close a pull request without merging.", Long: "Close a pull request without merging.",
Example: ` # Close PR #5 Example: ` # Close PR #5
fgj pr close 5 fj pr close 5
# Close with a comment # Close with a comment
fgj pr close 5 -c "Won't merge, superseded by #10"`, fj pr close 5 -c "Won't merge, superseded by #10"`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runPRClose, RunE: runPRClose,
} }
@ -110,7 +110,7 @@ var prReopenCmd = &cobra.Command{
Short: "Reopen a pull request", Short: "Reopen a pull request",
Long: "Reopen a closed pull request.", Long: "Reopen a closed pull request.",
Example: ` # Reopen PR #5 Example: ` # Reopen PR #5
fgj pr reopen 5`, fj pr reopen 5`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runPRReopen, RunE: runPRReopen,
} }
@ -120,13 +120,13 @@ var prEditCmd = &cobra.Command{
Short: "Edit a pull request", Short: "Edit a pull request",
Long: "Edit a pull request's title, body, or metadata.", Long: "Edit a pull request's title, body, or metadata.",
Example: ` # Update the title of PR #5 Example: ` # Update the title of PR #5
fgj pr edit 5 -t "Updated title" fj pr edit 5 -t "Updated title"
# Add assignees and labels # Add assignees and labels
fgj pr edit 5 --add-assignee user1 --add-label bug fj pr edit 5 --add-assignee user1 --add-label bug
# Remove a reviewer and set milestone # Remove a reviewer and set milestone
fgj pr edit 5 --remove-reviewer user2 --milestone "v1.0"`, fj pr edit 5 --remove-reviewer user2 --milestone "v1.0"`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runPREdit, RunE: runPREdit,
} }
@ -136,7 +136,7 @@ var prCheckoutCmd = &cobra.Command{
Short: "Check out a pull request locally", Short: "Check out a pull request locally",
Long: "Check out the head branch of a pull request.", Long: "Check out the head branch of a pull request.",
Example: ` # Check out PR #5 Example: ` # Check out PR #5
fgj pr checkout 5`, fj pr checkout 5`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runPRCheckout, RunE: runPRCheckout,
} }

View file

@ -4,9 +4,9 @@ import (
"fmt" "fmt"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"forgejo.zerova.net/public/fgj-sid/internal/api" "forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config" "forgejo.zerova.net/public/fj/internal/config"
"forgejo.zerova.net/public/fgj-sid/internal/iostreams" "forgejo.zerova.net/public/fj/internal/iostreams"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -15,10 +15,10 @@ var prChecksCmd = &cobra.Command{
Short: "Show CI status checks for a pull request", Short: "Show CI status checks for a pull request",
Long: "Show the status of CI checks for a pull request.", Long: "Show the status of CI checks for a pull request.",
Example: ` # Show checks for PR #5 Example: ` # Show checks for PR #5
fgj pr checks 5 fj pr checks 5
# Output as JSON # Output as JSON
fgj pr checks 5 --json`, fj pr checks 5 --json`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runPRChecks, RunE: runPRChecks,
} }

View file

@ -4,8 +4,8 @@ import (
"fmt" "fmt"
"strings" "strings"
"forgejo.zerova.net/public/fgj-sid/internal/api" "forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config" "forgejo.zerova.net/public/fj/internal/config"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -14,16 +14,16 @@ var prDiffCmd = &cobra.Command{
Short: "Show the diff for a pull request", Short: "Show the diff for a pull request",
Long: "Fetch and display the diff for a pull request.", Long: "Fetch and display the diff for a pull request.",
Example: ` # View the diff for PR #123 Example: ` # View the diff for PR #123
fgj pr diff 123 fj pr diff 123
# Colorized diff output # Colorized diff output
fgj pr diff 123 --color always fj pr diff 123 --color always
# Show only changed file names # Show only changed file names
fgj pr diff 123 --name-only fj pr diff 123 --name-only
# Show diffstat summary # Show diffstat summary
fgj pr diff 123 --stat`, fj pr diff 123 --stat`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runPRDiff, RunE: runPRDiff,
} }

View file

@ -6,8 +6,8 @@ import (
"os" "os"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"forgejo.zerova.net/public/fgj-sid/internal/api" "forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config" "forgejo.zerova.net/public/fj/internal/config"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -16,16 +16,16 @@ var prCommentCmd = &cobra.Command{
Short: "Add a comment to a pull request", Short: "Add a comment to a pull request",
Long: "Add a comment to an existing pull request.", Long: "Add a comment to an existing pull request.",
Example: ` # Add a comment Example: ` # Add a comment
fgj pr comment 123 -b "Looks good!" fj pr comment 123 -b "Looks good!"
# Comment from a file # Comment from a file
fgj pr comment 123 --body-file review-notes.md fj pr comment 123 --body-file review-notes.md
# Comment from stdin # Comment from stdin
echo "LGTM" | fgj pr comment 123 --body-file - echo "LGTM" | fj pr comment 123 --body-file -
# Output as JSON # Output as JSON
fgj pr comment 123 -b "Nice work" --json`, fj pr comment 123 -b "Nice work" --json`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runPRComment, RunE: runPRComment,
} }
@ -35,16 +35,16 @@ var prReviewCmd = &cobra.Command{
Short: "Submit a review on a pull request", Short: "Submit a review on a pull request",
Long: "Submit a review on a pull request. Exactly one of --approve, --request-changes, or --comment must be specified.", Long: "Submit a review on a pull request. Exactly one of --approve, --request-changes, or --comment must be specified.",
Example: ` # Approve a PR Example: ` # Approve a PR
fgj pr review 123 --approve -b "LGTM" fj pr review 123 --approve -b "LGTM"
# Request changes # Request changes
fgj pr review 123 --request-changes -b "Please fix the error handling" fj pr review 123 --request-changes -b "Please fix the error handling"
# Submit a review comment # Submit a review comment
fgj pr review 123 --comment -b "Some observations" fj pr review 123 --comment -b "Some observations"
# Request changes with body from file # Request changes with body from file
fgj pr review 123 --request-changes --body-file feedback.md`, fj pr review 123 --request-changes --body-file feedback.md`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runPRReview, RunE: runPRReview,
} }

View file

@ -9,9 +9,9 @@ import (
"time" "time"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"forgejo.zerova.net/public/fgj-sid/internal/api" "forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config" "forgejo.zerova.net/public/fj/internal/config"
"forgejo.zerova.net/public/fgj-sid/internal/text" "forgejo.zerova.net/public/fj/internal/text"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -27,13 +27,13 @@ var releaseListCmd = &cobra.Command{
Short: "List releases", Short: "List releases",
Long: "List releases in a repository.", Long: "List releases in a repository.",
Example: ` # List releases Example: ` # List releases
fgj release list fj release list
# List only draft releases # List only draft releases
fgj release list --draft fj release list --draft
# Output as JSON with a custom limit # Output as JSON with a custom limit
fgj release list --json --limit 10`, fj release list --json --limit 10`,
RunE: runReleaseList, RunE: runReleaseList,
} }
@ -42,16 +42,16 @@ var releaseViewCmd = &cobra.Command{
Short: "View a release", Short: "View a release",
Long: "Display detailed information about a release.", Long: "Display detailed information about a release.",
Example: ` # View a release by tag Example: ` # View a release by tag
fgj release view v1.0.0 fj release view v1.0.0
# View the latest release # View the latest release
fgj release view latest fj release view latest
# Open in browser # Open in browser
fgj release view v1.0.0 --web fj release view v1.0.0 --web
# Output as JSON # Output as JSON
fgj release view v1.0.0 --json`, fj release view v1.0.0 --json`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runReleaseView, RunE: runReleaseView,
} }
@ -61,16 +61,16 @@ var releaseCreateCmd = &cobra.Command{
Short: "Create a release", Short: "Create a release",
Long: "Create a new release and optionally upload assets.", Long: "Create a new release and optionally upload assets.",
Example: ` # Create a release Example: ` # Create a release
fgj release create v1.0.0 fj release create v1.0.0
# Create with title and notes # Create with title and notes
fgj release create v1.0.0 -t "First stable release" -n "Bug fixes and improvements" fj release create v1.0.0 -t "First stable release" -n "Bug fixes and improvements"
# Create a draft prerelease with assets # Create a draft prerelease with assets
fgj release create v2.0.0-rc1 --draft --prerelease dist/*.tar.gz fj release create v2.0.0-rc1 --draft --prerelease dist/*.tar.gz
# Create from release notes file # Create from release notes file
fgj release create v1.0.0 -F CHANGELOG.md`, fj release create v1.0.0 -F CHANGELOG.md`,
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
RunE: runReleaseCreate, RunE: runReleaseCreate,
} }
@ -80,10 +80,10 @@ var releaseUploadCmd = &cobra.Command{
Short: "Upload release assets", Short: "Upload release assets",
Long: "Upload assets to an existing release.", Long: "Upload assets to an existing release.",
Example: ` # Upload assets to a release Example: ` # Upload assets to a release
fgj release upload v1.0.0 dist/app-linux-amd64 dist/app-darwin-arm64 fj release upload v1.0.0 dist/app-linux-amd64 dist/app-darwin-arm64
# Upload to the latest release, overwriting existing assets # Upload to the latest release, overwriting existing assets
fgj release upload latest build/output.zip --clobber`, fj release upload latest build/output.zip --clobber`,
Args: cobra.MinimumNArgs(2), Args: cobra.MinimumNArgs(2),
RunE: runReleaseUpload, RunE: runReleaseUpload,
} }
@ -93,13 +93,13 @@ var releaseDownloadCmd = &cobra.Command{
Short: "Download release assets", Short: "Download release assets",
Long: "Download assets from a release.", Long: "Download assets from a release.",
Example: ` # Download all assets from a release Example: ` # Download all assets from a release
fgj release download v1.0.0 fj release download v1.0.0
# Download to a specific directory # Download to a specific directory
fgj release download v1.0.0 -D ./downloads fj release download v1.0.0 -D ./downloads
# Download a specific asset by name pattern # Download a specific asset by name pattern
fgj release download v1.0.0 -p "*.tar.gz"`, fj release download v1.0.0 -p "*.tar.gz"`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runReleaseDownload, RunE: runReleaseDownload,
} }
@ -109,13 +109,13 @@ var releaseDeleteCmd = &cobra.Command{
Short: "Delete a release", Short: "Delete a release",
Long: "Delete a release by tag, keeping its Git tag intact.", Long: "Delete a release by tag, keeping its Git tag intact.",
Example: ` # Delete a release by tag Example: ` # Delete a release by tag
fgj release delete v1.0.0 fj release delete v1.0.0
# Delete the latest release # Delete the latest release
fgj release delete latest fj release delete latest
# Delete without confirmation # Delete without confirmation
fgj release delete v1.0.0 -y`, fj release delete v1.0.0 -y`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runReleaseDelete, RunE: runReleaseDelete,
} }

View file

@ -8,9 +8,9 @@ import (
"strings" "strings"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"forgejo.zerova.net/public/fgj-sid/internal/api" "forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config" "forgejo.zerova.net/public/fj/internal/config"
"forgejo.zerova.net/public/fgj-sid/internal/text" "forgejo.zerova.net/public/fj/internal/text"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -67,22 +67,22 @@ var repoEditCmd = &cobra.Command{
Short: "Edit repository settings", Short: "Edit repository settings",
Long: "Edit settings of an existing repository such as visibility, description, homepage, and default branch.", Long: "Edit settings of an existing repository such as visibility, description, homepage, and default branch.",
Example: ` # Make a repository private Example: ` # Make a repository private
fgj repo edit owner/repo --private fj repo edit owner/repo --private
# Make a repository public # Make a repository public
fgj repo edit owner/repo --public fj repo edit owner/repo --public
# Update description and homepage # Update description and homepage
fgj repo edit owner/repo -d "New description" --homepage https://example.com fj repo edit owner/repo -d "New description" --homepage https://example.com
# Change default branch # Change default branch
fgj repo edit --default-branch develop fj repo edit --default-branch develop
# Rename a repository # Rename a repository
fgj repo edit owner/repo --name new-name fj repo edit owner/repo --name new-name
# Edit current repo (auto-detected from git context) # Edit current repo (auto-detected from git context)
fgj repo edit --public`, fj repo edit --public`,
Args: cobra.MaximumNArgs(1), Args: cobra.MaximumNArgs(1),
RunE: runRepoEdit, RunE: runRepoEdit,
} }
@ -90,12 +90,12 @@ var repoEditCmd = &cobra.Command{
var repoRenameCmd = &cobra.Command{ var repoRenameCmd = &cobra.Command{
Use: "rename <new-name>", Use: "rename <new-name>",
Short: "Rename a repository", Short: "Rename a repository",
Long: "Rename an existing repository. This is a shorthand for `fgj repo edit --name <new-name>`.", Long: "Rename an existing repository. This is a shorthand for `fj repo edit --name <new-name>`.",
Example: ` # Rename current repo Example: ` # Rename current repo
fgj repo rename new-name fj repo rename new-name
# Rename a specific repo # Rename a specific repo
fgj repo rename new-name -R owner/old-name`, fj repo rename new-name -R owner/old-name`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runRepoRename, RunE: runRepoRename,
} }

View file

@ -6,7 +6,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"forgejo.zerova.net/public/fgj-sid/internal/git" "forgejo.zerova.net/public/fj/internal/git"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
@ -15,9 +15,9 @@ var cfgFile string
var jsonErrors bool var jsonErrors bool
var rootCmd = &cobra.Command{ var rootCmd = &cobra.Command{
Use: "fgj", Use: "fj",
Short: "Forgejo CLI tool - work seamlessly with Forgejo from the command line", Short: "Forgejo CLI tool - work seamlessly with Forgejo from the command line",
Long: `fgj is a command line tool for Forgejo instances (including Codeberg). Long: `fj is a command line tool for Forgejo instances (including Codeberg).
It brings pull requests, issues, and other Forgejo concepts to the terminal.`, It brings pull requests, issues, and other Forgejo concepts to the terminal.`,
Version: "0.3.1", Version: "0.3.1",
SilenceErrors: true, SilenceErrors: true,
@ -35,7 +35,7 @@ func Execute() error {
func init() { func init() {
cobra.OnInitialize(initConfig) cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.config/fgj/config.yaml)") rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.config/fj/config.yaml)")
rootCmd.PersistentFlags().BoolVar(&jsonErrors, "json-errors", false, "output errors as structured JSON to stderr") rootCmd.PersistentFlags().BoolVar(&jsonErrors, "json-errors", false, "output errors as structured JSON to stderr")
rootCmd.PersistentFlags().String("hostname", "", "Forgejo instance hostname") rootCmd.PersistentFlags().String("hostname", "", "Forgejo instance hostname")
_ = viper.BindPFlag("hostname", rootCmd.PersistentFlags().Lookup("hostname")) _ = viper.BindPFlag("hostname", rootCmd.PersistentFlags().Lookup("hostname"))
@ -51,7 +51,7 @@ func initConfig() {
os.Exit(1) os.Exit(1)
} }
configDir := home + "/.config/fgj" configDir := home + "/.config/fj"
_ = os.MkdirAll(configDir, 0755) _ = os.MkdirAll(configDir, 0755)
viper.AddConfigPath(configDir) viper.AddConfigPath(configDir)

View file

@ -7,9 +7,9 @@ import (
"net/url" "net/url"
"time" "time"
"forgejo.zerova.net/public/fgj-sid/internal/api" "forgejo.zerova.net/public/fj/internal/api"
"forgejo.zerova.net/public/fgj-sid/internal/config" "forgejo.zerova.net/public/fj/internal/config"
"forgejo.zerova.net/public/fgj-sid/internal/text" "forgejo.zerova.net/public/fj/internal/text"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -61,13 +61,13 @@ var wikiListCmd = &cobra.Command{
Short: "List wiki pages", Short: "List wiki pages",
Long: "List all wiki pages for a repository.", Long: "List all wiki pages for a repository.",
Example: ` # List wiki pages for the current repo Example: ` # List wiki pages for the current repo
fgj wiki list fj wiki list
# List wiki pages for a specific repo # List wiki pages for a specific repo
fgj wiki list -R owner/repo fj wiki list -R owner/repo
# Output as JSON # Output as JSON
fgj wiki list --json`, fj wiki list --json`,
RunE: runWikiList, RunE: runWikiList,
} }
@ -76,16 +76,16 @@ var wikiViewCmd = &cobra.Command{
Short: "View a wiki page", Short: "View a wiki page",
Long: "Display the content of a wiki page.", Long: "Display the content of a wiki page.",
Example: ` # View a wiki page Example: ` # View a wiki page
fgj wiki view Home fj wiki view Home
# Open in browser # Open in browser
fgj wiki view Home --web fj wiki view Home --web
# View a wiki page as JSON (includes content) # View a wiki page as JSON (includes content)
fgj wiki view Home --json fj wiki view Home --json
# View a wiki page from a specific repo # View a wiki page from a specific repo
fgj wiki view "Getting-Started" -R owner/repo`, fj wiki view "Getting-Started" -R owner/repo`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runWikiView, RunE: runWikiView,
} }
@ -95,16 +95,16 @@ var wikiCreateCmd = &cobra.Command{
Short: "Create a wiki page", Short: "Create a wiki page",
Long: "Create a new wiki page in the repository.", Long: "Create a new wiki page in the repository.",
Example: ` # Create a wiki page with inline content Example: ` # Create a wiki page with inline content
fgj wiki create "Getting Started" -b "# Welcome\nThis is the getting started guide." fj wiki create "Getting Started" -b "# Welcome\nThis is the getting started guide."
# Create a wiki page from a file # Create a wiki page from a file
fgj wiki create "Setup Guide" --body-file setup.md fj wiki create "Setup Guide" --body-file setup.md
# Create a wiki page from stdin # Create a wiki page from stdin
echo "# FAQ" | fgj wiki create FAQ --body-file - echo "# FAQ" | fj wiki create FAQ --body-file -
# Output as JSON # Output as JSON
fgj wiki create "New Page" -b "Content here" --json`, fj wiki create "New Page" -b "Content here" --json`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runWikiCreate, RunE: runWikiCreate,
} }
@ -114,16 +114,16 @@ var wikiEditCmd = &cobra.Command{
Short: "Edit a wiki page", Short: "Edit a wiki page",
Long: "Edit an existing wiki page in the repository.", Long: "Edit an existing wiki page in the repository.",
Example: ` # Edit a wiki page with new content Example: ` # Edit a wiki page with new content
fgj wiki edit Home -b "# Updated Home\nNew content here." fj wiki edit Home -b "# Updated Home\nNew content here."
# Edit a wiki page from a file # Edit a wiki page from a file
fgj wiki edit "Setup Guide" --body-file updated-setup.md fj wiki edit "Setup Guide" --body-file updated-setup.md
# Edit a wiki page from stdin # Edit a wiki page from stdin
cat new-content.md | fgj wiki edit Home --body-file - cat new-content.md | fj wiki edit Home --body-file -
# Output as JSON # Output as JSON
fgj wiki edit Home -b "Updated content" --json`, fj wiki edit Home -b "Updated content" --json`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runWikiEdit, RunE: runWikiEdit,
} }
@ -133,13 +133,13 @@ var wikiDeleteCmd = &cobra.Command{
Short: "Delete a wiki page", Short: "Delete a wiki page",
Long: "Delete a wiki page from the repository.", Long: "Delete a wiki page from the repository.",
Example: ` # Delete a wiki page Example: ` # Delete a wiki page
fgj wiki delete "Old Page" fj wiki delete "Old Page"
# Delete without confirmation # Delete without confirmation
fgj wiki delete "Old Page" -y fj wiki delete "Old Page" -y
# Delete a wiki page from a specific repo # Delete a wiki page from a specific repo
fgj wiki delete "Outdated Guide" -R owner/repo`, fj wiki delete "Outdated Guide" -R owner/repo`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: runWikiDelete, RunE: runWikiDelete,
} }

2
go.mod
View file

@ -1,4 +1,4 @@
module forgejo.zerova.net/public/fgj-sid module forgejo.zerova.net/public/fj
go 1.24.0 go 1.24.0

View file

@ -9,7 +9,7 @@ import (
"time" "time"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"forgejo.zerova.net/public/fgj-sid/internal/config" "forgejo.zerova.net/public/fj/internal/config"
) )
var sharedHTTPClient = &http.Client{ var sharedHTTPClient = &http.Client{

View file

@ -3,7 +3,7 @@ package api
import ( import (
"testing" "testing"
"forgejo.zerova.net/public/fgj-sid/internal/config" "forgejo.zerova.net/public/fj/internal/config"
) )
func TestClient_Hostname(t *testing.T) { func TestClient_Hostname(t *testing.T) {

View file

@ -25,13 +25,13 @@ type HostConfig struct {
func GetConfigDir() (string, error) { func GetConfigDir() (string, error) {
if xdgConfigHome := os.Getenv("XDG_CONFIG_HOME"); xdgConfigHome != "" { if xdgConfigHome := os.Getenv("XDG_CONFIG_HOME"); xdgConfigHome != "" {
return filepath.Join(xdgConfigHome, "fgj"), nil return filepath.Join(xdgConfigHome, "fj"), nil
} }
home, err := os.UserHomeDir() home, err := os.UserHomeDir()
if err != nil { if err != nil {
return "", err return "", err
} }
return filepath.Join(home, ".config", "fgj"), nil return filepath.Join(home, ".config", "fj"), nil
} }
func GetConfigPath() (string, error) { func GetConfigPath() (string, error) {

View file

@ -83,7 +83,7 @@ func TestGetConfigDir_XDG(t *testing.T) {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("Unexpected error: %v", err)
} }
expected := "/custom/config/fgj" expected := "/custom/config/fj"
if dir != expected { if dir != expected {
t.Errorf("Expected %q, got %q", expected, dir) t.Errorf("Expected %q, got %q", expected, dir)
} }
@ -439,7 +439,7 @@ func TestResolveHostByPath(t *testing.T) {
"forgejo.zerova.net": { "forgejo.zerova.net": {
Hostname: "forgejo.zerova.net", Hostname: "forgejo.zerova.net",
Token: "token1", Token: "token1",
MatchDirs: []string{"/Users/sid/repos/fgj", "/Users/sid/repos/zerova"}, MatchDirs: []string{"/Users/sid/repos/fj", "/Users/sid/repos/zerova"},
}, },
"codeberg.org": { "codeberg.org": {
Hostname: "codeberg.org", Hostname: "codeberg.org",
@ -459,10 +459,10 @@ func TestResolveHostByPath(t *testing.T) {
cwd string cwd string
want string want string
}{ }{
{"exact dir match", "/Users/sid/repos/fgj", "forgejo.zerova.net"}, {"exact dir match", "/Users/sid/repos/fj", "forgejo.zerova.net"},
{"nested dir match", "/Users/sid/repos/fgj/cmd/root.go", "forgejo.zerova.net"}, {"nested dir match", "/Users/sid/repos/fj/cmd/root.go", "forgejo.zerova.net"},
{"second match dir", "/Users/sid/repos/zerova/pkg", "forgejo.zerova.net"}, {"second match dir", "/Users/sid/repos/zerova/pkg", "forgejo.zerova.net"},
{"longest prefix wins over /", "/Users/sid/repos/fgj/internal", "forgejo.zerova.net"}, {"longest prefix wins over /", "/Users/sid/repos/fj/internal", "forgejo.zerova.net"},
{"/ as global catch-all", "/tmp", "codeberg.org"}, {"/ as global catch-all", "/tmp", "codeberg.org"},
{"/ matches root itself", "/", "codeberg.org"}, {"/ matches root itself", "/", "codeberg.org"},
{"no match_dirs host not selected", "/some/random/path", "codeberg.org"}, {"no match_dirs host not selected", "/some/random/path", "codeberg.org"},
@ -512,7 +512,7 @@ func TestGetHost_MatchDirsIntegration(t *testing.T) {
"forgejo.zerova.net": { "forgejo.zerova.net": {
Hostname: "forgejo.zerova.net", Hostname: "forgejo.zerova.net",
Token: "token1", Token: "token1",
MatchDirs: []string{"/Users/sid/repos/fgj"}, MatchDirs: []string{"/Users/sid/repos/fj"},
}, },
"codeberg.org": { "codeberg.org": {
Hostname: "codeberg.org", Hostname: "codeberg.org",
@ -522,7 +522,7 @@ func TestGetHost_MatchDirsIntegration(t *testing.T) {
} }
// cwd match should resolve to forgejo.zerova.net // cwd match should resolve to forgejo.zerova.net
host, err := cfg.GetHost("", "", "/Users/sid/repos/fgj/cmd") host, err := cfg.GetHost("", "", "/Users/sid/repos/fj/cmd")
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }

View file

@ -17,41 +17,41 @@ func TestParseRemoteURL(t *testing.T) {
}{ }{
{ {
name: "HTTPS URL with .git", name: "HTTPS URL with .git",
url: "https://codeberg.org/romaintb/fgj.git", url: "https://codeberg.org/romaintb/fj.git",
wantOwner: "romaintb", wantOwner: "romaintb",
wantName: "fgj", wantName: "fj",
wantHost: "codeberg.org", wantHost: "codeberg.org",
wantErr: false, wantErr: false,
}, },
{ {
name: "HTTPS URL without .git", name: "HTTPS URL without .git",
url: "https://codeberg.org/romaintb/fgj", url: "https://codeberg.org/romaintb/fj",
wantOwner: "romaintb", wantOwner: "romaintb",
wantName: "fgj", wantName: "fj",
wantHost: "codeberg.org", wantHost: "codeberg.org",
wantErr: false, wantErr: false,
}, },
{ {
name: "SSH URL with .git", name: "SSH URL with .git",
url: "git@codeberg.org:romaintb/fgj.git", url: "git@codeberg.org:romaintb/fj.git",
wantOwner: "romaintb", wantOwner: "romaintb",
wantName: "fgj", wantName: "fj",
wantHost: "codeberg.org", wantHost: "codeberg.org",
wantErr: false, wantErr: false,
}, },
{ {
name: "SSH URL without .git", name: "SSH URL without .git",
url: "git@codeberg.org:romaintb/fgj", url: "git@codeberg.org:romaintb/fj",
wantOwner: "romaintb", wantOwner: "romaintb",
wantName: "fgj", wantName: "fj",
wantHost: "codeberg.org", wantHost: "codeberg.org",
wantErr: false, wantErr: false,
}, },
{ {
name: "SSH protocol URL", name: "SSH protocol URL",
url: "ssh://git@codeberg.org/romaintb/fgj.git", url: "ssh://git@codeberg.org/romaintb/fj.git",
wantOwner: "romaintb", wantOwner: "romaintb",
wantName: "fgj", wantName: "fj",
wantHost: "codeberg.org", wantHost: "codeberg.org",
wantErr: false, wantErr: false,
}, },

View file

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"os" "os"
"forgejo.zerova.net/public/fgj-sid/cmd" "forgejo.zerova.net/public/fj/cmd"
) )
func main() { func main() {

View file

@ -228,15 +228,15 @@ func (env *TestEnv) CleanupRepo(owner, repoName string) {
} }
} }
// GetBinaryPath returns the path to the built fgj binary // GetBinaryPath returns the path to the built fj binary
func (env *TestEnv) GetBinaryPath() string { func (env *TestEnv) GetBinaryPath() string {
binaryPath := os.Getenv("FGJ_BINARY_PATH") binaryPath := os.Getenv("FGJ_BINARY_PATH")
if binaryPath == "" { if binaryPath == "" {
// Look for the binary in common locations // Look for the binary in common locations
candidates := []string{ candidates := []string{
"./bin/fgj", "./bin/fj",
"bin/fgj", "bin/fj",
"/home/romain/work/fgj/bin/fgj", "/home/romain/work/fj/bin/fj",
} }
for _, candidate := range candidates { for _, candidate := range candidates {
if _, err := os.Stat(candidate); err == nil { if _, err := os.Stat(candidate); err == nil {
@ -248,7 +248,7 @@ func (env *TestEnv) GetBinaryPath() string {
} }
} }
// If no binary found, return default (will error when executed) // If no binary found, return default (will error when executed)
binaryPath = "./bin/fgj" binaryPath = "./bin/fj"
} }
return binaryPath return binaryPath
} }

View file

@ -199,7 +199,7 @@ func TestCLIIssueCreate(t *testing.T) {
"-R", fmt.Sprintf("%s/%s", env.Owner, env.RepoName), "-R", fmt.Sprintf("%s/%s", env.Owner, env.RepoName),
"issue", "create", "issue", "create",
"-t", "[FGJ E2E Test] CLI Created Issue", "-t", "[FGJ E2E Test] CLI Created Issue",
"-b", "Created directly via fgj CLI", "-b", "Created directly via fj CLI",
) )
if result.ExitCode != 0 { if result.ExitCode != 0 {
@ -624,14 +624,14 @@ func TestCLIPRComment(t *testing.T) {
"-R", fmt.Sprintf("%s/%s", env.Owner, env.RepoName), "-R", fmt.Sprintf("%s/%s", env.Owner, env.RepoName),
"pr", "comment", "pr", "comment",
fmt.Sprintf("%d", issueNum), fmt.Sprintf("%d", issueNum),
"-b", "Automated test comment via fgj pr comment", "-b", "Automated test comment via fj pr comment",
) )
if result.ExitCode != 0 { if result.ExitCode != 0 {
t.Fatalf("pr comment failed with exit code %d: %s", result.ExitCode, result.Stderr) t.Fatalf("pr comment failed with exit code %d: %s", result.ExitCode, result.Stderr)
} }
t.Logf("Successfully commented on issue #%d via fgj pr comment", issueNum) t.Logf("Successfully commented on issue #%d via fj pr comment", issueNum)
} }
// ===== CLI Repo Commands ===== // ===== CLI Repo Commands =====
@ -678,14 +678,14 @@ func TestCLIRepoList(t *testing.T) {
func TestCLIRepoCreate(t *testing.T) { func TestCLIRepoCreate(t *testing.T) {
env := NewTestEnv(t) env := NewTestEnv(t)
repoName := fmt.Sprintf("fgj-test-create-%d", time.Now().UnixNano()) repoName := fmt.Sprintf("fj-test-create-%d", time.Now().UnixNano())
defer env.CleanupRepo(env.Owner, repoName) defer env.CleanupRepo(env.Owner, repoName)
result := env.RunCLI( result := env.RunCLI(
"--hostname", env.Hostname, "--hostname", env.Hostname,
"repo", "create", repoName, "repo", "create", repoName,
"--public", "--public",
"-d", "Created by fgj functional test", "-d", "Created by fj functional test",
) )
if result.ExitCode != 0 { if result.ExitCode != 0 {
@ -703,8 +703,8 @@ func TestCLIRepoCreate(t *testing.T) {
if repo.Private { if repo.Private {
t.Fatalf("expected public repo, got private") t.Fatalf("expected public repo, got private")
} }
if repo.Description != "Created by fgj functional test" { if repo.Description != "Created by fj functional test" {
t.Fatalf("expected description %q, got %q", "Created by fgj functional test", repo.Description) t.Fatalf("expected description %q, got %q", "Created by fj functional test", repo.Description)
} }
t.Logf("Successfully created repository %s via CLI", repo.FullName) t.Logf("Successfully created repository %s via CLI", repo.FullName)
@ -756,7 +756,7 @@ func TestCLIRepoClone(t *testing.T) {
env := NewTestEnv(t) env := NewTestEnv(t)
tmpDir := t.TempDir() tmpDir := t.TempDir()
clonePath := fmt.Sprintf("%s/fgj-clone", tmpDir) clonePath := fmt.Sprintf("%s/fj-clone", tmpDir)
result := env.RunCLI( result := env.RunCLI(
"--hostname", env.Hostname, "--hostname", env.Hostname,
@ -799,13 +799,13 @@ func TestCLIReleaseList(t *testing.T) {
func TestCLIReleaseCreateUploadDelete(t *testing.T) { func TestCLIReleaseCreateUploadDelete(t *testing.T) {
env := NewTestEnv(t) env := NewTestEnv(t)
tag := fmt.Sprintf("fgj-test-%d", time.Now().UnixNano()) tag := fmt.Sprintf("fj-test-%d", time.Now().UnixNano())
title := "FGJ CLI Release Test" title := "FGJ CLI Release Test"
notes := "Release created by functional tests" notes := "Release created by functional tests"
tmpDir := t.TempDir() tmpDir := t.TempDir()
assetPath := fmt.Sprintf("%s/asset.txt", tmpDir) assetPath := fmt.Sprintf("%s/asset.txt", tmpDir)
if err := os.WriteFile(assetPath, []byte("fgj release asset"), 0600); err != nil { if err := os.WriteFile(assetPath, []byte("fj release asset"), 0600); err != nil {
t.Fatalf("failed to create asset file: %v", err) t.Fatalf("failed to create asset file: %v", err)
} }
@ -863,7 +863,7 @@ func TestCLIReleaseView(t *testing.T) {
env := NewTestEnv(t) env := NewTestEnv(t)
// Create a release to view // Create a release to view
tag := fmt.Sprintf("fgj-view-test-%d", time.Now().UnixNano()) tag := fmt.Sprintf("fj-view-test-%d", time.Now().UnixNano())
createResult := env.RunCLI( createResult := env.RunCLI(
"--hostname", env.Hostname, "--hostname", env.Hostname,
@ -1149,7 +1149,7 @@ func TestCLIAPIGet(t *testing.T) {
t.Fatalf("expected repo name %q in JSON output, got %v", env.RepoName, data["name"]) t.Fatalf("expected repo name %q in JSON output, got %v", env.RepoName, data["name"])
} }
t.Logf("Successfully retrieved repo info via fgj api GET") t.Logf("Successfully retrieved repo info via fj api GET")
} }
func TestCLIAPIPostAndDelete(t *testing.T) { func TestCLIAPIPostAndDelete(t *testing.T) {
@ -1162,7 +1162,7 @@ func TestCLIAPIPostAndDelete(t *testing.T) {
"api", endpoint, "api", endpoint,
"-X", "POST", "-X", "POST",
"-f", "title=[FGJ E2E Test] API Post Test", "-f", "title=[FGJ E2E Test] API Post Test",
"-f", "body=Created via fgj api command", "-f", "body=Created via fj api command",
) )
if result.ExitCode != 0 { if result.ExitCode != 0 {
@ -1182,7 +1182,7 @@ func TestCLIAPIPostAndDelete(t *testing.T) {
issueNum := int64(issueNumber) issueNum := int64(issueNumber)
defer env.CleanupIssue(issueNum) defer env.CleanupIssue(issueNum)
t.Logf("Successfully created issue #%d via fgj api POST", issueNum) t.Logf("Successfully created issue #%d via fj api POST", issueNum)
} }
// ===== Structured Error Output ===== // ===== Structured Error Output =====