Release & Publish Workflow Guide
Release & Publish Workflow Guide — forgejo-proxy org
Section titled “Release & Publish Workflow Guide — forgejo-proxy org”Status: Current state analysis + proposed unified flow Scope: All 7 repositories in the
forgejo-proxyorganization Registry: Verdaccio atnpm.registry.hochguertel.work
1. Organization Overview
Section titled “1. Organization Overview”The forgejo-proxy org contains 7 repositories, each with different
purposes and CI/CD patterns.

Repository categories
Section titled “Repository categories”| Category | Repos | Publish target | Workflows |
|---|---|---|---|
| npm packages | forgejo-proxy-cli, forgejo-proxy-api-spec | Verdaccio (private npm) | 9 total |
| Docker images | forgejo-proxy, forgejo-actions-proxy, forgejo-actions-proxy-docs-site | Docker registry | 6 total |
| Docs site | forgejo-proxy-docs-site | webhookd deploy | 2 total |
| Test suite | actions-proxy-test | None (test only) | 4 total |
Workflow inventory
Section titled “Workflow inventory”| Repo | Workflows | Triggers | Publishes |
|---|---|---|---|
forgejo-proxy-cli | ci.yml, e2e.yml, cli-help-capture.yml, release.yml, publish.yml | push, PR, tag v*.*.* | npm (Verdaccio) |
forgejo-proxy-api-spec | ci.yml, spec-lint.yml, sdk-ts-generate.yml, sdk-ts-publish.yml | push, PR, tag v*, path filters | npm (Verdaccio) |
forgejo-proxy | build.yml, deploy.yml, lint.yml, test.yml | push, PR, tag v* | Docker image |
forgejo-actions-proxy | build.yml | push, tag v*, path filters | Docker image + deploy |
forgejo-actions-proxy-docs-site | build.yml | push, tag v*, path filters | Docker image + deploy |
forgejo-proxy-docs-site | build.yml, deploy.yml | push, PR, path filters | webhookd deploy |
actions-proxy-test | benchmark.yml, integration-test.yml, debug-case-d.yml, test-latest-ref.yml | cron, push, manual | None |
2. npm Package Repos — Current State
Section titled “2. npm Package Repos — Current State”2.1 forgejo-proxy-cli (5 workflows)
Section titled “2.1 forgejo-proxy-cli (5 workflows)”| Workflow | Trigger | Purpose |
|---|---|---|
ci.yml | push to main, PRs | Lint + type-check + unit tests |
e2e.yml | separate | E2E tests |
cli-help-capture.yml | separate | Captures thfg vs gh help docs |
release.yml | tag v*.*.* | Builds CLI, captures comparison docs, commits to main |
publish.yml | tag v*.*.* + manual | Type-check, lint, test, build, publish to Verdaccio |

