Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 55 additions & 4 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -38,14 +49,16 @@ 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

# Create builder for multi-arch builds
./scripts/create_builder.sh

# Build
./scripts/build.sh
./scripts/build.sh --${{ matrix.os }} "main"

# Test
./scripts/test.sh
Expand All @@ -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"
60 changes: 53 additions & 7 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -33,24 +43,60 @@ 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

# Create builder for multi-arch builds
./scripts/create_builder.sh

# Build
./scripts/build.sh
./scripts/build.sh --${{ matrix.os }} "${{ github.ref_name }}" "latest"

# Test
./scripts/test.sh

# 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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need to manually login, since you have the login action above?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think yes, but I can double-check next time I create a PR.

./scripts/merge.sh "${{ github.ref_name }}" "latest"
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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...]
Expand All @@ -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 <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>p</kbd> 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.
Expand Down
51 changes: 29 additions & 22 deletions scripts/build.sh
Original file line number Diff line number Diff line change
@@ -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}"
35 changes: 35 additions & 0 deletions scripts/merge.sh
Original file line number Diff line number Diff line change
@@ -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
63 changes: 28 additions & 35 deletions scripts/publish.sh
Original file line number Diff line number Diff line change
@@ -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}"