1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-30 02:59:11 -05:00
denoland-deno/tools/release/promote_to_release.ts

257 lines
6.5 KiB
TypeScript
Raw Normal View History

#!/usr/bin/env -S deno run -A --lock=tools/deno.lock.json
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// deno-lint-ignore-file no-console
import { $ } from "jsr:@david/dax@0.41.0";
import { gray } from "jsr:@std/fmt@1/colors";
import { patchver } from "jsr:@deno/patchver@0.2.0";
const SUPPORTED_TARGETS = [
"aarch64-apple-darwin",
"aarch64-unknown-linux-gnu",
"x86_64-apple-darwin",
"x86_64-pc-windows-msvc",
"x86_64-unknown-linux-gnu",
];
const DENO_BINARIES = [
"deno",
"denort",
];
const CHANNEL = Deno.args[0];
if (CHANNEL !== "rc" && CHANNEL !== "lts") {
throw new Error(`Invalid channel: ${CHANNEL}`);
}
const CANARY_URL = "https://dl.deno.land";
function getCanaryBinaryUrl(
version: string,
binary: string,
target: string,
): string {
return `${CANARY_URL}/canary/${version}/${binary}-${target}.zip`;
}
function getUnzippedFilename(binary: string, target: string) {
if (target.includes("windows")) {
return `${binary}.exe`;
} else {
return binary;
}
}
function getBinaryName(binary: string, target: string): string {
let ext = "";
if (target.includes("windows")) {
ext = ".exe";
}
return `${binary}-${target}-${CHANNEL}${ext}`;
}
function getArchiveName(binary: string, target: string): string {
return `${binary}-${target}.zip`;
}
interface CanaryVersion {
target: string;
version: string;
}
async function remove(filePath: string) {
try {
await Deno.remove(filePath);
} catch {
// pass
}
}
async function fetchLatestCanaryBinary(
version: string,
binary: string,
target: string,
) {
const url = getCanaryBinaryUrl(version, binary, target);
await $.request(url).showProgress().pipeToPath();
}
async function fetchLatestCanaryBinaries(canaryVersion: string) {
for (const binary of DENO_BINARIES) {
for (const target of SUPPORTED_TARGETS) {
$.logStep("Download", binary, gray("target:"), target);
await fetchLatestCanaryBinary(canaryVersion, binary, target);
}
}
}
async function unzipArchive(archiveName: string, unzippedName: string) {
await remove(unzippedName);
const output = await $`unzip ./${archiveName}`;
if (output.code !== 0) {
$.logError(`Failed to unzip ${archiveName} (error code ${output.code})`);
Deno.exit(1);
}
}
async function createArchive(binaryName: string, archiveName: string) {
const output = await $`zip -r ./${archiveName} ./${binaryName}`;
if (output.code !== 0) {
$.logError(
`Failed to create archive ${archiveName} (error code ${output.code})`,
);
Deno.exit(1);
}
}
async function runPatchver(
binary: string,
target: string,
binaryName: string,
) {
const input = await Deno.readFile(binary);
const output = patchver(input, CHANNEL);
try {
await Deno.writeFile(binaryName, output);
} catch (e) {
$.logError(
`Failed to promote to RC ${binary} (${target}), error:`,
e,
);
Deno.exit(1);
}
}
async function runRcodesign(
target: string,
binaryName: string,
commitHash: string,
) {
if (!target.includes("apple") || binaryName.includes("denort")) {
return;
}
$.logStep(`Codesign ${binaryName}`);
const tempFile = $.path("temp.p12");
let output;
try {
await $`echo $APPLE_CODESIGN_KEY | base64 -d`.stdout(tempFile);
output =
await $`rcodesign sign ./${binaryName} --binary-identifier=deno-${commitHash} --code-signature-flags=runtime --code-signature-flags=runtime --p12-password="$APPLE_CODESIGN_PASSWORD" --p12-file=${tempFile} --entitlements-xml-file=cli/entitlements.plist`;
} finally {
try {
tempFile.removeSync();
} catch {
// pass
}
}
if (output.code !== 0) {
$.logError(
`Failed to codesign ${binaryName} (error code ${output.code})`,
);
Deno.exit(1);
}
await $`codesign -dv --verbose=4 ./deno`;
}
async function promoteBinaryToRc(
binary: string,
target: string,
commitHash: string,
) {
const unzippedName = getUnzippedFilename(binary, target);
const binaryName = getBinaryName(binary, target);
const archiveName = getArchiveName(binary, target);
await remove(unzippedName);
await remove(binaryName);
$.logStep(
"Unzip",
archiveName,
gray("binary"),
binary,
gray("binaryName"),
binaryName,
);
await unzipArchive(archiveName, unzippedName);
await remove(archiveName);
$.logStep(
"Patchver",
unzippedName,
`(${target})`,
gray("output to"),
binaryName,
);
await runPatchver(unzippedName, target, binaryName);
// Remove the unpatched binary and rename patched one.
await remove(unzippedName);
await Deno.rename(binaryName, unzippedName);
await runRcodesign(target, unzippedName, commitHash);
// Set executable permission
if (!target.includes("windows")) {
Deno.chmod(unzippedName, 0o777);
}
await createArchive(unzippedName, archiveName);
await remove(unzippedName);
}
async function promoteBinariesToRc(commitHash: string) {
const totalCanaries = SUPPORTED_TARGETS.length * DENO_BINARIES.length;
for (let targetIdx = 0; targetIdx < SUPPORTED_TARGETS.length; targetIdx++) {
const target = SUPPORTED_TARGETS[targetIdx];
for (let binaryIdx = 0; binaryIdx < DENO_BINARIES.length; binaryIdx++) {
const binaryName = DENO_BINARIES[binaryIdx];
const currentIdx = (targetIdx * 2) + binaryIdx + 1;
$.logLight(
`[${currentIdx}/${totalCanaries}]`,
"Promote",
binaryName,
target,
`to ${CHANNEL}...`,
);
await promoteBinaryToRc(binaryName, target, commitHash);
$.logLight(
`[${currentIdx}/${totalCanaries}]`,
"Promoted",
binaryName,
target,
`to ${CHANNEL}!`,
);
}
}
}
async function dumpRcVersion() {
$.logStep("Compute version");
await unzipArchive(getArchiveName("deno", Deno.build.target), "deno");
const output = await $`./deno -V`.stdout("piped");
const denoVersion = output.stdout.slice(5).split("+")[0];
$.logStep("Computed version", denoVersion);
await Deno.writeTextFile(
`./release-${CHANNEL}-latest.txt`,
`v${denoVersion}`,
);
}
async function main() {
const commitHash = Deno.args[1];
if (!commitHash) {
throw new Error("Commit hash needs to be provided as an argument");
}
$.logStep("Download canary binaries...");
await fetchLatestCanaryBinaries(commitHash);
console.log("All canary binaries ready");
$.logStep(`Promote canary binaries to ${CHANNEL}...`);
await promoteBinariesToRc(commitHash);
// Finally dump the version name to a `release.txt` file for uploading to GCP
await dumpRcVersion();
}
await main();