release
This commit is contained in:
67
.gitea/scripts/downloadArtifactByName.js
Normal file
67
.gitea/scripts/downloadArtifactByName.js
Normal file
@@ -0,0 +1,67 @@
|
||||
// .gitea/scripts/downloadArtifactByName.js
|
||||
// Purpose: Download and extract a single artifact by name from a given run.
|
||||
// Env inputs:
|
||||
// GITEA_BASE_URL, OWNER, REPO, GITEA_PAT
|
||||
// RUN_ID -> numeric/string
|
||||
// ARTIFACT_NAME -> e.g. "frontend" or "webapi"
|
||||
// OUTPUT_DIR -> e.g. "artifacts/frontend"
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { execSync } = require("child_process");
|
||||
|
||||
(async () => {
|
||||
const BASE = process.env.GITEA_BASE_URL;
|
||||
const OWNER = process.env.OWNER;
|
||||
const REPO = process.env.REPO;
|
||||
const TOKEN = process.env.GITEA_PAT;
|
||||
const RUN_ID = process.env.RUN_ID;
|
||||
const NAME = process.env.ARTIFACT_NAME;
|
||||
const OUT_DIR = process.env.OUTPUT_DIR || path.join("artifacts", NAME || "");
|
||||
|
||||
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);
|
||||
}
|
||||
if (!RUN_ID || !NAME) {
|
||||
console.error("Missing RUN_ID or ARTIFACT_NAME");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const url = `${BASE}/${OWNER}/${REPO}/actions/runs/${RUN_ID}/artifacts/${encodeURIComponent(NAME)}`;
|
||||
const zipPath = path.join(process.cwd(), `${NAME}.zip`);
|
||||
fs.mkdirSync(OUT_DIR, { recursive: true });
|
||||
|
||||
console.log(`Downloading artifact "${NAME}" from run ${RUN_ID}`);
|
||||
console.log(`GET ${url}`);
|
||||
|
||||
const res = await fetch(url, {
|
||||
method: "GET",
|
||||
redirect: "follow",
|
||||
headers: { Authorization: `token ${TOKEN}` }
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const text = await res.text().catch(() => "");
|
||||
console.error(`Download failed: ${res.status} ${res.statusText}\n${text}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const buf = Buffer.from(await res.arrayBuffer());
|
||||
fs.writeFileSync(zipPath, buf);
|
||||
|
||||
console.log(`Saved ZIP -> ${zipPath}`);
|
||||
console.log(`Extracting to -> ${OUT_DIR}`);
|
||||
|
||||
execSync(`unzip -o "${zipPath}" -d "${OUT_DIR}"`, { stdio: "inherit" });
|
||||
fs.unlinkSync(zipPath);
|
||||
|
||||
console.log("Done.");
|
||||
})().catch(err => {
|
||||
console.error(err.stack || err.message || String(err));
|
||||
process.exit(1);
|
||||
});
|
||||
126
.gitea/scripts/getLatestRunWithArtifacts.js
Normal file
126
.gitea/scripts/getLatestRunWithArtifacts.js
Normal file
@@ -0,0 +1,126 @@
|
||||
// .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.
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
(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
|
||||
const listUrl = `${BASE}/api/v1/repos/${OWNER}/${REPO}/actions/tasks?limit=${SCAN_LIMIT}`;
|
||||
const resp = await api(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));
|
||||
|
||||
if (!candidates.length) {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (!picked) {
|
||||
console.error("No run exposes all required artifacts. Consider increasing SCAN_LIMIT or verify artifact names.");
|
||||
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}`);
|
||||
}
|
||||
})().catch(err => {
|
||||
console.error(err.stack || err.message || String(err));
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user