Skip to content

Commit 54f65b0

Browse files
committed
refact: factorized the workflow waiting loops as a gh action
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
1 parent fe2baeb commit 54f65b0

File tree

3 files changed

+150
-80
lines changed

3 files changed

+150
-80
lines changed

.claude/CLAUDE.md

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Repository Overview
6+
7+
This repository provides shared, reusable GitHub Actions workflows for the go-openapi organization. The workflows are designed to be called from other go-openapi repositories to standardize CI/CD processes across the entire project family.
8+
9+
## Testing & Development Commands
10+
11+
### Running Tests
12+
```bash
13+
# Run all unit tests with coverage
14+
gotestsum --jsonfile 'unit.report.json' -- -race -p 2 -count 1 -timeout=20m -coverprofile='unit.coverage.out' -covermode=atomic -coverpkg="$(go list)"/... ./...
15+
16+
# Run a single test
17+
go test -v -run TestName ./path/to/package
18+
19+
# Run tests locally (as CI would)
20+
# Uses the local-* workflows which mirror how other repos consume these workflows
21+
```
22+
23+
### Linting
24+
```bash
25+
# Run golangci-lint (uses .golangci.yml configuration)
26+
golangci-lint run
27+
28+
# The linter configuration is highly customized with many linters disabled
29+
# to match go-openapi's established code style
30+
```
31+
32+
### Fuzz Testing
33+
```bash
34+
# List available fuzz tests across all packages
35+
go test -list Fuzz ./...
36+
37+
# Run a specific fuzz test for 1m30s
38+
go test -fuzz=FuzzTestName -fuzztime=1m30s ./path/to/package
39+
40+
# Fuzz corpus is cached in $(go env GOCACHE)/fuzz
41+
```
42+
43+
## Architecture & Design
44+
45+
### Workflow Types
46+
47+
This repository contains two types of workflows:
48+
49+
1. **Shared Workflows** (called by other repos):
50+
- `go-test.yml` - Complete test pipeline with linting, unit tests, fuzz tests, coverage
51+
- `auto-merge.yml` - Auto-approves and merges dependabot and bot PRs
52+
- `bump-release.yml` - Manually triggered release workflow (creates signed tags)
53+
- `tag-release.yml` - Release workflow triggered by pushing tags
54+
- `release.yml` - Common release building workflow (called by other release workflows)
55+
- `codeql.yml` - CodeQL security scanning for Go and GitHub Actions
56+
- `scanner.yml` - Trivy and govulncheck vulnerability scanning
57+
- `contributors.yml` - Automatically updates CONTRIBUTORS.md
58+
- `collect-coverage.yml` - Collects and publishes coverage to codecov
59+
- `collect-reports.yml` - Collects and publishes test reports
60+
- `fuzz-test.yml` - Orchestrates fuzz testing with cached corpus
61+
62+
2. **Local Test Workflows** (prefixed with `local-*`):
63+
- These workflows test the shared workflows within this repository
64+
- They mimic how consuming repos would invoke the shared workflows
65+
- Example: `local-go-test.yml` calls `./.github/workflows/go-test.yml`
66+
67+
### How Shared Workflows Are Used
68+
69+
Other go-openapi repos consume these workflows like:
70+
71+
```yaml
72+
jobs:
73+
test:
74+
uses: go-openapi/ci-workflows/.github/workflows/go-test.yml@master
75+
secrets: inherit
76+
```
77+
78+
Recommended practice: pin to a commit SHA and let dependabot update it:
79+
```yaml
80+
uses: go-openapi/ci-workflows/.github/workflows/go-test.yml@b28a8b978a5ee5b7f4241ffafd6cc6163edb5dfd # v0.1.0
81+
```
82+
83+
### Fuzz Test Architecture
84+
85+
Fuzz testing has a unique multi-stage design due to Go's limitation that `go test -fuzz` cannot run across multiple packages:
86+
87+
1. **fuzz-matrix job**: Discovers all fuzz tests using `go test -list Fuzz -json` and creates a matrix
88+
2. **fuzz-test job**: Runs each discovered fuzz test in parallel with:
89+
- Cached corpus stored in GitHub Actions cache (max 250MB)
90+
- Automatic cache purging to maintain size limits
91+
- Failed corpus uploaded as artifacts for 60 days
92+
- Default fuzz time: 1m30s, minimize time: 5m
93+
94+
### Release Process
95+
96+
Releases can be triggered in two ways:
97+
98+
1. **Manual bump** via `bump-release.yml`:
99+
- Select patch/minor/major bump
100+
- Creates GPG-signed tag using bot credentials
101+
- Triggers release build automatically
102+
103+
2. **Direct tag push** via `tag-release.yml`:
104+
- Push a semver tag (signed tags preferred)
105+
- Tag message is prepended to release notes
106+
- Triggers release build
107+
108+
Release notes are generated using [git-cliff](https://git-cliff.org/) with configuration in `.cliff.toml`.
109+
110+
### Auto-merge Logic
111+
112+
The `auto-merge.yml` workflow handles two bot types:
113+
- **dependabot[bot]**: Auto-approves all PRs, auto-merges the following groups:
114+
- `development-dependencies`
115+
- `go-openapi-dependencies` (for minor and patch updates only)
116+
- `golang-org-dependencies`
117+
- **bot-go-openapi[bot]**: Auto-approves and auto-merges all PRs (for contributor updates, etc.)
118+
119+
## Key Configuration Files
120+
121+
- `.golangci.yml` - Linter configuration (many linters disabled to match go-openapi style)
122+
- `.cliff.toml` - Release notes generation configuration
123+
- `.github/dependabot.yaml` - Dependency update configuration
124+
- `go.mod` - Requires Go 1.24.0
125+
126+
## Important Notes
127+
128+
- All workflow action versions are pinned to commit SHAs for security
129+
- Permissions are explicitly granted at job level to follow least-privilege principle
130+
- This repo itself uses minimal Go code (just sample tests); it's primarily YAML workflows
131+
- The `local-*` workflows serve as both tests and documentation of proper usage

.github/workflows/auto-merge.yml

Lines changed: 15 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,28 @@ jobs:
1717
pull-requests: write
1818
runs-on: ubuntu-latest
1919
if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }}
20+
env:
21+
PR_URL: ${{github.event.pull_request.html_url}}
22+
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
2023
steps:
2124
-
2225
name: Dependabot metadata
2326
id: metadata
2427
uses: dependabot/fetch-metadata@08eff52bf64351f401fb50d4972fa95b9f2c2d1b # v2.4.0
2528
-
2629
name: Auto-approve all dependabot PRs
27-
env:
28-
PR_URL: ${{github.event.pull_request.html_url}}
29-
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
3030
run: gh pr review --approve "$PR_URL"
3131
-
3232
name: Auto-merge dependabot PRs for development dependencies
3333
if: ${{ contains(steps.metadata.outputs.dependency-group, 'development-dependencies') }}
34-
env:
35-
PR_URL: ${{github.event.pull_request.html_url}}
36-
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
34+
run: gh pr merge --auto --rebase "$PR_URL"
35+
-
36+
name: Auto-merge dependabot PRs for go-openapi patches
37+
if: ${{ contains(steps.metadata.outputs.dependency-group, 'go-openapi-dependencies') && (steps.metadata.outputs.update-type == 'version-update:semver-minor' || steps.metadata.outputs.update-type == 'version-update:semver-patch') }}
38+
run: gh pr merge --auto --rebase "$PR_URL"
39+
-
40+
name: Auto-merge dependabot PRs for golang.org updates
41+
if: ${{ contains(steps.metadata.outputs.dependency-group, 'golang-org-dependencies') }}
3742
run: gh pr merge --auto --rebase "$PR_URL"
3843

