From c9e5f82e2843dcd251d19c94d5c230b323d3d405 Mon Sep 17 00:00:00 2001 From: Vadime Date: Tue, 18 Aug 2026 15:38:07 +0200 Subject: [PATCH] Execute scoped deploy action reliably --- .gitea/actions/quickstack-oci/deploy.mjs | 31 ++++++++++++++++--- .gitea/actions/quickstack-oci/deploy.test.mjs | 23 ++++++++++++++ 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/.gitea/actions/quickstack-oci/deploy.mjs b/.gitea/actions/quickstack-oci/deploy.mjs index 81f728e..c2671c6 100644 --- a/.gitea/actions/quickstack-oci/deploy.mjs +++ b/.gitea/actions/quickstack-oci/deploy.mjs @@ -259,6 +259,27 @@ export function validateTarget(target) { }; } +export function resolveDeploymentBranch({ eventName, environment = process.env, eventPayload } = {}) { + if (eventName !== "pull_request") { + return requiredString(environment.GITHUB_REF_NAME || environment.GITEA_REF_NAME, "Git branch"); + } + + const direct = String(environment.GITHUB_BASE_REF || environment.GITEA_BASE_REF || "").trim(); + if (direct) return direct; + + let payload = eventPayload; + if (payload === undefined) { + const eventPath = String(environment.GITHUB_EVENT_PATH || environment.GITEA_EVENT_PATH || "").trim(); + if (eventPath) { + payload = JSON.parse(fs.readFileSync(eventPath, "utf8")); + } + } + return requiredString( + payload?.pull_request?.base?.ref || payload?.pull_request?.base?.repo?.default_branch, + "Pull request base branch", + ); +} + function appendSummary(text) { const summary = process.env.GITHUB_STEP_SUMMARY ?? process.env.GITEA_STEP_SUMMARY; if (summary) fs.appendFileSync(summary, `${text}\n`); @@ -273,10 +294,8 @@ async function main() { } const config = JSON.parse(fs.readFileSync(configPath, "utf8")); const eventName = process.env.GITHUB_EVENT_NAME ?? process.env.GITEA_EVENT_NAME ?? ""; - const branch = eventName === "pull_request" - ? process.env.GITHUB_BASE_REF ?? process.env.GITEA_BASE_REF - : process.env.GITHUB_REF_NAME ?? process.env.GITEA_REF_NAME; - const deployment = selectDeployment(config, requiredString(branch, "Git branch")); + const branch = resolveDeploymentBranch({ eventName }); + const deployment = selectDeployment(config, branch); if (!deployment) { console.log(`No deployment is declared for branch ${branch}.`); return; @@ -351,7 +370,9 @@ async function main() { } } -if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { +const invokedPath = process.argv[1] ? fs.realpathSync(process.argv[1]) : ""; +const modulePath = fs.realpathSync(fileURLToPath(import.meta.url)); +if (invokedPath === modulePath) { main().catch((error) => { console.error(error instanceof Error ? error.message : String(error)); process.exitCode = 1; diff --git a/.gitea/actions/quickstack-oci/deploy.test.mjs b/.gitea/actions/quickstack-oci/deploy.test.mjs index 8e60b2a..2dcdc6f 100644 --- a/.gitea/actions/quickstack-oci/deploy.test.mjs +++ b/.gitea/actions/quickstack-oci/deploy.test.mjs @@ -4,6 +4,7 @@ import test from "node:test"; import { deployExactImage, mergeEnvironment, + resolveDeploymentBranch, selectDeployment, toSavePayload, validateTarget, @@ -33,6 +34,28 @@ test("selects only the deployment matching the triggering branch", () => { assert.equal(selectDeployment(config, "other"), null); }); +test("resolves push and pull request deployment branches without accepting empty context values", () => { + assert.equal( + resolveDeploymentBranch({ + eventName: "push", + environment: { GITHUB_REF_NAME: "", GITEA_REF_NAME: "main" }, + }), + "main", + ); + assert.equal( + resolveDeploymentBranch({ + eventName: "pull_request", + environment: { GITHUB_BASE_REF: "", GITEA_BASE_REF: "" }, + eventPayload: { pull_request: { base: { ref: "main" } } }, + }), + "main", + ); + assert.throws( + () => resolveDeploymentBranch({ eventName: "pull_request", environment: {}, eventPayload: {} }), + /Pull request base branch/, + ); +}); + test("validates OCI paths and repository-local build paths", () => { assert.equal(validateTarget({ name: "Web", image: "Owner/Web", appId: "app-1" }).image, "owner/web"); assert.throws(() => validateTarget({ name: "Web", image: "owner/web", appId: "app-1", context: "../secret" }), /inside/);