mirror of
https://github.com/actions/checkout.git
synced 2026-01-10 15:39:23 +00:00
Fix tag handling: preserve annotations and explicit fetch-tags (#2356)
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
This commit is contained in:
68
dist/index.js
vendored
68
dist/index.js
vendored
@@ -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 {
|
||||
return [`+${ref}:${ref}`];
|
||||
else if (upperRef.startsWith('REFS/TAGS/')) {
|
||||
if (!fetchTags) {
|
||||
result.push(`+${ref}:${ref}`);
|
||||
}
|
||||
}
|
||||
// Other refs
|
||||
else {
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user