Skip to content
Open
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
1,269 changes: 1,269 additions & 0 deletions .claude/skills/golang-monorepo.md

Large diffs are not rendered by default.

41 changes: 41 additions & 0 deletions .cliff-monorepo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{%- if version %}
## [{{ version | trim_start_matches(pat="v") }}]({{ self::remote_url() }}/tree/{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }}
{%- else %}
## [unreleased]
{%- endif %}
{%- if message %}
{%- raw %}
{% endraw %}
{{ message }}
{%- raw %}
{% endraw %}
{%- endif %}

---

{%- for group, commits in commits | group_by(attribute="group") %}
{%- raw %}
{% endraw %}
### {{ group | upper_first }}
{%- raw %}
{% endraw %}
{%- for commit in commits %}
{%- if commit.remote.pr_title %}
{%- set commit_message = commit.remote.pr_title %}
{%- else %}
{%- set commit_message = commit.message %}
{%- endif %}
* {{ commit_message | split(pat="\n") | first | trim }}
{%- if commit.remote.username %}
{%- raw %} {% endraw %}by [@{{ commit.remote.username }}](https://github.com/{{ commit.remote.username }})
{%- endif %}
{%- if commit.remote.pr_number %}
{%- raw %} {% endraw %}in [#{{ commit.remote.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.remote.pr_number }})
{%- endif %}
{%- raw %} {% endraw %}[...]({{ self::remote_url() }}/commit/{{ commit.id }})
{%- endfor %}
{%- endfor %}

{%- macro remote_url() -%}
https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}
{%- endmacro -%}
2 changes: 1 addition & 1 deletion .github/workflows/auto-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ jobs:
run: gh pr review --approve "$PR_URL"
-
name: Wait for all workflow runs to complete
uses: go-openapi/gh-actions/ci-jobs/wait-pending-jobs@eb161ed408645b24aaf6120cd5e4a893cf2c0af2 # v1.3.1
uses: go-openapi/gh-actions/ci-jobs/wait-pending-jobs@0ec18ca1ddc1e2257097ae8d57952733109d80a1 # v1.4.0
with:
pr-url: ${{ env.PR_URL }}
github-token: ${{ secrets.GITHUB_TOKEN }}
Expand Down
336 changes: 336 additions & 0 deletions .github/workflows/bump-release-monorepo.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,336 @@
name: Bump Release [monorepo]

permissions:
contents: read

# description: |
# Manual action to bump the current version and cut a release for mono-repo projects.
#
# Determine which version to bump.
# Generate release notes for each module.
# Tag all modules with the new version.
# Build a github release on pushed tag.

defaults:
run:
shell: bash

on:
workflow_call:
inputs:
bump-patch:
description: Bump a patch version release
type: string
required: false
default: 'true'
bump-minor:
description: Bump a minor version release
type: string
required: false
default: 'false'
bump-major:
description: Bump a major version release
type: string
required: false
default: 'false'
tag-message-title:
description: Tag message title to prepend to the release notes
required: false
type: string
tag-message-body:
description: |
Tag message body to prepend to the release notes.
(use "|" to replace end of line).
required: false
type: string
enable-tag-signing:
description: |
Enable PGP tag-signing by a bot user.

When enabled, you must pass the GPG secrets to this workflow.
required: false
type: string
default: 'true'
cliff-config:
type: string
required: false
default: '.cliff.toml'
description: 'Path to the git-cliff config file in the caller repository'
cliff-config-url:
type: string
required: false
default: 'https://raw.githubusercontent.com/go-openapi/ci-workflows/refs/heads/master/.cliff.toml'
description: 'URL to the remote git-cliff config file (used if local config does not exist)'
monorepo-cliff-template:
type: string
required: false
default: '.cliff-monorepo.toml'
description: 'Path to the git-cliff template used to generate module-specific release notes'
monorepo-cliff-template-url:
type: string
required: false
default: https://raw.githubusercontent.com/go-openapi/ci-workflows/refs/heads/master/.cliff-monorepo.toml
description: 'URL to the remote git-cliff template used to generate module-specific release notes'
secrets:
gpg-private-key:
description: |
GPG private key in armored format for signing tags.

Default for go-openapi: CI_BOT_GPG_PRIVATE_KEY

Required when enable-tag-signing is true.
required: false
gpg-passphrase:
description: |
Passphrase to unlock the GPG private key.

Default for go-openapi: CI_BOT_GPG_PASSPHRASE

Required when enable-tag-signing is true.
required: false
gpg-fingerprint:
description: |
Fingerprint of the GPG signing key (spaces removed).

Default for go-openapi: CI_BOT_SIGNING_KEY

Required when enable-tag-signing is true.
required: false

jobs:
detect-modules:
name: Detect mono-repo modules
runs-on: ubuntu-latest
outputs:
is_monorepo: ${{ steps.detect-monorepo.outputs.is_monorepo }}
names: ${{ steps.detect-monorepo.outputs.names }}
bash-relative-names: ${{ steps.detect-monorepo.outputs.bash-relative-names }}
steps:
-
name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
-
name: Setup Go
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version: stable
check-latest: true
cache: true
cache-dependency-path: '**/go.sum'
-
name: Detect go mono-repo
id: detect-monorepo
uses: go-openapi/gh-actions/ci-jobs/detect-go-monorepo@0ec18ca1ddc1e2257097ae8d57952733109d80a1 # v1.4.0

bump-release-single:
name: Bump release (single module)
needs: [detect-modules]
if: ${{ needs.detect-modules.outputs.is-monorepo != 'true' }}
permissions:
contents: write
uses: ./.github/workflows/bump-release.yml
with:
bump-patch: ${{ inputs.bump-patch }}
bump-minor: ${{ inputs.bump-minor }}
bump-major: ${{ inputs.bump-major }}
tag-message-title: ${{ inputs.tag-message-title }}
tag-message-body: ${{ inputs.tag-message-body }}
enable-tag-signing: ${{ inputs.enable-tag-signing }}
cliff-config: ${{ inputs.cliff-config }}
cliff-config-url: ${{ inputs.cliff-config-url }}
secrets: inherit

determine-next-tag:
name: Determine next tag [monorepo]
needs: [detect-modules]
if: ${{ needs.detect-modules.outputs.is-monorepo == 'true' }}
runs-on: ubuntu-latest
outputs:
next-tag: ${{ steps.bump-release.outputs.next-tag }}
steps:
-
name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
-
name: Determine next tag
id: bump-release
uses: go-openapi/gh-actions/ci-jobs/next-tag@0ec18ca1ddc1e2257097ae8d57952733109d80a1 # v1.4.0
with:
bump-patch: ${{ inputs.bump-patch }}
bump-minor: ${{ inputs.bump-minor }}
bump-major: ${{ inputs.bump-major }}

prepare-modules:
name: Prepare module updates [monorepo]
needs: [detect-modules, determine-next-tag]
if: ${{ needs.detect-modules.outputs.is-monorepo == 'true' }}
permissions:
contents: write
pull-requests: write
uses: ./.github/workflows/prepare-release-monorepo.yml
with:
target-tag: ${{ needs.determine-next-tag.outputs.next-tag }}
enable-commit-signing: 'true'
secrets: inherit

wait-for-merge:
name: Wait for PR merge [monorepo]
needs: [detect-modules, prepare-modules]
if: ${{ needs.detect-modules.outputs.is-monorepo == 'true' }}
runs-on: ubuntu-latest
env:
PR_URL: ${{ needs.prepare-modules.outputs.pull-request-url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
-
name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
-
name: Wait for PR to be merged
run: |
echo "::notice title=waiting-for-merge::Waiting for PR ${PR_URL} to be merged"

MAX_WAIT=1800 # 30 minutes maximum wait time
POLL_INTERVAL=30 # Check every 30 seconds
elapsed=0

while [ $elapsed -lt $MAX_WAIT ]; do
# Check PR state
PR_STATE=$(gh pr view "$PR_URL" --json state --jq '.state')

if [[ "$PR_STATE" == "MERGED" ]]; then
echo "::notice title=pr-merged::PR has been merged successfully"
exit 0
elif [[ "$PR_STATE" == "CLOSED" ]]; then
echo "::error title=pr-closed::PR was closed without merging"
exit 1
fi

echo "::notice title=polling::PR state: ${PR_STATE}, waiting... (${elapsed}s elapsed)"
sleep $POLL_INTERVAL
elapsed=$((elapsed + POLL_INTERVAL))
done

echo "::error title=timeout::Timed out waiting for PR to be merged after ${MAX_WAIT}s"
exit 1

tag-release-monorepo:
name: Tag release (mono-repo)
needs: [detect-modules, determine-next-tag, wait-for-merge]
if: ${{ needs.detect-modules.outputs.is-monorepo == 'true' }}
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
next-tag: ${{ needs.determine-next-tag.outputs.next-tag }}
all-tags: ${{ steps.tag-modules.outputs.all-tags }}
steps:
-
name: Checkout code (fresh after PR merge)
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
# Fetch the latest code after the PR has been merged
ref: ${{ github.ref }}
-
name: Setup Go
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version: stable
check-latest: true
cache: true
cache-dependency-path: '**/go.sum'
-
name: Configure bot credentials
if: ${{ inputs.enable-tag-signing == 'true' }}
uses: go-openapi/gh-actions/ci-jobs/bot-credentials@0ec18ca1ddc1e2257097ae8d57952733109d80a1 # v1.4.0
# This is using the GPG signature of bot-go-openapi.
#
# For go-openapi repos (using secrets: inherit):
# Falls back to: CI_BOT_GPG_PRIVATE_KEY, CI_BOT_GPG_PASSPHRASE, CI_BOT_SIGNING_KEY
#
# For other orgs: explicitly pass secrets with your custom names
# NOTE(fredbi): extracted w/ gpg -K --homedir gnupg --keyid-format LONG --with-keygrip --fingerprint --with-subkey-fingerprint
with:
enable-gpg-signing: 'true'
gpg-private-key: ${{ secrets.gpg-private-key || secrets.CI_BOT_GPG_PRIVATE_KEY }}
gpg-passphrase: ${{ secrets.gpg-passphrase || secrets.CI_BOT_GPG_PASSPHRASE }}
gpg-fingerprint: ${{ secrets.gpg-fingerprint || secrets.CI_BOT_SIGNING_KEY }}
enable-tag-signing: 'true'
enable-commit-signing: 'false'
-
name: Tag all modules
id: tag-modules
env:
NEXT_TAG: ${{ needs.determine-next-tag.outputs.next-tag }}
MESSAGE_TITLE: ${{ inputs.tag-message-title }}
MESSAGE_BODY: ${{ inputs.tag-message-body }}
run: |
# Tag all modules similar to hack/tag_modules.sh
# Note: The PR with updated go.mod files has been merged at this point
root="$(git rev-parse --show-toplevel)"
declare -a all_tags

cd "${root}"

# Construct the tag message
MESSAGE="${MESSAGE_TITLE}"
if [[ -n "${MESSAGE_BODY}" ]] ; then
BODY=$(echo "${MESSAGE_BODY}"|tr '|' '\n')
MESSAGE=$(printf "%s\n%s\n" "${MESSAGE}" "${BODY}")
fi

echo "::notice title=tag-message::Tagging all modules for ${NEXT_TAG}"

SIGNED=""
if [[ '${{ inputs.enable-tag-signing }}' == 'true' ]] ; then
SIGNED="-s"
fi

# Tag all modules
while read -r module_relative_name ; do
if [[ -z "${module_relative_name}" ]] ; then
module_tag="${NEXT_TAG}" # e.g. "v0.24.0"
else
module_tag="${module_relative_name}/${NEXT_TAG}" # e.g. "mangling/v0.24.0"
fi

all_tags+=("${module_tag}")
echo "::notice title=tagging::Creating tag: ${module_tag}"

git tag ${SIGNED} -m "${MESSAGE}" "${module_tag}"

if [[ -n "${SIGNED}" ]] ; then
git tag -v "${module_tag}"
fi
done < <(echo ${{ needs.detect-modules.outputs.bash-relative-names }})

# Save all tags for output
echo "all-tags=${all_tags[@]}" >> "${GITHUB_OUTPUT}"

# Push all tags to origin
echo "::notice title=pushing-tags::Pushing tags: ${all_tags[@]}"
git push origin ${all_tags[@]}

gh-release-monorepo:
# trigger release creation explicitly.
# The previous tagging action does not trigger the normal release workflow
# (github prevents cascading triggers from happening).
name: Create release [monorepo]
needs: [detect-modules, tag-release-monorepo]
if: ${{ needs.detect-modules.outputs.is-monorepo == 'true' }}
permissions:
contents: write
uses: ./.github/workflows/release.yml
with:
tag: ${{ needs.tag-release-monorepo.outputs.next-tag }}
is-monorepo: 'true'
cliff-config: ${{ inputs.cliff-config }}
cliff-config-url: ${{ inputs.cliff-config-url }}
monorepo-cliff-template: ${{ inputs.monorepo-cliff-template }}
monorepo-cliff-template-url: ${{ inputs.monorepo-cliff-temlate-url }}
secrets: inherit
Loading
Loading