diff --git a/.github/workflows/generic-rake-test-only.yml b/.github/workflows/generic-rake-test-only.yml new file mode 100644 index 0000000..4394a00 --- /dev/null +++ b/.github/workflows/generic-rake-test-only.yml @@ -0,0 +1,144 @@ +# Test-only variant of generic-rake.yml +# +# Sibling to generic-rake.yml. Runs the same rake test matrix but drops +# the downstream `tests-passed` job entirely, so: +# - callers do NOT need `contents: write`; `contents: read` is sufficient; +# - no `tests-passed` or `do-release` repository_dispatch events are fired. +# +# Use this variant for repositories that: +# - run `bundle exec rake` for CI, but +# - do NOT publish a gem on tag push (no do-release consumer), and +# - do NOT participate in the tests-passed downstream cascade +# (docker rebuilds, notify.yml, etc.). +# +# Typical consumers: mn-samples-*, metanorma-model-* (sample/doc repos +# that CI but do not release), and any gem whose release is triggered by +# an external mechanism rather than the do-release repository_dispatch. +# +# Repositories that DO participate in the release cascade must continue +# to consume `generic-rake.yml` and configure `contents: write` at the +# caller (see cimas-config/gh-actions/master/rake.yml). + +name: rake (test-only) + +on: + workflow_call: + inputs: + before-setup-ruby: + description: Command to execute before setting up Ruby + type: string + default: '' + required: false + after-setup-ruby: + description: Command to execute after setting up Ruby + type: string + default: '' + required: false + shell: + description: Shell to use for running commands + type: string + default: 'bash' + required: false + setup-inkscape: + description: 'Legacy flag — use setup-tools instead' + type: boolean + default: false + setup-tools: + description: >- + Comma-separated list of tools to install (no spaces): + inkscape,ghostscript,graphviz,libreoffice,xml2rfc,exiftool,ffmpeg,imagemagick + type: string + default: '' + required: false + submodules: + description: 'Checkout submodules: true, false, or recursive' + type: string + default: 'recursive' + required: false + private-fonts: + description: 'Set to true to enable private fonts via fontist-repo-setup' + type: string + default: 'false' + required: false + private-fonts-username: + description: 'Username for private fonts repository' + type: string + default: 'metanorma-ci' + required: false + choco-cache: + description: 'Whether to set up Chocolatey cache on Windows' + type: boolean + default: false + required: false + secrets: + pat_token: + required: false + +permissions: + contents: read + +jobs: + prepare: + uses: metanorma/ci/.github/workflows/prepare-rake.yml@main + + rake: + name: Test on Ruby ${{ matrix.ruby.version }} ${{ matrix.os }} + runs-on: ${{ matrix.os }} + + needs: prepare + + concurrency: + group: '${{ github.workflow }}-${{ matrix.os }}-${{ matrix.ruby.version }}-${{ github.head_ref || github.ref_name }}' + cancel-in-progress: true + + continue-on-error: ${{ matrix.ruby.experimental }} + strategy: + fail-fast: false + max-parallel: 6 + matrix: ${{ fromJson(needs.prepare.outputs.matrix) }} + + defaults: + run: + shell: ${{ inputs.shell }} + + steps: + - uses: actions/checkout@v6 + with: + token: ${{ secrets.pat_token || github.token }} + submodules: ${{ inputs.submodules }} + + - if: matrix.os == 'windows-latest' + uses: actions/setup-java@v5 + with: + distribution: 'temurin' + java-version: '17' + + - if: ${{ inputs.before-setup-ruby != '' }} + run: ${{ inputs.before-setup-ruby }} + + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby.version }} + bundler-cache: true + rubygems: ${{ matrix.ruby.rubygems }} + bundler: ${{ matrix.ruby.bundler }} + + - if: ${{ inputs.after-setup-ruby != '' }} + run: ${{ inputs.after-setup-ruby }} + + - if: ${{ inputs.choco-cache && runner.os == 'Windows' }} + uses: metanorma/ci/choco-cache-action@main + + - uses: metanorma/ci/tool-setup-action@main + with: + setup-tools: ${{ inputs.setup-tools }} + setup-inkscape: ${{ inputs.setup-inkscape }} + + - if: inputs.private-fonts == 'true' + uses: metanorma/ci/fontist-repo-setup@main + with: + private-fonts-pat: ${{ secrets.pat_token }} + private-fonts-username: ${{ inputs.private-fonts-username }} + use-bundler: 'true' + + - run: bundle exec rake diff --git a/cimas-config/gh-actions/master/rake_test_only.yml b/cimas-config/gh-actions/master/rake_test_only.yml new file mode 100644 index 0000000..52d1fe2 --- /dev/null +++ b/cimas-config/gh-actions/master/rake_test_only.yml @@ -0,0 +1,30 @@ +# Test-only caller for repositories that run rake but do not participate +# in the release cascade. Least-privilege variant of rake.yml. +# +# Use for repos that: +# - run `bundle exec rake` for CI, but +# - do NOT publish a gem on tag push (no do-release consumer), and +# - do NOT participate in the tests-passed downstream cascade. +# +# Consumers gain `contents: read` at the workflow level (least privilege) +# by giving up the `tests-passed` / `do-release` repository_dispatch fires. +# +# To switch a repo back to the cascade variant, replace this template +# mapping with `master/rake.yml` in the repo's cimas.yml entry. + +name: rake + +on: + push: + branches: [ master, main ] + tags: [ v* ] + pull_request: + +permissions: + contents: read + +jobs: + rake: + uses: metanorma/ci/.github/workflows/generic-rake-test-only.yml@main + secrets: + pat_token: ${{ secrets.METANORMA_CI_PAT_TOKEN }} diff --git a/docs/generic-rake.md b/docs/generic-rake.md index d8db700..cbb2019 100644 --- a/docs/generic-rake.md +++ b/docs/generic-rake.md @@ -52,3 +52,32 @@ jobs: 4. If the ref is a tag (`refs/tags/v*`), also dispatches `do-release` event (triggers the release pipeline). The test matrix is defined in [ruby-matrix.json](../.github/workflows/ruby-matrix.json). + +## Test-only variant (opt-in, least-privilege) + +For repositories that run `bundle exec rake` for CI but do **not** publish a gem on tag push and do **not** participate in the `tests-passed` downstream cascade, an opt-in variant is available: + +- Reusable: [`generic-rake-test-only.yml`](../.github/workflows/generic-rake-test-only.yml) +- Cimas template: [`cimas-config/gh-actions/master/rake_test_only.yml`](../cimas-config/gh-actions/master/rake_test_only.yml) + +### What the variant drops + +The `tests-passed` job (both `tests-passed` and `do-release` `repository_dispatch` events) is removed entirely. The `tests-passed-event` and `release-event` inputs are also removed since they are no longer consumed. + +### What the variant gains + +The caller can declare `permissions: contents: read` at the workflow level — the least-privilege ceiling that GitHub Actions permits for a reusable-workflow-caller pair. `generic-rake.yml`'s `tests-passed` job requires `contents: write` (to fire the `peter-evans/repository-dispatch` action), which forces its callers to declare `contents: write` at the workflow level; the test-only variant has no such requirement. + +### When to use it + +- **Sample repositories** (`mn-samples-*`) that run `bundle exec rake` for CI but are not gem-releasing. +- **Model repositories** (`metanorma-model-*`) that run `bundle exec rake` for grammar / schema validation but are not gem-releasing. +- Any gem whose release is triggered by an external mechanism (external dispatcher, manual `rake release`, etc.) rather than the `do-release` `repository_dispatch` event. + +### When NOT to use it + +Any gem that participates in the release cascade — i.e. any gem mapped in cimas.yml to `master/release.yml`, `master/release_manual_notes.yml`, `master/release_wo_bundle_install_manual_notes.yml`, `master/release_github_packages.yml`, or `master/release_wo_bundle_install.yml`. Those templates consume the `do-release` event that only `generic-rake.yml` fires; switching a release-participating repo to the test-only variant will silently break its release chain (the `do-release` dispatch will no longer fire from CI). + +### How to switch + +In the target repo's `cimas.yml` entry, replace the `master/rake.yml` mapping with `master/rake_test_only.yml`, then run the cimas sync wave to propagate the change. To switch back, replace in the other direction and re-sync.