From ac69fd12fac9cfe9ec798b300a327595ea38d12a Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Thu, 16 Jul 2026 14:50:26 +0200 Subject: [PATCH 1/3] ci: weekly sync of new capability IDs from canonical spec --- .github/scripts/sync_compliance_matrix.py | 61 ++++++++++++++++++++ .github/workflows/sync-compliance.yml | 70 +++++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 .github/scripts/sync_compliance_matrix.py create mode 100644 .github/workflows/sync-compliance.yml diff --git a/.github/scripts/sync_compliance_matrix.py b/.github/scripts/sync_compliance_matrix.py new file mode 100644 index 000000000..f03b9716f --- /dev/null +++ b/.github/scripts/sync_compliance_matrix.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +"""Append capability IDs from the canonical spec that are missing locally. + +Reads the canonical capability files checked out under `_sdk-spec/capabilities/` +and appends any IDs not already present in `sdk-compliance.yaml` as +`not_implemented`, leaving them for a human to triage. The list of added IDs is +also written to `new_ids.txt` for the workflow to use in the pull request body. +""" +import re +import sys +from pathlib import Path + +REPOSITORY_ROOT = Path(__file__).resolve().parents[2] +COMPLIANCE_FILE = REPOSITORY_ROOT / "sdk-compliance.yaml" +CAPABILITIES_DIRECTORY = REPOSITORY_ROOT / "_sdk-spec" / "capabilities" + +FEATURE_KEY = re.compile(r"^ ([a-z0-9_]+(?:\.[a-z0-9_]+)+):") +CANONICAL_ID = re.compile(r"^\s*-\s*id:\s*([a-z0-9_]+(?:\.[a-z0-9_]+)+)") + + +def read_canonical_ids(): + identifiers = set() + for path in sorted(CAPABILITIES_DIRECTORY.glob("*.yaml")): + for line in path.read_text().splitlines(): + found = CANONICAL_ID.match(line) + if found: + identifiers.add(found.group(1)) + return identifiers + + +def read_existing_ids(lines): + identifiers = set() + for line in lines: + found = FEATURE_KEY.match(line) + if found: + identifiers.add(found.group(1)) + return identifiers + + +def main(): + if not CAPABILITIES_DIRECTORY.is_dir(): + sys.exit(f"Canonical capabilities not found at {CAPABILITIES_DIRECTORY}") + text = COMPLIANCE_FILE.read_text() + new_identifiers = sorted(read_canonical_ids() - read_existing_ids(text.splitlines())) + Path("new_ids.txt").write_text("\n".join(new_identifiers)) + if not new_identifiers: + print("No new capability IDs.") + return + if not text.endswith("\n"): + text += "\n" + block = [ + "", + " # Newly synced from the canonical spec; triage status and symbols before merge.", + ] + block += [f" {identifier}: not_implemented" for identifier in new_identifiers] + COMPLIANCE_FILE.write_text(text + "\n".join(block) + "\n") + print(f"Added {len(new_identifiers)} new capability IDs.") + + +if __name__ == "__main__": + main() diff --git a/.github/workflows/sync-compliance.yml b/.github/workflows/sync-compliance.yml new file mode 100644 index 000000000..a42601c10 --- /dev/null +++ b/.github/workflows/sync-compliance.yml @@ -0,0 +1,70 @@ +name: Sync SDK Compliance Matrix + +on: + schedule: + - cron: '0 6 * * 1' + workflow_dispatch: + +permissions: + contents: read + +jobs: + sync: + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: write + pull-requests: write + steps: + - name: Generate token + id: app-token + uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.PRIVATE_KEY }} + + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + token: ${{ steps.app-token.outputs.token }} + + - name: Checkout capability spec + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: supabase/sdk + path: _sdk-spec + + - name: Append new capability IDs + run: python3 .github/scripts/sync_compliance_matrix.py + + - name: Create pull request + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + if git diff --quiet -- sdk-compliance.yaml; then + echo "No new capability IDs; nothing to do." + exit 0 + fi + + branch="chore/sync-compliance-matrix" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git checkout -b "$branch" + git add sdk-compliance.yaml + git commit -m "chore: sync new capability IDs from canonical spec" + git push --force-with-lease origin "$branch" + + body="$(printf 'New capability IDs from https://github.com/supabase/sdk were added as `not_implemented`.\n\nTriage each one: set the real status, add symbols/notes, and move it into the right section before merging.\n\n')" + while IFS= read -r feature_id; do + [ -n "$feature_id" ] && body="${body}- \`${feature_id}\`"$'\n' + done < new_ids.txt + + if [ -n "$(gh pr list --head "$branch" --state open --json number --jq '.[0].number')" ]; then + echo "Pull request already open for $branch; pushed the update." + else + gh pr create \ + --base main \ + --head "$branch" \ + --title "chore: sync new capability IDs from canonical spec" \ + --body "$body" + fi From bd0d866c835b2a28f61acebaf6790f449df08f6b Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Thu, 16 Jul 2026 14:53:24 +0200 Subject: [PATCH 2/3] ci: place synced capability IDs next to their group siblings --- .github/scripts/sync_compliance_matrix.py | 76 ++++++++++++++++------- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/.github/scripts/sync_compliance_matrix.py b/.github/scripts/sync_compliance_matrix.py index f03b9716f..16575880c 100644 --- a/.github/scripts/sync_compliance_matrix.py +++ b/.github/scripts/sync_compliance_matrix.py @@ -1,10 +1,13 @@ #!/usr/bin/env python3 -"""Append capability IDs from the canonical spec that are missing locally. +"""Insert capability IDs from the canonical spec that are missing locally. Reads the canonical capability files checked out under `_sdk-spec/capabilities/` -and appends any IDs not already present in `sdk-compliance.yaml` as -`not_implemented`, leaving them for a human to triage. The list of added IDs is -also written to `new_ids.txt` for the workflow to use in the pull request body. +and inserts any IDs not already present in `sdk-compliance.yaml` as +`not_implemented`, next to their existing siblings (same `area.group.` prefix) +so they land in the right section. IDs belonging to a group with no local +sibling yet are appended at the end under a comment for manual placement. The +list of added IDs is written to `new_ids.txt` for the workflow's pull request +body. """ import re import sys @@ -28,33 +31,64 @@ def read_canonical_ids(): return identifiers +def feature_key_at(lines, index): + found = FEATURE_KEY.match(lines[index]) + return found.group(1) if found else None + + def read_existing_ids(lines): - identifiers = set() - for line in lines: - found = FEATURE_KEY.match(line) - if found: - identifiers.add(found.group(1)) - return identifiers + return {key for index in range(len(lines)) if (key := feature_key_at(lines, index))} + + +def block_end(lines, start): + end = start + 1 + while end < len(lines) and lines[end].startswith(" "): + end += 1 + return end + + +def insertion_index(lines, prefix): + last = None + for index in range(len(lines)): + key = feature_key_at(lines, index) + if key and key.startswith(prefix): + last = index + if last is None: + return None + return block_end(lines, last) def main(): if not CAPABILITIES_DIRECTORY.is_dir(): sys.exit(f"Canonical capabilities not found at {CAPABILITIES_DIRECTORY}") - text = COMPLIANCE_FILE.read_text() - new_identifiers = sorted(read_canonical_ids() - read_existing_ids(text.splitlines())) + lines = COMPLIANCE_FILE.read_text().splitlines() + new_identifiers = sorted(read_canonical_ids() - read_existing_ids(lines)) Path("new_ids.txt").write_text("\n".join(new_identifiers)) if not new_identifiers: print("No new capability IDs.") return - if not text.endswith("\n"): - text += "\n" - block = [ - "", - " # Newly synced from the canonical spec; triage status and symbols before merge.", - ] - block += [f" {identifier}: not_implemented" for identifier in new_identifiers] - COMPLIANCE_FILE.write_text(text + "\n".join(block) + "\n") - print(f"Added {len(new_identifiers)} new capability IDs.") + + orphans = [] + for identifier in new_identifiers: + prefix = identifier.rsplit(".", 1)[0] + "." + index = insertion_index(lines, prefix) + if index is None: + orphans.append(identifier) + continue + lines.insert(index, f" {identifier}: not_implemented") + + if orphans: + lines.append("") + lines.append( + " # Newly synced from the canonical spec; no local group yet, place manually." + ) + lines += [f" {identifier}: not_implemented" for identifier in orphans] + + COMPLIANCE_FILE.write_text("\n".join(lines) + "\n") + print( + f"Added {len(new_identifiers)} new capability IDs " + f"({len(orphans)} without an existing group)." + ) if __name__ == "__main__": From 1ccd322841068d78b907543785660cb4743a8e0c Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Thu, 16 Jul 2026 15:00:56 +0200 Subject: [PATCH 3/3] ci: call supabase/sdk reusable compliance sync workflow --- .github/scripts/sync_compliance_matrix.py | 95 ----------------------- .github/workflows/sync-compliance.yml | 62 +-------------- 2 files changed, 4 insertions(+), 153 deletions(-) delete mode 100644 .github/scripts/sync_compliance_matrix.py diff --git a/.github/scripts/sync_compliance_matrix.py b/.github/scripts/sync_compliance_matrix.py deleted file mode 100644 index 16575880c..000000000 --- a/.github/scripts/sync_compliance_matrix.py +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env python3 -"""Insert capability IDs from the canonical spec that are missing locally. - -Reads the canonical capability files checked out under `_sdk-spec/capabilities/` -and inserts any IDs not already present in `sdk-compliance.yaml` as -`not_implemented`, next to their existing siblings (same `area.group.` prefix) -so they land in the right section. IDs belonging to a group with no local -sibling yet are appended at the end under a comment for manual placement. The -list of added IDs is written to `new_ids.txt` for the workflow's pull request -body. -""" -import re -import sys -from pathlib import Path - -REPOSITORY_ROOT = Path(__file__).resolve().parents[2] -COMPLIANCE_FILE = REPOSITORY_ROOT / "sdk-compliance.yaml" -CAPABILITIES_DIRECTORY = REPOSITORY_ROOT / "_sdk-spec" / "capabilities" - -FEATURE_KEY = re.compile(r"^ ([a-z0-9_]+(?:\.[a-z0-9_]+)+):") -CANONICAL_ID = re.compile(r"^\s*-\s*id:\s*([a-z0-9_]+(?:\.[a-z0-9_]+)+)") - - -def read_canonical_ids(): - identifiers = set() - for path in sorted(CAPABILITIES_DIRECTORY.glob("*.yaml")): - for line in path.read_text().splitlines(): - found = CANONICAL_ID.match(line) - if found: - identifiers.add(found.group(1)) - return identifiers - - -def feature_key_at(lines, index): - found = FEATURE_KEY.match(lines[index]) - return found.group(1) if found else None - - -def read_existing_ids(lines): - return {key for index in range(len(lines)) if (key := feature_key_at(lines, index))} - - -def block_end(lines, start): - end = start + 1 - while end < len(lines) and lines[end].startswith(" "): - end += 1 - return end - - -def insertion_index(lines, prefix): - last = None - for index in range(len(lines)): - key = feature_key_at(lines, index) - if key and key.startswith(prefix): - last = index - if last is None: - return None - return block_end(lines, last) - - -def main(): - if not CAPABILITIES_DIRECTORY.is_dir(): - sys.exit(f"Canonical capabilities not found at {CAPABILITIES_DIRECTORY}") - lines = COMPLIANCE_FILE.read_text().splitlines() - new_identifiers = sorted(read_canonical_ids() - read_existing_ids(lines)) - Path("new_ids.txt").write_text("\n".join(new_identifiers)) - if not new_identifiers: - print("No new capability IDs.") - return - - orphans = [] - for identifier in new_identifiers: - prefix = identifier.rsplit(".", 1)[0] + "." - index = insertion_index(lines, prefix) - if index is None: - orphans.append(identifier) - continue - lines.insert(index, f" {identifier}: not_implemented") - - if orphans: - lines.append("") - lines.append( - " # Newly synced from the canonical spec; no local group yet, place manually." - ) - lines += [f" {identifier}: not_implemented" for identifier in orphans] - - COMPLIANCE_FILE.write_text("\n".join(lines) + "\n") - print( - f"Added {len(new_identifiers)} new capability IDs " - f"({len(orphans)} without an existing group)." - ) - - -if __name__ == "__main__": - main() diff --git a/.github/workflows/sync-compliance.yml b/.github/workflows/sync-compliance.yml index a42601c10..9baf5b4fd 100644 --- a/.github/workflows/sync-compliance.yml +++ b/.github/workflows/sync-compliance.yml @@ -5,66 +5,12 @@ on: - cron: '0 6 * * 1' workflow_dispatch: -permissions: - contents: read - jobs: sync: - runs-on: ubuntu-latest - timeout-minutes: 10 permissions: contents: write pull-requests: write - steps: - - name: Generate token - id: app-token - uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0 - with: - app-id: ${{ secrets.APP_ID }} - private-key: ${{ secrets.PRIVATE_KEY }} - - - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - token: ${{ steps.app-token.outputs.token }} - - - name: Checkout capability spec - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - repository: supabase/sdk - path: _sdk-spec - - - name: Append new capability IDs - run: python3 .github/scripts/sync_compliance_matrix.py - - - name: Create pull request - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} - run: | - if git diff --quiet -- sdk-compliance.yaml; then - echo "No new capability IDs; nothing to do." - exit 0 - fi - - branch="chore/sync-compliance-matrix" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git checkout -b "$branch" - git add sdk-compliance.yaml - git commit -m "chore: sync new capability IDs from canonical spec" - git push --force-with-lease origin "$branch" - - body="$(printf 'New capability IDs from https://github.com/supabase/sdk were added as `not_implemented`.\n\nTriage each one: set the real status, add symbols/notes, and move it into the right section before merging.\n\n')" - while IFS= read -r feature_id; do - [ -n "$feature_id" ] && body="${body}- \`${feature_id}\`"$'\n' - done < new_ids.txt - - if [ -n "$(gh pr list --head "$branch" --state open --json number --jq '.[0].number')" ]; then - echo "Pull request already open for $branch; pushed the update." - else - gh pr create \ - --base main \ - --head "$branch" \ - --title "chore: sync new capability IDs from canonical spec" \ - --body "$body" - fi + uses: supabase/sdk/.github/workflows/sync-sdk-compliance.yml@main + secrets: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.PRIVATE_KEY }}