Fix bug introduced to updateChangelog in #158#181
Conversation
d973245 to
4e8abf3
Compare
- `updateChangelog` returns results even if `isReleaseCandidate` is false
2103825 to
4cedf60
Compare
updateChangelog in #158 and write testsupdateChangelog in #158
4cedf60 to
aeabb00
Compare
| const newChangelogContent = changelog.toString(); | ||
| const isChangelogUpdated = changelogContent !== newChangelogContent; | ||
| return isChangelogUpdated ? newChangelogContent : undefined; |
There was a problem hiding this comment.
update() in cli.ts expects that updateChangelog return undefined if there are no updates to be added to the changelog.
| if ( | ||
| newCommits.length === 0 && | ||
| (!isReleaseCandidate || hasUnreleasedChanges) | ||
| ) { | ||
| return undefined; | ||
| } |
There was a problem hiding this comment.
Previously, the undefined return branch was located here.
| const commitRange = | ||
| mostRecentTag === null ? 'HEAD' : `${mostRecentTag}..HEAD`; | ||
| const commitsHashesSinceLastRelease = await getCommitHashesInRange( | ||
| commitRange, | ||
| projectRootDirectory, | ||
| ); | ||
| const commits = await getCommits(commitsHashesSinceLastRelease); | ||
|
|
||
| const loggedPrNumbers = getAllLoggedPrNumbers(changelog); | ||
| const newCommits = commits.filter( | ||
| ({ prNumber }) => | ||
| prNumber === undefined || !loggedPrNumbers.includes(prNumber), | ||
| ); |
There was a problem hiding this comment.
This section is moved to getNewChangeEntries().
| const newChangeEntries = newCommits.map(({ prNumber, description }) => { | ||
| if (prNumber) { | ||
| const suffix = `([#${prNumber}](${repoUrl}/pull/${prNumber}))`; | ||
| return `${description} ${suffix}`; | ||
| } | ||
| return description; | ||
| }); |
There was a problem hiding this comment.
This section is moved to getNewChangeEntries().
| }); | ||
|
|
||
| // Ensure we have all tags on remote | ||
| await runCommand('git', ['fetch', '--tags']); |
There was a problem hiding this comment.
This line is moved to getMostRecentTag().
| } | ||
|
|
||
| if (mostRecentTag === `${tagPrefixes[0]}${currentVersion ?? ''}`) { | ||
| if (mostRecentTag === `${tagPrefixes[0]}${currentVersion}`) { |
There was a problem hiding this comment.
currentVersion is narrowed to string thanks to the if (!currentVersion) block being moved directly above. Previously as string or ?? '' was required.
| @@ -264,28 +289,31 @@ export async function updateChangelog({ | |||
| changelog.addRelease({ version: currentVersion }); | |||
There was a problem hiding this comment.
currentVersion is correctly narrowed due to all related branches being refactored to fall under the if (isReleaseCandidate) block instead of there being multiple if (isReleaseCandidate &&) blocks. currentVersion as Version casting is now unnecessary.
| category: ChangeCategory.Uncategorized, | ||
| description, | ||
| }); | ||
| } |
There was a problem hiding this comment.
This is the bracket that caused the bug. Moving it to above the getNewChangeEntries call resolves the error.
kanthesha
left a comment
There was a problem hiding this comment.
Overall looks good. Few minor suggestions.
| projectRootDirectory, | ||
| }: AddNewCommitsOptions) { | ||
| const commitRange = | ||
| mostRecentTag === null ? 'HEAD' : `${mostRecentTag}..HEAD`; |
There was a problem hiding this comment.
is there a possibility to be little more flexible here!
| mostRecentTag === null ? 'HEAD' : `${mostRecentTag}..HEAD`; | |
| !mostRecentTag ? 'HEAD' : `${mostRecentTag}..HEAD`; |
There was a problem hiding this comment.
Thanks for pointing these out! Unfortunately, this one results in a linter error:
Unexpected negated condition. eslint(no-negated-condition)
There was a problem hiding this comment.
How about this?
| mostRecentTag === null ? 'HEAD' : `${mostRecentTag}..HEAD`; | |
| mostRecentTag ? `${mostRecentTag}..HEAD` : 'HEAD'; |
There was a problem hiding this comment.
That would work, but I would prefer to prioritize type safety over brevity here, since mostRecentTag is derived from the output of running an actual git command in shell. There are no strong guarantees on what the runtime output will be, so we can't completely rule out edge cases where e.g. an empty string or undefined is returned.
This probably aligns with the original motivation for getMostRecentTag returning null in its early exit branch. It represents the exception case where we know for sure that no tag exists, which is distinct from the error case where some tags exist, but the most recent tag could not be found due to a failure or edge case. This second case would be better represented by an undefined.
What are your thoughts? In general, I feel less comfortable replacing === null with !, because our codebase mostly uses undefined, which makes me assume that any null usage is intentional.
There was a problem hiding this comment.
Hmm, we did use null intentionally to represent lack of a most recent tag, so the use of null matches the type that getMostRecentTag returns. However, I suppose that the tag name could be an empty string in theory if for some reason git describe returns an empty string, and that case we would want to fall back to HEAD. So, we could update getMostRecentTag to just return string instead of string | null, except that in case there is no recent tag, it would then return '';. That might look strange, but we could add a comment saying why we're doing that or give that empty string a name that describe its purpose.
In any case, it seems that this line was merely copied from another function, so maybe we want to make that change in another PR?
There was a problem hiding this comment.
I like using null for clear distinction and visibility, but in any case I agree with revisiting this in its own ticket since this wasn't a change introduced by this PR.
5bd3d65 to
3aef0c9
Compare
mcmire
left a comment
There was a problem hiding this comment.
Hopefully in writing the tests no bugs are revealed, but comparing this to the original version, this makes sense to me.
| projectRootDirectory, | ||
| }: AddNewCommitsOptions) { | ||
| const commitRange = | ||
| mostRecentTag === null ? 'HEAD' : `${mostRecentTag}..HEAD`; |
There was a problem hiding this comment.
Hmm, we did use null intentionally to represent lack of a most recent tag, so the use of null matches the type that getMostRecentTag returns. However, I suppose that the tag name could be an empty string in theory if for some reason git describe returns an empty string, and that case we would want to fall back to HEAD. So, we could update getMostRecentTag to just return string instead of string | null, except that in case there is no recent tag, it would then return '';. That might look strange, but we could add a comment saying why we're doing that or give that empty string a name that describe its purpose.
In any case, it seems that this line was merely copied from another function, so maybe we want to make that change in another PR?
| } | ||
|
|
||
| if (mostRecentTag === `${tagPrefixes[0]}${currentVersion ?? ''}`) { | ||
| if (mostRecentTag === `${tagPrefixes[0]}${currentVersion}`) { |
There was a problem hiding this comment.
Hmm, I just realized that we only use the first tag prefix here, which seems strange. This may be a potential bug. But we can investigate that later.
There was a problem hiding this comment.
The jsdoc does specify that the first prefix is the intended prefix and the rest are fallbacks. parseChangelog also only uses the first prefix.
getMostRecentTag does iterate over the tagPrefix list. It should return the used prefix, or we could extract the prefix from the returned most recent tag.
But this also wasn't a change introduced in this PR, so it should get its own ticket.
Motivation
#158 incorrectly refactored the branches in updateChangelog, resulting in a state where new changelog entries weren't added to the return value if isReleaseCandidate is false.
Explanation
References
updateChangelogdoes nothing ifisReleaseCandidateisfalse#180updateChangelogand write comprehensive unit tests #188