Skip to content
Merged
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
17 changes: 17 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: tests

on:
push:
branches:
- main
pull_request:

jobs:
tests:
name: Run Isolated Test Suite
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Run tests
run: bash scripts/tests.sh
18 changes: 18 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"chat.tools.terminal.autoApprove": {
"/^bash scripts/tests\\.sh$/": {
"approve": true,
"matchCommandLine": true
},
"/^tmpdir=\\$\\(mktemp -d\\) && cd \"\\$tmpdir\" && git init >/dev/null && git config user\\.email test@example\\.com && git config user\\.name tester && git remote add origin git@github\\.com:example/test\\.git && mkdir C && echo init > README\\.md && git add README\\.md C && git commit -m init >/dev/null && echo one > C/file1 && git add C/file1 && git commit -m 'Merge pull request #101 from example/ops/C_1' >/dev/null && git tag C_1\\.0\\.0 && echo major > C/file2 && git add C/file2 && bash -x /home/alex/git/alexatkinson/github-action-gitops-autover/scripts/detectNewVersion\\.sh -d C -n C$/": {
"approve": true,
"matchCommandLine": true
}
},
"cSpell.words": [
"Envars",
"hefpndl",
"marvinpinto",
"vfpn"
]
}
75 changes: 67 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Language/content agnostic method of automatically determining the [semantic vers

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.

**Burning Questions**
## 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.
Expand All @@ -22,6 +22,7 @@ This is accomplished by counting the merges of branches matching the [naming sch

## Recent Changes

- 1.1.0: Squash-merge support (PR/MR classified via semver labels), GitLab label lookups, hard-fail on label lookup errors (592), and parallel full-history re-evaluation.
- 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.
Expand Down Expand Up @@ -73,6 +74,16 @@ Note: Only required for setting up mono-repo versioning.
Eg: path/to/bob<br>
<i>Required:</i> if mono-repo-mode: true<br>
<i>Default:</i> ''</dd>
<dt>pr-label-overrides: [string]</dt>
<dd>Optional debug input for supplying PR label overrides manually.<br>
Format: '123=semver:patch;124=semver:minor'<br>
<i>Required:</i> false<br>
<i>Default:</i> ''</dd>
<dt>force-re-evaluate: [bool]</dt>
<dd>Forces a re-evaluation of the entire git history.<br>
Commit classification runs in parallel using nproc-1 jobs (minimum 1).<br>
<i>Required:</i> false<br>
<i>Default:</i> false</dd>
<dt>force-patch-increment: [bool]</dt>
<dd>Forces a PATCH increment if no other increment detected.<br>
(Intended for development purposes only.)<br>
Expand Down Expand Up @@ -123,6 +134,7 @@ To make use of the mono-repo support, simply add a block for the director you wi
uses: AlexAtkinson/github-action-gitops-autover@0.3.1
with:
mono-repo-product-name: bob
mono-repo-product-path: path/to/bob

This results in outputs like:

Expand Down Expand Up @@ -211,19 +223,48 @@ This action depends _only_ on the following _branch naming scheme_ being observe

For example, the name of the branch for a new awesome feature named Awesome Feature, might be: 'feature/awesome_feature'.

### PR Label Precedence

When a merged PR has one of the labels 'semver:patch', 'semver:minor', 'semver:major', or 'semver:breaking', that label takes precedence over the branch name for bump selection.

Label lookup support by platform:

<dl>
<dt>GitHub</dt>
<dd>Native label lookup. Requires GITHUB_TOKEN (or AUTOVER_GITHUB_TOKEN) and GITHUB_REPOSITORY.</dd>
<dt>GitLab</dt>
<dd>Native label lookup. Requires GITLAB_TOKEN (or AUTOVER_GITLAB_TOKEN) and CI_PROJECT_ID (or AUTOVER_GITLAB_PROJECT).</dd>
<dt>Bitbucket</dt>
<dd>Bitbucket Cloud has no PR labels. Use 'pr-label-overrides' or AUTOVER_PR_LABEL_OVERRIDES.</dd>
</dl>

If a label lookup is configured (credentials present) but cannot complete, the run fails with error 592 rather than silently falling back to branch-name classification.

### Squash Merges

Squash merges are supported where the PR/MR number can be recovered from the commit:

- GitHub: squash commit subjects retain the PR number as a '(#123)' suffix.
- Bitbucket: commit subjects use 'Merged in <branch> (pull request #123)'.
- GitLab: the MR reference ('!123') is read from the commit subject or body.

Since a squashed commit does not carry the source branch name, classification of squash merges relies on semver labels (or overrides). Unlabeled squash merges are ignored.

## Appropriate Use Cases

This action is _most_ suitable for git projects with the following operational design:

- Each merge into main|master is intended to produce an artifact, following the "everything is potentially releasabe" approach.

This action is _not_ suitable for projects requiring:
- pre-release, beta, etc., type fields. Such projects should depend upon their own language native tooling.
- specific version numbers to be planned and orchestrated ahead of time (usually marketing efforts).
- Exception: Major releases. These can be actioned on demand as outlined below.
- rebase merges. Reminder: this action _depends_ on merge commit messages.
- Exception: Patterns like: main < (merge-commit) < staging-branch < (rebase) work-branches
- As long as main|master gets a merge commit message, everyone is happy.

- pre-release, beta, etc., type fields. Such projects should depend upon their own language native tooling.
- specific version numbers to be planned and orchestrated ahead of time (usually marketing efforts).
- Exception: Major releases. These can be actioned on demand as outlined below.
- rebase merges. Reminder: this action _depends_ on merge or squash commit messages.
- Exception: Patterns like: main < (merge-commit) < staging-branch < (rebase) work-branches
- As long as main|master gets a merge commit message, everyone is happy.
- Exception: Squash merges where the PR carries a semver label (see Squash Merges above).

## Version Format

Expand All @@ -246,11 +287,13 @@ This increment can be accomplished in one of the following ways:
git tag 1.0.0
git push --tags

In mono-repo mode, the MAJOR indicator must appear on a commit that touches the scoped directory, otherwise that product's history will not include it.

## Version Increment Logic

For those interested, here's some pseudo code:

lastMajor = Extract from previous git tag (why option 1 is recommended)
lastMajor = Extract from previous git tag on the repo or scoped directory history
lastMinor = Extract from previous git tag
lastPatch = Extract from previous git tag
IF no previous git tag; THEN
Expand All @@ -261,6 +304,8 @@ For those interested, here's some pseudo code:
MAJOR = lastMajor + 1
MINOR = 0
PATCH = 0
ELSEIF merged PR has a semver label; THEN
use that label's bump instead of the branch name
ELSEIF merged feature/.* or enhancement/.* branches; THEN
MAJOR = lastMajor
MINOR = lastMinor + count of merged branches
Expand All @@ -278,6 +323,20 @@ For those interested, here's some pseudo code:

- When encountering this scenario, and a build is desired, you can simply create a branch with the appropriate naming convention and an empty commit, then merge it. Or use the bump scripts in the 'scripts/' directory of the repo for this action.

- If a PR/MR label lookup is configured but fails (network, auth, etc.), the action fails with:

ERROR: 592 - PR/MR label lookup failed!

This is intentional: a version must never be computed from incomplete label data.

## Local Testing

Run the isolated test harness locally with:

bash scripts/tests.sh

The script creates a temporary git repository, exercises repo and directory versioning paths, validates PR label precedence, squash-merge classification, and parallel full-history re-evaluation, and removes the fixture repo on exit. The same suite runs in CI on every pull request.

- Merged branches not conforming to the above naming scheme will simply be ignored.
- HINT: This can be useful when you don't want to increment the version.
- Align this with build 'on:push:branches:' workflow configuration to avoid unnecessary builds.
Expand Down
43 changes: 29 additions & 14 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ inputs:
description: "The path to the product. IE: 'path/to/bob'. Required if 'mono-repo-mode' is enabled."
required: false
default: ''
pr-label-overrides:
description: "Optional/debug PR label overrides. Format: '123=semver:patch;124=semver:minor'."
required: false
default: ''
outputs:
new-version:
description: "New Version"
Expand All @@ -45,19 +49,23 @@ runs:
id: previous-version
run: |
cd $GITHUB_WORKSPACE
echo "previous-version=$(${{ github.action_path }}/scripts/detectPreviousVersion.sh)" | tee $GITHUB_OUTPUT
opt=()
[[ "${{ inputs.mono-repo-product-name }}" != "" ]] && opt+=(-n "${{ inputs.mono-repo-product-name }}")
[[ "${{ inputs.mono-repo-product-path }}" != "" ]] && opt+=(-d "${{ inputs.mono-repo-product-path }}")
echo "previous-version=$(${{ github.action_path }}/scripts/detectPreviousVersion.sh "${opt[@]}")" | tee $GITHUB_OUTPUT
shell: bash
- name: Detect New Version
id: new-version
run: |
cd $GITHUB_WORKSPACE
opt=''
[[ "${{ github.event.inputs.force-re-evaluate }}" == 'true' ]] && opt='$opt -f'
[[ "${{ github.event.inputs.force-patch-increment }}" == 'true' ]] && opt='$opt -p'
[[ "${{ github.event.inputs.mono-repo-product-name }}" != "" ]] && opt='$opt -n ${{ github.event.inputs.mono-repo-product-name }}'
[[ "${{ github.event.inputs.mono-repo-product-path }}" != "" ]] && opt='$opt -d ${{ github.event.inputs.mono-repo-product-path }}'
new_version="$(${{ github.action_path }}/scripts/detectNewVersion.sh $opt)" || true
debug_cmd="echo ${{ github.action_path }}/scripts/detectNewVersion.sh $opt" || true
opt=()
[[ "${{ inputs.force-re-evaluate }}" == 'true' ]] && opt+=(-f)
[[ "${{ inputs.force-patch-increment }}" == 'true' ]] && opt+=(-p)
[[ "${{ inputs.mono-repo-product-name }}" != "" ]] && opt+=(-n "${{ inputs.mono-repo-product-name }}")
[[ "${{ inputs.mono-repo-product-path }}" != "" ]] && opt+=(-d "${{ inputs.mono-repo-product-path }}")
[[ "${{ inputs.pr-label-overrides }}" != "" ]] && opt+=(-l "${{ inputs.pr-label-overrides }}")
new_version="$(${{ github.action_path }}/scripts/detectNewVersion.sh "${opt[@]}")" || true
debug_cmd="${{ github.action_path }}/scripts/detectNewVersion.sh $(printf '%q ' "${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'"
Expand All @@ -75,21 +83,28 @@ runs:
echo -e "ERROR: 591 - Unsupported origin host."
exit 1 || true
fi
if [[ "$new_version" =~ "592" ]]; then
echo -e "ERROR: 592 - PR/MR label lookup failed!"
exit 1 || true
fi
if [[ "$new_version" =~ "599" ]]; then
echo -e "ERROR: 599 - No feature, enhancement, fix, bugfix, hotfix, or ops branches detected!"
exit 1 || true
fi
shell: bash
env:
GITHUB_TOKEN: ${{ github.token }}
- name: Debug Cmd
id: debug-cmd
run: |
cd $GITHUB_WORKSPACE
opt=''
[[ "${{ github.event.inputs.force-re-evaluate }}" == 'true' ]] && opt='$opt -f'
[[ "${{ github.event.inputs.force-patch-increment }}" == 'true' ]] && opt='$opt -p'
[[ "${{ github.event.inputs.mono-repo-product-name }}" != "" ]] && opt='$opt -n ${{ github.event.inputs.mono-repo-product-name }}'
[[ "${{ github.event.inputs.mono-repo-product-path }}" != "" ]] && opt='$opt -d ${{ github.event.inputs.mono-repo-product-path }}'
debug_cmd="echo ${{ github.action_path }}/scripts/detectNewVersion.sh $opt" || true
opt=()
[[ "${{ inputs.force-re-evaluate }}" == 'true' ]] && opt+=(-f)
[[ "${{ inputs.force-patch-increment }}" == 'true' ]] && opt+=(-p)
[[ "${{ inputs.mono-repo-product-name }}" != "" ]] && opt+=(-n "${{ inputs.mono-repo-product-name }}")
[[ "${{ inputs.mono-repo-product-path }}" != "" ]] && opt+=(-d "${{ inputs.mono-repo-product-path }}")
[[ "${{ inputs.pr-label-overrides }}" != "" ]] && opt+=(-l "${{ inputs.pr-label-overrides }}")
debug_cmd="${{ github.action_path }}/scripts/detectNewVersion.sh $(printf '%q ' "${opt[@]}")" || true
echo "debug-cmd=$debug_cmd" | tee $GITHUB_OUTPUT
shell: bash

Expand Down
Loading
Loading