Build / build (push) Successful in 48s
The workflow was written for github.com: actions/upload-artifact failed on the Gitea runner, softprops/action-gh-release only talks to the GitHub API, and gen-manifest.js hardcoded github.com URLs. - drop the artifact upload (release assets are the deliverable) - upload release assets with tools/release-gitea.js, a dependency-free Gitea API client; re-running a tag replaces the attachments - gen-manifest.js takes --server/PUBLIC_SERVER_URL and knows that Gitea spells the latest-asset URL /releases/download/latest/<file> - PUBLIC_SERVER_URL is set explicitly because the runner only sees the instance's internal LAN address Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
122 lines
4.3 KiB
JavaScript
122 lines
4.3 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Generates the two files the Homebrew Channel needs:
|
|
*
|
|
* dist/<id>.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/<file>, Gitea: /releases/download/latest/<file>
|
|
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`,
|
|
);
|
|
}
|