Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 24 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,15 @@ jobs:
shell: pwsh
run: ./scripts/check-installer.ps1 -Setup (Get-ChildItem demo/dist/*.exe | Where-Object Name -notlike "*uninstaller*" | Select-Object -First 1).FullName

# The installer's and uninstaller's pages, as a person sees them: run with
# their pages showing, stepped through with Next, a PNG of each page
# saved. They're looked at, not compared (the runner's fonts and scaling
# aren't a person's), so they're uploaded below.
- name: Capture the installer's pages
if: runner.os == 'Windows'
shell: pwsh
run: ./scripts/capture-installer.ps1 -Setup (Get-ChildItem demo/dist/*.exe | Where-Object Name -notlike "*uninstaller*" | Select-Object -First 1).FullName -Out installer-pages

# builder/index.js's config(): the .desktop file is named for the appId
# and the running window matches it (desktopName, StartupWMClass), and it
# lists the demo's file types' MIME types, with %F so a file manager hands
Expand All @@ -272,7 +281,7 @@ jobs:
dir=$(mktemp -d)
dpkg-deb -x dist/*.deb "$dir"
cat "$dir/$entry"
grep -x "MimeType=text/plain;text/markdown;" "$dir/$entry"
grep -x "MimeType=text/plain;text/markdown;application/json;text/csv;" "$dir/$entry"
grep -E '^Exec=.* %F$' "$dir/$entry"
grep -x "StartupWMClass=$name" "$dir/$entry"
grep -x "Categories=Development;" "$dir/$entry"
Expand All @@ -299,10 +308,20 @@ jobs:
KIT_DEMO_EXECUTABLE: demo/dist/win-unpacked/electron-kit Demo.exe
run: npx playwright test

# The only thing this job uploads: when a visual test fails, the image it
# expected, the one it took and the difference, so the failure can be
# seen. A workflow artifact is as private as the repository, and it's
# kept 3 days. Nothing else is uploaded.
# The installer's pages, captured above, to be looked at. A workflow
# artifact is as private as the repository, and it's kept 3 days.
- name: Upload the installer's pages
if: runner.os == 'Windows' && !cancelled()
uses: actions/upload-artifact@v7
with:
name: installer-pages-${{ github.run_attempt }}
path: installer-pages/*.png
if-no-files-found: ignore
retention-days: 3

# And when a visual test fails, the image it expected, the one it took
# and the difference, so the failure can be seen, kept 3 days too.
# Nothing else is uploaded.
- name: Upload the visual differences
if: failure() && runner.os == 'Windows'
uses: actions/upload-artifact@v7
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ dist/
test-results/
playwright-report/
*.tgz
installer-pages/
16 changes: 8 additions & 8 deletions builder/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
// });
//
// One list of file types gives each platform what it can do with it. On
// Windows, the asking installer (installer.js) offers each type, and the
// right-click entry, with a box ticked for each, and adds only what stays
// ticked; it asks whether to install for one person or everyone. On
// Windows, the asking installer (installer.js) offers each type, the
// shortcuts and the right-click entry, with a box ticked for each, and adds
// only what stays ticked; it asks whether to install for one person or
// everyone. On
// Linux, the .desktop file lists their MIME types, with %F, so the app is in
// the file manager's Open With for each and gets every file it's opened with;
// desktopName names the .desktop file and the running window after the
Expand All @@ -33,7 +34,7 @@ const fs = require("fs");
const os = require("os");
const path = require("path");
const { MIME_TYPES, ALL_FILES } = require("./mime");
const { installerScript, columnsOf, MAX_COLUMNS, ROWS, MAX_LABEL } = require("./installer");
const { installerScript, MAX_LABEL } = require("./installer");

/** What an app's config extends: the kit's base config, by the package's name. */
const KIT_BASE = "@diamonddigitaldev/electron-kit/builder/base.json";
Expand Down Expand Up @@ -67,7 +68,7 @@ const KIT_OWNED = Object.freeze({
/** What each key the kit owns is for, in config()'s refusal. */
function ownedReason(key) {
if (key === "build.extends") return "config() extends the kit's base config itself.";
if (key.startsWith("build.nsis.")) return "The kit's installer asks who it's for and what it adds, and makes both shortcuts.";
if (key.startsWith("build.nsis.")) return "The kit's installer asks who it's for, what it adds and which shortcuts it makes.";
return "Give the files the app opens as fileTypes (or allFiles).";
}

Expand Down Expand Up @@ -123,6 +124,8 @@ function checkFileTypes(fileTypes) {
return fileTypes.map((group, i) => {
const where = `config(): fileTypes[${i}]`;
if (typeof group?.name !== "string" || group.name.trim() === "") throw new Error(`${where} needs a name, such as "Audio File".`);
// The installer's tree would hide a branch named "-..." and make "!..." bold.
if (/^[-!]/.test(group.name)) throw new Error(`${where}: "${group.name}" can't start with - or !.`);
if (!Array.isArray(group.ext) || group.ext.length === 0) throw new Error(`${where} ("${group.name}") needs ext, a list of extensions.`);
const extra = group.mimeTypes ?? {};
if (typeof extra !== "object" || Array.isArray(extra)) throw new Error(`${where}.mimeTypes must map an extension to its MIME types.`);
Expand Down Expand Up @@ -184,9 +187,6 @@ function config(pkg, options) {
if (options.allFiles !== undefined && typeof options.allFiles !== "boolean") throw new Error("config(): allFiles must be true or false.");
if (options.allFiles && options.fileTypes !== undefined) throw new Error("config(): give fileTypes or allFiles, not both.");
const fileTypes = options.fileTypes === undefined ? [] : checkFileTypes(options.fileTypes);
if (columnsOf(fileTypes).length > MAX_COLUMNS) {
throw new Error(`config(): the installer's page holds ${MAX_COLUMNS} columns of ${ROWS} file types, each group starting a column of its own; these need ${columnsOf(fileTypes).length}.`);
}
const contextMenu = options.contextMenu === undefined ? undefined : checkContextMenu(options.contextMenu);
const productName = build.productName ?? pkg.productName ?? pkg.name;
if (typeof productName !== "string" || !/[A-Za-z0-9]/.test(productName) || /["\r\n]/.test(productName)) {
Expand Down
Loading
Loading