From 5df4f23a4068448ec75cd6f84a937655b0e9ed09 Mon Sep 17 00:00:00 2001 From: Bertrand Gouny Date: Fri, 16 Oct 2026 23:47:50 +0200 Subject: [PATCH] ci: build each arch natively and publish them together Build and test each arch on its own runner (ubuntu-latest and ubuntu-24.04-arm) instead of under QEMU, with osixia/dagger-build OCI archives: on tags, build jobs export them as artifacts, and the publish job deploys them as a single multi-arch image per tag. Integration scenarios now run on every built image, so on arm64 too: each runner builds its own arch, so the engine platform filter never skipped anything. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/go.yml | 52 ++++++++++++++++++++++++++++++++++------ CHANGELOG.md | 1 + build/README.md | 8 ++++--- build/go.mod | 2 +- build/go.sum | 4 ++-- build/tests/test.go | 23 ++---------------- 6 files changed, 56 insertions(+), 34 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index c97a9c1..dfcf5d5 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -60,11 +60,18 @@ jobs: go-version-input: '1.26.9' work-dir: build + # each arch is built and tested natively on its own runner instead of under QEMU build: - name: build - if: "!startsWith(github.ref, 'refs/tags/')" - runs-on: ubuntu-latest + name: build (${{ matrix.arch }}) + runs-on: ${{ matrix.runner }} needs: [lint, test, shellcheck, govulncheck] + strategy: + matrix: + include: + - arch: amd64 + runner: ubuntu-latest + - arch: arm64 + runner: ubuntu-24.04-arm steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -72,18 +79,39 @@ jobs: - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: '1.26.9' + cache-dependency-path: build/go.sum - name: Vendor build go dependencies working-directory: build run: go mod vendor + # on tags, images are only exported as OCI archives: the publish job deploys them together - name: Run tests working-directory: build - run: go run main.go provider github ../Dockerfile --github-ref "${GITHUB_REF}" ${{ vars.OSX_GITHUB_FLAGS }} + env: + ARCH: ${{ matrix.arch }} + GITHUB_TOKEN: ${{ github.ref_type == 'tag' && secrets.GITHUB_TOKEN || '' }} + OSX_GITHUB_FLAGS: ${{ vars.OSX_GITHUB_FLAGS }} + run: | + flags=(--arches "${ARCH}" --github-ref "${GITHUB_REF}") + if [ "${GITHUB_REF_TYPE}" = "tag" ]; then + flags+=(--github-token "${GITHUB_TOKEN}" --oci-export-to ../oci) + fi + # shellcheck disable=SC2086 + go run main.go provider github ../Dockerfile "${flags[@]}" ${OSX_GITHUB_FLAGS} + - name: Upload OCI archives + if: github.ref_type == 'tag' + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 + with: + name: oci-${{ matrix.arch }} + path: oci/ + if-no-files-found: error + retention-days: 1 + compression-level: 0 publish: name: publish - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') + if: github.event_name == 'push' && github.ref_type == 'tag' runs-on: ubuntu-latest - needs: [lint, test, shellcheck, govulncheck] + needs: [build] permissions: contents: write id-token: write @@ -101,16 +129,26 @@ jobs: uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 - name: Install syft uses: anchore/sbom-action/download-syft@66cbf4bc1f1c0d2edc94016e65bc221b6bb0ad6c # v0.24.3 + - name: Download OCI archives + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 + with: + pattern: oci-* + path: oci/ + merge-multiple: true - name: Vendor build go dependencies working-directory: build run: go mod vendor + # deploys the images tested by the build jobs as a single multi-arch image per tag - name: Run pipeline working-directory: build env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} - run: go run main.go provider github ../Dockerfile --github-ref "${GITHUB_REF}" --github-token "${GITHUB_TOKEN}" --changelog ../CHANGELOG.md --username "${REGISTRY_USERNAME}" --password "${REGISTRY_PASSWORD}" --digests-file "${DIGESTS_FILE}" ${{ vars.OSX_GITHUB_FLAGS }} + OSX_GITHUB_FLAGS: ${{ vars.OSX_GITHUB_FLAGS }} + run: | + # shellcheck disable=SC2086 + go run main.go provider github ../Dockerfile --github-ref "${GITHUB_REF}" --github-token "${GITHUB_TOKEN}" --oci-import-from ../oci --changelog ../CHANGELOG.md --username "${REGISTRY_USERNAME}" --password "${REGISTRY_PASSWORD}" --digests-file "${DIGESTS_FILE}" ${OSX_GITHUB_FLAGS} - name: Sign images and generate SBOM env: COSIGN_EXPERIMENTAL: '1' diff --git a/CHANGELOG.md b/CHANGELOG.md index 4955aac..b6ab75c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Project-specific changes only. See upstream [OpenLDAP release notes](https://www - Build the CI/CD tool with `osixia/dagger-build` (replaces `osixia/container-baseimage/build`) and Go 1.26.9. - Add integration tests: default bootstrap, all compatible modules and schemas, every bootstrap feature on a 3-node replicated cluster with TLS required, backup/restore and upgrades (simulated and from previous releases). - Harden the CI workflow: read-only default permissions, actions pinned by commit SHA, images only published from tags, Dependabot updates. +- Build and test each architecture natively on its own runner instead of under QEMU, including the integration tests on arm64, and publish them together as a single multi-arch image. - Allow authenticated users to read `entry`, `objectClass`, `ou`, `o` and `dc` in addition to `cn`, `sn`, `givenName`, `mail` and `uid` in the default database ACL (new bootstraps only). - Set `OPENLDAP_BOOTSTRAP_PPOLICY_DEFAULT_CHECK_QUALITY` to `1` (was `0`) so `pwdMinLength` is enforced on clear text password changes; pre-hashed passwords are still accepted (new bootstraps only). - Fail bootstrap with an explicit error when `OPENLDAP_BOOTSTRAP_SUFFIX` does not start with `dc=`, instead of creating a root entry with an empty `dc`. diff --git a/build/README.md b/build/README.md index 4ff0899..ebe47cf 100644 --- a/build/README.md +++ b/build/README.md @@ -13,10 +13,12 @@ go mod vendor go run main.go job test ../Dockerfile ``` -Limit a local run with `--arches amd64`. +Limit a local run with `--arches amd64`. Pass the arch of the host: scenarios run on every +built image, and other arches would be emulated. The CI builds and tests each arch on its +own native runner, then publishes them together from their OCI archives +(`--oci-export-to`, `--oci-import-from`). -After the shared version check, `tests/` runs integration scenarios on the image built for -the engine platform: +After the shared version check, `tests/` runs integration scenarios on each built image: - default bootstrap; - alternative schemas (`nis`, `virtualMail`); diff --git a/build/go.mod b/build/go.mod index 7c125dd..d293519 100644 --- a/build/go.mod +++ b/build/go.mod @@ -4,7 +4,7 @@ go 1.26.9 require ( dagger.io/dagger v0.21.10 - github.com/osixia/dagger-build v0.0.0-20261015194426-55e8bdd25dbb + github.com/osixia/dagger-build v0.0.0-20261016204440-cdddcccbc11c ) require ( diff --git a/build/go.sum b/build/go.sum index eb2d6cb..a530b2b 100644 --- a/build/go.sum +++ b/build/go.sum @@ -37,8 +37,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/osixia/dagger-build v0.0.0-20261015194426-55e8bdd25dbb h1:V8FLn00GbtrHvOTII63DaCSZDoi2zQvmIztr20FmOw4= -github.com/osixia/dagger-build v0.0.0-20261015194426-55e8bdd25dbb/go.mod h1:gxxc7tVjlUVYPeBpDBdWs1MFHuP48AR6zes7F1NIMKI= +github.com/osixia/dagger-build v0.0.0-20261016204440-cdddcccbc11c h1:xxoEJ8RLtMVqK5WSHRTSxXPL9z9bh9B3EAQW5/hVyQM= +github.com/osixia/dagger-build v0.0.0-20261016204440-cdddcccbc11c/go.mod h1:gxxc7tVjlUVYPeBpDBdWs1MFHuP48AR6zes7F1NIMKI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= diff --git a/build/tests/test.go b/build/tests/test.go index c54a6dc..83a1fa2 100644 --- a/build/tests/test.go +++ b/build/tests/test.go @@ -26,28 +26,13 @@ var scenarios = []scenario{ {"upgrade from previous releases", UpgradeFromPreviousReleases}, } -// Run checks OpenLDAP behavior after the reusable job's version check, only on the engine -// platform: emulated images are slow and fail as Dagger services. +// Run checks OpenLDAP behavior after the reusable job's version check. CI builds each arch +// on a native runner: run locally with --arches matching the host to avoid emulation. func Run(ctx context.Context, client *dagger.Client, builds []*job.BuildResult) error { - enginePlatform, err := client.DefaultPlatform(ctx) - if err != nil { - return fmt.Errorf("engine platform: %w", err) - } - - tested := 0 for _, build := range builds { for _, image := range build.Containers { - platform, err := image.Platform(ctx) - if err != nil { - return fmt.Errorf("image platform: %w", err) - } - if platform != enginePlatform { - continue - } - tested++ - for _, s := range scenarios { log.Printf("scenario: %s ...", s.name) sctx, cancel := context.WithTimeout(ctx, 10*time.Minute) @@ -61,9 +46,5 @@ func Run(ctx context.Context, client *dagger.Client, builds []*job.BuildResult) } } - if tested == 0 && len(builds) > 0 { - return fmt.Errorf("no image built for the engine platform %v", enginePlatform) - } - return nil }