Preview Deployment #32283
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Preview Deployment | |
# Secure deployment of pull request artifacts | |
# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ | |
# Changes to this file do not take effect until they are merged into the main / default branch. | |
on: | |
workflow_run: | |
workflows: ["Continuous Integration"] | |
types: [completed] | |
permissions: | |
deployments: write | |
packages: write | |
pull-requests: write | |
jobs: | |
preview-deployment: | |
runs-on: ubuntu-latest | |
if: > | |
github.event.workflow_run.event == 'pull_request' && | |
github.event.workflow_run.conclusion == 'success' && | |
github.event.workflow_run.pull_requests[0] != null | |
env: | |
PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number }} | |
IMAGE_REPO: ghcr.io/${{ github.repository }}/showcase-preview | |
IMAGE_TAG: rev-${{ github.event.workflow_run.head_sha }} | |
IMAGE_PR_TAG: preview-pr${{ github.event.workflow_run.pull_requests[0].number }} | |
ALLOWED_EXTENSIONS: css, gitignore, gitkeep, html, ico, jpg, js, json, png, scss, stackblitzrc, svg, ts | |
steps: | |
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 | |
- run: rm package.json yarn.lock | |
- uses: actions/setup-node@v4 | |
with: | |
node-version-file: '.nvmrc' | |
- run: npm i [email protected] | |
- name: Download Artifacts | |
id: artifacts | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const decompress = require('decompress'); | |
const fs = require('fs'); | |
const path = require('path'); | |
const artifactsResult = await github.request(context.payload.workflow_run.artifacts_url); | |
const artifact = artifactsResult.data.artifacts.find((a) => | |
a.name.startsWith('showcase-artifact-') | |
); | |
if (artifact) { | |
core.info(`Found artifact ${artifact.name}`); | |
try { | |
const outputPath = './dist/releases/showcase'; | |
const allowedExtensions = process.env.ALLOWED_EXTENSIONS.split(/[, ]+/g).map(e => `.${e}`); | |
const archive = await github.request(artifact.archive_download_url); | |
core.info(`Downloaded ${artifact.name}`); | |
const zipResult = await decompress(Buffer.from(archive.data), '.'); | |
core.info(`Decompressed ${artifact.name}:\n${zipResult.map(r => `- ${r.path}`).join('\n')}`); | |
fs.mkdirSync(outputPath, { recursive: true }); | |
const tgzResult = await decompress(zipResult[0].path, outputPath, { | |
// Only allow files that are reasonable | |
filter: (file) => allowedExtensions.includes(path.extname(file.path)), | |
map: (file) => { | |
file.path = file.path.replace(/^package\//, ''); | |
return file; | |
}, | |
}); | |
core.info(`Decompressed ${zipResult[0].path}:\n${tgzResult.map(r => `- ${r.path}`).join('\n')}`); | |
} catch (e) { | |
core.error(`Failed to download and unzip ${artifact.name}`); | |
core.error(e); | |
} | |
} else { | |
core.error(`No artifact found`); | |
} | |
- run: ls -llR ./dist/releases | |
- name: Create GitHub Deployment | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const environment = process.env.PR_NUMBER ? process.env.IMAGE_PR_TAG : 'preview-main'; | |
const payload = { owner: context.repo.owner, repo: context.repo.repo, environment }; | |
const { data: deployment } = await github.rest.repos.createDeployment({ | |
...payload, | |
ref: context.payload.workflow_run.head_sha, | |
auto_merge: false, | |
required_contexts: ['build', 'packages', 'showcase'] | |
}); | |
await github.rest.repos.createDeploymentStatus({ | |
...payload, | |
deployment_id: deployment.id, | |
state: 'in_progress', | |
environment_url: `https://${context.repo.repo}-${environment}.app.sbb.ch`, | |
}); | |
- uses: ./.github/actions/setup-mint | |
- name: Create and publish container image | |
run: | | |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin | |
docker build --tag $IMAGE_REPO:$IMAGE_TAG-fat . | |
mint slim \ | |
--target $IMAGE_REPO:$IMAGE_TAG-fat \ | |
--tag $IMAGE_REPO:$IMAGE_TAG \ | |
--preserve-path /usr/share/nginx/html | |
docker push $IMAGE_REPO:$IMAGE_TAG | |
docker tag $IMAGE_REPO:$IMAGE_TAG $IMAGE_REPO:$IMAGE_PR_TAG | |
docker push $IMAGE_REPO:$IMAGE_PR_TAG | |
docker image list | |
env: | |
DOCKER_BUILDKIT: 1 | |
- name: "Add 'preview-available' label" | |
# This label is used for filtering deployments in ArgoCD | |
run: gh issue edit $PR_NUMBER --add-label "preview-available" | |
env: | |
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |