diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 24fde92..5e7c85c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -31,7 +31,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} # Use .devcontainer from THIS repo for building and testing - - name: Check, Build, Test + - name: Check, Build, Test, Publish uses: devcontainers/ci@v0.3 with: # The .devcontainer is never published as pre-built container. @@ -50,15 +50,11 @@ jobs: # Test ./scripts/test.sh - # Upload devcontainer from src/s-core-devcontainer - - name: Publish - uses: devcontainers/ci@v0.3 - if: github.ref == 'refs/heads/main' - with: - # We do not use the push feature of devcontainers/ci here, since that would push the wrong container. - # Instead, we use the publish script which pushes the correct container (residing in src/s-core-devcontainer). - push: "never" - runCmd: | - # manually login to ghcr.io for publishing - echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin - ./scripts/publish.sh "main" + # Optionally: Publish + # We do not use the push feature of devcontainers/ci here, since that would push the wrong container. + # Instead, we use the publish script which pushes the correct container (residing in src/s-core-devcontainer). + if [ "${{ github.ref }}" = "refs/heads/main" ]; then + # manually login to ghcr.io for publishing + echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + ./scripts/publish.sh "main" + fi diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 5cc6630..a3970ab 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -26,7 +26,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} # Use .devcontainer from THIS repo for building and testing - - name: Check, Build, Test + - name: Check, Build, Test, Publish uses: devcontainers/ci@v0.3 with: # The .devcontainer is never published as pre-built container. @@ -45,15 +45,12 @@ jobs: # Test ./scripts/test.sh - # Upload devcontainer from src/s-core-devcontainer - - name: Publish - uses: devcontainers/ci@v0.3 - with: - # We do not use the push feature of devcontainers/ci here, since that would push the wrong container. - # Instead, we use the publish script which pushes the correct container (residing in src/s-core-devcontainer). - push: "never" - runCmd: | + # Publish + # We do not use the push feature of devcontainers/ci here, since that would push the wrong container. + # Instead, we use the publish script which pushes the correct container (residing in src/s-core-devcontainer). + # manually login to ghcr.io for publishing echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + # Note: "${{ github.ref_name }}" will be the tag name, e.g., "1.0.0" ./scripts/publish.sh "${{ github.ref_name }}" "latest" diff --git a/scripts/build.sh b/scripts/build.sh index ac68511..dc410ff 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -1,5 +1,37 @@ #!/usr/bin/env bash set -euxo pipefail -devcontainer build --platform linux/arm64 --workspace-folder src/s-core-devcontainer --image-name ghcr.io/eclipse-score/devcontainer --cache-from ghcr.io/eclipse-score/devcontainer -devcontainer build --platform linux/amd64 --workspace-folder src/s-core-devcontainer --image-name ghcr.io/eclipse-score/devcontainer --cache-from ghcr.io/eclipse-score/devcontainer +if [ "$#" -eq 0 ]; then + echo "Error: At least one parameter (label) must be provided." + exit 1 +fi + +LABELS=() +for LABEL in "$@"; do + LABELS+=("${LABEL}") +done + +# Define target architectures +ARCHITECTURES=("amd64" "arm64") + +# Build for each architecture, creating all requested tags +for ARCH in "${ARCHITECTURES[@]}"; do + echo "Building all labels (${LABELS[@]}) for architecture: ${ARCH}" + + # Prepare image names with tags (each tag includes a label and an architecture) + IMAGES=() + for LABEL in "${LABELS[@]}"; do + IMAGES+=("--image-name \"ghcr.io/eclipse-score/devcontainer:${LABEL}-${ARCH}\"") + done + + # Prepare devcontainer build command + DEVCONTAINER_CALL="devcontainer build --workspace-folder src/s-core-devcontainer --cache-from ghcr.io/eclipse-score/devcontainer" + + # Append image names to the build command + for IMAGE in "${IMAGES[@]}"; do + DEVCONTAINER_CALL+=" $IMAGE" + done + + # Execute the build for the specific architecture + eval "$DEVCONTAINER_CALL --platform linux/${ARCH}" +done diff --git a/scripts/publish.sh b/scripts/publish.sh index 4b8d049..6a7586c 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -1,24 +1,28 @@ #!/usr/bin/env bash set -euxo pipefail +if [ "$#" -eq 0 ]; then + echo "Error: At least one parameter (label) must be provided." + exit 1 +fi + +LABELS=() +for LABEL in "$@"; do + LABELS+=("${LABEL}") +done + # Define target architectures ARCHITECTURES=("amd64" "arm64") -# Prepare manifest creation command -MANIFEST_MAIN_CALL="docker manifest create ghcr.io/eclipse-score/devcontainer:main" - +# Build and push for each architecture, creating all requested tags for ARCH in "${ARCHITECTURES[@]}"; do - echo "Building for architecture: ${ARCH}" - - # Prepare image names - they should include the architectures and also tags if provided - IMAGES=("--image-name \"ghcr.io/eclipse-score/devcontainer:main-${ARCH}\"") - # Handle additional tags if provided - if [ "$#" -gt 0 ]; then - IMAGES=() - for arg in "$@"; do - IMAGES+=("--image-name \"ghcr.io/eclipse-score/devcontainer:${arg}\"") - done - fi + echo "Building all tags (${LABELS[@]}) for architecture: ${ARCH}" + + # Prepare image names with tags (each tag includes a label and an architecture) + IMAGES=() + for LABEL in "${LABELS[@]}"; do + IMAGES+=("--image-name \"ghcr.io/eclipse-score/devcontainer:${LABEL}-${ARCH}\"") + done # Prepare devcontainer build command DEVCONTAINER_CALL="devcontainer build --push --workspace-folder src/s-core-devcontainer --cache-from ghcr.io/eclipse-score/devcontainer" @@ -28,25 +32,19 @@ for ARCH in "${ARCHITECTURES[@]}"; do DEVCONTAINER_CALL+=" $IMAGE" done - # Execute the build for the specific architecture + # Execute the build and push all tags for the specific architecture eval "$DEVCONTAINER_CALL --platform linux/${ARCH}" - - # Append the architecture-specific image to the manifest creation command (those need to be merged into *one* manifest) - MANIFEST_MAIN_CALL+=" ghcr.io/eclipse-score/devcontainer:main-${ARCH}" done -# Create and push the manifest for 'main' tag -eval "$MANIFEST_MAIN_CALL" -docker manifest push ghcr.io/eclipse-score/devcontainer:main - -# If additional tags are provided: merge metadata and push those as well -if [ "$#" -gt 0 ]; then - for arg in "$@"; do - MANIFEST_TAG_CALL="docker manifest create ghcr.io/eclipse-score/devcontainer:${arg}" - for ARCH in "${ARCHITECTURES[@]}"; do - MANIFEST_TAG_CALL+=" ghcr.io/eclipse-score/devcontainer:${arg}-${ARCH}" - done - eval "$MANIFEST_TAG_CALL" - docker manifest push ghcr.io/eclipse-score/devcontainer:${arg} +# Create and push the merged multiarch manifest for each tag; each tag combines all architecture-specific tags into one tag +for LABEL in "${LABELS[@]}"; do + echo "Merging all architectures (${ARCHITECTURES[@]}) into single tag: ${LABEL}" + + MANIFEST_MERGE_CALL="docker buildx imagetools create -t ghcr.io/eclipse-score/devcontainer:${LABEL}" + + for ARCH in "${ARCHITECTURES[@]}"; do + MANIFEST_MERGE_CALL+=" ghcr.io/eclipse-score/devcontainer:${LABEL}-${ARCH}" done -fi + + eval "$MANIFEST_MERGE_CALL" +done