@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Assembles build/app and build/service from the sources in this repository.
|
||||
*
|
||||
* There is no bundler and there are no runtime dependencies: the frontend talks
|
||||
* to the Luna bus through PalmServiceBridge and the service uses the
|
||||
* webos-service module provided by the TV itself. The only thing this script
|
||||
* does is copy files and substitute the __APP_ID__ / __SERVICE_ID__ /
|
||||
* __VERSION__ / __TITLE__ placeholders, so that the application id lives in
|
||||
* exactly one place (the "name" field of package.json).
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const root = path.resolve(__dirname, '..');
|
||||
const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
|
||||
|
||||
const vars = {
|
||||
__APP_ID__: pkg.name,
|
||||
__SERVICE_ID__: `${pkg.name}.service`,
|
||||
__VERSION__: pkg.version,
|
||||
__TITLE__: pkg.title,
|
||||
};
|
||||
|
||||
const TEXT_EXTENSIONS = ['.js', '.json', '.html', '.css', '.txt', '.sh'];
|
||||
|
||||
function substitute(contents) {
|
||||
return Object.keys(vars).reduce(
|
||||
(acc, key) => acc.split(key).join(vars[key]),
|
||||
contents,
|
||||
);
|
||||
}
|
||||
|
||||
function copy(src, dst) {
|
||||
fs.mkdirSync(path.dirname(dst), { recursive: true });
|
||||
if (TEXT_EXTENSIONS.includes(path.extname(src))) {
|
||||
fs.writeFileSync(dst, substitute(fs.readFileSync(src, 'utf8')));
|
||||
} else {
|
||||
fs.copyFileSync(src, dst);
|
||||
}
|
||||
}
|
||||
|
||||
function copyTree(src, dst) {
|
||||
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
||||
const from = path.join(src, entry.name);
|
||||
const to = path.join(dst, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
copyTree(from, to);
|
||||
} else {
|
||||
copy(from, to);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const build = path.join(root, 'build');
|
||||
fs.rmSync(build, { recursive: true, force: true });
|
||||
|
||||
// Application (frontend)
|
||||
const appDir = path.join(build, 'app');
|
||||
copyTree(path.join(root, 'app'), appDir);
|
||||
copy(path.join(root, 'appinfo.json'), path.join(appDir, 'appinfo.json'));
|
||||
copy(path.join(root, 'assets', 'icon.png'), path.join(appDir, 'icon.png'));
|
||||
copy(path.join(root, 'assets', 'largeIcon.png'), path.join(appDir, 'largeIcon.png'));
|
||||
|
||||
// JS service
|
||||
const serviceDir = path.join(build, 'service');
|
||||
copyTree(path.join(root, 'service'), serviceDir);
|
||||
fs.writeFileSync(
|
||||
path.join(serviceDir, 'services.json'),
|
||||
`${JSON.stringify(
|
||||
{
|
||||
id: vars.__SERVICE_ID__,
|
||||
description: `${pkg.title} service`,
|
||||
services: [{ name: vars.__SERVICE_ID__ }],
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`,
|
||||
);
|
||||
|
||||
console.log(`Built ${vars.__APP_ID__} ${vars.__VERSION__} into ${build}`);
|
||||
@@ -0,0 +1,104 @@
|
||||
#!/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 stable repository URL is
|
||||
* https://github.com/<owner>/<repo>/releases/latest/download/apps.json
|
||||
* and the manifest/ipk URLs inside point at the exact tagged release.
|
||||
*
|
||||
* Usage: node tools/gen-manifest.js [--repo owner/repo] [--tag v1.0.0]
|
||||
* On GitHub Actions both 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}`,
|
||||
);
|
||||
|
||||
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
|
||||
? `https://github.com/${repository}/releases/download/${tag}`
|
||||
: 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 ? `https://github.com/${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` +
|
||||
` https://github.com/${repository}/releases/latest/download/apps.json`,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user