Skip to content

Commit

Permalink
fix: changelogs are derived from github releases
Browse files Browse the repository at this point in the history
  • Loading branch information
matthieu-locussol committed Feb 17, 2024
1 parent f3552d1 commit 5c24218
Show file tree
Hide file tree
Showing 11 changed files with 140 additions and 109 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ jobs:
releaseName: 'v__VERSION__'
releaseBody: |
${{ needs.changelog.outputs.CHANGELOG }}
See the assets to download this version and install.
See the assets to download this version and install it.
releaseDraft: false
prerelease: false

Expand Down
4 changes: 2 additions & 2 deletions apps/client/src/store/NewsStore.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ describe('NewsStore', () => {
json: async () => ({
changelogs: [
{
id: 1,
id: 'v1',
date: '2021-01-01T00:00:00.000Z',
text: 'Initial release',
},
Expand All @@ -65,7 +65,7 @@ describe('NewsStore', () => {
expect(fetchSpy).toHaveBeenCalledWith(`${import.meta.env.VITE_SERVER_URL}/changelog`);
expect(store.changelogs).toEqual([
{
id: 1,
id: 'v1',
date: '2021-01-01T00:00:00.000Z',
text: 'Initial release',
},
Expand Down
4 changes: 3 additions & 1 deletion apps/client/src/ui/hud/components/Changelog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Box from '@mui/material/Box';
import CircularProgress from '@mui/material/CircularProgress';
import Typography from '@mui/material/Typography';
import { observer } from 'mobx-react-lite';
import { TimeMgt } from 'shared/src/utils/timeMgt';
import { useStore } from '../../../store';

export const Changelog = observer(() => {
Expand All @@ -26,8 +27,9 @@ export const Changelog = observer(() => {
variant="body1"
color="textSecondary"
dangerouslySetInnerHTML={{
__html: `<b>${new Date(date).toLocaleDateString()}</b><br />${text}<br />`,
__html: `<b>${TimeMgt.formatDatetime(new Date(date))}</b><br />${text}<br />`,
}}
mb={2}
/>
))}
</>
Expand Down
42 changes: 26 additions & 16 deletions apps/server/src/routers/changelogRouter.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
import { RequestHandler } from 'express';
import { ChangelogSchema } from 'shared';
import { prisma } from '../utils/prisma';
import { ChangelogSchema, fetchGitHubReleases } from 'shared';

const CHANGELOG_DEFAULT_MESSAGE = 'See the assets to download this version and install it.';

export const changelogRouter: RequestHandler = async (_, res) => {
const changelogs = await prisma.changelog.findMany({
orderBy: {
date: 'desc',
},
take: 10,
});
try {
const releases = await fetchGitHubReleases();

const payload: ChangelogSchema = {
changelogs: releases
.map((release) => ({
...release,
body: release.body.replace(new RegExp(CHANGELOG_DEFAULT_MESSAGE, 'g'), ''),
}))
.filter(({ body }) => body !== '')
.map(({ tag_name, published_at, body }) => ({
id: tag_name,
date: published_at,
text: body,
})),
};

const result: ChangelogSchema = {
changelogs: changelogs.map((changelog) => ({
id: changelog.id,
date: changelog.date.toISOString(),
text: changelog.text,
})),
};
res.send(payload);
} catch (e) {
const payload: ChangelogSchema = {
changelogs: [],
};

res.send(result);
res.send(payload);
}
};
94 changes: 6 additions & 88 deletions apps/website/pages/api/version.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import type { NextRequest } from 'next/server';
import { fetchLatestGitHubRelease } from 'shared';

export const config = {
runtime: 'edge',
};

const RELEASE_ENDPOINT =
'https://api.github.com/repos/matthieu-locussol/taktix-app/releases/latest';

const ARCHITECTURES = [
'darwin-aarch64',
'darwin-x86_64',
Expand Down Expand Up @@ -35,8 +33,7 @@ export interface Version {

const handler = async (_: NextRequest) => {
try {
const data = await fetch(RELEASE_ENDPOINT);
const json: GitHubRelease = await data.json();
const { tag_name, published_at, assets } = await fetchLatestGitHubRelease();

const computeSignature = async (url: string) => {
const signatureData = await fetch(url);
Expand All @@ -46,12 +43,12 @@ const handler = async (_: NextRequest) => {

return new Response(
JSON.stringify({
version: json.tag_name,
notes: `Taktix ${json.tag_name}`,
pub_date: json.published_at,
version: tag_name,
notes: `Taktix ${tag_name}`,
pub_date: published_at,
platforms: (
await Promise.all(
json.assets
assets
.filter(({ name }) =>
['.AppImage.tar.gz', '.app.tar.gz', '.msi.zip'].some((extension) =>
name.endsWith(extension),
Expand Down Expand Up @@ -102,82 +99,3 @@ const handler = async (_: NextRequest) => {
};

export default handler;

export interface GitHubRelease {
url: string;
assets_url: string;
upload_url: string;
html_url: string;
id: number;
author: Author;
node_id: string;
tag_name: string;
target_commitish: string;
name: string;
draft: boolean;
prerelease: boolean;
created_at: string;
published_at: string;
assets: Asset[];
tarball_url: string;
zipball_url: string;
body: string;
}

export interface Author {
login: string;
id: number;
node_id: string;
avatar_url: string;
gravatar_id: string;
url: string;
html_url: string;
followers_url: string;
following_url: string;
gists_url: string;
starred_url: string;
subscriptions_url: string;
organizations_url: string;
repos_url: string;
events_url: string;
received_events_url: string;
type: string;
site_admin: boolean;
}

export interface Asset {
url: string;
id: number;
node_id: string;
name: string;
label: string;
uploader: Uploader;
content_type: string;
state: string;
size: number;
download_count: number;
created_at: string;
updated_at: string;
browser_download_url: string;
}

export interface Uploader {
login: string;
id: number;
node_id: string;
avatar_url: string;
gravatar_id: string;
url: string;
html_url: string;
followers_url: string;
following_url: string;
gists_url: string;
starred_url: string;
subscriptions_url: string;
organizations_url: string;
repos_url: string;
events_url: string;
received_events_url: string;
type: string;
site_admin: boolean;
}
37 changes: 37 additions & 0 deletions packages/shared/src/data/githubReleases.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { z } from 'zod';

export const GITHUB_RELEASES_ENDPOINT =
'https://api.github.com/repos/matthieu-locussol/taktix-app/releases';

export const GITHUB_LATEST_RELEASE_ENDPOINT =
'https://api.github.com/repos/matthieu-locussol/taktix-app/releases/latest';

export const zGitHubReleaseAsset = z.object({
name: z.string(),
browser_download_url: z.string(),
});

export type GitHubReleaseAsset = z.infer<typeof zGitHubReleaseAsset>;

export const zGitHubRelease = z.object({
tag_name: z.string(),
published_at: z.string(),
assets: z.array(zGitHubReleaseAsset),
body: z.string(),
});

export type GitHubRelease = z.infer<typeof zGitHubRelease>;

export const fetchGitHubReleases = async () => {
const response = await fetch(GITHUB_RELEASES_ENDPOINT);
const json = await response.json();

return z.array(zGitHubRelease).parse(json);
};

export const fetchLatestGitHubRelease = async () => {
const response = await fetch(GITHUB_LATEST_RELEASE_ENDPOINT);
const json = await response.json();

return zGitHubRelease.parse(json);
};
2 changes: 2 additions & 0 deletions packages/shared/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './config';
export * from './data/channelsInformations';
export * from './data/githubReleases';
export * from './data/rolesInformations';
export * from './data/roomsNames';
export * from './data/teleportationSpots';
Expand Down Expand Up @@ -27,3 +28,4 @@ export * from './utils/numberMgt';
export * from './utils/permissionMgt';
export * from './utils/stringMgt';
export * from './utils/timeMgt';
export * from './utils/zodMgt';
2 changes: 1 addition & 1 deletion packages/shared/src/schemas/ChangelogSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { z } from 'zod';
export const zChangelogSchema = z.object({
changelogs: z.array(
z.object({
id: z.number(),
id: z.string(),
date: z.string(),
text: z.string(),
}),
Expand Down
10 changes: 10 additions & 0 deletions packages/shared/src/utils/timeMgt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,14 @@ export namespace TimeMgt {
new Promise((resolve) => {
setTimeout(resolve, ms);
});

export const formatDatetime = (date: Date) => {
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
const hours = date.getHours();
const minutes = date.getMinutes();

return `${year}-${month}-${day} @ ${hours}:${minutes}`;
};
}
35 changes: 35 additions & 0 deletions packages/shared/src/utils/zodMgt.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { describe, expect, it } from 'vitest';
import { z } from 'zod';
import { ZodMgt } from './zodMgt';

describe('ZodMgt', () => {
describe('isValidZodLiteralUnion', () => {
it('should return true if the literals array has at least 2 elements', () => {
const literals = [z.literal('a'), z.literal('b')];
expect(ZodMgt.isValidZodLiteralUnion(literals)).toBe(true);
});

it('should return false if the literals array has less than 2 elements', () => {
const literals = [z.literal('a')];
expect(ZodMgt.isValidZodLiteralUnion(literals)).toBe(false);
});
});

describe('constructZodLiteralUnionType', () => {
it('should return a ZodUnion schema of the literals passed', () => {
const literals = [z.literal('a'), z.literal('b')];
const union = ZodMgt.constructZodLiteralUnionType(literals);

expect(JSON.stringify(union)).toEqual(
JSON.stringify(z.union([z.literal('a'), z.literal('b')])),
);
});

it('should throw an error if the literals array has less than 2 elements', () => {
const literals = [z.literal('a')];
expect(() => ZodMgt.constructZodLiteralUnionType(literals)).toThrowError(
'Literals passed do not meet the criteria for constructing a union schema, the minimum length is 2',
);
});
});
});
17 changes: 17 additions & 0 deletions packages/shared/src/utils/zodMgt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { z } from 'zod';

export namespace ZodMgt {
export const isValidZodLiteralUnion = <T extends z.ZodLiteral<unknown>>(
literals: T[],
): literals is [T, T, ...T[]] => literals.length >= 2;

export const constructZodLiteralUnionType = <T extends z.ZodLiteral<unknown>>(literals: T[]) => {
if (!isValidZodLiteralUnion(literals)) {
throw new Error(
'Literals passed do not meet the criteria for constructing a union schema, the minimum length is 2',
);
}

return z.union(literals);
};
}

0 comments on commit 5c24218

Please sign in to comment.