3 unstable releases
| 0.2.4 | Apr 12, 2026 |
|---|---|
| 0.1.1 | Apr 1, 2026 |
| 0.1.0 | Apr 1, 2026 |
#1977 in Development tools
305KB
7K
SLoC
Apiforge
Production-grade API release automation CLI.
From merged code to healthy pods in production — one command.
Table of Contents
- What Apiforge does
- Installation
- Quick start
- CLI reference
- Release pipeline behavior
- Rollback semantics
- Configuration reference (
apiforge.toml) - Template variables
- CI/CD integration
- Security and reliability model
- Developer guide
- Troubleshooting
- Known limitations
- Contributing
- License
What Apiforge does
Apiforge automates a full release path for API services:
- Preflight checks for repo and environment.
- Version bump in language-specific version files.
- Optional changelog generation.
- Commit and tag creation.
- Push to git remote.
- Optional Docker build/push.
- Optional Kubernetes image update and rollout wait.
- Optional GitHub release creation.
- Optional health-check verification.
- Automatic rollback of completed steps when a later step fails.
The goal is to make releases repeatable, reviewable, and recoverable.
Installation
Prerequisites
- Rust
1.91.1+(for building/running from source) gitdocker(if using Docker steps)kubectl(if using Kubernetes steps)awsCLI credentials/profile (if using ECR)
Option 1: Install from Cargo
cargo install apiforge
Option 2: Download release archives
# Linux (x86_64 / amd64)
curl -L https://github.com/PrazwalR/Apiforge/releases/latest/download/apiforge-linux-amd64.tar.gz -o apiforge.tar.gz
tar -xzf apiforge.tar.gz
chmod +x apiforge
sudo mv apiforge /usr/local/bin/
# Linux (arm64)
curl -L https://github.com/PrazwalR/Apiforge/releases/latest/download/apiforge-linux-arm64.tar.gz -o apiforge.tar.gz
tar -xzf apiforge.tar.gz
chmod +x apiforge
sudo mv apiforge /usr/local/bin/
# macOS (Apple Silicon)
curl -L https://github.com/PrazwalR/Apiforge/releases/latest/download/apiforge-darwin-arm64.tar.gz -o apiforge.tar.gz
tar -xzf apiforge.tar.gz
chmod +x apiforge
sudo mv apiforge /usr/local/bin/
# macOS (Intel / amd64)
curl -L https://github.com/PrazwalR/Apiforge/releases/latest/download/apiforge-darwin-amd64.tar.gz -o apiforge.tar.gz
tar -xzf apiforge.tar.gz
chmod +x apiforge
sudo mv apiforge /usr/local/bin/
Windows artifact is published as apiforge-windows-amd64.zip.
Option 3: Build from source
git clone https://github.com/PrazwalR/Apiforge.git
cd Apiforge
cargo build --release --locked
./target/release/apiforge --version
Quick start
1. Initialize config
apiforge init
This creates apiforge.toml with defaults.
2. Validate setup
apiforge doctor
3. Preview a release (no side effects)
apiforge release patch --dry-run
4. Execute release
apiforge release patch
5. Inspect history and status
apiforge history --limit 20
apiforge status
CLI reference
Global flags:
--config <path>: config file path (default:apiforge.toml)--debug: enable debug logs (APIFORGE_DEBUG=truealso works)
apiforge init
Initializes a new config file.
apiforge init [--name my-service] [--force]
apiforge doctor
Checks:
- required tools (
git,docker,kubectl,aws) - config file parse/validation
- repository visibility/basic git status
apiforge doctor
apiforge release <major|minor|patch>
Runs the release pipeline.
apiforge release patch \
--dry-run \
--skip-docker \
--skip-k8s \
--skip-github \
--skip-notify \
--no-changelog \
--output json \
--yes
Flags:
| Flag | Meaning |
|---|---|
--dry-run |
Simulate pipeline steps without mutating systems |
--skip-docker |
Skip Docker build and push steps |
--skip-k8s |
Skip Kubernetes update and rollout wait |
--skip-github |
Skip GitHub release step |
--skip-notify |
Skip post-release notification dispatch |
--no-changelog |
Skip changelog step even if enabled in config |
| `--output text | json` |
-y, --yes |
Skip confirmation prompt |
apiforge rollback
Rolls Kubernetes deployment image back to a target version.
apiforge rollback --to v1.2.3
apiforge rollback --to v1.2.3 --dry-run
apiforge history
Reads audit records from .apiforge/audit.
apiforge history --limit 50 --filter success --output text
apiforge history --output json
apiforge status
Shows project metadata, git HEAD/tag, and Kubernetes deployment image/replica state.
apiforge status
Release pipeline behavior
When you run apiforge release <bump>, step order is:
git-preflightversion-bumpchangelog(if enabled and not skipped)git-commitgit-taggit-pushdocker-build(if not skipped)docker-push(if not skipped)k8s-update(if not skipped)k8s-rollout(if not skipped)github-release(if configured and not skipped)health-check(if configured)
On success, Apiforge can send notification(s) and records a release audit entry.
Rollback semantics
Automatic rollback is triggered when a step fails after prior steps succeeded. Rollback runs in reverse order for completed steps.
| Step | Rollback behavior |
|---|---|
version-bump |
Restores original version-file content captured before mutation |
changelog |
Restores CHANGELOG.md from git checkout |
git-commit |
Soft reset to parent commit (changes remain staged) |
git-tag |
Deletes created tag |
git-push |
Deletes remote/local tag; intentionally does not force-rewrite shared commit history |
github-release |
Deletes created GitHub release when possible |
| docker/k8s/health | Step-specific best-effort behavior or no-op if not applicable |
Important design choice: on git-push rollback, commit history is preserved and only release marker tags are removed.
Configuration reference (apiforge.toml)
Full example
[project]
name = "my-api"
language = "rust" # rust | node | python | go | java
[git]
main_branch = "main"
tag_format = "v{version}"
changelog = true
commit_message = "chore: release v{{ version }}"
remote = "origin"
require_clean = true
require_main_branch = true
fetch_timeout_secs = 60
push_timeout_secs = 120
operation_timeout_secs = 30
[docker]
registry = "aws_ecr" # aws_ecr | docker_hub | ghcr | custom
repository = "my-api"
dockerfile = "Dockerfile"
context = "."
tags = ["{version}", "{major}.{minor}", "latest", "{git_sha}"]
# build_args = { APP_ENV = "production" }
[kubernetes]
context = "production"
namespace = "default"
deployment = "my-api"
manifest_path = "k8s/deployment.yaml"
image_field = ".spec.template.spec.containers[0].image"
rollout_timeout = 300
min_ready_percent = 100
[aws]
region = "us-east-1"
# profile = "prod"
[github]
repository = "org/repo"
token = "${GITHUB_TOKEN}"
create_release = true
prerelease = false
draft = false
[notifications.slack]
webhook_url = "${SLACK_WEBHOOK_URL}"
message = "{{ status_emoji }} Release {{ version }} of {{ project }}: {{ status }}"
notify_on = "both" # success | failure | both
# Optional generic webhook payload
# [notifications.webhook]
# url = "https://hooks.example.com/release"
# method = "POST"
# headers = { "Authorization" = "Bearer ${WEBHOOK_TOKEN}" }
# body = "{\"project\":\"{{ project }}\",\"version\":\"{{ version }}\",\"status\":\"{{ status }}\"}"
[health_check]
url = "https://api.example.com/health"
method = "GET" # GET | POST | HEAD | PUT
expected_status = 200
# expected_body_field = "/status"
# expected_body_value = "ok"
timeout = 60
interval = 5
Field details
[project]
| Key | Type | Required | Notes |
|---|---|---|---|
name |
string | yes | Displayed in output/messages |
language |
enum | yes | Determines version file (Cargo.toml, package.json, pyproject.toml, go.mod, pom.xml) |
[git]
| Key | Type | Default | Notes |
|---|---|---|---|
main_branch |
string | none | Expected release branch |
tag_format |
string | none | Must include {version} |
changelog |
bool | true |
Enable changelog step |
commit_message |
string | none | Supports {{ version }} / {{ project }} |
remote |
string | origin |
Target remote |
require_clean |
bool | true |
Require no unstaged/uncommitted changes |
require_main_branch |
bool | true |
Require release from main_branch |
fetch_timeout_secs |
u64 | 60 |
Timeout for fetch-like operations |
push_timeout_secs |
u64 | 120 |
Timeout for push operations |
operation_timeout_secs |
u64 | 30 |
Timeout for other git operations |
[docker]
| Key | Type | Default | Notes |
|---|---|---|---|
registry |
enum | none | aws_ecr, docker_hub, ghcr, custom |
repository |
string | none | Required non-empty |
dockerfile |
string | Dockerfile |
Relative to context |
context |
string | . |
Build context path |
tags |
array | none | At least one tag pattern required |
build_args |
table | none | Optional build args |
Docker tag placeholders supported by validation/runtime:
{version}{major}{minor}{patch}{git_sha}{git_sha_full}
[kubernetes]
| Key | Type | Default | Notes |
|---|---|---|---|
context |
string | none | kube context name |
namespace |
string | none | Required non-empty |
deployment |
string | none | Deployment to patch |
manifest_path |
string | none | Maintained for manifest-oriented workflows |
image_field |
string | none | JSON pointer-like selector for image path |
rollout_timeout |
u64 | 300 |
Max seconds for rollout wait |
min_ready_percent |
u8 | 100 |
Must be 0..=100 |
[aws]
| Key | Type | Required | Notes |
|---|---|---|---|
region |
string | yes for ECR | Required when docker.registry = "aws_ecr" |
profile |
string | no | Optional AWS profile |
[github] (optional)
| Key | Type | Default | Notes |
|---|---|---|---|
repository |
string | none | owner/repo |
token |
string | none | GitHub token |
create_release |
bool | true |
Kept for compatibility |
prerelease |
bool | false |
GitHub prerelease flag |
draft |
bool | false |
GitHub draft flag |
[notifications] (optional)
Slack:
| Key | Type | Default |
|---|---|---|
webhook_url |
string | none |
message |
string | none |
notify_on |
enum | both |
Webhook:
| Key | Type | Default |
|---|---|---|
url |
string | none |
method |
string | POST |
headers |
table | none |
body |
string | none |
[health_check] (optional)
| Key | Type | Default | Notes |
|---|---|---|---|
url |
string | none | Required if section present |
method |
enum | GET |
GET, POST, HEAD, PUT |
expected_status |
u16 | 200 |
Expected HTTP status |
expected_body_field |
string | none | JSON pointer path (e.g. /status) |
expected_body_value |
string | none | Compared against resolved response field |
timeout |
u64 | 60 |
Total check window |
interval |
u64 | 5 |
Retry interval, must be > 0 |
Template variables
Apiforge uses templates in multiple places. Available keys depend on context:
Commit message templates (git.commit_message)
{{ version }}{{ project }}
Docker tag templates (docker.tags)
{version},{major},{minor},{patch},{git_sha},{git_sha_full}
Notification templates (message/body)
Commonly provided:
{{ version }}{{ project }}{{ status }}{{ status_emoji }}
Health-check templates (health_check.url, expected_body_value)
{{ version }}{{ project }}
CI/CD integration
GitHub Actions (example)
name: Release via Apiforge
on:
workflow_dispatch:
inputs:
bump:
description: "Version bump type"
required: true
default: "patch"
type: choice
options: [patch, minor, major]
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install Apiforge
run: |
curl -L https://github.com/PrazwalR/Apiforge/releases/latest/download/apiforge-linux-amd64.tar.gz -o apiforge.tar.gz
tar -xzf apiforge.tar.gz
chmod +x apiforge
sudo mv apiforge /usr/local/bin/
- name: Run release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: apiforge release ${{ inputs.bump }} --yes
Security and reliability model
Built-in protections
- Config validation before release execution.
- Timeout wrappers around network-prone git operations.
- Automatic rollback orchestration for completed steps.
- Sanitization of sensitive data in rendered/logged error messages.
- Audit log persistence under
.apiforge/audit.
Audit storage
- Location:
.apiforge/audit - Retention: bounded record count
- Supports compaction and retry-aware writes
Vulnerability scanning
Use:
cargo audit
If advisories are intentionally suppressed due transitive ecosystem constraints, they are documented in .cargo/audit.toml.
Developer guide
Repository structure
src/
cli.rs # CLI definition
config.rs # Config model + validation
orchestrator/ # Pipeline execution + rollback orchestration
steps/ # Concrete step implementations
git/
docker/
kubernetes/
github/
health/
integrations/ # Service clients (git, docker, k8s, aws, github)
audit/ # Release history store
output/ # CLI output rendering
utils/ # Helpers (semver/template/retry/sanitize/version)
Local quality gates
cargo fmt --all -- --check
cargo test --all-features --locked
cargo clippy --locked --all-targets --all-features -- -D warnings
cargo build --release --locked
cargo doc --no-deps --locked
cargo bench --no-run --locked
cargo audit
Troubleshooting
git.tag_format must contain {version}
Your [git].tag_format is invalid. Use a format like:
tag_format = "v{version}"
Health-check never succeeds
Check:
- endpoint URL and network reachability
- method (
GET/POST/HEAD/PUT) - expected status code
- optional JSON pointer/value match
- timeout/interval values
ECR or AWS auth issues
Verify:
- correct
aws.region - IAM credentials/profile
- ability to call STS/ECR
Kubernetes rollout timeout
Check deployment events and image pull/access:
kubectl -n <namespace> describe deploy <name>
kubectl -n <namespace> get pods
kubectl -n <namespace> logs <pod>
Known limitations
apiforge rollbackcurrently requires explicit--to <version>; automatic rollback-target detection is not implemented yet.- Git push rollback intentionally avoids force-rewriting remote commit history; it removes release tags instead.
Contributing
See CONTRIBUTING.md.
License
MIT — see LICENSE.
Dependencies
~138MB
~2M SLoC