/** * Складывает артефакты electron-builder (win + mac) в публичный репозиторий, * ветка `updates`, чтобы generic URL …/raw/branch/updates/ указывал на актуальные latest*.yml и установщики. * * Переменные окружения (имена без префикса GITEA_ — в Gitea секреты GITEA_* зарезервированы): * DND_UPDATES_SERVER — https://git.example.com (без слэша в конце) * UPDATES_REPO — owner/repo (публичный репозиторий «только релизы») * DND_UPDATES_PUSH_TOKEN — PAT с правом push в UPDATES_REPO * ARTIFACT_WIN — каталог с файлами сборки Windows * ARTIFACT_MAC — каталог с файлами сборки macOS * GIT_COMMIT_TAG — опционально, для сообщения коммита */ import { execFileSync } from 'node:child_process'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const ALLOWED_EXT = new Set(['.yml', '.yaml', '.exe', '.blockmap', '.zip', '.dmg', '.pkg']); function mustEnv(name) { const v = process.env[name]?.trim(); if (!v) throw new Error(`Missing env ${name}`); return v; } function copyFlatReleaseFiles(fromDir, toDir) { if (!fs.existsSync(fromDir)) { console.warn(`[sync-update-feed] skip missing dir: ${fromDir}`); return 0; } let n = 0; for (const name of fs.readdirSync(fromDir)) { const src = path.join(fromDir, name); if (!fs.statSync(src).isFile()) continue; const ext = path.extname(name).toLowerCase(); if (!ALLOWED_EXT.has(ext)) continue; fs.copyFileSync(src, path.join(toDir, name)); n += 1; } return n; } function runGit(args, cwd) { execFileSync('git', args, { cwd, stdio: 'inherit' }); } function emptyWorkingTreeExceptGit(cwd) { for (const ent of fs.readdirSync(cwd)) { if (ent === '.git') continue; fs.rmSync(path.join(cwd, ent), { recursive: true, force: true }); } } function main() { const server = mustEnv('DND_UPDATES_SERVER').replace(/\/+$/u, ''); const updatesRepo = mustEnv('UPDATES_REPO'); const token = mustEnv('DND_UPDATES_PUSH_TOKEN'); const winDir = mustEnv('ARTIFACT_WIN'); const macDir = mustEnv('ARTIFACT_MAC'); const u = new URL(server); const host = u.host; const cloneUrl = `https://oauth2:${encodeURIComponent(token)}@${host}/${updatesRepo}.git`; const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'dnd-feed-')); const work = path.join(tmp, 'repo'); try { execFileSync('git', ['clone', '--depth', '1', '-b', 'updates', cloneUrl, work], { stdio: 'inherit' }); } catch { execFileSync('git', ['clone', '--depth', '1', cloneUrl, work], { stdio: 'inherit' }); runGit(['checkout', '-B', 'updates'], work); } runGit(['config', 'user.email', 'ci@gitea-actions.local'], work); runGit(['config', 'user.name', 'gitea-actions'], work); emptyWorkingTreeExceptGit(work); const copied = copyFlatReleaseFiles(winDir, work) + copyFlatReleaseFiles(macDir, work); if (copied === 0) { throw new Error('[sync-update-feed] no release files copied (check ARTIFACT_WIN / ARTIFACT_MAC)'); } const tag = process.env.GIT_COMMIT_TAG?.trim() || 'ci'; runGit(['add', '-A'], work); const st = execFileSync('git', ['status', '--porcelain'], { cwd: work }).toString().trim(); if (st) { runGit(['commit', '-m', `update feed ${tag}`], work); runGit(['push', '-u', 'origin', 'updates'], work); } else { console.warn('[sync-update-feed] nothing to commit (identical artifacts?)'); } fs.rmSync(tmp, { recursive: true, force: true }); console.log(`[sync-update-feed] done (${String(copied)} file(s) staged)`); } main();