Skip to content

Commit

Permalink
Add stable releases
Browse files Browse the repository at this point in the history
  • Loading branch information
kjarosh committed Jul 29, 2024
1 parent 3a16030 commit 409380c
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 40 deletions.
18 changes: 12 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@
"react": "^18",
"react-device-detect": "^2.2.3",
"react-dom": "^18",
"recharts": "^2.12.7"
"recharts": "^2.12.7",
"semver": "^7.6.3"
},
"devDependencies": {
"@types/d3": "^7.4.3",
"@types/jsdom": "^21.1.7",
"@types/node": "^22",
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/semver": "^7.5.8",
"@typescript-eslint/eslint-plugin": "^7.17.0",
"@typescript-eslint/parser": "^7.17.0",
"d3": "^7.9.0",
Expand Down
11 changes: 8 additions & 3 deletions src/app/downloads/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,29 @@ import {
IconBrandFirefox,
IconBrandJavascript,
IconBrandSafari,
IconBrandWindows
IconBrandWindows,
} from "@tabler/icons-react";
import React from "react";
import { IconBrandLinux } from "@/components/icons";
import { SummaryStatistics } from "@/app/compatibility/avm2/report_utils";

export const repository = { owner: "ruffle-rs", repo: "ruffle" };

export const maxMinor = 3;
export const maxMajor = 2;
export const maxNightlies = 5;

export const githubReleasesUrl = `https://github.com/${repository.owner}/${repository.repo}/releases`;
export const githubStableReleasesUrl = `${githubReleasesUrl}?q=prerelease:false`;
export const githubNightlyReleasesUrl = `${githubReleasesUrl}?q=prerelease:true`;

export interface GithubRelease {
id: number;
name: string;
prerelease: boolean;
downloads: ReleaseDownloads;
url: string;
tag: string;
avm2_report_asset_id?: number;
}

Expand Down Expand Up @@ -201,6 +206,6 @@ export const allLinks: DownloadLink[] = [
].flat();

export interface AVM2Report {
summary: SummaryStatistics,
classes: object,
summary: SummaryStatistics;
classes: object;
}
62 changes: 56 additions & 6 deletions src/app/downloads/github.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,25 @@ import {
DownloadKey,
FilenamePatterns,
GithubRelease,
maxMajor,
maxMinor,
maxNightlies,
ReleaseDownloads,
repository,
} from "@/app/downloads/config";
import { Octokit } from "octokit";
import { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods";
import { parse } from "node-html-parser";
import semver from "semver/preload";

function createGithubAuth() {
if (process.env.GITHUB_TOKEN) {
return createTokenAuth(process.env.GITHUB_TOKEN);
} else {
return createUnauthenticatedAuth({reason: "Please provide a GitHub Personal Access Token via the GITHUB_TOKEN environment variable."});
return createUnauthenticatedAuth({
reason:
"Please provide a GitHub Personal Access Token via the GITHUB_TOKEN environment variable.",
});
}
}

Expand All @@ -39,7 +45,7 @@ export async function getLatestReleases(): Promise<GithubRelease[]> {
const downloads: ReleaseDownloads = {};
for (const asset of release.assets) {
if (asset.name === "avm2_report.json") {
avm2_report_asset_id = asset.id;
avm2_report_asset_id = asset.id;
}
for (const [key, pattern] of Object.entries(FilenamePatterns)) {
if (asset.name.indexOf(pattern) > -1) {
Expand All @@ -53,6 +59,7 @@ export async function getLatestReleases(): Promise<GithubRelease[]> {
name: release.name || release.tag_name,
prerelease: release.prerelease,
url: release.html_url,
tag: release.tag_name,
downloads,
avm2_report_asset_id,
});
Expand All @@ -64,6 +71,43 @@ export async function getLatestReleases(): Promise<GithubRelease[]> {
}
}

export async function getLatestRelease(): Promise<GithubRelease> {
const releases = await getLatestReleases();
const stableReleases = releases.filter((release) => !release.prerelease);
return stableReleases.length > 0 ? stableReleases[0] : releases[0];
}

export function filterLatestStableReleases(
releases: GithubRelease[],
): GithubRelease[] {
let newestMajor = null;
const currentMajorReleases = new Map();
const olderMajors = new Map();
for (const release of releases) {
if (release.prerelease) {
continue;
}
const version = release.tag.replace(/^v/, "");
const major = semver.major(version);
const majorMinor = `${major}.${semver.minor(version)}`;
if (!newestMajor) {
newestMajor = major;
}
if (major === newestMajor) {
if (!currentMajorReleases.has(majorMinor)) {
currentMajorReleases.set(majorMinor, release);
}
} else {
if (!olderMajors.has(major)) {
olderMajors.set(major, release);
}
}
}
return Array.from(currentMajorReleases.values())
.slice(0, maxMinor)
.concat(Array.from(olderMajors.values()).slice(0, maxMajor - 1));
}

export async function getWeeklyContributions(): Promise<
RestEndpointMethodTypes["repos"]["getCommitActivityStats"]["response"]
> {
Expand All @@ -72,7 +116,9 @@ export async function getWeeklyContributions(): Promise<
}
export async function fetchReport(): Promise<AVM2Report | undefined> {
const releases = await getLatestReleases();
const latest = releases.find(release => release.avm2_report_asset_id !== undefined);
const latest = releases.find(
(release) => release.avm2_report_asset_id !== undefined,
);
if (!latest?.avm2_report_asset_id) {
throwBuildError();
return;
Expand Down Expand Up @@ -114,9 +160,13 @@ export async function getAVM1Progress(): Promise<number> {
continue;
}
const topLevelRoot = parse(topLevelContent);
totalItems += topLevelRoot.querySelectorAll("input.task-list-item-checkbox").length;
completedItems += topLevelRoot.querySelectorAll("input.task-list-item-checkbox:checked").length;
totalItems += topLevelRoot.querySelectorAll(
"input.task-list-item-checkbox",
).length;
completedItems += topLevelRoot.querySelectorAll(
"input.task-list-item-checkbox:checked",
).length;
}
if (totalItems < 3348) throwBuildError();
return Math.round(completedItems/totalItems*100);
return Math.round((completedItems / totalItems) * 100);
}
16 changes: 11 additions & 5 deletions src/app/downloads/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ import {
githubReleasesUrl,
maxNightlies,
} from "@/app/downloads/config";
import { getLatestReleases } from "@/app/downloads/github";
import {
filterLatestStableReleases,
getLatestReleases,
} from "@/app/downloads/github";

function WebDownload({ latest }: { latest: GithubRelease | null }) {
return (
Expand Down Expand Up @@ -95,18 +98,21 @@ function DesktopDownload({ latest }: { latest: GithubRelease | null }) {

export default async function Page() {
const releases = await getLatestReleases();
const latest = releases.length > 0 ? releases[0] : null;
const stableReleases = filterLatestStableReleases(releases);
const nightlies = releases
.filter((release) => release.prerelease)
.slice(0, maxNightlies);
const latestStable =
stableReleases.length > 0 ? stableReleases[0] : releases[0];
return (
<Container size="xl" className={classes.container}>
<Stack gap="xl">
<ExtensionList />
<WebDownload latest={latest} />
<DesktopDownload latest={latest} />
<WebDownload latest={latestStable} />
<DesktopDownload latest={latestStable} />

<ReleaseList releases={nightlies} />
<ReleaseList releases={stableReleases} nightly={false} />
<ReleaseList releases={nightlies} nightly={true} />
</Stack>
</Container>
);
Expand Down
66 changes: 51 additions & 15 deletions src/app/downloads/releases.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ import {
desktopLinks,
type DownloadLink,
extensionLinks,
githubNightlyReleasesUrl,
type GithubRelease,
githubReleasesUrl,
githubStableReleasesUrl,
webLinks,
} from "@/app/downloads/config";

Expand Down Expand Up @@ -52,8 +53,8 @@ function DownloadLink({
}

function ReleaseRow(release: GithubRelease) {
// The nightly prefix is a bit superfluous here
const name = release.name.replace(/^Nightly /, "");
// The prefix is a bit superfluous here
const name = release.name.replace(/^Nightly /, "").replace(/^Release /, "");
return (
<TableTr>
<TableTd>
Expand Down Expand Up @@ -114,20 +115,55 @@ function ReleaseCompactBox(release: GithubRelease) {
);
}

export function ReleaseList({ releases }: { releases: GithubRelease[] }) {
function ReleaseIntro({ nightly }: { nightly: boolean }) {
if (!nightly) {
return (
<>
<Title id="releases">Stable Releases</Title>
<Text>
If none of the above are suitable for you, you can manually download
one of the latest stable releases. Older versions are available on{" "}
<Link href={githubStableReleasesUrl} target="_blank">
GitHub
</Link>
.
</Text>
</>
);
} else {
return (
<>
<Title id="nightly-releases">Nightly Releases</Title>
<Text>
If you want to try out the latest updates and cutting-edge features,
you can download the latest nightly release. These are automatically
built every day (approximately midnight UTC, unless there are no
changes on that day) and they offer early access to new enhancements,
bug fixes, and improvements before they're officially rolled out.
Older nightly releases are available on{" "}
<Link href={githubNightlyReleasesUrl} target="_blank">
GitHub
</Link>
.
</Text>
</>
);
}
}

export function ReleaseList({
releases,
nightly,
}: {
releases: GithubRelease[];
nightly: boolean;
}) {
if (releases.length == 0) {
return <></>;
}
return (
<Stack>
<Title id="nightly-releases">Nightly Releases</Title>
<Text>
If none of the above are suitable for you, you can manually download the
latest Nightly release. These are automatically built every day
(approximately midnight UTC), unless there are no changes on that day.{" "}
Older nightly releases are available on{" "}
<Link href={githubReleasesUrl} target="_blank">
GitHub
</Link>
.
</Text>
<ReleaseIntro nightly={nightly} />
<Table
horizontalSpacing="md"
verticalSpacing="md"
Expand Down
6 changes: 2 additions & 4 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
import Image from "next/image";
import { IconCheck } from "@tabler/icons-react";
import React from "react";
import { getLatestReleases } from "@/app/downloads/github";
import { getLatestRelease } from "@/app/downloads/github";

const InteractiveLogo = dynamic(() => import("../components/logo"), {
ssr: false,
Expand All @@ -23,9 +23,7 @@ const Installers = dynamic(() => import("./installers"), {
});

export default async function Home() {
const releases = await getLatestReleases();
const latest = releases.length > 0 ? releases[0] : null;

const latest = await getLatestRelease();
return (
<Container size="xl" className={classes.container}>
<InteractiveLogo className={classes.logo} />
Expand Down

0 comments on commit 409380c

Please sign in to comment.