Skip to content

Port Go's csrf protection (#15595) #16

Port Go's csrf protection (#15595)

Port Go's csrf protection (#15595) #16

Workflow file for this run

name: Release
on:
push:
branches:
- main
- next
- "1-legacy"
- "2-legacy"
- "3-legacy"
- "4-legacy"
- "5-legacy"
- "6-legacy"
defaults:
run:
shell: bash
env:
FORCE_COLOR: true
jobs:
changelog:
name: Changelog PR or Release
if: ${{ github.repository_owner == 'withastro' }}
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# The release cohort is derived from the diff against the parent commit, so the
# default depth-1 checkout (no parent) is not enough.
fetch-depth: 2
- name: Setup PNPM
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
- name: Setup Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24.15.0
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build Packages
run: pnpm run build --force
- name: Create Release Pull Request or Publish
id: changesets
uses: changesets/action@8488615a623b1b9c987934bb89eae8af6a946ac1 # v2.1.1
with:
# Note: pnpm install after versioning is necessary to refresh lockfile
version-script: pnpm run version
publish-script: pnpm exec changeset publish
commit-message: "[ci] release"
pr-title: "[ci] release"
github-token: ${{ secrets.FREDKBOT_GITHUB_TOKEN }}
# On a re-run after npm already received the versions, changesets publishes nothing new
# and its `published`/`publishedPackages` outputs are empty, so any downstream work gated
# on them would be skipped while the run turns green. The version bumps for this release
# live in the release commit itself, so the cohort is rebuilt from the commit diff on
# retries. Only the squash-merged "[ci] release" version PR commit bumps package versions;
# fixture edits, manual version changes, merges, and reverts must not look like releases.
- name: Detect release cohort
id: release
run: |
HAS_RELEASE=false
HAS_VSCODE=false
COHORT=""
if [ "${{ steps.changesets.outputs.published }}" = "true" ]; then
PAYLOAD='${{ steps.changesets.outputs.publishedPackages }}'
HAS_RELEASE=true
elif git log -1 --format=%s HEAD | grep -q '^\[ci\] release'; then
# Nothing was published, so this is a re-run (or the same commit on another branch):
# npm already has these versions, so rebuild the released-package list from the
# version bumps in the release commit.
# Feed the loop the list of package.json files changed between the parent commit
# and this one (a release commit only changes the files it bumps).
while read -r f; do
# Skip fixture/test packages: their package.json files change with tests, not releases.
case "$f" in
*"/test/"*|*"/fixtures/"*|*"/node_modules/"*) continue ;;
esac
# Version of this package at the parent commit. Empty when the file is new there;
# the `|| true` keeps a missing file or unparsable JSON from failing the step
# (the job runs with `set -e`).
V_OLD=$(git show "$(git rev-parse HEAD^):$f" 2>/dev/null | jq -r .version 2>/dev/null || true)
# Same read at this commit.
V_NEW=$(git show "HEAD:$f" | jq -r .version 2>/dev/null || true)
# `private: true` packages are never published to npm, so they are not releases.
PRIVATE=$(git show "HEAD:$f" | jq -r .private 2>/dev/null || true)
# Released means: has a version, and that version differs from the parent commit.
if [ -n "$V_NEW" ] && [ "$V_NEW" != "null" ] && [ "$V_OLD" != "$V_NEW" ] && [ "$PRIVATE" != "true" ]; then
# Append one {"name":"...","version":"..."} entry; the trailing comma is
# stripped below.
COHORT="$COHORT{\"name\":\"$(git show "HEAD:$f" | jq -r .name)\",\"version\":\"$V_NEW\"},"
fi
done < <(git diff --name-only "$(git rev-parse HEAD^)" HEAD -- packages | grep -E '(^|/)package\.json$' | sort -u)
# Wrap the accumulated entries in a JSON array; ${COHORT%,} drops the trailing comma.
COHORT="[${COHORT%,}]"
if [ "$COHORT" != "[]" ]; then
PAYLOAD="$COHORT"
HAS_RELEASE=true
else
PAYLOAD=""
fi
else
PAYLOAD=""
fi
if echo '${{ steps.changesets.outputs.publishedPackages }}' | jq -e '.[] | select(.name == "astro-vscode")' > /dev/null 2>&1 \
|| { [ -n "$COHORT" ] && echo "$COHORT" | jq -e '.[] | select(.name == "astro-vscode")' > /dev/null 2>&1; }; then
HAS_VSCODE=true
fi
echo "has_vscode=$HAS_VSCODE" >> "$GITHUB_OUTPUT"
echo "has_release=$HAS_RELEASE" >> "$GITHUB_OUTPUT"
echo "cohort=$PAYLOAD" >> "$GITHUB_OUTPUT"
- name: Prepare vscode folder
if: steps.release.outputs.has_vscode == 'true'
working-directory: ./packages/language-tools/vscode
run: |
npm run build:grammar
mv node_modules/astro-ts-plugin-bundle ./astro-ts-plugin-bundle # move the ts-plugin out of node_modules to avoid deletion
rm -rf node_modules
# Also delete root package.json and node_modules to avoid vsce picking up any monorepo info
mv ../../../package.json ../../../package.temp.json
rm -rf ../../../node_modules
sleep 60s # wait for npm registry to update
npm i --workspaces=false # vsce does not support pnpm, so we need to pretend this is not a monorepo and use npm
mv ./astro-ts-plugin-bundle ./node_modules/astro-ts-plugin-bundle
- name: Publish to VSCode Marketplace
if: steps.release.outputs.has_vscode == 'true'
working-directory: ./packages/language-tools/vscode
run: |
npx vsce publish -p ${{ secrets.VSCE_TOKEN }} --skip-duplicate --target win32-x64 win32-arm64 linux-x64 linux-arm64 linux-armhf darwin-x64 darwin-arm64 alpine-x64 alpine-arm64
- name: Publish to OpenVSX
if: steps.release.outputs.has_vscode == 'true'
working-directory: ./packages/language-tools/vscode
run: |
npx ovsx publish -p ${{ secrets.OVSX_TOKEN }} --skip-duplicate --target win32-x64 win32-arm64 linux-x64 linux-arm64 linux-armhf darwin-x64 darwin-arm64 alpine-x64 alpine-arm64
- name: Restore root package.json and node_modules
if: steps.release.outputs.has_vscode == 'true'
run: |
mv ./package.temp.json ./package.json
pnpm install --frozen-lockfile
# Fail closed: an unverifiable marker read fails the run rather than risking a duplicate
# announcement; a clean "no such ref" (ls-remote exit 2) is the expected case.
- name: Check if this release was already announced
id: announced
if: steps.release.outputs.has_release == 'true'
run: |
rc=2
for attempt in 1 2; do
set +e
git ls-remote --exit-code origin "refs/release-announced/${GITHUB_SHA}" >/dev/null 2>&1
rc=$?
set -e
if [ "$rc" -eq 0 ]; then
echo "already_announced=true" >> "$GITHUB_OUTPUT"
exit 0
fi
[ "$rc" -eq 2 ] && break
done
if [ "$rc" -eq 2 ]; then
echo "already_announced=false" >> "$GITHUB_OUTPUT"
else
echo "::error::Could not verify the announcement marker (git ls-remote exit $rc)"
exit 1
fi
- name: Generate Announcement
id: message
if: steps.announced.outputs.already_announced == 'false'
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
run: node .github/scripts/announce.mjs '${{ steps.release.outputs.cohort }}'
- name: Send message on Discord
if: steps.announced.outputs.already_announced == 'false'
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
uses: Ilshidur/action-discord@d2594079a10f1d6739ee50a2471f0ca57418b554 # 0.4.0
with:
args: "${{ steps.message.outputs.DISCORD_MESSAGE }}"
# Record the announcement before the dispatch so a failed dispatch can be retried without
# re-announcing. Re-running the same release commit then skips the announcement entirely.
- name: Mark release as announced
if: steps.announced.outputs.already_announced == 'false'
run: |
git push origin HEAD:refs/release-announced/${GITHUB_SHA}
# The dispatch gets its own marker: re-running a completed release must not dispatch again
# (the consumer fails on "Already up to date"), while a failed dispatch must still be
# retried without re-announcing.
- name: Check if this release was already dispatched
id: dispatched
if: steps.release.outputs.has_release == 'true' && github.ref_name == 'main'
run: |
rc=2
for attempt in 1 2; do
set +e
git ls-remote --exit-code origin "refs/release-dispatched/${GITHUB_SHA}" >/dev/null 2>&1
rc=$?
set -e
if [ "$rc" -eq 0 ]; then
echo "already_dispatched=true" >> "$GITHUB_OUTPUT"
exit 0
fi
[ "$rc" -eq 2 ] && break
done
if [ "$rc" -eq 2 ]; then
echo "already_dispatched=false" >> "$GITHUB_OUTPUT"
else
echo "::error::Could not verify the dispatch marker (git ls-remote exit $rc)"
exit 1
fi
- name: Dispatch post-release event
if: steps.dispatched.outputs.already_dispatched == 'false'
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1
with:
token: ${{ secrets.FREDKBOT_GITHUB_TOKEN }}
event-type: release-published
- name: Mark release as dispatched
if: steps.dispatched.outputs.already_dispatched == 'false'
run: |
git push origin HEAD:refs/release-dispatched/${GITHUB_SHA}