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

Do not update {major}.json if an older version was published #483

Merged
merged 7 commits into from
Oct 3, 2023

Conversation

cleptric
Copy link
Member

@cleptric cleptric commented Aug 3, 2023

This should resolve an issue where craft wrongly updates the {major}.json if an older version is released. See getsentry/sentry-release-registry@6db6af4

Update (@Lms24): This PR not only resolves the issue mentioned above but it rewrites the linking cases to take previously existing symlinks into account. This way, we can more safely bump the respective symlinks. Added tests to cover the most important + edge case paths.

src/utils/symlink.ts Outdated Show resolved Hide resolved
src/utils/symlink.ts Outdated Show resolved Hide resolved
@cleptric cleptric marked this pull request as ready for review August 3, 2023 12:05
@cleptric cleptric requested a review from a team August 3, 2023 12:13
Copy link
Member

@asottile-sentry asottile-sentry left a comment

Choose a reason for hiding this comment

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

please add a test demonstrating the behaviour(s)

@cleptric
Copy link
Member Author

cleptric commented Aug 3, 2023

We currently do not have any tests in place. How would you envision these tests to look like?

@asottile-sentry
Copy link
Member

there's tests for other targets here -- can follow those as an example https://github.com/getsentry/craft/tree/master/src/targets/__tests__

@cleptric
Copy link
Member Author

cleptric commented Aug 3, 2023

Could you please provide a bit more specific guidance? How would you like these tests to be structured? Should we mock the symlinks in any way, etc.

@asottile-sentry
Copy link
Member

I don't particularly care beyond that there are tests for the functionality -- use your judgement to pick what makes sense for you

@asottile-sentry
Copy link
Member

I don't think this is quite right

consider this scenario:

  • release 1.0
  • release 2.0
  • release 1.0.1

I expect the symlink structure to be:

latest -> 2.0
2 -> 2.0
1 -> 1.0.1

before this patch it would do that I believe but after this patch the 1 symlink will be untouched because it was "older"

I think the actual fix needs to understand the current set of symlinks and make the symlink based on that

@tonyo
Copy link
Contributor

tonyo commented Oct 2, 2023

@asottile-sentry yep, you're right 👍
We had a chat with @Lms24 , we'll cover that case.

@Lms24
Copy link
Member

Lms24 commented Oct 2, 2023

Talked with @tonyo (writing down for future reference): The missing check is if new.major < old.major, in which case we also need to update the old_major.json file.

@asottile-sentry
Copy link
Member

asottile-sentry commented Oct 2, 2023

I'm not sure that's sufficient either -- the same problem will exist for minor revisions:

  • 1.1.0
  • 1.2.0
  • 1.1.1

@Lms24
Copy link
Member

Lms24 commented Oct 3, 2023

Ok, so I rewrote this logic to take existing minor.json and major.json links into account and only rewrite if applicable for them. (latest.json will only be bumped if we're dealing with an actually new, or the first version).
Also added a few tests for scenarios with previously existing symlinks and it seems like things work fine now.

Copy link
Member

@asottile-sentry asottile-sentry left a comment

Choose a reason for hiding this comment

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

new tests look useful! thanks for those!

Comment on lines 82 to 90
logger.debug(
`${
newVersionMajorSymlinkedVersion ? 'Updating' : 'Adding'
} symlink for "${majorVersionLink}" ${
newVersionMajorSymlinkedVersion
? `from version "${semVerToString(newVersionMajorSymlinkedVersion)}" `
: ''
}to "${newVersion}"`
);
Copy link
Member

Choose a reason for hiding this comment

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

there's way too much going on in this line -- if it's just for a debug line I'd just put the raw data in here instead of trying to make it end-user readable. should eliminate all the logic here as well since this seems error prone / more likely to break than the actual logic

Copy link
Member

Choose a reason for hiding this comment

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

I guess we can simplify it a little but while working on this, having some readable debug output was immensly helpful. (Fwiw, what we had before showed the wrong "from version" on many occasions).

Copy link
Member

Choose a reason for hiding this comment

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

I think logger.debug('symlinking', {before, after}) would probably be sufficient

Copy link
Member

Choose a reason for hiding this comment

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

updated it again

Comment on lines 115 to 120
try {
// using lstat instead of exists because broken symlinks return false for exists
fs.lstatSync(symlinkPath);
const linkedFile = fs.readlinkSync(symlinkPath);
return parseVersion(path.basename(linkedFile));
} catch {
Copy link
Member

Choose a reason for hiding this comment

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

this try catch is overly broad -- each of these 3 statements could raise errors but I think (?) you only intend to catch errors from the first one? this could hide a programming error especially after future maintenance

Copy link
Member

Choose a reason for hiding this comment

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

good catch!

const majorVersionLink = `${parsedNewVersion.major}.json`;
forceSymlink(baseVersionName, path.join(packageDir, majorVersionLink));
// Read possibly existing symlinks for major and minor versions of the new version
const newVersionMajorSymlinkedVersion = getExistingSymlinkedVersion(
Copy link
Member

Choose a reason for hiding this comment

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

this variable name seems overly verbose and also incorrect -- maybe existingMajorSymlink instead ? (same for the other one right below this)

Copy link
Member

Choose a reason for hiding this comment

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

I replaced it with existingLinkedMajorVersion which hopefully clarifies what this variable is holding (i.e. not the symlink but the already parsed version that this link is linking to).

Comment on lines 71 to 72
describe('correctly handles already existing symlinks', () => {
it('when updating an old major version', async () =>
Copy link
Member

Choose a reason for hiding this comment

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

the describe / its read backwards here -- I think they're supposed to be the other way around so the code "reads"?

Copy link
Member

Choose a reason for hiding this comment

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

I tried to "group" the three tests that cover multiple link calls and hence handle actually existing files/symlinks.
I'll just remove the describe block here to clear up the confusion.

Copy link
Member

@asottile-sentry asottile-sentry left a comment

Choose a reason for hiding this comment

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

@Lms24 Lms24 merged commit e391121 into master Oct 3, 2023
8 checks passed
@Lms24 Lms24 deleted the fix-major-symlinks branch October 3, 2023 14:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants