-
-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This section is moved to getNewChangeEntries()
.
@@ -213,43 +264,17 @@ export async function updateChangelog({ | |||
packageRename, | |||
}); | |||
|
|||
// Ensure we have all tags on remote | |||
await runCommand('git', ['fetch', '--tags']); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line is moved to getMostRecentTag()
.
if (isReleaseCandidate) { | ||
if (!currentVersion) { | ||
throw new Error( | ||
`A version must be specified if 'isReleaseCandidate' is set.`, | ||
); | ||
} | ||
|
||
if (mostRecentTag === `${tagPrefixes[0]}${currentVersion ?? ''}`) { | ||
if (mostRecentTag === `${tagPrefixes[0]}${currentVersion}`) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
version: isReleaseCandidate ? currentVersion : undefined, | ||
category: ChangeCategory.Uncategorized, | ||
description, | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the bracket that caused the bug. Moving it to above the getNewChangeEntries
call resolves the error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks good. Few minor suggestions.
projectRootDirectory, | ||
}: AddNewCommitsOptions) { | ||
const commitRange = | ||
mostRecentTag === null ? 'HEAD' : `${mostRecentTag}..HEAD`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about this?
mostRecentTag === null ? 'HEAD' : `${mostRecentTag}..HEAD`; | |
mostRecentTag ? `${mostRecentTag}..HEAD` : 'HEAD'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 (isReleaseCandidate) { | ||
if (!currentVersion) { | ||
throw new Error( | ||
`A version must be specified if 'isReleaseCandidate' is set.`, | ||
); | ||
} | ||
|
||
if (mostRecentTag === `${tagPrefixes[0]}${currentVersion ?? ''}`) { | ||
if (mostRecentTag === `${tagPrefixes[0]}${currentVersion}`) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
updateChangelog
does nothing ifisReleaseCandidate
isfalse
#180updateChangelog
and write comprehensive unit tests #188