diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index ea2bd28..f05557f 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -54,6 +54,14 @@ jobs: images: runs-on: ubuntu-latest + # Job-level permissions replace the workflow defaults, so repeat the ones + # this job needs and add packages: write for pushing to GHCR. + permissions: + actions: read + contents: read + id-token: write + packages: write + security-events: write strategy: fail-fast: false name: images/ubuntu @@ -87,3 +95,18 @@ jobs: run: | ${{ github.workspace }}/scripts/push_images.sh \ --tag=ubuntu + + - name: Authenticate to GHCR + if: github.event_name != 'pull_request' + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Push images to GHCR + if: github.event_name != 'pull_request' + run: | + ${{ github.workspace }}/scripts/push_images.sh \ + --tag=ubuntu \ + --registry=ghcr.io diff --git a/README.md b/README.md index b0af13b..840380b 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,22 @@ Each image is published with the following tag variants: > For backward compatibility, these images are also available with the `enterprise-` prefix > (e.g., `codercom/enterprise-base`), but the `example-` prefix is recommended for new deployments. +## Images on GHCR + +Each image is also published to the GitHub Container Registry (GHCR) under +`ghcr.io/coder/:`. The repository is taken from the filename of +the Dockerfile and the tag from its directory. For example, +`base/ubuntu.Dockerfile` is available at `ghcr.io/coder/ubuntu:base`. + +### Available Tags + +| Tag | Example | Description | +| -------------------------- | --------------------------------------------- | -------------------------------------------------- | +| `{image}` | `ghcr.io/coder/ubuntu:base` | Latest Ubuntu version (rolling) | +| `{image}-{version}` | `ghcr.io/coder/ubuntu:base-resolute` | Pinned to a specific Ubuntu release | +| `{image}-{date}` | `ghcr.io/coder/ubuntu:base-20250101` | Snapshot from a specific build date | +| `{image}-{version}-{date}` | `ghcr.io/coder/ubuntu:base-resolute-20250101` | Version-pinned snapshot from a specific build date | + ## Contributing See our [contributing guide](.github/CONTRIBUTING.md). diff --git a/scripts/push_images.sh b/scripts/push_images.sh index 943ec68..2a4ad99 100755 --- a/scripts/push_images.sh +++ b/scripts/push_images.sh @@ -15,11 +15,12 @@ PROJECT_ROOT="$(git rev-parse --show-toplevel)" TAG="ubuntu" DRY_RUN=false QUIET=false +REGISTRY="dockerhub" function usage() { echo "Usage: $(basename "$0") [options]" echo - echo "This script pushes Coder's container images to Docker Hub." + echo "This script pushes Coder's container images to a registry." echo echo "Options:" echo " -h, --help Show this help text and exit" @@ -28,6 +29,8 @@ function usage() { echo " --tag= Select an image tag group to build," echo " e.g. ubuntu)" echo " --quiet Suppress container build output" + echo " --registry= Target registry: dockerhub (default)" + echo " or ghcr.io" exit 1 } @@ -39,7 +42,8 @@ options=$(getopt \ help, \ dry-run, \ tag:, \ - quiet" \ + quiet, \ + registry:" \ --options="h" \ -- "$@") # allow checking the exit code separately here, because we need both @@ -63,6 +67,10 @@ while true; do --quiet) QUIET=true ;; + --registry) + shift + REGISTRY="$1" + ;; -h|--help) usage ;; @@ -88,14 +96,18 @@ if [ $QUIET = true ]; then ) fi +case "$REGISTRY" in +dockerhub | ghcr.io) ;; +*) + echo "Unknown registry: $REGISTRY" >&2 + usage + ;; +esac + date_str=$(date --utc +%Y%m%d) for image in "${IMAGES[@]}"; do image_dir="$PROJECT_ROOT/images/$image" image_file="${TAG}.Dockerfile" - enterprise_image_ref="codercom/enterprise-$image:$TAG" - enterprise_image_ref_date="${enterprise_image_ref}-${date_str}" - example_image_ref="codercom/example-$image:$TAG" - example_image_ref_date="${example_image_ref}-${date_str}" image_path="$image_dir/$image_file" if [ ! -f "$image_path" ]; then @@ -107,6 +119,24 @@ for image in "${IMAGES[@]}"; do build_id=$(cat "build_${image}.json" | jq -r .\[\"depot.build\"\].buildID) + image_ubuntu_version="$(ubuntu_version_for "$image")" + + if [ "$REGISTRY" = "ghcr.io" ]; then + # GHCR images use the distro as the repository and the image name as + # the tag, e.g. ghcr.io/coder/ubuntu:base. See coder/images#291. + ghcr_ref="ghcr.io/coder/${TAG}:${image}" + run_trace $DRY_RUN depot push --project "gb3p8xrshk" --tag "$ghcr_ref" "$build_id" + run_trace $DRY_RUN depot push --project "gb3p8xrshk" --tag "${ghcr_ref}-${date_str}" "$build_id" + run_trace $DRY_RUN depot push --project "gb3p8xrshk" --tag "${ghcr_ref}-${image_ubuntu_version}" "$build_id" + run_trace $DRY_RUN depot push --project "gb3p8xrshk" --tag "${ghcr_ref}-${image_ubuntu_version}-${date_str}" "$build_id" + continue + fi + + enterprise_image_ref="codercom/enterprise-$image:$TAG" + enterprise_image_ref_date="${enterprise_image_ref}-${date_str}" + example_image_ref="codercom/example-$image:$TAG" + example_image_ref_date="${example_image_ref}-${date_str}" + # Push example images (primary) run_trace $DRY_RUN depot push --project "gb3p8xrshk" --tag "$example_image_ref" "$build_id" run_trace $DRY_RUN depot push --project "gb3p8xrshk" --tag "$example_image_ref_date" "$build_id" @@ -121,7 +151,6 @@ for image in "${IMAGES[@]}"; do # release. The version comes from images.sh (the single source of # truth) via ubuntu_version_for, which honours per-image overrides so # each image is tagged with the release it is actually built from. - image_ubuntu_version="$(ubuntu_version_for "$image")" for prefix in "example" "enterprise"; do run_trace $DRY_RUN depot push --project "gb3p8xrshk" --tag "codercom/${prefix}-${image}:${TAG}-${image_ubuntu_version}" "$build_id" run_trace $DRY_RUN depot push --project "gb3p8xrshk" --tag "codercom/${prefix}-${image}:${TAG}-${image_ubuntu_version}-${date_str}" "$build_id"