diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..125cd8d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +.git +.github +.tools +**/node_modules +bin +dist +gog +gog.exe +*.test +coverage* +.DS_Store diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..4408c4d --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,96 @@ +name: docker + +on: + pull_request: + paths: + - ".dockerignore" + - ".github/workflows/docker.yml" + - "Dockerfile" + - "cmd/**" + - "go.mod" + - "go.sum" + - "internal/**" + push: + tags: + - "v*" + workflow_dispatch: + inputs: + tag: + description: "Optional release tag to publish (e.g. v0.14.0)" + required: false + type: string + +permissions: + contents: read + packages: write + +jobs: + image: + runs-on: ubuntu-latest + steps: + - name: Validate manual tag + if: ${{ github.event_name == 'workflow_dispatch' && inputs.tag != '' }} + env: + RELEASE_TAG: ${{ inputs.tag }} + run: | + if ! echo "$RELEASE_TAG" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$'; then + echo "::error::Invalid tag format: $RELEASE_TAG" + exit 1 + fi + + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + ref: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref }} + + - name: Docker metadata + id: meta + uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 + with: + images: ghcr.io/steipete/gogcli + tags: | + type=ref,event=pr + type=ref,event=tag + type=raw,value=${{ inputs.tag }},enable=${{ github.event_name == 'workflow_dispatch' && inputs.tag != '' }} + type=raw,value=latest,enable=${{ (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref_name, '-')) || (github.event_name == 'workflow_dispatch' && inputs.tag != '' && !contains(inputs.tag, '-')) }} + + - name: Build metadata + id: build-meta + env: + RELEASE_TAG: ${{ inputs.tag }} + run: | + version="dev" + if [[ -n "$RELEASE_TAG" ]]; then + version="$RELEASE_TAG" + elif [[ "$GITHUB_REF_TYPE" == "tag" ]]; then + version="$GITHUB_REF_NAME" + fi + { + echo "version=$version" + echo "commit=${GITHUB_SHA::12}" + echo "date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" + } >> "$GITHUB_OUTPUT" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 + + - name: Login to GHCR + if: ${{ startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'workflow_dispatch' && inputs.tag != '') }} + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build image + uses: docker/build-push-action@ee4ca427a2f43b6a16632044ca514c076267da23 # v6.19.0 + with: + context: . + push: ${{ startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'workflow_dispatch' && inputs.tag != '') }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + VERSION=${{ steps.build-meta.outputs.version }} + COMMIT=${{ steps.build-meta.outputs.commit }} + DATE=${{ steps.build-meta.outputs.date }} diff --git a/CHANGELOG.md b/CHANGELOG.md index aa2223c..a2821a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 0.15.0 - Unreleased ### Added +- Install: publish a GHCR Docker image for release tags, with a non-root runtime image and file-keyring docs for container automation. (#539, #444) — thanks @HuckOps and @rdehuyss. - Agent safety: add baked safety-profile builds for fail-closed agent binaries, with `agent-safe`, `readonly`, and `full` profiles, filtered help/schema output, docs, and build tooling. (#366, #239) — thanks @drewburchfield. - Calendar: add `--with-meet` to `calendar update` for adding Google Meet conferencing to existing events. (#538) — thanks @alexisperumal. - Calendar: add `calendar move` / `calendar transfer` to move an event to another calendar and change its organizer. (#448) — thanks @markusbkoch. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..81e0839 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,40 @@ +# syntax=docker/dockerfile:1.7 + +ARG GO_VERSION=1.26.2 +ARG ALPINE_VERSION=3.22 + +FROM golang:${GO_VERSION}-alpine AS build + +RUN apk add --no-cache ca-certificates git tzdata + +WORKDIR /src +COPY go.mod go.sum ./ +RUN go mod download + +COPY . . + +ARG VERSION=dev +ARG COMMIT=unknown +ARG DATE=unknown + +RUN CGO_ENABLED=0 GOOS=linux go build -trimpath \ + -ldflags="-s -w -X github.com/steipete/gogcli/internal/cmd.version=${VERSION} -X github.com/steipete/gogcli/internal/cmd.commit=${COMMIT} -X github.com/steipete/gogcli/internal/cmd.date=${DATE}" \ + -o /out/gog ./cmd/gog + +FROM alpine:${ALPINE_VERSION} + +LABEL org.opencontainers.image.source="https://github.com/steipete/gogcli" +LABEL org.opencontainers.image.description="Google services CLI for terminal automation" +LABEL org.opencontainers.image.licenses="MIT" + +RUN apk add --no-cache ca-certificates tzdata \ + && adduser -D -u 10001 -h /home/gog gog + +ENV HOME=/home/gog +WORKDIR /home/gog + +COPY --from=build /out/gog /usr/local/bin/gog + +USER gog +ENTRYPOINT ["gog"] +CMD ["--help"] diff --git a/README.md b/README.md index 224db64..05f5ff1 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,35 @@ $env:Path = "$env:Path;$dir" gog --version ``` +### Docker + +Release images are published to GitHub Container Registry: + +```bash +docker run --rm ghcr.io/steipete/gogcli:latest version +``` + +For authenticated automation in a container, mount a persistent config directory and use the encrypted file keyring: + +```bash +docker volume create gogcli-config +docker run --rm -it \ + -e GOG_KEYRING_BACKEND=file \ + -e GOG_KEYRING_PASSWORD='change-me' \ + -v gogcli-config:/home/gog/.config/gogcli \ + ghcr.io/steipete/gogcli:latest auth add you@gmail.com --services gmail,calendar,drive +``` + +Subsequent runs can reuse the same volume and password: + +```bash +docker run --rm \ + -e GOG_KEYRING_BACKEND=file \ + -e GOG_KEYRING_PASSWORD='change-me' \ + -v gogcli-config:/home/gog/.config/gogcli \ + ghcr.io/steipete/gogcli:latest gmail labels list --account you@gmail.com +``` + ### Build from Source ```bash