diff --git a/scripts/cleanup-workflow-runs.js b/scripts/cleanup-workflow-runs.js index b58b228..fc11cfe 100644 --- a/scripts/cleanup-workflow-runs.js +++ b/scripts/cleanup-workflow-runs.js @@ -11,7 +11,8 @@ * harmless one. * * Environment: - * GITEA_HOST origin of the instance, e.g. https://gitea.example.com + * GITEA_HOST origin of the instance, scheme included, e.g. + * https://gitea.example.com or http://gitea:3000 * GITEA_REPO "owner/name" * GITEA_ACCESS_TOKEN token permitted to delete runs * KEEP_DAYS keep runs newer than this many days (default 7) @@ -19,6 +20,7 @@ */ const https = require('https'); +const http = require('http'); const { URL } = require('url'); const HOST = process.env.GITEA_HOST; @@ -39,12 +41,41 @@ if (!Number.isFinite(KEEP_DAYS) || KEEP_DAYS < 0) { } const origin = new URL(HOST); + +/** + * The transport GITEA_HOST actually asks for, rather than the one assumed. + * + * This was hardcoded to https, which worked when the host was typed by hand and + * failed the moment the workflow started taking it from `github.server_url`. + * Inside the runner that is the address act_runner reaches Gitea on, which here + * is plain HTTP on a container port — and a TLS handshake sent to a plaintext + * port does not fail as a connection error. It fails as: + * + * write EPROTO ... ssl3_get_record:wrong version number + * + * which reads like a TLS misconfiguration and sends you looking at certificates + * and protocol versions. It is neither. The server answered in cleartext and + * OpenSSL tried to parse that as a TLS record. + * + * So the scheme is honoured rather than guessed, and the default port follows + * from it. Anything other than the two is refused up front: this script only + * speaks HTTP, and a URL naming some other scheme is a mistake worth reporting + * as one instead of failing later inside a request. + */ +if (origin.protocol !== 'https:' && origin.protocol !== 'http:') { + console.error(`GITEA_HOST must be an http or https URL, got ${HOST}`); + process.exit(1); +} +const secure = origin.protocol === 'https:'; +const transport = secure ? https : http; +const PORT = origin.port || (secure ? 443 : 80); + const BASE = `/api/v1/repos/${REPO}/actions/runs`; function call(method, path) { return new Promise((resolve, reject) => { - const req = https.request( - { hostname: origin.hostname, port: origin.port || 443, path, method, headers: { Authorization: `token ${TOKEN}` } }, + const req = transport.request( + { hostname: origin.hostname, port: PORT, path, method, headers: { Authorization: `token ${TOKEN}` } }, (res) => { let body = ''; res.on('data', (d) => (body += d)); @@ -126,6 +157,11 @@ function selectDoomed(runs, cutoffMs) { } (async () => { + // Printed before the first request rather than after it succeeds. A transport + // failure here says nothing about where it was pointed, and the last one cost + // a round trip to find out that the answer was "somewhere plaintext". + console.log(`endpoint : ${origin.protocol}//${origin.hostname}:${PORT}`); + const runs = await listAllRuns(); const cutoffMs = Date.now() - KEEP_DAYS * 86400000; const { doomed, kept } = selectDoomed(runs, cutoffMs);