From 658d076909e14eb0459bcb98aee9aa0472118265 Mon Sep 17 00:00:00 2001 From: Fexiven <48439988+Fexiven@users.noreply.github.com> Date: Tue, 14 Apr 2026 13:03:10 +0200 Subject: [PATCH] feat: add Docker image build and push to GHCR on release (#656) * feat: add Docker image build and push to GHCR on release Add Dockerfile (multi-stage build with node:22-slim) and a new docker job in the release workflow that builds and pushes to ghcr.io when release-please creates a tag. * feat(docker): run as non-root user and add smoke test Run the container as a non-root appuser to reduce blast radius. Add a smoke test step that runs --version before pushing to GHCR. --- .dockerignore | 16 ++++++++++ .github/workflows/release.yml | 55 +++++++++++++++++++++++++++++++++++ Dockerfile | 49 +++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..618de682 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,16 @@ +node_modules +dist +.git +.gitignore +.env +.env.* +!.env.example +coverage +reports +vscode-extension +python +docs +*.md +!README.md +.github +.tsbuildinfo diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b4bcee0b..0d159494 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -86,3 +86,58 @@ jobs: echo "- npm: https://www.npmjs.com/package/@gitlawb/openclaude" echo "- GitHub: https://github.com/Gitlawb/openclaude/releases/tag/${{ needs.release-please.outputs.tag_name }}" } >> "$GITHUB_STEP_SUMMARY" + + docker: + name: Build & Push Docker Image + needs: release-please + if: ${{ needs.release-please.outputs.release_created == 'true' }} + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Checkout release tag + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ needs.release-please.outputs.tag_name }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0 + with: + images: ghcr.io/${{ github.repository }} + tags: | + type=semver,pattern={{version}},value=${{ needs.release-please.outputs.version }} + type=semver,pattern={{major}}.{{minor}},value=${{ needs.release-please.outputs.version }} + type=raw,value=latest + + - name: Build and load locally + uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0 + with: + context: . + load: true + tags: openclaude:smoke + cache-from: type=gha + + - name: Smoke test + run: docker run --rm openclaude:smoke --version + + - name: Build and push + uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..3ac1b09a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,49 @@ +# ---- build stage ---- +FROM node:22-slim AS build + +# Install Bun +RUN npm install -g bun@1.3.11 + +WORKDIR /app + +# Copy dependency manifests first for better layer caching +COPY package.json bun.lock ./ + +# Install all dependencies (including devDependencies for build) +RUN bun install --frozen-lockfile + +# Copy source code +COPY src/ src/ +COPY scripts/ scripts/ +COPY bin/ bin/ +COPY tsconfig.json ./ + +# Build the CLI bundle +RUN bun run build + +# Prune devDependencies +RUN rm -rf node_modules && bun install --frozen-lockfile --production + +# ---- runtime stage ---- +FROM node:22-slim + +WORKDIR /app + +# Copy only what's needed to run +COPY --from=build /app/dist/cli.mjs dist/cli.mjs +COPY --from=build /app/bin/ bin/ +COPY --from=build /app/node_modules/ node_modules/ +COPY --from=build /app/package.json package.json +COPY README.md ./ + +# Install git — many CLI tool operations depend on it +RUN apt-get update && apt-get install -y --no-install-recommends git \ + && rm -rf /var/lib/apt/lists/* + +# Run as non-root user +RUN groupadd --gid 1000 appuser && useradd --uid 1000 --gid appuser --shell /bin/bash --create-home appuser +USER appuser +WORKDIR /home/appuser +ENV HOME=/home/appuser + +ENTRYPOINT ["node", "/app/dist/cli.mjs"]