diff options
Diffstat (limited to 'extra/release')
-rw-r--r-- | extra/release/beta.mjs | 65 | ||||
-rw-r--r-- | extra/release/final.mjs | 57 | ||||
-rw-r--r-- | extra/release/lib.mjs | 191 | ||||
-rw-r--r-- | extra/release/nightly.mjs | 16 |
4 files changed, 329 insertions, 0 deletions
diff --git a/extra/release/beta.mjs b/extra/release/beta.mjs new file mode 100644 index 0000000..a2c9809 --- /dev/null +++ b/extra/release/beta.mjs @@ -0,0 +1,65 @@ +import "dotenv/config"; +import { + ver, + buildDist, + buildImage, + checkDocker, + checkTagExists, + checkVersionFormat, + dryRun, + getRepoName, + pressAnyKey, + execSync, uploadArtifacts, +} from "./lib.mjs"; +import semver from "semver"; + +const repoName = getRepoName(); +const version = process.env.RELEASE_BETA_VERSION; +const githubToken = process.env.RELEASE_GITHUB_TOKEN; + +console.log("RELEASE_BETA_VERSION:", version); + +if (!githubToken) { + console.error("GITHUB_TOKEN is required"); + process.exit(1); +} + +// Check if the version is a valid semver +checkVersionFormat(version); + +// Check if the semver identifier is "beta" +const semverIdentifier = semver.prerelease(version); +console.log("Semver identifier:", semverIdentifier); +if (semverIdentifier[0] !== "beta") { + console.error("VERSION should have a semver identifier of 'beta'"); + process.exit(1); +} + +// Check if docker is running +checkDocker(); + +// Check if the tag exists +await checkTagExists(repoName, version); + +// node extra/beta/update-version.js +execSync("node ./extra/beta/update-version.js"); + +// Build frontend dist +buildDist(); + +// Build slim image (rootless) +buildImage(repoName, [ "beta-slim-rootless", ver(version, "slim-rootless") ], "rootless", "BASE_IMAGE=louislam/uptime-kuma:base2-slim"); + +// Build full image (rootless) +buildImage(repoName, [ "beta-rootless", ver(version, "rootless") ], "rootless"); + +// Build slim image +buildImage(repoName, [ "beta-slim", ver(version, "slim") ], "release", "BASE_IMAGE=louislam/uptime-kuma:base2-slim"); + +// Build full image +buildImage(repoName, [ "beta", version ], "release"); + +await pressAnyKey(); + +// npm run upload-artifacts +uploadArtifacts(); diff --git a/extra/release/final.mjs b/extra/release/final.mjs new file mode 100644 index 0000000..d190ac0 --- /dev/null +++ b/extra/release/final.mjs @@ -0,0 +1,57 @@ +import "dotenv/config"; +import { + ver, + buildDist, + buildImage, + checkDocker, + checkTagExists, + checkVersionFormat, + getRepoName, + pressAnyKey, execSync, uploadArtifacts +} from "./lib.mjs"; + +const repoName = getRepoName(); +const version = process.env.RELEASE_VERSION; +const githubToken = process.env.RELEASE_GITHUB_TOKEN; + +console.log("RELEASE_VERSION:", version); + +if (!githubToken) { + console.error("GITHUB_TOKEN is required"); + process.exit(1); +} + +// Check if the version is a valid semver +checkVersionFormat(version); + +// Check if docker is running +checkDocker(); + +// Check if the tag exists +await checkTagExists(repoName, version); + +// node extra/beta/update-version.js +execSync("node extra/update-version.js"); + +// Build frontend dist +buildDist(); + +// Build slim image (rootless) +buildImage(repoName, [ "2-slim-rootless", ver(version, "slim-rootless") ], "rootless", "BASE_IMAGE=louislam/uptime-kuma:base2-slim"); + +// Build full image (rootless) +buildImage(repoName, [ "2-rootless", ver(version, "rootless") ], "rootless"); + +// Build slim image +buildImage(repoName, [ "next-slim", "2-slim", ver(version, "slim") ], "release", "BASE_IMAGE=louislam/uptime-kuma:base2-slim"); + +// Build full image +buildImage(repoName, [ "next", "2", version ], "release"); + +await pressAnyKey(); + +// npm run upload-artifacts +uploadArtifacts(); + +// node extra/update-wiki-version.js +execSync("node extra/update-wiki-version.js"); diff --git a/extra/release/lib.mjs b/extra/release/lib.mjs new file mode 100644 index 0000000..d6c294c --- /dev/null +++ b/extra/release/lib.mjs @@ -0,0 +1,191 @@ +import "dotenv/config"; +import * as childProcess from "child_process"; +import semver from "semver"; + +export const dryRun = process.env.RELEASE_DRY_RUN === "1"; + +if (dryRun) { + console.info("Dry run enabled."); +} + +/** + * Check if docker is running + * @returns {void} + */ +export function checkDocker() { + try { + childProcess.execSync("docker ps"); + } catch (error) { + console.error("Docker is not running. Please start docker and try again."); + process.exit(1); + } +} + +/** + * Get Docker Hub repository name + */ +export function getRepoName() { + return process.env.RELEASE_REPO_NAME || "louislam/uptime-kuma"; +} + +/** + * Build frontend dist + * @returns {void} + */ +export function buildDist() { + if (!dryRun) { + childProcess.execSync("npm run build", { stdio: "inherit" }); + } else { + console.info("[DRY RUN] npm run build"); + } +} + +/** + * Build docker image and push to Docker Hub + * @param {string} repoName Docker Hub repository name + * @param {string[]} tags Docker image tags + * @param {string} target Dockerfile's target name + * @param {string} buildArgs Docker build args + * @param {string} dockerfile Path to Dockerfile + * @param {string} platform Build platform + * @returns {void} + */ +export function buildImage(repoName, tags, target, buildArgs = "", dockerfile = "docker/dockerfile", platform = "linux/amd64,linux/arm64,linux/arm/v7") { + let args = [ + "buildx", + "build", + "-f", + dockerfile, + "--platform", + platform, + ]; + + // Add tags + for (let tag of tags) { + args.push("-t", `${repoName}:${tag}`); + } + + args = [ + ...args, + "--target", + target, + ]; + + // Add build args + if (buildArgs) { + args.push("--build-arg", buildArgs); + } + + args = [ + ...args, + ".", + "--push", + ]; + + if (!dryRun) { + childProcess.spawnSync("docker", args, { stdio: "inherit" }); + } else { + console.log(`[DRY RUN] docker ${args.join(" ")}`); + } +} + +/** + * Check if the version already exists on Docker Hub + * TODO: use semver to compare versions if it is greater than the previous? + * @param {string} repoName Docker Hub repository name + * @param {string} version Version to check + * @returns {void} + */ +export async function checkTagExists(repoName, version) { + console.log(`Checking if version ${version} exists on Docker Hub`); + + // Get a list of tags from the Docker Hub repository + let tags = []; + + // It is mainly to check my careless mistake that I forgot to update the release version in .env, so `page_size` is set to 100 is enough, I think. + const response = await fetch(`https://hub.docker.com/v2/repositories/${repoName}/tags/?page_size=100`); + if (response.ok) { + const data = await response.json(); + tags = data.results.map((tag) => tag.name); + } else { + console.error("Failed to get tags from Docker Hub"); + process.exit(1); + } + + // Check if the version already exists + if (tags.includes(version)) { + console.error(`Version ${version} already exists`); + process.exit(1); + } +} + +/** + * Check the version format + * @param {string} version Version to check + * @returns {void} + */ +export function checkVersionFormat(version) { + if (!version) { + console.error("VERSION is required"); + process.exit(1); + } + + // Check the version format, it should be a semver and must be like this: "2.0.0-beta.0" + if (!semver.valid(version)) { + console.error("VERSION is not a valid semver version"); + process.exit(1); + } +} + +/** + * Press any key to continue + * @returns {Promise<void>} + */ +export function pressAnyKey() { + console.log("Git Push and Publish the release note on github, then press any key to continue"); + process.stdin.setRawMode(true); + process.stdin.resume(); + return new Promise(resolve => process.stdin.once("data", data => { + process.stdin.setRawMode(false); + process.stdin.pause(); + resolve(); + })); +} + +/** + * Append version identifier + * @param {string} version Version + * @param {string} identifier Identifier + * @returns {string} Version with identifier + */ +export function ver(version, identifier) { + const obj = semver.parse(version); + + if (obj.prerelease.length === 0) { + obj.prerelease = [ identifier ]; + } else { + obj.prerelease[0] = [ obj.prerelease[0], identifier ].join("-"); + } + return obj.format(); +} + +/** + * Upload artifacts to GitHub + * @returns {void} + */ +export function uploadArtifacts() { + execSync("npm run upload-artifacts"); +} + +/** + * Execute a command + * @param {string} cmd Command to execute + * @returns {void} + */ +export function execSync(cmd) { + if (!dryRun) { + childProcess.execSync(cmd, { stdio: "inherit" }); + } else { + console.info(`[DRY RUN] ${cmd}`); + } +} diff --git a/extra/release/nightly.mjs b/extra/release/nightly.mjs new file mode 100644 index 0000000..c6641ba --- /dev/null +++ b/extra/release/nightly.mjs @@ -0,0 +1,16 @@ +import { buildDist, buildImage, checkDocker, getRepoName } from "./lib.mjs"; + +// Docker Hub repository name +const repoName = getRepoName(); + +// Check if docker is running +checkDocker(); + +// Build frontend dist (it will build on the host machine, TODO: build on a container?) +buildDist(); + +// Build full image (rootless) +buildImage(repoName, [ "nightly2-rootless" ], "nightly-rootless"); + +// Build full image +buildImage(repoName, [ "nightly2" ], "nightly"); |