Skip to content

9.6.7__lts-22.44__2.11.0.0 #96

9.6.7__lts-22.44__2.11.0.0

9.6.7__lts-22.44__2.11.0.0 #96

# https://docs.github.com/en/actions/use-cases-and-examples/publishing-packages/publishing-docker-images
#
name: Build and Push
on:
push:
branches: ['*']
tags: ['*']
workflow_dispatch:
# Prevent concurrent runs for the same ref to avoid race conditions
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
env:
REGISTRY: ghcr.io
IMAGE_NAME: lcamel/haskell-devcontainer
IMAGE_NAME_TEMP: lcamel/haskell-devcontainer-temp
jobs:
# Validate ref name before starting any builds
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4 # Need script from repo
- name: Validate ref name
run: ./.github/scripts/validate-ref.sh "${{ github.ref_name }}" "${{ github.ref_type }}"
# Build and push images for each architecture
build:
needs: validate
strategy:
matrix:
include:
- platform: linux/amd64
runner: ubuntu-24.04
arch: amd64
- platform: linux/arm64
runner: ubuntu-24.04-arm
arch: arm64
runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- id: build
uses: docker/build-push-action@v6
with:
context: docker/
push: true
platforms: ${{ matrix.platform }}
pull: true
no-cache: ${{ github.ref_type == 'tag' }}
cache-from: type=gha,scope=${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=${{ matrix.arch }}
outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME_TEMP }},push-by-digest=true,name-canonical=true
- name: Export digest
run: |
mkdir -p /tmp/digests
echo "${{ steps.build.outputs.digest }}" > /tmp/digests/${{ matrix.arch }}
- uses: actions/upload-artifact@v4
with:
name: digest-${{ matrix.arch }}
path: /tmp/digests/${{ matrix.arch }}
# Create and push a single multi-architecture image (manifest)
merge:
needs: build
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/download-artifact@v4
with:
path: /tmp/digests
pattern: digest-*
merge-multiple: true
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create and push multi-arch manifest to temp package
run: |
AMD64_DIGEST=$(cat /tmp/digests/amd64)
ARM64_DIGEST=$(cat /tmp/digests/arm64)
IMAGE_TAG="${{ github.ref_name }}"
echo "Image Tag: ${IMAGE_TAG}"
docker buildx imagetools create \
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_TEMP }}:${IMAGE_TAG} \
--annotation "index:org.opencontainers.image.source=https://github.com/${{ github.repository }}" \
--annotation "index:org.opencontainers.image.description=Ready-to-use Haskell environment with GHC, Stackage, and HLS" \
--annotation "index:org.opencontainers.image.licenses=MIT" \
--annotation "index:org.opencontainers.image.revision=${{ github.sha }}" \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME_TEMP }}@${AMD64_DIGEST} \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME_TEMP }}@${ARM64_DIGEST}
# Run smoke tests against the multi-arch image
test:
needs: merge
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-24.04
platform: linux/amd64
- runner: ubuntu-24.04-arm
platform: linux/arm64
runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: read
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Run smoke test
run: |
FULL_IMAGE_NAME="${{ env.REGISTRY }}/${{ env.IMAGE_NAME_TEMP }}:${{ github.ref_name }}"
echo "Testing ${FULL_IMAGE_NAME} on ${{ matrix.platform }}..."
docker pull "${FULL_IMAGE_NAME}"
# GITHUB_TOKEN: "stack new" uses GitHub API to fetch templates, may hit rate limits without auth
# See: https://github.com/commercialhaskell/stack/issues/6034
docker run --rm \
--platform ${{ matrix.platform }} \
-e GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} \
-v ${{ github.workspace }}/.github/scripts/smoke-test.sh:/tmp/smoke-test.sh \
-v ${{ github.workspace }}/docker/haskell-versions.env:/tmp/haskell-versions.env \
-w /tmp \
"${FULL_IMAGE_NAME}" \
bash -li /tmp/smoke-test.sh
# Promote the image to production upon successful tests (tags only; branch builds remain in temp)
promote:
needs: [merge, test]
if: github.ref_type == 'tag'
runs-on: ubuntu-latest
permissions:
packages: write
steps:
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Promote image from temp package to production
run: |
IMAGE_TAG="${{ github.ref_name }}"
echo "Promoting from temp package to production..."
echo "Source: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_TEMP }}:${IMAGE_TAG}"
echo "Target: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${IMAGE_TAG}"
docker buildx imagetools create \
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${IMAGE_TAG} \
--annotation "index:org.opencontainers.image.source=https://github.com/${{ github.repository }}" \
--annotation "index:org.opencontainers.image.description=Ready-to-use Haskell environment with GHC, Stackage, and HLS" \
--annotation "index:org.opencontainers.image.licenses=MIT" \
--annotation "index:org.opencontainers.image.revision=${{ github.sha }}" \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME_TEMP }}:${IMAGE_TAG}
echo "Successfully promoted to ${IMAGE_TAG}"
echo "Note: Temp package versions will be cleaned up by scheduled cleanup workflow"
# Update floating tags (latest, ghc-X.Y.Z, stackage-lts-X.Y)
# This runs sequentially to avoid race conditions.
tag-floating:
needs: promote
runs-on: ubuntu-latest
# Ensure sequential execution to prevent race conditions.
concurrency:
group: update-floating-tags-lock
cancel-in-progress: false
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4 # Need checkout to get the script
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Update Floating Tags
run: |
TARGET_TAG="${{ github.ref_name }}"
echo "Target Tag: $TARGET_TAG"
# 1. Fetch all existing tags from Registry
echo "Fetching all tags from registry..."
REMOTE_TAGS=$(docker run --rm \
-v $HOME/.docker:/root/.docker \
gcr.io/go-containerregistry/crane:latest \
ls ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }})
# 2. Calculate which tags need to be updated
TAGS_TO_UPDATE=$(echo "$REMOTE_TAGS" | ./.github/scripts/calculate-floating-tags.sh "$TARGET_TAG")
echo "Tags to update:"
echo "$TAGS_TO_UPDATE"
# 3. Apply updates
if [ -n "$TAGS_TO_UPDATE" ]; then
TAG_ARGS=""
for tag in $TAGS_TO_UPDATE; do
echo "Queueing update for: $tag"
TAG_ARGS="$TAG_ARGS -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:$tag"
done
echo "Applying updates..."
docker buildx imagetools create $TAG_ARGS \
--annotation "index:org.opencontainers.image.source=https://github.com/${{ github.repository }}" \
--annotation "index:org.opencontainers.image.description=Ready-to-use Haskell environment with GHC, Stackage, and HLS" \
--annotation "index:org.opencontainers.image.licenses=MIT" \
--annotation "index:org.opencontainers.image.revision=${{ github.sha }}" \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:$TARGET_TAG
else
echo "No floating tags to update."
fi