mirror of
https://github.com/louislam/uptime-kuma.git
synced 2026-08-07 08:15:01 +00:00
chore: Fix full auto release process (#7663)
This commit is contained in:
@@ -84,7 +84,7 @@ jobs:
|
||||
RELEASE_BETA_VERSION: ${{ inputs.version }}
|
||||
RELEASE_PREVIOUS_VERSION: ${{ inputs.previous_version }}
|
||||
DRY_RUN: ${{ inputs.dry_run }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_TOKEN: ${{ secrets.RELEASE_PAT }}
|
||||
GITHUB_RUN_ID: ${{ github.run_id }}
|
||||
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
|
||||
run: npm run release-beta
|
||||
|
||||
@@ -84,7 +84,7 @@ jobs:
|
||||
RELEASE_VERSION: ${{ inputs.version }}
|
||||
RELEASE_PREVIOUS_VERSION: ${{ inputs.previous_version }}
|
||||
DRY_RUN: ${{ inputs.dry_run }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_TOKEN: ${{ secrets.RELEASE_PAT }}
|
||||
GITHUB_RUN_ID: ${{ github.run_id }}
|
||||
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
|
||||
run: npm run release-final
|
||||
|
||||
@@ -27,11 +27,7 @@ const outputFormat = JSON.stringify({
|
||||
others: [192, 21],
|
||||
});
|
||||
|
||||
const prompt = `Input Data:
|
||||
\`\`\`json
|
||||
{{ input }}
|
||||
\`\`\`
|
||||
|
||||
const prompt = `Input Data: {{ input }}
|
||||
LLM Task:
|
||||
- Output a one-line JSON object in the following format:
|
||||
{{ outputFormat }}
|
||||
@@ -190,6 +186,75 @@ export async function generateChangelog(previousVersion, categorizedMap) {
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Changelog using AI
|
||||
* @param {string} previousVersion Previous Version Tag
|
||||
* @returns {Promise<string>} Changelog Content
|
||||
*/
|
||||
export async function generateChangelogAI(previousVersion) {
|
||||
// 1. Generate changelog
|
||||
let categorizedMap = null;
|
||||
|
||||
console.log("Running opencode to categorize PRs...");
|
||||
const llmPrompt = (await getPrompt(previousVersion)).replaceAll("\n", " ");
|
||||
|
||||
console.log(llmPrompt);
|
||||
|
||||
console.log("Running opencode with the above prompt...");
|
||||
|
||||
try {
|
||||
const result = childProcess.spawnSync(
|
||||
"opencode",
|
||||
["run", "-m", "opencode/big-pickle", "--format", "json", llmPrompt],
|
||||
{
|
||||
encoding: "utf-8",
|
||||
timeout: 120000,
|
||||
shell: true,
|
||||
cwd: process.cwd(),
|
||||
env: process.env,
|
||||
}
|
||||
);
|
||||
|
||||
if (result.status === 0 && result.stdout) {
|
||||
// Parse NDJSON output: find "type":"text" line
|
||||
for (const line of result.stdout.trim().split("\n")) {
|
||||
try {
|
||||
const obj = JSON.parse(line);
|
||||
if (obj.type === "text" && obj.part?.text) {
|
||||
const jsonMatch = obj.part.text.match(/\{[\s\S]*\}/);
|
||||
if (jsonMatch) {
|
||||
categorizedMap = JSON.parse(jsonMatch[0]);
|
||||
console.log("LLM categorization applied.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// skip unparseable lines
|
||||
}
|
||||
}
|
||||
|
||||
if (!categorizedMap) {
|
||||
console.warn("No JSON found in opencode response.");
|
||||
console.warn(result.stdout);
|
||||
}
|
||||
} else {
|
||||
console.warn("opencode failed or returned no output (status:", result.status, ")");
|
||||
if (result.stderr) {
|
||||
console.warn("stderr:", result.stderr.slice(0, 500));
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("Failed to run opencode:", e.message);
|
||||
}
|
||||
|
||||
if (!categorizedMap) {
|
||||
categorizedMap = {};
|
||||
console.log("OpenCode unavailable, using uncategorized fallback.");
|
||||
}
|
||||
|
||||
return await generateChangelog(previousVersion, categorizedMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} previousVersion Previous Version Tag
|
||||
* @param {boolean} removeAuthor Whether to strip the author field from the returned PR list
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { createRelease } from "./lib.mjs";
|
||||
import { generateChangelogAI } from "../generate-changelog.mjs";
|
||||
|
||||
const version = process.env.RELEASE_VERSION || process.env.RELEASE_BETA_VERSION;
|
||||
const previousVersion = process.env.RELEASE_PREVIOUS_VERSION;
|
||||
|
||||
if (!version) {
|
||||
console.error("RELEASE_VERSION is required");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!previousVersion) {
|
||||
console.error("RELEASE_PREVIOUS_VERSION is required");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const isBeta = !!process.env.RELEASE_BETA_VERSION;
|
||||
const changelog = await generateChangelogAI(previousVersion);
|
||||
await createRelease(version, changelog, isBeta, "");
|
||||
@@ -1,7 +1,8 @@
|
||||
import "dotenv/config";
|
||||
import * as childProcess from "child_process";
|
||||
import fs from "fs";
|
||||
import { generateChangelog, getPrompt } from "../generate-changelog.mjs";
|
||||
import { generateChangelogAI } from "../generate-changelog.mjs";
|
||||
import { createRelease } from "./lib.mjs";
|
||||
|
||||
const version = process.env.RELEASE_VERSION || process.env.RELEASE_BETA_VERSION;
|
||||
const previousVersion = process.env.RELEASE_PREVIOUS_VERSION;
|
||||
@@ -41,96 +42,12 @@ if (!fs.existsSync(prNumberFile)) {
|
||||
}
|
||||
const prNumber = fs.readFileSync(prNumberFile, "utf-8").trim();
|
||||
|
||||
// 1. Generate changelog
|
||||
let categorizedMap = null;
|
||||
|
||||
console.log("Running opencode to categorize PRs...");
|
||||
const llmPrompt = await getPrompt(previousVersion);
|
||||
try {
|
||||
const result = childProcess.spawnSync(
|
||||
"opencode",
|
||||
["run", "-m", "opencode/big-pickle", "--format", "json", llmPrompt],
|
||||
{
|
||||
encoding: "utf-8",
|
||||
timeout: 120000,
|
||||
cwd: process.cwd(),
|
||||
env: process.env,
|
||||
}
|
||||
);
|
||||
|
||||
if (result.status === 0 && result.stdout) {
|
||||
// Parse NDJSON output: find "type":"text" line
|
||||
for (const line of result.stdout.trim().split("\n")) {
|
||||
try {
|
||||
const obj = JSON.parse(line);
|
||||
if (obj.type === "text" && obj.part?.text) {
|
||||
const jsonMatch = obj.part.text.match(/\{[\s\S]*\}/);
|
||||
if (jsonMatch) {
|
||||
categorizedMap = JSON.parse(jsonMatch[0]);
|
||||
console.log("LLM categorization applied.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// skip unparseable lines
|
||||
}
|
||||
}
|
||||
|
||||
if (!categorizedMap) {
|
||||
console.warn("No JSON found in opencode response.");
|
||||
console.warn("Last 500 chars:", result.stdout.slice(-500));
|
||||
}
|
||||
} else {
|
||||
console.warn("opencode failed or returned no output (status:", result.status, ")");
|
||||
if (result.stderr) {
|
||||
console.warn("stderr:", result.stderr.slice(0, 500));
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("Failed to run opencode:", e.message);
|
||||
}
|
||||
|
||||
if (!categorizedMap) {
|
||||
categorizedMap = {};
|
||||
console.log("OpenCode unavailable, using uncategorized fallback.");
|
||||
}
|
||||
|
||||
console.log("Generating changelog...");
|
||||
const changelog = await generateChangelog(previousVersion, categorizedMap);
|
||||
const changelog = await generateChangelogAI(previousVersion);
|
||||
console.log("Changelog generated.");
|
||||
|
||||
// 2. Squash merge the PR
|
||||
console.log(`Squash merging PR #${prNumber}...`);
|
||||
execSync(`gh pr merge ${prNumber} --squash --delete-branch --subject "Update to ${version}" --admin`);
|
||||
|
||||
// 3. Create draft release with changelog and dist.tar.gz
|
||||
console.log(`Creating draft release ${version}...`);
|
||||
if (!fs.existsSync(distTarGz)) {
|
||||
console.error(`dist.tar.gz not found: ${distTarGz}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const releaseBody = `## ${version}
|
||||
|
||||
${changelog}`;
|
||||
|
||||
const releaseArgs = ["release", "create", version, distTarGz, "--draft", "--title", version, "--notes", releaseBody];
|
||||
|
||||
if (isBeta) {
|
||||
releaseArgs.push("--prerelease");
|
||||
}
|
||||
|
||||
const result = childProcess.spawnSync("gh", releaseArgs, {
|
||||
encoding: "utf-8",
|
||||
stdio: "inherit",
|
||||
});
|
||||
|
||||
if (result.status !== 0) {
|
||||
console.error("Failed to create release");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`Release ${version} is ready (draft).`);
|
||||
console.log("Next steps:");
|
||||
console.log(` 1. Review the draft release: https://github.com/louislam/uptime-kuma/releases/tag/${version}`);
|
||||
console.log(` 2. Edit if needed and publish.`);
|
||||
await createRelease(version, changelog, isBeta, distTarGz);
|
||||
|
||||
@@ -373,3 +373,51 @@ The \`dist.tar.gz\` archive will be available as an artifact in the [workflow ru
|
||||
console.log("Successfully created draft pull request");
|
||||
return prNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} version
|
||||
* @param {string} distTarGz If empty, it will not be uploaded to the release
|
||||
* @param {string} changelog
|
||||
* @param {boolean} isBeta
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function createRelease(version, changelog, isBeta = false, distTarGz = undefined) {
|
||||
// 3. Create draft release with changelog and dist.tar.gz
|
||||
console.log(`Creating draft release ${version}...`);
|
||||
|
||||
const releaseBody = `## ${version}
|
||||
|
||||
${changelog}`;
|
||||
|
||||
let releaseArgs = ["release", "create", version];
|
||||
|
||||
if (distTarGz) {
|
||||
if (!fs.existsSync(distTarGz)) {
|
||||
console.error(`dist.tar.gz not found: ${distTarGz}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
releaseArgs.push(distTarGz);
|
||||
}
|
||||
|
||||
releaseArgs = releaseArgs.concat(["--draft", "--title", version, "--notes", releaseBody]);
|
||||
|
||||
if (isBeta) {
|
||||
releaseArgs.push("--prerelease");
|
||||
}
|
||||
|
||||
const result = childProcess.spawnSync("gh", releaseArgs, {
|
||||
encoding: "utf-8",
|
||||
stdio: "inherit",
|
||||
});
|
||||
|
||||
if (result.status !== 0) {
|
||||
console.error("Failed to create release");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`Release ${version} is ready (draft).`);
|
||||
console.log("Next steps:");
|
||||
console.log(` 1. Review the draft release: https://github.com/louislam/uptime-kuma/releases/tag/${version}`);
|
||||
console.log(` 2. Edit if needed and publish.`);
|
||||
}
|
||||
|
||||
+2
-1
@@ -66,7 +66,8 @@
|
||||
"start-dev-container": "cd docker && docker-compose -f docker-compose-dev.yml up --force-recreate",
|
||||
"rebase-pr-to-1.23.X": "node extra/rebase-pr.js 1.23.X",
|
||||
"reset-migrate-aggregate-table-state": "node extra/reset-migrate-aggregate-table-state.js",
|
||||
"generate-changelog": "node ./extra/generate-changelog.mjs"
|
||||
"generate-changelog": "node ./extra/generate-changelog.mjs",
|
||||
"create-release": "node ./extra/release/create-release.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@grpc/grpc-js": "~1.8.22",
|
||||
|
||||
Reference in New Issue
Block a user