Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"build": "printf $'\\x1b[K\\x1b[37;41mPlease run this script from the \\x1b[1;4mpackages/eui\\x1b[0m\\x1b[37;41m directory instead\\x1b[0m\\n'; exit 1",
"watch": "node scripts/watch-eui.js",
"release": "node scripts/release",
"release:prep": "bash scripts/release-prep.sh",
"release:publish": "bash scripts/release-publish.sh",
"clean": "node scripts/clean.mjs"
},
"repository": {
Expand Down
132 changes: 132 additions & 0 deletions scripts/release-prep.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#!/usr/bin/env bash
#
# Official release preparation script (pre-PR)
#
# Automates steps 1-8 of the official release process:
# 1. Log out of npm
# 2. Checkout main
# 3. Pull latest from upstream
# 4. Create a timestamped release branch
# 5. Build the release CLI
# 6. Run the release dry-run (interactive)
# 7. (User confirms in the interactive CLI)
# 8. Push the branch to origin and open a PR
#
# Usage: yarn release:prep
#

set -euo pipefail

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BOLD='\033[1m'
RESET='\033[0m'

step() {
echo ""
echo -e "${GREEN}${BOLD}[$1]${RESET} $2"
}

warn() {
echo -e "${YELLOW}Warning:${RESET} $1"
}

error() {
echo -e "${RED}Error:${RESET} $1" >&2
exit 1
}

REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || error "Not inside a git repository"
cd "$REPO_ROOT"

# Verify the upstream remote exists
git remote get-url upstream &>/dev/null || error "'upstream' remote not found. Please add it: git remote add upstream git@github.com:elastic/eui.git"

# ── Step 1: Log out of npm ──────────────────────────────────────────────────

step "1/8" "Ensuring npm is not authenticated..."
npm logout 2>/dev/null || true
yarn npm logout 2>/dev/null || true

# ── Step 2: Checkout main ───────────────────────────────────────────────────

step "2/8" "Checking out main branch..."
git checkout main

# ── Step 3: Pull latest ────────────────────────────────────────────────────

step "3/8" "Pulling latest changes from upstream..."
git pull upstream main

# ── Step 4: Create release branch ───────────────────────────────────────────

BRANCH_NAME="release/$(date +%s)"
step "4/8" "Creating release branch: ${BOLD}${BRANCH_NAME}${RESET}"
git checkout -b "$BRANCH_NAME"

# ── Step 5: Build release CLI ───────────────────────────────────────────────

step "5/8" "Installing dependencies and building release CLI..."
yarn
yarn workspace @elastic/eui-release-cli run build

# ── Step 6: Run release (dry-run) ───────────────────────────────────────────

step "6/8" "Starting release process (dry-run)..."
echo ""
yarn release run official --dry-run --allow-custom --skip-auth-check --use-auth-token

# ── Step 7: Push branch ────────────────────────────────────────────────────

step "7/8" "Pushing branch to origin..."
git push -u origin "$BRANCH_NAME"

# ── Step 8: Open PR ────────────────────────────────────────────────────────

step "8/8" "Opening release PR..."

# Detect changed packages by comparing versions on this branch vs main
PR_TITLE_PARTS=""
PR_BODY_LINES=""

for pkg_dir in packages/eui packages/eui-theme-common packages/eui-theme-borealis packages/docusaurus-preset packages/docusaurus-theme packages/eslint-plugin; do
pkg_json="${pkg_dir}/package.json"
[[ -f "$pkg_json" ]] || continue

new_version=$(node -p "require('./${pkg_json}').version")
old_version=$(git show "main:${pkg_json}" 2>/dev/null | node -p "JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')).version" 2>/dev/null || echo "")

if [[ -n "$old_version" && "$new_version" != "$old_version" ]]; then
pkg_name=$(node -p "require('./${pkg_json}').name")

if [[ -n "$PR_TITLE_PARTS" ]]; then
PR_TITLE_PARTS="${PR_TITLE_PARTS}, ${pkg_name} v${new_version}"
else
PR_TITLE_PARTS="${pkg_name} v${new_version}"
fi
PR_BODY_LINES="${PR_BODY_LINES}\n- \`${pkg_name}\` - v${old_version} → v${new_version}"
fi
done

if [[ -z "$PR_TITLE_PARTS" ]]; then
error "No changed package versions detected. Did the release dry-run update any versions?"
fi

PR_TITLE="Release: ${PR_TITLE_PARTS}"
PR_BODY="$(printf "Packages to release:\n${PR_BODY_LINES}")"

PR_URL=$(gh pr create \
--title "$PR_TITLE" \
--body "$PR_BODY" \
--label "skip-changelog" \
--label "release" \
--base main)

echo ""
echo -e "${GREEN}${BOLD}Prep complete!${RESET}"
echo ""
echo -e " PR: ${BOLD}${PR_URL}${RESET}"
echo ""
echo -e " ${BOLD}After the PR is merged:${RESET}"
echo -e " yarn release:publish"
129 changes: 129 additions & 0 deletions scripts/release-publish.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#!/usr/bin/env bash
#
# Official release publish script (post-merge)
#
# Automates steps 10-11 of the official release process:
# - Detects the EUI version and changed workspaces
# - Tags the merge commit
# - Pushes the tag to upstream
# - Triggers the GitHub Actions release workflow
#
# Usage: yarn release:publish [merge-commit-sha]
#
# If no SHA is provided, defaults to HEAD on main.
#

set -euo pipefail

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BOLD='\033[1m'
RESET='\033[0m'

step() {
echo ""
echo -e "${GREEN}${BOLD}[$1]${RESET} $2"
}

warn() {
echo -e "${YELLOW}Warning:${RESET} $1"
}

error() {
echo -e "${RED}Error:${RESET} $1" >&2
exit 1
}

REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || error "Not inside a git repository"
cd "$REPO_ROOT"

git remote get-url upstream &>/dev/null || error "'upstream' remote not found"

# ── Ensure we're on main and up to date ──────────────────────────────────────

step "1/5" "Updating main branch..."
git checkout main
git pull upstream main

# ── Determine the merge commit SHA ───────────────────────────────────────────

MERGE_SHA="${1:-$(git rev-parse HEAD)}"

step "2/5" "Detecting release details from ${BOLD}${MERGE_SHA:0:12}${RESET}..."

# Read the EUI version at the target commit
EUI_VERSION=$(git show "${MERGE_SHA}:packages/eui/package.json" | node -p "JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')).version")
TAG_NAME="v${EUI_VERSION}"

# Check if this tag already exists
if git rev-parse "$TAG_NAME" &>/dev/null; then
error "Tag ${TAG_NAME} already exists. Has this release already been published?"
fi

# ── Detect changed workspaces ────────────────────────────────────────────────

# Find the most recent existing release tag to compare against
PREV_TAG=$(git describe --tags --abbrev=0 "${MERGE_SHA}^" 2>/dev/null) || error "Could not find a previous release tag"

# Compare package.json versions between previous tag and release commit
# to determine which public packages changed
CHANGED_WORKSPACES=""
for pkg_dir in packages/eui packages/eui-theme-common packages/eui-theme-borealis packages/docusaurus-preset packages/docusaurus-theme packages/eslint-plugin; do
pkg_json="${pkg_dir}/package.json"

# Skip if package.json doesn't exist at the merge commit
git show "${MERGE_SHA}:${pkg_json}" &>/dev/null || continue

new_version=$(git show "${MERGE_SHA}:${pkg_json}" | node -p "JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')).version")
old_version=$(git show "${PREV_TAG}:${pkg_json}" 2>/dev/null | node -p "JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')).version" 2>/dev/null || echo "")

if [[ "$new_version" != "$old_version" ]]; then
pkg_name=$(git show "${MERGE_SHA}:${pkg_json}" | node -p "JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')).name")
if [[ -n "$CHANGED_WORKSPACES" ]]; then
CHANGED_WORKSPACES="${CHANGED_WORKSPACES},${pkg_name}"
else
CHANGED_WORKSPACES="${pkg_name}"
fi
fi
done

if [[ -z "$CHANGED_WORKSPACES" ]]; then
error "No changed workspaces detected. Are you sure the release PR was merged?"
fi

# ── Summary & confirmation ──────────────────────────────────────────────────

step "3/5" "Release summary"
echo ""
echo -e " Tag: ${BOLD}${TAG_NAME}${RESET}"
echo -e " Commit: ${BOLD}${MERGE_SHA:0:12}${RESET}"
echo -e " Previous tag: ${BOLD}${PREV_TAG}${RESET}"
echo -e " Workspaces: ${BOLD}${CHANGED_WORKSPACES}${RESET}"
echo ""
read -r -p "Proceed with tagging and triggering the release? (y/N) " confirm
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
echo "Aborted."
exit 0
fi

# ── Tag and push ────────────────────────────────────────────────────────────

step "4/5" "Creating and pushing tag ${BOLD}${TAG_NAME}${RESET}..."
git tag -a "$TAG_NAME" "$MERGE_SHA" -m "@elastic/eui ${TAG_NAME}"
git push upstream "$TAG_NAME" --no-verify

# ── Trigger release workflow ────────────────────────────────────────────────

step "5/5" "Triggering release workflow..."
gh workflow run release.yml \
--repo elastic/eui \
-f release_ref="$MERGE_SHA" \
-f type=official \
-f workspaces="$CHANGED_WORKSPACES" \
-f dry_run=false

echo ""
echo -e "${GREEN}${BOLD}Release triggered!${RESET}"
echo ""
echo -e " Monitor: ${BOLD}https://github.com/elastic/eui/actions/workflows/release.yml${RESET}"