git-release-manager 0.0.13 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -0
- package/dist/commands/branch/command.js +84 -14
- package/dist/commands/branch/command.js.map +1 -1
- package/dist/commands/branch/types/BranchCliArgs.js +0 -0
- package/dist/commands/branch/types/BranchCliArgs.js.map +0 -0
- package/dist/commands/branch/types/BranchCreateCliArgs.js +0 -0
- package/dist/commands/branch/types/BranchCreateCliArgs.js.map +0 -0
- package/dist/commands/branch/types/BranchDeleteCliArgs.js +0 -0
- package/dist/commands/branch/types/BranchDeleteCliArgs.js.map +0 -0
- package/dist/{cli/types/BranchDeleteCliArgs.js → commands/branch/types/BranchFinishCliArgs.js} +1 -1
- package/dist/commands/branch/types/BranchFinishCliArgs.js.map +1 -0
- package/dist/commands/branch/types/BranchListCliArgs.js +0 -0
- package/dist/commands/branch/types/BranchListCliArgs.js.map +0 -0
- package/dist/commands/branch/types/BranchMergeCliArgs.js +0 -0
- package/dist/commands/branch/types/BranchMergeCliArgs.js.map +0 -0
- package/dist/commands/branch/types/BranchProtectCliArgs.js +0 -0
- package/dist/commands/branch/types/BranchProtectCliArgs.js.map +0 -0
- package/dist/commands/branch/types/BranchRebaseCliArgs.js +0 -0
- package/dist/commands/branch/types/BranchRebaseCliArgs.js.map +0 -0
- package/dist/commands/branch/types/BranchSwitchCliArgs.js +0 -0
- package/dist/commands/branch/types/BranchSwitchCliArgs.js.map +0 -0
- package/dist/{cli/types/ChangelogCliArgs.js → commands/branch/types/BranchSyncCliArgs.js} +1 -1
- package/dist/commands/branch/types/BranchSyncCliArgs.js.map +1 -0
- package/dist/commands/branch/types/BranchUnProtectCliArgs.js +0 -0
- package/dist/commands/branch/types/BranchUnProtectCliArgs.js.map +0 -0
- package/dist/commands/changelog/command.js +28 -3
- package/dist/commands/changelog/command.js.map +1 -1
- package/dist/commands/changelog/types/ChangelogCliArgs.js +0 -0
- package/dist/commands/changelog/types/ChangelogCliArgs.js.map +0 -0
- package/dist/commands/changelog/types/ChangelogGenerateCliArgs.js +0 -0
- package/dist/commands/changelog/types/ChangelogGenerateCliArgs.js.map +0 -0
- package/dist/commands/commit/command.js +8 -3
- package/dist/commands/commit/command.js.map +1 -1
- package/dist/commands/commit/types/CommitAmendCliArgs.js +0 -0
- package/dist/commands/commit/types/CommitAmendCliArgs.js.map +0 -0
- package/dist/commands/commit/types/CommitCliArgs.js +0 -0
- package/dist/commands/commit/types/CommitCliArgs.js.map +0 -0
- package/dist/commands/commit/types/CommitCreateCliArgs.js +0 -0
- package/dist/commands/commit/types/CommitCreateCliArgs.js.map +0 -0
- package/dist/commands/commit/types/CommitListCliArgs.js +0 -0
- package/dist/commands/commit/types/CommitListCliArgs.js.map +0 -0
- package/dist/commands/flow/command.js +196 -6
- package/dist/commands/flow/command.js.map +1 -1
- package/dist/commands/track/command.js +136 -0
- package/dist/commands/track/command.js.map +1 -0
- package/dist/commands/types/CliArgs.js +0 -0
- package/dist/commands/types/CliArgs.js.map +0 -0
- package/dist/commands/version/command.js +115 -23
- package/dist/commands/version/command.js.map +1 -1
- package/dist/commands/version/types/VersionCliArgs.js +0 -0
- package/dist/commands/version/types/VersionCliArgs.js.map +0 -0
- package/dist/commands/version/types/VersionCompareCliArgs.js +0 -0
- package/dist/commands/version/types/VersionCompareCliArgs.js.map +0 -0
- package/dist/commands/version/types/VersionIncrementCliArgs.js +0 -0
- package/dist/commands/version/types/VersionIncrementCliArgs.js.map +0 -0
- package/dist/commands/version/types/VersionInitCliArgs.js +0 -0
- package/dist/commands/version/types/VersionInitCliArgs.js.map +0 -0
- package/dist/commands/version/types/VersionListCliArgs.js +0 -0
- package/dist/commands/version/types/VersionListCliArgs.js.map +0 -0
- package/dist/commands/version/types/VersionProjectCliArgs.js +0 -0
- package/dist/commands/version/types/VersionProjectCliArgs.js.map +0 -0
- package/dist/commands/version/types/VersionRemoteCliArgs.js +0 -0
- package/dist/commands/version/types/VersionRemoteCliArgs.js.map +0 -0
- package/dist/commands/version/types/VersionResetCliArgs.js +0 -0
- package/dist/commands/version/types/VersionResetCliArgs.js.map +0 -0
- package/dist/commands/version/types/VersionRevertCliArgs.js +0 -0
- package/dist/commands/version/types/VersionRevertCliArgs.js.map +0 -0
- package/dist/commands/version/types/VersionSetCliArgs.js +0 -0
- package/dist/commands/version/types/VersionSetCliArgs.js.map +0 -0
- package/dist/commands/version/types/VersionValidateCliArgs.js +0 -0
- package/dist/commands/version/types/VersionValidateCliArgs.js.map +0 -0
- package/dist/config/configLoader.js +0 -0
- package/dist/config/configLoader.js.map +0 -0
- package/dist/config/configManager.js +0 -0
- package/dist/config/configManager.js.map +0 -0
- package/dist/config/constants.js +0 -0
- package/dist/config/constants.js.map +0 -0
- package/dist/config/defaults/config.json +375 -250
- package/dist/config/types/BranchConfig.js +0 -0
- package/dist/config/types/BranchConfig.js.map +0 -0
- package/dist/config/types/ChannelType.js +0 -0
- package/dist/config/types/ChannelType.js.map +0 -0
- package/dist/config/types/CommitType.js +0 -0
- package/dist/config/types/CommitType.js.map +0 -0
- package/dist/config/types/Config.js +0 -0
- package/dist/config/types/Config.js.map +0 -0
- package/dist/config/types/FileGroups.js +0 -0
- package/dist/config/types/FileGroups.js.map +0 -0
- package/dist/config/types/Flow.js +34 -0
- package/dist/config/types/Flow.js.map +1 -0
- package/dist/config/types/LinkType.js +0 -0
- package/dist/config/types/LinkType.js.map +0 -0
- package/dist/config/types/MentionType.js +0 -0
- package/dist/config/types/MentionType.js.map +0 -0
- package/dist/config/types/NoteType.js +0 -0
- package/dist/config/types/NoteType.js.map +0 -0
- package/dist/config/types/Options.js +0 -0
- package/dist/config/types/Options.js.map +0 -0
- package/dist/config/types/Repository.js +0 -0
- package/dist/config/types/Repository.js.map +0 -0
- package/dist/{cli/types/CliArgs.js → config/types/Track.js} +1 -1
- package/dist/config/types/Track.js.map +1 -0
- package/dist/{cli/types/BranchCliArgs.js → config/types/Versioning.js} +1 -1
- package/dist/config/types/Versioning.js.map +1 -0
- package/dist/index.js +25 -6
- package/dist/index.js.map +1 -1
- package/dist/modules/FlowManager.js +47 -4
- package/dist/modules/FlowManager.js.map +1 -1
- package/dist/modules/branch/BranchController.js +33 -2
- package/dist/modules/branch/BranchController.js.map +1 -1
- package/dist/modules/branch/BranchManager.js +0 -0
- package/dist/modules/branch/BranchManager.js.map +0 -0
- package/dist/modules/changelog/ChangelogController.js +0 -0
- package/dist/modules/changelog/ChangelogController.js.map +1 -1
- package/dist/modules/changelog/ChangelogValidator.js +0 -0
- package/dist/modules/changelog/ChangelogValidator.js.map +0 -0
- package/dist/modules/changelog/templateOperations.js +0 -0
- package/dist/modules/changelog/templateOperations.js.map +1 -1
- package/dist/modules/changes/types/ChangeInformation.js +0 -0
- package/dist/modules/changes/types/ChangeInformation.js.map +0 -0
- package/dist/modules/changes/types/Context.js +0 -0
- package/dist/modules/changes/types/Context.js.map +0 -0
- package/dist/modules/changes/types/RangeInfo.js +0 -0
- package/dist/modules/changes/types/RangeInfo.js.map +0 -0
- package/dist/modules/changes/types/RangeSummary.js +0 -0
- package/dist/modules/changes/types/RangeSummary.js.map +0 -0
- package/dist/modules/changes/types/ReferenceTypesEnum.js +0 -0
- package/dist/modules/changes/types/ReferenceTypesEnum.js.map +0 -0
- package/dist/modules/commit/CommitController.js +0 -0
- package/dist/modules/commit/CommitController.js.map +0 -0
- package/dist/modules/commit/CommitManager.js +0 -0
- package/dist/modules/commit/CommitManager.js.map +0 -0
- package/dist/modules/flow/FlowController.js +764 -0
- package/dist/modules/flow/FlowController.js.map +1 -0
- package/dist/modules/git/commits/commitProcessor.js +21 -3
- package/dist/modules/git/commits/commitProcessor.js.map +1 -1
- package/dist/modules/git/commits/gitLogFetcher.js +0 -0
- package/dist/modules/git/commits/gitLogFetcher.js.map +0 -0
- package/dist/modules/git/commits/index.js +9 -1
- package/dist/modules/git/commits/index.js.map +1 -1
- package/dist/modules/git/commits/profileHandler.js +0 -0
- package/dist/modules/git/commits/profileHandler.js.map +0 -0
- package/dist/modules/git/commits/regexHandlers.js +0 -0
- package/dist/modules/git/commits/regexHandlers.js.map +0 -0
- package/dist/modules/git/context/branchHandler.js +0 -0
- package/dist/modules/git/context/branchHandler.js.map +0 -0
- package/dist/modules/git/context/commitHandler.js +0 -0
- package/dist/modules/git/context/commitHandler.js.map +0 -0
- package/dist/modules/git/context/index.js +0 -0
- package/dist/modules/git/context/index.js.map +0 -0
- package/dist/modules/git/context/remoteHandler.js +1 -1
- package/dist/modules/git/context/remoteHandler.js.map +1 -1
- package/dist/modules/git/context/statusHandler.js +0 -0
- package/dist/modules/git/context/statusHandler.js.map +0 -0
- package/dist/modules/git/context/tagHandler.js +0 -0
- package/dist/modules/git/context/tagHandler.js.map +0 -0
- package/dist/modules/git/gitOperations.js +0 -0
- package/dist/modules/git/gitOperations.js.map +1 -1
- package/dist/modules/git/lineTags.js +58 -0
- package/dist/modules/git/lineTags.js.map +1 -0
- package/dist/modules/git/types/Author.js +0 -0
- package/dist/modules/git/types/Author.js.map +0 -0
- package/dist/modules/git/types/Branch.js +0 -0
- package/dist/modules/git/types/Branch.js.map +0 -0
- package/dist/modules/git/types/BranchInfo.js +0 -0
- package/dist/modules/git/types/BranchInfo.js.map +0 -0
- package/dist/modules/git/types/CommitContent.js +0 -0
- package/dist/modules/git/types/CommitContent.js.map +0 -0
- package/dist/modules/git/types/CommitInfo.js +0 -0
- package/dist/modules/git/types/CommitInfo.js.map +0 -0
- package/dist/modules/git/types/CommitType.js +0 -0
- package/dist/modules/git/types/CommitType.js.map +0 -0
- package/dist/modules/git/types/GitCommit.js +0 -0
- package/dist/modules/git/types/GitCommit.js.map +0 -0
- package/dist/modules/git/types/GitReference.js +0 -0
- package/dist/modules/git/types/GitReference.js.map +0 -0
- package/dist/modules/git/types/Link.js +0 -0
- package/dist/modules/git/types/Link.js.map +0 -0
- package/dist/modules/git/types/Mention.js +0 -0
- package/dist/modules/git/types/Mention.js.map +0 -0
- package/dist/modules/git/types/Note.js +0 -0
- package/dist/modules/git/types/Note.js.map +0 -0
- package/dist/modules/git/types/RemoteInfo.js +0 -0
- package/dist/modules/git/types/RemoteInfo.js.map +0 -0
- package/dist/modules/git/types/Repository.js +0 -0
- package/dist/modules/git/types/Repository.js.map +0 -0
- package/dist/modules/git/types/StatusInfo.js +0 -0
- package/dist/modules/git/types/StatusInfo.js.map +0 -0
- package/dist/modules/git/types/Tag.js +0 -0
- package/dist/modules/git/types/Tag.js.map +0 -0
- package/dist/modules/git/types/TagInfo.js +0 -0
- package/dist/modules/git/types/TagInfo.js.map +0 -0
- package/dist/modules/git/utils/branchUtils.js +0 -0
- package/dist/modules/git/utils/branchUtils.js.map +0 -0
- package/dist/modules/git/utils/commitUtils.js +0 -0
- package/dist/modules/git/utils/commitUtils.js.map +0 -0
- package/dist/modules/git/utils/logUtils.js +0 -0
- package/dist/modules/git/utils/logUtils.js.map +0 -0
- package/dist/modules/git/utils/rangeOperations.js +0 -0
- package/dist/modules/git/utils/rangeOperations.js.map +0 -0
- package/dist/modules/git/utils/refUtils.js +0 -0
- package/dist/modules/git/utils/refUtils.js.map +0 -0
- package/dist/modules/git/utils/referenceUtils.js +0 -0
- package/dist/modules/git/utils/referenceUtils.js.map +0 -0
- package/dist/modules/git/utils/tagUtils.js +0 -0
- package/dist/modules/git/utils/tagUtils.js.map +0 -0
- package/dist/modules/output/writer.js +0 -0
- package/dist/modules/output/writer.js.map +0 -0
- package/dist/modules/sections/contributors.js +0 -0
- package/dist/modules/sections/contributors.js.map +0 -0
- package/dist/modules/sections/grouping.js +0 -0
- package/dist/modules/sections/grouping.js.map +0 -0
- package/dist/modules/sections/index.js +0 -0
- package/dist/modules/sections/index.js.map +0 -0
- package/dist/modules/sections/summarizing.js +0 -0
- package/dist/modules/sections/summarizing.js.map +0 -0
- package/dist/modules/sections/types/Contributor.js +0 -0
- package/dist/modules/sections/types/Contributor.js.map +0 -0
- package/dist/modules/sections/types/Group.js +0 -0
- package/dist/modules/sections/types/Group.js.map +0 -0
- package/dist/modules/sections/types/SectionSummary.js +0 -0
- package/dist/modules/sections/types/SectionSummary.js.map +0 -0
- package/dist/modules/sections/types/Sections.js +0 -0
- package/dist/modules/sections/types/Sections.js.map +0 -0
- package/dist/modules/track/TrackController.js +320 -0
- package/dist/modules/track/TrackController.js.map +1 -0
- package/dist/modules/version/BumpDeriver.js +119 -0
- package/dist/modules/version/BumpDeriver.js.map +1 -0
- package/dist/modules/version/GitVersionManager.js +42 -14
- package/dist/modules/version/GitVersionManager.js.map +1 -1
- package/dist/modules/version/ProjectVersion.js +84 -3
- package/dist/modules/version/ProjectVersion.js.map +1 -1
- package/dist/modules/version/ReleaseManager.js +0 -0
- package/dist/modules/version/ReleaseManager.js.map +0 -0
- package/dist/modules/version/VersionController.js +238 -3
- package/dist/modules/version/VersionController.js.map +1 -1
- package/dist/modules/version/VersionStamper.js +139 -0
- package/dist/modules/version/VersionStamper.js.map +1 -0
- package/dist/modules/version/VersionValidator.js +0 -0
- package/dist/modules/version/VersionValidator.js.map +0 -0
- package/dist/modules/version/branchChannel.js +94 -0
- package/dist/modules/version/branchChannel.js.map +1 -0
- package/dist/modules/version/resolveVersionRef.js +78 -0
- package/dist/modules/version/resolveVersionRef.js.map +1 -0
- package/dist/modules/version/types/const.js +0 -0
- package/dist/modules/version/types/const.js.map +0 -0
- package/dist/modules/version/versionFormatter.js +19 -1
- package/dist/modules/version/versionFormatter.js.map +1 -1
- package/dist/modules/version/versionHelper.js +0 -0
- package/dist/modules/version/versionHelper.js.map +0 -0
- package/dist/templates/constants.js +0 -0
- package/dist/templates/constants.js.map +0 -0
- package/dist/templates/defaults/changelog.detailed.ejs +0 -0
- package/dist/templates/defaults/changelog.ejs +0 -0
- package/dist/templates/templateRenderer.js +0 -0
- package/dist/templates/templateRenderer.js.map +0 -0
- package/dist/templates/templateResolver.js +0 -0
- package/dist/templates/templateResolver.js.map +0 -0
- package/dist/types/PackageJson.js +0 -0
- package/dist/types/PackageJson.js.map +0 -0
- package/dist/utils/cmd.js +15 -2
- package/dist/utils/cmd.js.map +1 -1
- package/dist/utils/date.js +0 -0
- package/dist/utils/date.js.map +0 -0
- package/package.json +5 -2
- package/dist/cli/types/BranchCliArgs.js.map +0 -1
- package/dist/cli/types/BranchCreateCliArgs.js +0 -3
- package/dist/cli/types/BranchCreateCliArgs.js.map +0 -1
- package/dist/cli/types/BranchDeleteCliArgs.js.map +0 -1
- package/dist/cli/types/BranchListCliArgs.js +0 -3
- package/dist/cli/types/BranchListCliArgs.js.map +0 -1
- package/dist/cli/types/BranchMergeCliArgs.js +0 -3
- package/dist/cli/types/BranchMergeCliArgs.js.map +0 -1
- package/dist/cli/types/BranchProtectCliArgs.js +0 -3
- package/dist/cli/types/BranchProtectCliArgs.js.map +0 -1
- package/dist/cli/types/BranchRebaseCliArgs.js +0 -3
- package/dist/cli/types/BranchRebaseCliArgs.js.map +0 -1
- package/dist/cli/types/BranchSwitchCliArgs.js +0 -3
- package/dist/cli/types/BranchSwitchCliArgs.js.map +0 -1
- package/dist/cli/types/BranchUnProtectCliArgs.js +0 -3
- package/dist/cli/types/BranchUnProtectCliArgs.js.map +0 -1
- package/dist/cli/types/ChangelogCliArgs.js.map +0 -1
- package/dist/cli/types/ChangelogGenerateCliArgs.js +0 -3
- package/dist/cli/types/ChangelogGenerateCliArgs.js.map +0 -1
- package/dist/cli/types/CliArgs.js.map +0 -1
- package/dist/cli/types/CommitAmendCliArgs.js +0 -3
- package/dist/cli/types/CommitAmendCliArgs.js.map +0 -1
- package/dist/cli/types/CommitCliArgs.js +0 -3
- package/dist/cli/types/CommitCliArgs.js.map +0 -1
- package/dist/cli/types/CommitCreateCliArgs.js +0 -3
- package/dist/cli/types/CommitCreateCliArgs.js.map +0 -1
- package/dist/cli/types/CommitListCliArgs.js +0 -3
- package/dist/cli/types/CommitListCliArgs.js.map +0 -1
- package/dist/cli/types/VersionCliArgs.js +0 -3
- package/dist/cli/types/VersionCliArgs.js.map +0 -1
- package/dist/cli/types/VersionCompareCliArgs.js +0 -3
- package/dist/cli/types/VersionCompareCliArgs.js.map +0 -1
- package/dist/cli/types/VersionIncrementCliArgs.js +0 -3
- package/dist/cli/types/VersionIncrementCliArgs.js.map +0 -1
- package/dist/cli/types/VersionInitCliArgs.js +0 -3
- package/dist/cli/types/VersionInitCliArgs.js.map +0 -1
- package/dist/cli/types/VersionListCliArgs.js +0 -3
- package/dist/cli/types/VersionListCliArgs.js.map +0 -1
- package/dist/cli/types/VersionProjectCliArgs.js +0 -3
- package/dist/cli/types/VersionProjectCliArgs.js.map +0 -1
- package/dist/cli/types/VersionRemoteCliArgs.js +0 -3
- package/dist/cli/types/VersionRemoteCliArgs.js.map +0 -1
- package/dist/cli/types/VersionResetCliArgs.js +0 -3
- package/dist/cli/types/VersionResetCliArgs.js.map +0 -1
- package/dist/cli/types/VersionRevertCliArgs.js +0 -3
- package/dist/cli/types/VersionRevertCliArgs.js.map +0 -1
- package/dist/cli/types/VersionSetCliArgs.js +0 -3
- package/dist/cli/types/VersionSetCliArgs.js.map +0 -1
- package/dist/cli/types/VersionValidateCliArgs.js +0 -3
- package/dist/cli/types/VersionValidateCliArgs.js.map +0 -1
- package/dist/commands/branchCommand.js +0 -150
- package/dist/commands/branchCommand.js.map +0 -1
- package/dist/commands/changelogCommand.js +0 -32
- package/dist/commands/changelogCommand.js.map +0 -1
- package/dist/commands/commitCommand.js +0 -58
- package/dist/commands/commitCommand.js.map +0 -1
- package/dist/commands/types/BranchCliArgs.js +0 -3
- package/dist/commands/types/BranchCliArgs.js.map +0 -1
- package/dist/commands/types/BranchCreateCliArgs.js +0 -3
- package/dist/commands/types/BranchCreateCliArgs.js.map +0 -1
- package/dist/commands/types/BranchDeleteCliArgs.js +0 -3
- package/dist/commands/types/BranchDeleteCliArgs.js.map +0 -1
- package/dist/commands/types/BranchListCliArgs.js +0 -3
- package/dist/commands/types/BranchListCliArgs.js.map +0 -1
- package/dist/commands/types/BranchMergeCliArgs.js +0 -3
- package/dist/commands/types/BranchMergeCliArgs.js.map +0 -1
- package/dist/commands/types/BranchProtectCliArgs.js +0 -3
- package/dist/commands/types/BranchProtectCliArgs.js.map +0 -1
- package/dist/commands/types/BranchRebaseCliArgs.js +0 -3
- package/dist/commands/types/BranchRebaseCliArgs.js.map +0 -1
- package/dist/commands/types/BranchSwitchCliArgs.js +0 -3
- package/dist/commands/types/BranchSwitchCliArgs.js.map +0 -1
- package/dist/commands/types/BranchUnProtectCliArgs.js +0 -3
- package/dist/commands/types/BranchUnProtectCliArgs.js.map +0 -1
- package/dist/commands/types/ChangelogCliArgs.js +0 -3
- package/dist/commands/types/ChangelogCliArgs.js.map +0 -1
- package/dist/commands/types/ChangelogGenerateCliArgs.js +0 -3
- package/dist/commands/types/ChangelogGenerateCliArgs.js.map +0 -1
- package/dist/commands/types/CommitAmendCliArgs.js +0 -3
- package/dist/commands/types/CommitAmendCliArgs.js.map +0 -1
- package/dist/commands/types/CommitCliArgs.js +0 -3
- package/dist/commands/types/CommitCliArgs.js.map +0 -1
- package/dist/commands/types/CommitCreateCliArgs.js +0 -3
- package/dist/commands/types/CommitCreateCliArgs.js.map +0 -1
- package/dist/commands/types/CommitListCliArgs.js +0 -3
- package/dist/commands/types/CommitListCliArgs.js.map +0 -1
- package/dist/commands/types/VersionCliArgs.js +0 -3
- package/dist/commands/types/VersionCliArgs.js.map +0 -1
- package/dist/commands/types/VersionCompareCliArgs.js +0 -3
- package/dist/commands/types/VersionCompareCliArgs.js.map +0 -1
- package/dist/commands/types/VersionIncrementCliArgs.js +0 -3
- package/dist/commands/types/VersionIncrementCliArgs.js.map +0 -1
- package/dist/commands/types/VersionInitCliArgs.js +0 -3
- package/dist/commands/types/VersionInitCliArgs.js.map +0 -1
- package/dist/commands/types/VersionListCliArgs.js +0 -3
- package/dist/commands/types/VersionListCliArgs.js.map +0 -1
- package/dist/commands/types/VersionProjectCliArgs.js +0 -3
- package/dist/commands/types/VersionProjectCliArgs.js.map +0 -1
- package/dist/commands/types/VersionRemoteCliArgs.js +0 -3
- package/dist/commands/types/VersionRemoteCliArgs.js.map +0 -1
- package/dist/commands/types/VersionResetCliArgs.js +0 -3
- package/dist/commands/types/VersionResetCliArgs.js.map +0 -1
- package/dist/commands/types/VersionRevertCliArgs.js +0 -3
- package/dist/commands/types/VersionRevertCliArgs.js.map +0 -1
- package/dist/commands/types/VersionSetCliArgs.js +0 -3
- package/dist/commands/types/VersionSetCliArgs.js.map +0 -1
- package/dist/commands/types/VersionValidateCliArgs.js +0 -3
- package/dist/commands/types/VersionValidateCliArgs.js.map +0 -1
- package/dist/commands/versionCommand.js +0 -134
- package/dist/commands/versionCommand.js.map +0 -1
- package/dist/core/app.js +0 -95
- package/dist/core/app.js.map +0 -1
- package/dist/core/changelog/app.js +0 -20
- package/dist/core/changelog/app.js.map +0 -1
- package/dist/modules/version/VersionManager.js +0 -92
- package/dist/modules/version/VersionManager.js.map +0 -1
|
@@ -0,0 +1,764 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.FlowController = void 0;
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const path_1 = require("path");
|
|
9
|
+
const semver_1 = __importDefault(require("semver"));
|
|
10
|
+
const simple_git_1 = __importDefault(require("simple-git"));
|
|
11
|
+
const BumpDeriver_1 = require("../version/BumpDeriver");
|
|
12
|
+
const lineTags_1 = require("../git/lineTags");
|
|
13
|
+
const BUMP_RANK = { patch: 0, minor: 1, major: 2 };
|
|
14
|
+
/**
|
|
15
|
+
* The higher of a derived and a declared bump, treating a missing declaration
|
|
16
|
+
* as "no opinion" (DECISIONS D3).
|
|
17
|
+
*
|
|
18
|
+
* A floor is a MAXIMUM against the derived level, never a replacement for it: a
|
|
19
|
+
* declared `minor` must not pull a `major:` in the range down, which is exactly
|
|
20
|
+
* what the override reading would have done.
|
|
21
|
+
*/
|
|
22
|
+
function maxBump(derived, declared) {
|
|
23
|
+
if (!declared)
|
|
24
|
+
return derived;
|
|
25
|
+
if (!derived)
|
|
26
|
+
return declared;
|
|
27
|
+
return BUMP_RANK[declared] > BUMP_RANK[derived] ? declared : derived;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* FlowController — run a declared phase: promote a branch, derive the version
|
|
31
|
+
* from the commits, tag it.
|
|
32
|
+
*
|
|
33
|
+
* ── THE NUMBER COMES FROM THE HISTORY, NOT FROM A NAME ──
|
|
34
|
+
*
|
|
35
|
+
* This is the one property the existing flow surfaces do not have.
|
|
36
|
+
* `branch finish` reads the version out of the branch name
|
|
37
|
+
* (`release/1.4.0` → `1.4.0`), which means the bump derivation — the whole
|
|
38
|
+
* commitTypes/noteTypes taxonomy a project configures for its changelog — is
|
|
39
|
+
* bypassed on the only path where it matters. Here the range is
|
|
40
|
+
* `<latest tag on this line>..<the phase's branch>` and `deriveBump` grades it,
|
|
41
|
+
* so `feat:` moves the minor whether or not anybody typed a number.
|
|
42
|
+
*
|
|
43
|
+
* ── AND IT IS COMPUTED AFTER THE MERGE, DELIBERATELY ──
|
|
44
|
+
*
|
|
45
|
+
* A promotion's version must describe the work it promotes. Derive before the
|
|
46
|
+
* merge and the range is empty (the target branch has not moved yet), so the
|
|
47
|
+
* derivation falls to the default and a release full of features gets a patch
|
|
48
|
+
* bump. Every step still reports success. So: merge, then derive, then tag —
|
|
49
|
+
* and the tag lands on the merge commit, which is the commit that carries the
|
|
50
|
+
* work.
|
|
51
|
+
*
|
|
52
|
+
* ── WHAT IT REFUSES ──
|
|
53
|
+
*
|
|
54
|
+
* · an unknown phase, listing the ones the project declared
|
|
55
|
+
* · a promotion with nothing to promote (the source carries no commit the
|
|
56
|
+
* target lacks) — that would mint a version for no change
|
|
57
|
+
* · a fast-forward merge unless the phase asked for `ff` in writing: a
|
|
58
|
+
* fast-forward creates no commit, so a pre-merge hook never runs and every
|
|
59
|
+
* gate hung on the promotion silently does not execute
|
|
60
|
+
* · deleting the source branch unless the phase asked for it. On a long-lived
|
|
61
|
+
* integration line that is not a cleanup, it is an outage.
|
|
62
|
+
*/
|
|
63
|
+
const OWNER_TOKEN = "FlowController/v1";
|
|
64
|
+
class FlowController {
|
|
65
|
+
constructor(config, root = process.cwd()) {
|
|
66
|
+
this.config = config;
|
|
67
|
+
this.root = root;
|
|
68
|
+
this.git = (0, simple_git_1.default)(this.root);
|
|
69
|
+
}
|
|
70
|
+
/** The phase declaration, or a refusal that names what IS declared. */
|
|
71
|
+
phase(name) {
|
|
72
|
+
var _a, _b;
|
|
73
|
+
const phases = (_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.flow) === null || _b === void 0 ? void 0 : _b.phases;
|
|
74
|
+
if (!phases || Object.keys(phases).length === 0) {
|
|
75
|
+
throw new Error("No flow is declared. Add a `flow.phases` block to the config " +
|
|
76
|
+
"(see src/config/types/Flow.ts) naming each phase's branch.");
|
|
77
|
+
}
|
|
78
|
+
const p = phases[name];
|
|
79
|
+
if (!p) {
|
|
80
|
+
throw new Error(`Unknown phase '${name}'. Declared: ${Object.keys(phases).join(", ")}`);
|
|
81
|
+
}
|
|
82
|
+
if (!p.branch) {
|
|
83
|
+
throw new Error(`Phase '${name}' declares no branch.`);
|
|
84
|
+
}
|
|
85
|
+
return p;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The phase's branch must EXIST before any question about it means anything.
|
|
89
|
+
*
|
|
90
|
+
* 🔴 WITHOUT THIS A MISSING BRANCH ANSWERS A VERSION. Every tag walk filters
|
|
91
|
+
* on `merge-base --is-ancestor <tag> <branch>`, which simply fails for a
|
|
92
|
+
* branch that is not there — so no tag is reachable, the baseline falls to
|
|
93
|
+
* 0.0.0, and the phase confidently reports `v0.0.1` for a line it cannot see.
|
|
94
|
+
* Measured 2026-08-06 in a `git clone --branch dev` of a repository at v1.3.0:
|
|
95
|
+
* `main` existed only as a remote-tracking ref, and `flow next prod` answered
|
|
96
|
+
* v0.0.1 while the sibling driver answered v1.0.0. A number that names nothing
|
|
97
|
+
* is worse than a refusal, because a caller substitutes it into a tag.
|
|
98
|
+
*/
|
|
99
|
+
async assertBranch(branch, role) {
|
|
100
|
+
const ok = await this.git
|
|
101
|
+
.raw(["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`])
|
|
102
|
+
.then(out => String(out).trim() !== "")
|
|
103
|
+
.catch(() => false);
|
|
104
|
+
if (!ok) {
|
|
105
|
+
throw new Error(`${role} '${branch}' does not exist in this repository ` +
|
|
106
|
+
`(a remote-tracking ref is not enough — create the local branch).`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
prefixOf(p) {
|
|
110
|
+
var _a, _b, _c, _d;
|
|
111
|
+
return (_d = (_a = p.prefix) !== null && _a !== void 0 ? _a : (_c = (_b = this.config) === null || _b === void 0 ? void 0 : _b.tag) === null || _c === void 0 ? void 0 : _c.prefix) !== null && _d !== void 0 ? _d : "";
|
|
112
|
+
}
|
|
113
|
+
strategyOf(p) {
|
|
114
|
+
return p.merge === "ff" ? "ff" : "no-ff";
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The highest tag on THIS line, reachable from the phase's branch.
|
|
118
|
+
*
|
|
119
|
+
* The line is `lineTags` (annotated + reachable + version-sorted); this adds
|
|
120
|
+
* the one filter that is specific to the baseline question. A phase with a
|
|
121
|
+
* channel wants that channel's newest tag; a stable phase must not adopt a
|
|
122
|
+
* prerelease as its baseline, or every stable cut would inherit `-dev.N` and
|
|
123
|
+
* never leave it.
|
|
124
|
+
*/
|
|
125
|
+
async currentVersion(p) {
|
|
126
|
+
const prefix = this.prefixOf(p);
|
|
127
|
+
for (const name of await this.lineTags(p)) {
|
|
128
|
+
const core = name.slice(prefix.length);
|
|
129
|
+
if (p.channel) {
|
|
130
|
+
if (!core.includes(`-${p.channel}.`))
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
else if (core.includes("-")) {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
return name;
|
|
137
|
+
}
|
|
138
|
+
return "";
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Every annotated tag on this line, newest-version first, reachable from the
|
|
142
|
+
* phase's branch. One walk; the baseline questions above and in
|
|
143
|
+
* `nextVersion` filter it.
|
|
144
|
+
*
|
|
145
|
+
* THE WALK ITSELF MOVED to modules/git/lineTags (2026-08-24), because a
|
|
146
|
+
* version TRACK asks the same question about its own branch and the answer
|
|
147
|
+
* must not exist twice. Everything below — why annotated-only, why
|
|
148
|
+
* `--merged`, why a version sort, and why the answer is read from stdout
|
|
149
|
+
* rather than from an exit code — is kept THERE, with the code it governs.
|
|
150
|
+
*
|
|
151
|
+
* Three filters, and each one is load-bearing. ANNOTATED only
|
|
152
|
+
* (`%(objecttype)` is `tag`): a lightweight tag carries no tagger, so it
|
|
153
|
+
* records nothing about who cut it. REACHABLE from the branch (`--merged`):
|
|
154
|
+
* a tag on a side branch names code this line never carried. Sorted by
|
|
155
|
+
* VERSION, not lexically — `-v:refname`, because a lexical sort puts
|
|
156
|
+
* v1.10.0 below v1.9.0 and the baseline would walk backwards on the tenth
|
|
157
|
+
* minor.
|
|
158
|
+
*
|
|
159
|
+
* ── 🔴 REACHABILITY IS READ FROM STDOUT, NEVER FROM AN EXIT CODE ──
|
|
160
|
+
*
|
|
161
|
+
* This used to enumerate every tag and then ask, per tag,
|
|
162
|
+
* `merge-base --is-ancestor <tag>^{commit} <branch>`, mapping a resolved
|
|
163
|
+
* promise to `true` and a rejection to `false`. That command answers ONLY
|
|
164
|
+
* through its exit status: 0 ancestor, 1 not an ancestor, and it prints
|
|
165
|
+
* nothing either way. simple-git 3.27.0 RESOLVES a non-zero exit whose
|
|
166
|
+
* stderr is empty — measured, and `git tag --merged` in FlowManager already
|
|
167
|
+
* avoids the idiom — so `.then(() => true)` fired for BOTH answers and the
|
|
168
|
+
* filter passed every tag. Consequence on the repository this tool manages:
|
|
169
|
+
* `flow next prod` baselined on v1.6.0, a tag that lives only on `dev`, and
|
|
170
|
+
* answered v1.7.0 where the release line stood at v1.0.8 and the correct
|
|
171
|
+
* answer was v1.1.0 — six minor versions ahead, reported as a success.
|
|
172
|
+
* VersionController.resolveBaselineRef carries the same warning for
|
|
173
|
+
* `rev-parse --verify --quiet`: THE ANSWER IS THE OUTPUT, NOT THE ABSENCE
|
|
174
|
+
* OF A THROW.
|
|
175
|
+
*
|
|
176
|
+
* `for-each-ref --merged=<branch>` moves the answer onto stdout, and it
|
|
177
|
+
* costs ONE process instead of one per tag. Both failure directions now
|
|
178
|
+
* converge on the SAME safe answer: if git fails, and whether the wrapper
|
|
179
|
+
* rejects (the catch below) or resolves with empty output, the result is an
|
|
180
|
+
* empty set — no baseline rather than a foreign one. A phase whose branch
|
|
181
|
+
* does not exist yet lands here too, which is the answer it had before.
|
|
182
|
+
*/
|
|
183
|
+
async lineTags(p) {
|
|
184
|
+
return (0, lineTags_1.lineTags)(this.git, p.branch, this.prefixOf(p));
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* The version this phase would cut next.
|
|
188
|
+
*
|
|
189
|
+
* `at` overrides the ref the range ENDS at — the runner passes the merge
|
|
190
|
+
* commit, because after a promotion the branch ref may not have moved yet
|
|
191
|
+
* (a worktree merge lands on a detached HEAD).
|
|
192
|
+
*
|
|
193
|
+
* ── A PRERELEASE HAS TWO BASELINES, AND THEY ARE DIFFERENT QUESTIONS ──
|
|
194
|
+
*
|
|
195
|
+
* The CORE comes from the release line — the newest STABLE tag — because
|
|
196
|
+
* `1.2.0-dev.3` is a preview of the 1.2.0 that is coming, and a preview whose
|
|
197
|
+
* core is derived from another preview drifts away from the line it previews.
|
|
198
|
+
* The COUNTER comes from the newest tag already in this channel AT THAT CORE.
|
|
199
|
+
*
|
|
200
|
+
* Conflating them is not theoretical: with one baseline this method answered
|
|
201
|
+
* `v0.0.1-dev.1` on a repository whose release line was at v1.1.0 (no stable
|
|
202
|
+
* tag matched the channel filter, so the base fell to 0.0.0), and the second
|
|
203
|
+
* cut then recomputed the SAME string and died on an existing tag. Measured
|
|
204
|
+
* 2026-08-06. The bump is applied to the stable core exactly once, and the
|
|
205
|
+
* channel counter never bumps the core again.
|
|
206
|
+
*/
|
|
207
|
+
async nextVersion(p, at) {
|
|
208
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
209
|
+
await this.assertBranch(p.branch, "the phase's branch");
|
|
210
|
+
const prefix = this.prefixOf(p);
|
|
211
|
+
const tags = await this.lineTags(p);
|
|
212
|
+
const core = (t) => t.slice(prefix.length);
|
|
213
|
+
const stable = (_a = tags.find(t => !core(t).includes("-"))) !== null && _a !== void 0 ? _a : "";
|
|
214
|
+
const current = p.channel
|
|
215
|
+
? ((_b = tags.find(t => core(t).includes(`-${p.channel}.`))) !== null && _b !== void 0 ? _b : stable)
|
|
216
|
+
: stable;
|
|
217
|
+
// The core is ALWAYS built from the stable line; see the note above.
|
|
218
|
+
const base = stable ? core(stable) : "0.0.0";
|
|
219
|
+
let bump = (_f = (_c = p.bump) !== null && _c !== void 0 ? _c : (_e = (_d = this.config) === null || _d === void 0 ? void 0 : _d.versioning) === null || _e === void 0 ? void 0 : _e.defaultBump) !== null && _f !== void 0 ? _f : "patch";
|
|
220
|
+
let why;
|
|
221
|
+
if (p.derive !== false && stable) {
|
|
222
|
+
// ── THE RANGE, AND WHY IT IS A UNION BEFORE THE MERGE ──
|
|
223
|
+
//
|
|
224
|
+
// After a promotion the target branch contains the source, so one
|
|
225
|
+
// range `current..<merge commit>` is the whole story — that is the
|
|
226
|
+
// `at` case, passed by the runner.
|
|
227
|
+
//
|
|
228
|
+
// BEFORE the merge it is not. `current..main` is EMPTY (main has not
|
|
229
|
+
// moved yet), so a plan that used it derived nothing, fell to
|
|
230
|
+
// defaultBump, and answered `v1.0.1` for a promotion the run then
|
|
231
|
+
// correctly cut as `v1.1.0` — the plan and the act disagreeing about
|
|
232
|
+
// the number, which is the one thing a plan may never do. Measured
|
|
233
|
+
// 2026-08-06 on a two-commit fixture with a `feat:` in it.
|
|
234
|
+
//
|
|
235
|
+
// `current..main` alone is wrong and `current..dev` alone is too: the
|
|
236
|
+
// target may carry commits since the tag that the source lacks (the
|
|
237
|
+
// divergence the plan reports as `behind`). `git log A..B A..C` is the
|
|
238
|
+
// UNION of both, which is exactly what the merge will produce, so the
|
|
239
|
+
// plan grades the same set the run will.
|
|
240
|
+
const ends = at ? [at] : (p.mergeFrom ? [p.branch, p.mergeFrom] : [p.branch]);
|
|
241
|
+
// From the STABLE tag, not from the channel's newest: a prerelease
|
|
242
|
+
// counter moving does not mean the work was already graded.
|
|
243
|
+
const ranges = ends.map(e => `${stable}..${e}`);
|
|
244
|
+
const pathspecs = ((_g = p.paths) !== null && _g !== void 0 ? _g : "").split(/\s+/).filter(Boolean);
|
|
245
|
+
// --no-merges: a merge commit carries no type and never could — its
|
|
246
|
+
// content is the commits it joins, and those are in this range too.
|
|
247
|
+
const args = [
|
|
248
|
+
...ranges.slice(1),
|
|
249
|
+
"--no-merges",
|
|
250
|
+
...(pathspecs.length ? ["--", ...pathspecs] : []),
|
|
251
|
+
];
|
|
252
|
+
const commits = await this.logCommits([...ranges, ...args]);
|
|
253
|
+
if (commits === null || commits === void 0 ? void 0 : commits.length) {
|
|
254
|
+
const evidence = (0, BumpDeriver_1.deriveBump)(commits, this.config);
|
|
255
|
+
why = (0, BumpDeriver_1.explainBump)(evidence);
|
|
256
|
+
// ── A DECLARED phase.bump IS A FLOOR (DECISIONS D3) ──────────
|
|
257
|
+
//
|
|
258
|
+
// Derive from the commits, then never go BELOW what the phase
|
|
259
|
+
// declared. Three readings were possible and this is the one
|
|
260
|
+
// taken:
|
|
261
|
+
//
|
|
262
|
+
// override the declaration always wins — then a `major:` in
|
|
263
|
+
// the range is silently downgraded to the declared
|
|
264
|
+
// level, which is worse than either alternative
|
|
265
|
+
// fallback the declaration applies only when the range is
|
|
266
|
+
// empty — what this did until now, and the opposite
|
|
267
|
+
// of what "a phase may override the level" reads like
|
|
268
|
+
// floor derive, then raise to the declaration if it asked
|
|
269
|
+
// for more. A project writing `minor` on a phase
|
|
270
|
+
// means every promotion through it is AT LEAST a
|
|
271
|
+
// minor, and a `feat!:` can still make it a major.
|
|
272
|
+
//
|
|
273
|
+
// Measured before this: on a one-commit fixture `prod.bump=minor`
|
|
274
|
+
// yielded v1.1.0 and on a repository with a real range the same
|
|
275
|
+
// declaration yielded a patch — the declaration won exactly where
|
|
276
|
+
// it mattered least.
|
|
277
|
+
bump = maxBump(evidence.bump, p.bump);
|
|
278
|
+
if (p.bump && bump !== evidence.bump) {
|
|
279
|
+
// SAID OUT LOUD. Without this the operator reads "derived
|
|
280
|
+
// patch from 3 commits" beside a minor version and has no
|
|
281
|
+
// way to tell which input produced it.
|
|
282
|
+
why = `${why} — raised to ${bump} by the phase's declared floor (phase.bump = ${p.bump})`;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const nextCore = (_h = semver_1.default.inc(base, bump !== null && bump !== void 0 ? bump : "patch")) !== null && _h !== void 0 ? _h : base;
|
|
287
|
+
if (!p.channel) {
|
|
288
|
+
return { version: `${prefix}${nextCore}`, current: current || "none", why };
|
|
289
|
+
}
|
|
290
|
+
// The counter continues within THIS core. A new core restarts at 1, which
|
|
291
|
+
// is what makes `1.2.0-dev.1` follow `1.1.0-dev.7` rather than `.8`.
|
|
292
|
+
const inChannel = tags.filter(t => core(t).startsWith(`${nextCore}-${p.channel}.`));
|
|
293
|
+
let n = 0;
|
|
294
|
+
for (const t of inChannel) {
|
|
295
|
+
const m = core(t).match(new RegExp(`^${this.escapeRe(nextCore)}-${this.escapeRe(p.channel)}\\.(\\d+)$`));
|
|
296
|
+
if (m)
|
|
297
|
+
n = Math.max(n, parseInt(m[1], 10));
|
|
298
|
+
}
|
|
299
|
+
return { version: `${prefix}${nextCore}-${p.channel}.${n + 1}`, current: current || "none", why };
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Remove one worktree this tool created, and deregister it.
|
|
303
|
+
*
|
|
304
|
+
* `keep: true` leaves it for inspection — of the run that just happened. The
|
|
305
|
+
* next run reclaims it (see reclaimWorktrees), because `keep` is a debugging
|
|
306
|
+
* aid and not a licence to hoard checkouts. With back-merges or routes in the
|
|
307
|
+
* same run, that means the LAST merge's worktree is the one left standing:
|
|
308
|
+
* each merge reclaims the leftovers it finds before starting. Bounded on
|
|
309
|
+
* purpose — the alternative is one full checkout per branch, kept forever.
|
|
310
|
+
*
|
|
311
|
+
* `worktree remove` can refuse (a lock, a file it cannot delete), and a
|
|
312
|
+
* refusal that leaves the directory behind is exactly the accumulation this
|
|
313
|
+
* guards against — so the removal falls back to deleting the directory, and
|
|
314
|
+
* `prune` then clears the registration git is left holding.
|
|
315
|
+
*/
|
|
316
|
+
async removeWorktree(dir) {
|
|
317
|
+
var _a, _b, _c;
|
|
318
|
+
if (((_c = (_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.flow) === null || _b === void 0 ? void 0 : _b.worktree) === null || _c === void 0 ? void 0 : _c.keep) === true)
|
|
319
|
+
return;
|
|
320
|
+
// SAFE to swallow: nothing reads this call's outcome. Whether it removed
|
|
321
|
+
// the worktree is re-measured on the next line by LOOKING — existsSync —
|
|
322
|
+
// so a wrapper that misreports the exit status changes nothing.
|
|
323
|
+
await this.git.raw(["worktree", "remove", "--force", dir]).catch(() => { });
|
|
324
|
+
if ((0, fs_1.existsSync)(dir)) {
|
|
325
|
+
try {
|
|
326
|
+
(0, fs_1.rmSync)(dir, { recursive: true, force: true });
|
|
327
|
+
}
|
|
328
|
+
catch ( /* reported by prune */_d) { /* reported by prune */ }
|
|
329
|
+
}
|
|
330
|
+
// SAFE to swallow: housekeeping with no answer. Nothing branches on
|
|
331
|
+
// whether the registration was cleared, and a stale registration is
|
|
332
|
+
// reclaimed by the next run.
|
|
333
|
+
await this.git.raw(["worktree", "prune"]).catch(() => { });
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Reclaim leftovers before starting: this phase's directory and any other
|
|
337
|
+
* `flow-*` under the configured root that carries OUR owner marker.
|
|
338
|
+
*
|
|
339
|
+
* A killed process (a laptop closing, a CI timeout, a SIGKILL) leaves a
|
|
340
|
+
* worktree no finally ever runs for. Without this they pile up one full
|
|
341
|
+
* checkout at a time, and the only symptom is a disk that fills.
|
|
342
|
+
*
|
|
343
|
+
* ⚠️ AN UNOWNED DIRECTORY IS REFUSED, NOT RECLAIMED — and the refusal comes
|
|
344
|
+
* FIRST. A directory that merely looks like ours may hold somebody's
|
|
345
|
+
* in-flight work; two sessions have already been lost to a tool removing
|
|
346
|
+
* worktrees it did not create.
|
|
347
|
+
*/
|
|
348
|
+
async reclaimWorktrees(current) {
|
|
349
|
+
if ((0, fs_1.existsSync)(current) && !(0, fs_1.existsSync)((0, path_1.join)(current, ".grm-flow", "owner"))) {
|
|
350
|
+
throw new Error(`${current} exists and carries no owner marker — refusing to reuse it.`);
|
|
351
|
+
}
|
|
352
|
+
const rootDir = (0, path_1.join)(current, "..");
|
|
353
|
+
let entries = [];
|
|
354
|
+
try {
|
|
355
|
+
entries = (0, fs_1.readdirSync)(rootDir).filter(e => e.startsWith("flow-"));
|
|
356
|
+
}
|
|
357
|
+
catch (_a) {
|
|
358
|
+
return; // the root does not exist yet
|
|
359
|
+
}
|
|
360
|
+
for (const e of entries) {
|
|
361
|
+
const dir = (0, path_1.join)(rootDir, e);
|
|
362
|
+
if (!(0, fs_1.existsSync)((0, path_1.join)(dir, ".grm-flow", "owner")))
|
|
363
|
+
continue; // not ours
|
|
364
|
+
// SAFE to swallow, for the same reason as removeWorktree: the
|
|
365
|
+
// outcome is re-measured by existsSync rather than inferred.
|
|
366
|
+
await this.git.raw(["worktree", "remove", "--force", dir]).catch(() => { });
|
|
367
|
+
if ((0, fs_1.existsSync)(dir)) {
|
|
368
|
+
try {
|
|
369
|
+
(0, fs_1.rmSync)(dir, { recursive: true, force: true });
|
|
370
|
+
}
|
|
371
|
+
catch ( /* prune reports */_b) { /* prune reports */ }
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
// SAFE to swallow: housekeeping with no answer. Nothing branches on
|
|
375
|
+
// whether the registration was cleared, and a stale registration is
|
|
376
|
+
// reclaimed by the next run.
|
|
377
|
+
await this.git.raw(["worktree", "prune"]).catch(() => { });
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* The commits in a range, read from THIS controller's repository.
|
|
381
|
+
*
|
|
382
|
+
* 🔴 NOT `getGitLogAsJson`. That helper constructs `simpleGit()` with no
|
|
383
|
+
* working directory, so it reads whatever `process.cwd()` happens to be —
|
|
384
|
+
* which is the repository under management only by coincidence. Measured
|
|
385
|
+
* 2026-08-06: seven cases of this suite failed because the derivation was
|
|
386
|
+
* grading the commits of the grm checkout the tests run from instead of the
|
|
387
|
+
* fixture repository it was handed. The CLI never noticed, because there cwd
|
|
388
|
+
* IS the repository. A class that takes a `root` has to honour it.
|
|
389
|
+
*/
|
|
390
|
+
async logCommits(args) {
|
|
391
|
+
try {
|
|
392
|
+
const log = await this.git.log(args);
|
|
393
|
+
return log.all.map(c => ({
|
|
394
|
+
hash: c.hash,
|
|
395
|
+
shortHash: c.hash.substring(0, 7),
|
|
396
|
+
message: c.message,
|
|
397
|
+
body: c.body,
|
|
398
|
+
authorName: c.author_name,
|
|
399
|
+
authorEmail: c.author_email,
|
|
400
|
+
date: c.date,
|
|
401
|
+
}));
|
|
402
|
+
}
|
|
403
|
+
catch (_a) {
|
|
404
|
+
// SAFE to swallow: `git log` answers with its OUTPUT, so this is not
|
|
405
|
+
// the exit-code shape lineTags warns about. An empty range and a
|
|
406
|
+
// failed one both produce no commits, and `nextVersion` then falls to
|
|
407
|
+
// the configured defaultBump — a smaller bump than the history would
|
|
408
|
+
// have justified, never a larger one.
|
|
409
|
+
return [];
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
escapeRe(s) {
|
|
413
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
414
|
+
}
|
|
415
|
+
/** What `run` would do. Reads only. */
|
|
416
|
+
async plan(name) {
|
|
417
|
+
var _a, _b, _c, _d;
|
|
418
|
+
const p = this.phase(name);
|
|
419
|
+
let ahead = 0, behind = 0;
|
|
420
|
+
if (p.mergeFrom) {
|
|
421
|
+
ahead = await this.count(`${p.branch}..${p.mergeFrom}`);
|
|
422
|
+
behind = await this.count(`${p.mergeFrom}..${p.branch}`);
|
|
423
|
+
}
|
|
424
|
+
const { version, current, why } = await this.nextVersion(p);
|
|
425
|
+
return {
|
|
426
|
+
phase: name,
|
|
427
|
+
branch: p.branch,
|
|
428
|
+
channel: p.channel,
|
|
429
|
+
mergeFrom: p.mergeFrom,
|
|
430
|
+
ahead, behind,
|
|
431
|
+
current, next: version, bumpWhy: why,
|
|
432
|
+
willTag: p.tag !== false,
|
|
433
|
+
willDeleteSource: p.deleteSource === true,
|
|
434
|
+
mergeStrategy: this.strategyOf(p),
|
|
435
|
+
backMerge: (_a = p.backMerge) !== null && _a !== void 0 ? _a : [],
|
|
436
|
+
worktree: ((_d = (_c = (_b = this.config) === null || _b === void 0 ? void 0 : _b.flow) === null || _c === void 0 ? void 0 : _c.worktree) === null || _d === void 0 ? void 0 : _d.enabled) ? this.worktreeDir(name) : undefined,
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
async count(range) {
|
|
440
|
+
// SAFE to swallow: `rev-list --count` PRINTS its answer, so the number is
|
|
441
|
+
// read out of stdout and not out of an exit status — see lineTags for
|
|
442
|
+
// the shape that is not safe. Both failure directions land on 0: the
|
|
443
|
+
// catch returns "0", and a wrapper that resolved an error with empty
|
|
444
|
+
// output gives NaN, which the guard below also turns into 0. Zero is the
|
|
445
|
+
// fail-CLOSED direction — `run` refuses a promotion it measures as
|
|
446
|
+
// carrying nothing, so a broken measurement mints no version.
|
|
447
|
+
const out = await this.git.raw(["rev-list", "--count", range]).catch(() => "0");
|
|
448
|
+
const n = parseInt(String(out).trim(), 10);
|
|
449
|
+
return Number.isFinite(n) ? n : 0;
|
|
450
|
+
}
|
|
451
|
+
worktreeDir(phase) {
|
|
452
|
+
var _a, _b, _c, _d;
|
|
453
|
+
const dir = (_d = (_c = (_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.flow) === null || _b === void 0 ? void 0 : _b.worktree) === null || _c === void 0 ? void 0 : _c.dir) !== null && _d !== void 0 ? _d : ".grm/worktrees";
|
|
454
|
+
return (0, path_1.resolve)(this.root, dir, `flow-${phase}`.replace(/[^A-Za-z0-9_.-]/g, "-"));
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* mergeInto — carry `source` into `target`, and move `target`'s ref.
|
|
458
|
+
*
|
|
459
|
+
* THE ONE MECHANISM. A promotion, a back-merge and a route are the same act
|
|
460
|
+
* with different names, and writing them three times is how they drift: the
|
|
461
|
+
* promotion would keep its worktree isolation and its expected-value
|
|
462
|
+
* update-ref while a back-merge quietly did a `checkout` in the shared tree.
|
|
463
|
+
* Every caller gets both properties or neither.
|
|
464
|
+
*
|
|
465
|
+
* Returns the merge commit, or "" when there was nothing to merge — the
|
|
466
|
+
* callers treat "nothing to do" differently, so this does not decide it.
|
|
467
|
+
*/
|
|
468
|
+
/**
|
|
469
|
+
* THE MERGE ENGINE, exposed so that the only other thing in this codebase
|
|
470
|
+
* that carries a branch into another one does not grow a second copy.
|
|
471
|
+
*
|
|
472
|
+
* `TrackController.forwardPort` cascades a maintenance fix up the tracks,
|
|
473
|
+
* and every hazard `mergeInto` handles applies there identically: a target
|
|
474
|
+
* held in another worktree, `update-ref` desyncing a checked-out branch, a
|
|
475
|
+
* dirty tree that would carry changes across a checkout, `--no-ff` so the
|
|
476
|
+
* pre-merge hooks actually run. Re-implementing that for tracks would put
|
|
477
|
+
* those four decisions in two places, and the first one to drift would do
|
|
478
|
+
* it silently — every step still reporting success.
|
|
479
|
+
*
|
|
480
|
+
* Returns the merge commit, or "" when the target already contains the
|
|
481
|
+
* source (nothing to do is not a failure).
|
|
482
|
+
*/
|
|
483
|
+
async mergeBranch(target, source, strategy, label) {
|
|
484
|
+
return this.mergeInto(target, source, strategy, label);
|
|
485
|
+
}
|
|
486
|
+
async mergeInto(target, source, strategy, label) {
|
|
487
|
+
var _a, _b, _c;
|
|
488
|
+
// Both ends must exist. `rev-list --count a..b` FAILS for a missing ref and
|
|
489
|
+
// count() maps a failure to 0, which reads as "nothing to merge" — so a
|
|
490
|
+
// route pointing at a branch that is not there would be reported as
|
|
491
|
+
// skipped-because-already-current.
|
|
492
|
+
await this.assertBranch(target, "the merge target");
|
|
493
|
+
await this.assertBranch(source, "the merge source");
|
|
494
|
+
const ahead = await this.count(`${target}..${source}`);
|
|
495
|
+
if (ahead === 0)
|
|
496
|
+
return "";
|
|
497
|
+
// ── WHO HOLDS THE TARGET DECIDES WHERE THE MERGE HAPPENS ──
|
|
498
|
+
//
|
|
499
|
+
// 🔴 `update-ref` ON A CHECKED-OUT BRANCH DESYNCS THAT CHECKOUT. The ref
|
|
500
|
+
// moves, HEAD follows it, and the working tree and index do NOT — so every
|
|
501
|
+
// file that differs between the old and new commit shows up as a local
|
|
502
|
+
// modification or deletion in a checkout the operator was not even looking
|
|
503
|
+
// at. The promotion path never hit this because its target (the release
|
|
504
|
+
// line) is not the branch anyone stands on; a BACK-MERGE targets the
|
|
505
|
+
// integration line, which is exactly the branch everyone stands on.
|
|
506
|
+
//
|
|
507
|
+
// So: held HERE → merge in place, which is the only way that working tree
|
|
508
|
+
// stays consistent (git updates it as part of the merge). Held in ANOTHER
|
|
509
|
+
// worktree → refuse by name; moving somebody else's checked-out branch is
|
|
510
|
+
// not ours to do.
|
|
511
|
+
const holder = await this.worktreeHolding(target);
|
|
512
|
+
const heldHere = holder !== "" && (0, path_1.resolve)(holder) === (0, path_1.resolve)(this.root);
|
|
513
|
+
if (holder && !heldHere) {
|
|
514
|
+
throw new Error(`${target} is checked out in another worktree (${holder}), so its ref cannot be ` +
|
|
515
|
+
`moved from here without desyncing that checkout. Run the merge there, or detach it.`);
|
|
516
|
+
}
|
|
517
|
+
const useWorktree = ((_c = (_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.flow) === null || _b === void 0 ? void 0 : _b.worktree) === null || _c === void 0 ? void 0 : _c.enabled) === true && !heldHere;
|
|
518
|
+
let workGit = this.git;
|
|
519
|
+
let createdDir = "";
|
|
520
|
+
// The branch to return to when this merge did not happen in a worktree.
|
|
521
|
+
let restoreTo = "";
|
|
522
|
+
try {
|
|
523
|
+
if (!useWorktree) {
|
|
524
|
+
// ── WITHOUT A WORKTREE THE TARGET HAS TO BE CHECKED OUT ──
|
|
525
|
+
//
|
|
526
|
+
// `git merge <source>` merges into HEAD, so merging into a branch
|
|
527
|
+
// you are not standing on is not a merge at all: with HEAD on main
|
|
528
|
+
// and the target dev, `merge main` answers "Already up to date",
|
|
529
|
+
// dev's ref never moves, and the caller is told it succeeded.
|
|
530
|
+
// Measured 2026-08-06 — five cases of the back-merge and route
|
|
531
|
+
// suites failed exactly here, and the promotion path had hidden it
|
|
532
|
+
// because its target was always the branch you were on.
|
|
533
|
+
const head = (await this.git.raw(["rev-parse", "--abbrev-ref", "HEAD"])).trim();
|
|
534
|
+
if (head !== target) {
|
|
535
|
+
// A checkout across a dirty tree either refuses or carries the
|
|
536
|
+
// changes onto the other branch. Neither belongs in a
|
|
537
|
+
// promotion, so it is refused by name — and this is the reason
|
|
538
|
+
// `worktree.enabled` exists.
|
|
539
|
+
const dirty = (await this.git.raw(["status", "--porcelain", "--untracked-files=no"])).trim();
|
|
540
|
+
if (dirty) {
|
|
541
|
+
throw new Error(`${target} is not checked out and the tree is not clean, so it cannot be ` +
|
|
542
|
+
`checked out to merge ${source} into it. Enable flow.worktree, or clean the tree.`);
|
|
543
|
+
}
|
|
544
|
+
await this.git.raw(["checkout", target]);
|
|
545
|
+
restoreTo = head;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
if (useWorktree) {
|
|
549
|
+
const wtDir = this.worktreeDir(label);
|
|
550
|
+
await this.reclaimWorktrees(wtDir);
|
|
551
|
+
(0, fs_1.mkdirSync)((0, path_1.join)(wtDir, ".."), { recursive: true });
|
|
552
|
+
// DETACHED, because git refuses the same branch in two worktrees
|
|
553
|
+
// and the target is very often checked out where you are standing.
|
|
554
|
+
await this.git.raw(["worktree", "add", "--detach", wtDir, target]);
|
|
555
|
+
createdDir = wtDir;
|
|
556
|
+
(0, fs_1.mkdirSync)((0, path_1.join)(wtDir, ".grm-flow"), { recursive: true });
|
|
557
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(wtDir, ".grm-flow", "owner"), OWNER_TOKEN + "\n");
|
|
558
|
+
workGit = (0, simple_git_1.default)(wtDir);
|
|
559
|
+
}
|
|
560
|
+
const args = ["merge", "--no-edit", source];
|
|
561
|
+
// See FlowPhase.merge: `ff` has to be asked for in writing.
|
|
562
|
+
if (strategy === "no-ff")
|
|
563
|
+
args.splice(1, 0, "--no-ff");
|
|
564
|
+
await workGit.raw(args);
|
|
565
|
+
// ── 🔴 A CONFLICT IS NOT AN ERROR HERE. IT IS A RESOLVED PROMISE. ──
|
|
566
|
+
//
|
|
567
|
+
// `git merge` exits 1 on a conflict and writes "CONFLICT (add/add):
|
|
568
|
+
// …" to STDOUT, leaving stderr empty; simple-git 3.27.0 RESOLVES a
|
|
569
|
+
// non-zero exit whose stderr is empty. Measured 2026-08-24 against
|
|
570
|
+
// this repository's own simple-git: the promise resolved with the
|
|
571
|
+
// conflict text as its VALUE and the tree was left at `AA <file>`.
|
|
572
|
+
//
|
|
573
|
+
// So the await above cannot be trusted to have merged anything, and
|
|
574
|
+
// nothing downstream would notice: the promotion goes on to cut a
|
|
575
|
+
// tag over an unmerged index, and every step reports success. This
|
|
576
|
+
// is the same class the lineTags header describes — THE ANSWER IS
|
|
577
|
+
// THE OUTPUT, NOT THE ABSENCE OF A THROW — reaching the write path.
|
|
578
|
+
//
|
|
579
|
+
// The state is read from git rather than from the merge's text,
|
|
580
|
+
// because the text is localised and the index is not.
|
|
581
|
+
const unmerged = (await workGit.raw(["ls-files", "--unmerged"])).trim();
|
|
582
|
+
if (unmerged) {
|
|
583
|
+
// ABORT RATHER THAN LEAVE IT. A caller that continues past this
|
|
584
|
+
// is walking a cascade, and each later step would merge from a
|
|
585
|
+
// branch whose working tree is mid-conflict. Worse on a shared
|
|
586
|
+
// checkout, where the half-merged state belongs to whoever else
|
|
587
|
+
// is standing on that branch.
|
|
588
|
+
await workGit.raw(["merge", "--abort"]).catch(() => { });
|
|
589
|
+
throw new Error(`merging ${source} into ${target} conflicts (${unmerged.split("\n").length} unmerged path(s)); the merge was aborted and nothing moved`);
|
|
590
|
+
}
|
|
591
|
+
const head = (await workGit.revparse(["HEAD"])).trim();
|
|
592
|
+
if (useWorktree) {
|
|
593
|
+
// The worktree is DETACHED, so the merge did not move the branch.
|
|
594
|
+
// `update-ref` with the OLD value as the expected one, so a peer
|
|
595
|
+
// that advanced it in the meantime makes this FAIL rather than
|
|
596
|
+
// silently discarding their work.
|
|
597
|
+
const before = (await this.git.revparse([target])).trim();
|
|
598
|
+
await this.git.raw([
|
|
599
|
+
"update-ref", "-m", `grm flow ${label}: merge ${source}`,
|
|
600
|
+
`refs/heads/${target}`, head, before,
|
|
601
|
+
]);
|
|
602
|
+
}
|
|
603
|
+
return head;
|
|
604
|
+
}
|
|
605
|
+
finally {
|
|
606
|
+
if (createdDir)
|
|
607
|
+
await this.removeWorktree(createdDir);
|
|
608
|
+
// Put the operator back where they were, even on the throw path: a
|
|
609
|
+
// failed promotion that also leaves you on another branch turns one
|
|
610
|
+
// problem into two.
|
|
611
|
+
if (restoreTo)
|
|
612
|
+
await this.git.raw(["checkout", restoreTo]).catch(() => { });
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
/** The route declaration, or a refusal that names what IS declared. */
|
|
616
|
+
route(name) {
|
|
617
|
+
var _a, _b, _c;
|
|
618
|
+
const routes = (_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.flow) === null || _b === void 0 ? void 0 : _b.routes;
|
|
619
|
+
if (!routes || !routes[name]) {
|
|
620
|
+
throw new Error(`Unknown route '${name}'. Declared: ${routes && Object.keys(routes).length ? Object.keys(routes).join(", ") : "(none)"}`);
|
|
621
|
+
}
|
|
622
|
+
const r = routes[name];
|
|
623
|
+
if (!r.from || !((_c = r.into) === null || _c === void 0 ? void 0 : _c.length)) {
|
|
624
|
+
throw new Error(`Route '${name}' needs both 'from' and a non-empty 'into'.`);
|
|
625
|
+
}
|
|
626
|
+
this.checkDirection(name, r);
|
|
627
|
+
return r;
|
|
628
|
+
}
|
|
629
|
+
/** What `syncRoute` would carry, per target. Reads only. */
|
|
630
|
+
async planRoute(name) {
|
|
631
|
+
const r = this.route(name);
|
|
632
|
+
const ahead = {};
|
|
633
|
+
for (const t of r.into)
|
|
634
|
+
ahead[t] = await this.count(`${t}..${r.from}`);
|
|
635
|
+
return { route: name, ahead };
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* The worktree path that has `branch` checked out, or "" if none does.
|
|
639
|
+
*
|
|
640
|
+
* `git worktree list --porcelain` is the only surface that answers this for
|
|
641
|
+
* EVERY checkout including the main one; `rev-parse --abbrev-ref HEAD` only
|
|
642
|
+
* ever answers about the directory you asked in, so a branch held by a
|
|
643
|
+
* sibling worktree would read as free.
|
|
644
|
+
*/
|
|
645
|
+
async worktreeHolding(branch) {
|
|
646
|
+
const out = await this.git.raw(["worktree", "list", "--porcelain"]).catch(() => "");
|
|
647
|
+
let path = "";
|
|
648
|
+
for (const line of String(out).split("\n")) {
|
|
649
|
+
if (line.startsWith("worktree "))
|
|
650
|
+
path = line.slice("worktree ".length).trim();
|
|
651
|
+
else if (line.startsWith("branch ")) {
|
|
652
|
+
const ref = line.slice("branch ".length).trim();
|
|
653
|
+
if (ref === `refs/heads/${branch}`)
|
|
654
|
+
return path;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
return "";
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Run a declared route: carry one branch's content to the others, with no
|
|
661
|
+
* version involved. This is the hotfix/bugfix shape.
|
|
662
|
+
*
|
|
663
|
+
* The DIRECTION is checked against `flow.lines` when both are declared. A
|
|
664
|
+
* hotfix route that says `down` but points up would merge the whole
|
|
665
|
+
* integration line into the release line — a release nobody planned, which
|
|
666
|
+
* every step would report as success.
|
|
667
|
+
*/
|
|
668
|
+
async syncRoute(name) {
|
|
669
|
+
const r = this.route(name);
|
|
670
|
+
const merged = {};
|
|
671
|
+
const skipped = [];
|
|
672
|
+
for (const target of r.into) {
|
|
673
|
+
const head = await this.mergeInto(target, r.from, r.merge === "ff" ? "ff" : "no-ff", `route-${name}-${target}`);
|
|
674
|
+
if (head)
|
|
675
|
+
merged[target] = head;
|
|
676
|
+
else
|
|
677
|
+
skipped.push(target);
|
|
678
|
+
}
|
|
679
|
+
return { route: name, merged, skipped };
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* The declared direction has to match the ladder. A branch not on the ladder
|
|
683
|
+
* is not an error — topic branches are not lines — so it is simply not checked.
|
|
684
|
+
*/
|
|
685
|
+
checkDirection(name, r) {
|
|
686
|
+
var _a, _b;
|
|
687
|
+
const lines = (_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.flow) === null || _b === void 0 ? void 0 : _b.lines;
|
|
688
|
+
if (!(lines === null || lines === void 0 ? void 0 : lines.length) || !r.direction)
|
|
689
|
+
return;
|
|
690
|
+
const from = lines.indexOf(r.from);
|
|
691
|
+
if (from === -1)
|
|
692
|
+
return;
|
|
693
|
+
for (const target of r.into) {
|
|
694
|
+
const to = lines.indexOf(target);
|
|
695
|
+
if (to === -1)
|
|
696
|
+
continue;
|
|
697
|
+
const actual = to > from ? "down" : to < from ? "up" : "same";
|
|
698
|
+
if (actual !== r.direction) {
|
|
699
|
+
throw new Error(`Route '${name}' declares direction '${r.direction}' but ${r.from} -> ${target} ` +
|
|
700
|
+
`is '${actual}' on the declared ladder [${lines.join(" > ")}].`);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Execute the phase.
|
|
706
|
+
*
|
|
707
|
+
* Returns the plan it acted on, with `tagged` set when a tag was created.
|
|
708
|
+
*/
|
|
709
|
+
async run(name) {
|
|
710
|
+
var _a;
|
|
711
|
+
const p = this.phase(name);
|
|
712
|
+
const plan = await this.plan(name);
|
|
713
|
+
if (p.mergeFrom && plan.ahead === 0) {
|
|
714
|
+
throw new Error(`${p.mergeFrom} carries nothing that ${p.branch} does not already have. ` +
|
|
715
|
+
`There is nothing to promote.`);
|
|
716
|
+
}
|
|
717
|
+
const mergeCommit = p.mergeFrom
|
|
718
|
+
// The label IS the phase name, so the directory `plan` reports
|
|
719
|
+
// (`flow-<phase>`) is the directory `run` creates. A prettier label
|
|
720
|
+
// here made the plan advertise a path nothing ever used.
|
|
721
|
+
? await this.mergeInto(p.branch, p.mergeFrom, this.strategyOf(p), name)
|
|
722
|
+
: "";
|
|
723
|
+
// AFTER the merge — see the header. The range now contains the work.
|
|
724
|
+
const { version, current, why } = await this.nextVersion(p, mergeCommit || p.branch);
|
|
725
|
+
plan.next = version;
|
|
726
|
+
plan.current = current;
|
|
727
|
+
plan.bumpWhy = why;
|
|
728
|
+
// The tag is created from the MAIN repository, not from the worktree that
|
|
729
|
+
// performed the merge: the merge commit is in the object database and the
|
|
730
|
+
// branch already points at it, so the worktree has no part left to play —
|
|
731
|
+
// and it has been removed by now. Naming the commit explicitly, because
|
|
732
|
+
// HEAD here is whatever the operator has checked out.
|
|
733
|
+
if (p.tag !== false) {
|
|
734
|
+
await this.git.raw([
|
|
735
|
+
"tag", "-a", version, "-m", `${version}\n\ngrm flow ${name}`,
|
|
736
|
+
...(mergeCommit ? [mergeCommit] : [p.branch]),
|
|
737
|
+
]);
|
|
738
|
+
}
|
|
739
|
+
// ── BACK-MERGE, AFTER THE TAG ──
|
|
740
|
+
//
|
|
741
|
+
// After the tag, so every branch that receives the merge also receives the
|
|
742
|
+
// tag and can describe it. Before deleteSource, because a source that is
|
|
743
|
+
// about to be deleted may itself be one of the branches that needs it.
|
|
744
|
+
const backMerged = {};
|
|
745
|
+
for (const target of (_a = p.backMerge) !== null && _a !== void 0 ? _a : []) {
|
|
746
|
+
if (target === p.branch)
|
|
747
|
+
continue; // merging a branch into itself
|
|
748
|
+
const head = await this.mergeInto(target, p.branch, this.strategyOf(p), `back-${name}-${target}`);
|
|
749
|
+
if (head)
|
|
750
|
+
backMerged[target] = head;
|
|
751
|
+
}
|
|
752
|
+
if (p.deleteSource === true && p.mergeFrom) {
|
|
753
|
+
await this.git.raw(["branch", "-d", p.mergeFrom]);
|
|
754
|
+
}
|
|
755
|
+
return {
|
|
756
|
+
...plan,
|
|
757
|
+
tagged: p.tag !== false ? plan.next : undefined,
|
|
758
|
+
mergeCommit: mergeCommit || undefined,
|
|
759
|
+
backMerged: Object.keys(backMerged).length ? backMerged : undefined,
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
exports.FlowController = FlowController;
|
|
764
|
+
//# sourceMappingURL=FlowController.js.map
|