diff options
| author | eric sciple <ericsciple@users.noreply.github.com> | 2026-01-09 13:42:23 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-01-09 13:42:23 -0600 |
| commit | 5921bc8a76f21dbff8ef5088dc2dc3b479ab5309 (patch) | |
| tree | b4c76471127cb4a7e8d0205a8ed8b04f646baaa8 /dist/index.js | |
| parent | 72bc64336da5b556e438a70b19caa4f80eebee98 (diff) | |
Fix tag handling: preserve annotations and explicit fetch-tags (#2356)v6.0.2
This PR fixes several issues with tag handling in the checkout action:
1. fetch-tags: true now works (fixes #1471)
- Tags refspec is now included in getRefSpec() when fetchTags=true
- Previously tags were only fetched during a separate fetch that was
overwritten by the main fetch
2. Tag checkout preserves annotations (fixes #290)
- Tags are fetched via refspec (+refs/tags/*:refs/tags/*) instead of
--tags flag
- This fetches the actual tag objects, preserving annotations
3. Tag checkout with fetch-tags: true no longer fails (fixes #1467)
- When checking out a tag with fetchTags=true, only the wildcard
refspec is used (specific tag refspec is redundant)
Changes:
- src/ref-helper.ts: getRefSpec() now accepts fetchTags parameter and
prepends tags refspec when true
- src/git-command-manager.ts: fetch() simplified to always use --no-tags,
tags are fetched explicitly via refspec
- src/git-source-provider.ts: passes fetchTags to getRefSpec()
- Added E2E test for fetch-tags option
Related #1471, #1467, #290
Diffstat (limited to 'dist/index.js')
| -rw-r--r-- | dist/index.js | 66 |
1 files changed, 47 insertions, 19 deletions
diff --git a/dist/index.js b/dist/index.js index 4eab86e..fe3f317 100644 --- a/dist/index.js +++ b/dist/index.js @@ -653,7 +653,6 @@ const fs = __importStar(__nccwpck_require__(7147)); const fshelper = __importStar(__nccwpck_require__(7219)); const io = __importStar(__nccwpck_require__(7436)); const path = __importStar(__nccwpck_require__(1017)); -const refHelper = __importStar(__nccwpck_require__(8601)); const regexpHelper = __importStar(__nccwpck_require__(3120)); const retryHelper = __importStar(__nccwpck_require__(2155)); const git_version_1 = __nccwpck_require__(3142); @@ -831,9 +830,9 @@ class GitCommandManager { fetch(refSpec, options) { return __awaiter(this, void 0, void 0, function* () { const args = ['-c', 'protocol.version=2', 'fetch']; - if (!refSpec.some(x => x === refHelper.tagsRefSpec) && !options.fetchTags) { - args.push('--no-tags'); - } + // Always use --no-tags for explicit control over tag fetching + // Tags are fetched explicitly via refspec when needed + args.push('--no-tags'); args.push('--prune', '--no-recurse-submodules'); if (options.showProgress) { args.push('--progress'); @@ -1539,13 +1538,26 @@ function getSource(settings) { if (!(yield refHelper.testRef(git, settings.ref, settings.commit))) { refSpec = refHelper.getRefSpec(settings.ref, settings.commit); yield git.fetch(refSpec, fetchOptions); + // Verify the ref now matches. For branches, the targeted fetch above brings + // in the specific commit. For tags (fetched by ref), this will fail if + // the tag was moved after the workflow was triggered. + if (!(yield refHelper.testRef(git, settings.ref, settings.commit))) { + throw new Error(`The ref '${settings.ref}' does not point to the expected commit '${settings.commit}'. ` + + `The ref may have been updated after the workflow was triggered.`); + } } } else { fetchOptions.fetchDepth = settings.fetchDepth; - fetchOptions.fetchTags = settings.fetchTags; - const refSpec = refHelper.getRefSpec(settings.ref, settings.commit); + const refSpec = refHelper.getRefSpec(settings.ref, settings.commit, settings.fetchTags); yield git.fetch(refSpec, fetchOptions); + // For tags, verify the ref still points to the expected commit. + // Tags are fetched by ref (not commit), so if a tag was moved after the + // workflow was triggered, we would silently check out the wrong commit. + if (!(yield refHelper.testRef(git, settings.ref, settings.commit))) { + throw new Error(`The ref '${settings.ref}' does not point to the expected commit '${settings.commit}'. ` + + `The ref may have been updated after the workflow was triggered.`); + } } core.endGroup(); // Checkout info @@ -2284,53 +2296,67 @@ function getRefSpecForAllHistory(ref, commit) { } return result; } -function getRefSpec(ref, commit) { +function getRefSpec(ref, commit, fetchTags) { if (!ref && !commit) { throw new Error('Args ref and commit cannot both be empty'); } const upperRef = (ref || '').toUpperCase(); + const result = []; + // When fetchTags is true, always include the tags refspec + if (fetchTags) { + result.push(exports.tagsRefSpec); + } // SHA if (commit) { // refs/heads if (upperRef.startsWith('REFS/HEADS/')) { const branch = ref.substring('refs/heads/'.length); - return [`+${commit}:refs/remotes/origin/${branch}`]; + result.push(`+${commit}:refs/remotes/origin/${branch}`); } // refs/pull/ else if (upperRef.startsWith('REFS/PULL/')) { const branch = ref.substring('refs/pull/'.length); - return [`+${commit}:refs/remotes/pull/${branch}`]; + result.push(`+${commit}:refs/remotes/pull/${branch}`); } // refs/tags/ else if (upperRef.startsWith('REFS/TAGS/')) { - return [`+${commit}:${ref}`]; + if (!fetchTags) { + result.push(`+${ref}:${ref}`); + } } // Otherwise no destination ref else { - return [commit]; + result.push(commit); } } // Unqualified ref, check for a matching branch or tag else if (!upperRef.startsWith('REFS/')) { - return [ - `+refs/heads/${ref}*:refs/remotes/origin/${ref}*`, - `+refs/tags/${ref}*:refs/tags/${ref}*` - ]; + result.push(`+refs/heads/${ref}*:refs/remotes/origin/${ref}*`); + if (!fetchTags) { + result.push(`+refs/tags/${ref}*:refs/tags/${ref}*`); + } } // refs/heads/ else if (upperRef.startsWith('REFS/HEADS/')) { const branch = ref.substring('refs/heads/'.length); - return [`+${ref}:refs/remotes/origin/${branch}`]; + result.push(`+${ref}:refs/remotes/origin/${branch}`); } // refs/pull/ else if (upperRef.startsWith('REFS/PULL/')) { const branch = ref.substring('refs/pull/'.length); - return [`+${ref}:refs/remotes/pull/${branch}`]; + result.push(`+${ref}:refs/remotes/pull/${branch}`); } // refs/tags/ + else if (upperRef.startsWith('REFS/TAGS/')) { + if (!fetchTags) { + result.push(`+${ref}:${ref}`); + } + } + // Other refs else { - return [`+${ref}:${ref}`]; + result.push(`+${ref}:${ref}`); } + return result; } /** * Tests whether the initial fetch created the ref at the expected commit @@ -2366,7 +2392,9 @@ function testRef(git, ref, commit) { // refs/tags/ else if (upperRef.startsWith('REFS/TAGS/')) { const tagName = ref.substring('refs/tags/'.length); - return ((yield git.tagExists(tagName)) && commit === (yield git.revParse(ref))); + // Use ^{commit} to dereference annotated tags to their underlying commit + return ((yield git.tagExists(tagName)) && + commit === (yield git.revParse(`${ref}^{commit}`))); } // Unexpected else { |
