diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 5e7c85c..d9f994a 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -12,8 +12,19 @@ concurrency:
jobs:
build:
- name: 'Check, Build, Test, Publish DevContainer'
- runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ os: [arm64, amd64]
+ include:
+ - os: amd64
+ name: 'DevContainer (amd64)'
+ runner: ubuntu-24.04
+ - os: arm64
+ name: 'DevContainer (arm64)'
+ runner: ubuntu-24.04-arm
+
+ name: '${{ matrix.name }}'
+ runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
@@ -38,6 +49,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 +58,7 @@ jobs:
./scripts/create_builder.sh
# Build
- ./scripts/build.sh
+ ./scripts/build.sh --${{ matrix.os }} "main"
# Test
./scripts/test.sh
@@ -56,5 +69,43 @@ jobs:
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"
+ ./scripts/publish.sh --${{ matrix.os }} "main"
fi
+
+ merge:
+ name: 'Merge Labels (main only)'
+ needs: ["build"]
+ runs-on: ubuntu-24.04
+ if: github.ref == 'refs/heads/main'
+ 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
+
+ # 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).
+ # manually login to ghcr.io for publishing
+ echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
+ ./scripts/merge.sh "main"
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index a3970ab..4f93c6a 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -4,11 +4,21 @@ on:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+'
-
jobs:
build:
- name: 'Check, Build, Test, Publish DevContainer'
- runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ os: [arm64, amd64]
+ include:
+ - os: amd64
+ name: 'DevContainer (amd64)'
+ runner: ubuntu-24.04
+ - os: arm64
+ name: 'DevContainer (arm64)'
+ runner: ubuntu-24.04-arm
+
+ name: '${{ matrix.name }}'
+ runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
@@ -33,6 +43,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
@@ -40,7 +52,7 @@ jobs:
./scripts/create_builder.sh
# Build
- ./scripts/build.sh
+ ./scripts/build.sh --${{ matrix.os }} "${{ github.ref_name }}" "latest"
# Test
./scripts/test.sh
@@ -48,9 +60,43 @@ jobs:
# 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
+ ./scripts/publish.sh --${{ matrix.os }} "${{ github.ref_name }}" "latest"
+
+ merge:
+ name: 'Merge Labels'
+ needs: ["build"]
+ 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
- # Note: "${{ github.ref_name }}" will be the tag name, e.g., "1.0.0"
- ./scripts/publish.sh "${{ github.ref_name }}" "latest"
+ # 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).
+ # manually login to ghcr.io for publishing
+ echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
+ ./scripts/merge.sh "${{ github.ref_name }}" "latest"
diff --git a/README.md b/README.md
index a1f10fe..d448c49 100644
--- a/README.md
+++ b/README.md
@@ -106,9 +106,9 @@ It is very simple to develop the development container.
You can change files related to the container and then simply run the `scripts/*`.
They are used by the CI, but especially the build and test scripts can be run also locally out of the box:
````console
-$ ./scripts/build.sh
+$ ./scripts/build.sh --amd64 local
[... build output..]
-{"outcome":"success","imageName":["ghcr.io/eclipse-score/devcontainer"]}
+{"outcome":"success","imageName":["ghcr.io/eclipse-score/devcontainer:local-amd64"]}
$ ./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/eclipse-score/devcontainer:local-amd64" > 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/eclipse-score/devcontainer:local-amd64"`.
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.sh b/scripts/build.sh
index dc410ff..71d4ac8 100755
--- a/scripts/build.sh
+++ b/scripts/build.sh
@@ -1,37 +1,44 @@
#!/usr/bin/env bash
set -euxo pipefail
-if [ "$#" -eq 0 ]; then
- echo "Error: At least one parameter (label) must be provided."
+if [[ "$#" -lt 1 || "$1" != "--arm64" && "$1" != "--amd64" ]]; then
+ echo "Error: First parameter must be --arm64 or --amd64."
exit 1
fi
+if [ "$#" -lt 2 ]; then
+ echo "Error: At least one label must be provided after the architecture option."
+ exit 1
+fi
+
+ARCH_OPTION="$1"
+shift
+
+ARCH="amd64"
+if [[ "$ARCH_OPTION" == "--arm64" ]]; then
+ ARCH="arm64"
+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
+echo "Building all labels (${LABELS[@]}) for architecture: ${ARCH}"
- # Prepare devcontainer build command
- DEVCONTAINER_CALL="devcontainer build --workspace-folder src/s-core-devcontainer --cache-from ghcr.io/eclipse-score/devcontainer"
+# Prepare image names with tags (each tag includes a label and the architecture)
+IMAGES=()
+for LABEL in "${LABELS[@]}"; do
+ IMAGES+=("--image-name \"ghcr.io/eclipse-score/devcontainer:${LABEL}-${ARCH}\"")
+done
- # Append image names to the build command
- for IMAGE in "${IMAGES[@]}"; do
- DEVCONTAINER_CALL+=" $IMAGE"
- done
+# Prepare devcontainer build command
+DEVCONTAINER_CALL="devcontainer build --workspace-folder src/s-core-devcontainer --cache-from ghcr.io/eclipse-score/devcontainer"
- # Execute the build for the specific architecture
- eval "$DEVCONTAINER_CALL --platform linux/${ARCH}"
+# 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}"
diff --git a/scripts/merge.sh b/scripts/merge.sh
new file mode 100755
index 0000000..600064a
--- /dev/null
+++ b/scripts/merge.sh
@@ -0,0 +1,35 @@
+#!/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")
+
+# Pull all architecture-specific images for each label
+for LABEL in "${LABELS[@]}"; do
+ for ARCH in "${ARCHITECTURES[@]}"; do
+ docker pull --platform "linux/${ARCH}" "ghcr.io/eclipse-score/devcontainer:${LABEL}-${ARCH}"
+ done
+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/eclipse-score/devcontainer:${LABEL}"
+
+ for ARCH in "${ARCHITECTURES[@]}"; do
+ MANIFEST_MERGE_CALL+=" ghcr.io/eclipse-score/devcontainer:${LABEL}-${ARCH}"
+ done
+
+ eval "$MANIFEST_MERGE_CALL"
+done
diff --git a/scripts/publish.sh b/scripts/publish.sh
index 6a7586c..1fd7edb 100755
--- a/scripts/publish.sh
+++ b/scripts/publish.sh
@@ -1,50 +1,43 @@
#!/usr/bin/env bash
set -euxo pipefail
-if [ "$#" -eq 0 ]; then
- echo "Error: At least one parameter (label) must be provided."
+if [[ "$#" -lt 1 || "$1" != "--arm64" && "$1" != "--amd64" ]]; then
+ echo "Error: First parameter must be --arm64 or --amd64."
exit 1
fi
-LABELS=()
-for LABEL in "$@"; do
- LABELS+=("${LABEL}")
-done
-
-# Define target architectures
-ARCHITECTURES=("amd64" "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/eclipse-score/devcontainer:${LABEL}-${ARCH}\"")
- done
+if [ "$#" -lt 2 ]; then
+ echo "Error: At least one label must be provided after the architecture option."
+ exit 1
+fi
- # Prepare devcontainer build command
- DEVCONTAINER_CALL="devcontainer build --push --workspace-folder src/s-core-devcontainer --cache-from ghcr.io/eclipse-score/devcontainer"
+ARCH_OPTION="$1"
+shift
- # Append image names to the build command
- for IMAGE in "${IMAGES[@]}"; do
- DEVCONTAINER_CALL+=" $IMAGE"
- done
+ARCH="amd64"
+if [[ "$ARCH_OPTION" == "--arm64" ]]; then
+ ARCH="arm64"
+fi
- # Execute the build and push all tags for the specific architecture
- eval "$DEVCONTAINER_CALL --platform linux/${ARCH}"
+LABELS=()
+for LABEL in "$@"; do
+ LABELS+=("${LABEL}")
done
-# Create and push the merged multiarch manifest for each tag; each tag combines all architecture-specific tags into one tag
+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
- echo "Merging all architectures (${ARCHITECTURES[@]}) into single tag: ${LABEL}"
-
- MANIFEST_MERGE_CALL="docker buildx imagetools create -t ghcr.io/eclipse-score/devcontainer:${LABEL}"
+ IMAGES+=("--image-name \"ghcr.io/eclipse-score/devcontainer:${LABEL}-${ARCH}\"")
+done
- for ARCH in "${ARCHITECTURES[@]}"; do
- MANIFEST_MERGE_CALL+=" 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"
- eval "$MANIFEST_MERGE_CALL"
+# 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}"