Create new release diffs #457
Workflow file for this run
This file contains hidden or 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: Create new release diffs | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| releaseVersion: | |
| description: "Backstage release version to process (e.g. 1.48.0). If omitted, auto-detects all untracked releases." | |
| required: false | |
| default: "" | |
| force: | |
| description: "Re-process already-tracked releases (useful for backfilling missing diffs). Only applies when no releaseVersion is specified." | |
| type: boolean | |
| required: false | |
| default: false | |
| jobs: | |
| detect: | |
| name: Detect new releases | |
| runs-on: ubuntu-latest | |
| outputs: | |
| versions: ${{ steps.detect.outputs.versions }} | |
| steps: | |
| - name: Detect new @backstage/create-app versions | |
| id: detect | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -e | |
| resolve_manifest() { | |
| local RELEASE_VERSION="$1" | |
| MANIFEST=$(curl -sf "https://raw.githubusercontent.com/backstage/versions/main/v1/releases/${RELEASE_VERSION}/manifest.json") || { | |
| echo "Error: failed to fetch manifest for $RELEASE_VERSION (HTTP error)" >&2 | |
| exit 1 | |
| } | |
| CREATE_APP_VERSION=$(echo "$MANIFEST" | jq -r '.packages[] | select(.name == "@backstage/create-app") | .version') | |
| if [ -z "$CREATE_APP_VERSION" ] || [ "$CREATE_APP_VERSION" = "null" ]; then | |
| echo "Error: no @backstage/create-app found in manifest for $RELEASE_VERSION" >&2 | |
| exit 1 | |
| fi | |
| echo "$CREATE_APP_VERSION" | |
| } | |
| VERSIONS_JSON="[]" | |
| if [ -n "${{ github.event.inputs.releaseVersion }}" ]; then | |
| RELEASE_VERSION="${{ github.event.inputs.releaseVersion }}" | |
| RELEASE_VERSION="${RELEASE_VERSION#v}" | |
| CREATE_APP_VERSION=$(resolve_manifest "$RELEASE_VERSION") | |
| echo "Forced version: createApp=$CREATE_APP_VERSION releaseVersion=$RELEASE_VERSION" | |
| VERSIONS_JSON=$(echo "$VERSIONS_JSON" | jq -c \ | |
| --arg v "$CREATE_APP_VERSION" \ | |
| --arg rv "$RELEASE_VERSION" \ | |
| '. + [{"version": $v, "releaseVersion": $rv}]') | |
| else | |
| KNOWN_RELEASES=$(curl -sf https://raw.githubusercontent.com/backstage/upgrade-helper-diff/master/releases.json | \ | |
| jq -r 'keys[]') | |
| while IFS= read -r TAG; do | |
| RELEASE_VERSION="${TAG#v}" | |
| if echo "$KNOWN_RELEASES" | grep -qx "$RELEASE_VERSION"; then | |
| if [ "${{ github.event.inputs.force }}" != "true" ]; then | |
| echo "Release $RELEASE_VERSION already tracked, skipping" | |
| continue | |
| fi | |
| echo "Release $RELEASE_VERSION already tracked, re-processing (force=true)" | |
| fi | |
| CREATE_APP_VERSION=$(resolve_manifest "$RELEASE_VERSION") || continue | |
| echo "Found new version: createApp=$CREATE_APP_VERSION releaseVersion=$RELEASE_VERSION" | |
| VERSIONS_JSON=$(echo "$VERSIONS_JSON" | jq -c \ | |
| --arg v "$CREATE_APP_VERSION" \ | |
| --arg rv "$RELEASE_VERSION" \ | |
| '. + [{"version": $v, "releaseVersion": $rv}]') | |
| done < <(gh api "/repos/backstage/backstage/releases?per_page=50" | jq -r 'reverse | .[].tag_name') | |
| fi | |
| echo "versions=$VERSIONS_JSON" >> $GITHUB_OUTPUT | |
| process: | |
| name: Process new releases | |
| needs: detect | |
| if: needs.detect.outputs.versions != '[]' | |
| runs-on: ubuntu-latest | |
| env: | |
| BACKSTAGE_APP_NAME: backstagediffapp | |
| steps: | |
| - run: echo "π The job was automatically triggered by a ${{ github.event_name }} event." | |
| - name: Check out repository | |
| uses: actions/checkout@v5 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22 | |
| - name: Setup git bot user | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| - name: Process all new versions | |
| env: | |
| VERSIONS: ${{ needs.detect.outputs.versions }} | |
| run: | | |
| set -e | |
| while IFS= read -r ENTRY; do | |
| VERSION=$(echo "$ENTRY" | jq -r '.version') | |
| RELEASE_VERSION=$(echo "$ENTRY" | jq -r '.releaseVersion') | |
| echo "=== Processing createApp=$VERSION releaseVersion=$RELEASE_VERSION ===" | |
| # ββ Release branch ββββββββββββββββββββββββββββββββββββββββββββββ | |
| if [ -z "$(git ls-remote --heads origin release/${VERSION})" ]; then | |
| git fetch origin release-base | |
| git checkout -fB release-base origin/release-base | |
| git clean -fd | |
| printf "$BACKSTAGE_APP_NAME\n\t" | npx @backstage/create-app@${VERSION} --skip-install | |
| rm -rf "$(pwd)/$BACKSTAGE_APP_NAME/.git" | |
| cp -ar "$(pwd)/$BACKSTAGE_APP_NAME/." "$(pwd)" | |
| git checkout -b release/${VERSION} | |
| git add . | |
| git reset -- $BACKSTAGE_APP_NAME | |
| git commit -m "Release ${VERSION}" | |
| git push origin release/${VERSION} | |
| else | |
| echo "Branch release/${VERSION} already exists, skipping" | |
| fi | |
| # ββ Yarn plugin branch ββββββββββββββββββββββββββββββββββββββββββ | |
| if [ -z "$(git ls-remote --heads origin release/yarn-plugin/${RELEASE_VERSION})" ]; then | |
| git fetch origin "release/${VERSION}" | |
| git checkout -fB "release/${VERSION}" "origin/release/${VERSION}" | |
| git clean -fd | |
| yarn plugin import https://versions.backstage.io/v1/tags/next/yarn-plugin | |
| YARN_ENABLE_IMMUTABLE_INSTALLS=false npx --legacy-peer-deps -p @backstage/cli@latest backstage-cli versions:bump --release $RELEASE_VERSION | |
| git checkout -b release/yarn-plugin/${RELEASE_VERSION} | |
| git add . | |
| git reset -- $BACKSTAGE_APP_NAME yarn.lock | |
| git commit -m "Release ${RELEASE_VERSION}" | |
| git push origin release/yarn-plugin/${RELEASE_VERSION} | |
| else | |
| echo "Branch release/yarn-plugin/${RELEASE_VERSION} already exists, skipping" | |
| fi | |
| # ββ Legacy diffs βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| git fetch origin release-diff-base | |
| git checkout -fB release-diff-base origin/release-diff-base | |
| git clean -fd | |
| if [ -z "$(git ls-remote --heads origin release-diff/legacy/v${VERSION})" ]; then | |
| git checkout -b release-diff/legacy/v${VERSION} | |
| mkdir diffs | |
| else | |
| git fetch origin "release-diff/legacy/v${VERSION}" | |
| git checkout -fB "release-diff/legacy/v${VERSION}" "origin/release-diff/legacy/v${VERSION}" | |
| git clean -fd | |
| fi | |
| git fetch origin release/$VERSION | |
| for ver in $(curl -sf https://raw.githubusercontent.com/backstage/upgrade-helper-diff/master/releases.json | jq -r 'to_entries | .[] | .value.createApp // .key'); do | |
| [ "$(printf '%s\n%s\n' "$ver" "$VERSION" | sort -V | head -1)" != "$ver" ] && continue | |
| DIFF_FILE="diffs/$ver..$VERSION.diff" | |
| if [ ! -f "$DIFF_FILE" ]; then | |
| echo "Creating diff between $ver and $VERSION" | |
| git fetch origin release/$ver | |
| git diff -U1 origin/release/$ver..origin/release/$VERSION > "$DIFF_FILE" | |
| git add "$DIFF_FILE" | |
| fi | |
| done | |
| if ! git diff --cached --quiet; then | |
| git commit -m "Add release ${VERSION}" | |
| git push origin release-diff/legacy/v${VERSION} | |
| else | |
| echo "No new diffs for release-diff/legacy/v${VERSION}" | |
| fi | |
| # ββ Backfill legacy diff branches βββββββββββββββββββββββββββββββββ | |
| git fetch origin "release/${VERSION}" | |
| while IFS= read -r future_ver; do | |
| [ "$future_ver" = "$VERSION" ] && continue | |
| OLDER=$(printf '%s\n%s\n' "$VERSION" "$future_ver" | sort -V | head -1) | |
| [ "$OLDER" != "$VERSION" ] && continue | |
| FUTURE_BRANCH="release-diff/legacy/v${future_ver}" | |
| [ -z "$(git ls-remote --heads origin "${FUTURE_BRANCH}")" ] && continue | |
| git fetch origin "${FUTURE_BRANCH}" | |
| git checkout -fB "${FUTURE_BRANCH}" "origin/${FUTURE_BRANCH}" | |
| git clean -fd | |
| DIFF_FILE="diffs/${VERSION}..${future_ver}.diff" | |
| if [ ! -f "$DIFF_FILE" ]; then | |
| echo "Backfilling legacy diff ${VERSION}..${future_ver}" | |
| git fetch origin "release/${future_ver}" | |
| git diff -U1 "origin/release/${VERSION}..origin/release/${future_ver}" > "$DIFF_FILE" | |
| git add "$DIFF_FILE" | |
| if ! git diff --cached --quiet; then | |
| git commit -m "Backfill diff ${VERSION}..${future_ver}" | |
| git push origin "${FUTURE_BRANCH}" | |
| else | |
| echo "No changes to commit for ${DIFF_FILE}, skipping" | |
| fi | |
| fi | |
| done < <(curl -sf https://raw.githubusercontent.com/backstage/upgrade-helper-diff/master/releases.json | jq -r '[to_entries[] | .value.createApp // .key] | unique[]') | |
| # ββ Yarn plugin diffs ββββββββββββββββββββββββββββββββββββββββββββ | |
| git fetch origin release-diff-base | |
| git checkout -fB release-diff-base origin/release-diff-base | |
| git clean -fd | |
| if [ -z "$(git ls-remote --heads origin release-diff/v${RELEASE_VERSION})" ]; then | |
| git checkout -b release-diff/v${RELEASE_VERSION} | |
| mkdir diffs | |
| else | |
| git fetch origin "release-diff/v${RELEASE_VERSION}" | |
| git checkout -fB "release-diff/v${RELEASE_VERSION}" "origin/release-diff/v${RELEASE_VERSION}" | |
| git clean -fd | |
| fi | |
| git fetch origin release/yarn-plugin/$RELEASE_VERSION | |
| for ver in $(curl -sf https://raw.githubusercontent.com/backstage/upgrade-helper-diff/master/releases-yarn-plugin.json | jq -r 'to_entries | .[] | .key'); do | |
| [ "$(printf '%s\n%s\n' "$ver" "$RELEASE_VERSION" | sort -V | head -1)" != "$ver" ] && continue | |
| DIFF_FILE="diffs/$ver..$RELEASE_VERSION.diff" | |
| if [ ! -f "$DIFF_FILE" ]; then | |
| echo "Creating yarn-plugin diff between $ver and $RELEASE_VERSION" | |
| git fetch origin release/yarn-plugin/$ver | |
| git diff -U1 origin/release/yarn-plugin/$ver..origin/release/yarn-plugin/$RELEASE_VERSION > "$DIFF_FILE" | |
| git add "$DIFF_FILE" | |
| fi | |
| done | |
| if ! git diff --cached --quiet; then | |
| git commit -m "Add release ${RELEASE_VERSION}" | |
| git push origin release-diff/v${RELEASE_VERSION} | |
| else | |
| echo "No new diffs for release-diff/v${RELEASE_VERSION}" | |
| fi | |
| # ββ Backfill yarn-plugin diff branches ββββββββββββββββββββββββββββ | |
| git fetch origin "release/yarn-plugin/${RELEASE_VERSION}" | |
| while IFS= read -r future_rv; do | |
| [ "$future_rv" = "$RELEASE_VERSION" ] && continue | |
| OLDER=$(printf '%s\n%s\n' "$RELEASE_VERSION" "$future_rv" | sort -V | head -1) | |
| [ "$OLDER" != "$RELEASE_VERSION" ] && continue | |
| FUTURE_BRANCH="release-diff/v${future_rv}" | |
| [ -z "$(git ls-remote --heads origin "${FUTURE_BRANCH}")" ] && continue | |
| git fetch origin "${FUTURE_BRANCH}" | |
| git checkout -fB "${FUTURE_BRANCH}" "origin/${FUTURE_BRANCH}" | |
| git clean -fd | |
| DIFF_FILE="diffs/${RELEASE_VERSION}..${future_rv}.diff" | |
| if [ ! -f "$DIFF_FILE" ]; then | |
| echo "Backfilling yarn-plugin diff ${RELEASE_VERSION}..${future_rv}" | |
| git fetch origin "release/yarn-plugin/${future_rv}" | |
| git diff -U1 "origin/release/yarn-plugin/${RELEASE_VERSION}..origin/release/yarn-plugin/${future_rv}" > "$DIFF_FILE" | |
| git add "$DIFF_FILE" | |
| if ! git diff --cached --quiet; then | |
| git commit -m "Backfill diff ${RELEASE_VERSION}..${future_rv}" | |
| git push origin "${FUTURE_BRANCH}" | |
| else | |
| echo "No changes to commit for ${DIFF_FILE}, skipping" | |
| fi | |
| fi | |
| done < <(curl -sf https://raw.githubusercontent.com/backstage/upgrade-helper-diff/master/releases-yarn-plugin.json | jq -r 'keys[]') | |
| # ββ Update JSON files on master ββββββββββββββββββββββββββββββββββ | |
| git fetch origin master | |
| git checkout -fB master origin/master | |
| git clean -fd | |
| jq ".\"$RELEASE_VERSION\" = { createApp: \"$VERSION\" }" < releases.json > _releases.json | |
| mv _releases.json releases.json | |
| jq ".\"$RELEASE_VERSION\" = { createApp: \"$VERSION\" }" < releases-yarn-plugin.json > _releases-yarn-plugin.json | |
| mv _releases-yarn-plugin.json releases-yarn-plugin.json | |
| git add releases.json releases-yarn-plugin.json | |
| if ! git diff --cached --quiet; then | |
| git commit -m "Add release ${VERSION}" | |
| git push origin master | |
| else | |
| echo "releases.json already up to date, skipping" | |
| fi | |
| done <<< "$(echo "$VERSIONS" | jq -c '.[]')" | |
| - run: echo "π This job's status is ${{ job.status }}." |