diff --git a/.github/workflows/fork-release.yml b/.github/workflows/fork-release.yml index bb8be20795e7b..1485635ed18bb 100644 --- a/.github/workflows/fork-release.yml +++ b/.github/workflows/fork-release.yml @@ -90,7 +90,7 @@ jobs: if [[ -n "$last" ]]; then body=$(gh release view "$last" --repo "$GITHUB_REPOSITORY" --json body --jq .body) fi - if [[ "$FORCE" != "true" && "$body" == *"Fork: $FORK_SHA"* && "$body" == *"Upstream: $UPSTREAM_SHA"* ]]; then + if [[ "$FORCE" != "true" && "$body" == *"fork=$FORK_SHA upstream=$UPSTREAM_SHA "* ]]; then echo "$last already has fork $FORK_SHA and upstream $UPSTREAM_SHA." echo "due=false" >> "$GITHUB_OUTPUT" else @@ -253,7 +253,7 @@ jobs: if [[ -n "$last" ]]; then body=$(gh release view "$last" --repo "$GITHUB_REPOSITORY" --json body --jq .body) fi - if [[ "$body" == *"upstream $UPSTREAM_TAG "* ]]; then + if [[ "$body" == *"upstream-tag=$UPSTREAM_TAG "* ]]; then echo "$UPSTREAM_TAG is already released." exit 0 fi @@ -459,11 +459,16 @@ jobs: run: | set -euo pipefail ls -la publish - notes=$(printf '%s\n' \ - "Windows x64 and Linux x64 builds of upstream $UPSTREAM_TAG with the fork's features (see FORK.md). Unsigned." \ - "" \ - "Fork: $FORK_SHA" \ - "Upstream: $UPSTREAM_SHA") + # Missing notes must not hold back a build; the marker alone still records it. + marker="" + previous=$(gh release list --repo "$GITHUB_REPOSITORY" --limit 1 --json tagName --jq '.[0].tagName // ""') + if ! { gh api "repos/$GITHUB_REPOSITORY/contents/fork/release-notes.sh?ref=$GITHUB_SHA" \ + -H "Accept: application/vnd.github.raw" > "$RUNNER_TEMP/release-notes.sh" && + notes=$(REPO="$GITHUB_REPOSITORY" TARGET="$FORK_SHA" PREVIOUS_TAG="$previous" \ + bash "$RUNNER_TEMP/release-notes.sh"); }; then + echo "::warning::release notes failed; publishing without them" + notes="$marker" + fi gh release create "$TAG" publish/* \ --repo "$GITHUB_REPOSITORY" \ --target "$FORK_BRANCH" \ diff --git a/FORK.md b/FORK.md index 8b9792b0252eb..0447a3df6832d 100644 --- a/FORK.md +++ b/FORK.md @@ -82,8 +82,10 @@ check. - T3 Connect needs the public relay and Clerk values that upstream bakes in. They are repository variables here: `T3CODE_RELAY_URL`, `T3CODE_CLERK_PUBLISHABLE_KEY`, `T3CODE_CLERK_JWT_TEMPLATE`, `T3CODE_CLERK_CLI_OAUTH_CLIENT_ID`. Upstream's telemetry tokens are deliberately not copied. -- The tag sits on the `collin/main` commit; the release notes name the fork and upstream commits - the build merged. +- The tag sits on the `collin/main` commit. The release notes (`fork/release-notes.sh`) are + upstream's notes for every nightly since the previous fork build, then the fork's pull + requests since then; the update popup shows them. A hidden comment at the end records the fork + and upstream commits, which the skip check, the watcher and the watchdog read. ## Keeping changes mergeable diff --git a/fork/release-notes.sh b/fork/release-notes.sh new file mode 100755 index 0000000000000..f4b1a61778d62 --- /dev/null +++ b/fork/release-notes.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# Release notes of a fork build: the notes of every upstream nightly since the previous fork +# release, then the fork's own pull requests since then. The app's update popup and the GitHub +# release page both show this body. A trailing HTML comment, invisible in both, records what was +# built for fork-release.yml and the watcher. +# +# Env: REPO (owner/name), TAG (new release tag), TARGET (fork commit), UPSTREAM_TAG, +# UPSTREAM_SHA, PREVIOUS_TAG (the fork's latest release before this one, may be empty). +set -euo pipefail + +# Only the bullet list of a GitHub "What's Changed" body; the app stops reading at +# "New Contributors" and "Full Changelog", so neither may appear before the fork's list. +changes() { + sed -e '/^## New Contributors/,$d' -e '/^\*\*Full Changelog\*\*/,$d' | grep -E '^[*-] ' || true +} + +previous_upstream="" +if [[ -n "$PREVIOUS_TAG" ]]; then + previous_body=$(gh release view "$PREVIOUS_TAG" --repo "$REPO" --json body --jq .body) + previous_upstream=$(grep -oE 'upstream-tag=[^ ]+|builds of upstream [^ ]+' <<<"$previous_body" | head -n 1 | sed -E 's/.*[= ]//' || true) +fi + +# Upstream nightlies after the previous build's, up to this build's, oldest first: the app +# shows the last items of a body first, so the newest nightly sits right above the fork's list. +nightlies=$(gh release list --repo pingdotgg/t3code --limit 50 --exclude-drafts --json tagName,publishedAt \ + --jq "[.[] | select(.tagName | contains(\"-nightly.\"))] | sort_by(.publishedAt) | map(.tagName) + | (index(\"$UPSTREAM_TAG\")) as \$to | (index(\"$previous_upstream\") // ((\$to // 0) - 1)) as \$from + | if \$to == null then [\"$UPSTREAM_TAG\"] else .[(\$from + 1):(\$to + 1)] end | .[]") + +upstream_changes="" +for nightly in $nightlies; do + upstream_changes+=$(gh release view "$nightly" --repo pingdotgg/t3code --json body --jq .body | changes) + upstream_changes+=$'\n' +done + +fork_changes="" +if [[ -n "$PREVIOUS_TAG" ]]; then + fork_changes=$(gh api "repos/$REPO/releases/generate-notes" -f tag_name="$TAG" \ + -f target_commitish="$TARGET" -f previous_tag_name="$PREVIOUS_TAG" --jq .body | changes) +fi + +if [[ -n "${upstream_changes//[[:space:]]/}" ]]; then + printf '## T3 Code\n%s\n' "$upstream_changes" +fi +if [[ -n "$fork_changes" ]]; then + printf '## Fork\n%s\n\n' "$fork_changes" +fi +printf '\n' "$TARGET" "$UPSTREAM_SHA" "$UPSTREAM_TAG" diff --git a/fork/upstream-nightly-watcher/worker.js b/fork/upstream-nightly-watcher/worker.js index b7dd10c44ce73..11f8059f5ec22 100644 --- a/fork/upstream-nightly-watcher/worker.js +++ b/fork/upstream-nightly-watcher/worker.js @@ -64,8 +64,8 @@ async function check(env) { if (!nightly) return "no upstream nightly"; const tag = nightly.tagName; - // fork-release.yml writes "builds of upstream with" into each release. - if (latest?.description?.includes(`upstream ${tag} `)) return `${tag} already released`; + // fork/release-notes.sh ends each release with a hidden "upstream-tag= " marker. + if (latest?.description?.includes(`upstream-tag=${tag} `)) return `${tag} already released`; // A failed build must not be redispatched every minute; the workflow's cron retries it. const workflow = `repos/${env.FORK}/actions/workflows/fork-release.yml`;