Skip to content

Commit b6df2d1

Browse files
authored
chore(ci): pipeline updates and improvements (#1771)
1 parent bced454 commit b6df2d1

File tree

7 files changed

+190
-39
lines changed

7 files changed

+190
-39
lines changed

.github/workflows/block-merge.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Block WIP/Draft Merges
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened, labeled, unlabeled, ready_for_review]
6+
7+
permissions:
8+
pull-requests: write
9+
checks: write
10+
11+
jobs:
12+
block-merge:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: Check if PR should be blocked
16+
uses: actions/github-script@v7
17+
with:
18+
script: |
19+
const pr = context.payload.pull_request;
20+
const title = pr.title || '';
21+
const isDraft = pr.draft;
22+
const labels = pr.labels.map(l => l.name);
23+
24+
const hasDoNotMerge = labels.includes('do-not-merge');
25+
const hasWIPInTitle = /\b(wip|do not merge)\b/i.test(title);
26+
27+
const shouldBlock = isDraft || hasDoNotMerge || hasWIPInTitle;
28+
29+
if (shouldBlock) {
30+
core.setFailed('This PR is blocked from merging: ' +
31+
(isDraft ? 'Draft PR' : '') +
32+
(hasDoNotMerge ? ' do-not-merge label' : '') +
33+
(hasWIPInTitle ? ' WIP in title' : ''));
34+
}

.github/workflows/label-issues.yml

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1-
name: Label Issues
1+
name: Label Issues and PRs
22

33
on:
44
issues:
55
types:
66
- opened
77
- transferred
8+
pull_request:
9+
types:
10+
- opened
811

912
permissions:
1013
issues: write
14+
pull-requests: write
1115

