diff --git a/.github/skills/prepare-release/SKILL.md b/.github/skills/prepare-release/SKILL.md new file mode 100644 index 00000000..669de9ff --- /dev/null +++ b/.github/skills/prepare-release/SKILL.md @@ -0,0 +1,133 @@ +--- +name: prepare-release +description: Prepare a release for the Azure App Configuration JavaScript Provider. Use when user mentions release preparation, version bump, creating merge PRs, preview release, or stable release for this project. +--- + +# Prepare Release + +This skill automates the release preparation workflow for the [Azure App Configuration JavaScript Provider](https://github.com/Azure/AppConfiguration-JavaScriptProvider) project. + +## When to Use This Skill + +Use this skill when you need to: +- Bump the package version for a new stable or preview release +- Create merge PRs to sync branches (test-main → test-preview, test-main → test-release/stable, test-preview → test-release) +- Prepare all the PRs needed before publishing a new release +- Resolve merge conflicts between test-main and test-preview branches + +## Background + +### Repository Information +- **GitHub Repo**: https://github.com/Azure/AppConfiguration-JavaScriptProvider +- **Package Name**: `@azure/app-configuration-provider` + +### Branch Structure +- `test-main` – primary development branch for stable releases +- `test-preview` – development branch for preview releases +- `test-release/stable/v{major}` – release branch for stable versions (e.g., `test-release/stable/v2`) +- `test-release/v{major}` – release branch for preview versions (e.g., `test-release/v2`) + +### Version Files +The version must be updated in **all four locations** simultaneously: +1. `src/version.ts` – line 4: `export const VERSION = "";` +2. `package.json` – line 3: `"version": "",` +3. `package-lock.json` – line 3: `"version": "",` +4. `package-lock.json` – line 9: `"version": "",` + +### Version Format +- **Stable**: `{major}.{minor}.{patch}` (e.g., `2.4.0`) +- **Preview**: `{major}.{minor}.{patch}-preview` (e.g., `2.4.1-preview`) + +## Quick Start + +Ask the user whether this is a **stable** or **preview** release, and what the **new version number** should be. Then follow the appropriate workflow below. + +--- + +### Workflow A: Stable Release + +#### Step 1: Version Bump PR + +Create a version bump PR targeting `test-main` by running the version bump script: + +```bash +./scripts/version-bump.sh +``` + +For example: `./scripts/version-bump.sh 2.5.0` + +The script will automatically: +1. Read the current version from `src/version.ts`. +2. Create a new branch from `test-main` named `/version-` (e.g., `linglingye/version-2.5.0`). +3. Update the version in all four files (`src/version.ts`, `package.json`, `package-lock.json` lines 3 and 9). +4. Commit, push, and create a PR to `test-main` with title: `Version bump `. + +When the script prompts `Proceed? [y/N]`, confirm by entering `y`. + +#### Step 2: Merge Main to Release Branch + +After the version bump PR is merged, create a PR to merge `test-main` into the stable release branch by running: + +```bash +./scripts/merge-to-release.sh +``` + +For example: `./scripts/merge-to-release.sh 2.5.0` + +When the script prompts `Proceed? [y/N]`, confirm by entering `y`. + +> **Important**: Use "Merge commit" (not squash) when merging this PR to preserve commit history. + +--- + +### Workflow B: Preview Release + +#### Step 1: Merge Main to Preview (Conflict Resolution) + +Create a PR to merge `test-main` into `test-preview`. This will likely have conflicts. + +1. Fetch the latest `test-main` and `test-preview` branches. +2. Create a new branch from `test-preview` named `/resolve-conflict` (or similar). +3. Merge `test-main` into this branch. If there are conflicts, inform the user and let them resolve manually. +4. Push the branch and create a PR targeting `test-preview` with title: `Merge test-main to test-preview`. + +> **Important**: Use "Merge commit" (not squash) when merging this PR. + +**Sample PR**: https://github.com/Azure/AppConfiguration-JavaScriptProvider/pull/272 + +#### Step 2: Version Bump PR + +After the merge-to-preview PR is merged, create a version bump PR targeting `test-preview` by running the version bump script with the `--preview` flag: + +```bash +./scripts/version-bump.sh --preview +``` + +For example: `./scripts/version-bump.sh 2.5.1-preview --preview` + +When the script prompts `Proceed? [y/N]`, confirm by entering `y`. + +#### Step 3: Merge Preview to Release Branch + +After the version bump PR is merged, create a PR to merge `test-preview` into the preview release branch by running: + +```bash +./scripts/merge-to-release.sh --preview +``` + +For example: `./scripts/merge-to-release.sh 2.5.1-preview --preview` + +When the script prompts `Proceed? [y/N]`, confirm by entering `y`. + +> **Important**: Use "Merge commit" (not squash) when merging this PR. + +--- + +## Review Checklist + +Each PR should be reviewed with the following checks: +- [ ] Version is updated consistently across all 3 files +- [ ] No unintended file changes are included +- [ ] Merge PRs use **merge commit** strategy (not squash) +- [ ] Branch names follow the naming conventions +- [ ] All CI checks pass diff --git a/scripts/merge-to-release.sh b/scripts/merge-to-release.sh new file mode 100644 index 00000000..4005718e --- /dev/null +++ b/scripts/merge-to-release.sh @@ -0,0 +1,140 @@ +#!/bin/bash + +# ============================================================================ +# merge-to-release.sh +# +# Creates a PR to merge a development branch into its corresponding release +# branch. Used after a version bump PR has been merged. +# +# Usage: +# ./scripts/merge-to-release.sh [--preview] +# +# Examples: +# ./scripts/merge-to-release.sh 2.5.0 # test-main → test-release/stable/v2 +# ./scripts/merge-to-release.sh 2.5.1-preview --preview # test-preview → test-release/v2 +# +# Prerequisites: +# - git and gh (GitHub CLI) must be installed and authenticated +# ============================================================================ + +set -euo pipefail + +# ── Helpers ────────────────────────────────────────────────────────────────── + +usage() { + cat < [--preview] + +Arguments: + version The version that was just bumped (used to determine major version) + --preview Merge test-preview → test-release/v{major} instead of + test-main → test-release/stable/v{major} + +Examples: + $(basename "$0") 2.5.0 # test-main → test-release/stable/v2 + $(basename "$0") 2.5.1-preview --preview # test-preview → test-release/v2 +EOF + exit 1 +} + +error() { + echo "ERROR: $1" >&2 + exit 1 +} + +info() { + echo "── $1" +} + +# ── Parse arguments ────────────────────────────────────────────────────────── + +VERSION="" +IS_PREVIEW=false + +while [[ $# -gt 0 ]]; do + case "$1" in + --preview) + IS_PREVIEW=true + shift + ;; + -h|--help) + usage + ;; + *) + if [[ -z "$VERSION" ]]; then + VERSION="$1" + else + error "Unexpected argument: $1" + fi + shift + ;; + esac +done + +[[ -z "$VERSION" ]] && usage + +# Validate version format +if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-preview)?$'; then + error "Invalid version format '$VERSION'. Expected: X.Y.Z or X.Y.Z-preview" +fi + +# ── Determine branches ────────────────────────────────────────────────────── + +# Extract major version (e.g. "2" from "2.5.0" or "2.5.1-preview") +MAJOR_VERSION=$(echo "$VERSION" | cut -d. -f1) + +if [[ "$IS_PREVIEW" == true ]]; then + SOURCE_BRANCH="test-preview" + TARGET_BRANCH="test-release/v${MAJOR_VERSION}" + PR_TITLE="Merge test-preview to test-release/v${MAJOR_VERSION}" +else + SOURCE_BRANCH="test-main" + TARGET_BRANCH="test-release/stable/v${MAJOR_VERSION}" + PR_TITLE="Merge test-main to test-release/stable/v${MAJOR_VERSION}" +fi + +info "Source branch : $SOURCE_BRANCH" +info "Target branch : $TARGET_BRANCH" +info "PR title : $PR_TITLE" +echo "" + +# ── Confirm with user ──────────────────────────────────────────────────────── + +read -rp "Proceed? [y/N] " confirm +if [[ ! "$confirm" =~ ^[Yy]$ ]]; then + echo "Aborted." + exit 0 +fi + +echo "" + +# ── Resolve project directory ──────────────────────────────────────────────── + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +cd "$PROJECT_DIR" + +# ── Fetch latest branches ─────────────────────────────────────────────────── + +info "Fetching latest branches..." +git fetch origin "$SOURCE_BRANCH" +git fetch origin "$TARGET_BRANCH" + +# ── Create PR ──────────────────────────────────────────────────────────────── + +info "Creating pull request..." +PR_URL=$(gh pr create \ + --base "$TARGET_BRANCH" \ + --head "$SOURCE_BRANCH" \ + --title "$PR_TITLE" \ + --body "Merge \`$SOURCE_BRANCH\` into \`$TARGET_BRANCH\` after version bump \`$VERSION\`. + +> **Important**: Use **Merge commit** (not squash) when merging this PR to preserve commit history. + +--- +*This PR was created automatically by \`scripts/merge-to-release.sh\`.*") + +echo "" +info "Done! PR created: $PR_URL" +echo "" +echo "⚠️ Remember: Use \"Merge commit\" (not squash) when merging this PR." diff --git a/scripts/version-bump.sh b/scripts/version-bump.sh new file mode 100644 index 00000000..f396060a --- /dev/null +++ b/scripts/version-bump.sh @@ -0,0 +1,214 @@ +#!/bin/bash + +# ============================================================================ +# version-bump.sh +# +# Automates the version bump workflow for @azure/app-configuration-provider. +# Updates version in all required files, creates a branch, commits, pushes, +# and opens a PR via the GitHub CLI (gh). +# +# Usage: +# ./scripts/version-bump.sh [--preview] +# +# Examples: +# ./scripts/version-bump.sh 2.5.0 # stable release → PR to test-main +# ./scripts/version-bump.sh 2.5.1-preview --preview # preview release → PR to test-preview +# +# Prerequisites: +# - git, sed, and gh (GitHub CLI) must be installed and authenticated +# ============================================================================ + +set -euo pipefail + +# ── Helpers ────────────────────────────────────────────────────────────────── + +usage() { + cat < [--preview] + +Arguments: + new_version The version to bump to (e.g. 2.5.0 or 2.5.1-preview) + --preview Target the test-preview branch instead of test-main + +Examples: + $(basename "$0") 2.5.0 # stable → PR to test-main + $(basename "$0") 2.5.1-preview --preview # preview → PR to test-preview +EOF + exit 1 +} + +error() { + echo "ERROR: $1" >&2 + exit 1 +} + +info() { + echo "── $1" +} + +# ── Parse arguments ────────────────────────────────────────────────────────── + +NEW_VERSION="" +IS_PREVIEW=false + +while [[ $# -gt 0 ]]; do + case "$1" in + --preview) + IS_PREVIEW=true + shift + ;; + -h|--help) + usage + ;; + *) + if [[ -z "$NEW_VERSION" ]]; then + NEW_VERSION="$1" + else + error "Unexpected argument: $1" + fi + shift + ;; + esac +done + +[[ -z "$NEW_VERSION" ]] && usage + +# Validate version format: major.minor.patch or major.minor.patch-preview +if ! echo "$NEW_VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-preview)?$'; then + error "Invalid version format '$NEW_VERSION'. Expected: X.Y.Z or X.Y.Z-preview" +fi + +# If version ends with -preview, ensure --preview flag is set +if echo "$NEW_VERSION" | grep -qE '\-preview$'; then + if [[ "$IS_PREVIEW" == false ]]; then + error "Version '$NEW_VERSION' looks like a preview version. Did you forget --preview?" + fi +fi + +# ── Resolve paths & context ───────────────────────────────────────────────── + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" + +VERSION_TS="$PROJECT_DIR/src/version.ts" +PACKAGE_JSON="$PROJECT_DIR/package.json" +PACKAGE_LOCK="$PROJECT_DIR/package-lock.json" + +# Determine target branch and branch prefix +if [[ "$IS_PREVIEW" == true ]]; then + TARGET_BRANCH="test-preview" +else + TARGET_BRANCH="test-main" +fi + +# Get git username for branch naming (e.g. "linglingye" from "linglingye/version-2.4.0") +GIT_USERNAME=$(git config user.name 2>/dev/null || echo "") +if [[ -z "$GIT_USERNAME" ]]; then + error "Could not determine git user.name. Please set it with: git config user.name " +fi +# Use the first token (lowercase, no spaces) as the branch prefix +BRANCH_PREFIX=$(echo "$GIT_USERNAME" | awk '{print $1}' | tr '[:upper:]' '[:lower:]') + +BRANCH_NAME="${BRANCH_PREFIX}/version-${NEW_VERSION}" + +# ── Show plan ───────────────────────────────────────────────────────────────── + +info "New version : $NEW_VERSION" +info "Target branch : $TARGET_BRANCH" +info "New branch : $BRANCH_NAME" +echo "" + +# ── Confirm with user ──────────────────────────────────────────────────────── + +read -rp "Proceed? [y/N] " confirm +if [[ ! "$confirm" =~ ^[Yy]$ ]]; then + echo "Aborted." + exit 0 +fi + +echo "" + +# ── Create branch from target ──────────────────────────────────────────────── + +cd "$PROJECT_DIR" + +info "Fetching latest $TARGET_BRANCH..." +git fetch origin "$TARGET_BRANCH" + +info "Creating branch '$BRANCH_NAME' from origin/$TARGET_BRANCH..." +git checkout -b "$BRANCH_NAME" "origin/$TARGET_BRANCH" + +# ── Read current version (after checkout so we read from the target branch) ── + +CURRENT_VERSION=$(grep -oP 'VERSION = "\K[^"]+' "$VERSION_TS") +info "Current version : $CURRENT_VERSION" + +if [[ "$CURRENT_VERSION" == "$NEW_VERSION" ]]; then + error "Current version is already $NEW_VERSION. Nothing to do." +fi + +# ── Update version in all files ────────────────────────────────────────────── + +info "Updating src/version.ts..." +sed -i "s/export const VERSION = \"$CURRENT_VERSION\"/export const VERSION = \"$NEW_VERSION\"/" "$VERSION_TS" + +info "Updating package.json..." +sed -i "0,/\"version\": \"$CURRENT_VERSION\"/s//\"version\": \"$NEW_VERSION\"/" "$PACKAGE_JSON" + +info "Updating package-lock.json (line 3)..." +# package-lock.json has the version on line 3 and line 9 — update both +sed -i "0,/\"version\": \"$CURRENT_VERSION\"/s//\"version\": \"$NEW_VERSION\"/" "$PACKAGE_LOCK" + +info "Updating package-lock.json (line 9)..." +sed -i "0,/\"version\": \"$CURRENT_VERSION\"/s//\"version\": \"$NEW_VERSION\"/" "$PACKAGE_LOCK" + +# ── Verify changes ────────────────────────────────────────────────────────── + +info "Verifying updates..." + +verify_version() { + local file="$1" + local expected="$2" + if ! grep -q "\"$expected\"" "$file" 2>/dev/null && ! grep -q "\"$expected\"" "$file" 2>/dev/null; then + error "Version not found in $file after update. Please check manually." + fi +} + +# Check src/version.ts specifically +if ! grep -q "export const VERSION = \"$NEW_VERSION\"" "$VERSION_TS"; then + error "Version not updated in src/version.ts" +fi +verify_version "$PACKAGE_JSON" "$NEW_VERSION" +verify_version "$PACKAGE_LOCK" "$NEW_VERSION" + +info "All version files updated ✓" +echo "" + +# ── Commit, push, and create PR ───────────────────────────────────────────── + +COMMIT_MSG="version bump $NEW_VERSION" + +info "Committing changes..." +git add "$VERSION_TS" "$PACKAGE_JSON" "$PACKAGE_LOCK" +git commit -m "$COMMIT_MSG" + +info "Pushing branch '$BRANCH_NAME'..." +git push origin "$BRANCH_NAME" + +info "Creating pull request..." +PR_URL=$(gh pr create \ + --base "$TARGET_BRANCH" \ + --head "$BRANCH_NAME" \ + --title "Version bump $NEW_VERSION" \ + --body "Bump version from \`$CURRENT_VERSION\` to \`$NEW_VERSION\`. + +### Changes +- \`src/version.ts\` – updated VERSION constant +- \`package.json\` – updated version field +- \`package-lock.json\` – updated version fields (lines 3 and 9) + +--- +*This PR was created automatically by \`scripts/version-bump.sh\`.*") + +echo "" +info "Done! PR created: $PR_URL"