Skip to content

Commit eaebe1f

Browse files
committed
ci(orchestrator): central deps.json + reverse-closure fan-out + receiver templates
Adds a central release orchestrator that handles bidirectional dependency propagation: when any package in the constellation ships a release, the set of downstream nodes that need a bump is computed automatically and each gets a dispatch in topological order. Files: .github/deps.json Canonical dependency graph. 25 nodes: hub (web4), 15 Go siblings, 1 Go app, 1 FFI fan-in (libpilot), 3 SDKs, 1 brew tap, 1 surface (website), 3 freeloaders (cosift, pilot-ca, wallet). Each node declares its depends_on edges and a bump_policy (stable_only, auto_commit_main, open_pr_instead_of_main). This is the single source of truth — changes to the graph are PR-reviewed diffs here. .github/workflows/orchestrator.yml Receives package-released dispatches from any node. Computes the reverse-transitive closure of the released package (BFS by depth in deps.json), filters per-receiver stable_only when the upstream is a prerelease, then fan-outs bump-upstream dispatches to each affected repo. Supports workflow_dispatch with a dry_run flag for rehearsal. .github/workflows/_template-emit-release.yml Reusable workflow that any sibling adopts: at the end of its own release.yml it calls this with the node name, and it dispatches package-released to the orchestrator. One token per sibling (ORCHESTRATOR_DISPATCH_TOKEN) covers the hop. .github/workflows/_template-bump-upstream.yml Generic receiver template for Go-sibling repos: receives bump-upstream, runs go mod edit -require=<module>@<version> + go mod tidy + go build sanity check, then either commits to main or opens a PR based on BUMP_MODE. SDK repos will need their own package-manager-specific variants. Closure smoke-tests pass: web4 → 22 targets (every depending node, ordered by depth) policy → 4 targets (libpilot + 3 SDKs) handshake → 4 targets (libpilot + 3 SDKs) libpilot → 3 targets (3 SDKs) sdk-node → 0 targets (leaf) cosift → 0 targets (freeloader) This SUPERSEDES the direct shockwave fan-out in PR #151's release.yml once orchestrator adoption rolls out — the four hardcoded repos there become entries in deps.json instead. Keeping #151's direct fan-out for now as the bootstrap path (orchestrator needs SHOCKWAVE_DISPATCH_TOKEN set before it can do anything).
1 parent c755a07 commit eaebe1f

4 files changed

Lines changed: 602 additions & 0 deletions

File tree

.github/deps.json

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
{
2+
"$schema": "https://pilotprotocol.network/.well-known/deps.schema.json",
3+
"version": 1,
4+
"description": "Canonical dependency graph of every Pilot Protocol repo. Read by .github/workflows/orchestrator.yml to compute which downstream nodes need a bump when any package ships a release. This is the SINGLE source of truth — keep it in sync whenever a repo gains or drops an upstream dependency.",
5+
"nodes": {
6+
"web4": {
7+
"repo": "TeoSlayer/pilotprotocol",
8+
"type": "hub",
9+
"go_module": "github.com/TeoSlayer/pilotprotocol",
10+
"depends_on": []
11+
},
12+
13+
"beacon": {
14+
"repo": "pilot-protocol/beacon",
15+
"type": "go-sibling",
16+
"go_module": "github.com/TeoSlayer/pilotprotocol/beacon",
17+
"depends_on": ["web4"],
18+
"bump_policy": { "stable_only": true, "auto_commit_main": true }
19+
},
20+
"dataexchange": {
21+
"repo": "pilot-protocol/dataexchange",
22+
"type": "go-sibling",
23+
"go_module": "github.com/TeoSlayer/pilotprotocol/dataexchange",
24+
"depends_on": ["web4"],
25+
"bump_policy": { "stable_only": true, "auto_commit_main": true }
26+
},
27+
"eventstream": {
28+
"repo": "pilot-protocol/eventstream",
29+
"type": "go-sibling",
30+
"go_module": "github.com/TeoSlayer/pilotprotocol/eventstream",
31+
"depends_on": ["web4"],
32+
"bump_policy": { "stable_only": true, "auto_commit_main": true }
33+
},
34+
"examples": {
35+
"repo": "pilot-protocol/examples",
36+
"type": "go-sibling",
37+
"go_module": "github.com/TeoSlayer/pilotprotocol/examples",
38+
"depends_on": ["web4"],
39+
"bump_policy": { "stable_only": true, "auto_commit_main": true }
40+
},
41+
"gateway": {
42+
"repo": "pilot-protocol/gateway",
43+
"type": "go-sibling",
44+
"go_module": "github.com/TeoSlayer/pilotprotocol/gateway",
45+
"depends_on": ["web4"],
46+
"bump_policy": { "stable_only": true, "auto_commit_main": true }
47+
},
48+
"handshake": {
49+
"repo": "pilot-protocol/handshake",
50+
"type": "go-sibling",
51+
"go_module": "github.com/TeoSlayer/pilotprotocol/handshake",
52+
"depends_on": ["web4"],
53+
"bump_policy": { "stable_only": true, "auto_commit_main": true }
54+
},
55+
"nameserver": {
56+
"repo": "pilot-protocol/nameserver",
57+
"type": "go-sibling",
58+
"go_module": "github.com/TeoSlayer/pilotprotocol/nameserver",
59+
"depends_on": ["web4"],
60+
"bump_policy": { "stable_only": true, "auto_commit_main": true }
61+
},
62+
"policy": {
63+
"repo": "pilot-protocol/policy",
64+
"type": "go-sibling",
65+
"go_module": "github.com/TeoSlayer/pilotprotocol/policy",
66+
"depends_on": ["web4"],
67+
"bump_policy": { "stable_only": true, "auto_commit_main": true }
68+
},
69+
"rendezvous": {
70+
"repo": "pilot-protocol/rendezvous",
71+
"type": "go-sibling",
72+
"go_module": "github.com/TeoSlayer/pilotprotocol/rendezvous",
73+
"depends_on": ["web4"],
74+
"bump_policy": { "stable_only": true, "auto_commit_main": true }
75+
},
76+
"runtime": {
77+
"repo": "pilot-protocol/runtime",
78+
"type": "go-sibling",
79+
"go_module": "github.com/TeoSlayer/pilotprotocol/runtime",
80+
"depends_on": ["web4"],
81+
"bump_policy": { "stable_only": true, "auto_commit_main": true }
82+
},
83+
"skillinject": {
84+
"repo": "pilot-protocol/skillinject",
85+
"type": "go-sibling",
86+
"go_module": "github.com/TeoSlayer/pilotprotocol/skillinject",
87+
"depends_on": ["web4"],
88+
"bump_policy": { "stable_only": true, "auto_commit_main": true }
89+
},
90+
"trustedagents": {
91+
"repo": "pilot-protocol/trustedagents",
92+
"type": "go-sibling",
93+
"go_module": "github.com/TeoSlayer/pilotprotocol/trustedagents",
94+
"depends_on": ["web4"],
95+
"bump_policy": { "stable_only": true, "auto_commit_main": true }
96+
},
97+
"updater": {
98+
"repo": "pilot-protocol/updater",
99+
"type": "go-sibling",
100+
"go_module": "github.com/TeoSlayer/pilotprotocol/updater",
101+
"depends_on": ["web4"],
102+
"bump_policy": { "stable_only": true, "auto_commit_main": true }
103+
},
104+
"webhook": {
105+
"repo": "pilot-protocol/webhook",
106+
"type": "go-sibling",
107+
"go_module": "github.com/TeoSlayer/pilotprotocol/webhook",
108+
"depends_on": ["web4"],
109+
"bump_policy": { "stable_only": true, "auto_commit_main": true }
110+
},
111+
"app-store": {
112+
"repo": "pilot-protocol/app-store",
113+
"type": "go-app",
114+
"go_module": "github.com/TeoSlayer/pilotprotocol/app-store/integration",
115+
"depends_on": ["web4"],
116+
"bump_policy": { "stable_only": true, "auto_commit_main": true }
117+
},
118+
119+
"libpilot": {
120+
"repo": "pilot-protocol/libpilot",
121+
"type": "ffi-fan-in",
122+
"go_module": "github.com/TeoSlayer/pilotprotocol/libpilot",
123+
"depends_on": ["web4", "handshake", "policy", "runtime", "skillinject"],
124+
"bump_policy": {
125+
"stable_only": true,
126+
"open_pr_instead_of_main": true,
127+
"_note": "FFI fan-in feeds all 3 SDKs at build time — a bad bump cascades to every SDK. Use PR + CI gate."
128+
}
129+
},
130+
131+
"sdk-node": {
132+
"repo": "pilot-protocol/sdk-node",
133+
"type": "sdk",
134+
"package_manager": "npm",
135+
"depends_on": ["libpilot"],
136+
"bump_policy": { "stable_only": true, "open_pr_instead_of_main": true }
137+
},
138+
"sdk-python": {
139+
"repo": "pilot-protocol/sdk-python",
140+
"type": "sdk",
141+
"package_manager": "pypi",
142+
"depends_on": ["libpilot"],
143+
"bump_policy": { "stable_only": true, "open_pr_instead_of_main": true }
144+
},
145+
"sdk-swift": {
146+
"repo": "pilot-protocol/sdk-swift",
147+
"type": "sdk",
148+
"package_manager": "swiftpm",
149+
"depends_on": ["libpilot"],
150+
"bump_policy": { "stable_only": true, "open_pr_instead_of_main": true }
151+
},
152+
153+
"homebrew-pilot": {
154+
"repo": "TeoSlayer/homebrew-pilot",
155+
"type": "package-tap",
156+
"depends_on": ["web4"],
157+
"bump_policy": { "stable_only": true, "auto_commit_main": true }
158+
},
159+
"website": {
160+
"repo": "pilot-protocol/website",
161+
"type": "surface",
162+
"depends_on": ["web4"],
163+
"bump_policy": {
164+
"stable_only": false,
165+
"auto_commit_main": true,
166+
"_note": "Edge channel also publishes; manifest preserves latest_stable when upstream is prerelease."
167+
}
168+
},
169+
170+
"cosift": { "repo": "pilot-protocol/cosift", "type": "freeloader", "depends_on": [] },
171+
"pilot-ca": { "repo": "pilot-protocol/pilot-ca", "type": "freeloader", "depends_on": [] },
172+
"wallet": { "repo": "pilot-protocol/wallet", "type": "indirect", "depends_on": ["app-store"] }
173+
}
174+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
name: bump-upstream
2+
3+
# TEMPLATE — copy this file into a Go-sibling repo's `.github/workflows/`
4+
# and rename to `bump-upstream.yml`. It receives a `bump-upstream` dispatch
5+
# from web4's orchestrator and rewrites the local `go.mod` to require the
6+
# new version of the upstream that just shipped.
7+
#
8+
# Behavior:
9+
# - If client_payload.is_prerelease == true and this repo's policy is
10+
# stable_only (the orchestrator already filtered, but we re-check), skip.
11+
# - `go mod edit -require=<module>@<version>`
12+
# - `go mod tidy`
13+
# - Commit to main OR open a PR depending on BUMP_MODE below.
14+
#
15+
# Tunables (set as repo variables or hardcode):
16+
# BUMP_MODE auto-commit | pr (default: auto-commit)
17+
# UPSTREAM_MODULE_PATH the import path of the upstream — e.g.
18+
# "github.com/TeoSlayer/pilotprotocol". Each
19+
# sibling has the same hub path; FFI nodes like
20+
# libpilot may receive multiple upstreams (the
21+
# orchestrator sends one dispatch per upstream).
22+
#
23+
# This template targets Go modules. SDK repos (sdk-node, sdk-python,
24+
# sdk-swift) need their own variants — see the per-SDK bump templates.
25+
26+
on:
27+
repository_dispatch:
28+
types: [bump-upstream]
29+
workflow_dispatch:
30+
inputs:
31+
upstream:
32+
description: 'Upstream node name (informational)'
33+
required: true
34+
version:
35+
description: 'Upstream version (e.g. v1.10.6)'
36+
required: true
37+
38+
permissions:
39+
contents: write
40+
pull-requests: write
41+
42+
env:
43+
BUMP_MODE: auto-commit
44+
UPSTREAM_MODULE_PATH: github.com/TeoSlayer/pilotprotocol
45+
46+
jobs:
47+
bump:
48+
runs-on: ubuntu-latest
49+
steps:
50+
- uses: actions/checkout@v4
51+
52+
- uses: actions/setup-go@v5
53+
with:
54+
go-version-file: go.mod
55+
56+
- name: Resolve incoming dispatch
57+
id: input
58+
env:
59+
DISPATCH_UP: ${{ github.event.client_payload.upstream }}
60+
DISPATCH_VER: ${{ github.event.client_payload.version }}
61+
DISPATCH_PRE: ${{ github.event.client_payload.is_prerelease }}
62+
MANUAL_UP: ${{ inputs.upstream }}
63+
MANUAL_VER: ${{ inputs.version }}
64+
run: |
65+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
66+
UP="$MANUAL_UP"; VER="$MANUAL_VER"; PRE="false"
67+
else
68+
UP="$DISPATCH_UP"; VER="$DISPATCH_VER"; PRE="$DISPATCH_PRE"
69+
fi
70+
# Re-check the prerelease gate — defense in depth in case a
71+
# mis-configured orchestrator forgets to filter.
72+
if [ "$PRE" = "true" ]; then
73+
echo "::notice::upstream ${UP} ${VER} is a prerelease; skipping (stable_only policy)"
74+
echo "skip=1" >> "$GITHUB_OUTPUT"
75+
exit 0
76+
fi
77+
echo "upstream=$UP" >> "$GITHUB_OUTPUT"
78+
echo "version=$VER" >> "$GITHUB_OUTPUT"
79+
echo "skip=0" >> "$GITHUB_OUTPUT"
80+
81+
- name: Bump go.mod
82+
if: steps.input.outputs.skip != '1'
83+
env:
84+
VER: ${{ steps.input.outputs.version }}
85+
run: |
86+
# The upstream module path is the hub module — siblings require
87+
# it directly. Sub-package nodes (handshake, policy, ...) are
88+
# currently part of the same Go module, so the require line
89+
# always points at the hub path. If a sibling later becomes its
90+
# own top-level Go module, set UPSTREAM_MODULE_PATH per-repo.
91+
go mod edit -require="${UPSTREAM_MODULE_PATH}@${VER}"
92+
go mod tidy
93+
echo "=== updated go.mod ==="
94+
grep "${UPSTREAM_MODULE_PATH}" go.mod || true
95+
96+
- name: Verify build still works
97+
if: steps.input.outputs.skip != '1'
98+
run: go build ./... || (echo "::error::build broke after bump"; exit 1)
99+
100+
- name: Commit (auto-commit mode)
101+
if: steps.input.outputs.skip != '1' && env.BUMP_MODE == 'auto-commit'
102+
env:
103+
UP: ${{ steps.input.outputs.upstream }}
104+
VER: ${{ steps.input.outputs.version }}
105+
run: |
106+
if git diff --quiet go.mod go.sum; then
107+
echo "go.mod unchanged — nothing to commit."
108+
exit 0
109+
fi
110+
git config user.name "pilot-release-bot"
111+
git config user.email "release-bot@pilotprotocol.network"
112+
git add go.mod go.sum
113+
git commit -m "deps: bump ${UP} to ${VER}" \
114+
-m "Triggered by orchestrator dispatch from upstream release."
115+
git push origin HEAD:main
116+
117+
- name: Open PR (pr mode)
118+
if: steps.input.outputs.skip != '1' && env.BUMP_MODE == 'pr'
119+
env:
120+
GH_TOKEN: ${{ github.token }}
121+
UP: ${{ steps.input.outputs.upstream }}
122+
VER: ${{ steps.input.outputs.version }}
123+
run: |
124+
if git diff --quiet go.mod go.sum; then
125+
echo "go.mod unchanged — nothing to commit."
126+
exit 0
127+
fi
128+
BRANCH="bump-${UP}-${VER}"
129+
git config user.name "pilot-release-bot"
130+
git config user.email "release-bot@pilotprotocol.network"
131+
git checkout -b "$BRANCH"
132+
git add go.mod go.sum
133+
git commit -m "deps: bump ${UP} to ${VER}"
134+
git push origin "$BRANCH"
135+
gh pr create \
136+
--title "deps: bump ${UP} to ${VER}" \
137+
--body "Orchestrator dispatch from upstream release ${UP} ${VER}." \
138+
--base main \
139+
--head "$BRANCH"

0 commit comments

Comments
 (0)