1216
jobs:
1317
label-issue:
@@ -17,58 +21,75 @@ jobs:
1721
uses: actions/github-script@v7
1822
with:
1923
script: |
20-
// Define the mapping from repo/template value to label
2124
const labelMap = {
2225
'supabase/auth-js': 'auth-js',
2326
'supabase/functions-js': 'functions-js',
2427
'supabase/postgrest-js': 'postgrest-js',
2528
'supabase/storage-js': 'storage-js',
2629
'supabase/realtime-js': 'realtime-js',
30+
'supabase/supabase-js': 'supabase-js',
2731
'auth-js': 'auth-js',
2832
'functions-js': 'functions-js',
2933
'postgrest-js': 'postgrest-js',
3034
'storage-js': 'storage-js',
3135
'realtime-js': 'realtime-js',
36+
'supabase-js': 'supabase-js',
37+
};
38+
39+
const scopeToLabel = {
40+
'auth': 'auth-js',
41+
'functions': 'functions-js',
42+
'postgrest': 'postgrest-js',
43+
'storage': 'storage-js',
44+
'realtime': 'realtime-js',
45+
'supabase': 'supabase-js',
3246
};
3347
3448
let labels = [];
49+
const isPR = !!context.payload.pull_request;
50+
const item = context.payload.pull_request || context.payload.issue;
51+
const itemNumber = item.number;
3552
36-
const oldRepoFullName = context.payload.changes?.old_repository?.full_name;
37-
if (oldRepoFullName) {
38-
const fullNameLower = (oldRepoFullName || '').toLowerCase();
39-
const shortName = fullNameLower.split('/')?.[1];
40-
console.log('old_repository', fullNameLower, shortName);
41-
const transferLabel = labelMap[fullNameLower] || labelMap[shortName];
42-
if (transferLabel) labels.push(transferLabel);
43-
} else {
44-
// Label based on "Library affected" field in the issue body
45-
const body = context.payload.issue.body || '';
46-
const match = body.match(/### Library affected\s*\n+([\s\S]*?)(\n###|\n$)/i);
47-
if (match) {
48-
const libsRaw = match[1];
49-
// Split by comma, semicolon, or newlines
50-
const libs = libsRaw.split(/,|;|\n/).map(s => s.trim().toLowerCase()).filter(Boolean);
51-
for (const lib of libs) {
52-
if (labelMap[lib]) labels.push(labelMap[lib]);
53+
if (isPR) {
54+
const title = item.title || '';
55+
const scopeMatch = title.match(/^[a-z]+\(([a-z]+)\):/);
56+
if (scopeMatch) {
57+
const scope = scopeMatch[1];
58+
if (scopeToLabel[scope]) {
59+
labels.push(scopeToLabel[scope]);
5360
}
5461
}
55-
// Check the title for "[migration]"
56-
const title = context.payload.issue.title || '';
57-
if (title.toLowerCase().includes('[migration]')) {
58-
labels.push('migration');
62+
} else {
63+
const oldRepoFullName = context.payload.changes?.old_repository?.full_name;
64+
if (oldRepoFullName) {
65+
const fullNameLower = (oldRepoFullName || '').toLowerCase();
66+
const shortName = fullNameLower.split('/')?.[1];
67+
const transferLabel = labelMap[fullNameLower] || labelMap[shortName];
68+
if (transferLabel) labels.push(transferLabel);
69+
} else {
70+
const body = item.body || '';
71+
const match = body.match(/### Library affected\s*\n+([\s\S]*?)(\n###|\n$)/i);
72+
if (match) {
73+
const libsRaw = match[1];
74+
const libs = libsRaw.split(/,|;|\n/).map(s => s.trim().toLowerCase()).filter(Boolean);
75+
for (const lib of libs) {
76+
if (labelMap[lib]) labels.push(labelMap[lib]);
77+
}
78+
}
79+
const title = item.title || '';
80+
if (title.toLowerCase().includes('[migration]')) {
81+
labels.push('migration');
82+
}
5983
}
6084
}
6185
62-
// Remove duplicates
6386
labels = [...new Set(labels)];
6487
6588
if (labels.length > 0) {
6689
await github.rest.issues.addLabels({
6790
owner: context.repo.owner,
6891
repo: context.repo.repo,
69-
issue_number: context.payload.issue.number,
92+
issue_number: itemNumber,
7093
labels,
7194
});
72-
} else {
73-
console.log('No matching label found; no label added.');
7495
}

.github/workflows/publish.yml

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ jobs:
1919
release-stable: # stable releases can only be manually triggered
2020
if: ${{ github.event_name == 'workflow_dispatch' }}
2121
runs-on: ubuntu-latest
22+
outputs:
23+
released_version: ${{ steps.extract-version.outputs.version }}
2224
permissions:
2325
contents: read
2426
id-token: write
@@ -99,14 +101,25 @@ jobs:
99101
exit 1
100102
fi
101103
102-
- name: Release & create PR
104+
- name: Release stable version
103105
env:
104106
NPM_CONFIG_PROVENANCE: true
105107
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
106108
RELEASE_GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
107109
GH_TOKEN: ${{ steps.app-token.outputs.token }}
110+
shell: bash
111+
run: npm run release-stable -- --versionSpecifier "${{ github.event.inputs.version_specifier }}"
112+
113+
- name: Extract released version
114+
id: extract-version
115+
shell: bash
108116
run: |
109-
npm run release-stable -- --versionSpecifier "${{ github.event.inputs.version_specifier }}"
117+
set -euo pipefail
118+
VERSION=$(cat .release-version)
119+
if [[ -z "$VERSION" ]]; then
120+
exit 1
121+
fi
122+
echo "version=$VERSION" >> $GITHUB_OUTPUT
110123
111124
- name: Summary
112125
if: ${{ success() }}
@@ -150,7 +163,7 @@ jobs:
150163
workflow_id: 'update-js-libs.yml',
151164
ref: 'master',
152165
inputs: {
153-
version: '${{ github.event.inputs.version_specifier }}',
166+
version: '${{ needs.release-stable.outputs.released_version }}',
154167
source: 'supabase-js-stable-release'
155168
}
156169
});
@@ -181,7 +194,7 @@ jobs:
181194
workflow_id: 'docs-js-libs-update.yml',
182195
ref: 'master',
183196
inputs: {
184-
version: '${{ github.event.inputs.version_specifier }}',
197+
version: '${{ needs.release-stable.outputs.released_version }}',
185198
source: 'supabase-js-stable-release'
186199
}
187200
});
@@ -276,7 +289,19 @@ jobs:
276289
uses: ./.github/workflows/slack-notify.yml
277290
secrets: inherit
278291
with:
279-
subject: 'Stable Release'
292+
title: 'Stable Release'
293+
status: 'failure'
294+
295+
notify-stable-success:
296+
name: Notify Slack for Stable success
297+
needs: release-stable
298+
if: ${{ github.event_name == 'workflow_dispatch' && needs.release-stable.result == 'success' }}
299+
uses: ./.github/workflows/slack-notify.yml
300+
secrets: inherit
301+
with:
302+
title: 'Stable Release'
303+
status: 'success'
304+
version: ${{ needs.release-stable.outputs.released_version }}
280305

281306
notify-canary-failure:
282307
name: Notify Slack for Canary failure
@@ -285,4 +310,5 @@ jobs:
285310
uses: ./.github/workflows/slack-notify.yml
286311
secrets: inherit
287312
with:
288-
subject: 'Canary Release'
313+
title: 'Canary Release'
314+
status: 'failure'

.github/workflows/slack-notify.yml

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,69 @@ name: Reusable Slack Notification
33
on:
44
workflow_call:
55
inputs:
6-
subject:
7-
description: 'Short subject describing what failed (e.g., Canary Release)'
6+
title:
7+
description: 'Short title (e.g., Stable Release, Canary Release)'
88
required: true
99
type: string
10+
status:
11+
description: 'Status for the notification (success|failure|info)'
12+
required: false
13+
default: 'info'
14+
type: string
15+
version:
16+
description: 'Version string to display (e.g., v1.2.3 or 1.2.3-canary.1)'
17+
required: false
18+
type: string
1019

1120
jobs:
1221
notify:
1322
runs-on: ubuntu-latest
1423
steps:
1524
- name: Send Slack notification
1625
run: |
26+
TITLE="${{ inputs.title }}"
27+
STATUS="${{ inputs.status }}"
28+
VERSION_INPUT="${{ inputs.version }}"
29+
30+
STATUS_ICON=""
31+
STATUS_PREFIX=""
32+
case "$STATUS" in
33+
success)
34+
STATUS_ICON="✅";
35+
STATUS_PREFIX="succeeded";
36+
;;
37+
failure)
38+
STATUS_ICON="❌";
39+
STATUS_PREFIX="failed";
40+
;;
41+
*)
42+
STATUS_ICON="ℹ️";
43+
STATUS_PREFIX="update";
44+
;;
45+
esac
46+
VERSION_VALUE=${VERSION_INPUT:-n/a}
47+
VERSION_LINE=", version ${VERSION_VALUE}"
48+
1749
payload=$(cat <<EOF
1850
{
19-
"text": " ${{ inputs.subject }} failed in ${{ github.repository }}",
51+
"text": "${STATUS_ICON} ${TITLE} ${STATUS_PREFIX}${VERSION_LINE} in ${{ github.repository }}",
2052
"blocks": [
2153
{
2254
"type": "header",
23-
"text": { "type": "plain_text", "text": "❌ ${{ inputs.subject }} failed", "emoji": true }
55+
"text": { "type": "plain_text", "text": "${STATUS_ICON} ${TITLE} ${STATUS_PREFIX}", "emoji": true }
2456
},
2557
{
2658
"type": "section",
27-
"text": { "type": "mrkdwn", "text": "The workflow run failed. See details below. @group-client-libs" }
59+
"text": { "type": "mrkdwn", "text": "See workflow details below. @group-client-libs" }
2860
},
2961
{
3062
"type": "section",
3163
"fields": [
3264
{ "type": "mrkdwn", "text": "*Repository*\n${{ github.repository }}" },
3365
{ "type": "mrkdwn", "text": "*Workflow*\n${{ github.workflow }}" },
3466
{ "type": "mrkdwn", "text": "*Ref*\n${{ github.ref_name }}" },
35-
{ "type": "mrkdwn", "text": "*Actor*\n${{ github.actor }}" }
67+
{ "type": "mrkdwn", "text": "*Actor*\n${{ github.actor }}" },
68+
{ "type": "mrkdwn", "text": "*Version*\n${VERSION_VALUE}" }
3669
]
3770
},
3871
{

.github/workflows/stale.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Mark as Stale and Close Stale Issues
2+
3+
on:
4+
schedule:
5+
- cron: '0 0 * * 0'
6+
workflow_dispatch:
7+
8+
permissions:
9+
issues: write
10+
pull-requests: write
11+
12+
jobs:
13+
stale:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/stale@v9
17+
with:
18+
days-before-stale: 365
19+
days-before-close: 180
20+
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.'
21+
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.'
22+
stale-issue-label: 'stale'
23+
exempt-issue-labels: 'security,needs-discussion,pinned'
24+
exempt-all-assignees: true
25+
remove-stale-when-updated: true
26+
operations-per-run: 100

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ testem.log
3535
/typings
3636
.env
3737

38+
# release artifacts
39+
.release-version
40+
3841
# System Files
3942
.DS_Store
4043
Thumbs.db

scripts/release-stable.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { releaseVersion, releaseChangelog, releasePublish } from 'nx/release'
22
import { execSync } from 'child_process'
3+
import { writeFile } from 'fs/promises'
34

45
function getLastStableTag(): string {
56
try {
@@ -163,6 +164,13 @@ function safeExec(cmd: string, opts = {}) {
163164

164165
const version = result.workspaceChangelog?.releaseVersion.rawVersion || workspaceVersion
165166

167+
// Write version to file for CI to read
168+
try {
169+
await writeFile('.release-version', version ?? '', 'utf-8')
170+
} catch (error) {
171+
console.error('❌ Failed to write release version to file', error)
172+
}
173+
166174
// Validate version to prevent command injection
167175
if (
168176
!version ||

0 commit comments

Comments
 (0)