docs: publish complete product guide and UI fixes
Expand the English README into a practical guide covering installation, accounts, queue controls, automation, history, backups, updates, troubleshooting, and development. Add four sanitized workflow screenshots and enforce their exact public manifest. Restore the animated Backup submenu outside its parent menu, keep expanded account groups naturally sized in the scrolling Accounts list, and strengthen Electron UI regression exit codes.
This commit is contained in:
@@ -17,8 +17,15 @@ const rootFiles = [
|
||||
'preload-drop-target.js',
|
||||
'preload.js'
|
||||
];
|
||||
const directoryRoots = ['assets', 'lib', 'renderer', 'services/backup-api', 'tests'];
|
||||
const directoryRoots = ['assets', 'docs', 'lib', 'renderer', 'services/backup-api', 'tests'];
|
||||
const scriptFiles = ['scripts/afterPack.cjs', 'scripts/dev-runner.cjs', 'scripts/release-plan.mjs', 'scripts/verify-public-release.mjs'];
|
||||
const screenshotFiles = [
|
||||
'assets/product-overview.png',
|
||||
'docs/screenshots/upload-workspace.png',
|
||||
'docs/screenshots/account-management.png',
|
||||
'docs/screenshots/automation-settings.png',
|
||||
'docs/screenshots/history.png'
|
||||
];
|
||||
const currentVersion = require('../package.json').version;
|
||||
|
||||
function copyDirectory(source, destination) {
|
||||
@@ -32,6 +39,14 @@ function copyDirectory(source, destination) {
|
||||
}
|
||||
}
|
||||
|
||||
function copyDocumentationScreenshots(stage) {
|
||||
for (const relativePath of screenshotFiles.slice(1)) {
|
||||
const destination = path.join(stage, relativePath);
|
||||
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
||||
fs.copyFileSync(path.join(root, screenshotFiles[0]), destination);
|
||||
}
|
||||
}
|
||||
|
||||
function createStage() {
|
||||
const stage = fs.mkdtempSync(path.join(os.tmpdir(), 'mhu-public-verifier-'));
|
||||
for (const relativePath of rootFiles) {
|
||||
@@ -39,7 +54,10 @@ function createStage() {
|
||||
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
||||
fs.copyFileSync(path.join(root, relativePath), destination);
|
||||
}
|
||||
for (const relativePath of directoryRoots) copyDirectory(path.join(root, relativePath), path.join(stage, relativePath));
|
||||
for (const relativePath of directoryRoots) {
|
||||
if (relativePath === 'docs') copyDocumentationScreenshots(stage);
|
||||
else copyDirectory(path.join(root, relativePath), path.join(stage, relativePath));
|
||||
}
|
||||
for (const relativePath of scriptFiles) {
|
||||
const destination = path.join(stage, relativePath);
|
||||
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
||||
@@ -49,8 +67,11 @@ function createStage() {
|
||||
return stage;
|
||||
}
|
||||
|
||||
function verify(stage, version = currentVersion) {
|
||||
return spawnSync(process.execPath, ['scripts/verify-public-release.mjs', '--source-only', '--version', version], {
|
||||
function verify(stage, version = currentVersion, sourceOnly = true) {
|
||||
const args = ['scripts/verify-public-release.mjs'];
|
||||
if (sourceOnly) args.push('--source-only');
|
||||
args.push('--version', version);
|
||||
return spawnSync(process.execPath, args, {
|
||||
cwd: stage,
|
||||
encoding: 'utf8'
|
||||
});
|
||||
@@ -70,7 +91,41 @@ test('public release verifier accepts only the exact source manifest and target
|
||||
assert.match(extra.stderr, /tests\/unexpected\.json\tsource-layout-allowlist/);
|
||||
fs.rmSync(path.join(stage, 'tests', 'unexpected.json'));
|
||||
|
||||
fs.copyFileSync(path.join(stage, screenshotFiles[1]), path.join(stage, 'docs', 'screenshots', 'unexpected.png'));
|
||||
const extraDocumentation = verify(stage);
|
||||
assert.equal(extraDocumentation.status, 1);
|
||||
assert.match(extraDocumentation.stderr, /docs\/screenshots\/unexpected\.png\tsource-layout-allowlist/);
|
||||
fs.rmSync(path.join(stage, 'docs', 'screenshots', 'unexpected.png'));
|
||||
|
||||
const wrongVersion = verify(stage, '2.0.5');
|
||||
assert.equal(wrongVersion.status, 1);
|
||||
assert.match(wrongVersion.stderr, /package\.json\tpackage-version-target/);
|
||||
});
|
||||
|
||||
test('public release verifier requires and validates every approved screenshot', (t) => {
|
||||
const stage = createStage();
|
||||
t.after(() => fs.rmSync(stage, { recursive: true, force: true }));
|
||||
fs.copyFileSync(path.join(root, screenshotFiles[0]), path.join(stage, screenshotFiles[0]));
|
||||
|
||||
const baseline = verify(stage, currentVersion, false);
|
||||
assert.equal(baseline.status, 0, baseline.stderr);
|
||||
|
||||
for (const relativePath of screenshotFiles) {
|
||||
const absolutePath = path.join(stage, relativePath);
|
||||
const original = fs.readFileSync(absolutePath);
|
||||
const invalid = Buffer.from(original);
|
||||
invalid.writeUInt32BE(999, 16);
|
||||
fs.writeFileSync(absolutePath, invalid);
|
||||
|
||||
const invalidScreenshot = verify(stage, currentVersion, false);
|
||||
assert.equal(invalidScreenshot.status, 1);
|
||||
assert.ok(invalidScreenshot.stderr.includes(`${relativePath}\tproduct-screenshot`), invalidScreenshot.stderr);
|
||||
fs.writeFileSync(absolutePath, original);
|
||||
}
|
||||
|
||||
const missingPath = screenshotFiles.at(-1);
|
||||
fs.rmSync(path.join(stage, missingPath));
|
||||
const missingScreenshot = verify(stage, currentVersion, false);
|
||||
assert.equal(missingScreenshot.status, 1);
|
||||
assert.ok(missingScreenshot.stderr.includes(`${missingPath}\trequired-screenshot`), missingScreenshot.stderr);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user