diff --git a/.github/workflows/sync-upstream.yml b/.github/workflows/sync-upstream.yml index 0155431..064285c 100644 --- a/.github/workflows/sync-upstream.yml +++ b/.github/workflows/sync-upstream.yml @@ -1,9 +1,11 @@ -# Generated from operatorstack/intelligence-flow. +# Generated by labkit (python -m labkit gen). Do not edit by hand. +# Edit labs//publish.config.json and regenerate; drift fails `labkit doctor`. +# Install into operatorstack/boatstack at .github/workflows/sync-upstream.yml (bootstrap step). name: Sync from Intelligence Flow on: schedule: - - cron: "17 */6 * * *" + - cron: "2 */6 * * *" workflow_dispatch: inputs: source_commit: @@ -23,7 +25,7 @@ jobs: sync: runs-on: ubuntu-latest steps: - - name: Create repository automation token + - name: Create publisher token id: app-token uses: actions/create-github-app-token@v3 with: @@ -38,7 +40,7 @@ jobs: - name: Check out Boatstack uses: actions/checkout@v4 with: - path: boatstack-repo + path: public-repo token: ${{ steps.app-token.outputs.token }} - name: Check out Intelligence Flow uses: actions/checkout@v4 @@ -48,20 +50,28 @@ jobs: fetch-depth: 0 path: intelligence-flow token: ${{ steps.app-token.outputs.token }} + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install labkit + shell: bash + run: python3 -m pip install --quiet ./intelligence-flow/labkit - name: Generate projection id: generate shell: bash run: | source_commit="$(git -C intelligence-flow log -1 --format=%H -- labs/12-product-engineering-loop)" - current_commit="$(jq -r '.source.commit // empty' boatstack-repo/UPSTREAM.json)" + current_commit="$(jq -r '.source.commit // empty' public-repo/UPSTREAM.json 2>/dev/null || echo '')" if [[ -n "$current_commit" ]] && ! git -C intelligence-flow merge-base --is-ancestor "$current_commit" "$source_commit"; then - echo "Ignoring stale projection request $source_commit; Boatstack already records $current_commit." + echo "Ignoring stale request; Boatstack already records $current_commit." echo "stale=true" >> "$GITHUB_OUTPUT" exit 0 fi - python3 intelligence-flow/labs/12-product-engineering-loop/scripts/build_boatstack.py \ - --repo boatstack-repo \ + python3 -m labkit project \ + --config intelligence-flow/labs/12-product-engineering-loop/publish.config.json \ + --repo public-repo \ --source-commit "$source_commit" \ --write echo "source_commit=$source_commit" >> "$GITHUB_OUTPUT" @@ -73,62 +83,49 @@ jobs: SOURCE_COMMIT: ${{ steps.generate.outputs.source_commit }} shell: bash run: | - cd boatstack-repo + cd public-repo if [[ -z "$(git status --porcelain)" ]]; then echo "Boatstack already matches Intelligence Flow." exit 0 fi git add -A - rewritten_notes=() - while IFS= read -r note; do - rewritten_notes+=("$note") - done < <(git diff --cached --name-only --diff-filter=MD --no-renames -- \ - 'release-notes/*.md') - if (( ${#rewritten_notes[@]} > 0 )); then + rewritten=() + while IFS= read -r note; do rewritten+=("$note"); done < <( + git diff --cached --name-only --diff-filter=MD --no-renames -- 'release-notes/*.md') + if (( ${#rewritten[@]} > 0 )); then echo "BLOCKED: Boatstack release notes are append-only:" >&2 - printf ' %s\n' "${rewritten_notes[@]}" >&2 + printf ' %s\n' "${rewritten[@]}" >&2 exit 1 fi - added_notes=() - while IFS= read -r note; do - added_notes+=("$note") - done < <(git diff --cached --name-only --diff-filter=A --no-renames -- \ - 'release-notes/*.md' | LC_ALL=C sort) - if (( ${#added_notes[@]} == 0 )); then - echo "BLOCKED: projected Boatstack changes require a release message." >&2 + added=() + while IFS= read -r note; do added+=("$note"); done < <( + git diff --cached --name-only --diff-filter=A --no-renames -- 'release-notes/*.md' | LC_ALL=C sort) + if (( ${#added[@]} == 0 )); then + echo "BLOCKED: projected changes require a release note in release-notes/." >&2 exit 1 fi body_file="$(mktemp)" { - echo "## What this sync releases" - echo - for note in "${added_notes[@]}"; do - cat "$note" - echo - done - echo "
" - echo "Projection provenance" - echo + echo "## What this sync releases"; echo + for note in "${added[@]}"; do cat "$note"; echo; done + echo "
Projection provenance"; echo echo "Generated from \`operatorstack/intelligence-flow@$SOURCE_COMMIT\`." - echo "Review provenance, tests, examples, and context-cost changes before merging." - echo + echo "Review provenance, tests, and examples before merging."; echo echo "
" } > "$body_file" short="${SOURCE_COMMIT:0:12}" branch="sync/intelligence-flow-$short" existing="$(gh pr list --head "$branch" --state open --json url --jq '.[0].url')" - if [[ -n "$existing" ]]; then - echo "Upstream PR already open: $existing" - exit 0 - fi git config user.name "${{ steps.app-token.outputs.app-slug }}[bot]" git config user.email "${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com" git switch -c "$branch" git commit -m "Sync Boatstack from Intelligence Flow Labs @ $short" - git push --set-upstream origin "$branch" - pr_url="$(gh pr create \ - --base main \ - --head "$branch" \ - --title "Sync Boatstack from Intelligence Flow Labs @ $short" \ - --body-file "$body_file")" - echo "Opened generated PR: $pr_url" + git push --force --set-upstream origin "$branch" + if [[ -z "$existing" ]]; then + pr_url="$(gh pr create --base main --head "$branch" \ + --title "Sync Boatstack from Intelligence Flow Labs @ $short" \ + --body-file "$body_file")" + echo "Opened generated PR: $pr_url" + else + echo "Updated existing PR: $existing" + fi