diff --git a/Makefile b/Makefile index e006a624f..b817a246b 100644 --- a/Makefile +++ b/Makefile @@ -60,3 +60,38 @@ build-swift-package: build test-swift-package: build $(call XCODEBUILD_CMD, test) + +release: + @echo "--- :rocket: Starting GutenbergKit Release Process" + @echo "Usage: make release VERSION_TYPE=[ | major | minor | patch | premajor | preminor | prepatch | prerelease | from-git] [DRY_RUN=true]" + @echo "" + @echo "Version Types:" + @echo " Custom version number (e.g., 1.2.3)" + @echo " major Increment major version (1.0.0 -> 2.0.0)" + @echo " minor Increment minor version (1.2.0 -> 1.3.0)" + @echo " patch Increment patch version (1.2.3 -> 1.2.4)" + @echo " premajor Increment major version and add prerelease (1.2.3 -> 2.0.0-alpha.0)" + @echo " preminor Increment minor version and add prerelease (1.2.3 -> 1.3.0-alpha.0)" + @echo " prepatch Increment patch version and add prerelease (1.2.3 -> 1.2.4-alpha.0)" + @echo " prerelease Increment prerelease version (1.2.3-alpha.0 -> 1.2.3-alpha.1)" + @echo " from-git Use version from git tag" + @echo "" + @echo "Examples:" + @echo " make release VERSION_TYPE=patch" + @echo " make release VERSION_TYPE=minor" + @echo " make release VERSION_TYPE=major" + @echo " make release VERSION_TYPE=1.2.3" + @echo " make release VERSION_TYPE=premajor" + @echo " make release VERSION_TYPE=prerelease" + @echo " make release VERSION_TYPE=patch DRY_RUN=true" + @echo "" + @if [ -z "$(VERSION_TYPE)" ]; then \ + echo "Error: VERSION_TYPE is required."; \ + echo "Use one of: , major, minor, patch, premajor, preminor, prepatch, prerelease, from-git"; \ + exit 1; \ + fi + @if [ "$(DRY_RUN)" = "true" ]; then \ + ./bin/release.sh $(VERSION_TYPE) --dry-run; \ + else \ + ./bin/release.sh $(VERSION_TYPE); \ + fi diff --git a/README.md b/README.md index 29898b6e0..228d70cea 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,10 @@ make build Once finished, the Swift and Kotlin packages are ready to publish. Consuming iOS or Android host apps can then include the GutenbergKit package as a dependency. +## Releases + +See the [release documentation](./docs/releases.md) for more information. + ## Remote Editor By default, GutenbergKit utilizes local `@wordpress` modules. This approach is similar to most modern web applications, where the `@wordpress` modules are bundled with the application. diff --git a/bin/README.md b/bin/README.md new file mode 100644 index 000000000..6074c7e6b --- /dev/null +++ b/bin/README.md @@ -0,0 +1,94 @@ +# Bin Directory + +This directory contains utility scripts for the GutenbergKit project. + +## Scripts + +### `release.sh` + +Automates the GutenbergKit release process. This script performs all the steps outlined in the [release documentation](../docs/releases.md). + +#### Usage + +```bash +# Direct usage +./bin/release.sh [ | major | minor | patch | premajor | preminor | prepatch | prerelease | from-git] [--dry-run] + +# Via Makefile (recommended) +make release VERSION_TYPE=[ | major | minor | patch | premajor | preminor | prepatch | prerelease | from-git] [DRY_RUN=true] +``` + +#### Version Types + +| Type | Description | Example | +| -------------- | ------------------------------- | --------------------------------- | +| `major` | Increment major version | `1.2.3` → `2.0.0` | +| `minor` | Increment minor version | `1.2.3` → `1.3.0` | +| `patch` | Increment patch version | `1.2.3` → `1.2.4` | +| `premajor` | Increment major with prerelease | `1.2.3` → `2.0.0-alpha.0` | +| `preminor` | Increment minor with prerelease | `1.2.3` → `1.3.0-alpha.0` | +| `prepatch` | Increment patch with prerelease | `1.2.3` → `1.2.4-alpha.0` | +| `prerelease` | Increment prerelease version | `1.2.3-alpha.0` → `1.2.3-alpha.1` | +| `from-git` | Use version from git tag | Uses latest git tag | +| `` | Set specific version | `1.2.3` → `1.2.3` | + +#### Examples + +```bash +# Standard releases +make release VERSION_TYPE=patch +make release VERSION_TYPE=minor +make release VERSION_TYPE=major + +# Custom version +make release VERSION_TYPE=1.2.3 + +# Prerelease versions +make release VERSION_TYPE=premajor +make release VERSION_TYPE=preminor +make release VERSION_TYPE=prepatch +make release VERSION_TYPE=prerelease + +# Use version from git tag +make release VERSION_TYPE=from-git + +# Test without making changes +make release VERSION_TYPE=patch DRY_RUN=true +``` + +#### What it does + +1. **Pre-flight checks:** + + - Verifies you're on the `trunk` branch + - Checks that your working directory is clean + - Ensures required dependencies are installed (`gh`, `npm`, `make`) + +2. **Release process:** + - Increments the version number in `package.json` + - Builds the project using `make build` + - Commits all changes with the version number as the commit message + - Creates a git tag with the version number + - Pushes changes to `origin/trunk` with tags + - Creates a GitHub release with auto-generated notes + +#### Dependencies + +- `gh` (GitHub CLI) - for creating GitHub releases +- `npm` - for version management and building +- `make` - for building the project +- Git - for version control operations + +### `prep-translations.js` + +Prepares translations for the GutenbergKit project. This script is typically run as part of the build process. + +#### Usage + +```bash +# Direct usage +node bin/prep-translations.js + +# Via npm script +npm run prep-translations +``` diff --git a/bin/release.sh b/bin/release.sh new file mode 100755 index 000000000..f91c9dd82 --- /dev/null +++ b/bin/release.sh @@ -0,0 +1,354 @@ +#!/bin/bash + +# GutenbergKit Release Script +# This script automates the release process for GutenbergKit + +set -e # Exit on any error + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Function to print colored output +print_status() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +print_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +print_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Function to show usage +show_usage() { + echo "Usage: $0 [ | major | minor | patch | premajor | preminor | prepatch | prerelease | from-git] [--dry-run]" + echo "" + echo "Arguments:" + echo " Custom version number (e.g., 1.2.3)" + echo " major Increment major version (1.0.0 -> 2.0.0)" + echo " minor Increment minor version (1.2.0 -> 1.3.0)" + echo " patch Increment patch version (1.2.3 -> 1.2.4)" + echo " premajor Increment major version and add prerelease (1.2.3 -> 2.0.0-alpha.0)" + echo " preminor Increment minor version and add prerelease (1.2.3 -> 1.3.0-alpha.0)" + echo " prepatch Increment patch version and add prerelease (1.2.3 -> 1.2.4-alpha.0)" + echo " prerelease Increment prerelease version (1.2.3-alpha.0 -> 1.2.3-alpha.1)" + echo " from-git Use version from git tag" + echo " --dry-run Run the script without making actual changes" + echo "" + echo "Examples:" + echo " $0 patch # Increment patch version (0.3.0 -> 0.3.1)" + echo " $0 minor # Increment minor version (0.3.0 -> 0.4.0)" + echo " $0 major # Increment major version (0.3.0 -> 1.0.0)" + echo " $0 1.2.3 # Set specific version" + echo " $0 premajor # Increment major with prerelease (0.3.0 -> 1.0.0-alpha.0)" + echo " $0 prerelease # Increment prerelease (1.2.3-alpha.0 -> 1.2.3-alpha.1)" + echo " $0 patch --dry-run # Test the release process without committing" +} + +# Function to check if we're on the trunk branch +check_branch() { + local current_branch=$(git branch --show-current) + if [ "$current_branch" != "trunk" ]; then + print_error "You must be on the 'trunk' branch to create a release." + print_error "Current branch: $current_branch" + print_error "Please switch to trunk: git checkout trunk" + exit 1 + fi +} + +# Function to check if working directory is clean +check_working_directory() { + if [ -n "$(git status --porcelain)" ]; then + print_error "Working directory is not clean. Please commit or stash your changes." + git status --short + exit 1 + fi +} + +# Function to check if required tools are available +check_dependencies() { + local missing_deps=() + + if ! command -v gh &> /dev/null; then + missing_deps+=("gh (GitHub CLI)") + fi + + if ! command -v npm &> /dev/null; then + missing_deps+=("npm") + fi + + if ! command -v make &> /dev/null; then + missing_deps+=("make") + fi + + if [ ${#missing_deps[@]} -ne 0 ]; then + print_error "Missing required dependencies:" + for dep in "${missing_deps[@]}"; do + echo " - $dep" + done + exit 1 + fi +} + +# Function to get current version +get_current_version() { + node -p "require('./package.json').version" +} + +# Function to validate version type +validate_version_type() { + local version_type=$1 + + # Check if it's a valid npm version type or a custom version number + case $version_type in + major|minor|patch|premajor|preminor|prepatch|prerelease|from-git) + return 0 + ;; + *) + # Check if it's a valid semver version number + if [[ $version_type =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$ ]]; then + return 0 + else + return 1 + fi + ;; + esac +} + +# Function to get version description +get_version_description() { + local version_type=$1 + local current_version=$(get_current_version) + + case $version_type in + major) + echo "major version increment" + ;; + minor) + echo "minor version increment" + ;; + patch) + echo "patch version increment" + ;; + premajor) + echo "major version increment with prerelease" + ;; + preminor) + echo "minor version increment with prerelease" + ;; + prepatch) + echo "patch version increment with prerelease" + ;; + prerelease) + echo "prerelease version increment" + ;; + from-git) + echo "version from git tag" + ;; + *) + echo "custom version: $version_type" + ;; + esac +} + +# Function to increment version +increment_version() { + local version_type=$1 + local current_version=$(get_current_version) + local version_description=$(get_version_description "$version_type") + + print_status "Current version: $current_version" + print_status "Incrementing version ($version_description)..." + + if [ "$DRY_RUN" = "true" ]; then + return + fi + + # For prerelease types, always use 'alpha' as the preid + case $version_type in + premajor|preminor|prepatch|prerelease) + npm --no-git-tag-version version "$version_type" --preid=alpha + ;; + *) + npm --no-git-tag-version version "$version_type" + ;; + esac + + local new_version=$(get_current_version) + print_success "Version incremented to: $new_version" +} + +# Function to build the project +build_project() { + print_status "Building GutenbergKit..." + + if [ "$DRY_RUN" = "true" ]; then + return + fi + + make build + print_success "Build completed successfully" +} + +# Function to commit changes +commit_changes() { + local version=$1 + + print_status "Adding changes to git..." + + if [ "$DRY_RUN" = "true" ]; then + return + fi + + git add . + git commit -m "$version" + print_success "Changes committed with message: $version" +} + +# Function to create git tag +create_tag() { + local version=$1 + + print_status "Creating git tag: v$version" + + if [ "$DRY_RUN" = "true" ]; then + return + fi + + git tag "v$version" + print_success "Tag created: v$version" +} + +# Function to push changes +push_changes() { + local version=$1 + + print_status "Pushing changes to origin/trunk..." + + if [ "$DRY_RUN" = "true" ]; then + return + fi + + git push origin trunk --tags + print_success "Changes pushed successfully" +} + +# Function to create GitHub release +create_github_release() { + local version=$1 + + print_status "Creating GitHub release: v$version" + + if [ "$DRY_RUN" = "true" ]; then + return + fi + + gh release create "v$version" --generate-notes --title "$version" + print_success "GitHub release created: v$version" +} + +# Main function +main() { + local version_type="" + local DRY_RUN=false + + # Parse arguments + while [[ $# -gt 0 ]]; do + case $1 in + --dry-run) + DRY_RUN=true + shift + ;; + -h|--help) + show_usage + exit 0 + ;; + *) + if [ -z "$version_type" ]; then + version_type="$1" + else + print_error "Unknown option: $1" + show_usage + exit 1 + fi + shift + ;; + esac + done + + # Validate required argument + if [ -z "$version_type" ]; then + print_error "Version type is required" + show_usage + exit 1 + fi + + # Validate version type + if ! validate_version_type "$version_type"; then + print_error "Invalid version type: $version_type" + show_usage + exit 1 + fi + + if [ "$DRY_RUN" = "true" ]; then + print_warning "DRY RUN MODE - No actual changes will be made" + echo + fi + + # Pre-flight checks + print_status "Running pre-flight checks..." + check_dependencies + check_branch + check_working_directory + print_success "Pre-flight checks passed" + echo + + # Get current version before incrementing + local current_version=$(get_current_version) + + # Execute release steps + increment_version "$version_type" + echo + + build_project + echo + + # Get new version after incrementing + local new_version=$(get_current_version) + + commit_changes "$new_version" + echo + + create_tag "$new_version" + echo + + push_changes "$new_version" + echo + + create_github_release "$new_version" + echo + + # Summary + print_success "Release process completed successfully!" + print_status "Version: $current_version -> $new_version" + + if [ "$DRY_RUN" = "true" ]; then + print_warning "This was a dry run. No actual changes were made." + print_status "To perform the actual release, run: make release VERSION_TYPE=$version_type" + else + print_status "The release is ready for integration into the WordPress app." + fi +} + +# Run main function with all arguments +main "$@" diff --git a/docs/releases.md b/docs/releases.md new file mode 100644 index 000000000..dd82f1068 --- /dev/null +++ b/docs/releases.md @@ -0,0 +1,43 @@ +# GutenbergKit Release Process + +Use the provided release script to automate the entire process: + +```bash +# Standard version increments +make release VERSION_TYPE=patch # 0.3.0 -> 0.3.1 +make release VERSION_TYPE=minor # 0.3.0 -> 0.4.0 +make release VERSION_TYPE=major # 0.3.0 -> 1.0.0 + +# Custom version number +make release VERSION_TYPE=1.2.3 + +# Prerelease versions (using alpha identifier) +make release VERSION_TYPE=premajor # 0.3.0 -> 1.0.0-alpha.0 +make release VERSION_TYPE=preminor # 0.3.0 -> 0.4.0-alpha.0 +make release VERSION_TYPE=prepatch # 0.3.0 -> 0.3.1-alpha.0 +make release VERSION_TYPE=prerelease # 1.2.3-alpha.0 -> 1.2.3-alpha.1 + +# Use version from git tag +make release VERSION_TYPE=from-git + +# Test the release process without making changes +make release VERSION_TYPE=patch DRY_RUN=true +``` + +The script: + +1. Verifies you're on the `trunk` branch +1. Checks that your working directory is clean +1. Ensures required dependencies are installed +1. Increments the version number[^1] +1. Builds the project[^2] +1. Commits changes +1. Creates a Git tag +1. Pushes to `origin/trunk` with tags +1. Creates a GitHub release +1. Creates a new release on GitHub: `gh release create vX.X.X --generate-notes --title "X.X.X"` + +After the release is created, it is ready for integration into the WordPress app. + +[^1]: We increment the version before building and without tagging so that (1) the correct version number is included in the build's [error reporting metadata](https://github.com/wordpress-mobile/GutenbergKit/blob/8195901ec8883125dcfa102abf2b6a2a3962af3e/src/utils/exception-parser.js#L99) and (2) the Git tag includes the latest build output. +[^2]: CI tasks create new Android builds for each commit. However, such infrastructure is not yet in place for iOS. Therefore, we must manually create and commit the iOS build.