diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5e7c85c..0b2e492 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -11,9 +11,9 @@ concurrency: cancel-in-progress: true jobs: - build: - name: 'Check, Build, Test, Publish DevContainer' - runs-on: ubuntu-latest + build-x86_64: + name: 'Check, Build, Test, Publish DevContainer (x86_64)' + runs-on: ubuntu-24.04 permissions: contents: read packages: write @@ -38,6 +38,8 @@ jobs: # We want to only use it for building and testing the actual container, which resides in src/s-core-devcontainer. push: "never" runCmd: | + set -eux pipefail + # Check pre-commit run --show-diff-on-failure --color=always --all-files || exit -1 @@ -45,7 +47,7 @@ jobs: ./scripts/create_builder.sh # Build - ./scripts/build.sh + ./scripts/build.sh "main" # Test ./scripts/test.sh @@ -58,3 +60,91 @@ jobs: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin ./scripts/publish.sh "main" fi + + build-arm64: + name: 'Check, Build, Test, Publish DevContainer (arm64)' + runs-on: ubuntu-24.04-arm + permissions: + contents: read + packages: write + id-token: write + + steps: + - name: Checkout (GitHub) + uses: actions/checkout@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Use .devcontainer from THIS repo for building and testing + - name: Check, Build, Test, Publish + uses: devcontainers/ci@v0.3 + with: + # The .devcontainer is never published as pre-built container. + # We want to only use it for building and testing the actual container, which resides in src/s-core-devcontainer. + push: "never" + runCmd: | + set -eux pipefail + + # Check + pre-commit run --show-diff-on-failure --color=always --all-files || exit -1 + + # Create builder for multi-arch builds + ./scripts/create_builder.sh + + # Build + ./scripts/build-arm.sh "main" + + # Test + ./scripts/test.sh + + # 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-arm.sh "main" + fi + merge: + name: 'Merge Labels' + needs: ["build-x86_64", "build-arm64"] + runs-on: ubuntu-24.04 + permissions: + contents: read + packages: write + id-token: write + + steps: + - name: Checkout (GitHub) + uses: actions/checkout@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Use .devcontainer from THIS repo for building and testing + - name: Merge + uses: devcontainers/ci@v0.3 + with: + # The .devcontainer is never published as pre-built container. + # We want to only use it for building and testing the actual container, which resides in src/s-core-devcontainer. + push: "never" + runCmd: | + set -eux pipefail + + # Optionally: Merge + # 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/merge.sh "main" + fi diff --git a/README.md b/README.md index a1f10fe..88c3fa1 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ It should contain the following: ````json { "name": "eclipse-s-core", - "image": "ghcr.io/eclipse-score/devcontainer:" + "image": "ghcr.io/opajonk/eclipse-score_devcontainer:" } ```` @@ -108,7 +108,7 @@ They are used by the CI, but especially the build and test scripts can be run al ````console $ ./scripts/build.sh [... build output..] -{"outcome":"success","imageName":["ghcr.io/eclipse-score/devcontainer"]} +{"outcome":"success","imageName":["ghcr.io/opajonk/eclipse-score_devcontainer"]} $ ./scripts/test.sh [... test output...] @@ -133,9 +133,9 @@ So in order to execute `S-CORE DevContainer` on your host (and test it as part o Concretely, this can be done as follows: -* Run `docker save "ghcr.io/eclipse-score/devcontainer" > export.img` in `Development Container A`. +* Run `docker save "ghcr.io/opajonk/eclipse-score_devcontainer" > export.img` in `Development Container A`. * On your **host machine** (!!), open a console and run `docker load < /path/to/export.img`. -* In the working copy of the targeted S-CORE module, edit the file `.devcontainer/devcontainer.json` and change the `"image": "..."` entry to `"image": "ghcr.io/eclipse-score/devcontainer:latest"` (if not already set like this). +* In the working copy of the targeted S-CORE module, edit the file `.devcontainer/devcontainer.json` and change the `"image": "..."` entry to `"image": "ghcr.io/opajonk/eclipse-score_devcontainer:latest"` (if not already set like this). The Visual Studio Code instance related to the targeted S-CORE module will now ask you to rebuild the DevContainer. If not, press Ctrl + Shift + p and run from there "Dev Containers: Rebuilt Container Without Cache". Do so, and you have a running instance of `S-CORE DevContainer` related to the targeted S-CORE module. diff --git a/scripts/build-arm.sh b/scripts/build-arm.sh new file mode 100755 index 0000000..4694d9c --- /dev/null +++ b/scripts/build-arm.sh @@ -0,0 +1,37 @@ +#!/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=("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/opajonk/eclipse-score_devcontainer:${LABEL}-${ARCH}\"") + done + + # Prepare devcontainer build command + DEVCONTAINER_CALL="devcontainer build --workspace-folder src/s-core-devcontainer --cache-from ghcr.io/opajonk/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/build.sh b/scripts/build.sh index dc410ff..3956fdc 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -12,7 +12,7 @@ for LABEL in "$@"; do done # Define target architectures -ARCHITECTURES=("amd64" "arm64") +ARCHITECTURES=("amd64") # Build for each architecture, creating all requested tags for ARCH in "${ARCHITECTURES[@]}"; do @@ -21,11 +21,11 @@ for ARCH in "${ARCHITECTURES[@]}"; do # 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}\"") + IMAGES+=("--image-name \"ghcr.io/opajonk/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" + DEVCONTAINER_CALL="devcontainer build --workspace-folder src/s-core-devcontainer --cache-from ghcr.io/opajonk/eclipse-score_devcontainer" # Append image names to the build command for IMAGE in "${IMAGES[@]}"; do diff --git a/scripts/merge.sh b/scripts/merge.sh new file mode 100755 index 0000000..770798f --- /dev/null +++ b/scripts/merge.sh @@ -0,0 +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") + +# 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/opajonk/eclipse-score_devcontainer:${LABEL}" + + for ARCH in "${ARCHITECTURES[@]}"; do + MANIFEST_MERGE_CALL+=" ghcr.io/opajonk/eclipse-score_devcontainer:${LABEL}-${ARCH}" + done + + eval "$MANIFEST_MERGE_CALL" +done diff --git a/scripts/publish-arm.sh b/scripts/publish-arm.sh new file mode 100755 index 0000000..0fd90b0 --- /dev/null +++ b/scripts/publish-arm.sh @@ -0,0 +1,50 @@ +#!/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=("arm64") + +# Build and push for each architecture, creating all requested tags +for ARCH in "${ARCHITECTURES[@]}"; do + 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/opajonk/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/opajonk/eclipse-score_devcontainer" + + # Append image names to the build command + for IMAGE in "${IMAGES[@]}"; do + DEVCONTAINER_CALL+=" $IMAGE" + done + + # Execute the build and push all tags for the specific architecture + eval "$DEVCONTAINER_CALL --platform linux/${ARCH}" +done + +# 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/opajonk/eclipse-score_devcontainer:${LABEL}" + + for ARCH in "${ARCHITECTURES[@]}"; do + MANIFEST_MERGE_CALL+=" ghcr.io/opajonk/eclipse-score_devcontainer:${LABEL}-${ARCH}" + done + + eval "$MANIFEST_MERGE_CALL" +done diff --git a/scripts/publish.sh b/scripts/publish.sh index 6a7586c..50faa12 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -12,7 +12,7 @@ for LABEL in "$@"; do done # Define target architectures -ARCHITECTURES=("amd64" "arm64") +ARCHITECTURES=("amd64") # Build and push for each architecture, creating all requested tags for ARCH in "${ARCHITECTURES[@]}"; do @@ -21,11 +21,11 @@ for ARCH in "${ARCHITECTURES[@]}"; do # 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}\"") + IMAGES+=("--image-name \"ghcr.io/opajonk/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" + DEVCONTAINER_CALL="devcontainer build --push --workspace-folder src/s-core-devcontainer --cache-from ghcr.io/opajonk/eclipse-score_devcontainer" # Append image names to the build command for IMAGE in "${IMAGES[@]}"; do @@ -40,10 +40,10 @@ done 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}" + MANIFEST_MERGE_CALL="docker buildx imagetools create -t ghcr.io/opajonk/eclipse-score_devcontainer:${LABEL}" for ARCH in "${ARCHITECTURES[@]}"; do - MANIFEST_MERGE_CALL+=" ghcr.io/eclipse-score/devcontainer:${LABEL}-${ARCH}" + MANIFEST_MERGE_CALL+=" ghcr.io/opajonk/eclipse-score_devcontainer:${LABEL}-${ARCH}" done eval "$MANIFEST_MERGE_CALL" diff --git a/src/s-core-devcontainer/manifest.json b/src/s-core-devcontainer/manifest.json index 59a1e1e..79fe135 100644 --- a/src/s-core-devcontainer/manifest.json +++ b/src/s-core-devcontainer/manifest.json @@ -1,5 +1,5 @@ { "version": "1.0.0", - "image_name": "ghcr.io/eclipse-score/devcontainer", + "image_name": "ghcr.io/opajonk/eclipse-score_devcontainer", "description": "Develop the Eclipse S-CORE project" }