Skip to content

fix: version validation scripts for release tagging (#1364) #3991

fix: version validation scripts for release tagging (#1364)

fix: version validation scripts for release tagging (#1364) #3991

Workflow file for this run

name: "[Blocking] Build"
on:
push:
branches:
- main
- release-*
tags:
- v*
paths-ignore:
- "**.md"
- "website/**"
- "docs/**"
- "demo/**"
pull_request:
branches:
- main
- release-*
paths-ignore:
- "**.md"
- "website/**"
- "docs/**"
- "demo/**"
workflow_dispatch:
env:
TRIVY_VERSION: 0.59.1
BUILDKIT_VERSION: 0.19.0
TRIVY_DISABLE_VEX_NOTICE: "true"
permissions:
contents: read
jobs:
unit-test:
name: Unit Test
runs-on: oracle-vm-16cpu-64gb-x86-64
timeout-minutes: 5
steps:
- name: Harden Runner
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.3.1
with:
egress-policy: audit
- name: Check out code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
with:
go-version: "1.25"
check-latest: true
- name: Add containerd-snapshotter to docker daemon
run: |
echo '{"features": { "containerd-snapshotter": true }}' | sudo tee /etc/docker/daemon.json
sudo systemctl restart docker
- name: Unit test
shell: bash
env:
CODECOV_OPTS: "-coverprofile=coverage.txt -covermode=atomic"
run: make test
- name: Upload coverage to Codecov
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
build:
name: Build
runs-on: oracle-vm-16cpu-64gb-x86-64
timeout-minutes: 5
permissions:
packages: write
contents: read
steps:
- name: Harden Runner
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.3.1
with:
egress-policy: audit
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
with:
go-version: "1.25"
check-latest: true
- name: Build copa
shell: bash
run: |
make build
make archive
- name: Upload copa to build artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: copa_edge_linux_amd64.tar.gz
path: dist/linux_amd64/release/copa_edge_linux_amd64.tar.gz
- name: Load test cases for patch testing
id: load-test-envs-matrix
shell: bash
run: echo "buildkitenvs=$(.github/workflows/scripts/buildkit-env-matrix.sh)" | tee -a "${GITHUB_OUTPUT}"
- name: Load multiplatform test environments
id: load-multiplatform-envs-matrix
shell: bash
run: echo "multiplatformenvs=[\"docker\",\"podman/container\"]" | tee -a "${GITHUB_OUTPUT}"
outputs:
buildkitenvs: ${{ steps.load-test-envs-matrix.outputs.buildkitenvs }}
multiplatformenvs: ${{ steps.load-multiplatform-envs-matrix.outputs.multiplatformenvs }}
test-patch-trivy:
needs: build
name: Test patch with trivy ${{ matrix.buildkit_mode }}
runs-on: oracle-vm-16cpu-64gb-x86-64
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
buildkit_mode: ${{fromJson(needs.build.outputs.buildkitenvs)}}
steps:
- name: Download copa from build artifacts
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: copa_edge_linux_amd64.tar.gz
- run: docker system prune -a -f --volumes
- name: Check out code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
with:
go-version: "1.25"
check-latest: true
- name: Install required tools
shell: bash
run: .github/workflows/scripts/download-tooling.sh
- name: Download copa from build artifacts
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: copa_edge_linux_amd64.tar.gz
- name: Extract copa
shell: bash
run: |
tar xzf copa_edge_linux_amd64.tar.gz
./copa --version
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Run functional test
shell: bash
run: |
set -eu -o pipefail
. .github/workflows/scripts/buildkitenvs/${{ matrix.buildkit_mode}}
gotestsum --format testname --junitfile test-results.xml -- ./integration/singlearch --addr="${COPA_BUILDKIT_ADDR}" --copa="$(pwd)/copa" -timeout 0 --report-file
- name: Test Summary
uses: test-summary/action@v2
with:
paths: "test-results.xml"
test-patch-no-report:
needs: build
name: Test patch no report ${{ matrix.buildkit_mode }}
runs-on: oracle-vm-16cpu-64gb-x86-64
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
buildkit_mode: ${{fromJson(needs.build.outputs.buildkitenvs)}}
steps:
- name: Download copa from build artifacts
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: copa_edge_linux_amd64.tar.gz
- run: docker system prune -a -f --volumes
- name: Check out code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
with:
go-version: "1.25"
check-latest: true
- name: Install required tools
shell: bash
run: .github/workflows/scripts/download-tooling.sh
- name: Download copa from build artifacts
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: copa_edge_linux_amd64.tar.gz
- name: Extract copa
shell: bash
run: |
tar xzf copa_edge_linux_amd64.tar.gz
./copa --version
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Run functional test
shell: bash
run: |
set -eu -o pipefail
. .github/workflows/scripts/buildkitenvs/${{ matrix.buildkit_mode}}
echo "COPA_BUILDKIT_ADDR=${COPA_BUILDKIT_ADDR}" >> "$GITHUB_ENV"
gotestsum --format testname --junitfile test-results.xml -- ./integration/singlearch --addr="${COPA_BUILDKIT_ADDR}" --copa="$(pwd)/copa" -timeout 0
- name: Test Summary
uses: test-summary/action@v2
with:
paths: "test-results.xml"
- name: Test RPM validation - noreplace files
if: ${{ !startsWith(env.COPA_BUILDKIT_ADDR, 'podman-container://') }}
shell: bash
run: |
set -eux -o pipefail
if [[ -n "${COPA_BUILDKIT_ADDR}" && "${COPA_BUILDKIT_ADDR}" == docker://* ]]; then
export DOCKER_HOST="${COPA_BUILDKIT_ADDR#docker://}"
fi
docker create --name test ghcr.io/project-copacetic/copacetic/test/openssl:test-rpm-patched /bin/sh
tmp="$(mktemp)"
docker cp test:/etc/pki/tls/openssl.cnf "${tmp}"
if ! grep -q foo "${tmp}"; then
echo "Error: openssl.cnf content replaced" >&2
rm "${tmp}"
docker rm -f test
exit 1
fi
rm "${tmp}"
docker rm -f test
- name: Test RPM validation - symlink
if: ${{ !startsWith(env.COPA_BUILDKIT_ADDR, 'podman-container://') }}
shell: bash
run: |
set -eux -o pipefail
if [[ -n "${COPA_BUILDKIT_ADDR}" && "${COPA_BUILDKIT_ADDR}" == docker://* ]]; then
export DOCKER_HOST="${COPA_BUILDKIT_ADDR#docker://}"
fi
docker create --name test ghcr.io/project-copacetic/copacetic/test/openssl:test-rpm-patched /bin/sh
tmp="$(mktemp)"
symlink_path="/sbin"
docker cp test:"$symlink_path" "${tmp}_symlink"
if [ ! -L "${tmp}_symlink" ]; then
echo "Error: The path $symlink_path is not a symlink."
rm "${tmp}" "${tmp}_symlink"
docker rm -f test
exit 1
fi
rm "${tmp}" "${tmp}_symlink"
docker rm -f test
- name: Test Debian validation - noreplace files
if: ${{ !startsWith(env.COPA_BUILDKIT_ADDR, 'podman-container://') }}
shell: bash
run: |
set -eux -o pipefail
_cleanup() {
docker rm -f "$DOCKER_CUSTOM_UNIX_ID"
sudo rm -rf "$SOCK_DIR"
}
if [[ -n "${COPA_BUILDKIT_ADDR}" && "${COPA_BUILDKIT_ADDR}" == docker://* ]]; then
export DOCKER_HOST="${COPA_BUILDKIT_ADDR#docker://}"
trap '_cleanup' EXIT
fi
docker create --name test ghcr.io/project-copacetic/copacetic/test/openssl:test-debian-patched /bin/sh
tmp="$(mktemp)"
docker cp test:/etc/ssl/openssl.cnf "${tmp}"
if ! grep -q foo "${tmp}"; then
echo "Error: openssl.cnf content replaced" >&2
rm "${tmp}"
docker rm -f test
exit 1
fi
rm "${tmp}"
docker rm -f test
test-plugin:
needs: build
name: Test plugin
runs-on: oracle-vm-16cpu-64gb-x86-64
timeout-minutes: 5
steps:
- name: Harden Runner
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.3.1
with:
egress-policy: audit
- name: Check out code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
with:
go-version: "1.25"
check-latest: true
- name: Install scanner-plugin-template
shell: bash
run: |
go install github.com/project-copacetic/scanner-plugin-template@latest
mv $(go env GOPATH)/bin/scanner-plugin-template $(go env GOPATH)/bin/copa-fake
mv $(go env GOPATH)/bin/copa-fake /usr/local/bin
- name: Install required tools
shell: bash
run: .github/workflows/scripts/download-tooling.sh
- name: Download copa from build artifacts
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: copa_edge_linux_amd64.tar.gz
- name: Extract copa
shell: bash
run: |
tar xzf copa_edge_linux_amd64.tar.gz
./copa --version
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Run e2e tests
shell: bash
run: |
set -eu -o pipefail
. .github/workflows/scripts/buildkitenvs/direct/tcp
gotestsum --format testname --junitfile test-results.xml -- ./test/e2e/plugin --addr="${COPA_BUILDKIT_ADDR}" --copa="$(pwd)/copa" --scanner fake -timeout 0
- name: Test Summary
uses: test-summary/action@v2
with:
paths: "test-results.xml"
test-push:
needs: build
name: Test push
runs-on: oracle-vm-16cpu-64gb-x86-64
timeout-minutes: 5
steps:
- name: Check out code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
with:
go-version: "1.25"
check-latest: true
- name: Add containerd-snapshotter to docker daemon
run: |
echo '{"features": { "containerd-snapshotter": true }}' | sudo tee /etc/docker/daemon.json
sudo systemctl restart docker
- name: Set up buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
with:
install: true
- name: Install oras CLI
uses: oras-project/setup-oras@22ce207df3b08e061f537244349aac6ae1d214f6 # v1.2.4
- name: Install required tools
shell: bash
run: .github/workflows/scripts/download-tooling.sh
- name: Download copa from build artifacts
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: copa_edge_linux_amd64.tar.gz
- name: Extract copa
shell: bash
run: |
tar xzf copa_edge_linux_amd64.tar.gz
./copa --version
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Run e2e tests
shell: bash
run: |
set -eu -o pipefail
gotestsum --format testname --junitfile test-results.xml -- ./test/e2e/push --addr="docker://" --copa="$(pwd)/copa"
- name: Test Summary
uses: test-summary/action@v2
with:
paths: "test-results.xml"
test-patch-multiplatform:
needs: build
name: Test patch with multiplatform ${{ matrix.buildkit_mode }}
runs-on: oracle-vm-16cpu-64gb-x86-64
timeout-minutes: 40
strategy:
fail-fast: false
matrix:
buildkit_mode: ${{fromJson(needs.build.outputs.multiplatformenvs)}}
steps:
- name: Change docker daemon config
run: |
echo '{"features": { "containerd-snapshotter": true }, "insecure-registries": ["localhost:5000"]}' | sudo tee /etc/docker/daemon.json
sudo systemctl restart docker
- name: Set up buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435
with:
install: true
platforms: linux/amd64,linux/arm/v5,linux/arm64,linux/386,linux/mips64le
- name: Install oras CLI
uses: oras-project/setup-oras@22ce207df3b08e061f537244349aac6ae1d214f6 # v1.2.4
- name: Set up local registry
run: |
docker run -d -p 5000:5000 --restart=always --name registry registry:2
# wait for the registry to be healthy
for i in $(seq 1 60); do
if curl -f http://localhost:5000/v2/_catalog; then
break
fi
sleep 1
done
- name: Test registry
run: |
set -eu -o pipefail
docker pull nginx:latest
docker tag nginx:latest localhost:5000/nginx:latest
docker push localhost:5000/nginx:latest
- name: Download copa from build artifacts
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: copa_edge_linux_amd64.tar.gz
- run: docker system prune -a -f --volumes
- name: Check out code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
with:
go-version: "1.25"
check-latest: true
- name: Install required tools
shell: bash
run: .github/workflows/scripts/download-tooling.sh
- name: Install multiplatform tooling
shell: bash
run: .github/workflows/scripts/download-multiplatform-tooling.sh
- name: Download copa from build artifacts
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: copa_edge_linux_amd64.tar.gz
- name: Extract copa
shell: bash
run: |
tar xzf copa_edge_linux_amd64.tar.gz
./copa --version
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Run functional test
shell: bash
run: |
set -eu -o pipefail
if [[ "${{ matrix.buildkit_mode }}" == "docker" ]]; then
# For docker mode, use the default docker daemon
export COPA_BUILDKIT_ADDR="docker://"
else
# For other modes, source the corresponding script in the same shell session
. .github/workflows/scripts/buildkitenvs/${{ matrix.buildkit_mode }}
fi
gotestsum --format testname --junitfile test-results.xml -- ./integration/multiarch --addr="${COPA_BUILDKIT_ADDR}" --copa="$(pwd)/copa" -timeout 0
- name: Test Summary
uses: test-summary/action@v2
with:
paths: "test-results.xml"
test-generate:
needs: build
name: Test generate command ${{ matrix.buildkit_mode }}
runs-on: oracle-vm-16cpu-64gb-x86-64
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
buildkit_mode: ${{fromJson(needs.build.outputs.buildkitenvs)}}
steps:
- name: Download copa from build artifacts
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: copa_edge_linux_amd64.tar.gz
- run: docker system prune -a -f --volumes
- name: Check out code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
with:
go-version: "1.25"
check-latest: true
- name: Install required tools
shell: bash
run: .github/workflows/scripts/download-tooling.sh
- name: Download copa from build artifacts
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: copa_edge_linux_amd64.tar.gz
- name: Extract copa
shell: bash
run: |
tar xzf copa_edge_linux_amd64.tar.gz
./copa --version
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Run e2e tests
shell: bash
run: |
set -eu -o pipefail
. .github/workflows/scripts/buildkitenvs/${{ matrix.buildkit_mode}}
go test -v ./test/e2e/generate --addr="${COPA_BUILDKIT_ADDR}" --copa="$(pwd)/copa" -timeout 0
test-patch-multiplatform-plugin:
needs: build
name: Test multiplatform with plugin
runs-on: oracle-vm-16cpu-64gb-x86-64
timeout-minutes: 50
steps:
- name: Change docker daemon config
run: |
echo '{"features": { "containerd-snapshotter": true }, "insecure-registries": ["localhost:5000"], "dns": ["8.8.8.8", "8.8.4.4"]}' | sudo tee /etc/docker/daemon.json
echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf > /dev/null
sudo systemctl restart docker
- name: Set up buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435
with:
install: true
platforms: linux/amd64,linux/arm64
- name: Install oras CLI
uses: oras-project/setup-oras@22ce207df3b08e061f537244349aac6ae1d214f6 # v1.2.4
- name: Set up local registry
run: |
docker run -d -p 5000:5000 --restart=always --name registry registry:2
# wait for the registry to be healthy
for i in $(seq 1 60); do
if curl -f http://localhost:5000/v2/_catalog; then
break
fi
sleep 1
done
- name: Test registry
run: |
set -eu -o pipefail
docker pull nginx:latest
docker tag nginx:latest localhost:5000/nginx:latest
docker push localhost:5000/nginx:latest
- name: Check out code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
with:
go-version: "1.25"
check-latest: true
- name: Install scanner-plugin-template
shell: bash
run: |
go install github.com/project-copacetic/scanner-plugin-template@latest
mv $(go env GOPATH)/bin/scanner-plugin-template $(go env GOPATH)/bin/copa-fake
mv $(go env GOPATH)/bin/copa-fake /usr/local/bin
- name: Install required tools
shell: bash
run: .github/workflows/scripts/download-tooling.sh
- name: Install multiplatform tooling
shell: bash
run: .github/workflows/scripts/download-multiplatform-tooling.sh
- name: Download copa from build artifacts
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: copa_edge_linux_amd64.tar.gz
- run: docker system prune -a -f --volumes
- name: Extract copa
shell: bash
run: |
tar xzf copa_edge_linux_amd64.tar.gz
./copa --version
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Run e2e multiplatform plugin tests
shell: bash
run: |
set -eu -o pipefail
gotestsum --format testname --junitfile test-results.xml -- ./test/e2e/multiplatform-plugin --addr=docker:// --copa="$(pwd)/copa" -timeout 0
- name: Test Summary
uses: test-summary/action@v2
with:
paths: "test-results.xml"
test-patch-local-multiplatform-oci-layout:
needs: build
name: Test multiplatform OCI layout creation
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Change docker daemon config
run: |
echo '{"features": { "containerd-snapshotter": true }, "insecure-registries": ["localhost:5000"]}' | sudo tee /etc/docker/daemon.json
sudo systemctl restart docker
- name: Set up buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435
with:
install: true
platforms: linux/amd64,linux/arm64
- name: Check out code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
with:
go-version: "1.25"
check-latest: true
- name: Install required tools
shell: bash
run: .github/workflows/scripts/download-tooling.sh
- name: Install multiplatform tooling
shell: bash
run: .github/workflows/scripts/download-multiplatform-tooling.sh
- name: Download copa from build artifacts
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: copa_edge_linux_amd64.tar.gz
- run: docker system prune -a -f --volumes
- name: Extract copa
shell: bash
run: |
tar xzf copa_edge_linux_amd64.tar.gz
./copa --version
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Set up local registry with insecure access
run: |
docker run -d -p 5000:5000 --restart=always --name registry registry:2
# wait for the registry to be healthy
for i in $(seq 1 60); do
if curl -f http://localhost:5000/v2/_catalog; then
echo "Registry is healthy after ${i} attempts"
break
fi
echo "Waiting for registry... attempt ${i}/60"
sleep 1
done
# Verify registry is accessible
curl -f http://localhost:5000/v2/_catalog || { echo "ERROR: Registry not accessible"; exit 1; }
- name: Test OCI layout creation with local multiplatform manifest
shell: bash
run: |
set -eu -o pipefail
BASE_IMAGE="mcr.microsoft.com/cbl-mariner/base/core:2.0.20240112"
LOCAL_REGISTRY="localhost:5000"
PLATFORM_AMD64_IMAGE="${LOCAL_REGISTRY}/mariner:2.0.20240112-amd64"
PLATFORM_ARM64_IMAGE="${LOCAL_REGISTRY}/mariner:2.0.20240112-arm64"
LOCAL_MANIFEST="${LOCAL_REGISTRY}/mariner:2.0.20240112-multiplatform"
echo "Building and pushing platform-specific images to local registry..."
# Get platform-specific digests from the multi-platform image
AMD64_DIGEST=$(docker manifest inspect "${BASE_IMAGE}" | jq -r '.manifests[] | select(.platform.architecture=="amd64") | .digest')
ARM64_DIGEST=$(docker manifest inspect "${BASE_IMAGE}" | jq -r '.manifests[] | select(.platform.architecture=="arm64") | .digest')
echo "AMD64 digest: ${AMD64_DIGEST}"
echo "ARM64 digest: ${ARM64_DIGEST}"
# Pull and push amd64 image using specific digest
echo "Pulling amd64 image by digest..."
docker pull "${BASE_IMAGE}@${AMD64_DIGEST}"
echo "Tagging and pushing amd64 image..."
docker tag "${BASE_IMAGE}@${AMD64_DIGEST}" "${PLATFORM_AMD64_IMAGE}"
docker push "${PLATFORM_AMD64_IMAGE}"
# Pull and push arm64 image using specific digest
echo "Pulling arm64 image by digest..."
docker pull "${BASE_IMAGE}@${ARM64_DIGEST}"
echo "Tagging and pushing arm64 image..."
docker tag "${BASE_IMAGE}@${ARM64_DIGEST}" "${PLATFORM_ARM64_IMAGE}"
docker push "${PLATFORM_ARM64_IMAGE}"
echo "Verifying platform-specific images exist in local registry..."
curl -s http://localhost:5000/v2/mariner/tags/list | jq .
# Verify images using registry API with retries
for i in $(seq 1 10); do
if curl -s -f "http://localhost:5000/v2/mariner/manifests/2.0.20240112-amd64" > /dev/null; then
echo "✓ AMD64 image verified in registry"
break
fi
echo "Waiting for AMD64 image... attempt ${i}/10"
sleep 2
done
for i in $(seq 1 10); do
if curl -s -f "http://localhost:5000/v2/mariner/manifests/2.0.20240112-arm64" > /dev/null; then
echo "✓ ARM64 image verified in registry"
break
fi
echo "Waiting for ARM64 image... attempt ${i}/10"
sleep 2
done
echo "Enabling Docker CLI experimental features for manifest commands..."
export DOCKER_CLI_EXPERIMENTAL=enabled
echo "Creating local Docker manifest that references local registry platform images..."
# Pull the images locally first to ensure they're available for manifest creation
docker pull "${PLATFORM_AMD64_IMAGE}"
docker pull "${PLATFORM_ARM64_IMAGE}"
# Create local manifest list using --insecure for local registry
docker manifest create --insecure "${LOCAL_MANIFEST}" "${PLATFORM_AMD64_IMAGE}" "${PLATFORM_ARM64_IMAGE}"
docker manifest push --insecure "${LOCAL_MANIFEST}"
echo "Verifying local multiplatform manifest was created..."
docker manifest inspect "${LOCAL_MANIFEST}"
# Verify manifest exists in registry
curl -s "http://localhost:5000/v2/mariner/manifests/2.0.20240112-multiplatform" | jq .
# Create temp directory for OCI layout
OCI_LAYOUT_DIR=$(mktemp -d)
echo "Patching local multiplatform manifest and creating OCI layout (patching only amd64)..."
# Set buildkit to use Docker daemon to ensure it can access the local registry
export COPA_BUILDKIT_ADDR="docker://"
# Patch only amd64 platform, preserve arm64 platform
./copa patch \
-i="${LOCAL_MANIFEST}" \
-t="patched" \
--platform="linux/amd64" \
--timeout=30m \
--ignore-errors=true \
--oci-dir="${OCI_LAYOUT_DIR}" \
--debug
echo "Validating OCI layout structure..."
# Check that OCI layout directory was created
if [ ! -d "${OCI_LAYOUT_DIR}" ]; then
echo "ERROR: OCI layout directory was not created"
exit 1
fi
# Check for required OCI layout files
if [ ! -f "${OCI_LAYOUT_DIR}/oci-layout" ]; then
echo "ERROR: oci-layout file missing"
exit 1
fi
if [ ! -f "${OCI_LAYOUT_DIR}/index.json" ]; then
echo "ERROR: index.json file missing"
exit 1
fi
if [ ! -d "${OCI_LAYOUT_DIR}/blobs" ]; then
echo "ERROR: blobs directory missing"
exit 1
fi
echo "Validating OCI layout content..."
# Check that index.json contains multiplatform index
MEDIA_TYPE=$(jq -r '.mediaType' "${OCI_LAYOUT_DIR}/index.json")
echo "Index media type: ${MEDIA_TYPE}"
if [ "${MEDIA_TYPE}" != "application/vnd.oci.image.index.v1+json" ]; then
echo "ERROR: Expected OCI image index media type, got ${MEDIA_TYPE}"
exit 1
fi
# Check for multiple platforms in the index (mixed layout)
PLATFORM_COUNT=$(jq '.manifests | length' "${OCI_LAYOUT_DIR}/index.json")
echo "Found ${PLATFORM_COUNT} platform(s) in index"
if [ "${PLATFORM_COUNT}" -ne 2 ]; then
echo "ERROR: Expected exactly 2 platforms (1 patched, 1 preserved), found ${PLATFORM_COUNT}"
exit 1
fi
# Validate specific platforms exist
AMD64_FOUND=$(jq -r '.manifests[] | select(.platform.architecture=="amd64") | .platform.architecture' "${OCI_LAYOUT_DIR}/index.json")
ARM64_FOUND=$(jq -r '.manifests[] | select(.platform.architecture=="arm64") | .platform.architecture' "${OCI_LAYOUT_DIR}/index.json")
if [ "${AMD64_FOUND}" != "amd64" ]; then
echo "ERROR: amd64 platform not found in index"
exit 1
fi
if [ "${ARM64_FOUND}" != "arm64" ]; then
echo "ERROR: arm64 platform not found in index"
exit 1
fi
# Cleanup
echo "Cleaning up..."
rm -rf "${OCI_LAYOUT_DIR}"
docker stop registry || true
docker rm registry || true
echo "Local registry mixed OCI layout test completed successfully!"
test-nodejs:
needs: build
name: Test Node.js patching
runs-on: ubuntu-latest
timeout-minutes: 10
permissions: read-all
steps:
- name: Check out code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
with:
go-version: "1.25"
check-latest: true
- name: Add containerd-snapshotter to docker daemon
run: |
echo '{"features": { "containerd-snapshotter": true }}' | sudo tee /etc/docker/daemon.json
sudo systemctl restart docker
- name: Install required tools
shell: bash
run: .github/workflows/scripts/download-tooling.sh
- name: Download copa from build artifacts
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: copa_edge_linux_amd64.tar.gz
- name: Extract copa
shell: bash
run: |
tar xzf copa_edge_linux_amd64.tar.gz
./copa --version
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Run Node.js e2e tests
shell: bash
run: |
set -eu -o pipefail
gotestsum --format testname --junitfile test-results.xml -- ./test/e2e/nodejs --addr=docker:// --copa="$(pwd)/copa" -timeout 0
- name: Test Summary
uses: test-summary/action@v2
with:
paths: "test-results.xml"