From 825f97c037fdd73ff899d60e801570003588b213 Mon Sep 17 00:00:00 2001 From: Alex Atkinson Date: Tue, 17 Dec 2024 15:47:33 -0500 Subject: [PATCH 1/6] THX-1138: mono-repo --- .github/workflows/build-and-release.yml | 4 +- README.md | 61 ++++++++++++++++++++++--- action.yml | 47 +++++++++++++++---- scripts/detectNewVersion.sh | 49 ++++++++++++++------ scripts/detectPreviousVersion.sh | 47 +++++++++++++++++-- scripts/validateSemver.sh | 29 +++++++++++- 6 files changed, 198 insertions(+), 39 deletions(-) diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 0aaa058..03f3c4b 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -27,7 +27,7 @@ jobs: echo "FUNCTION=$FUNCTION" >> $GITHUB_ENV - name: Run GitOps Automatic Versioning Action id: gitops-autover - uses: AlexAtkinson/github-action-gitops-autover@0.1.7 + uses: AlexAtkinson/github-action-gitops-autover@0.3.1 - name: Set Version Dependent Envars run: | VERSION=${{ steps.gitops-autover.outputs.new-version }} @@ -47,4 +47,4 @@ jobs: automatic_release_tag: "${{ env.VERSION }}" title: "${{ env.VERSION }}" files: | - ${{ env.ZIPFILE }} \ No newline at end of file + ${{ env.ZIPFILE }} diff --git a/README.md b/README.md index 0973a77..acdba89 100644 --- a/README.md +++ b/README.md @@ -8,14 +8,27 @@ -Language/content agnostic method of automatically determining the [semantic version](https://semver.org/) for a product based on _branch_ merge history with MINIMAL discipline dependencies. +Language/content agnostic method of automatically determining the [semantic version](https://semver.org/) for a product based on _branch_ merge history with MINIMAL [discipline dependencies](https://github.com/marketplace/actions/gitops-automatic-versioning?#discipline-dependency). This is accomplished by counting the merges of branches matching the [naming scheme](#branch-naming-scheme) into the [main|master] branch. Folks familiar with Scrum/SAFe or GitFlow/fooFlow strategies will recognize this scheme. -**Yes**, this can be implemented in repos that previously used different version increment methods. +**Burning Questions** + +- **Yes** - This can indeed be implemented in repos that previously used different version increment methods. +- **Yes** - Jira will recognize the issue tag anywhere in the branch name -- it does not have to be a prefix for the integration to function. +- **Yes** - This aligns with and extends on [guidance](https://confluence.atlassian.com/bitbucketserver050/using-branches-in-bitbucket-server-913474726.html) from Atlassian on branch naming schemes. > [Convenience link to this action on the marketplace](https://github.com/marketplace/actions/gitops-automatic-versioning) +## Recent Changes + +- 1.0.0: (non-breaking) Addition of support for mono-repos. IE: Discretely version specific directories. + - NOTE: Github, Jira, etc., were designed to host one product per repo/project. DO NOT create new mono-repo projects unless you're specifically tooling out to support them well. +- 0.3.1: Update the checkout action version to v4. +- 0.3.0: Bring back the unshallowing, which ensures the full git log is available. + - TODO: Adjust scripts to use `git log --remotes` to avoid unshallowing large repos. +- 0.2.9: Fix 'ops' increments; add user friendly error outputs. + ## Usage ### Setup @@ -45,6 +58,33 @@ git push --set-upstream origin fix/not-a-feature # THEN: Click the link to create a PR & merge it +### Inputs + +Note: Only required for setting up mono-repo versioning. + +
+
mono-repo-mode: [bool]
+
Enable semver matching against any tag structure that includes a valid semver string.
+ Eg: '<product-name>_<semver>'
+ Required: false
+ Default: false
+
mono-repo-product-name: [string]
+
The product name to match against.
+ Eg: 'bob', would match the tags like 'bob_1.2.3'.
+ Required: if mono-repo-mode: true
+ Default: ''
+
mono-repo-product-path: [string]
+
The path to the product.
+ Eg: path/to/bob
+ Required: if mono-repo-mode: true
+ Default: ''
+
force-patch-increment: [bool]
+
Forces a PATCH increment if no other increment detected.
+ (Intended for development purposes only.)
+ Required: false
+ Default: false
+
+ ### Outputs
@@ -73,7 +113,7 @@ Below is a valid workflow utilizing this action. If you wanted to extend it to d - uses: actions/checkout@v3 - name: Run GitOps Automatic Versioning Action id: gitops-autover - uses: AlexAtkinson/github-action-gitops-autover@0.1.7 + uses: AlexAtkinson/github-action-gitops-autover@0.3.0 - name: Verify Outputs run: | NEW_VERSION=${{ steps.gitops-autover.outputs.new-version }} @@ -81,6 +121,15 @@ Below is a valid workflow utilizing this action. If you wanted to extend it to d PREVIOUS_VERSION=${{ steps.gitops-autover.outputs.previous-version }} echo "previous-version: $PREVIOUS_VERSION" +To make use of the mono-repo support, simply add a block for the director you wish to version. + + - name: Run GitOps Automatic Versioning Action + id: gitops-autover + uses: AlexAtkinson/github-action-gitops-autover@0.3.0 + with: + mono-repo-mode: true + mono-repo-product-name: bob + This results in outputs like: _major:_ @@ -140,7 +189,7 @@ Additionally, this repo uses its own action for versioning, so feel free to inve echo "PRODUCT_NAME_LOWER=$PRODUCT_NAME_LOWER" >> $GITHUB_OUTPUT - name: GitOps Automatic Versioning id: gitops-autover - uses: AlexAtkinson/github-action-gitops-autover@0.1.7 + uses: AlexAtkinson/github-action-gitops-autover@0.3.0 build: name: "Build" runs-on: ubuntu-latest @@ -254,5 +303,5 @@ For those interested, here's some pseudo code: PRs are welcome. - input(s): iteration-branches (map) - inform MINOR and PATCH incrementing branch name patterns. -- input(s): mono-mode (bool) - version subdirs discretely -- ~~CAN'T DO~~: DONE: unshallow from last version tag to latest commit to... Seems a limitation of (git at first glance). See the [Checkout From Tag](https://github.com/marketplace/actions/checkout-from-tag) action. +- DONE: input(s): mono-mode (bool) - version subdirs discretely +- UNDONE-CAN'T DO: ~~CAN'T DO~~: ~~DONE:~~ unshallow from last version tag to latest commit to... Seems a limitation of (git at first glance). See the [Checkout From Tag](https://github.com/marketplace/actions/checkout-from-tag) action. diff --git a/action.yml b/action.yml index 583bda9..3c8bdaf 100644 --- a/action.yml +++ b/action.yml @@ -13,6 +13,18 @@ inputs: description: "Forces a PATCH increment if no other increment detected. NOTE: This is intended for development purposes only." required: false default: 'false' + mono-repo-mode: + description: "Enable semver matching against any tag structure that includes a valid semver string. Eg: _" + required: false + default: 'false' + mono-repo-product-name: + description: "The product name to match against. For example, 'bob', would match the tags like 'bob_1.2.3'. Required if 'mono-repo-mode' is enabled." + required: false + default: '' + mono-repo-product-path: + description: "The path to the product. IE: 'path/to/bob'. Required if 'mono-repo-mode' is enabled." + required: false + default: '' outputs: new-version: description: "New Version" @@ -23,8 +35,13 @@ outputs: runs: using: "composite" steps: - - name: Checkout to the depth of the last DETECTED version. - uses: AlexAtkinson/github-action-checkout-from-tag@latest + - name: Checkout + uses: actions/checkout@v4 + - name: Ensure full commit history + run: | + cd $GITHUB_WORKSPACE + git pull --unshallow + shell: bash - name: Detect Previous Version id: previous-version run: | @@ -36,27 +53,37 @@ runs: run: | cd $GITHUB_WORKSPACE opt='' - [[ "${{ inputs.force-re-evaluate }}" == 'true' ]] && opt='-f' - [[ "${{ inputs.force-re-evaluate }}" == 'true' ]] && opt='-p' - new_version="$(${{ github.action_path }}/scripts/detectNewVersion.sh $opt)" + [[ "${{ inputs.force-re-evaluate }}" == 'true' ]] && opt='$opt -f' + [[ "${{ inputs.force-patch-increment }}" == 'true' ]] && opt='$opt -p' + [[ "${{ inputs.mono-repo-mode }}" == 'true' ]] && opt='$opt -m' + if [[ "${{ inputs.mono-repo-mode }}" == 'true' && -z ${{ inputs.mono-repo-product-name }} ]]; then + echo -e "ERROR: 571 - mono-repo-product-name must be set and NOT null!" + exit 1 || true + fi + [[ -n "${{ inputs.mono-repo-product-name }}" ]] && opt='$opt -n ${{ inputs.mono-repo-product-name }}' + new_version="$(${{ github.action_path }}/scripts/detectNewVersion.sh $opt)" || true echo "new-version=$new_version" | tee $GITHUB_OUTPUT + if [[ "$new_version" =~ "520" ]]; then + echo -e "ERROR: 520 - You must source this script when specifying an environment variable! Eg: '. ./foo.sh -e bar_ver'" + exit 1 || true + fi if [[ "$new_version" =~ "570" ]]; then - echo -e "\e[01;31mERROR\e[00m: 570 - Invalid argument!" + echo -e "ERROR: 570 - Invalid argument!" exit 1 || true fi if [[ "$new_version" =~ "590" ]]; then - echo -e "\e[01;31mFATAL\e[00m: 501 - This is not a git repository!" + echo -e "FATAL: 501 - This is not a git repository!" exit 1 || true fi if [[ "$new_version" =~ "591" ]]; then - echo -e "\e[01;31mERROR\e[0m: 591 - Unsupported origin host." + echo -e "ERROR: 591 - Unsupported origin host." exit 1 || true fi if [[ "$new_version" =~ "599" ]]; then - echo -e "\e[01;31mERROR\e[00m: 599 - No feature, enhancement, fix, bugfix, hotfix, or ops branches detected!" + echo -e "ERROR: 599 - No feature, enhancement, fix, bugfix, hotfix, or ops branches detected!" exit 1 || true fi shell: bash # See github contexts for more like github.action_path. -# REF: https://docs.github.com/en/actions/learn-github-actions/contexts#github-context \ No newline at end of file +# REF: https://docs.github.com/en/actions/learn-github-actions/contexts#github-context diff --git a/scripts/detectNewVersion.sh b/scripts/detectNewVersion.sh index 2f18669..a21267d 100755 --- a/scripts/detectNewVersion.sh +++ b/scripts/detectNewVersion.sh @@ -47,6 +47,13 @@ DESCRIPTION -p Increments PATCH version on _every_ run. WARN: This is intended development use only. + -m Enables mono-repo mode, allowing matching against semvers prefixed with + a product name. Eg: 'cool-app_1.2.3' + + -n The product name to match against. EG: 'bob' would match tags like 'bob_1.2.3'. + + -d The directory of the product to version. EG: 'path/to/bob'. + EXAMPLES The following detects the new version for the repo. @@ -75,7 +82,7 @@ fi # -------------------------------------------------------------------------------------------------- OPTIND=1 -while getopts "he:vfp" opt; do +while getopts "he:vfpmn:d:" opt; do case $opt in h) printHelp @@ -95,9 +102,23 @@ while getopts "he:vfp" opt; do p) arg_p='set' ;; + m) + arg_m='set' + arg_opts="$arg_opts -m" + ;; + n) + arg_n='set' + arg_n_val="$OPTARG" + arg_opts="$arg_opts -n $OPTARG" + ;; + d) + arg_d='set' + arg_d_val="$OPTARG" + arg_d_opt="--full-history" + arg_opts="$arg_opts -d $OPTARG" + ;; *) echo -e "\e[01;31mERROR\e[00m: 570 - Invalid argument!" - echo "::error title=Argument Error::ERROR 570 - Invalid argument!" printHelp if [[ "$sourced" == 0 ]]; then exit 0 @@ -119,13 +140,13 @@ tsCmd='date --utc +%FT%T.%3NZ' relative_path="$(dirname "${BASH_SOURCE[0]}")" dir="$(realpath "${relative_path}")" -lastVersion=$(/usr/bin/env bash -c "${dir}/detectPreviousVersion.sh") -lastVersionMajor=$(/usr/bin/env bash -c "${dir}/validateSemver.sh -p major $lastVersion") -lastVersionMinor=$(/usr/bin/env bash -c "${dir}/validateSemver.sh -p minor $lastVersion") -lastVersionPatch=$(/usr/bin/env bash -c "${dir}/validateSemver.sh -p patch $lastVersion") -lastVersionCommitHash=$(/usr/bin/env bash -c "${dir}/detectPreviousVersion.sh -c") +lastVersion=$(/usr/bin/env bash -c "${dir}/detectPreviousVersion.sh -9 $arg_opts") +lastVersionMajor=$(/usr/bin/env bash -c "${dir}/validateSemver.sh -p major $lastVersion $arg_opts") +lastVersionMinor=$(/usr/bin/env bash -c "${dir}/validateSemver.sh -p minor $lastVersion $arg_opts") +lastVersionPatch=$(/usr/bin/env bash -c "${dir}/validateSemver.sh -p patch $lastVersion $arg_opts") +lastVersionCommitHash=$(/usr/bin/env bash -c "${dir}/detectPreviousVersion.sh -9 -c $arg_opts") lastCommitHash=$(git rev-parse HEAD) -firstCommitHash=$(git rev-list --max-parents=0 HEAD) +firstCommitHash=$(git rev-list --max-parents=0 HEAD | tail -n 1) ci_name=$("${dir}/detect-ci.sh") origin=$(git config --get remote.origin.url) @@ -155,7 +176,6 @@ case "$origin_host" in ;; *) echo -e "\e[01;31mERROR\e[0m: 591 - Unsupported origin host." - echo "::error title=Origin Host Error::ERROR 591 - Unsupported origin host!" exit 1 ;; esac @@ -167,17 +187,16 @@ esac if [[ -n $arg_e ]]; then if [[ "$sourced" == 0 ]]; then echo -e "[$(${tsCmd})] \e[01;31mERROR\e[00m: 520 - You must source this script when specifying an environment variable! Eg: '. ./${0##*/} -e foo_ver'\n" - echo "::error title=Usage Error::ERROR 520 - You must source this script when specifying an environment variable! Eg: '. ./foo.sh -e bar_ver'" exit 1 fi fi -git log --pretty=oneline "$lastVersionCommitHash".."$lastCommitHash" | grep '+semver' | grep -q 'major\|breaking' && incrementMajor='true' +git log $arg_d_opt --pretty=oneline "$lastVersionCommitHash".."$lastCommitHash" $arg_d_val | grep '+semver' | grep -q 'major\|breaking' && incrementMajor='true' if [[ $incrementMajor != 'true' ]]; then IFS=$'\r\n' if [[ -n $arg_f ]]; then - for i in $(git log --pretty=oneline "${firstCommitHash}".."${lastCommitHash}" | awk -v s="$merge_string" -v c="$column" '$0 ~ s {print $c}' | awk -v f="$field" -F'/' '{print $f}' | tr -d "'" | grep -i '^enhancement$\|^feature$\|^fix$\|^hotfix$\|^bugfix$\|^ops$' | awk -F '\r' '{print $1}' | sort | uniq -c | sort -nr) ; do + for i in $(git log $arg_d_opt --pretty=oneline "${firstCommitHash}".."${lastCommitHash}" $arg_d_val | awk -v s="$merge_string" -v c="$column" '$0 ~ s {print $c}' | awk -v f="$field" -F'/' '{print $f}' | tr -d "'" | grep -i '^enhancement$\|^feature$\|^fix$\|^hotfix$\|^bugfix$\|^ops$' | awk -F '\r' '{print $1}' | sort | uniq -c | sort -nr) ; do varname=$(echo "$i" | awk '{print $2}') varname=${varname,,} value=$(echo "$i" | awk '{print $1}') @@ -185,7 +204,7 @@ if [[ $incrementMajor != 'true' ]]; then declare count_"$varname"="$value" done else - for i in $(git log --pretty=oneline "${lastVersionCommitHash}".."${lastCommitHash}" | awk -v s="$merge_string" -v c="$column" '$0 ~ s {print $c}' | awk -v f="$field" -F'/' '{print $f}' | tr -d "'" | grep -i '^enhancement$\|^feature$\|^fix$\|^hotfix$\|^bugfix$\|^ops$' | awk -F '\r' '{print $1}' | sort | uniq -c | sort -nr) ; do + for i in $(git log $arg_d_opt --pretty=oneline "${lastVersionCommitHash}".."${lastCommitHash}" $arg_d_val | awk -v s="$merge_string" -v c="$column" '$0 ~ s {print $c}' | awk -v f="$field" -F'/' '{print $f}' | tr -d "'" | grep -i '^enhancement$\|^feature$\|^fix$\|^hotfix$\|^bugfix$\|^ops$' | awk -F '\r' '{print $1}' | sort | uniq -c | sort -nr) ; do varname=$(echo "$i" | awk '{print $2}') varname=${varname,,} value=$(echo "$i" | awk '{print $1}') @@ -208,7 +227,6 @@ if [[ -n $arg_f ]]; then else if [[ -z $incrementMajor && -z $count_feature && -z $count_enhancement && -z $count_fix && -z $count_bugfix && -z $count_hotfix && -z $count_ops ]]; then echo -e "\e[01;31mERROR\e[00m: 599 - No feature, enhancement, fix, bugfix, hotfix, or ops branches detected!" - echo "::error title=No Valid Merge Detected::ERROR 599 - No feature, enhancement, fix, bugfix, hotfix, or ops branches detected!" exit 1 fi fi @@ -240,7 +258,8 @@ elif [[ -n $arg_p ]]; then newVersionPatch=$((lastVersionPatch + 1)) fi -newVersion=$(/usr/bin/env bash -c "${dir}/validateSemver.sh -9p full $newVersionMajor.$newVersionMinor.$newVersionPatch") +newVersion=$(/usr/bin/env bash -c "${dir}/validateSemver.sh -9p full $newVersionMajor.$newVersionMinor.$newVersionPatch $arg_opts") +[[ -n $arg_n ]] && newVersion="${arg_n_val}_${newVersion}" if [[ -n $arg_e ]]; then export_var="$arg_e_val" diff --git a/scripts/detectPreviousVersion.sh b/scripts/detectPreviousVersion.sh index 4d72153..973dd5b 100755 --- a/scripts/detectPreviousVersion.sh +++ b/scripts/detectPreviousVersion.sh @@ -30,6 +30,13 @@ DESCRIPTION -c Prints the commit hash instead of the detected version to stdout. + -m Enables mono-repo mode, allowing matching against semvers prefixed with + a product name. Eg: 'cool-app_1.2.3' + + -n The product name to match against. EG: 'bob' would match tags like 'bob_1.2.3'. + + -d The directory of the product to version. EG: 'path/to/bob'. + EXAMPLES Detects previous version, printing additional information if available. @@ -59,7 +66,7 @@ fi # -------------------------------------------------------------------------------------------------- OPTIND=1 -while getopts "hvc" opt; do +while getopts "hv9cmn:d:" opt; do case $opt in h) printHelp @@ -71,6 +78,24 @@ while getopts "hvc" opt; do c) arg_c='set' ;; + 9) + arg_9='set' + ;; + m) + arg_m='set' + arg_opts="$arg_opts -m" + ;; + n) + arg_n='set' + arg_n_val="$OPTARG" + arg_opts="$arg_opts -n $OPTARG" + ;; + d) + arg_d='set' + arg_d_val="$OPTARG" + arg_d_opt="--full-history" + arg_opts="$arg_opts -d $OPTARG" + ;; *) echo -e "\e[01;31mERROR\e[00m: Invalid argument!" printHelp @@ -85,31 +110,43 @@ shift $((OPTIND-1)) # -------------------------------------------------------------------------------------------------- tsCmd='date --utc +%FT%T.%3NZ' -semverRegex="^[v]?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-([0-9A-Za-z]+))?(\\+((([1-9])|([1-9][0-9]+))))?$" + +if [[ -n $arg_9 ]]; then + semverRegex="^[v]?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$" + [[ -n $arg_m ]] && semverRegex="([0-9A-Za-z]+)?[_-]?[v]?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$" +else + semverRegex="^[v]?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-([0-9A-Za-z]+))?(\\+((([1-9])|([1-9][0-9]+))))?$" + [[ -n $arg_m ]] && semverRegex="^([0-9A-Za-z]+)?[_-]?[v]?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-([0-9A-Za-z]+))?(\\+((([1-9])|([1-9][0-9]+))))?$" +fi relative_path="$(dirname "${BASH_SOURCE[0]}")" dir="$(realpath "${relative_path}")" lastVersion=$(git for-each-ref --sort=creatordate --format '%(refname:lstrip=2)' refs/tags | grep -E "$semverRegex" | tail -n 1) +# Support mono-repos where a product name is specified. +[[ -n $arg_n ]] && lastVersion=$(git for-each-ref --sort=creatordate --format '%(refname:lstrip=2)' refs/tags | grep "$arg_n_val" | grep -E "$semverRegex" | tail -n 1) # -------------------------------------------------------------------------------------------------- # Sanity (2/2) # -------------------------------------------------------------------------------------------------- if [[ "$lastVersion" == '' ]]; then - [[ -n $arg_v ]] && echo -e "[$(${tsCmd})] INFO: No previous version detected. Initializing at '0.0.0'.\n" + [[ -n $arg_v && -z $arg_n ]] && echo -e "[$(${tsCmd})] INFO: No previous version detected. Initializing at '0.0.0'.\n" + [[ -n $arg_v && -n $arg_n ]] && echo -e "[$(${tsCmd})] INFO: No previous version detected. Initializing at '${arg_n_val}_0.0.0'.\n" lastVersion='0.0.0' lastVersionCommitHash=$(git rev-list --max-parents=0 HEAD) else - if ! bash -c "${dir}/validateSemver.sh -v9 $lastVersion"; then + if ! bash -c "${dir}/validateSemver.sh -v9 $arg_opts $lastVersion"; then exit 1 else lastVersionCommitHash=$(git rev-list -n 1 "$lastVersion") # Ensure lastVersion does not include a leading [vV] - lastVersion=$(bash -c "${dir}/validateSemver.sh -v9p full $lastVersion") + lastVersion=$(bash -c "${dir}/validateSemver.sh -v9p full $arg_opts $lastVersion") fi fi +[[ -n $arg_n ]] && lastVersion="${arg_n_val}_${lastVersion}" + # -------------------------------------------------------------------------------------------------- # Main Operations # -------------------------------------------------------------------------------------------------- diff --git a/scripts/validateSemver.sh b/scripts/validateSemver.sh index e3ba25c..5291291 100755 --- a/scripts/validateSemver.sh +++ b/scripts/validateSemver.sh @@ -48,6 +48,13 @@ DESCRIPTION (v)[Major].[Minor].[Patch] + -m Enables mono-repo mode, allowing matching against semvers prefixed with + a product name. Eg: 'cool-app_1.2.3' + + -n The product name to match against. EG: 'bob' would match tags like 'bob_1.2.3'. + + -d The directory of the product to version. EG: 'path/to/bob'. + EXAMPLES The following returns an exit code of 0, as the supplied version is Agile CICD compliant. @@ -80,7 +87,7 @@ fi # -------------------------------------------------------------------------------------------------- OPTIND=1 -while getopts "hp:v9" opt; do +while getopts "hp:v9mn:d:" opt; do case $opt in h) printHelp @@ -96,6 +103,21 @@ while getopts "hp:v9" opt; do 9) arg_9='set' ;; + m) + arg_m='set' + arg_opts="$arg_opts -m" + ;; + n) + arg_n='set' + arg_n_val="$OPTARG" + arg_opts="$arg_opts -n $OPTARG" + ;; + d) + arg_d='set' + arg_d_val="$OPTARG" + arg_d_opt="--full-history" + arg_opts="$arg_opts -d $OPTARG" + ;; *) echo -e "\e[01;31mERROR\e[00m: Invalid argument!" printHelp @@ -113,8 +135,10 @@ tsCmd='date --utc +%FT%T.%3NZ' if [[ -n $arg_9 ]]; then semverRegex="^[v]?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$" + [[ -n $arg_m ]] && semverRegex="([0-9A-Za-z]+)?[_-]?[v]?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$" else semverRegex="^[v]?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-([0-9A-Za-z]+))?(\\+((([1-9])|([1-9][0-9]+))))?$" + [[ -n $arg_m ]] && semverRegex="^([0-9A-Za-z]+)?[_-]?[v]?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-([0-9A-Za-z]+))?(\\+((([1-9])|([1-9][0-9]+))))?$" fi # -------------------------------------------------------------------------------------------------- @@ -123,6 +147,8 @@ fi function validateSemver { local version=$1 + version=${version##*_} + version=${version##*-} [[ "$version" =~ ^[vV]* ]] && version=${version//^[vV]/""/} if [[ "$version" =~ $semverRegex ]]; then local major=${BASH_REMATCH[1]} @@ -149,6 +175,7 @@ function validateSemver { else [[ -z $arg_9 && -n $arg_v ]] && echo -e "[$(${tsCmd})] \e[01;31mFATAL\e[00m: '$version' does not match the semver schema: '(v)[Major].[Minor].[Patch](-PRERELEASE)(+BUILD)'!\n" [[ -n $arg_9 && -n $arg_v ]] && echo -e "[$(${tsCmd})] \e[01;31mFATAL\e[00m: '$version' does not match the semver schema: '(v)[Major].[Minor].[Patch]'!\n" + [[ -n $arg_9 && -n $arg_v && -n $arg_m ]] && echo -e "[$(${tsCmd})] \e[01;31mFATAL\e[00m: '$version' does not match the semver schema: '[product_name][-_](v)[Major].[Minor].[Patch]'!\n" exit 1 fi } From 13bf4c15241d7fd9cad04f071f8e96ac2ff38542 Mon Sep 17 00:00:00 2001 From: Alex Atkinson Date: Tue, 17 Dec 2024 17:11:13 -0500 Subject: [PATCH 2/6] THX-1138: Deprecate arg m --- scripts/detectPreviousVersion.sh | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/scripts/detectPreviousVersion.sh b/scripts/detectPreviousVersion.sh index 973dd5b..7884b5f 100755 --- a/scripts/detectPreviousVersion.sh +++ b/scripts/detectPreviousVersion.sh @@ -15,7 +15,7 @@ NAME detectPreviousVersion.sh SYNOPSIS - ${0##*/} [-hvc] + ${0##*/} [-hvcnd] DESCRIPTION Detects most recent version tag of the repository. @@ -30,10 +30,9 @@ DESCRIPTION -c Prints the commit hash instead of the detected version to stdout. - -m Enables mono-repo mode, allowing matching against semvers prefixed with - a product name. Eg: 'cool-app_1.2.3' - - -n The product name to match against. EG: 'bob' would match tags like 'bob_1.2.3'. + -n Enables mono-repo mode allowing the product name to match against tags. + EG: 'bob' would match tags like 'bob_1.2.3'. + TIP: dir names and product names should match. This arg exists in case they do not. -d The directory of the product to version. EG: 'path/to/bob'. @@ -66,7 +65,7 @@ fi # -------------------------------------------------------------------------------------------------- OPTIND=1 -while getopts "hv9cmn:d:" opt; do +while getopts "hv9cn:d:" opt; do case $opt in h) printHelp @@ -81,10 +80,6 @@ while getopts "hv9cmn:d:" opt; do 9) arg_9='set' ;; - m) - arg_m='set' - arg_opts="$arg_opts -m" - ;; n) arg_n='set' arg_n_val="$OPTARG" @@ -113,10 +108,10 @@ tsCmd='date --utc +%FT%T.%3NZ' if [[ -n $arg_9 ]]; then semverRegex="^[v]?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$" - [[ -n $arg_m ]] && semverRegex="([0-9A-Za-z]+)?[_-]?[v]?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$" + [[ -n $arg_d ]] && semverRegex="([0-9A-Za-z]+)?[_-]?[v]?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$" else semverRegex="^[v]?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-([0-9A-Za-z]+))?(\\+((([1-9])|([1-9][0-9]+))))?$" - [[ -n $arg_m ]] && semverRegex="^([0-9A-Za-z]+)?[_-]?[v]?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-([0-9A-Za-z]+))?(\\+((([1-9])|([1-9][0-9]+))))?$" + [[ -n $arg_d ]] && semverRegex="^([0-9A-Za-z]+)?[_-]?[v]?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-([0-9A-Za-z]+))?(\\+((([1-9])|([1-9][0-9]+))))?$" fi relative_path="$(dirname "${BASH_SOURCE[0]}")" From 4fb63a7d398d406ead0fc254728eb88ee6910861 Mon Sep 17 00:00:00 2001 From: Alex Atkinson Date: Tue, 17 Dec 2024 17:25:06 -0500 Subject: [PATCH 3/6] THX-1138: Add test --- scripts/tests.sh | 164 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100755 scripts/tests.sh diff --git a/scripts/tests.sh b/scripts/tests.sh new file mode 100755 index 0000000..8aafcb2 --- /dev/null +++ b/scripts/tests.sh @@ -0,0 +1,164 @@ +#!/usr/bin/env bash +# test.sh + +TEST="test_$(date --utc +"%s")" +trap 'rm -rf $TEST; \ + git reset --hard $STARTING_COMMIT;' EXIT + #git reset HEAD~$TEST_COMMIT_COUNT;' EXIT + +STARTING_COMMIT=$(git rev-parse HEAD) + +TEST_COMMIT_COUNT=0 + +function printHeading () { + txt="$@"; + printf "\n\e[01;39m${txt}\e[0m "; + printf '\n%*s' "$((${COLUMNS}-$((${COLUMNS}-$(wc -c<<<$txt)+1))))" | tr ' ' -; + printf '\n' +} + +function add_history() { + if [[ $# -ne 3 ]] && [[ $# -ne 4 ]]; then echo "ERROR: Exactly 3 or 4 arguments required!"; return 1; fi + DIR="$1" + COUNT="$2" + BRANCH_TYPE="$3" + MAJOR="$4" + cd "$DIR" + for i in $(seq 1 $COUNT); do + TEST_FILE="${i}_$(date +"%s%6N")" + touch $TEST_FILE + git add "$TEST_FILE" >/dev/null 2>&1 + [[ -n $MAJOR ]] && git commit -m "+semver major $TEST" >/dev/null 2>&1 + git commit -m "Merge pull request #9999 from AlexAtkinson/$BRANCH_TYPE/${TEST}_$i" >/dev/null 2>&1 + done + cd - >/dev/null 2>&1 +} + +function test_previous() { + if [[ $# -lt 3 ]]; then echo "ERROR: At least 3 arguments required!"; return 1; fi + TEST_TYPE="${1:-Repository}" # Repository, Directory + DIRECTORY="${2:-./}" + ASSERTION="$3" + COMMENT="${@:4}"; [[ -z $COMMENT ]] && COMMENT="No Comment" + if [[ "$TEST_TYPE" == "Repository" ]]; then + echo -e "\e[01;39m$TEST_TYPE: Previous Version ($COMMENT)\e[0m" + TEST_OUTPUT=$(scripts/detectPreviousVersion.sh) + if grep -q "$ASSERTION" <<<$TEST_OUTPUT; then RESULT="\e[01;32mOK\e[0m"; else RESULT="\e[01;31mFAIL\e[0m"; FAILURE="TRUE"; fi + echo -e " $RESULT - $TEST_OUTPUT" + fi + if [[ "$TEST_TYPE" == "Directory" ]]; then + echo -e "\e[01;39m$TEST_TYPE - $DIRECTORY: Previous Version ($COMMENT)\e[0m" + TEST_OUTPUT=$(scripts/detectPreviousVersion.sh -d "$DIRECTORY" -n "${DIRECTORY##*/}") + if grep -q "$ASSERTION" <<<$TEST_OUTPUT; then RESULT="\e[01;32mOK\e[0m"; else RESULT="\e[01;31mFAIL\e[0m"; FAILURE="TRUE"; fi + echo -e " $RESULT - $TEST_OUTPUT" + fi +} + +function test_new() { + if [[ $# -lt 3 ]]; then echo "ERROR: At least 3 arguments required!"; return 1; fi + TEST_TYPE="${1:-Repository}" # Repository, Directory + DIRECTORY="${2:-./}" + ASSERTION="$3" + COMMENT="${@:4}"; [[ -z $COMMENT ]] && COMMENT="No Comment" + if [[ "$TEST_TYPE" == "Repository" ]]; then + echo -e "\e[01;39m$TEST_TYPE: New Version ($COMMENT)\e[0m" + TEST_OUTPUT=$(scripts/detectNewVersion.sh) + if grep -q "$ASSERTION" <<<$TEST_OUTPUT; then RESULT="\e[01;32mOK\e[0m"; else RESULT="\e[01;31mFAIL\e[0m"; FAILURE="TRUE"; fi + echo -e " $RESULT - $TEST_OUTPUT" + fi + if [[ "$TEST_TYPE" == "Directory" ]]; then + echo -e "\e[01;39m$TEST_TYPE - $DIRECTORY: New Version ($COMMENT)\e[0m" + TEST_OUTPUT=$(scripts/detectNewVersion.sh -d "$DIRECTORY" -n "${DIRECTORY##*/}") + if grep -q "$ASSERTION" <<<$TEST_OUTPUT; then RESULT="\e[01;32mOK\e[0m"; else RESULT="\e[01;31mFAIL\e[0m"; FAILURE="TRUE"; fi + echo -e " $RESULT - $TEST_OUTPUT" + fi +} + +relative_path="$(dirname "${BASH_SOURCE[0]}")" +dir="$(realpath "${relative_path}")" + +ci_name=$("${dir}/detect-ci.sh") +origin=$(git config --get remote.origin.url) + +#origin=${ci_name:-origin} +# Executes in ANY CI so long as repo origin is one of the following. +# Uncomment origin override to restrict this. +[[ "$origin" =~ "git@github.com"* || "$ci_name" == "github" ]] && origin_host=github +[[ "$origin" =~ "git@gitlab.com"* || "$ci_name" == "gitlab" ]] && origin_host=gitlab +[[ "$origin" =~ "git@bitbucket.com"* || "$ci_name" == "bitbucket" ]] && origin_host=bitbucket + +case "$origin_host" in + github) + merge_string="Merge pull request #" + column=7 + field=2 + ;; + gitlab) + merge_string="Merge branch" + column=4 + field=1 + ;; + bitbucket) + merge_string="Merged in" + column=4 + field=1 + ;; + *) + echo -e "\e[01;31mERROR\e[0m: 591 - Unsupported origin host." + exit 1 + ;; +esac + + +# TEST: Repo Versioning + +## Activities + +# Tests: +# - Repo Versioning +# - Diectory Versioning +# - x Minor Increments +# - x Patch Increments + +printHeading Running Test: $TEST + +echo NOTE: These tests are not committed. + +mkdir -p $TEST/{A..C} + +# Directory Test: A +# ASSERTIONS: +# - Previous version is: A_0.0.0 +# - New Version is: ERROR: 599 +test_previous "Repository" "./" "$(scripts/detectPreviousVersion.sh)" +test_new "Repository" "./" " 599" + +test_previous "Directory" "$TEST/A" "A_0.0.0" +test_new "Directory" "$TEST/A" " 599" + +test_previous "Directory" "$TEST/B" "B_0.0.0" +add_history "$TEST/B" 3 ops +test_new "Directory" "$TEST/B" "B_0.0.3" patches +3 +git tag -a "B_0.0.3" -m "TAG" +test_previous "Directory" "$TEST/B" "B_0.0.3" previous version tagged +add_history "$TEST/B" 17 ops +test_new "Directory" "$TEST/B" "B_0.0.20" patches +17 +git tag -d "B_0.0.3" >/dev/null 2>&1 + +test_previous "Directory" "$TEST/C" "C_0.0.0" +add_history "$TEST/C" 9 ops +test_new "Directory" "$TEST/C" "C_0.0.9" patches +9 +git tag -a "C_0.0.9" -m "TAG" +test_previous "Directory" "$TEST/C" "C_0.0.9" previous version tagged +test_new "Directory" "$TEST/C" " 599" + +add_history "$TEST/C" 5 feature +test_new "Directory" "$TEST/C" "C_0.5.0" features +5 +git tag -a "C_0.5.0" -m "TAG" +add_history "$TEST/C" 19 ops +test_new "Directory" "$TEST/C" "C_0.5.19" patches +19 +add_history "$TEST/C" 1 features major +test_new "Directory" "$TEST/C" "C_1.0.0" features +1 BREAKING +git tag -d "C_0.0.9" >/dev/null 2>&1 +git tag -d "C_0.5.0" >/dev/null 2>&1 + From 471c98f7de70ec5cda11efb491f6990523eeb750 Mon Sep 17 00:00:00 2001 From: Alex Atkinson Date: Tue, 17 Dec 2024 17:30:22 -0500 Subject: [PATCH 4/6] THX-1198: Deprecate m some more. --- scripts/detectNewVersion.sh | 17 ++++++----------- scripts/validateSemver.sh | 21 ++++++++------------- 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/scripts/detectNewVersion.sh b/scripts/detectNewVersion.sh index a21267d..7e28f9a 100755 --- a/scripts/detectNewVersion.sh +++ b/scripts/detectNewVersion.sh @@ -29,7 +29,7 @@ NAME ${0##*/} SYNOPSIS - ${0##*/} [-hv] + ${0##*/} [-hefpnd] DESCRIPTION Detects the new version for the repository by analyzing the gitflow branch history since the @@ -47,10 +47,9 @@ DESCRIPTION -p Increments PATCH version on _every_ run. WARN: This is intended development use only. - -m Enables mono-repo mode, allowing matching against semvers prefixed with - a product name. Eg: 'cool-app_1.2.3' - - -n The product name to match against. EG: 'bob' would match tags like 'bob_1.2.3'. + -n Enables mono-repo mode allowing the product name to match against tags. + EG: 'bob' would match tags like 'bob_1.2.3'. + TIP: dir names and product names should match. This arg exists in case they do not. -d The directory of the product to version. EG: 'path/to/bob'. @@ -82,7 +81,7 @@ fi # -------------------------------------------------------------------------------------------------- OPTIND=1 -while getopts "he:vfpmn:d:" opt; do +while getopts "he:vfpn:d:" opt; do case $opt in h) printHelp @@ -102,10 +101,6 @@ while getopts "he:vfpmn:d:" opt; do p) arg_p='set' ;; - m) - arg_m='set' - arg_opts="$arg_opts -m" - ;; n) arg_n='set' arg_n_val="$OPTARG" @@ -267,4 +262,4 @@ if [[ -n $arg_e ]]; then export export_var else echo "$newVersion" -fi \ No newline at end of file +fi diff --git a/scripts/validateSemver.sh b/scripts/validateSemver.sh index 5291291..6bd23dd 100755 --- a/scripts/validateSemver.sh +++ b/scripts/validateSemver.sh @@ -15,7 +15,7 @@ NAME validateSemver.sh SYNOPSIS - ${0##*/} [-hpv9] + ${0##*/} [-hpv9nd] DESCRIPTION Validates the schema of a provided version string against the semantic versioning standard. @@ -48,10 +48,9 @@ DESCRIPTION (v)[Major].[Minor].[Patch] - -m Enables mono-repo mode, allowing matching against semvers prefixed with - a product name. Eg: 'cool-app_1.2.3' - - -n The product name to match against. EG: 'bob' would match tags like 'bob_1.2.3'. + -n Enables mono-repo mode allowing the product name to match against tags. + EG: 'bob' would match tags like 'bob_1.2.3'. + TIP: dir names and product names should match. This arg exists in case they do not. -d The directory of the product to version. EG: 'path/to/bob'. @@ -87,7 +86,7 @@ fi # -------------------------------------------------------------------------------------------------- OPTIND=1 -while getopts "hp:v9mn:d:" opt; do +while getopts "hp:v9n:d:" opt; do case $opt in h) printHelp @@ -103,10 +102,6 @@ while getopts "hp:v9mn:d:" opt; do 9) arg_9='set' ;; - m) - arg_m='set' - arg_opts="$arg_opts -m" - ;; n) arg_n='set' arg_n_val="$OPTARG" @@ -135,10 +130,10 @@ tsCmd='date --utc +%FT%T.%3NZ' if [[ -n $arg_9 ]]; then semverRegex="^[v]?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$" - [[ -n $arg_m ]] && semverRegex="([0-9A-Za-z]+)?[_-]?[v]?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$" + [[ -n $arg_d ]] && semverRegex="([0-9A-Za-z]+)?[_-]?[v]?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$" else semverRegex="^[v]?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-([0-9A-Za-z]+))?(\\+((([1-9])|([1-9][0-9]+))))?$" - [[ -n $arg_m ]] && semverRegex="^([0-9A-Za-z]+)?[_-]?[v]?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-([0-9A-Za-z]+))?(\\+((([1-9])|([1-9][0-9]+))))?$" + [[ -n $arg_d ]] && semverRegex="^([0-9A-Za-z]+)?[_-]?[v]?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-([0-9A-Za-z]+))?(\\+((([1-9])|([1-9][0-9]+))))?$" fi # -------------------------------------------------------------------------------------------------- @@ -175,7 +170,7 @@ function validateSemver { else [[ -z $arg_9 && -n $arg_v ]] && echo -e "[$(${tsCmd})] \e[01;31mFATAL\e[00m: '$version' does not match the semver schema: '(v)[Major].[Minor].[Patch](-PRERELEASE)(+BUILD)'!\n" [[ -n $arg_9 && -n $arg_v ]] && echo -e "[$(${tsCmd})] \e[01;31mFATAL\e[00m: '$version' does not match the semver schema: '(v)[Major].[Minor].[Patch]'!\n" - [[ -n $arg_9 && -n $arg_v && -n $arg_m ]] && echo -e "[$(${tsCmd})] \e[01;31mFATAL\e[00m: '$version' does not match the semver schema: '[product_name][-_](v)[Major].[Minor].[Patch]'!\n" + [[ -n $arg_9 && -n $arg_v && -n $arg_d ]] && echo -e "[$(${tsCmd})] \e[01;31mFATAL\e[00m: '$version' does not match the semver schema: '[product_name][-_](v)[Major].[Minor].[Patch]'!\n" exit 1 fi } From e7470ed072b7b8fd0ed4186b1ce4fda6bc22eaf9 Mon Sep 17 00:00:00 2001 From: Alex Atkinson Date: Tue, 17 Dec 2024 17:39:09 -0500 Subject: [PATCH 5/6] THX-1138: Deprecate m some more. --- action.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/action.yml b/action.yml index 3c8bdaf..84795df 100644 --- a/action.yml +++ b/action.yml @@ -13,12 +13,8 @@ inputs: description: "Forces a PATCH increment if no other increment detected. NOTE: This is intended for development purposes only." required: false default: 'false' - mono-repo-mode: - description: "Enable semver matching against any tag structure that includes a valid semver string. Eg: _" - required: false - default: 'false' mono-repo-product-name: - description: "The product name to match against. For example, 'bob', would match the tags like 'bob_1.2.3'. Required if 'mono-repo-mode' is enabled." + description: "Enables mono-repo mode. The product name to match against. EG: 'bob', match the tags like 'bob_1.2.3'." required: false default: '' mono-repo-product-path: @@ -55,8 +51,7 @@ runs: opt='' [[ "${{ inputs.force-re-evaluate }}" == 'true' ]] && opt='$opt -f' [[ "${{ inputs.force-patch-increment }}" == 'true' ]] && opt='$opt -p' - [[ "${{ inputs.mono-repo-mode }}" == 'true' ]] && opt='$opt -m' - if [[ "${{ inputs.mono-repo-mode }}" == 'true' && -z ${{ inputs.mono-repo-product-name }} ]]; then + if [[ -z ${{ inputs.mono-repo-product-name }} ]]; then echo -e "ERROR: 571 - mono-repo-product-name must be set and NOT null!" exit 1 || true fi From 3fde0f01c946e8e84def7e0d43f6ef2396a928da Mon Sep 17 00:00:00 2001 From: Alex Atkinson Date: Tue, 17 Dec 2024 17:47:02 -0500 Subject: [PATCH 6/6] THX-1139: Deprecate m some more. --- README.md | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index acdba89..0fa02e5 100644 --- a/README.md +++ b/README.md @@ -63,13 +63,8 @@ git push --set-upstream origin fix/not-a-feature Note: Only required for setting up mono-repo versioning.
-
mono-repo-mode: [bool]
-
Enable semver matching against any tag structure that includes a valid semver string.
- Eg: '<product-name>_<semver>'
- Required: false
- Default: false
mono-repo-product-name: [string]
-
The product name to match against.
+
Enables mono-repo mode. The product name to match against.
Eg: 'bob', would match the tags like 'bob_1.2.3'.
Required: if mono-repo-mode: true
Default: ''
@@ -113,7 +108,7 @@ Below is a valid workflow utilizing this action. If you wanted to extend it to d - uses: actions/checkout@v3 - name: Run GitOps Automatic Versioning Action id: gitops-autover - uses: AlexAtkinson/github-action-gitops-autover@0.3.0 + uses: AlexAtkinson/github-action-gitops-autover@0.3.1 - name: Verify Outputs run: | NEW_VERSION=${{ steps.gitops-autover.outputs.new-version }} @@ -125,9 +120,8 @@ To make use of the mono-repo support, simply add a block for the director you wi - name: Run GitOps Automatic Versioning Action id: gitops-autover - uses: AlexAtkinson/github-action-gitops-autover@0.3.0 + uses: AlexAtkinson/github-action-gitops-autover@0.3.1 with: - mono-repo-mode: true mono-repo-product-name: bob This results in outputs like: @@ -189,7 +183,7 @@ Additionally, this repo uses its own action for versioning, so feel free to inve echo "PRODUCT_NAME_LOWER=$PRODUCT_NAME_LOWER" >> $GITHUB_OUTPUT - name: GitOps Automatic Versioning id: gitops-autover - uses: AlexAtkinson/github-action-gitops-autover@0.3.0 + uses: AlexAtkinson/github-action-gitops-autover@0.3.1 build: name: "Build" runs-on: ubuntu-latest