aboutsummaryrefslogtreecommitdiff
path: root/packages/bun-release/src/github.ts
diff options
context:
space:
mode:
authorGravatar Ashcon Partovi <ashcon@partovi.net> 2023-02-01 10:27:04 -0800
committerGravatar Ashcon Partovi <ashcon@partovi.net> 2023-02-01 10:28:01 -0800
commit73d6c888b9d4a9ac94452253d924f7ccea069429 (patch)
treeda5c67a32b9547a057b50dabd0e155e35dcfa03d /packages/bun-release/src/github.ts
parent661fca9cde8bd82fa848944e0c1e0d6f6f12d403 (diff)
downloadbun-73d6c888b9d4a9ac94452253d924f7ccea069429.tar.gz
bun-73d6c888b9d4a9ac94452253d924f7ccea069429.tar.zst
bun-73d6c888b9d4a9ac94452253d924f7ccea069429.zip
Add bun-release package
Diffstat (limited to 'packages/bun-release/src/github.ts')
-rw-r--r--packages/bun-release/src/github.ts113
1 files changed, 113 insertions, 0 deletions
diff --git a/packages/bun-release/src/github.ts b/packages/bun-release/src/github.ts
new file mode 100644
index 000000000..fbbbbf267
--- /dev/null
+++ b/packages/bun-release/src/github.ts
@@ -0,0 +1,113 @@
+import type { Endpoints, RequestParameters, Route } from "@octokit/types";
+import { Octokit } from "octokit";
+import { fetch } from "./fetch";
+import { debug, log, warn, error } from "./console";
+
+const [owner, repo] = process.env["GITHUB_REPOSITORY"]?.split("/") ?? [
+ "oven-sh",
+ "bun",
+];
+
+const octokit = new Octokit({
+ auth: process.env["GITHUB_TOKEN"],
+ request: {
+ fetch,
+ },
+ log: {
+ debug,
+ info: log,
+ warn,
+ error,
+ },
+});
+
+export async function github<R extends Route>(
+ url: R | keyof Endpoints,
+ options?: Omit<
+ R extends keyof Endpoints
+ ? Endpoints[R]["parameters"] & RequestParameters
+ : RequestParameters,
+ "owner" | "repo"
+ >,
+): Promise<
+ R extends keyof Endpoints ? Endpoints[R]["response"]["data"] : unknown
+> {
+ // @ts-ignore
+ const { data } = await octokit.request(url, {
+ owner,
+ repo,
+ ...options,
+ });
+ return data;
+}
+
+export async function getRelease(tag?: string) {
+ if (!tag) {
+ return github("GET /repos/{owner}/{repo}/releases/latest");
+ }
+ return github("GET /repos/{owner}/{repo}/releases/tags/{tag}", {
+ tag: formatTag(tag),
+ });
+}
+
+export async function uploadAsset(tag: string, name: string, blob: Blob) {
+ const release = await getRelease(tag);
+ const asset = release.assets.find((asset) => asset.name === name);
+ // Github requires that existing assets are deleted before uploading
+ // a new asset, but does not provide a rename or re-upload API?!?
+ if (asset) {
+ await github("DELETE /repos/{owner}/{repo}/releases/assets/{asset_id}", {
+ asset_id: asset.id,
+ });
+ }
+ return github(
+ "POST {origin}/repos/{owner}/{repo}/releases/{release_id}/assets{?name,label}",
+ {
+ baseUrl: "https://uploads.github.com",
+ release_id: release.id,
+ name,
+ headers: {
+ "content-type": blob.type,
+ "content-length": blob.size,
+ },
+ data: Buffer.from(await blob.arrayBuffer()),
+ },
+ );
+}
+
+export async function downloadAsset(tag: string, name: string): Promise<Blob> {
+ const release = await getRelease(tag);
+ const asset = release.assets.find((asset) => asset.name === name);
+ if (!asset) {
+ throw new Error(`Asset not found: ${name}`);
+ }
+ const response = await fetch(asset.browser_download_url);
+ return response.blob();
+}
+
+export async function getSha(tag: string, format?: "short" | "long") {
+ const { sha } = await github("GET /repos/{owner}/{repo}/git/tags/{tag_sha}", {
+ tag_sha: formatTag(tag),
+ });
+ return format === "short" ? sha.substring(0, 7) : sha;
+}
+
+export async function getSemver(tag?: string, build?: number): Promise<string> {
+ const { tag_name } = await getRelease(tag);
+ if (tag_name !== "canary") {
+ return tag_name.replace("bun-v", "");
+ }
+ const sha = await getSha(tag_name, "short");
+ const date = new Date().toISOString().split("T")[0].replace(/-/g, "");
+ return `${Bun.version}-canary.${date}.${build ?? 1}+${sha}`;
+}
+
+export function formatTag(tag: string): string {
+ if (tag === "canary" || tag.startsWith("bun-v")) {
+ return tag;
+ }
+ if (tag.startsWith("v")) {
+ return tag.slice(1);
+ }
+ return `bun-v${tag}`;
+}