Skip to content

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-proxy organization Registry: Verdaccio at npm.registry.hochguertel.work


The forgejo-proxy org contains 7 repositories, each with different purposes and CI/CD patterns.

System Context — All Repos

CategoryReposPublish targetWorkflows
npm packagesforgejo-proxy-cli, forgejo-proxy-api-specVerdaccio (private npm)9 total
Docker imagesforgejo-proxy, forgejo-actions-proxy, forgejo-actions-proxy-docs-siteDocker registry6 total
Docs siteforgejo-proxy-docs-sitewebhookd deploy2 total
Test suiteactions-proxy-testNone (test only)4 total
RepoWorkflowsTriggersPublishes
forgejo-proxy-clici.yml, e2e.yml, cli-help-capture.yml, release.yml, publish.ymlpush, PR, tag v*.*.*npm (Verdaccio)
forgejo-proxy-api-specci.yml, spec-lint.yml, sdk-ts-generate.yml, sdk-ts-publish.ymlpush, PR, tag v*, path filtersnpm (Verdaccio)
forgejo-proxybuild.yml, deploy.yml, lint.yml, test.ymlpush, PR, tag v*Docker image
forgejo-actions-proxybuild.ymlpush, tag v*, path filtersDocker image + deploy
forgejo-actions-proxy-docs-sitebuild.ymlpush, tag v*, path filtersDocker image + deploy
forgejo-proxy-docs-sitebuild.yml, deploy.ymlpush, PR, path filterswebhookd deploy
actions-proxy-testbenchmark.yml, integration-test.yml, debug-case-d.yml, test-latest-ref.ymlcron, push, manualNone

WorkflowTriggerPurpose
ci.ymlpush to main, PRsLint + type-check + unit tests
e2e.ymlseparateE2E tests
cli-help-capture.ymlseparateCaptures thfg vs gh help docs
release.ymltag v*.*.*Builds CLI, captures comparison docs, commits to main
publish.ymltag v*.*.* + manualType-check, lint, test, build, publish to Verdaccio

Current CLI Release Flow

What happens on tag push v1.0.0:

  1. release.yml fires — builds CLI, runs help-capture script, commits comparison docs back to main
  2. publish.yml fires — type-checks, lints, tests, builds, publishes @forgejo-proxy/core + @forgejo-proxy/cli
  3. 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.yml and publish.yml duplicate setup/build steps
  • If publish.yml fails, release.yml still commits docs to main (no gating)
  • ci.yml and release.yml still use _authToken (wrong — should use _auth)
WorkflowTriggerPurpose
ci.ymlpush to main, PRsValidates TypeSpec, runs invariant tests, type-checks SDKs
spec-lint.ymlpush to spec/**Compiles TypeSpec
sdk-ts-generate.ymlpush to openapi.jsonRegenerates TS SDK, commits to main
sdk-ts-publish.ymltag v* + manualInstalls deps, publishes @forgejo-proxy/sdk

Current API-SPEC Release Flow

What happens on tag push v0.1.2:

  1. sdk-ts-publish.yml fires — installs deps, publishes to Verdaccio
  2. 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

Current Manual Release Process

Step-by-step:

  1. Developer manually edits package.json version field
  2. Developer manually creates git tag (git tag v1.0.0)
  3. Developer pushes tag (git push --tags)
  4. 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
  5. No Forgejo Release is created
  6. No changelog is generated
  7. No version bump commit after publish
IssueCLI repoAPI-SPEC repo
Auth in publish_auth with base64 -w0 (correct)_auth with base64 -w0 (fixed)
Auth in CI/releaseci.yml and release.yml still use _authToken (wrong)ci.yml doesn’t configure registry auth
Pre-publish verificationpublish.yml runs type-check + lint + tests + buildsdk-ts-publish.yml does NOT run any verification
Release workflowHas release.yml (comparison docs)No release workflow at all
Version bumpManualManual
Tag formatv*.*.*v* (less specific)
Forgejo ReleaseNot createdNot created
ChangelogNot generatedNot generated

Three repos build and deploy Docker images. They follow a similar pattern but with some differences.

Docker Repos Release Flow

WorkflowTriggerPurpose
lint.ymlpush to main, PRsRuff check + format check (Python)
test.ymlpush to main, PRsRuns pytest
build.ymltag v*Builds Docker image with Buildx + cache
deploy.ymlpush to mainTriggers 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.

WorkflowTriggerPurpose
build.ymlpush to main, tag v*, path filtersValidate 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)”
WorkflowTriggerPurpose
build.ymlpush to main, tag v*, path filtersValidate Astro build → build + push Docker image → deploy via webhook → notify

Flow: same pattern as actions-proxy — single unified workflow.

WorkflowTriggerPurpose
build.ymlpush to main, PRs, path filtersnpm ci + Astro build + drift check
deploy.ymlpush to mainTriggers 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.


Test Repo Workflows

WorkflowTriggerPurpose
benchmark.ymldaily cron 6:00 AM + manualMeasures proxy latency + action timing (cold + warm)
integration-test.ymldaily cron 7:00 AM + push to mainTests all proxy tiers (1-3), actions checkout/setup-uv/setup-bun
debug-case-d.ymlpush to main + manualVerifies GITHUB_API_URL patching, regression test
test-latest-ref.ymlpush to main + manualTests @latest ref injection

No publishing, no release. Pure test/benchmark repo. No changes needed.


  1. Single workflow per repo triggered by tag push or workflow_dispatch
  2. CI gate — lint + type-check + tests must pass before publish
  3. Build — all packages built after CI passes
  4. Publish — correct tool per package type (npm or Docker)
  5. Forgejo Release — auto-created with changelog from commits
  6. Deploy — triggered after successful publish (Docker repos)
  7. Docs update — CLI repo updates comparison docs as final step

Proposed Unified Release Flow

Proposed Unified Release Process

  1. Developer pushes tag v*.*.* (or triggers workflow_dispatch)
  2. CI Gate — lint + type-check + unit tests run. If any fail, the release aborts.
  3. Build — all packages are built
  4. Publish — packages published to correct registry:
    • npm repos: bun pm pack + npm publish to Verdaccio (with _auth)
    • Docker repos: docker build + docker push to private registry
  5. Forgejo Release — created automatically with changelog from commits since last tag
  6. Deploy (Docker repos) — webhookd triggered to redeploy
  7. Docs update (CLI only) — comparison docs regenerated and committed to main
  • Sequential, not parallel — each step gates the next. No publish if tests fail.
  • Same auth pattern everywhere_auth with base64 -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
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 deploy

TaskRepoChange
Fix ci.yml authforgejo-proxy-cliReplace _authToken with _auth + base64 -w0
Fix release.yml authforgejo-proxy-cliReplace _authToken with _auth + base64 -w0
Add CI gate to SDK publishforgejo-proxy-api-specAdd type-check + test steps before npm publish
Standardize tag formatforgejo-proxy-api-specChange v* to v*.*.*
TaskRepoChange
Merge release.yml + publish.ymlforgejo-proxy-cliSingle workflow: ci-gate → build → publish → release → docs
Add Forgejo Release creationforgejo-proxy-cliUse Forgejo API to create release with changelog
Add Forgejo Release creationforgejo-proxy-api-specSame
Add changelog generationboth npm reposUse git log between tags or git-cliff
TaskRepoChange
Merge build.yml + deploy.ymlforgejo-proxySingle workflow: lint → test → build → push → deploy
Merge build.yml + deploy.ymlforgejo-proxy-docs-siteSingle workflow: build → deploy
Add Forgejo Release creationall Docker reposCreate release with changelog on tag push
TaskScopeChange
Automated version bumpnpm reposworkflow_dispatch with version input → bumps package.json → tags → triggers release
Automated changelogall reposgit-cliff or git log between tags
Release notes from commitsall reposConventional commit parsing

All workflows that interact with Verdaccio must use this pattern:

Terminal window
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"
} >> ~/.npmrc

Never 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.


SecretValueUsed by
NPM_REGISTRY_URLhttps://npm.registry.hochguertel.worknpm repos (publish, ci)
NPM_REGISTRY_TOKENforgejo-ci htpasswd passwordnpm repos (publish, ci)
CI_FORGEJO_TOKENForgejo API tokenCLI repo (tests, release)
THFG_TOKENthfg API tokenCLI repo
REGISTRY_URLDocker registry URLDocker repos
REGISTRY_USERNAMEDocker registry usernameDocker repos
REGISTRY_PASSWORDDocker registry passwordDocker repos
RepoSecretPurpose
forgejo-proxy-api-specFORGEJO_TOKENSDK generation commit push
actions-proxy-test(various)Test configuration