diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..d55ceb5 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,72 @@ +name: docker-publish + +on: + push: + branches: [main] + release: + types: [published] + workflow_call: + inputs: + tag: + description: Published release tag to build + required: true + type: string + +permissions: + contents: read + +concurrency: + group: docker-publish-${{ inputs.tag || github.event.release.tag_name || github.ref }} + cancel-in-progress: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + +jobs: + publish: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ (inputs.tag && format('refs/tags/{0}', inputs.tag)) || (github.event.release.tag_name && format('refs/tags/{0}', github.event.release.tag_name)) || github.sha }} + persist-credentials: false + - name: Read build metadata + id: build + run: | + { + echo "version=$(cat VERSION)" + echo "commit=$(git rev-parse HEAD)" + echo "date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" + } >> "$GITHUB_OUTPUT" + - uses: docker/setup-buildx-action@v3 + - name: Generate image tags and labels + id: meta + uses: docker/metadata-action@v5 + with: + context: workflow + images: ghcr.io/${{ github.repository }} + flavor: latest=false + tags: | + type=raw,value=${{ inputs.tag || github.event.release.tag_name || 'latest' }} + type=sha,format=long + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push image + uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/amd64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + VERSION=${{ steps.build.outputs.version }} + COMMIT=${{ steps.build.outputs.commit }} + BUILD_DATE=${{ steps.build.outputs.date }} + DIRTY=false + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a299577..24e296b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -69,3 +69,13 @@ jobs: --title "$RELEASE_TAG" --generate-notes \ ./*.tar.gz SHA256SUMS gh release edit "$RELEASE_TAG" --draft=false + + # GITHUB_TOKEN-created releases do not trigger release-event workflows. + image: + needs: publish + permissions: + contents: read + packages: write + uses: ./.github/workflows/docker-publish.yml + with: + tag: ${{ github.ref_name }} diff --git a/README.md b/README.md index 70ad18c..ae6556b 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,19 @@ The version endpoint confirms that the proxy is running; it does not contact you gateway. Next, configure the client you use below. Keep your existing API credentials: the proxy forwards them to the gateway. +Prebuilt Linux amd64 images are published to GitHub Container Registry: + +```bash +docker pull ghcr.io/xllent-ai/steady-proxy:latest +``` + +`latest` follows `main`; release tags such as `v0.1.1` select a release instead. +To use a prebuilt image with the example Compose configuration, change its +`image:` to `ghcr.io/xllent-ai/steady-proxy:latest`, remove the `build:` block, then +run `docker compose pull && docker compose up -d`. See +[container publishing](docs/RELEASING.md#container-images) for tagging and registry +access details. + ### Claude Code Merge this `env` block into `~/.claude/settings.json`, keeping your other settings diff --git a/docs/RELEASING.md b/docs/RELEASING.md index ac92983..178d420 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -64,10 +64,33 @@ The workflow then: 4. Refuses to proceed if any release already uses the tag, including a draft. 5. Creates a draft with generated GitHub release notes and all five assets, then publishes it after uploading succeeds. +6. Calls the container publishing workflow to build the tagged source and push + `ghcr.io/xllent-ai/steady-proxy:v0.1.1` (using the actual release tag). -The workflow uses GitHub's automatic token; no personal token is needed. It does -not upload container images. Review the published release notes for any migration -or configuration guidance that commit and pull-request summaries do not explain. +The workflows use GitHub's automatic token; no personal token is needed. The +container job has `packages: write` permission. Review the published release notes +for any migration or configuration guidance that commit and pull-request summaries +do not explain. + +## Container images + +The [container workflow](../.github/workflows/docker-publish.yml) publishes Linux +amd64 images to `ghcr.io/xllent-ai/steady-proxy`: + +- Every push to `main` updates `latest` from that push's commit. +- Publishing a GitHub release builds its tag and publishes an image with that + tag, such as `v0.1.1`. Release images never move `latest`. +- Both paths also publish `sha-` and stamp the executable with + `VERSION`, the checked-out commit, build time, and clean working-tree status. + +The release workflow calls the container workflow directly because releases +created with `GITHUB_TOKEN` do not trigger another workflow via a `release` event. +Manually published releases use the `release: published` trigger instead. + +After the first image is published, check the package's visibility in GitHub's +package settings; change it to public to allow unauthenticated pulls. The image's +source label links it to this repository. If a package with the same name already +exists, grant this repository Actions access to it before publishing. ## If a release fails @@ -76,6 +99,9 @@ failure creates no GitHub release. An upload or publication failure can leave a draft; inspect it and delete that draft before rerunning the failed jobs. The workflow will not overwrite its assets automatically. +If only the container job fails after the release is published, rerun that failed +job; do not recreate the release. It rebuilds the same release tag. + If a published release needs a fix, use a new version and tag. Keep existing published tags and assets unchanged so downloads continue to identify the same code and checksums.