#!/usr/bin/env bash # # Release a new version to the GitLab flow production branch. # # For a new major/minor version, bump version on the main branch, and then merge into the production branch. # # For a patch version, bump the version number on the patch branch, then merge that branch into the main branch # and production branch. # # # Usage: run on main branch or the patch branch # # Requires: Git, NPM and RubyGems set -eu opt_pre=false # preview mode option working_branch="$(git branch --show-current)" # AKA the default branch, main/master branch STAGING_BRANCH="$(git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@')" PROD_BRANCH="production" GEM_SPEC="jekyll-theme-chirpy.gemspec" NODE_CONFIG="package.json" JS_DIST="assets/js/dist" BACKUP_PATH="$(mktemp -d)" FILES=( "$GEM_SPEC" "$NODE_CONFIG" ) TOOLS=( "git" "npm" "standard-version" "gem" ) help() { echo "A tool to release new version Chirpy gem" echo echo "Usage:" echo echo " bash ./tools/release [options]" echo echo "Options:" echo " -p, --preview Enable preview mode, only package, and will not modify the branches" echo " -h, --help Print this information." } _check_cli() { for i in "${!TOOLS[@]}"; do cli="${TOOLS[$i]}" if ! command -v "$cli" &>/dev/null; then echo "> Command '$cli' not found!" exit 1 fi done } _check_git() { # ensure nothing is uncommitted if [[ -n $(git status . -s) ]]; then echo "> Abort: Commit the staged files first, and then run this tool again." exit 1 fi # ensure the working branch is the default/patch branch if [[ $working_branch != "$STAGING_BRANCH" && $working_branch != hotfix/* ]]; then echo "> Abort: Please run on the $STAGING_BRANCH branch or a patch branche." exit 1 fi } _check_src() { for i in "${!FILES[@]}"; do _src="${FILES[$i]}" if [[ ! -f $_src && ! -d $_src ]]; then echo -e "> Error: Missing file \"$_src\"!\n" exit 1 fi done } _check_node_packages() { if [[ ! -d node_modules || "$(du node_modules | awk '{print $1}')" == "0" ]]; then npm i fi } check() { _check_cli _check_git _check_src _check_node_packages } # auto-generate a new version number to the file 'package.json' and bump_node() { if $opt_pre; then standard-version --prerelease rc else standard-version fi # Change heading of Patch version to heading level 2 (a bug from `standard-version`) sed -i "s/^### \[/## \[/g" CHANGELOG.md # Replace multiple empty lines with a single empty line sed -i "/^$/N;/^\n$/D" CHANGELOG.md } ## Bump new version to gem config file bump_gem() { sed -i "s/[[:digit:]]\+\.[[:digit:]]\+\.[[:digit:]]\+/$1/" "$GEM_SPEC" } # Update the git branches, create a new tag, and then build the gem package. release() { _version="$1" # X.Y.Z _latest_commit="$(git rev-parse HEAD)" # Create a new tag on production branch echo -e "> Create tag v$_version\n" git tag "v$_version" git checkout "$STAGING_BRANCH" if [[ $working_branch == hotfix/* ]]; then git merge --no-ff --no-edit "$working_branch" # delete the patch branch git branch -D "$working_branch" fi # cherry-pick the latest commit from production branch to default branch git cherry-pick "$_latest_commit" } ## Build a gem package build_gem() { # Remove unnecessary theme settings sed -i "s/^img_cdn:.*/img_cdn:/;s/^avatar:.*/avatar:/" _config.yml rm -f ./*.gem npm run build git add "$JS_DIST" -f # add JS dist to gem gem build "$GEM_SPEC" cp "$JS_DIST"/* "$BACKUP_PATH" # Resume the settings git reset git checkout . # restore the dist files for future development mkdir -p "$JS_DIST" && cp "$BACKUP_PATH"/* "$JS_DIST" } main() { check if [[ $opt_pre = false ]]; then git checkout "$PROD_BRANCH" git merge --no-ff --no-edit "$working_branch" fi bump_node _version="$(grep '"version":' "$NODE_CONFIG" | sed 's/.*: "//;s/".*//')" bump_gem "$_version" echo -e "> Build the gem package for v$_version\n" if [[ $opt_pre = false ]]; then echo -e "> Bumped version number to $_version\n" git add . git commit -m "chore(release): $_version" release "$_version" fi build_gem } while (($#)); do opt="$1" case $opt in -p | --preview) opt_pre=true shift ;; -h | --help) help exit 0 ;; *) # unknown option help exit 1 ;; esac done main