Skip to content
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

Merged
merged 6 commits into from
Feb 8, 2024

Conversation

MajorLift
Copy link
Contributor

@MajorLift MajorLift commented Dec 8, 2023

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

  • This error is fixed by moving the logic for retrieving new entries out of the if (isReleaseCandidate) block.
  • The code for retrieving new entries is also extracted into its own method: getNewChangeEntries, abstracting away details that obscured the flow of the method's core logic.
  • To verify that the buggy logic is correctly restored, it's necessary to compare the current state of updateChangelog to its state before the bug was introduced. For this, see: diff link.

References

@MajorLift MajorLift added bug Something isn't working team-wallet-framework labels Dec 8, 2023
@MajorLift MajorLift self-assigned this Dec 8, 2023
@MajorLift MajorLift force-pushed the 231207-update-changelog-fix-write-tests branch 3 times, most recently from d973245 to 4e8abf3 Compare December 14, 2023 21:39
@MajorLift MajorLift force-pushed the 231207-update-changelog-fix-write-tests branch from 2103825 to 4cedf60 Compare February 8, 2024 06:23
@MajorLift MajorLift changed the title Fix bug introduced to updateChangelog in #158 and write tests Fix bug introduced to updateChangelog in #158 Feb 8, 2024
@MajorLift MajorLift force-pushed the 231207-update-changelog-fix-write-tests branch from 4cedf60 to aeabb00 Compare February 8, 2024 06:40
Comment on lines +314 to +316
const newChangelogContent = changelog.toString();
const isChangelogUpdated = changelogContent !== newChangelogContent;
return isChangelogUpdated ? newChangelogContent : undefined;
Copy link
Contributor Author

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.

Comment on lines -238 to -243
if (
newCommits.length === 0 &&
(!isReleaseCandidate || hasUnreleasedChanges)
) {
return undefined;
}
Copy link
Contributor Author

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.

Comment on lines -222 to -234
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),
);
Copy link
Contributor Author

@MajorLift MajorLift Feb 8, 2024

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().

Comment on lines -271 to -277
const newChangeEntries = newCommits.map(({ prNumber, description }) => {
if (prNumber) {
const suffix = `([#${prNumber}](${repoUrl}/pull/${prNumber}))`;
return `${description} ${suffix}`;
}
return description;
});
Copy link
Contributor Author

@MajorLift MajorLift Feb 8, 2024

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']);
Copy link
Contributor Author

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}`) {
Copy link
Contributor Author

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 });
Copy link
Contributor Author

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,
});
}
Copy link
Contributor Author

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.

Copy link
Contributor

@kanthesha kanthesha left a 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`;
Copy link
Contributor

@kanthesha kanthesha Feb 8, 2024

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!

Suggested change
mostRecentTag === null ? 'HEAD' : `${mostRecentTag}..HEAD`;
!mostRecentTag ? 'HEAD' : `${mostRecentTag}..HEAD`;

Copy link
Contributor Author

@MajorLift MajorLift Feb 8, 2024

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)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about this?

Suggested change
mostRecentTag === null ? 'HEAD' : `${mostRecentTag}..HEAD`;
mostRecentTag ? `${mostRecentTag}..HEAD` : 'HEAD';

Copy link
Contributor Author

@MajorLift MajorLift Feb 8, 2024

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.

Copy link
Contributor

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?

Copy link
Contributor Author

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.

src/update-changelog.ts Outdated Show resolved Hide resolved
@MajorLift MajorLift force-pushed the 231207-update-changelog-fix-write-tests branch from 5bd3d65 to 3aef0c9 Compare February 8, 2024 14:53
Copy link
Contributor

@mcmire mcmire left a 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`;
Copy link
Contributor

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}`) {
Copy link
Contributor

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.

Copy link
Contributor Author

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.

@MajorLift MajorLift merged commit 5ebfe97 into main Feb 8, 2024
20 checks passed
@MajorLift MajorLift deleted the 231207-update-changelog-fix-write-tests branch February 8, 2024 22:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working team-wallet-framework
Projects
None yet
Development

Successfully merging this pull request may close these issues.

updateChangelog does nothing if isReleaseCandidate is false
3 participants