Skip to content

Commit f01bb98

Browse files
Coly010FrozenPandaz
authored andcommitted
fix(release): add null-safe fallback for version in createGitTagValues (#34598)
## Current Behavior When nx release runs with docker-configured projects (either via explicit config or @nx/docker plugin inference), git tags are created with the literal string {version} instead of the actual version number (e.g., v{version} instead of v1.0.6, or app-3@{version} instead of app-3@1.0.0). This happens because: 1. If ANY project in a release group has docker config, preferDockerVersion is auto-set to true for the ENTIRE group 2. createGitTagValues() then blindly selects projectVersionData.dockerVersion, which is null for non-docker projects (or projects with no changes) 3. The interpolate() function receives null for {version} and returns the literal placeholder unchanged Commit messages are unaffected because createCommitMessageValues() only uses newVersion and already guards against null. The changelog code (changelog.ts:1117-1121) also already has the correct null-safe pattern. ## Expected Behavior When preferDockerVersion is true but dockerVersion is null, git tags should fall back to using newVersion instead of producing literal {version} placeholders. When both versions are null, no tag should be created. For mixed release groups (some projects have docker config, some don't), the auto-enable logic should use 'both' mode instead of true, which already has proper null-safe checks for each version type. ## Changes - shared.ts: Added null-safe fallback (??) in createGitTagValues() for both independent and fixed group code paths, plus a guard to skip tag creation when both versions are null - config.ts: Refined auto-enable logic to check whether ALL or only SOME projects have docker config — mixed groups now get 'both' mode instead of true - shared.spec.ts: Added 5 test cases covering null version fallback scenarios for fixed groups, independent groups, both-null, reverse fallback, and mixed groups ## Related Issue(s) Fixes #34382 Fixes #33890 Fixes #34391 (cherry picked from commit df9eb0b)
1 parent a584ad8 commit f01bb98

3 files changed

Lines changed: 213 additions & 21 deletions

File tree

packages/nx/src/command-line/release/config/config.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -920,7 +920,7 @@ export async function createNxReleaseConfig(
920920
// If any project in the group has docker configuration, disable semver requirement
921921
releaseGroup.releaseTag.requireSemver = false;
922922

923-
// Set preferDockerVersion to true by default when docker projects exist,
923+
// Set preferDockerVersion by default when docker projects exist,
924924
// unless user has explicitly configured it
925925
if (
926926
releaseGroup.releaseTag.preferDockerVersion === false &&
@@ -931,7 +931,19 @@ export async function createNxReleaseConfig(
931931
userConfig.releaseTag?.preferDockerVersion === undefined &&
932932
userConfig.releaseTagPatternPreferDockerVersion === undefined
933933
) {
934-
releaseGroup.releaseTag.preferDockerVersion = true;
934+
// Check if ALL projects have docker config, or just some
935+
const allProjectsHaveDocker = releaseGroup.projects.every(
936+
(projectName) => {
937+
const projectNode = projectGraph.nodes[projectName];
938+
const projectDockerConfig = projectNode?.data.release?.docker;
939+
return projectDockerConfig !== undefined || !!releaseGroup.docker;
940+
}
941+
);
942+
943+
// Use 'both' for mixed groups so non-docker projects fall back correctly
944+
releaseGroup.releaseTag.preferDockerVersion = allProjectsHaveDocker
945+
? true
946+
: 'both';
935947
}
936948
}
937949
}

packages/nx/src/command-line/release/utils/shared.spec.ts

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,178 @@ describe('shared', () => {
636636
expect(tags).toEqual(['my-group/a@1.1.0', 'my-group/b@1.2.0']);
637637
});
638638

639+
it('should fall back to newVersion when preferDockerVersion is true but dockerVersion is null (fixed group)', () => {
640+
const { releaseGroup, releaseGroupToFilteredProjects } =
641+
setUpReleaseGroup();
642+
releaseGroup.releaseTag.preferDockerVersion = true;
643+
644+
const tags = createGitTagValues(
645+
[releaseGroup],
646+
releaseGroupToFilteredProjects,
647+
{
648+
a: {
649+
currentVersion: '1.0.0',
650+
dependentProjects: [],
651+
newVersion: '1.1.0',
652+
dockerVersion: null,
653+
},
654+
b: {
655+
currentVersion: '1.0.0',
656+
dependentProjects: [],
657+
newVersion: '1.1.0',
658+
dockerVersion: null,
659+
},
660+
}
661+
);
662+
663+
expect(tags).toEqual(['my-group-1.1.0']);
664+
});
665+
666+
it('should fall back to newVersion when preferDockerVersion is true but dockerVersion is null (independent group)', () => {
667+
const projects = ['a', 'b'];
668+
const releaseGroup: ReleaseGroupWithName = {
669+
name: 'my-group',
670+
projects,
671+
projectsRelationship: 'independent',
672+
releaseTag: {
673+
pattern: '{projectName}-{version}',
674+
checkAllBranchesWhen: undefined,
675+
requireSemver: true,
676+
preferDockerVersion: true,
677+
strictPreid: false,
678+
},
679+
changelog: undefined,
680+
version: undefined,
681+
versionPlans: false,
682+
resolvedVersionPlans: false,
683+
};
684+
const releaseGroupToFilteredProjects = new Map().set(
685+
releaseGroup,
686+
new Set(projects)
687+
);
688+
689+
const tags = createGitTagValues(
690+
[releaseGroup],
691+
releaseGroupToFilteredProjects,
692+
{
693+
a: {
694+
currentVersion: '1.0.0',
695+
dependentProjects: [],
696+
newVersion: '1.1.0',
697+
dockerVersion: null,
698+
},
699+
b: {
700+
currentVersion: '1.0.0',
701+
dependentProjects: [],
702+
newVersion: '1.2.0',
703+
dockerVersion: null,
704+
},
705+
}
706+
);
707+
708+
expect(tags).toEqual(['a-1.1.0', 'b-1.2.0']);
709+
});
710+
711+
it('should produce no tag when both dockerVersion and newVersion are null with preferDockerVersion true', () => {
712+
const { releaseGroup, releaseGroupToFilteredProjects } =
713+
setUpReleaseGroup();
714+
releaseGroup.releaseTag.preferDockerVersion = true;
715+
716+
const tags = createGitTagValues(
717+
[releaseGroup],
718+
releaseGroupToFilteredProjects,
719+
{
720+
a: {
721+
currentVersion: '1.0.0',
722+
dependentProjects: [],
723+
newVersion: null,
724+
dockerVersion: null,
725+
},
726+
b: {
727+
currentVersion: '1.0.0',
728+
dependentProjects: [],
729+
newVersion: null,
730+
dockerVersion: null,
731+
},
732+
}
733+
);
734+
735+
expect(tags).toEqual([]);
736+
});
737+
738+
it('should fall back to dockerVersion when preferDockerVersion is false but newVersion is null', () => {
739+
const { releaseGroup, releaseGroupToFilteredProjects } =
740+
setUpReleaseGroup();
741+
releaseGroup.releaseTag.preferDockerVersion = false;
742+
743+
const tags = createGitTagValues(
744+
[releaseGroup],
745+
releaseGroupToFilteredProjects,
746+
{
747+
a: {
748+
currentVersion: '1.0.0',
749+
dependentProjects: [],
750+
newVersion: null,
751+
dockerVersion: '2024.01.abc123',
752+
},
753+
b: {
754+
currentVersion: '1.0.0',
755+
dependentProjects: [],
756+
newVersion: null,
757+
dockerVersion: '2024.01.abc123',
758+
},
759+
}
760+
);
761+
762+
expect(tags).toEqual(['my-group-2024.01.abc123']);
763+
});
764+
765+
it('should handle mixed independent group where some projects have docker version and some do not', () => {
766+
const projects = ['a', 'b'];
767+
const releaseGroup: ReleaseGroupWithName = {
768+
name: 'my-group',
769+
projects,
770+
projectsRelationship: 'independent',
771+
releaseTag: {
772+
pattern: '{projectName}-{version}',
773+
checkAllBranchesWhen: undefined,
774+
requireSemver: true,
775+
preferDockerVersion: true,
776+
strictPreid: false,
777+
},
778+
changelog: undefined,
779+
version: undefined,
780+
versionPlans: false,
781+
resolvedVersionPlans: false,
782+
};
783+
const releaseGroupToFilteredProjects = new Map().set(
784+
releaseGroup,
785+
new Set(projects)
786+
);
787+
788+
const tags = createGitTagValues(
789+
[releaseGroup],
790+
releaseGroupToFilteredProjects,
791+
{
792+
a: {
793+
currentVersion: '1.0.0',
794+
dependentProjects: [],
795+
newVersion: '1.1.0',
796+
dockerVersion: '2024.01.abc123',
797+
},
798+
b: {
799+
currentVersion: '1.0.0',
800+
dependentProjects: [],
801+
newVersion: '1.2.0',
802+
dockerVersion: null,
803+
},
804+
}
805+
);
806+
807+
// Project 'a' has dockerVersion so uses it; project 'b' falls back to newVersion
808+
expect(tags).toEqual(['a-2024.01.abc123', 'b-1.2.0']);
809+
});
810+
639811
function setUpReleaseGroup() {
640812
const projects = ['a', 'b'];
641813
const releaseGroup: ReleaseGroupWithName = {

packages/nx/src/command-line/release/utils/shared.ts

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -313,16 +313,21 @@ export function createGitTagValues(
313313
);
314314
}
315315
} else {
316-
// Use either docker version or semver version based on preference
317-
tags.push(
318-
interpolate(releaseGroup.releaseTag.pattern, {
319-
version: preferDockerVersion
320-
? projectVersionData.dockerVersion
321-
: projectVersionData.newVersion,
322-
projectName: sanitizeProjectNameForGitTag(project),
323-
releaseGroupName: releaseGroup.name,
324-
})
325-
);
316+
// Use either docker version or semver version based on preference, with null fallback
317+
const version = preferDockerVersion
318+
? (projectVersionData.dockerVersion ??
319+
projectVersionData.newVersion)
320+
: (projectVersionData.newVersion ??
321+
projectVersionData.dockerVersion);
322+
if (version) {
323+
tags.push(
324+
interpolate(releaseGroup.releaseTag.pattern, {
325+
version,
326+
projectName: sanitizeProjectNameForGitTag(project),
327+
releaseGroupName: releaseGroup.name,
328+
})
329+
);
330+
}
326331
}
327332
}
328333
}
@@ -356,15 +361,18 @@ export function createGitTagValues(
356361
);
357362
}
358363
} else {
359-
// Use either docker version or semver version based on preference
360-
tags.push(
361-
interpolate(releaseGroup.releaseTag.pattern, {
362-
version: preferDockerVersion
363-
? projectVersionData.dockerVersion
364-
: projectVersionData.newVersion,
365-
releaseGroupName: releaseGroup.name,
366-
})
367-
);
364+
// Use either docker version or semver version based on preference, with null fallback
365+
const version = preferDockerVersion
366+
? (projectVersionData.dockerVersion ?? projectVersionData.newVersion)
367+
: (projectVersionData.newVersion ?? projectVersionData.dockerVersion);
368+
if (version) {
369+
tags.push(
370+
interpolate(releaseGroup.releaseTag.pattern, {
371+
version,
372+
releaseGroupName: releaseGroup.name,
373+
})
374+
);
375+
}
368376
}
369377
}
370378
}

0 commit comments

Comments
 (0)