From 9f0efb2a3340d50f7b16addf052997047552bcbc Mon Sep 17 00:00:00 2001 From: Marko Justinek <1733327+surpher@users.noreply.github.com> Date: Mon, 9 Dec 2024 17:29:40 +1100 Subject: [PATCH] Major refactor of Support scripts --- .../Scripts/{ => CI}/build_rust_dependencies | 4 +- Support/Scripts/CI/build_xcframework | 78 ++-- Support/Scripts/CI/bump_version_number | 76 ---- Support/Scripts/CI/release | 273 -------------- Support/Scripts/CI/version_numbers | 99 ++++++ .../configure_rust_tools.sh} | 5 - .../Scripts/{ => Config}/prepare_build_tools | 4 +- Support/Scripts/release | 333 ++++++++++++++++++ Support/Scripts/{build_test => test} | 0 Support/Scripts/utils.sh | 4 +- TAG_MESSAGE_FILE.md | 1 + 11 files changed, 475 insertions(+), 402 deletions(-) rename Support/Scripts/{ => CI}/build_rust_dependencies (98%) delete mode 100644 Support/Scripts/CI/bump_version_number delete mode 100755 Support/Scripts/CI/release create mode 100644 Support/Scripts/CI/version_numbers rename Support/Scripts/{configure_rust_tools => Config/configure_rust_tools.sh} (97%) rename Support/Scripts/{ => Config}/prepare_build_tools (95%) create mode 100755 Support/Scripts/release rename Support/Scripts/{build_test => test} (100%) create mode 100644 TAG_MESSAGE_FILE.md diff --git a/Support/Scripts/build_rust_dependencies b/Support/Scripts/CI/build_rust_dependencies similarity index 98% rename from Support/Scripts/build_rust_dependencies rename to Support/Scripts/CI/build_rust_dependencies index 7a67646..e04b90c 100755 --- a/Support/Scripts/build_rust_dependencies +++ b/Support/Scripts/CI/build_rust_dependencies @@ -34,8 +34,8 @@ LIBPACT_FFI_DIR="${WORKSPACE}/pact-reference/rust/pact_ffi" LIBPACT_FFI_VERSION=$(awk 'NR==1 {print; exit}' libpact_ffi.version) # "import" -source "$SOURCE_DIR/utils.sh" -source "$SOURCE_DIR/configure_rust_tools" +source "$SOURCE_DIR/../utils.sh" +source "$SOURCE_DIR/../Config/configure_rust_tools.sh" ####################### # Pre-requisite # diff --git a/Support/Scripts/CI/build_xcframework b/Support/Scripts/CI/build_xcframework index 309cdf7..bf03877 100755 --- a/Support/Scripts/CI/build_xcframework +++ b/Support/Scripts/CI/build_xcframework @@ -1,4 +1,5 @@ #!/usr/bin/env bash +# shellcheck disable=SC1091 # PactSwiftMockService # @@ -26,8 +27,11 @@ set -o pipefail IPHONEOS_DEPLOYMENT_TARGET=16.0 MACOSX_DEPLOYMENT_TARGET=13.0 -TMP_DIR="../../.tmp/build-xcframework" +BUILD_XCFRAMEWORK_SOURCE_DIR="${BASH_SOURCE[0]%/*}" + +TMP_DIR="${BUILD_XCFRAMEWORK_SOURCE_DIR}/../../../.tmp/build-xcframework" PRODUCT_NAME="PactSwiftMockServer" +source "${BUILD_XCFRAMEWORK_SOURCE_DIR}/../utils.sh" # Only use xcbeautify if it can be found in path. XCBEAUTIFY=$(command -v xcbeautify || command -v cat) @@ -40,78 +44,70 @@ XCBEAUTIFY=$(command -v xcbeautify || command -v cat) # Otherwise a _concurrency fatal issue will be raised if PactSwiftMockSerer is # used in a project built with Xcode 12.x -echo "โš ๏ธ Checking for the right Xcode tools..." -xcode-select -p && xcode-select -v - -echo "๐Ÿšจ Are you running this using Xcode 16.x tools!?" -select yn in "Yes" "No"; do - case $yn in - Yes) break;; - No) exit 1;; - esac -done +check_xcode # Setup - -echo "โ„น๏ธ Looking for ${PRODUCT_NAME}.xcodeproj" -if [ ! -d "${PRODUCT_NAME}.xcodeproj" ]; then - echo "๐Ÿšจ Run this in the same folder as \"${PRODUCT_NAME}.xcodeproj\"." +echo "โ„น๏ธ Looking for $PRODUCT_NAME.xcodeproj" +if [ ! -d "$PRODUCT_NAME.xcodeproj" ]; then + echo "๐Ÿšจ Run this in the same folder as \"$PRODUCT_NAME.xcodeproj\"!" + echo "Your current location is: $(pwd)" exit 1 fi -echo "โ„น๏ธ Removing existing XCFramework" -rm -fr "./${PRODUCT_NAME}.xcframework" -echo "๐Ÿ‘ Removed existing XCFramework" +echo "โ„น๏ธ Removing deprecated XCFramework..." +rm -fr "./$PRODUCT_NAME.xcframework" +echo "๐Ÿ‘ Removed deprecated XCFramework" -echo "โ„น๏ธ Preparing ${TMP_DIR} folder" -mkdir -p $TMP_DIR -rm -fr $TMP_DIR +echo "โ„น๏ธ Setting up $TMP_DIR folder" +rm -fr "$TMP_DIR" +mkdir -p "$TMP_DIR" # iOS echo "๐Ÿ— Building for iOS..." xcodebuild archive \ - -sdk iphoneos IPHONEOS_DEPLOYMENT_TARGET=${IPHONEOS_DEPLOYMENT_TARGET} \ + -sdk iphoneos IPHONEOS_DEPLOYMENT_TARGET=$IPHONEOS_DEPLOYMENT_TARGET \ -arch arm64 \ - -scheme "${PRODUCT_NAME}-iphoneos" \ - -archivePath "${TMP_DIR}/iphoneos/${PRODUCT_NAME}.xcarchive" \ + -scheme "$PRODUCT_NAME-iphoneos" \ + -archivePath "$TMP_DIR/iphoneos/$PRODUCT_NAME.xcarchive" \ BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ - SKIP_INSTALL=NO | ${XCBEAUTIFY} - -echo "๐Ÿ‘ Framework for arm64 device built" + SKIP_INSTALL=NO | $XCBEAUTIFY +echo "๐Ÿ‘ Framework for physical arm64 device built" echo "๐Ÿ— Building for iOS Simulator..." xcodebuild archive \ - -sdk iphonesimulator IPHONEOS_DEPLOYMENT_TARGET=${IPHONEOS_DEPLOYMENT_TARGET} \ - -arch x86_64 -arch arm64 \ - -scheme "${PRODUCT_NAME}-iOS" \ - -archivePath "${TMP_DIR}/iphonesimulator/${PRODUCT_NAME}.xcarchive" \ + -sdk iphonesimulator IPHONEOS_DEPLOYMENT_TARGET=$IPHONEOS_DEPLOYMENT_TARGET \ + -arch x86_64 \ + -arch arm64 \ + -scheme "$PRODUCT_NAME-iOS" \ + -archivePath "$TMP_DIR/iphonesimulator/$PRODUCT_NAME.xcarchive" \ BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ - SKIP_INSTALL=NO | ${XCBEAUTIFY} + SKIP_INSTALL=NO | $XCBEAUTIFY echo "๐Ÿ‘ Framework for iOS Simulator built for x86_64 and arm64 architecture" # macOS echo "๐Ÿ— Building for macOS..." xcodebuild archive \ - -sdk macosx MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} \ - -arch x86_64 -arch arm64 \ - -scheme "${PRODUCT_NAME}-macOS" \ - -archivePath "${TMP_DIR}/macos/${PRODUCT_NAME}.xcarchive" \ + -sdk macosx MACOSX_DEPLOYMENT_TARGET=$MACOSX_DEPLOYMENT_TARGET \ + -arch x86_64 \ + -arch arm64 \ + -scheme "$PRODUCT_NAME-macOS" \ + -archivePath "$TMP_DIR/macos/$PRODUCT_NAME.xcarchive" \ BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ - SKIP_INSTALL=NO | ${XCBEAUTIFY} + SKIP_INSTALL=NO | $XCBEAUTIFY echo "๐Ÿ‘ Framework for macOS built for x86_64 and arm64 architectures" # XCFramework echo "๐Ÿ— Building XCFramework..." xcodebuild -create-xcframework -output ./$PRODUCT_NAME.xcframework \ - -framework $TMP_DIR/iphoneos/$PRODUCT_NAME.xcarchive/Products/Library/Frameworks/$PRODUCT_NAME.framework \ - -framework $TMP_DIR/iphonesimulator/$PRODUCT_NAME.xcarchive/Products/Library/Frameworks/$PRODUCT_NAME.framework \ - -framework $TMP_DIR/macos/$PRODUCT_NAME.xcarchive/Products/Library/Frameworks/$PRODUCT_NAME.framework + -framework "$TMP_DIR"/iphoneos/$PRODUCT_NAME.xcarchive/Products/Library/Frameworks/"$PRODUCT_NAME".framework \ + -framework "$TMP_DIR"/iphonesimulator/$PRODUCT_NAME.xcarchive/Products/Library/Frameworks/"$PRODUCT_NAME".framework \ + -framework "$TMP_DIR"/macos/$PRODUCT_NAME.xcarchive/Products/Library/Frameworks/"$PRODUCT_NAME".framework # Cleanup echo "โ„น๏ธ Removing $TMP_DIR folder..." -rm -fr $TMP_DIR +rm -fr "$TMP_DIR" echo "๐ŸŽ‰ Done!" diff --git a/Support/Scripts/CI/bump_version_number b/Support/Scripts/CI/bump_version_number deleted file mode 100644 index 141ccab..0000000 --- a/Support/Scripts/CI/bump_version_number +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env bash - -# PactSwiftMockService -# -# Created by Marko Justinek on 09/12/24. -# Copyright ยฉ 2024 Marko Justinek. All rights reserved. -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY -# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR -# IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -# Check if a version part is provided -if [[ -z "$1" ]]; then - echo "Usage: $0 {major|minor|patch}" - exit 1 -fi - -# Get the latest tag from the repository -VERSION=$(git describe --abbrev=0 --tags 2>/dev/null) - -# Default to v0.0.0 if no tags are found -if [ -z "$VERSION" ]; then - VERSION="v0.0.0" -fi - -# Remove 'v' prefix for easier manipulation -VERSION=${VERSION#v} - -# Split the version into major, minor, and patch components -IFS='.' read -r VNUM1 VNUM2 VNUM3 <<< "$VERSION" - -# Determine which part of the version to increment based on the argument -case $1 in - major) - echo "โ„น๏ธ Updating major version" - VNUM1=$((VNUM1 + 1)) - VNUM2=0 - VNUM3=0 - ;; - minor) - echo "โ„น๏ธ Updating minor version" - VNUM2=$((VNUM2 + 1)) - VNUM3=0 - ;; - patch) - echo "โ„น๏ธ Updating patch version" - VNUM3=$((VNUM3 + 1)) - ;; - *) - echo "Invalid argument: $1. Use 'major', 'minor', or 'patch'." - exit 1 - ;; -esac - -# Construct the new tag -NEW_TAG="v$VNUM1.$VNUM2.$VNUM3" - -# Check if the current commit already has a tag -GIT_COMMIT=$(git rev-parse HEAD) -NEEDS_TAG=$(git describe --contains "$GIT_COMMIT" 2>/dev/null) - -# Only create a new tag if it doesn't already exist for this commit -if [ -z "$NEEDS_TAG" ]; then - echo "๐Ÿท๏ธ Tagging with $NEW_TAG" - git tag "$NEW_TAG" -else - echo "Current commit already has a tag: $NEEDS_TAG" -fi diff --git a/Support/Scripts/CI/release b/Support/Scripts/CI/release deleted file mode 100755 index 979c3a1..0000000 --- a/Support/Scripts/CI/release +++ /dev/null @@ -1,273 +0,0 @@ -#!/usr/bin/env bash -# shellcheck disable=SC1091 - -# PactSwiftMockService -# -# Created by Marko Justinek on 19/8/21. -# Copyright ยฉ 2021 Marko Justinek. All rights reserved. -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY -# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR -# IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# -# release -# -# Runs the required steps to prepare and tag new version of PactSwift. -# Usage: `./Scripts/release 1.0.0 'Bugfix Release' [-d]` -# -# Before you run this project make sure you commit the changes of the following scripts: -# 1. build_rust_dependencies -# 2. build_xcframework -# -# Notes: -# - Updates PactSwiftMockServer version number in PactSwiftVersion.swift file -# - Updates Marketing version number in Project-Shared.xcconfig file -# - Updates CHANGELOG.md -# - Commits the updates on a new 'rc/__TAG__' branch -# - Creates a tag on the last commit -# - Pushes the updates and tags to 'surpher/PactSwiftServer' repo -# -# ๐Ÿšจ๐Ÿšจ๐Ÿšจ WARNING ๐Ÿšจ๐Ÿšจ๐Ÿšจ -# This is still a fragile script... because I just can't find decent time to DRY it up and set it up correctly. -# If you end up editing it, just be ready for a world of pain. Or not. Either way, you have been warned. -# - -# set -x -set -o pipefail - -# "import" -source "$SOURCE_DIR/../utils.sh" - -#################### -# Utilities # -#################### - -function help { - echo "Usage: release VERSION RELEASE_NAME [DRY_RUN]" - echo - echo "VERSION should be the version to release and should not include the 'v' prefix" - echo "RELEASE_NAME should be the type of release 'Bugfix Release / Maintenance Release'" - echo - echo "FLAGS" - echo " -d Dry run, won't push the changes" - echo - echo " Example: ./Support/release 1.0.0 'Bugfix Release' -d" - echo - exit 2 -} - -function die { - echo "๐Ÿšจ [ERROR] $*" - echo - exit 1 -} - -function handleInput { - if [[ ! $1 =~ ^[Yy]$ ]] - then - echo "โš ๏ธ Release aborted!" - exit 1 - fi -} - -############################## -# Overridable Environment # -############################## - -if [[ "$CI" == true ]] ; then - die "๐Ÿšจ Running on CI is not supported! Requires user input while running the release script." -else - echo "๐Ÿ‘ฎโ€โ™€๏ธ Running on local machine..." - SCRIPTS_DIR="${BASH_SOURCE[0]%/*}" -fi - -############################## -# Pre-release checks # -############################## - -# Get the latest release tag -LATEST_TAG=$(git describe --match "v[0-9].*" --abbrev=0 HEAD --tags) - -read -r -p "Have you ran build_rust_dependencies and build_xcframework scripts? And did you commit the newly built XCFrameworks? [Y/n] " -n 1 USER_INPUT -handleInput "$USER_INPUT" -echo - -echo "โ„น๏ธ The last release tag number is: ${LATEST_TAG}" - -read -r -p "Did you provide the correctly incremented number for the new release tag? [Y/n] " -n 1 USER_INPUT -handleInput "$USER_INPUT" -echo - -read -r -p "Have you updated all the documentation files? [Y/n] " -n 1 USER_INPUT -handleInput "$USER_INPUT" -echo - -echo "โ„น๏ธ The following commits will be recorded with the tag:" -git log --pretty='* %h - %s (%an)' "${LATEST_TAG}"..HEAD -echo - -read -r -p "Have you prepared a draft release on GitHub.com? [Y/n] " -n 1 USER_INPUT -echo -handleInput "$USER_INPUT" - -read -r -p "You named the release name with something meaningful like \"Bugfix\" or \"Feature \". Right? [Y/n] " -n 1 USER_INPUT -echo -handleInput "$USER_INPUT" - -#################### -# Variables # -#################### - -CONFIGURATION_FILE="${SCRIPTS_DIR}/../Configurations/Project-Shared.xcconfig" -RELEASE_NOTES="${SCRIPTS_DIR}/../CHANGELOG.md" -# REMOTE_BRANCH=${REMOTE_BRANCH:-"main"} -VERSION=$1 -RELEASE_NAME=$2 -DRY_RUN=$3 -VERSION_TAG="v$VERSION" - -#################### -# Setup # -#################### - -if [ $# -lt 2 ]; then - help -fi - -function update_version_file { - MARKETING_VERSION="MARKETING_VERSION = $*" - sed -i '' "2s/.*/$MARKETING_VERSION/" "$CONFIGURATION_FILE" -} - -function create_and_push_new_version { - local REMOTE_NAME="$1" - local REMOTE_BRANCH="$2" - - update_version_file "$VERSION" - - echo "${RELEASE_NAME}" > TAG_MESSAGE_FILE.md - local GIT_LOG="git log --pretty='* %h - %s (%an)' ${LATEST_TAG}..HEAD >> TAG_MESSAGE_FILE.md" - local GIT_ADD="git add $CONFIGURATION_FILE $RELEASE_NOTES PactSwiftMockServer.xcframework" - local GIT_COMMIT="git commit -m \"$RELEASE_NAME\"" - local GIT_TAG="git tag $VERSION_TAG -F TAG_MESSAGE_FILE.md" - local GIT_PUSH="git push $REMOTE_NAME $REMOTE_BRANCH" - - if [ -z "$DRY_RUN" ]; then - eval "$GIT_LOG" - eval "$GIT_ADD" - eval "$GIT_COMMIT" - eval "$GIT_TAG" || die "Failed to tag current commit!" - eval "$GIT_PUSH" || die "Failed to push the release commit with tag '$VERSION_TAG' to '$REMOTE_NAME/$REMOTE_BRANCH'!" - else - echo "$TAG_FILE" - eval "$GIT_ADD" - eval "$GIT_COMMIT" - eval "$GIT_TAG" - eval "$GIT_PUSH" - fi -} - -function checkout_new_release_candidate_branch { - local REMOTE_NAME="$1" - local VERSION_TAG="$2" - local REMOTE_MAIN_BRANCH= - - # Check remote master/main - if check_remote_branch "$REMOTE_NAME" "master"; then - REMOTE_MAIN_BRANCH="master" - elif check_remote_branch "$REMOTE_NAME" "main"; then - REMOTE_MAIN_BRANCH="main" - else - echo -e "๐Ÿšจ Remote $RED$REMOTE_NAME$NOCOLOR' does not have 'master' or 'main' branch!" - fi - - echo "โ„น๏ธ - Checking out ${YELLOW}rc/$VERSION_TAG$NOCOLOR branch with '$REMOTE_NAME/$REMOTE_MAIN_BRANCH' as starting point..." - git checkout -b "rc/$VERSION_TAG" "$REMOTE_NAME/$REMOTE_MAIN_BRANCH" -} - -#################### -# Release flow # -#################### - -#################### -# Setup - -REMOTE_NAME="releases" -REPO_NAME="PactSwiftServer" -REMOTE_REPO_BASE="git@github.com:surpher" - -echo "๐Ÿ‘ฎโ€โ™€๏ธ Verifying if version tag is reasonable..." - -if ! echo "$VERSION_TAG" | grep -q "^vv"; then - die "Tag ($VERSION) is in an incorrect format. You should remove the 'v' prefix." -fi - -if ! echo "$VERSION_TAG" | grep -q -E "^v[0-9]+\.[0-9]+\.[0-9]+(-\w+(\.\d)?)?$"; then - die "Tag $VERSION is in an incorrect format. It should be in 'v{MAJOR}.{MINOR}.{PATCH}(-{PRERELEASE_NAME}.{PRERELEASE_VERSION})' form." -fi - -echo "๐Ÿ‘ฎโ€โ™€๏ธ Verifying version ($VERSION) is unique..." -if ! git describe --exact-match "$VERSION_TAG" > /dev/null 2>&1; then - die "Tag ($VERSION) already exists! Aborting." -else - echo "๐Ÿ‘ Tag is unique" -fi - -# Configure remote for release candidate and release version to push to -echo "๐ŸŒŽ Adding '$REMOTE_NAME' remote..." -git remote add "$REMOTE_NAME" "$REMOTE_REPO_BASE/$REPO_NAME" - -# Check out the RC branch to make changes on -echo -e "๐Ÿ–– Checking out RC branch ${YELLOW}rc/$VERSION_TAG$NOCOLOR..." -checkout_new_release_candidate_branch "$REMOTE_NAME/$REPO_NAME" "$VERSION_TAG" - -#################### -# Generate RC - -echo "๐Ÿ“ Generating release notes in $RELEASE_NOTES..." -# backup the existing CHANGELOG.md to CHANGELOG.backup -cp "$RELEASE_NOTES" "${RELEASE_NOTES}.backup" - -# Prepare the title for this release -echo "# ${VERSION} - ${RELEASE_NAME}" > "${RELEASE_NOTES}.next" - -# Get the commits from last contribution -git log --pretty='* %h - %s (%an)' "${LATEST_TAG}"..HEAD >> "${RELEASE_NOTES}.next" - -# Stage the updated CHANGELOG.md -git add "$RELEASE_NOTES" || { die "Failed to add $RELEASE_NOTES to INDEX"; } - -# Read the notes for this release and append them to the old CHANGELOG.md -< "$RELEASE_NOTES.next" tr ' ' _ | nl -while IFS= read -r i; do echo "${i%?}"; done < "$RELEASE_NOTES" - -# Building the XCFramework -echo "๐Ÿ—๏ธ Building the XCFramework..." -"${SCRIPTS_DIR}"/build_xcframework - -#################### -# Push RC changes - -echo "๐Ÿšข Releasing version $VERSION (tag: $VERSION_TAG)..." - -# if [ -z "$DRY_RUN" ]; then - create_and_push_new_version "$REMOTE_NAME" "$VERSION_TAG" -# else - # echo "-> Dry run completed." -# fi - -#################### -# Cleanup # -#################### - -rm "${RELEASE_NOTES}".next -rm "${RELEASE_NOTES}".backup -rm TAG_MESSAGE_FILE.md -rm -fr "*.xcframework" diff --git a/Support/Scripts/CI/version_numbers b/Support/Scripts/CI/version_numbers new file mode 100644 index 0000000..3ac91d1 --- /dev/null +++ b/Support/Scripts/CI/version_numbers @@ -0,0 +1,99 @@ +#!/usr/bin/env bash + +# PactSwiftMockService +# +# Created by Marko Justinek on 09/12/24. +# Copyright ยฉ 2024 Marko Justinek. All rights reserved. +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +# IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +function latest_tag { + git describe --tags "$(git rev-list --tags --max-count=1)" 2>/dev/null +} + +# Gets the next bumped version number +function generate_version_number { + # Get parameters + local version_part="${VERSION_PART:-$1}" + local description="${DESCRIPTION:-$2}" + + if [[ -z "$version_part" ]]; then + echo "Usage: $0 {major|minor|patch}" + exit 1 + fi + + # Get the latest tag from the repository + local version= + version=$(latest_tag) + + # Default to v0.0.0 if no tags are found + if [ -z "$version" ]; then + version="v0.0.0" + fi + + # Remove 'v' prefix for easier manipulation + version=${version#v} + + # Split the version into major, minor, and patch components + IFS='.' read -r vnum1 vnum2 vnum3 <<< "$version" + + # Determine which part of the version to increment based on the argument + case $version_part in + major) + vnum1=$((vnum1 + 1)) + vnum2=0 + vnum3=0 + ;; + minor) + vnum2=$((vnum2 + 1)) + vnum3=0 + ;; + patch) + vnum3=$((vnum3 + 1)) + ;; + *) + echo "error: Invalid argument: $version_part" + return 1 + ;; + esac + + # Construct the new tag + local new_tag="v$vnum1.$vnum2.$vnum3" + + if [ -n "$description" ]; then + new_tag="$new_tag - $description" + fi + # Check if the current commit already has a tag + local git_commit= + git_commit=$(git rev-parse HEAD) + local needs_tag= + needs_tag=$(git describe --contains "$git_commit" 2>/dev/null) + + if [ -z "$needs_tag" ]; then + echo "$new_tag" + return 0 + else + echo "Current commit already has a tag: $needs_tag" + return 1 + fi +} + +# Sets the tag on the current commit and pushes it +function bump_version_tag { + local remote="$1" + local version_tag="$2" + + echo "๐Ÿท๏ธ Tagging version $version_tag and pushing to $remote..." + git tag "$version_tag" + git push "$remote" --tags +} diff --git a/Support/Scripts/configure_rust_tools b/Support/Scripts/Config/configure_rust_tools.sh similarity index 97% rename from Support/Scripts/configure_rust_tools rename to Support/Scripts/Config/configure_rust_tools.sh index 612c537..c4140cd 100644 --- a/Support/Scripts/configure_rust_tools +++ b/Support/Scripts/Config/configure_rust_tools.sh @@ -20,11 +20,6 @@ set -o pipefail -SOURCE_DIR="${BASH_SOURCE[0]%/*}" - -# "import" -source "$SOURCE_DIR/utils.sh" - echo "โ„น๏ธ List installed apple triples" executeCommand "rustup target list | grep apple" diff --git a/Support/Scripts/prepare_build_tools b/Support/Scripts/Config/prepare_build_tools similarity index 95% rename from Support/Scripts/prepare_build_tools rename to Support/Scripts/Config/prepare_build_tools index 2f89ed9..2e199e5 100755 --- a/Support/Scripts/prepare_build_tools +++ b/Support/Scripts/Config/prepare_build_tools @@ -20,10 +20,8 @@ set -euo pipefail -SOURCE_DIR="${BASH_SOURCE[0]%/*}" - # "import" -source "$SOURCE_DIR/utils.sh" +sourc "${BASH_SOURCE[0]%/*}/../utils.sh" brew update diff --git a/Support/Scripts/release b/Support/Scripts/release new file mode 100755 index 0000000..5689822 --- /dev/null +++ b/Support/Scripts/release @@ -0,0 +1,333 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1091 + +# PactSwiftMockService +# +# Created by Marko Justinek on 19/8/21. +# Copyright ยฉ 2021 Marko Justinek. All rights reserved. +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +# IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# release +# +# Runs the required steps to prepare and tag new version of PactSwift. +# +# Before you run this project make sure you commit the changes of the following scripts: +# 1. build_rust_dependencies +# 2. build_xcframework +# +# Notes: +# - Updates PactSwiftMockServer version number in PactSwiftVersion.swift file +# - Updates Marketing version number in Project-Shared.xcconfig file +# - Updates CHANGELOG.md +# - Commits the updates on a new 'rc/__TAG__' branch +# - Creates a tag on the last commit +# - Pushes the updates and tags to 'surpher/PactSwiftServer' repo +# +# ๐Ÿšจ๐Ÿšจ๐Ÿšจ WARNING ๐Ÿšจ๐Ÿšจ๐Ÿšจ +# This is still a fragile script... because I just can't find decent time to DRY it up and set it up correctly. +# If you end up editing it, just be ready for a world of pain. Or not. Either way, you have been warned. +# + +set -eu +set -o pipefail + +function show_help { + echo "Usage: $0 -v {major|minor|patch} [-d description] [--dry-run]" + echo + echo "FLAGS" + echo " -v, --version Version number to bump. Possible values are major, minor or patch." + echo " -d, --description Release description." + echo " --dry-run Dry run won't tag or push the changes." + echo + echo "Example: ./Support/release -v minor 'Bugfix Release' --dry-run" + echo + exit 2 +} + +if [[ "${CI:-false}" == true ]] ; then + die "๐Ÿšจ Running on CI is not supported! Yet." +else + echo "๐Ÿ‘ฎโ€โ™€๏ธ Running on a local machine..." + RELEASE_SOURCE_DIR="${BASH_SOURCE[0]%/*}" +fi + +if [ $# -lt 2 ]; then + show_help +fi + +# "import" +source "$RELEASE_SOURCE_DIR/utils.sh" +source "$RELEASE_SOURCE_DIR/CI/version_numbers" + +CONFIGURATION_FILE="$RELEASE_SOURCE_DIR/../../Configurations/Project-Shared.xcconfig" +RELEASE_NOTES="$RELEASE_SOURCE_DIR/../../CHANGELOG.md" +TAG_MESSAGE_FILE="$RELEASE_SOURCE_DIR/../../TAG_MESSAGE_FILE.md" +XCFRAMEWORK_NAME="$RELEASE_SOURCE_DIR/../../PactSwiftMockServer.xcframework" + +CURRENT_BRANCH=$(git branch --show-current) +RELEASE_VERSION_PART="" +RELEASE_DESCRIPTION="" +DRY_RUN=false + +REMOTE_NAME="releases" +RELEASE_REPO_NAME="PactSwiftServer" +DEFAULT_REPO_NAME="PactSwiftMockServer" +REPO_OWNER="surpher" +REMOTE_REPO_BASE="git@github.com:$REPO_OWNER" + +LATEST_TAG=$(latest_tag) + +#################### +# Utilities +#################### + +function die { + echo "๐Ÿšจ [ERROR] $*" + echo + exit 1 +} + +function git_clean_orphan { + echo -e "๐Ÿ›€ Cleaning up the branch..." + EXCEPT_FILES="${CONFIGURATION_FILE##*/../} ${RELEASE_NOTES##*/../} ${XCFRAMEWORK_NAME##*/../}" + EXCEPT_FILES="${EXCEPT_FILES// /|}" + git ls-files | grep -v -E "$EXCEPT_FILES" | xargs git rm -f +} + +function git_add_remote { + local remote_name="$1" + local remote_url="$2" + + # Check if the remote already exists + if git remote get-url "$remote_name" &>/dev/null; then + echo "๐Ÿ‘ Remote '$remote_name' already exists." + else + git remote add "$remote_name" "$remote_url" + echo "๐Ÿ‘ Remote '$remote_name' added with URL: $remote_url" + fi + git fetch "$remote_name" +} + +function git_check_remote_branch { + local remote_name="$1" + local branch_name="$2" + git ls-remote --exit-code --heads "$remote_name" "$branch_name" >/dev/null 2>&1 + return $? +} + +function git_checkout_new_release_candidate_branch { + local REMOTE_NAME="$1" + local VERSION_BRANCH="$2" + + git fetch "$REMOTE_NAME" + + echo -e "โ‘‡ Checking out orphan $YELLOW$VERSION_BRANCH$NOCOLOR branch..." + git checkout --orphan "$VERSION_BRANCH" +} + +function github_open_draft_pull_request { + local version_tag="$1" + local release_notes="${RELEASE_NOTES##*/../}" + local dry_run=; dry_run=$([ "$DRY_RUN" = true ] && echo "--dry-run" || echo "") + + local gh_config_disable="gh config set prompt disabled" + local gh_repo_set_default="gh repo set-default $REPO_OWNER/$RELEASE_REPO_NAME" + local gh_pr_create="gh pr create --draft $dry_run --title \"RC: $version_tag\" --body-file \"$release_notes\"" + local gh_repo_reset="gh repo set-default $REPO_OWNER/$DEFAULT_REPO_NAME" + local gh_config_enable="gh config set prompt enabled" + + if command -v gh &> /dev/null; then + echo "๐Ÿ—ƒ๏ธ Opening a draft pull request..." + if [ "$DRY_RUN" = false ]; then + eval "$gh_config_disable" + eval "$gh_repo_set_default" + eval "$gh_pr_create" + eval "$gh_repo_reset" + eval "$gh_config_enable" + else + echo "๐Ÿธ Dry run..." + echo "$gh_config_disable" + echo "$gh_repo_set_default" + echo "$gh_pr_create" + echo "$gh_repo_reset" + echo "$gh_config_enable" + fi + else + echo "๐Ÿคท 'gh' not installed." + echo "Open https://github.com/$REPO_OWNER/$RELEASE_REPO_NAME/pulls and open one manually..." + fi +} + +function update_version_file { + local MARKETING_VERSION="MARKETING_VERSION = $*" + sed -i '' "2s/.*/$MARKETING_VERSION/" "$CONFIGURATION_FILE" +} + +function git_create_and_push_new_version { + local REMOTE_NAME="$1" + local REMOTE_BRANCH="$2" + local RELEASE_NAME="$3" + + echo "$RELEASE_NAME" > "$TAG_MESSAGE_FILE" + local GIT_LOG="git log --pretty='* %h - %s (%an)' ${LATEST_TAG}..HEAD >> $TAG_MESSAGE_FILE" + local GIT_ADD="git add $CONFIGURATION_FILE $RELEASE_NOTES $XCFRAMEWORK_NAME" + local GIT_COMMIT="git commit -S -m \"$RELEASE_NAME\"" + local GIT_TAG="git tag $VERSION_TAG -F $TAG_MESSAGE_FILE" + local GIT_PUSH="git push $REMOTE_NAME $REMOTE_BRANCH" + + if [ -z "$DRY_RUN" ]; then + eval "$GIT_LOG" + eval "$GIT_ADD" + eval "$GIT_COMMIT" + git_clean_orphan + eval "$GIT_TAG" || die "Failed to tag current commit!" + eval "$GIT_PUSH" || die "Failed to push the release commit with tag '$VERSION_TAG' to '$REMOTE_NAME/$REMOTE_BRANCH'!" + else + echo "๐Ÿธ DRY RUN..." + echo "$GIT_LOG" + echo "$GIT_ADD" + echo "$GIT_COMMIT" + git_clean_orphan + echo "$GIT_TAG" + echo "$GIT_PUSH" + fi +} + +function cleanup { + local release_notes="${RELEASE_NOTES##*/../}" + local configuration_file="${CONFIGURATION_FILE##*/../}" + local xcframework_name="${XCFRAMEWORK_NAME##*/../}" + + echo "๐Ÿงน Cleanning up..." + + local git_restore="git rm --cached -f -r ." + echo "$git_restore" + eval "$git_restore" + + local rm_files="rm -fr $xcframework_name && rm $release_notes && rm $release_notes.* && rm $configuration_file" + echo "$rm_files" + eval "$rm_files" + + git checkout "$CURRENT_BRANCH" +} + +################## +# Pre-checks +################## +while getopts ":v:d:-:" opt; do + case ${opt} in + v) + RELEASE_VERSION_PART=$OPTARG + ;; + d) + RELEASE_DESCRIPTION=$OPTARG + ;; + -) + case "${OPTARG}" in + dry-run) + DRY_RUN=true + shift 2 + ;; + description) + RELEASE_DESCRIPTION=$1 + shift 2 + ;; + *) + echo "Invalid option: --${OPTARG}" >&2 + show_help + ;; + esac + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + show_help + ;; + :) + echo "Option -$OPTARG requires an argument." >&2 + show_help + ;; + esac +done + +# Check if the version part is provided and valid +if [[ -z $RELEASE_VERSION_PART || ! "$RELEASE_VERSION_PART" =~ ^(major|minor|patch)$ ]]; then + show_help +fi + +################## +# Generate RC +################## +VERSION_TAG=$(generate_version_number "$RELEASE_VERSION_PART" "$RELEASE_DESCRIPTION") +VERSION_BRANCH="rc/${VERSION_TAG// /_}" +MARKETING_VERSION=${VERSION_TAG#v} +MARKETING_VERSION=${MARKETING_VERSION%% -*} + +# Configure remote for release candidate and release version to push to +echo "๐ŸŒŽ Adding '$REMOTE_NAME' remote..." +git_add_remote "$REMOTE_NAME" "$REMOTE_REPO_BASE/$RELEASE_REPO_NAME.git" + +echo "๐Ÿ“ Generating release notes in $RELEASE_NOTES..." +# backup the existing CHANGELOG.md to CHANGELOG.backup +cp "$RELEASE_NOTES" "${RELEASE_NOTES}.backup" + +# Prepare the title for this release +echo "# ${VERSION_TAG}" > "${RELEASE_NOTES}.next" + +# Get the commits from last contribution +git log --pretty='* %h - %s (%an)' "${LATEST_TAG}"..HEAD >> "${RELEASE_NOTES}.next" + +# Stage the updated CHANGELOG.md +git add "$RELEASE_NOTES" || { die "Failed to add $RELEASE_NOTES to INDEX"; } + +# Read the notes for this release and append them to the old CHANGELOG.md +< "$RELEASE_NOTES.next" tr ' ' _ | nl +while IFS= read -r i; do echo "${i%?}"; done < "$RELEASE_NOTES" + +# Build libpact_ffi dependencies +echo "๐Ÿ—๏ธ Building the binaries..." +eval "$RELEASE_SOURCE_DIR/CI/build_rust_dependencies" + +# Building the XCFramework +echo "๐Ÿ—๏ธ Building the XCFramework..." +eval "$RELEASE_SOURCE_DIR/CI/build_xcframework" + +# Bump up the MARKETING_VERSION in xcconfig +update_version_file "$MARKETING_VERSION" + +# Check out the RC branch to make changes on +echo -e "๐Ÿ–– Checking out RC branch $YELLOW$VERSION_BRANCH$NOCOLOR..." +git_checkout_new_release_candidate_branch "$REMOTE_NAME" "$VERSION_BRANCH" + +# Push RC changes +echo -e "๐Ÿšข Releasing version v$MARKETING_VERSION (tag: $VERSION_TAG)..." +git_create_and_push_new_version "$REMOTE_NAME" "$VERSION_BRANCH" "$VERSION_TAG" + +github_open_draft_pull_request "$MARKETING_VERSION" + +################## +# Cleanup +################## +if [ "$DRY_RUN" = true ]; then + echo "๐Ÿธ Dry run done" + read -r -p "Clean up what has been generated during dry run? [Y/n]" -n 1 USER_INPUT + echo + if [[ $USER_INPUT =~ ^[Yy]$ ]]; then + cleanup + git checkout "$CURRENT_BRANCH" + else + echo "Leaving everything that's been generated during dry run as is..." + exit 0 + fi +fi + +echo "๐Ÿ Done." diff --git a/Support/Scripts/build_test b/Support/Scripts/test similarity index 100% rename from Support/Scripts/build_test rename to Support/Scripts/test diff --git a/Support/Scripts/utils.sh b/Support/Scripts/utils.sh index 65e5b86..8ba01c9 100644 --- a/Support/Scripts/utils.sh +++ b/Support/Scripts/utils.sh @@ -53,7 +53,7 @@ MIN_XCODE_VERSION="16.1" XCODE_VERSION_MIN_SUGGESTED="16.1" XCODE_VERSION_MIN_SUPPORTED="16.0" -function check_xcode_version_number { +function __check_xcode_version_number { local major=${1:-0} local minor=${2:-0} @@ -70,7 +70,7 @@ function check_xcode() { if ! xcode_version="$(xcodebuild -version | sed -n '1s/^Xcode \([0-9.]*\)$/\1/p')"; then echo 'Failed to get Xcode version' 1>&2 exit 1 - elif check_xcode_version_number ${xcode_version//./ }; then # not double quoting to pass two parameters + elif __check_xcode_version_number ${xcode_version//./ }; then # not double quoting to pass two parameters echo "Xcode version '$xcode_version' not supported, version $MIN_XCODE_VERSION or above is required" 1>&2; exit 1 fi diff --git a/TAG_MESSAGE_FILE.md b/TAG_MESSAGE_FILE.md new file mode 100644 index 0000000..60f6343 --- /dev/null +++ b/TAG_MESSAGE_FILE.md @@ -0,0 +1 @@ +v0.6.0