Skip to content

Publish gem

Publish gem #4

Workflow file for this run

name: Publish gem
# TODO: Implement a dry-run mode to verify the checks without publishing
on: workflow_dispatch
concurrency: "rubygems" # Only one publish job at a time
jobs:
verify-checks:
name: Verify commit status checks
runs-on: ubuntu-24.04
permissions:
checks: read
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: ruby/setup-ruby@8388f20e6a9c43cd241131b678469a9f89579f37 # v1.216.0
with:
ruby-version: '3.3.7'
- id: version
run: echo "version=$(ruby -e 'puts Gem::Specification::load(Dir.glob("*.gemspec").first).version')" >> $GITHUB_OUTPUT
# Check if the gem version is already published
- name: Verify gem version
env:
GEM_VERSION: ${{ steps.version.outputs.version }}
run: |
if gem search datadog --exact --remote --version "$GEM_VERSION" | grep -q "($GEM_VERSION)"; then
echo "::error::Version $GEM_VERSION is already published"
exit 1
else
echo "Version $GEM_VERSION is not published yet"
fi
# TODO: Verify draft release
# TODO: Verify milestone
# Check if the commit has passed all Github checks
# API: https://docs.github.com/en/rest/checks/runs?apiVersion=2022-11-28#list-check-runs-for-a-git-reference
- name: Verify check runs
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
const checkRuns = await github.paginate(github.rest.checks.listForRef, {
owner: context.repo.owner,
repo: context.repo.repo,
ref: context.sha,
per_page: 100
});
const failedChecks = checkRuns.filter(check =>
check.status === 'completed' &&
check.conclusion !== 'success' &&
check.conclusion !== 'skipped'
);
if (failedChecks.length > 0) {
const failedNames = failedChecks.map(c => c.name).join(', ');
core.setFailed(`Check runs failed: ${failedNames}`);
}
# Check if the commit has passed external CI checks
# API: https://docs.github.com/en/rest/commits/statuses?apiVersion=2022-11-28#get-the-combined-status-for-a-specific-reference
- name: Verify commit status
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
const { data: status } = await github.rest.repos.getCombinedStatusForRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: context.sha
});
if (status.state !== 'success') {
core.setFailed(`Commit status is ${status.state}`);
}
# Check if the commit has all the checks passed
- name: Verify deferred commit data
# NOTE:
#
# This step uses Github's internal API (for rendering the status of the checks in UI),
# which includes Github check runs and external CI statuses and possibly more.
#
# Although Github check runs and external CI statuses are already covered by the previous steps,
# it is still useful to have a double-check and also possibly unearth missing validations.
#
# However, not depending on Github's public API (REST/GraphQL) suggested that this might change in the future.
run: |
COMMIT_URL="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/commit/$GITHUB_SHA"
STATUS=$(curl -sS --fail --retry 3 --retry-delay 5 "$COMMIT_URL/deferred_commit_data" | jq -r ".data.statusCheckStatus.state")
if [ "$STATUS" != "success" ]; then
echo "::error::Status check state is '$STATUS'. See: $COMMIT_URL"
exit 1
fi
rubygems-release:
name: Build and push gem to RubyGems.org
runs-on: ubuntu-24.04
environment: "rubygems.org" # see: https://github.com/DataDog/dd-trace-rb/settings/environments
needs: verify-checks # Make sure to release from a healthy commit
permissions:
id-token: write
contents: write
env:
SKIP_SIMPLECOV: 1
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Ruby
uses: ruby/setup-ruby@8388f20e6a9c43cd241131b678469a9f89579f37 # v1.216.0
with:
ruby-version: '3.3.7'
- run: bundle install
- uses: rubygems/release-gem@a25424ba2ba8b387abc8ef40807c2c85b96cbe32 # v1.1.1
with:
attestations: false # PENDING decision for attestations
github-release:
name: Attach gem to Github Release and publish
runs-on: ubuntu-24.04
needs:
- verify-checks
- rubygems-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GEM_VERSION: ${{ needs.verify-checks.outputs.version }}
permissions:
contents: write
steps:
- name: Download from RubyGems
run: |
gem fetch datadog --version ${GEM_VERSION} --verbose
- name: Attach to existing release draft
run: |
gh release upload "v${GEM_VERSION}" *.gem --clobber
gh release edit "v${GEM_VERSION}" --draft=false
update-gem-version:
if: github.ref_name == 'master'
name: Prepare next gem version
runs-on: ubuntu-24.04
needs:
- verify-checks
- rubygems-release
env:
GEM_VERSION: ${{ needs.verify-checks.outputs.version }}
outputs:
next_version: ${{ steps.next_version.outputs.next_version }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- run: bundle install
- id: next_version
run: |
echo "next_version=$(bundle exec rake version:next)" >> $GITHUB_OUTPUT
# https://docs.github.com/en/rest/issues/milestones?apiVersion=2022-11-28
milestone:
if: github.ref_name == 'master'
name: Open/Close Github milestones
runs-on: ubuntu-24.04
needs:
- verify-checks
- rubygems-release
- update-gem-version
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GEM_VERSION: ${{ needs.verify-checks.outputs.version }}
NEXT_VERSION: ${{ needs.update-gem-version.outputs.next_version }}
permissions:
issues: write
pull-requests: write
steps:
- name: list milestones
id: milestones
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
// https://octokit.github.io/rest.js/v21/#issues-list-milestones
// https://docs.github.com/en/rest/issues/milestones?apiVersion=2022-11-28#list-milestones
const milestones = await github.rest.issues.listMilestones({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open'
});
return milestones.data;
- name: Close milestone
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
const milestones = ${{steps.milestones.outputs.result}}
const milestone = milestones.data.find(
m => m.title === process.env.GEM_VERSION
);
if (!milestone) {
console.log(`No open milestone found with version ${process.env.GEM_VERSION} - skipping close operation`);
return;
}
// https://octokit.github.io/rest.js/v21/#issues-update-milestone
// https://docs.github.com/en/rest/issues/milestones?apiVersion=2022-11-28#update-a-milestone
try {
await github.rest.issues.updateMilestone({
owner: context.repo.owner,
repo: context.repo.repo,
milestone_number: milestone.number,
state: 'closed'
});
console.log(`Successfully closed milestone: ${process.env.GEM_VERSION}`);
} catch (error) {
core.setFailed(`Failed to close milestone: ${error.message}`);
}
- name: Create milestone
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
const milestones = ${{steps.milestones.outputs.result}}
const milestone = milestones.data.find(
m => m.title === process.env.NEXT_VERSION
);
if (milestone) {
console.log(`Milestone "${process.env.NEXT_VERSION}" already exists - skipping creation`);
return;
}
// https://octokit.github.io/rest.js/v21/#issues-create-milestone
// https://docs.github.com/en/rest/issues/milestones?apiVersion=2022-11-28#create-a-milestone
try {
await github.rest.issues.createMilestone({
owner: context.repo.owner,
repo: context.repo.repo,
title: process.env.NEXT_VERSION
});
console.log(`Successfully created milestone: ${process.env.NEXT_VERSION}`);
} catch (error) {
core.setFailed(`Failed to create milestone: ${error.message}`);
}
update-release-branch:
if: github.ref_name == 'master'
name: Pull request to update 'release' branch
runs-on: ubuntu-24.04
needs:
- verify-checks
- rubygems-release
permissions:
issues: write
pull-requests: write
env:
GITHUB_TOKEN: ${{ secrets.GHA_PAT }}
GEM_VERSION: ${{ needs.verify-checks.outputs.version }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- run: |
JOB_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/jobs/${{ github.job }}"
gh pr create \
--base release \
--head master \
--title "Update document v${GEM_VERSION}" \
--body "This is an auto-generated PR to update documentation from [here](${JOB_URL}). Please merge (with a merge commit) when ready." \
--label "docs"