Improve the build process (#5267)
This commit is contained in:
parent
e99fbf1ab7
commit
fed6e4bdb3
|
@ -1,19 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
const childProcess = require("child_process");
|
|
||||||
let env = process.env;
|
|
||||||
|
|
||||||
let cmd = process.argv[2];
|
|
||||||
let args = process.argv.slice(3);
|
|
||||||
let replacedArgs = [];
|
|
||||||
|
|
||||||
for (let arg of args) {
|
|
||||||
for (let key in env) {
|
|
||||||
arg = arg.replaceAll(`$${key}`, env[key]);
|
|
||||||
}
|
|
||||||
replacedArgs.push(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
let child = childProcess.spawn(cmd, replacedArgs);
|
|
||||||
child.stdout.pipe(process.stdout);
|
|
||||||
child.stderr.pipe(process.stderr);
|
|
|
@ -15,7 +15,6 @@ if (newVersion) {
|
||||||
// Process package.json
|
// Process package.json
|
||||||
pkg.version = newVersion;
|
pkg.version = newVersion;
|
||||||
pkg.scripts.setup = pkg.scripts.setup.replaceAll(oldVersion, newVersion);
|
pkg.scripts.setup = pkg.scripts.setup.replaceAll(oldVersion, newVersion);
|
||||||
pkg.scripts["build-docker"] = pkg.scripts["build-docker"].replaceAll(oldVersion, newVersion);
|
|
||||||
fs.writeFileSync("package.json", JSON.stringify(pkg, null, 4) + "\n");
|
fs.writeFileSync("package.json", JSON.stringify(pkg, null, 4) + "\n");
|
||||||
|
|
||||||
// Process README.md
|
// Process README.md
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
console.log("Git Push and Publish the release note on github, then press any key to continue");
|
|
||||||
|
|
||||||
process.stdin.setRawMode(true);
|
|
||||||
process.stdin.resume();
|
|
||||||
process.stdin.on("data", process.exit.bind(process, 0));
|
|
||||||
|
|
|
@ -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();
|
|
@ -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");
|
|
@ -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}`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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");
|
|
@ -1,9 +0,0 @@
|
||||||
// Check if docker is running
|
|
||||||
const { exec } = require("child_process");
|
|
||||||
|
|
||||||
exec("docker ps", (err, stdout, stderr) => {
|
|
||||||
if (err) {
|
|
||||||
console.error("Docker is not running. Please start docker and try again.");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
});
|
|
14
package.json
14
package.json
|
@ -34,20 +34,13 @@
|
||||||
"playwright-show-report": "playwright show-report ./private/playwright-report",
|
"playwright-show-report": "playwright show-report ./private/playwright-report",
|
||||||
"tsc": "tsc",
|
"tsc": "tsc",
|
||||||
"vite-preview-dist": "vite preview --host --config ./config/vite.config.js",
|
"vite-preview-dist": "vite preview --host --config ./config/vite.config.js",
|
||||||
"build-docker": "npm run build && npm run build-docker-full && npm run build-docker-slim",
|
|
||||||
"build-docker-base": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base2 --target base2 . --push",
|
"build-docker-base": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base2 --target base2 . --push",
|
||||||
"build-docker-base-slim": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base2-slim --target base2-slim . --push",
|
"build-docker-base-slim": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base2-slim --target base2-slim . --push",
|
||||||
"build-docker-builder-go": "docker buildx build -f docker/builder-go.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:builder-go . --push",
|
"build-docker-builder-go": "docker buildx build -f docker/builder-go.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:builder-go . --push",
|
||||||
"build-docker-slim": "node ./extra/env2arg.js docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:next-slim -t louislam/uptime-kuma:2-slim -t louislam/uptime-kuma:$VERSION-slim --target release --build-arg BASE_IMAGE=louislam/uptime-kuma:base2-slim . --push",
|
|
||||||
"build-docker-full": "node ./extra/env2arg.js docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:next -t louislam/uptime-kuma:2 -t louislam/uptime-kuma:$VERSION --target release . --push",
|
|
||||||
"build-docker-nightly": "node ./extra/test-docker.js && npm run build && docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly2 --target nightly . --push",
|
|
||||||
"build-docker-slim-rootless": "node ./extra/env2arg.js docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:2-slim-rootless -t louislam/uptime-kuma:$VERSION-slim-rootless --target rootless --build-arg BASE_IMAGE=louislam/uptime-kuma:base2-slim . --push",
|
|
||||||
"build-docker-full-rootless": "node ./extra/env2arg.js docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:2-rootless -t louislam/uptime-kuma:$VERSION-rootless --target rootless . --push",
|
|
||||||
"build-docker-nightly-rootless": "node ./extra/test-docker.js && npm run build && docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly2-rootless --target nightly-rootless . --push",
|
|
||||||
"build-docker-nightly-local": "npm run build && docker build -f docker/dockerfile -t louislam/uptime-kuma:nightly2 --target nightly .",
|
"build-docker-nightly-local": "npm run build && docker build -f docker/dockerfile -t louislam/uptime-kuma:nightly2 --target nightly .",
|
||||||
"build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test2 --target pr-test2 . --push",
|
"build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test2 --target pr-test2 . --push",
|
||||||
"upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain",
|
"upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain",
|
||||||
"setup": "git checkout 1.23.14 && npm ci --production && npm run download-dist",
|
"setup": "git checkout 1.23.15 && npm ci --production && npm run download-dist",
|
||||||
"download-dist": "node extra/download-dist.js",
|
"download-dist": "node extra/download-dist.js",
|
||||||
"mark-as-nightly": "node extra/mark-as-nightly.js",
|
"mark-as-nightly": "node extra/mark-as-nightly.js",
|
||||||
"reset-password": "node extra/reset-password.js",
|
"reset-password": "node extra/reset-password.js",
|
||||||
|
@ -58,8 +51,9 @@
|
||||||
"simple-postgres": "docker run --rm -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres",
|
"simple-postgres": "docker run --rm -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres",
|
||||||
"simple-mariadb": "docker run --rm -p 3306:3306 -e MYSQL_ROOT_PASSWORD=mariadb# mariadb",
|
"simple-mariadb": "docker run --rm -p 3306:3306 -e MYSQL_ROOT_PASSWORD=mariadb# mariadb",
|
||||||
"update-language-files": "cd extra/update-language-files && node index.js && cross-env-shell eslint ../../src/languages/$npm_config_language.js --fix",
|
"update-language-files": "cd extra/update-language-files && node index.js && cross-env-shell eslint ../../src/languages/$npm_config_language.js --fix",
|
||||||
"release-final": "node ./extra/test-docker.js && node extra/update-version.js && npm run build-docker && node ./extra/press-any-key.js && npm run upload-artifacts && node ./extra/update-wiki-version.js",
|
"release-final": "node ./extra/release/final.mjs",
|
||||||
"release-beta": "node ./extra/test-docker.js && node extra/beta/update-version.js && npm run build && node ./extra/env2arg.js docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:$VERSION -t louislam/uptime-kuma:beta . --target release --push && node ./extra/press-any-key.js && npm run upload-artifacts",
|
"release-beta": "node ./extra/release/beta.mjs",
|
||||||
|
"release-nightly": "node ./extra/release/nightly.mjs",
|
||||||
"git-remove-tag": "git tag -d",
|
"git-remove-tag": "git tag -d",
|
||||||
"build-dist-and-restart": "npm run build && npm run start-server-dev",
|
"build-dist-and-restart": "npm run build && npm run start-server-dev",
|
||||||
"start-pr-test": "node extra/checkout-pr.js && npm install && npm run dev",
|
"start-pr-test": "node extra/checkout-pr.js && npm install && npm run dev",
|
||||||
|
|
Loading…
Reference in New Issue