diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0a7bfd5..03b5663 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,24 +30,34 @@ jobs: - name: build mailpress.exe run: node scripts/build.mjs + - name: zip installer bundle + # Single-file install bundle: unzip, double-click mailpress.exe, done. + shell: pwsh + run: | + Compress-Archive ` + -Path dist/mailpress.exe,dist/print-files.ps1,dist/INSTALL.txt ` + -DestinationPath dist/mailpress-installer.zip ` + -Force + - name: upload artifact + # Includes the zip + the loose files so users can grab either form. uses: actions/upload-artifact@v4 with: name: mailpress-windows-x64 path: | + dist/mailpress-installer.zip dist/mailpress.exe dist/print-files.ps1 - dist/config.example.json - dist/mailpress.ico + dist/INSTALL.txt - name: release (on tag) if: startsWith(github.ref, 'refs/tags/v') uses: softprops/action-gh-release@v2 with: files: | + dist/mailpress-installer.zip dist/mailpress.exe dist/print-files.ps1 - dist/config.example.json - dist/mailpress.ico + dist/INSTALL.txt draft: false generate_release_notes: true diff --git a/README.md b/README.md index 355e1af..d2c2c84 100644 --- a/README.md +++ b/README.md @@ -5,16 +5,23 @@ wired Windows printer. Runs as a Scheduled Task on the office PC. ## Install (the easy way) -1. Download `mailpress.exe`, `print-files.ps1`, and `config.example.json` - from the latest [GitHub Release][releases] (or the artifact of a - `build on windows` workflow run). Put them all in the same folder, - e.g. `C:\mailpress\`. -2. Double-click `mailpress.exe`. The setup wizard runs the first time - there's no config: it lists installed printers, walks you through the - Google OAuth client creation, runs the consent flow in your browser, - picks a polling interval, does a test print, and installs itself as a - Scheduled Task. -3. Done. Send a test email to the office inbox and confirm it prints. +1. Download **`mailpress-installer.zip`** from the latest + [GitHub Release][releases] (or the artifact of a `build on windows` + workflow run if no release is tagged yet). +2. Unzip it on the office PC, e.g. into `C:\mailpress\`. You'll see: + ``` + mailpress.exe ← double-click this + print-files.ps1 ← PowerShell print helper (must sit next to the exe) + INSTALL.txt ← step-by-step install guide + ``` +3. Double-click `mailpress.exe`. The setup wizard runs on first launch: + - picks the printer + - walks through the one-time Google OAuth client creation + - runs the consent flow in your browser + - asks how often to check for new mail + - does a test print + - installs itself as a Scheduled Task +4. Done. Send a test email to the office inbox and confirm it prints. [releases]: https://github.com/turetsky/mailpress/releases @@ -129,7 +136,6 @@ lib/ wizard.mjs # first-run interactive setup test.mjs # post-install test menu (--test) print-files.ps1 # PowerShell helper: print one or more files -config.example.json # template — copy to config.local.json assets/ icon.svg # source icon (envelope on slant + speed lines) icon.ico # multi-size .ico embedded into mailpress.exe diff --git a/assets/icon-preview-256.png b/assets/icon-preview-256.png deleted file mode 100644 index 2c0a5cc..0000000 Binary files a/assets/icon-preview-256.png and /dev/null differ diff --git a/config.example.json b/config.example.json deleted file mode 100644 index 1f38a02..0000000 --- a/config.example.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "_comment": "Copy this to config.local.json and fill in. config.local.json is gitignored.", - - "googleClientId": "FILL_IN_from_gauth_.local-credentials.json", - "googleClientSecret": "FILL_IN_from_gauth_.local-credentials.json", - - "gmailAddress": "FILL_IN_office_account@gmail.com", - "gmailQuery": "in:inbox category:primary is:unread", - - "printerName": "FILL_IN_Windows_printer_name (run Get-Printer on the office PC)", - - "pollIntervalMs": 300000, - - "tokenPath": "./.local-token.json", - "spoolDir": "./spool", - "logPath": "./mailpress.log", - - "notifyEmail": "yjturetsky@gmail.com", - "notifyFromAddress": "FILL_IN_office_account@gmail.com", - - "maxAttachmentBytes": 26214400, - "perPrintSleepMs": 3000 -} diff --git a/lib/config.mjs b/lib/config.mjs index 6c78325..486ecef 100644 --- a/lib/config.mjs +++ b/lib/config.mjs @@ -49,12 +49,6 @@ export function configPath() { return join(installRoot(), "config.local.json"); } -export function examplePath() { - // The example ships beside the source files. When running from a SEA exe, - // it may not be on disk — callers should handle ENOENT. - return join(installRoot(), "config.example.json"); -} - export function resolveInRoot(p) { return resolve(installRoot(), p); } @@ -106,7 +100,3 @@ export function validateConfig(cfg) { return errors; } -export function isConfigured() { - const cfg = loadConfig(); - return cfg && validateConfig(cfg).length === 0; -} diff --git a/lib/oauth.mjs b/lib/oauth.mjs index c664a10..bfc0f9c 100644 --- a/lib/oauth.mjs +++ b/lib/oauth.mjs @@ -182,5 +182,3 @@ export async function runConsent({ clientId, clientSecret, gmailAddress, port = return outer; } - -export { SCOPES }; diff --git a/scripts/build.mjs b/scripts/build.mjs index 518ecda..2a98341 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -153,19 +153,74 @@ async function embedIcon() { } function copyAssets() { - const assets = ["print-files.ps1", "config.example.json"]; - for (const a of assets) { - const src = join(ROOT, a); - if (existsSync(src)) { - copyFileSync(src, join(DIST, a)); - } else { - console.warn(`asset missing, skipping: ${a}`); - } - } - // The icon is embedded in the exe; also drop a copy beside it for users - // who want a desktop shortcut with a matching icon. - const ico = join(ROOT, "assets", "icon.ico"); - if (existsSync(ico)) copyFileSync(ico, join(DIST, "mailpress.ico")); + // Only files mailpress.exe actually loads at runtime ship next to it. + // print-files.ps1 is invoked by lib/printer.mjs for each print job. + // Icon is already embedded in the exe; config is created by the wizard. + const src = join(ROOT, "print-files.ps1"); + if (existsSync(src)) copyFileSync(src, join(DIST, "print-files.ps1")); + else console.warn(`print-files.ps1 missing — printing will fail at runtime`); +} + +// Drop a plaintext install guide into the bundle so anyone who unzips it +// has the install steps + command list on paper, without needing internet. +function writeInstallTxt() { + const content = `mailpress - Gmail-to-printer relay +=================================== + +WHAT'S IN THIS FOLDER + + mailpress.exe double-click this to install + print-files.ps1 PowerShell print helper (must stay next to the exe) + INSTALL.txt this file + +After the wizard runs, you'll also see these auto-generated files +appear in the same folder: + + config.local.json your config (gitignored, contains client secret) + .local-token.json your OAuth refresh token (gitignored) + mailpress.log poll-loop activity + mailpress-setup.log every prompt + response from the wizard + + +INSTALL (Windows, ~5 minutes including the Google OAuth client step) + + 1. Copy this entire folder somewhere stable on the office PC. + Recommended: C:\\mailpress\\ + + 2. Double-click mailpress.exe. + The first run launches the setup wizard. The wizard walks you + through: + - picking the printer + - creating a Google Cloud OAuth client (one-time, in your browser) + - signing in as the office Gmail account + - picking how often to check for new mail + - a test print + - installing itself as a Windows Scheduled Task + + 3. Done. mailpress now polls the office inbox and prints every new + email body + attachments. + +The one part not automated: creating the OAuth client in Google Cloud. +Google requires a human in a browser for that. The wizard prints the +exact URLs and what to paste back. + + +USEFUL COMMANDS (run from a console in this folder) + + mailpress poll forever (or run wizard if not configured) + mailpress --test switch printer, test print any file, change + polling interval - safe with task running + mailpress --doctor run health checks and tell you what is broken + mailpress --consent re-do the OAuth consent if the token dies + mailpress --setup re-run the setup wizard + mailpress --uninstall remove the scheduled task + mailpress --help full command list + +If anything goes wrong, run "mailpress --doctor" first - it identifies +which subsystem (config, token, Gmail API, printer, scheduled task) +broke and what to do about each. +`; + writeFileSync(join(DIST, "INSTALL.txt"), content); } function smokeTest() { @@ -189,9 +244,10 @@ async function main() { inject(); await embedIcon(); copyAssets(); + writeInstallTxt(); smokeTest(); console.log(`\nDone. Artifacts in ${DIST}:`); - for (const f of [EXE_NAME, "print-files.ps1", "config.example.json", "mailpress.ico"]) { + for (const f of [EXE_NAME, "print-files.ps1", "INSTALL.txt"]) { if (existsSync(join(DIST, f))) console.log(` ${f}`); } }