Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions .github/actions/authorize-release/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Authorize release
description: >-
Authorize a release by verifying its tag signature against the releasers listed
for the package in releasers.yml. Loads the authorized releasers' GPG and SSH
signing keys, then runs `git tag -v`. Fails if the tag is not signed by one of
the authorized releasers.

inputs:
package-name:
description: Package name to look up in releasers.yml
required: true
target-path:
description: Path where the target repo is checked out
required: true
tag:
description: Release tag to verify (e.g., v1.0.0)
required: true

runs:
using: composite
steps:
- name: Load authorized releaser keys
shell: bash
env:
PACKAGE_NAME: ${{ inputs.package-name }}
run: |
set -e

RELEASERS_YML="releasers.yml"
RELEASER_KEYS_DIR="releasers"

# Default releasers + package-specific releasers
RELEASERS=$(
{
yq eval '.default[]' "$RELEASERS_YML" 2>/dev/null
yq eval ".packages.\"$PACKAGE_NAME\"[]" "$RELEASERS_YML" 2>/dev/null
} | sort -u
)

if [ -z "$RELEASERS" ]; then
echo "ERROR: No releasers configured for '$PACKAGE_NAME'"
exit 1
fi

# Import releaser GPG keys
while read -r username; do
GPG_FILE="$RELEASER_KEYS_DIR/${username}.gpg.asc"
if [ -f "$GPG_FILE" ] && grep -q "^-----BEGIN" "$GPG_FILE"; then
gpg --import "$GPG_FILE" 2>/dev/null || true
fi
done <<< "$RELEASERS"

# Write SSH allowed_signers for releaser keys
mkdir -p ~/.ssh
touch ~/.ssh/allowed_signers

while read -r username; do
SSH_FILE="$RELEASER_KEYS_DIR/${username}.ssh.txt"
if [ -f "$SSH_FILE" ]; then
grep -v '^#' "$SSH_FILE" | grep -v '^$' >> ~/.ssh/allowed_signers || true
fi
done <<< "$RELEASERS"

chmod 600 ~/.ssh/allowed_signers
git config --global gpg.ssh.allowedSignersFile ~/.ssh/allowed_signers

- name: Verify tag signature
shell: bash
working-directory: ${{ inputs.target-path }}
run: git tag -v "${{ inputs.tag }}"
35 changes: 35 additions & 0 deletions .github/actions/publish-github-release/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Publish GitHub release
description: >-
Create a GitHub release entry for the given tag, populated from the target
repository's CHANGELOG.md. Installs Ruby internally (setup-ruby is idempotent
when the same version is already installed).

inputs:
target-path:
description: Path where the target repo is checked out
required: true
repo:
description: GitHub repository of the released package (owner/repo)
required: true
tag:
description: Release tag (e.g., v1.0.0)
required: true
github-token:
description: Token with permission to create releases on the target repo
required: true

runs:
using: composite
steps:
- uses: ruby/setup-ruby@v1
with:
ruby-version: 3.4

- shell: bash
working-directory: ${{ inputs.target-path }}
env:
GITHUB_TOKEN: ${{ inputs.github-token }}
run: |
ruby "$GITHUB_WORKSPACE/.github/scripts/create-github-release.rb" \
"${{ inputs.repo }}" \
"${{ inputs.tag }}"
31 changes: 31 additions & 0 deletions .github/actions/trigger-announcements/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Trigger announcements
description: Dispatch the announce.yml workflow for a released package.

inputs:
repo:
description: GitHub repository of the released package (owner/repo)
required: true
tag:
description: Release tag (e.g., v1.0.0)
required: true
package-name:
description: Package name
required: true

runs:
using: composite
steps:
- uses: actions/github-script@v7
with:
script: |
await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: "announce.yml",
ref: "main",
inputs: {
repo: "${{ inputs.repo }}",
tag: "${{ inputs.tag }}",
package_name: "${{ inputs.package-name }}"
}
});
46 changes: 46 additions & 0 deletions .github/actions/update-release-history/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Update release history
description: >-
Append a release entry to RELEASES.md in the release-machine repo and push the
commit. Assumes release-machine is checked out at the workspace root.

inputs:
target-path:
description: Path where the target repo is checked out (used to look up the tagger email)
required: true
package-name:
description: Name of the package being released
required: true
tag:
description: Release tag (e.g., v1.0.0)
required: true
github-token:
description: Token used by add-release.sh to resolve the tagger's GitHub username
required: true

