Skip to content

Commit 01a5c83

Browse files
franky47Copilot
andauthored
chore: Securing workflows (#1105)
Co-authored-by: Copilot <[email protected]>
1 parent 6b61cfd commit 01a5c83

File tree

8 files changed

+151
-131
lines changed

8 files changed

+151
-131
lines changed

.github/workflows/analyse-nextjs-release.yml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,25 @@ on:
1111

1212
env:
1313
FORCE_COLOR: 3 # Diplay chalk colors
14+
VERSION: ${{ inputs.version }}
1415

1516
jobs:
1617
analyse-release:
1718
runs-on: ubuntu-24.04-arm
1819
name: Check for relevant Next.js core changes
1920
steps:
21+
- name: Ensure input follows SemVer
22+
# Source: https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
23+
run: |
24+
node -e "
25+
const version = process.env.VERSION;
26+
const semverRegex = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
27+
if (!semverRegex.test(version)) {
28+
console.error(\`Error: Version '\${version}' does not follow SemVer format\`);
29+
process.exit(1);
30+
}
31+
console.log(\`Version '\${version}' is valid SemVer\`);
32+
"
2033
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
2134
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda
2235
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
@@ -26,7 +39,7 @@ jobs:
2639
- name: Install dependencies
2740
run: pnpm install
2841
- name: Check for changes in app router
29-
run: ./next-release-analyser.mjs --version ${{ inputs.version }}
42+
run: ./next-release-analyser.mjs --version "${{ env.VERSION }}"
3043
working-directory: packages/scripts
3144
env:
3245
MAILPACE_API_TOKEN: ${{ secrets.MAILPACE_API_TOKEN }}

.github/workflows/milestone-automation.yml

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@ on:
66
branches:
77
- next
88

9+
env:
10+
BACKLOG_MILESTONE_ID: 3 # ID for "Backlog" milestone
11+
SHIPPING_NEXT_MILESTONE_ID: 2 # ID for "Shipping Next" milestone
12+
913
permissions:
1014
pull-requests: write
15+
issues: write
1116

1217
jobs:
1318
update_milestones:
@@ -20,7 +25,7 @@ jobs:
2025
id: check_milestone
2126
run: |
2227
milestone_id=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }} --jq '.milestone.number')
23-
if [[ "$milestone_id" == "3" ]]; then
28+
if [[ "$milestone_id" == "${{ env.BACKLOG_MILESTONE_ID }}" ]]; then
2429
echo "backlog_milestone=true" >> $GITHUB_OUTPUT
2530
else
2631
echo "backlog_milestone=false" >> $GITHUB_OUTPUT
@@ -33,6 +38,94 @@ jobs:
3338
run: |
3439
gh api -X PATCH \
3540
repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }} \
36-
-f milestone=2
41+
-f milestone=${{ env.SHIPPING_NEXT_MILESTONE_ID }}
42+
env:
43+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
44+
45+
- name: Update linked issues milestone to "shipping next"
46+
if: steps.check_milestone.outputs.backlog_milestone == 'true'
47+
run: |
48+
set -euo pipefail # Exit on any error
49+
50+
# Validate PR number is numeric
51+
if ! [[ "${{ github.event.pull_request.number }}" =~ ^[0-9]+$ ]]; then
52+
echo "Invalid PR number: ${{ github.event.pull_request.number }}"
53+
exit 1
54+
fi
55+
56+
# Get linked issues using GraphQL API
57+
query='query LinkedIssues($owner: String!, $repo: String!, $prNumber: Int!) {
58+
repository(owner: $owner, name: $repo) {
59+
pullRequest(number: $prNumber) {
60+
closingIssuesReferences(first: 20) {
61+
nodes {
62+
number
63+
milestone {
64+
number
65+
}
66+
}
67+
}
68+
}
69+
}
70+
}'
71+
72+
# Extract owner and repo from repository
73+
owner=$(echo "${{ github.repository }}" | cut -d'/' -f1)
74+
repo=$(echo "${{ github.repository }}" | cut -d'/' -f2)
75+
76+
# Execute GraphQL query with error handling
77+
echo "Fetching linked issues for PR #${{ github.event.pull_request.number }}"
78+
79+
if ! response=$(gh api graphql \
80+
-f query="$query" \
81+
-f owner="$owner" \
82+
-f repo="$repo" \
83+
-F prNumber="${{ github.event.pull_request.number }}" 2>&1); then
84+
echo "Failed to fetch linked issues: $response"
85+
exit 1
86+
fi
87+
88+
# Extract issue numbers, keep only those with milestone "Backlog" (number 3)
89+
linked_issues=$(echo "$response" | jq -r '
90+
.data.repository.pullRequest.closingIssuesReferences.nodes[]
91+
| select(.milestone != null and .milestone.number == ${{ env.BACKLOG_MILESTONE_ID }})
92+
| .number
93+
')
94+
95+
# Check if there are any issues to update
96+
if [[ -z "$linked_issues" ]]; then
97+
echo "No linked issues found or all issues already have the correct milestone"
98+
exit 0
99+
fi
100+
101+
echo "Found linked issues to update: $(echo "$linked_issues" | tr '\n' ' ')"
102+
103+
# Update milestone for each linked issue
104+
failed_updates=0
105+
updated_count=0
106+
107+
while IFS= read -r issue_number; do
108+
[[ -z "$issue_number" ]] && continue
109+
110+
echo "Updating milestone for issue #$issue_number"
111+
if gh api -X PATCH \
112+
"repos/${{ github.repository }}/issues/$issue_number" \
113+
-f milestone=${{ env.SHIPPING_NEXT_MILESTONE_ID }} \
114+
--silent; then
115+
echo "✓ Successfully updated milestone for issue #$issue_number"
116+
updated_count=$((updated_count + 1))
117+
else
118+
echo "✗ Failed to update milestone for issue #$issue_number"
119+
failed_updates=$((failed_updates + 1))
120+
fi
121+
done <<< "$linked_issues"
122+
123+
echo "Summary: Updated $updated_count issue(s), $failed_updates failure(s)"
124+
125+
# Fail the step if any updates failed
126+
if [[ $failed_updates -gt 0 ]]; then
127+
echo "Failed to update $failed_updates issue(s)"
128+
exit 1
129+
fi
37130
env:
38131
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/pkg.pr.new.yml

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,16 @@ name: PR Preview
22
on:
33
pull_request:
44
types: [opened, synchronize, labeled]
5-
pull_request_review:
6-
types: [submitted]
5+
paths:
6+
- 'packages/nuqs/**'
77

88
jobs:
9-
check-changes:
10-
name: Check for relevant changes to deploy
9+
deploy-preview:
10+
name: Deploy to pkg.pr.new
1111
if: |
12-
github.event.review.state == 'APPROVED' ||
1312
contains(github.event.pull_request.author_association, 'MEMBER') ||
1413
contains(github.event.pull_request.labels.*.name, 'deploy:preview')
1514
runs-on: ubuntu-24.04-arm
16-
outputs:
17-
enable: ${{ steps.check-for-changes.outputs.enable }}
18-
steps:
19-
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
20-
with:
21-
fetch-depth: 2
22-
- name: Check for relevant changes
23-
id: check-for-changes
24-
run: |
25-
if git diff --quiet HEAD^ HEAD ./packages/nuqs; then
26-
echo "No changes to nuqs package, skipping preview deployment."
27-
echo "enable=false" >> $GITHUB_OUTPUT
28-
else
29-
echo "enable=true" >> $GITHUB_OUTPUT
30-
fi
31-
32-
deploy-preview:
33-
name: Deploy to pkg.pr.new
34-
needs: check-changes
35-
if: needs.check-changes.outputs.enable == 'true'
36-
runs-on: ubuntu-24.04-arm
3715
steps:
3816
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
3917
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda

.github/workflows/pr-base-enforcement.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
steps:
1313
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
1414
- name: Post comment to update base branch
15-
run: gh pr comment ${{ github.event.pull_request.number }} --body "⚠️ Pull requests targetting the \`master\` branch are not allowed. Please update the base branch to \`next\`."
15+
run: gh pr comment ${{ github.event.pull_request.number }} --body "⚠️ Pull requests targetting the \`master\` branch are not allowed. Please update the base branch to \`next\` before reopening."
1616
env:
1717
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
1818
- name: Close PR

.github/workflows/pr-lint.yml

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,23 @@ jobs:
1818
node-version-file: .node-version
1919
cache: pnpm
2020
- name: Install dependencies
21-
run: pnpm install --ignore-scripts --frozen-lockfile --workspace-root
21+
run: pnpm add -D @commitlint/load @commitlint/lint
2222
- name: Lint PR title
23-
run: echo "${{ github.event.pull_request.title }}" | ./node_modules/.bin/commitlint > $GITHUB_STEP_SUMMARY
23+
env:
24+
TITLE: ${{ github.event.pull_request.title }}
25+
run: |
26+
node -e "
27+
import loadConfig from '@commitlint/load';
28+
import lint from '@commitlint/lint';
29+
import pkgJson from './package.json' with { type: 'json' };
30+
const config = await loadConfig(pkgJson.commitlint);
31+
const result = await lint(process.env.TITLE, config.rules, config.parserPreset ? { parserOpts: config.parserPreset.parserOpts } : {});
32+
process.env.GITHUB_STEP_SUMMARY += \`## Linting Result\n- Valid: \${result.valid}\n\`;
33+
if (result.valid === false) {
34+
for (const { message } of result.errors) {
35+
process.env.GITHUB_STEP_SUMMARY += \`- Error: \${message}\n\`;
36+
console.error(message);
37+
}
38+
process.exit(1);
39+
}
40+
"

.github/workflows/ship-it.yml

Lines changed: 0 additions & 41 deletions
This file was deleted.

.github/workflows/snapshot-release.yml

Lines changed: 0 additions & 53 deletions
This file was deleted.

.github/workflows/test-against-nextjs-release.yml

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ on:
1111

1212
env:
1313
FORCE_COLOR: 3 # Diplay chalk colors
14+
VERSION: ${{ inputs.version }}
1415

1516
jobs:
1617
test_against_nextjs_release:
@@ -22,6 +23,18 @@ jobs:
2223
base-path: [false, '/base']
2324
react-compiler: [true, false]
2425
steps:
26+
- name: Ensure input follows SemVer
27+
# Source: https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
28+
run: |
29+
node -e "
30+
const version = process.env.VERSION;
31+
const semverRegex = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
32+
if (!semverRegex.test(version)) {
33+
console.error(\`Error: Version '\${version}' does not follow SemVer format\`);
34+
process.exit(1);
35+
}
36+
console.log(\`Version '\${version}' is valid SemVer\`);
37+
"
2538
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
2639
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda
2740
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
@@ -30,8 +43,8 @@ jobs:
3043
cache: pnpm
3144
- name: Install dependencies
3245
run: pnpm install
33-
- name: Install Next.js version ${{ inputs.version }}
34-
run: pnpm add --filter e2e-next --filter nuqs next@${{ inputs.version }}
46+
- name: Install Next.js version ${{ env.VERSION }}
47+
run: pnpm add --filter e2e-next --filter nuqs "next@${{ env.VERSION }}"
3548
- name: Run integration tests
3649
run: pnpm run test --filter e2e-next
3750
env:
@@ -45,13 +58,13 @@ jobs:
4558
if: failure()
4659
with:
4760
path: packages/e2e-next/cypress/screenshots
48-
name: ci-next-${{ inputs.version }}${{ matrix.base-path && '-basePath' || ''}}${{ matrix.react-compiler && '-react-compiler' || ''}}
61+
name: ci-next-${{ env.VERSION }}${{ matrix.base-path && '-basePath' || ''}}${{ matrix.react-compiler && '-react-compiler' || ''}}
4962
- uses: 47ng/actions-slack-notify@main
5063
name: Notify on Slack
5164
if: failure()
5265
with:
5366
status: ${{ job.status }}
54-
jobName: next@${{ inputs.version }}${{ matrix.base-path && ' ⚾' || ''}}${{ matrix.react-compiler && ' ⚛️' || ''}}
67+
jobName: next@${{ env.VERSION }}${{ matrix.base-path && ' ⚾' || ''}}${{ matrix.react-compiler && ' ⚛️' || ''}}
5568
env:
5669
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
5770

@@ -73,6 +86,6 @@ jobs:
7386
- uses: 47ng/actions-slack-notify@main
7487
with:
7588
status: ${{ job.status }}
76-
jobName: ${{ inputs.version }}
89+
jobName: '${{ env.VERSION }}'
7790
env:
7891
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

0 commit comments

Comments
 (0)