From a94c2dd1158dc117233eb390c2345b9d4e36568f Mon Sep 17 00:00:00 2001 From: Perry Hertler Date: Thu, 18 Jun 2026 11:11:59 -0500 Subject: [PATCH] Auto-trigger CD when CI passes and the gem version is bumped MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously CD only ran via workflow_dispatch. Wire it to fire automatically after CI succeeds on main, following the rubyatscale shared-config convention (workflow_run trigger, secrets read directly by CD) used by code_teams, packs-specification, rubocop-packs, etc. We can't call shared-config's reusable cd.yml because this gem builds cross-platform native binaries (oxidize-rb matrix) rather than a pure Ruby gem, so CD stays bespoke — but the trigger shape now matches. cd.yml: - Trigger on workflow_run (workflows: [CI], types: [completed], branches: [main]) instead of workflow_call; keep workflow_dispatch. - Add a check-release gate job: reads CodeOwnership::VERSION and queries the RubyGems API, proceeding only when the version is unpublished. This gate is our one deviation from shared-config — it avoids running the expensive cross-compile matrix on every main merge (shared-config doesn't need it because its publish action no-ops cheaply). Manual dispatch bypasses the gate to support dry-run testing / forced runs. - Gate ci-data/build/release on check-release and on the triggering CI run's conclusion == 'success'. - Add a dry_run input to workflow_dispatch that skips the irreversible gem push (logs "would push"), making the full pipeline testable manually without publishing. The Release step is already gated on new_version, so it skips automatically in dry-run. - Add a notify_on_release Slack notification on successful release. ci.yml is left untouched. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/cd.yml | 69 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 1ce06b5..6c4431a 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -2,15 +2,60 @@ name: CD on: - workflow_call: + workflow_run: + workflows: [CI] + types: [completed] + branches: [main] workflow_dispatch: + inputs: + dry_run: + type: boolean + default: false + description: "Build gems but don't publish or release" concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: + check-release: + name: Check if release needed + runs-on: ubuntu-latest + # Only proceed when the triggering CI run succeeded (auto path) or on manual dispatch. + if: >- + github.event_name == 'workflow_dispatch' || + github.event.workflow_run.conclusion == 'success' + outputs: + should_release: ${{ steps.check.outputs.should_release }} + version: ${{ steps.check.outputs.version }} + steps: + - uses: actions/checkout@v6 + with: + sparse-checkout: lib/code_ownership/version.rb + sparse-checkout-cone-mode: false + fetch-depth: 1 + - id: check + run: | + VERSION=$(ruby -r ./lib/code_ownership/version.rb -e "puts CodeOwnership::VERSION") + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + # Manual dispatch always proceeds (dry-run testing / forced release); + # the publish step still skips versions already on RubyGems. + SHOULD_RELEASE=true + REASON="manual dispatch (dry_run=${{ inputs.dry_run }})" + elif curl -sf -o /dev/null "https://rubygems.org/api/v2/rubygems/code_ownership/versions/${VERSION}.json"; then + SHOULD_RELEASE=false + REASON="${VERSION} already on RubyGems — skipping release" + else + SHOULD_RELEASE=true + REASON="${VERSION} not published — will release" + fi + echo "should_release=${SHOULD_RELEASE}" >> "$GITHUB_OUTPUT" + echo "::notice::code_ownership ${REASON}." + ci-data: + needs: check-release + if: needs.check-release.outputs.should_release == 'true' runs-on: ubuntu-latest outputs: result: ${{ steps.fetch.outputs.result }} @@ -129,6 +174,11 @@ for i in *.gem; do if [ -f "$i" ]; then echo "⏳ Attempting to push $i..." + if [ "${{ inputs.dry_run }}" = "true" ]; then + echo "🧪 dry-run: would push $i (skipping)" + SKIPPED_GEMS+=("$i") + continue + fi if ! gem push "$i" >push.out 2>&1; then gemerr=$? if grep -q "Repushing of gem" push.out; then @@ -179,4 +229,19 @@ --title "v${{ steps.push-gem.outputs.gem_version }}" \ --notes "$RELEASE_NOTES" \ --generate-notes \ - pkg/*.gem \ No newline at end of file + pkg/*.gem + + notify_on_release: + runs-on: ubuntu-latest + needs: [check-release, release] + if: ${{ needs.release.result == 'success' }} + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK + steps: + - uses: slackapi/slack-github-action@v1.25.0 + with: + payload: | + { + "text": "🎉 Released ${{ github.repository }} v${{ needs.check-release.outputs.version }}" + } \ No newline at end of file