Skip to content

Commit 257c6a7

Browse files
committed
bugfix-329-it-ends-up-stuck-in-an-unknown-version-loop-if-no-version-is-detected-in-the-repository: Implement default version handling in Execution and InitialSetupUseCase. Update tests to verify default version creation when no tags exist. Enhance title update logic to prevent 'Unknown Version' loops. Add utility functions for default version management.
1 parent cc637b2 commit 257c6a7

20 files changed

Lines changed: 399 additions & 41 deletions

build/cli/index.js

Lines changed: 66 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48252,10 +48252,7 @@ class Execution {
4825248252
if (this.release.type === undefined) {
4825348253
return;
4825448254
}
48255-
const lastTag = await branchRepository.getLatestTag();
48256-
if (lastTag === undefined) {
48257-
return;
48258-
}
48255+
const lastTag = await branchRepository.getLatestTag() ?? version_utils_1.DEFAULT_BASE_VERSION;
4825948256
this.release.version = (0, version_utils_1.incrementVersion)(lastTag, this.release.type);
4826048257
}
4826148258
}
@@ -48269,10 +48266,7 @@ class Execution {
4826948266
this.hotfix.version = versionInfo.payload['hotfixVersion'];
4827048267
}
4827148268
else {
48272-
this.hotfix.baseVersion = await branchRepository.getLatestTag();
48273-
if (this.hotfix.baseVersion === undefined) {
48274-
return;
48275-
}
48269+
this.hotfix.baseVersion = await branchRepository.getLatestTag() ?? version_utils_1.DEFAULT_BASE_VERSION;
4827648270
this.hotfix.version = (0, version_utils_1.incrementVersion)(this.hotfix.baseVersion, 'Patch');
4827748271
}
4827848272
this.hotfix.branch = `${this.branches.hotfixTree}/${this.hotfix.version}`;
@@ -50413,6 +50407,7 @@ class IssueRepository {
5041350407
}
5041450408
const sanitizedTitle = issueTitle
5041550409
.replace(/\b\d+(\.\d+){2,}\b/g, '')
50410+
.replace(/\bUnknown Version\b/gi, '')
5041650411
.replace(/[^\p{L}\p{N}\p{P}\p{Z}^$\n]/gu, '')
5041750412
.replace(/\u200D/g, '')
5041850413
.replace(/[^\S\r\n]+/g, ' ')
@@ -52164,6 +52159,19 @@ class ProjectRepository {
5216452159
return undefined;
5216552160
}
5216652161
};
52162+
this.getDefaultBranch = async (owner, repo, token) => {
52163+
try {
52164+
const octokit = github.getOctokit(token);
52165+
const { data } = await octokit.rest.repos.get({ owner, repo });
52166+
const branch = data.default_branch;
52167+
(0, logger_1.logDebugInfo)(`Default branch for ${owner}/${repo}: ${branch}`);
52168+
return branch;
52169+
}
52170+
catch (error) {
52171+
(0, logger_1.logError)(`Error getting default branch for ${owner}/${repo}: ${error}`);
52172+
return undefined;
52173+
}
52174+
};
5216752175
this.createTag = async (owner, repo, branch, tag, token) => {
5216852176
const octokit = github.getOctokit(token);
5216952177
try {
@@ -54189,9 +54197,11 @@ exports.DeployedActionUseCase = DeployedActionUseCase;
5418954197

5419054198
Object.defineProperty(exports, "__esModule", ({ value: true }));
5419154199
exports.InitialSetupUseCase = void 0;
54200+
const branch_repository_1 = __nccwpck_require__(7701);
5419254201
const issue_repository_1 = __nccwpck_require__(57);
5419354202
const project_repository_1 = __nccwpck_require__(7917);
5419454203
const result_1 = __nccwpck_require__(7305);
54204+
const version_utils_1 = __nccwpck_require__(9887);
5419554205
const logger_1 = __nccwpck_require__(8836);
5419654206
const task_emoji_1 = __nccwpck_require__(9785);
5419754207
const setup_files_1 = __nccwpck_require__(1666);
@@ -54266,6 +54276,14 @@ class InitialSetupUseCase {
5426654276
else {
5426754277
steps.push(`✅ Issue types checked: ${issueTypesResult.created} created, ${issueTypesResult.existing} already existed`);
5426854278
}
54279+
// 4. Si no hay tags en el repo, crear versión por defecto v1.0.0
54280+
const defaultVersionResult = await this.ensureDefaultVersion(param);
54281+
if (defaultVersionResult.step) {
54282+
steps.push(defaultVersionResult.step);
54283+
}
54284+
if (defaultVersionResult.error) {
54285+
errors.push(defaultVersionResult.error);
54286+
}
5426954287
results.push(new result_1.Result({
5427054288
id: this.taskId,
5427154289
success: errors.length === 0,
@@ -54342,6 +54360,39 @@ class InitialSetupUseCase {
5434254360
return { success: false, created: 0, existing: 0, errors: [`Error asegurando tipos de Issue: ${error}`] };
5434354361
}
5434454362
}
54363+
/**
54364+
* If the repository has no version tags, create default tag v1.0.0 on the default branch.
54365+
* Used by "copilot setup" so release/hotfix issues get a base version.
54366+
*/
54367+
async ensureDefaultVersion(param) {
54368+
try {
54369+
const branchRepository = new branch_repository_1.BranchRepository();
54370+
const existingTag = await branchRepository.getLatestTag();
54371+
if (existingTag !== undefined) {
54372+
(0, logger_1.logDebugInfo)(`Repository already has version tags (latest: ${existingTag}). Skipping default tag.`);
54373+
return {};
54374+
}
54375+
(0, logger_1.logInfo)(`🏷️ No version tags found. Creating default tag ${version_utils_1.DEFAULT_INITIAL_TAG}...`);
54376+
const projectRepository = new project_repository_1.ProjectRepository();
54377+
const defaultBranch = await projectRepository.getDefaultBranch(param.owner, param.repo, param.tokens.token);
54378+
if (!defaultBranch) {
54379+
const msg = 'Could not get default branch to create initial version tag.';
54380+
(0, logger_1.logError)(msg);
54381+
return { error: msg };
54382+
}
54383+
const sha = await projectRepository.createTag(param.owner, param.repo, defaultBranch, version_utils_1.DEFAULT_INITIAL_TAG, param.tokens.token);
54384+
if (sha) {
54385+
const step = `✅ Default version tag ${version_utils_1.DEFAULT_INITIAL_TAG} created on branch ${defaultBranch}. Run \`git fetch --tags\` to update local refs.`;
54386+
return { step };
54387+
}
54388+
return { error: `Failed to create tag ${version_utils_1.DEFAULT_INITIAL_TAG} on ${param.owner}/${param.repo}` };
54389+
}
54390+
catch (error) {
54391+
const msg = `Error ensuring default version: ${error}`;
54392+
(0, logger_1.logError)(msg);
54393+
return { error: msg };
54394+
}
54395+
}
5434554396
}
5434654397
exports.InitialSetupUseCase = InitialSetupUseCase;
5434754398

@@ -57720,10 +57771,10 @@ class UpdateTitleUseCase {
5772057771
const _title = await this.issueRepository.getTitle(param.owner, param.repo, param.issue.number, param.tokens.token) ?? param.issue.title;
5772157772
let _version = '';
5772257773
if (param.release.active) {
57723-
_version = param.release.version ?? 'Unknown Version';
57774+
_version = param.release.version ?? '';
5772457775
}
5772557776
else if (param.hotfix.active) {
57726-
_version = param.hotfix.version ?? 'Unknown Version';
57777+
_version = param.hotfix.version ?? '';
5772757778
}
5772857779
const title = await this.issueRepository.updateTitleIssueFormat(param.owner, param.repo, _version, _title, param.issue.number, param.issue.branchManagementAlways, param.emoji.branchManagementEmoji, param.labels, param.tokens.token);
5772957780
if (title) {
@@ -61018,8 +61069,12 @@ exports.extractIssueNumberFromPush = extractIssueNumberFromPush;
6101861069
"use strict";
6101961070

6102061071
Object.defineProperty(exports, "__esModule", ({ value: true }));
61021-
exports.getLatestVersion = exports.incrementVersion = void 0;
61072+
exports.getLatestVersion = exports.incrementVersion = exports.DEFAULT_INITIAL_TAG = exports.DEFAULT_BASE_VERSION = void 0;
6102261073
const logger_1 = __nccwpck_require__(8836);
61074+
/** Default base version when the repository has no existing tags (e.g. new repo). */
61075+
exports.DEFAULT_BASE_VERSION = '1.0.0';
61076+
/** Default initial tag name (with "v" prefix) for repos with no tags. Used by setup. */
61077+
exports.DEFAULT_INITIAL_TAG = `v${exports.DEFAULT_BASE_VERSION}`;
6102361078
const incrementVersion = (version, releaseType) => {
6102461079
(0, logger_1.logDebugInfo)(`Incrementing version ${version}.`);
6102561080
const versionParts = version.split('.').map(Number);

build/cli/src/data/repository/project_repository.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,6 @@ export declare class ProjectRepository {
3838
updateTag: (owner: string, repo: string, sourceTag: string, targetTag: string, token: string) => Promise<void>;
3939
updateRelease: (owner: string, repo: string, sourceTag: string, targetTag: string, token: string) => Promise<string | undefined>;
4040
createRelease: (owner: string, repo: string, version: string, title: string, changelog: string, token: string) => Promise<string | undefined>;
41+
getDefaultBranch: (owner: string, repo: string, token: string) => Promise<string | undefined>;
4142
createTag: (owner: string, repo: string, branch: string, tag: string, token: string) => Promise<string | undefined>;
4243
}

build/cli/src/usecase/actions/initial_setup_use_case.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,9 @@ export declare class InitialSetupUseCase implements ParamUseCase<Execution, Resu
88
private ensureLabels;
99
private ensureProgressLabels;
1010
private ensureIssueTypes;
11+
/**
12+
* If the repository has no version tags, create default tag v1.0.0 on the default branch.
13+
* Used by "copilot setup" so release/hotfix issues get a base version.
14+
*/
15+
private ensureDefaultVersion;
1116
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
1+
/** Default base version when the repository has no existing tags (e.g. new repo). */
2+
export declare const DEFAULT_BASE_VERSION = "1.0.0";
3+
/** Default initial tag name (with "v" prefix) for repos with no tags. Used by setup. */
4+
export declare const DEFAULT_INITIAL_TAG = "v1.0.0";
15
export declare const incrementVersion: (version: string, releaseType: string) => string;
26
export declare const getLatestVersion: (versions: string[]) => string | undefined;

build/github_action/index.js

Lines changed: 66 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43351,10 +43351,7 @@ class Execution {
4335143351
if (this.release.type === undefined) {
4335243352
return;
4335343353
}
43354-
const lastTag = await branchRepository.getLatestTag();
43355-
if (lastTag === undefined) {
43356-
return;
43357-
}
43354+
const lastTag = await branchRepository.getLatestTag() ?? version_utils_1.DEFAULT_BASE_VERSION;
4335843355
this.release.version = (0, version_utils_1.incrementVersion)(lastTag, this.release.type);
4335943356
}
4336043357
}
@@ -43368,10 +43365,7 @@ class Execution {
4336843365
this.hotfix.version = versionInfo.payload['hotfixVersion'];
4336943366
}
4337043367
else {
43371-
this.hotfix.baseVersion = await branchRepository.getLatestTag();
43372-
if (this.hotfix.baseVersion === undefined) {
43373-
return;
43374-
}
43368+
this.hotfix.baseVersion = await branchRepository.getLatestTag() ?? version_utils_1.DEFAULT_BASE_VERSION;
4337543369
this.hotfix.version = (0, version_utils_1.incrementVersion)(this.hotfix.baseVersion, 'Patch');
4337643370
}
4337743371
this.hotfix.branch = `${this.branches.hotfixTree}/${this.hotfix.version}`;
@@ -45494,6 +45488,7 @@ class IssueRepository {
4549445488
}
4549545489
const sanitizedTitle = issueTitle
4549645490
.replace(/\b\d+(\.\d+){2,}\b/g, '')
45491+
.replace(/\bUnknown Version\b/gi, '')
4549745492
.replace(/[^\p{L}\p{N}\p{P}\p{Z}^$\n]/gu, '')
4549845493
.replace(/\u200D/g, '')
4549945494
.replace(/[^\S\r\n]+/g, ' ')
@@ -47245,6 +47240,19 @@ class ProjectRepository {
4724547240
return undefined;
4724647241
}
4724747242
};
47243+
this.getDefaultBranch = async (owner, repo, token) => {
47244+
try {
47245+
const octokit = github.getOctokit(token);
47246+
const { data } = await octokit.rest.repos.get({ owner, repo });
47247+
const branch = data.default_branch;
47248+
(0, logger_1.logDebugInfo)(`Default branch for ${owner}/${repo}: ${branch}`);
47249+
return branch;
47250+
}
47251+
catch (error) {
47252+
(0, logger_1.logError)(`Error getting default branch for ${owner}/${repo}: ${error}`);
47253+
return undefined;
47254+
}
47255+
};
4724847256
this.createTag = async (owner, repo, branch, tag, token) => {
4724947257
const octokit = github.getOctokit(token);
4725047258
try {
@@ -49270,9 +49278,11 @@ exports.DeployedActionUseCase = DeployedActionUseCase;
4927049278

4927149279
Object.defineProperty(exports, "__esModule", ({ value: true }));
4927249280
exports.InitialSetupUseCase = void 0;
49281+
const branch_repository_1 = __nccwpck_require__(7701);
4927349282
const issue_repository_1 = __nccwpck_require__(57);
4927449283
const project_repository_1 = __nccwpck_require__(7917);
4927549284
const result_1 = __nccwpck_require__(7305);
49285+
const version_utils_1 = __nccwpck_require__(9887);
4927649286
const logger_1 = __nccwpck_require__(8836);
4927749287
const task_emoji_1 = __nccwpck_require__(9785);
4927849288
const setup_files_1 = __nccwpck_require__(1666);
@@ -49347,6 +49357,14 @@ class InitialSetupUseCase {
4934749357
else {
4934849358
steps.push(`✅ Issue types checked: ${issueTypesResult.created} created, ${issueTypesResult.existing} already existed`);
4934949359
}
49360+
// 4. Si no hay tags en el repo, crear versión por defecto v1.0.0
49361+
const defaultVersionResult = await this.ensureDefaultVersion(param);
49362+
if (defaultVersionResult.step) {
49363+
steps.push(defaultVersionResult.step);
49364+
}
49365+
if (defaultVersionResult.error) {
49366+
errors.push(defaultVersionResult.error);
49367+
}
4935049368
results.push(new result_1.Result({
4935149369
id: this.taskId,
4935249370
success: errors.length === 0,
@@ -49423,6 +49441,39 @@ class InitialSetupUseCase {
4942349441
return { success: false, created: 0, existing: 0, errors: [`Error asegurando tipos de Issue: ${error}`] };
4942449442
}
4942549443
}
49444+
/**
49445+
* If the repository has no version tags, create default tag v1.0.0 on the default branch.
49446+
* Used by "copilot setup" so release/hotfix issues get a base version.
49447+
*/
49448+
async ensureDefaultVersion(param) {
49449+
try {
49450+
const branchRepository = new branch_repository_1.BranchRepository();
49451+
const existingTag = await branchRepository.getLatestTag();
49452+
if (existingTag !== undefined) {
49453+
(0, logger_1.logDebugInfo)(`Repository already has version tags (latest: ${existingTag}). Skipping default tag.`);
49454+
return {};
49455+
}
49456+
(0, logger_1.logInfo)(`🏷️ No version tags found. Creating default tag ${version_utils_1.DEFAULT_INITIAL_TAG}...`);
49457+
const projectRepository = new project_repository_1.ProjectRepository();
49458+
const defaultBranch = await projectRepository.getDefaultBranch(param.owner, param.repo, param.tokens.token);
49459+
if (!defaultBranch) {
49460+
const msg = 'Could not get default branch to create initial version tag.';
49461+
(0, logger_1.logError)(msg);
49462+
return { error: msg };
49463+
}
49464+
const sha = await projectRepository.createTag(param.owner, param.repo, defaultBranch, version_utils_1.DEFAULT_INITIAL_TAG, param.tokens.token);
49465+
if (sha) {
49466+
const step = `✅ Default version tag ${version_utils_1.DEFAULT_INITIAL_TAG} created on branch ${defaultBranch}. Run \`git fetch --tags\` to update local refs.`;
49467+
return { step };
49468+
}
49469+
return { error: `Failed to create tag ${version_utils_1.DEFAULT_INITIAL_TAG} on ${param.owner}/${param.repo}` };
49470+
}
49471+
catch (error) {
49472+
const msg = `Error ensuring default version: ${error}`;
49473+
(0, logger_1.logError)(msg);
49474+
return { error: msg };
49475+
}
49476+
}
4942649477
}
4942749478
exports.InitialSetupUseCase = InitialSetupUseCase;
4942849479

@@ -53036,10 +53087,10 @@ class UpdateTitleUseCase {
5303653087
const _title = await this.issueRepository.getTitle(param.owner, param.repo, param.issue.number, param.tokens.token) ?? param.issue.title;
5303753088
let _version = '';
5303853089
if (param.release.active) {
53039-
_version = param.release.version ?? 'Unknown Version';
53090+
_version = param.release.version ?? '';
5304053091
}
5304153092
else if (param.hotfix.active) {
53042-
_version = param.hotfix.version ?? 'Unknown Version';
53093+
_version = param.hotfix.version ?? '';
5304353094
}
5304453095
const title = await this.issueRepository.updateTitleIssueFormat(param.owner, param.repo, _version, _title, param.issue.number, param.issue.branchManagementAlways, param.emoji.branchManagementEmoji, param.labels, param.tokens.token);
5304553096
if (title) {
@@ -56513,8 +56564,12 @@ exports.extractIssueNumberFromPush = extractIssueNumberFromPush;
5651356564
"use strict";
5651456565

5651556566
Object.defineProperty(exports, "__esModule", ({ value: true }));
56516-
exports.getLatestVersion = exports.incrementVersion = void 0;
56567+
exports.getLatestVersion = exports.incrementVersion = exports.DEFAULT_INITIAL_TAG = exports.DEFAULT_BASE_VERSION = void 0;
5651756568
const logger_1 = __nccwpck_require__(8836);
56569+
/** Default base version when the repository has no existing tags (e.g. new repo). */
56570+
exports.DEFAULT_BASE_VERSION = '1.0.0';
56571+
/** Default initial tag name (with "v" prefix) for repos with no tags. Used by setup. */
56572+
exports.DEFAULT_INITIAL_TAG = `v${exports.DEFAULT_BASE_VERSION}`;
5651856573
const incrementVersion = (version, releaseType) => {
5651956574
(0, logger_1.logDebugInfo)(`Incrementing version ${version}.`);
5652056575
const versionParts = version.split('.').map(Number);

build/github_action/src/data/repository/project_repository.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,6 @@ export declare class ProjectRepository {
3838
updateTag: (owner: string, repo: string, sourceTag: string, targetTag: string, token: string) => Promise<void>;
3939
updateRelease: (owner: string, repo: string, sourceTag: string, targetTag: string, token: string) => Promise<string | undefined>;
4040
createRelease: (owner: string, repo: string, version: string, title: string, changelog: string, token: string) => Promise<string | undefined>;
41+
getDefaultBranch: (owner: string, repo: string, token: string) => Promise<string | undefined>;
4142
createTag: (owner: string, repo: string, branch: string, tag: string, token: string) => Promise<string | undefined>;
4243
}

build/github_action/src/usecase/actions/initial_setup_use_case.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,9 @@ export declare class InitialSetupUseCase implements ParamUseCase<Execution, Resu
88
private ensureLabels;
99
private ensureProgressLabels;
1010
private ensureIssueTypes;
11+
/**
12+
* If the repository has no version tags, create default tag v1.0.0 on the default branch.
13+
* Used by "copilot setup" so release/hotfix issues get a base version.
14+
*/
15+
private ensureDefaultVersion;
1116
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
1+
/** Default base version when the repository has no existing tags (e.g. new repo). */
2+
export declare const DEFAULT_BASE_VERSION = "1.0.0";
3+
/** Default initial tag name (with "v" prefix) for repos with no tags. Used by setup. */
4+
export declare const DEFAULT_INITIAL_TAG = "v1.0.0";
15
export declare const incrementVersion: (version: string, releaseType: string) => string;
26
export declare const getLatestVersion: (versions: string[]) => string | undefined;

0 commit comments

Comments
 (0)