From 83a112785ef7827cbf433dd2976069dd4cd2e6da Mon Sep 17 00:00:00 2001 From: Rob MacEachern Date: Wed, 17 Jun 2026 09:48:30 -0500 Subject: [PATCH] Update release process --- CHANGELOG.md | 7 +- RELEASING.md | 50 ++++++-- Scripts/release.sh | 231 +++++++++++++++++++++++++------------ Scripts/stamp-changelog.sh | 79 ------------- 4 files changed, 197 insertions(+), 170 deletions(-) delete mode 100755 Scripts/stamp-changelog.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index d19ca4cbd..f85ad6007 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ # Changelog -All notable changes to this project will be documented in this file. +> [!NOTE] +> This file is no longer maintained. Starting June 17, 2026, Blueprint uses GitHub auto-generated release notes for new releases. +> See [RELEASING.md](RELEASING.md) for the current release process and [GitHub Releases](https://github.com/square/Blueprint/releases) for current release notes. -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +Historical release notes are preserved below. ## [Main] diff --git a/RELEASING.md b/RELEASING.md index afbab78ef..12ed58454 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -1,23 +1,49 @@ # Releasing a new version -1. Prepare a release by updating the changelog. +Blueprint no longer maintains `CHANGELOG.md` for new releases. Release notes are generated by GitHub from merged pull requests and commits. -1. Make sure you're on the `main` branch, and `git pull` to get the latest commits. +## Prepare the release -1. Create a branch off `main`. +1. Choose the next semantic version tag, using the existing bare version format, for example `6.8.0`. -1. Update `CHANGELOG.md` (in the root of the repo), moving current changes under `Main` to a new section for the version you are releasing. - - The changelog uses [reference links](https://daringfireball.net/projects/markdown/syntax#link) to link each version's changes. Remember to add a link to the new version at the bottom of the file, and to update the link to `[main]`. +1. Make sure all release-bound changes are merged to `main`. -1. Push your branch and open a PR into `main`. +1. Confirm the latest `main` branch build is passing in [GitHub Actions](https://github.com/square/Blueprint/actions). -1. Go to the [Releases](https://github.com/square/Blueprint/releases) and `Draft a new release`. +1. Do not add entries to `CHANGELOG.md`. The file is retained only for historical release notes. -1. `Choose a tag` and create a tag for the new version. +## Cut the release -1. In the release notes, copy the changes from the changelog. +The release script creates a draft GitHub release for the requested version and asks GitHub to generate the release notes. -1. Ensure the `Title` corresponds to the version we're publishing. +```sh +git checkout main +git pull --ff-only origin main +Scripts/release.sh --version 6.8.0 +``` -1. Hit `Publish release`. +By default, the script targets `origin/main` and creates a draft release. Review the generated release notes in GitHub, make any necessary edits, then publish the release. If you intentionally want to skip the draft review step, pass `--publish`. + +To target a specific merge commit instead of `origin/main`, pass its full SHA: + +```sh +Scripts/release.sh --version 6.8.0 --target 0123456789abcdef0123456789abcdef01234567 +``` + +## Manual fallback + +1. Go to [Releases](https://github.com/square/Blueprint/releases) and choose `Draft a new release`. + +1. Choose or create the tag for the new version. Target the latest `main` commit that should be included in the release. + +1. Use GitHub's generated release notes. + +1. Set the release title to the version number, review the generated notes, and publish the release. + +## After publishing + +Fetch tags locally if you need the new release tag in your checkout: + +```sh +git fetch --tags origin +``` diff --git a/Scripts/release.sh b/Scripts/release.sh index 2580c7604..0ccd35b2d 100755 --- a/Scripts/release.sh +++ b/Scripts/release.sh @@ -1,111 +1,190 @@ -#!/bin/bash - -echo "TODO: Update this script to use the new release process." -echo "Use the manual steps in RELEASING.md for now." -exit 0 +#!/usr/bin/env bash set -euo pipefail -branch="main" -diff_check=false +target="origin/main" +draft=true +open_release=true +dry_run=false +fail_on_no_commits=true +prerelease=false +notes_start_tag="" -# Function to display usage usage() { - echo "Usage: $0 --version [--branch ] [--no-diff-check]" - exit 1 + cat <<'END' +Usage: Scripts/release.sh --version [options] + +Creates a GitHub release with auto-generated release notes. + +Options: + -v, --version Version tag to release, for example 6.8.0. + -t, --target Git ref or commit SHA to release. Defaults to origin/main. + --notes-start-tag Tag to start generated release notes from. + --publish Publish immediately instead of creating a draft. + --draft Create a draft release. This is the default. + --prerelease Mark the release as a prerelease. + --allow-no-commits Allow a release with no commits since the previous release. + --no-open Do not open the release in a browser. + --dry-run Print the gh command without creating the release. + -h, --help Show this help text. +END } -# Check if gh CLI is installed -if ! command -v gh &> /dev/null; then - echo "Error: GitHub CLI (gh) is not installed. It is required by this script." - echo "Please install it from https://cli.github.com/, authenticate, and try again." - exit 1 -fi +require_value() { + if [[ $# -lt 2 || -z "${2:-}" ]]; then + echo "Error: $1 requires a value." >&2 + usage + exit 1 + fi + + if [[ "${2:0:1}" == "-" ]]; then + echo "Error: $1 requires a value." >&2 + usage + exit 1 + fi +} -# Parse options while [[ $# -gt 0 ]]; do - case $1 in - -v|--version) version="$2"; shift 2 ;; - -b|--branch) branch="$2"; shift 2 ;; - -n|--no-diff-check) diff_check=false; shift ;; - --) shift; break ;; - -*|--*) echo "Unknown option $1"; usage ;; - *) break ;; + case "$1" in + -v|--version) + require_value "$@" + version="${2:-}" + shift 2 + ;; + -t|--target) + require_value "$@" + target="${2:-}" + shift 2 + ;; + --notes-start-tag) + require_value "$@" + notes_start_tag="${2:-}" + shift 2 + ;; + --publish) + draft=false + shift + ;; + --draft) + draft=true + shift + ;; + --prerelease) + prerelease=true + shift + ;; + --allow-no-commits) + fail_on_no_commits=false + shift + ;; + --no-open) + open_release=false + shift + ;; + --dry-run) + dry_run=true + shift + ;; + -h|--help) + usage + exit 0 + ;; + --) + shift + break + ;; + -*|--*) + echo "Unknown option: $1" >&2 + usage + exit 1 + ;; + *) + echo "Unexpected argument: $1" >&2 + usage + exit 1 + ;; esac done -# Check if version argument is provided -if [ -z "${version:-}" ]; then - echo "Error: You must provide a version number." +if [[ -z "${version:-}" ]]; then + echo "Error: You must provide a version number." >&2 usage + exit 1 fi -# Ensure there are no unstaged changes -if [ "$diff_check" = true ] && ! git diff --quiet origin/"$branch"; then - echo "Error: This branch has differences compared to origin/$branch. Please push or undo these changes before continuing." - echo "You can bypass this check with the --no-diff-check flag." +if [[ -z "$target" ]]; then + echo "Error: --target cannot be empty." >&2 exit 1 fi -# This timestamp is used during branch creation. -# It's helpful in cases where the script fails and a new branch needs to -# be created on a subsequent attempt. -timestamp=$(date +"%Y-%m-%d-%H_%M_%S") - -git checkout "$branch" -git pull +if ! command -v gh > /dev/null; then + echo "Error: GitHub CLI (gh) is required. Install it from https://cli.github.com/ and authenticate before retrying." >&2 + exit 1 +fi -# Create a new branch with the version and timestamp -branch_name="$(whoami)/release-$version-$timestamp" -git checkout -b "$branch_name" +if ! gh auth status > /dev/null 2>&1; then + echo "Error: GitHub CLI (gh) is not authenticated. Run 'gh auth login' before retrying." >&2 + exit 1 +fi -# Define the git repo root repo_root=$(git rev-parse --show-toplevel) +cd "$repo_root" -# Extract the previous version number from version.rb -previous_version=$(grep 'BLUEPRINT_VERSION' "$repo_root/version.rb" | awk -F"'" '{print $2}') +if [[ "$dry_run" == false ]] && { ! git diff --quiet || ! git diff --cached --quiet; }; then + echo "Error: The working tree has uncommitted changes. Commit or stash them before cutting a release." >&2 + exit 1 +fi -# Update the library version in version.rb -sed -i '' "s/BLUEPRINT_VERSION ||= .*/BLUEPRINT_VERSION ||= '$version'/" "$repo_root/version.rb" +git fetch origin --tags -# Update CHANGELOG.md using stamp-changelog.sh -"$repo_root/Scripts/stamp-changelog.sh" --version "$version" --previous-version "$previous_version" +target_sha=$(git rev-parse --verify "$target^{commit}") -# Change directory into the SampleApp dir and update Podfile.lock using a subshell -( - cd "$repo_root/SampleApp" - bundle exec pod install +if gh release view "$version" > /dev/null 2>&1; then + echo "Error: A GitHub release already exists for $version." >&2 + exit 1 +fi + +release_command=( + gh release create "$version" + --target "$target_sha" + --title "$version" + --generate-notes ) -# Commit the changes -git add . -git commit -m "Bumping versions to $version." +if [[ "$draft" == true ]]; then + release_command+=(--draft) +fi -# Push the branch and open a PR into main -git push origin "$branch_name" +if [[ "$prerelease" == true ]]; then + release_command+=(--prerelease) +fi -pr_body=$(cat <<-END -https://github.com/square/Blueprint/blob/main/CHANGELOG.md +if [[ "$fail_on_no_commits" == true ]]; then + release_command+=(--fail-on-no-commits) +fi -Post-merge steps: +if [[ -n "$notes_start_tag" ]]; then + release_command+=(--notes-start-tag "$notes_start_tag") +fi -- Once the PR is merged, fetch changes and tag the release, using the merge commit: - \`\`\` - git fetch - git tag $version - git push origin $version - \`\`\` +release_kind="" +if [[ "$draft" == true ]]; then + release_kind="draft " +fi -- Publish to CocoaPods: - \`\`\` - bundle exec pod trunk push BlueprintUI.podspec - bundle exec pod trunk push --synchronous BlueprintUICommonControls.podspec - \`\`\` -END -) +printf 'Creating %srelease %s from %s (%s).\n' "$release_kind" "$version" "$target" "$target_sha" -gh pr create --draft --title "release: Blueprint $version" --body "$pr_body" +if [[ "$dry_run" == true ]]; then + printf 'Dry run:' + printf ' %q' "${release_command[@]}" + printf '\n' + exit 0 +fi + +"${release_command[@]}" -gh pr view --web +if [[ "$open_release" == true ]]; then + gh release view "$version" --web +fi -echo "Branch $branch_name created and pushed. A draft PR has been created." +echo "Release $version created. Review the generated notes in GitHub before publishing if this is a draft." diff --git a/Scripts/stamp-changelog.sh b/Scripts/stamp-changelog.sh deleted file mode 100755 index 99d908fb6..000000000 --- a/Scripts/stamp-changelog.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/bash - -# Function to display usage -usage() { - echo "Usage: $0 -v -p " - echo " -v, --version Version number (required)" - echo " -p, --previous-version Previous version number (required)" - echo " -d, --release-date The date of the release (optional). Defaults to: date +%Y-%m-%d" - exit 1 -} - -# Parse options -while [[ "$#" -gt 0 ]]; do - case "$1" in - -v|--version) version="$2"; shift 2 ;; - -p|--previous-version) previous_version="$2"; shift 2 ;; - -d|--release-date) release_date="$2"; shift 2 ;; - --) shift; break ;; - -*|--*) echo "Unknown option $1"; usage ;; - *) break ;; - esac -done - -# Check if both version and previous_version arguments are provided -if [ -z "$version" ] || [ -z "$previous_version" ]; then - echo "Error: You must provide both version and previous version numbers." - usage -fi - -if [ -z "$release_date" ]; then - release_date=$(date +%Y-%m-%d) -fi - -repo_root=$(git rev-parse --show-toplevel) -changelog_file="$repo_root/CHANGELOG.md" - -changelog=$(ruby < "$changelog_file" - -echo "CHANGELOG.md updated for version $version." \ No newline at end of file