3944
actions-bot:
@@ -57,43 +62,10 @@ jobs:
5762
run: gh pr review --approve "$PR_URL"
5863
-
5964
name: Wait for all workflow runs to complete
60-
run: |
61-
PR_NUMBER=$(echo "$PR_URL" | grep -oP '\d+$')
62-
echo "Waiting for all workflow runs to complete on PR #${PR_NUMBER}..."
63-
64-
# Get the PR's head SHA
65-
HEAD_SHA=$(gh pr view "$PR_NUMBER" --json headRefOid --jq '.headRefOid')
66-
echo "Head SHA: ${HEAD_SHA}"
67-
68-
MAX_WAIT=600 # 10 minutes max wait
69-
ELAPSED=0
70-
SLEEP_INTERVAL=10
71-
72-
while [ $ELAPSED -lt $MAX_WAIT ]; do
73-
# Get all workflow runs for this SHA using the GitHub API
74-
RUNS=$(gh api "/repos/${{ github.repository }}/actions/runs?head_sha=${HEAD_SHA}&per_page=100" --jq '.workflow_runs')
75-
76-
# Count how many are still in progress, queued, or waiting
77-
IN_PROGRESS=$(echo "$RUNS" | jq '[.[] | select(.status == "in_progress" or .status == "queued" or .status == "waiting")] | length')
78-
79-
if [ "$IN_PROGRESS" -eq 0 ]; then
80-
echo "::notice::All workflow runs completed for SHA ${HEAD_SHA}"
81-
TOTAL_RUNS=$(echo "$RUNS" | jq 'length')
82-
echo "Total workflow runs: ${TOTAL_RUNS}"
83-
break
84-
fi
85-
86-
# Show which workflows are still running
87-
RUNNING=$(echo "$RUNS" | jq -r '.[] | select(.status == "in_progress" or .status == "queued" or .status == "waiting") | .name' | tr '\n' ', ' | sed 's/,$//')
88-
echo "Still waiting for ${IN_PROGRESS} workflow run(s): ${RUNNING}"
89-
90-
sleep $SLEEP_INTERVAL
91-
ELAPSED=$((ELAPSED + SLEEP_INTERVAL))
92-
done
93-
94-
if [ $ELAPSED -ge $MAX_WAIT ]; then
95-
echo "::warning::Timeout waiting for all workflow runs to complete, proceeding anyway"
96-
fi
65+
uses: go-openapi/gh-actions/ci-jobs/wait-pending-jobs@583ffca16d371aa3df4b9d5ac58c88050df94a76 # v1.1.0
66+
with:
67+
pr-url: ${{ env.PR_URL }}
68+
gh-token: ${{ secrets.GITHUB_TOKEN }}
9769
-
9870
name: Auto-merge bot-go-openapi PRs
9971
run: gh pr merge --auto --rebase "$PR_URL"

