Skip to content

Commit ea0a088

Browse files
committed
Merge ctl/main into sync branch: upstream workflows, contract tooling, concurrency improvements, workspace version strategy
2 parents 1a7579c + 25795ce commit ea0a088

235 files changed

Lines changed: 19194 additions & 2911 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
name: Auto Bump Module Dependencies
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
coreVersion:
7+
description: 'Core modular version (e.g. v1.9.0)'
8+
required: true
9+
type: string
10+
workflow_call:
11+
inputs:
12+
coreVersion:
13+
required: true
14+
type: string
15+
secrets:
16+
GH_TOKEN:
17+
required: true
18+
19+
permissions:
20+
contents: write
21+
pull-requests: write
22+
actions: read
23+
checks: write
24+
25+
jobs:
26+
bump:
27+
runs-on: ubuntu-latest
28+
env:
29+
GOTOOLCHAIN: auto
30+
CGO_ENABLED: 0
31+
steps:
32+
- name: Checkout
33+
uses: actions/checkout@v5
34+
with:
35+
fetch-depth: 0
36+
37+
- name: Remove go.work files
38+
run: |
39+
git rm -f go.work || true
40+
git rm -f go.work.sum || true
41+
rm -f go.work go.work.sum || true
42+
43+
- name: Set up Go
44+
uses: actions/setup-go@v5
45+
with:
46+
go-version: '^1.25'
47+
check-latest: true
48+
49+
- name: Determine version
50+
id: ver
51+
run: |
52+
V='${{ inputs.coreVersion || github.event.inputs.coreVersion }}'
53+
[[ $V == v* ]] || V="v$V"
54+
echo "Using core version $V"
55+
echo "core_version=$V" >> $GITHUB_OUTPUT
56+
57+
- name: Update module go.mod files
58+
run: |
59+
set -euo pipefail
60+
CORE=${{ steps.ver.outputs.core_version }}
61+
UPDATED=0
62+
for mod in modules/*/go.mod; do
63+
[ -f "$mod" ] || continue
64+
dir=$(dirname "$mod")
65+
# If the require line exists with a version different from CORE, update via go mod edit (portable, avoids sed incompat)
66+
if grep -q "github.com/GoCodeAlone/modular v" "$mod" && ! grep -q "github.com/GoCodeAlone/modular ${CORE}" "$mod"; then
67+
(cd "$dir" && go mod edit -require=github.com/GoCodeAlone/modular@${CORE})
68+
UPDATED=1
69+
fi
70+
# Drop any replace directive pointing to local modular path to avoid accidental pinning
71+
(cd "$dir" && go mod edit -dropreplace=github.com/GoCodeAlone/modular 2>/dev/null || true)
72+
done
73+
if [ "$UPDATED" = 0 ]; then
74+
echo "No module files needed updating"
75+
else
76+
echo "Module go.mod files updated to ${CORE}"
77+
fi
78+
79+
- name: Go mod tidy each module
80+
run: |
81+
set -euo pipefail
82+
for dir in modules/*/; do
83+
[ -f "$dir/go.mod" ] || continue
84+
echo "Tidying $dir"
85+
(cd "$dir" && go mod tidy)
86+
done
87+
88+
- name: Go mod tidy each example
89+
run: |
90+
set -euo pipefail
91+
for dir in examples/*/; do
92+
[ -f "$dir/go.mod" ] || continue
93+
echo "Tidying $dir"
94+
(cd "$dir" && go mod tidy)
95+
done
96+
97+
- name: Go mod tidy root (defensive)
98+
run: |
99+
set -euo pipefail
100+
if [ -f go.mod ]; then
101+
echo "Tidying root module"
102+
go mod tidy
103+
fi
104+
105+
- name: Go mod tidy modcli
106+
run: |
107+
set -euo pipefail
108+
if [ -f cmd/modcli/go.mod ]; then
109+
echo "Tidying cmd/modcli"
110+
(cd cmd/modcli && go mod tidy)
111+
fi
112+
113+
- name: Update documentation version references
114+
run: |
115+
set -euo pipefail
116+
CORE=${{ steps.ver.outputs.core_version }}
117+
OLD=$(git grep -h -o 'github.com/GoCodeAlone/modular v[0-9]\+\.[0-9]\+\.[0-9]\+' -- '*.md' | grep -v $CORE | head -n1 | awk '{print $1}' || true)
118+
# Replace any explicit old version with current in markdown examples
119+
if [ -n "$OLD" ]; then
120+
find . -name '*.md' -print0 | xargs -0 sed -i "" -E "s#github.com/GoCodeAlone/modular v[0-9]+\.[0-9]+\.[0-9]+#github.com/GoCodeAlone/modular ${CORE}#g" || find . -name '*.md' -print0 | xargs -0 sed -i -E "s#github.com/GoCodeAlone/modular v[0-9]+\.[0-9]+\.[0-9]+#github.com/GoCodeAlone/modular ${CORE}#g"
121+
fi
122+
123+
- name: Create PR
124+
id: pr
125+
env:
126+
GH_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }}
127+
run: |
128+
set -euo pipefail
129+
CORE=${{ steps.ver.outputs.core_version }}
130+
BRANCH=auto/bump-modules-${CORE}
131+
git config user.name 'github-actions'
132+
git config user.email 'github-actions@users.noreply.github.com'
133+
# Stage and commit changes first on detached HEAD (checkout provided by actions/checkout)
134+
if git diff --quiet; then
135+
echo "No changes to commit"
136+
echo "created=false" >> $GITHUB_OUTPUT
137+
exit 0
138+
fi
139+
git add .
140+
git commit -m "chore: bump module dependencies to ${CORE}"
141+
# Create or reset branch name to current commit (safe overwrite of existing remote branch)
142+
git branch -f "$BRANCH"
143+
git checkout -B "$BRANCH"
144+
echo "Pushing branch $BRANCH (force-with-lease)"
145+
if ! git push --force-with-lease origin "$BRANCH"; then
146+
git push --force origin "$BRANCH"
147+
fi
148+
PR_URL=$(gh pr view "$BRANCH" --json url --jq .url 2>/dev/null || gh pr create --title "chore: bump module dependencies to ${CORE}" --body "Automated update of module go.mod files and docs to ${CORE}." --head "$BRANCH" --base main --draft=false)
149+
echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT
150+
echo "created=true" >> $GITHUB_OUTPUT
151+
152+
- name: Run full tests (core + modules + examples + CLI)
153+
if: steps.pr.outputs.created == 'true'
154+
run: |
155+
set -euo pipefail
156+
echo "Enabling CGO for race builds"
157+
export CGO_ENABLED=1
158+
if command -v golangci-lint >/dev/null 2>&1; then golangci-lint run; fi
159+
go test ./... -count=1 -race -timeout=15m
160+
for module in modules/*/; do
161+
if [ -f "$module/go.mod" ]; then
162+
echo "Testing $module"; (cd "$module" && go test ./... -count=1 -race -timeout=15m)
163+
fi
164+
done
165+
for example in examples/*/; do
166+
if [ -f "$example/go.mod" ]; then
167+
echo "Testing $example"; (cd "$example" && go test ./... -count=1 -race -timeout=15m)
168+
fi
169+
done
170+
(cd cmd/modcli && go test ./... -count=1 -race -timeout=15m)
171+
172+
173+
- name: Merge PR
174+
if: steps.pr.outputs.created == 'true'
175+
env:
176+
GH_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }}
177+
run: |
178+
set -euo pipefail
179+
PR=${{ steps.pr.outputs.pr_url }}
180+
[ -z "$PR" ] && { echo 'No PR URL'; exit 0; }
181+
# Try to enable auto-merge first; if policies block it, attempt an admin squash merge; otherwise leave PR open
182+
if ! gh pr merge "$PR" --squash --delete-branch --auto; then
183+
gh pr merge "$PR" --squash --delete-branch --admin || echo "Merge deferred: branch policies prevent automatic merge"
184+
fi

.github/workflows/bdd-matrix.yml

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
name: BDD Matrix
2+
3+
permissions:
4+
contents: read
5+
6+
on:
7+
pull_request:
8+
branches: [ main ]
9+
paths:
10+
- '**.go'
11+
- 'go.*'
12+
- 'modules/**'
13+
- '.github/workflows/bdd-matrix.yml'
14+
push:
15+
branches: [ main ]
16+
paths:
17+
- '**.go'
18+
- 'go.*'
19+
- 'modules/**'
20+
- '.github/workflows/bdd-matrix.yml'
21+
workflow_dispatch:
22+
23+
env:
24+
GO_VERSION: '^1.25'
25+
26+
jobs:
27+
# Discover modules (reused for matrix)
28+
discover:
29+
runs-on: ubuntu-latest
30+
outputs:
31+
modules: ${{ steps.set-matrix.outputs.modules }}
32+
matrix: ${{ steps.set-matrix.outputs.matrix }}
33+
steps:
34+
- uses: actions/checkout@v5
35+
with:
36+
fetch-depth: 0
37+
- id: set-matrix
38+
run: |
39+
ALL_MODULES=$(find modules -maxdepth 1 -mindepth 1 -type d -exec basename {} \; | sort)
40+
MODULES_JSON=$(echo "$ALL_MODULES" | tr ' ' '\n' | grep -v '^$' | jq -R . | jq -s .)
41+
echo "Modules: $MODULES_JSON"
42+
{
43+
echo "matrix<<EOF"; echo "{\"module\":$MODULES_JSON}"; echo "EOF"; } >> $GITHUB_OUTPUT
44+
{ echo "modules<<EOF"; echo "$MODULES_JSON"; echo "EOF"; } >> $GITHUB_OUTPUT
45+
46+
# Core framework BDD tests (single job)
47+
core-bdd:
48+
runs-on: ubuntu-latest
49+
needs: discover
50+
permissions:
51+
contents: read
52+
steps:
53+
- uses: actions/checkout@v5
54+
- uses: actions/setup-go@v5
55+
with:
56+
go-version: ${{ env.GO_VERSION }}
57+
check-latest: true
58+
cache: true
59+
- name: Run core framework BDD tests
60+
run: |
61+
set -e
62+
echo '=== Core Framework BDD Tests ==='
63+
export CGO_ENABLED=1
64+
export GORACE=halt_on_error=1
65+
go test -race -v -coverprofile=core-bdd-coverage.txt -covermode=atomic -run 'TestApplicationLifecycle|TestConfigurationManagement|TestBaseConfigBDDFeatures|TestLoggerDecorator' .
66+
- name: Upload core BDD coverage
67+
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0 pinned
68+
with:
69+
token: ${{ secrets.CODECOV_TOKEN }}
70+
slug: GoCodeAlone/modular
71+
files: core-bdd-coverage.txt
72+
flags: core-bdd
73+
- name: Persist core BDD coverage artifact
74+
uses: actions/upload-artifact@v4
75+
with:
76+
name: core-bdd-coverage
77+
path: core-bdd-coverage.txt
78+
79+
# Run each module's BDD tests in parallel matrix
80+
module-bdd:
81+
runs-on: ubuntu-latest
82+
needs: discover
83+
permissions:
84+
contents: read
85+
strategy:
86+
fail-fast: false
87+
matrix: ${{ fromJson(needs.discover.outputs.matrix) }}
88+
name: BDD ${{ matrix.module }}
89+
steps:
90+
- uses: actions/checkout@v5
91+
- uses: actions/setup-go@v5
92+
with:
93+
go-version: ${{ env.GO_VERSION }}
94+
check-latest: true
95+
cache: true
96+
- name: Run BDD tests
97+
working-directory: modules/${{ matrix.module }}
98+
run: |
99+
echo "=== BDD for ${{ matrix.module }} ==="
100+
# Run BDD-focused Go tests (naming convention: *BDD*)
101+
export CGO_ENABLED=1
102+
export GORACE=halt_on_error=1
103+
set -euo pipefail
104+
# Modules expected to contain BDD tests (update as coverage grows)
105+
EXPECTED_BDD_MODULES=(reverseproxy httpserver scheduler cache auth database eventbus)
106+
expected=false
107+
for m in "${EXPECTED_BDD_MODULES[@]}"; do
108+
if [ "$m" = "${{ matrix.module }}" ]; then expected=true; break; fi
109+
done
110+
if go test -race -v -coverprofile=bdd-${{ matrix.module }}-coverage.txt -covermode=atomic -run '.*BDD|.*Module' .; then
111+
echo "BDD tests executed for ${{ matrix.module }}"
112+
else
113+
echo "::error title=BDD Tests Failed::go test command failed for ${{ matrix.module }}" >&2
114+
exit 1
115+
fi
116+
if [ ! -s bdd-${{ matrix.module }}-coverage.txt ]; then
117+
if [ "$expected" = true ]; then
118+
echo "::error title=Missing Expected BDD Tests::Module ${{ matrix.module }} is in EXPECTED_BDD_MODULES but produced no BDD coverage" >&2
119+
exit 1
120+
else
121+
echo "::notice title=No BDD Tests Detected::No matching BDD tests for non-BDD module ${{ matrix.module }}" >&2
122+
echo 'mode: atomic' > bdd-${{ matrix.module }}-coverage.txt
123+
fi
124+
fi
125+
- name: Upload module BDD coverage
126+
if: always()
127+
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0 pinned
128+
with:
129+
token: ${{ secrets.CODECOV_TOKEN }}
130+
slug: GoCodeAlone/modular
131+
files: modules/${{ matrix.module }}/bdd-${{ matrix.module }}-coverage.txt
132+
flags: bdd-${{ matrix.module }}
133+
- name: Persist module BDD coverage artifact
134+
if: always()
135+
uses: actions/upload-artifact@v4
136+
with:
137+
name: bdd-${{ matrix.module }}-coverage
138+
path: modules/${{ matrix.module }}/bdd-${{ matrix.module }}-coverage.txt
139+
140+
# Summary job collates results
141+
summary:
142+
if: always()
143+
needs: [core-bdd, module-bdd, discover]
144+
runs-on: ubuntu-latest
145+
steps:
146+
- name: Checkout repository (for merge script)
147+
uses: actions/checkout@v5
148+
- name: Summarize
149+
run: |
150+
echo '# BDD Matrix Summary' >> $GITHUB_STEP_SUMMARY
151+
echo '' >> $GITHUB_STEP_SUMMARY
152+
echo 'Core BDD Job: ${{ needs.core-bdd.result }}' >> $GITHUB_STEP_SUMMARY
153+
echo '' >> $GITHUB_STEP_SUMMARY
154+
echo 'Modules (parallel) overall result: ${{ needs.module-bdd.result }}' >> $GITHUB_STEP_SUMMARY
155+
- name: Download all coverage artifacts
156+
uses: actions/download-artifact@v5
157+
with:
158+
path: bdd-coverage
159+
- name: Merge BDD coverage
160+
run: |
161+
set -euo pipefail
162+
ls -R bdd-coverage || true
163+
shopt -s globstar nullglob
164+
FILES=(bdd-coverage/**/*.txt)
165+
if [ ${#FILES[@]} -eq 0 ]; then
166+
echo "No coverage files found to merge" >&2
167+
# Nothing to do (don't fail)
168+
exit 0
169+
fi
170+
echo "Found ${#FILES[@]} coverage profiles"
171+
# Prefer central script if present, else inline merge
172+
if [ -f scripts/merge-coverprofiles.sh ]; then
173+
chmod +x scripts/merge-coverprofiles.sh
174+
if ! ./scripts/merge-coverprofiles.sh merged-bdd-coverage.txt "${FILES[@]}"; then
175+
echo "[warn] merge-coverprofiles.sh failed, falling back to inline merge" >&2
176+
else
177+
echo "Merged via script"; exit 0
178+
fi
179+
else
180+
echo "[info] merge-coverprofiles.sh not found, using inline merge" >&2
181+
fi
182+
# Inline merge fallback
183+
OUT=merged-bdd-coverage.txt
184+
FIRST=1
185+
: > "$OUT"
186+
for f in "${FILES[@]}"; do
187+
[ -f "$f" ] || { echo "[warn] Missing profile $f" >&2; continue; }
188+
if [ $FIRST -eq 1 ]; then
189+
cat "$f" >> "$OUT"; FIRST=0
190+
else
191+
{ grep -v '^mode:' "$f" || true; } >> "$OUT"
192+
fi
193+
done
194+
# Ensure a mode line exists at top
195+
if ! grep -q '^mode:' "$OUT"; then
196+
echo 'mode: atomic' | cat - "$OUT" > "$OUT.tmp" && mv "$OUT.tmp" "$OUT"
197+
fi
198+
# Never fail this step due to merge nuances
199+
true
200+
echo "Merged (fallback) into $OUT from ${#FILES[@]} files" >&2
201+
- name: Upload merged BDD coverage
202+
if: always()
203+
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0 pinned
204+
with:
205+
token: ${{ secrets.CODECOV_TOKEN }}
206+
slug: GoCodeAlone/modular
207+
files: merged-bdd-coverage.txt
208+
flags: merged-bdd
209+
- name: Persist merged BDD coverage artifact
210+
if: always()
211+
uses: actions/upload-artifact@v4
212+
with:
213+
name: merged-bdd-coverage
214+
path: merged-bdd-coverage.txt

0 commit comments

Comments
 (0)