Sync from Intelligence Flow #35
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Generated from operatorstack/intelligence-flow. | |
| name: Sync from Intelligence Flow | |
| on: | |
| schedule: | |
| - cron: "17 */6 * * *" | |
| workflow_dispatch: | |
| inputs: | |
| source_commit: | |
| description: Exact Intelligence Flow commit to project (defaults to main) | |
| required: false | |
| type: string | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| concurrency: | |
| group: sync-intelligence-flow | |
| cancel-in-progress: false | |
| jobs: | |
| sync: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Create repository automation token | |
| id: app-token | |
| uses: actions/create-github-app-token@v3 | |
| with: | |
| client-id: ${{ vars.BOATSTACK_APP_CLIENT_ID }} | |
| private-key: ${{ secrets.BOATSTACK_APP_PRIVATE_KEY }} | |
| owner: operatorstack | |
| repositories: | | |
| intelligence-flow | |
| boatstack | |
| permission-contents: write | |
| permission-pull-requests: write | |
| - name: Check out Boatstack | |
| uses: actions/checkout@v4 | |
| with: | |
| path: boatstack-repo | |
| token: ${{ steps.app-token.outputs.token }} | |
| - name: Check out Intelligence Flow | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: operatorstack/intelligence-flow | |
| ref: ${{ inputs.source_commit || 'main' }} | |
| fetch-depth: 0 | |
| path: intelligence-flow | |
| token: ${{ steps.app-token.outputs.token }} | |
| - name: Generate projection | |
| id: generate | |
| shell: bash | |
| run: | | |
| source_commit="$(git -C intelligence-flow log -1 --format=%H -- examples/12-product-engineering-loop)" | |
| current_commit="$(jq -r '.source.commit // empty' boatstack-repo/UPSTREAM.json)" | |
| 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 "stale=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| python3 intelligence-flow/examples/12-product-engineering-loop/scripts/build_boatstack.py \ | |
| --repo boatstack-repo \ | |
| --source-commit "$source_commit" \ | |
| --write | |
| echo "source_commit=$source_commit" >> "$GITHUB_OUTPUT" | |
| echo "stale=false" >> "$GITHUB_OUTPUT" | |
| - name: Open generated pull request | |
| if: steps.generate.outputs.stale != 'true' | |
| env: | |
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | |
| SOURCE_COMMIT: ${{ steps.generate.outputs.source_commit }} | |
| shell: bash | |
| run: | | |
| cd boatstack-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 | |
| echo "BLOCKED: Boatstack release notes are append-only:" >&2 | |
| printf ' %s\n' "${rewritten_notes[@]}" >&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 | |
| exit 1 | |
| fi | |
| body_file="$(mktemp)" | |
| { | |
| echo "## What this sync releases" | |
| echo | |
| for note in "${added_notes[@]}"; do | |
| cat "$note" | |
| echo | |
| done | |
| echo "<details>" | |
| echo "<summary>Projection provenance</summary>" | |
| echo | |
| echo "Generated from \`operatorstack/intelligence-flow@$SOURCE_COMMIT\`." | |
| echo "Review provenance, tests, examples, and context-cost changes before merging." | |
| echo | |
| echo "</details>" | |
| } > "$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 $short" | |
| git push --set-upstream origin "$branch" | |
| pr_url="$(gh pr create \ | |
| --base main \ | |
| --head "$branch" \ | |
| --title "Sync Boatstack from Intelligence Flow $short" \ | |
| --body-file "$body_file")" | |
| echo "Opened generated PR: $pr_url" |