🐛 Fix script injection in GitHub Actions workflows

Values coming from `${{ }}` expressions were interpolated directly into
`run:` scripts, so GitHub substituted them into the shell source before
bash parsed it. A commit title containing a double quote broke the
"Write step summary" step of the bundle build with a syntax error, and
the same pattern allowed arbitrary command execution on the
self-hosted runners.

Pass every expression used inside `run:` through step/job `env:` and
reference it as a quoted shell variable instead. Use the runner's
default variables (GITHUB_RUN_ID, GITHUB_REPOSITORY, ...) where the
value comes from the `github` context.

Also validate `plugin_name` in plugins-deploy-package.yml against
`^[a-z0-9][a-z0-9-]*$`, since it is free-form and reaches paths,
worker names, GITHUB_ENV and action inputs.

Affected workflows: build-bundle, build-docker,
build-docker-admin-console, plugins-deploy-package,
plugins-deploy-api-doc, plugins-deploy-styles-doc, release, tests-e2e.

Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
This commit is contained in:
David Barragán Merino 2026-09-28 11:52:25 +02:00
parent c0714def01
commit 534b1a6702
8 changed files with 128 additions and 60 deletions

View File

@ -62,8 +62,10 @@ jobs:
- name: Extract some useful variables
id: vars
env:
GH_REF: ${{ inputs.gh_ref || github.ref_name }}
run: |
echo "gh_ref=${{ inputs.gh_ref || github.ref_name }}" >> $GITHUB_OUTPUT
echo "gh_ref=$GH_REF" >> $GITHUB_OUTPUT
echo "bundle_version=$(git describe --tags --always)" >> $GITHUB_OUTPUT
echo "short_sha=$(git rev-parse --short=12 HEAD)" >> $GITHUB_OUTPUT
echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
@ -79,8 +81,12 @@ jobs:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
S3_BUCKET: ${{ secrets.S3_BUCKET }}
FORCE: ${{ inputs.force }}
SHORT_SHA: ${{ steps.vars.outputs.short_sha }}
BUNDLE_VERSION: ${{ steps.vars.outputs.bundle_version }}
run: |
if [ "${{ inputs.force }}" = "true" ]; then
if [ "$FORCE" = "true" ]; then
echo "exists=false" >> $GITHUB_OUTPUT
{
echo "### 🔁 Bundle build forced"
@ -91,8 +97,8 @@ jobs:
fi
BUNDLE_EXISTS=$(aws s3api head-object \
--bucket ${{ secrets.S3_BUCKET }} \
--key "penpot-sha-${{ steps.vars.outputs.short_sha }}.zip" \
--bucket "$S3_BUCKET" \
--key "penpot-sha-${SHORT_SHA}.zip" \
> /dev/null 2>&1 && echo "true" || echo "false")
if [ "$BUNDLE_EXISTS" = "true" ]; then
@ -100,7 +106,7 @@ jobs:
{
echo "### ⏭️ Bundle build skipped"
echo ""
echo "The bundle in S3 was already built from \`sha-${{ steps.vars.outputs.short_sha }}\` (\`${{ steps.vars.outputs.bundle_version }}\`)."
echo "The bundle in S3 was already built from \`sha-${SHORT_SHA}\` (\`${BUNDLE_VERSION}\`)."
} >> "$GITHUB_STEP_SUMMARY"
else
echo "exists=false" >> $GITHUB_OUTPUT
@ -142,18 +148,25 @@ jobs:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
S3_BUCKET: ${{ secrets.S3_BUCKET }}
SHORT_SHA: ${{ needs.check.outputs.short_sha }}
BUNDLE_VERSION: ${{ needs.check.outputs.bundle_version }}
run: |
aws s3 cp zips/penpot.zip \
s3://${{ secrets.S3_BUCKET }}/penpot-sha-${{ needs.check.outputs.short_sha }}.zip \
--metadata bundle-version=${{ needs.check.outputs.bundle_version }}
"s3://${S3_BUCKET}/penpot-sha-${SHORT_SHA}.zip" \
--metadata "bundle-version=${BUNDLE_VERSION}"
- name: Write step summary
env:
SHORT_SHA: ${{ needs.check.outputs.short_sha }}
BUNDLE_VERSION: ${{ needs.check.outputs.bundle_version }}
COMMIT_TITLE: ${{ needs.check.outputs.commit_title }}
run: |
{
echo "### ✅ Bundle built"
echo ""
echo "- Version: \`${{ needs.check.outputs.bundle_version }}\` (\`git describe --tags --always\`)"
echo "- Commit: [\`${{ needs.check.outputs.short_sha }}\`](https://github.com/${{ github.repository }}/commit/${{ needs.check.outputs.short_sha }}) — ${{ needs.check.outputs.commit_title }}"
echo "- Version: \`${BUNDLE_VERSION}\` (\`git describe --tags --always\`)"
echo "- Commit: [\`${SHORT_SHA}\`](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/commit/${SHORT_SHA}) — ${COMMIT_TITLE}"
echo "- Built at: $(date -u +'%Y-%m-%d %H:%M:%S UTC')"
} >> "$GITHUB_STEP_SUMMARY"

View File

@ -48,16 +48,17 @@ jobs:
WORKFLOW: build-docker-admin-console.yml
GH_REF: ${{ inputs.gh_ref }}
DISPATCH_REF: ${{ inputs.dispatch_ref }}
FORCE: ${{ inputs.force }}
steps:
- name: Trigger nitrate docker build
id: dispatch
run: |
DISTINCT_ID="${{ github.run_id }}-${{ github.run_attempt }}"
CALLER_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
DISTINCT_ID="${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
CALLER_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
gh workflow run "$WORKFLOW" --repo "$REPO" --ref "$DISPATCH_REF" \
-f gh_ref="$GH_REF" \
-f force="${{ inputs.force }}" \
-f force="$FORCE" \
-f caller_run_id="$DISTINCT_ID" \
-f caller_run_url="$CALLER_URL"
@ -83,20 +84,25 @@ jobs:
echo "::notice title=Nitrate docker build::$RUN_URL"
- name: Wait for nitrate docker build
env:
RUN_ID: ${{ steps.dispatch.outputs.run_id }}
run: |
gh run watch "${{ steps.dispatch.outputs.run_id }}" \
gh run watch "$RUN_ID" \
--repo "$REPO" \
--interval 30 \
--exit-status
- name: Report result
if: always() && steps.dispatch.outputs.run_id != ''
env:
RUN_ID: ${{ steps.dispatch.outputs.run_id }}
RUN_URL: ${{ steps.dispatch.outputs.run_url }}
run: |
CONCLUSION=$(gh run view "${{ steps.dispatch.outputs.run_id }}" \
CONCLUSION=$(gh run view "$RUN_ID" \
--repo "$REPO" --json conclusion --jq '.conclusion')
{
echo "### 🐳 Nitrate docker build"
echo ""
echo "- Result: \`${CONCLUSION:-in_progress}\`"
echo "- Run: ${{ steps.dispatch.outputs.run_url }}"
echo "- Run: ${RUN_URL}"
} >> "$GITHUB_STEP_SUMMARY"

View File

@ -73,8 +73,9 @@ jobs:
- name: Extract some useful variables
id: vars
env:
GH_REF: ${{ inputs.gh_ref || github.ref_name }}
run: |
GH_REF="${{ inputs.gh_ref || github.ref_name }}"
echo "gh_ref=$GH_REF" >> $GITHUB_OUTPUT
echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
echo "short_sha=$(git rev-parse --short=12 HEAD)" >> $GITHUB_OUTPUT
@ -92,13 +93,16 @@ jobs:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
S3_BUCKET: ${{ secrets.S3_BUCKET }}
FORCE: ${{ inputs.force }}
SHORT_SHA: ${{ steps.vars.outputs.short_sha }}
run: |
if [ "${{ inputs.force }}" = "true" ]; then
if [ "$FORCE" = "true" ]; then
echo "exists=false" >> $GITHUB_OUTPUT
mkdir -p "$BUNDLE_CACHE"
find "$BUNDLE_CACHE" -type f -mtime +1 -delete || true
ZIP="$BUNDLE_CACHE/penpot-sha-${{ steps.vars.outputs.short_sha }}.zip"
aws s3 cp "s3://${{ secrets.S3_BUCKET }}/penpot-sha-${{ steps.vars.outputs.short_sha }}.zip" "$ZIP.$$.tmp"
ZIP="$BUNDLE_CACHE/penpot-sha-${SHORT_SHA}.zip"
aws s3 cp "s3://${S3_BUCKET}/penpot-sha-${SHORT_SHA}.zip" "$ZIP.$$.tmp"
mv "$ZIP.$$.tmp" "$ZIP"
{
echo "### 🔁 Image set build forced"
@ -109,14 +113,14 @@ jobs:
fi
if aws s3api head-object \
--bucket ${{ secrets.S3_BUCKET }} \
--key "markers/images-sha-${{ steps.vars.outputs.short_sha }}" \
--bucket "$S3_BUCKET" \
--key "markers/images-sha-${SHORT_SHA}" \
> /dev/null 2>&1; then
echo "exists=true" >> $GITHUB_OUTPUT
{
echo "### ⏭️ Image set build skipped"
echo ""
echo "The whole set was already built and promoted for \`sha-${{ steps.vars.outputs.short_sha }}\`."
echo "The whole set was already built and promoted for \`sha-${SHORT_SHA}\`."
} >> "$GITHUB_STEP_SUMMARY"
else
echo "exists=false" >> $GITHUB_OUTPUT
@ -126,9 +130,9 @@ jobs:
# prune stale bundles while at it.
mkdir -p "$BUNDLE_CACHE"
find "$BUNDLE_CACHE" -type f -mtime +1 -delete || true
ZIP="$BUNDLE_CACHE/penpot-sha-${{ steps.vars.outputs.short_sha }}.zip"
ZIP="$BUNDLE_CACHE/penpot-sha-${SHORT_SHA}.zip"
if [ ! -f "$ZIP" ]; then
aws s3 cp "s3://${{ secrets.S3_BUCKET }}/penpot-sha-${{ steps.vars.outputs.short_sha }}.zip" "$ZIP.$$.tmp"
aws s3 cp "s3://${S3_BUCKET}/penpot-sha-${SHORT_SHA}.zip" "$ZIP.$$.tmp"
mv "$ZIP.$$.tmp" "$ZIP"
fi
fi
@ -197,18 +201,21 @@ jobs:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
S3_BUCKET: ${{ secrets.S3_BUCKET }}
SHORT_SHA: ${{ needs.prepare.outputs.short_sha }}
IMAGE: ${{ matrix.image }}
run: |
ZIP="$BUNDLE_CACHE/penpot-sha-${{ needs.prepare.outputs.short_sha }}.zip"
ZIP="$BUNDLE_CACHE/penpot-sha-${SHORT_SHA}.zip"
if [ ! -f "$ZIP" ]; then
echo "Bundle not found in host cache; falling back to S3."
mkdir -p "$BUNDLE_CACHE"
aws s3 cp "s3://${{ secrets.S3_BUCKET }}/penpot-sha-${{ needs.prepare.outputs.short_sha }}.zip" "$ZIP.$$.tmp"
aws s3 cp "s3://${S3_BUCKET}/penpot-sha-${SHORT_SHA}.zip" "$ZIP.$$.tmp"
mv "$ZIP.$$.tmp" "$ZIP"
fi
# Extract only the bundle this job needs.
pushd docker/images
unzip -q "$ZIP" "penpot/${{ matrix.image }}/*"
mv "penpot/${{ matrix.image }}" "bundle-${{ matrix.image }}"
unzip -q "$ZIP" "penpot/${IMAGE}/*"
mv "penpot/${IMAGE}" "bundle-${IMAGE}"
popd
- name: Set up QEMU (stable)
@ -266,12 +273,16 @@ jobs:
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Point branch tags to the new build key
env:
DOCKER_REGISTRY: ${{ secrets.DOCKER_REGISTRY }}
GH_REF: ${{ needs.prepare.outputs.gh_ref }}
SHORT_SHA: ${{ needs.prepare.outputs.short_sha }}
run: |
set -e
for image in $ALL_IMAGES; do
docker buildx imagetools create \
-t "${{ secrets.DOCKER_REGISTRY }}/$image:${{ needs.prepare.outputs.gh_ref }}" \
"${{ secrets.DOCKER_REGISTRY }}/$image:sha-${{ needs.prepare.outputs.short_sha }}"
-t "${DOCKER_REGISTRY}/${image}:${GH_REF}" \
"${DOCKER_REGISTRY}/${image}:sha-${SHORT_SHA}"
done
# The marker is written LAST: its presence certifies that all five
@ -281,20 +292,27 @@ jobs:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
S3_BUCKET: ${{ secrets.S3_BUCKET }}
SHORT_SHA: ${{ needs.prepare.outputs.short_sha }}
run: |
echo "${{ github.run_id }}" | aws s3 cp - \
"s3://${{ secrets.S3_BUCKET }}/markers/images-sha-${{ needs.prepare.outputs.short_sha }}"
echo "$GITHUB_RUN_ID" | aws s3 cp - \
"s3://${S3_BUCKET}/markers/images-sha-${SHORT_SHA}"
- name: Write step summary
env:
GH_REF: ${{ needs.prepare.outputs.gh_ref }}
SHORT_SHA: ${{ needs.prepare.outputs.short_sha }}
BUNDLE_VERSION: ${{ needs.prepare.outputs.bundle_version }}
COMMIT_TITLE: ${{ needs.prepare.outputs.commit_title }}
run: |
{
echo "### ✅ Image set promoted"
echo ""
echo "- Version: \`${{ needs.prepare.outputs.bundle_version }}\` (\`git describe --tags --always\`)"
echo "- Commit: [\`${{ needs.prepare.outputs.short_sha }}\`](https://github.com/${{ github.repository }}/commit/${{ needs.prepare.outputs.short_sha }}) — ${{ needs.prepare.outputs.commit_title }}"
echo "- Version: \`${BUNDLE_VERSION}\` (\`git describe --tags --always\`)"
echo "- Commit: [\`${SHORT_SHA}\`](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/commit/${SHORT_SHA}) — ${COMMIT_TITLE}"
echo "- Built at: $(date -u +'%Y-%m-%d %H:%M:%S UTC')"
echo ""
echo "All \`:${{ needs.prepare.outputs.gh_ref }}\` tags now point to \`sha-${{ needs.prepare.outputs.short_sha }}\`."
echo "All \`:${GH_REF}\` tags now point to \`sha-${SHORT_SHA}\`."
} >> "$GITHUB_STEP_SUMMARY"
# ── 3b. Skip path: make sure THIS ref's tags point to the existing
@ -324,24 +342,33 @@ jobs:
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Point branch tags to the existing build key
env:
DOCKER_REGISTRY: ${{ secrets.DOCKER_REGISTRY }}
GH_REF: ${{ needs.prepare.outputs.gh_ref }}
SHORT_SHA: ${{ needs.prepare.outputs.short_sha }}
run: |
set -e
for image in $ALL_IMAGES; do
docker buildx imagetools create \
-t "${{ secrets.DOCKER_REGISTRY }}/$image:${{ needs.prepare.outputs.gh_ref }}" \
"${{ secrets.DOCKER_REGISTRY }}/$image:sha-${{ needs.prepare.outputs.short_sha }}"
-t "${DOCKER_REGISTRY}/${image}:${GH_REF}" \
"${DOCKER_REGISTRY}/${image}:sha-${SHORT_SHA}"
done
- name: Write step summary
env:
GH_REF: ${{ needs.prepare.outputs.gh_ref }}
SHORT_SHA: ${{ needs.prepare.outputs.short_sha }}
BUNDLE_VERSION: ${{ needs.prepare.outputs.bundle_version }}
COMMIT_TITLE: ${{ needs.prepare.outputs.commit_title }}
run: |
{
echo "### ✅ Image set already built (branch tags ensured)"
echo ""
echo "- Version: \`${{ needs.prepare.outputs.bundle_version }}\` (\`git describe --tags --always\`)"
echo "- Commit: [\`${{ needs.prepare.outputs.short_sha }}\`](https://github.com/${{ github.repository }}/commit/${{ needs.prepare.outputs.short_sha }}) — ${{ needs.prepare.outputs.commit_title }}"
echo "- Version: \`${BUNDLE_VERSION}\` (\`git describe --tags --always\`)"
echo "- Commit: [\`${SHORT_SHA}\`](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/commit/${SHORT_SHA}) — ${COMMIT_TITLE}"
echo "- Checked at: $(date -u +'%Y-%m-%d %H:%M:%S UTC')"
echo ""
echo "All \`:${{ needs.prepare.outputs.gh_ref }}\` tags now point to \`sha-${{ needs.prepare.outputs.short_sha }}\`."
echo "All \`:${GH_REF}\` tags now point to \`sha-${SHORT_SHA}\`."
} >> "$GITHUB_STEP_SUMMARY"
# ── 4. Single failure notification for the whole workflow ─────────────

View File

@ -33,8 +33,10 @@ jobs:
steps:
- name: Extract some useful variables
id: vars
env:
GH_REF: ${{ inputs.gh_ref || github.ref_name }}
run: |
echo "gh_ref=${{ inputs.gh_ref || github.ref_name }}" >> $GITHUB_OUTPUT
echo "gh_ref=$GH_REF" >> $GITHUB_OUTPUT
- name: Checkout
uses: actions/checkout@v6
@ -78,8 +80,9 @@ jobs:
run: pnpm run build:doc
- name: Select Worker name
env:
REF: ${{ steps.vars.outputs.gh_ref }}
run: |
REF="${{ steps.vars.outputs.gh_ref }}"
case "$REF" in
main)
echo "WORKER_NAME=penpot-plugins-api-doc-pro" >> $GITHUB_ENV
@ -97,7 +100,7 @@ jobs:
working-directory: plugins
shell: bash
run: |
sed -i "s/WORKER_URI/${{ env.WORKER_URI }}/g" wrangler-penpot-plugins-api-doc.toml
sed -i "s/WORKER_URI/${WORKER_URI}/g" wrangler-penpot-plugins-api-doc.toml
- name: Add noindex header and robots.txt files for non-production environments
if: ${{ steps.vars.outputs.gh_ref != 'main' }}

View File

@ -35,7 +35,20 @@ permissions:
jobs:
deploy:
runs-on: penpot-standar-runner
env:
GH_REF: ${{ inputs.gh_ref }}
PLUGIN_NAME: ${{ inputs.plugin_name }}
steps:
# plugin_name is a free-form string that ends up in paths, worker names
# and GITHUB_ENV; reject anything that is not a plain slug before it is
# used anywhere.
- name: Validate inputs
run: |
if ! [[ "$PLUGIN_NAME" =~ ^[a-z0-9][a-z0-9-]*$ ]]; then
echo "::error::Invalid plugin_name: must match ^[a-z0-9][a-z0-9-]*$"
exit 1
fi
- name: Checkout
uses: actions/checkout@v6
with:
@ -75,29 +88,28 @@ jobs:
- name: "Build package for ${{ inputs.plugin_name }}-plugin"
working-directory: plugins
shell: bash
run: pnpm --filter ${{ inputs.plugin_name }}-plugin build
run: pnpm --filter "${PLUGIN_NAME}-plugin" build
- name: Select Worker name
run: |
REF="${{ inputs.gh_ref }}"
case "$REF" in
case "$GH_REF" in
main)
echo "WORKER_NAME=${{ inputs.plugin_name }}-plugin-pro" >> $GITHUB_ENV
echo "WORKER_URI=${{ inputs.plugin_name }}.plugins.penpot.app" >> $GITHUB_ENV ;;
echo "WORKER_NAME=${PLUGIN_NAME}-plugin-pro" >> $GITHUB_ENV
echo "WORKER_URI=${PLUGIN_NAME}.plugins.penpot.app" >> $GITHUB_ENV ;;
staging)
echo "WORKER_NAME=${{ inputs.plugin_name }}-plugin-pre" >> $GITHUB_ENV
echo "WORKER_URI=${{ inputs.plugin_name }}.plugins.penpot.dev" >> $GITHUB_ENV ;;
echo "WORKER_NAME=${PLUGIN_NAME}-plugin-pre" >> $GITHUB_ENV
echo "WORKER_URI=${PLUGIN_NAME}.plugins.penpot.dev" >> $GITHUB_ENV ;;
develop)
echo "WORKER_NAME=${{ inputs.plugin_name }}-plugin-hourly" >> $GITHUB_ENV
echo "WORKER_URI=${{ inputs.plugin_name }}.plugins.hourly.penpot.dev" >> $GITHUB_ENV ;;
*) echo "Unsupported branch ${REF}" && exit 1 ;;
echo "WORKER_NAME=${PLUGIN_NAME}-plugin-hourly" >> $GITHUB_ENV
echo "WORKER_URI=${PLUGIN_NAME}.plugins.hourly.penpot.dev" >> $GITHUB_ENV ;;
*) echo "Unsupported branch ${GH_REF}" && exit 1 ;;
esac
- name: Set the custom url
working-directory: plugins
shell: bash
run: |
sed -i "s/WORKER_URI/${{ env.WORKER_URI }}/g" apps/${{ inputs.plugin_name }}-plugin/wrangler.toml
sed -i "s/WORKER_URI/${WORKER_URI}/g" "apps/${PLUGIN_NAME}-plugin/wrangler.toml"
- name: Deploy to Cloudflare Workers
uses: cloudflare/wrangler-action@v3

View File

@ -31,8 +31,10 @@ jobs:
steps:
- name: Extract some useful variables
id: vars
env:
GH_REF: ${{ inputs.gh_ref || github.ref_name }}
run: |
echo "gh_ref=${{ inputs.gh_ref || github.ref_name }}" >> $GITHUB_OUTPUT
echo "gh_ref=$GH_REF" >> $GITHUB_OUTPUT
- name: Checkout
uses: actions/checkout@v6
@ -76,8 +78,9 @@ jobs:
run: pnpm run build:styles-example
- name: Select Worker name
env:
REF: ${{ steps.vars.outputs.gh_ref }}
run: |
REF="${{ steps.vars.outputs.gh_ref }}"
case "$REF" in
main)
echo "WORKER_NAME=penpot-plugins-styles-doc-pro" >> $GITHUB_ENV
@ -95,7 +98,7 @@ jobs:
working-directory: plugins
shell: bash
run: |
sed -i "s/WORKER_URI/${{ env.WORKER_URI }}/g" wrangler-penpot-plugins-styles-doc.toml
sed -i "s/WORKER_URI/${WORKER_URI}/g" wrangler-penpot-plugins-styles-doc.toml
- name: Add noindex header and robots.txt files for non-production environments
if: ${{ steps.vars.outputs.gh_ref != 'main' }}

View File

@ -26,8 +26,10 @@ jobs:
steps:
- name: Extract some useful variables
id: vars
env:
GH_REF: ${{ inputs.gh_ref || github.ref_name }}
run: |
echo "gh_ref=${{ inputs.gh_ref || github.ref_name }}" >> $GITHUB_OUTPUT
echo "gh_ref=$GH_REF" >> $GITHUB_OUTPUT
- name: Checkout code
uses: actions/checkout@v6

View File

@ -190,12 +190,14 @@ jobs:
working-directory: ./frontend
env:
WORKERS: ${{ inputs.workers }}
SHARD: ${{ matrix.shard }}
SHARD_TOTAL: ${{ strategy.job-total }}
run: |
WORKERS=${WORKERS:-2}
echo "Running shard ${{ matrix.shard }}/${{ strategy.job-total }} with $WORKERS workers"
echo "Running shard ${SHARD}/${SHARD_TOTAL} with $WORKERS workers"
pnpm exec playwright test --project default \
--workers="$WORKERS" \
--shard=${{ matrix.shard }}/${{ strategy.job-total }} \
--shard="${SHARD}/${SHARD_TOTAL}" \
--reporter=blob
- name: Upload blob report