#!/usr/bin/env node /** * Publie les artefacts d'auto-update du launcher sur une release Gitea à tag * fixe ("latest"). electron-updater (provider generic) lit ensuite latest.yml * à l'URL configurée dans electron-builder.yml. * * Pré-requis : * - `npm run build:win` a produit dist/ (latest.yml + installeur + .blockmap) * - variable d'env GITEA_TOKEN (scope write:repository) * * Config par variables d'env (avec valeurs par défaut) : * GITEA_URL base de l'instance (def. https://gitea.ldpt.fr) * GITEA_OWNER propriétaire du repo (def. zertus) * GITEA_REPO nom du repo launcher (def. OFLauncher) * GITEA_TAG tag fixe de la release (def. latest) */ import { readdir, readFile } from 'node:fs/promises' import { join } from 'node:path' const BASE = process.env.GITEA_URL ?? 'https://gitea.ldpt.fr' const OWNER = process.env.GITEA_OWNER ?? 'zertus' const REPO = process.env.GITEA_REPO ?? 'OFLauncher' const TAG = process.env.GITEA_TAG ?? 'latest' const TOKEN = process.env.GITEA_TOKEN const DIST = join(process.cwd(), 'dist') const API = `${BASE}/api/v1` if (!TOKEN) { console.error('GITEA_TOKEN manquant (scope write:repository).') process.exit(1) } /** Fichiers de dist/ à publier pour l'auto-update Windows. */ function isUpdateArtifact(name) { return ( name === 'latest.yml' || name.endsWith('-setup.exe') || name.endsWith('-setup.exe.blockmap') ) } async function api(path, init = {}) { const res = await fetch(`${API}${path}`, { ...init, headers: { Authorization: `token ${TOKEN}`, Accept: 'application/json', ...(init.headers ?? {}) } }) if (!res.ok) { const body = await res.text().catch(() => '') throw new Error(`Gitea ${init.method ?? 'GET'} ${path} -> ${res.status} ${body}`) } return res.status === 204 ? null : res.json() } /** Récupère la release au tag fixe, ou la crée si absente. */ async function getOrCreateRelease() { const res = await fetch(`${API}/repos/${OWNER}/${REPO}/releases/tags/${TAG}`, { headers: { Authorization: `token ${TOKEN}`, Accept: 'application/json' } }) if (res.ok) return res.json() if (res.status !== 404) { throw new Error(`Gitea GET release -> ${res.status} ${await res.text()}`) } console.log(`Création de la release "${TAG}"…`) return api(`/repos/${OWNER}/${REPO}/releases`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ tag_name: TAG, name: 'Dernière version', body: 'Artefacts d’auto-update du launcher (écrasés à chaque publication).' }) }) } async function deleteExistingAssets(releaseId) { const assets = await api(`/repos/${OWNER}/${REPO}/releases/${releaseId}/assets`) for (const a of assets) { console.log(`Suppression de l'ancien asset ${a.name}…`) await api(`/repos/${OWNER}/${REPO}/releases/${releaseId}/assets/${a.id}`, { method: 'DELETE' }) } } async function uploadAsset(releaseId, name) { const buf = await readFile(join(DIST, name)) const form = new FormData() form.append('attachment', new Blob([buf]), name) console.log(`Upload de ${name} (${(buf.length / 1e6).toFixed(1)} Mo)…`) await api( `/repos/${OWNER}/${REPO}/releases/${releaseId}/assets?name=${encodeURIComponent(name)}`, { method: 'POST', body: form } ) } async function main() { const files = (await readdir(DIST)).filter(isUpdateArtifact) if (!files.some((f) => f === 'latest.yml')) { throw new Error('dist/latest.yml introuvable — lance d’abord `npm run build:win`.') } console.log(`Publication sur ${OWNER}/${REPO} (tag ${TAG}) : ${files.join(', ')}`) const release = await getOrCreateRelease() await deleteExistingAssets(release.id) for (const name of files) await uploadAsset(release.id, name) console.log('✓ Publication terminée.') } main().catch((e) => { console.error(e.message) process.exit(1) })