mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-02 09:13:56 +08:00
🎁 Sync 2026-10-09 12:18:17
This commit is contained in:
1 parent
c43003617d
commit
1ee3366de6
139 files changed
+1934
-500
No files matched your search
@@ -0,0 +1,84 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('node:assert/strict');
|
||||
const { spawnSync, execFileSync } = require('node:child_process');
|
||||
const { mkdtempSync, readFileSync, writeFileSync, rmSync } = require('node:fs');
|
||||
const { tmpdir } = require('node:os');
|
||||
const path = require('node:path');
|
||||
const { test } = require('node:test');
|
||||
|
||||
const scripts = path.resolve(__dirname, '../.github/scripts');
|
||||
|
||||
function metadata(tag) {
|
||||
const output = execFileSync('python3', [path.join(scripts, 'release_metadata.py'), tag], { encoding: 'utf8' });
|
||||
return Object.fromEntries(output.trim().split('\n').map(line => line.split('=')));
|
||||
}
|
||||
|
||||
test('stable versions retain their package versions and preview suffixes use native package formats', () => {
|
||||
for (const tag of ['v2.4.8', '2.5.0']) {
|
||||
const result = metadata(tag);
|
||||
assert.equal(result.tag, tag);
|
||||
assert.equal(result.prerelease, 'false');
|
||||
assert.equal(result.apk_version, tag.replace(/^v/, ''));
|
||||
assert.equal(result.ipk_version, result.apk_version);
|
||||
}
|
||||
for (const stage of ['alpha', 'beta', 'rc']) {
|
||||
const result = metadata(`v2.5.0-${stage}.12`);
|
||||
assert.equal(result.prerelease, 'true');
|
||||
assert.equal(result.version, `2.5.0-${stage}.12`);
|
||||
assert.equal(result.apk_version, `2.5.0_${stage}12`);
|
||||
assert.equal(result.ipk_version, `2.5.0~${stage}12-1`);
|
||||
}
|
||||
});
|
||||
|
||||
test('invalid and unsupported tags fail before producing release outputs', () => {
|
||||
for (const tag of ['latest', 'v2.5.0-beta', 'v2.5.0-beta.0', 'v2.5.0-preview.1', 'v2.5.0+build', 'v2.5.0\nprerelease=false']) {
|
||||
const result = spawnSync('python3', [path.join(scripts, 'release_metadata.py'), tag], { encoding: 'utf8' });
|
||||
assert.notEqual(result.status, 0, tag);
|
||||
assert.equal(result.stdout, '', tag);
|
||||
}
|
||||
});
|
||||
|
||||
test('preview preparation and documentation sync cannot overwrite stable metadata', t => {
|
||||
const dir = mkdtempSync(path.join(tmpdir(), 'argon-preview-docs-'));
|
||||
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
||||
const files = ['Makefile', 'README.md', 'README_ZH.md', 'RELEASE.md', 'RELEASE_ZH.md'];
|
||||
for (const file of files) writeFileSync(path.join(dir, file), 'stable v2.4.8\n');
|
||||
const env = { ...process.env, GITHUB_REPOSITORY: '', GITHUB_TOKEN: '' };
|
||||
const prepare = spawnSync('bash', [path.join(scripts, 'prepare_release_commit.sh'), 'v2.5.0-beta.1'], { cwd: dir, env });
|
||||
assert.equal(prepare.status, 1);
|
||||
assert.match(prepare.stderr.toString(), /matching tags/);
|
||||
const sync = spawnSync('bash', [path.join(scripts, 'update_release_docs.sh'), 'v2.5.0-beta.1'], { cwd: dir, env });
|
||||
assert.equal(sync.status, 0, sync.stderr.toString());
|
||||
for (const file of files) assert.equal(readFileSync(path.join(dir, file), 'utf8'), 'stable v2.4.8\n');
|
||||
});
|
||||
|
||||
test('config source resolves matching lightweight and annotated tags, and never falls back for previews', t => {
|
||||
const dir = mkdtempSync(path.join(tmpdir(), 'argon-preview-config-'));
|
||||
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
||||
const git = (...args) => execFileSync('git', ['-C', dir, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
|
||||
git('init', '-b', 'master');
|
||||
git('config', 'user.name', 'Release test');
|
||||
git('config', 'user.email', 'release-test@example.invalid');
|
||||
git('commit', '--allow-empty', '-m', 'config preview');
|
||||
const tagged = git('rev-parse', 'HEAD');
|
||||
git('tag', 'v2.5.0-beta.1');
|
||||
git('tag', '-a', 'v2.5.0-rc.1', '-m', 'annotated preview');
|
||||
git('tag', 'v2.5.0');
|
||||
git('commit', '--allow-empty', '-m', 'later master');
|
||||
const master = git('rev-parse', 'HEAD');
|
||||
const resolve = (tag, remote = dir) => spawnSync('bash', [path.join(scripts, 'resolve_config.sh'), remote, tag], { encoding: 'utf8' });
|
||||
for (const tag of ['v2.5.0-beta.1', 'v2.5.0-rc.1', 'v2.5.0']) {
|
||||
const result = resolve(tag);
|
||||
assert.equal(result.status, 0, result.stderr);
|
||||
assert.equal(result.stdout.trim(), `sha=${tagged}`);
|
||||
}
|
||||
assert.equal(resolve('v2.6.0').stdout.trim(), `sha=${master}`);
|
||||
const missing = resolve('v2.5.0-beta.2');
|
||||
assert.notEqual(missing.status, 0);
|
||||
assert.match(missing.stdout, /matching config tag/);
|
||||
assert.doesNotMatch(missing.stdout, /sha=/);
|
||||
const unavailable = resolve('v2.6.0', path.join(dir, 'missing.git'));
|
||||
assert.notEqual(unavailable.status, 0);
|
||||
assert.doesNotMatch(unavailable.stdout, /sha=/);
|
||||
});
|
||||
@@ -260,3 +260,67 @@ jsonfilter() { return 1; }
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
// Exercise the real filesystem module, including malformed state and stale files.
|
||||
test('custom branding accepts only published assets and safely falls back', () => {
|
||||
const dir = mkdtempSync(path.join(tmpdir(), 'argon-branding-test-'));
|
||||
const assets = path.join(dir, 'branding');
|
||||
const module = path.join(dir, 'luci', 'argon-branding.uc');
|
||||
fs.mkdirSync(path.dirname(module));
|
||||
const source = fs.readFileSync(path.join(root, 'root/usr/share/ucode/luci/argon-branding.uc'), 'utf8');
|
||||
writeFileSync(module, source.replace('/www/luci-static/argon/branding/', assets + '/'));
|
||||
const ucode = process.env.UCODE_BIN || 'ucode';
|
||||
const args = ['-L', path.join(path.dirname(ucode), '*.so'), '-L', path.join(dir, '*.uc')];
|
||||
const read = () => JSON.parse(execFileSync(ucode, [...args, '-e',
|
||||
"import { get_branding } from 'luci.argon-branding'; print(sprintf('%J', get_branding()));"
|
||||
], { encoding: 'utf8' }));
|
||||
const image = 'image-' + 'a'.repeat(64) + '.png';
|
||||
const manifest = image.replace('.png', '.webmanifest');
|
||||
const index = path.join(assets, 'current.json');
|
||||
try {
|
||||
assert.deepEqual(read(), {});
|
||||
fs.mkdirSync(assets);
|
||||
writeFileSync(path.join(assets, image), Buffer.alloc(100));
|
||||
writeFileSync(path.join(assets, manifest), '{}');
|
||||
writeFileSync(index, JSON.stringify({ favicon: image, logo: image }));
|
||||
assert.deepEqual(read(), { favicon: image, logo: image, manifest });
|
||||
for (const bad of ['broken', 'null', '[]', '123', '"text"', '{"logo":"../elsewhere.png"}', ' '.repeat(1025)]) {
|
||||
writeFileSync(index, bad);
|
||||
assert.deepEqual(read(), {});
|
||||
}
|
||||
writeFileSync(index, JSON.stringify({ logo: image, favicon: '../outside.png' }));
|
||||
assert.deepEqual(read(), { logo: image });
|
||||
fs.unlinkSync(path.join(assets, image));
|
||||
assert.deepEqual(read(), {});
|
||||
writeFileSync(path.join(dir, 'outside.png'), Buffer.alloc(100));
|
||||
fs.symlinkSync(path.join(dir, 'outside.png'), path.join(assets, image));
|
||||
assert.deepEqual(read(), {});
|
||||
fs.unlinkSync(index);
|
||||
fs.symlinkSync(path.join(dir, 'outside.png'), index);
|
||||
assert.deepEqual(read(), {});
|
||||
fs.renameSync(assets, path.join(dir, 'saved'));
|
||||
fs.symlinkSync(path.join(dir, 'saved'), assets);
|
||||
assert.deepEqual(read(), {});
|
||||
} finally { rmSync(dir, { recursive: true, force: true }); }
|
||||
});
|
||||
|
||||
test('custom head icons replace every default candidate and restore as a group', () => {
|
||||
const dir = mkdtempSync(path.join(tmpdir(), 'argon-head-test-'));
|
||||
const source = fs.readFileSync(path.join(root, 'ucode/template/themes/argon/head_meta.ut'), 'utf8')
|
||||
.replace("import { get_branding } from 'luci.argon-branding';", 'function get_branding() { return fixture; }');
|
||||
const file = path.join(dir, 'head.ut');
|
||||
writeFileSync(file, '{% let media="/luci-static/argon"; let hostname="Router"; let node={}; let bar_color="#123456"; %}' + source);
|
||||
const render = fixture => execFileSync(process.env.UCODE_BIN || 'ucode', ['-T,', '-Dfixture=' + JSON.stringify(fixture), file], { encoding: 'utf8' });
|
||||
try {
|
||||
const defaults = render({});
|
||||
assert.match(defaults, /favicon\.ico/);
|
||||
assert.match(defaults, /apple-icon-144x144\.png/);
|
||||
assert.match(defaults, /icon\/manifest\.json/);
|
||||
const custom = render({ favicon: 'custom.png', manifest: 'custom.webmanifest' });
|
||||
assert.match(custom, /rel="icon" type="image\/png" href="\/luci-static\/argon\/branding\/custom.png"/);
|
||||
assert.match(custom, /rel="apple-touch-icon" href="\/luci-static\/argon\/branding\/custom.png"/);
|
||||
assert.match(custom, /branding\/custom.webmanifest/);
|
||||
assert.doesNotMatch(custom, /favicon\.ico|icon\/favicon|icon\/android|icon\/apple|icon\/manifest/);
|
||||
assert.equal(render({ logo: 'custom.png' }), defaults);
|
||||
} finally { rmSync(dir, { recursive: true, force: true }); }
|
||||
});
|
||||
Reference in new issue
Block a user