From 39e7d03c4c2aa4b938dceb702f2e8a34bed5a325 Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Tue, 14 Oct 2025 15:09:09 +0300 Subject: [PATCH 1/6] chore(ci): slack notify for successful stable and generic slack notif --- .github/workflows/publish.yml | 34 +++++++++++++++++++--- .github/workflows/slack-notify.yml | 45 ++++++++++++++++++++++++++---- .gitignore | 3 ++ scripts/release-stable.ts | 8 ++++++ 4 files changed, 80 insertions(+), 10 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index bab02ffb7..e5623877c 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -19,6 +19,8 @@ jobs: release-stable: # stable releases can only be manually triggered if: ${{ github.event_name == 'workflow_dispatch' }} runs-on: ubuntu-latest + outputs: + released_version: ${{ steps.extract-version.outputs.version }} permissions: contents: read id-token: write @@ -99,14 +101,25 @@ jobs: exit 1 fi - - name: Release & create PR + - name: Release stable version env: NPM_CONFIG_PROVENANCE: true GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} RELEASE_GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} GH_TOKEN: ${{ steps.app-token.outputs.token }} + shell: bash + run: npm run release-stable -- --versionSpecifier "${{ github.event.inputs.version_specifier }}" + + - name: Extract released version + id: extract-version + shell: bash run: | - npm run release-stable -- --versionSpecifier "${{ github.event.inputs.version_specifier }}" + set -euo pipefail + VERSION=$(cat .release-version) + if [[ -z "$VERSION" ]]; then + exit 1 + fi + echo "version=$VERSION" >> $GITHUB_OUTPUT - name: Summary if: ${{ success() }} @@ -276,7 +289,19 @@ jobs: uses: ./.github/workflows/slack-notify.yml secrets: inherit with: - subject: 'Stable Release' + title: 'Stable Release' + status: 'failure' + + notify-stable-success: + name: Notify Slack for Stable success + needs: release-stable + if: ${{ github.event_name == 'workflow_dispatch' && needs.release-stable.result == 'success' }} + uses: ./.github/workflows/slack-notify.yml + secrets: inherit + with: + title: 'Stable Release' + status: 'success' + version: ${{ needs.release-stable.outputs.released_version }} notify-canary-failure: name: Notify Slack for Canary failure @@ -285,4 +310,5 @@ jobs: uses: ./.github/workflows/slack-notify.yml secrets: inherit with: - subject: 'Canary Release' + title: 'Canary Release' + status: 'failure' diff --git a/.github/workflows/slack-notify.yml b/.github/workflows/slack-notify.yml index d228ace82..8c7419c38 100644 --- a/.github/workflows/slack-notify.yml +++ b/.github/workflows/slack-notify.yml @@ -3,10 +3,19 @@ name: Reusable Slack Notification on: workflow_call: inputs: - subject: - description: 'Short subject describing what failed (e.g., Canary Release)' + title: + description: 'Short title (e.g., Stable Release, Canary Release)' required: true type: string + status: + description: 'Status for the notification (success|failure|info)' + required: false + default: 'info' + type: string + version: + description: 'Version string to display (e.g., v1.2.3 or 1.2.3-canary.1)' + required: false + type: string jobs: notify: @@ -14,17 +23,40 @@ jobs: steps: - name: Send Slack notification run: | + TITLE="${{ inputs.title }}" + STATUS="${{ inputs.status }}" + VERSION_INPUT="${{ inputs.version }}" + + STATUS_ICON="" + STATUS_PREFIX="" + case "$STATUS" in + success) + STATUS_ICON="✅"; + STATUS_PREFIX="succeeded"; + ;; + failure) + STATUS_ICON="❌"; + STATUS_PREFIX="failed"; + ;; + *) + STATUS_ICON="ℹ️"; + STATUS_PREFIX="update"; + ;; + esac + VERSION_VALUE=${VERSION_INPUT:-n/a} + VERSION_LINE=", version ${VERSION_VALUE}" + payload=$(cat < Date: Tue, 14 Oct 2025 15:11:00 +0300 Subject: [PATCH 2/6] chore(ci): pass correct version to supabase docs and libs update --- .github/workflows/publish.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e5623877c..f96b8d2c8 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -163,7 +163,7 @@ jobs: workflow_id: 'update-js-libs.yml', ref: 'master', inputs: { - version: '${{ github.event.inputs.version_specifier }}', + version: '${{ needs.release-stable.outputs.released_version }}', source: 'supabase-js-stable-release' } }); @@ -194,7 +194,7 @@ jobs: workflow_id: 'docs-js-libs-update.yml', ref: 'master', inputs: { - version: '${{ github.event.inputs.version_specifier }}', + version: '${{ needs.release-stable.outputs.released_version }}', source: 'supabase-js-stable-release' } }); From 054a15c8568896d654a228909261e98f4a2594ea Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Tue, 14 Oct 2025 15:19:15 +0300 Subject: [PATCH 3/6] chore(ci): add supabase-js to issue labeler --- .github/workflows/label-issues.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/label-issues.yml b/.github/workflows/label-issues.yml index e1f082fa1..2bc49f408 100644 --- a/.github/workflows/label-issues.yml +++ b/.github/workflows/label-issues.yml @@ -24,11 +24,13 @@ jobs: 'supabase/postgrest-js': 'postgrest-js', 'supabase/storage-js': 'storage-js', 'supabase/realtime-js': 'realtime-js', + 'supabase/supabase-js': 'supabase-js', 'auth-js': 'auth-js', 'functions-js': 'functions-js', 'postgrest-js': 'postgrest-js', 'storage-js': 'storage-js', 'realtime-js': 'realtime-js', + 'supabase-js': 'supabase-js', }; let labels = []; From f8d91d5b827dac8b28e05cecf26926ddbfd51866 Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Tue, 14 Oct 2025 15:23:16 +0300 Subject: [PATCH 4/6] chore(ci): label PRs as well --- .github/workflows/label-issues.yml | 73 +++++++++++++++++++----------- 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/.github/workflows/label-issues.yml b/.github/workflows/label-issues.yml index 2bc49f408..e4bcac237 100644 --- a/.github/workflows/label-issues.yml +++ b/.github/workflows/label-issues.yml @@ -1,13 +1,17 @@ -name: Label Issues +name: Label Issues and PRs on: issues: types: - opened - transferred + pull_request: + types: + - opened permissions: issues: write + pull-requests: write jobs: label-issue: @@ -17,7 +21,6 @@ jobs: uses: actions/github-script@v7 with: script: | - // Define the mapping from repo/template value to label const labelMap = { 'supabase/auth-js': 'auth-js', 'supabase/functions-js': 'functions-js', @@ -33,44 +36,60 @@ jobs: 'supabase-js': 'supabase-js', }; + const scopeToLabel = { + 'auth': 'auth-js', + 'functions': 'functions-js', + 'postgrest': 'postgrest-js', + 'storage': 'storage-js', + 'realtime': 'realtime-js', + 'supabase': 'supabase-js', + }; + let labels = []; + const isPR = !!context.payload.pull_request; + const item = context.payload.pull_request || context.payload.issue; + const itemNumber = item.number; - const oldRepoFullName = context.payload.changes?.old_repository?.full_name; - if (oldRepoFullName) { - const fullNameLower = (oldRepoFullName || '').toLowerCase(); - const shortName = fullNameLower.split('/')?.[1]; - console.log('old_repository', fullNameLower, shortName); - const transferLabel = labelMap[fullNameLower] || labelMap[shortName]; - if (transferLabel) labels.push(transferLabel); - } else { - // Label based on "Library affected" field in the issue body - const body = context.payload.issue.body || ''; - const match = body.match(/### Library affected\s*\n+([\s\S]*?)(\n###|\n$)/i); - if (match) { - const libsRaw = match[1]; - // Split by comma, semicolon, or newlines - const libs = libsRaw.split(/,|;|\n/).map(s => s.trim().toLowerCase()).filter(Boolean); - for (const lib of libs) { - if (labelMap[lib]) labels.push(labelMap[lib]); + if (isPR) { + const title = item.title || ''; + const scopeMatch = title.match(/^[a-z]+\(([a-z]+)\):/); + if (scopeMatch) { + const scope = scopeMatch[1]; + if (scopeToLabel[scope]) { + labels.push(scopeToLabel[scope]); } } - // Check the title for "[migration]" - const title = context.payload.issue.title || ''; - if (title.toLowerCase().includes('[migration]')) { - labels.push('migration'); + } else { + const oldRepoFullName = context.payload.changes?.old_repository?.full_name; + if (oldRepoFullName) { + const fullNameLower = (oldRepoFullName || '').toLowerCase(); + const shortName = fullNameLower.split('/')?.[1]; + const transferLabel = labelMap[fullNameLower] || labelMap[shortName]; + if (transferLabel) labels.push(transferLabel); + } else { + const body = item.body || ''; + const match = body.match(/### Library affected\s*\n+([\s\S]*?)(\n###|\n$)/i); + if (match) { + const libsRaw = match[1]; + const libs = libsRaw.split(/,|;|\n/).map(s => s.trim().toLowerCase()).filter(Boolean); + for (const lib of libs) { + if (labelMap[lib]) labels.push(labelMap[lib]); + } + } + const title = item.title || ''; + if (title.toLowerCase().includes('[migration]')) { + labels.push('migration'); + } } } - // Remove duplicates labels = [...new Set(labels)]; if (labels.length > 0) { await github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.repo, - issue_number: context.payload.issue.number, + issue_number: itemNumber, labels, }); - } else { - console.log('No matching label found; no label added.'); } From fa6b4779e5d69397c3c26f2dd0b8d358df017fbb Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Tue, 14 Oct 2025 15:46:59 +0300 Subject: [PATCH 5/6] chore(ci): stale issues marker --- .github/workflows/stale.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/stale.yml diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 000000000..0c676d352 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,26 @@ +name: Mark as Stale and Close Stale Issues + +on: + schedule: + - cron: '0 0 * * 0' + workflow_dispatch: + +permissions: + issues: write + pull-requests: write + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v9 + with: + days-before-stale: 365 + days-before-close: 180 + stale-issue-message: 'This issue has been automatically marked as stale because it has not had any activity for 1 year. It will be closed in 6 months if no further activity occurs. If this issue is still relevant, please comment to keep it open.' + close-issue-message: 'This issue has been automatically closed due to inactivity. If you believe this is still relevant, please reopen or create a new issue.' + stale-issue-label: 'stale' + exempt-issue-labels: 'security,needs-discussion,pinned' + exempt-all-assignees: true + remove-stale-when-updated: true + operations-per-run: 100 From 98bd2bb05f8e0d973f1ce470ebfb4b1fde724a77 Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Tue, 14 Oct 2025 15:50:49 +0300 Subject: [PATCH 6/6] chore(ci): block merging for wip and do-not-merge --- .github/workflows/block-merge.yml | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/block-merge.yml diff --git a/.github/workflows/block-merge.yml b/.github/workflows/block-merge.yml new file mode 100644 index 000000000..00f2c4d57 --- /dev/null +++ b/.github/workflows/block-merge.yml @@ -0,0 +1,34 @@ +name: Block WIP/Draft Merges + +on: + pull_request: + types: [opened, synchronize, reopened, labeled, unlabeled, ready_for_review] + +permissions: + pull-requests: write + checks: write + +jobs: + block-merge: + runs-on: ubuntu-latest + steps: + - name: Check if PR should be blocked + uses: actions/github-script@v7 + with: + script: | + const pr = context.payload.pull_request; + const title = pr.title || ''; + const isDraft = pr.draft; + const labels = pr.labels.map(l => l.name); + + const hasDoNotMerge = labels.includes('do-not-merge'); + const hasWIPInTitle = /\b(wip|do not merge)\b/i.test(title); + + const shouldBlock = isDraft || hasDoNotMerge || hasWIPInTitle; + + if (shouldBlock) { + core.setFailed('This PR is blocked from merging: ' + + (isDraft ? 'Draft PR' : '') + + (hasDoNotMerge ? ' do-not-merge label' : '') + + (hasWIPInTitle ? ' WIP in title' : '')); + }