Skip to content

Commit e01a5fd

Browse files
authored
Merge pull request #34 from AlexAtkinson/feature/monorepo
Feature/monorepo
2 parents 80bc0c3 + e3a57c9 commit e01a5fd

7 files changed

Lines changed: 731 additions & 230 deletions

File tree

.github/workflows/tests.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: tests
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
9+
jobs:
10+
tests:
11+
name: Run Isolated Test Suite
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v4
16+
- name: Run tests
17+
run: bash scripts/tests.sh

.vscode/settings.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"chat.tools.terminal.autoApprove": {
3+
"/^bash scripts/tests\\.sh$/": {
4+
"approve": true,
5+
"matchCommandLine": true
6+
},
7+
"/^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$/": {
8+
"approve": true,
9+
"matchCommandLine": true
10+
}
11+
},
12+
"cSpell.words": [
13+
"Envars",
14+
"hefpndl",
15+
"marvinpinto",
16+
"vfpn"
17+
]
18+
}

README.md

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Language/content agnostic method of automatically determining the [semantic vers
1212

1313
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.
1414

15-
**Burning Questions**
15+
## Burning Questions
1616

1717
- **Yes** - This can indeed be implemented in repos that previously used different version increment methods.
1818
- **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.
@@ -22,6 +22,7 @@ This is accomplished by counting the merges of branches matching the [naming sch
2222
2323
## Recent Changes
2424

25+
- 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.
2526
- 1.0.0: (non-breaking) Addition of support for mono-repos. IE: Discretely version specific directories.
2627
- 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.
2728
- 0.3.1: Update the checkout action version to v4.
@@ -73,6 +74,16 @@ Note: Only required for setting up mono-repo versioning.
7374
Eg: path/to/bob<br>
7475
<i>Required:</i> if mono-repo-mode: true<br>
7576
<i>Default:</i> ''</dd>
77+
<dt>pr-label-overrides: [string]</dt>
78+
<dd>Optional debug input for supplying PR label overrides manually.<br>
79+
Format: '123=semver:patch;124=semver:minor'<br>
80+
<i>Required:</i> false<br>
81+
<i>Default:</i> ''</dd>
82+
<dt>force-re-evaluate: [bool]</dt>
83+
<dd>Forces a re-evaluation of the entire git history.<br>
84+
Commit classification runs in parallel using nproc-1 jobs (minimum 1).<br>
85+
<i>Required:</i> false<br>
86+
<i>Default:</i> false</dd>
7687
<dt>force-patch-increment: [bool]</dt>
7788
<dd>Forces a PATCH increment if no other increment detected.<br>
7889
(Intended for development purposes only.)<br>
@@ -123,6 +134,7 @@ To make use of the mono-repo support, simply add a block for the director you wi
123134
uses: AlexAtkinson/github-action-gitops-autover@0.3.1
124135
with:
125136
mono-repo-product-name: bob
137+
mono-repo-product-path: path/to/bob
126138

127139
This results in outputs like:
128140

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

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

226+
### PR Label Precedence
227+
228+
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.
229+
230+
Label lookup support by platform:
231+
232+
<dl>
233+
<dt>GitHub</dt>
234+
<dd>Native label lookup. Requires GITHUB_TOKEN (or AUTOVER_GITHUB_TOKEN) and GITHUB_REPOSITORY.</dd>
235+
<dt>GitLab</dt>
236+
<dd>Native label lookup. Requires GITLAB_TOKEN (or AUTOVER_GITLAB_TOKEN) and CI_PROJECT_ID (or AUTOVER_GITLAB_PROJECT).</dd>
237+
<dt>Bitbucket</dt>
238+
<dd>Bitbucket Cloud has no PR labels. Use 'pr-label-overrides' or AUTOVER_PR_LABEL_OVERRIDES.</dd>
239+
</dl>
240+
241+
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.
242+
243+
### Squash Merges
244+
245+
Squash merges are supported where the PR/MR number can be recovered from the commit:
246+
247+
- GitHub: squash commit subjects retain the PR number as a '(#123)' suffix.
248+
- Bitbucket: commit subjects use 'Merged in <branch> (pull request #123)'.
249+
- GitLab: the MR reference ('!123') is read from the commit subject or body.
250+
251+
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.
252+
214253
## Appropriate Use Cases
215254

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

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

220259
This action is _not_ suitable for projects requiring:
221-
- pre-release, beta, etc., type fields. Such projects should depend upon their own language native tooling.
222-
- specific version numbers to be planned and orchestrated ahead of time (usually marketing efforts).
223-
- Exception: Major releases. These can be actioned on demand as outlined below.
224-
- rebase merges. Reminder: this action _depends_ on merge commit messages.
225-
- Exception: Patterns like: main < (merge-commit) < staging-branch < (rebase) work-branches
226-
- As long as main|master gets a merge commit message, everyone is happy.
260+
261+
- pre-release, beta, etc., type fields. Such projects should depend upon their own language native tooling.
262+
- specific version numbers to be planned and orchestrated ahead of time (usually marketing efforts).
263+
- Exception: Major releases. These can be actioned on demand as outlined below.
264+
- rebase merges. Reminder: this action _depends_ on merge or squash commit messages.
265+
- Exception: Patterns like: main < (merge-commit) < staging-branch < (rebase) work-branches
266+
- As long as main|master gets a merge commit message, everyone is happy.
267+
- Exception: Squash merges where the PR carries a semver label (see Squash Merges above).
227268

228269
## Version Format
229270

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

290+
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.
291+
249292
## Version Increment Logic
250293

251294
For those interested, here's some pseudo code:
252295

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

279324
- 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.
280325

326+
- If a PR/MR label lookup is configured but fails (network, auth, etc.), the action fails with:
327+
328+
ERROR: 592 - PR/MR label lookup failed!
329+
330+
This is intentional: a version must never be computed from incomplete label data.
331+
332+
## Local Testing
333+
334+
Run the isolated test harness locally with:
335+
336+
bash scripts/tests.sh
337+
338+
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.
339+
281340
- Merged branches not conforming to the above naming scheme will simply be ignored.
282341
- HINT: This can be useful when you don't want to increment the version.
283342
- Align this with build 'on:push:branches:' workflow configuration to avoid unnecessary builds.

action.yml

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ inputs:
2121
description: "The path to the product. IE: 'path/to/bob'. Required if 'mono-repo-mode' is enabled."
2222
required: false
2323
default: ''
24+
pr-label-overrides:
25+
description: "Optional/debug PR label overrides. Format: '123=semver:patch;124=semver:minor'."
26+
required: false
27+
default: ''
2428
outputs:
2529
new-version:
2630
description: "New Version"
@@ -45,19 +49,23 @@ runs:
4549
id: previous-version
4650
run: |
4751
cd $GITHUB_WORKSPACE
48-
echo "previous-version=$(${{ github.action_path }}/scripts/detectPreviousVersion.sh)" | tee $GITHUB_OUTPUT
52+
opt=()
53+
[[ "${{ inputs.mono-repo-product-name }}" != "" ]] && opt+=(-n "${{ inputs.mono-repo-product-name }}")
54+
[[ "${{ inputs.mono-repo-product-path }}" != "" ]] && opt+=(-d "${{ inputs.mono-repo-product-path }}")
55+
echo "previous-version=$(${{ github.action_path }}/scripts/detectPreviousVersion.sh "${opt[@]}")" | tee $GITHUB_OUTPUT
4956
shell: bash
5057
- name: Detect New Version
5158
id: new-version
5259
run: |
5360
cd $GITHUB_WORKSPACE
54-
opt=''
55-
[[ "${{ github.event.inputs.force-re-evaluate }}" == 'true' ]] && opt='$opt -f'
56-
[[ "${{ github.event.inputs.force-patch-increment }}" == 'true' ]] && opt='$opt -p'
57-
[[ "${{ github.event.inputs.mono-repo-product-name }}" != "" ]] && opt='$opt -n ${{ github.event.inputs.mono-repo-product-name }}'
58-
[[ "${{ github.event.inputs.mono-repo-product-path }}" != "" ]] && opt='$opt -d ${{ github.event.inputs.mono-repo-product-path }}'
59-
new_version="$(${{ github.action_path }}/scripts/detectNewVersion.sh $opt)" || true
60-
debug_cmd="echo ${{ github.action_path }}/scripts/detectNewVersion.sh $opt" || true
61+
opt=()
62+
[[ "${{ inputs.force-re-evaluate }}" == 'true' ]] && opt+=(-f)
63+
[[ "${{ inputs.force-patch-increment }}" == 'true' ]] && opt+=(-p)
64+
[[ "${{ inputs.mono-repo-product-name }}" != "" ]] && opt+=(-n "${{ inputs.mono-repo-product-name }}")
65+
[[ "${{ inputs.mono-repo-product-path }}" != "" ]] && opt+=(-d "${{ inputs.mono-repo-product-path }}")
66+
[[ "${{ inputs.pr-label-overrides }}" != "" ]] && opt+=(-l "${{ inputs.pr-label-overrides }}")
67+
new_version="$(${{ github.action_path }}/scripts/detectNewVersion.sh "${opt[@]}")" || true
68+
debug_cmd="${{ github.action_path }}/scripts/detectNewVersion.sh $(printf '%q ' "${opt[@]}")" || true
6169
echo "new-version=$new_version" | tee $GITHUB_OUTPUT
6270
if [[ "$new_version" =~ "520" ]]; then
6371
echo -e "ERROR: 520 - You must source this script when specifying an environment variable! Eg: '. ./foo.sh -e bar_ver'"
@@ -75,21 +83,28 @@ runs:
7583
echo -e "ERROR: 591 - Unsupported origin host."
7684
exit 1 || true
7785
fi
86+
if [[ "$new_version" =~ "592" ]]; then
87+
echo -e "ERROR: 592 - PR/MR label lookup failed!"
88+
exit 1 || true
89+
fi
7890
if [[ "$new_version" =~ "599" ]]; then
7991
echo -e "ERROR: 599 - No feature, enhancement, fix, bugfix, hotfix, or ops branches detected!"
8092
exit 1 || true
8193
fi
8294
shell: bash
95+
env:
96+
GITHUB_TOKEN: ${{ github.token }}
8397
- name: Debug Cmd
8498
id: debug-cmd
8599
run: |
86100
cd $GITHUB_WORKSPACE
87-
opt=''
88-
[[ "${{ github.event.inputs.force-re-evaluate }}" == 'true' ]] && opt='$opt -f'
89-
[[ "${{ github.event.inputs.force-patch-increment }}" == 'true' ]] && opt='$opt -p'
90-
[[ "${{ github.event.inputs.mono-repo-product-name }}" != "" ]] && opt='$opt -n ${{ github.event.inputs.mono-repo-product-name }}'
91-
[[ "${{ github.event.inputs.mono-repo-product-path }}" != "" ]] && opt='$opt -d ${{ github.event.inputs.mono-repo-product-path }}'
92-
debug_cmd="echo ${{ github.action_path }}/scripts/detectNewVersion.sh $opt" || true
101+
opt=()
102+
[[ "${{ inputs.force-re-evaluate }}" == 'true' ]] && opt+=(-f)
103+
[[ "${{ inputs.force-patch-increment }}" == 'true' ]] && opt+=(-p)
104+
[[ "${{ inputs.mono-repo-product-name }}" != "" ]] && opt+=(-n "${{ inputs.mono-repo-product-name }}")
105+
[[ "${{ inputs.mono-repo-product-path }}" != "" ]] && opt+=(-d "${{ inputs.mono-repo-product-path }}")
106+
[[ "${{ inputs.pr-label-overrides }}" != "" ]] && opt+=(-l "${{ inputs.pr-label-overrides }}")
107+
debug_cmd="${{ github.action_path }}/scripts/detectNewVersion.sh $(printf '%q ' "${opt[@]}")" || true
93108
echo "debug-cmd=$debug_cmd" | tee $GITHUB_OUTPUT
94109
shell: bash
95110

0 commit comments

Comments
 (0)