From c5c0ec41f05bf47ef4baf1fdde29b6fe09c9e688 Mon Sep 17 00:00:00 2001 From: abtreece Date: Fri, 24 Apr 2026 23:35:40 -0500 Subject: [PATCH 1/9] feat: add contributor CI workflow for fork-friendly testing Add a credentialless CI workflow that runs on contributor forks and PRs, enabling third-party contributors to validate their changes without access to project infrastructure. - Contributor CI workflow (ci-cd-contributor) builds Ruby packages for two representative distributions (Ubuntu 24.04 and EL 9) with applicable variants and runs smoke tests - ok-to-test label gate lets maintainers trigger the full CI pipeline; a label guard workflow removes the label on new pushes, requiring re-review before re-triggering - Main CI/CD workflow restricted to upstream repository only - Respects variant_exclusions from config.yml (skips malloctrim for Ruby >= 3.3) - Test script fixes: createrepo -> createrepo_c, added adduser package - Updated CONTRIBUTING.md with fork CI and local build/test docs --- .github/workflows/ci-cd-contributor.yml | 368 ++++++++++++++++++++ .github/workflows/ci-cd-contributor.yml.erb | 290 +++++++++++++++ .github/workflows/ci-cd-label-guard.yml | 34 ++ .github/workflows/ci-cd-main.yml | 12 + .github/workflows/ci-cd-main.yml.erb | 12 + CONTRIBUTING.md | 21 ++ config.yml | 7 + container-entrypoints/test-rpms-prepare | 2 +- dev-handbook/building-packages-locally.md | 2 + internal-scripts/generate-ci-cd-yaml.rb | 1 + internal-scripts/test-debs | 2 +- lib/ci_workflow_support.rb | 18 + 12 files changed, 767 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/ci-cd-contributor.yml create mode 100644 .github/workflows/ci-cd-contributor.yml.erb create mode 100644 .github/workflows/ci-cd-label-guard.yml diff --git a/.github/workflows/ci-cd-contributor.yml b/.github/workflows/ci-cd-contributor.yml new file mode 100644 index 00000000..5ecd65d3 --- /dev/null +++ b/.github/workflows/ci-cd-contributor.yml @@ -0,0 +1,368 @@ +# WARNING: DO NOT EDIT THIS FILE!!! +# +# This file is autogenerated from .github/workflows/ci-cd-contributor.yml.erb +# by ./internal-scripts/generate-ci-cd-yaml.rb. +# Please edit the .erb file instead, then regenerate YAML +# by running that script. +# +# TIP: run this on your development machine to ensure generate-ci-cd-yaml.rb +# is run automatically as a Git pre-commit hook: +# +# git config core.hooksPath .githooks + +name: 'CI/CD: contributor' + +on: + push: + paths-ignore: + - '**.md' + - 'dev-handbook/**' + pull_request: + paths-ignore: + - '**.md' + - 'dev-handbook/**' + workflow_dispatch: + +jobs: + lint: + name: Check whether workflow is up-to-date + runs-on: ubuntu-24.04 + if: github.event_name == 'pull_request' || github.repository != 'fullstaq-ruby/server-edition' + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3' + - name: Check + run: ./internal-scripts/ci-cd/check-workflow-uptodate/check.sh + + + build_images: + name: Build Docker images + needs: lint + runs-on: ubuntu-24.04 + if: github.event_name == 'pull_request' || github.repository != 'fullstaq-ruby/server-edition' + steps: + - uses: actions/checkout@v4 + + - name: 'Build Docker image [ubuntu-24.04]' + run: ./internal-scripts/ci-cd/build-docker-images/build.sh + env: + IMAGE_NAME: 'ghcr.io/fullstaq-ruby/server-edition-ci-images' + IMAGE_TAG: 'ubuntu-24.04-v2' + SOURCE_DIR: 'environments/ubuntu-24.04' + - name: 'Dump Docker image [ubuntu-24.04]' + run: ./internal-scripts/ci-cd/build-docker-images/dump-image.sh + env: + IMAGE_NAME: 'ghcr.io/fullstaq-ruby/server-edition-ci-images' + IMAGE_TAG: 'ubuntu-24.04-v2' + - name: 'Upload Docker image artifact [ubuntu-24.04]' + uses: actions/upload-artifact@v4 + with: + name: 'docker-image-ubuntu-24.04' + path: output + - name: Clean up Docker image output + run: rm -rf output + + - name: 'Build Docker image [el-9]' + run: ./internal-scripts/ci-cd/build-docker-images/build.sh + env: + IMAGE_NAME: 'ghcr.io/fullstaq-ruby/server-edition-ci-images' + IMAGE_TAG: 'el-9-v2' + SOURCE_DIR: 'environments/el-9' + - name: 'Dump Docker image [el-9]' + run: ./internal-scripts/ci-cd/build-docker-images/dump-image.sh + env: + IMAGE_NAME: 'ghcr.io/fullstaq-ruby/server-edition-ci-images' + IMAGE_TAG: 'el-9-v2' + - name: 'Upload Docker image artifact [el-9]' + uses: actions/upload-artifact@v4 + with: + name: 'docker-image-el-9' + path: output + - name: Clean up Docker image output + run: rm -rf output + + - name: 'Build Docker image [utility]' + run: ./internal-scripts/ci-cd/build-docker-images/build.sh + env: + IMAGE_NAME: 'ghcr.io/fullstaq-ruby/server-edition-ci-images' + IMAGE_TAG: 'utility-v3' + SOURCE_DIR: 'environments/utility' + - name: 'Dump Docker image [utility]' + run: ./internal-scripts/ci-cd/build-docker-images/dump-image.sh + env: + IMAGE_NAME: 'ghcr.io/fullstaq-ruby/server-edition-ci-images' + IMAGE_TAG: 'utility-v3' + - name: 'Upload Docker image artifact [utility]' + uses: actions/upload-artifact@v4 + with: + name: 'docker-image-utility' + path: output + - name: Clean up Docker image output + run: rm -rf output + + + build_packages: + name: Build packages + needs: build_images + runs-on: ubuntu-24.04 + if: github.event_name == 'pull_request' || github.repository != 'fullstaq-ruby/server-edition' + timeout-minutes: 60 + steps: + - uses: actions/checkout@v4 + + ### Download and load Docker images ### + + - name: 'Download Docker image artifact [ubuntu-24.04]' + uses: actions/download-artifact@v4 + with: + name: 'docker-image-ubuntu-24.04' + path: docker-image-ubuntu-24.04 + - name: 'Load Docker image [ubuntu-24.04]' + run: ./internal-scripts/ci-cd/load-docker-image.sh + env: + TARBALL: docker-image-ubuntu-24.04/image.tar.zst + + - name: 'Download Docker image artifact [el-9]' + uses: actions/download-artifact@v4 + with: + name: 'docker-image-el-9' + path: docker-image-el-9 + - name: 'Load Docker image [el-9]' + run: ./internal-scripts/ci-cd/load-docker-image.sh + env: + TARBALL: docker-image-el-9/image.tar.zst + + - name: 'Download Docker image artifact [utility]' + uses: actions/download-artifact@v4 + with: + name: 'docker-image-utility' + path: docker-image-utility + - name: 'Load Docker image [utility]' + run: ./internal-scripts/ci-cd/load-docker-image.sh + env: + TARBALL: docker-image-utility/image.tar.zst + + ### Download sources ### + + - name: Download Ruby source + run: curl -fsSL -o ruby-src.tar.gz 'https://cache.ruby-lang.org/pub/ruby/4.0/ruby-4.0.3.tar.gz' + - name: Download Jemalloc source + run: curl -fsSL -o jemalloc-src.tar.bz2 'https://github.com/jemalloc/jemalloc/releases/download/3.6.0/jemalloc-3.6.0.tar.bz2' + - name: Clone Rbenv source + run: | + git clone 'https://github.com/fullstaq-ruby/rbenv.git' rbenv-src + cd rbenv-src + git checkout 'fbaa15993171bf' + + ### Build Jemalloc ### + + - name: 'Build Jemalloc [ubuntu-24.04]' + run: | + ./build-jemalloc \ + -n 'ubuntu-24.04' \ + -s "$(pwd)/jemalloc-src.tar.bz2" \ + -o "$(pwd)/jemalloc-bin-ubuntu-24.04.tar.gz" \ + -j 2 + + - name: 'Build Jemalloc [el-9]' + run: | + ./build-jemalloc \ + -n 'el-9' \ + -s "$(pwd)/jemalloc-src.tar.bz2" \ + -o "$(pwd)/jemalloc-bin-el-9.tar.gz" \ + -j 2 + + ### Build Ruby binaries ### + + - name: 'Build Ruby binaries [ubuntu-24.04/normal]' + run: | + ./build-ruby \ + -n 'ubuntu-24.04' \ + -s "$(pwd)/ruby-src.tar.gz" \ + -v '4.0' \ + -o "$(pwd)/ruby-bin-ubuntu-24.04-normal.tar.gz" \ + -j 2 + + - name: 'Build Ruby binaries [ubuntu-24.04/jemalloc]' + run: | + ./build-ruby \ + -n 'ubuntu-24.04' \ + -s "$(pwd)/ruby-src.tar.gz" \ + -v '4.0' \ + -o "$(pwd)/ruby-bin-ubuntu-24.04-jemalloc.tar.gz" \ + -m "$(pwd)/jemalloc-bin-ubuntu-24.04.tar.gz" \ + -j 2 + + - name: 'Build Ruby binaries [el-9/normal]' + run: | + ./build-ruby \ + -n 'el-9' \ + -s "$(pwd)/ruby-src.tar.gz" \ + -v '4.0' \ + -o "$(pwd)/ruby-bin-el-9-normal.tar.gz" \ + -j 2 + + - name: 'Build Ruby binaries [el-9/jemalloc]' + run: | + ./build-ruby \ + -n 'el-9' \ + -s "$(pwd)/ruby-src.tar.gz" \ + -v '4.0' \ + -o "$(pwd)/ruby-bin-el-9-jemalloc.tar.gz" \ + -m "$(pwd)/jemalloc-bin-el-9.tar.gz" \ + -j 2 + + - name: Create packages directory + run: mkdir -p packages + + ### Build Ruby packages ### + + - name: 'Build Ruby package [ubuntu-24.04/normal]' + run: | + ./build-ruby-deb \ + -b "$(pwd)/ruby-bin-ubuntu-24.04-normal.tar.gz" \ + -o "$(pwd)/packages/fullstaq-ruby-4.0_2-ubuntu-24.04_amd64.deb" \ + -r '2' + + - name: 'Build Ruby package [ubuntu-24.04/jemalloc]' + run: | + ./build-ruby-deb \ + -b "$(pwd)/ruby-bin-ubuntu-24.04-jemalloc.tar.gz" \ + -o "$(pwd)/packages/fullstaq-ruby-4.0-jemalloc_2-ubuntu-24.04_amd64.deb" \ + -r '2' + + - name: 'Build Ruby package [el-9/normal]' + run: | + ./build-ruby-rpm \ + -b "$(pwd)/ruby-bin-el-9-normal.tar.gz" \ + -o "$(pwd)/packages/fullstaq-ruby-4.0-rev2-el9.x86_64.rpm" \ + -r '2' + + - name: 'Build Ruby package [el-9/jemalloc]' + run: | + ./build-ruby-rpm \ + -b "$(pwd)/ruby-bin-el-9-jemalloc.tar.gz" \ + -o "$(pwd)/packages/fullstaq-ruby-4.0-jemalloc-rev2-el9.x86_64.rpm" \ + -r '2' + + ### Build Rbenv packages ### + + - name: Build Rbenv DEB + run: | + ./build-rbenv-deb \ + -s "$(pwd)/rbenv-src" \ + -o "$(pwd)/packages/fullstaq-rbenv_1.1.2-16-1_all.deb" \ + -n '1.1.2-16' \ + -r '1' + - name: Build Rbenv RPM + run: | + ./build-rbenv-rpm \ + -s "$(pwd)/rbenv-src" \ + -o "$(pwd)/packages/fullstaq-rbenv-1.1.2_16-1.noarch.rpm" \ + -n '1.1.2-16' \ + -r '1' + + ### Build common packages ### + + - name: Build common DEB + run: | + ./build-common-deb \ + -o "$(pwd)/packages/fullstaq-ruby-common_1.0-1_all.deb" + - name: Build common RPM + run: | + ./build-common-rpm \ + -o "$(pwd)/packages/fullstaq-ruby-common-1.0-1.noarch.rpm" + + ### Upload all packages ### + + - name: Upload packages + uses: actions/upload-artifact@v4 + with: + name: contributor-packages + path: packages + + + test_packages: + name: 'Test [${{ matrix.distro }}/${{ matrix.variant }}]' + needs: build_packages + runs-on: ubuntu-24.04 + if: github.event_name == 'pull_request' || github.repository != 'fullstaq-ruby/server-edition' + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + include: + - distro: 'ubuntu-24.04' + variant: 'normal' + test_image: 'ubuntu:24.04' + package_format: 'DEB' + - distro: 'ubuntu-24.04' + variant: 'jemalloc' + test_image: 'ubuntu:24.04' + package_format: 'DEB' + - distro: 'el-9' + variant: 'normal' + test_image: 'rockylinux:9' + package_format: 'RPM' + - distro: 'el-9' + variant: 'jemalloc' + test_image: 'rockylinux:9' + package_format: 'RPM' + steps: + - uses: actions/checkout@v4 + + - name: Download packages + uses: actions/download-artifact@v4 + with: + name: contributor-packages + path: packages + + - name: Download utility Docker image + uses: actions/download-artifact@v4 + with: + name: 'docker-image-utility' + path: docker-image-utility + - name: Load utility Docker image + run: ./internal-scripts/ci-cd/load-docker-image.sh + env: + TARBALL: docker-image-utility/image.tar.zst + + - name: Determine Ruby package filename + id: ruby_pkg + run: | + VARIANT="${{ matrix.variant }}" + DISTRO="${{ matrix.distro }}" + FORMAT="${{ matrix.package_format }}" + if [ "$VARIANT" = "normal" ]; then + VARIANT_SUFFIX="" + else + VARIANT_SUFFIX="-${VARIANT}" + fi + if [ "$FORMAT" = "DEB" ]; then + FILENAME="fullstaq-ruby-4.0${VARIANT_SUFFIX}_2-${DISTRO}_amd64.deb" + else + SANITIZED_DISTRO=$(echo "$DISTRO" | tr -d '-') + FILENAME="fullstaq-ruby-4.0${VARIANT_SUFFIX}-rev2-${SANITIZED_DISTRO}.x86_64.rpm" + fi + echo "filename=${FILENAME}" >> "$GITHUB_OUTPUT" + + - name: Test DEBs + if: matrix.package_format == 'DEB' + run: | + ./test-debs \ + -i '${{ matrix.test_image }}' \ + -v '${{ matrix.variant }}' \ + -r "$(pwd)/packages/${{ steps.ruby_pkg.outputs.filename }}" \ + -b "$(pwd)/packages/fullstaq-rbenv_1.1.2-16-1_all.deb" \ + -c "$(pwd)/packages/fullstaq-ruby-common_1.0-1_all.deb" + - name: Test RPMs + if: matrix.package_format == 'RPM' + run: | + ./test-rpms \ + -i '${{ matrix.test_image }}' \ + -v '${{ matrix.variant }}' \ + -r "$(pwd)/packages/${{ steps.ruby_pkg.outputs.filename }}" \ + -b "$(pwd)/packages/fullstaq-rbenv-1.1.2_16-1.noarch.rpm" \ + -c "$(pwd)/packages/fullstaq-ruby-common-1.0-1.noarch.rpm" diff --git a/.github/workflows/ci-cd-contributor.yml.erb b/.github/workflows/ci-cd-contributor.yml.erb new file mode 100644 index 00000000..bbb32b77 --- /dev/null +++ b/.github/workflows/ci-cd-contributor.yml.erb @@ -0,0 +1,290 @@ +<%= editing_warning_comment('ci-cd-contributor') %> +<%- rpv = latest_ruby_package_version -%> + +name: 'CI/CD: contributor' + +on: + push: + paths-ignore: + - '**.md' + - 'dev-handbook/**' + pull_request: + paths-ignore: + - '**.md' + - 'dev-handbook/**' + workflow_dispatch: + +jobs: + lint: + name: Check whether workflow is up-to-date + runs-on: ubuntu-24.04 + if: github.event_name == 'pull_request' || github.repository != 'fullstaq-ruby/server-edition' + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3' + - name: Check + run: ./internal-scripts/ci-cd/check-workflow-uptodate/check.sh + + + build_images: + name: Build Docker images + needs: lint + runs-on: ubuntu-24.04 + if: github.event_name == 'pull_request' || github.repository != 'fullstaq-ruby/server-edition' + steps: + - uses: actions/checkout@v4 + + <%- contributor_distributions.each do |distro| -%> + <%- image = docker_images.find { |img| img[:id] == distro[:name] } -%> + - name: 'Build Docker image [<%= distro[:name] %>]' + run: ./internal-scripts/ci-cd/build-docker-images/build.sh + env: + IMAGE_NAME: '<%= image[:name] %>' + IMAGE_TAG: '<%= image[:tag] %>' + SOURCE_DIR: 'environments/<%= image[:id] %>' + - name: 'Dump Docker image [<%= distro[:name] %>]' + run: ./internal-scripts/ci-cd/build-docker-images/dump-image.sh + env: + IMAGE_NAME: '<%= image[:name] %>' + IMAGE_TAG: '<%= image[:tag] %>' + - name: 'Upload Docker image artifact [<%= distro[:name] %>]' + uses: actions/upload-artifact@v4 + with: + name: '<%= docker_image_artifact_name(distro[:name]) %>' + path: output + - name: Clean up Docker image output + run: rm -rf output + + <%- end -%> + <%- utility_image = docker_images.find { |img| img[:id] == 'utility' } -%> + - name: 'Build Docker image [utility]' + run: ./internal-scripts/ci-cd/build-docker-images/build.sh + env: + IMAGE_NAME: '<%= utility_image[:name] %>' + IMAGE_TAG: '<%= utility_image[:tag] %>' + SOURCE_DIR: 'environments/utility' + - name: 'Dump Docker image [utility]' + run: ./internal-scripts/ci-cd/build-docker-images/dump-image.sh + env: + IMAGE_NAME: '<%= utility_image[:name] %>' + IMAGE_TAG: '<%= utility_image[:tag] %>' + - name: 'Upload Docker image artifact [utility]' + uses: actions/upload-artifact@v4 + with: + name: '<%= docker_image_artifact_name('utility') %>' + path: output + - name: Clean up Docker image output + run: rm -rf output + + + build_packages: + name: Build packages + needs: build_images + runs-on: ubuntu-24.04 + if: github.event_name == 'pull_request' || github.repository != 'fullstaq-ruby/server-edition' + timeout-minutes: 60 + steps: + - uses: actions/checkout@v4 + + ### Download and load Docker images ### + + <%- contributor_distributions.each do |distro| -%> + - name: 'Download Docker image artifact [<%= distro[:name] %>]' + uses: actions/download-artifact@v4 + with: + name: '<%= docker_image_artifact_name(distro[:name]) %>' + path: docker-image-<%= distro[:name] %> + - name: 'Load Docker image [<%= distro[:name] %>]' + run: ./internal-scripts/ci-cd/load-docker-image.sh + env: + TARBALL: docker-image-<%= distro[:name] %>/image.tar.zst + + <%- end -%> + - name: 'Download Docker image artifact [utility]' + uses: actions/download-artifact@v4 + with: + name: '<%= docker_image_artifact_name('utility') %>' + path: docker-image-utility + - name: 'Load Docker image [utility]' + run: ./internal-scripts/ci-cd/load-docker-image.sh + env: + TARBALL: docker-image-utility/image.tar.zst + + ### Download sources ### + + - name: Download Ruby source + run: curl -fsSL -o ruby-src.tar.gz '<%= ruby_source_url(rpv[:full_version]) %>' + - name: Download Jemalloc source + run: curl -fsSL -o jemalloc-src.tar.bz2 '<%= jemalloc_source_url %>' + - name: Clone Rbenv source + run: | + git clone '<%= config[:rbenv][:repo] %>' rbenv-src + cd rbenv-src + git checkout '<%= config[:rbenv][:ref] %>' + + ### Build Jemalloc ### + + <%- contributor_distributions.each do |distro| -%> + - name: 'Build Jemalloc [<%= distro[:name] %>]' + run: | + ./build-jemalloc \ + -n '<%= distro[:name] %>' \ + -s "$(pwd)/jemalloc-src.tar.bz2" \ + -o "$(pwd)/jemalloc-bin-<%= distro[:name] %>.tar.gz" \ + -j 2 + + <%- end -%> + ### Build Ruby binaries ### + + <%- contributor_distributions.each do |distro| -%> + <%- variants_for_ruby_version(rpv).each do |variant| -%> + - name: 'Build Ruby binaries [<%= distro[:name] %>/<%= variant[:name] %>]' + run: | + ./build-ruby \ + -n '<%= distro[:name] %>' \ + -s "$(pwd)/ruby-src.tar.gz" \ + -v '<%= rpv[:id] %>' \ + -o "$(pwd)/ruby-bin-<%= distro[:name] %>-<%= variant[:name] %>.tar.gz" \ + <%- if variant[:name] == 'jemalloc' -%> + -m "$(pwd)/jemalloc-bin-<%= distro[:name] %>.tar.gz" \ + <%- elsif variant[:name] == 'malloctrim' -%> + -t \ + <%- end -%> + -j 2 + + <%- end -%> + <%- end -%> + - name: Create packages directory + run: mkdir -p packages + + ### Build Ruby packages ### + + <%- contributor_distributions.each do |distro| -%> + <%- variants_for_ruby_version(rpv).each do |variant| -%> + <%- pkg_basename = ruby_package_basename(rpv, distro, variant) -%> + - name: 'Build Ruby package [<%= distro[:name] %>/<%= variant[:name] %>]' + run: | + <%- if distro[:package_format] == :DEB -%> + ./build-ruby-deb \ + <%- else -%> + ./build-ruby-rpm \ + <%- end -%> + -b "$(pwd)/ruby-bin-<%= distro[:name] %>-<%= variant[:name] %>.tar.gz" \ + -o "$(pwd)/packages/<%= pkg_basename %>" \ + -r '<%= rpv[:package_revision] %>' + + <%- end -%> + <%- end -%> + ### Build Rbenv packages ### + + - name: Build Rbenv DEB + run: | + ./build-rbenv-deb \ + -s "$(pwd)/rbenv-src" \ + -o "$(pwd)/packages/<%= rbenv_package_basename(:DEB) %>" \ + -n '<%= rbenv_version %>' \ + -r '<%= rbenv_package_revision %>' + - name: Build Rbenv RPM + run: | + ./build-rbenv-rpm \ + -s "$(pwd)/rbenv-src" \ + -o "$(pwd)/packages/<%= rbenv_package_basename(:RPM) %>" \ + -n '<%= rbenv_version %>' \ + -r '<%= rbenv_package_revision %>' + + ### Build common packages ### + + - name: Build common DEB + run: | + ./build-common-deb \ + -o "$(pwd)/packages/<%= common_package_basename(:DEB) %>" + - name: Build common RPM + run: | + ./build-common-rpm \ + -o "$(pwd)/packages/<%= common_package_basename(:RPM) %>" + + ### Upload all packages ### + + - name: Upload packages + uses: actions/upload-artifact@v4 + with: + name: contributor-packages + path: packages + + + test_packages: + name: 'Test [${{ matrix.distro }}/${{ matrix.variant }}]' + needs: build_packages + runs-on: ubuntu-24.04 + if: github.event_name == 'pull_request' || github.repository != 'fullstaq-ruby/server-edition' + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + include: + <%- contributor_distributions.each do |distro| -%> + <%- variants_for_ruby_version(rpv).each do |variant| -%> + - distro: '<%= distro[:name] %>' + variant: '<%= variant[:name] %>' + test_image: '<%= distro[:test_image] %>' + package_format: '<%= distro[:package_format] %>' + <%- end -%> + <%- end -%> + steps: + - uses: actions/checkout@v4 + + - name: Download packages + uses: actions/download-artifact@v4 + with: + name: contributor-packages + path: packages + + - name: Download utility Docker image + uses: actions/download-artifact@v4 + with: + name: '<%= docker_image_artifact_name('utility') %>' + path: docker-image-utility + - name: Load utility Docker image + run: ./internal-scripts/ci-cd/load-docker-image.sh + env: + TARBALL: docker-image-utility/image.tar.zst + + - name: Determine Ruby package filename + id: ruby_pkg + run: | + VARIANT="${{ matrix.variant }}" + DISTRO="${{ matrix.distro }}" + FORMAT="${{ matrix.package_format }}" + if [ "$VARIANT" = "normal" ]; then + VARIANT_SUFFIX="" + else + VARIANT_SUFFIX="-${VARIANT}" + fi + if [ "$FORMAT" = "DEB" ]; then + FILENAME="fullstaq-ruby-<%= rpv[:id] %>${VARIANT_SUFFIX}_<%= rpv[:package_revision] %>-${DISTRO}_amd64.deb" + else + SANITIZED_DISTRO=$(echo "$DISTRO" | tr -d '-') + FILENAME="fullstaq-ruby-<%= rpv[:id] %>${VARIANT_SUFFIX}-rev<%= rpv[:package_revision] %>-${SANITIZED_DISTRO}.x86_64.rpm" + fi + echo "filename=${FILENAME}" >> "$GITHUB_OUTPUT" + + - name: Test DEBs + if: matrix.package_format == 'DEB' + run: | + ./test-debs \ + -i '${{ matrix.test_image }}' \ + -v '${{ matrix.variant }}' \ + -r "$(pwd)/packages/${{ steps.ruby_pkg.outputs.filename }}" \ + -b "$(pwd)/packages/<%= rbenv_package_basename(:DEB) %>" \ + -c "$(pwd)/packages/<%= common_package_basename(:DEB) %>" + - name: Test RPMs + if: matrix.package_format == 'RPM' + run: | + ./test-rpms \ + -i '${{ matrix.test_image }}' \ + -v '${{ matrix.variant }}' \ + -r "$(pwd)/packages/${{ steps.ruby_pkg.outputs.filename }}" \ + -b "$(pwd)/packages/<%= rbenv_package_basename(:RPM) %>" \ + -c "$(pwd)/packages/<%= common_package_basename(:RPM) %>" diff --git a/.github/workflows/ci-cd-label-guard.yml b/.github/workflows/ci-cd-label-guard.yml new file mode 100644 index 00000000..dcf44fd6 --- /dev/null +++ b/.github/workflows/ci-cd-label-guard.yml @@ -0,0 +1,34 @@ +name: 'CI/CD: label guard' + +on: + pull_request_target: + types: [synchronize] + +jobs: + remove_ok_to_test: + name: Remove ok-to-test label on new push + runs-on: ubuntu-24.04 + permissions: + pull-requests: write + steps: + - name: Remove ok-to-test label if present + uses: actions/github-script@v7 + with: + script: | + const { owner, repo } = context.repo; + const issue_number = context.payload.pull_request.number; + try { + await github.rest.issues.removeLabel({ + owner, + repo, + issue_number, + name: 'ok-to-test' + }); + console.log('Removed ok-to-test label'); + } catch (e) { + if (e.status === 404) { + console.log('Label ok-to-test not present, nothing to remove'); + } else { + throw e; + } + } diff --git a/.github/workflows/ci-cd-main.yml b/.github/workflows/ci-cd-main.yml index 14fe99ee..832c4e47 100644 --- a/.github/workflows/ci-cd-main.yml +++ b/.github/workflows/ci-cd-main.yml @@ -22,6 +22,11 @@ on: paths-ignore: - '**.md' - 'dev-handbook/**' + pull_request_target: + types: [labeled] + paths-ignore: + - '**.md' + - 'dev-handbook/**' env: ## Set the following variable to a specific number to make the @@ -39,6 +44,10 @@ jobs: determine_necessary_jobs: name: Determine necessary jobs runs-on: ubuntu-24.04 + if: > + github.repository == 'fullstaq-ruby/server-edition' + && (github.event_name != 'pull_request_target' + || github.event.label.name == 'ok-to-test') environment: test permissions: id-token: write @@ -54,6 +63,8 @@ jobs: if: github.event_name == 'push' && github.ref == 'refs/heads/fix/cicd-new-workflows' - uses: actions/checkout@v4 + with: + ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.ref }} - uses: google-github-actions/auth@v2 with: project_id: ${{ vars.GCLOUD_PROJECT_ID }} @@ -83,6 +94,7 @@ jobs: check_workflow_uptodate: name: Check whether workflow is up-to-date runs-on: ubuntu-24.04 + if: github.repository == 'fullstaq-ruby/server-edition' steps: - uses: actions/checkout@v4 - name: Check diff --git a/.github/workflows/ci-cd-main.yml.erb b/.github/workflows/ci-cd-main.yml.erb index 85245e8f..9f8ddaa1 100644 --- a/.github/workflows/ci-cd-main.yml.erb +++ b/.github/workflows/ci-cd-main.yml.erb @@ -12,6 +12,11 @@ on: paths-ignore: - '**.md' - 'dev-handbook/**' + pull_request_target: + types: [labeled] + paths-ignore: + - '**.md' + - 'dev-handbook/**' env: ## Set the following variable to a specific number to make the @@ -29,6 +34,10 @@ jobs: determine_necessary_jobs: name: Determine necessary jobs runs-on: ubuntu-24.04 + if: > + github.repository == 'fullstaq-ruby/server-edition' + && (github.event_name != 'pull_request_target' + || github.event.label.name == 'ok-to-test') environment: test permissions: id-token: write @@ -44,6 +53,8 @@ jobs: if: github.event_name == 'push' && github.ref == 'refs/heads/fix/cicd-new-workflows' - uses: actions/checkout@v4 + with: + ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.ref }} - uses: google-github-actions/auth@v2 with: project_id: ${{ vars.GCLOUD_PROJECT_ID }} @@ -73,6 +84,7 @@ jobs: check_workflow_uptodate: name: Check whether workflow is up-to-date runs-on: ubuntu-24.04 + if: github.repository == 'fullstaq-ruby/server-edition' steps: - uses: actions/checkout@v4 - name: Check diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0a113448..88ec7864 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -26,6 +26,27 @@ Have a look at our [issue tracker](https://github.com/fullstaq-ruby/server-editi To learn how the Fullstaq Ruby Server Edition codebase works and how to develop it, please read our [development handbook](dev-handbook/README.md). +## Testing your changes + +### Automated CI on forks + +When you push to your fork or open a pull request, a contributor CI workflow runs automatically. It: + + * Validates CI/CD workflow YAML is up-to-date. + * Builds Ruby packages for two representative distributions (Ubuntu 24.04 and Enterprise Linux 9) with all three variants (normal, jemalloc, malloctrim). + * Runs smoke tests that install the packages and verify Ruby works correctly. + +No cloud credentials or special setup are needed — the workflow uses only Docker and GitHub Actions. + +If a maintainer wants to run the full CI pipeline against your PR (which tests all distributions and publishes to test repositories), they will add the `ok-to-test` label. This label is automatically removed when you push new commits, so the maintainer must re-review and re-label after each update. + +### Local builds and testing + +For faster iteration, you can build and test packages on your own machine. You only need Docker and Ruby >= 3.2: + + * **Building packages:** see [Building packages locally](dev-handbook/building-packages-locally.md) + * **Testing packages:** see [Testing packages locally](dev-handbook/testing-packages-locally.md) + ## Stuck? Need help? Please post something on our [discussion forum](https://github.com/fullstaq-ruby/server-edition/discussions). diff --git a/config.yml b/config.yml index 0689593d..77ebd88c 100644 --- a/config.yml +++ b/config.yml @@ -105,6 +105,13 @@ variant_exclusions: ## `package_revision` numbers too! jemalloc_version: '3.6.0' +## (Optional) +## Which distributions to include in the contributor CI workflow. +## This is a small subset used for fast feedback on forks. +contributor_distributions: + - ubuntu-24.04 + - el-9 + ## (Required) ## Specify which distributions to build packages for. ## When set to 'all', will build for all supported distributions. diff --git a/container-entrypoints/test-rpms-prepare b/container-entrypoints/test-rpms-prepare index 7b32b120..be928908 100755 --- a/container-entrypoints/test-rpms-prepare +++ b/container-entrypoints/test-rpms-prepare @@ -14,4 +14,4 @@ require_container_mount "$OUTPUT_PATH" run cp /input/* /output/ -run createrepo /output +run createrepo_c /output diff --git a/dev-handbook/building-packages-locally.md b/dev-handbook/building-packages-locally.md index 62c12b33..8ef60a93 100644 --- a/dev-handbook/building-packages-locally.md +++ b/dev-handbook/building-packages-locally.md @@ -1,5 +1,7 @@ # Building packages locally +> **Tip:** If you just want to validate that your changes compile and pass tests, the contributor CI workflow does this automatically when you push to your fork. See [CONTRIBUTING.md](../CONTRIBUTING.md#testing-your-changes) for details. The instructions below are for local builds when you want faster iteration or need to debug packaging issues. + This tutorial shows how you can build packages on your own computer. Because we follow [the "minimal dependencies" principle](minimal-dependencies-principle.md), you can build packages for any distribution, regardless of which OS or distribution you're running on. diff --git a/internal-scripts/generate-ci-cd-yaml.rb b/internal-scripts/generate-ci-cd-yaml.rb index db4f87c3..e8ef5a0a 100755 --- a/internal-scripts/generate-ci-cd-yaml.rb +++ b/internal-scripts/generate-ci-cd-yaml.rb @@ -35,6 +35,7 @@ def main end generate_yaml_file_from_template(template_name: 'ci-cd-publish-test-test') generate_yaml_file_from_template(template_name: 'ci-cd-publish-test-production') + generate_yaml_file_from_template(template_name: 'ci-cd-contributor') end private diff --git a/internal-scripts/test-debs b/internal-scripts/test-debs index 3e07aa4d..edbf41d0 100755 --- a/internal-scripts/test-debs +++ b/internal-scripts/test-debs @@ -9,7 +9,7 @@ source "$SELFDIR/../lib/library.sh" header "Installing packages" -ESSENTIAL_PACKAGES=(perl sudo binutils) +ESSENTIAL_PACKAGES=(perl sudo binutils adduser) if [[ "$SERVER" = "" ]]; then echo "+ Create /etc/apt/sources.list.d/local.list" diff --git a/lib/ci_workflow_support.rb b/lib/ci_workflow_support.rb index 5255440d..a8fa265e 100644 --- a/lib/ci_workflow_support.rb +++ b/lib/ci_workflow_support.rb @@ -76,6 +76,20 @@ def distributions end end + def contributor_distributions + @contributor_distributions ||= begin + names = config[:contributor_distributions] + return [] if names.nil? + names.map do |name| + { + name: name, + package_format: autodetect_package_format(name), + test_image: determine_test_image_for(name), + } + end + end + end + def distribution_buckets @distribution_buckets ||= GeneralSupport.bucketize(distributions, DISTRIBUTIONS_BUCKETS_MAX_NUM) @@ -261,6 +275,10 @@ def ruby_package_versions end end + def latest_ruby_package_version + ruby_package_versions.first + end + def ruby_package_versions_for_distro(distro) result = ruby_package_versions.find_all do |ruby_package_version| !ruby_package_version_excluded_from_distro?(ruby_package_version, distro) From 1aaef3c1ab243a4c35e57be35c02d25f37c671dd Mon Sep 17 00:00:00 2001 From: abtreece Date: Fri, 22 May 2026 12:52:16 -0500 Subject: [PATCH 2/9] refactor(ci): extract build_distribution_entries helper Deduplicates the distribution entry mapping between `distributions` and `contributor_distributions`, and fixes a memoization break where `return [] if names.nil?` exited the method without assigning the `||= begin` block. --- lib/ci_workflow_support.rb | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/lib/ci_workflow_support.rb b/lib/ci_workflow_support.rb index a8fa265e..c9a1af07 100644 --- a/lib/ci_workflow_support.rb +++ b/lib/ci_workflow_support.rb @@ -66,27 +66,21 @@ def distributions abort "Config error: 'distributions' must be set to 'all' or to a list" end - names.map do |name| - { - name: name, - package_format: autodetect_package_format(name), - test_image: determine_test_image_for(name), - } - end + build_distribution_entries(names) end end def contributor_distributions - @contributor_distributions ||= begin - names = config[:contributor_distributions] - return [] if names.nil? - names.map do |name| - { - name: name, - package_format: autodetect_package_format(name), - test_image: determine_test_image_for(name), - } - end + @contributor_distributions ||= build_distribution_entries(config[:contributor_distributions] || []) + end + + def build_distribution_entries(names) + names.map do |name| + { + name: name, + package_format: autodetect_package_format(name), + test_image: determine_test_image_for(name), + } end end From 67b807cc2008975456d9705b1fbe13b1cfb52f3b Mon Sep 17 00:00:00 2001 From: abtreece Date: Fri, 22 May 2026 13:00:46 -0500 Subject: [PATCH 3/9] fix(ci): make latest_ruby_package_version independent of YAML order Previously relied on `ruby_package_versions.first`, which in turn depended on the order of `minor_version_packages` and `tiny_version_packages` in `config.yml`. Use `max_by` with `Gem::Version` so the result is deterministic regardless of config ordering. Considered sorting the full `ruby_package_versions` list (the broader fix Noah suggested), but that interleaves minor/tiny entries and produces a ~9000-line rendered-YAML diff with no functional change to which version is "latest". Targeting `latest_ruby_package_version` directly addresses the robustness concern with minimal blast radius. --- lib/ci_workflow_support.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ci_workflow_support.rb b/lib/ci_workflow_support.rb index c9a1af07..71407734 100644 --- a/lib/ci_workflow_support.rb +++ b/lib/ci_workflow_support.rb @@ -270,7 +270,7 @@ def ruby_package_versions end def latest_ruby_package_version - ruby_package_versions.first + ruby_package_versions.max_by { |entry| Gem::Version.new(entry[:full_version]) } end def ruby_package_versions_for_distro(distro) From f2897ce2c716c7dc194b597457704dd2b7e1ae95 Mon Sep 17 00:00:00 2001 From: abtreece Date: Fri, 22 May 2026 13:14:13 -0500 Subject: [PATCH 4/9] fix(ci): skip contributor workflow for same-repo PRs The previous guard `event_name == 'pull_request' || repository != upstream` fired for both fork PRs AND same-repo PRs, causing the contributor workflow to run alongside ci-cd-main on internal branches and duplicating every build. Tighten the condition so the workflow only runs when either (a) it's executing in a fork's own repo, or (b) it's executing in upstream for a PR whose head is in a fork. --- .github/workflows/ci-cd-contributor.yml | 20 ++++++++++++++++---- .github/workflows/ci-cd-contributor.yml.erb | 20 ++++++++++++++++---- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci-cd-contributor.yml b/.github/workflows/ci-cd-contributor.yml index 5ecd65d3..a565d452 100644 --- a/.github/workflows/ci-cd-contributor.yml +++ b/.github/workflows/ci-cd-contributor.yml @@ -27,7 +27,10 @@ jobs: lint: name: Check whether workflow is up-to-date runs-on: ubuntu-24.04 - if: github.event_name == 'pull_request' || github.repository != 'fullstaq-ruby/server-edition' + if: > + github.repository != 'fullstaq-ruby/server-edition' + || (github.event_name == 'pull_request' + && github.event.pull_request.head.repo.full_name != github.repository) steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 @@ -41,7 +44,10 @@ jobs: name: Build Docker images needs: lint runs-on: ubuntu-24.04 - if: github.event_name == 'pull_request' || github.repository != 'fullstaq-ruby/server-edition' + if: > + github.repository != 'fullstaq-ruby/server-edition' + || (github.event_name == 'pull_request' + && github.event.pull_request.head.repo.full_name != github.repository) steps: - uses: actions/checkout@v4 @@ -107,7 +113,10 @@ jobs: name: Build packages needs: build_images runs-on: ubuntu-24.04 - if: github.event_name == 'pull_request' || github.repository != 'fullstaq-ruby/server-edition' + if: > + github.repository != 'fullstaq-ruby/server-edition' + || (github.event_name == 'pull_request' + && github.event.pull_request.head.repo.full_name != github.repository) timeout-minutes: 60 steps: - uses: actions/checkout@v4 @@ -288,7 +297,10 @@ jobs: name: 'Test [${{ matrix.distro }}/${{ matrix.variant }}]' needs: build_packages runs-on: ubuntu-24.04 - if: github.event_name == 'pull_request' || github.repository != 'fullstaq-ruby/server-edition' + if: > + github.repository != 'fullstaq-ruby/server-edition' + || (github.event_name == 'pull_request' + && github.event.pull_request.head.repo.full_name != github.repository) timeout-minutes: 30 strategy: fail-fast: false diff --git a/.github/workflows/ci-cd-contributor.yml.erb b/.github/workflows/ci-cd-contributor.yml.erb index bbb32b77..e6ce3a16 100644 --- a/.github/workflows/ci-cd-contributor.yml.erb +++ b/.github/workflows/ci-cd-contributor.yml.erb @@ -18,7 +18,10 @@ jobs: lint: name: Check whether workflow is up-to-date runs-on: ubuntu-24.04 - if: github.event_name == 'pull_request' || github.repository != 'fullstaq-ruby/server-edition' + if: > + github.repository != 'fullstaq-ruby/server-edition' + || (github.event_name == 'pull_request' + && github.event.pull_request.head.repo.full_name != github.repository) steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 @@ -32,7 +35,10 @@ jobs: name: Build Docker images needs: lint runs-on: ubuntu-24.04 - if: github.event_name == 'pull_request' || github.repository != 'fullstaq-ruby/server-edition' + if: > + github.repository != 'fullstaq-ruby/server-edition' + || (github.event_name == 'pull_request' + && github.event.pull_request.head.repo.full_name != github.repository) steps: - uses: actions/checkout@v4 @@ -83,7 +89,10 @@ jobs: name: Build packages needs: build_images runs-on: ubuntu-24.04 - if: github.event_name == 'pull_request' || github.repository != 'fullstaq-ruby/server-edition' + if: > + github.repository != 'fullstaq-ruby/server-edition' + || (github.event_name == 'pull_request' + && github.event.pull_request.head.repo.full_name != github.repository) timeout-minutes: 60 steps: - uses: actions/checkout@v4 @@ -218,7 +227,10 @@ jobs: name: 'Test [${{ matrix.distro }}/${{ matrix.variant }}]' needs: build_packages runs-on: ubuntu-24.04 - if: github.event_name == 'pull_request' || github.repository != 'fullstaq-ruby/server-edition' + if: > + github.repository != 'fullstaq-ruby/server-edition' + || (github.event_name == 'pull_request' + && github.event.pull_request.head.repo.full_name != github.repository) timeout-minutes: 30 strategy: fail-fast: false From 8a02f8b4b46854ae55c73016724ac9553aa90620 Mon Sep 17 00:00:00 2001 From: abtreece Date: Fri, 22 May 2026 13:15:25 -0500 Subject: [PATCH 5/9] fix(ci): add timeout-minutes to contributor lint and build_images jobs Prevents stuck runners from lingering at the default 360-minute timeout. build_packages (60m) and test_packages (30m) already had explicit timeouts; this adds 10m for lint and 30m for build_images. --- .github/workflows/ci-cd-contributor.yml | 2 ++ .github/workflows/ci-cd-contributor.yml.erb | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/ci-cd-contributor.yml b/.github/workflows/ci-cd-contributor.yml index a565d452..57c3f7de 100644 --- a/.github/workflows/ci-cd-contributor.yml +++ b/.github/workflows/ci-cd-contributor.yml @@ -31,6 +31,7 @@ jobs: github.repository != 'fullstaq-ruby/server-edition' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) + timeout-minutes: 10 steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 @@ -48,6 +49,7 @@ jobs: github.repository != 'fullstaq-ruby/server-edition' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) + timeout-minutes: 30 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/ci-cd-contributor.yml.erb b/.github/workflows/ci-cd-contributor.yml.erb index e6ce3a16..a33870a7 100644 --- a/.github/workflows/ci-cd-contributor.yml.erb +++ b/.github/workflows/ci-cd-contributor.yml.erb @@ -22,6 +22,7 @@ jobs: github.repository != 'fullstaq-ruby/server-edition' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) + timeout-minutes: 10 steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 @@ -39,6 +40,7 @@ jobs: github.repository != 'fullstaq-ruby/server-edition' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) + timeout-minutes: 30 steps: - uses: actions/checkout@v4 From 00efae4c2e2354e355b6f5a9c717a093af49507a Mon Sep 17 00:00:00 2001 From: abtreece Date: Fri, 22 May 2026 14:10:47 -0500 Subject: [PATCH 6/9] refactor(ci): parallelize contributor builds via matrix jobs Splits the contributor workflow's previously-monolithic build_images and build_packages jobs into per-distro matrix jobs, and extracts distro-agnostic packages (rbenv, common) into a separate build_common_packages job that runs in parallel. Also sets retention-days: 1 on all contributor artifacts -- they're purely job-to-job intermediate state and don't need the default 90-day retention. --- .github/workflows/ci-cd-contributor.yml | 266 ++++++++++---------- .github/workflows/ci-cd-contributor.yml.erb | 198 +++++++++------ 2 files changed, 245 insertions(+), 219 deletions(-) diff --git a/.github/workflows/ci-cd-contributor.yml b/.github/workflows/ci-cd-contributor.yml index 57c3f7de..d0587471 100644 --- a/.github/workflows/ci-cd-contributor.yml +++ b/.github/workflows/ci-cd-contributor.yml @@ -42,7 +42,7 @@ jobs: build_images: - name: Build Docker images + name: 'Build Docker image [${{ matrix.distro }}]' needs: lint runs-on: ubuntu-24.04 if: > @@ -50,69 +50,48 @@ jobs: || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + include: + - distro: 'ubuntu-24.04' + image_name: 'ghcr.io/fullstaq-ruby/server-edition-ci-images' + image_tag: 'ubuntu-24.04-v2' + source_dir: 'environments/ubuntu-24.04' + artifact_name: 'docker-image-ubuntu-24.04' + - distro: 'el-9' + image_name: 'ghcr.io/fullstaq-ruby/server-edition-ci-images' + image_tag: 'el-9-v2' + source_dir: 'environments/el-9' + artifact_name: 'docker-image-el-9' + - distro: 'utility' + image_name: 'ghcr.io/fullstaq-ruby/server-edition-ci-images' + image_tag: 'utility-v3' + source_dir: 'environments/utility' + artifact_name: 'docker-image-utility' steps: - uses: actions/checkout@v4 - - - name: 'Build Docker image [ubuntu-24.04]' - run: ./internal-scripts/ci-cd/build-docker-images/build.sh - env: - IMAGE_NAME: 'ghcr.io/fullstaq-ruby/server-edition-ci-images' - IMAGE_TAG: 'ubuntu-24.04-v2' - SOURCE_DIR: 'environments/ubuntu-24.04' - - name: 'Dump Docker image [ubuntu-24.04]' - run: ./internal-scripts/ci-cd/build-docker-images/dump-image.sh - env: - IMAGE_NAME: 'ghcr.io/fullstaq-ruby/server-edition-ci-images' - IMAGE_TAG: 'ubuntu-24.04-v2' - - name: 'Upload Docker image artifact [ubuntu-24.04]' - uses: actions/upload-artifact@v4 - with: - name: 'docker-image-ubuntu-24.04' - path: output - - name: Clean up Docker image output - run: rm -rf output - - - name: 'Build Docker image [el-9]' - run: ./internal-scripts/ci-cd/build-docker-images/build.sh - env: - IMAGE_NAME: 'ghcr.io/fullstaq-ruby/server-edition-ci-images' - IMAGE_TAG: 'el-9-v2' - SOURCE_DIR: 'environments/el-9' - - name: 'Dump Docker image [el-9]' - run: ./internal-scripts/ci-cd/build-docker-images/dump-image.sh - env: - IMAGE_NAME: 'ghcr.io/fullstaq-ruby/server-edition-ci-images' - IMAGE_TAG: 'el-9-v2' - - name: 'Upload Docker image artifact [el-9]' - uses: actions/upload-artifact@v4 - with: - name: 'docker-image-el-9' - path: output - - name: Clean up Docker image output - run: rm -rf output - - - name: 'Build Docker image [utility]' + - name: Build Docker image run: ./internal-scripts/ci-cd/build-docker-images/build.sh env: - IMAGE_NAME: 'ghcr.io/fullstaq-ruby/server-edition-ci-images' - IMAGE_TAG: 'utility-v3' - SOURCE_DIR: 'environments/utility' - - name: 'Dump Docker image [utility]' + IMAGE_NAME: '${{ matrix.image_name }}' + IMAGE_TAG: '${{ matrix.image_tag }}' + SOURCE_DIR: '${{ matrix.source_dir }}' + - name: Dump Docker image run: ./internal-scripts/ci-cd/build-docker-images/dump-image.sh env: - IMAGE_NAME: 'ghcr.io/fullstaq-ruby/server-edition-ci-images' - IMAGE_TAG: 'utility-v3' - - name: 'Upload Docker image artifact [utility]' + IMAGE_NAME: '${{ matrix.image_name }}' + IMAGE_TAG: '${{ matrix.image_tag }}' + - name: Upload Docker image artifact uses: actions/upload-artifact@v4 with: - name: 'docker-image-utility' + name: '${{ matrix.artifact_name }}' path: output - - name: Clean up Docker image output - run: rm -rf output + retention-days: 1 build_packages: - name: Build packages + name: 'Build packages [${{ matrix.distro }}]' needs: build_images runs-on: ubuntu-24.04 if: > @@ -120,37 +99,36 @@ jobs: || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + include: + - distro: 'ubuntu-24.04' + package_format: 'DEB' + image_artifact_name: 'docker-image-ubuntu-24.04' + - distro: 'el-9' + package_format: 'RPM' + image_artifact_name: 'docker-image-el-9' steps: - uses: actions/checkout@v4 ### Download and load Docker images ### - - name: 'Download Docker image artifact [ubuntu-24.04]' - uses: actions/download-artifact@v4 - with: - name: 'docker-image-ubuntu-24.04' - path: docker-image-ubuntu-24.04 - - name: 'Load Docker image [ubuntu-24.04]' - run: ./internal-scripts/ci-cd/load-docker-image.sh - env: - TARBALL: docker-image-ubuntu-24.04/image.tar.zst - - - name: 'Download Docker image artifact [el-9]' + - name: Download distro Docker image uses: actions/download-artifact@v4 with: - name: 'docker-image-el-9' - path: docker-image-el-9 - - name: 'Load Docker image [el-9]' + name: '${{ matrix.image_artifact_name }}' + path: docker-image-distro + - name: Load distro Docker image run: ./internal-scripts/ci-cd/load-docker-image.sh env: - TARBALL: docker-image-el-9/image.tar.zst - - - name: 'Download Docker image artifact [utility]' + TARBALL: docker-image-distro/image.tar.zst + - name: Download utility Docker image uses: actions/download-artifact@v4 with: name: 'docker-image-utility' path: docker-image-utility - - name: 'Load Docker image [utility]' + - name: Load utility Docker image run: ./internal-scripts/ci-cd/load-docker-image.sh env: TARBALL: docker-image-utility/image.tar.zst @@ -161,68 +139,36 @@ jobs: run: curl -fsSL -o ruby-src.tar.gz 'https://cache.ruby-lang.org/pub/ruby/4.0/ruby-4.0.3.tar.gz' - name: Download Jemalloc source run: curl -fsSL -o jemalloc-src.tar.bz2 'https://github.com/jemalloc/jemalloc/releases/download/3.6.0/jemalloc-3.6.0.tar.bz2' - - name: Clone Rbenv source - run: | - git clone 'https://github.com/fullstaq-ruby/rbenv.git' rbenv-src - cd rbenv-src - git checkout 'fbaa15993171bf' ### Build Jemalloc ### - - name: 'Build Jemalloc [ubuntu-24.04]' - run: | - ./build-jemalloc \ - -n 'ubuntu-24.04' \ - -s "$(pwd)/jemalloc-src.tar.bz2" \ - -o "$(pwd)/jemalloc-bin-ubuntu-24.04.tar.gz" \ - -j 2 - - - name: 'Build Jemalloc [el-9]' + - name: Build Jemalloc run: | ./build-jemalloc \ - -n 'el-9' \ + -n '${{ matrix.distro }}' \ -s "$(pwd)/jemalloc-src.tar.bz2" \ - -o "$(pwd)/jemalloc-bin-el-9.tar.gz" \ + -o "$(pwd)/jemalloc-bin.tar.gz" \ -j 2 ### Build Ruby binaries ### - - name: 'Build Ruby binaries [ubuntu-24.04/normal]' - run: | - ./build-ruby \ - -n 'ubuntu-24.04' \ - -s "$(pwd)/ruby-src.tar.gz" \ - -v '4.0' \ - -o "$(pwd)/ruby-bin-ubuntu-24.04-normal.tar.gz" \ - -j 2 - - - name: 'Build Ruby binaries [ubuntu-24.04/jemalloc]' + - name: 'Build Ruby binaries [${{ matrix.distro }}/normal]' run: | ./build-ruby \ - -n 'ubuntu-24.04' \ + -n '${{ matrix.distro }}' \ -s "$(pwd)/ruby-src.tar.gz" \ -v '4.0' \ - -o "$(pwd)/ruby-bin-ubuntu-24.04-jemalloc.tar.gz" \ - -m "$(pwd)/jemalloc-bin-ubuntu-24.04.tar.gz" \ + -o "$(pwd)/ruby-bin-normal.tar.gz" \ -j 2 - - name: 'Build Ruby binaries [el-9/normal]' + - name: 'Build Ruby binaries [${{ matrix.distro }}/jemalloc]' run: | ./build-ruby \ - -n 'el-9' \ + -n '${{ matrix.distro }}' \ -s "$(pwd)/ruby-src.tar.gz" \ -v '4.0' \ - -o "$(pwd)/ruby-bin-el-9-normal.tar.gz" \ - -j 2 - - - name: 'Build Ruby binaries [el-9/jemalloc]' - run: | - ./build-ruby \ - -n 'el-9' \ - -s "$(pwd)/ruby-src.tar.gz" \ - -v '4.0' \ - -o "$(pwd)/ruby-bin-el-9-jemalloc.tar.gz" \ - -m "$(pwd)/jemalloc-bin-el-9.tar.gz" \ + -o "$(pwd)/ruby-bin-jemalloc.tar.gz" \ + -m "$(pwd)/jemalloc-bin.tar.gz" \ -j 2 - name: Create packages directory @@ -230,35 +176,78 @@ jobs: ### Build Ruby packages ### - - name: 'Build Ruby package [ubuntu-24.04/normal]' + - name: 'Build Ruby DEB [${{ matrix.distro }}/normal]' + if: matrix.package_format == 'DEB' run: | ./build-ruby-deb \ - -b "$(pwd)/ruby-bin-ubuntu-24.04-normal.tar.gz" \ - -o "$(pwd)/packages/fullstaq-ruby-4.0_2-ubuntu-24.04_amd64.deb" \ + -b "$(pwd)/ruby-bin-normal.tar.gz" \ + -o "$(pwd)/packages/fullstaq-ruby-4.0_2-${{ matrix.distro }}_amd64.deb" \ + -r '2' + - name: 'Build Ruby RPM [${{ matrix.distro }}/normal]' + if: matrix.package_format == 'RPM' + run: | + SANITIZED_DISTRO=$(echo '${{ matrix.distro }}' | tr -d '-') + ./build-ruby-rpm \ + -b "$(pwd)/ruby-bin-normal.tar.gz" \ + -o "$(pwd)/packages/fullstaq-ruby-4.0-rev2-${SANITIZED_DISTRO}.x86_64.rpm" \ -r '2' - - name: 'Build Ruby package [ubuntu-24.04/jemalloc]' + - name: 'Build Ruby DEB [${{ matrix.distro }}/jemalloc]' + if: matrix.package_format == 'DEB' run: | ./build-ruby-deb \ - -b "$(pwd)/ruby-bin-ubuntu-24.04-jemalloc.tar.gz" \ - -o "$(pwd)/packages/fullstaq-ruby-4.0-jemalloc_2-ubuntu-24.04_amd64.deb" \ + -b "$(pwd)/ruby-bin-jemalloc.tar.gz" \ + -o "$(pwd)/packages/fullstaq-ruby-4.0-jemalloc_2-${{ matrix.distro }}_amd64.deb" \ -r '2' - - - name: 'Build Ruby package [el-9/normal]' + - name: 'Build Ruby RPM [${{ matrix.distro }}/jemalloc]' + if: matrix.package_format == 'RPM' run: | + SANITIZED_DISTRO=$(echo '${{ matrix.distro }}' | tr -d '-') ./build-ruby-rpm \ - -b "$(pwd)/ruby-bin-el-9-normal.tar.gz" \ - -o "$(pwd)/packages/fullstaq-ruby-4.0-rev2-el9.x86_64.rpm" \ + -b "$(pwd)/ruby-bin-jemalloc.tar.gz" \ + -o "$(pwd)/packages/fullstaq-ruby-4.0-jemalloc-rev2-${SANITIZED_DISTRO}.x86_64.rpm" \ -r '2' - - name: 'Build Ruby package [el-9/jemalloc]' + ### Upload packages ### + + - name: Upload packages + uses: actions/upload-artifact@v4 + with: + name: 'contributor-packages-${{ matrix.distro }}' + path: packages + retention-days: 1 + + + build_common_packages: + name: Build common and rbenv packages + needs: build_images + runs-on: ubuntu-24.04 + if: > + github.repository != 'fullstaq-ruby/server-edition' + || (github.event_name == 'pull_request' + && github.event.pull_request.head.repo.full_name != github.repository) + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + + - name: Download utility Docker image + uses: actions/download-artifact@v4 + with: + name: 'docker-image-utility' + path: docker-image-utility + - name: Load utility Docker image + run: ./internal-scripts/ci-cd/load-docker-image.sh + env: + TARBALL: docker-image-utility/image.tar.zst + + - name: Clone Rbenv source run: | - ./build-ruby-rpm \ - -b "$(pwd)/ruby-bin-el-9-jemalloc.tar.gz" \ - -o "$(pwd)/packages/fullstaq-ruby-4.0-jemalloc-rev2-el9.x86_64.rpm" \ - -r '2' + git clone 'https://github.com/fullstaq-ruby/rbenv.git' rbenv-src + cd rbenv-src + git checkout 'fbaa15993171bf' - ### Build Rbenv packages ### + - name: Create packages directory + run: mkdir -p packages - name: Build Rbenv DEB run: | @@ -274,9 +263,6 @@ jobs: -o "$(pwd)/packages/fullstaq-rbenv-1.1.2_16-1.noarch.rpm" \ -n '1.1.2-16' \ -r '1' - - ### Build common packages ### - - name: Build common DEB run: | ./build-common-deb \ @@ -286,18 +272,19 @@ jobs: ./build-common-rpm \ -o "$(pwd)/packages/fullstaq-ruby-common-1.0-1.noarch.rpm" - ### Upload all packages ### - - name: Upload packages uses: actions/upload-artifact@v4 with: - name: contributor-packages + name: contributor-packages-common path: packages + retention-days: 1 test_packages: name: 'Test [${{ matrix.distro }}/${{ matrix.variant }}]' - needs: build_packages + needs: + - build_packages + - build_common_packages runs-on: ubuntu-24.04 if: > github.repository != 'fullstaq-ruby/server-edition' @@ -327,10 +314,15 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Download packages + - name: Download distro packages + uses: actions/download-artifact@v4 + with: + name: 'contributor-packages-${{ matrix.distro }}' + path: packages + - name: Download common packages uses: actions/download-artifact@v4 with: - name: contributor-packages + name: contributor-packages-common path: packages - name: Download utility Docker image diff --git a/.github/workflows/ci-cd-contributor.yml.erb b/.github/workflows/ci-cd-contributor.yml.erb index a33870a7..bc1ac66b 100644 --- a/.github/workflows/ci-cd-contributor.yml.erb +++ b/.github/workflows/ci-cd-contributor.yml.erb @@ -33,7 +33,7 @@ jobs: build_images: - name: Build Docker images + name: 'Build Docker image [${{ matrix.distro }}]' needs: lint runs-on: ubuntu-24.04 if: > @@ -41,54 +41,47 @@ jobs: || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + include: + <%- contributor_distributions.each do |distro| -%> + <%- image = docker_images.find { |img| img[:id] == distro[:name] } -%> + - distro: '<%= distro[:name] %>' + image_name: '<%= image[:name] %>' + image_tag: '<%= image[:tag] %>' + source_dir: 'environments/<%= image[:id] %>' + artifact_name: '<%= docker_image_artifact_name(distro[:name]) %>' + <%- end -%> + <%- utility_image = docker_images.find { |img| img[:id] == 'utility' } -%> + - distro: 'utility' + image_name: '<%= utility_image[:name] %>' + image_tag: '<%= utility_image[:tag] %>' + source_dir: 'environments/utility' + artifact_name: '<%= docker_image_artifact_name('utility') %>' steps: - uses: actions/checkout@v4 - - <%- contributor_distributions.each do |distro| -%> - <%- image = docker_images.find { |img| img[:id] == distro[:name] } -%> - - name: 'Build Docker image [<%= distro[:name] %>]' + - name: Build Docker image run: ./internal-scripts/ci-cd/build-docker-images/build.sh env: - IMAGE_NAME: '<%= image[:name] %>' - IMAGE_TAG: '<%= image[:tag] %>' - SOURCE_DIR: 'environments/<%= image[:id] %>' - - name: 'Dump Docker image [<%= distro[:name] %>]' + IMAGE_NAME: '${{ matrix.image_name }}' + IMAGE_TAG: '${{ matrix.image_tag }}' + SOURCE_DIR: '${{ matrix.source_dir }}' + - name: Dump Docker image run: ./internal-scripts/ci-cd/build-docker-images/dump-image.sh env: - IMAGE_NAME: '<%= image[:name] %>' - IMAGE_TAG: '<%= image[:tag] %>' - - name: 'Upload Docker image artifact [<%= distro[:name] %>]' + IMAGE_NAME: '${{ matrix.image_name }}' + IMAGE_TAG: '${{ matrix.image_tag }}' + - name: Upload Docker image artifact uses: actions/upload-artifact@v4 with: - name: '<%= docker_image_artifact_name(distro[:name]) %>' + name: '${{ matrix.artifact_name }}' path: output - - name: Clean up Docker image output - run: rm -rf output - - <%- end -%> - <%- utility_image = docker_images.find { |img| img[:id] == 'utility' } -%> - - name: 'Build Docker image [utility]' - run: ./internal-scripts/ci-cd/build-docker-images/build.sh - env: - IMAGE_NAME: '<%= utility_image[:name] %>' - IMAGE_TAG: '<%= utility_image[:tag] %>' - SOURCE_DIR: 'environments/utility' - - name: 'Dump Docker image [utility]' - run: ./internal-scripts/ci-cd/build-docker-images/dump-image.sh - env: - IMAGE_NAME: '<%= utility_image[:name] %>' - IMAGE_TAG: '<%= utility_image[:tag] %>' - - name: 'Upload Docker image artifact [utility]' - uses: actions/upload-artifact@v4 - with: - name: '<%= docker_image_artifact_name('utility') %>' - path: output - - name: Clean up Docker image output - run: rm -rf output + retention-days: 1 build_packages: - name: Build packages + name: 'Build packages [${{ matrix.distro }}]' needs: build_images runs-on: ubuntu-24.04 if: > @@ -96,29 +89,35 @@ jobs: || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + include: + <%- contributor_distributions.each do |distro| -%> + - distro: '<%= distro[:name] %>' + package_format: '<%= distro[:package_format] %>' + image_artifact_name: '<%= docker_image_artifact_name(distro[:name]) %>' + <%- end -%> steps: - uses: actions/checkout@v4 ### Download and load Docker images ### - <%- contributor_distributions.each do |distro| -%> - - name: 'Download Docker image artifact [<%= distro[:name] %>]' + - name: Download distro Docker image uses: actions/download-artifact@v4 with: - name: '<%= docker_image_artifact_name(distro[:name]) %>' - path: docker-image-<%= distro[:name] %> - - name: 'Load Docker image [<%= distro[:name] %>]' + name: '${{ matrix.image_artifact_name }}' + path: docker-image-distro + - name: Load distro Docker image run: ./internal-scripts/ci-cd/load-docker-image.sh env: - TARBALL: docker-image-<%= distro[:name] %>/image.tar.zst - - <%- end -%> - - name: 'Download Docker image artifact [utility]' + TARBALL: docker-image-distro/image.tar.zst + - name: Download utility Docker image uses: actions/download-artifact@v4 with: name: '<%= docker_image_artifact_name('utility') %>' path: docker-image-utility - - name: 'Load Docker image [utility]' + - name: Load utility Docker image run: ./internal-scripts/ci-cd/load-docker-image.sh env: TARBALL: docker-image-utility/image.tar.zst @@ -129,66 +128,98 @@ jobs: run: curl -fsSL -o ruby-src.tar.gz '<%= ruby_source_url(rpv[:full_version]) %>' - name: Download Jemalloc source run: curl -fsSL -o jemalloc-src.tar.bz2 '<%= jemalloc_source_url %>' - - name: Clone Rbenv source - run: | - git clone '<%= config[:rbenv][:repo] %>' rbenv-src - cd rbenv-src - git checkout '<%= config[:rbenv][:ref] %>' ### Build Jemalloc ### - <%- contributor_distributions.each do |distro| -%> - - name: 'Build Jemalloc [<%= distro[:name] %>]' + - name: Build Jemalloc run: | ./build-jemalloc \ - -n '<%= distro[:name] %>' \ + -n '${{ matrix.distro }}' \ -s "$(pwd)/jemalloc-src.tar.bz2" \ - -o "$(pwd)/jemalloc-bin-<%= distro[:name] %>.tar.gz" \ + -o "$(pwd)/jemalloc-bin.tar.gz" \ -j 2 - <%- end -%> ### Build Ruby binaries ### - <%- contributor_distributions.each do |distro| -%> <%- variants_for_ruby_version(rpv).each do |variant| -%> - - name: 'Build Ruby binaries [<%= distro[:name] %>/<%= variant[:name] %>]' + - name: 'Build Ruby binaries [${{ matrix.distro }}/<%= variant[:name] %>]' run: | ./build-ruby \ - -n '<%= distro[:name] %>' \ + -n '${{ matrix.distro }}' \ -s "$(pwd)/ruby-src.tar.gz" \ -v '<%= rpv[:id] %>' \ - -o "$(pwd)/ruby-bin-<%= distro[:name] %>-<%= variant[:name] %>.tar.gz" \ + -o "$(pwd)/ruby-bin-<%= variant[:name] %>.tar.gz" \ <%- if variant[:name] == 'jemalloc' -%> - -m "$(pwd)/jemalloc-bin-<%= distro[:name] %>.tar.gz" \ + -m "$(pwd)/jemalloc-bin.tar.gz" \ <%- elsif variant[:name] == 'malloctrim' -%> -t \ <%- end -%> -j 2 - <%- end -%> <%- end -%> - name: Create packages directory run: mkdir -p packages ### Build Ruby packages ### - <%- contributor_distributions.each do |distro| -%> <%- variants_for_ruby_version(rpv).each do |variant| -%> - <%- pkg_basename = ruby_package_basename(rpv, distro, variant) -%> - - name: 'Build Ruby package [<%= distro[:name] %>/<%= variant[:name] %>]' + - name: 'Build Ruby DEB [${{ matrix.distro }}/<%= variant[:name] %>]' + if: matrix.package_format == 'DEB' run: | - <%- if distro[:package_format] == :DEB -%> ./build-ruby-deb \ - <%- else -%> + -b "$(pwd)/ruby-bin-<%= variant[:name] %>.tar.gz" \ + -o "$(pwd)/packages/fullstaq-ruby-<%= rpv[:id] %><%= variant[:package_suffix] %>_<%= rpv[:package_revision] %>-${{ matrix.distro }}_amd64.deb" \ + -r '<%= rpv[:package_revision] %>' + - name: 'Build Ruby RPM [${{ matrix.distro }}/<%= variant[:name] %>]' + if: matrix.package_format == 'RPM' + run: | + SANITIZED_DISTRO=$(echo '${{ matrix.distro }}' | tr -d '-') ./build-ruby-rpm \ - <%- end -%> - -b "$(pwd)/ruby-bin-<%= distro[:name] %>-<%= variant[:name] %>.tar.gz" \ - -o "$(pwd)/packages/<%= pkg_basename %>" \ + -b "$(pwd)/ruby-bin-<%= variant[:name] %>.tar.gz" \ + -o "$(pwd)/packages/fullstaq-ruby-<%= rpv[:id] %><%= variant[:package_suffix] %>-rev<%= rpv[:package_revision] %>-${SANITIZED_DISTRO}.x86_64.rpm" \ -r '<%= rpv[:package_revision] %>' <%- end -%> - <%- end -%> - ### Build Rbenv packages ### + ### Upload packages ### + + - name: Upload packages + uses: actions/upload-artifact@v4 + with: + name: 'contributor-packages-${{ matrix.distro }}' + path: packages + retention-days: 1 + + + build_common_packages: + name: Build common and rbenv packages + needs: build_images + runs-on: ubuntu-24.04 + if: > + github.repository != 'fullstaq-ruby/server-edition' + || (github.event_name == 'pull_request' + && github.event.pull_request.head.repo.full_name != github.repository) + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + + - name: Download utility Docker image + uses: actions/download-artifact@v4 + with: + name: '<%= docker_image_artifact_name('utility') %>' + path: docker-image-utility + - name: Load utility Docker image + run: ./internal-scripts/ci-cd/load-docker-image.sh + env: + TARBALL: docker-image-utility/image.tar.zst + + - name: Clone Rbenv source + run: | + git clone '<%= config[:rbenv][:repo] %>' rbenv-src + cd rbenv-src + git checkout '<%= config[:rbenv][:ref] %>' + + - name: Create packages directory + run: mkdir -p packages - name: Build Rbenv DEB run: | @@ -204,9 +235,6 @@ jobs: -o "$(pwd)/packages/<%= rbenv_package_basename(:RPM) %>" \ -n '<%= rbenv_version %>' \ -r '<%= rbenv_package_revision %>' - - ### Build common packages ### - - name: Build common DEB run: | ./build-common-deb \ @@ -216,18 +244,19 @@ jobs: ./build-common-rpm \ -o "$(pwd)/packages/<%= common_package_basename(:RPM) %>" - ### Upload all packages ### - - name: Upload packages uses: actions/upload-artifact@v4 with: - name: contributor-packages + name: contributor-packages-common path: packages + retention-days: 1 test_packages: name: 'Test [${{ matrix.distro }}/${{ matrix.variant }}]' - needs: build_packages + needs: + - build_packages + - build_common_packages runs-on: ubuntu-24.04 if: > github.repository != 'fullstaq-ruby/server-edition' @@ -249,10 +278,15 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Download packages + - name: Download distro packages + uses: actions/download-artifact@v4 + with: + name: 'contributor-packages-${{ matrix.distro }}' + path: packages + - name: Download common packages uses: actions/download-artifact@v4 with: - name: contributor-packages + name: contributor-packages-common path: packages - name: Download utility Docker image From f6d60505b9c6e8cbe3d568a632ae5e29d8ae53f8 Mon Sep 17 00:00:00 2001 From: abtreece Date: Fri, 22 May 2026 14:14:48 -0500 Subject: [PATCH 7/9] chore(ci): pin action SHAs in contributor and label-guard workflows Pins all third-party action references in the workflows introduced by this PR to commit SHAs, with a trailing tag comment for readability. Mitigates supply-chain risk from mutable tag re-pointing in workflows that run untrusted contributor code. Also adds .github/dependabot.yml so the github-actions ecosystem tracks SHA updates automatically. Scope: ci-cd-contributor.yml.erb and ci-cd-label-guard.yml. Pinning the pre-existing workflows (main, prepare, build-packages-N, publish-test-*) is out of scope here and can land separately. --- .github/dependabot.yml | 6 +++++ .github/workflows/ci-cd-contributor.yml | 30 ++++++++++----------- .github/workflows/ci-cd-contributor.yml.erb | 30 ++++++++++----------- .github/workflows/ci-cd-label-guard.yml | 2 +- 4 files changed, 37 insertions(+), 31 deletions(-) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..5ace4600 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/ci-cd-contributor.yml b/.github/workflows/ci-cd-contributor.yml index d0587471..e706fe83 100644 --- a/.github/workflows/ci-cd-contributor.yml +++ b/.github/workflows/ci-cd-contributor.yml @@ -33,8 +33,8 @@ jobs: && github.event.pull_request.head.repo.full_name != github.repository) timeout-minutes: 10 steps: - - uses: actions/checkout@v4 - - uses: ruby/setup-ruby@v1 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: ruby/setup-ruby@afeafc3d1ab54a631816aba4c914a0081c12ff2f # v1 with: ruby-version: '3.3' - name: Check @@ -70,7 +70,7 @@ jobs: source_dir: 'environments/utility' artifact_name: 'docker-image-utility' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Build Docker image run: ./internal-scripts/ci-cd/build-docker-images/build.sh env: @@ -83,7 +83,7 @@ jobs: IMAGE_NAME: '${{ matrix.image_name }}' IMAGE_TAG: '${{ matrix.image_tag }}' - name: Upload Docker image artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: '${{ matrix.artifact_name }}' path: output @@ -110,12 +110,12 @@ jobs: package_format: 'RPM' image_artifact_name: 'docker-image-el-9' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 ### Download and load Docker images ### - name: Download distro Docker image - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: '${{ matrix.image_artifact_name }}' path: docker-image-distro @@ -124,7 +124,7 @@ jobs: env: TARBALL: docker-image-distro/image.tar.zst - name: Download utility Docker image - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: 'docker-image-utility' path: docker-image-utility @@ -211,7 +211,7 @@ jobs: ### Upload packages ### - name: Upload packages - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: 'contributor-packages-${{ matrix.distro }}' path: packages @@ -228,10 +228,10 @@ jobs: && github.event.pull_request.head.repo.full_name != github.repository) timeout-minutes: 30 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Download utility Docker image - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: 'docker-image-utility' path: docker-image-utility @@ -273,7 +273,7 @@ jobs: -o "$(pwd)/packages/fullstaq-ruby-common-1.0-1.noarch.rpm" - name: Upload packages - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: contributor-packages-common path: packages @@ -312,21 +312,21 @@ jobs: test_image: 'rockylinux:9' package_format: 'RPM' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Download distro packages - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: 'contributor-packages-${{ matrix.distro }}' path: packages - name: Download common packages - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: contributor-packages-common path: packages - name: Download utility Docker image - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: 'docker-image-utility' path: docker-image-utility diff --git a/.github/workflows/ci-cd-contributor.yml.erb b/.github/workflows/ci-cd-contributor.yml.erb index bc1ac66b..140bde1e 100644 --- a/.github/workflows/ci-cd-contributor.yml.erb +++ b/.github/workflows/ci-cd-contributor.yml.erb @@ -24,8 +24,8 @@ jobs: && github.event.pull_request.head.repo.full_name != github.repository) timeout-minutes: 10 steps: - - uses: actions/checkout@v4 - - uses: ruby/setup-ruby@v1 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: ruby/setup-ruby@afeafc3d1ab54a631816aba4c914a0081c12ff2f # v1 with: ruby-version: '3.3' - name: Check @@ -60,7 +60,7 @@ jobs: source_dir: 'environments/utility' artifact_name: '<%= docker_image_artifact_name('utility') %>' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Build Docker image run: ./internal-scripts/ci-cd/build-docker-images/build.sh env: @@ -73,7 +73,7 @@ jobs: IMAGE_NAME: '${{ matrix.image_name }}' IMAGE_TAG: '${{ matrix.image_tag }}' - name: Upload Docker image artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: '${{ matrix.artifact_name }}' path: output @@ -99,12 +99,12 @@ jobs: image_artifact_name: '<%= docker_image_artifact_name(distro[:name]) %>' <%- end -%> steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 ### Download and load Docker images ### - name: Download distro Docker image - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: '${{ matrix.image_artifact_name }}' path: docker-image-distro @@ -113,7 +113,7 @@ jobs: env: TARBALL: docker-image-distro/image.tar.zst - name: Download utility Docker image - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: '<%= docker_image_artifact_name('utility') %>' path: docker-image-utility @@ -183,7 +183,7 @@ jobs: ### Upload packages ### - name: Upload packages - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: 'contributor-packages-${{ matrix.distro }}' path: packages @@ -200,10 +200,10 @@ jobs: && github.event.pull_request.head.repo.full_name != github.repository) timeout-minutes: 30 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Download utility Docker image - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: '<%= docker_image_artifact_name('utility') %>' path: docker-image-utility @@ -245,7 +245,7 @@ jobs: -o "$(pwd)/packages/<%= common_package_basename(:RPM) %>" - name: Upload packages - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: contributor-packages-common path: packages @@ -276,21 +276,21 @@ jobs: <%- end -%> <%- end -%> steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Download distro packages - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: 'contributor-packages-${{ matrix.distro }}' path: packages - name: Download common packages - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: contributor-packages-common path: packages - name: Download utility Docker image - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: '<%= docker_image_artifact_name('utility') %>' path: docker-image-utility diff --git a/.github/workflows/ci-cd-label-guard.yml b/.github/workflows/ci-cd-label-guard.yml index dcf44fd6..4737773e 100644 --- a/.github/workflows/ci-cd-label-guard.yml +++ b/.github/workflows/ci-cd-label-guard.yml @@ -12,7 +12,7 @@ jobs: pull-requests: write steps: - name: Remove ok-to-test label if present - uses: actions/github-script@v7 + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7 with: script: | const { owner, repo } = context.repo; From 69f521182d875ef6fb5f8fef2d171c5357f7506e Mon Sep 17 00:00:00 2001 From: abtreece Date: Fri, 22 May 2026 14:18:18 -0500 Subject: [PATCH 8/9] fix(ci): gate check_workflow_uptodate with ok-to-test and check out PR head Under pull_request_target the bare actions/checkout@v4 was checking out the base branch, so the "is the generated YAML up-to-date with the ERB?" check silently passed regardless of what the PR changed, making the check meaningless for fork PRs. Add the same ref: override and ok-to-test label gate used by determine_necessary_jobs so the check actually validates PR-head contents, and only runs after a maintainer has labeled the PR. --- .github/workflows/ci-cd-main.yml | 7 ++++++- .github/workflows/ci-cd-main.yml.erb | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-cd-main.yml b/.github/workflows/ci-cd-main.yml index 832c4e47..a6792703 100644 --- a/.github/workflows/ci-cd-main.yml +++ b/.github/workflows/ci-cd-main.yml @@ -94,9 +94,14 @@ jobs: check_workflow_uptodate: name: Check whether workflow is up-to-date runs-on: ubuntu-24.04 - if: github.repository == 'fullstaq-ruby/server-edition' + if: > + github.repository == 'fullstaq-ruby/server-edition' + && (github.event_name != 'pull_request_target' + || github.event.label.name == 'ok-to-test') steps: - uses: actions/checkout@v4 + with: + ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.ref }} - name: Check run: ./internal-scripts/ci-cd/check-workflow-uptodate/check.sh diff --git a/.github/workflows/ci-cd-main.yml.erb b/.github/workflows/ci-cd-main.yml.erb index 9f8ddaa1..ae97cf54 100644 --- a/.github/workflows/ci-cd-main.yml.erb +++ b/.github/workflows/ci-cd-main.yml.erb @@ -84,9 +84,14 @@ jobs: check_workflow_uptodate: name: Check whether workflow is up-to-date runs-on: ubuntu-24.04 - if: github.repository == 'fullstaq-ruby/server-edition' + if: > + github.repository == 'fullstaq-ruby/server-edition' + && (github.event_name != 'pull_request_target' + || github.event.label.name == 'ok-to-test') steps: - uses: actions/checkout@v4 + with: + ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.ref }} - name: Check run: ./internal-scripts/ci-cd/check-workflow-uptodate/check.sh From ddc36707e21dd105f4cafffdf6e48e656f0c707f Mon Sep 17 00:00:00 2001 From: abtreece Date: Fri, 22 May 2026 14:22:56 -0500 Subject: [PATCH 9/9] docs(ci): document pull_request/pull_request_target invariant in workflow headers Adds a short header comment to ci-cd-main, ci-cd-contributor, and ci-cd-label-guard describing how the three files combine to form the fork-PR security boundary. A future change to any single file (e.g., flipping pull_request_target to pull_request, removing the label gate, renaming the ok-to-test label) silently breaks the model otherwise. --- .github/workflows/ci-cd-contributor.yml | 17 +++++++++++++++++ .github/workflows/ci-cd-contributor.yml.erb | 17 +++++++++++++++++ .github/workflows/ci-cd-label-guard.yml | 17 +++++++++++++++++ .github/workflows/ci-cd-main.yml | 17 +++++++++++++++++ .github/workflows/ci-cd-main.yml.erb | 17 +++++++++++++++++ 5 files changed, 85 insertions(+) diff --git a/.github/workflows/ci-cd-contributor.yml b/.github/workflows/ci-cd-contributor.yml index e706fe83..76f292b1 100644 --- a/.github/workflows/ci-cd-contributor.yml +++ b/.github/workflows/ci-cd-contributor.yml @@ -10,6 +10,23 @@ # # git config core.hooksPath .githooks +# Security invariant: the contributor pipeline is split across three workflows. +# +# ci-cd-main - Privileged (secrets, OIDC, write tokens). Runs on +# push to upstream branches, and on pull_request_target +# when a maintainer applies the ok-to-test label. +# Checks out PR head only when label-gated. +# ci-cd-contributor - Unprivileged. Runs on pull_request for fork PRs and +# on push in forks. No secrets. Safe on arbitrary +# contributor commits. +# ci-cd-label-guard - Strips ok-to-test on every push to a labeled PR +# (pull_request_target: synchronize), forcing +# maintainer re-review before the privileged +# pipeline re-runs. +# +# Changing triggers, if-guards, or the label name in any of these files +# without considering the others can break the security model. + name: 'CI/CD: contributor' on: diff --git a/.github/workflows/ci-cd-contributor.yml.erb b/.github/workflows/ci-cd-contributor.yml.erb index 140bde1e..a42716cd 100644 --- a/.github/workflows/ci-cd-contributor.yml.erb +++ b/.github/workflows/ci-cd-contributor.yml.erb @@ -1,6 +1,23 @@ <%= editing_warning_comment('ci-cd-contributor') %> <%- rpv = latest_ruby_package_version -%> +# Security invariant: the contributor pipeline is split across three workflows. +# +# ci-cd-main - Privileged (secrets, OIDC, write tokens). Runs on +# push to upstream branches, and on pull_request_target +# when a maintainer applies the ok-to-test label. +# Checks out PR head only when label-gated. +# ci-cd-contributor - Unprivileged. Runs on pull_request for fork PRs and +# on push in forks. No secrets. Safe on arbitrary +# contributor commits. +# ci-cd-label-guard - Strips ok-to-test on every push to a labeled PR +# (pull_request_target: synchronize), forcing +# maintainer re-review before the privileged +# pipeline re-runs. +# +# Changing triggers, if-guards, or the label name in any of these files +# without considering the others can break the security model. + name: 'CI/CD: contributor' on: diff --git a/.github/workflows/ci-cd-label-guard.yml b/.github/workflows/ci-cd-label-guard.yml index 4737773e..a73f6366 100644 --- a/.github/workflows/ci-cd-label-guard.yml +++ b/.github/workflows/ci-cd-label-guard.yml @@ -1,3 +1,20 @@ +# Security invariant: the contributor pipeline is split across three workflows. +# +# ci-cd-main - Privileged (secrets, OIDC, write tokens). Runs on +# push to upstream branches, and on pull_request_target +# when a maintainer applies the ok-to-test label. +# Checks out PR head only when label-gated. +# ci-cd-contributor - Unprivileged. Runs on pull_request for fork PRs and +# on push in forks. No secrets. Safe on arbitrary +# contributor commits. +# ci-cd-label-guard - This file. Strips ok-to-test on every push to a +# labeled PR (pull_request_target: synchronize), +# forcing maintainer re-review before the privileged +# pipeline re-runs. +# +# Changing triggers, if-guards, or the label name in any of these files +# without considering the others can break the security model. + name: 'CI/CD: label guard' on: diff --git a/.github/workflows/ci-cd-main.yml b/.github/workflows/ci-cd-main.yml index a6792703..0fe22470 100644 --- a/.github/workflows/ci-cd-main.yml +++ b/.github/workflows/ci-cd-main.yml @@ -10,6 +10,23 @@ # # git config core.hooksPath .githooks +# Security invariant: the contributor pipeline is split across three workflows. +# +# ci-cd-main - Privileged (secrets, OIDC, write tokens). Runs on +# push to upstream branches, and on pull_request_target +# when a maintainer applies the ok-to-test label. +# Checks out PR head only when label-gated. +# ci-cd-contributor - Unprivileged. Runs on pull_request for fork PRs and +# on push in forks. No secrets. Safe on arbitrary +# contributor commits. +# ci-cd-label-guard - Strips ok-to-test on every push to a labeled PR +# (pull_request_target: synchronize), forcing +# maintainer re-review before the privileged +# pipeline re-runs. +# +# Changing triggers, if-guards, or the label name in any of these files +# without considering the others can break the security model. + name: 'CI/CD: main' on: diff --git a/.github/workflows/ci-cd-main.yml.erb b/.github/workflows/ci-cd-main.yml.erb index ae97cf54..25179873 100644 --- a/.github/workflows/ci-cd-main.yml.erb +++ b/.github/workflows/ci-cd-main.yml.erb @@ -1,5 +1,22 @@ <%= editing_warning_comment('ci-cd-main') %> +# Security invariant: the contributor pipeline is split across three workflows. +# +# ci-cd-main - Privileged (secrets, OIDC, write tokens). Runs on +# push to upstream branches, and on pull_request_target +# when a maintainer applies the ok-to-test label. +# Checks out PR head only when label-gated. +# ci-cd-contributor - Unprivileged. Runs on pull_request for fork PRs and +# on push in forks. No secrets. Safe on arbitrary +# contributor commits. +# ci-cd-label-guard - Strips ok-to-test on every push to a labeled PR +# (pull_request_target: synchronize), forcing +# maintainer re-review before the privileged +# pipeline re-runs. +# +# Changing triggers, if-guards, or the label name in any of these files +# without considering the others can break the security model. + name: 'CI/CD: main' on: