Skip to content

Commit 155c468

Browse files
authored
install protected native auto-merge control (#15)
1 parent a5a0002 commit 155c468

2 files changed

Lines changed: 70 additions & 37 deletions

File tree

Lines changed: 65 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1+
# Generated by labkit (python -m labkit gen). Do not edit by hand.
2+
# Edit labs/<lab>/publish.config.json and regenerate; drift fails `labkit doctor`.
3+
# Install into operatorstack/value-map at .github/workflows/sync-upstream.yml (bootstrap step).
14
name: Sync from Intelligence Flow
25

36
on:
47
schedule:
5-
- cron: "23 */6 * * *"
8+
- cron: "9 */6 * * *"
69
workflow_dispatch:
710
inputs:
811
source_commit:
9-
description: Exact Intelligence Flow commit dispatched by Operator Stack Publisher
12+
description: Exact Intelligence Flow commit to project (defaults to main)
1013
required: false
1114
type: string
1215

@@ -23,69 +26,96 @@ jobs:
2326
runs-on: ubuntu-latest
2427
steps:
2528
- name: Create Operator Stack Publisher token
26-
id: publisher-token
29+
id: app-token
2730
uses: actions/create-github-app-token@v3
2831
with:
2932
client-id: ${{ vars.OPERATOR_STACK_PUBLISHER_APP_CLIENT_ID || vars.BOATSTACK_APP_CLIENT_ID }}
3033
private-key: ${{ secrets.OPERATOR_STACK_PUBLISHER_APP_PRIVATE_KEY || secrets.BOATSTACK_APP_PRIVATE_KEY }}
3134
owner: operatorstack
32-
repositories: value-map
33-
permission-contents: read
35+
repositories: |
36+
intelligence-flow
37+
value-map
38+
permission-contents: write
3439
permission-pull-requests: write
3540
- name: Check out Value Map
3641
uses: actions/checkout@v4
3742
with:
38-
path: value-map-repo
43+
path: public-repo
44+
token: ${{ steps.app-token.outputs.token }}
3945
- name: Check out Intelligence Flow
4046
uses: actions/checkout@v4
4147
with:
4248
repository: operatorstack/intelligence-flow
4349
ref: ${{ inputs.source_commit || 'main' }}
44-
path: intelligence-flow
4550
fetch-depth: 0
46-
- name: Project canonical skill
47-
id: project
51+
path: intelligence-flow
52+
token: ${{ steps.app-token.outputs.token }}
53+
- name: Set up Python
54+
uses: actions/setup-python@v5
55+
with:
56+
python-version: "3.12"
57+
- name: Install labkit
58+
shell: bash
59+
run: python3 -m pip install --quiet ./intelligence-flow/labkit
60+
- name: Generate projection
61+
id: generate
4862
shell: bash
4963
run: |
50-
if [[ -n "${{ inputs.source_commit }}" ]]; then
51-
source_commit="$(git -C intelligence-flow rev-parse HEAD)"
52-
else
53-
source_commit="$(git -C intelligence-flow log -1 --format=%H -- labs/14-product-value-projection)"
64+
source_commit="$(git -C intelligence-flow log -1 --format=%H -- labs/14-product-value-projection)"
65+
current_commit="$(jq -r '.source.commit // empty' public-repo/UPSTREAM.json 2>/dev/null || echo '')"
66+
if [[ -n "$current_commit" ]] &&
67+
! git -C intelligence-flow merge-base --is-ancestor "$current_commit" "$source_commit"; then
68+
echo "Ignoring stale request; Value Map already records $current_commit."
69+
echo "stale=true" >> "$GITHUB_OUTPUT"
70+
exit 0
5471
fi
55-
python3 value-map-repo/scripts/project_upstream.py \
56-
--source intelligence-flow \
57-
--repo value-map-repo \
58-
--commit "$source_commit" \
72+
python3 -m labkit project \
73+
--config intelligence-flow/labs/14-product-value-projection/publish.config.json \
74+
--repo public-repo \
75+
--source-commit "$source_commit" \
5976
--write
6077
echo "source_commit=$source_commit" >> "$GITHUB_OUTPUT"
78+
echo "stale=false" >> "$GITHUB_OUTPUT"
6179
- name: Open generated pull request
80+
if: steps.generate.outputs.stale != 'true'
6281
env:
63-
GH_TOKEN: ${{ steps.publisher-token.outputs.token }}
64-
SOURCE_COMMIT: ${{ steps.project.outputs.source_commit }}
82+
GH_TOKEN: ${{ steps.app-token.outputs.token }}
83+
SOURCE_COMMIT: ${{ steps.generate.outputs.source_commit }}
6584
shell: bash
6685
run: |
67-
cd value-map-repo
86+
cd public-repo
6887
if [[ -z "$(git status --porcelain)" ]]; then
6988
echo "Value Map already matches Intelligence Flow."
7089
exit 0
7190
fi
91+
git add -A
92+
body_file="$(mktemp)"
93+
{
94+
echo "## Projection provenance"; echo
95+
echo "Generated from \`operatorstack/intelligence-flow@$SOURCE_COMMIT\`."
96+
echo "Review the operator contract, provenance, and compatibility before merging."
97+
} > "$body_file"
7298
short="${SOURCE_COMMIT:0:12}"
7399
branch="sync/intelligence-flow-$short"
74100
existing="$(gh pr list --head "$branch" --state open --json url --jq '.[0].url')"
75-
if [[ -n "$existing" ]]; then
76-
echo "Upstream PR already open: $existing"
77-
gh pr merge "$existing" --auto --squash
78-
exit 0
79-
fi
80-
git config user.name "github-actions[bot]"
81-
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
82-
git checkout -B "$branch"
83-
git add value-map/SKILL.md UPSTREAM.json
84-
git commit -m "Sync Value Map from Intelligence Flow $short"
101+
git config user.name "${{ steps.app-token.outputs.app-slug }}[bot]"
102+
git config user.email "${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com"
103+
git switch -c "$branch"
104+
git commit -m "Sync Value Map from Intelligence Flow @ $short"
85105
git push --force --set-upstream origin "$branch"
86-
pr_url="$(gh pr create \
87-
--base main \
88-
--head "$branch" \
89-
--title "Sync Value Map from Intelligence Flow $short" \
90-
--body "Generated from operatorstack/intelligence-flow@$SOURCE_COMMIT. Review the operator contract, provenance, and compatibility checks before merging.")"
106+
if [[ -z "$existing" ]]; then
107+
pr_url="$(gh pr create --base main --head "$branch" \
108+
--title "Sync Value Map from Intelligence Flow @ $short" \
109+
--body-file "$body_file")"
110+
echo "Opened generated PR: $pr_url"
111+
else
112+
pr_url="$existing"
113+
echo "Updated existing PR: $existing"
114+
fi
115+
required_checks="$(gh api "repos/operatorstack/value-map/branches/main/protection/required_status_checks" --jq '(.contexts // []) + ([.checks[]?.context] // []) | unique | length')"
116+
if [[ "$required_checks" -lt 1 ]]; then
117+
echo "BLOCKED: main has no required status checks; refusing unsafe auto-merge." >&2
118+
exit 1
119+
fi
91120
gh pr merge "$pr_url" --auto --squash
121+
echo "Native auto-merge requested; branch protection owns merge eligibility."

tests/test_distribution.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,12 @@ def test_sync_accepts_exact_publisher_dispatch_with_polling_fallback(self) -> No
101101
self.assertIn("source_commit:", workflow)
102102
self.assertIn("Operator Stack Publisher", workflow)
103103
self.assertIn("inputs.source_commit || 'main'", workflow)
104-
self.assertIn('cron: "23 */6 * * *"', workflow)
104+
self.assertIn('cron: "9 */6 * * *"', workflow)
105105
self.assertIn("actions/create-github-app-token@v3", workflow)
106-
self.assertIn("steps.publisher-token.outputs.token", workflow)
106+
self.assertIn("steps.app-token.outputs.token", workflow)
107+
self.assertIn("permission-contents: write", workflow)
108+
self.assertIn('gh pr merge "$pr_url" --auto --squash', workflow)
109+
self.assertIn("required_status_checks", workflow)
107110
self.assertNotIn("GH_TOKEN: ${{ github.token }}", workflow)
108111

109112

0 commit comments

Comments
 (0)