#!/usr/bin/env node /** * Generates the two files the Homebrew Channel needs: * * dist/.manifest.json - package manifest (what "Install" reads) * dist/apps.json - a one-package repository index, so the release * can be added under Homebrew Channel -> * Settings -> Add repository * * Both are uploaded as release assets, so the repository URL stays stable * across releases, while the manifest/ipk URLs inside point at the exact * tagged release. * * Works against GitHub and Gitea/Forgejo; they spell the "latest release * asset" URL differently, which is the only thing --server changes. * * Usage: node tools/gen-manifest.js [--server https://host] [--repo owner/repo] * [--tag v1.0.0] * On GitHub/Gitea Actions all three are picked up from the environment. */ 'use strict'; const fs = require('fs'); const path = require('path'); const crypto = require('crypto'); const root = path.resolve(__dirname, '..'); const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')); function arg(name, fallback) { const index = process.argv.indexOf(name); return index !== -1 && process.argv[index + 1] ? process.argv[index + 1] : fallback; } const repository = arg('--repo', process.env.GITHUB_REPOSITORY || ''); const tag = arg( '--tag', process.env.GITHUB_REF_TYPE === 'tag' ? process.env.GITHUB_REF_NAME : `v${pkg.version}`, ); // PUBLIC_SERVER_URL wins over GITHUB_SERVER_URL because a Gitea runner is // handed the instance's internal URL (a LAN address), which is useless in a // manifest the TV has to fetch. const server = arg( '--server', process.env.PUBLIC_SERVER_URL || process.env.GITHUB_SERVER_URL || 'https://github.com', ).replace(/\/+$/, ''); const isGitHub = /(^|\.)github\.com$/i.test(new URL(server).hostname); const ipkName = `${pkg.name}_${pkg.version}_all.ipk`; const ipkPath = path.join(root, 'dist', ipkName); if (!fs.existsSync(ipkPath)) { console.error(`${ipkPath} not found - run "npm run build && npm run package" first.`); process.exit(1); } const ipk = fs.readFileSync(ipkPath); const releaseBase = repository ? `${server}/${repository}/releases/download/${tag}` : null; // GitHub: /releases/latest/download/, Gitea: /releases/download/latest/ const latestBase = repository ? isGitHub ? `${server}/${repository}/releases/latest/download` : `${server}/${repository}/releases/download/latest` : null; if (!releaseBase) { console.warn( 'No repository known (pass --repo owner/repo or set GITHUB_REPOSITORY); ' + 'generating relative URLs, which only work when every file sits next to the manifest.', ); } const iconUri = `data:image/png;base64,${fs .readFileSync(path.join(root, 'assets', 'icon.png')) .toString('base64')}`; const manifestName = `${pkg.name}.manifest.json`; const manifest = { id: pkg.name, version: pkg.version, type: 'web', title: pkg.title, appDescription: pkg.description, iconUri, sourceUrl: repository ? `${server}/${repository}` : undefined, rootRequired: true, ipkUrl: releaseBase ? `${releaseBase}/${ipkName}` : ipkName, ipkHash: { sha256: crypto.createHash('sha256').update(ipk).digest('hex') }, ipkSize: ipk.length, }; const appsJson = { paging: { page: 1, count: 1, maxPage: 1, itemsTotal: 1, prevUrl: null, nextUrl: null }, packages: [ { id: manifest.id, title: manifest.title, iconUri, manifestUrl: releaseBase ? `${releaseBase}/${manifestName}` : manifestName, manifest, pool: 'main', shortDescription: pkg.description, fullDescriptionUrl: releaseBase ? `${releaseBase}/description.html` : 'description.html', }, ], }; fs.writeFileSync(path.join(root, 'dist', manifestName), `${JSON.stringify(manifest, null, 2)}\n`); fs.writeFileSync(path.join(root, 'dist', 'apps.json'), `${JSON.stringify(appsJson, null, 2)}\n`); fs.copyFileSync(path.join(root, 'docs', 'description.html'), path.join(root, 'dist', 'description.html')); console.log(`ipk : dist/${ipkName} (${(ipk.length / 1024).toFixed(1)} kB)`); console.log(`sha256 : ${manifest.ipkHash.sha256}`); console.log(`manifest : dist/${manifestName}`); console.log(`repository: dist/apps.json`); if (repository) { console.log( `\nAdd this in Homebrew Channel -> Settings -> Add repository:\n` + ` ${latestBase}/apps.json`, ); }