What happens on tag push v1.0.0:
release.ymlfires — builds CLI, runs help-capture script, commits comparison docs back to mainpublish.ymlfires — type-checks, lints, tests, builds, publishes@forgejo-proxy/core+@forgejo-proxy/cli- Both run in parallel — no ordering, no shared state, no failure gating
Gaps:
- No automated version bump — developer manually edits
package.json - No git tag creation — developer creates tag manually
- No Forgejo Release creation (no release notes, no changelog)
release.ymlandpublish.ymlduplicate setup/build steps- If
publish.ymlfails,release.ymlstill commits docs to main (no gating) ci.ymlandrelease.ymlstill use_authToken(wrong — should use_auth)
2.2 forgejo-proxy-api-spec (4 workflows)
Section titled “2.2 forgejo-proxy-api-spec (4 workflows)”| Workflow | Trigger | Purpose |
|---|---|---|
ci.yml | push to main, PRs | Validates TypeSpec, runs invariant tests, type-checks SDKs |
spec-lint.yml | push to spec/** | Compiles TypeSpec |
sdk-ts-generate.yml | push to openapi.json | Regenerates TS SDK, commits to main |
sdk-ts-publish.yml | tag v* + manual | Installs deps, publishes @forgejo-proxy/sdk |

What happens on tag push v0.1.2:
sdk-ts-publish.ymlfires — installs deps, publishes to Verdaccio- No tests, no type-check, no build verification before publishing
Gaps:
- No release workflow (no release notes, no Forgejo Release)
- No pre-publish verification — could publish broken code
- No automated version bump
- No changelog generation
2.3 Current manual release process
Section titled “2.3 Current manual release process”
Step-by-step:
- Developer manually edits
package.jsonversion field - Developer manually creates git tag (
git tag v1.0.0) - Developer pushes tag (
git push --tags) - Multiple workflows fire independently and in parallel:
- CLI:
release.yml(docs) +publish.yml(npm) — no coordination - API-SPEC:
sdk-ts-publish.yml(npm) — no verification
- CLI:
- No Forgejo Release is created
- No changelog is generated
- No version bump commit after publish
2.4 Inconsistencies between npm repos
Section titled “2.4 Inconsistencies between npm repos”| Issue | CLI repo | API-SPEC repo |
|---|---|---|
| Auth in publish | _auth with base64 -w0 (correct) | _auth with base64 -w0 (fixed) |
| Auth in CI/release | ci.yml and release.yml still use _authToken (wrong) | ci.yml doesn’t configure registry auth |
| Pre-publish verification | publish.yml runs type-check + lint + tests + build | sdk-ts-publish.yml does NOT run any verification |
| Release workflow | Has release.yml (comparison docs) | No release workflow at all |
| Version bump | Manual | Manual |
| Tag format | v*.*.* | v* (less specific) |
| Forgejo Release | Not created | Not created |
| Changelog | Not generated | Not generated |
3. Docker Image Repos — Current State
Section titled “3. Docker Image Repos — Current State”3.1 Overview
Section titled “3.1 Overview”Three repos build and deploy Docker images. They follow a similar pattern but with some differences.

3.2 forgejo-proxy (4 workflows)
Section titled “3.2 forgejo-proxy (4 workflows)”| Workflow | Trigger | Purpose |
|---|---|---|
lint.yml | push to main, PRs | Ruff check + format check (Python) |
test.yml | push to main, PRs | Runs pytest |
build.yml | tag v* | Builds Docker image with Buildx + cache |
deploy.yml | push to main | Triggers webhookd redeploy |
Flow: lint + test on push/PR → build on tag → deploy on main push
Gap: build and deploy are separate workflows with no coordination. Build doesn’t trigger deploy automatically after a tag.
3.3 forgejo-actions-proxy (1 workflow)
Section titled “3.3 forgejo-actions-proxy (1 workflow)”| Workflow | Trigger | Purpose |
|---|---|---|
build.yml | push to main, tag v*, path filters | Validate nginx config → build + push Docker image → deploy via webhook → notify |
Flow: single workflow handles everything (validate → publish → deploy → notify)
Strength: this is the most unified workflow in the org — all steps in one job, sequential.
3.4 forgejo-actions-proxy-docs-site (1 workflow)
Section titled “3.4 forgejo-actions-proxy-docs-site (1 workflow)”| Workflow | Trigger | Purpose |
|---|---|---|
build.yml | push to main, tag v*, path filters | Validate Astro build → build + push Docker image → deploy via webhook → notify |
Flow: same pattern as actions-proxy — single unified workflow.
3.5 forgejo-proxy-docs-site (2 workflows)
Section titled “3.5 forgejo-proxy-docs-site (2 workflows)”| Workflow | Trigger | Purpose |
|---|---|---|
build.yml | push to main, PRs, path filters | npm ci + Astro build + drift check |
deploy.yml | push to main | Triggers webhookd redeploy |
Flow: build on push/PR → deploy on main push (separate workflows)
Gap: build and deploy are separate, no coordination. No Docker image — deploys via webhookd only.
4. Test Repo — Current State
Section titled “4. Test Repo — Current State”actions-proxy-test (4 workflows)
Section titled “actions-proxy-test (4 workflows)”
| Workflow | Trigger | Purpose |
|---|---|---|
benchmark.yml | daily cron 6:00 AM + manual | Measures proxy latency + action timing (cold + warm) |
integration-test.yml | daily cron 7:00 AM + push to main | Tests all proxy tiers (1-3), actions checkout/setup-uv/setup-bun |
debug-case-d.yml | push to main + manual | Verifies GITHUB_API_URL patching, regression test |
test-latest-ref.yml | push to main + manual | Tests @latest ref injection |
No publishing, no release. Pure test/benchmark repo. No changes needed.
5. Proposed Unified Release Flow
Section titled “5. Proposed Unified Release Flow”5.1 Design goals
Section titled “5.1 Design goals”- Single workflow per repo triggered by tag push or
workflow_dispatch - CI gate — lint + type-check + tests must pass before publish
- Build — all packages built after CI passes
- Publish — correct tool per package type (npm or Docker)
- Forgejo Release — auto-created with changelog from commits
- Deploy — triggered after successful publish (Docker repos)
- Docs update — CLI repo updates comparison docs as final step
5.2 Proposed flow
Section titled “5.2 Proposed flow”
5.3 Process flow
Section titled “5.3 Process flow”
5.4 Step-by-step
Section titled “5.4 Step-by-step”- Developer pushes tag
v*.*.*(or triggersworkflow_dispatch) - CI Gate — lint + type-check + unit tests run. If any fail, the release aborts.
- Build — all packages are built
- Publish — packages published to correct registry:
- npm repos:
bun pm pack+npm publishto Verdaccio (with_auth) - Docker repos:
docker build+docker pushto private registry
- npm repos:
- Forgejo Release — created automatically with changelog from commits since last tag
- Deploy (Docker repos) — webhookd triggered to redeploy
- Docs update (CLI only) — comparison docs regenerated and committed to main
5.5 Key principles
Section titled “5.5 Key principles”- Sequential, not parallel — each step gates the next. No publish if tests fail.
- Same auth pattern everywhere —
_authwithbase64 -w0, never_authToken - Pre-publish verification — all repos run CI before publishing
- Automated changelog — generated from conventional commits between tags
- Forgejo Release — created after successful publish, not before
- Single workflow — no more parallel
release.yml+publish.yml
5.6 Workflow structure (pseudo-YAML)
Section titled “5.6 Workflow structure (pseudo-YAML)”on: push: tags: ['v*.*.*'] workflow_dispatch: inputs: tag: description: 'Release tag (e.g. v1.0.0)' required: false
jobs: ci-gate: steps: - checkout - setup-bun # or setup-python, setup-node - configure-registry # _auth, not _authToken - install dependencies - lint - type-check - unit tests
build: needs: ci-gate steps: - checkout - setup - install dependencies - build all packages
publish: needs: build steps: - checkout - setup - configure-registry # _auth with base64 -w0 # npm repos: - bun pm pack # replaces workspace:* deps - npm publish *.tgz --access restricted # Docker repos: - docker build + push
release: needs: publish steps: - checkout - create Forgejo Release with auto changelog # CLI only: update comparison docs # Docker repos: trigger webhookd deploy6. Migration Plan
Section titled “6. Migration Plan”Phase 1: Fix auth everywhere (quick wins)
Section titled “Phase 1: Fix auth everywhere (quick wins)”| Task | Repo | Change |
|---|---|---|
Fix ci.yml auth | forgejo-proxy-cli | Replace _authToken with _auth + base64 -w0 |
Fix release.yml auth | forgejo-proxy-cli | Replace _authToken with _auth + base64 -w0 |
| Add CI gate to SDK publish | forgejo-proxy-api-spec | Add type-check + test steps before npm publish |
| Standardize tag format | forgejo-proxy-api-spec | Change v* to v*.*.* |
Phase 2: Unify release workflows
Section titled “Phase 2: Unify release workflows”| Task | Repo | Change |
|---|---|---|
Merge release.yml + publish.yml | forgejo-proxy-cli | Single workflow: ci-gate → build → publish → release → docs |
| Add Forgejo Release creation | forgejo-proxy-cli | Use Forgejo API to create release with changelog |
| Add Forgejo Release creation | forgejo-proxy-api-spec | Same |
| Add changelog generation | both npm repos | Use git log between tags or git-cliff |
Phase 3: Unify Docker repo workflows
Section titled “Phase 3: Unify Docker repo workflows”| Task | Repo | Change |
|---|---|---|
Merge build.yml + deploy.yml | forgejo-proxy | Single workflow: lint → test → build → push → deploy |
Merge build.yml + deploy.yml | forgejo-proxy-docs-site | Single workflow: build → deploy |
| Add Forgejo Release creation | all Docker repos | Create release with changelog on tag push |
Phase 4: Automation
Section titled “Phase 4: Automation”| Task | Scope | Change |
|---|---|---|
| Automated version bump | npm repos | workflow_dispatch with version input → bumps package.json → tags → triggers release |
| Automated changelog | all repos | git-cliff or git log between tags |
| Release notes from commits | all repos | Conventional commit parsing |
7. Auth Pattern Reference
Section titled “7. Auth Pattern Reference”All workflows that interact with Verdaccio must use this pattern:
REGISTRY="${{ secrets.NPM_REGISTRY_URL }}"REGISTRY_HOST="${REGISTRY#https://}"REGISTRY_HOST="${REGISTRY_HOST#http://}"REGISTRY_HOST="${REGISTRY_HOST%/}"AUTH=$(echo -n "forgejo-ci:${{ secrets.NPM_REGISTRY_TOKEN }}" | base64 -w0){ echo "@forgejo-proxy:registry=${REGISTRY}" echo "//${REGISTRY_HOST}/:_auth=${AUTH}" echo "//${REGISTRY_HOST}/:always-auth=true"} >> ~/.npmrcNever use _authToken — Verdaccio htpasswd requires Basic auth, not Bearer token.
Always use base64 -w0 — without -w0, base64 wraps at 76 chars and breaks .npmrc.
See the npm Registry CI/CD guide and the Verdaccio usage guide for full documentation.
8. Required Secrets
Section titled “8. Required Secrets”Org-level secrets (forgejo-proxy)
Section titled “Org-level secrets (forgejo-proxy)”| Secret | Value | Used by |
|---|---|---|
NPM_REGISTRY_URL | https://npm.registry.hochguertel.work | npm repos (publish, ci) |
NPM_REGISTRY_TOKEN | forgejo-ci htpasswd password | npm repos (publish, ci) |
CI_FORGEJO_TOKEN | Forgejo API token | CLI repo (tests, release) |
THFG_TOKEN | thfg API token | CLI repo |
REGISTRY_URL | Docker registry URL | Docker repos |
REGISTRY_USERNAME | Docker registry username | Docker repos |
REGISTRY_PASSWORD | Docker registry password | Docker repos |
Repo-level secrets
Section titled “Repo-level secrets”| Repo | Secret | Purpose |
|---|---|---|
forgejo-proxy-api-spec | FORGEJO_TOKEN | SDK generation commit push |
actions-proxy-test | (various) | Test configuration |