Skip to content

Commit 2cfa11e

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 2cfa11e

File tree

3 files changed

+136
-78
lines changed

3 files changed

+136
-78
lines changed

.claude/CLAUDE.md

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
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 only development-dependencies group
114+
- **bot-go-openapi[bot]**: Auto-approves and auto-merges all PRs (for contributor updates, etc.)
115+
116+
## Key Configuration Files
117+
118+
- `.golangci.yml` - Linter configuration (many linters disabled to match go-openapi style)
119+
- `.cliff.toml` - Release notes generation configuration
120+
- `.github/dependabot.yaml` - Dependency update configuration
121+
- `go.mod` - Requires Go 1.24.0
122+
123+
## Important Notes
124+
125+
- All workflow action versions are pinned to commit SHAs for security
126+
- Permissions are explicitly granted at job level to follow least-privilege principle
127+
- This repo itself uses minimal Go code (just sample tests); it's primarily YAML workflows
128+
- The `local-*` workflows serve as both tests and documentation of proper usage

.github/workflows/auto-merge.yml

Lines changed: 4 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,6 @@ jobs:
4646
pull-requests: write
4747
runs-on: ubuntu-latest
4848
if: ${{ github.event.pull_request.user.login == 'bot-go-openapi[bot]' }}
49-
env:
50-
PR_URL: ${{github.event.pull_request.html_url}}
51-
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
5249
steps:
5350
-
5451
name: Checkout repository
@@ -57,43 +54,10 @@ jobs:
5754
run: gh pr review --approve "$PR_URL"
5855
-
5956
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
57+
uses: go-openapi/gh-actions/ci-jobs/wait-pending-jobs@583ffca16d371aa3df4b9d5ac58c88050df94a76 # v1.1.0
58+
with:
59+
pr-url: ${{ github.event.pull_request.html_url }}
60+
gh-token: ${{ secrets.GITHUB_TOKEN }}
9761
-
9862
name: Auto-merge bot-go-openapi PRs
9963
run: gh pr merge --auto --rebase "$PR_URL"

.github/workflows/contributors.yml

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ jobs:
7575
runs-on: ubuntu-latest
7676
env:
7777
PR_URL: ${{needs.update-contributors.outputs.pull-request-url}}
78-
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
7978
steps:
8079
-
8180
name: Checkout repository
@@ -85,43 +84,10 @@ jobs:
8584
run: gh pr review --approve "$PR_URL"
8685
-
8786
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
87+
uses: go-openapi/gh-actions/ci-jobs/wait-pending-jobs@i583ffca16d371aa3df4b9d5ac58c88050df94a76 # v1.1.0
88+
with:
89+
pr-url: ${{ github.event.pull_request.html_url }}
90+
gh-token: ${{ secrets.GITHUB_TOKEN }}
12591
-
12692
name: Auto-merge PR
12793
run: gh pr merge --auto --rebase "$PR_URL"

0 commit comments

Comments
 (0)