.github/workflows/contributors.yml

Lines changed: 4 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -85,43 +85,10 @@ jobs:
8585
run: gh pr review --approve "$PR_URL"
8686
-
8787
name: Wait for all workflow runs to complete
88-
run: |
89-
PR_NUMBER=$(echo "$PR_URL" | grep -oP '\d+$')
90-
echo "Waiting for all workflow runs to complete on PR #${PR_NUMBER}..."
91-
92-
# Get the PR's head SHA
93-
HEAD_SHA=$(gh pr view "$PR_NUMBER" --json headRefOid --jq '.headRefOid')
94-
echo "Head SHA: ${HEAD_SHA}"
95-
96-
MAX_WAIT=600 # 10 minutes max wait
97-
ELAPSED=0
98-
SLEEP_INTERVAL=10
99-
100-
while [ $ELAPSED -lt $MAX_WAIT ]; do
101-
# Get all workflow runs for this SHA using the GitHub API
102-
RUNS=$(gh api "/repos/${{ github.repository }}/actions/runs?head_sha=${HEAD_SHA}&per_page=100" --jq '.workflow_runs')
103-
104-
# Count how many are still in progress, queued, or waiting
105-
IN_PROGRESS=$(echo "$RUNS" | jq '[.[] | select(.status == "in_progress" or .status == "queued" or .status == "waiting")] | length')
106-
107-
if [ "$IN_PROGRESS" -eq 0 ]; then
108-
echo "::notice::All workflow runs completed for SHA ${HEAD_SHA}"
109-
TOTAL_RUNS=$(echo "$RUNS" | jq 'length')
110-
echo "Total workflow runs: ${TOTAL_RUNS}"
111-
break
112-
fi
113-
114-
# Show which workflows are still running
115-
RUNNING=$(echo "$RUNS" | jq -r '.[] | select(.status == "in_progress" or .status == "queued" or .status == "waiting") | .name' | tr '\n' ', ' | sed 's/,$//')
116-
echo "Still waiting for ${IN_PROGRESS} workflow run(s): ${RUNNING}"
117-
118-
sleep $SLEEP_INTERVAL
119-
ELAPSED=$((ELAPSED + SLEEP_INTERVAL))
120-
done
121-
122-
if [ $ELAPSED -ge $MAX_WAIT ]; then
123-
echo "::warning::Timeout waiting for all workflow runs to complete, proceeding anyway"
124-
fi
88+
uses: go-openapi/gh-actions/ci-jobs/wait-pending-jobs@583ffca16d371aa3df4b9d5ac58c88050df94a76 # v1.1.0
89+
with:
90+
pr-url: ${{ env.PR_URL }}
91+
gh-token: ${{ secrets.GITHUB_TOKEN }}
12592
-
12693
name: Auto-merge PR
12794
run: gh pr merge --auto --rebase "$PR_URL"

0 commit comments

Comments
 (0)