Skip to content

Commit 91691ba

Browse files
authored
Merge pull request #167 from complexdatacollective/fix/update-notification
Fix: Semantic versioning logic
2 parents 903254a + c9479da commit 91691ba

4 files changed

Lines changed: 136 additions & 49 deletions

File tree

components/VersionSection.tsx

Lines changed: 1 addition & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -8,43 +8,12 @@ import { env } from '~/env';
88
import trackEvent from '~/lib/analytics';
99
import { getInstallationId } from '~/queries/appSettings';
1010
import { ensureError } from '~/utils/ensureError';
11+
import { getSemverUpdateType, semverSchema } from '~/utils/semVer';
1112
import SettingsSection from './layout/SettingsSection';
1213
import { Button } from './ui/Button';
1314
import Heading from './ui/typography/Heading';
1415
import Paragraph from './ui/typography/Paragraph';
1516

16-
const semverSchema = z
17-
.string()
18-
.regex(
19-
/^v(\d+)\.(\d+)\.(\d+)$/,
20-
"Invalid version format. Expected format is 'v1.2.3'.",
21-
)
22-
.transform((version) => {
23-
const [, major, minor, patch] = version.match(
24-
/^v(\d+)\.(\d+)\.(\d+)$/,
25-
) as string[];
26-
27-
if (!major || !minor || !patch) {
28-
throw new Error('Invalid version format');
29-
}
30-
31-
// Convert version parts to numbers
32-
const majorNum = parseInt(major, 10);
33-
const minorNum = parseInt(minor, 10);
34-
const patchNum = parseInt(patch, 10);
35-
36-
return {
37-
major: majorNum,
38-
minor: minorNum,
39-
patch: patchNum,
40-
toString() {
41-
return `v${majorNum}.${minorNum}.${patchNum}`;
42-
},
43-
};
44-
});
45-
46-
type SemVer = z.infer<typeof semverSchema>;
47-
4817
const GithubApiResponseSchema = z
4918
.object({
5019
html_url: z.string().url(),
@@ -58,22 +27,6 @@ const GithubApiResponseSchema = z
5827
releaseUrl: value.html_url,
5928
}));
6029

61-
function getSemverUpdateType(currentVersion: SemVer, newVersion: SemVer) {
62-
if (currentVersion.major < newVersion.major) {
63-
return 'major';
64-
}
65-
66-
if (currentVersion.minor < newVersion.minor) {
67-
return 'minor';
68-
}
69-
70-
if (currentVersion.patch < newVersion.patch) {
71-
return 'patch';
72-
}
73-
74-
return null;
75-
}
76-
7730
async function checkForUpdate() {
7831
unstable_noStore();
7932

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "fresco",
3-
"version": "1.2.0",
3+
"version": "1.2.1",
44
"private": true,
55
"type": "module",
66
"packageManager": "pnpm@9.1.1+sha256.9551e803dcb7a1839fdf5416153a844060c7bce013218ce823410532504ac10b",

utils/semVer.test.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { getSemverUpdateType } from '~/utils/semVer';
3+
4+
const currentVersion = { major: 1, minor: 2, patch: 3 };
5+
6+
describe('getSemverUpdateType', () => {
7+
it('returns "major" when new major version is higher', () => {
8+
expect(
9+
getSemverUpdateType(currentVersion, { major: 2, minor: 0, patch: 0 }),
10+
).toBe('major');
11+
expect(
12+
getSemverUpdateType(currentVersion, { major: 2, minor: 2, patch: 3 }),
13+
).toBe('major');
14+
});
15+
16+
it('returns "minor" when major is the same and new minor version is higher', () => {
17+
expect(
18+
getSemverUpdateType(currentVersion, { major: 1, minor: 3, patch: 0 }),
19+
).toBe('minor');
20+
expect(
21+
getSemverUpdateType(currentVersion, { major: 1, minor: 3, patch: 3 }),
22+
).toBe('minor');
23+
});
24+
25+
it('returns "patch" when major and minor are the same and new patch version is higher', () => {
26+
expect(
27+
getSemverUpdateType(currentVersion, { major: 1, minor: 2, patch: 4 }),
28+
).toBe('patch');
29+
});
30+
31+
it('returns null when versions are identical', () => {
32+
expect(
33+
getSemverUpdateType(currentVersion, { major: 1, minor: 2, patch: 3 }),
34+
).toBe(null);
35+
});
36+
37+
it('returns null when new version is lower than current version', () => {
38+
expect(
39+
getSemverUpdateType(currentVersion, { major: 1, minor: 2, patch: 2 }),
40+
).toBe(null);
41+
expect(
42+
getSemverUpdateType(currentVersion, { major: 1, minor: 1, patch: 3 }),
43+
).toBe(null);
44+
expect(
45+
getSemverUpdateType(currentVersion, { major: 0, minor: 9, patch: 5 }),
46+
).toBe(null);
47+
});
48+
49+
it('returns correct type when versions differ across all parts', () => {
50+
// Major is higher
51+
expect(
52+
getSemverUpdateType(
53+
{ major: 1, minor: 0, patch: 0 },
54+
{ major: 2, minor: 1, patch: 1 },
55+
),
56+
).toBe('major');
57+
// Minor is higher
58+
expect(
59+
getSemverUpdateType(
60+
{ major: 1, minor: 1, patch: 0 },
61+
{ major: 1, minor: 2, patch: 0 },
62+
),
63+
).toBe('minor');
64+
// Patch is higher
65+
expect(
66+
getSemverUpdateType(
67+
{ major: 1, minor: 2, patch: 3 },
68+
{ major: 1, minor: 2, patch: 4 },
69+
),
70+
).toBe('patch');
71+
});
72+
});

utils/semVer.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { z } from 'zod';
2+
3+
export const semverSchema = z
4+
.string()
5+
.regex(
6+
/^v(\d+)\.(\d+)\.(\d+)$/,
7+
"Invalid version format. Expected format is 'v1.2.3'.",
8+
)
9+
.transform((version) => {
10+
const [, major, minor, patch] = version.match(
11+
/^v(\d+)\.(\d+)\.(\d+)$/,
12+
) as string[];
13+
14+
if (!major || !minor || !patch) {
15+
throw new Error('Invalid version format');
16+
}
17+
18+
// Convert version parts to numbers
19+
const majorNum = parseInt(major, 10);
20+
const minorNum = parseInt(minor, 10);
21+
const patchNum = parseInt(patch, 10);
22+
23+
return {
24+
major: majorNum,
25+
minor: minorNum,
26+
patch: patchNum,
27+
toString() {
28+
return `v${majorNum}.${minorNum}.${patchNum}`;
29+
},
30+
};
31+
});
32+
33+
type SemVer = z.infer<typeof semverSchema>;
34+
35+
export function getSemverUpdateType(
36+
currentVersion: SemVer,
37+
newVersion: SemVer,
38+
): 'major' | 'minor' | 'patch' | null {
39+
// early return if versions are identical
40+
if (currentVersion === newVersion) {
41+
return null;
42+
}
43+
44+
if (newVersion.major > currentVersion.major) {
45+
return 'major';
46+
}
47+
48+
if (newVersion.major === currentVersion.major) {
49+
if (newVersion.minor > currentVersion.minor) {
50+
return 'minor';
51+
}
52+
53+
if (newVersion.minor === currentVersion.minor) {
54+
if (newVersion.patch > currentVersion.patch) {
55+
return 'patch';
56+
}
57+
}
58+
}
59+
60+
// If we reach this point, we know the current version is higher than the new version
61+
return null;
62+
}

0 commit comments

Comments
 (0)