mirror of https://github.com/sveltejs/svelte
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
220 lines
7.3 KiB
220 lines
7.3 KiB
name: pkg.pr.new
|
|
on:
|
|
pull_request_target:
|
|
types: [opened, synchronize]
|
|
push:
|
|
branches: [main]
|
|
|
|
permissions: {}
|
|
|
|
jobs:
|
|
# This job determines the environment to use for the build job. It ensures that:
|
|
# - For pushes to main, we use the "Publish pkg.pr.new (maintainers)" environment.
|
|
# - For PRs from the same repository, we also use the "Publish pkg.pr.new (maintainers)" environment, since these are trusted.
|
|
# - For PRs from forks, we use the "Publish pkg.pr.new (external contributors)" environment, which requires manual approval by a maintainer before the build job can run.
|
|
# This protects us from running untrusted code while still allowing external contributors to use pkg.pr.new.
|
|
resolve-env:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
environment: ${{ steps.resolve.outputs.environment }}
|
|
steps:
|
|
- name: Determine environment
|
|
id: resolve
|
|
run: |
|
|
if [[ "${{ github.event_name }}" == "push" ]]; then
|
|
echo "environment=Publish pkg.pr.new (maintainers)" >> "$GITHUB_OUTPUT"
|
|
elif [[ "${{ github.event.pull_request.head.repo.full_name }}" == "${{ github.repository }}" ]]; then
|
|
echo "environment=Publish pkg.pr.new (maintainers)" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "environment=Publish pkg.pr.new (external contributors)" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
build:
|
|
needs: resolve-env
|
|
runs-on: ubuntu-latest
|
|
# This is the line that ensures forks require manual approval before running the build job
|
|
environment: ${{ needs.resolve-env.outputs.environment }}
|
|
|
|
# No permissions — this job runs user-controlled code
|
|
permissions: {}
|
|
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
# For pull_request_target, we must explicitly check out the PR head.
|
|
# This is safe because the environment gate above has already fired —
|
|
# an org member has approved this specific commit for external PRs.
|
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: 22.x
|
|
cache: pnpm
|
|
|
|
- name: Install dependencies
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
- name: Build
|
|
run: pnpm build
|
|
|
|
- run: pnpx pkg-pr-new publish --comment=off --json output.json --compact --no-template './packages/svelte'
|
|
|
|
- name: Upload output
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: output
|
|
path: ./output.json
|
|
|
|
# Sanitizes the untrusted output from the build job before it's consumed by
|
|
# jobs with elevated permissions. This ensures that only known package names
|
|
# and valid SHA prefixes make it through.
|
|
sanitize:
|
|
needs: build
|
|
runs-on: ubuntu-latest
|
|
|
|
permissions: {}
|
|
|
|
steps:
|
|
- name: Download artifact
|
|
uses: actions/download-artifact@v7
|
|
with:
|
|
name: output
|
|
|
|
- name: Sanitize output
|
|
uses: actions/github-script@v8
|
|
with:
|
|
script: |
|
|
const fs = require('fs');
|
|
const raw = JSON.parse(fs.readFileSync('output.json', 'utf8'));
|
|
|
|
const ALLOWED_PACKAGES = new Set(['svelte']);
|
|
const SHA_PATTERN = /^[0-9a-f]{7}$/;
|
|
|
|
const packages = (raw.packages || [])
|
|
.filter(p => {
|
|
if (!ALLOWED_PACKAGES.has(p.name)) {
|
|
console.log(`Skipping unexpected package: ${JSON.stringify(p.name)}`);
|
|
return false;
|
|
}
|
|
const sha = p.url?.replace(/^.+@([^@]+)$/, '$1');
|
|
if (!sha || !SHA_PATTERN.test(sha)) {
|
|
console.log(`Skipping package with invalid SHA: ${JSON.stringify(p.url)}`);
|
|
return false;
|
|
}
|
|
return true;
|
|
})
|
|
.map(p => ({
|
|
name: p.name,
|
|
sha: p.url.replace(/^.+@([^@]+)$/, '$1'),
|
|
}));
|
|
|
|
fs.writeFileSync('sanitized-output.json', JSON.stringify({ packages }), 'utf8');
|
|
|
|
- name: Upload sanitized output
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: sanitized-output
|
|
path: ./sanitized-output.json
|
|
|
|
comment:
|
|
needs: sanitize
|
|
if: github.event_name == 'pull_request_target'
|
|
runs-on: ubuntu-latest
|
|
|
|
permissions:
|
|
pull-requests: write
|
|
|
|
steps:
|
|
- name: Download sanitized artifact
|
|
uses: actions/download-artifact@v7
|
|
with:
|
|
name: sanitized-output
|
|
|
|
- name: Post or update comment
|
|
uses: actions/github-script@v8
|
|
with:
|
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
script: |
|
|
const fs = require('fs');
|
|
const { packages } = JSON.parse(fs.readFileSync('sanitized-output.json', 'utf8'));
|
|
|
|
if (packages.length === 0) {
|
|
console.log('No valid packages found. Skipping comment.');
|
|
return;
|
|
}
|
|
|
|
// Issue number from trusted event context, never from the artifact
|
|
const issue_number = context.issue.number;
|
|
|
|
const bot_comment_identifier = `<!-- pkg.pr.new comment -->`;
|
|
|
|
const body = `${bot_comment_identifier}
|
|
|
|
[Playground](https://svelte.dev/playground?version=pr-${issue_number})
|
|
|
|
\`\`\`
|
|
${packages.map(p => `pnpm add https://pkg.pr.new/${p.name}@${issue_number}`).join('\n')}
|
|
\`\`\`
|
|
`;
|
|
|
|
const comments = await github.rest.issues.listComments({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number,
|
|
});
|
|
const existing = comments.data.find(c => c.body.includes(bot_comment_identifier));
|
|
|
|
if (existing) {
|
|
await github.rest.issues.updateComment({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
comment_id: existing.id,
|
|
body,
|
|
});
|
|
} else {
|
|
await github.rest.issues.createComment({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number,
|
|
body,
|
|
});
|
|
}
|
|
|
|
log:
|
|
needs: sanitize
|
|
if: github.event_name == 'push'
|
|
runs-on: ubuntu-latest
|
|
|
|
permissions: {}
|
|
|
|
steps:
|
|
- name: Download sanitized artifact
|
|
uses: actions/download-artifact@v7
|
|
with:
|
|
name: sanitized-output
|
|
|
|
- name: Log publish info
|
|
uses: actions/github-script@v8
|
|
with:
|
|
script: |
|
|
const fs = require('fs');
|
|
const { packages } = JSON.parse(fs.readFileSync('sanitized-output.json', 'utf8'));
|
|
|
|
if (packages.length === 0) {
|
|
console.log('No valid packages found.');
|
|
return;
|
|
}
|
|
|
|
console.log('\n' + '='.repeat(50));
|
|
console.log('Publish Information');
|
|
console.log('='.repeat(50));
|
|
for (const p of packages) {
|
|
console.log(`${p.name} - pnpm add https://pkg.pr.new/${p.name}@${p.sha}`);
|
|
}
|
|
const svelte = packages.find(p => p.name === 'svelte');
|
|
if (svelte) {
|
|
console.log(`\nPlayground: https://svelte.dev/playground?version=commit-${svelte.sha}`);
|
|
}
|
|
console.log('='.repeat(50));
|