runs:
using: composite
steps:
- shell: bash
env:
GITHUB_TOKEN: ${{ inputs.github-token }}
TARGET_PATH: ${{ inputs.target-path }}
PACKAGE_NAME: ${{ inputs.package-name }}
TAG: ${{ inputs.tag }}
run: |
# Extract tagger email from the release tag
pushd "$TARGET_PATH" > /dev/null
TAGGER_EMAIL=$(git for-each-ref --format='%(taggeremail)' "refs/tags/$TAG" | tr -d '<>')
if [ -z "$TAGGER_EMAIL" ]; then
TAGGER_EMAIL=$(git log -1 --pretty=format:'%ae' "$TAG")
fi
popd > /dev/null

# Add release entry
./.github/scripts/add-release.sh "$PACKAGE_NAME" "$TAG" "$TAGGER_EMAIL"

# Commit and push
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add RELEASES.md
git commit -m "Add release: $PACKAGE_NAME $TAG"
git push
8 changes: 4 additions & 4 deletions .github/scripts/add-release.sh
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail

# Usage: add-release.sh <gem-name> <tag> <tagger-email>
# Usage: add-release.sh <package-name> <tag> <tagger-email>
#
# Adds a release entry to RELEASES.md in the format:
# - YYYY-MM-DD - gem-name vX.Y.Z by @username
# - YYYY-MM-DD - package-name vX.Y.Z by @username
#
# Requires GITHUB_TOKEN environment variable for API access.

GEM_NAME="${1:?Gem name required}"
PACKAGE_NAME="${1:?Package name required}"
TAG="${2:?Tag required}"
TAGGER_EMAIL="${3:?Tagger email required}"
GITHUB_TOKEN="${GITHUB_TOKEN:?GITHUB_TOKEN environment variable required}"
Expand All @@ -29,7 +29,7 @@ else
DISPLAY_USER=$(echo "$TAGGER_EMAIL" | cut -d'@' -f1)
fi

NEW_ENTRY="- ${DATE} - ${GEM_NAME} ${TAG} by ${DISPLAY_USER}"
NEW_ENTRY="- ${DATE} - ${PACKAGE_NAME} ${TAG} by ${DISPLAY_USER}"

# Insert before first list item, or append if none exist
if grep -q "^- " RELEASES.md; then
Expand Down
12 changes: 6 additions & 6 deletions .github/scripts/announce-to-forum.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

# Usage: announce-to-forum.rb <gem-name> <repo> <tag>
# Usage: announce-to-forum.rb <package-name> <repo> <tag>
#
# Fetches a GitHub release and posts an announcement to Discourse forum.
#
# Arguments:
# gem-name - Name of the gem being released
# repo - GitHub repository in owner/repo format
# tag - Release tag (e.g., v1.0.0)
# package-name - Name of the package being released
# repo - GitHub repository in owner/repo format
# tag - Release tag (e.g., v1.0.0)
#
# Required environment variables:
# FORUM_URL - Discourse forum URL
Expand All @@ -26,7 +26,7 @@
require "json"

# Prepare input
gem_name = ARGV[0] or abort "ERROR: Gem name required"
package_name = ARGV[0] or abort "ERROR: Package name required"
repo = ARGV[1] or abort "ERROR: Repository required (format: owner/repo)"
tag = ARGV[2] or abort "ERROR: Release tag required"

Expand Down Expand Up @@ -101,7 +101,7 @@
end

# Prepare forum post
title = "#{gem_name} #{version} released"
title = "#{package_name} #{version} released"

body = release_body.strip
body += "\n\n---\n\n"
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/announce.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ on:
description: "Release tag (e.g., v1.0.0)"
required: true
type: string
gem_name:
description: "Gem name"
package_name:
description: "Package name"
required: true
type: string

Expand All @@ -40,7 +40,7 @@ jobs:
run: |
OUTPUT=$(
ruby .github/scripts/announce-to-forum.rb \
"${{ github.event.inputs.gem_name }}" \
"${{ github.event.inputs.package_name }}" \
"${{ github.event.inputs.repo }}" \
"${{ github.event.inputs.tag }}"
)
Expand All @@ -56,7 +56,7 @@ jobs:
steps:
- name: Prepare summary
run: |
echo "### ${{ github.event.inputs.gem_name }} ${{ github.event.inputs.tag }} announcements" >> $GITHUB_STEP_SUMMARY
echo "### ${{ github.event.inputs.package_name }} ${{ github.event.inputs.tag }} announcements" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.forum.result }}" == "success" ]; then
echo "- [Forum post](${{ needs.forum.outputs.forum_url }})" >> $GITHUB_STEP_SUMMARY
Expand Down
Loading