wip release

This commit is contained in:
Michał Zieliński
2025-09-18 08:58:49 +02:00
parent e2e0e753f2
commit 687f5b2be3
2 changed files with 78 additions and 107 deletions

View File

@@ -1,125 +1,96 @@
// .gitea/scripts/getLatestRunWithArtifacts.js
// Purpose: Find latest successful run that exposes all REQUIRED_ARTIFACTS via GUI URLs.
// Outputs: sets `run_id` to GITHUB_OUTPUT and writes .gitea/.cache/run_id file.
// Robust finder: uses only Gitea API (no GUI URLs).
const fs = require("fs");
const path = require("path");
const BASE = process.env.GITEA_BASE_URL; // e.g. https://code.bim-it.pl
const OWNER = process.env.OWNER; // e.g. mz
const REPO = process.env.REPO; // e.g. DiunaBI
const TOKEN = process.env.GITEA_PAT;
const SCAN_LIMIT = Number(process.env.SCAN_LIMIT || "100");
const REQUIRED_ARTIFACTS = (process.env.REQUIRED_ARTIFACTS || "frontend,webapi")
.split(",").map(s => s.trim()).filter(Boolean);
if (!BASE || !OWNER || !REPO) {
console.error("Missing one of: GITEA_BASE_URL, OWNER, REPO"); process.exit(1);
}
if (!TOKEN) {
console.error("Missing GITEA_PAT"); process.exit(1);
}
const cacheDir = path.join(".gitea", ".cache");
fs.mkdirSync(cacheDir, { recursive: true });
async function apiJSON(url) {
const res = await fetch(url, { headers: { Authorization: `token ${TOKEN}` } });
if (!res.ok) {
const t = await res.text().catch(()=>"");
throw new Error(`API ${res.status} for ${url}\n${t}`);
}
return res.json();
}
(async () => {
// --- Config from environment ---
const BASE = process.env.GITEA_BASE_URL; // e.g. https://code.bim-it.pl
const OWNER = process.env.OWNER; // e.g. mz
const REPO = process.env.REPO; // e.g. DiunaBI
const TOKEN = process.env.GITEA_PAT; // PAT
const SCAN_LIMIT = Number(process.env.SCAN_LIMIT || "100");
const REQUIRED_ARTIFACTS = (process.env.REQUIRED_ARTIFACTS || "frontend,webapi")
.split(",")
.map(s => s.trim())
.filter(Boolean);
if (!BASE || !OWNER || !REPO) {
console.error("Missing one of: GITEA_BASE_URL, OWNER, REPO");
process.exit(1);
}
if (!TOKEN) {
console.error("Missing GITEA_PAT");
process.exit(1);
}
// Ensure cache dir exists
const cacheDir = path.join(".gitea", ".cache");
fs.mkdirSync(cacheDir, { recursive: true });
// Helpers
const api = async (url) => {
const res = await fetch(url, {
headers: { Authorization: `token ${TOKEN}` }
});
if (!res.ok) {
const text = await res.text().catch(() => "");
throw new Error(`API ${res.status} ${res.statusText} for ${url}\n${text}`);
}
return res.json();
};
const headOk = async (url) => {
// Try HEAD first; some instances may require GET for redirects
let res = await fetch(url, {
method: "HEAD",
redirect: "follow",
headers: { Authorization: `token ${TOKEN}` }
});
if (res.ok) return true;
// Fallback to GET (no download) just to test availability
res = await fetch(url, {
method: "GET",
redirect: "manual",
headers: { Authorization: `token ${TOKEN}` }
});
// Accept 200 OK, or 3xx redirect to a signed download URL
return res.status >= 200 && res.status < 400;
};
// 1) Get recent workflow runs (a.k.a. tasks) via REST
// 1) Pobierz listę tasków (runs)
const listUrl = `${BASE}/api/v1/repos/${OWNER}/${REPO}/actions/tasks?limit=${SCAN_LIMIT}`;
const resp = await api(listUrl);
const resp = await apiJSON(listUrl);
// 2) Build candidate list: only status == "success", newest first by id
const runs = Array.isArray(resp.workflow_runs) ? resp.workflow_runs : [];
const candidates = runs
.filter(r => r && r.status === "success")
.sort((a, b) => (b.id ?? 0) - (a.id ?? 0));
// Gitea 1.24 może zwracać różne kształty normalizujemy:
const raw = Array.isArray(resp)
? resp
: (resp.workflow_runs || resp.tasks || resp.data || []);
if (!Array.isArray(raw) || raw.length === 0) {
console.error("No runs returned by API."); process.exit(1);
}
if (!candidates.length) {
console.error("No successful runs found.");
process.exit(1);
// 2) Filtrujemy „successful”
const isSuccess = (r) => {
const s = (r.status || "").toLowerCase(); // "success" / "completed"
const c = (r.conclusion || "").toLowerCase(); // "success" (czasem brak)
return s === "success" || (s === "completed" && c === "success");
};
const candidates = raw
.filter(r => r && (r.id != null))
.sort((a, b) => (b.id ?? 0) - (a.id ?? 0))
.filter(isSuccess);
if (candidates.length === 0) {
console.error("No successful runs found."); process.exit(1);
}
console.log(`Scanning ${candidates.length} successful runs for artifacts: ${REQUIRED_ARTIFACTS.join(", ")}`);
// 3) Find the first run that exposes all required artifacts via GUI URLs
let picked = null;
// 3) Sprawdź artefakty przez API
let pickedId = null;
for (const r of candidates) {
const runId = r.id;
const urls = REQUIRED_ARTIFACTS.map(name =>
`${BASE}/${OWNER}/${REPO}/actions/runs/${runId}/artifacts/${encodeURIComponent(name)}`
);
let allPresent = true;
for (const u of urls) {
const ok = await headOk(u).catch(() => false);
if (!ok) {
allPresent = false;
console.log(`Run ${runId}: artifact not accessible -> ${u}`);
break;
}
}
if (allPresent) {
picked = { id: runId };
console.log(`Picked run_id=${runId}`);
break;
const artsUrl = `${BASE}/api/v1/repos/${OWNER}/${REPO}/actions/runs/${runId}/artifacts`;
try {
const arts = await apiJSON(artsUrl);
const names = (Array.isArray(arts?.artifacts) ? arts.artifacts : arts || [])
.map(a => a?.name)
.filter(Boolean);
const ok = REQUIRED_ARTIFACTS.every(req => names.includes(req));
if (ok) { pickedId = runId; break; }
console.log(`Run ${runId}: lacks required artifacts (has: ${names.join(", ") || "none"})`);
} catch (e) {
console.log(`Run ${runId}: cannot read artifacts via API -> ${e.message.split("\n")[0]}`);
}
}
if (!picked) {
console.error("No run exposes all required artifacts. Consider increasing SCAN_LIMIT or verify artifact names.");
if (!pickedId) {
console.error("No run exposes all required artifacts via API.");
process.exit(1);
}
// 4) Write outputs
const runIdStr = String(picked.id);
// Write to cache (handy for debugging)
fs.writeFileSync(path.join(cacheDir, "run_id"), runIdStr, "utf8");
// Export as GitHub-style output (supported by Gitea runners)
const outFile = process.env.GITHUB_OUTPUT;
if (outFile) {
fs.appendFileSync(outFile, `run_id=${runIdStr}\n`);
} else {
// Fallback: also print for visibility
console.log(`::set-output name=run_id::${runIdStr}`);
// 4) Zapisz outputy
fs.writeFileSync(path.join(cacheDir, "run_id"), String(pickedId), "utf8");
if (process.env.GITHUB_OUTPUT) {
fs.appendFileSync(process.env.GITHUB_OUTPUT, `run_id=${pickedId}\n`);
}
console.log(`Picked run_id=${pickedId}`);
})().catch(err => {
console.error(err.stack || err.message || String(err));
process.exit(1);