From 66060348dac3b109ba83fdcc411bdf0dd2a829fc Mon Sep 17 00:00:00 2001 From: NuklearRabbit <145918611+NuklearRabbit@users.noreply.github.com> Date: Fri, 24 Jul 2026 20:29:23 +0200 Subject: [PATCH] Update --- .gitignore | 6 + CHANGELOG.md | 66 + LICENSE | 21 + README.md | 332 ++ SOURCE_MANIFEST.txt | 105 + START_HERE.md | 60 + UPDATE_FROM_0.3.2.md | 19 + build-windows.ps1 | 58 + build/icon-128.png | Bin 0 -> 10176 bytes build/icon-16.png | Bin 0 -> 483 bytes build/icon-256.png | Bin 0 -> 27094 bytes build/icon-32.png | Bin 0 -> 1321 bytes build/icon-48.png | Bin 0 -> 2470 bytes build/icon-512.png | Bin 0 -> 34515 bytes build/icon-64.png | Bin 0 -> 3710 bytes build/icon.ico | Bin 0 -> 47784 bytes build/icon.png | Bin 0 -> 34515 bytes docs/ARCHITECTURE.md | 221 ++ docs/DEPLOYMENT_SETUP.md | 58 + docs/DIAGNOSTICS.md | 150 + docs/LUMAOPS_SERVER_AUDIT.md | 147 + docs/RELEASE_NOTES_0.2.0.md | 32 + docs/RELEASE_NOTES_0.3.0.md | 140 + docs/RELEASE_NOTES_0.3.1.md | 25 + docs/RELEASE_NOTES_0.3.2.md | 72 + docs/RELEASE_NOTES_0.4.0.md | 57 + docs/RELEASE_NOTES_0.4.1.md | 26 + docs/RELEASE_NOTES_0.4.2.md | 29 + docs/RELEASE_NOTES_0.4.3.md | 9 + docs/ROADMAP.md | 95 + docs/SECURITY.md | 132 + docs/SETUP_GUIDE.md | 485 +++ docs/SSH_UNRAID_DEPLOYMENT.md | 112 + docs/STATUS_ENDPOINT.md | 59 + docs/STITCH_REVIEW.md | 99 + docs/TEST_MATRIX.md | 106 + docs/UPDATING.md | 55 + docs/screenshots/deploy-confirmation.png | Bin 0 -> 140415 bytes docs/screenshots/deployment-run.png | Bin 0 -> 107166 bytes docs/screenshots/deployment-success.png | Bin 0 -> 118819 bytes docs/screenshots/overview.png | Bin 0 -> 153240 bytes docs/screenshots/repository-workspace.png | Bin 0 -> 135102 bytes examples/gitea-actions/deploy.yml | 51 + examples/gitea-actions/rollback.yml | 50 + examples/server/forgeflow-deploy | 148 + examples/server/forgeflow-runner.sudoers | 4 + examples/server/forgeflow-targets.conf | 9 + examples/server/nginx-forgeflow-status.conf | 8 + examples/server/status-example.json | 11 + main.cjs | 202 + package-lock.json | 3677 +++++++++++++++++++ package.json | 89 + preload.cjs | 87 + scripts/apply-source-update.ps1 | 92 + scripts/doctor.mjs | 88 + scripts/serve-demo.mjs | 28 + scripts/verify.mjs | 97 + setup-windows.ps1 | 62 + src/main/config-store.cjs | 424 +++ src/main/deployment-service.cjs | 424 +++ src/main/diagnostics-service.cjs | 356 ++ src/main/git-service.cjs | 298 ++ src/main/gitea-service.cjs | 277 ++ src/main/ipc.cjs | 409 +++ src/main/log-redaction.cjs | 93 + src/main/preflight-service.cjs | 208 ++ src/main/process-runner.cjs | 37 + src/main/repository-monitor.cjs | 70 + src/main/repository-service.cjs | 193 + src/main/ssh-service.cjs | 146 + src/main/unraid-deployment-service.cjs | 595 +++ src/main/update-service.cjs | 135 + src/renderer/app.js | 1055 ++++++ src/renderer/assets/itworx-mark.png | Bin 0 -> 31424 bytes src/renderer/assets/itworx-wordmark.png | Bin 0 -> 64378 bytes src/renderer/index.html | 22 + src/renderer/mock-bridge.js | 350 ++ src/renderer/styles.css | 535 +++ src/shared/clone-target.cjs | 24 + src/shared/git-status.cjs | 93 + src/shared/repository-match.cjs | 39 + src/shared/semver.cjs | 32 + src/shared/shell-verification.cjs | 36 + src/shared/tool-invocation.cjs | 42 + src/shared/validation.cjs | 125 + src/shared/zip-writer.cjs | 91 + tests/clone-target.test.mjs | 91 + tests/deployment-status.test.mjs | 154 + tests/diagnostics.test.mjs | 75 + tests/git-integration.test.mjs | 54 + tests/git-status.test.mjs | 34 + tests/git-workflows.test.mjs | 46 + tests/gitea-actions.test.mjs | 90 + tests/log-redaction.test.mjs | 47 + tests/preflight.test.mjs | 65 + tests/renderer-workflow.test.mjs | 24 + tests/repository-matching.test.mjs | 18 + tests/repository-monitor.test.mjs | 30 + tests/security-validation.test.mjs | 66 + tests/semver.test.mjs | 13 + tests/shell-verification.test.mjs | 49 + tests/tool-invocation.test.mjs | 41 + tests/unraid-deployment.test.mjs | 126 + tests/update-service.test.mjs | 52 + tests/validation.test.mjs | 17 + tests/zip-writer.test.mjs | 41 + update-windows.ps1 | 50 + 107 files changed, 14771 insertions(+) create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 SOURCE_MANIFEST.txt create mode 100644 START_HERE.md create mode 100644 UPDATE_FROM_0.3.2.md create mode 100644 build-windows.ps1 create mode 100644 build/icon-128.png create mode 100644 build/icon-16.png create mode 100644 build/icon-256.png create mode 100644 build/icon-32.png create mode 100644 build/icon-48.png create mode 100644 build/icon-512.png create mode 100644 build/icon-64.png create mode 100644 build/icon.ico create mode 100644 build/icon.png create mode 100644 docs/ARCHITECTURE.md create mode 100644 docs/DEPLOYMENT_SETUP.md create mode 100644 docs/DIAGNOSTICS.md create mode 100644 docs/LUMAOPS_SERVER_AUDIT.md create mode 100644 docs/RELEASE_NOTES_0.2.0.md create mode 100644 docs/RELEASE_NOTES_0.3.0.md create mode 100644 docs/RELEASE_NOTES_0.3.1.md create mode 100644 docs/RELEASE_NOTES_0.3.2.md create mode 100644 docs/RELEASE_NOTES_0.4.0.md create mode 100644 docs/RELEASE_NOTES_0.4.1.md create mode 100644 docs/RELEASE_NOTES_0.4.2.md create mode 100644 docs/RELEASE_NOTES_0.4.3.md create mode 100644 docs/ROADMAP.md create mode 100644 docs/SECURITY.md create mode 100644 docs/SETUP_GUIDE.md create mode 100644 docs/SSH_UNRAID_DEPLOYMENT.md create mode 100644 docs/STATUS_ENDPOINT.md create mode 100644 docs/STITCH_REVIEW.md create mode 100644 docs/TEST_MATRIX.md create mode 100644 docs/UPDATING.md create mode 100644 docs/screenshots/deploy-confirmation.png create mode 100644 docs/screenshots/deployment-run.png create mode 100644 docs/screenshots/deployment-success.png create mode 100644 docs/screenshots/overview.png create mode 100644 docs/screenshots/repository-workspace.png create mode 100644 examples/gitea-actions/deploy.yml create mode 100644 examples/gitea-actions/rollback.yml create mode 100644 examples/server/forgeflow-deploy create mode 100644 examples/server/forgeflow-runner.sudoers create mode 100644 examples/server/forgeflow-targets.conf create mode 100644 examples/server/nginx-forgeflow-status.conf create mode 100644 examples/server/status-example.json create mode 100644 main.cjs create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 preload.cjs create mode 100644 scripts/apply-source-update.ps1 create mode 100644 scripts/doctor.mjs create mode 100644 scripts/serve-demo.mjs create mode 100644 scripts/verify.mjs create mode 100644 setup-windows.ps1 create mode 100644 src/main/config-store.cjs create mode 100644 src/main/deployment-service.cjs create mode 100644 src/main/diagnostics-service.cjs create mode 100644 src/main/git-service.cjs create mode 100644 src/main/gitea-service.cjs create mode 100644 src/main/ipc.cjs create mode 100644 src/main/log-redaction.cjs create mode 100644 src/main/preflight-service.cjs create mode 100644 src/main/process-runner.cjs create mode 100644 src/main/repository-monitor.cjs create mode 100644 src/main/repository-service.cjs create mode 100644 src/main/ssh-service.cjs create mode 100644 src/main/unraid-deployment-service.cjs create mode 100644 src/main/update-service.cjs create mode 100644 src/renderer/app.js create mode 100644 src/renderer/assets/itworx-mark.png create mode 100644 src/renderer/assets/itworx-wordmark.png create mode 100644 src/renderer/index.html create mode 100644 src/renderer/mock-bridge.js create mode 100644 src/renderer/styles.css create mode 100644 src/shared/clone-target.cjs create mode 100644 src/shared/git-status.cjs create mode 100644 src/shared/repository-match.cjs create mode 100644 src/shared/semver.cjs create mode 100644 src/shared/shell-verification.cjs create mode 100644 src/shared/tool-invocation.cjs create mode 100644 src/shared/validation.cjs create mode 100644 src/shared/zip-writer.cjs create mode 100644 tests/clone-target.test.mjs create mode 100644 tests/deployment-status.test.mjs create mode 100644 tests/diagnostics.test.mjs create mode 100644 tests/git-integration.test.mjs create mode 100644 tests/git-status.test.mjs create mode 100644 tests/git-workflows.test.mjs create mode 100644 tests/gitea-actions.test.mjs create mode 100644 tests/log-redaction.test.mjs create mode 100644 tests/preflight.test.mjs create mode 100644 tests/renderer-workflow.test.mjs create mode 100644 tests/repository-matching.test.mjs create mode 100644 tests/repository-monitor.test.mjs create mode 100644 tests/security-validation.test.mjs create mode 100644 tests/semver.test.mjs create mode 100644 tests/shell-verification.test.mjs create mode 100644 tests/tool-invocation.test.mjs create mode 100644 tests/unraid-deployment.test.mjs create mode 100644 tests/update-service.test.mjs create mode 100644 tests/validation.test.mjs create mode 100644 tests/zip-writer.test.mjs create mode 100644 update-windows.ps1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0705472 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +dist/ +.DS_Store +*.log +coverage/ +.forgeflow/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..96c983d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,66 @@ +## 0.4.3 + +- Removed the platform-dependent Unraid inspection integration test that mixed Windows temporary paths with remote Linux semantics. +- Added deterministic SSH inspection contract coverage. +- Released as one complete clean source archive. + +# Changelog + +## 0.4.2 + +- Replaced nested SSH Bash quoting with a single-line base64 transport. +- Fixed Windows Git Bash inspection failures caused by multiline quote parsing. +- Made temporary test cleanup resilient to short-lived Windows directory locks. +- Corrected remote status-file base64 invocation. +- Added Windows-focused regression coverage and a recovery updater from 0.4.0/0.4.1. + +## 0.4.1 + +- Fixed Windows `bash -n` verification for ForgeFlow paths such as `C:\Projects\ForgeFlow`. +- Bash now receives a relative POSIX script path with the project root supplied through `cwd`. +- Added cross-platform regression tests, including a project root containing spaces. + +## 0.4.0 + +- Made the changed-file panel independently scrollable for large working trees. +- Added explicit commit-message readiness and clarified that commit actions stage selected files automatically. +- Integrated the supplied ITWorx.tech logo into the title bar, setup and desktop icons. +- Added a private-Gitea source updater pinned to an exact update-branch commit. +- Added secure SSH / Unraid server profiles with host-key pinning. +- Added exact-SHA deployment to Git-backed `/mnt/user/appdata/` folders. +- Added existing deployment inspection, tracked-change blocking, origin alignment and nested-Git warnings. +- Added repository Compose adoption and basic generated Compose for simple Dockerfile applications. +- Added exact previous-SHA rollback with repeat health verification. +- Added the LumaOps server-versus-Gitea migration audit. +- Expanded the automated suite to 59 passing tests. + +## 0.3.2 + +- Clone from Gitea now uses the first configured project root automatically. +- Repository-named target folders are created without reopening the folder picker. +- Added an explicit Choose another location action for exceptional clones. +- Existing matching checkouts are linked instead of cloned again. +- Different repositories, non-empty ordinary folders and file conflicts are blocked. +- Clone identity and destination are resolved again in the privileged backend. +- Added a Windows source-update script and upgrade guide. +- Expanded the automated suite from 39 to 45 passing tests. + +## 0.3.1 + +- Fixed the Windows environment doctor failing with `spawn npm ENOENT` after a successful npm installation. +- Added safe npm invocation through `npm_execpath` with a `cmd.exe` fallback. +- Added Windows and cross-platform regression tests for npm discovery. +- Removed the duplicated hard-coded doctor version. + +## 0.1.0 — Functional MVP foundation + +- Reworked the Stitch static screens into one coherent desktop application. +- Added secure Electron main/preload/renderer architecture. +- Added real Gitea connection and repository loading. +- Added bounded local Git repository discovery and remote matching. +- Added real Git status, diff, staging, commit, push, fetch and fast-forward pull. +- Added deployment profiles and exact-SHA Gitea Actions dispatch. +- Added deployment confirmation, operation history and healthcheck foundation. +- Added dark/light themes, onboarding and browser demo. +- Added unit tests and a real temporary Git remote integration test. +- Added safe workflow and server deployment examples. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cb0c466 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Jens Caers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..5838817 --- /dev/null +++ b/README.md @@ -0,0 +1,332 @@ +# ForgeFlow + +ForgeFlow is a desktop release cockpit that turns the complete path from a local +code change to a verified server deployment into one guided flow: + +```text +Local working tree -> Gitea repository -> exact deployed server version +``` + +It is not an IDE and it does not replace Gitea Actions. ForgeFlow coordinates +local Git, a private Gitea instance and fixed deployment workflows while keeping +credentials on the user's own computer. + +![ForgeFlow overview](docs/screenshots/overview.png) + +## Current status: v0.4.2 integrated desktop test release + +### v0.4.2 workflow and deployment expansion + +- large changed-file sets scroll independently; +- commit actions explain the missing prerequisite and auto-stage selected files; +- ITWorx.tech branding is integrated; +- the app can update itself from `Jens/ForgeFlow` in the configured Gitea instance; +- SSH / Unraid servers can be configured once with pinned host identity; +- exact commits can be deployed to `/mnt/user/appdata/`; +- existing Compose definitions and untracked runtime data are preserved; +- a basic Unraid-compatible Compose file can be generated for simple new projects. + +Read [SSH / Unraid deployment](docs/SSH_UNRAID_DEPLOYMENT.md) and the +[LumaOps audit](docs/LUMAOPS_SERVER_AUDIT.md). + +### v0.4.2 automatic clone workflow + +**Clone from Gitea** now uses the first configured project root and creates the +repository-named subfolder automatically. The native folder picker is reserved +for the explicit **Choose another location** action. Existing matching checkouts +are linked; conflicting folders are never overwritten. + +The Windows environment doctor retains the v0.3.1 npm command-shim correction. + +Version 0.3.2 is designed so the user does not need to give a developer any +Gitea token, SSH key, server password or other credential. The application now +includes: + +- a five-step setup wizard; +- computer, Gitea and deployment preflights; +- protected local token entry; +- structured local diagnostics with aggressive secret redaction; +- standard and strict privacy support bundles; +- fail-closed bundle safety auditing; +- request IDs that correlate desktop, Actions and server events; +- root-owned allowlisted server target configuration; +- a complete setup guide from fresh checkout to first rollback test. + +Start with [START_HERE.md](START_HERE.md). The full installation and acceptance +procedure is in [docs/SETUP_GUIDE.md](docs/SETUP_GUIDE.md). + +## What ForgeFlow can do + +### Repository workflow + +- Connect to a private Gitea instance and validate the account locally. +- Discover Git working trees beneath one or more project roots. +- Use the first project root as the automatic clone destination. +- Match local folders to Gitea repositories using normalized `origin` identity. +- Link existing repositories or clone directly into the default project root. +- Read real Git status through porcelain v2 and NUL-delimited output. +- Inspect diffs and select files. +- Stage and unstage changes. +- Commit locally or commit and push in one action. +- Push, fetch and use fast-forward-only pull. +- Review recent history. +- Create, switch and publish branches. +- Stash, list and restore local work. +- Mark favorites and prioritize repositories requiring attention. +- Refresh repository state automatically while the application is open. + +### Deployment workflow + +- Configure multiple environments per repository. +- Bind each environment to a fixed branch and fixed Gitea Actions workflows. +- Run a visible deployment preflight before confirmation. +- Re-run mandatory checks in the privileged backend before dispatch. +- Verify a clean tree, correct branch, upstream and ahead/behind state. +- Verify that the exact full SHA exists on the allowed remote branch. +- Verify local and remote workflow files and Gitea Actions availability. +- Dispatch a unique request ID with the exact commit SHA. +- Poll Gitea Actions runs and jobs. +- Correlate the workflow with the server status endpoint. +- Verify that the requested SHA is actually live. +- Run an independent application healthcheck. +- Roll back through a separate fixed workflow to the previous exact SHA. +- Preserve a bounded local operation history. + +### Safe diagnostics + +ForgeFlow stores structured JSONL diagnostics in the application-data folder. +They are intended to make development and troubleshooting possible without +requesting credentials. + +The logger records useful operational facts such as: + +- timestamps, stable event names and duration; +- Git action outcomes and repository state; +- Gitea endpoint paths and HTTP status, but not authorization headers; +- deployment request IDs, workflow stages and health states; +- preflight results; +- sanitized error names, codes, messages and stacks. + +It excludes or redacts: + +- Gitea tokens and encrypted token blobs; +- passwords, authorization headers and credential-bearing URLs; +- private keys and common token patterns; +- arbitrary environment-variable dumps; +- local source contents and Git diffs; +- raw Gitea runner logs from support bundles; +- user-home paths. + +Support bundles are audited before writing. If a known runtime secret, private +key marker or URL credential remains, ForgeFlow aborts the export instead of +creating the ZIP. Strict privacy mode also hashes repository and user +identifiers while keeping related events correlatable. + +Read [docs/DIAGNOSTICS.md](docs/DIAGNOSTICS.md) for the exact policy and its +limitations. + +## Security model + +- Electron main/preload/renderer separation. +- Sandboxed renderer with context isolation and no direct Node.js access. +- Narrow, frozen preload API. +- Trusted renderer-origin checks on privileged IPC. +- No arbitrary shell command field in the interface. +- Gitea token stored using Electron `safeStorage` when available. +- Blank token updates preserve the existing protected token. +- Atomic local configuration writes and schema migration. +- Repository-relative path validation for file actions. +- Supported-protocol and credential-in-URL validation. +- Fixed workflow, branch and environment allowlists. +- Root-owned server target configuration. +- Exact-SHA deployment and rollback. +- Server-side locking, health validation and status records. +- Local diagnostic retention, rotation and clear/export controls. + +More detail is available in [docs/SECURITY.md](docs/SECURITY.md). + +## Prerequisites + +For a Windows source setup: + +- Windows 10 or 11; +- Node.js 22 or newer; +- npm; +- Git on `PATH`; +- a normal signed-in desktop session; +- access to your own Gitea instance. + +For deployment: + +- Gitea Actions enabled; +- a trusted Gitea runner with an environment-specific label; +- a Linux target server reachable by that runner; +- Git, Docker Compose, `curl` and `flock` on the target server; +- an existing non-interactive server checkout of the application. + +## Fastest Windows start + +Extract the release, open PowerShell in the folder and run: + +```powershell +Set-ExecutionPolicy -Scope Process Bypass +.\setup-windows.ps1 +``` + +The script: + +1. checks Node.js, npm and Git; +2. installs the exact dependency versions from `package-lock.json` when present; +3. runs the environment doctor; +4. validates the source tree; +5. runs all automated tests; +6. starts ForgeFlow. + +No Gitea or server credential is requested by the PowerShell script. Tokens are +entered later in the local desktop wizard. + +Manual equivalent: + +```bash +npm install --no-audit --no-fund +npm run doctor +npm run check +npm start +``` + +## Setup and first acceptance test + +Follow these documents in order: + +1. [START_HERE.md](START_HERE.md) +2. [docs/SETUP_GUIDE.md](docs/SETUP_GUIDE.md) +3. [docs/UPDATING.md](docs/UPDATING.md) +4. [docs/DEPLOYMENT_SETUP.md](docs/DEPLOYMENT_SETUP.md) +5. [docs/STATUS_ENDPOINT.md](docs/STATUS_ENDPOINT.md) +6. [docs/DIAGNOSTICS.md](docs/DIAGNOSTICS.md) + +The recommended first test uses a non-critical staging deployment and validates: + +```text +preflight -> commit -> push -> deploy -> Actions -> server SHA -> healthcheck -> rollback +``` + +## Updating an existing source installation + +Close ForgeFlow, copy the v0.4.2 update overlay over the existing source folder +and run: + +```powershell +Set-ExecutionPolicy -Scope Process Bypass +.\update-windows.ps1 +``` + +Application configuration and protected credentials are stored outside the +source folder and are not reset. See [docs/UPDATING.md](docs/UPDATING.md). + +## Browser demo + +The standalone visual demo requires no Gitea connection or credentials: + +```bash +npm run demo +``` + +Open the printed local URL. The demo uses `src/renderer/mock-bridge.js`; the +packaged desktop application uses the privileged `preload.cjs` bridge. + +## Quality checks + +```bash +npm run verify +npm test +npm run check +npm run doctor -- --json +``` + +Version 0.3.2 contains 45 automated tests. Two integration suites create real +temporary bare Git remotes and exercise commit, push, branch, stash and exact +remote-SHA behavior. Detailed coverage is listed in +[docs/TEST_MATRIX.md](docs/TEST_MATRIX.md). + +## Development packages + +```bash +npm run dist:win +npm run dist:linux +npm run dist:mac +``` + +Windows users can run: + +```powershell +.\build-windows.ps1 +``` + +These commands create unsigned development packages. Public distribution still +requires platform code signing and platform-specific release acceptance. + +## Server-side deployment model + +ForgeFlow never sends a free-form deployment command. It dispatches a fixed +workflow with controlled inputs: + +```json +{ + "ref": "main", + "inputs": { + "environment": "staging", + "commit_sha": "0123456789abcdef0123456789abcdef01234567", + "request_id": "generated-by-forgeflow" + } +} +``` + +The workflow calls a root-owned allowlisted server script. Deployment targets +are defined in `/etc/forgeflow/targets.conf`, not supplied by the desktop app. +The server checks repository, environment, paths, branch, exact remote ancestry, +locking, Docker Compose result and health before it updates the public status +JSON. + +## Project structure + +```text +main.cjs Electron lifecycle and service composition +preload.cjs Whitelisted renderer API and event bridge +src/main/config-store.cjs State, migration and protected token storage +src/main/git-service.cjs Real Git CLI adapter +src/main/gitea-service.cjs Gitea REST and Actions adapter +src/main/repository-service.cjs Discovery, matching and repository aggregation +src/main/repository-monitor.cjs Background working-tree awareness +src/main/deployment-service.cjs Dispatch, polling, SHA checks and rollback +src/main/preflight-service.cjs Computer and deployment readiness checks +src/main/diagnostics-service.cjs Safe JSONL diagnostics and support bundles +src/main/log-redaction.cjs Recursive secret and privacy sanitizer +src/main/ipc.cjs Validated privileged operation boundary +src/shared/clone-target.cjs Safe automatic clone target planning +src/shared/zip-writer.cjs Dependency-free diagnostic ZIP writer +src/renderer/ Desktop UI and browser demo +examples/gitea-actions/ Fixed deploy and rollback workflow templates +examples/server/ Allowlisted Linux deployment implementation +tests/ Unit and real Git integration tests +docs/ Setup, diagnostics, security and architecture +``` + +## Scope and remaining acceptance + +The v0.4.2 logic is prepared for self-service configuration and testing, but no +source release can prove compatibility with a private environment it has never +connected to. The remaining acceptance is deliberately local to the user: + +- validate the exact Gitea version and Actions API; +- validate the runner label and runner permissions; +- adapt one target line in the root-owned server configuration; +- verify the application-specific Docker Compose and health endpoint; +- execute the documented staging deployment and rollback test; +- export a Strict support bundle if a failure occurs. + +No credential needs to be shared for that process. + +## License + +MIT. See [LICENSE](LICENSE). diff --git a/SOURCE_MANIFEST.txt b/SOURCE_MANIFEST.txt new file mode 100644 index 0000000..7f421db --- /dev/null +++ b/SOURCE_MANIFEST.txt @@ -0,0 +1,105 @@ +755f4db7d76bfec0963ef051748a82810c0d58acd4ffd823aa6928a5167fceb4 .gitignore +4bf1a097cf93b11eb49c09a9d7f92a341eab15c5eafd7014f6087aac352648ca CHANGELOG.md +4a561ead5ba7cdfaf4efce91842a4308c5f2a77980205879d83835efb8a579db LICENSE +f1a69461c2ea8edec2b0fecc23db196cafccd730a309f0ad84acd5c5110b45ef README.md +e5414be56177664a12d31f8d668d6273628c62ae1a608b9f61580c2f9359bbf1 START_HERE.md +8f36b542736f2933bad8b9464ad7fa37b68196009c81cf702ce3b677cd637dea UPDATE_FROM_0.3.2.md +7fbfbba99e6f38029b9a77c8fad8a8e5a91c186306e9e167aae5357c666aab2e build/icon-128.png +2e4519b3fff1f06c2f706b11ab3ee87c5100092df6c6e03763c2e3010dfdca8f build/icon-16.png +5e331c05fc5f76beb229ebc973ca4c88ad6663cef23ed38b67b03d233275371d build/icon-256.png +d6342683ccadb6d5ae4b4013f4b4fa8dfb3c27d2d3449ba23e82c4dd4f68cb46 build/icon-32.png +1a58c1454ec6bf39509f6c56094ee9927ac1fac71d4a52d4e35068ead80dd696 build/icon-48.png +c2b306a28dc374700c857a482a28ebc805845141e8eba1907486eeec9a76aba4 build/icon-512.png +4d2adb598bf4ee3593f7293009a7dd2739742f44234daf4ecebd8b77bc74608f build/icon-64.png +ca6fbcee9cd986155c760953ab4a56cf6eea448ba8adf227412e7069aa4e211f build/icon.ico +c2b306a28dc374700c857a482a28ebc805845141e8eba1907486eeec9a76aba4 build/icon.png +6337a7d0791e8749e3a576d7735257b0031db7a9eff4a2d6818cc251544774d2 build-windows.ps1 +2d9836ae6d576bab5494b9f094bc673e5ca4772bf5771006583d1bc46fe46698 docs/ARCHITECTURE.md +30a92bcf5daadb019efa2f82cb820ea302490dd1d68fb772674dc3faccd3e594 docs/DEPLOYMENT_SETUP.md +eb42f979666e05d51c587e4223282914926a2b9b1ade9f3fb75525019ce7f738 docs/DIAGNOSTICS.md +a0cd06a96f23a94e118feb012be0fa1ac51345951cb2ba8e67fb8c889c4c342a docs/LUMAOPS_SERVER_AUDIT.md +a0c00ff76acd1682bb5e0e8dcf6589c9480da436c9c6d30780a1ed58b4dad94f docs/RELEASE_NOTES_0.2.0.md +5773ead01aa4c522c556295553787482d01b1f5242f053b2c61f120c4de4fa76 docs/RELEASE_NOTES_0.3.0.md +d46de73cf6c4cd5c2ba3f455a7a2af2e0d64ee9d94a97fd1a0bfb44e35c1624a docs/RELEASE_NOTES_0.3.1.md +0d697d241a08d2427a6e7f5c2f27bd1830a41836a01e08eeff239c7ad5d89982 docs/RELEASE_NOTES_0.3.2.md +bc6933c303d3d9b3bfdbf678cae1a717bfe5a893780a1871af8b48589f62f0e3 docs/RELEASE_NOTES_0.4.0.md +343862445061e1a8282a7aa9b2304e7d799e58f9956d50eb5352db18d790efad docs/RELEASE_NOTES_0.4.1.md +e2d67c816a919f00f9e26bf59cf29e5e8cf894536b743d282075c646c5accc96 docs/RELEASE_NOTES_0.4.2.md +1aef74fb109541903c4dbc4d9c48d2bd63507420eaf8cceb31890797a5e4f5fd docs/RELEASE_NOTES_0.4.3.md +c465f1a9c4454c9a18f38f68a243037b8897c2c9929077a586604acd4ff26d35 docs/ROADMAP.md +322624242d246d07180cc719e14c91e8fb69e123676a02e5046f4e576cca1ca1 docs/SECURITY.md +c79123aa4c718ac3ab0d79771f2967710c28f939b58fca0094b02e3172f2c024 docs/SETUP_GUIDE.md +b5ba1f7580e47e1f01900964b866d9f15b973a9e9dcccf2650f403595020e949 docs/SSH_UNRAID_DEPLOYMENT.md +b6a178215dab054006aae4944b8ffcbe7f6100691c30f08e221e3a2dbff4cd42 docs/STATUS_ENDPOINT.md +0adfeabb98168a7fc0b02bae8d4af436d3c59459012fb05b2216e02265190128 docs/STITCH_REVIEW.md +08640f1b5e26048b5ae501909f415d2426b07cc316a0bd2178023f2457aa7a2a docs/TEST_MATRIX.md +a620b634532b3cc13c17a5e7de35efab1eb34673279d64de86df9b369cf7d65b docs/UPDATING.md +1ccde232c060395d7aedce27e89a7647b77afe28ab71de0a5a3efeded57369d3 docs/screenshots/deploy-confirmation.png +b39506254ffa2c73c389fb4795b3a745368bbeb7d8514cc47a636316d6d9a6aa docs/screenshots/deployment-run.png +070e6700bdae8c628c907ba181bbf0dde0bbbbb4208f7a875503f933ff1b882e docs/screenshots/deployment-success.png +bcb1e4daf1eeedc5b3f61d2406f1a65312dba130082528007e1629d9df99570a docs/screenshots/overview.png +224e34ab45877bbb97b07d2a14c4a5aa6e28339522a8015b33a2a81477177143 docs/screenshots/repository-workspace.png +c230b931abf2293d2d44b7a69b94c35f1142c093cc46b88739a0de5cbd6d1896 examples/gitea-actions/deploy.yml +4c792cc9fd57ed36da291300c252a6ef75b08a249cf6f2561e23c4c22522138a examples/gitea-actions/rollback.yml +1d2cde1bef4882f56006823d2806f6105882fa098a665a303150fdf18ada2004 examples/server/forgeflow-deploy +4fe3eee5c2d8705964c24b8c4dd909883a05e7d6eb85629c84b0d64473e0a92b examples/server/forgeflow-runner.sudoers +0423fe2cc7f43fe793986a3f62a395668897cdf07348756aa7742a8cd40ac51c examples/server/forgeflow-targets.conf +106538d4a14a5a7b13419f9520c582b19809e8fafe2cb8c7dce2bc3e600dd10a examples/server/nginx-forgeflow-status.conf +2dff25fb39ce8fc7844026a50524b23f241bec5b614eb05371c7f908a080f69a examples/server/status-example.json +6e4ef7ec12358d756d2ee6105420a5a440ba8d207a7ee26a35c873c24661d84d main.cjs +4a3d70ddcb00778d98b38662fa6af27ce33017e95139a2726298e3a20948985e package.json +1e9cfc496c61702a9b083265f08ce451008d5ce8829bb4a447429e6abea3dc89 preload.cjs +a39ab8ac36fc81c37c1718ec620d4e590a1c404d01264e07e3c904a5189bdc27 scripts/apply-source-update.ps1 +f427dfcd7b5ee7079de13633c8d7d22a91115e0bbc4f2a9a96246f42176d4880 scripts/doctor.mjs +444b397d515d65a7ee59d3088cba869cbb812d2b8cc18fc5d255105e3edb58c2 scripts/serve-demo.mjs +d8c56b85186f36579e8b311f929db2a7c7b77f817cc95ea953fceaba0467a8ed scripts/verify.mjs +92524adae60aced3af23f8afe82c011873ae9f1e53d854e4d12a94e8d1be1aa9 setup-windows.ps1 +366c1edbc90a00fcbf660002e55291ba234d896e7afbe24002d9d6db84b9f44c src/main/config-store.cjs +a970ff3f47d1641bf1ab9611e1122349aa65ff8fee4789585e078431368b8c6b src/main/deployment-service.cjs +c157640e76d558906a9aa9881eda811196623ef1c65fa3467f32f0f84b0ddd0c src/main/diagnostics-service.cjs +620226c8518c8308e0095f17102371238aeb8db92005392fe1ed0163d2e567b5 src/main/git-service.cjs +ab7344b1951c87e982cab5c293891bc45dad48a48a3b4b63991b0e76ef785ba6 src/main/gitea-service.cjs +2af3674ca1faaae24a8858ec6dc2f4ccb271112de1534e5d49c00ff50047e731 src/main/ipc.cjs +62f2c80c8210e19370b8556b1f296cbae50dae6b758a39e209f8fb461691fd4c src/main/log-redaction.cjs +958595a99fb242c127f475f3d8622bdba4c07b2d658703f69fe3992227a9107e src/main/preflight-service.cjs +9e35f867b1be78cb850c7872456b2fc00c837b8552c6244057862b7166c26661 src/main/process-runner.cjs +e89b54e7e3174b4b0a1dcd9058d8344e29431f9d16d0e6bb8d11559b691440a0 src/main/repository-monitor.cjs +eca26673564284fce8715bd74201e59fa390800926d968d642d07eec5cc3af66 src/main/repository-service.cjs +65db01a05d842c40bb784c34560870b2e9c3b973c088fcc5ce0db654a585b146 src/main/ssh-service.cjs +db7fa63d85cec92afc9017207495571e7c5ec215aca02cf2aa18e6b8e265ba27 src/main/unraid-deployment-service.cjs +2b39c0c1e84da52026dc95c9962c4976b7f69bc636b186dc8a41f9d4904f208a src/main/update-service.cjs +5fe59bb1a3aa7afea4694891dc32836c7a15d0fa42a3d87bf7217365ee97d95b src/renderer/app.js +2f3448ddaa016105769d20cbe30c461fd7ba5d3bd105865b750aa0ef6b66e1af src/renderer/assets/itworx-mark.png +37f7da5a438b88be731c45c027a0fd88d08bd1af3150afa787836fa7baadbc48 src/renderer/assets/itworx-wordmark.png +0fc26fbc70918e92586098fb0ee5c2f9946758020f930a08a005b270794b5998 src/renderer/index.html +c25dd7ee946fc7973fe3e9431cb39b22ee4ad608f9c5056f778788c7a387c2b9 src/renderer/mock-bridge.js +7fcd281b5307ed9dccbf2fee9b917943d9b02bb0bd3a8577fa607f62ea66d1d5 src/renderer/styles.css +0a1e9d9d6cd4d190eb7f85dbc6668d80600b1cf2749cc0c2c51cc428f506f20d src/shared/clone-target.cjs +029e600229714d033c28e2dcb77817aa8269847001782ae0012960e83ffd183f src/shared/git-status.cjs +2778ebcbdf60fdc1cb0749f15565e0e1bd66f3a0d31eb70ae7942a7511a3de75 src/shared/repository-match.cjs +7f4d057a3c8e8d22eda9477eea7b144237824ef0f514737831d1881ff8e7f4a4 src/shared/semver.cjs +c3135dea1c0d35ad3b4cda9597eac3335d1f16a556f8fb96694df4f9d3b0bf6f src/shared/shell-verification.cjs +2daa98fd421598bfe5fc9757c9b6f4d82c31d1bfece15829928473581d5d2639 src/shared/tool-invocation.cjs +a97c83b8023d6c0cf49d6f2d5b626ef2341f02670f0de170e840026d28fd1f0e src/shared/validation.cjs +13b731c38863b1007b0312fd9d89562401b7cce875c952f52429bde74f77a8af src/shared/zip-writer.cjs +454edeaccb2bd41043bc918d3e3a6127db14339031d6a1c1562ac855e90455d2 tests/clone-target.test.mjs +abb65b39f285da518a48be41aff40d89ceb9c5b0e6091772c2bde171f65daf9b tests/deployment-status.test.mjs +fae3634bae871abade4d487b94b4741b50e787804dbd6135249f634fdd83c6d0 tests/diagnostics.test.mjs +7ed195eba8f40ea1e4a57678b0e3dd2defbe888c5c8aab5e931898d506ad638e tests/git-integration.test.mjs +5ea94c6b241a02060d531fad94e449eecd3772eed2137581d4e2babfb09e56db tests/git-status.test.mjs +681ab7bcd02c4dd98d1d8d2092a3521c489d941131e7ffe5903971b940046474 tests/git-workflows.test.mjs +e914b2bcafbd674c06adfd9bd851ca04e134210691b7f91cd3de26cee37ef5f3 tests/gitea-actions.test.mjs +caf98cbd9de9b119dae610ee53fa333a7a11214f34762247452fbb85e8bbf725 tests/log-redaction.test.mjs +c0f8f5a3784835f19d9ff1015185ccb385840b6fa1c9ec19f233393a7d952b65 tests/preflight.test.mjs +035c18b6801313f9fbdb5d8c5a26e12beae37d33b18c8433b96bd6cb65910a36 tests/renderer-workflow.test.mjs +2b4956fa4df4624a04117737e57ba74020564330ff71303b5746d8ccc881e880 tests/repository-matching.test.mjs +f679072548554a64974f0452337ce5e7b0c567343c287223770cc0974b905348 tests/repository-monitor.test.mjs +3c71aa5fb30d9c6fbc4b0ccfcf5112f45cbcc2a60cb990e4551a8813f7155505 tests/security-validation.test.mjs +ecfdad2a03c24898c822fcf05abac89c8f8fe452a05b16fdafc0236a64c27a23 tests/semver.test.mjs +60ce18a6e538acb29191b0857f45b78ce71102440655f5e070251325261a8151 tests/shell-verification.test.mjs +0f80df71dda957d18a7dabc8286e117a01b3c0de73dc06458beb6e9fc23af0da tests/tool-invocation.test.mjs +80f748687bc3fb72812388faf34732c8e71cbccd1f9258bc95b7f32c34eb3a84 tests/unraid-deployment.test.mjs +cfc143a618be64456512313f0b244c1310e9c79ce94b7dacb1b627d9de26f750 tests/update-service.test.mjs +4d1f0a4c46190ca72b51fddf79ec6d4d02e65fa6f42ef3755de5d414f7da75bb tests/validation.test.mjs +7ef4d4b9f5f3e6979293b29d571ce0e39f83197f3cade2d999a9cea7bacdd84d tests/zip-writer.test.mjs +3ea68269b66f639b3aba50c9605ccbfaa32c22a1d2cc842d8296c4ee59df6212 update-windows.ps1 diff --git a/START_HERE.md b/START_HERE.md new file mode 100644 index 0000000..73a5670 --- /dev/null +++ b/START_HERE.md @@ -0,0 +1,60 @@ +# Start here — ForgeFlow v0.4.2 + +You do **not** need to send anyone your Gitea token, SSH key or server password. +All credentials are entered locally in ForgeFlow during setup. Diagnostic logging +is designed to exclude them. + +## Already running v0.3.2? + +Use the v0.4.2 update overlay and follow [docs/UPDATING.md](docs/UPDATING.md). +Your Gitea token and ForgeFlow configuration are stored outside the source +folder and are preserved. + + +## Fast path on Windows + +1. Extract the complete ForgeFlow ZIP to a normal local folder. +2. Open PowerShell in that folder. +3. Run: + +```powershell +Set-ExecutionPolicy -Scope Process Bypass +.\setup-windows.ps1 +``` + +The script checks Node.js and Git, installs the desktop dependencies, runs the +full source/test quality gate and opens ForgeFlow. + +4. Follow the five-step setup wizard in the application. +5. Continue with [`docs/SETUP_GUIDE.md`](docs/SETUP_GUIDE.md) to connect one + repository to a staging or production environment. + +## When something fails + +Open **Diagnostics** in ForgeFlow and: + +1. Run **System preflight** or the environment **Deployment preflight**. +2. Resolve any blocking checks shown in red. +3. Choose **Create diagnostic ZIP**. +4. Prefer **Strict privacy** when the bundle will be shared. +5. Inspect the ZIP before sharing it. + +The bundle contains redacted application events, configuration structure, +repository/deployment states, preflight output and a local safety-audit result. +It intentionally excludes access tokens, encrypted token blobs and raw runner +logs. + +Detailed diagnostic behavior: [`docs/DIAGNOSTICS.md`](docs/DIAGNOSTICS.md). + + +## Unraid deployments + +After the desktop app opens: + +1. Open **Settings → SSH / Unraid servers**. +2. Add the Unraid host and `/mnt/user/appdata`. +3. Save it and run **Test & trust**. +4. Open a repository, go to **Deployments**, and choose **SSH / Unraid**. +5. Run **Preflight** before the first deployment. + +See [docs/SSH_UNRAID_DEPLOYMENT.md](docs/SSH_UNRAID_DEPLOYMENT.md). diff --git a/UPDATE_FROM_0.3.2.md b/UPDATE_FROM_0.3.2.md new file mode 100644 index 0000000..283c59f --- /dev/null +++ b/UPDATE_FROM_0.3.2.md @@ -0,0 +1,19 @@ +# Update ForgeFlow 0.3.2 to 0.4.0 + +1. Close ForgeFlow completely. +2. Extract `ForgeFlow-0.4.0-update-from-0.3.2.zip`. +3. Copy the contents of the included `ForgeFlow` folder into your existing + `C:\Users\Jens\dyad-apps\ForgeFlow` folder and replace existing files. +4. Do not create a nested `ForgeFlow\ForgeFlow` folder. +5. Open Windows PowerShell in the existing ForgeFlow folder and run: + +```powershell +Set-ExecutionPolicy -Scope Process Bypass +.\update-windows.ps1 +``` + +The script installs the pinned SSH dependency, runs the environment doctor, +source verification and all automated tests, then starts ForgeFlow. + +Credentials, repository mappings, diagnostics, server definitions and + deployment profiles are stored outside the source folder and remain intact. diff --git a/build-windows.ps1 b/build-windows.ps1 new file mode 100644 index 0000000..37d141f --- /dev/null +++ b/build-windows.ps1 @@ -0,0 +1,58 @@ +$ErrorActionPreference = "Stop" +Set-StrictMode -Version Latest +Set-Location $PSScriptRoot + +function Assert-Command { + param([Parameter(Mandatory = $true)][string]$Name) + if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) { + throw "Required command '$Name' was not found on PATH. Read START_HERE.md for prerequisites." + } +} + +function Invoke-Step { + param( + [Parameter(Mandatory = $true)][string]$Title, + [Parameter(Mandatory = $true)][scriptblock]$Action + ) + Write-Host "`n$Title" -ForegroundColor Yellow + & $Action + if ($LASTEXITCODE -ne 0) { + throw "$Title failed with exit code $LASTEXITCODE." + } +} + +Write-Host "ForgeFlow v0.4.2 Windows development build" -ForegroundColor Cyan +Write-Host "Artifacts are unsigned and intended for local testing." -ForegroundColor DarkGray + +Assert-Command node +Assert-Command npm +Assert-Command git + +$nodeVersionText = (node --version).Trim() +$nodeMajor = [int]($nodeVersionText.TrimStart('v').Split('.')[0]) +if ($nodeMajor -lt 22) { + throw "Node.js 22 or newer is required. Detected: $nodeVersionText" +} + +Invoke-Step "Installing exact project dependencies..." { + if (Test-Path ".\package-lock.json") { + npm ci --no-audit --no-fund + } else { + npm install --no-audit --no-fund + } +} + +Invoke-Step "Running the environment doctor..." { + npm run doctor +} + +Invoke-Step "Running the release quality gate..." { + npm run check +} + +Invoke-Step "Building NSIS installer and portable package..." { + npm run dist:win +} + +Write-Host "`nBuild complete. Artifacts are available in .\dist" -ForegroundColor Green +Get-ChildItem -Path .\dist -File | Select-Object Name, Length, LastWriteTime diff --git a/build/icon-128.png b/build/icon-128.png new file mode 100644 index 0000000000000000000000000000000000000000..bd27c9e0a06b1de58ac7ca1d071f0ad39062a0cf GIT binary patch literal 10176 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 HRrvn_UYGg0 literal 0 HcmV?d00001 diff --git a/build/icon-16.png b/build/icon-16.png new file mode 100644 index 0000000000000000000000000000000000000000..251c178a3a8cea19a5a8df4ea35ede3e5345d14f GIT binary patch literal 483 zcmV<90UZ8`P)v!G}v)}w+m-&ioFC|Tb?u%=>Y*)4omt|mJVDQ^?qdsN-Eg6^| zCS=X4!x;ZEFf#t%_4)7afBzZpD~ocN2{Qb!WMt>O{Q3T!sUABXR=obp63+hq{dcS` z$lZTS#%{;cMV4!BoIsaz>R480zViD2z?$j$$l`Dp_-?!zbm|@B2}UM12?cKELIwr~ z29IsG*cliY7?L-hRr#i4TflbnOxd9&i{{Cg_(fpYZn^r}E4THx!x0 zUx0yuf#KGzTkPhmZ#=MBajBkxfq_BJBFIHt%lRpW3mE=0aAaxx*~Y-Y!0_tppQJT+ zf9(JNKa@G)(!Vv#EUagbMCdg#FfcGYI|K}hmZe%KY#h}!u313jLeK~udePkXJBApV1XH6B+L|k z?mgq0yFWNC-n{qqpE&FPYkyf7UOZYb@%5}bw~n!}vN8Ps|DTD8ndzyx5P!rgtkEzs Z0Ra4~sE4?Ct+4lRaabH({0l<9y~yBeIPiYafdV>+#Lcm5Zr?|5-bFFC%9{X;7)+X-QC?CKHhPD z##uLe-|Vq#t+H9OYK18)N@JpvpaTE^Oj#L-Dgc1+_KE;NMSgqeI~73z0ILjHh`9Rq z^y3V7Z~fluhIWgcm1T`nC-%lMn-a_d(@AOsDp4M93+5r zV$l+N#w7~<#>6%@9hjuyNEUQIQKJpUKIAigmF4?-kn=$m=h0k6Wn2mj zk^`AGpSJOz-0v!>=1|SGp(1qv82(Hkl0edLo;Uyub-)HTYlVqjOxwC@^1r1ddS~l3 z)7rBGz9@pB-Tq2=5(0Gj(NqZ3pbtSC$vqDYFMXwko>KhPsNSn6+K9S{a)^V{hO*V0 ze8BNgh`1dO*@YneQIqpV4}Me6feZ)+pj1ulk;yZc4t);-O|nh1cGe+G`NiUpXpFZq&#sxz-=n{ zFf1|Sd-Tiv9&lN*?twL&G{&}s_B22QD!j4G8=vFAPUTE&>F79_zMO=R_0QpsXa0}0 z65qxd87M@(7EDlvKf;H;vy1_mN=j)H%*_oSKH1pkSsMV{_%IpXM8bg*CM1Yy5bR6s zjNf-O@|_(klxM7_lP2ldvsm{mZ68+E{Ox9-CJ&cNS^6j=GXa4?qoEx0_mL|^y5L;C z4dYF4)Yo}9@xO3aG}uWXE>6$RGhJt%PAtejTE@-E!Z|Lo=j+H>PX50Emn-5zvel|r z5wA9fqZpJ`dC2L4QqIA_Hx)y}HUldVoW)b|J2Vk+b;B|>f%c}gu9C;tP$6VgxM^I+ z(2#e8uhWZeOYL{oM7wVnn=wMlEb@F6%pd@!t-3pMGHPeJEQlkGpp_wud1yLnqk z-Fv`E^QnmY@xq^yl9F#7mA~OEVKT0qQC7f;4>ntt+_&V|1SGEi;Y?E z`L;RYVQN8=;V<`9*TtuhICCmj=f5ITnN_Aw+TO&AtvbZ%pR2 zawqef!fk)`BT9GX#brgN`d=(4P)OVHOS!yp#VvYPfSYpqYn`MwI~97ZKGUpAkvUGC zz1t1mye$A4+a2%zlKw)WH@B3OQYDOw4)g62ND_lA#c#=au(9bZExr6=&~n)kzByrN zLSh>@5~^~sbY{Nv3pV%@CN_4~`7u;xO7s$&C5s8kPj2eOe z9QJmx>JB9}($7xc=#147Yv3_%0AASfyNbHE1m0TA&Qkh_IXVvAl%WLyvngeaxRdXj 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

T@(XOh^x927yOsAO?DsXPt?(_5 z5E2wQCG2%;)bU+FKvol*EL2D6v{G+y^^L5td#LG+$W|UAKaf(8Ujs}uLAhpP*tZ2S z;cJBRTx_7?Wp+U+6a=F0kLeZ<9JI1o#*}QNIAb)oT8?@$e_BxTZQU-WAD*p#$EEoX z7PLva>|kF0`}mNHwJHhR9waZ`z_s~cXqZ(~S*cy$BU!(H>fqerJ=rydLZ4-nI9v7d zeN&KyUC9LX}Wp3LGXzMvZN2O5fO}X~&av#1*JSYcMO)ado&z$n1#Kg*gY-SNopOza??rW_Ec6VsX4g z%D^vs%{%C$u+5mReEA8%;TBIt$<5o1 zE0DQ^T^Tbo{f3&19g{zzk%%O-h2t{2Lv11%ZG|JzwbL%xJ>@t=)C%nCnVU{72|e76 zVFUnqp=J|&*R`M-Zg$NCH)NDdeLeLe`_e>9Eo0=zCzV)SHq%?g9MX3>F_na)KnTOp2Rq5sJg2yRWk~#O?N;fzF}E-IPvp`~zetoBWvmR_gE??`(h^6xqEWG;7S!XqLJ2X~>7epS_I({)raUxY zMS}ZwR*WcPvkS{-If;m#*uwfr;W&V&zOrb)sY%-h4Hp1=!B<6Vd3O0UPx+OXMwQwQ zLMMue=>yq1ze#KUn>&ECx(G&}#|!8VK6R8Hpol{xpd%q2t*tqba zU};#WX|Tpmw-`lFOZw#uS$IY83f`d38@n%8*CY<0E@QLBtNud?w6_>> zG*9t@o0`5Dnwk!+G>5r3Ed9YjPnY*NKa`#s+pOb33^H+6Q~;gYmZXcii^6Q*Ul+BU zJNntXu6&{UtLhcHm!<{qPbsgr*ke4c%P9NFFxwJ?s7J=&X1TrvvdLOs_n?K_yFVBb zi9OOIlI>C$WaJ`#>w7l|tg!Fbf*h(ub7qt%dJp71DLv{?m$#jif!~BJMMWlC>Jq?g zJ%9?w4BZLsQzrPa>D$a(-4tU$;eNK#@)~CZKUEc$nB2Egbhu%Cyqt8ok^C0KHTcH5KaQEJFVe2Hy4< literal 0 HcmV?d00001 diff --git a/build/icon-32.png b/build/icon-32.png new file mode 100644 index 0000000000000000000000000000000000000000..01d1d477d925faef6ac92722cec1673a96f80db2 GIT binary patch literal 1321 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 literal 0 HcmV?d00001 diff --git a/build/icon-512.png b/build/icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..c5461b2b454b0a5d4ed82a9d3331a61c9ddc4e84 GIT binary patch literal 34515 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 literal 0 HcmV?d00001 diff --git a/build/icon-64.png b/build/icon-64.png new file mode 100644 index 0000000000000000000000000000000000000000..ec64a9e085cd7668e55d676f334fda85845be6b5 GIT binary patch literal 3710 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 literal 0 HcmV?d00001 diff --git a/build/icon.ico b/build/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..be4121f6a215f3814cd8fbf06b8157cff164dc61 GIT binary patch literal 47784 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 X_QFGeYB$*dm`7DnOQBlMD)j#V&TjeI literal 0 HcmV?d00001 diff --git a/build/icon.png b/build/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c5461b2b454b0a5d4ed82a9d3331a61c9ddc4e84 GIT binary patch literal 34515 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 literal 0 HcmV?d00001 diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..b1cb207 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,221 @@ +# Architecture + +## Process model + +```text ++---------------- Electron renderer ----------------+ +| Desktop UI | +| No Node.js, filesystem, process or token access | ++-------------------------+---------------------------+ + | + frozen preload API + | ++-------------------------v---------------------------+ +| Electron main process | +| | +| IPC validation + trusted sender checks | +| ConfigStore -------- schema 5 + protected Gitea/SSH secrets | +| GitService ----------- Git through execFile args | +| GiteaService --------- repositories + Actions API | +| RepositoryService ---- discovery + aggregation | +| RepositoryMonitor ---- local state awareness | +| PreflightService ----- system/deployment readiness | +| DeploymentService ---- dispatch/poll/verify | +| DiagnosticsService --- JSONL/redaction/support ZIP | ++----------------+--------------------+---------------+ + | | + local Git Gitea API + | | + working trees repositories/actions + | + trusted runner + | + fixed allowlisted entry point + | + app + independent status JSON +``` + +## Trust boundaries + +### Renderer + +The renderer is untrusted input. It can request only methods exposed by +`preload.cjs`. Node integration is disabled, context isolation and sandboxing +are enabled, and IPC requests are accepted only from the packaged file origin. +Renderer crashes and unhandled rejections are reported through a sanitized +one-way diagnostic method. + +### Main process + +Paths, URLs, file selections, branch names, commit messages, diagnostic export +modes and deployment requests are checked here. The renderer cannot supply a +server command. UI eligibility is advisory; main-process services re-read the +working tree and remote ancestry immediately before privileged actions. + +### Gitea + +Gitea supplies repository metadata and the Actions control plane. ForgeFlow +handles run-list response variants, retries servers that reject optional query +filters and can fall back to the older tasks listing. Requests log only method, +API path, status and duration—not authorization headers or request bodies. + +### Runner and server + +The runner consumes only committed trusted workflows. The root-owned server +entry point reads an exact repository/environment target from a root-owned, +non-writable data file. It validates the full SHA again and uses a +per-environment lock. The runner receives no free-form command from ForgeFlow. + +## Local state + +`forgeflow-config.json` lives below Electron's platform-specific user-data path +and is written atomically. Schema version 3 contains: + +- Gitea connection metadata and an OS-encrypted token blob where available; +- workspace roots and explicit repository mappings; +- favorites and application preferences; +- diagnostic retention and level preferences; +- multiple deployment profiles and last server state; +- up to 250 operation records. + +Renderer-visible public state never contains the plaintext or encrypted token. + +Structured diagnostic JSONL files live in a separate `diagnostics` directory. +They have independent rotation and retention and never block normal app use when +logging itself fails. + +## Repository aggregation + +1. Fetch accessible Gitea repositories. +2. Scan bounded workspace roots for Git working trees. +3. Normalize HTTPS and SCP-style SSH remotes. +4. Match local `origin` identity to `owner/repository`. +5. Apply explicit mappings where present. +6. Read Git state with bounded concurrency. +7. Attach favorites, deployment profiles and last server state. +8. Derive attention and ready-to-deploy status. +9. Feed linked paths to the repository monitor. + +## Repository clone lifecycle + +The first configured project root is the default. The renderer submits only the +current Gitea repository identity and either `default` or `custom` location +mode. The main process resolves the repository again, selects a configured root +or a native-dialog result, calculates the repository-named child path and asks +GitService to inspect it. + +```text +repository identity + | +current Gitea metadata + | +project root + safe repository folder name + | +missing / empty / matching checkout / conflict + | +clone or reuse -> save mapping -> refresh -> monitor +``` + +A matching existing checkout is reused. Different repositories and arbitrary +non-empty folders are rejected. + +## Git execution + +ForgeFlow invokes the installed Git executable through `execFile`; it never +builds shell command strings. File arguments must remain repository-relative +and cannot contain traversal segments. + +Core status command: + +```bash +git status --porcelain=v2 --branch -z --untracked-files=all +``` + +Synchronization is intentionally limited to: + +```bash +git pull --ff-only +``` + +Branch switching requires a clean working tree. Stash supports untracked files. +Deployment and rollback verify the full SHA against `origin/` +with `merge-base --is-ancestor`. + +## Preflight model + +### System preflight + +Checks Git, author identity, app storage, diagnostic storage, OS credential +protection, configured workspace roots and—when credentials are present—Gitea +connectivity and repository visibility. + +### Deployment preflight + +Checks repository link, Git working tree, allowed branch, clean state, upstream, +ahead/behind state, exact remote SHA, local and remote workflow presence, +Actions API access, server status endpoint and healthcheck. + +Only failed required checks block readiness. The deployment backend repeats +safety-critical Git/SHA validation after the user continues. + +## Repository monitor + +The current monitor periodically fingerprints Git state. It establishes a +baseline, reports later changes and pauses during mutating operations to avoid +intermediate noise. It is dependency-free rather than a native filesystem +watcher. + +## Deployment lifecycle + +```text +requested -> queued -> running -> health/version verification -> terminal +``` + +A deployment operation stores the exact SHA, fixed profile, environment, +workflow and a UUID request ID. Polling then: + +1. finds the matching Actions run by SHA, branch, workflow and dispatch time; +2. normalizes run status; +3. retrieves jobs and locally redacted runner output; +4. maps jobs to ForgeFlow stages; +5. reads the independent status endpoint and healthcheck after runner success; +6. verifies live SHA equality; +7. stores success, rolled-back, failed or cancelled. + +The request ID is sent to the workflow and server status document, making one +operation correlatable without using a credential as an identifier. + +## Diagnostics pipeline + +```text +event -> recursive sanitization -> ordered JSONL write + | + support export requested + | + fresh state + preflight + redacted logs + | + strict/standard privacy transformation + | + fail-closed local secret safety audit + | + ZIP + SHA-256 result +``` + +Raw runner output is deliberately omitted from exported support bundles. + +## Status endpoint + +The recommended endpoint is a static JSON file served independently from the +application. It reports live, previous and requested SHAs, request ID, health +and last exit code. See `STATUS_ENDPOINT.md`. + + +## v0.4 services + +- `UpdateService` reads `package.json` at an exact Gitea branch SHA, downloads an + authenticated archive and launches the rollback-capable Windows source updater. +- `SshService` provides pinned-host SSH execution with encrypted password or + private-key passphrase storage. +- `UnraidDeploymentService` inspects existing application folders and performs + exact-SHA Git and Docker Compose deployments without deleting untracked + runtime data. diff --git a/docs/DEPLOYMENT_SETUP.md b/docs/DEPLOYMENT_SETUP.md new file mode 100644 index 0000000..0f572aa --- /dev/null +++ b/docs/DEPLOYMENT_SETUP.md @@ -0,0 +1,58 @@ +# Deployment setup guide + +This guide connects one Gitea repository to one server environment without giving the desktop arbitrary shell access. + +## 1. Add fixed workflows + +Copy these examples into the repository: + +```text +examples/gitea-actions/deploy.yml -> .gitea/workflows/deploy.yml +examples/gitea-actions/rollback.yml -> .gitea/workflows/rollback.yml +``` + +Change the runner label to the label registered for the target environment. + +## 2. Install the allowlisted server entry point + +Copy `examples/server/forgeflow-deploy` to `/usr/local/bin/forgeflow-deploy`, customize its repository/environment allowlist and make it root-owned: + +```bash +sudo install -o root -g root -m 0755 forgeflow-deploy /usr/local/bin/forgeflow-deploy +``` + +Grant the runner account permission to execute only this entry point where elevation is needed. Do not grant unrestricted shell or Docker administration merely for ForgeFlow. + +## 3. Expose deployment status + +The example script writes an atomic JSON document beneath `/var/lib/forgeflow-status`. Serve the appropriate file at a fixed HTTPS URL, for example with `examples/server/nginx-forgeflow-status.conf`. + +See `STATUS_ENDPOINT.md` for the contract. + +## 4. Configure the ForgeFlow profile + +Open the repository, choose **Deployments** and add an environment with: + +- Name: `Production` or `Staging`. +- Environment: the fixed workflow input. +- Branch: usually `main`. +- Workflow file: `deploy.yml`. +- Rollback workflow: `rollback.yml`. +- Status URL: the JSON endpoint. +- Healthcheck URL: the application health endpoint. +- Confirmation: enabled for production. + +## 5. Validate the complete path + +Test these scenarios before relying on production: + +1. Clean commit and push. +2. Successful deployment to the exact SHA. +3. Gitea runner failure. +4. Application healthcheck failure. +5. Server reports the wrong SHA. +6. Second deployment while the lock is held. +7. Rollback to the recorded previous SHA. +8. Token without sufficient permissions. + +Keep a manual recovery path documented even after rollback works. diff --git a/docs/DIAGNOSTICS.md b/docs/DIAGNOSTICS.md new file mode 100644 index 0000000..7ddf26f --- /dev/null +++ b/docs/DIAGNOSTICS.md @@ -0,0 +1,150 @@ +# Diagnostics, privacy and support bundles + +ForgeFlow v0.3.2 records development-oriented diagnostics locally so failures +can be investigated without requesting the user's Gitea token, SSH key or +server password. + +## Storage + +Diagnostic events are stored beneath the Electron application-data directory in: + +```text +diagnostics/forgeflow-YYYY-MM-DD.jsonl +``` + +The Diagnostics page displays an aliased path such as `` rather than the +Windows account name. Files use restrictive permissions where the operating +system supports them. + +Defaults: + +- enabled; +- minimum level `info`; +- 14-day retention; +- 8 MB maximum per log segment; +- daily filenames with numbered rotation; +- ordered asynchronous writes; +- no application crash when diagnostic storage itself fails. + +These values can be changed in **Diagnostics -> Recording policy**. + +## What an event can contain + +Useful fields include: + +- UTC timestamp; +- level and stable event name; +- per-launch session identifier; +- operation or deployment request identifier; +- Git/Gitea operation outcome; +- HTTP status, duration and endpoint path without request headers; +- repository state and branch/SHA metadata; +- preflight result; +- sanitized exception name, code, message and stack; +- renderer crash or unhandled-rejection metadata. + +ForgeFlow does not log IPC payloads, request authorization headers or Gitea +response bodies merely because a request was made. + +## Redaction + +Every event passes through a recursive sanitizer before it is written. +Redaction covers: + +- the currently active Gitea token; +- token, password, authorization, credential, API-key, client-secret and + encrypted-token object fields, including camelCase variants; +- bearer/token/basic authorization values in strings; +- common token query parameters; +- credentials embedded in URLs; +- PEM private-key blocks; +- known Gitea/Git hosting token patterns; +- Windows, macOS and Linux home-directory paths; +- application source path aliases; +- circular data structures and oversized strings. + +Credential values are replaced with `[REDACTED]`; user paths use aliases such as +``. + +## Support bundle + +**Create diagnostic ZIP** exports a local archive containing: + +```text +manifest.json +safety-audit.json +README.txt +system.json +diagnostics-status.json +configuration-sanitized.json +repositories-sanitized.json +operations-sanitized.json +preflight.json +context.json +logs/*.jsonl +``` + +The application returns the SHA-256 of the generated archive so a shared file +can be identified exactly. + +### Excluded data + +The bundle intentionally excludes: + +- plaintext Gitea access tokens; +- Electron `safeStorage` encrypted-token blobs; +- request authorization headers; +- passwords and private keys; +- raw Gitea runner logs; +- arbitrary environment-variable dumps; +- full local file contents and Git diffs. + +ForgeFlow does not automatically ingest or persist raw runner output. Job names, +statuses and safe operation summaries are stored locally; full runner output remains +available only in the trusted Gitea Actions interface when deeper server-side +investigation is necessary. + +## Privacy modes + +### Standard + +Preserves repository names and user-facing identifiers. Local home paths and +credentials are still redacted. Use when the recipient already knows the +project context. + +### Strict + +Additionally replaces repository and user identifiers with deterministic +SHA-256-based aliases. Related events remain correlatable without revealing the +original names. + +## Fail-closed bundle audit + +Immediately before writing the archive, ForgeFlow scans every prepared entry +for: + +- the known active runtime secret values; +- private-key begin markers; +- unredacted credentials embedded in HTTP(S) URLs. + +The result is stored as `safety-audit.json`. When a finding remains, archive +creation is aborted and the unsafe ZIP is not written. + +## Practical limitation + +No generic logger can mathematically identify every unknown secret if a third- +party process prints an arbitrary value without a label or recognizable format. +ForgeFlow reduces this risk by not including raw runner logs, not recording IPC +payloads and applying both structured and textual redaction. Always inspect a +support bundle before sharing it, particularly when custom integrations have +been added. + +## Development workflow after a failure + +1. Reproduce the issue once when safe. +2. Note the approximate time and repository/environment. +3. Run the relevant preflight. +4. Export a Strict diagnostic bundle. +5. Keep the returned SHA-256 with the bug report. +6. Describe the visible action that failed. +7. Share no separate token, key or password. diff --git a/docs/LUMAOPS_SERVER_AUDIT.md b/docs/LUMAOPS_SERVER_AUDIT.md new file mode 100644 index 0000000..81b563d --- /dev/null +++ b/docs/LUMAOPS_SERVER_AUDIT.md @@ -0,0 +1,147 @@ +# LumaOps server versus Gitea audit + +This audit compares the supplied `lumaops_server.zip` and `LumaOps_gitea.zip`. + +## Main result + +The main Unraid working tree and the supplied Gitea checkout point to exactly the same commit: + +```text +d42d4a7f08240c478d07466e3fabec654dc71367 +``` + +Latest subject: + +```text +Preserve colors across Aura zone updates +``` + +There is therefore no source-version drift at the root of the live LumaOps folder. + +## Root Git repository + +The root `.git` directory should remain in place. It enables: + +- exact-SHA verification; +- controlled fetch and reset; +- a reliable previous-version reference; +- rollback without copying a second complete source tree. + +The archived server copy showed one root status difference for `scripts/unraid-hardware-setup.sh`: file mode `100755 → 100644`. This is consistent with Unix executable bits being lost during ZIP handling. The file content did not differ. Check the executable bit directly on Unraid before deployment. + + +## Origin URL mismatch to resolve + +The supplied server root uses: + +```text +ssh://git@127.0.0.1:222/NuklearRabbit/LumaOps.git +``` + +The supplied Gitea checkout uses: + +```text +https://gitea.itworx.tech/Jens/LumaOps.git +``` + +Although both archives currently point to the same commit, these are different +repository paths. Before the first ForgeFlow deployment, choose the server- +reachable URL for the authoritative `Jens/LumaOps` repository, for example an +SSH URL through `127.0.0.1:222` when Gitea runs on the same Unraid host. + +ForgeFlow 0.4 detects this mismatch. Deployment is blocked unless the profile +matches the existing origin or **Align an existing server origin to this URL** +is explicitly enabled. + +## Runtime and persistent paths + +The server copy contains runtime data that must not be replaced by source updates: + +- `appdata/` +- `data/` +- `logs/` +- `.env` and application-specific configuration + +The repository `.gitignore` already excludes the principal runtime paths. ForgeFlow's SSH strategy uses Git reset without `git clean`, so untracked persistent data remains in place. + +## Compose and Unraid integration + +The root `docker-compose.yml` is already suitable as the authoritative deployment definition. It includes: + +- build context at the project root; +- container name `lumaops`; +- the Unraid `dockerman` label; +- a Web UI label; +- an Unraid icon label; +- `${WEB_PORT:-1223}:${APP_PORT:-8080}`; +- persistent relative volumes; +- USB, HID and I²C devices; +- a healthcheck. + +ForgeFlow should use this existing Compose file rather than generate a replacement. Ports and complex device mappings belong in the repository's maintained Compose definition. + +The supplied `.dockerignore` already excludes `.git`, so keeping the root Git +working tree does **not** copy Git history into the Docker build context. It does +not yet explicitly exclude the existing runtime/legacy folders `appdata/`, +`data/`, `logs/` and `source/`. Before the first production rebuild, add the +paths that are not build inputs: + +```text +appdata/ +data/ +logs/ +source/ +.forgeflow/ +``` + +ForgeFlow 0.4 detects existing preserved paths and nested Git repositories that +are missing from `.dockerignore` and reports them as a preflight warning. The +tool does not silently edit a source-controlled `.dockerignore`; the correction +should be committed to Gitea so every deployment uses the same build context. + +## Nested `source/` repository + +The server archive also contains a nested Git working tree under: + +```text +source/ +``` + +Its HEAD is: + +```text +b746a52af1613f4291235f5e8165b8197a269a79 +``` + +It was ahead of its own upstream and included rebase metadata in the supplied archive. The root Compose file uses build context `.` and does not reference `source/`. This strongly indicates that `source/` is an abandoned or historical checkout rather than the active deployment source. + +ForgeFlow reports this as a nested-repository warning and does not remove it automatically. + +Recommended migration: + +1. Back up `/mnt/user/appdata/lumaops`. +2. Verify on Unraid that `docker compose config` uses the root project. +3. Stop changing files in `source/`. +4. Rename it temporarily to `source.legacy-backup`. +5. Rebuild and test LumaOps from the root. +6. Remove the legacy copy only after a successful validation period. + +Do not delete the root `.git` directory. Also do not delete the nested `source/` +directory as part of the first ForgeFlow test. Treat its cleanup as a separate, +backed-up migration after the root deployment and rollback have both been +validated. + +## Recommended ForgeFlow profile + +```text +Provider: SSH / Unraid +Server folder: lumaops +Branch: main +Compose mode: Repository/server Compose +Compose file: docker-compose.yml +Clone URL: the Git URL reachable from Unraid +Healthcheck: the existing LumaOps health URL, when exposed +Preserve paths: .env, appdata, data, logs, config, compose.override.yml +``` + +No folder rename is required for LumaOps because `lumaops` already aligns with the repository name. diff --git a/docs/RELEASE_NOTES_0.2.0.md b/docs/RELEASE_NOTES_0.2.0.md new file mode 100644 index 0000000..5cade5a --- /dev/null +++ b/docs/RELEASE_NOTES_0.2.0.md @@ -0,0 +1,32 @@ +# ForgeFlow 0.2.0 release notes + +ForgeFlow 0.2.0 turns the original visual prototype into a substantially more operational personal release cockpit. + +## Highlights + +- Automatic repository status monitoring with safe pause/resume around Git mutations. +- Commit-only and commit-and-push flows with recoverable push failures. +- Branch creation, switching, publication and stash workflows. +- Favorite repositories and action-oriented attention queues. +- Multiple deployment environments per repository. +- Exact remote-branch SHA verification before deploy and rollback. +- Gitea Actions run, job and available log polling. +- Live server version, previous version and health verification. +- Fixed-workflow rollback to a recorded full commit SHA. +- Stronger IPC, URL, path and secret-handling controls. +- Reworked renderer with command palette and live deployment states. +- 21 passing automated tests, including real temporary Git remotes. +- Headless browser smoke coverage at three desktop viewport sizes. + +## Upgrade notes + +Configuration is migrated automatically to schema version 2. Existing Gitea tokens are preserved when the settings form is saved with an empty token field. + +Deployment profiles now support independent branch, workflow, rollback workflow, healthcheck and status endpoint settings. Review existing profiles before using them against production. + +## Known limitations + +- No signed installer or automatic update channel is included in this source release. +- Partial-hunk staging, conflict resolution and protected-branch awareness are not yet implemented. +- Gitea Actions behavior still needs acceptance testing against the intended Gitea and act_runner versions. +- Native desktop notifications, tray mode and accessibility acceptance remain future work. diff --git a/docs/RELEASE_NOTES_0.3.0.md b/docs/RELEASE_NOTES_0.3.0.md new file mode 100644 index 0000000..671a267 --- /dev/null +++ b/docs/RELEASE_NOTES_0.3.0.md @@ -0,0 +1,140 @@ +# ForgeFlow 0.3.0 release notes + +Release date: 2026-07-24 +Release type: self-service test release + +## Goal + +Version 0.3.0 closes the gap between a functional developer preview and a build +that can be configured and tested by its owner without sharing credentials with +a developer. The release concentrates on setup guidance, deterministic +preflight checks, safe diagnostics and server-side allowlisting. + +## Setup and readiness + +- Replaced the lightweight onboarding with a five-step setup wizard. +- Added a computer readiness preflight for Git, Git identity, writable app data, + writable diagnostics and OS credential encryption. +- Added a Gitea validation stage before setup completion. +- Added visible repository discovery results. +- Added safe setup diagnostics before Gitea is connected. +- Added a comprehensive start page and end-to-end setup guide. +- Added a JSON-capable command-line doctor for local environment validation. + +## Deployment preflight + +A deployment now receives a visible preflight before confirmation and a second +mandatory backend validation immediately before dispatch. Checks include: + +- linked local Git repository; +- allowed deployment branch; +- clean working tree; +- configured upstream; +- local/remote ahead and behind state; +- exact full SHA on the remote branch; +- local deploy and rollback workflow files; +- remote workflow visibility through Gitea; +- Gitea Actions API availability; +- deployment status endpoint; +- application health endpoint. + +Optional environment checks can warn without hiding required failures. +Deployment cannot bypass the mandatory checks through the renderer. + +## Diagnostic logging + +- Added ordered structured JSONL logging in the Electron app-data directory. +- Added daily files, size rotation and retention pruning. +- Added configurable logging level, retention and file-size policy. +- Added process, renderer, Git, repository, Gitea, IPC, preflight and deployment + diagnostics. +- Added per-launch session IDs and per-operation deployment request IDs. +- Added a no-throw logging design so diagnostic storage does not crash the app. +- Added local clear and open-folder controls. + +## Secret and privacy protection + +- Added recursive sensitive-key detection, including camelCase variants. +- Added bearer/basic/token/password/API-key/client-secret redaction. +- Added runtime-secret replacement. +- Added URL credential, token query parameter and private-key redaction. +- Added common hosting-token pattern redaction. +- Added user-home and source-root path aliases. +- Added strict privacy mode with deterministic identifier hashing. +- Stopped automatically ingesting or persisting raw runner logs; full output stays in Gitea. +- Added fail-closed bundle auditing before the ZIP is written. +- Added SHA-256 output for every generated support bundle. + +No Gitea token, SSH key or server password is needed by the developer to use +these diagnostics. + +## Support bundle contents + +A support bundle can contain: + +- manifest and safety audit; +- system and application version information; +- sanitized public configuration; +- sanitized repository state; +- sanitized operation history; +- latest preflight report; +- safe diagnostic status; +- redacted JSONL logs. + +It intentionally excludes protected token blobs, authorization headers, +private keys, source files, Git diffs, environment dumps and raw runner output. + +## Server deployment hardening + +- Moved target definitions to a root-owned `/etc/forgeflow/targets.conf` file. +- Added exact repository/environment allowlisting. +- Made the server status URL mandatory and require matching SHA plus request ID before success. +- Added configuration ownership and permission checks. +- Added absolute and restricted path validation. +- Added exact remote-SHA and branch ancestry validation. +- Added per-target `flock` locking. +- Added Docker Compose result and health verification. +- Added current, previous, requested SHA, request ID and exit code to server + status output. +- Added a restrictive sudoers template for the runner. +- Added explicit deploy and rollback workflow request-ID inputs. +- Added backend repository re-resolution so renderer-supplied paths and identities + cannot select an arbitrary local folder or Gitea repository. +- Captured pre-dispatch Actions run IDs so polling cannot attach to an older run + with the same commit SHA. +- Required repository, environment, live SHA, requested SHA, request ID, zero + server exit code and explicit health success before marking a release complete. +- Restricted rollback to the exact previous SHA currently reported by the server + status endpoint. + +## User interface + +- Added a dedicated Diagnostics workspace. +- Added system and deployment preflight presentation. +- Added diagnostic policy controls. +- Added Standard and Strict support-bundle export. +- Added support-bundle checksum and reveal action. +- Added readiness explanations to onboarding. +- Replaced duplicate sidebar navigation with a compact safe-diagnostics state. + +## Validation + +- 39 required project files validated. +- 35 JavaScript files passed syntax checks. +- 36 of 36 automated tests passed. +- Two real temporary Git remotes remain part of the integration suite. +- New tests cover redaction, diagnostic rotation/export, support ZIP generation, + fail-closed safety auditing, preflight and Gitea workflow-file checks. + +## Known boundaries + +- The release is not code-signed. +- A platform-native installer is not guaranteed by the source ZIP alone. +- The private Gitea, runner and server environment still requires the documented + local acceptance test. +- Application-specific compose commands and health endpoints remain target + configuration, because they cannot be inferred safely. +- No redactor can mathematically identify an arbitrary unknown secret printed by + custom third-party code; raw runner logs therefore remain only in the trusted + Gitea Actions interface, and exported bundles should still be inspected before + sharing. diff --git a/docs/RELEASE_NOTES_0.3.1.md b/docs/RELEASE_NOTES_0.3.1.md new file mode 100644 index 0000000..e6c1dd6 --- /dev/null +++ b/docs/RELEASE_NOTES_0.3.1.md @@ -0,0 +1,25 @@ +# ForgeFlow 0.3.1 release notes + +## Windows environment-doctor hotfix + +Version 0.3.1 fixes a Windows-only false negative in the environment doctor. +The setup script could invoke npm successfully, install all dependencies and then +report `spawn npm ENOENT` from Node.js. Windows exposes npm through a command +shim (`npm.cmd`), which cannot always be executed directly through +`child_process.execFile`. + +The doctor now: + +- uses `npm_execpath` through the active Node executable when launched by npm; +- falls back to `cmd.exe /c npm --version` on Windows; +- continues to invoke npm directly on Linux and macOS; +- reports which safe invocation path succeeded; +- reads its displayed application version from `package.json` instead of a + duplicated hard-coded value. + +Three regression tests cover npm-script execution, the Windows command-shim +fallback and the normal non-Windows path. + +The npm deprecation messages printed during dependency installation are warnings +from transitive build-tool dependencies. They were not the cause of the setup +failure and do not prevent ForgeFlow from starting. diff --git a/docs/RELEASE_NOTES_0.3.2.md b/docs/RELEASE_NOTES_0.3.2.md new file mode 100644 index 0000000..2f93f8b --- /dev/null +++ b/docs/RELEASE_NOTES_0.3.2.md @@ -0,0 +1,72 @@ +# ForgeFlow 0.3.2 release notes + +## Automatic clone destinations + +The normal **Clone from Gitea** action no longer opens a Windows folder picker +for every repository. ForgeFlow now: + +1. uses the first configured project root as the default; +2. derives a safe folder name from the repository clone URL; +3. creates `/`; +4. clones into that folder; +5. validates the resulting Git repository; +6. saves the repository mapping; +7. starts monitoring the working tree; +8. opens the linked repository in ForgeFlow. + +Example: + +```text +Default project root: C:\Users\Jens\Projects +Gitea repository: Jens/Portfolio +Automatic target: C:\Users\Jens\Projects\Portfolio +``` + +A separate **Choose another location** action remains available for exceptional +cases. That choice selects a parent project root; ForgeFlow still creates the +repository-named subfolder itself. + +## Existing-folder safety + +The clone backend now inspects the automatic target before running Git: + +- a missing target is created through `git clone`; +- an existing empty directory is accepted; +- an existing Git checkout with the same normalized origin is linked; +- a different Git repository is blocked; +- an ordinary non-empty directory is blocked; +- a file at the target path is blocked. + +ForgeFlow never silently overwrites a conflicting folder and does not create a +duplicated `Repository\Repository` directory. + +## Security and consistency + +The renderer no longer supplies a free-form remote URL or clone destination to +the privileged Git operation. It sends the Gitea repository identity and a +location mode. The main process then: + +- resolves the current repository again from Gitea; +- selects the configured root or a native-dialog result; +- calculates the destination itself; +- performs conflict checks; +- clones or reuses the checkout; +- persists the mapping atomically. + +Clone diagnostics contain repository identity, target, branch and commit state, +but no Gitea token or authorization header. + +## Validation + +Version 0.3.2 contains 45 passing automated tests. New coverage includes: + +- HTTPS and SSH repository folder-name derivation; +- automatic target construction; +- missing and empty target handling; +- same-origin checkout reuse; +- different-repository rejection; +- non-empty ordinary-folder rejection; +- file-at-target rejection. + +Existing Git, deployment, diagnostics, redaction, rollback and Windows doctor +tests continue to pass. diff --git a/docs/RELEASE_NOTES_0.4.0.md b/docs/RELEASE_NOTES_0.4.0.md new file mode 100644 index 0000000..38d320d --- /dev/null +++ b/docs/RELEASE_NOTES_0.4.0.md @@ -0,0 +1,57 @@ +# ForgeFlow 0.4.0 release notes + +## Scrollable change list + +The changed-file panel now has an independent bounded vertical scroll area. Large commits no longer make lower files unreachable. + +## Commit readiness + +The action panel now labels the commit message as required and displays the exact reason why commit actions are disabled. Selected files are staged automatically during commit; manual staging remains available as an optional index-review step. + +## ITWorx.tech branding + +The supplied ITWorx.tech logo is integrated into the title bar, first-run setup and application icons. + +## Built-in source updater + +ForgeFlow can check the configured private Gitea repository, defaulting to `Jens/ForgeFlow` on `main`. + +The updater: + +- reads the remote `package.json` at an exact branch commit; +- compares semantic versions; +- downloads an authenticated exact-SHA archive; +- verifies a SHA-256 checksum; +- closes ForgeFlow; +- backs up the current source; +- installs dependencies; +- runs the complete quality gate; +- restores the previous source when validation fails; +- restarts ForgeFlow. + +## SSH / Unraid deployment + +A server can be configured once with hostname, SSH port, username, encrypted credentials and `/mnt/user/appdata` as base path. + +Deployment profiles support: + +- existing Git-backed application folders; +- automatic new folder creation; +- exact commit verification; +- pinned SSH host identity; +- existing or generated Compose configuration; +- host and container ports; +- Unraid Web UI and icon labels; +- server-folder mapping; +- tracked-change blocking; +- nested-Git warnings; +- runtime-data preservation; +- rollback to the previous SHA. + +## LumaOps audit + +The supplied server and Gitea roots both match commit `d42d4a7f08240c478d07466e3fabec654dc71367`. The root Git checkout and Compose file should remain. A stale nested `source/` Git checkout is documented for controlled cleanup. + +## Validation + +ForgeFlow 0.4.0 has 59 passing automated tests, including real temporary Git remotes, update exact-SHA checks, SSH path safety, renderer workflow contracts, diagnostics redaction, exact previous-SHA rollback enforcement and deployment controls. diff --git a/docs/RELEASE_NOTES_0.4.1.md b/docs/RELEASE_NOTES_0.4.1.md new file mode 100644 index 0000000..bade9eb --- /dev/null +++ b/docs/RELEASE_NOTES_0.4.1.md @@ -0,0 +1,26 @@ +# ForgeFlow 0.4.1 release notes + +## Windows Bash path fix + +ForgeFlow 0.4.1 fixes the source quality gate on Windows when the project is stored at a path such as `C:\Projects\ForgeFlow`. + +The previous verifier passed an absolute Windows path directly to `bash -n`. Bash interpreted the backslashes as escape characters, producing a collapsed path such as `C:ProjectsForgeFlow...` and a false validation failure. + +The verifier now starts Bash with the ForgeFlow project root as its working directory and passes the deployment example as a relative POSIX path: + +```text +examples/server/forgeflow-deploy +``` + +This keeps the project root separate from the script argument and works across Windows Git Bash, Linux and macOS. + +## Regression coverage + +New automated coverage verifies that: + +- a Windows project root remains in `cwd`; +- no drive letter or backslash is passed as the Bash script argument; +- absolute and escaping script paths are rejected; +- Bash validation succeeds from a project root containing spaces. + +No Gitea token, repository mapping, SSH credential, deployment profile or diagnostic history is changed by this update. diff --git a/docs/RELEASE_NOTES_0.4.2.md b/docs/RELEASE_NOTES_0.4.2.md new file mode 100644 index 0000000..a9d0cc7 --- /dev/null +++ b/docs/RELEASE_NOTES_0.4.2.md @@ -0,0 +1,29 @@ +# ForgeFlow 0.4.2 + +## Windows Git Bash reliability hotfix + +This release fixes the two remaining Windows-only failures, including the temporary-directory lock seen during cleanup while validating the 0.4.1 recovery update. + +### Remote shell transport + +SSH / Unraid scripts are now sent through a single-line base64 transport and decoded by Bash on the server. This removes nested quote parsing from the transport layer and prevents Git Bash from misreading multiline commands, single quotes, or newline-stripping expressions. + +The generated command contains no raw multiline payload. The decoded script still enables strict shell mode, disables interactive Git prompts, and requires batch-mode SSH for server-side Git operations. + +### Temporary-directory lock cleanup + +The Bash syntax regression test now retries cleanup when Windows briefly retains a working-directory handle after `bash -n` exits. A successful syntax validation is no longer reported as failed solely because of a short-lived `EBUSY`, `EPERM`, or `ENOTEMPTY` cleanup condition. + +### Additional correction + +The remote status reader now invokes `base64` with the status filename in the correct argument position before stripping CR/LF characters. + +## Regression coverage + +Coverage verifies: + +- Windows Git Bash execution from a project root containing spaces; +- a single-line base64 transport for generated Unraid inspection commands; +- preserved runtime-path inspection without nested quoting failures; +- strict, non-interactive server-side Git settings after decoding; +- safe rollback to ForgeFlow 0.4.0 when an update validation fails. diff --git a/docs/RELEASE_NOTES_0.4.3.md b/docs/RELEASE_NOTES_0.4.3.md new file mode 100644 index 0000000..dd92189 --- /dev/null +++ b/docs/RELEASE_NOTES_0.4.3.md @@ -0,0 +1,9 @@ +# ForgeFlow 0.4.3 + +## Full clean release + +- Replaced the OS-dependent local Bash/Windows-temp-path Unraid inspection test with a platform-independent mocked SSH inspection contract. +- The SSH inspection command is still verified to use Base64 transport and the returned Unraid metadata is parsed and evaluated deterministically. +- Removed the false Windows failure where a local temporary path was interpreted as a remote Linux path. +- Regression coverage confirms preserved runtime paths, nested Git directories, `.dockerignore` handling, and remote target resolution. +- This release is distributed as a complete source package rather than another incremental updater. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md new file mode 100644 index 0000000..9e9d3f9 --- /dev/null +++ b/docs/ROADMAP.md @@ -0,0 +1,95 @@ +# ForgeFlow roadmap + +## Delivered through v0.4 + +- coherent Local -> Gitea -> Server desktop model; +- protected Gitea credential storage and strict IPC boundary; +- real Git status, diff, stage, commit, push, fetch and fast-forward pull; +- branches, stashes, favorites and automatic local awareness; +- multiple Gitea Actions and SSH / Unraid deployment profiles; +- exact-SHA remote-branch validation, runner polling, request-ID verification, health and rollback; +- five-step readiness/setup wizard; +- system and deployment preflight engine; +- structured rotating diagnostic JSONL logs; +- aggressive credential/path redaction; +- standard/strict support bundles with SHA-256 and fail-closed safety audit; +- no automatic ingestion or persistence of raw runner logs; +- root-owned declarative server target configuration; +- cross-layer request-ID correlation; +- canonical end-to-end setup guide; +- 36 automated tests. + +The source is now intended to be locally configured and testable without +sharing credentials. It remains a developer preview until a real environment +acceptance pass is completed. + +## Milestone A — Real personal acceptance + +- run the canonical setup guide on the target Windows machine; +- connect the actual Gitea instance locally; +- use one non-critical staging repository; +- register a narrowly scoped trusted runner; +- install the target configuration, entry point and status endpoint; +- pass Deployment preflight; +- validate commit -> push -> deploy -> status -> health -> rollback; +- deliberately test stopped runner, wrong branch, missing workflow, failed + health and lock contention; +- export/inspect a strict diagnostic bundle from a failed test; +- capture only non-secret environment-specific adjustments in documentation. + +Exit: one real application can be released and restored without code changes to +ForgeFlow itself. + +## Milestone B — Git completeness + +- partial-hunk staging/discard; +- amend and signing checks; +- richer branch publication/upstream controls; +- conflict helper and editor integration; +- protected-branch awareness; +- pull-request creation; +- submodule/worktree policy. + +## Milestone C — Desktop operations + +- native notifications and system tray; +- background start preference; +- notification center; +- native menus and expanded keyboard navigation; +- configurable editor/terminal commands; +- repository attention rules and snoozing; +- safer periodic remote fetch scheduling. + +## Milestone D — Recovery and audit + +- append-only audit export distinct from diagnostics; +- deployment notes and release annotations; +- explicit reconciliation of externally deployed versions; +- per-environment recovery runbook links; +- encrypted configuration backup/restore without token export; +- deployment freeze and maintenance-window policies. + +## Milestone E — Additional controlled adapters + +- mutually authenticated ForgeFlow server agent; +- Portainer stack deployment; +- systemd adapter; +- Kubernetes adapter. + +Every adapter must retain exact version identity, allowlisting, lock control, +health verification, diagnostic correlation and no arbitrary shell input. + +## Milestone F — Productization + +- Windows installer/portable acceptance; +- macOS/Linux package validation; +- code signing, notarization and signed updates; +- dependency/secret/package scans; +- accessibility review; +- hundreds-of-repositories performance tests; +- opt-in privacy-aware crash reporting; +- documented Gitea/Git/runner support matrix; +- stable configuration migration rollback policy. + +- built-in private-Gitea source updater with backup and rollback; +- Unraid server inventory, generated basic Compose and exact-SHA SSH deployment. diff --git a/docs/SECURITY.md b/docs/SECURITY.md new file mode 100644 index 0000000..65ae0f7 --- /dev/null +++ b/docs/SECURITY.md @@ -0,0 +1,132 @@ +# Security model + +ForgeFlow bridges developer credentials, local source trees and production +release controls. The design favors constrained operations over arbitrary +flexibility. + +## Desktop boundary + +- `nodeIntegration: false`; +- `contextIsolation: true`; +- renderer sandbox enabled; +- Content Security Policy limited to packaged resources; +- narrow frozen preload API; +- IPC rejected unless it originates from the packaged file renderer; +- external navigation restricted to HTTP(S); +- renderer errors reported through sanitized diagnostic IPC; +- support-bundle reveal restricted to the last archive created by the main + process. + +## Credentials and persistence + +- Gitea token encrypted through Electron `safeStorage` where available; +- session-only fallback when OS encryption is unavailable; +- token omitted from renderer-visible public state; +- encrypted token blob excluded from diagnostic bundles; +- blank settings token field preserves the existing token; +- atomic config replacement and restrictive permissions where supported; +- service URLs reject embedded user credentials; +- no token is required by setup/build scripts or documentation. + +## Git operations + +- Git executed through `execFile` argument arrays, never shell interpolation; +- local repository and Git root verified; +- file actions accept only repository-relative paths; +- absolute paths, traversal and NUL characters rejected; +- clone remotes restricted to supported Git protocols; embedded passwords + rejected; +- renderer supplies repository identity rather than a free-form remote URL; +- automatic clone targets are calculated in the main process below a selected + project root; +- matching existing origins may be linked, while different repositories and + non-empty ordinary folders are blocked; +- branch names validated by Git; +- fast-forward-only pull; +- branch switching/creation require a clean tree; +- selected-commit flow refuses hidden staged files outside the selection; +- monitor pauses around mutating actions. + +## Deployment + +- fixed workflow filenames, branch and environment; +- full 40–64 character SHA required; +- local state re-read immediately before dispatch; +- clean, published and synchronized branch required; +- deployment SHA must equal local `HEAD`; +- deploy and rollback SHA must belong to the allowed remote branch; +- no free-form server commands over IPC or workflow inputs; +- mandatory deployment preflight in the normal UI flow; +- backend validation repeated after preflight; +- exact target displayed in confirmation; +- independent health and live-SHA checks; +- rollback uses a separate fixed workflow and previous full SHA; +- UUID request ID correlates desktop, workflow and server state. + +## Diagnostics and redaction + +- structured events are sanitized before writing; +- IPC payloads and HTTP authorization headers are not logged; +- sensitive object keys, including camelCase, are removed; +- known active tokens, authorization strings, query tokens, URL passwords, + private-key blocks and common token formats are redacted; +- home/source paths are aliased; +- strings and collections are bounded; +- logs rotate by day/size and expire by retention policy; +- support bundles offer deterministic strict-privacy aliases; +- raw runner logs, diffs and source file contents are omitted from bundles; +- bundle creation performs a final fail-closed scan for known secrets, + private-key markers and URL credentials; +- bundle SHA-256 is displayed for exact identification. + +No generic detector can identify a completely unknown arbitrary secret printed +without context by third-party code. ForgeFlow minimizes that residual risk by +not exporting raw runner output and by requiring user inspection before sharing. + +## Runner boundary + +Use a production-capable runner only for repositories you trust. Give it a +label unique to the intended environment and the narrowest repository or +organization scope. + +Avoid exposing a host Docker socket to untrusted jobs. Treat a runner capable of +host deployment as privileged infrastructure. + +## Server entry point + +The runner account should not receive unrestricted sudo or SSH access. The +included model uses: + +- root-owned `/usr/local/bin/forgeflow-deploy`; +- root-owned `/etc/forgeflow/targets.conf` without group/other write access; +- a sudoers rule for that exact executable only; +- exact repository/environment matching; +- absolute-path and branch validation; +- full-SHA remote ancestry proof; +- per-target `flock` lock; +- fixed Compose and healthcheck configuration; +- atomic non-secret status JSON; +- previous-SHA recording and non-zero failure exits. + +## Remaining release hardening + +- code-sign packages and signed updates; +- validate private CA/TLS behavior in the target network; +- dependency, secret and binary scans in CI; +- package-level IPC/navigation regression tests; +- OS-specific credential storage and installer acceptance; +- rate/approval policies for team use; +- threat-model every future deployment adapter separately. + + +## SSH and updater additions + +- SSH passwords and private-key passphrases use Electron `safeStorage`; +- diagnostics receive those runtime secrets only for redaction and never export + encrypted credential fields; +- SSH deployment requires a pinned host-key fingerprint; +- remote folders and Compose paths are validated against traversal; +- tracked server-side changes block exact-SHA reset; +- updater tokens are sent only to the configured Gitea origin; +- update archives are checksummed and validated by the full local quality gate; +- source backup is restored when an update fails. diff --git a/docs/SETUP_GUIDE.md b/docs/SETUP_GUIDE.md new file mode 100644 index 0000000..cc5599b --- /dev/null +++ b/docs/SETUP_GUIDE.md @@ -0,0 +1,485 @@ +# ForgeFlow setup and first-test guide + +This is the canonical guide for turning the source release into a locally +configured desktop application and testing one complete path: + +```text +local change -> commit -> push -> exact-SHA deployment -> healthcheck -> rollback +``` + +You never need to provide your Gitea token, SSH key or server credentials to a +developer. Enter them only on the computer or server where they belong. + +--- + +## Part 1 — Prepare the Windows desktop + +### 1. Extract the release + +Extract the complete ForgeFlow ZIP to a normal local directory, for example: + +```text +C:\Tools\ForgeFlow +``` + +Avoid running it directly from inside the ZIP or from a temporary email folder. + +### 2. Install the prerequisites + +Required: + +- Node.js 22 or newer; +- npm, normally installed with Node.js; +- Git for Windows available on `PATH`; +- a normal signed-in Windows desktop session so Electron can use OS credential + encryption. + +Optional manual check: + +```powershell +node --version +npm --version +git --version +git config --global user.name +git config --global user.email +``` + +Configure the Git identity when either value is empty: + +```powershell +git config --global user.name "YOUR NAME" +git config --global user.email "YOUR EMAIL" +``` + +### 3. Run the local setup command + +Open PowerShell in the extracted folder and run: + +```powershell +Set-ExecutionPolicy -Scope Process Bypass +.\setup-windows.ps1 +``` + +This command: + +1. checks Node.js, npm and Git; +2. installs the declared project dependency versions; +3. runs source validation and all automated tests; +4. starts the Electron desktop application. + +No Gitea or server credential is requested by the PowerShell script. + +--- + +## Part 2 — Complete the ForgeFlow desktop wizard + +The first launch uses five explicit steps. + +### Step 1. Readiness + +Select **Run readiness check**. ForgeFlow verifies: + +- Git CLI availability; +- Git author identity; +- writable application storage; +- writable diagnostic storage; +- availability of operating-system credential encryption. + +Warnings are informative. Red required checks block completion until resolved. +A safe setup diagnostic ZIP can already be exported at this stage. + +### Step 2. Gitea + +Create a Gitea access token (personal access token) in your own Gitea account. The exact scope labels +can vary by Gitea version. Give it only the minimum rights needed for: + +- reading the repositories you want to show in ForgeFlow; +- reading repository contents and branches; +- reading Actions runs and jobs; +- dispatching the fixed deployment and rollback workflows. + +Do not put this token in a Markdown file, `.env`, workflow or chat message. + +Enter locally in ForgeFlow: + +```text +Instance URL: https://YOUR-GITEA-HOST +Access token: PASTE LOCALLY IN THE PASSWORD FIELD +``` + +Select **Validate & continue**. ForgeFlow confirms the user identity and +repository access. When OS encryption is available, the token is stored with +Electron `safeStorage`; otherwise it remains session-only and must be entered +again after restarting. + +### Step 3. Folders + +Choose one or more project roots that contain local repositories, for +example: + +```text +C:\Development +D:\Projects +``` + +Do not select the entire system disk. A focused project root produces faster +and clearer discovery. + +The first configured root is also the default clone destination. When cloning +`owner/repository`, ForgeFlow automatically creates: + +```text +\repository +``` + +The normal **Clone from Gitea** action does not open a folder picker. Use +**Choose another location** only when a repository belongs under a different +parent directory. ForgeFlow still creates the repository-named subfolder. + +### Step 4. Discovery + +ForgeFlow scans Git metadata and matches each local `origin` to a Gitea +repository. Generated dependency directories are skipped. + +### Step 5. Ready + +Enter ForgeFlow. Repositories that could not be matched can still be linked or +cloned from their repository screen. A clone is automatically linked and +monitored after Git completes. + +--- + +## Part 3 — Prepare one repository for deployment + +Start with a non-critical staging application when possible. + +### 1. Verify the local repository + +The repository should have: + +- a configured `origin` pointing to the same Gitea repository; +- a normal branch such as `main`; +- no unresolved conflicts; +- an upstream branch after the first push. + +### 2. Add the fixed Gitea Actions workflows + +Copy: + +```text +examples/gitea-actions/deploy.yml +examples/gitea-actions/rollback.yml +``` + +to the target repository as: + +```text +.gitea/workflows/deploy.yml +.gitea/workflows/rollback.yml +``` + +Review the runner label in both files: + +```yaml +runs-on: forgeflow-production +``` + +Replace it with the exact label of the trusted runner that can reach the target +server environment. Commit and push these workflow files before running the +deployment preflight. + +The workflows accept only controlled inputs: + +```text +environment +commit_sha or target_sha +request_id +``` + +ForgeFlow creates the `request_id` automatically so desktop diagnostics, +Actions output and server status can be correlated without exposing a secret. + +--- + +## Part 4 — Prepare the server and trusted runner + +The example implementation targets a dedicated Git checkout deployed with +Docker Compose. Adapt the allowlisted target values, not the security model. + +### 1. Confirm the server prerequisites + +On the target server, verify: + +```bash +git --version +docker --version +docker compose version +curl --version +flock --version +``` + +The application checkout must already exist and have a working `origin` that the +server can fetch without interactive prompts. + +Example: + +```text +/srv/YOUR-APP +/srv/YOUR-APP/compose.yml +``` + +### 2. Install the target configuration + +Copy the template: + +```bash +sudo install -d -o root -g root -m 0755 /etc/forgeflow +sudo install -o root -g root -m 0640 \ + examples/server/forgeflow-targets.conf \ + /etc/forgeflow/targets.conf +``` + +Edit it as root: + +```bash +sudo nano /etc/forgeflow/targets.conf +``` + +Each active line has seven pipe-separated fields: + +```text +repository|environment|app_dir|branch|compose_file|healthcheck_url|status_file +``` + +Example: + +```text +jens/my-app|staging|/srv/my-app-staging|main|/srv/my-app-staging/compose.yml|http://127.0.0.1:18080/health|/var/lib/forgeflow-status/my-app-staging.json +``` + +Rules: + +- repository must exactly match `owner/repository` in Gitea; +- environment must exactly match the ForgeFlow profile value; +- all filesystem paths must be absolute; +- status files must stay under `/var/lib/forgeflow-status/`; +- the configuration must remain root-owned and not group/other writable. + +Validate permissions: + +```bash +sudo stat -c '%U %G %a %n' /etc/forgeflow/targets.conf +``` + +Expected owner is `root`; a mode such as `640` is appropriate. + +### 3. Install the allowlisted deployment entry point + +```bash +sudo install -o root -g root -m 0755 \ + examples/server/forgeflow-deploy \ + /usr/local/bin/forgeflow-deploy +``` + +The script: + +- accepts only a valid repository, environment, full SHA and request ID; +- resolves the repository/environment through the root-owned target file; +- rejects unsafe paths and branches; +- prevents concurrent deployments with `flock`; +- fetches the allowed branch; +- proves that the requested SHA is an ancestor of the remote branch; +- resets only the dedicated deployment checkout; +- runs the fixed Docker Compose redeploy; +- performs repeated healthchecks; +- writes live, previous and requested SHAs atomically; +- records the correlation request ID and last exit code. + +### 4. Restrict runner elevation + +Copy and edit the sudoers example: + +```bash +sudo install -o root -g root -m 0440 \ + examples/server/forgeflow-runner.sudoers \ + /etc/sudoers.d/forgeflow-runner +sudo visudo -cf /etc/sudoers.d/forgeflow-runner +``` + +Replace `act_runner` with the actual trusted runner account. Do not grant that +account unrestricted passwordless `sudo`, shell access or wildcard commands. + +### 5. Register and start the Gitea runner + +Register a dedicated trusted runner according to your Gitea instance and runner +version. Attach the exact label referenced by the workflow, for example: + +```text +forgeflow-production +``` + +Only repositories you control should be able to schedule jobs on a runner with +production access. + +--- + +## Part 5 — Publish server version status + +The server script writes one non-secret JSON status document per environment. +Serve it over HTTPS independently of the application process so it can still +report a failed release. + +Copy and adapt: + +```text +examples/server/nginx-forgeflow-status.conf +``` + +Example URL: + +```text +https://YOUR-APP-HOST/.well-known/forgeflow +``` + +Expected response: + +```json +{ + "repository": "jens/my-app", + "environment": "staging", + "request_id": "00000000-0000-0000-0000-000000000000", + "commit_sha": "0123456789abcdef0123456789abcdef01234567", + "previous_sha": "89abcdef0123456789abcdef0123456789abcdef", + "requested_sha": "0123456789abcdef0123456789abcdef01234567", + "deployed_at": "2026-07-24T12:00:00Z", + "health": "healthy", + "last_exit_code": 0 +} +``` + +Test from the ForgeFlow desktop computer: + +```powershell +Invoke-WebRequest "https://YOUR-APP-HOST/.well-known/forgeflow" +Invoke-WebRequest "https://YOUR-APP-HOST/health" +``` + +See [`STATUS_ENDPOINT.md`](STATUS_ENDPOINT.md) for the accepted contract. + +--- + +## Part 6 — Create the deployment profile in ForgeFlow + +Open the linked repository and add an environment. + +Fill in: + +```text +Profile name: Staging +Environment input: staging +Allowed branch: main +Deploy workflow: deploy.yml +Rollback workflow: rollback.yml +Status URL: https://YOUR-APP-HOST/.well-known/forgeflow +Healthcheck URL: https://YOUR-APP-HOST/health +Confirmation: enabled +``` + +Save the profile. + +--- + +## Part 7 — Run Deployment preflight + +Select **Preflight** on the environment card. ForgeFlow must verify: + +1. local repository link; +2. valid Git working tree; +3. allowed current branch; +4. clean working tree; +5. published upstream; +6. zero commits ahead and zero behind; +7. exact local SHA exists on the allowed remote branch; +8. local deploy workflow exists; +9. remote deploy workflow exists on Gitea; +10. Gitea Actions API is readable; +11. a configured server status endpoint; +12. current status-endpoint reachability; +13. application healthcheck result when configured. + +The status URL is mandatory because ForgeFlow uses it after the workflow to prove +that the server applied the exact SHA for the exact request ID. An unreachable +status document can be a warning before the very first deployment because the +server script may create it, but the operation cannot finish successfully until +the endpoint returns the requested SHA and request ID. Required failures block +the Continue button and the deployment backend repeats its own Git/SHA checks at +dispatch time. + +--- + +## Part 8 — First safe end-to-end test + +Use a staging profile first. + +1. Make a harmless visible change. +2. Review the diff in ForgeFlow. +3. Enter a commit message. +4. Select **Commit & push**. +5. Confirm that Local and Gitea show the same SHA. +6. Select **Deploy SHA -> Staging**. +7. Review and continue through Deployment preflight. +8. Confirm the exact SHA. +9. Follow workflow, job and healthcheck progress. +10. Confirm that the server status endpoint reports the same full SHA. +11. Create a second harmless commit and deploy it. +12. Use **Rollback** to restore the recorded previous SHA. + +Also test deliberately: + +- an uncommitted local change; +- a local commit that was not pushed; +- the wrong branch; +- a missing workflow file; +- a stopped runner; +- a failed healthcheck; +- a second deployment while the lock is held. + +ForgeFlow should block unsafe local states and clearly retain failed operation +metadata for diagnostics. + +--- + +## Part 9 — Export a diagnostic bundle without sharing credentials + +Open **Diagnostics**. + +1. Run **System preflight**. +2. Select **Strict privacy** when sharing externally. +3. Select **Create diagnostic ZIP**. +4. Inspect the ZIP before sending it. + +The bundle deliberately contains no encrypted token field and omits raw runner +logs. Before writing the ZIP, ForgeFlow runs a safety audit for known runtime +secrets, private-key markers and unredacted URL credentials. If that audit +fails, no bundle is written. + +See [`DIAGNOSTICS.md`](DIAGNOSTICS.md) for the exact contents and limitations. + + +--- + +## Part 8 — Configure an Unraid server + +Open **Settings → SSH / Unraid servers**. Enter the host, SSH port, username and +`/mnt/user/appdata` as the base path. Prefer a private key. Save, then run +**Test & trust** to record the server host-key fingerprint. + +For an existing project, enter its current server folder name. ForgeFlow +inspects the root Git repository, tracked modifications, Compose files and +nested repositories before it permits deployment. + +For a new project, use the repository name as server folder. Select the +repository Compose file or enable basic generated Compose and enter host and +container ports. + +See `docs/SSH_UNRAID_DEPLOYMENT.md`. diff --git a/docs/SSH_UNRAID_DEPLOYMENT.md b/docs/SSH_UNRAID_DEPLOYMENT.md new file mode 100644 index 0000000..6a29bb1 --- /dev/null +++ b/docs/SSH_UNRAID_DEPLOYMENT.md @@ -0,0 +1,112 @@ +# SSH / Unraid deployment + +ForgeFlow 0.4 can deploy an exact Gitea commit directly to an Unraid server over SSH. + +## Security model + +- Enter credentials only in the local ForgeFlow desktop window. +- Prefer an Ed25519 private key over a password. +- ForgeFlow stores passwords and private-key passphrases through Electron safe storage. +- The first successful test records the SSH host-key fingerprint. +- Later connections fail closed when that fingerprint changes. +- Diagnostics redact the Gitea token, SSH password and private-key passphrase. +- ForgeFlow never sends arbitrary commands entered through the renderer. Deployment commands are assembled from validated profile fields. + +## Configure the server + +Open **Settings → SSH / Unraid servers → Add server**. + +Typical Unraid values: + +```text +Name: Unraid +Host: 192.168.1.10 +Port: 22 +Username: root +Base path: /mnt/user/appdata +Auth: Private key +``` + +Save the server, then choose **Test & trust**. ForgeFlow verifies SSH, Git and Docker Compose and records the host-key fingerprint. + +## Existing application folder + +Create a deployment profile and choose **SSH / Unraid**. + +For an existing folder: + +```text +Server folder: lumaops +Remote path: /mnt/user/appdata/lumaops +Compose file: docker-compose.yml +``` + +ForgeFlow inspects the folder before deployment. An existing deployment is adopted only when the project root is a Git working tree. Tracked server-side changes block deployment. Untracked runtime paths such as `.env`, `appdata`, `data`, `logs`, `config` and `compose.override.yml` remain untouched by `git reset --hard`. + +Keep the root `.git` directory. It is used to verify the exact commit, update the working tree and roll back to the previous SHA. + +Nested Git repositories are reported as warnings and are never removed automatically. + +When a Dockerfile is present, preflight also inspects `.dockerignore`. It reports +whether `.git` is excluded and warns when existing preserved runtime folders or +nested repositories would still be sent as Docker build context. Fix those +rules in the repository and commit them rather than changing only the live +server copy. + +## New application folder + +For a new project, ForgeFlow creates: + +```text +/mnt/user/appdata/ +``` + +The Unraid server clones the configured Git URL on the selected branch. The server therefore needs access to that repository, normally through an SSH deploy key or an existing trusted Gitea SSH identity. + +Two Compose modes are available: + +1. **Use repository Compose file** — recommended for real applications. Keep ports, volumes, devices, networks and Unraid labels version-controlled. +2. **Generate basic ForgeFlow Compose** — suitable for a simple Dockerfile-based application. ForgeFlow asks for host port, container port, service/container name, Web UI URL and icon URL and writes `.forgeflow/compose.forgeflow.yml`. + +Generated Compose deliberately stays minimal. Projects requiring USB devices, GPU access, custom networks, secrets or multiple services should provide their own Compose file. + +## Deployment sequence + +1. Verify that the local repository is clean, on the allowed branch and fully synchronized with Gitea. +2. Verify that the exact requested SHA exists on `origin/`. +3. Verify the repository Compose file or Dockerfile locally. +4. Connect through pinned SSH. +5. Inspect the target folder. +6. Refuse tracked server-only modifications. +7. Clone when the folder does not exist. +8. Fetch the configured branch without allowing interactive credential prompts. +9. Verify again on the server that the requested full SHA belongs to `origin/`. +10. Save the current SHA as the rollback target. +11. Reset the working tree to the exact requested SHA. +12. Validate the selected Compose file. +13. Run `docker compose up -d --build --remove-orphans`. +14. Store non-secret state under `.forgeflow/` and run the configured healthcheck. + +## Folder names + +The default folder is the repository name. Existing deployments can keep another folder name by entering it explicitly in the profile. ForgeFlow does not rename populated application folders automatically because Docker paths, scripts and external integrations may depend on them. + +A later controlled migration can align names after a successful backup and downtime window. + +## Rollback + +After a successful deployment, the previous SHA is stored in: + +```text +.forgeflow/previous-sha +``` + +Rollback is accepted only for the exact SHA currently recorded as the previous +deployment. ForgeFlow rechecks that SHA against the configured Gitea branch, +refuses tracked server-side changes, resets the same working tree, runs Docker +Compose again and repeats the healthcheck. The version that was live before the +rollback becomes the new rollback target. + +## Before the first real deployment + +Back up the application folder and its persistent data. Run **Preflight** and resolve every failed check. Warnings, such as a nested Git repository, should be reviewed but do not automatically delete or modify anything. diff --git a/docs/STATUS_ENDPOINT.md b/docs/STATUS_ENDPOINT.md new file mode 100644 index 0000000..d63adcd --- /dev/null +++ b/docs/STATUS_ENDPOINT.md @@ -0,0 +1,59 @@ +# Server status endpoint contract + +A workflow can report success while the wrong application version is running. +ForgeFlow therefore supports a small server-side endpoint that independently +reports the deployed commit. + +## Canonical response + +```json +{ + "repository": "jens/example-app", + "environment": "production", + "request_id": "3a6ed71c-d52d-4d8d-9678-96e0c9456a81", + "commit_sha": "0123456789abcdef0123456789abcdef01234567", + "previous_sha": "89abcdef0123456789abcdef0123456789abcdef", + "requested_sha": "0123456789abcdef0123456789abcdef01234567", + "deployed_at": "2026-07-24T13:00:00Z", + "health": "healthy", + "last_exit_code": 0 +} +``` + +Required for exact version verification: + +- `commit_sha`: full 40–64 character hexadecimal commit identity. + +Recommended: + +- `previous_sha`: previous successful commit used by rollback; +- `requested_sha`: SHA requested by the latest deployment attempt; +- `request_id`: ForgeFlow operation correlation identifier; +- `deployed_at`: ISO-8601 timestamp; +- `health`: `healthy`, `deploying` or `unhealthy`; +- `last_exit_code`: server entry-point result, with `0` for success; +- `repository` and `environment`: useful for human consistency checks. + +ForgeFlow also accepts `commitSha`, `sha`, `previousSha`, `requestId` and nested +`version.sha`, but canonical snake-case fields are preferred. + +## Isolation + +Serve the endpoint independently from the deployed application when practical. +A static JSON file exposed by the reverse proxy remains readable when the +application fails to boot. The included deployment script writes it atomically. + +The JSON file is non-secret and can be served read-only. Do not include tokens, +host credentials, environment variables, registry secrets or stack traces. + +## Profile configuration + +Set both URLs when available: + +- **Status URL**: returns this document and exact live SHA; +- **Healthcheck URL**: returns a successful HTTP status only when the application + is operational. + +After an Actions run succeeds, ForgeFlow checks both. It marks the operation +failed when the healthcheck is unhealthy or the server reports another SHA than +the requested deployment. diff --git a/docs/STITCH_REVIEW.md b/docs/STITCH_REVIEW.md new file mode 100644 index 0000000..0b2f853 --- /dev/null +++ b/docs/STITCH_REVIEW.md @@ -0,0 +1,99 @@ +# Stitch review and design corrections + +## What worked well + +The Stitch export established a strong visual starting point: + +- A restrained graphite theme suitable for long sessions. +- Compact desktop density. +- Clear technical typography. +- Useful deployment progress and failure concepts. +- A credible developer-tool tone without excessive decoration. +- Good use of green, amber and red for operational state. + +## What was changed + +### 1. From IDE shell to release cockpit + +The export contained navigation for Editor, Monitoring and Extensions. Those features would blur the product into an incomplete IDE. ForgeFlow instead complements the user's existing editor and terminal. + +The product boundary is now: + +```text +Understand repository state -> perform safe Git action -> release exact version +``` + +### 2. One navigation model + +The mock-ups mixed a top product navigation with a broad left application navigation. The implementation uses: + +- A compact top bar for global search, identity, refresh and theme. +- A left rail for Overview, Deployments, Settings and repositories. +- Repository tabs only inside the selected project. + +### 3. Operational cards instead of generic statistics + +CPU, queue or server graphs are not useful unless ForgeFlow becomes a monitoring suite. The overview now answers: + +- Which projects have local changes? +- Which commits are not pushed? +- Which repositories are behind or conflicted? +- Which exact commits are ready to deploy? + +### 4. Persistent Local -> Gitea -> Server rail + +The most important state was made visible at the top of every repository workspace. The user no longer needs to infer synchronization from several unrelated badges. + +### 5. Contextual action panel + +The right panel now changes with state: + +- Link or clone. +- Resolve conflict. +- Commit and push. +- Fast-forward synchronize. +- Push commits. +- Configure deployment. +- Deploy exact SHA. +- Explain the blocking error. + +Only one action is visually dominant. + +### 6. Diff viewer, not editor + +ForgeFlow displays changed files and diffs, but deliberately opens the real project folder for editing. This avoids duplicating editor features and keeps the application technically realistic. + +### 7. Safer deployment language + +A generic **Deploy** button can hide too much. ForgeFlow displays the exact action: + +```text +Deploy b82f91a -> Production +``` + +The confirmation state shows the repository, branch, full SHA and workflow file. + +### 8. Desktop behavior + +The implementation adds details that static screens could not provide: + +- Native directory selection. +- External-link restrictions. +- Keyboard shortcut for global search. +- Ctrl/Cmd+Enter for commit and push. +- Resizable desktop layout. +- Offline and error handling foundations. +- Secure process boundary between UI and system operations. + +## Visual direction retained + +The implementation intentionally keeps: + +- Deep neutral background and panels. +- Blue primary actions. +- Green synchronization and health. +- Amber pending work. +- Red actual failures and conflicts. +- Compact status badges. +- Monospace only for branches, commits, paths and logs. +- Minimal decorative effects. diff --git a/docs/TEST_MATRIX.md b/docs/TEST_MATRIX.md new file mode 100644 index 0000000..668f4b2 --- /dev/null +++ b/docs/TEST_MATRIX.md @@ -0,0 +1,106 @@ +# Test matrix + +## Automated in v0.4.0 + +The suite contains 59 passing tests. + +### Git and repository behavior + +- porcelain v2 ordinary and rename parsing; +- HTTPS and SCP-style remote matching; +- real temporary bare remote: status, diff, selected commit and push; +- real temporary bare remote: commit-only, branch creation/publication and + remote-SHA ancestry verification; +- real stash creation, listing, pop and untracked-file restoration; +- repository monitor baseline, change detection and pause/resume; +- safe repository folder-name derivation from HTTPS and SSH clone URLs; +- automatic target construction beneath the project root; +- missing, empty and matching-checkout clone target handling; +- different repository, ordinary non-empty folder and file conflict rejection. + +### Gitea and deployment behavior + +- Gitea URL/credential validation; +- Actions run normalization across payload shapes; +- optional query-filter compatibility retry; +- runs-to-tasks fallback; +- newest matching run selection; +- repository workflow contents lookup and 404 behavior; +- deployment terminal-status mapping; +- controlled dispatch inputs that cannot be overridden by profile data; +- exact post-workflow SHA and request-ID verification; +- rollback input allowlisting and exact current previous-SHA enforcement; +- complete deployment preflight with Git, workflow, Actions, status and health + mocks. + +### Security and diagnostics + +- repository path traversal and absolute-path rejection; +- workflow filename, branch, environment and full-SHA validation; +- clone protocol and embedded-password rejection; +- runtime token, authorization, query token, URL credential and private-key + redaction; +- camelCase and nested sensitive-key removal; +- home-path aliasing; +- deterministic strict-privacy identifier hashing; +- required versus optional preflight blocking behavior; +- system preflight before credentials are entered; +- structured JSONL diagnostic writes; +- support-bundle strict privacy and secret exclusion; +- ZIP structure, deflate payloads and CRC validation. +- Windows npm command-shim discovery through `npm_execpath` and `cmd.exe`; +- normal direct npm discovery on non-Windows systems. + +## Static source quality gate + +`npm run verify` checks: + +- all required source, documentation and server-template files; +- JavaScript syntax across the project; +- package version and required scripts; +- desktop packaging metadata and icons; +- Bash syntax for the server entry point; +- status JSON parsing; +- required setup-guide sections; +- renderer entry hooks. + +## Manual before a real production release + +- setup wizard against the installed Gitea version; +- repository discovery on the target Windows system; +- token persistence through Windows credential protection; +- HTTPS and/or SSH Git authentication; +- actual Actions dispatch, run resolution and job visibility; +- runner label and repository trust scope; +- server target-file ownership/mode enforcement; +- status endpoint through the real reverse proxy; +- deployment lock, failed healthcheck and rollback; +- diagnostic ZIP inspection after a deliberately failed deployment; +- unsigned installer and portable build on Windows; +- keyboard-only and screen-reader smoke test. + +## Renderer smoke target + +The standalone demo should be checked at minimum at: + +- 1120 × 720; +- 1440 × 900; +- 1920 × 1080. + +Required views now include setup readiness, dashboard, repository workspace, +deployment preflight, active run, success/failure and Diagnostics. + + +### v0.4 additions + +- bounded independently scrollable changed-file layout; +- explicit commit-message and selection readiness contract; +- ITWorx.tech asset integration; +- semantic update-version comparison; +- exact-SHA Gitea update manifest lookup; +- update repository path-injection rejection; +- SSH host-key fingerprint helper; +- remote shell quoting; +- Unraid folder and Compose path escape rejection; +- server inspection payload decoding; +- SSH deployment preflight summary behavior. diff --git a/docs/UPDATING.md b/docs/UPDATING.md new file mode 100644 index 0000000..8141b08 --- /dev/null +++ b/docs/UPDATING.md @@ -0,0 +1,55 @@ +# Updating ForgeFlow on Windows + +ForgeFlow stores its local token, server credentials, repository mappings, +preferences, deployment profiles, diagnostics and operation history outside the +source directory. + +## Manual update to v0.4.0 + +1. Close ForgeFlow. +2. Extract the v0.4.0 update package. +3. Copy the contents of its `ForgeFlow` folder over the existing source folder. +4. Do not create `ForgeFlow\ForgeFlow`. +5. Open PowerShell in the existing folder. +6. Run: + +```powershell +Set-ExecutionPolicy -Scope Process Bypass +.\update-windows.ps1 +``` + +The script installs the new `ssh2` dependency, runs the environment doctor, +verifies the source and executes all tests before starting ForgeFlow. + +## Built-in updates after v0.4.0 + +Open **Settings → ForgeFlow updates**. + +The default source is: + +```text +Gitea instance: the instance already configured in ForgeFlow +Repository: Jens/ForgeFlow +Branch: main +``` + +The repository must contain a newer semantic version in `package.json`. + +Choose: + +1. **Check now** +2. **Download update** +3. **Apply & restart** + +The source updater downloads the exact branch commit, records a SHA-256 checksum, +backs up the installed source, applies the archive, runs `npm install` and +`npm run check`, and restarts ForgeFlow. When validation fails it restores the +previous source and starts that version again. + +The update log is written beneath ForgeFlow's local user-data `updates` folder +and does not contain the Gitea token. + +The updater deliberately checks the semantic version stored in the remote +`package.json`. Merely pushing a new commit without increasing that version does +not present an update. Publish the complete validated ForgeFlow source to the +configured repository and bump the version for every release. diff --git a/docs/screenshots/deploy-confirmation.png b/docs/screenshots/deploy-confirmation.png new file mode 100644 index 0000000000000000000000000000000000000000..364d034646653994e72929e6b8d14643c492c063 GIT binary patch literal 140415 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 Ef(=M>^ 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

bo5H10|1V&9C^^9hXcT1uc z-1Ou!D=W8#zIgTa_6CKX$DwNl$LHe*G_|xUa2&}-eXfG)y>JGHhGdmOBkL%Mxj1yZ zZw!RooOVl8x6|$(@64>sEs?zz(=k{N8+%)TM=Wx%50Kh6T7v*5)lxu->4qGsJu@vY z!|dcf6Z{ea)g1{bGMH+JCNLw)c_d}K^ztTyV>@lH)J=h!-tgU5fYw26S?^w~k!sozm+IdzF4qj)7dhe-8pp&zWR3J~#~?jebt$ ze1&NjHd>=#6^a(xzNFDYnCFW1Ul)sgnEQ}Q?pauvYRiD@Xec7K5X+f0dphG(qYaF^ z=^6G;PQvy>$wyeJPd?P3);)4hW>?I{!(4rTpN^w^>i~17?-%`Z6z*d>d~7 zS;?hEgpSpu^R3a&w)W(=M8{l~M^3%^tTaBTfr&GV9hd6c_I1`5ZWQ5Tyg8&O1r54! zKCr#Cn>Ze!k0R?oL}u?rFSKl!kR)9pBL%_i8El$AkQ&v`l*mbwf5fqHq=_@nQ1MqC zUmV+pHarD>{uE8KzyPMpyGJ|YwMhU!N5mfIKFoma#c>?sOWr{D{9ao~T zA+K-6_I)23`6SJAcHXDHKlKSPPFQoA?#F7AjQsJ{E?IzhX*J>nPtmxcCJ~XofWwa- zh&MLwpSX(sI6BL*x?_^bqJ4$q!VRusg9^m9qPCX?J;Uxu(c^MZ}<{Wx=n?~dHpP| zexZ^}o%YPf%+~OnhM~CzlbRYb`*kL?`-xFE4>AV(G$CyHUkP{ws3)ELL%ngz)%lHq z*ZDMgv(6fSSDz$?&5=}JQ`h70*LGRiaMV8p8Ue>oB4`>}*x5;6S=yS~x~%;cJ;HOf zImJ9o;^O0El$TUmFg3D`)k{Au6k{lNbJ;&SxGg80@pNM291`iq0)>deje)ev*g}IM z8(Ujbn^hGZm7j9O_aw{~_sjIvzs1I~d{a~)PX?NQ0YzI&UDLw&!`c!iEiO7+C>nG6 zKsqodw1YxkQK5HxErJerp9(08`+l75A6_s8s&%SME3ba_SPlO65q}}NySO`K29%n} zg^YhZ^*U%7U!;`|2bvym#;+RJG73`xkG3A;8X#0=FQrh|chAp0iJ(vm66YV5vpy^F3ds&D}0Vk9a?~5)n`zd@#6{%%OoPF~#ciqEL zpGRu8g>F6UOB^jW|5n~q+o1J!LV}ws-^R_EW^ppED}fAK6Ir_RVS&pI5{di@DxlgS z;o^TyFZfo~;04IrPV$$IFt&^^_Npa)K)_i+BMYKES@)Is>4&|h$U1y)0bL-Vny40^8%iom8{16;|<&O!_gOL?v=~)(QC;?J%MS4rm`A zRtC$dI)KjtXV`S=q${f&PPACbC)Z+R+ACd64P15)1)W4ej)0re8n6v}VKV}oWK9B7 zP8kO+d;2&Fl&u7u4yJDnsx~&($rFNXZvIeptQYu+>wyS_z#d`T|s$Zo{MG1IR1 z_HEI_c2mHhBZAV#*!Tn3o|>$pthBnSw5QviJ4F#{>r`Z3J$%fN{;oE{d#Y`HIVZI3 z>zJjk_XuC<<^tvwkPtz|yz^%ox5!z^>kki?0K=bzHF61=h$xH|vW%!OJe@b|+Mvh$ zi4sRn&Lc;Zk%57ZzCH}b%Go=>%E?&>EgTZyt*9tt?i$bzzd`i$Iem1JQNnk=_siE}-1Im)yo_)E+=G})3dQs`l8|jA zpVk3sDB@W;5Kzl4@~}(eHB|qj1)44-w~wu!{aN5#Og20P&t5kSdgr@K$_v={^tcTU ziV8Oqg+yml+vp3U?iqU4c`9+(Tdu6c52P!}B0p+1OghAds9TPYO)xTcFw2GZva#tS z)e7o-IIk0L(JX_M+~G9o92Pp!dTxYriX6gx*5}8Reb-dp`*1OglS1jKwLu>zG-QCu z%Q&#SI;^|Ls3Ut>%XLaJCi&O}Vv&69AgQE^iXF>L$pq_~i4E?1+$$3%jtE$t74Ivp zXgJpMmUXnY^UwszDV=f7M1JSZmK*V%D%CaT9o`CFL!$~5VwdvK7|yW`PAhUsNN$7Z zlkBRhTqRv{d|!Iujfi9>vn*PNuSRu?^zsF~7<5>HgIq2^;?*i(+S@;7WoPH;>cJmf zS<%wMAN8G3)!=%?quppupgy7&khxBKUkYZ%s5m0P_1f3nsGM~(bU%<{Ba)6ZIVqQ} zJui=8Yo@f;I^O<@^vA-Z#wG!vW3;_psuM40%HGfkasqZP$#G|Q^EO)J70v0(hdj=R z$nXu%Y*>>^!Q`ESy``jmgAz4V-7}$)VJIE(u%Q{tFVj0~fIm)Wn+l&ufolIeY^7DC zFUZ}TqJf5tGei!OLhy2A^ysP;oW8n~5Fqzr?4&kFYW`n*PKSbC4rFv zVZ3r*YkaR;&7&jUd+5i#$5lrrCnbcmsD4^31~s}=nN&d}Xv2f}LVoEm)E~JPz}cGd zeLV&u*k1{td_pDVfxy@r+CAFBk;F?nbvxku^FFRU-4DIyc$F8x(w zGGg(yEXg=tKe&W~XQJLZisCHR+>*(ygnP`9#d)Xwg$%1tDLYMVMOm)>5uuEYbE9$^ zMIAUaD_={Waa= zr{0?Rtj`mIO7==sIhBCp`j>|VZGKMV(t6A0R&`!adI!3RANThcWnu$}h4Azwpn{l*a>7FWPbAejuWoD{>>2G3UEub&}OdRJwF|oF`bJ$@cR#T6j7R63T zNI1hG%goD5PfB5WWK&R}A$~O7-{a~^yaoJ#vLH)6{okc!OZ~~Qgd~7^tv4g!kO*|s zhaDV_BqF*Cn$L!2es_2D&!GD=97A`t#MdYye>s78P$tJ8qMossj!3!ELfLOLB+v1z zE^jX1Y=@B3*n;bzyK*TS>H;PwMoPV&s^I6(HE(E?WpI406?Nt8J)Ej^!hIKo(^un^ zA9)lO)$y;0Pv!_47HVj!2rU#cS~z(Og=bedMC#r@gYrXq%XQ73#!)`sOg$w^6CT3P3T$L$*Dp0V2KFdhCt)vqBp7s zsQC)3c+cEb?NBOJJvFU^lfFGb2)G*eK4C3wFIXSrY$Gg8-d6LQ9Nv1GQ;~VBW}xa{ z-TpgpF)fVc{QP}~U)l>>haAphx&DIhNR$X5;jC{4tD0H$>S`pU`z^OX8Bp061WPGo ztwfbZ716I$UGDZQzy$Ybl~Wg_tJB zsOH@z;J!P3v&e5Hpv>d!JXF2b_0bH_O`M6ferTo$6X|K9F27EFt6|72fR+aYv#pSL?R(y=p$nY~iQ=cX9Pa%lbzn!RCH7MeEv6Um$}5oL#twkNRg7COzd^QfZ1nfReURl9ZXP zGi)Ck{Vejl(-0w7(FD7FH&pcM&MU{R)m^+j{=8J-sEX)pCC$E%K_)xL(|f&vH^+O; zSqfU>83(7bBZ_^!2TtY1pxU3foVo|~KEh0ElC)d1->3E-vQe1b9VLnZ%OMU1rjfj@ z@5D1;u+>#@ctM#kg>nrR)<5L8ya2#4Q!3+SDZt^MhzdvIWlsX`t~!4>}7!vU&>+~J{oc8aNo za%!vp4MqE(1BE2fz7pQ1cop!0>pN%+K{4y(#7pZ5V}0$ad-`wm>vmivanmd()*ql0 zis}BATAYOvO=LmoL@;Z8wF}ueWbAXfs~)RNjze5Gn>ax87XVKh$BU-f+@yEFAY!yu13O9sYNETX}f%pRbAna?|8ye0Dz@ z`SlFTxxF8)8G<`irwOjWl%Pt`;tf!6y}3z`N?itS$b;L?;-5nYl zrt{rmFKB4!JI|k@ zetAKhH-U!s8cph(h>CmK!6K$Mo*5Qc@9La$>%B@q+5U(MH8ub;l~c7t=ITRt!QUq2Hl`W@qc7wIHV z8UEMbnRu=LT9yOp#E0D!IC<9vIAR2`6J`D{kLjssnRL| z!K;MjXodc;%6EUq`aUEf1(MSh$zoxS^;3_I^h?f6QSU0YhCU@hYb)L^qV)a<0Kl#E zV=`iy3Rhq9i?qssKz-x*7ZD6Q!%Ai|5c#iasyYRm)ika3lkHgrRnw|ElmJ)F&Ns_a zM@6}6noLI#VzFyNpuGmd22&XB(U6_mU7q9wb(cfV*@-{J zYB5riQ!;Q%tFC5vr`tyd>8_tV)nks(jv<2OuzsAPR6pIxVPGe&Zj9xqD74%q+d-P`rIGkNDf~}3`;{5%yAuS?m zrhIpb4c8j^ri8a1^%v?X9-mP*FcV$+$*4+LXpNxbCjFt~u{{A1KgoeZ2p80|t19Up z;;;_s?Ck98lbEhA5nhBjIy%zD#0d@b4_lRW>oG9eq)%E2nH6{l=o*MThXYHaAVje?-}}exK(~)$9$J+35>)l#?qJ-zG^;A|W9T#=uO`(jZPwS-ft& zG>1q|+`DrR_qO*9xwWzMm9AxX0O;%D3_x>yJbYf-yly2G6=F{!LdvrW8pHfrY*kfN zm8CruC4VT=8qfV63uM2h6auMTF_bIuJ2>rrl~7-e9(KHcIudQHioeOJ0%0id{-P@y z|G-@0%@9Z|V6Ecf60v+KBWpJ1*0$1u4A$lW@A-ZQ+t@5F6Pu|=?N$QuAXv*&9bSRx42P=^Q#Aa~MgO1*jy_TKIZF^tlD=H@! z0f8({0@iMfZqKH{H>-DRYis4}f1)UbZCBedf`Y_R>6dVu($#SV&li)Fj^*>W!a|7FlS+fjYo*4iw{bw5L z(&ZDNz5U4j?YUa1Ht3fgi+Rs$==sB4vDt76!P~cdb`Rd3b+^~ccNeG4-nVB^w`HTb zb~in6A0r@-B}{W^z$5I{@gp}MTet`B?%r;1kMqI^$E&BLw7Rh-WVU(+cC&r)vSqlb;Q||b zf|G+OLzt0<#%#GO`6&aDn}VoO?2~}t#Ds(&|17C0D_`v$Q304~XaGdS6n&qy2j5bv z?Ck7;=4KWed?R*g>FF(fp1q2Sz{Y6-H^OMCIGN}N#*5XDA4Hil_#Y-XclLK9-_1Sl zMev(38y6PR;u6nv*Ct*MYPlt=t4DmX-}GEO8dFq{jkeHv{QlkRH})6I*!XCmy6Go! zD>?CN4vJBoQN`d4y%;A}+Vf5u%ioTceeF=od|ga(fEB+l=khV%xW~>e=__I`%VF5z zX}Qv6|LleSUKQ_GrTGlWq~vJVvzZS@ca*$vl?xzEeZt^3&`FiJtr>H1Yxg-5Q?%3MB8U~z0Np@xZJ zLr4r7o?AeG?CXZFc{pEgsjBI=I)2y)flHWfCFFF=Wm8u4g@(cP8u8yc4=??;m|E!4 z^LcpU<;b9_f%wC}-=Z-NUO{bOf}Ad#R-|{Br>tJCwy9iBEjh7HU%%duqCYy;ua!Sb znZrgrkrPuL{5?tsU|+uo>A0X;s!Bdz5NtMwxkc$_fbW^QqQ~SkuRHS3%@nXH_)>g- z33%G$3f_Al8~pFJQ#j3cB!R%nMgE#G@yKKrPJs>IN0E-5!#dZ^dt{^AUw7%dE4zKq z&x@)!iQP$2btcHI`JtQIR1IBk$RPZ8?ec5lSKHl}daA0YI|d^@V4+s%W813rcN1F! zLEkeDBfRsmwQNA*(3mzo8K-GXB%~#!p`kRLiahw>=I$Egl*n6OC-gH)u*{6VwqzhH ztUq_!RN@E5xH@NFfG_eim7*DeP01-l!+$>VpkomO0YWYJVs%qDk5PZ`*Z0n(a#ne7gs(-ocwA8nlf>*aP;>3^d;N=5l8TPV9*tZ);ULM8j710l9MG@Bh&?-8BEtlFP;W;^D`dn7aZ8KXjEeufa#($+91B^IPMr*jHA?-@Q`520Ut zCD9QLD9sMWeH&{bSOxHOy{@XRiHnYwk{fRF68=#Wyr%_9s&C}oHMb(bC&UOAAFIG_ zJC6n`84q`|kx%!#p6)b!bwZyPqcYhrW{+IR9ga(b#(eLOWg3g2WZ_FvI2Lj!AH^91 z5SnQ4A~A_mQ&hppS5swPWDF}1aG%&yfq zX90%j+_cj7F&8ky?NEv#g2}1NNvKwhZd9pSc|`?;-|dy0ck@vy>X>2HsR_@=#3h;N zo>teS5frr9vx1K0d@JvBpQs<>a!gg6SMaCaNWAGkvkxJ>cZ{gc@>wK=;r8{ zE!-U%igk2EQMH+plETqHe9Sfe>IicoTVGO3U4Y}^z#~jXCJc<+ozDrra(8z=-&+bR zUEkXqT0ixjJsI4TG3%Ctn4Y<_=PfsGF=|2}DQC#(4BdKX+qF1iYaX%N^4LnPMUn*p zye_kWP>Ho%ID?r2x!&CiPT>&JvIgYy&iH(~q2qxAtm|Dnl7Pwl4ZZ3l_qwQM#bkY`Q&zOd6SF=R) zyw7YsE!P$>`e)nU zc~I2hsG_D)#nb$De0+R(u-Db)T%Ye(3-=iy`F~n~pM$jFLM%M5rlzKjR&Tg%r^ssM z6k1iyu_ME9S5{IY!0<6BIvhj5va@ZbuCp04DmX~u`gprz>eSh-rix`Ayt6R{zA7q$ zdb*U}`O0TSWr~+M4X3Sd%Mb zk+Jhe76zW1h(juxPr|&oM_Nis9ydG5`@T=EYNp~e%3VrI2X4I|e&-aR2Xx%sUQJBC z$(ZgHve1{jfgrmpDxD;7C_mBA2*I<}hi#jn(DX-&7UxSsYPCH}=-76H>$osIpU~&4 z-wB6ayHw*7BqY}HD%ZA04SaJ%umu5MM0QMewnCJx^=;cjbB+<^!OivSO%v4SmXXu{ z&lmE@uZuK2?%$%GRfdbZ+9?KCBXZzsEzC!JImN}b`n_6uKCQdRsMe!NGqm_Rw@cl} zel>)W!6IT=_+jm?1wDu@26(u|?7JUk$Nf;|&|g>{o9wD@ZH+RU89Vwa8|Sj&C3^Zk z6W&7?rnv0vw@9qFq~w?92p8wDs0av$%V#m_fpZJ7@6?8SmkowM`cWgZH(U8)6i6rD z#Ykkez}^y_xC-}_SE(`UuEsIrz1P_`Wok#q*V*~4Ww6`cn&jix#Kc5OzE7_XkCt)6 zqrhMAY>#ZC2yd-qw6z7Og!+g5k5C?jp0>6um>8GBJMNr!ChXCYhMM~FXfoCIMaUZE zKV;4D&8ib$)WLw_9t5flfp+~0xlc$Ven@^JE=ZbcO9G&jxi znJlm&(j!RBznR-xt9-kLv|LWF>`pdURb3qH8mS8C^7v3SZZB85QPM|+V7WPz-kGj% ztcmM=pV=)LaBdhq5Bg`ZbW1(>pT(SX@8csIJoZyNi9u^|U0W_>sD|#4Aowb`p&>{d zkBUU{R;N`~*j|v>dQzfvZ6O(NbD<*h?g{TK2o=Qr)o#8tDk%YrY3=UMTnRQeEiEk- zbOK-y->r^O8<2o5_1#O4S_&a*YU+YW%G{WdmAmVEyURWP@1?QH(J9Fg`qvZxEH(ml zx(bd}E-)BYhU9=RWorB-RrbOAd~!F-4f$sJ?pG5^eLXw#aWyrO&=kJ-65EBhiQ~3p69eA<(9arT8 zb#%**FsyT@*82;cl>RDN1A`VAmHzg7$ol9ZjA9yiz@7nwo@*HGExqOe#|6*APbNmopA?g0)Af#sd^`EZ=fpA; zahaZWfINOigpWU(Vt2P(TTKW)SI_)R&DqMcN}6jHwl8UDfid598MQ{ zQNiQx-hL(6Yd#?>S}NfDGJhzk(ftlW4wo$ZJ^agQT3XXW zU?t7YPlzqiaC*LE*n8m#E{L_|paz{^=!c8478s3x2c7Fjmmt-|u8!#$Cd_r(s~Q$jmi2w|3pqR1)r^p?IvP zMoxZOlFV*4BByC?X!tQ5Cj65h`7okiJk#~^<-X^JsY#gEa{z!&=@xl+-5p83esGY+ z^a2`5&cAhdXkuoku13Fv%{6Q(T&3mga;d61IfXnaTsQO(TdXaX+5R!c zHgdMlu2*XXeN_p3kjkn+0KmpJ6R3-)_llwz2%;koi+syh6Y%lx;^Xh_?c=B{3lD$p z;h4~pWgsitIj%l>;bzcRQ^7`IqE$o}f_#Q!RB(&i`u&CFKkaj#dTsdeGrN|)Iv8B_ z`VxMHG0qtUDuG|Q5w|0a1@ORXMslvCWGzQZUFu(2y4UH+Pni)x`S~!lUO+(x$!>hb zW-_tG7~Gp1rc1_uS`Awg93NNqKfc}GsJdvk54^n<7te3L!%$P z^!1&2Jw#*_eG&fY>fp-G!jX@q5FVwQsEk_Stf=^6ExR3&J~yY}pyY>=i8Q6#V=FnT ztJueYkcMXnSD!`Z?_(eNNoBT~uHpcpxoY_ zOi`d{`WUsVEmF^eb7E3?oIMYh?lDGQT|gjr?G#FaQwF7k1*?6X9j_3p%&jz!Zcx`b z+ewKD6zek0U(2BhU7Cp{19{vA&S|cl%eQ|t^!8~wIJCyX!lEk$&@mdNj7*U1*~Q~y zsD@%;0Y67V^&~GX=4mfUxlJ`39A22{T0iYU6Tp0xNBA_Wq=|>Ih-sBx7-DdG9y%MM zDMB@^`zt=SvYddXiQSahP^O`-u3J3p(c<}w28mWx4bXf_ILr9xWKL`LVw2a^ECgj_1`gST$j_>+@-A}4!qCzt ztgN3N)(oz1fZS{Gx&C>;Mx< zMmGBIA8=@dnU8x-!otEXFPw40_bM1GJ}D9*)UQP zk}_O5Ia%UU;VWqcs!;qgCHH8P1z7k2lh^R=~^>jo9 znQ3H}+X}04Z-VWngg_GcOYqkp^ApBb7+(uc24pFfzQ!22rddWJb2r3LMgq_8s9!CoN=<%Db4FPYlKpTySZ zRr5?m-=6^(?N*Ol7&|}@S9hG=4*@qboj>YqmxKnV^O+CXDqOZLCBY1z{H~8Swv6v? zuJ*S!#~X*+F876OZbj55I`l;Ic9KdTI*N&gISUnkZ|c_D2U%cmT3L6B_bvvq(L!YF4oQC489cac%y@I z%s5!N26_hM6^50SlxjSA^wrRZA90eoGMlm;kNwsoO7F`jWunL_06sI?yQ*<FAy*l?$?#Cl-*GIG`ZoljOTI~tAt-mk!>x$u*R0dEq}=z zlpQ?HWL=sx!()j_-`5Asm(30q5)vdQ>#R3baUO_`kB#j_SA9jnff6WyYHF@)ogrf& zhtoAD>|kjafg~Z5#>wrr)!oHzN7X>U56pwL%V{!7%5KR9ldeCVV}w@+qwHKvu&OGK zPaG3dQ-6l!1rRHxSHx&x7YCCx{D4?rNVZ; z8tylk^M^rN5)!WRDW}Q#n`i_$6h+GGowzNbRy#c3_$;Bx^%y?KCIy!lI2CN;uMX$y zS5p<}+MQgFM@|d)0xl+Un-&fpI{wfL9NdXxhs$=$rp2edo4%hRV&K;Tu#Zc0)O`M& zM-KpuQZUgo#X~402L@$lvbwQxE|EtIjYmYUNi$|9cH~VK9GiptmB|a3Ttl%+g#-)g zs$|7LQmSHsK9Ycmk-|GfX-+9!X?{+rs@flu3>7^+bxjx?-t9V}tga4Vps#n?e)vsQ zP<-S4^5u9wq`ZieBUD!%1gg*SqqtL)k{X@JJ$fer1Pby}BhMzax)pRL#wVvn6wS~e zQDxbo$T}0Ua~XceN&!yw9ym;Lcv!%J2HD0bKK8yyKBxcl=Rb{Jp5vv#3K{U0<&C|m z>QT$sg27}K4^vC!_QCzcTX<@RbWr@Fd|=xo48B6nl{;SakLQWG!^ba%jn-skT@#{GOWH z<9Nx3qkOA_vp^nla~>sZj_8{#o`&mzE6%1`YgN_MG&MC5i%oCdlBocs_&}`>2rCCE z)=jW+mVnv6X`W48YH zhqX8f1qE&fmHTxqMg7-EU~Wzh9x<_MZ?W-Q3Jj+E=qDN!{>}+>W<^k2!tp`K^~|br z_ho2ADe_|WC9RE#m1Ut74h5gGnAl`xYHejPzn2nP(XW*|%{jgfiHICRSpv_S8^kh{ z^d9h$p=W7SYI%f(er;EnrXpoF{Lj8qJqbyQf{u-*rgZB=z-}0kO{ORj(cc{K3dU@s zla-0bZ6hTtUC3*7<^{a;UjJ~6TYU-VWPj33pUAAXfXR3MI3*N1rv;d+AE6R}l%iTW}A8O>G40%-A1!C8cQJx3zBe zmMy+3f{z9{pOaERJA1_8B-~=2SgY-McGr{~{w-?_ZkPEH40QB{4_>yqyjp|n5gB|1 z$8B#Gy)G%aO@>ZbW7my7Ht=|*q-Y7dIG9k2E$c2>_hc6qj?8xhxZ&TgSj1T3G%gR8 z;<3mNN`i5YtgJs#P-S$l6O;U;K}FAk3u&Y}W-78xVX^3{nHmKO$49yl+oxH-Eb{uK zbu~Q;U$Ddfrv(u5yEm0b|NiCYqn*Alj-{rmy1TV2ooZ%fWMO0ShWzfkk21<(xyZ#j zI}>aYoK#VL`&M~})_!B;$&sx**{klkF>NWO^52CF@dA*(j_{IEPL9gJ^3xZK%>uIF zI2JE4aZd7lx+I50(>QE%{aTnzTwS>ms%Sjo1$cQubG}zUzUh#c{zmsUG47OTiKFmz zb3!d8X^nnegR&avXP51+Uj(g7$q18^)YeZ(p9JYbZ2@(S`JHTuR|iaVbdXB@+3zA9 z$2U3=q1ZN6bAwhy<1Vqdy8Sa9`?P-v+ln+%NvYS@u5fl8Anjb&<;v=JBeOC$Ma( zPTj>Ux!kBK>|2!Yi`SE+*U$v=>%CDPP!!`G_LSX>qOOvV>4iBJ)%rqHlhrf~uGiW! zn&RMa>a;zW%I+1Jm9?tlcuYa97M}a)BriYI1;%pm0RQfQo6r7tsaj7cgX?~2#Vs=iaE2%|>g#OZ9;r;MoYGmeOeYiC*<5He% zHY5Ia=8Nq;6x#ZTh$jIuFF)z(!U%qKkN{)hqz0*pbMdY|;q=MxN|_56Uu^a2?wh;n zw*x=i2E)U`Qk#wjl4B)k@x87)hAS#7{Eu!)iPtRyP;2ePQ027v>R`dp#>!4#zoZ1} zi9EZlitB7|e~Q4d8}i@euX(t7FO;9$h2fu~2iD}it*(#FA5u|Nk(QBq@L6}`on5P3 zg+krI$gA~EI`+E@Qf(ELvg75}*jNe{8^0;1ip1D~4z4VBAwnI%~xyI3#Qpg|v@XgZ*-)611;T3HOgmjOQOmfG;shNpM?S zEG?mS+PtF6FwSb{9rEz!&3eC52R9S8j0LZwh480 zBK2`a^#XxNLy=xu{ke#vFW+fi0B+;#dy0xV>KM?-*I(AfxA%4bvCFpqqN>WM=lB_@ zB=Uf}FH|JVE%3?SMN;Rp{pK2|p?sXb2KlF|ax5l>5qv*Z`?wMUG#gTUdri8jswOTG zXmjtN^RoN7B_m?f=stHklAO>@INL{+;Wj;BFubCY?X!GxG$dh6-~2XVeJ6FMx@<3- za&4wW+Hp~2%K)?3(@3YhC_$1>b1LT?GdY--NXyYMoqZg_!O7|4b)DauRBOAK9US{v z{{H^F+QY*`H0YHP9{?y5JTT#&PmWE>t0tjt_t^T#GHkqgm$6vqDa3O^f&SNIp*e8l z-c!ay_lHlzwu`7>{;EG}Kk1Rn$caBP+}gv!0(n}=7Ze^|l9HcasFsut88OGO!i+L) zaS=8{FRO8JcDUyAv1UHU+CAEoCmxC4Kji#~^##-Y3b&;syaXCk(!?O4ruOZ(eT1U- z)(V~ve98A34_!28oaPnALd)-}>Mib;K;uo6EW_v#PvB8+<7Day#K*^{rb(UFkv2T| zQ55%2x{yPXkL0PEudZzX>pxmB0>Lu+{tOBg$?5O*W9(WlO8{OITyf?(b*$8bV{zC~tf~pW_h_Iu+nXP=}my;$VfojUE zFTYY2#)@SHaIp$$!GVJgc;sQ zjB(3&60~R#e&8bk% z8%8K&^VFv{Fwj(-4?UvZ<4~DYSFx7hF|G62K>=E^EyGo?*{iAjU{$SY+^`Oh60n+1 zJ2f$`Oog?yy$oiWCq?k5q(lk+bYaO9%D^mQ@Z%-muhF25c%mZt2){%V>RtS7Dj}l_yS|}r`VWCFY0=x4_<>Jryl;e$H@}g zZ$mqGjy$LjPh{iH=p`ogx-M(XCm8{hQp~Y!m_*_$HqizD z4}v6L)X*TUq?GKZ9_OFU*4#Cy@&yIVT?$+V=;^AJ*jfK}mVPEqxM?DWE!hKw?pHN& zwN~()Au<-SIi&M9@FzD3RP)viLS`J`dwO9ftL7k86I8nJ;l+cNs6rD>p}6bwzf+SQ zC#h1LTk@xv)@VgFH_xc2n89w#n~VePyZZ3B@J)5KYWyb3#3sEVpB4&DbU8pEUJPmz zJg3I{g$qps3B94}@io*2@E`o^eO6W;&1;oJe8Ytd^@s#mbXtiv$im=!@-MXaFI$H~ z>i)ylp&&aHTld@&>Q1nkCzkGH3?st$4|CXNMBAR3u>$Cq*`0PL6KFc ze$~O~fe>K`Kuz(PR}ij{v+sY&L=qHcq^dftqNo@X-|=gSnLF!?qK=NLl5XF2wIK^0 z>SYq=Dt%6}V*hXSqOZc=3HaOoU*aAA-inWt7bt`If59#PGdm;sv_$fexc`B^P-G|NgiAKeCg5|G>xj|0$mN|JM-JX1HY&6BB|B9sqj!a$J-mE*gMtk!@9bG}qv1 z{UJk-fW)~qJ3H-v{3x3HXSRXLv)#q%N-MYBju4>fI~3GJoEn)SAgU=z;CXe_?<^G8 z!l-c;R4k$Xe@-=D?e>me9xOT<;(0m7du3p^{9s9vLiPp->_{ZEG&hG9TUFTyCaBd= ziAUw&hvz#vIu1ejr1#C{g2PKn{QYB~Q2Kv&3lu=e;o%`>BS=L}zzLdtM9}hm&B}yk zHx;sIDFTrbeO1d<&NwKGG}&Z9C}O_B`lm_S*1=#1aPq8rue$1+nMbs%Yveg0GrXuM zBr-HJcnEeRv+QoxzZG#9XAmEsaDREHfPEi~LR1tWta^x(o)&U%p{Rdf|GRpae|AN5 zvYZP+)Nt|ZkZ%KE(%_Eh=y=ZZvSBF&D9rZgxumX0$Q5aARTZ@Gi;7nNm*AD2o}RuQ zlGNEv<4=Y`P#d|h1dSOM6?pyziAf12CMRfdn_jLe@o#GH*7Ht`NwYuO5rJ;G zt?%8kcNg9FUY02!*01#l2?#GpS)6YQ%O!9RUp5@u*;v)O&(5QS-X`}44s>RA4>6Ah z+&Y))>}>zf(BfNJn)SWa&BO7G&;M}xXJ_AMHu~1jt|W*a=DhOw!Epz%g$ zn^)~e(IY|c4?b1S7t*0&h9CXkQg%)(Qxu_e6~J~BE``wVg+9}6y|s;$mRdzl2^u#7 zPEJHP=Cg&K{@$1)EnkA;ktG}d6cSg}Y32;6=mXimYuJ5vMCk+hhPtYu_7>kq)(?lT zshmjNYG z&F#&LlT)4Rj?Y*5xh1j^-+Ak2>FJ?k?MH%M+fvE}TS|MzhL(hHoulO7l9tlSD#l6a zK15>Fc|AX0(>pj`>YL5FInXoGLL!2> zOhq;MmgN;C&AEmMn4*L_cxFdxPm?d?bky+-tNHF#dAVEm*~y3({sK?EdtXn_3@;CW zo=zeVQ;@S6+L}K4=0gJ_2%kmjB>>Q8;Uz5-x**W#=5U((7Q|csW?e7m3q2KQ^j1Ti z$L$6AnFjLL-9>*=rB&_HWglr?X^yb>06q@m;g10qJlcUb^ zYR}|ogx?6)*SYO46$>LlnFBIfT8UFRYRATAo`)1VUjIVeCuieh?9e~Sgb_usHTw#2 zf&Xa%7W>59oa(^tUs{@dZDFE!dNSRGMfJ?nq!ag67WHPwelGLJ;*QFwb;5R6R#^7E ze{-x8cYkV6paJUxGl2cPsF7Jx|- zZXk%RQ2i|NENEADJND*19~C1=V%dHn7sLe9eNqcA+l(W^Ty7oDWYg+dKlMbs;$Y#R zqhs=R477y^@{lE4Z7XaTo6!~*$|u+NOm+ZvMZz=lKQr9eMlGQqkm)qkd7_YZs*WRe z#y_?N4f*-;)y)kJpA!KOM$&%xHc>T^OE*PMI1#c&IbUta(@?p!4*#cLpLUT7yPlB+ zGr|0xaL?w-YCl5s%5tX(CY!dw>3WmK{#$#ya@W&Lkf9-2RYk>IF=ywCm@Yb9!bR+e zL312l&~BETQUjQI0^PXE797@8zjv@U8BqO<7AzLKNto zp&>tHi?2UB>n0^Hoh!PAc%qiGUsO_pLTx!%S=9?XG&Q$}$SB#Yv2F0XH0XMzcpMM} zirPt(?^e3S1@O%jd&YRt8Pp7PnM~j*b9`fOrPFuB@lMO5-E6f>^gt5B-;V&PJD*Eo zMXErZbShrbXmyZMQbs|sUE5G-2)2vk{@i3EH-^^rk#>P(m<0|o2qY(GGdBc8Lx6EUH428c+TYnS6;5#8-Zn|qnlrc9FE^9p3RR;n ze52@z60Xn$-OkX)*A7*bRCDSg1noFDd9zU9jBjvtc-1EWsn;BC9(CZq(NoiavB{?W z%@pk%;~=&LGpeq@0j|ryCuw17e)ZpJUZ{k(*V;WQ8PP~lD(^o&>?;@>Z!A6Dgdry7 zGkDg(cjD*8ebHo%B&5PqKBGM|aYyXb$zQ)|NKV@>rfV;tc&C188Rb~KqF;JC@u~g% zZhJ}2Z)(Pa-HrB2gSFIBq9LZoJH{qP1=&y%f;Uquv#FX2j$`P|#}GBH8UYIC9pAQ- z=ykFN9t#T#;mgf789)Dc-vIH2MyCL(j_BA}0R6$lE@v>+!+?zZkJ8x0)O4y|TdXsu zyudM)Ff6)E3vZo|O2{$|mFr(r$t5`T;DY7MXaWLltV)RXL~nI!Snza60KXN4#|z}S zrSJT$%o&znb2Xy0*7`Pix7XnJlb8a$#C`$K4+p{V=bcO!Jn7wDCVrP}|4r7!q^n_gx-1ayvqIuk zsHM1+gOaIu%HQaI-gg#s@cw8DcBF%cw}#q&E>1+WWmQ$s1x?(Hd2Jbz$+< zHp+%ginJ~+OhZSz z7Iam_9`(FBjhC+zySv#=&T_nIcv)c*#}C!j?L8+eX$M2|$j~>q+IciIGt;5rGY30GYi?sZq){qGb4HS@ z$iTA@&iv4*#IQ@LPPI#u2g?$hO&Xl!sba7yd^}H8sbE`#bG_{eZt!G9w_^SNDg5UW~A1jS6ou z72_XiX(c(8fIvAERt_B3`!Jy5{jQgLzc(G7Dk|T( zQ{UX_SZS`Fs7}l;;zHHDe>Xjj$0!j6iT8M z+r8fPE$Ca&GgKV{&O5Hp)U1(bRDE3^ziRPKsmdC&ERv*mMfzPDdl10cs9Be$e$S4~ zFmUrz(XkG7G}d}?Q>wbip|%bkK~$bqa`%h+K|XykR7)}c{(TTLF99nJ&G?@s3)G<> zHwc>L8}aRl*FO9}&oDBjd;c|NEvMS$G9r8sRSS`v5Q5#fTzveCHP+PBEPuQ&@GGhc zPalbeZyosT*~JBU+rLx$@7z9RkPfRsUhWsN;%RFyT8hcKs%`D!1y{0D;(+l8-fYd5 z@^4DeNxA6Ayrt-b2>6zn!F>h2Zn1u}%Mi%}_j2#-?{5$&$8U#aa%MsRg-Q0-N5F!k zu{YA9qfJdrVv|n3FMiEf@>w^^e)s*AvCTGZ;TwE{&E+QV{E}P^P5C0i#dp0k%42B7 zN^w|9`kj&pU8V0RZr}a%;td?)V@btyy zaLuff_M!kGpJ&Nhb5j#1Iqd=dM+nXt6{cEQZQo-;t#_2{v#Tv1&#{5LrL(-JlD6z72?s%DI}IVBRxKg21PhJJ4S>=>Qi1(@;IL}3y*x* z-v_8IH~EGzI=i|?L`F7w?vkOO=own>{vxCN^Y3SCduejGqV${5!UH+WUN8*dY%U2W z#eb^|>UDB*866wbd|V&)@VGs0tvwqg03_Gs6lI;RVjjEh?J%4s z5YE}%@+t^l?_6$%`S^G(H+jftI&Snhyb5Nw7<0c`&GbV*IIw<2DTG+W6=qHU0@oEx zWYRr3bQAs~?A|7TDN5DBJmu?%7^ojF7-l;ZU+&r9UCtcJNuC~a99}{PtVB}6LkkKE zJB0*X-R)o-b6}dtgK^&(9Qse6SU3vv%xL}_9yigl=eKehAtqXD9(?vLKz?6(2(R|% zxBIIz4rVUld3j>&{d;>mWc(Ew>4aE;yUEGiu;qmoBRBTxFS&8uwQlgtm65PSCiY>2 zzKsyO)=NZphVPNv{;TowDi`PMY@-_M#ih(NMq*w=RKlNA&feH0Rd0BJ7%rE_GBwkg zo89!0k8i8-Qh0M~gYB?wV&a>YmKLxwy1bIq#MZRN^HQ2d0@H+Gv$NCKQ>XU$23YT( z)>IJ_lb`=g4wsbAeQUr1d|!Wgv^bRH%i;b)tJbE~MeF{pscb6ZsPWP3u|ol-+dS0t z2_o1=&%C|V;&Iq}_Dlbh+J9Ez30rv?i(Zrc5>1BBE~R~~@2#A$!*#ZTkf+}J*8(oo z7Q4Go+>Wg*&2MfllDOSm)m(?_&xul@*2el@uJ^$UN%5Chk(IK$liOCpxA$Q#6p*9A zY482P&GYTSp?>B%_tlE>a;4>#mPj44f)=h~RQPFO6^u)nqxBL%N=kZsv(4<{a(Q!)iacAmmDwdGJ||=_DBj)MYiQsZPerMV2v5(8wlYOkHeGI2=n8t#)6t2Gi`(6qnQ3d^M~mKFTsjwRJqYqM>9in@myrCm zcDz1U+HQ8tViiF!UVB2wp15~9mTFLr_BacV{Cjnl% z+us2{K$ZDsrlvNFR|)|d+v&B-nL8455*FR^hl~8RaLwDRO~5qmkwBT<^#gjag^1>d zL8R#-5tRhC*6k4{$3AkhctJ;}4*)n`Y;N7X9=vGaAsad~2{Ro`$;vJ4VPt;XOk9r2 z;OVMr6??foCNp!fzlGZL#zavufewEy%zL}^oQP=>j*5<}&e92d?rtm#i^aM-ZD-0@ zAN<@;P9cJg!x{YZy}jjCRSYb&?&EN9TtdQ_v^kECw-?as6>il1I^2epm61IBhPRjH zKL(BVXybZSV_@(GRWT-&3=8!YfFXlb!XEc zvz`^dej?^=pgT-yNjf50-Xj*GGM|dBZ#=XFtkLPIi*sEiW}EFzL26@l;hW zdn0ev_gl=)-$yHI+?{w2(BK>_N3oGhugzS9THkq@85oRye}c?)F&#?sysLQ}HKOr! zzDBqY-i7BjE+~+;zzMKgA0N84YS9yzKs>ew<~0?lvJw;TIMz`uF0ZTbXsRHt$|4wS z2%n9D&skPM;d)#_h=YTpP`I|Zz|2xJG__%knl#rBEM7ox0L=w&&e+*(o7l^oL>$WV`gDxEwWNqm<^^fBNuYC-i8Ts7?|2}WrFq4 zeVTh?Y3ONtTF;N}tPeQLz{{wwsB|JRjw*wrgx$tf^a!;yb3HUIp=jv_YLr|FgRpw} z62L?CbU4kfWQmB(YP^U>71gIlCnJA`8dLJk%%>*3s~o5Ai+WlT9zHNpg`FZPD;fSX zJUh|O#G?Kx%*AZzC){LvcbC<;CzH7av9(cDd3qZ1=1ug)UP&oKqwA%;n^W(A^)gTb zRZok9b200rpSMOSV9xTrIorRzC%WSv{mAET@tLOv2y}UCMyd48Wuu4H$Hyns5S8}_ zb!|(Ci&GcmDtl3|8q$iz3L`IGs!mNzXrHfoE$QkUV?JX)#6=}0I3gc388w@dKBFqG zZI>Im?zu*ca=Ce-qO|edchql)Nt((M7;UCS(`jdtud- z<0zEb#wI>Gdi7v~$889kugB5(bw-$GE2(^|)oT`B5cb#$ChaE=5?0eii$!vL-@jHptNUim;CCl%_U$@!;)nw;GA_(B6RaLoW z!w^|7RF=KP^^i|4x1a!*obQLnW|OmA*oaxO&?v|0`7r4tVjp)4^nZ}{mSIt@?c2Ap z6a|5$w3Ld7v~+`lfOLbjba%Ik2$Iqb(#_BfgLHS-$j~{!&@eO4sq0?%^MAMZ{qR29 zGauaiz_2lMUDt6Qwg2{Nx8GOne3;+&dLsMV3ad`jwV()IIe-&5IDDSmHX*ubt(26uieN89Vea^=fuur+Wo0Vxe^C&p8 zxVVJ9l0BfUfdHSLt)PhcTlCAwjPyfu{Sse{(z_0b?{Vt)#V}?M>Kw4OZvoZ{lnlxP za`>(elI?td%PEKl9JaYZ}%&tXSUsqSi z>XMV8x4)n9VT4!7I>=`igM(#wDaaVCeTBYBxqQ3P7!z7-EhS|kE*{XFS^{FjS85&> zt)oj+>xaW2L@@S12u(QOc9XCREcw($JA-pQ4xZ|4LnTf>o0GFWO2d1Te)YwEJ=nwl0mJL}8JHvPHM(bve_nZT8Ow~yMjw#muK zgNZ`XKKU7?=*3!n-p5Ef4=;&$hKj?a$K6Bb6ekmOL)*?EcUwR?7NHLP)IKHrb!oO^Ipbo8{|PNY$#d zU)t%9ziu>xW@Rl#b>b+b@?Ujoz+TyGPs7mJVHfB|$VO+i$I>z>GZVh;2J2<)Nf$IRb#N%J3>gRE#l_j#R5rcM zIc8756`z`W)z1$4hV?lgZFRhY8x9T6Df&*!tg08$c>_stA^TqeDlcL>K) z`CY>Gb|c7JvdT_cGiWlkBBP>|-4op3mQ|Xu$|iH0bRAD09U0omDaSciwtBJ<0BD9-KO;ZKe)vZ&6W z)29DD2DPDMWyK8EY7C{I=K_l~3Vc~wu*n0|!Qyushn<7CxOkEJ$#Cgn`&Ya)5x3I| zBuOo*QbkJ1p{uIk^3wC-PQXsfMa#u;@X1;O0;YF(*;s@IYSzc(B+%vVFx7JuzDTLB z-P*z|M}OTyYwItO+9qsRkna_0cxHPCU}<|6amGQH#O;KE&s~M8qb5~ zW@PLm{LYFXP^0H7AWN*y{Rf1#3o#zmJB(Sb$#u-P?nLM{YBS5ymV+Rr~mS7$IjST zN3ByvMyA<$YYeb5tSl|F6@=XOn<9iSgC6Q1xLCe@Tjx9|6UW5H#+AzN0p#m0&Z-l+ za?rbXm(m3-j?V9|r0VL_EgXS|^!j)u!a9D!9}B;s(Tj5i4P?&DdQ~%{qmkW3eLe4> z&Rg&)8*Pu+rptNE7F{~&W3q5Ejw$fC&-WKKWu{CayU6W8luB=39tjchTHiGCzn!X-fPIA zQdwMFoYftkChStEX7pjyl+|w~>~N)P{8W+2ac{0#p9l4bfV_VwwuKdJq{^A6#s7U0Yw{o_moV zlMut}IguX+^~V*~v}cirL5wkfCSxjTUkpzE-I?8sX=wJI*H#p z0~ME!JXVGO+dG?GAW8?fq)g&OT*-XmR5BZ?+D)Sw^)pEUP&T@*&o;>u1vkhiJUEeB zT6jWbH}oEZiY=K3v%IoG!#p}VsaxS(V3*!fX*OK!mzfR@luYrRSlW0uH6^7qev`tQ zxKnMr=H4JybHfU+GiYwE3yL>C91Md31M`xT=YeQmWb5%vM!@Flq_LvQ`B%mVKP;k_ z14$40z6wDxOS^&8zVFZEbj@_R)T%w4HUQ9Ps^qF+bPt0%6Y)GbiWIJH?)ddnjF>4! zUnFdM9)c({XwSjdfcS!%Gax0$7>YR+h^-`gfcs!e zf-N{L4PB}WPbW!faK6|(8ywS+d$~Sy-p{H5(L=#7T0RbmxuIkn>mkF??B|(i)8noA z_|vX5A+NMz*tPoPFagNahd~F+EU%=b1g3jpOVZ6E8^SeXJvZ!s7dNxm9%+g_P9@+l zwz!CD#}|o-e2Aa+&daNBm0sej1bBQ6H(Jh?w#7q;rC={C-DxDlv&$@gUSEu|v5m!! z>>^q)pTZJJdlL(jG3WkT;Z>h&zeGnPuMiMZt=u=`fG{)W`$GqDG98~c(J1jiQvEcL zdTRV@IX?Xp^vN_VS2htqEX46)?kj^JUzng8U}oB!g1|n~hLGH;E@m`DHs%XULtOXf z_k(!PQ8IeuR`!!lX4~V>9R+!Q`GT03>SyaX1+Q~_l>=_o(2#t8JTqo%BR!jIV<0sdNW&;7 z7OJf@Km(6~OwhfoI!w+{c`T|^W~O6xb2JO2qu)MfwtYPj8CzIxmmKN4ed`u!@69qd zH@l>JKA9`MtMn=~S(%^x{kuXR%N>%HY=cI{V1Iw3D=I0qBsD&sWHOQ4W-da+_Jcsc zrpe|=d0wNgM<)`}OR6up_nHJ0QYnwpsC0eC}NN_!&)V)Vor zZF6&4!JnQzquDIDIzd1RGNiC(&Jc^NfC|((d%PVTAtLO(`YOO#z-^zMhsO^&$P-S* zr>v(W|LuO*!Nb;ndjVI4ATVMUo0Dt2N8#ezmfOxLm?>?$Js327thvSKNuUvYknYS3)_JUS6>#LaRJo{^>PUf-^!RWv znFnwR$eG5j)lt{TVObmi1}gU6pma@2J3F4`7?L02Fdg`ym5VmjZ*<*TxNzFH-LEtq zjEarbEjJW5H}Bb-ug!_2e_1ceqF+;9o%1fzFiF_wQqjayKOe=U*Oahrys?@h>=DII zBfbBUZfs&g3whY>WI7yI+A-D_-11NvAS?b(Pe^zx5GV(!xjxZwf6LokLhW1i)wQk8 zVZ|3boI;p=ygKrO3AZrM@3y6Tb60nd*LVINo%St}{Q}*IOd| zKt@Jhx%TGMC((@W8ogIww;FSeRIs&`mylSD6=-T|qJQ)E^nGmGGBP(?kS$r$%l&nJ zWw*p6j4LgtYqL4>^y1>8kD+g4D3ynu6R2wK-Q1vV2tCvtz^3UACGVH_t6IE`5T5WV)nT? zIX~oM^9fR{K>Vx5EX29-;nSug(5eyTRu6nObE$Y&{ZixM*Mf#Wm7_KEa z%1ia3L-HzOnYGH2l9G=P%X&#@grANg&kt8X{bKE*g81Cjbe)xZGAc<(J^CdbHt4j$ zIwG^P)Ern+Vq<8y7;Bh6$fVnFygo?IDP-6Y6h`isai6F6Tf^@C(9qRmv6}t_s9$T7 zhZ7+E#pzGvChAW7SZ7W4t#?3v4GGzS`Pmt%JoOQ3RuD$TJak)e6mb6_Qr#Pi59|2# zqZJFE>Qz*liS@UtSTw>9V|5|$9uutw!JIJ;4g<>}=dqljE_(OO1XS{FJ25>9=krz51Rl=$y@O|Lb+q`9T&}be`o`biG|1@P|L~$_c6rHipto^kW(Wv5<&; zFE>Q^_qSADXVmZHr0(P3t<$kYT|-0b^0^9>w|(&=YiVLKx(4 z47K(^ZA^|saEOJ3gmP{Y2XKuY2S*yaiU$Osu#WSdNN9?SgMkhI>jbc6I)1CPv~(=D zGgl)tnVIJ*aEu* z96~Rj$oslK+%a`El5thK9rG4e=a5yS&S|%F%ZAe7ui5@Q+b>*dvBb>>4e_ufw=y(c zQHvQ`=g(%VGbvaoX2lX0zvKGZkjeFY`|b>ltU3{5^nE|qWCU)`F(S?HD~vVw6x-` znc3%C{^sgxLUX=%E*kD2kw-ZJe0+SnUrGU15oNDPBuE&-EB5ovDVw$ zt6g*LvOB}JRm#Zhv0Jcpo|5@Yg8hbT|Lx-39>xhF86^iLneAHRBCWE=5_37>+UhTu z2YP!=v~ouW9f8E}sUK3O-jpn9uBnOJbLo8dwZR>+s0Id4$EW+Y zW@ZXJeNgohPo7r?=&p308v6&ue~ti+Nlr zA4X1Gyi+Xd&6gSrvqgB|vZz1Jb{^cZe`a(uOfFQWUwT$l%)g;j$Rz5RPJqsAvM@F& zNedL=d!vgeZe3NCExnEI;-ytwE<->-fNY4(xrKjbrZOrjQ}mF} z)?}gLZjp_iOpWy1vbg&GC~W(#(mNsu%gJ|Fw`ar=XG@tVG6+FPYeBoKCP3HlS6bH&A_1tos4CE=e@M zz>LyfHwL)Mt2hPRx(S;4U^TiG=NS!+7(!IVNxzgQ{{oEK|6CV(dN@akv$M+{nAlre z=lEo;4<@Ilr;`v9OGwGdOmTRh-&0XJEE}1xwF8yxGdb_`<)jPb1_zwwsjag@WZdU$ z1uTIyRqbzKVazNnQ{crjLd|ac(u??MfQi{~YIJmDNO-tm;4cqlS=p*j&r2VR_=;K= zpC6Xy~z6ApU5W`qgwC^I~%&2RwSZL3(&QGd?$~ zs9kU0z9Z-%p(9(=&4}!c`09>tE8XPG5urhkxt&SnLaBs2!D+o4pS{+vuis?s;GlFE zLC&25J(#K1$fiT`RH}{L=LQlbpd5`CS;aNg1W=H`7E3F5@j$ zgCZrvsSM1*9&<^HYX&SWB+Sf6K)F2K-ODI{gi{C{4K}z~XDIbIq)AQMX;m?Txqjcf zOt$nuvsHjZtT7e>6Isywzsz@&4nos?q*(2 zOipa647@unJKj!T=@J(D=&M|`^akiQFi-SM>Va-6e`!+YW9M?u0YlPtNl`@O`mUR&bv!6eUh4~78>lP zq}c1e+`%EP$Wx@vdCpTk0S<;)+%z6LD#@<| zi;as@w2n+lD$FS`Iklh2$luH_D;r5jcu$wdVp(X3v4)j{`k zZi>qWT5Tc{61u6sCez4+Zj=Nrsi9KkD3YJ?Fbb`ecz)zo#) zKX<)F$4(Im$yoz{^Cr$3TNGfqQaQUr@*uT_o!Ay0WVO3j2&mn%bPEvUV@GVh^4h21 z?@DK6&MP+VE~Vrg6=VPGWo<3nuke>u2ABZ#E-fu}cT1{>MMv>kco?frbmc+-2^)R~ zn~9iN>+N#SwFfe1jW7rV9jx6+Qjzn6c75O3?1F-7&y%Z@cp<0t0m@$5nW^H7UuQ6$ zy?rM z`vTYdjR*(cl$(!K-OqcL>7(;rF@H~vXI$KU^T?X-jv0UvB!dk1GL}`hg%gCf8Y^;; z=r&^ZFXCT({_GEC8xhm`e*RIpvyaB&p}wfE661^#g2wLeB_zHkCX(g&p55NLlu~*; zU!u)pktzWTxVz&qQN>xk%~Jw0+2RXx+;R4dj0|!fTknljH*>WUg|#f)^ZhHtjJ%q{ zB)I$$uKg!F(-NVi1sSCd>jRoKubw}DyK#t=LfI}EYE&B+)cG088cFH=8#>;|xyXA) zmDe*v3zeT2cGiEPU?USZi7lS^>(h_i0!uh5F4EZ8w4a%|G&Yo??{(mhEGnRWb73yY zYk4yp^>r~3_JbIpG=Qe;VC;vVEeW$0uPex7jJkMvT}P+JKn03}hdo#6hEJJaYfrni zwFPPx%?4*OS|$mJE^l8zb2#md?gn)SAe!btAa+JJVw0c6L=$Qx90mYE_=SARJBygq zRG|os*t9g)izC{;yqV~gqzZ%fOOUQAn|8Yj7run>f^Oluh%YM(gK75ESkS+{0LBre z%ZY+$@G#kEXz<*=?GKp86B99@M2<|WCLt#m@I21%3?Ug#;!37`CdB>>J`6=Sd0#jp z=dU|S^skSjiM^)uT;!?)a&nH7oE0*gLYKm8)tlrHkzk48PcM~82#1zEY|5@z-3PhH z1MV#viSUW>afNq5m>mf7!{ud=N=rCCZK|s~2f?wfzL98^gaY()ThX_fACzS$qt=6o zR=UCgBJl@c$LiN#@8rrUa2MwHU@$ zXe?J8cYbazN!roT$#sv=ioxER(8T^>ivXxVs%c=o%#NXhK9M<`p{82n+pLTsXTzG z>JIkHhD?ugs4FXFwOg!1PY*b_j7P<`7z#P zpmdnuHu>}XtzA3Cetj7nhhw?j1a3oXLuvi--wU=MG2B2My?Xv?y`ECUHxqD=IN3ao zvOz+di&FZl^;Af+%6)WC+lKEhaZ}Upnd;tEjl+sWusf;yE!T&j+`IAG%c?)7G*E&1 z>%&xD&+j)sZW!6wA(rE|o zifp(k2`Di@2-DX78KT4AEf_sX?W3fwbu^6-;b5KnkwLS!+j!l)HVsf;&#&`j)7ZuN zm&Uio^R!(aPaxknUkWw+qY?$8Qz<%r1$I^*ffs)WB2!C|nbbDA&Fxoi9IPBZKDT8U zt;q1O;;jSB6g7Kgq@?P7uBK{AwEyU0f1mngH59k4uCpzMU9q3e-)4L!XpDivtz_tX z7@6K*<_6@P#0`?GsC2B*o*nJ|1`{&NrGmw7@@bY%3CYdv?UKu1l^+Fs!8jsZzEnKm z4qZ4^&pQPZD#RyonTzA?qO79$^mw;5_-w*v5KxJ|P)PLv1X!&&h8Y3pMCf@~QT$NA zU05E`Qj_m&c}Pe`7%qBqAsj0^BG|;M^qb2p%SB*`~OpmEK ztMY&Rf(ML8EuOG?_-L=uxHCy0xG!T#$OmZVvY zJc$`k1OEMnZsFbKaXLQwrz|fDy@r?iVOnZBI&V*av;mL^=da_yE=^;D*V18QX>P&c zTxfk^q2)A0PgXW4PqPs{`-qZkdlryZF>e65`O4L1!#=^Hk{HbU|GoY<4mfS>v6sVk zG)#xJnds>Hg?FbG&DydpF#9uYf`PJ9`V6{0wRcnUp!PkAm8{*5G=TSy z<-MQ-BKt2dj7?0sv`o?>pU?mXf_eHL@v3%7gNBzC>N9LxMItD5VnCl%* zp3&ef{*~;DmVf!TqB8e~1A7&x5O95i9a#o5Ul)V*Jn&TJ(o<&T_4E#XkJtExEc#>n z@%!0vPvX8;1~U-)7A9atG!+PTUl1P5n!6w=>czrbGF@`?9h$hQY>_VyKSo67Is z|H#X8L-pO+dPdvX>8!6iNP>OG#KgqfK#eFOY$ItlYAouRYxe2&wN8VEqN?>luwhmk z7Ct4P&3cn9faxhmf4+_Q??oB8Wy2BVCF|T#-hM>~*Qc&QiTfceYdgg0idVsX;9(UZ4<3l!_m1n^p#(E*wA2F|d9{{E&*>wM{jUWypjU ze)1*oE*tasK%C;jsr~2iO^`bBCG(WHA&=R4YAJZ#zbGJlMSRXoT;#a^Bfq_IW6-j) z=DlyN5;8E**EawefE1uYuYAPy~ zRW;M&<2bnYWusQ*+z<g(>Yxce)x}1ZPN6%r{t%HwSZ)a$_zcSA%z=%AL^x1u>syrHL z(2a*c?g@N2s?zkRuLqBG8(yrqIWFM`dWM%0+>|0h1*6OsxxzU{x+%I>xTI|>YI^nF z@^ZS1MS&68)3yb@-{xy=*B$Hs=&gP~41&0Sw)oWn(T9+#x0fsJ^Zh$q=vvfoX|KSK z8&;E8=@$U=fgW&JPxLl_#ID*%k?N%Qm!b={s%D`LG_CB3mCvta2##UF<#Kw9Z zQe#@Z+-APzau*cU4*z&c<9of2q>HEIGtF$wt9||Q!`sZF_~F36?v}0j2ZrtMk4tKb z(>`Un>=?aml0$1<9~E^3Sf|A{ln=sW0oNOff{QU!Ic>6gPyA&>P+1WLN1BBP2HugH zh7@NXElKL1FE#;=kBZ?X{rE<(Wq>5!zJ;PZt65iyc(|^PWnmHG&Vr?V3lhGDA^7&z z4^#ckhu3l-8-Q4+AZ14Lc1jQ71WLLKf?vZ)^XxvvM8-L;uSQl)#OYl-ADY__T9!*~t(hLi z9)Q>%#!R^|P_TQ4M%anaipQRxZ~8RW0GWGbY`V7sWkVg42P*Y)c+CxU)N6$G*t#`X z&1xUkOfYgOUk5-XD|B*Bj)(cB1$e-;Vt-%{R?S zc-&MtWInwvyNE|ruybs)zK1^LLN5IgYE%`)bAgq{;q7t19&pW}SC(=vF4f6iwFQ;y zn1d6#wFPz+r+%_ZlCel{6O)Ud%>r4^L<=fHLqP+=^<@7k9V6xm%jL{+b00$?wQobX zX$k7Gn=WFnx#8@wpmR(^uDX^q{DOmSY&@z!QBK!r%&O9QuW{m_s9kh_{0%QHYYd^R zr;Y2|_x1qg2DU!_-v$Tt@b7DbZo;zCL0NxGYin;@CmQ|y_f zDw9+Cpq%o;xhkc?Dr%|`lWxCQ$)}WvSC%G|pJgWmjWGDCL4kWxXOi@Qk_@)V^z+UtF=Osj8U{Xo3OaLqIkRQfN_6;Wf$qRv>QLEEAa~eMT+T=uH*G)3pnL7Xu2Ly@`c}0 z&pk1(?Uo2saW}Kt;B~pwv1B$eap7B;ks1eHE6|g(^z2yO#YUydg=W++9G-b!#WXaT zS1pN1_#&;VTcQLFd*qt5)B@6FGH~=)r2-2aBBC__xTlluD+HM!H89QLplfBIXOK(d zPOgbta`RNcoGo>;DJ_4y?;uNnYu);5`B-zl4$UYGwY*;4GNO~eLQdnV3Y*MP0F}9j z-@Kx(3Wv*O%6fVgy_}D+c^u#(94M`9dQ?`jr!}ORF8#^pKe~c_uwS%JJ6&sO!0UpN zE&N#q8-aeKBbqZuBjy{EkI~wsjG3KI^Co z_K>MTmmgMgA-pg4x>$uh;)#o)rrKWJz=(!&p_Ybo(-%ONiJ3f*fiCIlYKQAA-nvin zFAkHdV}u5Et5Vdx4j1khLUMEUR1+|00U0`lWaMS=S!Pzd2kmRb z_8v8RGjOtT4G#`wia7wQE8C%KlZMN?fFta@s<2F%LmIlupS>j961sYxg4@$GEpjb! zrkS`^r3QRPxV(jv@BFodM6B~PvqC>N$`=Qgm=k<02MmC%1<8-P#?D{iMKbm$|;RO6y8~^ zTdGC#c)ez4)2Rho!gor`UwS!! z2Ar@+{0o*FV#^EJm805A#p`rEf?2}s&Mc$m_#47n@=o*-2TDf2t*2_r@IfWfC6}PC zH^j`$xcrKJ%u=3ZpQcN8Vw~g0`FahE%L1A$;l^66T_|RUyZADNi>uO@afkeIGq+D# zN)s5WF2shIiGAKz&d99^4-S5utfj2Y+%w2kS(bgHsNk-tv0>8jlW<`NPD&C2@x4Ii z;h`Zxx6{Ao=PZnv0KwhSa)$7`=KDFtT~dOFr!Q~V6gL$_40P*X15SlpK#}nKeiroB z730ep%c`ebCrkfi$iHO(uk*NVrbGcf4Qyxd)ooBG{rLFdumYo|p^~lQg1n?<$WXLs zuT3v+4$OSk<{m33jk~E>Zw6@rkP{OHegRkWh))?x9K(~1!ykcn*UMi&wstgF2#JUl zsf^8yMKij<3V;3&>w@AhV~K~+xs^BL-l_JdEwB0QB!MK^3@XAvc<;JYp9&m?fK5M{ zrwOdWn)we{nR z>Mt*(I044HA&8IrI5&ikW6W_XP=YR+tWEFmH~ZU%IdF!o$Nl z0NDN*8L7AimeDaN3y*E?xHut9KNvUd4L7J<&)^A!K7amL$kx(UUR~Yv1}oMv$Vz6*H)eYVL{2|5(Yfa&?jEH7y%QL*hHsz zbmI!EC11?JkmdouQ|&&H(hGNUOlimY3`^<$xEb?18M)k;yn;oq<=y+P0aG=ndP1J5 zd;lP471vz9eHj}PQ(9@GpjSpShC}|Of38r zl8~0DzhyI$H54qO+}Ge)U-CmL$!RO$JgRSeFM`ir%FS0gfw>eq)5MO1aCH%Il9r?65vMNdpsD2Xr;qyqQCEcwhf+XP(YO0-Qq6^~@%|Ss>pKpV!um2Q zAr@p>Pv<-RZ$UtEsyRP1`ju#P<3bl&VY*ifH-nTe9iFWxZ6{sI@HGYt1YRHZUjh+J zQq)iKSQ4nOo1Yix?0Ui6}ei2srKN`?Ko6 zn!H$rR3^T#W_)6z>)x0qk(yfWov+~(GD<c`V+A`l5}7pD%tR}m1MxkNePIi0dd#pD3Sbq_85z4zakEKCsR_hG(uG{UoZjs0 zb|Psh1uaOu;Y6bo|9Uzl0cXTEYVdL}lvkVSaxV|@Se2B+w-D&MP8c^VlHlUf`m=|X z41N6o7RAgJVcM5|8Cd`+_&PCnhWDqW!UYzz?)1Gu-2x;I#@qbkq-4n~UOUYXu!|m1 zKY!67=V?Q%E&ud$=!3j~ofy?PkBWrlo(YK9nPbH;7q4m&@Y9({&;1beLa@OFG1a`m zO5V~pI9O9rlg{Qfnhv$;=^r#L@;KY%vfN!2@k0T5Qc)sTyi@<_KHBR%-+`uE78@To z+gXes?v)jzrb;K5YGPmlZ2$V-J^JKHu8y`(h0n;#iDgw(h$=}%wlDv+(4wTJCEI_* zknRgwhm7RFdd>p+6f~61bnD%}8b;DWpvZ2B$m+&gp;f+y{f@GDOM<(RDlHkAkjtXF zntHyBYkWy_R$TaghK~TbkFs>UupY{k#O$n`Aa4pgfUqx=ROsn5ri8SdH*(lafED#l z{=2a}#nRQ}85HCPz3)8B)dU64D$zmjfYaL+eAn`Wc?aHUhd~Edk$Y?mxjM3_RF_|p zd1`htRYUV9XV@dzLTZACap{apR8rhpL!2S5UT)O-*mKz+b+8nqQn*yS)HBbwiqo*q zSsQ!vRnORu({J{5_I zhT}d6%^lQwucf>&z(w`~bdfZa4j1FC01>>|>ypJ|B{kjeT=``&ImD;$xc-DZS!I_60sio-39&NF$?hMyK>Lcmp8vO=J9lX1x`{66%`;I z;BEBe=WA4|QdQT=W$<8;aO6&`4igjgwhnl{nPs=sgfDH}_0bbroh4;cJM-+I@ zxxwt*&A!ETO|bb{pN{O{fMDYZa48(jN=|FJxhGZQ2pYRaDo8)fMM#aDre+v3;k^Jj z!7D5AoHcldw3@EJ4N+3koYvf20lgFp04sT3*j^saw|y406tRMq%B&qB9Up{-7O)F& zkAp_PQ}Q60g#!TxPK``t`L}w-_u4wwF*4M10{kjNM^A*+wSdzA2n^4C@^kgh82b98 zCnveG*SKBrJnH%2(I({c&B55gw*y0eEOxG_y`U4SvtRE3g#~>pJT8`ym{{>(;sb~E z9t8tK-ENRID8XZxRHSEQKbpWgPj(Avtm*0+-E2dX&y&JSshX!jk^eiKt{-ne$Y+*p z;LNPd>S`*{EHd?ZuV3~I4_Xv9B}NQHCt1EL@j?6!GdJeGKe&B^m54uuO2lKm6Pfoi z7$nY*xs@rh6!Wul3Q9^d3Q9^i{6@*8rCqwW-B5O6$%0{y*tf-2G?DQQ4Ib=KwuFT5 z&7~!5ONU{I1_cBnY+fejv+~O<||`v2|}Zk*iuN zG`l;@O9R`1RN#t5OKa}iH~6PdQPGb|tasHWJGv*oO1x4r<+3FmlYeixa52WZP{BfMpF~2W+u6Ah}pnMXH2=1y`hGq{jf+8ruIv7 zDxXWtGa_jSsgz{4K~|TS3qo8wzc^d8UO~FToyQk*(mi$w9Mv|4DYA}(`+)L(Z+|PN zs7P{LEHEO&IwwE>qvKf6!FOOn$`RGYXSDy!vmCydlc71pY&OieyJ+^qJ&-YISWwRU zZ;ga_8G+!)P#;?Z3!kg?YSYtiu#^5puj9CQrg`8r+lb~#<~u9d-zzHzt)-Z>v%Vk& z61i;aQN0SWmjtRRMKZ>p_pbz;9Y%Rx89&deXZ3O-n!jH@y4YV-m~C!%o_`2BE#PRb zVw`!}3SY@AxbK44>MwN~I4>Z4IsR7KF*B|vydd$8-1vTVO?lo-QQ}}OeiCR4&6mEE zVq&H@xKfZyuKq6k1lVUr=4$Ll2fu~;s)ZUm*U&;B*;Q4EN4di6VU~qYt52K%^pwrT zHa~&%YLdDmb$G+?2(3*cslc!icXy%X4Q(L)uIElF{yq#D5&j@c6&7+uH|&x@~-3Ju-L}xw)`qgcH}4NJ?D`0Ngplu?c^>nZt&Dws!-6 zVOC+`?RZ@MN~{6Q{sOznT*@T1U+;#-oke7x3%gB8fn3zG{Y!l1@A2u4n=T|85&a}X zWd(obkOIdS8JG@j@x!2mITXg$h;=~n=`G5X%B*|k*+v{f-}Dxt0+`2qrek$d<4C8< zTGSy<0HLbzKmEiCmzPJRLavYYmi%hafB<>aM#~r~H}F!9*KPg$sCFr}b~7y1&u-n` z__P1M(gS0wjSsdK%~n^`yA{9(F#E*&y7LT#^S+On&pH+xU$ST(T?&lZyvOtdA<R{Z7%zsdwuGnpZ6^}1*U*o(@)Sh1Uzdlo2k#An-;$R@|uR|nCJSMdT!TqbjLN7 zE6d4oFf&WBzl)ps=~t_e!oLF$FViDq@-p&G_Ua1*ZKAh)uJCkDerg5xmg?}!%vYLk z&HJHi(zMl2H|)r}LetqTce}DX&ViXuIa)JNAvpM6&idhH@TBE-5-{}Th6cDaDrwjL zG*jv}59#2pq z$Z$u+B*rEk`Vk`XY20}l>1e`~(L7cGj?_L-N?{huw>4D6{_O<>oIc3Wb#$zlcxMYE zjL4Z(y)EE+02?L9E!9C4yneD87i)c`BG^V5U=mXb;+*sDGg{itovD^#;Vod7iccXZ zs-YnA{6O#BFWs#!LCvKJ$y|Clu59&Yw?CoPCMe6!u=~dAWcF(3cmI=j`vZ&k&kulq zoGt%@?FIk;q4NJg^4?cHZYmprY14K#86c5_Q7X!^Q}0- z^*?Mz@Mn$?rxjtv1qW|~daBpaSp3=O(4JpsH<0LQSxO^>QPrH)VZ_jKt1Er5>mj3_ z1FSrqJ4m&?B)MEp7Iupp-L6GHBY0H&Mn)oCPj%5tM@5QH6sJd0*t`tf-z&E6C3DJ1 zX=*+<{P!FAHF=(F;u|}1E;iDEQIprH3z%K%j{UlD8t`U4+dDht@3l1h?RiDNDt(X7+Zs4lb)A9=)cpII0AJm` z^hQx#{yPcvI5~)_+GGQ7BRl*CV+G*zVuPF#h^Lt~@tLixHvWD{<#^rW>*Q!^cMbvU<%b35=s-qw;27O-SQ19JNk zX0sp2j=_Ub5Ey|tml_YJSQrP#6{s8jS;K?Ch@Fklw6*MQc{O; zgCZ&yL8An_lnsh&#mC3$hg$SglSB0DUyoCzErI6W(9pmP)D0^_ zgU@H`78qUZXs}j@>Fiya$-FygroqDGZ1n_ zM$i6%W@aWjjoy)CY+yUOCfAGy(SpLtc}}-KU!B@+021XNfcW(91#ZPT*%c}S5T^Y* z3)n5xug8}beH@+GBPf?7$3Y`wbA558FUJWCm*ivzfsi=8p%B=+f~H_Ng%G-3VH!-n zRidNmoBR4?^EAO#7#!>sE_0)kGc(QhIq%h=S_FVa&+^O^8Ct3P4sf)7)Kf^Gv~*N7 zm0fiLaagYnFplVHCgA}shvU0>JK zQs?C2(2~Pd9E^QTrMlXMlap&2RVJl1-Z~Ow5q2$j*!k;a5j-xgOsKG`sv76WX_kW; z2M5PpkV>0_o5A~FBof?DJL`)a%v|*UaRm7D?Xk{PV!iJa7YE)max!xF?>C%15w3xK ze2OH70-~7}$QZz?goIxPDt1eFs7Afe)Mpy2D z!T-rN$48%pYmE3{@`NcT%PVS9`P>ZNDSiMU@XoE9+DpJ)Tq`3sM^06> z&OeI)8XO-R+4t`k)`~-0R9MwfM&6!;&1RgLtLKib9@|zU^i{pS(=V^_Hr>j&o6tD zPJDQ{NB8KP`QdJ{EEyia+9nqyUZiSIGclKV2Y-z(p&6>u?E({D{p zF0`~f#giBEI>`ylc>2kAI$d}S@P+?*HT^y8h5ht~;QhS6!GitoS5*f04&asTNs7tn zYpUrgDM_izr~T*A-z@&}Imyyj%|smRDBK)(1$(m2mNDhN%);=N-*^%5I#ABKtxcrHwO$X} zP@CC=gt3-|7yo@{2%a?&QEcMI*Eb6{`RRd=fsWJP|GZDn()oi-VC{g@|5+;Y@#e>& z2me?e{Qf=vKeM9MiR$BjZwauTCmpV>+zQgIv5^UbfBu>qX@L(UC6wMsya}hFYmZz5 zK0302e}A(S7ma+UsIASMzox-a{VbQDmHx|r-f$x|D5(8^G4_@LRcKqdCF!QNx}>|iyHk+v?#@Lmy5mmw*8R>o@BX;{Ltrs`jyc8`-Faj^PQo;=bX((WnL zn3m~cCEW45e*>*#9t8mn#ioc%MVU&DO8H8&{y;Angx`NORrdRoR9F}!NEyj2s?mq; zWEIfRet*yLNLLP35A`EdNs{jQaiw=Z!q5{G_6D)!08A^z z&*|yQGrhigCnrn{41fw#Yj)j8!EFGZDMkv8N~8;b)>O~yi#+lZaOsxRtMUP6v%v+$ zeg-gMY1B$v$NMV-x&lK27J;b4^730yL9RG~2dHj6v%K8Y%VqAiw02*KWcH51Uwqao z;|-wpcy@XUZl=cvi+39_;($TGAPN#ei;Y*jzKCU|m84W;P52m>0Pvgzh#p-3^m>^~ zNJ98aJ7jMm$PmGZ&AZV!yZu+jGt2aDmnPsF(2>Gp_p2!_RjU6*Z|4MrTGySg09OBC zIpE6>1{72}3Uxy!7WA85x|I!edE&$n22?JcszW zj(_Ih_M)4#m!9TH$XE6gg2$AJJbxYT00+U$(}4Gr?nfPkGd86B^;T=0Q51?~F2?TM zjEq%!B{UiSuSq13F+Z9fkCSo#;3m({2aZ}jeBVD)0gsFa z4DK!5|9MEXdb|+4-`LhUO&5Uhpip`$3OcbWlA?knUUQ3cRYio&XPMP&B(Cc9Ms^1W z2QwVPY@#26=M)6b#4 z4dk#?2#_wt#Z59_Xr$I?{J9Yzsh$W_ZyLY4nwsw2-`%vg6Y>$fL}Q}=J;N>Au)$Ol z#u!f!V3K8lLf*~=&~Ult&>_r#McAzHMyFO&ySu*`pbqhlzExx{43T%Cg>F)7gp318pBg4w1sNWA5Kwx9Ah z{%Y9@49W7u%KG~0MYUNBwVGdO=+bRh3J}jsqynz6+CQ5Lek#J@a2RI-xh;SHp4u{< zECc~QzBg|IZZrm>Z8OuKrvQ5Ay_-wRB~PPI>dhBhE4O?E!rmUvCxOAf@`)4eGLSXmM6lJlg;(?@&LobLmPyf?QR;djbxj4S>R9z-VDO!NRJ#kgz(z0^SCQG$ix|KwRMTK06w@=SaK% zq|E-XTP)b%pW|zp>(j_#7N)Fsit1?owV8ELknCTNFv9$-te`uqlTn%j03TAA7gkeQ zeR42!ai4?@@HiaKD7CH!7{4Q?Ljol#9CHQ{_5;gvNJAbQ^-NJ>uCX5Kc?(x$P$h?l z3;jw>1ngA%K=v6k#yYp%^8>z|{kq=2jIkrT$;ecwFw-U`LNuk~n{Pyfh0+6p!`6Jq zQ0>OA_~&a&E)=pic9ASAG(M^*a2HcDao8_3t7tHe8G$QMD{+B6Nxi`bZb{rP@TI@; z3=chm$Z6u6e`e64vWUI+%+y~_TsOq`V$)k zwhmF`krgmt?B#vVPH|wZ&HjF)i;Kie@u-H*sgE_D`k8p4SYJgVTPC-E&$q=&xRN4@ z8UAsce)0$#29Cmgg`l-gUDT^CDQ$j{{qE~;rbiBs@De!I4U~30E-uCQ#WOR~hE1F;M zTPD#2(m_UE)-Om;@)v2w6nyO0=Wm$;p1YjmX_*?k-wvBIA`MtY=ua-iuD0FUtjV2=r9pb79M*fS{RSy;-D$3`6Mby+&kOM8c zyikcD>J_ND|B~gjp{|1&4O@aFu_!Up?ohC@IZIkqb6YfR%4vxx-L% z{*JMOxw^FC)ZW2ZNq&IfS(apPL2j+}3aa=iHVY*U2^-V<^lSlFIDLAGNORBPUt2LeZ5*6h$;h;IopKf=boy_dO^5FN3 zN=Z@G0FDaLtE-FR5(*M;QKhueLV+W~4~6A%epfqh+uhaGGz%?0;qMt0zEW=+?T;d* zNQJ~jFgUbT(5H$`7c&df)f801lft40K6LPVHwGMEONyM$oXPgf^%Q*PRHUaUYb=s z08-F5aY_svmGH1A>AFGqCq@k22u&29Q6dsK@3qUx@@#~A_g20kw=TEiKPa=_St>~dh<9xFhkJIg+?d};u;9RcApL3dCU4fSKf-ao*?pv*)ph3;| zRv6%UC=1o)u-3YGlO{F2{dImc+yq_HVSlkeG36bw{Hw808ypnUF#uroG!vAgC?qGe zsAosCR{MMnQd|L=F(XU8ZKt1t)GS)oS(7xBZph8NDH-= z5T_A_inXr?8=EXh~;4?y&&h}CL3wA zrTe4v^%_ZYHbAbbtGgXf|Jb8E>c9sXa^Ocd*H^OyTuy#VdYf^}QLJ`b$4Z1&M>xG? zxqfet2qVPePs6j9*#l3 z`ssuL)Wq0mK=bsvC}FCx(T$<9vihzrFSEA0b=VNti~`6MPulW4WDQj8Bb~$(KaRDH z<~Ih32Kqi-La4|8V7jjKyscQXPKcOd>B01Z%L9O?K7Oo2#-eMiR^W4ijMm9|d&4er zI3rTb_@lacXlZjo1FHofyY=y z0sO>#^F~dv21vKf^Ljxg6Y12kektKoD;?{b6-m8)ak)^7g&q79=kHCWA}Ap%uOOTZ zIsGQj_;oZufok-9S9cM}UL6`|zz}dTCpZz4=H-tI6-!I05a4|n>>SkIO=%Hgdm|pp za5q@A3#_CgmBgLQ1|@e}(v?Ismkwi)mPkp-n@5TKV~cMFX!)6=YYi!G}4OuW1ix!soWej$hnOg4B$->T!xEAe-@ z@{*hHt0>TOL;Uk*Kn0KRpDc^#Y`<#EXOIkANrw1;0A)FS95^U|)W?z2at? zR$6@y(1&M|a!eJaL1Eu0jq=EP^TCteII=M_i%1%`H*|DtEMBF>KhMcZ`cal511>I_ z1ao#rp_yq!#H*)|9}(iQaV?Ja`h>qdvi+>C?c`KfU$0O9DB5D_5hkRvM|gl;)Lw!K z{9;&mq!11HHs@nRoSbA=R8sD} zE?w_|ISdkFFd#epJ}?#1b%?lGH8s@ZAU~gO$kQ0d7E9pnJ^IAf(=Sw0Ws~}h-lp&P zxI7@>82B^F(%X=SMMcmv*Ju^UALWmaGhpAbb@AiRt6NhbTaaB~v? z)c&1tpwBB>pHgsGr0}0mKrnW4-Gt=^d6dB6=Q9sZPEG@@XT znS+eaC?Tntq9~0Ce2lz-^-&^+2dK#51G~b?NT7@AXoEoEAXx(VDp?kVna_zkyx}kQ zjcvWz@hpzYYXSoUMMMKXe4>z#kt5wU?(Vj)zZa-#o81XNMt6+jQ}ce2y5E#r@H?H}Dk|wiSn^K- z6|3s8wzw6mEXC(!Ver^p`_~rMQzAn_PQ4pVj&tg4X6}XAGqWN397QwOe{=FMwgfmn zFRcr{p}Au6!(zg+h9n{iMvrNulcN64_EO-T255?pOdzn^NIm~I)BI^mmLh-SNOQTC zlZ>SHPc8tG^!(oh@BgbKEeA*IYIip|DQOQGDY)P0U>>xAxmG-uJ5q}XP~Z6-Zf>C!?^hUtj(EA?a5zqG{C%tuU=? z-`cQ%`SR}o73L$KjgD4NclT;PwFKF0-09h$tUIVaoT+h?W{YDM;`ozX<0{$jYJKInVj9ZJ zt?lfBn~KmT5T6GNiHYd-O@I;=!#LvU+5Lh?kiza<-`x05AyQOMA)z{S3=zA2vEZ8v zNRN3?dAZEeB1cd#Qof`7(X*a++R}U1`=&+pPPO^Dq+6RCODM=W{U)6zYZDW4zKDAf zP!-3$aac2|0ldXz0fB2Zb0;x19x>C$Pln&s62tzgl$; zJ($n&Cix1|O+}tl?RNdzunv1Gn zRW(#8>d{dJ@}i}tKeXl-u1hEKRfNlo%E*i*;q1>G&o_Sk_7nAaBbW-{@>8tVz|V;A zJn-(x*F>ECof2+ARZ(8`yXc3z01-SYv{!E+S{A$;)Dp~!bV9$LYl zhL>kR<+i+{!%KSv?xEoUW0c}|<2*x78#zkh&YC06XWmKD}4EXUS0#A-xjV7Y5!rf6BWQmP+!7j%ATdOffnVm8{98%u_4J# zQ<|As8JLmO)KnE%u?A*_+=>0q#s@oJFoj2GLo*w{ zmD8DfJtxl6%g-mTWIBIW1gxlKX1M}UW1^)6vh&m1IDx3i$gos~Kb<+NxM5i(HDv0`_=b;d)k< z!5MUmSJK#2_k*q$zM_F3d1!#YgOzn@HC@hOfh6=mBia0M&tGcn8d352j$ULFSgf|5 z4GFJ(+smpM6lLTx4TB#2JI|vp72gqAp@f6IiawOm{4m_(m&+3H1?&)MMOi9pG7b+& zr!2Z;U-#(9NIaK|h_LX?z?{R3=nE#xjcYTJRE-4oYygzud`Ct^na>TYGCbAgvRb=x zy4RBjae(^Dyz9`IGQW1Qvy+vT7a!{X=)B6#_Klrtu^=a=BI1eJVbpF7sow6+flTLy z0AEM8f$o1y|F4IV_<-!xC`G@xfkx$l#sSvOPNXDlk%7MPe2HXe6THL+7&8%okW!Ty z9#{Jj~BQ2ZBLU!Ne+Xkcy{76HpwYzz++j2LHSBj{bJ1Zx4pvQcy6H5eFM_ zFavuw^#z_bg5RnlK(N86=_yAkU4>1Y!K;^OqRjs;APxP2v)23~G;3Xruhc3qxIvug zrx2~*pPS0d5F`1_IA2d!^N70~CizQYAwXIDD`kM2UI$YUKO{Rft_QPhWgP#J(N*oA zmkI#Cc`=}C`5T)6tNTjZ;PrJ>E9!UP%3EJYt2_+?{|OoUb9{XAb)3cgO5mf|BQGN# zp(qs@7N!St-)?N;4NC7C{FsEV{tV=>bTa49sNCx6`uuS7-$yAx=lHf`?Eh{s%y1qk zI{yB`Qv#I1|9s>Bu>=hY{J($WQx1?}hPi*uFkgh^V=M1Kr&u-|BP(sZBJ(vMzqiuW zTufzgW4PQ;7cN!VP=EN*aDQr3S}-^KCwebrPyO?qmFa0Z^6MkV$MKlIe%{c+Q0MQqJV}kS z;{nA1%61r-#(L+ZTQv>UyQ_yY0#J26{cVLwe}9VhKvDFJEYg!1VM5eFUG&?ZS)wtB=1Lx;OD_lr+S#4koeinCPs{ zT=SkQJF%KsxY#LDz=4i@d>W5g$y`bMt0TV}rr`vhDa#X?V_p2xnh{3u&p#U1HxDBI z`s;uFck`Q$!DO@+2-h@t1@T}fUf&b3>hUimhah@p#`RRo(#uMZo;R`!6BAahx;_&KU+pzx!b!!{8j(IJB;_M!;B+PBC!Uesi<~Q zJ_-IXBj){jYe)b8>1&kle}+%LOzsXbwC(JsOK$Q5L@+(=z5f4mM-vEg+}5`C7dtC` z62NmI1b3eQH8q|F@WCb`{s}u@FM{H7fn8{zWqW*x7q1{KpY*}*U>`e1HUfWp2NR^h zCdNm}$h#40-HH-b1<FmjO8D?8l5wNj8^Nn08BnV2`5viWCm3 z?+3f+8XsLKxo8B3TBj>i9jI`DF6QlWD~t-)6PQDs1Gl;=BF+x0Nm95}xADpGzq?8A zb?(FT_u2zXVMa95F6YIh+=+!ogV5_Lo=2*m0bMpBNzYhcIP7)1->6J@p()NdV%V;a#)YgeDE`-CJ~?rd{@N%eUnDJgp1;GFy>ZJmOOnERS9 z(h_7Nfh=>A0TehIs?ls&=kbZFd+C><7^TcQki4p*9Fj!vMn zLGd8zP9rV^Jy~-R`M7WWOataR-1Z`n3aqT+|Ji=lMhj%$s;biG_YmuW`};7jNVe@6 zVjC}%f|MLstOJaXK>nwv2k1a1~NW+dM1+IK=W8cRHD{tgI%5}LN0giu`(tj zk`?cYC7u~#hw3S%_tolwoUP=T=`yh!DRthl4 zTMiQgNMJ=sQM?$9Ap+}K5wvk}xbyGr1C9t6NM$j6bb9z`|5eG@Q^_Dh=ZvN~6OVPU$8vh-k>+Hmr5A-K^Q?d)`8U^xd3-g$>W@U*KnWiKgUetZ~cvfzxt?1{;2wVMNjYnJvO`+oT< z0khGv2%+>+US9D0fzZv++^8ruw_893puT~xxvWT3a`^g~L+O_<{y~T&gj8|*N95#k zg*&e(fXW;IB=lDNMBz(69nW-eFc(W?swqv)`Y|^18eWu$1cfeqQ5BG26$eua(VjKj zoI=^OrjK|VFkw7%Uh6F&u56&)xSqig3YYW}wKS3L8qvd;Wf#YtDN)?#`7{%OX0m}J z_`lY2iQOXwH}KBAhU;W5NzREB5f|5)k5uk_ug^Mz8HY(m5LRjux(N6?6Rw;SVj@8{ zN86i_jfkWuZJjM8b@ilZa^z|Z;T%fN7IF^anU00PHp=WMzN&P0FP|J@5&>`ciXm8- z;a8_9@+ra4E04pzCYqXIUUQXds83XHy$Y3PdvG(9_Aq7Y7TPqb|K@UF2{9g%o#H=_ zyGjRoFZ5zN9jyrib3NW?G7<`K;xREXKxj)!IXt3z% zeqQuA6q1*(65R?xFwWmCA?IY`5mc018qOphVl%!qq2FqeV0L>#7$#(UF=>ZHl@zv? z1M;-Rk#U<@(sWW6}KTDrss^1Hjw)I&X*Qq{ph@az8i&%py^gZ~1(Cx=#f3F;fO zKN-sR=6!c4+F`&zVVqA}^bP;olN($oU;g`a9V`?N2}`*HFMm(PR{Gm9*kP6b%Vl}Y z@PrbX zU$GYpE{ng8al$oBs2hAVPRK8GV6lHhdAtO-ej(AYr$o-7exy4Wv)*g<>>~LMRP;-J zIv7M*OG34plmZbKhKl*s+k}_D?#D{5|GY(mbtA}k>dE7MyjgoDZbg8Nm}L48tlP)u z*5UFDi1G}l7~7G(1mOsG878&t+2YU09buUX>qd-zjZF7A!9#4dh5kn4 z^eq1W7jS^aKc>VbyeTJ0TzqzDtJhKY(Z?RqR>2=za{SnY&uofUf(W_vQn)2a7Y0vc z-m$T|-p5F`O~ie)#8Ch6um2fhv*4#n0p#%$u85^5hqhoSt{XoL`@@RehHEO#zDGxT zDW`CBB61*cAOFZvt@Rtt73_Hb?D7_;alv^1w7C(A&D?n+KN6;ad34oQztYxdiP|A>ZBE9yBVGA&@lO(FqPvJPXV)>5Lmt6gqAUw! zj|M+7l+=?Zv(D_aKfwBq71b_|V-40CC5{n}R=Pc{iSaQEB01_cDV%q}-0s_=_U zC$I{O`pIrpsy;tEah+Fd7Z0A1TfKqod~?6ZIFj+MV{2KZ!6;L7OP`A^w{;AGTe75=N?zbEq zCR@q8=p~mUHjrINW5Z|vii(Wza2`J0y-D1g8OqI`-o58WlZWr_APRD7=@~`*f$(Zv zWtLYiBV{PbuJ86lf)Gz%N5~E(wjArOPHw+2#G}XBaG16>y-t7`s{nn-u&uFlj zS(f30u@lH_2mP`ZL%hpncbU>(xEGJb?LdR#9Q+mQhi}1>?ZIcsipK}uuJ;3ymGIk1 z>An#pL0Al}4(s3QDuuYUN51MoWweLz)MIGk&2;pn8y#rj;2XFlO_RMcrwzzU6 zu*^Q|ZhO#N-lQ<0qsK6v2{5!$-*-)VxH;R~RaW@>1>ET0mDD-?NnpX;UNm0H{v{ZS zA;b*_OROV+e7`#dCCxl+z#X3J+6@LeJc?_UEL`p7nOFrxO*@B`oVc)y0roSK?!pu0aCa@xPwS?%$nh={A@umI|wmzU=R_2^@t zSKCoEG>(YpF~OKFnYt34qS0ukHJSaXZ_HGouMCuNWV>)#tjXC4>_;!dZ1g44+N?5s zn(J&eK7W30X6)p&kz=x0_JLk+m(9-V13e?B>z=8!735- zRb3tI(!(h{P*t^o_E|7ZF2UJ=0KWNNNnCy-N3%wNziS|g)oHPiY_zWL&48GML|{+| z{_vCmq%BUo>7{rar`=wXXX{!Hwe5xV!{GvRoIOEbm~?c^gjD#Ld>Xd+FfY08?`LVM zjpHJoQaN2Z3dxSULm{v)4uZese)n>z@If5p%G!DsqM{nBG#~hsC*lZ;Ly)V_3eL`t zs>>^7cG}BYe}H-9b@_W9W*123>!Y3Avh{zFB-4TMaqN{V}R|DlUdQ6BM(_s0-H2bCaMdRjN$^qXZm-Fgg+CmE=_Pj%%MMIvO ziohh0>`6=x78hD<(P=Mz=jJ|J8G#qTmyT)kMjpXi&fToXg&o&i#rch9D3`+~s-}fx zK1fGp<&aV~xbK=T=wUGHV^Ey{b9QVyuJhrMF_4_f5JX%oHw)dIS-QYmXtay@(tJPS zmf+SjZjn35oKAa#l5}a-<(|EXsOIKkS5eMDLr$%ij1wi&!8oqB3n|?(Z>%>Jh$)bP zkn_v%S4IBLJ$O!@*FKuem1-wz3W6GlATJc_;|X(rxvu@t>#1w)BrJ?6`$ZAZTkFcPNE_?tqyuF&6+HtKs8g9naWkwbg&JMsi9eg{!`}>a^z)72f}I1@8^+o3H+ zXz{Hf!DcH2#ka_xe>8(+$f%br57Waof%{RkZ^Q0u#8M?3rm5JWO=J9TLSb(NHGMnQ zx?w*Hr2Qchrk=4YfO<^98$|kyHi~xfP(C?|hn@x3IyDaRfHvHsq5N21Uc>e0u)Ze- z$NaQU3-oOViroZH5CR)~?anXG4yI}Zm0|`1qnnqM*4_uG@*!OqzG%@xIH3jfG9JMRHUVS`6R^;%cMtZYFA+#CiPwUu3fTaqB>1Y zST=Q7O7;Uiz4FZl{O7Xu{ws>Iqn;->XS(s3-}@lReH-Mcj9yX3Z(?r*J>_qjb7Ot) zOxy@bo1AS%+I60h{VYT(HTL?Dt&ha@!|U46srJ3|UJIMV_klIb;Inl-9yplgBc=2G zxBZl;*=SNT-Ai#reH5W$Rv1ee%VL>4Z$HCebvaaySJdSCiAamABeu#0xRb3?yo7;? zKUdmn#lrkd8kP4oI#X1)zgvoTGbJ*t2rqJd|LU-|%^N}g0V+;K!rwbUV`*PGwN?Os z!@~AaNhQ``DzUb=Yb>cG;{?!pxVS8iiu`g*sd4S>?66GcU+jh+t}lc(Z%d2zWlQ(ie( z@4o>>&BPny!vs`L7m4J0P6~>tw*J%3{f3z0dB1vm6Ft4?DD{W4Yey#xEDY0!yX9G^ zyYj`|dps!3OM`Y#8aC#X1KyAJmpaxLjb91El=>NXcCpJYTK#8Zm_D}^&UQ98C!-)R zc12`Q1Z_DJu=v#PZ6<954CvV)+F}?+*cOUiYuwA&AS~36mrKHLrex}veV&}~U9pZg z-z;-f3)R7=J(NS3yOG!uFPY**yxSl6uDN9D{K{9@jmjBb=7&Dt)FAd7I5$?wsnO}H zx-UM-9`wgGC!54yQU>%>lZI`ovn_~XW_WW=akhE%N|;Hcl&3m_dW&f|#!lBOjhtva zs?CF&3&gv#r@We-f3OV8-|dwf#8n3r4dqO2vsBSMd1$0&Q#8E4wVQh=-bHASv+rvi zOXeh#8>Xc-9aJQ!Bx7plH6qM}atyS$(rk__73xc<-gxVe_VA8*EyNy-W4lE#(8}A) zGBBYH?WHWJdwXCmEfO5|p*tsQ%;!I-||^06yZk35kMQx@4~*TSpmVz^gzpr%@+g2BziEo z?3KJfX_*<(8ntz2kPI@K+8-X~NkE^vHXj#_8MJqW%0I9!RXngPv{cd=bKepbAI}j9Ns0tD55iw)1l73TV{dP{cPc=Gx}7MWQ*oH3ysMsL~rHTpr@0jOM-U-aJ<`zI_e05Eh%o@V)s)Zo(AK`4)z99XIBuGsMRI6-9L@V^gQ2mM6E^zLTwu+GE5qMvr>ERbJ!hGCvH5Ad!bh~xJQICIs;|t1kJIlA` z?0)0+5I{^LnZ&?EOID@rDL>b)7v#_(7mn53fRhr->l*7_>*Zxd-qP=8M z?cy$bgwQw0UEfcQda2P)asP*N6)Ew2t=?AYk2_A+3*gO7K&Az7NLkm%$Gr*Dh=^bjBkh7k~ zc@ubcn99$jnk6$4f{;O%aCec$q8d7t?wO2LarH5Poc4)XIB7xYp2Fky&xJRzV^GKB zvgEGxG69YAD61KN%>y(8bZiI9GiRIh<_h`6Fl^*CXMz5^Stk`PUK-!IF|H|_((UV; zhgHQp4NHQy{f-r;cAmWU#3zas?rcM}%kb{BQvtz@hC5Xe8BTmz-LjAm-74Rt_GZdSE8 z$$&CKnMssTAZkg8g4k$J_C@r>>dRU_R?;0iywXRS&7mS#mBKNWNoE5|76s;H+NV$jwc%KVC=LlYW%rMFM1wuMxIizmXhgfO%u zs_a2@S%6;WbIWGaa<0TOtIbnF!Mj+!jkf&oBd9xOYOGD_ro~iRt-3HX-2G_E+}#~t zjQP~6p5u(ZXzqhqjeo(#bTCAMLk<6d7Pdq~v@^ls1Pwhh$IT=2RR8f%TQ@W*FFT3d zI&@xlS(AvWAf3y@HUp-QWjHFML=^h+&O1CxK5wa(O;}lVXtW`*ielc~6vfJ1_;~io zhC?_c@3^5yKTQKMMtq5h)rHz7_-wZ+$F8Pl-ffDZqTqrN?GR#mE$VF3o0E(nIEQ%P zx;7K#Y`=L{jsFCNZOB_E*cJEbGgc#P$x$Cgi-NudkBR(HXG7zd{k{X58~zj1o--CG zb7z9@)hI&$tP|}+xA?$jOxaoD$*DUUEyN0cKNSV}iJ4M6&Firjk}W6(+p!o5#G8CH z5Bv&hc}9>H>*$cUw7b{?N@Ud+DdLP0vezt}b2;R!f+qr8w}X{TRKY?*_nWEufcFCT zbmB%n+m3vdJ%h{5LxAe4Zq=Ob$$p&t!`tgYf7gwM-fafwZu&{EHZ#9on;0A^@!|^x zoI#+390m8BjZvR8ttv|}ZhkN=U1Up-M;-M+8hNK6fqt{HZ~6BTBo@SPKUk;i)1pIWWZ5bcDm}~uLXvU#KhZ7i63a)h7LCJ$V&dEk77itE{-S zr59k?tactR#C9_75m9jzn4_p{5xkHC>HX64l^=v8jNawt0a+GBg+(Q7^LeZl#ddo? zXYPD=BpY-t-VGQQ)(eQ=_WdBkuDR8K;#^^wG~dOUBa!&mVsq?vTxWRiHqlgp;-JMh z1u`S$v*_Ud*qbepF&vRR?irkgSbeQ3*L9M>K-}(X(V?E%!h+C++z;eqOZllK8l<5T z!EP=EU8^IHC=3?kuA+tlgiY0x)=N-W#C8^xNUAg1aB|KYE_N=+;~GyS5jtjw>zUq1 zYAnQE1Y5x~U#DqWGe*0Y%ifXroI<^I$nkZh zg69y1a#1l#E+PwpM4}>}Q#xM)k__IaAeZ^mvbcrwCRvNQjOh1Yil1rjq3OSo@#IIL zXG+;rO(;FH4R4;+oKtNd2t7u=8Cf_EnU7SEYv{*#+v5_BuI254l{Ky4_HjEF8}o)0 zJt@>Wem1lXmQb@RKNBZ}bLk=}@9Sk@W&70pCdb;*ac(V24eeR=>o>1!jiY7q8XsB) za5!+Eptv*`)br6SkoLOOVoI@W(Aj)Bj` zt(Q8GN|jbkDKw~8=@zGSdOEb=K?U6&pl+egvM62Wy8mlb)o5}igMggTMX^@6lr5uq z@D+*E0`~xRlFMQIH5)w>z0(%|tQ1+n#5Xm&In=}^Rj8tJ=O+DGPJk@_<>%R%h*65` zavtY1)M1-)nLIhP5uc6Tp*{hRI<6WndWoonKcWsR55NBxjf zhD0+hT9m!!H82Doz2VSXHcqI!ORZlA{6cqv?UH+A#5k#V>0z5UbhDIBfkrDIYO ztA`aiZ>7${I6V$)c?pG{_7nel6`Q=x-3&cdYB@R>GmL_;3QYh$tf#@EMEBE)IZ)_9 z^@#g+qht{1Wr!$&aAtDYL{1?a-$SwC_P*{6h3A(Xbx4&`!n-6^AJ+(E>MIh0ZOxq>GnaUJ+sJt_ z(k3ibHhDYiVzIaDEkT7jcgE?u*oIG>a1@VPTs9NdkXHp@#KOWQAiZe+w=q$!Mg!j zftLIYl!p%^C|1f@QWZ|aT-;;rg=XmUnivxPUTHNtGf6~t8k=iM?le(nU1%ynx9a!y zfySoT#a*pqU)&EaST374)10%LB{m!)$~{nMSX@fFzFc5j*2LPMredRsSdgsa)@idf z22Sepqo?@D4X5ovU|khM{=?oArNM}``)9;F9u znzg)HiO0rvVk0WyQnTDsnAv|C#rK18yr{->wC%aFGUL>zP?`MM626GH7sq~2&)(R* z2ime110P+;P41Qb&hhj*qAC%3YiRXSsr(;IYxKnucjw&?ZhYUGe#Yh)*acoc^ zb8IyBd6`V=1U_V7ZG6E*=frN?gku$T3%Y820AvMJNrJDn! zVXl4{IZO-~LOD<$h$05E`nP@!TLOfAqyP@lQ?w7mkE67R%XS7jtS1GTJsqwD6 z?AXx=@+#AJuDelo&hbw+1+L5V=o!Y{#V)Yd9@uGp^%E_B7MA48;%UJBC@;CbJt~av z%gkTjzs!IiP;{k@zh9ZMvev9dF1b9u^O;-Z?QOvinX)`NQDQGXKduykrDmTR9A@?^ zOrOp7DCHq2aQ)8hvYDO6smAN>Bxk(9#ku2lJlkbg{dyK}d#iSBos(D*?{>dY?m2AW zK(R=7bhP43Fmi;VLd=2$!?W`bhozD?kdmCU+ub)=R~6Y;lcTQqO!Y`6)K71_gmkm` zM6hqYYO2RMq3kxp?Spz4uGdiQyx8;;>7xR7sQV$u853cYiTxHsd;g5x{fcr za^t+qHR563xK;nee9Yvx*U_&h5GQRMt7}4tCHp#4`*zE>sFaoEg)iOe9}*6*8b{)J zA@U+>qE)#h;4Wp=fV z)W3OulyZ=7mO8#SFwuAwuMT(CnR?}bJCV|0AHV-C>s~}|9Cu^*Fffb>>MO}Oy}rD3 z>bE8`Z{~O!EUlD=gOk~!-Hfz=9!at7fG|NnJR6e4-g4fciMoIFHrr(cWt*6YIEVfL zrYJ{eD}KkRk(R*$x_`?XCX(048u6*X#qpZ{jjR(_2K)N~BN5FkHDT@Ov(%7!CVhAO z{_VpF_ADv2BxxyhMru{D4vGHLp@!eRA*`RIX^{w%{ZTL-X z&hyvKmyI06I=eHCUQ^TT#UXWJ@8jrpSJg3N_;0e1UpqsT629GDXZIz(Lqnm9wDum0 zbl>E-H7tqx^58kj)^kiAwtq-{Ol~!kqv?Ma@Z)4S zc-YG zVJW1S51L#ieOR--@ z*KT?iNBNJ9yMq{Dwkrc`?xp&G3+&V4$8gE-6o!4xS5pM(WXj_l% zhtoWeXHEkKM$7b3%86|y^{(O3EN=4@%roL!)?%eMO~Y##i{1q2NkZ0G%)aM{M!Zd}Bm5$l^`7@W5+ zzBQ-2>y3%xVAs~{iGqjCfnFBJu0I_0-|D=~s(FndAsD?R{QSx(Y;D{fQ*}@-XESEv z>6?4}{$)%ENePD`Te(S0k*Igxl@okPUjT({Gl%Z0)fGAuOURiL+&#j2z-YozGeT!&tg<(8GJm~F>GZfhkl3fx$L-CX1EQRWK8t) zRmJ^bhl&#qkB`KUdrVM_lGq&{7_Xll-1m}J7e{-bQzD1<%v&vLyQ0J>^Oj#|46^2O zb}?-zFbLL{i1cPj@kXi=^gNt|7uwb2=F6QizpE0y{#?==5!Khvo#l~uma)Q=SgFtT ze;E7fxG1}*TTB!cR7zSvYDnpn4u^)JOS-$HRRjbCL>N+FzG+?(TbhU;Xa; z{d0f!`EwZNsdM((XYIAux=IPYkR*rbv9MY)m;FE_zB-H?&Gq}7YROBHkCk=>WVI=W z54!gb*MT66;Z~K1*Mf;Yb@j6Z(kVrffZQP>rJhCzsEuFm>#D*s~^*WhOudfEeT7Od8EOqfm8V|00->?PiBl|-q z$x91oN3%C~wxp3kf567Y= zS?Bh<$sd$h@USs2X7amcx0)Un2OJX86H%QY>b{?)U)MyKH!`+>bn?Y1Zeh+Q--goJ z=iqi|<>;Y7pjZ_8hC#uc!`a;AaLmW(lt3IZsVB=#^^srQ6h?Hb2 zD;vRO)HY1ql7E1p(_*N`8^wWjz@edEKc)-qA)+Lav!MQY-MHPO4d|>Wmn+R41#usb zm!I9qq59d|+S%9E*Ve|wm|x%0a^5rTc3V)%PrwUUly>+kC=A#g451>OH^&;fhQi|q zzShqlI0_4MzePOvxO4UM=ls~%-DUa0v7Ef5xUXNI*xMZ}EQC2YI?^D~^6|ok>%8(0 zztW+{E-cU6trc;9UY|@iR#p!F`0-;xLOFwMx?8zi*4JRqmtnNLd{c}%wLM0~7l&&y zg0Y$Yh2@dP$})MDahN;o?pl3Si*T|$|Ct2b)_&igP!~kh-(sKgQ3&DQs1(FEGb=9* zCXuNCL{uhKHHuW}LRH=8=9dUKc23V-O=G63aWRPwR^X{LicD;5wKp%X=bN+9(T_FJ z#m!eV8Ol}i3yAru?C?zmsCfbd{E;?u0p7^mcw!yWNJVQ+;z%RGez+zBroX6hk5)L; z@VUPfWu$ykMaANIsm8F-+!8Ivs#q7K4?iW#MY`nR%*IIvyzlsZ4~*x1EhX1iB0@nDCnbv#;Bqpgz|pvAHg6> zpeiKihl1wq;P5_3MoIeN_b+IAg{tBzDhxw*rC31rS@O{%$A$s2x15bN9b?=mz>jWTzYg!mQp1KOz(z!R=-&QuN2 z?XI=EqX?74*2B~A>zub$Wiz%>Y!8P-3(>$AYW_%}p;(I0qN--a^fN-1brllRTTc#c zs_l;M$9CJ=+FCe3mS(9Q0iSvVg&`WED5K)DDY2p=COLV^0dAfg)-ciycReXN`FppX zkFOJifuXx&z;m2ANiHfnn$N)I=4UclsEDRkSkCZZ*XX@m;UtBGB{Zt4Ka_NnY;~^s ztk$V1sh1haa-BUR>Y462dyICKyq6tEX_GHrtlL*=nweTyXyOrA19RxGw``3$SavSm ziI6n$kb=XnU6eAUy=ig*ja;7dIUkd>%kz?ew_g|kS=GNU08A3|3FQw!y=HN9ajaNe z)k-^Fg6{a>fPtDCZTDo(&c(e}W=DIPx!7+1;88Ty$Df%2Xde^u%R~L2b<5C2^mPvp z19>iDI-Kkx1vzzfBfu9iGg@!ao6?Lk{f=7r5T=$-f>1EEFkm&1;Lj9srDBdTllmI; z>CJz4R{TOaC#6R-dY?a$Ti$__k#2gi9u5IfFy2w97& z#J~S&GCJhP@K&Ttf6(*!N%E#Dk!M6@ZhB=9rmT3#tq!Tpy(lfL0D8;)_iz*Hg>)hOZA zaI`<1t;AftVlG}HEp$KQcLqXh@coBGFwiuZD4j9|q2g;88-mXEsj8@ic=xORd!2p0 zG>gpsBCoP=ctgoi%|Z8t2`Z#Ba24>i|2ZzH{?m}b{-1`764U>X*8cY!jdfW++?=*< z>B8mIG_e2kLnyM|=}h%xI1v#RIN91u_Ql$#r^WfvHz@o!^|(5o)6K(x)%x#Yaufek z7plUjlS*(vVq{=3Kfj)qpWplUhf2J1@!qMasxqiu&n!u<$P)8NV6;mAz@I3+NGCz& zW@k$tMKb&-EGs#j~?y=(knVYO6r7uWt!@`-Kc~Fny^aXwb8dQH^S;LVLVkVZ37PasTn_*X)(J zE*E2ScOIvh<-}9k+8(@kqxUr^&I2xBv(uAnZ!E*wkHN=g(WJ^~t;6yta4IJ!JCd=f zxy3|_SCV#P4>NsyVv>rY$*KzXvREf$j~T8-|Hq z;ey_dPBviWA0O@=te<{8x}3VlUgnf8OT^=LuA2Uhp)np$ks>42laTO71|A=xa}>0v z|2jBYO+q5|mBoBD84|dhft>Q$u@1-fByxIk)a&$1;irz@ZS&UFI^9!Om$xf5tt_Ol z2GKKN5cJ6ObZY|>3o8pVQ!}gFO1xck@-{Ol+h>c5#Tv*yN*4MALpG>>xKgk#vo(@dotJrZ?4@%N< zlhaDIzGEOP2i}6RiDOm4X*yBxg#|12nbf z>UKC(bQ*g`yFk@KSRa!ia&(slJ+`r53DpI{2N$8k^3VDs3<%U)i&~!)v>cU0zwRUD zmM++V-Z~J$#>F;61ESPG*JyRsgct{VakT7pc>&rcu7Rncxw)}8hD%MI$n#|*90yra znQn3=PtSN`=?{p;!8JKd6w|4Y;MB1U|2%5PfkQdDZ!62&<05>HLMN46Fj}0$oVPS<=R*nmL z(RmG;3ryE5AfKo8L>)vz?E@E^K7$#{#Z?VFQNUD=j*8;qsSYO~7%V=-n%>yha9*Dp zZfI}+`Lm;KOhhv*r&(kgfzmn3C_wbi<-#Z}UEZmsgw}$+wXf}S_gO4`!VFt&{H@4z z@*Es$Y`ecCT=ic4dtihW+vLz<JbZWJ&1ITe3G*zg&5G@*d+d4Mul2z!TwtSytRRWdDFO)9ggG={~12vR6B z(GA@%SB~0D8$WxQG`;Zn-o3Ng{EJTmtW;Ey*LVCJH~NjcjY;HPm8##vv9YnkI<6(&fRyJtryT!})E^2p4k!q69Dx&51#H??;wfe@HA6qD&Ey6_XG5 zP6>m<*?e>;4UMjE5mA?ew}UV$J4SbZb93lV4rIF1{NjYG+s%?Wo|6!;fKVwzs`cXN zSE-ZJWumJbcDW%{dGayvV|gzBKkb~ksm-@We#zi?eCi^iJ98(VoRq&wv7~`u7hg-X z@C@=&9+zQvhI2K~>vKG^Zc@3lRJr{zF)ItO3>q^EdyB>`2sGT%C`{~t$@-HNci`1< z3lISPEBdQv>*e(+HF>=z7@49P7v89=G>Uz#d~UT$GA1vpa|2A{rH5u~U2sDbta@kLUL;)MOIhvR!A!Wk1bSG^lXArC9_)kE!sxE z1|{6UFb%j>zKD%l-#;^!Aw;A@Xosy6LK3*Q(6%O~+u9`PaLl+vs+|39)5Xv@W~+TT z{?xd%zJC;J@@sv0rdWjp6SMgA6m_LHK9gvxb!DT|@VO5m>hnLzen~Knj!$1dbSIiY z+0^AHhNtBE)p>U#$B=Or_xs#$9VAwg&|ci&RkNk7cYR$mG6|eHFi7>$s->kRrppS> z;PY@<(HZ14Ma~MaNM6v_$7ct^yE908T5L)g7a2Mn3W_Fvb(PUPO+p^snAq6X;oV0n z3|c11@$>Q0{9d%r0$Zh`<~Qe^mWK+{CKGtv$RNyQspjQ0EJp3vMc^B=60cGx%9heE zr=}W&eKl0owQ?)l-eCq3kvQ}7v2jsX6zA)p@J&^E*gH8b0~<_A*#xC;33d7#l+wlF zO?QDP=HVf$m`7?7H6w?(N@Rj6i(Ga|#g`fUBPDd>o)kZ>@#xYxTwH7%oW-^MBVt|? z<57~K;bG&#^@K2w`b5$stwvTuki3O2E>2gM`FwVAbg;{@6jlT=`+pjKp{mNq@$_*< zbE=UlVqA5{TAORtUR)e~-+FLnYv=2(ZI;XZn4{8vE}F7whIshGQnxkFF#y)#wmlU>?4p=QnC^GUV(KfTv|MtmJy z1tUaVPbJ~ANPH%f=G?DBg=qubG9hMfX2Z@8{4MUsQ+KFE)E?;qfiIx7-r3nHRPB_X zg|oO^^3&DH!OqssZtwV5R_d#{&KkDq`)#L6O>I><0onuIJM3-);dm{Slow7n+8oHN z=wsXb(Ld7RC?T8AoI5!ZdOR(^2CAmz+Jn}0y3V|)iacD#^mtUbVM)pbrokuRQLQGl&h=Kh0%M9bg19!36o2z6I}riw=O z@&RS~pT4mVBYR%}M>tN<6z+aAe0imU77j7$t}WR^T5Qs3{>Zg0WH(7keO?IM9b>N| zA3cLM&DX`rAz)Anvj15q+sTQAOf0h$l_|p7Z|wA5Y7qzKH)dvd?&~XQP9up3weoxu zbmLh`ZeFTz$RZTrFTD84=`;N!gvDhl9JW8JzA{Lh9sre+L6&#w5-=@_(2`A$R?^RZ zQa#x|^5A+1T_k%>cpL2bIKN%0lB*fnbQF~|LeKrqrw}4gCT*oyxxDSIHC3YoF$98t z7Kd87incD?p0^r;Wf*|3xMdDV%4Kg7Wx_FLLW0|%vWAJ4-@ar4!wfF^%G5-F?gVD5 z(c%0RjLBX64Umy#=mY$m1(T&X=PRq5c%=E9F)eL}P}Olsnd7&11OLK)&;mI`mm|(^ zFS*gN&)ofh4%k}8d))VLCLiim! z&H0#Tno*6CD>S*F3Q}Po!|E3G>(bcCKF3*P$lh zk-E!Oi8;z-Ju!LvbBl0}$r-$Ay6f(2_SV01Noe6dOyEvIS)C+qK zFDhd`)+Rv_STNp5PYPPGdYvbRomf-4S}4vy;wE|d(Aw)Drz zFF9gO+0mF?kBKzDMaD)9Y${q6MqSoyZp^Ki(Gvd9>~`VC`Uo~jHy?t1^yFD|a4%!2 zwXq)FlOhoN*dNd5#tMYxLZhScJh?7ot!r@}YHWS~LWeVW*jD<5$C=Z@DqW`GPg++W zHrR1XnJ;3*365=8;wOdoP2uc>MhSC~c`X_fRh<=}LHJAtFgra9*Y(EEXUgI57^ijU zJ>WqiOpckW2*<(o`FxyeYFfzS>rh96Ef0$trAfU&R&$tI&rXam?DshGpsr9IRsE_< ztH?NA-AjSJ;IkX*S}~4*M_ipQU`UyN@%P^zd)AKi??f;8G{TFjEbPJ+eSY8fj$d&9 z+yUPL3t&6A}Xakd&o2I!k%smu;8acZ!AOK7#?i6#B?py#UqL^q zE?);UkOl#qtrOGFAHOg%u@Jf^OX5{1ar$yuX6L%mHzYbWC51eFQHspJ%GrLGMIZGE z5fPrPFeMyd&5s2Ru&{Bxj@Qy1E`Bv)tj+x*6<+lmOzYm;dpKR~oKh4Si3`UdnS)obBbkj}{B06O=BP7=ry$ER_dIPtT{1LG zmx(hNYQ%P2?v8VwZ}R_m=YH+^x;f}RYYic}IbD$L>gqx#;x;fh=W#!^$%Iw8pE3WL zNr^5KBVc7ngvVB2nCBzKe40)_r)dDQb2^uWpqwr#^jH@Cmap|6P^3Q$7ra@Y;e+N6 z3=AySvFaccY`Heu71Pl1IjOI#Y8XHZTpvuc~E;|s1ftc6e zi~bWT;}w6*|54TH9xRiYM@Km_k(s}~zSxm>*-xc$f0H1Ok^a0N>5U|xc5`)Q4tYgS zu0<9)rmCh>VKcWmUJONiZ`Lz!F}}*(%pruP-A5;sSn|AcKImc8y~~hBS-(D>jf;JL zxFvkT#l?jafwRyWBny=y1ZEUth5Czjl?nOzlw*IZ0_b0Vqo#U#$&A`HWBEFDxH|5( z(^ZZSAMRlh)8EVnpf?C6k&4yzjHo%qnC{*2HRugS6? z7g&N7gt?<?%c+4RGS}G4+znM1 z{%a;KmP>67mZ|VM31DHIl?q(6d~D3~_}DG7(MK=O8R%uFz^p6TEc;ka|Jp{G$ z!UEkaKMa#SFML0{6Z_=xSFzv(;0hWS@m&J$Z+lYAJeVP;q?TkVhQXM?zeX}tsZvLJ z;d}*&Y^UZ_q`@xF*D5M0+G2UPnyHTp4Sb}f$>)B_Eyvt!RLt$`z}VIa?CZ1QR8D=u zEZBjq4XG*3+o7byqOZED>KCRK4QALli>*`DxmJ|V20aE3?!6}i@7W|psm<_j%;k9% z*?AYfBv#_4q20tAA8}7rrpJmola-XA%6L{#Gd@_8(vaBSJM7VPu_|4+@W;eY%qhOz z`$a4~PUtMm%tpoI%Qz7rkxh+^i;od8WF)zXZT2B#5JxC<7!2fm9jdsHKcZxWwCN8C zkHSi?z=q#^E4}buHbv-{bq3vkF6r>e*Q{*FpC0@VPLi45HVE)Wx+U$f@kRX397+^n)OUs z#WemoObP$R3wcsLw(+s19Le%GT2)6f($ESI@|v~EjWq(3-&b;R;KNgG8Xx3eLxhRl zKX7)N_coE1rwb5r`1jrRS+%D=2YK6aVd=~Id7Fx|<6un&=Yd0)!@lw1V#KWoX!Dom zD$l%VxXrMmt`f1_BD-{qb|ATaJ`uY%p*)LJbXOGpsIH!WEphnpF#`Y zimBTTO~+DF8UdqvI(mBO+pp$&x9~T9KOF>DYT>c2G-7PqKF`F=H^+eJjugo<*x>AYV#$ZZF}J4YtJIdfnmM4G4R{_#g69rf6!3ii$Z}=TOa*OG~Fj zP&Tu83A^zJ(W~Z82tQy`A+1rOzY@;^Wb$;syRAFj&3S1RCkEOH#G z;q?4p&s6Z?2QR$;MDVX0@Pt*-fgVciEJNTDPX#jFw8 ztCAT`(Jb2UY`wB#_!{NrElBr?c$`6*6T(PeMPTdwhVpRF*3K#0GO_Y8Lu_|ElOc|* zxbsb(PBU3jX0+&5^IO(nk_k(&hkb!nv~$vZ*f%|xZXV)UXFV-oC~NHnMy!WN> Jwsxg-wLud5rPca?KUoyr* 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%0DV(^=%#x%n7-#+nsx_PxHs0@18N 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%0D@ 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 z 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

jZ|+<=$0m^R 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 MqOV?{_W1Ar0@^`aCjbBd literal 0 HcmV?d00001 diff --git a/examples/gitea-actions/deploy.yml b/examples/gitea-actions/deploy.yml new file mode 100644 index 0000000..e0ca2f4 --- /dev/null +++ b/examples/gitea-actions/deploy.yml @@ -0,0 +1,51 @@ +name: ForgeFlow deployment + +on: + workflow_dispatch: + inputs: + environment: + description: Fixed ForgeFlow deployment environment + required: true + default: production + commit_sha: + description: Exact commit verified by ForgeFlow + required: true + request_id: + description: ForgeFlow correlation identifier + required: true + +concurrency: + group: forgeflow-${{ gitea.repository }}-${{ inputs.environment }} + cancel-in-progress: false + +jobs: + deploy: + # Register a trusted runner with this label, or replace it with your own. + runs-on: forgeflow-production + steps: + - name: Validate dispatch inputs + shell: bash + run: | + set -euo pipefail + [[ "${{ inputs.environment }}" =~ ^[a-z0-9][a-z0-9._-]{0,63}$ ]] || { + echo "Invalid environment identifier" >&2 + exit 64 + } + [[ "${{ inputs.commit_sha }}" =~ ^[0-9a-fA-F]{40,64}$ ]] || { + echo "Invalid commit SHA" >&2 + exit 64 + } + [[ "${{ inputs.request_id }}" =~ ^[A-Za-z0-9._:-]{1,100}$ ]] || { + echo "Invalid request identifier" >&2 + exit 64 + } + + - name: Deploy exact allowlisted version + shell: bash + run: | + set -euo pipefail + sudo /usr/local/bin/forgeflow-deploy \ + "${{ gitea.repository }}" \ + "${{ inputs.environment }}" \ + "${{ inputs.commit_sha }}" \ + "${{ inputs.request_id }}" diff --git a/examples/gitea-actions/rollback.yml b/examples/gitea-actions/rollback.yml new file mode 100644 index 0000000..88fd532 --- /dev/null +++ b/examples/gitea-actions/rollback.yml @@ -0,0 +1,50 @@ +name: ForgeFlow rollback + +on: + workflow_dispatch: + inputs: + environment: + description: Fixed ForgeFlow deployment environment + required: true + default: production + target_sha: + description: Exact previously successful commit verified by ForgeFlow + required: true + request_id: + description: ForgeFlow correlation identifier + required: true + +concurrency: + group: forgeflow-${{ gitea.repository }}-${{ inputs.environment }} + cancel-in-progress: false + +jobs: + rollback: + runs-on: forgeflow-production + steps: + - name: Validate rollback inputs + shell: bash + run: | + set -euo pipefail + [[ "${{ inputs.environment }}" =~ ^[a-z0-9][a-z0-9._-]{0,63}$ ]] || { + echo "Invalid environment identifier" >&2 + exit 64 + } + [[ "${{ inputs.target_sha }}" =~ ^[0-9a-fA-F]{40,64}$ ]] || { + echo "Invalid target SHA" >&2 + exit 64 + } + [[ "${{ inputs.request_id }}" =~ ^[A-Za-z0-9._:-]{1,100}$ ]] || { + echo "Invalid request identifier" >&2 + exit 64 + } + + - name: Restore exact allowlisted version + shell: bash + run: | + set -euo pipefail + sudo /usr/local/bin/forgeflow-deploy \ + "${{ gitea.repository }}" \ + "${{ inputs.environment }}" \ + "${{ inputs.target_sha }}" \ + "${{ inputs.request_id }}" diff --git a/examples/server/forgeflow-deploy b/examples/server/forgeflow-deploy new file mode 100644 index 0000000..008e617 --- /dev/null +++ b/examples/server/forgeflow-deploy @@ -0,0 +1,148 @@ +#!/usr/bin/env bash +set -Eeuo pipefail +umask 027 + +# Install as /usr/local/bin/forgeflow-deploy, owned by root and not writable by +# the Gitea runner. Targets are read from the root-owned data file below. + +readonly CONFIG_FILE="/etc/forgeflow/targets.conf" +readonly REPOSITORY="${1:-}" +readonly ENVIRONMENT="${2:-}" +readonly SHA="${3:-}" +readonly REQUEST_ID="${4:-manual-$(date +%s)}" + +fail_usage() { + echo "Usage: forgeflow-deploy [request-id]" >&2 + exit 64 +} + +[[ "$REPOSITORY" =~ ^[A-Za-z0-9._-]+/[A-Za-z0-9._-]+$ ]] || fail_usage +[[ "$ENVIRONMENT" =~ ^[A-Za-z0-9._-]+$ ]] || fail_usage +[[ "$SHA" =~ ^[0-9a-fA-F]{40,64}$ ]] || fail_usage +[[ "$REQUEST_ID" =~ ^[A-Za-z0-9._:-]{1,100}$ ]] || fail_usage +[[ -f "$CONFIG_FILE" ]] || { echo "Missing target configuration: $CONFIG_FILE" >&2; exit 78; } + +# The target file is security-sensitive because it controls root-executed paths. +config_owner="$(stat -c '%U' "$CONFIG_FILE")" +config_mode="$(stat -c '%a' "$CONFIG_FILE")" +[[ "$config_owner" == "root" ]] || { echo "Target configuration must be owned by root" >&2; exit 78; } +# Reject group/other write bits. GNU stat returns an octal string such as 640. +(( (8#$config_mode & 8#022) == 0 )) || { echo "Target configuration may not be group/other writable" >&2; exit 78; } + +APP_DIR="" +BRANCH="" +COMPOSE_FILE="" +HEALTHCHECK_URL="" +STATUS_FILE="" +while IFS='|' read -r config_repository config_environment config_app_dir config_branch config_compose config_health config_status extra; do + [[ -z "${config_repository// }" || "$config_repository" == \#* ]] && continue + [[ -z "${extra:-}" ]] || { echo "Invalid extra field in $CONFIG_FILE" >&2; exit 78; } + if [[ "$config_repository" == "$REPOSITORY" && "$config_environment" == "$ENVIRONMENT" ]]; then + APP_DIR="$config_app_dir" + BRANCH="$config_branch" + COMPOSE_FILE="$config_compose" + HEALTHCHECK_URL="$config_health" + STATUS_FILE="$config_status" + break + fi +done < "$CONFIG_FILE" + +[[ -n "$APP_DIR" ]] || { echo "Repository/environment is not allowlisted" >&2; exit 64; } +[[ "$APP_DIR" == /* && "$COMPOSE_FILE" == /* && "$STATUS_FILE" == /var/lib/forgeflow-status/* ]] || { + echo "Target configuration contains an unsafe path" >&2 + exit 78 +} +[[ "$BRANCH" =~ ^[A-Za-z0-9._/-]+$ && "$BRANCH" != *..* ]] || { echo "Unsafe branch in target configuration" >&2; exit 78; } +[[ -d "$APP_DIR/.git" ]] || { echo "Application directory is not a Git working tree: $APP_DIR" >&2; exit 72; } +[[ -f "$COMPOSE_FILE" ]] || { echo "Compose file does not exist: $COMPOSE_FILE" >&2; exit 72; } + +install -d -o root -g root -m 0755 "$(dirname "$STATUS_FILE")" + +json_string() { + # Inputs accepted by this script are deliberately restricted to characters + # that do not need JSON escaping. This guard prevents accidental expansion. + [[ "$1" =~ ^[A-Za-z0-9._:/-]*$ ]] || return 1 + printf '%s' "$1" +} + +write_status() { + local health="$1" + local live_sha="$2" + local previous_sha="$3" + local exit_code="${4:-0}" + local deployed_at temporary + deployed_at="$(date --utc +%Y-%m-%dT%H:%M:%SZ)" + temporary="${STATUS_FILE}.${$}.tmp" + json_string "$health" >/dev/null + json_string "$REQUEST_ID" >/dev/null + [[ "$live_sha" =~ ^[0-9a-fA-F]{40,64}$ ]] || { echo "Invalid live SHA for status output" >&2; return 1; } + [[ "$previous_sha" =~ ^[0-9a-fA-F]{40,64}$ ]] || { echo "Invalid previous SHA for status output" >&2; return 1; } + cat > "$temporary" <"/run/lock/forgeflow-${REPOSITORY//\//-}-${ENVIRONMENT}.lock" +flock -n 9 || { echo "Another deployment is already running" >&2; exit 75; } + +current_sha="$(git -C "$APP_DIR" rev-parse HEAD)" +previous_sha="$(cat "$APP_DIR/.forgeflow-live-sha" 2>/dev/null || printf '%s' "$current_sha")" +[[ "$previous_sha" =~ ^[0-9a-fA-F]{40,64}$ ]] || previous_sha="$current_sha" + +echo "ForgeFlow request: $REQUEST_ID" +echo "Target: $REPOSITORY / $ENVIRONMENT" +echo "Current SHA: $current_sha" +echo "Requested SHA: $SHA" + +on_error() { + local exit_code=$? + local actual_sha + trap - ERR + actual_sha="$(git -C "$APP_DIR" rev-parse HEAD 2>/dev/null || printf '%s' "$current_sha")" + write_status "unhealthy" "$actual_sha" "$previous_sha" "$exit_code" || true + echo "Deployment failed with exit code $exit_code" >&2 + exit "$exit_code" +} +trap on_error ERR + +git -C "$APP_DIR" fetch --prune origin "$BRANCH" +git -C "$APP_DIR" cat-file -e "$SHA^{commit}" +git -C "$APP_DIR" merge-base --is-ancestor "$SHA" "origin/$BRANCH" || { + echo "Requested SHA is not part of origin/$BRANCH" >&2 + exit 65 +} + +write_status "deploying" "$current_sha" "$previous_sha" 0 +git -C "$APP_DIR" reset --hard "$SHA" +docker compose -f "$COMPOSE_FILE" up -d --build --remove-orphans + +for attempt in $(seq 1 30); do + if curl --fail --silent --show-error --max-time 5 "$HEALTHCHECK_URL" >/dev/null; then + printf '%s\n' "$SHA" > "$APP_DIR/.forgeflow-live-sha" + printf '%s\n' "$previous_sha" > "$APP_DIR/.forgeflow-previous-sha" + chmod 0640 "$APP_DIR/.forgeflow-live-sha" "$APP_DIR/.forgeflow-previous-sha" + write_status "healthy" "$SHA" "$previous_sha" 0 + trap - ERR + echo "Deployment healthy at $SHA" + exit 0 + fi + echo "Healthcheck attempt $attempt/30 did not pass yet" + sleep 2 +done + +trap - ERR +write_status "unhealthy" "$SHA" "$previous_sha" 70 +echo "Healthcheck failed after deployment" >&2 +exit 70 diff --git a/examples/server/forgeflow-runner.sudoers b/examples/server/forgeflow-runner.sudoers new file mode 100644 index 0000000..a871e02 --- /dev/null +++ b/examples/server/forgeflow-runner.sudoers @@ -0,0 +1,4 @@ +# Validate with: sudo visudo -cf /etc/sudoers.d/forgeflow-runner +# Replace "act_runner" with the account that executes your trusted Gitea runner. +# Do not add shell wildcards or other commands. +act_runner ALL=(root) NOPASSWD: /usr/local/bin/forgeflow-deploy diff --git a/examples/server/forgeflow-targets.conf b/examples/server/forgeflow-targets.conf new file mode 100644 index 0000000..87b7717 --- /dev/null +++ b/examples/server/forgeflow-targets.conf @@ -0,0 +1,9 @@ +# ForgeFlow deployment targets +# +# Format (one target per line, no shell syntax): +# repository|environment|app_dir|branch|compose_file|healthcheck_url|status_file +# +# Keep this file root-owned and not writable by the runner account. + +jens/example-app|production|/srv/example-app|main|/srv/example-app/compose.yml|http://127.0.0.1:8080/health|/var/lib/forgeflow-status/example-app-production.json +jens/example-app|staging|/srv/example-app-staging|main|/srv/example-app-staging/compose.yml|http://127.0.0.1:18080/health|/var/lib/forgeflow-status/example-app-staging.json diff --git a/examples/server/nginx-forgeflow-status.conf b/examples/server/nginx-forgeflow-status.conf new file mode 100644 index 0000000..81665ed --- /dev/null +++ b/examples/server/nginx-forgeflow-status.conf @@ -0,0 +1,8 @@ +# Example virtual-host fragment. Keep this endpoint outside the application +# process so it can still report a failed release when the app itself is down. +location = /.well-known/forgeflow { + default_type application/json; + alias /var/lib/forgeflow-status/example-app-production.json; + add_header Cache-Control "no-store" always; + add_header X-Content-Type-Options "nosniff" always; +} diff --git a/examples/server/status-example.json b/examples/server/status-example.json new file mode 100644 index 0000000..5fad942 --- /dev/null +++ b/examples/server/status-example.json @@ -0,0 +1,11 @@ +{ + "repository": "jens/example-app", + "environment": "production", + "request_id": "3a6ed71c-d52d-4d8d-9678-96e0c9456a81", + "commit_sha": "0123456789abcdef0123456789abcdef01234567", + "previous_sha": "89abcdef0123456789abcdef0123456789abcdef", + "requested_sha": "0123456789abcdef0123456789abcdef01234567", + "deployed_at": "2026-07-24T13:00:00Z", + "health": "healthy", + "last_exit_code": 0 +} diff --git a/main.cjs b/main.cjs new file mode 100644 index 0000000..da44e63 --- /dev/null +++ b/main.cjs @@ -0,0 +1,202 @@ +'use strict'; + +const path = require('node:path'); +const { app, BrowserWindow, shell, session, safeStorage } = require('electron'); +const { ConfigStore } = require('./src/main/config-store.cjs'); +const { GitService } = require('./src/main/git-service.cjs'); +const { GiteaService } = require('./src/main/gitea-service.cjs'); +const { RepositoryService } = require('./src/main/repository-service.cjs'); +const { DeploymentService } = require('./src/main/deployment-service.cjs'); +const { RepositoryMonitor } = require('./src/main/repository-monitor.cjs'); +const { DiagnosticsService } = require('./src/main/diagnostics-service.cjs'); +const { PreflightService } = require('./src/main/preflight-service.cjs'); +const { UpdateService } = require('./src/main/update-service.cjs'); +const { SshService } = require('./src/main/ssh-service.cjs'); +const { UnraidDeploymentService } = require('./src/main/unraid-deployment-service.cjs'); +const { registerIpc } = require('./src/main/ipc.cjs'); + +let mainWindow; +let repositoryMonitor; +let operationTimer; +let diagnostics; +let quitCleanupStarted = false; + +function broadcast(channel, payload) { + for (const window of BrowserWindow.getAllWindows()) { + if (!window.isDestroyed()) window.webContents.send(channel, payload); + } +} + +function createWindow() { + mainWindow = new BrowserWindow({ + width: 1480, + height: 940, + minWidth: 1120, + minHeight: 720, + show: false, + backgroundColor: '#0b0e14', + title: 'ForgeFlow', + icon: path.join(__dirname, 'build', 'icon.png'), + autoHideMenuBar: true, + titleBarStyle: process.platform === 'darwin' ? 'hiddenInset' : 'default', + webPreferences: { + preload: path.join(__dirname, 'preload.cjs'), + contextIsolation: true, + nodeIntegration: false, + sandbox: true, + webSecurity: true, + spellcheck: false + } + }); + + mainWindow.loadFile(path.join(__dirname, 'src', 'renderer', 'index.html')); + mainWindow.once('ready-to-show', () => { + mainWindow.show(); + diagnostics?.info('window.ready', { size: mainWindow.getSize() }); + }); + mainWindow.on('unresponsive', () => diagnostics?.warning('window.unresponsive', {})); + mainWindow.webContents.on('render-process-gone', (_event, details) => diagnostics?.error('renderer.process.gone', details)); + mainWindow.webContents.on('did-fail-load', (_event, code, description, validatedUrl) => diagnostics?.error('renderer.load.failed', { code, description, validatedUrl })); + mainWindow.webContents.setWindowOpenHandler(({ url }) => { + if (/^https?:\/\//i.test(url)) shell.openExternal(url); + return { action: 'deny' }; + }); + mainWindow.webContents.on('will-navigate', (event, url) => { + if (url !== mainWindow.webContents.getURL()) event.preventDefault(); + }); +} + +app.whenReady().then(async () => { + session.defaultSession.webRequest.onHeadersReceived((details, callback) => { + callback({ + responseHeaders: { + ...details.responseHeaders, + 'Content-Security-Policy': [ + "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'; connect-src 'self'" + ] + } + }); + }); + + const userDataPath = app.getPath('userData'); + const store = new ConfigStore(userDataPath); + await store.load(); + diagnostics = new DiagnosticsService({ + userDataPath, + appInfo: { name: app.getName(), version: app.getVersion(), packaged: app.isPackaged }, + secretProvider: () => [ + store.getToken(), + ...(store.data.servers || []).flatMap((server) => { + try { + const credentials = store.getServerCredentials(server.id); + return [credentials.password, credentials.passphrase]; + } catch { return []; } + }) + ], + preferencesProvider: () => store.data.preferences + }); + await diagnostics.initialize(); + + process.on('uncaughtException', (error) => { + diagnostics?.error('process.uncaught-exception', error).finally(() => app.exit(1)); + }); + process.on('unhandledRejection', (reason) => diagnostics?.error('process.unhandled-rejection', reason instanceof Error ? reason : { reason })); + + const git = new GitService(); + const gitea = new GiteaService(store, diagnostics); + const repositories = new RepositoryService(store, git, gitea, diagnostics); + const deployments = new DeploymentService(store, gitea, git, diagnostics); + const ssh = new SshService({ store, diagnostics }); + const unraid = new UnraidDeploymentService({ store, ssh, git, diagnostics }); + const updates = new UpdateService({ + store, + gitea, + diagnostics, + appInfo: { version: app.getVersion(), packaged: app.isPackaged }, + sourcePath: app.getAppPath(), + userDataPath + }); + const preflight = new PreflightService({ + store, + git, + gitea, + deployments, + diagnostics, + userDataPath, + secureStorageAvailable: () => safeStorage.isEncryptionAvailable() + }); + repositoryMonitor = new RepositoryMonitor({ + store, + git, + diagnostics, + onChange: (payload) => broadcast('repositories:changed', payload) + }); + repositoryMonitor.restart(); + registerIpc({ store, git, gitea, repositories, deployments, unraid, ssh, updates, preflight, diagnostics, monitor: repositoryMonitor }); + createWindow(); + + if (store.data.setupComplete && store.data.updates?.autoCheck && store.getToken()) { + setTimeout(async () => { + try { + const status = await updates.check(); + broadcast('updates:changed', status); + } catch (error) { + await diagnostics.warning('updates.startup-check.failed', { message: error.message, code: error.code }); + } + }, 2500).unref?.(); + } + + const gitAvailability = await git.isAvailable(); + await diagnostics.info('app.ready', { + platform: process.platform, + arch: process.arch, + setupComplete: store.data.setupComplete, + git: gitAvailability, + secureStorageAvailable: safeStorage.isEncryptionAvailable() + }); + + const scheduleOperationPoll = () => { + if (operationTimer) clearTimeout(operationTimer); + const intervalMs = Math.max(3, Number(store.data.preferences.operationPollSeconds) || 5) * 1000; + operationTimer = setTimeout(async () => { + if (store.data.setupComplete && store.getToken()) { + const active = store.data.operations.some((item) => item.type === 'deployment' && !['success', 'failed', 'cancelled', 'rolled-back'].includes(item.status)); + if (active) { + const updated = await deployments.refreshActiveOperations().catch(async (error) => { + await diagnostics.error('operation-monitor.failed', error); + return []; + }); + if (updated.length) broadcast('operations:changed', { operations: updated }); + } + } + scheduleOperationPoll(); + }, intervalMs); + operationTimer.unref?.(); + }; + scheduleOperationPoll(); + + app.on('activate', () => { + if (BrowserWindow.getAllWindows().length === 0) createWindow(); + }); +}).catch(async (error) => { + console.error('[startup]', error); + await diagnostics?.error('app.startup.failed', error); + await diagnostics?.flush(); + app.exit(1); +}); + +app.on('before-quit', (event) => { + if (quitCleanupStarted) return; + event.preventDefault(); + quitCleanupStarted = true; + repositoryMonitor?.stop(); + if (operationTimer) clearTimeout(operationTimer); + Promise.resolve() + .then(() => diagnostics?.info('app.quitting', {})) + .then(() => diagnostics?.flush()) + .finally(() => app.quit()); +}); + +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') app.quit(); +}); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a2b79b7 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3677 @@ +{ + "name": "forgeflow", + "version": "0.4.3", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "forgeflow", + "version": "0.4.3", + "dependencies": { + "ssh2": "1.17.0" + }, + "devDependencies": { + "electron": "43.2.0", + "electron-builder": "26.15.3" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@electron-internal/extract-zip": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@electron-internal/extract-zip/-/extract-zip-1.0.4.tgz", + "integrity": "sha512-Zr1Vs7E9tpCNhZHDAbFVXc2gEVCG9RqPDjrno5+bdgB6LRAuvgyMHJut4NCVyYwtAieapMzc3fiQ3CSTi75ARg==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@electron/asar": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.4.1.tgz", + "integrity": "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "bin": { + "asar": "bin/asar.js" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/@electron/asar/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@electron/asar/node_modules/brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@electron/asar/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@electron/fuses": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@electron/fuses/-/fuses-1.8.0.tgz", + "integrity": "sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.1", + "fs-extra": "^9.0.1", + "minimist": "^1.2.5" + }, + "bin": { + "electron-fuses": "dist/bin.js" + } + }, + "node_modules/@electron/fuses/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/get": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-5.0.0.tgz", + "integrity": "sha512-pjoBpru1KdEtcExBnuHAP1cAc/5faoedw0hzJkL3o4/IJp7HNF1+fbrdxT3gMYRX2oJfvnA/WXeCTVQpYYxyJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^3.0.0", + "graceful-fs": "^4.2.11", + "progress": "^2.0.3", + "semver": "^7.6.3", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=22.12.0" + }, + "optionalDependencies": { + "undici": "^7.24.4" + } + }, + "node_modules/@electron/notarize": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.5.0.tgz", + "integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.1", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/notarize/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/osx-sign": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.3.tgz", + "integrity": "sha512-KZ8mhXvWv2rIEgMbWZ4y33bDHyUKMXnx4M0sTyPNK/vcB81ImdeY9Ggdqy0SWbMDgmbqyQ+phgejh6V3R2QuSg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.js", + "electron-osx-sign": "bin/electron-osx-sign.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/@electron/rebuild": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-4.2.0.tgz", + "integrity": "sha512-RKL/O+jGoXJMxrx/5771y1n0xTKmFuOYGO3gMmwypBM6rsH0kou0mswwdXA2JrhIkE4xyC7v9vGk0n6NPzgOxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@malept/cross-spawn-promise": "^2.0.0", + "debug": "^4.1.1", + "node-abi": "^4.2.0", + "node-api-version": "^0.2.1", + "node-gyp": "^12.2.0", + "read-binary-file-arch": "^1.0.6" + }, + "bin": { + "electron-rebuild": "lib/cli.js" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@electron/universal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.3.tgz", + "integrity": "sha512-Wn9sPYIVFRFl5HmwMJkARCCf7rqK/EurkfQ/rJZ14mHP3iYTjZSIOSVonEAnhWeAXwtw7zOekGRlc6yTtZ0t+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/asar": "^3.3.1", + "@malept/cross-spawn-promise": "^2.0.0", + "debug": "^4.3.1", + "dir-compare": "^4.2.0", + "fs-extra": "^11.1.1", + "minimatch": "^9.0.3", + "plist": "^3.1.0" + }, + "engines": { + "node": ">=16.4" + } + }, + "node_modules/@electron/universal/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@electron/universal/node_modules/brace-expansion": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@electron/universal/node_modules/fs-extra": { + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.0.tgz", + "integrity": "sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@electron/universal/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@electron/windows-sign": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@electron/windows-sign/-/windows-sign-1.2.2.tgz", + "integrity": "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "dependencies": { + "cross-dirname": "^0.1.0", + "debug": "^4.3.4", + "fs-extra": "^11.1.1", + "minimist": "^1.2.8", + "postject": "^1.0.0-alpha.6" + }, + "bin": { + "electron-windows-sign": "bin/electron-windows-sign.js" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@electron/windows-sign/node_modules/fs-extra": { + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.0.tgz", + "integrity": "sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@malept/cross-spawn-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", + "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/@malept/flatpak-bundler": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", + "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.0", + "lodash": "^4.17.15", + "tmp-promise": "^3.0.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@noble/hashes": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz", + "integrity": "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@peculiar/asn1-schema": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.8.0.tgz", + "integrity": "sha512-7YT0U/ze0tF2QOBbE15gKZwy5tvgGyLRiRHLzhlbOpf7BT032oBSd0haZqXn5W6l26WLlu3dyxzjM+2638/z2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/utils": "^2.0.2", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/json-schema": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz", + "integrity": "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@peculiar/utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@peculiar/utils/-/utils-2.0.3.tgz", + "integrity": "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/webcrypto": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.7.1.tgz", + "integrity": "sha512-ODOov0sGMJMf3jPonOkgGqPknTsu+DdQ7kD++gz8aI+aFMOMHFbWAA2taqXXVTdP+OTOQR/znGvSpmkeI0WTYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/json-schema": "^1.1.12", + "@peculiar/utils": "^2.0.2", + "tslib": "^2.8.1", + "webcrypto-core": "^1.9.2" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.13", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz", + "integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/abbrev": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", + "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/app-builder-lib": { + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-26.15.3.tgz", + "integrity": "sha512-2VnyWkqsP5v5XbBhL3tD5Syx8iNPBYsoU7kY4S2fz7wg8Rj/nztWKCUzGKaFRTv0Xwf3/H058CR1Kvtd/3lRow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/asar": "3.4.1", + "@electron/fuses": "^1.8.0", + "@electron/get": "^3.0.0", + "@electron/notarize": "2.5.0", + "@electron/osx-sign": "1.3.3", + "@electron/rebuild": "^4.0.4", + "@electron/universal": "2.0.3", + "@malept/flatpak-bundler": "^0.4.0", + "@noble/hashes": "^2.2.0", + "@peculiar/webcrypto": "^1.7.1", + "@types/fs-extra": "9.0.13", + "ajv": "^8.18.0", + "asn1js": "^3.0.10", + "async-exit-hook": "^2.0.1", + "builder-util": "26.15.3", + "builder-util-runtime": "9.7.0", + "chromium-pickle-js": "^0.2.0", + "ci-info": "4.3.1", + "debug": "^4.3.4", + "dotenv": "^16.4.5", + "dotenv-expand": "^11.0.6", + "ejs": "^3.1.8", + "electron-publish": "26.15.3", + "fs-extra": "^10.1.0", + "hosted-git-info": "^4.1.0", + "isbinaryfile": "^5.0.0", + "jiti": "^2.4.2", + "js-yaml": "^4.1.0", + "json5": "^2.2.3", + "lazy-val": "^1.0.5", + "minimatch": "^10.2.5", + "pkijs": "^3.4.0", + "plist": "3.1.0", + "proper-lockfile": "^4.1.2", + "resedit": "^1.7.0", + "semver": "~7.7.3", + "tar": "^7.5.7", + "temp-file": "^3.4.0", + "tiny-async-pool": "1.3.0", + "unzipper": "^0.12.3", + "which": "^5.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "dmg-builder": "26.15.3", + "electron-builder-squirrel-windows": "26.15.3" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/get": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-3.1.0.tgz", + "integrity": "sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=14" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/get/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/get/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/app-builder-lib/node_modules/ci-info": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", + "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/app-builder-lib/node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/app-builder-lib/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/app-builder-lib/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/app-builder-lib/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/asn1js": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.10.tgz", + "integrity": "sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.5", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-exit-hook": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", + "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/brace-expansion": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/buildcheck": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.7.tgz", + "integrity": "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA==", + "optional": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/builder-util": { + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-26.15.3.tgz", + "integrity": "sha512-q2hn7Mbo2nFNkVekPiHFx6Nfo3hURmES3tfBn+k5Pqxl2RkmP3QGqZUhH/q9Pch/4G05NRhPjDlVj1O8q4Txvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/debug": "^4.1.6", + "builder-util-runtime": "9.7.0", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.6", + "debug": "^4.3.4", + "fs-extra": "^10.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "js-yaml": "^4.1.0", + "sanitize-filename": "^1.6.3", + "source-map-support": "^0.5.19", + "stat-mode": "^1.0.0", + "temp-file": "^3.4.0", + "tiny-async-pool": "1.3.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/builder-util-runtime": { + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.7.0.tgz", + "integrity": "sha512-g/kR520giAFYkSXTzcmF3kqQq7wi8F6N6SzeDgZrqTBN+VHdmgWOyTdD1yD7AATDId/yXLvuP34CxW46/BwCdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "sax": "^1.2.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/bytestreamjs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.1.tgz", + "integrity": "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/chromium-pickle-js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", + "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/compare-version": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", + "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cpu-features": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", + "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "buildcheck": "~0.0.6", + "nan": "^2.19.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/cross-dirname": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz", + "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/dir-compare": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-4.2.0.tgz", + "integrity": "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimatch": "^3.0.5", + "p-limit": "^3.1.0 " + } + }, + "node_modules/dir-compare/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/dir-compare/node_modules/brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/dir-compare/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/dmg-builder": { + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.15.3.tgz", + "integrity": "sha512-O3zJUFUYHJKgzPqioHxfxzBzlSC1eXCSr79gMSBKBP5AgjjpmrydMsMLotEg9fAJF36vdUncb+4ndRNxoPdlSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "26.15.3", + "builder-util": "26.15.3", + "fs-extra": "^10.1.0", + "js-yaml": "^4.1.0" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", + "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron": { + "version": "43.2.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-43.2.0.tgz", + "integrity": "sha512-80zvrgG7ZRXD+tD0IyLvrnN9n+veSxadMRsMaC9wKKP3iUbtC7rGM8+dVuCmOb0Rrwwv8ESW4awnUZh9Hbp1fA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron-internal/extract-zip": "^1.0.1", + "@electron/get": "^5.0.0", + "@types/node": "^24.9.0" + }, + "bin": { + "electron": "cli.js", + "install-electron": "install.js" + }, + "engines": { + "node": ">= 22.12.0" + } + }, + "node_modules/electron-builder": { + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-26.15.3.tgz", + "integrity": "sha512-a1KM5heqS3gQCZzizXEI8RjJy3QVogULPdeSknt76uLDpBIW/HDGsMg/XgP0riP6PI9COsRvFITKKGDqA8fJxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "26.15.3", + "builder-util": "26.15.3", + "builder-util-runtime": "9.7.0", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "dmg-builder": "26.15.3", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "simple-update-notifier": "2.0.0", + "yargs": "^17.6.2" + }, + "bin": { + "electron-builder": "cli.js", + "install-app-deps": "install-app-deps.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/electron-builder-squirrel-windows": { + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-26.15.3.tgz", + "integrity": "sha512-Jc19XPV9y9+2bAdZPkXuVNGNIEFBq9poHC61l8Kv6FdK7DRG3+Ic0rerC0DXOaeHNz8yW0fg/JnF8GQROOF5MA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "app-builder-lib": "26.15.3", + "builder-util": "26.15.3", + "electron-winstaller": "5.4.0" + } + }, + "node_modules/electron-publish": { + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-26.15.3.tgz", + "integrity": "sha512-g/2bn8YTavY4cuS5F+jOS7zmZbXXBV8KZ8yHKfJjFPoKtzBqrpCdNPxBd3tqdBwP7BVd0lGzf7Bk2s0KesWZ4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^9.0.11", + "aws4": "^1.13.2", + "builder-util": "26.15.3", + "builder-util-runtime": "9.7.0", + "chalk": "^4.1.2", + "form-data": "^4.0.5", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "mime": "^2.5.2" + } + }, + "node_modules/electron-winstaller": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.4.0.tgz", + "integrity": "sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@electron/asar": "^3.2.1", + "debug": "^4.1.1", + "fs-extra": "^7.0.1", + "lodash": "^4.17.21", + "temp": "^0.9.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "@electron/windows-sign": "^1.1.2" + } + }, + "node_modules/electron-winstaller/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/electron-winstaller/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "peer": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-winstaller/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/filelist": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isbinaryfile": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.7.tgz", + "integrity": "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-yaml": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/lazy-val": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", + "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nan": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.28.0.tgz", + "integrity": "sha512-fTsDz99OTq2sVePhGdp4qQhggZFtKr64ZNVyVajRKtMOkJxYekplBh577PiJB12v/D3s2E5cGtOI45LWp6rnLQ==", + "license": "MIT", + "optional": true + }, + "node_modules/node-abi": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-4.33.0.tgz", + "integrity": "sha512-vLBWCKb+7LWsX+TbfzWOkw0W81m377tyx3hOweBTjO43CXZnRGS1/JPWs20fr0PgZyDXk6ROYrylsEycK8raDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.6.3" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/node-api-version": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.1.tgz", + "integrity": "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + } + }, + "node_modules/node-gyp": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.4.0.tgz", + "integrity": "sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "nopt": "^9.0.0", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "tar": "^7.5.4", + "tinyglobby": "^0.2.12", + "undici": "^6.25.0", + "which": "^6.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-gyp/node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/node-gyp/node_modules/undici": { + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.28.0.tgz", + "integrity": "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", + "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^4.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pe-library": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-0.4.1.tgz", + "integrity": "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkijs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.4.0.tgz", + "integrity": "sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@noble/hashes": "1.4.0", + "asn1js": "^3.0.6", + "bytestreamjs": "^2.0.1", + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/pkijs/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/plist": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/postject": { + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/postject/-/postject-1.0.0-alpha.6.tgz", + "integrity": "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "commander": "^9.4.0" + }, + "bin": { + "postject": "dist/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/postject/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pvtsutils": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", + "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/pvutils": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz", + "integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-binary-file-arch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz", + "integrity": "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "bin": { + "read-binary-file-arch": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resedit": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz", + "integrity": "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pe-library": "^0.4.1" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sanitize-filename": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.4.tgz", + "integrity": "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg==", + "dev": true, + "license": "WTFPL OR ISC", + "dependencies": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/ssh2": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.17.0.tgz", + "integrity": "sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==", + "hasInstallScript": true, + "dependencies": { + "asn1": "^0.2.6", + "bcrypt-pbkdf": "^1.0.2" + }, + "engines": { + "node": ">=10.16.0" + }, + "optionalDependencies": { + "cpu-features": "~0.0.10", + "nan": "^2.23.0" + } + }, + "node_modules/stat-mode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", + "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "7.5.22", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz", + "integrity": "sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/temp": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", + "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "mkdirp": "^0.5.1", + "rimraf": "~2.6.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/temp-file": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", + "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-exit-hook": "^2.0.1", + "fs-extra": "^10.0.0" + } + }, + "node_modules/tiny-async-pool": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz", + "integrity": "sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^5.5.0" + } + }, + "node_modules/tiny-async-pool/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tmp": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/tmp-promise": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tmp": "^0.2.0" + } + }, + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", + "dev": true, + "license": "WTFPL", + "dependencies": { + "utf8-byte-length": "^1.0.1" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "license": "Unlicense" + }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/undici": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz", + "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unzipper": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.12.5.tgz", + "integrity": "sha512-tXYOi9R57Uj/2Z25SOs5RRSzq886MBQj2gY8dPL+xl/kv6s6SvByoKfAtvfVeEuhntWDgjd2o9p2lb4TVPAz0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "~3.7.2", + "duplexer2": "~0.1.4", + "fs-extra": "11.3.1", + "graceful-fs": "^4.2.2", + "node-int64": "^0.4.0" + } + }, + "node_modules/unzipper/node_modules/fs-extra": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", + "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/utf8-byte-length": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", + "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/webcrypto-core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.9.2.tgz", + "integrity": "sha512-gsXecm82UQNlTBURJGuqOWy1Ww08S3kZUcr3aOJS02Pk0xLtkfeUAVC0u0xhgdonFme80edSJUIJyuvL/7250Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/json-schema": "^1.1.12", + "@peculiar/utils": "^2.0.2", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, + "node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..c79d3e6 --- /dev/null +++ b/package.json @@ -0,0 +1,89 @@ +{ + "name": "forgeflow", + "version": "0.4.3", + "private": true, + "description": "Desktop release cockpit for local Git, Gitea Actions and controlled exact-commit deployments.", + "main": "main.cjs", + "type": "module", + "scripts": { + "start": "electron .", + "dev": "electron . --dev", + "demo": "node scripts/serve-demo.mjs", + "test": "node --test tests/*.test.mjs", + "verify": "node scripts/verify.mjs", + "dist:win": "electron-builder --win nsis portable", + "dist:linux": "electron-builder --linux AppImage", + "dist:mac": "electron-builder --mac dmg", + "doctor": "node scripts/doctor.mjs", + "check": "npm run verify && npm test" + }, + "devDependencies": { + "electron": "43.2.0", + "electron-builder": "26.15.3" + }, + "build": { + "appId": "be.jenscaers.forgeflow", + "productName": "ForgeFlow", + "asar": true, + "files": [ + "main.cjs", + "preload.cjs", + "src/**/*", + "package.json", + "build/icon.png", + "docs/SETUP_GUIDE.md", + "docs/DIAGNOSTICS.md", + "docs/STATUS_ENDPOINT.md", + "examples/gitea-actions/**/*", + "examples/server/**/*", + "README.md", + "START_HERE.md", + "docs/DEPLOYMENT_SETUP.md", + "docs/SECURITY.md", + "docs/TEST_MATRIX.md", + "docs/RELEASE_NOTES_0.3.0.md", + "docs/RELEASE_NOTES_0.3.1.md", + "docs/RELEASE_NOTES_0.3.2.md", + "docs/UPDATING.md", + "scripts/apply-source-update.ps1", + "docs/RELEASE_NOTES_0.4.0.md", + "docs/LUMAOPS_SERVER_AUDIT.md", + "docs/SSH_UNRAID_DEPLOYMENT.md", + "docs/RELEASE_NOTES_0.4.1.md", + "docs/RELEASE_NOTES_0.4.2.md", + "docs/RELEASE_NOTES_0.4.3.md" + ], + "directories": { + "output": "dist" + }, + "win": { + "target": [ + "nsis", + "portable" + ], + "icon": "build/icon.ico" + }, + "linux": { + "target": [ + "AppImage" + ], + "category": "Development", + "icon": "build/icon.png" + }, + "mac": { + "target": [ + "dmg" + ], + "category": "public.app-category.developer-tools", + "icon": "build/icon.png" + }, + "artifactName": "${productName}-${version}-${os}-${arch}.${ext}" + }, + "engines": { + "node": ">=22" + }, + "author": "Jens", + "dependencies": { + "ssh2": "1.17.0" + } +} diff --git a/preload.cjs b/preload.cjs new file mode 100644 index 0000000..1508325 --- /dev/null +++ b/preload.cjs @@ -0,0 +1,87 @@ +'use strict'; + +const { contextBridge, ipcRenderer } = require('electron'); + +async function invoke(channel, payload) { + const result = await ipcRenderer.invoke(channel, payload); + if (!result?.ok) { + const error = new Error(result?.error?.message || 'ForgeFlow operation failed.'); + error.code = result?.error?.code; + error.status = result?.error?.status; + error.recoverable = result?.error?.recoverable; + error.commitSha = result?.error?.commitSha; + throw error; + } + return result.data; +} + +function subscribe(channel, listener) { + if (typeof listener !== 'function') return () => {}; + const handler = (_event, payload) => listener(payload); + ipcRenderer.on(channel, handler); + return () => ipcRenderer.removeListener(channel, handler); +} + +contextBridge.exposeInMainWorld('forgeflow', Object.freeze({ + bootstrap: () => invoke('app:bootstrap'), + selectDirectory: (payload) => invoke('dialog:select-directory', payload), + selectKeyFile: (payload) => invoke('dialog:select-key-file', payload), + setupPreflight: (payload) => invoke('setup:preflight', payload), + validateGitea: (payload) => invoke('setup:validate-gitea', payload), + completeSetup: (payload) => invoke('setup:complete', payload), + updateGitea: (payload) => invoke('settings:update-gitea', payload), + setWorkspaceRoots: (roots) => invoke('settings:set-roots', { roots }), + setAppearance: (appearance) => invoke('settings:set-appearance', { appearance }), + setPreferences: (preferences) => invoke('settings:set-preferences', { preferences }), + setUpdatePreferences: (updates) => invoke('updates:preferences', { updates }), + checkForUpdates: () => invoke('updates:check'), + downloadUpdate: () => invoke('updates:download'), + applyUpdate: () => invoke('updates:apply'), + saveServer: (server, password = '', passphrase = '') => invoke('server:save', { server, password, passphrase }), + deleteServer: (serverId) => invoke('server:delete', { serverId }), + testServer: (serverId) => invoke('server:test', { serverId }), + inspectServerProject: (repository, profileId) => invoke('server:inspect-project', { repository, profileId }), + refreshRepositories: () => invoke('repositories:refresh'), + discoverRepositories: (roots) => invoke('repositories:discover', { roots }), + favoriteRepository: (fullName, favorite) => invoke('repository:favorite', { fullName, favorite }), + linkRepository: (fullName, localPath) => invoke('repository:link', { fullName, localPath }), + unlinkRepository: (fullName) => invoke('repository:unlink', { fullName }), + repositoryStatus: (localPath) => invoke('repository:status', { localPath }), + repositoryDiff: (localPath, filePath, staged = false) => invoke('repository:diff', { localPath, filePath, staged }), + stageFiles: (localPath, files) => invoke('repository:stage', { localPath, files }), + unstageFiles: (localPath, files) => invoke('repository:unstage', { localPath, files }), + commit: (localPath, message, files) => invoke('repository:commit', { localPath, message, files }), + commitAndPush: (localPath, message, files) => invoke('repository:commit-push', { localPath, message, files }), + push: (localPath) => invoke('repository:push', { localPath }), + fetch: (localPath) => invoke('repository:fetch', { localPath }), + pull: (localPath) => invoke('repository:pull', { localPath }), + history: (localPath, limit = 20) => invoke('repository:history', { localPath, limit }), + branches: (localPath) => invoke('repository:branches', { localPath }), + checkoutBranch: (localPath, branch) => invoke('repository:checkout-branch', { localPath, branch }), + createBranch: (localPath, branch) => invoke('repository:create-branch', { localPath, branch }), + stash: (localPath, message) => invoke('repository:stash', { localPath, message }), + stashList: (localPath) => invoke('repository:stash-list', { localPath }), + popStash: (localPath, ref) => invoke('repository:stash-pop', { localPath, ref }), + cloneRepository: (fullName, mode = 'default') => invoke('repository:clone', { fullName, mode }), + openPath: (localPath) => invoke('repository:open-path', { localPath }), + openExternal: (url) => invoke('external:open', { url }), + saveDeploymentProfile: (fullName, profile) => invoke('deployment:save-profile', { fullName, profile }), + deploymentPreflight: (repository, profileId) => invoke('deployment:preflight', { repository, profileId }), + deleteDeploymentProfile: (fullName, profileId) => invoke('deployment:delete-profile', { fullName, profileId }), + deploy: (repository, profileId, sha) => invoke('deployment:dispatch', { repository, profileId, sha }), + rollback: (repository, profileId, targetSha) => invoke('deployment:rollback', { repository, profileId, targetSha }), + healthcheck: (url) => invoke('deployment:health', { url }), + refreshProfileState: (fullName, profileId) => invoke('deployment:profile-state', { fullName, profileId }), + refreshOperations: (operationId = null) => invoke('operations:refresh', { operationId }), + getOperation: (operationId) => invoke('operations:get', { operationId }), + diagnosticsStatus: () => invoke('diagnostics:status'), + clearDiagnostics: () => invoke('diagnostics:clear'), + openDiagnosticsFolder: () => invoke('diagnostics:open-folder'), + exportDiagnostics: (privacyMode = 'standard') => invoke('diagnostics:export', { privacyMode }), + showDiagnosticBundle: (filePath) => invoke('diagnostics:show-bundle', { filePath }), + reportRendererEvent: (level, event, details = {}) => invoke('renderer:report', { level, event, details }), + onRepositoriesChanged: (listener) => subscribe('repositories:changed', listener), + onOperationsChanged: (listener) => subscribe('operations:changed', listener), + onUpdatesChanged: (listener) => subscribe('updates:changed', listener), + reset: () => invoke('app:reset') +})); diff --git a/scripts/apply-source-update.ps1 b/scripts/apply-source-update.ps1 new file mode 100644 index 0000000..0174487 --- /dev/null +++ b/scripts/apply-source-update.ps1 @@ -0,0 +1,92 @@ +param( + [Parameter(Mandatory=$true)][string]$SourcePath, + [Parameter(Mandatory=$true)][string]$ArchivePath, + [Parameter(Mandatory=$true)][string]$ExpectedVersion, + [Parameter(Mandatory=$true)][string]$ExpectedSha256, + [Parameter(Mandatory=$true)][int]$ParentPid, + [Parameter(Mandatory=$true)][string]$LogPath +) + +$ErrorActionPreference = "Stop" +$ProgressPreference = "SilentlyContinue" + +function Write-UpdateLog { + param([string]$Message) + $line = "$(Get-Date -Format o) $Message" + New-Item -ItemType Directory -Force -Path (Split-Path -Parent $LogPath) | Out-Null + Add-Content -Path $LogPath -Value $line -Encoding UTF8 +} + +function Invoke-Robocopy { + param([string]$From, [string]$To) + New-Item -ItemType Directory -Force -Path $To | Out-Null + & robocopy.exe $From $To /MIR /R:2 /W:1 /NFL /NDL /NJH /NJS /NP /XD node_modules .git dist | Out-Null + if ($LASTEXITCODE -gt 7) { throw "robocopy failed with exit code $LASTEXITCODE" } +} + +try { + Write-UpdateLog "ForgeFlow source update started for version $ExpectedVersion." + $deadline = (Get-Date).AddMinutes(2) + while (Get-Process -Id $ParentPid -ErrorAction SilentlyContinue) { + if ((Get-Date) -gt $deadline) { throw "ForgeFlow did not exit before the update timeout." } + Start-Sleep -Milliseconds 500 + } + + $actualHash = (Get-FileHash -Path $ArchivePath -Algorithm SHA256).Hash.ToLowerInvariant() + if ($actualHash -ne $ExpectedSha256.ToLowerInvariant()) { throw "Update archive checksum mismatch." } + + $working = Join-Path ([IO.Path]::GetTempPath()) ("forgeflow-update-" + [guid]::NewGuid().ToString("N")) + $extract = Join-Path $working "extract" + $backup = Join-Path $working "backup" + New-Item -ItemType Directory -Force -Path $extract | Out-Null + Write-UpdateLog "Creating source backup." + Invoke-Robocopy -From $SourcePath -To $backup + + Write-UpdateLog "Extracting update archive." + Expand-Archive -LiteralPath $ArchivePath -DestinationPath $extract -Force + $manifest = Get-ChildItem -Path $extract -Filter package.json -File -Recurse | + Where-Object { + try { + $json = Get-Content $_.FullName -Raw | ConvertFrom-Json + return $json.name -eq "forgeflow" -and $json.version -eq $ExpectedVersion + } catch { return $false } + } | + Select-Object -First 1 + + if (-not $manifest) { throw "The update does not contain ForgeFlow version $ExpectedVersion." } + $incoming = Split-Path -Parent $manifest.FullName + Write-UpdateLog "Applying verified source files." + Invoke-Robocopy -From $incoming -To $SourcePath + + Push-Location $SourcePath + try { + Write-UpdateLog "Installing exact dependencies." + & cmd.exe /d /s /c "npm install --no-audit --no-fund" *>> $LogPath + if ($LASTEXITCODE -ne 0) { throw "npm install failed with exit code $LASTEXITCODE." } + Write-UpdateLog "Running ForgeFlow quality gate." + & cmd.exe /d /s /c "npm run check" *>> $LogPath + if ($LASTEXITCODE -ne 0) { throw "npm run check failed with exit code $LASTEXITCODE." } + } finally { Pop-Location } + + Write-UpdateLog "Update validated successfully. Restarting ForgeFlow." + Start-Process -FilePath "cmd.exe" -WorkingDirectory $SourcePath -ArgumentList "/d", "/s", "/c", "npm start" + Remove-Item -LiteralPath $working -Recurse -Force -ErrorAction SilentlyContinue + exit 0 +} +catch { + Write-UpdateLog ("Update failed: " + $_.Exception.Message) + try { + if ($backup -and (Test-Path $backup)) { + Write-UpdateLog "Restoring previous source version." + Invoke-Robocopy -From $backup -To $SourcePath + Push-Location $SourcePath + try { + & cmd.exe /d /s /c "npm install --no-audit --no-fund" *>> $LogPath + } finally { Pop-Location } + Start-Process -FilePath "cmd.exe" -WorkingDirectory $SourcePath -ArgumentList "/d", "/s", "/c", "npm start" + } + } catch { + Write-UpdateLog ("Rollback failed: " + $_.Exception.Message) + } + exit 1 +} diff --git a/scripts/doctor.mjs b/scripts/doctor.mjs new file mode 100644 index 0000000..6da4d9a --- /dev/null +++ b/scripts/doctor.mjs @@ -0,0 +1,88 @@ +import { execFile } from 'node:child_process'; +import { access, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; +import os from 'node:os'; +import path from 'node:path'; +import { promisify } from 'node:util'; +import toolInvocation from '../src/shared/tool-invocation.cjs'; + +const exec = promisify(execFile); +const { npmProbeCandidates } = toolInvocation; +const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8')); +const checks = []; +const jsonMode = process.argv.includes('--json'); + +function add(id, name, ok, detail, help = '') { + checks.push({ id, name, status: ok ? 'pass' : 'fail', ok, detail, help }); +} + +const major = Number(process.versions.node.split('.')[0]); +add('node', 'Node.js', major >= 22, process.version, 'Install Node.js 22 or newer.'); + +try { + const failures = []; + let version = ''; + let source = ''; + for (const candidate of npmProbeCandidates()) { + try { + const { stdout } = await exec(candidate.file, candidate.args, { windowsHide: true }); + version = stdout.trim(); + source = candidate.source; + if (version) break; + } catch (error) { + failures.push(`${candidate.source}: ${error.message}`); + } + } + if (!version) throw new Error(failures.join(' | ') || 'No npm invocation candidate succeeded.'); + add('npm', 'npm', true, `${version} (${source})`); +} catch (error) { + add('npm', 'npm', false, error.message, 'Install npm together with Node.js and ensure npm.cmd is available on PATH.'); +} + +try { + const { stdout } = await exec('git', ['--version']); + add('git', 'Git', true, stdout.trim()); + const [name, email] = await Promise.all([ + exec('git', ['config', '--global', '--get', 'user.name']).then((result) => result.stdout.trim()).catch(() => ''), + exec('git', ['config', '--global', '--get', 'user.email']).then((result) => result.stdout.trim()).catch(() => '') + ]); + add('git-identity', 'Git identity', Boolean(name && email), name && email ? `${name} <${email}>` : 'user.name or user.email is missing', 'Configure git config --global user.name and user.email.'); +} catch (error) { + add('git', 'Git', false, error.message, 'Install Git and ensure git is on PATH.'); +} + +try { + await access(new URL('../node_modules/electron/package.json', import.meta.url)); + add('electron', 'Electron dependency', true, 'installed'); +} catch { + add('electron', 'Electron dependency', false, 'not installed', 'Run npm install.'); +} + +let markerDirectory = null; +try { + markerDirectory = await mkdtemp(path.join(os.tmpdir(), 'forgeflow-doctor-')); + await writeFile(path.join(markerDirectory, 'write-test'), 'ok'); + add('temp-storage', 'Local diagnostic storage', true, markerDirectory.replace(os.homedir(), '')); +} catch (error) { + add('temp-storage', 'Local diagnostic storage', false, error.message, 'Check local disk permissions and free space.'); +} finally { + if (markerDirectory) await rm(markerDirectory, { recursive: true, force: true }).catch(() => {}); +} + +const report = { + product: 'ForgeFlow', + version: packageJson.version, + generatedAt: new Date().toISOString(), + platform: process.platform, + arch: process.arch, + ready: checks.every((check) => check.ok), + checks +}; + +if (jsonMode) console.log(JSON.stringify(report, null, 2)); +else { + console.log('ForgeFlow doctor\n'); + for (const check of checks) console.log(`${check.ok ? 'PASS' : 'FAIL'} ${check.name.padEnd(26)} ${check.detail}`); + console.log(`\n${report.ready ? 'Environment is ready.' : 'Resolve failed checks before starting ForgeFlow.'}`); +} + +if (!report.ready) process.exitCode = 1; diff --git a/scripts/serve-demo.mjs b/scripts/serve-demo.mjs new file mode 100644 index 0000000..3f96cbb --- /dev/null +++ b/scripts/serve-demo.mjs @@ -0,0 +1,28 @@ +import http from 'node:http'; +import { readFile, stat } from 'node:fs/promises'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'src', 'renderer'); +const port = Number(process.env.PORT || 4173); +const mime = { '.html': 'text/html; charset=utf-8', '.css': 'text/css; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.svg': 'image/svg+xml' }; + +const server = http.createServer(async (request, response) => { + try { + const pathname = decodeURIComponent(new URL(request.url, `http://${request.headers.host}`).pathname); + const relative = pathname === '/' ? 'index.html' : pathname.replace(/^\//, ''); + const target = path.resolve(root, relative); + if (!target.startsWith(root)) throw Object.assign(new Error('Forbidden'), { code: 'EACCES' }); + const info = await stat(target); + if (!info.isFile()) throw Object.assign(new Error('Not found'), { code: 'ENOENT' }); + response.writeHead(200, { 'Content-Type': mime[path.extname(target)] || 'application/octet-stream', 'Cache-Control': 'no-store' }); + response.end(await readFile(target)); + } catch (error) { + response.writeHead(error.code === 'ENOENT' ? 404 : 403, { 'Content-Type': 'text/plain' }); + response.end(error.code === 'ENOENT' ? 'Not found' : 'Forbidden'); + } +}); + +server.listen(port, '127.0.0.1', () => { + console.log(`ForgeFlow demo: http://127.0.0.1:${port}`); +}); diff --git a/scripts/verify.mjs b/scripts/verify.mjs new file mode 100644 index 0000000..441b420 --- /dev/null +++ b/scripts/verify.mjs @@ -0,0 +1,97 @@ +import { access, readFile, readdir } from 'node:fs/promises'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { spawnSync } from 'node:child_process'; +import shellVerification from '../src/shared/shell-verification.cjs'; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); +const required = [ + 'package.json', 'main.cjs', 'preload.cjs', + 'src/renderer/index.html', 'src/renderer/styles.css', 'src/renderer/app.js', 'src/renderer/mock-bridge.js', + 'src/renderer/assets/itworx-mark.png', 'src/renderer/assets/itworx-wordmark.png', + 'src/main/config-store.cjs', 'src/main/git-service.cjs', 'src/main/gitea-service.cjs', + 'src/main/repository-service.cjs', 'src/main/repository-monitor.cjs', 'src/main/deployment-service.cjs', + 'src/main/unraid-deployment-service.cjs', 'src/main/ssh-service.cjs', 'src/main/update-service.cjs', + 'src/main/diagnostics-service.cjs', 'src/main/preflight-service.cjs', 'src/main/log-redaction.cjs', 'src/main/ipc.cjs', + 'src/shared/clone-target.cjs', 'src/shared/semver.cjs', 'src/shared/zip-writer.cjs', + 'src/shared/tool-invocation.cjs', 'src/shared/shell-verification.cjs', 'START_HERE.md', 'README.md', + 'setup-windows.ps1', 'update-windows.ps1', 'build-windows.ps1', 'UPDATE_FROM_0.3.2.md', 'scripts/apply-source-update.ps1', + 'docs/ARCHITECTURE.md', 'docs/SECURITY.md', 'docs/ROADMAP.md', 'docs/SETUP_GUIDE.md', + 'docs/UPDATING.md', 'docs/DIAGNOSTICS.md', 'docs/DEPLOYMENT_SETUP.md', 'docs/SSH_UNRAID_DEPLOYMENT.md', + 'docs/LUMAOPS_SERVER_AUDIT.md', 'docs/STATUS_ENDPOINT.md', 'docs/TEST_MATRIX.md', 'docs/RELEASE_NOTES_0.4.0.md', 'docs/RELEASE_NOTES_0.4.1.md', 'docs/RELEASE_NOTES_0.4.2.md', 'docs/RELEASE_NOTES_0.4.3.md', + 'examples/gitea-actions/deploy.yml', 'examples/gitea-actions/rollback.yml', + 'examples/server/forgeflow-deploy', 'examples/server/forgeflow-targets.conf', + 'examples/server/forgeflow-runner.sudoers', 'examples/server/status-example.json', + 'build/icon.png', 'build/icon.ico' +]; + +for (const file of required) await access(path.join(root, file)); + +const packageJson = JSON.parse(await readFile(path.join(root, 'package.json'), 'utf8')); +if (packageJson.version !== '0.4.3') throw new Error(`Expected package version 0.4.3, got ${packageJson.version}.`); +for (const group of ['dependencies', 'devDependencies']) { + for (const [name, version] of Object.entries(packageJson[group] || {})) { + if (/^[~^*]/.test(version)) throw new Error(`${group} dependency ${name} must be pinned exactly, got ${version}.`); + } +} +if (packageJson.dependencies?.ssh2 !== '1.17.0') throw new Error('ssh2 must remain pinned to 1.17.0.'); +for (const script of ['start', 'demo', 'test', 'verify', 'check']) { + if (!packageJson.scripts?.[script]) throw new Error(`Required npm script is missing: ${script}`); +} +if (!packageJson.build?.win?.icon || !packageJson.build?.linux?.icon || !packageJson.build?.mac?.icon) { + throw new Error('Package icon configuration is incomplete.'); +} + +async function collect(directory, extensions, output = []) { + for (const entry of await readdir(directory, { withFileTypes: true })) { + if (['node_modules', 'dist'].includes(entry.name)) continue; + const absolute = path.join(directory, entry.name); + if (entry.isDirectory()) await collect(absolute, extensions, output); + else if (extensions.has(path.extname(entry.name))) output.push(absolute); + } + return output; +} + +const javascriptFiles = await collect(root, new Set(['.js', '.cjs', '.mjs'])); +for (const file of javascriptFiles) { + const result = spawnSync(process.execPath, ['--check', file], { encoding: 'utf8' }); + if (result.status !== 0) throw new Error(`${path.relative(root, file)} failed syntax validation:\n${result.stderr}`); +} + +const bashCheck = shellVerification.bashSyntaxCheckInvocation(root); +const shell = spawnSync(bashCheck.command, bashCheck.args, bashCheck.options); +if (shell.error) throw new Error(`Unable to start Bash for server deployment syntax validation: ${shell.error.message}`); +if (shell.status !== 0) throw new Error(`Server deployment example failed bash syntax validation:\n${shell.stderr}`); + +JSON.parse(await readFile(path.join(root, 'examples/server/status-example.json'), 'utf8')); +const setupGuide = await readFile(path.join(root, 'docs/SETUP_GUIDE.md'), 'utf8'); +const sshGuide = await readFile(path.join(root, 'docs/SSH_UNRAID_DEPLOYMENT.md'), 'utf8'); +const audit = await readFile(path.join(root, 'docs/LUMAOPS_SERVER_AUDIT.md'), 'utf8'); +const releaseNotes = await readFile(path.join(root, 'docs/RELEASE_NOTES_0.4.3.md'), 'utf8'); +if (!setupGuide.includes('Gitea access token') || !setupGuide.includes('diagnostic bundle')) { + throw new Error('Setup guide is missing required connection or diagnostics instructions.'); +} +if (!sshGuide.includes('/mnt/user/appdata') || !sshGuide.includes('host-key fingerprint')) { + throw new Error('SSH / Unraid guide is missing its base path or host identity policy.'); +} +if (!audit.includes('d42d4a7f08240c478d07466e3fabec654dc71367') || !audit.includes('source/')) { + throw new Error('LumaOps audit is missing the exact matching SHA or nested repository finding.'); +} +for (const phrase of ['platform-independent', 'SSH inspection', 'Windows', 'Regression coverage']) { + if (!releaseNotes.includes(phrase)) throw new Error(`Release notes are missing: ${phrase}`); +} +const renderer = await readFile(path.join(root, 'src/renderer/app.js'), 'utf8'); +const styles = await readFile(path.join(root, 'src/renderer/styles.css'), 'utf8'); +const preload = await readFile(path.join(root, 'preload.cjs'), 'utf8'); +const ipc = await readFile(path.join(root, 'src/main/ipc.cjs'), 'utf8'); +for (const phrase of ['Commit selected & push to Gitea', 'checkForUpdates', 'saveServer', 'profile-provider', 'itworx-mark.png']) { + if (!renderer.includes(phrase) && !preload.includes(phrase)) throw new Error(`Frontend integration is missing: ${phrase}`); +} +if (!styles.includes('.file-list { flex: 1 1 auto;') || !styles.includes('.main-canvas.repository-canvas')) { + throw new Error('Changed-file scrolling constraints are missing.'); +} +for (const channel of ['updates:check', 'updates:download', 'updates:apply', 'server:save', 'server:test', 'server:inspect-project']) { + if (!ipc.includes(channel)) throw new Error(`IPC registration is missing: ${channel}`); +} + +console.log(`Verified ${required.length} required project files and ${javascriptFiles.length} JavaScript files for ForgeFlow ${packageJson.version}.`); diff --git a/setup-windows.ps1 b/setup-windows.ps1 new file mode 100644 index 0000000..752311a --- /dev/null +++ b/setup-windows.ps1 @@ -0,0 +1,62 @@ +$ErrorActionPreference = "Stop" +Set-StrictMode -Version Latest +Set-Location $PSScriptRoot + +function Assert-Command { + param([Parameter(Mandatory = $true)][string]$Name) + if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) { + throw "Required command '$Name' was not found on PATH. Read START_HERE.md for prerequisites." + } +} + +function Invoke-Step { + param( + [Parameter(Mandatory = $true)][string]$Title, + [Parameter(Mandatory = $true)][scriptblock]$Action + ) + Write-Host "`n$Title" -ForegroundColor Yellow + & $Action + if ($LASTEXITCODE -ne 0) { + throw "$Title failed with exit code $LASTEXITCODE." + } +} + +Write-Host "ForgeFlow v0.4.2 self-service setup" -ForegroundColor Cyan +Write-Host "No Gitea token, SSH key or server password is requested by this script." -ForegroundColor DarkGray + +Assert-Command node +Assert-Command npm +Assert-Command git + +$nodeVersionText = (node --version).Trim() +$nodeMajor = [int]($nodeVersionText.TrimStart('v').Split('.')[0]) +if ($nodeMajor -lt 22) { + throw "Node.js 22 or newer is required. Detected: $nodeVersionText" +} + +Write-Host "Node: $nodeVersionText" -ForegroundColor DarkGray +Write-Host "npm: $((npm --version).Trim())" -ForegroundColor DarkGray +Write-Host "Git: $((git --version).Trim())" -ForegroundColor DarkGray + +Invoke-Step "Installing project dependencies..." { + if (Test-Path ".\package-lock.json") { + npm ci --no-audit --no-fund + } else { + Write-Host "No package-lock.json is present; installing the pinned top-level dependency versions." -ForegroundColor DarkGray + npm install --no-audit --no-fund + } +} + +Invoke-Step "Running the environment doctor..." { + npm run doctor +} + +Invoke-Step "Running source verification and automated tests..." { + npm run check +} + +Write-Host "`nAll local checks passed." -ForegroundColor Green +Write-Host "ForgeFlow will now open the five-step setup wizard." -ForegroundColor Green +Write-Host "Enter credentials only inside the local ForgeFlow password field." -ForegroundColor DarkGray + +npm start diff --git a/src/main/config-store.cjs b/src/main/config-store.cjs new file mode 100644 index 0000000..f8ab0ea --- /dev/null +++ b/src/main/config-store.cjs @@ -0,0 +1,424 @@ +'use strict'; + +const fs = require('node:fs/promises'); +const path = require('node:path'); +const crypto = require('node:crypto'); +const { safeStorage } = require('electron'); +const { assertHttpUrl, assertWorkflowFileName, assertBranchName, assertEnvironmentName, assertCloneRemote, assertRepositoryRelativePaths } = require('../shared/validation.cjs'); + +const DEFAULT_CONFIG = { + schemaVersion: 5, + setupComplete: false, + appearance: 'dark', + gitea: { baseUrl: '', user: null, encryptedToken: null }, + workspaceRoots: [], + repositoryMappings: {}, + deploymentProfiles: {}, + deploymentStates: {}, + favorites: [], + updates: { + owner: 'Jens', + repo: 'ForgeFlow', + branch: 'main', + autoCheck: true, + lastCheckedAt: null + }, + servers: [], + preferences: { + autoRefresh: true, + repositoryPollSeconds: 4, + operationPollSeconds: 5, + fetchIntervalMinutes: 10, + preferredCloneProtocol: 'https', + diagnosticsEnabled: true, + diagnosticLevel: 'info', + logRetentionDays: 14, + maxLogFileMb: 8 + }, + operations: [] +}; + +function uniqueStrings(values) { + return [...new Set((Array.isArray(values) ? values : []).map((value) => String(value || '').trim()).filter(Boolean))]; +} + +class ConfigStore { + constructor(userDataPath) { + this.filePath = path.join(userDataPath, 'forgeflow-config.json'); + this.sessionToken = null; + this.data = structuredClone(DEFAULT_CONFIG); + } + + migrate(parsed) { + const source = parsed && typeof parsed === 'object' ? parsed : {}; + return { + ...structuredClone(DEFAULT_CONFIG), + ...source, + schemaVersion: DEFAULT_CONFIG.schemaVersion, + gitea: { ...DEFAULT_CONFIG.gitea, ...(source.gitea || {}) }, + workspaceRoots: uniqueStrings(source.workspaceRoots), + repositoryMappings: source.repositoryMappings && typeof source.repositoryMappings === 'object' ? source.repositoryMappings : {}, + deploymentProfiles: source.deploymentProfiles && typeof source.deploymentProfiles === 'object' ? source.deploymentProfiles : {}, + deploymentStates: source.deploymentStates && typeof source.deploymentStates === 'object' ? source.deploymentStates : {}, + favorites: uniqueStrings(source.favorites).map((item) => item.toLowerCase()), + updates: { ...DEFAULT_CONFIG.updates, ...(source.updates || {}) }, + servers: Array.isArray(source.servers) ? source.servers.filter((item) => item && typeof item === 'object') : [], + preferences: { ...DEFAULT_CONFIG.preferences, ...(source.preferences || {}) }, + operations: Array.isArray(source.operations) ? source.operations.slice(0, 250).map((operation) => { const { runnerLog, ...safeOperation } = operation || {}; return safeOperation; }) : [] + }; + } + + async load() { + try { + const raw = await fs.readFile(this.filePath, 'utf8'); + this.data = this.migrate(JSON.parse(raw)); + await this.save(); + } catch (error) { + if (error.code !== 'ENOENT') throw error; + await this.save(); + } + return this.getPublicState(); + } + + async save() { + await fs.mkdir(path.dirname(this.filePath), { recursive: true }); + const temporary = `${this.filePath}.${process.pid}.${Date.now()}.tmp`; + await fs.writeFile(temporary, JSON.stringify(this.data, null, 2), { mode: 0o600 }); + await fs.rename(temporary, this.filePath); + try { await fs.chmod(this.filePath, 0o600); } catch {} + } + + setToken(token, { preserveExisting = false } = {}) { + const value = String(token || '').trim(); + if (!value && preserveExisting && this.getToken()) return { persistent: Boolean(this.data.gitea.encryptedToken), preserved: true }; + if (!value) { + this.data.gitea.encryptedToken = null; + this.sessionToken = null; + return { persistent: true, preserved: false }; + } + + if (safeStorage.isEncryptionAvailable()) { + this.data.gitea.encryptedToken = safeStorage.encryptString(value).toString('base64'); + this.sessionToken = null; + return { persistent: true, preserved: false }; + } + + this.data.gitea.encryptedToken = null; + this.sessionToken = value; + return { persistent: false, preserved: false }; + } + + getToken() { + if (this.sessionToken) return this.sessionToken; + if (!this.data.gitea.encryptedToken) return ''; + try { + return safeStorage.decryptString(Buffer.from(this.data.gitea.encryptedToken, 'base64')); + } catch { + return ''; + } + } + + + encryptSecret(value) { + const text = String(value || ''); + if (!text) return null; + if (!safeStorage.isEncryptionAvailable()) { + const error = new Error('Secure credential storage is unavailable. ForgeFlow will not persist server passwords or key passphrases.'); + error.code = 'SECURE_STORAGE_UNAVAILABLE'; + throw error; + } + return safeStorage.encryptString(text).toString('base64'); + } + + decryptSecret(value) { + if (!value) return ''; + try { return safeStorage.decryptString(Buffer.from(value, 'base64')); } + catch { return ''; } + } + + normalizeServer(server, existing = null) { + const source = server || {}; + const name = String(source.name || existing?.name || 'Unraid').trim().slice(0, 100); + const host = String(source.host || existing?.host || '').trim(); + if (!host || /[\s/@]/.test(host)) throw new Error('Enter a valid SSH hostname or IP address.'); + const port = Math.min(Math.max(Number(source.port || existing?.port || 22), 1), 65535); + const username = String(source.username || existing?.username || '').trim(); + if (!username || /[\s@]/.test(username)) throw new Error('Enter a valid SSH username.'); + const authType = ['password', 'privateKey'].includes(source.authType) ? source.authType : (existing?.authType || 'privateKey'); + const basePath = String(source.basePath || existing?.basePath || '/mnt/user/appdata').trim().replace(/\/+$/, ''); + if (!basePath.startsWith('/') || /[\r\n\0]/.test(basePath)) throw new Error('The server base path must be an absolute Unix path.'); + const privateKeyPath = String(source.privateKeyPath || existing?.privateKeyPath || '').trim(); + const hostFingerprint = String(source.hostFingerprint || existing?.hostFingerprint || '').trim(); + return { + id: source.id || existing?.id || crypto.randomUUID(), + name, + host, + port, + username, + authType, + basePath, + privateKeyPath, + hostFingerprint, + encryptedPassword: existing?.encryptedPassword || null, + encryptedPassphrase: existing?.encryptedPassphrase || null, + createdAt: existing?.createdAt || new Date().toISOString(), + updatedAt: new Date().toISOString() + }; + } + + async saveServer(server, secrets = {}) { + const existing = this.data.servers.find((item) => item.id === server?.id) || null; + const normalized = this.normalizeServer(server, existing); + if (Object.prototype.hasOwnProperty.call(secrets, 'password') && String(secrets.password || '')) { + normalized.encryptedPassword = this.encryptSecret(secrets.password); + } + if (Object.prototype.hasOwnProperty.call(secrets, 'passphrase') && String(secrets.passphrase || '')) { + normalized.encryptedPassphrase = this.encryptSecret(secrets.passphrase); + } + if (normalized.authType === 'password') { + normalized.privateKeyPath = ''; + normalized.encryptedPassphrase = null; + } else { + normalized.encryptedPassword = null; + } + if (normalized.authType === 'password' && !normalized.encryptedPassword) throw new Error('A password is required for password authentication.'); + if (normalized.authType === 'privateKey' && !normalized.privateKeyPath) throw new Error('Select a private key file.'); + this.data.servers = [normalized, ...this.data.servers.filter((item) => item.id !== normalized.id)]; + await this.save(); + return this.getPublicServer(normalized); + } + + async deleteServer(serverId) { + this.data.servers = this.data.servers.filter((item) => item.id !== serverId); + for (const [key, profiles] of Object.entries(this.data.deploymentProfiles)) { + this.data.deploymentProfiles[key] = profiles.filter((profile) => profile.serverId !== serverId); + if (!this.data.deploymentProfiles[key].length) delete this.data.deploymentProfiles[key]; + } + await this.save(); + } + + getServer(serverId) { + return this.data.servers.find((item) => item.id === serverId) || null; + } + + getServerCredentials(serverId) { + const server = this.getServer(serverId); + if (!server) throw new Error('The configured server no longer exists.'); + return { + password: this.decryptSecret(server.encryptedPassword), + passphrase: this.decryptSecret(server.encryptedPassphrase) + }; + } + + getPublicServer(server) { + if (!server) return null; + const { encryptedPassword, encryptedPassphrase, ...publicServer } = server; + return { + ...structuredClone(publicServer), + hasPassword: Boolean(encryptedPassword), + hasPassphrase: Boolean(encryptedPassphrase) + }; + } + + async setUpdatePreferences(updates) { + const next = { ...this.data.updates, ...(updates || {}) }; + next.owner = String(next.owner || 'Jens').trim().slice(0, 100); + next.repo = String(next.repo || 'ForgeFlow').trim().slice(0, 100); + next.branch = assertBranchName(next.branch || 'main'); + next.autoCheck = next.autoCheck !== false; + this.data.updates = next; + await this.save(); + return this.getPublicState(); + } + + async patch(patch) { + this.data = this.migrate({ ...this.data, ...patch }); + await this.save(); + return this.getPublicState(); + } + + async updateGitea({ baseUrl, token, user }) { + const tokenState = this.setToken(token, { preserveExisting: true }); + this.data.gitea = { + ...this.data.gitea, + baseUrl, + user: user || this.data.gitea.user, + encryptedToken: this.data.gitea.encryptedToken + }; + await this.save(); + return tokenState; + } + + async completeSetup({ baseUrl, token, user, workspaceRoots }) { + const tokenState = this.setToken(token); + this.data.setupComplete = true; + this.data.gitea = { baseUrl, user, encryptedToken: this.data.gitea.encryptedToken }; + this.data.workspaceRoots = uniqueStrings(workspaceRoots); + await this.save(); + return { state: this.getPublicState(), tokenState }; + } + + async saveMapping(fullName, localPath) { + this.data.repositoryMappings[String(fullName).toLowerCase()] = localPath; + await this.save(); + } + + async removeMapping(fullName) { + delete this.data.repositoryMappings[String(fullName).toLowerCase()]; + await this.save(); + } + + async setFavorite(fullName, favorite) { + const key = String(fullName || '').toLowerCase(); + const favorites = new Set(this.data.favorites || []); + if (favorite) favorites.add(key); else favorites.delete(key); + this.data.favorites = [...favorites]; + await this.save(); + return this.getPublicState(); + } + + normalizeDeploymentProfile(profile) { + const environment = assertEnvironmentName(profile.environment || 'production'); + const provider = ['gitea-actions', 'ssh-unraid'].includes(profile.provider) ? profile.provider : 'gitea-actions'; + const healthcheckUrl = assertHttpUrl(profile.healthcheckUrl, { optional: true, label: 'Healthcheck URL' }); + const common = { + id: profile.id || crypto.randomUUID(), + name: String(profile.name || environment || 'Production').trim().slice(0, 100), + environment, + provider, + branch: assertBranchName(profile.branch || 'main'), + healthcheckUrl, + confirmationRequired: profile.confirmationRequired !== false, + inputs: {} + }; + if (provider === 'ssh-unraid') { + const remoteFolder = String(profile.remoteFolder || '').trim(); + if (!remoteFolder || !/^[a-zA-Z0-9._-]+$/.test(remoteFolder)) throw new Error('Remote folder must contain only letters, numbers, dots, underscores and dashes.'); + const preservePaths = assertRepositoryRelativePaths(uniqueStrings(profile.preservePaths || ['.env', 'appdata', 'data', 'logs', 'config', 'compose.override.yml'])); + return { + ...common, + serverId: String(profile.serverId || '').trim(), + remoteFolder, + composeFile: String(profile.composeFile || 'docker-compose.yml').trim(), + composeService: String(profile.composeService || '').trim(), + cloneUrl: profile.cloneUrl ? assertCloneRemote(profile.cloneUrl) : '', + alignRemote: profile.alignRemote === true, + hostPort: profile.hostPort ? Math.min(Math.max(Number(profile.hostPort), 1), 65535) : null, + containerPort: profile.containerPort ? Math.min(Math.max(Number(profile.containerPort), 1), 65535) : null, + webUiUrl: assertHttpUrl(profile.webUiUrl, { optional: true, label: 'Web UI URL' }), + iconUrl: assertHttpUrl(profile.iconUrl, { optional: true, label: 'Icon URL' }), + preservePaths, + generatedCompose: profile.generatedCompose === true + }; + } + const statusUrl = assertHttpUrl(profile.statusUrl, { label: 'Application status URL' }); + return { + ...common, + workflowFile: assertWorkflowFileName(profile.workflowFile || 'deploy.yml'), + rollbackWorkflowFile: profile.rollbackWorkflowFile ? assertWorkflowFileName(profile.rollbackWorkflowFile) : '', + statusUrl + }; + } + + async saveDeploymentProfile(fullName, profile) { + const key = String(fullName).toLowerCase(); + const profiles = Array.isArray(this.data.deploymentProfiles[key]) ? this.data.deploymentProfiles[key] : []; + const normalized = this.normalizeDeploymentProfile(profile || {}); + const next = profiles.filter((item) => item.id !== normalized.id); + next.push(normalized); + this.data.deploymentProfiles[key] = next; + await this.save(); + return normalized; + } + + async deleteDeploymentProfile(fullName, profileId) { + const key = String(fullName || '').toLowerCase(); + const profiles = Array.isArray(this.data.deploymentProfiles[key]) ? this.data.deploymentProfiles[key] : []; + const next = profiles.filter((item) => item.id !== profileId); + if (next.length) this.data.deploymentProfiles[key] = next; + else delete this.data.deploymentProfiles[key]; + delete this.data.deploymentStates[profileId]; + await this.save(); + return next; + } + + getDeploymentProfiles(fullName) { + return structuredClone(this.data.deploymentProfiles[String(fullName || '').toLowerCase()] || []); + } + + getDeploymentProfile(fullName, profileId) { + return this.getDeploymentProfiles(fullName).find((item) => item.id === profileId) || null; + } + + async saveDeploymentState(profileId, state) { + this.data.deploymentStates[profileId] = { + ...(this.data.deploymentStates[profileId] || {}), + ...state, + checkedAt: state.checkedAt || new Date().toISOString() + }; + await this.save(); + return structuredClone(this.data.deploymentStates[profileId]); + } + + getDeploymentState(profileId) { + return structuredClone(this.data.deploymentStates[profileId] || null); + } + + async addOperation(operation) { + const existing = this.data.operations.find((item) => item.id === operation.id); + const normalized = { + id: operation.id || crypto.randomUUID(), + createdAt: existing?.createdAt || operation.createdAt || new Date().toISOString(), + ...existing, + ...operation, + updatedAt: new Date().toISOString() + }; + this.data.operations = [normalized, ...this.data.operations.filter((item) => item.id !== normalized.id)].slice(0, 250); + await this.save(); + return structuredClone(normalized); + } + + getOperation(operationId) { + return structuredClone(this.data.operations.find((item) => item.id === operationId) || null); + } + + async setPreferences(preferences) { + const next = { ...this.data.preferences, ...(preferences || {}) }; + next.repositoryPollSeconds = Math.min(Math.max(Number(next.repositoryPollSeconds) || 4, 2), 60); + next.operationPollSeconds = Math.min(Math.max(Number(next.operationPollSeconds) || 5, 3), 120); + next.fetchIntervalMinutes = Math.min(Math.max(Number(next.fetchIntervalMinutes) || 10, 0), 240); + next.autoRefresh = next.autoRefresh !== false; + next.preferredCloneProtocol = ['https', 'ssh'].includes(next.preferredCloneProtocol) ? next.preferredCloneProtocol : 'https'; + next.diagnosticsEnabled = next.diagnosticsEnabled !== false; + next.diagnosticLevel = ['debug', 'info', 'warning', 'error'].includes(next.diagnosticLevel) ? next.diagnosticLevel : 'info'; + next.logRetentionDays = Math.min(Math.max(Number(next.logRetentionDays) || 14, 1), 90); + next.maxLogFileMb = Math.min(Math.max(Number(next.maxLogFileMb) || 8, 1), 50); + this.data.preferences = next; + await this.save(); + return this.getPublicState(); + } + + getPublicState() { + return { + schemaVersion: this.data.schemaVersion, + setupComplete: this.data.setupComplete, + appearance: this.data.appearance, + gitea: { + baseUrl: this.data.gitea.baseUrl, + user: this.data.gitea.user, + hasToken: Boolean(this.getToken()) + }, + workspaceRoots: [...this.data.workspaceRoots], + repositoryMappings: { ...this.data.repositoryMappings }, + deploymentProfiles: structuredClone(this.data.deploymentProfiles), + deploymentStates: structuredClone(this.data.deploymentStates), + favorites: [...this.data.favorites], + updates: { ...this.data.updates }, + servers: this.data.servers.map((server) => this.getPublicServer(server)), + preferences: { ...this.data.preferences }, + operations: structuredClone(this.data.operations) + }; + } +} + +module.exports = { ConfigStore, DEFAULT_CONFIG }; diff --git a/src/main/deployment-service.cjs b/src/main/deployment-service.cjs new file mode 100644 index 0000000..5fc6dde --- /dev/null +++ b/src/main/deployment-service.cjs @@ -0,0 +1,424 @@ +'use strict'; + +const crypto = require('node:crypto'); +const { assertDeploymentRequest, assertFullCommitSha, assertHttpUrl } = require('../shared/validation.cjs'); +const { redactSecrets } = require('./log-redaction.cjs'); + +const TERMINAL_STATUSES = new Set(['success', 'failed', 'cancelled', 'rolled-back']); + +function applicationVerificationFailure(operation, state) { + if (!state?.statusConfigured) return { stage: 'version-verification', message: 'No server status endpoint is configured.' }; + if (!state.statusReachable) return { stage: 'version-verification', message: state.error || 'The server status endpoint is not reachable.' }; + if (!state.statusRepository) return { stage: 'version-verification', message: 'The server status endpoint did not identify its repository.' }; + if (state.statusRepository !== operation.repository) return { stage: 'version-verification', message: `The status endpoint belongs to ${state.statusRepository}, not ${operation.repository}.` }; + if (!state.statusEnvironment) return { stage: 'version-verification', message: 'The server status endpoint did not identify its environment.' }; + if (state.statusEnvironment !== operation.environment) return { stage: 'version-verification', message: `The status endpoint belongs to ${state.statusEnvironment}, not ${operation.environment}.` }; + if (!state.liveSha) return { stage: 'version-verification', message: 'The server status endpoint did not return a valid full commit SHA.' }; + if (state.liveSha !== operation.sha) return { stage: 'version-verification', message: `Server reports ${state.liveSha.slice(0, 7)} instead of ${operation.shortSha}.` }; + if (!state.requestedSha) return { stage: 'version-verification', message: 'The server status endpoint did not return the requested commit SHA.' }; + if (state.requestedSha !== operation.sha) return { stage: 'version-verification', message: 'The server status document was created for a different requested commit.' }; + if (!state.requestId) return { stage: 'version-verification', message: 'The server status endpoint did not return the deployment request ID.' }; + if (state.requestId !== operation.id) return { stage: 'version-verification', message: 'The server status belongs to a different deployment request.' }; + if (state.lastExitCode !== 0) return { stage: 'server-command', message: `The server deployment command reported exit code ${state.lastExitCode ?? 'unknown'}.` }; + if (state.healthy !== true) return { stage: 'healthcheck', message: state.error || `The server did not report a healthy application state (${state.healthStatus || 'unknown'}).` }; + return null; +} + +function terminalRunConclusion(run) { + const value = String(run?.conclusion || run?.status || '').toLowerCase(); + if (['success'].includes(value)) return 'success'; + if (['failure', 'failed', 'timed_out', 'startup_failure'].includes(value)) return 'failed'; + if (['cancelled', 'canceled', 'skipped'].includes(value)) return 'cancelled'; + return null; +} + +function isRunningStatus(value) { + return ['running', 'in_progress', 'processing'].includes(String(value || '').toLowerCase()); +} + +function isQueuedStatus(value) { + return ['pending', 'queued', 'waiting', 'blocked', 'requested'].includes(String(value || '').toLowerCase()); +} + +class DeploymentService { + constructor(store, giteaService, gitService, diagnostics = null) { + this.store = store; + this.gitea = giteaService; + this.git = gitService; + this.diagnostics = diagnostics; + this.refreshLocks = new Set(); + } + + splitRepository(fullName) { + const [owner, repo, ...unexpected] = String(fullName || '').split('/'); + if (!owner || !repo || unexpected.length) throw new Error('Invalid Gitea repository identity.'); + return { owner, repo }; + } + + makeStages() { + return [ + { id: 'requested', label: 'Requested', status: 'complete' }, + { id: 'verified', label: 'Verified', status: 'complete' }, + { id: 'queued', label: 'Workflow queued', status: 'active' }, + { id: 'runner', label: 'Runner execution', status: 'pending' }, + { id: 'healthcheck', label: 'Healthcheck', status: 'pending' }, + { id: 'complete', label: 'Complete', status: 'pending' } + ]; + } + + setStage(operation, id, status) { + const stage = operation.stages?.find((item) => item.id === id); + if (stage) stage.status = status; + } + + appendLog(operation, line) { + const clean = redactSecrets(line, [this.store.getToken()]); + operation.logs = Array.isArray(operation.logs) ? operation.logs : []; + if (operation.logs.at(-1) !== clean) operation.logs.push(clean); + operation.logs = operation.logs.slice(-1000); + } + + async captureBaselineRunIds(owner, repo, branch, operation) { + try { + const result = await this.gitea.listWorkflowRuns({ owner, repo, branch, limit: 50 }); + const ids = (result.runs || []).map((run) => run.id).filter((id) => id !== null && id !== undefined).map(String); + operation.baselineRunIds = [...new Set(ids)].slice(0, 100); + this.appendLog(operation, `[info] Captured ${operation.baselineRunIds.length} existing Actions run identifier(s) before dispatch.`); + } catch (error) { + operation.baselineRunIds = []; + this.appendLog(operation, `[warning] Could not capture the pre-dispatch run baseline: ${error.message}`); + } + } + + async validateDeploy(repository, profile, sha) { + assertDeploymentRequest(profile, sha); + const localStatus = await this.git.status(repository.localPath); + if (localStatus.head !== sha) throw new Error('The selected commit no longer matches the local repository. Refresh before deploying.'); + if (localStatus.branch.head !== profile.branch) throw new Error(`This profile only allows deployments from ${profile.branch}.`); + if (localStatus.counts.changed) throw new Error('Commit local changes before deploying.'); + if (localStatus.branch.ahead) throw new Error('Push all local commits before deploying.'); + if (localStatus.branch.behind) throw new Error('Synchronize with Gitea before deploying.'); + if (!localStatus.branch.upstream) throw new Error('Publish this branch to Gitea before deploying.'); + await this.git.verifyCommitOnRemoteBranch(repository.localPath, sha, profile.branch); + return localStatus; + } + + async deploy({ repository, profileId, sha }) { + if (!repository?.fullName || !repository?.localPath) throw new Error('A linked local repository is required for deployment.'); + const profile = this.store.getDeploymentProfile(repository.fullName, profileId); + const fullSha = assertFullCommitSha(sha); + await this.validateDeploy(repository, profile, fullSha); + const { owner, repo } = this.splitRepository(repository.fullName); + + const operation = { + id: crypto.randomUUID(), + type: 'deployment', + action: 'deploy', + status: 'requested', + repository: repository.fullName, + profileId, + profileName: profile.name, + environment: profile.environment, + workflowFile: profile.workflowFile, + branch: profile.branch, + sha: fullSha, + shortSha: fullSha.slice(0, 7), + dispatchedAt: new Date().toISOString(), + stages: this.makeStages(), + logs: [ + `[info] Verified clean ${profile.branch} at ${fullSha}`, + `[info] Dispatching ${profile.workflowFile} for ${repository.fullName}` + ] + }; + await this.captureBaselineRunIds(owner, repo, profile.branch, operation); + await this.store.addOperation(operation); + await this.diagnostics?.info('deployment.dispatch.requested', { operationId: operation.id, repository: operation.repository, profileId, environment: operation.environment, branch: operation.branch, sha: operation.sha, workflowFile: operation.workflowFile }); + + try { + await this.gitea.dispatchWorkflow({ + owner, + repo, + workflowFile: profile.workflowFile, + ref: profile.branch, + inputs: { environment: profile.environment, commit_sha: fullSha, request_id: operation.id } + }); + operation.status = 'queued'; + this.appendLog(operation, '[ok] Gitea accepted the workflow dispatch request.'); + this.appendLog(operation, '[info] Resolving the corresponding Actions run…'); + const saved = await this.store.addOperation(operation); + await this.diagnostics?.info('deployment.dispatch.accepted', { operationId: operation.id, repository: operation.repository, status: operation.status }); + return saved; + } catch (error) { + operation.status = 'failed'; + this.setStage(operation, 'queued', 'failed'); + operation.failure = { stage: 'dispatch', message: error.message }; + this.appendLog(operation, `[error] ${error.message}`); + await this.store.addOperation(operation); + await this.diagnostics?.error('deployment.dispatch.failed', { operationId: operation.id, repository: operation.repository, message: error.message, code: error.code, status: error.status }); + throw error; + } + } + + async rollback({ repository, profileId, targetSha }) { + if (!repository?.fullName || !repository?.localPath) throw new Error('A linked local repository is required for rollback.'); + const profile = this.store.getDeploymentProfile(repository.fullName, profileId); + if (!profile) throw new Error('Deployment profile not found.'); + if (!profile.rollbackWorkflowFile) throw new Error('No rollback workflow is configured for this profile.'); + const fullSha = assertFullCommitSha(targetSha); + assertDeploymentRequest({ ...profile, workflowFile: profile.rollbackWorkflowFile }, fullSha); + const state = await this.refreshProfileState(repository.fullName, profileId); + if (!state.statusReachable) throw new Error(state.error || 'The server status endpoint must be reachable before rollback.'); + if (state.statusRepository !== repository.fullName || state.statusEnvironment !== profile.environment) throw new Error('The status endpoint does not match this repository and environment.'); + if (!state.previousSha) throw new Error('The server status endpoint does not report a previous version.'); + if (state.previousSha !== fullSha) throw new Error('The requested rollback SHA is no longer the previous server version. Refresh the environment state.'); + if (state.liveSha === fullSha) throw new Error('The requested rollback version is already live.'); + await this.git.verifyCommitOnRemoteBranch(repository.localPath, fullSha, profile.branch); + const { owner, repo } = this.splitRepository(repository.fullName); + + const operation = { + id: crypto.randomUUID(), + type: 'deployment', + action: 'rollback', + status: 'requested', + repository: repository.fullName, + profileId, + profileName: profile.name, + environment: profile.environment, + workflowFile: profile.rollbackWorkflowFile, + branch: profile.branch, + sha: fullSha, + shortSha: fullSha.slice(0, 7), + dispatchedAt: new Date().toISOString(), + stages: this.makeStages(), + logs: [ + `[warning] Rollback target verified on origin/${profile.branch}: ${fullSha}`, + `[info] Dispatching ${profile.rollbackWorkflowFile}` + ] + }; + await this.captureBaselineRunIds(owner, repo, profile.branch, operation); + await this.store.addOperation(operation); + await this.diagnostics?.info('deployment.rollback.requested', { operationId: operation.id, repository: operation.repository, profileId, environment: operation.environment, branch: operation.branch, sha: operation.sha, workflowFile: operation.workflowFile }); + + try { + await this.gitea.dispatchWorkflow({ + owner, + repo, + workflowFile: profile.rollbackWorkflowFile, + ref: profile.branch, + inputs: { environment: profile.environment, target_sha: fullSha, request_id: operation.id } + }); + operation.status = 'queued'; + this.appendLog(operation, '[ok] Gitea accepted the rollback request.'); + const saved = await this.store.addOperation(operation); + await this.diagnostics?.info('deployment.rollback.accepted', { operationId: operation.id, repository: operation.repository }); + return saved; + } catch (error) { + operation.status = 'failed'; + this.setStage(operation, 'queued', 'failed'); + operation.failure = { stage: 'dispatch', message: error.message }; + this.appendLog(operation, `[error] ${error.message}`); + await this.store.addOperation(operation); + await this.diagnostics?.error('deployment.rollback.failed', { operationId: operation.id, repository: operation.repository, message: error.message, code: error.code, status: error.status }); + throw error; + } + } + + mapJobsToStages(operation, jobs) { + operation.jobs = jobs; + if (!jobs.length) return; + const running = jobs.some((job) => isRunningStatus(job.status)); + const failed = jobs.some((job) => terminalRunConclusion(job) === 'failed'); + const allDone = jobs.every((job) => terminalRunConclusion(job)); + this.setStage(operation, 'queued', 'complete'); + this.setStage(operation, 'runner', failed ? 'failed' : allDone ? 'complete' : running ? 'active' : 'pending'); + } + + async refreshOperation(operationId) { + if (this.refreshLocks.has(operationId)) return this.store.getOperation(operationId); + const operation = this.store.getOperation(operationId); + if (!operation || operation.type !== 'deployment') throw new Error('Deployment operation not found.'); + if (TERMINAL_STATUSES.has(operation.status)) return operation; + + this.refreshLocks.add(operationId); + try { + const profile = this.store.getDeploymentProfile(operation.repository, operation.profileId); + if (!profile) throw new Error('The deployment profile used by this operation no longer exists.'); + const { owner, repo } = this.splitRepository(operation.repository); + const found = await this.gitea.findWorkflowRun({ + owner, + repo, + sha: operation.sha, + branch: operation.branch, + workflowFile: operation.workflowFile, + dispatchedAt: operation.dispatchedAt || operation.createdAt, + excludeRunIds: operation.baselineRunIds || [] + }); + + if (!found.run) { + operation.status = 'queued'; + this.setStage(operation, 'queued', 'active'); + this.appendLog(operation, '[info] Workflow is queued or not visible through the Actions API yet.'); + return await this.store.addOperation(operation); + } + + operation.run = { ...found.run, source: found.source }; + operation.runUrl = found.run.htmlUrl || `${this.store.data.gitea.baseUrl}/${operation.repository}/actions/runs/${found.run.runNumber}`; + this.setStage(operation, 'queued', 'complete'); + const runConclusion = terminalRunConclusion(found.run); + if (!runConclusion) { + operation.status = isRunningStatus(found.run.status) ? 'running' : 'queued'; + this.setStage(operation, 'runner', operation.status === 'running' ? 'active' : 'pending'); + } + + try { + const jobs = await this.gitea.listWorkflowJobs({ owner, repo, runNumber: found.run.runNumber }); + this.mapJobsToStages(operation, jobs); + for (const job of jobs) { + const conclusion = job.conclusion || job.status; + this.appendLog(operation, `[job] ${job.name}: ${conclusion}`); + } + // Raw runner output is intentionally not ingested or persisted. Open the trusted Gitea run for full logs. + } catch (error) { + this.appendLog(operation, `[warning] Job details unavailable: ${error.message}`); + } + + if (runConclusion === 'success') { + this.setStage(operation, 'runner', 'complete'); + this.setStage(operation, 'healthcheck', 'active'); + const state = await this.refreshProfileState(operation.repository, operation.profileId, { expectedSha: operation.sha }); + operation.applicationState = state; + const verificationFailure = applicationVerificationFailure(operation, state); + if (verificationFailure) { + operation.status = 'failed'; + this.setStage(operation, 'healthcheck', 'failed'); + this.setStage(operation, 'complete', 'failed'); + operation.failure = verificationFailure; + this.appendLog(operation, `[error] ${verificationFailure.message}`); + } else { + operation.status = operation.action === 'rollback' ? 'rolled-back' : 'success'; + this.setStage(operation, 'healthcheck', 'complete'); + this.setStage(operation, 'complete', 'complete'); + this.appendLog(operation, `[ok] ${operation.action === 'rollback' ? 'Rollback' : 'Deployment'} completed successfully.`); + } + } else if (runConclusion === 'failed' || runConclusion === 'cancelled') { + operation.status = runConclusion; + this.setStage(operation, 'runner', runConclusion === 'failed' ? 'failed' : 'cancelled'); + this.setStage(operation, 'healthcheck', 'skipped'); + this.setStage(operation, 'complete', runConclusion === 'failed' ? 'failed' : 'cancelled'); + operation.failure = { stage: 'runner', message: `Gitea Actions finished with ${runConclusion}.` }; + this.appendLog(operation, `[error] ${operation.failure.message}`); + } + + const saved = await this.store.addOperation(operation); + if (TERMINAL_STATUSES.has(operation.status)) { + await this.diagnostics?.info('deployment.operation.terminal', { operationId: operation.id, repository: operation.repository, status: operation.status, failure: operation.failure || null, applicationState: operation.applicationState || null }); + } else { + await this.diagnostics?.debug('deployment.operation.refreshed', { operationId: operation.id, repository: operation.repository, status: operation.status, run: operation.run ? { id: operation.run.id, runNumber: operation.run.runNumber, status: operation.run.status, conclusion: operation.run.conclusion } : null }); + } + return saved; + } catch (error) { + operation.pollError = error.message; + this.appendLog(operation, `[warning] Status refresh failed: ${error.message}`); + await this.diagnostics?.warning('deployment.operation.poll-failed', { operationId: operation.id, repository: operation.repository, message: error.message }); + return await this.store.addOperation(operation); + } finally { + this.refreshLocks.delete(operationId); + } + } + + async refreshActiveOperations() { + const active = this.store.data.operations.filter((item) => item.type === 'deployment' && !TERMINAL_STATUSES.has(item.status)); + const results = []; + for (const operation of active.slice(0, 20)) results.push(await this.refreshOperation(operation.id)); + return results; + } + + async checkHealth(url) { + if (!url) return { configured: false, healthy: null }; + const normalized = assertHttpUrl(url, { label: 'Healthcheck URL' }); + const started = Date.now(); + try { + const response = await fetch(normalized, { signal: AbortSignal.timeout(10_000), redirect: 'follow', headers: { Accept: 'application/json, text/plain, */*' } }); + return { configured: true, healthy: response.ok, status: response.status, latencyMs: Date.now() - started }; + } catch (error) { + return { configured: true, healthy: false, error: error.message, latencyMs: Date.now() - started }; + } + } + + async readStatusEndpoint(url) { + if (!url) return { configured: false }; + const normalized = assertHttpUrl(url, { label: 'Application status URL' }); + const started = Date.now(); + try { + const response = await fetch(normalized, { signal: AbortSignal.timeout(10_000), redirect: 'follow', headers: { Accept: 'application/json' } }); + if (!response.ok) return { configured: true, reachable: true, ok: false, status: response.status, latencyMs: Date.now() - started }; + const payload = await response.json(); + const liveSha = payload.commit_sha || payload.commitSha || payload.sha || payload.version?.commit_sha || payload.version?.sha || null; + const previousSha = payload.previous_sha || payload.previousSha || payload.previous?.sha || null; + const requestId = payload.request_id || payload.requestId || null; + const requestedSha = payload.requested_sha || payload.requestedSha || null; + const repository = payload.repository || null; + const environment = payload.environment || null; + const rawExitCode = payload.last_exit_code ?? payload.lastExitCode ?? null; + return { + configured: true, + reachable: true, + ok: true, + status: response.status, + latencyMs: Date.now() - started, + liveSha: /^[a-f0-9]{40,64}$/i.test(String(liveSha || '')) ? String(liveSha).toLowerCase() : null, + previousSha: /^[a-f0-9]{40,64}$/i.test(String(previousSha || '')) ? String(previousSha).toLowerCase() : null, + requestId: typeof requestId === 'string' ? requestId.slice(0, 100) : null, + requestedSha: /^[a-f0-9]{40,64}$/i.test(String(requestedSha || '')) ? String(requestedSha).toLowerCase() : null, + repository: typeof repository === 'string' ? repository.slice(0, 200) : null, + environment: typeof environment === 'string' ? environment.slice(0, 64).toLowerCase() : null, + lastExitCode: rawExitCode !== null && rawExitCode !== '' && Number.isInteger(Number(rawExitCode)) ? Number(rawExitCode) : null, + deployedAt: payload.deployed_at || payload.deployedAt || null, + health: payload.health || payload.status || null, + payload + }; + } catch (error) { + return { configured: true, reachable: false, ok: false, error: error.message, latencyMs: Date.now() - started }; + } + } + + async refreshProfileState(fullName, profileId, { expectedSha = null } = {}) { + const profile = this.store.getDeploymentProfile(fullName, profileId); + if (!profile) throw new Error('Deployment profile not found.'); + const [status, health] = await Promise.all([ + this.readStatusEndpoint(profile.statusUrl), + this.checkHealth(profile.healthcheckUrl) + ]); + const state = { + profileId, + repository: fullName, + environment: profile.environment, + liveSha: status.liveSha || null, + previousSha: status.previousSha || null, + deployedAt: status.deployedAt || null, + statusConfigured: Boolean(status.configured), + statusReachable: status.configured ? Boolean(status.reachable && status.ok) : null, + statusCode: status.status || null, + statusRepository: status.repository || null, + statusEnvironment: status.environment || null, + requestedSha: status.requestedSha || null, + lastExitCode: status.lastExitCode, + healthConfigured: Boolean(health.configured), + healthy: health.configured + ? Boolean(health.healthy) + : (['healthy', 'ok', 'success', 'ready'].includes(String(status.health || '').toLowerCase()) + ? true + : (['unhealthy', 'failed', 'error', 'degraded'].includes(String(status.health || '').toLowerCase()) ? false : null)), + healthStatus: health.status || status.health || null, + latencyMs: health.latencyMs ?? status.latencyMs ?? null, + expectedSha: expectedSha || null, + requestId: status.requestId || null, + versionMatches: expectedSha && status.liveSha ? status.liveSha === expectedSha : null, + error: health.error || status.error || null, + checkedAt: new Date().toISOString() + }; + return this.store.saveDeploymentState(profileId, state); + } +} + +module.exports = { DeploymentService, TERMINAL_STATUSES, terminalRunConclusion, applicationVerificationFailure }; diff --git a/src/main/diagnostics-service.cjs b/src/main/diagnostics-service.cjs new file mode 100644 index 0000000..49bc130 --- /dev/null +++ b/src/main/diagnostics-service.cjs @@ -0,0 +1,356 @@ +'use strict'; + +const fs = require('node:fs/promises'); +const path = require('node:path'); +const os = require('node:os'); +const crypto = require('node:crypto'); +const { createZip } = require('../shared/zip-writer.cjs'); +const { sanitizeForDiagnostics, redactSecrets } = require('./log-redaction.cjs'); + +const LEVELS = { debug: 10, info: 20, warning: 30, error: 40 }; + +function dateKey(value = new Date()) { + return value.toISOString().slice(0, 10); +} + +function byteSizeLabel(bytes) { + if (bytes < 1024) return `${bytes} B`; + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; + return `${(bytes / 1024 / 1024).toFixed(1)} MB`; +} + +function safeJson(value) { + return `${JSON.stringify(value, null, 2)}\n`; +} + +function auditBundleEntries(entries, secrets = []) { + const candidates = [...new Set((secrets || []).map((item) => String(item || '').trim()).filter((item) => item.length >= 4))]; + const findings = []; + for (const entry of entries) { + const text = Buffer.isBuffer(entry.data) ? entry.data.toString('utf8') : String(entry.data ?? ''); + for (const secret of candidates) { + if (text.includes(secret)) findings.push({ file: entry.name, type: 'known-runtime-secret' }); + } + if (/-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/i.test(text)) findings.push({ file: entry.name, type: 'private-key-marker' }); + if (/https?:\/\/[^\s:@/]+:(?!\[REDACTED\])[^@\s/]+@/i.test(text)) findings.push({ file: entry.name, type: 'url-credential' }); + } + return { passed: findings.length === 0, checkedFiles: entries.length, knownRuntimeSecretCount: candidates.length, findings }; +} + +class DiagnosticsService { + constructor({ userDataPath, appInfo = {}, secretProvider = () => [], preferencesProvider = () => ({}) }) { + this.userDataPath = userDataPath; + this.logDirectory = path.join(userDataPath, 'diagnostics'); + this.appInfo = appInfo; + this.secretProvider = secretProvider; + this.preferencesProvider = preferencesProvider; + this.sessionId = crypto.randomUUID(); + this.writeChain = Promise.resolve(); + this.initialized = false; + this.lastWriteError = null; + this.lastBundlePath = null; + } + + preferences() { + const source = this.preferencesProvider?.() || {}; + return { + enabled: source.diagnosticsEnabled !== false, + level: ['debug', 'info', 'warning', 'error'].includes(source.diagnosticLevel) ? source.diagnosticLevel : 'info', + retentionDays: Math.min(Math.max(Number(source.logRetentionDays) || 14, 1), 90), + maxFileMb: Math.min(Math.max(Number(source.maxLogFileMb) || 8, 1), 50) + }; + } + + sanitize(value, options = {}) { + return sanitizeForDiagnostics(value, { + secrets: this.secretProvider?.() || [], + homeDir: os.homedir(), + cwd: process.cwd(), + ...options + }); + } + + async initialize() { + await fs.mkdir(this.logDirectory, { recursive: true, mode: 0o700 }); + try { await fs.chmod(this.logDirectory, 0o700); } catch {} + this.initialized = true; + await this.prune(); + await this.info('diagnostics.session.started', { + sessionId: this.sessionId, + app: this.appInfo, + platform: process.platform, + arch: process.arch, + node: process.versions.node, + electron: process.versions.electron || null + }); + } + + shouldWrite(level) { + const preferences = this.preferences(); + return preferences.enabled && LEVELS[level] >= LEVELS[preferences.level]; + } + + filePathForToday() { + return path.join(this.logDirectory, `forgeflow-${dateKey()}.jsonl`); + } + + async rotateIfNeeded(filePath) { + const limit = this.preferences().maxFileMb * 1024 * 1024; + const stat = await fs.stat(filePath).catch(() => null); + if (!stat || stat.size < limit) return filePath; + for (let index = 1; index < 100; index += 1) { + const candidate = path.join(this.logDirectory, `forgeflow-${dateKey()}-${String(index).padStart(2, '0')}.jsonl`); + const candidateStat = await fs.stat(candidate).catch(() => null); + if (!candidateStat || candidateStat.size < limit) return candidate; + } + return path.join(this.logDirectory, `forgeflow-${dateKey()}-${Date.now()}.jsonl`); + } + + log(level, event, details = {}) { + if (!this.shouldWrite(level)) return Promise.resolve(false); + const record = this.sanitize({ + timestamp: new Date().toISOString(), + level, + event: String(event || 'diagnostics.event').slice(0, 160), + sessionId: this.sessionId, + details + }); + const line = `${JSON.stringify(record)}\n`; + this.writeChain = this.writeChain.then(async () => { + try { + if (!this.initialized) await fs.mkdir(this.logDirectory, { recursive: true, mode: 0o700 }); + const target = await this.rotateIfNeeded(this.filePathForToday()); + await fs.appendFile(target, line, { encoding: 'utf8', mode: 0o600 }); + try { await fs.chmod(target, 0o600); } catch {} + this.lastWriteError = null; + return true; + } catch (error) { + this.lastWriteError = error.message; + return false; + } + }); + return this.writeChain; + } + + debug(event, details) { return this.log('debug', event, details); } + info(event, details) { return this.log('info', event, details); } + warning(event, details) { return this.log('warning', event, details); } + error(event, details) { return this.log('error', event, details); } + + async flush() { + await this.writeChain; + } + + async listLogFiles() { + await fs.mkdir(this.logDirectory, { recursive: true, mode: 0o700 }); + const entries = await fs.readdir(this.logDirectory, { withFileTypes: true }); + const files = []; + for (const entry of entries) { + if (!entry.isFile() || !/^forgeflow-.*\.jsonl$/i.test(entry.name)) continue; + const absolute = path.join(this.logDirectory, entry.name); + const stat = await fs.stat(absolute).catch(() => null); + if (stat) files.push({ name: entry.name, path: absolute, size: stat.size, modifiedAt: stat.mtime.toISOString() }); + } + return files.sort((a, b) => b.modifiedAt.localeCompare(a.modifiedAt)); + } + + async prune() { + const cutoff = Date.now() - this.preferences().retentionDays * 24 * 60 * 60 * 1000; + for (const file of await this.listLogFiles()) { + if (new Date(file.modifiedAt).getTime() < cutoff) await fs.rm(file.path, { force: true }).catch(() => {}); + } + } + + async getStatus() { + await this.flush(); + const files = await this.listLogFiles(); + const totalBytes = files.reduce((sum, file) => sum + file.size, 0); + return { + enabled: this.preferences().enabled, + level: this.preferences().level, + retentionDays: this.preferences().retentionDays, + maxFileMb: this.preferences().maxFileMb, + directory: this.sanitize(this.logDirectory), + fileCount: files.length, + totalBytes, + totalSize: byteSizeLabel(totalBytes), + latestAt: files[0]?.modifiedAt || null, + lastWriteError: this.lastWriteError + }; + } + + async clear() { + await this.flush(); + for (const file of await this.listLogFiles()) await fs.rm(file.path, { force: true }); + await this.info('diagnostics.logs.cleared', {}); + return this.getStatus(); + } + + async collectLogs(maxBytes = 20 * 1024 * 1024) { + await this.flush(); + const output = []; + let used = 0; + for (const file of await this.listLogFiles()) { + if (used >= maxBytes) break; + const remaining = maxBytes - used; + const content = await fs.readFile(file.path); + const slice = content.length > remaining ? content.subarray(content.length - remaining) : content; + output.push({ name: `logs/${file.name}`, data: Buffer.from(redactSecrets(slice.toString('utf8'), this.secretProvider?.() || []), 'utf8') }); + used += slice.length; + } + return output; + } + + async exportSupportBundle({ destinationPath, publicState, repositories = [], operations = [], preflight = null, privacyMode = 'standard', extra = {} }) { + if (!destinationPath) throw new Error('No support bundle destination was selected.'); + if (!['standard', 'strict'].includes(privacyMode)) throw new Error('Unsupported diagnostic privacy mode.'); + if (path.extname(destinationPath).toLowerCase() !== '.zip') throw new Error('Diagnostic bundles must use the .zip extension.'); + await this.info('diagnostics.bundle.requested', { privacyMode, repositoryCount: repositories.length, operationCount: operations.length }); + const strict = privacyMode === 'strict'; + const sanitize = (value) => this.sanitize(value, { strictIdentifiers: strict }); + const generatedAt = new Date().toISOString(); + const diagnosticsStatus = await this.getStatus(); + const system = sanitize({ + app: this.appInfo, + generatedAt, + sessionId: this.sessionId, + platform: process.platform, + arch: process.arch, + release: os.release(), + type: os.type(), + cpus: os.cpus()?.map((cpu) => cpu.model).filter((value, index, array) => array.indexOf(value) === index), + cpuCount: os.cpus()?.length || null, + totalMemoryBytes: os.totalmem(), + freeMemoryBytes: os.freemem(), + uptimeSeconds: os.uptime(), + locale: Intl.DateTimeFormat().resolvedOptions().locale, + timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, + versions: process.versions + }); + + const sanitizedState = sanitize(publicState || {}); + if (sanitizedState.gitea) sanitizedState.gitea.hasToken = Boolean(publicState?.gitea?.hasToken); + const sanitizedRepositories = sanitize(repositories.map((repository) => ({ + id: repository.id, + fullName: repository.fullName, + linkState: repository.linkState, + localPath: repository.localPath, + attention: repository.attention, + attentionReason: repository.attentionReason, + readyToDeploy: repository.readyToDeploy, + localStatus: repository.localStatus ? { + branch: repository.localStatus.branch, + head: repository.localStatus.head, + counts: repository.localStatus.counts, + clean: repository.localStatus.clean, + remoteUrl: repository.localStatus.remoteUrl + } : null, + deploymentProfiles: repository.deploymentProfiles?.map((profile) => ({ + id: profile.id, + name: profile.name, + environment: profile.environment, + branch: profile.branch, + workflowFile: profile.workflowFile, + rollbackWorkflowFile: profile.rollbackWorkflowFile, + healthcheckUrl: profile.healthcheckUrl, + statusUrl: profile.statusUrl, + state: profile.state + })) || [] + }))); + const sanitizedOperations = sanitize(operations.map((operation) => ({ + id: operation.id, + type: operation.type, + action: operation.action, + status: operation.status, + repository: operation.repository, + profileId: operation.profileId, + profileName: operation.profileName, + environment: operation.environment, + workflowFile: operation.workflowFile, + branch: operation.branch, + sha: operation.sha, + shortSha: operation.shortSha, + createdAt: operation.createdAt, + updatedAt: operation.updatedAt, + dispatchedAt: operation.dispatchedAt, + stages: operation.stages, + jobs: operation.jobs, + logs: operation.logs, + failure: operation.failure, + pollError: operation.pollError, + applicationState: operation.applicationState, + run: operation.run ? { + id: operation.run.id, + runNumber: operation.run.runNumber, + name: operation.run.name, + status: operation.run.status, + conclusion: operation.run.conclusion, + headSha: operation.run.headSha, + headBranch: operation.run.headBranch, + workflowPath: operation.run.workflowPath, + createdAt: operation.run.createdAt, + updatedAt: operation.run.updatedAt + } : null, + runnerLog: operation.runnerLog ? { + included: false, + reason: 'Raw runner output is intentionally omitted from diagnostic bundles.', + characters: String(operation.runnerLog).length, + lines: String(operation.runnerLog).split(/\r?\n/).length + } : null + }))); + const manifest = { + schemaVersion: 1, + product: 'ForgeFlow Support Bundle', + generatedAt, + privacyMode, + containsSecrets: false, + redaction: { + knownRuntimeSecrets: true, + sensitiveObjectKeys: true, + authorizationHeaders: true, + credentialUrls: true, + privateKeys: true, + userHomePaths: true, + identifiersHashed: strict + }, + files: [] + }; + + const entries = [ + { name: 'README.txt', data: `ForgeFlow diagnostic support bundle\nGenerated: ${generatedAt}\nPrivacy mode: ${privacyMode}\n\nThis bundle is generated locally. Access tokens, passwords, authorization headers, embedded URL credentials, encrypted token blobs and private keys are removed. Review the bundle before sharing it.\n` }, + { name: 'system.json', data: safeJson(system) }, + { name: 'diagnostics-status.json', data: safeJson(sanitize(diagnosticsStatus)) }, + { name: 'configuration-sanitized.json', data: safeJson(sanitizedState) }, + { name: 'repositories-sanitized.json', data: safeJson(sanitizedRepositories) }, + { name: 'operations-sanitized.json', data: safeJson(sanitizedOperations) }, + { name: 'preflight.json', data: safeJson(sanitize(preflight || {})) }, + { name: 'context.json', data: safeJson(sanitize(extra || {})) }, + ...(await this.collectLogs()) + ]; + + const safetyAudit = auditBundleEntries(entries, this.secretProvider?.() || []); + if (!safetyAudit.passed) { + await this.error('diagnostics.bundle.safety-check-failed', { findings: safetyAudit.findings }); + throw new Error('The diagnostic bundle failed its local secret-safety check and was not written.'); + } + entries.push({ name: 'safety-audit.json', data: safeJson(safetyAudit) }); + manifest.files = entries.map((entry) => ({ name: entry.name, bytes: Buffer.byteLength(entry.data) })); + entries.unshift({ name: 'manifest.json', data: safeJson(manifest) }); + const archive = createZip(entries); + const temporary = `${destinationPath}.${process.pid}.${Date.now()}.tmp`; + await fs.mkdir(path.dirname(destinationPath), { recursive: true }); + await fs.writeFile(temporary, archive, { mode: 0o600 }); + await fs.rename(temporary, destinationPath); + try { await fs.chmod(destinationPath, 0o600); } catch {} + this.lastBundlePath = path.resolve(destinationPath); + const sha256 = crypto.createHash('sha256').update(archive).digest('hex'); + await this.info('diagnostics.bundle.created', { destinationPath, bytes: archive.length, sha256, privacyMode }); + return { path: destinationPath, bytes: archive.length, size: byteSizeLabel(archive.length), sha256, privacyMode, generatedAt }; + } + + isKnownBundlePath(filePath) { + return Boolean(filePath && this.lastBundlePath && path.resolve(filePath) === this.lastBundlePath); + } +} + +module.exports = { DiagnosticsService, dateKey, byteSizeLabel, auditBundleEntries, LEVELS }; diff --git a/src/main/git-service.cjs b/src/main/git-service.cjs new file mode 100644 index 0000000..b9f7931 --- /dev/null +++ b/src/main/git-service.cjs @@ -0,0 +1,298 @@ +'use strict'; + +const path = require('node:path'); +const fs = require('node:fs/promises'); +const { run } = require('./process-runner.cjs'); +const { parsePorcelainV2 } = require('../shared/git-status.cjs'); +const { normalizeRemoteUrl } = require('../shared/repository-match.cjs'); +const { + assertSafeRepositoryPath, + assertRepositoryRelativePath, + assertRepositoryRelativePaths, + assertCommitMessage, + assertFullCommitSha, + assertCloneRemote +} = require('../shared/validation.cjs'); + +class GitService { + async isAvailable() { + try { + const result = await run('git', ['--version'], { timeout: 10_000 }); + return { available: true, version: result.stdout.trim() }; + } catch (error) { + return { available: false, version: null, error: error.message }; + } + } + + async ensureRepository(repoPath) { + const resolved = assertSafeRepositoryPath(repoPath); + const stat = await fs.stat(resolved).catch(() => null); + if (!stat?.isDirectory()) throw new Error('The linked local folder no longer exists.'); + const result = await run('git', ['rev-parse', '--show-toplevel'], { cwd: resolved, timeout: 15_000 }); + return path.resolve(result.stdout.trim()); + } + + async status(repoPath) { + const root = await this.ensureRepository(repoPath); + const result = await run('git', ['status', '--porcelain=v2', '--branch', '-z', '--untracked-files=all'], { + cwd: root, + timeout: 30_000 + }); + const parsed = parsePorcelainV2(result.stdout); + const remoteUrl = await this.getRemoteUrl(root).catch(() => ''); + const head = parsed.branch.oid && parsed.branch.oid !== '(initial)' ? parsed.branch.oid : null; + return { ...parsed, root, remoteUrl, head, shortHead: head ? head.slice(0, 7) : null }; + } + + statusFingerprint(status) { + return JSON.stringify({ + head: status?.head || null, + branch: status?.branch || null, + files: (status?.files || []).map((file) => [file.path, file.originalPath, file.indexCode, file.worktreeCode]) + }); + } + + async getRemoteUrl(repoPath, remote = 'origin') { + const result = await run('git', ['remote', 'get-url', remote], { cwd: repoPath, timeout: 15_000 }); + return result.stdout.trim(); + } + + async diff(repoPath, filePath, staged = false) { + const root = await this.ensureRepository(repoPath); + const safeFile = filePath ? assertRepositoryRelativePath(filePath) : ''; + const args = ['diff', '--no-ext-diff', '--no-color', '--unified=4']; + if (staged) args.push('--cached'); + if (safeFile) args.push('--', safeFile); + const result = await run('git', args, { cwd: root, timeout: 30_000, maxBuffer: 16 * 1024 * 1024 }); + if (!result.stdout && safeFile && !staged) { + const candidate = path.resolve(root, safeFile); + if (candidate !== root && !candidate.startsWith(`${root}${path.sep}`)) throw new Error('File path escapes repository root.'); + const content = await fs.readFile(candidate, 'utf8').catch(() => ''); + if (content) return `diff --git a/${safeFile} b/${safeFile}\nnew file mode 100644\n--- /dev/null\n+++ b/${safeFile}\n${content.split('\n').map((line) => `+${line}`).join('\n')}`; + } + return result.stdout; + } + + async stage(repoPath, files) { + const root = await this.ensureRepository(repoPath); + const selected = assertRepositoryRelativePaths(files); + await run('git', selected.length ? ['add', '--', ...selected] : ['add', '--all'], { cwd: root, timeout: 60_000 }); + return this.status(root); + } + + async unstage(repoPath, files) { + const root = await this.ensureRepository(repoPath); + const selected = assertRepositoryRelativePaths(files); + const hasHead = await run('git', ['rev-parse', '--verify', 'HEAD'], { cwd: root, allowExitCodes: [128] }); + if (hasHead.exitCode === 0) { + await run('git', selected.length ? ['restore', '--staged', '--', ...selected] : ['restore', '--staged', '.'], { cwd: root }); + } else { + await run('git', selected.length ? ['rm', '--cached', '--', ...selected] : ['rm', '--cached', '-r', '.'], { cwd: root, allowExitCodes: [1] }); + } + return this.status(root); + } + + async prepareSelectedStage(root, files) { + const selected = assertRepositoryRelativePaths(files); + if (selected.length) { + const stagedBefore = await run('git', ['diff', '--cached', '--name-only', '-z'], { cwd: root }); + const alreadyStaged = stagedBefore.stdout.split('\0').filter(Boolean); + const excludedStaged = alreadyStaged.filter((file) => !selected.includes(file)); + if (excludedStaged.length) { + throw new Error(`Some staged files are not selected (${excludedStaged.slice(0, 3).join(', ')}${excludedStaged.length > 3 ? ', …' : ''}). Select them or unstage them first.`); + } + } + await this.stage(root, selected); + const stagedCheck = await run('git', ['diff', '--cached', '--quiet'], { cwd: root, allowExitCodes: [1] }); + if (stagedCheck.exitCode === 0) throw new Error('There are no staged changes to commit.'); + return selected; + } + + async commit(repoPath, message, files = []) { + const root = await this.ensureRepository(repoPath); + const commitMessage = assertCommitMessage(message); + await this.prepareSelectedStage(root, files); + const result = await run('git', ['commit', '-m', commitMessage], { cwd: root, timeout: 120_000, maxBuffer: 16 * 1024 * 1024 }); + const status = await this.status(root); + return { output: result.stdout.trim(), sha: status.head, shortSha: status.shortHead, status }; + } + + async commitAndPush(repoPath, message, files = []) { + const committed = await this.commit(repoPath, message, files); + try { + const pushed = await this.push(repoPath); + return { commitOutput: committed.output, pushOutput: pushed.output, status: pushed.status, sha: committed.sha }; + } catch (error) { + const wrapped = new Error(`Commit ${committed.shortSha} was created locally, but push failed: ${error.message}`); + wrapped.code = 'PUSH_AFTER_COMMIT_FAILED'; + wrapped.commitSha = committed.sha; + wrapped.recoverable = true; + throw wrapped; + } + } + + async push(repoPath) { + const root = await this.ensureRepository(repoPath); + const status = await this.status(root); + const branch = status.branch.head; + if (!branch || branch === '(detached)') throw new Error('Cannot push from a detached HEAD.'); + const args = status.branch.upstream ? ['push', '--porcelain'] : ['push', '--porcelain', '--set-upstream', 'origin', branch]; + const result = await run('git', args, { cwd: root, timeout: 180_000, maxBuffer: 16 * 1024 * 1024 }); + return { output: `${result.stdout}\n${result.stderr}`.trim(), status: await this.status(root) }; + } + + async fetch(repoPath) { + const root = await this.ensureRepository(repoPath); + const result = await run('git', ['fetch', '--prune'], { cwd: root, timeout: 180_000 }); + return { output: `${result.stdout}\n${result.stderr}`.trim(), status: await this.status(root) }; + } + + async pullFastForward(repoPath) { + const root = await this.ensureRepository(repoPath); + const status = await this.status(root); + if (!status.clean) throw new Error('Commit or stash local changes before synchronizing.'); + if (!status.branch.upstream) throw new Error('This branch has no upstream branch. Publish it first.'); + const result = await run('git', ['pull', '--ff-only'], { cwd: root, timeout: 180_000 }); + return { output: `${result.stdout}\n${result.stderr}`.trim(), status: await this.status(root) }; + } + + async history(repoPath, limit = 20) { + const root = await this.ensureRepository(repoPath); + const format = '%H%x1f%h%x1f%an%x1f%ae%x1f%aI%x1f%s%x1e'; + const result = await run('git', ['log', `-${Math.min(Math.max(Number(limit) || 20, 1), 100)}`, `--format=${format}`], { cwd: root, allowExitCodes: [128] }); + if (result.exitCode === 128) return []; + return result.stdout.split('\x1e').map((record) => record.trim()).filter(Boolean).map((record) => { + const [sha, shortSha, author, email, date, subject] = record.split('\x1f'); + return { sha, shortSha, author, email, date, subject }; + }); + } + + async branches(repoPath) { + const root = await this.ensureRepository(repoPath); + const format = '%(refname:short)%x1f%(objectname)%x1f%(HEAD)%x1f%(upstream:short)%x1f%(upstream:track)%x1e'; + const result = await run('git', ['for-each-ref', `--format=${format}`, 'refs/heads'], { cwd: root }); + return result.stdout.split('\x1e').map((record) => record.trim()).filter(Boolean).map((record) => { + const [name, sha, current, upstream, track] = record.split('\x1f'); + const ahead = Number(track?.match(/ahead (\d+)/)?.[1] || 0); + const behind = Number(track?.match(/behind (\d+)/)?.[1] || 0); + return { name, sha, shortSha: sha?.slice(0, 7), current: current === '*', upstream: upstream || null, ahead, behind }; + }); + } + + assertBranchName(branch) { + const value = String(branch || '').trim(); + if (!value) throw new Error('Branch name is required.'); + return value; + } + + async checkoutBranch(repoPath, branch) { + const root = await this.ensureRepository(repoPath); + const status = await this.status(root); + if (!status.clean) throw new Error('Commit or stash local changes before switching branches.'); + const value = this.assertBranchName(branch); + await run('git', ['check-ref-format', '--branch', value], { cwd: root }); + await run('git', ['switch', value], { cwd: root, timeout: 60_000 }); + return this.status(root); + } + + async createBranch(repoPath, branch) { + const root = await this.ensureRepository(repoPath); + const status = await this.status(root); + if (!status.clean) throw new Error('Commit or stash local changes before creating a branch.'); + const value = this.assertBranchName(branch); + await run('git', ['check-ref-format', '--branch', value], { cwd: root }); + await run('git', ['switch', '-c', value], { cwd: root, timeout: 60_000 }); + return this.status(root); + } + + async stash(repoPath, message = '') { + const root = await this.ensureRepository(repoPath); + const status = await this.status(root); + if (status.clean) throw new Error('There are no changes to stash.'); + const args = ['stash', 'push', '--include-untracked']; + const label = String(message || '').trim(); + if (label) args.push('-m', label.slice(0, 200)); + const result = await run('git', args, { cwd: root, timeout: 120_000 }); + return { output: result.stdout.trim(), status: await this.status(root), stashes: await this.stashList(root) }; + } + + async stashList(repoPath) { + const root = await this.ensureRepository(repoPath); + const format = '%gd%x1f%H%x1f%aI%x1f%gs%x1e'; + const result = await run('git', ['stash', 'list', `--format=${format}`], { cwd: root }); + return result.stdout.split('\x1e').map((record) => record.trim()).filter(Boolean).map((record) => { + const [ref, sha, date, subject] = record.split('\x1f'); + return { ref, sha, shortSha: sha.slice(0, 7), date, subject }; + }); + } + + async popStash(repoPath, ref = 'stash@{0}') { + const root = await this.ensureRepository(repoPath); + const value = String(ref || 'stash@{0}'); + if (!/^stash@\{\d+\}$/.test(value)) throw new Error('Invalid stash reference.'); + const result = await run('git', ['stash', 'pop', value], { cwd: root, timeout: 120_000 }); + return { output: result.stdout.trim(), status: await this.status(root), stashes: await this.stashList(root) }; + } + + async verifyCommitOnRemoteBranch(repoPath, sha, branch) { + const root = await this.ensureRepository(repoPath); + const fullSha = assertFullCommitSha(sha); + const branchName = this.assertBranchName(branch); + await run('git', ['fetch', '--prune', 'origin', branchName], { cwd: root, timeout: 180_000 }); + await run('git', ['cat-file', '-e', `${fullSha}^{commit}`], { cwd: root, timeout: 30_000 }); + const ancestor = await run('git', ['merge-base', '--is-ancestor', fullSha, `origin/${branchName}`], { cwd: root, allowExitCodes: [1] }); + if (ancestor.exitCode !== 0) throw new Error(`Commit ${fullSha.slice(0, 7)} is not contained in origin/${branchName}.`); + return { valid: true, sha: fullSha, branch: branchName }; + } + + async inspectCloneTarget(remoteUrl, destination) { + const remote = assertCloneRemote(remoteUrl); + const target = assertSafeRepositoryPath(destination); + const existing = await fs.stat(target).catch(() => null); + + if (!existing) return { state: 'missing', remote, target }; + if (!existing.isDirectory()) { + const error = new Error('The automatic clone target exists and is not a folder.'); + error.code = 'CLONE_TARGET_NOT_DIRECTORY'; + throw error; + } + + const entries = await fs.readdir(target); + if (!entries.length) return { state: 'empty', remote, target }; + + const existingRemote = await this.getRemoteUrl(target).catch(() => ''); + const expected = normalizeRemoteUrl(remote); + const actual = normalizeRemoteUrl(existingRemote); + const sameRepository = Boolean( + expected && actual + && expected.host === actual.host + && expected.path === actual.path + ); + + if (sameRepository) return { state: 'matching-repository', remote, target }; + + const error = new Error(existingRemote + ? 'The automatic clone target already contains a different Git repository.' + : 'The automatic clone target already contains files. Choose another location or link the existing folder.'); + error.code = existingRemote ? 'CLONE_TARGET_DIFFERENT_REPOSITORY' : 'CLONE_TARGET_NOT_EMPTY'; + throw error; + } + + async clone(remoteUrl, destination) { + const assessment = await this.inspectCloneTarget(remoteUrl, destination); + if (assessment.state === 'matching-repository') { + const status = await this.status(assessment.target); + return { ...status, reused: true }; + } + + if (assessment.state === 'missing') { + await fs.mkdir(path.dirname(assessment.target), { recursive: true }); + } + + await run('git', ['clone', '--progress', assessment.remote, assessment.target], { timeout: 15 * 60_000, maxBuffer: 32 * 1024 * 1024 }); + const status = await this.status(assessment.target); + return { ...status, reused: false }; + } +} + +module.exports = { GitService }; diff --git a/src/main/gitea-service.cjs b/src/main/gitea-service.cjs new file mode 100644 index 0000000..e4db77e --- /dev/null +++ b/src/main/gitea-service.cjs @@ -0,0 +1,277 @@ +'use strict'; + +const { normalizeBaseUrl } = require('../shared/validation.cjs'); +const { redactSecrets } = require('./log-redaction.cjs'); + +class GiteaService { + constructor(store, diagnostics = null) { + this.store = store; + this.diagnostics = diagnostics; + } + + async request(pathname, options = {}) { + const baseUrl = normalizeBaseUrl(options.baseUrl || this.store.data.gitea.baseUrl); + const token = options.token || this.store.getToken(); + if (!token && options.auth !== false) throw new Error('No Gitea access token is available.'); + + const headers = { + Accept: options.accept || 'application/json', + ...(token && options.auth !== false ? { Authorization: `token ${token}` } : {}), + ...(options.body ? { 'Content-Type': 'application/json' } : {}), + ...(options.headers || {}) + }; + + const started = Date.now(); + let response; + try { + response = await fetch(`${baseUrl}/api/v1${pathname}`, { + method: options.method || 'GET', + headers, + body: options.body ? JSON.stringify(options.body) : undefined, + signal: AbortSignal.timeout(options.timeout || 30_000), + redirect: 'follow' + }); + } catch (error) { + const wrapped = new Error(`Could not reach Gitea: ${redactSecrets(error.message, [token])}`); + wrapped.code = error.code || 'GITEA_NETWORK_ERROR'; + await this.diagnostics?.warning('gitea.request.failed', { method: options.method || 'GET', pathname, durationMs: Date.now() - started, code: wrapped.code, message: wrapped.message }); + throw wrapped; + } + + let text = ''; + let payload = null; + if (options.responseType === 'buffer') { + payload = Buffer.from(await response.arrayBuffer()); + } else { + text = await response.text(); + if (text) { + if (options.responseType === 'text') payload = text; + else { + try { payload = JSON.parse(text); } catch { payload = text; } + } + } + } + + if (!response.ok) { + const detail = typeof payload === 'object' && !Buffer.isBuffer(payload) && payload?.message ? payload.message : text || response.statusText; + const error = new Error(`Gitea returned ${response.status}: ${redactSecrets(detail, [token])}`); + error.status = response.status; + error.payload = payload; + await this.diagnostics?.warning('gitea.request.rejected', { method: options.method || 'GET', pathname, status: response.status, durationMs: Date.now() - started, message: error.message }); + throw error; + } + + await this.diagnostics?.debug('gitea.request.completed', { method: options.method || 'GET', pathname, status: response.status, durationMs: Date.now() - started }); + return { status: response.status, headers: response.headers, data: payload }; + } + + async validateConnection(baseUrl, token) { + const normalized = normalizeBaseUrl(baseUrl); + const user = await this.request('/user', { baseUrl: normalized, token }); + const repositories = await this.listRepositories({ baseUrl: normalized, token, limitPages: 1 }); + const version = await this.request('/version', { baseUrl: normalized, token }).then((result) => result.data?.version || null).catch(() => null); + return { baseUrl: normalized, user: user.data, repositoryCount: repositories.length, version }; + } + + async listRepositories(options = {}) { + const repositories = []; + const pageSize = 50; + const limitPages = options.limitPages || 20; + for (let page = 1; page <= limitPages; page += 1) { + const result = await this.request(`/user/repos?limit=${pageSize}&page=${page}&sort=updated`, options); + const batch = Array.isArray(result.data) ? result.data : []; + repositories.push(...batch); + if (batch.length < pageSize) break; + } + return repositories; + } + + async getRepository(owner, repo) { + return (await this.request(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`)).data; + } + + async repositoryFileExists({ owner, repo, filePath, ref }) { + const encodedPath = String(filePath || '').split('/').map(encodeURIComponent).join('/'); + const query = ref ? `?ref=${encodeURIComponent(ref)}` : ''; + try { + await this.request(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents/${encodedPath}${query}`); + return true; + } catch (error) { + if (error.status === 404) return false; + throw error; + } + } + + + async getBranch(owner, repo, branch) { + return (await this.request(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/branches/${encodeURIComponent(branch)}`)).data; + } + + async getRepositoryFile({ owner, repo, filePath, ref }) { + const encodedPath = String(filePath || '').split('/').map(encodeURIComponent).join('/'); + const query = ref ? `?ref=${encodeURIComponent(ref)}` : ''; + const payload = (await this.request(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents/${encodedPath}${query}`)).data; + if (!payload || Array.isArray(payload)) throw new Error(`Repository path ${filePath} is not a file.`); + if (payload.encoding === 'base64' && typeof payload.content === 'string') { + return { ...payload, decoded: Buffer.from(payload.content.replace(/\s/g, ''), 'base64').toString('utf8') }; + } + if (typeof payload.content === 'string') return { ...payload, decoded: payload.content }; + throw new Error(`Gitea did not return readable content for ${filePath}.`); + } + + async getLatestRelease(owner, repo) { + try { + return (await this.request(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/latest`)).data; + } catch (error) { + if (error.status === 404) return null; + throw error; + } + } + + async downloadAuthenticated(url, { timeout = 180_000 } = {}) { + const baseUrl = normalizeBaseUrl(this.store.data.gitea.baseUrl); + const base = new URL(baseUrl); + const token = this.store.getToken(); + let target = new URL(url, `${baseUrl}/`); + for (let redirects = 0; redirects <= 5; redirects += 1) { + if (target.origin !== base.origin) throw new Error('Refusing to send the Gitea token to a different origin.'); + const response = await fetch(target, { + headers: { Authorization: `token ${token}`, Accept: 'application/octet-stream' }, + signal: AbortSignal.timeout(timeout), + redirect: 'manual' + }); + if ([301, 302, 303, 307, 308].includes(response.status)) { + const location = response.headers.get('location'); + if (!location) throw new Error('The update download redirect did not contain a destination.'); + target = new URL(location, target); + continue; + } + if (!response.ok) throw new Error(`Update download failed with HTTP ${response.status}.`); + return Buffer.from(await response.arrayBuffer()); + } + throw new Error('The update download exceeded the redirect limit.'); + } + + async dispatchWorkflow({ owner, repo, workflowFile, ref, inputs = {} }) { + const result = await this.request( + `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/actions/workflows/${encodeURIComponent(workflowFile)}/dispatches`, + { method: 'POST', body: { ref, inputs }, timeout: 60_000 } + ); + return { accepted: [200, 201, 204].includes(result.status), status: result.status }; + } + + normalizeRun(run) { + if (!run || typeof run !== 'object') return null; + const status = String(run.status || run.conclusion || '').toLowerCase(); + const conclusion = String(run.conclusion || '').toLowerCase() || (['success', 'failure', 'cancelled', 'skipped'].includes(status) ? status : null); + return { + id: run.id ?? run.run_id ?? run.task_id ?? null, + runNumber: run.run_number ?? run.index ?? run.id ?? null, + name: run.name || run.workflow_name || run.workflow_id || 'Workflow', + event: run.event || null, + status, + conclusion, + headSha: run.head_sha || run.commit_sha || run.commit?.sha || null, + headBranch: run.head_branch || run.ref || run.branch || null, + workflowPath: run.path || run.workflow_path || run.workflow_file || null, + displayTitle: run.display_title || run.title || run.name || null, + actor: run.actor?.login || run.trigger_user?.login || run.user?.login || null, + createdAt: run.created_at || run.started || run.start_time || null, + updatedAt: run.updated_at || run.stopped || run.end_time || null, + htmlUrl: run.html_url || run.url || null, + raw: run + }; + } + + async listWorkflowRuns({ owner, repo, sha, branch, limit = 30 } = {}) { + const base = `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/actions`; + const normalizedLimit = String(Math.min(Math.max(limit, 1), 100)); + const filtered = new URLSearchParams({ limit: normalizedLimit }); + if (sha) filtered.set('head_sha', sha); + if (branch) filtered.set('branch', branch); + const basic = new URLSearchParams({ limit: normalizedLimit }); + + const tryEndpoint = async (endpoint) => { + try { + return await this.request(`${base}/${endpoint}?${filtered}`); + } catch (error) { + // Action API query support differs across Gitea releases. Retry without + // optional filters and apply SHA/branch matching locally. + if (![400, 422].includes(error.status) || String(filtered) === String(basic)) throw error; + return this.request(`${base}/${endpoint}?${basic}`); + } + }; + + let result; + let source = 'runs'; + try { + result = await tryEndpoint('runs'); + } catch (error) { + if (![404, 405].includes(error.status)) throw error; + source = 'tasks'; + result = await tryEndpoint('tasks'); + } + + const data = result.data; + const items = Array.isArray(data) ? data : data?.workflow_runs || data?.runs || data?.tasks || []; + return { source, runs: items.map((item) => this.normalizeRun(item)).filter(Boolean), totalCount: data?.total_count ?? items.length }; + } + + async findWorkflowRun({ owner, repo, sha, branch, workflowFile, dispatchedAt, excludeRunIds = [] }) { + const { runs, source } = await this.listWorkflowRuns({ owner, repo, sha, branch, limit: 50 }); + const earliest = dispatchedAt ? new Date(dispatchedAt).getTime() - 120_000 : 0; + const workflowBase = String(workflowFile || '').split('/').pop(); + const excluded = new Set((excludeRunIds || []).map((value) => String(value))); + const candidates = runs.filter((run) => { + if (run.id !== null && run.id !== undefined && excluded.has(String(run.id))) return false; + if (sha && run.headSha && run.headSha.toLowerCase() !== sha.toLowerCase()) return false; + if (branch && run.headBranch && run.headBranch.replace(/^refs\/heads\//, '') !== branch) return false; + if (earliest && run.createdAt && new Date(run.createdAt).getTime() < earliest) return false; + if (workflowBase && run.workflowPath) { + const runBase = String(run.workflowPath).split('/').pop(); + if (runBase && runBase !== workflowBase) return false; + } + return true; + }); + candidates.sort((a, b) => new Date(b.createdAt || 0) - new Date(a.createdAt || 0)); + return { source, run: candidates[0] || null }; + } + + async listWorkflowJobs({ owner, repo, runNumber }) { + if (runNumber === null || runNumber === undefined) return []; + const result = await this.request(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/actions/runs/${encodeURIComponent(runNumber)}/jobs?limit=100`); + const data = result.data; + const jobs = Array.isArray(data) ? data : data?.jobs || []; + return jobs.map((job) => ({ + id: job.id, + name: job.name || job.job_name || `Job ${job.id}`, + status: String(job.status || '').toLowerCase(), + conclusion: String(job.conclusion || '').toLowerCase() || null, + startedAt: job.started_at || null, + completedAt: job.completed_at || null, + steps: Array.isArray(job.steps) ? job.steps.map((step) => ({ + name: step.name, + status: String(step.status || '').toLowerCase(), + conclusion: String(step.conclusion || '').toLowerCase() || null, + number: step.number + })) : [] + })); + } + + async getJobLogs({ owner, repo, jobId }) { + if (!jobId) return ''; + try { + const result = await this.request(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/actions/jobs/${encodeURIComponent(jobId)}/logs`, { + accept: 'text/plain, application/octet-stream', + responseType: 'text', + timeout: 60_000 + }); + return String(result.data || '').slice(-500_000); + } catch (error) { + if ([404, 410].includes(error.status)) return ''; + throw error; + } + } +} + +module.exports = { GiteaService }; diff --git a/src/main/ipc.cjs b/src/main/ipc.cjs new file mode 100644 index 0000000..01c1f7f --- /dev/null +++ b/src/main/ipc.cjs @@ -0,0 +1,409 @@ +'use strict'; + +const path = require('node:path'); +const fs = require('node:fs/promises'); +const { fileURLToPath } = require('node:url'); +const { ipcMain, dialog, shell, app } = require('electron'); +const { matchRemoteToRepository } = require('../shared/repository-match.cjs'); +const { cloneDirectoryName, resolveCloneTarget } = require('../shared/clone-target.cjs'); + +let diagnosticsService = null; +const TRUSTED_RENDERER_PATH = path.resolve(__dirname, '..', 'renderer', 'index.html'); + +function toErrorPayload(error) { + return { + message: error?.message || 'Unknown error', + code: error?.code || null, + status: error?.status || null, + recoverable: Boolean(error?.recoverable), + commitSha: error?.commitSha || null + }; +} + +function assertTrustedSender(event) { + const url = event?.senderFrame?.url || event?.sender?.getURL?.() || ''; + try { + const parsed = new URL(url); + if (parsed.protocol !== 'file:') throw new Error('not a file URL'); + const senderPath = path.resolve(fileURLToPath(parsed)); + const normalize = (value) => process.platform === 'win32' ? value.toLowerCase() : value; + if (normalize(senderPath) !== normalize(TRUSTED_RENDERER_PATH)) throw new Error('unexpected renderer file'); + } catch { + throw new Error('Rejected IPC request from an untrusted renderer origin.'); + } +} + +function register(channel, handler) { + ipcMain.handle(channel, async (event, payload) => { + const started = Date.now(); + try { + assertTrustedSender(event); + const data = await handler(payload || {}, event); + await diagnosticsService?.debug('ipc.completed', { channel, durationMs: Date.now() - started }); + return { ok: true, data }; + } catch (error) { + await diagnosticsService?.error('ipc.failed', { + channel, + durationMs: Date.now() - started, + error: { name: error?.name, message: error?.message, code: error?.code, status: error?.status, stack: error?.stack } + }); + console.error(`[${channel}]`, error); + return { ok: false, error: toErrorPayload(error) }; + } + }); +} + +function registerIpc({ store, git, gitea, repositories, deployments, unraid, ssh, updates, preflight, diagnostics, monitor }) { + diagnosticsService = diagnostics; + const withRepositoryPause = async (localPath, action) => { + monitor?.pause(localPath); + try { return await action(); } + finally { monitor?.resume(localPath); } + }; + + const canonicalPath = async (value) => { + const resolved = path.resolve(String(value || '')); + return fs.realpath(resolved).catch(() => resolved); + }; + + const assertKnownRepositoryPath = async (localPath) => { + const candidate = await canonicalPath(localPath); + let knownPaths = repositories.getWatchPaths(); + if (!knownPaths.length && store.data.setupComplete) { + await repositories.refresh(); + knownPaths = repositories.getWatchPaths(); + } + const canonicalKnown = await Promise.all(knownPaths.map(canonicalPath)); + if (!canonicalKnown.some((known) => known === candidate)) throw new Error('The requested local repository is not linked or discovered by ForgeFlow.'); + return candidate; + }; + + const resolveRepository = async (repositoryPayload) => { + const fullName = String(repositoryPayload?.fullName || '').trim(); + if (!fullName) throw new Error('Repository identity is required.'); + const current = (await repositories.refresh()).find((item) => item.fullName === fullName); + if (!current) throw new Error('The repository is no longer available through the configured Gitea account.'); + return current; + }; + + + const assertProjectRoot = async (rootValue) => { + const root = await canonicalPath(rootValue); + const stat = await fs.stat(root).catch(() => null); + if (!stat?.isDirectory()) throw new Error('The selected project root no longer exists.'); + return root; + }; + + const cloneRepositoryInto = async (fullName, projectRoot) => { + const current = await resolveRepository({ fullName }); + if (current.localPath) throw new Error('This repository already has a linked local folder.'); + + const remoteUrl = current.preferredCloneUrl || current.cloneUrl || current.sshUrl; + if (!remoteUrl) throw new Error('Gitea did not provide a usable clone URL for this repository.'); + + const root = await assertProjectRoot(projectRoot); + const { target } = resolveCloneTarget(root, remoteUrl); + const status = await git.clone(remoteUrl, target); + + await store.saveMapping(current.fullName, target); + const result = await repositories.refresh(); + monitor?.setPaths(repositories.getWatchPaths()); + await diagnostics.info(status.reused ? 'repository.clone.reused' : 'repository.cloned', { + fullName: current.fullName, + projectRoot: root, + target, + head: status.head, + branch: status.branch?.head + }); + + return { + target, + status, + reused: Boolean(status.reused), + repositories: result, + state: store.getPublicState() + }; + }; + + register('app:bootstrap', async () => ({ + appVersion: app.getVersion(), + platform: process.platform, + state: store.getPublicState(), + git: await git.isAvailable(), + diagnostics: await diagnostics.getStatus() + })); + + register('dialog:select-directory', async ({ title = 'Select folder', defaultPath }) => { + const result = await dialog.showOpenDialog({ title, defaultPath, properties: ['openDirectory', 'createDirectory'] }); + return result.canceled ? null : result.filePaths[0]; + }); + + register('dialog:select-key-file', async ({ title = 'Select SSH private key', defaultPath }) => { + const result = await dialog.showOpenDialog({ + title, + defaultPath, + properties: ['openFile'] + }); + return result.canceled ? null : result.filePaths[0]; + }); + + register('setup:preflight', ({ baseUrl, token, roots }) => preflight.runSystem({ baseUrl, token, roots })); + register('setup:validate-gitea', ({ baseUrl, token }) => gitea.validateConnection(baseUrl, token)); + register('setup:complete', async ({ baseUrl, token, workspaceRoots }) => { + const report = await preflight.runSystem({ baseUrl, token, roots: workspaceRoots }); + if (!report.summary.ready || !report.giteaValidation) throw new Error('Setup readiness checks must pass before configuration can be completed.'); + const validation = report.giteaValidation; + const result = await store.completeSetup({ baseUrl: validation.baseUrl, token, user: validation.user, workspaceRoots }); + await diagnostics.info('setup.completed', { baseUrl: validation.baseUrl, user: validation.user?.login || null, workspaceRootCount: workspaceRoots?.length || 0, tokenPersistent: result.tokenState.persistent }); + return result; + }); + + register('settings:update-gitea', async ({ baseUrl, token }) => { + const effectiveToken = String(token || '').trim() || store.getToken(); + const validation = await gitea.validateConnection(baseUrl, effectiveToken); + const tokenState = await store.updateGitea({ baseUrl: validation.baseUrl, token, user: validation.user }); + await diagnostics.info('settings.gitea.updated', { baseUrl: validation.baseUrl, user: validation.user?.login || null, tokenPersistent: tokenState.persistent, tokenPreserved: tokenState.preserved }); + return { validation, tokenState, state: store.getPublicState() }; + }); + + register('settings:set-roots', async ({ roots }) => { + store.data.workspaceRoots = [...new Set((roots || []).filter(Boolean))]; + await store.save(); + await diagnostics.info('settings.workspace-roots.updated', { rootCount: store.data.workspaceRoots.length, roots: store.data.workspaceRoots }); + return store.getPublicState(); + }); + + register('settings:set-appearance', async ({ appearance }) => { + if (!['dark', 'light', 'system'].includes(appearance)) throw new Error('Unsupported appearance setting.'); + store.data.appearance = appearance; + await store.save(); + return store.getPublicState(); + }); + + register('settings:set-preferences', async ({ preferences }) => { + const state = await store.setPreferences(preferences); + monitor?.restart(); + await diagnostics.info('settings.preferences.updated', { preferences: state.preferences }); + return state; + }); + + register('updates:preferences', ({ updates: next }) => store.setUpdatePreferences(next)); + register('updates:check', () => updates.check()); + register('updates:download', () => updates.download()); + register('updates:apply', async () => { + const result = await updates.apply(); + setTimeout(() => app.quit(), 650).unref?.(); + return result; + }); + + register('server:save', async ({ server, password = '', passphrase = '' }) => { + const saved = await store.saveServer(server, { password, passphrase }); + await diagnostics.info('server.saved', { + serverId: saved.id, + name: saved.name, + host: saved.host, + port: saved.port, + username: saved.username, + authType: saved.authType, + basePath: saved.basePath + }); + return { server: saved, state: store.getPublicState() }; + }); + register('server:delete', async ({ serverId }) => { + await store.deleteServer(serverId); + await diagnostics.info('server.deleted', { serverId }); + return store.getPublicState(); + }); + register('server:test', async ({ serverId }) => { + const server = store.getServer(serverId); + if (!server) throw new Error('The configured server no longer exists.'); + const result = await ssh.test(serverId, { trustOnFirstUse: !server.hostFingerprint }); + if (!server.hostFingerprint) { + await store.saveServer({ ...server, hostFingerprint: result.fingerprint }, {}); + result.trusted = true; + } + return { ...result, state: store.getPublicState() }; + }); + register('server:inspect-project', async ({ repository, profileId }) => unraid.inspect({ repository: await resolveRepository(repository), profileId })); + + register('repositories:refresh', async () => { + const result = await repositories.refresh(); + monitor?.setPaths(repositories.getWatchPaths()); + return result; + }); + + register('repositories:discover', async ({ roots }) => { + const paths = await repositories.discoverAll(roots || store.data.workspaceRoots); + return repositories.getLocalDescriptors(paths); + }); + + register('repository:favorite', async ({ fullName, favorite }) => store.setFavorite(fullName, favorite)); + + register('repository:link', async ({ fullName, localPath }) => { + await git.ensureRepository(localPath); + const remoteUrl = await git.getRemoteUrl(localPath).catch(() => ''); + if (!remoteUrl || !matchRemoteToRepository(remoteUrl, [{ full_name: fullName }])) { + throw new Error(`The selected folder's origin does not match ${fullName}.`); + } + await store.saveMapping(fullName, localPath); + await diagnostics.info('repository.linked', { fullName, localPath }); + const result = await repositories.refresh(); + monitor?.setPaths(repositories.getWatchPaths()); + return result; + }); + + register('repository:unlink', async ({ fullName }) => { + await store.removeMapping(fullName); + await diagnostics.info('repository.unlinked', { fullName }); + const result = await repositories.refresh(); + monitor?.setPaths(repositories.getWatchPaths()); + return result; + }); + + register('repository:status', async ({ localPath }) => git.status(await assertKnownRepositoryPath(localPath))); + register('repository:diff', async ({ localPath, filePath, staged }) => git.diff(await assertKnownRepositoryPath(localPath), filePath, staged)); + register('repository:stage', async ({ localPath, files }) => { const safePath = await assertKnownRepositoryPath(localPath); return withRepositoryPause(safePath, () => git.stage(safePath, files)); }); + register('repository:unstage', async ({ localPath, files }) => { const safePath = await assertKnownRepositoryPath(localPath); return withRepositoryPause(safePath, () => git.unstage(safePath, files)); }); + register('repository:commit', async ({ localPath, message, files }) => { const safePath = await assertKnownRepositoryPath(localPath); return withRepositoryPause(safePath, () => git.commit(safePath, message, files)); }); + register('repository:commit-push', async ({ localPath, message, files }) => { const safePath = await assertKnownRepositoryPath(localPath); return withRepositoryPause(safePath, () => git.commitAndPush(safePath, message, files)); }); + register('repository:push', async ({ localPath }) => { const safePath = await assertKnownRepositoryPath(localPath); return withRepositoryPause(safePath, () => git.push(safePath)); }); + register('repository:fetch', async ({ localPath }) => { const safePath = await assertKnownRepositoryPath(localPath); return withRepositoryPause(safePath, () => git.fetch(safePath)); }); + register('repository:pull', async ({ localPath }) => { const safePath = await assertKnownRepositoryPath(localPath); return withRepositoryPause(safePath, () => git.pullFastForward(safePath)); }); + register('repository:history', async ({ localPath, limit }) => git.history(await assertKnownRepositoryPath(localPath), limit)); + register('repository:branches', async ({ localPath }) => git.branches(await assertKnownRepositoryPath(localPath))); + register('repository:checkout-branch', async ({ localPath, branch }) => { const safePath = await assertKnownRepositoryPath(localPath); return withRepositoryPause(safePath, () => git.checkoutBranch(safePath, branch)); }); + register('repository:create-branch', async ({ localPath, branch }) => { const safePath = await assertKnownRepositoryPath(localPath); return withRepositoryPause(safePath, () => git.createBranch(safePath, branch)); }); + register('repository:stash', async ({ localPath, message }) => { const safePath = await assertKnownRepositoryPath(localPath); return withRepositoryPause(safePath, () => git.stash(safePath, message)); }); + register('repository:stash-list', async ({ localPath }) => git.stashList(await assertKnownRepositoryPath(localPath))); + register('repository:stash-pop', async ({ localPath, ref }) => { const safePath = await assertKnownRepositoryPath(localPath); return withRepositoryPause(safePath, () => git.popStash(safePath, ref)); }); + + register('repository:clone', async ({ fullName, mode = 'default' }) => { + if (!['default', 'custom'].includes(mode)) throw new Error('Unsupported clone location mode.'); + + let projectRoot = store.data.workspaceRoots[0] || null; + if (mode === 'custom' || !projectRoot) { + const result = await dialog.showOpenDialog({ + title: `Choose a project root for ${String(fullName || 'repository')}`, + defaultPath: projectRoot || undefined, + buttonLabel: 'Use this project root', + properties: ['openDirectory', 'createDirectory'] + }); + if (result.canceled || !result.filePaths[0]) return { cancelled: true }; + projectRoot = result.filePaths[0]; + } + + return cloneRepositoryInto(fullName, projectRoot); + }); + + register('repository:open-path', async ({ localPath }) => { + const safePath = await assertKnownRepositoryPath(localPath); + const error = await shell.openPath(safePath); + if (error) throw new Error(error); + return true; + }); + + register('external:open', async ({ url }) => { + const parsed = new URL(url); + if (!['http:', 'https:'].includes(parsed.protocol)) throw new Error('Only HTTP and HTTPS links can be opened.'); + await shell.openExternal(parsed.toString()); + return true; + }); + + register('deployment:save-profile', async ({ fullName, profile }) => { + const saved = await store.saveDeploymentProfile(fullName, profile); + await diagnostics.info('deployment.profile.saved', { repository: fullName, profile: saved }); + return { profile: saved, state: store.getPublicState() }; + }); + register('deployment:delete-profile', async ({ fullName, profileId }) => { + const profiles = await store.deleteDeploymentProfile(fullName, profileId); + await diagnostics.info('deployment.profile.deleted', { repository: fullName, profileId }); + return { profiles, state: store.getPublicState() }; + }); + register('deployment:preflight', async ({ repository, profileId }) => { + const current = await resolveRepository(repository); + const profile = store.getDeploymentProfile(current.fullName, profileId); + if (profile?.provider === 'ssh-unraid') return unraid.preflight({ repository: current, profileId }); + return preflight.runDeployment({ repository: current, profileId }); + }); + register('deployment:dispatch', async ({ repository, profileId, sha }) => { + const current = await resolveRepository(repository); + const profile = store.getDeploymentProfile(current.fullName, profileId); + if (profile?.provider === 'ssh-unraid') return unraid.deploy({ repository: current, profileId, sha }); + return deployments.deploy({ repository: current, profileId, sha }); + }); + register('deployment:rollback', async ({ repository, profileId, targetSha }) => { + const current = await resolveRepository(repository); + const profile = store.getDeploymentProfile(current.fullName, profileId); + if (profile?.provider === 'ssh-unraid') return unraid.rollback({ repository: current, profileId, targetSha }); + return deployments.rollback({ repository: current, profileId, targetSha }); + }); + register('deployment:health', ({ url }) => deployments.checkHealth(url)); + register('deployment:profile-state', ({ fullName, profileId }) => { + const profile = store.getDeploymentProfile(fullName, profileId); + if (profile?.provider === 'ssh-unraid') return unraid.refreshProfileState(fullName, profileId); + return deployments.refreshProfileState(fullName, profileId); + }); + register('operations:refresh', async ({ operationId }) => { + if (operationId) { + const operation = store.getOperation(operationId); + if (operation?.provider === 'ssh-unraid') return operation; + return deployments.refreshOperation(operationId); + } + return deployments.refreshActiveOperations(); + }); + register('operations:get', ({ operationId }) => store.getOperation(operationId)); + + register('diagnostics:status', () => diagnostics.getStatus()); + register('diagnostics:clear', () => diagnostics.clear()); + register('diagnostics:open-folder', async () => { + const error = await shell.openPath(diagnostics.logDirectory); + if (error) throw new Error(error); + return true; + }); + register('diagnostics:export', async ({ privacyMode = 'standard' }) => { + if (!['standard', 'strict'].includes(privacyMode)) throw new Error('Unsupported diagnostic privacy mode.'); + const result = await dialog.showSaveDialog({ + title: 'Export ForgeFlow diagnostic bundle', + defaultPath: path.join(app.getPath('downloads'), `ForgeFlow-Diagnostics-${new Date().toISOString().replace(/[:.]/g, '-')}.zip`), + filters: [{ name: 'ZIP archive', extensions: ['zip'] }] + }); + if (result.canceled || !result.filePath) return null; + const repositoryState = await repositories.refresh().catch((error) => { + diagnostics.warning('diagnostics.repository-snapshot.failed', error); + return []; + }); + const systemPreflight = await preflight.runSystem().catch((error) => ({ error: error.message })); + const destinationPath = path.extname(result.filePath).toLowerCase() === '.zip' ? result.filePath : `${result.filePath}.zip`; + return diagnostics.exportSupportBundle({ + destinationPath, + publicState: store.getPublicState(), + repositories: repositoryState, + operations: store.data.operations, + preflight: systemPreflight, + privacyMode, + extra: { appVersion: app.getVersion(), setupComplete: store.data.setupComplete } + }); + }); + register('diagnostics:show-bundle', async ({ filePath }) => { + if (!diagnostics.isKnownBundlePath(filePath)) throw new Error('Only the most recently generated support bundle can be revealed.'); + shell.showItemInFolder(filePath); + return true; + }); + register('renderer:report', async ({ level = 'info', event = 'renderer.event', details = {} }) => { + const method = ['debug', 'info', 'warning', 'error'].includes(level) ? level : 'info'; + await diagnostics[method](`renderer.${String(event || 'event').slice(0, 120)}`, details); + return true; + }); + + register('app:reset', async () => { + await diagnostics.info('app.reset.requested', {}); + store.data = store.migrate({}); + store.sessionToken = null; + await store.save(); + monitor?.setPaths([]); + monitor?.restart(); + return store.getPublicState(); + }); +} + +module.exports = { registerIpc, cloneDirectoryName, assertTrustedSender, toErrorPayload }; diff --git a/src/main/log-redaction.cjs b/src/main/log-redaction.cjs new file mode 100644 index 0000000..5a09ba4 --- /dev/null +++ b/src/main/log-redaction.cjs @@ -0,0 +1,93 @@ +'use strict'; + +const path = require('node:path'); +const os = require('node:os'); +const crypto = require('node:crypto'); + +const SENSITIVE_KEY = /(^|_)(token|password|passwd|authorization|secret|credential|clientsecret|client_secret|apikey|api_key|privatekey|private_key|encryptedtoken|encrypted_token)($|_)/i; +const MAX_DIAGNOSTIC_STRING = 200_000; + +function redactSecrets(value, secrets = []) { + let text = String(value ?? ''); + const candidates = [...new Set((secrets || []).map((item) => String(item || '').trim()).filter((item) => item.length >= 4))] + .sort((a, b) => b.length - a.length); + for (const secret of candidates) text = text.split(secret).join('[REDACTED]'); + + text = text + .replace(/-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/gi, '[REDACTED PRIVATE KEY]') + .replace(/(authorization\s*[:=]\s*(?:token|bearer|basic)\s+)[^\s,;]+/gi, '$1[REDACTED]') + .replace(/([?&](?:access_token|token|api_key|apikey|key|secret|password)=)[^&#\s]+/gi, '$1[REDACTED]') + .replace(/((?:access_token|token|api_key|apikey|client_secret|password|passwd|secret)\s*[=:]\s*)[^\s,;]+/gi, '$1[REDACTED]') + .replace(/("(?:access_token|token|api_key|apikey|client_secret|password|passwd|secret)"\s*:\s*")[^"]+("?)/gi, '$1[REDACTED]$2') + .replace(/(https?:\/\/[^\s:@/]+:)[^@\s/]+@/gi, '$1[REDACTED]@') + .replace(/\b(?:ghp|github_pat|glpat|gitea)_[A-Za-z0-9_-]{16,}\b/g, '[REDACTED TOKEN]'); + + return text.length > MAX_DIAGNOSTIC_STRING ? `${text.slice(0, MAX_DIAGNOSTIC_STRING)}\n[TRUNCATED]` : text; +} + +function pathAlias(value, { homeDir = os.homedir(), cwd = process.cwd() } = {}) { + let text = String(value ?? ''); + const replacements = [ + [homeDir, ''], + [cwd, ''] + ].filter(([candidate]) => candidate && candidate.length > 3) + .sort((a, b) => b[0].length - a[0].length); + for (const [candidate, replacement] of replacements) { + const normalized = path.resolve(candidate); + text = text.split(normalized).join(replacement); + text = text.split(normalized.replace(/\\/g, '/')).join(replacement); + text = text.split(normalized.replace(/\//g, '\\')).join(replacement); + } + text = text + .replace(/[A-Za-z]:\\Users\\[^\\\s]+/g, '') + .replace(/\/(?:home|Users)\/[^/\s]+/g, ''); + return text; +} + +function stableAlias(value, prefix = 'item') { + const hash = crypto.createHash('sha256').update(String(value || '')).digest('hex').slice(0, 12); + return `${prefix}-${hash}`; +} + +function sanitizeForDiagnostics(value, options = {}, seen = new WeakSet()) { + const { + secrets = [], + pathMode = 'alias', + homeDir = os.homedir(), + cwd = process.cwd(), + strictIdentifiers = false + } = options; + + if (value === null || value === undefined || typeof value === 'boolean' || typeof value === 'number') return value; + if (typeof value === 'bigint') return value.toString(); + if (typeof value === 'string') { + let output = redactSecrets(value, secrets); + if (pathMode === 'alias') output = pathAlias(output, { homeDir, cwd }); + return output; + } + if (value instanceof Error) { + return sanitizeForDiagnostics({ name: value.name, message: value.message, code: value.code, stack: value.stack }, options, seen); + } + if (Array.isArray(value)) return value.slice(0, 1000).map((item) => sanitizeForDiagnostics(item, options, seen)); + if (typeof value !== 'object') return redactSecrets(String(value), secrets); + if (seen.has(value)) return '[CIRCULAR]'; + seen.add(value); + + const output = {}; + for (const [key, item] of Object.entries(value)) { + const normalizedKey = key.replace(/([a-z0-9])([A-Z])/g, '$1_$2').replace(/[-.]/g, '_'); + if (SENSITIVE_KEY.test(normalizedKey)) { + output[key] = '[REDACTED]'; + continue; + } + if (strictIdentifiers && ['fullName', 'repository', 'owner', 'user', 'login', 'email'].includes(key)) { + output[key] = stableAlias(typeof item === 'object' ? JSON.stringify(item) : item, key.toLowerCase()); + continue; + } + output[key] = sanitizeForDiagnostics(item, options, seen); + } + seen.delete(value); + return output; +} + +module.exports = { redactSecrets, sanitizeForDiagnostics, pathAlias, stableAlias, SENSITIVE_KEY }; diff --git a/src/main/preflight-service.cjs b/src/main/preflight-service.cjs new file mode 100644 index 0000000..d52630f --- /dev/null +++ b/src/main/preflight-service.cjs @@ -0,0 +1,208 @@ +'use strict'; + +const fs = require('node:fs/promises'); +const path = require('node:path'); +const { run } = require('./process-runner.cjs'); + +function check(id, label, status, detail, { required = false, help = '' } = {}) { + return { id, label, status, detail, required, help }; +} + +function summarize(checks) { + const counts = checks.reduce((acc, item) => { + acc[item.status] = (acc[item.status] || 0) + 1; + return acc; + }, { pass: 0, warning: 0, fail: 0, skipped: 0 }); + const blocking = checks.filter((item) => item.required && item.status === 'fail'); + return { counts, blocking: blocking.map((item) => item.id), ready: blocking.length === 0 }; +} + +class PreflightService { + constructor({ store, git, gitea, deployments, diagnostics, userDataPath, secureStorageAvailable = () => false }) { + this.store = store; + this.git = git; + this.gitea = gitea; + this.deployments = deployments; + this.diagnostics = diagnostics; + this.userDataPath = userDataPath; + this.secureStorageAvailable = secureStorageAvailable; + } + + async writableDirectory(directory) { + const marker = path.join(directory, `.forgeflow-write-test-${process.pid}-${Date.now()}`); + await fs.mkdir(directory, { recursive: true }); + await fs.writeFile(marker, 'ok', { mode: 0o600 }); + await fs.rm(marker, { force: true }); + return true; + } + + async gitIdentity() { + const [name, email] = await Promise.all([ + run('git', ['config', '--global', '--get', 'user.name'], { allowExitCodes: [1], timeout: 10_000 }), + run('git', ['config', '--global', '--get', 'user.email'], { allowExitCodes: [1], timeout: 10_000 }) + ]); + return { name: name.stdout.trim(), email: email.stdout.trim() }; + } + + async runSystem({ baseUrl = '', token = '', roots = [] } = {}) { + const startedAt = new Date().toISOString(); + const checks = []; + + const git = await this.git.isAvailable(); + checks.push(check('git.available', 'Git command line', git.available ? 'pass' : 'fail', git.available ? git.version : git.error || 'Git was not found on PATH.', { + required: true, + help: 'Install Git for Windows and ensure git.exe is available on PATH.' + })); + + if (git.available) { + try { + const identity = await this.gitIdentity(); + checks.push(check('git.identity', 'Git author identity', identity.name && identity.email ? 'pass' : 'warning', identity.name && identity.email ? `${identity.name} <${identity.email}>` : 'Global user.name or user.email is missing.', { + help: 'Set git config --global user.name and user.email before creating commits.' + })); + } catch (error) { + checks.push(check('git.identity', 'Git author identity', 'warning', error.message)); + } + } + + try { + await this.writableDirectory(this.userDataPath); + checks.push(check('storage.userdata', 'Application data storage', 'pass', 'ForgeFlow can write its local configuration.', { required: true })); + } catch (error) { + checks.push(check('storage.userdata', 'Application data storage', 'fail', error.message, { required: true })); + } + + try { + await this.writableDirectory(this.diagnostics.logDirectory); + checks.push(check('storage.diagnostics', 'Diagnostic log storage', 'pass', 'The diagnostic directory is writable.', { required: true })); + } catch (error) { + checks.push(check('storage.diagnostics', 'Diagnostic log storage', 'fail', error.message, { required: true })); + } + + checks.push(check('storage.credentials', 'Protected credential storage', this.secureStorageAvailable() ? 'pass' : 'warning', this.secureStorageAvailable() + ? 'The operating system can encrypt the Gitea token at rest.' + : 'OS credential encryption is unavailable; the token will remain session-only.', { + help: 'Use a normal signed-in desktop session and make sure the OS credential service is available.' + })); + + const normalizedRoots = [...new Set((roots || []).map((item) => String(item || '').trim()).filter(Boolean))]; + if (!normalizedRoots.length) { + checks.push(check('workspace.roots', 'Development folders', 'warning', 'No development folder has been selected yet.')); + } else { + for (let index = 0; index < normalizedRoots.length; index += 1) { + const root = normalizedRoots[index]; + try { + const stat = await fs.stat(root); + checks.push(check(`workspace.root.${index}`, `Development folder ${index + 1}`, stat.isDirectory() ? 'pass' : 'fail', stat.isDirectory() ? root : 'The selected path is not a directory.', { required: true })); + } catch (error) { + checks.push(check(`workspace.root.${index}`, `Development folder ${index + 1}`, 'fail', error.message, { required: true })); + } + } + } + + const effectiveBaseUrl = String(baseUrl || this.store.data.gitea.baseUrl || '').trim(); + const effectiveToken = String(token || this.store.getToken() || '').trim(); + let giteaValidation = null; + if (!effectiveBaseUrl || !effectiveToken) { + checks.push(check('gitea.connection', 'Gitea connection', 'warning', 'Enter the Gitea URL and a local access token to test the connection.')); + } else { + try { + giteaValidation = await this.gitea.validateConnection(effectiveBaseUrl, effectiveToken); + checks.push(check('gitea.connection', 'Gitea connection', 'pass', `Connected to Gitea ${giteaValidation.version || 'unknown version'} as ${giteaValidation.user?.login || 'user'}.`, { required: true })); + checks.push(check('gitea.repositories', 'Repository access', giteaValidation.repositoryCount >= 0 ? 'pass' : 'warning', `${giteaValidation.repositoryCount} accessible repositories returned.`)); + } catch (error) { + checks.push(check('gitea.connection', 'Gitea connection', 'fail', error.message, { required: true })); + } + } + + const result = { kind: 'system', startedAt, completedAt: new Date().toISOString(), checks, summary: summarize(checks), giteaValidation }; + await this.diagnostics.info('preflight.system.completed', { summary: result.summary, checks }); + return result; + } + + async fileExists(filePath) { + const stat = await fs.stat(filePath).catch(() => null); + return Boolean(stat?.isFile()); + } + + async runDeployment({ repository, profileId }) { + const checks = []; + const startedAt = new Date().toISOString(); + if (!repository?.fullName) throw new Error('Repository identity is required.'); + const profile = this.store.getDeploymentProfile(repository.fullName, profileId); + if (!profile) throw new Error('Deployment profile not found.'); + + checks.push(check('repository.linked', 'Local repository link', repository.localPath ? 'pass' : 'fail', repository.localPath || 'No local folder is linked.', { required: true })); + if (!repository.localPath) { + const result = { kind: 'deployment', repository: repository.fullName, profileId, startedAt, completedAt: new Date().toISOString(), checks, summary: summarize(checks) }; + await this.diagnostics.info('preflight.deployment.completed', result); + return result; + } + + let status = null; + try { + status = await this.git.status(repository.localPath); + checks.push(check('git.repository', 'Git working tree', 'pass', status.root, { required: true })); + checks.push(check('git.branch', 'Allowed branch', status.branch.head === profile.branch ? 'pass' : 'fail', `Current: ${status.branch.head || 'detached'}; required: ${profile.branch}.`, { required: true })); + checks.push(check('git.clean', 'Clean working tree', status.clean ? 'pass' : 'fail', status.clean ? 'No uncommitted changes.' : `${status.counts.changed} changed file(s) remain.`, { required: true })); + checks.push(check('git.upstream', 'Published upstream', status.branch.upstream ? 'pass' : 'fail', status.branch.upstream || 'No upstream branch configured.', { required: true })); + checks.push(check('git.sync', 'Local and Gitea synchronized', !status.branch.ahead && !status.branch.behind ? 'pass' : 'fail', `${status.branch.ahead || 0} ahead, ${status.branch.behind || 0} behind.`, { required: true })); + if (status.head) { + try { + await this.git.verifyCommitOnRemoteBranch(repository.localPath, status.head, profile.branch); + checks.push(check('git.remote-sha', 'Exact commit on remote branch', 'pass', `${status.head.slice(0, 7)} exists on origin/${profile.branch}.`, { required: true })); + } catch (error) { + checks.push(check('git.remote-sha', 'Exact commit on remote branch', 'fail', error.message, { required: true })); + } + } + } catch (error) { + checks.push(check('git.repository', 'Git working tree', 'fail', error.message, { required: true })); + } + + const workflowPath = path.join(repository.localPath, '.gitea', 'workflows', profile.workflowFile); + checks.push(check('workflow.deploy.local', 'Deploy workflow in local repository', await this.fileExists(workflowPath) ? 'pass' : 'fail', workflowPath, { required: true })); + if (profile.rollbackWorkflowFile) { + const rollbackPath = path.join(repository.localPath, '.gitea', 'workflows', profile.rollbackWorkflowFile); + checks.push(check('workflow.rollback.local', 'Rollback workflow in local repository', await this.fileExists(rollbackPath) ? 'pass' : 'warning', rollbackPath)); + } + + try { + const [owner, repo] = repository.fullName.split('/'); + const remoteWorkflow = await this.gitea.repositoryFileExists({ owner, repo, filePath: `.gitea/workflows/${profile.workflowFile}`, ref: profile.branch }); + checks.push(check('workflow.deploy.remote', 'Deploy workflow on Gitea branch', remoteWorkflow ? 'pass' : 'fail', remoteWorkflow ? `${profile.workflowFile} exists on ${profile.branch}.` : `${profile.workflowFile} is not present on ${profile.branch}.`, { required: true })); + try { + await this.gitea.listWorkflowRuns({ owner, repo, branch: profile.branch, limit: 1 }); + checks.push(check('gitea.actions', 'Gitea Actions API', 'pass', 'The Actions runs endpoint is accessible.', { required: true })); + } catch (error) { + checks.push(check('gitea.actions', 'Gitea Actions API', 'fail', error.message, { required: true })); + } + } catch (error) { + checks.push(check('workflow.deploy.remote', 'Deploy workflow on Gitea branch', 'fail', error.message, { required: true })); + } + + if (profile.statusUrl) { + const state = await this.deployments.readStatusEndpoint(profile.statusUrl); + checks.push(check('server.status.configured', 'Server version endpoint configured', 'pass', profile.statusUrl, { required: true })); + checks.push(check('server.status.reachable', 'Server version endpoint reachable', state.reachable && state.ok ? 'pass' : 'warning', state.reachable && state.ok ? `Endpoint reachable${state.liveSha ? `; live ${state.liveSha.slice(0, 7)}` : '; no live SHA reported yet'}.` : state.error || `HTTP ${state.status || 'unavailable'}.`, { help: 'The first deployment may create the status file. Successful completion still requires the endpoint to return the exact SHA and request ID.' })); + if (state.reachable && state.ok) { + const identityMatches = (!state.repository || state.repository === repository.fullName) && (!state.environment || state.environment === profile.environment); + checks.push(check('server.status.identity', 'Status endpoint target identity', identityMatches ? (state.repository && state.environment ? 'pass' : 'warning') : 'fail', state.repository && state.environment ? `${state.repository} / ${state.environment}` : 'Repository or environment is not present in the current status document.', { required: !identityMatches })); + } + } else checks.push(check('server.status.configured', 'Server version endpoint configured', 'fail', 'A status URL is required for exact post-deployment verification.', { required: true })); + + if (profile.healthcheckUrl) { + const health = await this.deployments.checkHealth(profile.healthcheckUrl); + checks.push(check('server.health', 'Application healthcheck', health.healthy ? 'pass' : 'warning', health.healthy ? `HTTP ${health.status} in ${health.latencyMs} ms.` : health.error || `HTTP ${health.status || 'unavailable'}.`)); + } else checks.push(check('server.health', 'Application healthcheck', 'warning', 'No healthcheck URL is configured.')); + + const result = { + kind: 'deployment', repository: repository.fullName, profileId, profileName: profile.name, + startedAt, completedAt: new Date().toISOString(), checks, summary: summarize(checks), + head: status?.head || null + }; + await this.diagnostics.info('preflight.deployment.completed', { repository: repository.fullName, profileId, summary: result.summary, checks }); + return result; + } +} + +module.exports = { PreflightService, summarize, check }; diff --git a/src/main/process-runner.cjs b/src/main/process-runner.cjs new file mode 100644 index 0000000..a7ed6c2 --- /dev/null +++ b/src/main/process-runner.cjs @@ -0,0 +1,37 @@ +'use strict'; + +const { execFile } = require('node:child_process'); + +function run(command, args = [], options = {}) { + const { + cwd, + timeout = 60_000, + maxBuffer = 8 * 1024 * 1024, + env, + allowExitCodes = [] + } = options; + + return new Promise((resolve, reject) => { + execFile(command, args, { + cwd, + timeout, + maxBuffer, + windowsHide: true, + encoding: 'utf8', + env: { ...process.env, ...(env || {}) } + }, (error, stdout, stderr) => { + if (error && !allowExitCodes.includes(error.code)) { + const wrapped = new Error((stderr || stdout || error.message).trim()); + wrapped.code = error.code; + wrapped.stdout = stdout; + wrapped.stderr = stderr; + wrapped.command = `${command} ${args.join(' ')}`; + reject(wrapped); + return; + } + resolve({ stdout: stdout || '', stderr: stderr || '', exitCode: error?.code || 0 }); + }); + }); +} + +module.exports = { run }; diff --git a/src/main/repository-monitor.cjs b/src/main/repository-monitor.cjs new file mode 100644 index 0000000..d102871 --- /dev/null +++ b/src/main/repository-monitor.cjs @@ -0,0 +1,70 @@ +'use strict'; + +class RepositoryMonitor { + constructor({ store, git, onChange, diagnostics = null }) { + this.store = store; + this.git = git; + this.onChange = onChange; + this.diagnostics = diagnostics; + this.paths = []; + this.fingerprints = new Map(); + this.timer = null; + this.running = false; + this.paused = new Set(); + } + + setPaths(paths) { + this.paths = [...new Set((paths || []).filter(Boolean))]; + for (const existing of [...this.fingerprints.keys()]) { + if (!this.paths.includes(existing)) this.fingerprints.delete(existing); + } + } + + pause(localPath) { if (localPath) this.paused.add(localPath); } + resume(localPath) { if (localPath) this.paused.delete(localPath); } + + restart() { + this.stop(); + if (!this.store.data.preferences.autoRefresh) return; + const seconds = Math.min(Math.max(Number(this.store.data.preferences.repositoryPollSeconds) || 4, 2), 60); + this.timer = setInterval(() => this.tick().catch((error) => this.diagnostics?.warning('repository-monitor.tick.failed', error)), seconds * 1000); + this.timer.unref?.(); + } + + stop() { + if (this.timer) clearInterval(this.timer); + this.timer = null; + } + + async tick() { + if (this.running || !this.paths.length) return; + this.running = true; + try { + for (const localPath of this.paths) { + if (this.paused.has(localPath)) continue; + try { + const status = await this.git.status(localPath); + const next = this.git.statusFingerprint(status); + const previous = this.fingerprints.get(localPath); + this.fingerprints.set(localPath, next); + if (previous && previous !== next) { + await this.diagnostics?.debug('repository-monitor.changed', { localPath, head: status.head, branch: status.branch?.head, counts: status.counts }); + this.onChange?.({ localPath, status, reason: 'working-tree-changed' }); + } + } catch (error) { + const next = `error:${error.message}`; + const previous = this.fingerprints.get(localPath); + this.fingerprints.set(localPath, next); + if (previous && previous !== next) { + await this.diagnostics?.warning('repository-monitor.unavailable', { localPath, message: error.message }); + this.onChange?.({ localPath, error: error.message, reason: 'repository-unavailable' }); + } + } + } + } finally { + this.running = false; + } + } +} + +module.exports = { RepositoryMonitor }; diff --git a/src/main/repository-service.cjs b/src/main/repository-service.cjs new file mode 100644 index 0000000..9560944 --- /dev/null +++ b/src/main/repository-service.cjs @@ -0,0 +1,193 @@ +'use strict'; + +const fs = require('node:fs/promises'); +const path = require('node:path'); +const { matchRemoteToRepository, repositoryKey } = require('../shared/repository-match.cjs'); + +const SKIP_DIRECTORIES = new Set([ + '.git', '.svn', '.hg', 'node_modules', '.next', '.nuxt', 'dist', 'build', 'coverage', + '.cache', '.venv', 'venv', '__pycache__', '$RECYCLE.BIN', 'System Volume Information' +]); + +async function mapLimit(items, limit, mapper) { + const output = new Array(items.length); + let cursor = 0; + const workers = Array.from({ length: Math.min(limit, items.length) }, async () => { + while (cursor < items.length) { + const index = cursor++; + output[index] = await mapper(items[index], index); + } + }); + await Promise.all(workers); + return output; +} + +class RepositoryService { + constructor(store, gitService, giteaService, diagnostics = null) { + this.store = store; + this.git = gitService; + this.gitea = giteaService; + this.diagnostics = diagnostics; + this.lastKnownLocalPaths = []; + } + + async discoverInRoot(root, maxDepth = 4) { + const found = []; + const seen = new Set(); + + const visit = async (directory, depth) => { + let real; + try { real = await fs.realpath(directory); } catch { return; } + if (seen.has(real)) return; + seen.add(real); + + const gitMarker = path.join(directory, '.git'); + const marker = await fs.stat(gitMarker).catch(() => null); + if (marker) { + found.push(real); + return; + } + if (depth >= maxDepth) return; + + let entries; + try { entries = await fs.readdir(real, { withFileTypes: true }); } catch { return; } + await mapLimit(entries + .filter((entry) => entry.isDirectory() && !entry.isSymbolicLink() && !SKIP_DIRECTORIES.has(entry.name)), 12, + (entry) => visit(path.join(real, entry.name), depth + 1)); + }; + + await visit(root, 0); + return found; + } + + async discoverAll(roots) { + const grouped = await mapLimit((roots || []).filter(Boolean), 4, (root) => this.discoverInRoot(root)); + return [...new Set(grouped.flat())]; + } + + async getLocalDescriptors(paths) { + return mapLimit(paths, 5, async (localPath) => { + try { + const status = await this.git.status(localPath); + return { localPath: status.root, remoteUrl: status.remoteUrl, status }; + } catch (error) { + return { localPath, remoteUrl: '', status: null, error: error.message }; + } + }); + } + + getWatchPaths() { + return [...this.lastKnownLocalPaths]; + } + + async refresh() { + const started = Date.now(); + const remoteRepositories = this.store.data.gitea.baseUrl && this.store.getToken() + ? await this.gitea.listRepositories() + : []; + + const discoveredPaths = await this.discoverAll(this.store.data.workspaceRoots); + const mappedPaths = Object.values(this.store.data.repositoryMappings || {}); + const localPaths = [...new Set([...discoveredPaths, ...mappedPaths])]; + const localDescriptors = await this.getLocalDescriptors(localPaths); + this.lastKnownLocalPaths = localDescriptors.filter((item) => item.status).map((item) => item.status.root); + + const usedLocalPaths = new Set(); + const repositories = []; + + for (const remote of remoteRepositories) { + const key = repositoryKey(remote); + const explicitPath = this.store.data.repositoryMappings[key]; + let local = explicitPath ? localDescriptors.find((item) => path.resolve(item.localPath) === path.resolve(explicitPath)) : null; + if (!local) local = localDescriptors.find((item) => !usedLocalPaths.has(item.localPath) && matchRemoteToRepository(item.remoteUrl, [remote])); + if (local) usedLocalPaths.add(local.localPath); + + const profiles = this.store.getDeploymentProfiles(remote.full_name).map((profile) => ({ + ...profile, + state: this.store.getDeploymentState(profile.id) + })); + repositories.push(this.decorate(remote, local, profiles)); + } + + for (const local of localDescriptors.filter((item) => !usedLocalPaths.has(item.localPath))) { + const name = path.basename(local.localPath); + repositories.push({ + id: `local:${local.localPath}`, + name, + fullName: name, + owner: { login: 'local' }, + description: 'Local repository not matched to Gitea', + private: true, + defaultBranch: local.status?.branch.head || 'main', + htmlUrl: null, + cloneUrl: null, + sshUrl: null, + preferredCloneUrl: null, + localPath: local.localPath, + localStatus: local.status, + linkState: 'unmatched-local', + deploymentProfiles: [], + readyToDeploy: false, + favorite: false, + attention: Boolean(local.error), + attentionReason: local.error || null + }); + } + + const sorted = repositories.sort((a, b) => { + const score = (repo) => (repo.attention ? 100 : 0) + + (repo.localStatus?.counts.changed ? 50 : 0) + + (repo.localStatus?.branch.ahead ? 30 : 0) + + (repo.readyToDeploy ? 20 : 0) + + (repo.favorite ? 5 : 0); + return score(b) - score(a) || a.fullName.localeCompare(b.fullName); + }); + await this.diagnostics?.debug('repositories.refresh.completed', { + durationMs: Date.now() - started, + remoteCount: remoteRepositories.length, + discoveredCount: discoveredPaths.length, + linkedCount: sorted.filter((item) => item.localPath).length, + attentionCount: sorted.filter((item) => item.attention).length, + readyToDeployCount: sorted.filter((item) => item.readyToDeploy).length + }); + return sorted; + } + + decorate(remote, local, profiles) { + const status = local?.status || null; + const hasChanges = Boolean(status?.counts.changed); + const ahead = status?.branch.ahead || 0; + const behind = status?.branch.behind || 0; + const conflict = Boolean(status?.counts.conflicts); + const profileForBranch = profiles.find((profile) => profile.branch === status?.branch.head); + const readyToDeploy = Boolean(profileForBranch && status?.head && status?.branch.upstream && !hasChanges && ahead === 0 && behind === 0); + const key = String(remote.full_name || '').toLowerCase(); + const preferredCloneUrl = this.store.data.preferences.preferredCloneProtocol === 'ssh' + ? (remote.ssh_url || remote.clone_url) + : (remote.clone_url || remote.ssh_url); + return { + id: remote.id, + name: remote.name, + fullName: remote.full_name, + owner: remote.owner, + description: remote.description || '', + private: remote.private, + defaultBranch: remote.default_branch || 'main', + htmlUrl: remote.html_url, + cloneUrl: remote.clone_url, + sshUrl: remote.ssh_url, + preferredCloneUrl, + updatedAt: remote.updated_at, + localPath: local?.localPath || null, + localStatus: status, + linkState: local ? 'linked' : 'remote-only', + deploymentProfiles: profiles, + readyToDeploy, + favorite: (this.store.data.favorites || []).includes(key), + attention: conflict || behind > 0 || Boolean(local?.error), + attentionReason: conflict ? 'Merge conflict' : behind > 0 ? `${behind} commit${behind === 1 ? '' : 's'} behind remote` : local?.error || null + }; + } +} + +module.exports = { RepositoryService, SKIP_DIRECTORIES, mapLimit }; diff --git a/src/main/ssh-service.cjs b/src/main/ssh-service.cjs new file mode 100644 index 0000000..01178a5 --- /dev/null +++ b/src/main/ssh-service.cjs @@ -0,0 +1,146 @@ +'use strict'; + +const fs = require('node:fs/promises'); +const crypto = require('node:crypto'); + +function loadSshClient() { + try { return require('ssh2').Client; } + catch { + const error = new Error('The ssh2 dependency is not installed. Run npm install before configuring SSH deployments.'); + error.code = 'SSH2_NOT_INSTALLED'; + throw error; + } +} + +function fingerprintKey(key) { + const buffer = Buffer.isBuffer(key) ? key : Buffer.from(key); + return `SHA256:${crypto.createHash('sha256').update(buffer).digest('base64').replace(/=+$/, '')}`; +} + +function shellQuote(value) { + return `'${String(value ?? '').replace(/'/g, `'\\''`)}'`; +} + +class SshService { + constructor({ store, diagnostics }) { + this.store = store; + this.diagnostics = diagnostics; + } + + async connectionOptions(server, { trustOnFirstUse = false } = {}) { + const credentials = this.store.getServerCredentials(server.id); + let observedFingerprint = null; + const options = { + host: server.host, + port: server.port || 22, + username: server.username, + readyTimeout: 20_000, + keepaliveInterval: 10_000, + keepaliveCountMax: 3, + hostVerifier: (key) => { + observedFingerprint = fingerprintKey(key); + return trustOnFirstUse || Boolean(server.hostFingerprint && observedFingerprint === server.hostFingerprint); + } + }; + if (server.authType === 'password') { + options.password = credentials.password; + } else { + options.privateKey = await fs.readFile(server.privateKeyPath); + if (credentials.passphrase) options.passphrase = credentials.passphrase; + } + return { options, getObservedFingerprint: () => observedFingerprint }; + } + + async withClient(serverId, action, options = {}) { + const server = this.store.getServer(serverId); + if (!server) throw new Error('The configured SSH server no longer exists.'); + const Client = loadSshClient(); + const connection = await this.connectionOptions(server, options); + const client = new Client(); + const started = Date.now(); + return new Promise((resolve, reject) => { + let settled = false; + const finish = (callback, value) => { + if (settled) return; + settled = true; + try { client.end(); } catch {} + callback(value); + }; + client.once('ready', async () => { + try { + const data = await action(client, server, connection.getObservedFingerprint()); + await this.diagnostics?.debug('ssh.connection.completed', { + serverId, + host: server.host, + durationMs: Date.now() - started + }); + finish(resolve, data); + } catch (error) { finish(reject, error); } + }); + client.once('error', async (error) => { + const wrapped = new Error(`SSH connection failed: ${error.message}`); + wrapped.code = error.code || 'SSH_CONNECTION_FAILED'; + await this.diagnostics?.warning('ssh.connection.failed', { + serverId, + host: server.host, + durationMs: Date.now() - started, + code: wrapped.code, + message: wrapped.message + }); + finish(reject, wrapped); + }); + client.connect(connection.options); + }); + } + + execClient(client, command, { timeout = 15 * 60_000, maxOutput = 2 * 1024 * 1024 } = {}) { + return new Promise((resolve, reject) => { + const timer = setTimeout(() => reject(new Error('The SSH command timed out.')), timeout); + client.exec(command, (error, stream) => { + if (error) { + clearTimeout(timer); + reject(error); + return; + } + let stdout = ''; + let stderr = ''; + stream.on('data', (chunk) => { if (stdout.length < maxOutput) stdout += chunk.toString(); }); + stream.stderr.on('data', (chunk) => { if (stderr.length < maxOutput) stderr += chunk.toString(); }); + stream.on('close', (code, signal) => { + clearTimeout(timer); + if (code !== 0) { + const failure = new Error(`Remote command failed with exit code ${code}: ${(stderr || stdout).trim().slice(-4000)}`); + failure.code = 'SSH_COMMAND_FAILED'; + failure.exitCode = code; + failure.signal = signal; + reject(failure); + } else resolve({ stdout, stderr, exitCode: code }); + }); + }); + }); + } + + async test(serverId, { trustOnFirstUse = true } = {}) { + return this.withClient(serverId, async (client, server, fingerprint) => { + const result = await this.execClient(client, 'uname -srm && command -v git && (docker compose version || docker-compose version)', { timeout: 30_000 }); + return { + connected: true, + fingerprint, + server: { id: server.id, name: server.name, host: server.host, basePath: server.basePath }, + output: result.stdout.trim() + }; + }, { trustOnFirstUse }); + } + + async exec(serverId, command, options = {}) { + const server = this.store.getServer(serverId); + if (!server?.hostFingerprint) { + const error = new Error('Test and trust the SSH server fingerprint before running deployment commands.'); + error.code = 'SSH_HOST_NOT_TRUSTED'; + throw error; + } + return this.withClient(serverId, (client) => this.execClient(client, command, options), { trustOnFirstUse: false }); + } +} + +module.exports = { SshService, shellQuote, fingerprintKey }; diff --git a/src/main/unraid-deployment-service.cjs b/src/main/unraid-deployment-service.cjs new file mode 100644 index 0000000..d1b3ff6 --- /dev/null +++ b/src/main/unraid-deployment-service.cjs @@ -0,0 +1,595 @@ +'use strict'; + +const fs = require('node:fs/promises'); +const path = require('node:path').posix; +const nativePath = require('node:path'); +const crypto = require('node:crypto'); +const { shellQuote } = require('./ssh-service.cjs'); +const { assertFullCommitSha } = require('../shared/validation.cjs'); +const { normalizeRemoteUrl } = require('../shared/repository-match.cjs'); + +function safeRemoteFolder(value) { + const text = String(value || '').trim(); + if (!/^[a-zA-Z0-9._-]+$/.test(text) || text === '.' || text === '..') throw new Error('Remote folder contains unsupported characters.'); + return text; +} + +function safeRelativeRemoteFile(value, fallback = '') { + const text = String(value || fallback).trim().replace(/\\/g, '/'); + if (!text || text.startsWith('/') || text.split('/').some((part) => !part || part === '.' || part === '..')) { + throw new Error('Remote file path must remain inside the project folder.'); + } + return text; +} + +function bash(command) { + const script = `set -euo pipefail\nexport GIT_TERMINAL_PROMPT=0\nexport GIT_SSH_COMMAND='ssh -o BatchMode=yes'\n${command}`; + const payload = Buffer.from(script, 'utf8').toString('base64'); + return `printf '%s' ${shellQuote(payload)} | base64 -d | bash`; +} + +function parseInspection(text) { + const jsonMarker = '__FORGEFLOW_JSON__'; + const jsonIndex = text.lastIndexOf(jsonMarker); + if (jsonIndex >= 0) return JSON.parse(text.slice(jsonIndex + jsonMarker.length).trim()); + + const kvMarker = '__FORGEFLOW_KV__'; + const kvIndex = text.lastIndexOf(kvMarker); + if (kvIndex < 0) throw new Error('The server inspection did not return a ForgeFlow result.'); + const fields = {}; + for (const line of text.slice(kvIndex + kvMarker.length).trim().split(/\r?\n/)) { + const separator = line.indexOf('='); + if (separator > 0) fields[line.slice(0, separator)] = line.slice(separator + 1); + } + const decodeLines = (value) => { + try { return value ? Buffer.from(value, 'base64').toString('utf8').split(/\r?\n/).filter(Boolean) : []; } + catch { return []; } + }; + const decodeText = (value) => { + try { return value ? Buffer.from(value, 'base64').toString('utf8') : ''; } + catch { return ''; } + }; + return { + exists: fields.exists === 'true', + rootGit: fields.rootGit === 'true', + head: fields.head || null, + branch: fields.branch || null, + remote: fields.remote ? Buffer.from(fields.remote, 'base64').toString('utf8') : null, + trackedChanges: decodeLines(fields.trackedChanges), + composeFiles: decodeLines(fields.composeFiles), + nestedGit: decodeLines(fields.nestedGit), + dockerfile: fields.dockerfile === 'true', + dockerignoreContent: decodeText(fields.dockerignoreContent), + existingPreservePaths: decodeLines(fields.existingPreservePaths) + }; +} + +function dockerIgnoreHasPath(content, value) { + const target = String(value || '').replace(/\\/g, '/').replace(/^\.\//, '').replace(/^\//, '').replace(/\/$/, ''); + if (!target) return false; + return String(content || '').split(/\r?\n/).some((line) => { + let rule = line.trim(); + if (!rule || rule.startsWith('#') || rule.startsWith('!')) return false; + rule = rule.replace(/^\.\//, '').replace(/^\//, '').replace(/\/$/, ''); + return rule === target || rule === `${target}/**` || rule === `${target}/**/*`; + }); +} + +function checksSummary(checks) { + const counts = { + pass: checks.filter((item) => item.status === 'pass').length, + warning: checks.filter((item) => item.status === 'warning').length, + fail: checks.filter((item) => item.status === 'fail').length + }; + return { + ready: counts.fail === 0, + counts, + blocking: checks.filter((item) => item.status === 'fail').map((item) => item.id) + }; +} + +class UnraidDeploymentService { + constructor({ store, ssh, git, diagnostics }) { + this.store = store; + this.ssh = ssh; + this.git = git; + this.diagnostics = diagnostics; + } + + resolve(repository, profileId) { + const profile = this.store.getDeploymentProfile(repository.fullName, profileId); + if (!profile || profile.provider !== 'ssh-unraid') throw new Error('The SSH / Unraid deployment profile no longer exists.'); + const server = this.store.getServer(profile.serverId); + if (!server) throw new Error('The deployment server no longer exists.'); + const remoteFolder = safeRemoteFolder(profile.remoteFolder || repository.name); + const remotePath = path.join(server.basePath, remoteFolder); + if (!remotePath.startsWith(`${server.basePath}/`)) throw new Error('Remote project path escapes the configured server base path.'); + return { profile, server, remoteFolder, remotePath }; + } + + async inspect({ repository, profileId }) { + const { profile, server, remotePath } = this.resolve(repository, profileId); + const preserveProbe = (profile.preservePaths || []).map((relativePath) => + `if [ -e "$root"/${shellQuote(relativePath)} ]; then printf '%s\\n' ${shellQuote(relativePath)}; fi` + ).join('\n'); + const script = ` +root=${shellQuote(remotePath)} +exists=false; root_git=false; head=""; branch=""; remote=""; tracked_changes=""; compose_files=""; nested_git=""; dockerfile=false; dockerignore_content=""; existing_preserve_paths="" +if [ -d "$root" ]; then + exists=true + if [ -d "$root/.git" ]; then + root_git=true + head=$(git -C "$root" rev-parse HEAD 2>/dev/null || true) + branch=$(git -C "$root" branch --show-current 2>/dev/null || true) + remote=$(git -C "$root" remote get-url origin 2>/dev/null || true) + tracked_changes=$(git -C "$root" status --porcelain --untracked-files=no 2>/dev/null | head -n 25 | base64 | tr -d '\\r\\n' || true) + fi + compose_files=$(find "$root" -maxdepth 2 -type f \\( -name 'docker-compose.yml' -o -name 'docker-compose.yaml' -o -name 'compose.yml' -o -name 'compose.yaml' -o -name 'compose.forgeflow.yml' \\) -printf '%P\\n' 2>/dev/null | sort | base64 | tr -d '\\r\\n' || true) + nested_git=$(find "$root" -mindepth 2 -maxdepth 5 -type d -name .git -printf '%h\\n' 2>/dev/null | sed "s#^$root/##" | sort | base64 | tr -d '\\r\\n' || true) + [ -f "$root/Dockerfile" ] && dockerfile=true + [ -f "$root/.dockerignore" ] && dockerignore_content=$(base64 < "$root/.dockerignore" | tr -d '\\r\\n' || true) + existing_preserve_paths=$({ ${preserveProbe || ':'}; } | sort -u | base64 | tr -d '\\r\\n' || true) +fi +printf '__FORGEFLOW_KV__\\n' +printf 'exists=%s\\n' "$exists" +printf 'rootGit=%s\\n' "$root_git" +printf 'head=%s\\n' "$head" +printf 'branch=%s\\n' "$branch" +printf 'remote=%s\\n' "$(printf '%s' "$remote" | base64 | tr -d '\\r\\n')" +printf 'trackedChanges=%s\\n' "$tracked_changes" +printf 'composeFiles=%s\\n' "$compose_files" +printf 'nestedGit=%s\\n' "$nested_git" +printf 'dockerfile=%s\\n' "$dockerfile" +printf 'dockerignoreContent=%s\\n' "$dockerignore_content" +printf 'existingPreservePaths=%s\\n' "$existing_preserve_paths" +`; + const wrapped = bash(script); + const result = await this.ssh.exec(server.id, wrapped, { timeout: 60_000 }); + const parsed = parseInspection(result.stdout); + const contextCandidates = [...new Set([...(parsed.existingPreservePaths || []), ...(parsed.nestedGit || [])])]; + const inspection = { + ...parsed, + dockerignore: Boolean(parsed.dockerignoreContent), + dockerignoreGitExcluded: dockerIgnoreHasPath(parsed.dockerignoreContent, '.git'), + dockerContextExclusionsMissing: parsed.dockerfile + ? contextCandidates.filter((item) => !dockerIgnoreHasPath(parsed.dockerignoreContent, item)) + : [], + serverId: server.id, + serverName: server.name, + remotePath, + profileId: profile.id + }; + await this.diagnostics?.info('unraid.inspected', { + repository: repository.fullName, + serverId: server.id, + remotePath, + exists: inspection.exists, + rootGit: inspection.rootGit, + head: inspection.head, + composeFiles: inspection.composeFiles, + nestedGitCount: inspection.nestedGit.length, + trackedChangeCount: inspection.trackedChanges.length, + dockerContextExclusionsMissing: inspection.dockerContextExclusionsMissing + }); + return inspection; + } + + async preflight({ repository, profileId, sha = null }) { + const { profile, server, remotePath } = this.resolve(repository, profileId); + const targetSha = assertFullCommitSha(sha || repository.localStatus?.head); + const checks = []; + let inspection = null; + + if (!repository.localPath) { + checks.push({ id: 'local-repository', label: 'Local repository', status: 'fail', detail: 'Link or clone the repository locally before deploying.' }); + } else { + try { + const localStatus = await this.git.status(repository.localPath); + checks.push({ id: 'local-repository', label: 'Local repository', status: 'pass', detail: localStatus.root }); + checks.push({ id: 'local-branch', label: 'Allowed branch', status: localStatus.branch.head === profile.branch ? 'pass' : 'fail', detail: `Current: ${localStatus.branch.head || 'detached'}; required: ${profile.branch}.` }); + checks.push({ id: 'local-clean', label: 'Clean local working tree', status: localStatus.clean ? 'pass' : 'fail', detail: localStatus.clean ? 'No uncommitted changes.' : `${localStatus.counts.changed} changed file(s) remain.` }); + checks.push({ id: 'local-upstream', label: 'Published upstream', status: localStatus.branch.upstream ? 'pass' : 'fail', detail: localStatus.branch.upstream || 'No upstream branch is configured.' }); + checks.push({ id: 'local-sync', label: 'Local and Gitea synchronized', status: !localStatus.branch.ahead && !localStatus.branch.behind ? 'pass' : 'fail', detail: `${localStatus.branch.ahead || 0} ahead, ${localStatus.branch.behind || 0} behind.` }); + checks.push({ id: 'local-target-sha', label: 'Selected deployment commit', status: localStatus.head === targetSha ? 'pass' : 'fail', detail: localStatus.head === targetSha ? targetSha : `Local HEAD is ${localStatus.head || 'unknown'}, but deployment requested ${targetSha}.` }); + try { + await this.git.verifyCommitOnRemoteBranch(repository.localPath, targetSha, profile.branch); + checks.push({ id: 'remote-target-sha', label: 'Exact commit on Gitea branch', status: 'pass', detail: `${targetSha.slice(0, 7)} exists on origin/${profile.branch}.` }); + } catch (error) { + checks.push({ id: 'remote-target-sha', label: 'Exact commit on Gitea branch', status: 'fail', detail: error.message }); + } + + const localDeploymentFile = profile.generatedCompose + ? nativePath.join(repository.localPath, 'Dockerfile') + : nativePath.join(repository.localPath, safeRelativeRemoteFile(profile.composeFile || 'docker-compose.yml')); + const localDeploymentFileExists = Boolean((await fs.stat(localDeploymentFile).catch(() => null))?.isFile()); + checks.push({ + id: 'local-deployment-file', + label: profile.generatedCompose ? 'Dockerfile in repository' : 'Compose file in repository', + status: localDeploymentFileExists ? 'pass' : 'fail', + detail: localDeploymentFileExists ? localDeploymentFile : `${localDeploymentFile} was not found in the exact local checkout.` + }); + } catch (error) { + checks.push({ id: 'local-repository', label: 'Local repository', status: 'fail', detail: error.message }); + } + } + + try { + const connection = await this.ssh.test(server.id, { trustOnFirstUse: false }); + checks.push({ id: 'ssh', label: 'SSH connection', status: 'pass', detail: `${server.username}@${server.host}:${server.port}` }); + if (!/docker compose|docker-compose/i.test(connection.output)) { + checks.push({ id: 'compose-command', label: 'Docker Compose', status: 'fail', detail: 'Docker Compose was not detected on the server.' }); + } else checks.push({ id: 'compose-command', label: 'Docker Compose', status: 'pass', detail: 'Docker Compose is available.' }); + } catch (error) { + checks.push({ id: 'ssh', label: 'SSH connection', status: 'fail', detail: error.message }); + } + if (!server.hostFingerprint) checks.push({ id: 'host-key', label: 'Server identity', status: 'fail', detail: 'Test and trust the SSH host key first.' }); + else checks.push({ id: 'host-key', label: 'Server identity', status: 'pass', detail: server.hostFingerprint }); + + try { + inspection = await this.inspect({ repository, profileId }); + if (!inspection.exists) { + checks.push({ id: 'remote-folder', label: 'Remote project folder', status: 'pass', detail: `${remotePath} will be created.` }); + } else if (!inspection.rootGit) { + checks.push({ id: 'remote-folder', label: 'Remote project folder', status: 'fail', detail: `${remotePath} exists but is not a Git working tree. Adopt or migrate it before deployment.` }); + } else { + checks.push({ id: 'remote-folder', label: 'Remote Git working tree', status: 'pass', detail: `${remotePath} at ${String(inspection.head || '').slice(0, 7) || 'unknown'}.` }); + } + if (inspection.trackedChanges.length) { + checks.push({ id: 'tracked-changes', label: 'Server-side tracked changes', status: 'fail', detail: `${inspection.trackedChanges.length} tracked change(s) would be overwritten. Commit, revert or migrate them first.` }); + } else if (inspection.rootGit) checks.push({ id: 'tracked-changes', label: 'Server-side tracked changes', status: 'pass', detail: 'No tracked server-only edits detected.' }); + + if (inspection.rootGit && profile.cloneUrl && inspection.remote) { + const expectedRemote = normalizeRemoteUrl(profile.cloneUrl); + const currentRemote = normalizeRemoteUrl(inspection.remote); + const matches = Boolean(expectedRemote && currentRemote && expectedRemote.host === currentRemote.host && expectedRemote.path === currentRemote.path); + if (!matches && profile.alignRemote) { + checks.push({ id: 'origin-url', label: 'Server Git origin', status: 'warning', detail: `Origin will be aligned from ${inspection.remote} to the configured clone URL before fetch.` }); + } else if (!matches) { + checks.push({ id: 'origin-url', label: 'Server Git origin', status: 'fail', detail: `Current origin ${inspection.remote} does not match the configured clone URL. Enable controlled origin alignment or correct the profile.` }); + } else { + checks.push({ id: 'origin-url', label: 'Server Git origin', status: 'pass', detail: inspection.remote }); + } + } + if (inspection.nestedGit.length) { + checks.push({ id: 'nested-git', label: 'Nested Git repositories', status: 'warning', detail: `Detected: ${inspection.nestedGit.join(', ')}. ForgeFlow will not delete them automatically.` }); + } + if (inspection.dockerfile && !inspection.dockerignore) { + checks.push({ id: 'dockerignore', label: 'Docker build context', status: 'warning', detail: 'A Dockerfile exists but .dockerignore is missing. Add one in the repository before large builds.' }); + } else if (inspection.dockerfile && !inspection.dockerignoreGitExcluded) { + checks.push({ id: 'dockerignore-git', label: 'Git metadata excluded from Docker', status: 'warning', detail: '.dockerignore does not explicitly exclude .git.' }); + } else if (inspection.dockerfile) { + checks.push({ id: 'dockerignore-git', label: 'Git metadata excluded from Docker', status: 'pass', detail: '.git is excluded from the Docker build context.' }); + } + if (inspection.dockerContextExclusionsMissing.length) { + checks.push({ id: 'dockerignore-runtime', label: 'Runtime data excluded from Docker', status: 'warning', detail: `Add these existing runtime or legacy paths to .dockerignore: ${inspection.dockerContextExclusionsMissing.join(', ')}.` }); + } else if (inspection.dockerfile && inspection.existingPreservePaths.length) { + checks.push({ id: 'dockerignore-runtime', label: 'Runtime data excluded from Docker', status: 'pass', detail: 'Detected preserved runtime paths are excluded from the Docker build context.' }); + } + const composeFile = safeRelativeRemoteFile(profile.composeFile || 'docker-compose.yml'); + if (inspection.exists && !inspection.composeFiles.includes(composeFile) && !profile.generatedCompose) { + checks.push({ id: 'compose-file', label: 'Compose configuration', status: 'fail', detail: `${composeFile} was not found. Select an existing file or enable generated Compose.` }); + } else { + checks.push({ id: 'compose-file', label: 'Compose configuration', status: 'pass', detail: profile.generatedCompose ? 'ForgeFlow will generate an isolated Compose file.' : composeFile }); + } + } catch (error) { + checks.push({ id: 'inspection', label: 'Server project inspection', status: 'fail', detail: error.message }); + } + checks.push({ id: 'exact-sha', label: 'Exact deployment commit', status: 'pass', detail: targetSha }); + return { + provider: 'ssh-unraid', + repository: repository.fullName, + environment: profile.environment, + sha: targetSha, + server: { id: server.id, name: server.name, host: server.host }, + remotePath, + inspection, + checks, + summary: checksSummary(checks) + }; + } + + generatedCompose(profile, repository) { + const service = String(profile.composeService || repository.name || 'app').toLowerCase().replace(/[^a-z0-9_-]/g, '-') || 'app'; + if (!profile.hostPort || !profile.containerPort) throw new Error('Host and container ports are required for generated Compose.'); + const labels = [ + 'net.unraid.docker.managed=dockerman', + profile.webUiUrl ? `net.unraid.docker.webui=${profile.webUiUrl}` : '', + profile.iconUrl ? `net.unraid.docker.icon=${profile.iconUrl}` : '' + ].filter(Boolean); + return [ + 'services:', + ` ${service}:`, + ' build:', + ' context: ..', + ` container_name: ${service}`, + ' restart: unless-stopped', + ' ports:', + ` - "${profile.hostPort}:${profile.containerPort}"`, + ...(labels.length ? [' labels:', ...labels.map((label) => ` - ${JSON.stringify(label)}`)] : []) + ].join('\n') + '\n'; + } + + async checkHealth(url) { + if (!url) return { configured: false, healthy: null, status: null, latencyMs: null }; + let last = null; + for (let attempt = 1; attempt <= 5; attempt += 1) { + const started = Date.now(); + try { + const response = await fetch(url, { signal: AbortSignal.timeout(8_000), redirect: 'manual' }); + last = { configured: true, healthy: response.ok, status: response.status, latencyMs: Date.now() - started }; + if (response.ok) return last; + } catch (error) { + last = { configured: true, healthy: false, status: null, latencyMs: Date.now() - started, error: error.message }; + } + if (attempt < 5) await new Promise((resolve) => setTimeout(resolve, 3_000)); + } + return last; + } + + async deploy({ repository, profileId, sha }) { + const targetSha = assertFullCommitSha(sha); + const { profile, server, remotePath } = this.resolve(repository, profileId); + const preflight = await this.preflight({ repository, profileId, sha: targetSha }); + if (!preflight.summary.ready) { + const error = new Error(`SSH deployment preflight failed: ${preflight.summary.blocking.join(', ')}`); + error.code = 'SSH_DEPLOYMENT_PREFLIGHT_FAILED'; + throw error; + } + const requestId = crypto.randomUUID(); + const operation = await this.store.addOperation({ + id: requestId, + type: 'deployment', + action: 'deploy', + provider: 'ssh-unraid', + repository: repository.fullName, + environment: profile.environment, + profileId, + serverId: server.id, + remotePath, + sha: targetSha, + shortSha: targetSha.slice(0, 7), + status: 'running', + logs: ['SSH connection verified.', `Deploying exact commit ${targetSha}.`] + }); + + const cloneUrl = String(profile.cloneUrl || repository.sshUrl || repository.preferredCloneUrl || '').trim(); + if (!cloneUrl) throw new Error('No server-usable Git clone URL is configured.'); + const composeFile = profile.generatedCompose ? '.forgeflow/compose.forgeflow.yml' : safeRelativeRemoteFile(profile.composeFile || 'docker-compose.yml'); + const generated = profile.generatedCompose ? this.generatedCompose(profile, repository) : ''; + const branch = String(profile.branch || 'main'); + const statusJson = JSON.stringify({ + repository: repository.fullName, + environment: profile.environment, + requested_sha: targetSha, + live_sha: targetSha, + request_id: requestId, + healthy: null, + healthcheck_url_configured: Boolean(profile.healthcheckUrl), + deployed_at: new Date().toISOString() + }); + const script = ` +root=${shellQuote(remotePath)} +parent=$(dirname "$root") +mkdir -p "$parent" +if [ ! -d "$root" ]; then + git clone --branch ${shellQuote(branch)} --single-branch ${shellQuote(cloneUrl)} "$root" +fi +test -d "$root/.git" || { echo "Existing folder is not a Git working tree" >&2; exit 32; } +${profile.alignRemote ? `git -C "$root" remote set-url origin ${shellQuote(cloneUrl)}` : ''} +changes=$(git -C "$root" status --porcelain --untracked-files=no) +test -z "$changes" || { echo "Tracked server-side changes block deployment" >&2; printf '%s\\n' "$changes" >&2; exit 33; } +git -C "$root" fetch --prune origin ${shellQuote(branch)} +git -C "$root" cat-file -e ${shellQuote(`${targetSha}^{commit}`)} +git -C "$root" merge-base --is-ancestor ${shellQuote(targetSha)} ${shellQuote(`origin/${branch}`)} +previous=$(git -C "$root" rev-parse HEAD 2>/dev/null || true) +git -C "$root" checkout -B ${shellQuote(branch)} ${shellQuote(`origin/${branch}`)} +git -C "$root" reset --hard ${shellQuote(targetSha)} +mkdir -p "$root/.forgeflow" +printf '%s' "$previous" > "$root/.forgeflow/previous-sha" +printf '%s' ${shellQuote(targetSha)} > "$root/.forgeflow/current-sha" +${profile.generatedCompose ? `cat > "$root/.forgeflow/compose.forgeflow.yml" <<'FORGEFLOW_COMPOSE'\n${generated}FORGEFLOW_COMPOSE` : ''} +cd "$root" +docker compose -f ${shellQuote(composeFile)} config >/dev/null +docker compose -f ${shellQuote(composeFile)} up -d --build --remove-orphans +cat > "$root/.forgeflow/status.json" <<'FORGEFLOW_STATUS' +${statusJson} +FORGEFLOW_STATUS +`; + try { + const result = await this.ssh.exec(server.id, bash(script), { timeout: 30 * 60_000, maxOutput: 4 * 1024 * 1024 }); + const health = await this.checkHealth(profile.healthcheckUrl); + const finalStatus = health.healthy === false ? 'failed' : 'success'; + const finalLogs = [ + ...operation.logs, + ...result.stdout.trim().split('\n').filter(Boolean).slice(-60), + 'Docker Compose deployment completed.', + health.configured ? `Healthcheck ${health.healthy ? 'passed' : 'failed'}${health.status ? ` with HTTP ${health.status}` : ''}.` : 'No desktop healthcheck URL configured.' + ]; + const completed = await this.store.addOperation({ + ...operation, + status: finalStatus, + previousSha: preflight.inspection?.head || null, + health, + logs: finalLogs, + error: health.healthy === false ? 'The application healthcheck did not pass after deployment.' : null + }); + await this.store.saveDeploymentState(profileId, { + liveSha: targetSha, + previousSha: preflight.inspection?.head || null, + healthy: health.healthy, + healthStatus: health.status, + healthLatencyMs: health.latencyMs, + requestId, + remotePath, + provider: 'ssh-unraid' + }); + await this.diagnostics?.info('unraid.deployment.completed', { + requestId, + repository: repository.fullName, + serverId: server.id, + remotePath, + sha: targetSha, + healthy: health.healthy, + healthStatus: health.status + }); + if (health.healthy === false) { + const error = new Error('Deployment completed, but the configured healthcheck failed. The previous SHA remains available for rollback.'); + error.code = 'DEPLOYMENT_HEALTHCHECK_FAILED'; + error.operationId = completed.id; + throw error; + } + return completed; + } catch (error) { + if (error.code !== 'DEPLOYMENT_HEALTHCHECK_FAILED') { + await this.store.addOperation({ ...operation, status: 'failed', error: error.message, logs: [...operation.logs, error.message] }); + } + await this.diagnostics?.error('unraid.deployment.failed', { + requestId, + repository: repository.fullName, + serverId: server.id, + remotePath, + sha: targetSha, + error + }); + throw error; + } + } + + async rollback({ repository, profileId, targetSha }) { + const target = assertFullCommitSha(targetSha); + const { profile, server, remotePath } = this.resolve(repository, profileId); + const deploymentState = this.store.getDeploymentState(profileId); + if (!deploymentState?.previousSha || deploymentState.previousSha !== target) { + const error = new Error('Rollback is allowed only to the exact previous SHA reported by ForgeFlow for this deployment profile.'); + error.code = 'ROLLBACK_TARGET_NOT_PREVIOUS_SHA'; + throw error; + } + if (!repository.localPath) throw new Error('A linked local repository is required for rollback verification.'); + await this.git.verifyCommitOnRemoteBranch(repository.localPath, target, profile.branch); + const inspection = await this.inspect({ repository, profileId }); + if (!inspection.rootGit) throw new Error('The configured server project is not a root Git working tree.'); + if (inspection.trackedChanges.length) throw new Error('Tracked server-side changes block rollback. Commit, revert or migrate them first.'); + const composeFile = profile.generatedCompose ? '.forgeflow/compose.forgeflow.yml' : safeRelativeRemoteFile(profile.composeFile || 'docker-compose.yml'); + const requestId = crypto.randomUUID(); + const operation = await this.store.addOperation({ + id: requestId, + type: 'deployment', + action: 'rollback', + provider: 'ssh-unraid', + repository: repository.fullName, + environment: profile.environment, + profileId, + serverId: server.id, + remotePath, + sha: target, + shortSha: target.slice(0, 7), + status: 'running', + logs: [`Rolling back to exact commit ${target}.`] + }); + const statusJson = JSON.stringify({ + repository: repository.fullName, + environment: profile.environment, + requested_sha: target, + live_sha: target, + request_id: requestId, + healthy: null, + healthcheck_url_configured: Boolean(profile.healthcheckUrl), + rollback: true, + deployed_at: new Date().toISOString() + }); + const script = ` +root=${shellQuote(remotePath)} +test -d "$root/.git" +git -C "$root" fetch --prune origin ${shellQuote(profile.branch)} +git -C "$root" cat-file -e ${shellQuote(`${target}^{commit}`)} +current=$(git -C "$root" rev-parse HEAD) +git -C "$root" reset --hard ${shellQuote(target)} +cd "$root" +docker compose -f ${shellQuote(composeFile)} config >/dev/null +docker compose -f ${shellQuote(composeFile)} up -d --build --remove-orphans +printf '%s' "$current" > "$root/.forgeflow/previous-sha" +printf '%s' ${shellQuote(target)} > "$root/.forgeflow/current-sha" +cat > "$root/.forgeflow/status.json" <<'FORGEFLOW_STATUS' +${statusJson} +FORGEFLOW_STATUS +`; + try { + const result = await this.ssh.exec(server.id, bash(script), { timeout: 30 * 60_000, maxOutput: 4 * 1024 * 1024 }); + const health = await this.checkHealth(profile.healthcheckUrl); + const finalStatus = health.healthy === false ? 'failed' : 'rolled-back'; + const completed = await this.store.addOperation({ + ...operation, + status: finalStatus, + previousSha: deploymentState.liveSha || inspection.head || null, + health, + error: health.healthy === false ? 'The application healthcheck did not pass after rollback.' : null, + logs: [ + ...operation.logs, + ...result.stdout.trim().split('\n').filter(Boolean).slice(-60), + 'Rollback completed.', + health.configured ? `Healthcheck ${health.healthy ? 'passed' : 'failed'}${health.status ? ` with HTTP ${health.status}` : ''}.` : 'No desktop healthcheck URL configured.' + ] + }); + await this.store.saveDeploymentState(profileId, { + liveSha: target, + previousSha: deploymentState.liveSha || inspection.head || null, + healthy: health.healthy, + healthStatus: health.status, + healthLatencyMs: health.latencyMs, + requestId, + remotePath, + provider: 'ssh-unraid' + }); + if (health.healthy === false) { + const error = new Error('Rollback completed, but the configured healthcheck failed.'); + error.code = 'ROLLBACK_HEALTHCHECK_FAILED'; + error.operationId = completed.id; + throw error; + } + return completed; + } catch (error) { + if (error.code !== 'ROLLBACK_HEALTHCHECK_FAILED') { + await this.store.addOperation({ ...operation, status: 'failed', error: error.message, logs: [...operation.logs, error.message] }); + } + throw error; + } + } + + async refreshProfileState(fullName, profileId) { + const repository = { fullName, name: fullName.split('/').pop() }; + const { profile, server, remotePath } = this.resolve(repository, profileId); + const script = ` +root=${shellQuote(remotePath)} +live=""; previous=""; status="" +[ -f "$root/.forgeflow/current-sha" ] && live=$(cat "$root/.forgeflow/current-sha") +[ -f "$root/.forgeflow/previous-sha" ] && previous=$(cat "$root/.forgeflow/previous-sha") +[ -f "$root/.forgeflow/status.json" ] && status=$(base64 "$root/.forgeflow/status.json" | tr -d '\\r\\n') +printf '__FORGEFLOW_JSON__\\n{"liveSha":"%s","previousSha":"%s","statusBase64":"%s"}\\n' "$live" "$previous" "$status" +`; + const result = await this.ssh.exec(server.id, bash(script), { timeout: 30_000 }); + const raw = parseInspection(result.stdout); + let remoteStatus = null; + try { remoteStatus = raw.statusBase64 ? JSON.parse(Buffer.from(raw.statusBase64, 'base64').toString('utf8')) : null; } catch {} + const existing = this.store.getDeploymentState(profile.id) || {}; + return this.store.saveDeploymentState(profile.id, { + liveSha: /^[0-9a-f]{40}$/i.test(raw.liveSha || '') ? raw.liveSha : null, + previousSha: /^[0-9a-f]{40}$/i.test(raw.previousSha || '') ? raw.previousSha : null, + healthy: remoteStatus?.healthy ?? existing.healthy ?? null, + healthStatus: existing.healthStatus ?? null, + healthLatencyMs: existing.healthLatencyMs ?? null, + requestId: remoteStatus?.request_id || existing.requestId || null, + remotePath, + provider: 'ssh-unraid' + }); + } +} + +module.exports = { + UnraidDeploymentService, + safeRemoteFolder, + safeRelativeRemoteFile, + parseInspection, + dockerIgnoreHasPath, + checksSummary, + bash +}; diff --git a/src/main/update-service.cjs b/src/main/update-service.cjs new file mode 100644 index 0000000..c76d1e7 --- /dev/null +++ b/src/main/update-service.cjs @@ -0,0 +1,135 @@ +'use strict'; + +const fs = require('node:fs/promises'); +const path = require('node:path'); +const crypto = require('node:crypto'); +const { spawn } = require('node:child_process'); +const { isNewerVersion } = require('../shared/semver.cjs'); + +function safeRepositoryPart(value, label) { + const text = String(value || '').trim(); + if (!/^[a-zA-Z0-9_.-]+$/.test(text)) throw new Error(`${label} contains unsupported characters.`); + return text; +} + +class UpdateService { + constructor({ store, gitea, diagnostics, appInfo, sourcePath, userDataPath }) { + this.store = store; + this.gitea = gitea; + this.diagnostics = diagnostics; + this.appInfo = appInfo; + this.sourcePath = sourcePath; + this.updateDirectory = path.join(userDataPath, 'updates'); + this.staged = null; + } + + async check() { + const settings = this.store.data.updates || {}; + const owner = safeRepositoryPart(settings.owner || 'Jens', 'Update repository owner'); + const repo = safeRepositoryPart(settings.repo || 'ForgeFlow', 'Update repository name'); + const branchName = String(settings.branch || 'main').trim(); + const branch = await this.gitea.getBranch(owner, repo, branchName); + const remoteSha = branch?.commit?.id || branch?.commit?.sha || branch?.commit?.commit?.id; + if (!/^[0-9a-f]{40}$/i.test(String(remoteSha || ''))) throw new Error('Gitea did not return a full commit SHA for the update branch.'); + + const file = await this.gitea.getRepositoryFile({ owner, repo, filePath: 'package.json', ref: remoteSha }); + let manifest; + try { manifest = JSON.parse(file.decoded); } + catch { throw new Error('The remote ForgeFlow package.json is not valid JSON.'); } + if (manifest.name !== 'forgeflow') throw new Error('The configured update repository is not a ForgeFlow source repository.'); + const remoteVersion = String(manifest.version || '').trim(); + const currentVersion = String(this.appInfo.version || '').trim(); + const available = isNewerVersion(remoteVersion, currentVersion); + const result = { + checkedAt: new Date().toISOString(), + owner, + repo, + branch: branchName, + currentVersion, + remoteVersion, + remoteSha, + shortSha: remoteSha.slice(0, 7), + available, + packaged: Boolean(this.appInfo.packaged), + mode: this.appInfo.packaged ? 'packaged' : 'source' + }; + this.store.data.updates.lastCheckedAt = result.checkedAt; + await this.store.save(); + await this.diagnostics?.info('updates.checked', { + repository: `${owner}/${repo}`, + branch: branchName, + currentVersion, + remoteVersion, + remoteSha, + available, + mode: result.mode + }); + return result; + } + + async download(expected = null) { + const update = expected?.remoteSha ? expected : await this.check(); + if (!update.available) return { ...update, downloaded: false, reason: 'up-to-date' }; + if (this.appInfo.packaged) { + const error = new Error('This developer release uses source updates. Install a signed packaged release before using binary auto-update.'); + error.code = 'PACKAGED_UPDATE_NOT_CONFIGURED'; + throw error; + } + + await fs.mkdir(this.updateDirectory, { recursive: true }); + const archiveUrl = `${this.store.data.gitea.baseUrl.replace(/\/+$/, '')}/${encodeURIComponent(update.owner)}/${encodeURIComponent(update.repo)}/archive/${update.remoteSha}.zip`; + const archive = await this.gitea.downloadAuthenticated(archiveUrl); + if (archive.length < 1000 || archive[0] !== 0x50 || archive[1] !== 0x4b) throw new Error('The downloaded update is not a valid ZIP archive.'); + const sha256 = crypto.createHash('sha256').update(archive).digest('hex'); + const archivePath = path.join(this.updateDirectory, `ForgeFlow-${update.remoteVersion}-${update.shortSha}.zip`); + const metadataPath = `${archivePath}.json`; + await fs.writeFile(archivePath, archive, { mode: 0o600 }); + const metadata = { ...update, archivePath, sha256, downloadedAt: new Date().toISOString() }; + await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2), { mode: 0o600 }); + this.staged = metadata; + await this.diagnostics?.info('updates.downloaded', { + remoteVersion: update.remoteVersion, + remoteSha: update.remoteSha, + bytes: archive.length, + sha256 + }); + return { ...metadata, downloaded: true }; + } + + async apply(staged = null) { + const update = staged?.archivePath ? staged : this.staged; + if (!update?.archivePath) throw new Error('Download an update before applying it.'); + if (process.platform !== 'win32') throw new Error('The integrated source updater currently supports Windows only.'); + const stat = await fs.stat(update.archivePath).catch(() => null); + if (!stat?.isFile()) throw new Error('The staged update archive is no longer available.'); + + const scriptPath = path.join(this.sourcePath, 'scripts', 'apply-source-update.ps1'); + const scriptStat = await fs.stat(scriptPath).catch(() => null); + if (!scriptStat?.isFile()) throw new Error('The source update helper is missing.'); + const logPath = path.join(this.updateDirectory, `apply-${Date.now()}.log`); + const args = [ + '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', scriptPath, + '-SourcePath', this.sourcePath, + '-ArchivePath', update.archivePath, + '-ExpectedVersion', update.remoteVersion, + '-ExpectedSha256', update.sha256, + '-ParentPid', String(process.pid), + '-LogPath', logPath + ]; + const child = spawn('powershell.exe', args, { + detached: true, + stdio: 'ignore', + windowsHide: false, + cwd: this.sourcePath + }); + child.unref(); + await this.diagnostics?.info('updates.apply-launched', { + remoteVersion: update.remoteVersion, + remoteSha: update.remoteSha, + logPath + }); + return { launched: true, version: update.remoteVersion, logPath }; + } +} + +module.exports = { UpdateService, safeRepositoryPart }; diff --git a/src/renderer/app.js b/src/renderer/app.js new file mode 100644 index 0000000..e96bf3b --- /dev/null +++ b/src/renderer/app.js @@ -0,0 +1,1055 @@ +const app = document.querySelector('#app'); +const toastRoot = document.querySelector('#toast-root'); + +const icons = { + overview: '', + repository: '', + deploy: '', + settings: '', + search: '', + refresh: '', + folder: '', + git: '', + branch: '', + file: '', + check: '', + warning: '', + error: '', + arrowRight: '', + arrowUp: '', + arrowDown: '', + external: '', + play: '', + terminal: '', + clock: '', + sun: '', + moon: '', + plus: '', + trash: '', + close: '', + copy: '', + chevron: '', + link: '', + cloud: '', + pulse: '', + history: '', + more: '', + star: '', + archive: '', + shield: '', + rocket: '', + layers: '', + undo: '', + menu: '', + download: '', + server: '', + key: '', + update: '' +}; + +function icon(name, className = '') { + return ``; +} + +function escapeHtml(value) { + return String(value ?? '').replace(/[&<>'"]/g, (character) => ({ '&': '&', '<': '<', '>': '>', "'": ''', '"': '"' })[character]); +} +function attr(value) { return escapeHtml(value).replace(/`/g, '`'); } +function formatDate(value) { + if (!value) return 'Unknown'; + const date = new Date(value); + if (Number.isNaN(date.getTime())) return String(value); + const diff = Date.now() - date.getTime(); + if (diff < 60_000) return 'just now'; + if (diff < 3_600_000) return `${Math.max(1, Math.floor(diff / 60_000))}m ago`; + if (diff < 86_400_000) return `${Math.floor(diff / 3_600_000)}h ago`; + if (diff < 604_800_000) return `${Math.floor(diff / 86_400_000)}d ago`; + return date.toLocaleDateString(undefined, { day: '2-digit', month: 'short', year: date.getFullYear() !== new Date().getFullYear() ? 'numeric' : undefined }); +} +function truncate(value, length = 76) { const text = String(value || ''); return text.length > length ? `${text.slice(0, length - 1)}…` : text; } +function shortSha(value) { return String(value || '').slice(0, 7) || '—'; } +function defaultWorkspaceRoot() { return ui.boot?.state?.workspaceRoots?.[0] || null; } +function safeCloneFolderName(repository) { + return String(repository?.name || 'repository').replace(/\.git$/i, '').replace(/[^a-zA-Z0-9._-]/g, '-') || 'repository'; +} +function displayCloneTarget(repository) { + const root = defaultWorkspaceRoot(); + if (!root) return null; + const separator = ui.boot?.platform === 'win32' ? '\\' : '/'; + return `${String(root).replace(/[\\/]+$/, '')}${separator}${safeCloneFolderName(repository)}`; +} +function clonePrimaryLabel(repository) { + return defaultWorkspaceRoot() ? `Clone to ${safeCloneFolderName(repository)}` : 'Choose project root & clone'; +} +function isTerminalOperation(status) { return ['success', 'failed', 'cancelled', 'rolled-back'].includes(status); } +function toneForStatus(status) { + if (['success', 'healthy', 'complete', 'rolled-back'].includes(status)) return 'success'; + if (['failed', 'failure', 'unhealthy', 'danger'].includes(status)) return 'danger'; + if (['queued', 'running', 'requested', 'warning', 'active'].includes(status)) return 'warning'; + return ''; +} + +const ui = { + boot: null, + repositories: [], + currentView: 'overview', + selectedRepoId: null, + repositoryTab: 'changes', + selectedFile: null, + selectedFiles: new Set(), + selectedProfileId: null, + diff: '', + history: [], + branches: [], + stashes: [], + search: '', + repoSearch: '', + commitMessage: '', + loading: false, + loadingMessage: '', + modal: null, + setupStep: 0, + systemPreflight: null, + deploymentPreflight: null, + diagnosticsStatus: null, + lastDiagnosticBundle: null, + setupDraft: { baseUrl: 'https://', token: '', user: null, roots: [], discovered: [] }, + setupValidation: null, + activeDeployment: null, + isMock: false, + refreshError: null, + autoRefreshPending: false, + paletteQuery: '', + updateStatus: null, + updateChecking: false, + servers: [], + serverInspection: null +}; + +function selectedRepository() { return ui.repositories.find((repository) => String(repository.id) === String(ui.selectedRepoId)) || null; } +function selectedProfile(repository = selectedRepository()) { + if (!repository?.deploymentProfiles?.length) return null; + return repository.deploymentProfiles.find((profile) => profile.id === ui.selectedProfileId) + || repository.deploymentProfiles.find((profile) => profile.branch === repository.localStatus?.branch.head) + || repository.deploymentProfiles[0]; +} +function operations() { return ui.boot?.state?.operations || []; } +function repositoryOperations(repository) { return operations().filter((operation) => operation.repository === repository?.fullName); } + +function applyTheme(appearance) { + const resolved = appearance === 'system' ? (matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark') : appearance; + document.documentElement.dataset.theme = resolved || 'dark'; +} + +function showToast(title, message, type = 'info') { + const toast = document.createElement('div'); + toast.className = `toast ${type}`; + toast.innerHTML = `${icon(type === 'error' ? 'error' : type === 'success' ? 'check' : 'warning')}

${escapeHtml(title)}${escapeHtml(message)}
`; + toastRoot.append(toast); + setTimeout(() => toast.remove(), 5600); +} + +function setLoading(loading, message = '') { ui.loading = loading; ui.loadingMessage = message; render(); } +function updateOperationInState(operation) { + if (!operation || !ui.boot) return; + const list = operations(); + ui.boot.state.operations = [operation, ...list.filter((item) => item.id !== operation.id)].slice(0, 250); + if (ui.activeDeployment?.id === operation.id) ui.activeDeployment = operation; +} + +async function bootstrap() { + try { + ui.boot = await window.forgeflow.bootstrap(); + ui.isMock = String(ui.boot.appVersion).includes('demo'); + ui.diagnosticsStatus = ui.boot.diagnostics || null; + applyTheme(ui.boot.state.appearance); + ui.setupDraft.roots = [...(ui.boot.state.workspaceRoots || [])]; + if (ui.boot.state.setupComplete) { + await refreshRepositories(false); + await refreshActiveOperations(false); + } + window.forgeflow.onRepositoriesChanged?.(() => scheduleAutoRefresh()); + window.forgeflow.onOperationsChanged?.((payload) => { + for (const operation of payload?.operations || []) updateOperationInState(operation); + render(); + }); + window.forgeflow.onUpdatesChanged?.((payload) => { + ui.updateStatus = payload; + render(); + if (payload?.available) showToast('ForgeFlow update available', `Version ${payload.remoteVersion} is ready to download.`, 'success'); + }); + render(); + } catch (error) { + app.innerHTML = `
${icon('error')}ForgeFlow could not start${escapeHtml(error.message)}
`; + } +} + +function scheduleAutoRefresh() { + if (ui.loading || ui.autoRefreshPending || !ui.boot?.state?.preferences?.autoRefresh) return; + ui.autoRefreshPending = true; + setTimeout(async () => { + ui.autoRefreshPending = false; + await refreshRepositories(false, true); + }, 450); +} + +async function refreshRepositories(withLoader = true, silent = false) { + if (withLoader) setLoading(true, 'Refreshing Local → Gitea → Server state…'); + try { + const selectedId = ui.selectedRepoId; + ui.repositories = await window.forgeflow.refreshRepositories(); + ui.refreshError = null; + if (selectedId && !selectedRepository()) ui.selectedRepoId = null; + const repository = selectedRepository(); + if (repository && !repository.deploymentProfiles.some((profile) => profile.id === ui.selectedProfileId)) ui.selectedProfileId = selectedProfile(repository)?.id || null; + if (!ui.selectedRepoId && ui.currentView === 'repository' && ui.repositories.length) selectRepository(ui.repositories[0].id, false); + } catch (error) { + ui.refreshError = error.message; + if (!silent) showToast('Refresh failed', error.message, 'error'); + } finally { + if (withLoader) setLoading(false); else render(); + } +} + +async function refreshActiveOperations(showErrors = true) { + try { + const updated = await window.forgeflow.refreshOperations(); + for (const operation of Array.isArray(updated) ? updated : []) updateOperationInState(operation); + return updated; + } catch (error) { + if (showErrors) showToast('Actions status unavailable', error.message, 'error'); + return []; + } +} + +function selectRepository(id, shouldRender = true) { + ui.selectedRepoId = id; + ui.currentView = 'repository'; + ui.repositoryTab = 'changes'; + ui.commitMessage = ''; + ui.history = []; + ui.branches = []; + ui.stashes = []; + const repository = selectedRepository(); + ui.selectedProfileId = selectedProfile(repository)?.id || null; + const files = repository?.localStatus?.files || []; + ui.selectedFiles = new Set(files.map((file) => file.path)); + ui.selectedFile = files[0]?.path || null; + ui.diff = ''; + if (ui.selectedFile && repository?.localPath) loadDiff(repository, ui.selectedFile); + if (shouldRender) render(); +} + +async function loadDiff(repository, filePath) { + ui.diff = 'Loading diff…'; + render(); + try { + const file = repository.localStatus?.files.find((item) => item.path === filePath); + ui.diff = await window.forgeflow.repositoryDiff(repository.localPath, filePath, Boolean(file?.staged && !file?.unstaged)); + } catch (error) { ui.diff = `Unable to load diff: ${error.message}`; } + render(); +} + +function repositoryAction(repository) { + if (!repository.localPath) return { kind: 'link', title: 'Connect this repository', detail: 'Link an existing local folder or clone it from Gitea.' }; + const status = repository.localStatus; + if (!status) return { kind: 'error', title: 'Local repository unavailable', detail: repository.attentionReason || 'The linked folder could not be read.' }; + if (status.counts.conflicts) return { kind: 'conflict', title: 'Resolve merge conflicts', detail: `${status.counts.conflicts} conflicted file${status.counts.conflicts === 1 ? '' : 's'} block synchronization.` }; + if (status.counts.changed) return { kind: 'commit', title: 'Commit local changes', detail: `${status.counts.changed} changed file${status.counts.changed === 1 ? '' : 's'} detected.` }; + if (status.branch.behind && status.branch.ahead) return { kind: 'diverged', title: 'Branches have diverged', detail: `Local is ${status.branch.ahead} ahead and ${status.branch.behind} behind. Resolve this in your Git tooling.` }; + if (status.branch.behind) return { kind: 'pull', title: 'Synchronize from Gitea', detail: `Local ${status.branch.head} is ${status.branch.behind} commit${status.branch.behind === 1 ? '' : 's'} behind.` }; + if (status.branch.ahead) return { kind: 'push', title: 'Push local commits', detail: `${status.branch.ahead} commit${status.branch.ahead === 1 ? '' : 's'} ready to push.` }; + if (!repository.deploymentProfiles?.length) return { kind: 'configure', title: 'Configure deployment', detail: 'Connect a predefined Gitea Actions workflow before deploying.' }; + const profile = selectedProfile(repository); + if (profile?.branch !== status.branch.head) return { kind: 'branch-profile', title: 'No deployment for this branch', detail: `The selected profile accepts ${profile.branch}; you are on ${status.branch.head}.` }; + if (repository.readyToDeploy) return { kind: 'deploy', title: 'Ready for deployment', detail: `Commit ${status.shortHead} can be released to ${profile.environment}.` }; + return { kind: 'clean', title: 'Repository synchronized', detail: 'No local or remote action is required.' }; +} + +function navButton(view, label, iconName, count = '') { + return ``; +} + +function renderTitlebar() { + const state = ui.boot?.state; + const user = state?.gitea?.user; + const connected = Boolean(state?.gitea?.hasToken); + const repository = selectedRepository(); + const title = ui.currentView === 'repository' && repository ? repository.fullName : ({ overview: 'Release overview', deployments: 'Deployments', diagnostics: 'Diagnostics', settings: 'Settings', 'deployment-run': 'Deployment run' }[ui.currentView] || 'Workspace'); + return `
+
ForgeFlowby ITWorx.tech
${escapeHtml(title)}
+
+ +
${icon('search')}
+ ${connected ? escapeHtml(user?.login || 'Gitea') : 'Offline'} + + +
+
`; +} + +function renderRepositoryRow(repository) { + const status = repository.localStatus; + const badges = []; + if (status?.counts.conflicts) badges.push('!'); + else if (status?.counts.changed) badges.push(`${status.counts.changed}`); + if (status?.branch.ahead) badges.push(`↑${status.branch.ahead}`); + if (status?.branch.behind) badges.push(`↓${status.branch.behind}`); + if (repository.readyToDeploy) badges.push(''); + if (!repository.localPath) badges.push(''); + const branch = status?.branch.head || repository.defaultBranch || 'remote'; + return ``; +} + +function renderSidebar() { + const query = `${ui.search} ${ui.repoSearch}`.trim().toLowerCase(); + const repositories = ui.repositories.filter((repository) => !query || `${repository.name} ${repository.fullName} ${repository.description}`.toLowerCase().includes(query)); + const favorites = repositories.filter((repository) => repository.favorite); + const others = repositories.filter((repository) => !repository.favorite); + const attention = ui.repositories.filter((repository) => repository.attention || repository.localStatus?.counts.changed || repository.localStatus?.branch.ahead || repository.readyToDeploy).length; + const rows = (list) => list.map(renderRepositoryRow).join(''); + return ``; +} + +function renderSummaryCard(label, value, note, iconName, tone = '') { + return `
${icon(iconName)}
${label}
${value}
${note}
`; +} + +function queueActionFor(repository) { + const action = repositoryAction(repository); + const mapping = { + link: ['folder', 'Link folder', 'Local project is not connected', ''], + error: ['error', 'Inspect problem', action.detail, 'danger'], + conflict: ['warning', 'Resolve conflicts', action.detail, 'danger'], + commit: ['file', 'Review & commit', action.detail, 'warning'], + diverged: ['warning', 'Resolve divergence', action.detail, 'danger'], + pull: ['arrowDown', 'Synchronize', action.detail, 'warning'], + push: ['arrowUp', 'Push commits', action.detail, ''], + configure: ['settings', 'Configure deploy', action.detail, ''], + 'branch-profile': ['branch', 'Select profile', action.detail, ''], + deploy: ['rocket', 'Deploy release', action.detail, 'success'], + clean: ['check', 'Synchronized', action.detail, 'success'] + }; + return mapping[action.kind] || mapping.clean; +} + +function renderOverview() { + const changed = ui.repositories.filter((repository) => repository.localStatus?.counts.changed).length; + const unpushed = ui.repositories.filter((repository) => repository.localStatus?.branch.ahead).length; + const deployable = ui.repositories.filter((repository) => repository.readyToDeploy).length; + const unhealthy = ui.repositories.flatMap((repository) => repository.deploymentProfiles || []).filter((profile) => profile.state?.healthy === false).length; + const queue = ui.repositories.filter((repository) => repositoryAction(repository).kind !== 'clean').slice(0, 8); + const recent = operations().slice(0, 7); + const active = recent.filter((operation) => !isTerminalOperation(operation.status)); + return `
+ + ${ui.refreshError ? `
${icon('error')} ${escapeHtml(ui.refreshError)}
` : ''} +
+ ${renderSummaryCard('Local work', changed, changed === 1 ? 'repository has changes' : 'repositories have changes', 'file', changed ? 'warning' : 'success')} + ${renderSummaryCard('Unpushed', unpushed, 'repositories ahead of Gitea', 'arrowUp', unpushed ? 'warning' : 'success')} + ${renderSummaryCard('Ready', deployable, 'exact commits ready to deploy', 'rocket', deployable ? 'success' : '')} + ${renderSummaryCard('Health', unhealthy || active.length, unhealthy ? 'unhealthy environments' : active.length ? 'operations in progress' : 'all checked environments healthy', 'pulse', unhealthy ? 'danger' : active.length ? 'warning' : 'success')} +
+

Action queue

Sorted by required attention
+ ${queue.length ? queue.map((repository) => { const [iconName, label, reason, tone] = queueActionFor(repository); return `
${icon(iconName)}
${escapeHtml(repository.name)}
${escapeHtml(repository.localStatus?.branch.head || repository.defaultBranch || 'remote')} ${repository.localStatus?.shortHead ? `• ${repository.localStatus.shortHead}` : ''}
${escapeHtml(label)}${escapeHtml(reason)}
`; }).join('') : '

Everything is synchronized

No repository needs immediate attention.

'} +
+
+

Recent deployments

${recent.length ? recent.map((operation) => `
${escapeHtml(operation.repository)} → ${escapeHtml(operation.environment || 'environment')}
${escapeHtml(operation.action === 'rollback' ? 'Rollback' : 'Deploy')} ${escapeHtml(operation.shortSha || shortSha(operation.sha))} · ${escapeHtml(operation.status)}
${formatDate(operation.updatedAt || operation.createdAt)}
`).join('') : '

No deployment history yet.

'}
+

Workspace readiness

+ ${readinessRow('Git executable', ui.boot.git.available, ui.boot.git.version || ui.boot.git.error)} + ${readinessRow('Gitea connection', ui.boot.state.gitea.hasToken, ui.boot.state.gitea.baseUrl || 'Not configured')} + ${readinessRow('Workspace folders', ui.boot.state.workspaceRoots.length > 0, `${ui.boot.state.workspaceRoots.length} configured`)} + ${readinessRow('Automatic awareness', ui.boot.state.preferences?.autoRefresh !== false, ui.boot.state.preferences?.autoRefresh === false ? 'Manual refresh only' : `Every ${ui.boot.state.preferences?.repositoryPollSeconds || 4}s`)} +
+
+
`; +} + +function readinessRow(label, ok, detail) { return `
${escapeHtml(label)}${escapeHtml(detail)}
`; } +function releaseNode(label, value, description, tone = '') { return `
${label}
${escapeHtml(value)}${escapeHtml(description)}
`; } + +function renderDiff(diff) { + if (!diff) return '

No textual diff

Select another file or open the project folder for binary changes.

'; + return escapeHtml(diff).split('\n').map((line) => { + const type = line.startsWith('+') && !line.startsWith('+++') ? 'add' : line.startsWith('-') && !line.startsWith('---') ? 'remove' : line.startsWith('@@') ? 'hunk' : ''; + return `${line || ' '}`; + }).join(''); +} +function fileStatusCode(file) { + if (file.conflict) return 'U'; + if (file.untracked) return '?'; + return ({ modified: 'M', added: 'A', deleted: 'D', renamed: 'R', copied: 'C', 'type-changed': 'T' }[file.status] || 'M'); +} + +function renderChanges(repository) { + const status = repository.localStatus; + if (!repository.localPath) { + const target = displayCloneTarget(repository); + return `
${icon('link')}

Connect a local project

Clone directly into your default project root, or link an existing working tree.

${target ? `
${icon('folder')}Automatic destination${escapeHtml(target)}
` : '
No default project root is configured. ForgeFlow will ask for one.
'}
`; + } + if (!status) return `
${icon('error')}

Repository unavailable

${escapeHtml(repository.attentionReason || 'The local working tree could not be read.')}

`; + if (!status.files.length) return `
${icon('check')}

Working tree clean

Local ${escapeHtml(status.branch.head)} is at ${escapeHtml(status.shortHead)} with no uncommitted files.

`; + return `
${ui.selectedFiles.size} selected · ${status.counts.changed} changed · ${status.counts.staged} staged
${status.files.map((file) => `
${fileStatusCode(file)}${escapeHtml(file.path)}${file.staged ? '●' : '○'}
`).join('')}
${escapeHtml(ui.selectedFile || 'Select a file')}
${ui.selectedFile ? escapeHtml(status.files.find((file) => file.path === ui.selectedFile)?.status || '') : ''}
${renderDiff(ui.diff)}
`; +} + +function renderHistory(repository) { + if (!repository.localPath) return '

Link a local repository to view commit history.

'; + if (!ui.history.length) return `
${icon('history')}

Load local commit history

Review the last commits from this working tree.

`; + return `
${ui.history.map((commit) => ``).join('')}
CommitMessageAuthorDate
${escapeHtml(commit.shortSha)}${escapeHtml(commit.subject)}${escapeHtml(commit.author)}${formatDate(commit.date)}
`; +} + +function environmentState(profile) { + const state = profile.state || {}; + if (state.healthy === false) return { label: 'Unhealthy', tone: 'danger' }; + if (state.healthy === true) return { label: 'Healthy', tone: 'success' }; + if (profile.provider === 'ssh-unraid' || state.statusConfigured || state.healthConfigured) return { label: 'Not checked', tone: '' }; + return { label: 'Status not configured', tone: '' }; +} + +function renderProfileCard(repository, profile, compact = false) { + const state = profile.state || {}; + const health = environmentState(profile); + const ready = repository.readyToDeploy && repository.localStatus?.branch.head === profile.branch; + const isSsh = profile.provider === 'ssh-unraid'; + const providerDetail = isSsh + ? `SSH / Unraid · ${profile.remoteFolder || repository.name} · ${profile.branch}` + : `${profile.workflowFile} · ${profile.branch}`; + const rollbackConfigured = isSsh || Boolean(profile.rollbackWorkflowFile); + return `
${escapeHtml(profile.environment)}

${escapeHtml(profile.name)}

${escapeHtml(providerDetail)}

${health.label}
${ready ? `` : ''}${state.previousSha && rollbackConfigured ? `` : ''}
`; +} + +function renderRepositoryDeployments(repository) { + const profiles = repository.deploymentProfiles || []; + const repoOps = repositoryOperations(repository).slice(0, 10); + return `

Deployment environments

Exact-commit Gitea Actions or pinned SSH / Unraid deployments
${profiles.length ? `
${profiles.map((profile) => renderProfileCard(repository, profile)).join('')}
` : '

No deployment profile

Connect a Gitea Actions workflow or a trusted SSH / Unraid server.

'}

Release history

${repoOps.length ? `${repoOps.map((operation) => ``).join('')}
ActionEnvironmentCommitStatusUpdated
${escapeHtml(operation.action || 'deploy')}${escapeHtml(operation.environment)}${escapeHtml(operation.shortSha || shortSha(operation.sha))}${escapeHtml(operation.status)}${formatDate(operation.updatedAt || operation.createdAt)}
` : '

No releases for this repository yet.

'}
`; +} + +function renderGitTools(repository) { + if (!repository.localPath) return '

Link a local repository to manage branches and stashes.

'; + return `

Branches

${ui.branches.length ? ui.branches.map((branch) => `
${escapeHtml(branch.name)}${escapeHtml(branch.shortSha)}${branch.upstream ? ` · ${escapeHtml(branch.upstream)}` : ' · unpublished'}
${branch.current ? 'Current' : ``}
`).join('') : '

Load branch information.

'}

Stashes

${ui.stashes.length ? ui.stashes.map((stash) => `
${escapeHtml(stash.ref)}${escapeHtml(stash.subject)} · ${formatDate(stash.date)}
`).join('') : '

No stashes, or Git tools have not been loaded.

'}
`; +} + +function renderRepositorySettings(repository) { + const automaticTarget = displayCloneTarget(repository); + return `

Repository identity

${repository.localPath ? `` : ``}

Repository behavior

${icon('shield')}ForgeFlow automatically creates a repository-named subfolder and never overwrites a non-empty conflicting folder. Existing matching clones are linked instead.
`; +} + +function renderRepositoryWorkspace(repository) { + const status = repository.localStatus; + const profile = selectedProfile(repository); + const serverState = profile?.state || {}; + const localTone = status?.counts.conflicts ? 'danger' : status?.counts.changed ? 'warning' : status ? 'success' : ''; + const remoteTone = status?.branch.behind ? 'danger' : status?.branch.ahead ? 'warning' : status?.branch.upstream ? 'success' : ''; + const serverTone = serverState.healthy === false ? 'danger' : serverState.healthy === true ? 'success' : ''; + const content = ({ changes: renderChanges, history: renderHistory, deployments: renderRepositoryDeployments, gittools: renderGitTools, settings: renderRepositorySettings }[ui.repositoryTab] || renderChanges)(repository); + return `

${escapeHtml(repository.fullName)}

${escapeHtml(repository.localPath || 'No local working tree linked')}

+
${releaseNode('Local', status?.shortHead || 'Not linked', status ? `${status.counts.changed} changes · ${status.branch.head}` : 'No working tree', localTone)}${releaseNode('Gitea', status?.shortHead || 'Unknown', status?.branch.upstream ? `${status.branch.ahead} ahead · ${status.branch.behind} behind` : 'Branch not published', remoteTone)}${releaseNode(`Server${profile ? ` · ${profile.environment}` : ''}`, serverState.liveSha ? shortSha(serverState.liveSha) : 'Unknown', profile ? (serverState.checkedAt ? `checked ${formatDate(serverState.checkedAt)}` : 'not checked') : 'No deployment profile', serverTone)}
+
${content}
`; +} + +function renderActionPanel(repository) { + const action = repositoryAction(repository); + const status = repository.localStatus; + const profile = selectedProfile(repository); + let body = ''; + if (action.kind === 'link') { + const target = displayCloneTarget(repository); + body = `
${icon('link')}

${action.title}

${action.detail}

${target ? `
Project root${escapeHtml(defaultWorkspaceRoot())}New folder${escapeHtml(safeCloneFolderName(repository))}
` : '
No default project root is configured yet.
'}
`; + } + else if (action.kind === 'commit') { + const commitReady = Boolean(ui.selectedFiles.size && ui.commitMessage.trim()); + const commitBlocker = !ui.selectedFiles.size ? 'Select at least one changed file.' : !ui.commitMessage.trim() ? 'Enter a commit message to enable commit and push.' : 'Ready to commit. ForgeFlow stages the selected files automatically.'; + body = `
${ui.selectedFiles.size} of ${status.counts.changed} files selectedCtrl+Enter
${icon(commitReady ? 'check' : 'warning')}${escapeHtml(commitBlocker)}
Manual staging is optional. Use it only to review the Git index before committing.
`; + } + else if (action.kind === 'pull') body = `
${icon('arrowDown')}

${action.title}

${action.detail}

`; + else if (action.kind === 'push') body = `
${icon('arrowUp')}

${action.title}

${action.detail}

`; + else if (action.kind === 'diverged' || action.kind === 'conflict' || action.kind === 'error') body = `
${icon('error')}

${action.title}

${action.detail}

`; + else if (action.kind === 'configure') body = `
${icon('settings')}

${action.title}

${action.detail}

`; + else if (action.kind === 'branch-profile') body = `
${icon('branch')}

${action.title}

${action.detail}

${repository.deploymentProfiles.length > 1 ? `` : ''}
`; + else if (action.kind === 'deploy') body = `
${icon('rocket')}

Release ${escapeHtml(status.shortHead)}

${escapeHtml(profile.name)} will deploy the exact commit from ${escapeHtml(profile.branch)} to ${escapeHtml(profile.environment)}.

${repository.deploymentProfiles.length > 1 ? `` : ''}
Local${escapeHtml(status.shortHead)}Gitea${escapeHtml(status.shortHead)}Target${escapeHtml(profile.environment)}
${profile.state?.previousSha && profile.rollbackWorkflowFile ? `` : ''}
`; + else body = `
${icon('check')}

${action.title}

${action.detail}

${profile ? `` : ''}
`; + return ``; +} + +function renderDeployments() { + const cards = ui.repositories.flatMap((repository) => (repository.deploymentProfiles || []).map((profile) => ({ repository, profile }))); + const active = operations().filter((operation) => !isTerminalOperation(operation.status)); + return `
${active.length ? `
${icon('pulse')} ${active.length} deployment operation${active.length === 1 ? ' is' : 's are'} still active.
` : ''}
${cards.length ? cards.map(({ repository, profile }) => renderProfileCard(repository, profile, true)).join('') : '

No deployment environments configured

Open a repository and add an environment.

'}

All operations

Newest first
${operations().length ? `${operations().map((operation) => ``).join('')}
RepositoryActionEnvironmentCommitStatusUpdated
${escapeHtml(operation.repository)}${escapeHtml(operation.action || 'deploy')}${escapeHtml(operation.environment || '—')}${escapeHtml(operation.shortSha || shortSha(operation.sha))}${escapeHtml(operation.status)}${formatDate(operation.updatedAt || operation.createdAt)}
` : '

No operations recorded.

'}
`; +} + +function renderSettings() { + const state = ui.boot.state; + const prefs = state.preferences || {}; + const update = ui.updateStatus; + const servers = state.servers || []; + return `
+

Gitea connection

${state.gitea.hasToken ? `Connected as ${escapeHtml(state.gitea.user?.login || 'user')}` : 'Not connected'}
${escapeHtml(state.gitea.baseUrl || 'No Gitea instance configured')}
+

ForgeFlow updates

Secure source update from ${escapeHtml(state.updates?.owner || 'Jens')}/${escapeHtml(state.updates?.repo || 'ForgeFlow')}
${icon(update?.available ? 'download' : 'check')}${update ? (update.available ? `ForgeFlow ${escapeHtml(update.remoteVersion)} is available` : `ForgeFlow ${escapeHtml(update.currentVersion)} is up to date`) : `Current version ${escapeHtml(ui.boot.appVersion)}`}${update ? `Branch ${escapeHtml(update.branch)} · commit ${escapeHtml(update.shortSha)} · checked ${formatDate(update.checkedAt)}` : 'No update check in this session.'}
${update?.available && !update.downloaded ? `` : ''}${update?.downloaded ? `` : ''}
${icon('shield')}The updater downloads an authenticated ZIP for the exact remote commit, verifies its SHA-256 checksum, runs the complete quality gate and restores the previous source version if validation fails.
+

SSH / Unraid servers

Credentials are entered locally and encrypted with the Windows credential protection used by Electron.
${servers.length ? `
${servers.map((server) => `
${icon('server')}
${escapeHtml(server.name)}${escapeHtml(server.username)}@${escapeHtml(server.host)}:${escapeHtml(server.port)} · ${escapeHtml(server.basePath)}${server.hostFingerprint ? `Trusted ${escapeHtml(server.hostFingerprint)}` : 'Host identity not trusted yet'}
`).join('')}
` : '

No SSH server configured. Add your Unraid server before creating an SSH deployment profile.

'}
+

Project roots

The first folder is the default clone destination. ForgeFlow automatically creates one subfolder per repository.

${state.workspaceRoots.map((root, index) => `
${index === 0 ? 'Default' : ''}
`).join('')}
+

Background awareness

+

Appearance

+

Danger zone

Reset removes local ForgeFlow configuration, repository links, profiles and operation history. It does not modify Git repositories or Gitea.

+
`; +} + +function preflightTone(status) { + return status === 'pass' ? 'success' : status === 'fail' ? 'danger' : status === 'warning' ? 'warning' : ''; +} + +function renderPreflightChecks(report, emptyMessage = 'Run the preflight to verify this configuration.') { + if (!report?.checks?.length) return `

${escapeHtml(emptyMessage)}

`; + return `
${report.checks.map((item) => `
${item.status === 'pass' ? icon('check') : item.status === 'fail' ? icon('error') : icon('warning')}
${escapeHtml(item.label)}${escapeHtml(item.detail)}${item.help ? `${escapeHtml(item.help)}` : ''}
${escapeHtml(item.status)}
`).join('')}
`; +} + +function renderDiagnostics() { + const prefs = ui.boot.state.preferences || {}; + const status = ui.diagnosticsStatus || ui.boot.diagnostics || {}; + const report = ui.systemPreflight; + return `
+
${icon('shield')}Credentials are never added to the diagnostic bundle. Known runtime secrets are redacted again during export. You can inspect the ZIP before sharing it.
+
+

Log storage

${status.lastWriteError ? 'Write error' : status.enabled ? 'Recording' : 'Disabled'}
Files${escapeHtml(status.fileCount ?? '—')}
Total size${escapeHtml(status.totalSize || '—')}
Latest event${status.latestAt ? formatDate(status.latestAt) : 'None'}
Retention${escapeHtml(status.retentionDays || prefs.logRetentionDays || 14)} days
Location${escapeHtml(status.directory || 'Unavailable')}
Level${escapeHtml(status.level || prefs.diagnosticLevel || 'info')}
${status.lastWriteError ? `
Error${escapeHtml(status.lastWriteError)}
` : ''}
+

Recording policy

+
+

System preflight

Git, writable storage, credential protection, folders and Gitea
${report ? `${report.summary.ready ? 'Ready' : `${report.summary.blocking.length} blocking`}${report.summary.counts.pass} passed · ${report.summary.counts.warning} warnings · ${report.summary.counts.fail} failed` : 'Not run in this session'}
${renderPreflightChecks(report)}
+

Export support bundle

Configuration summary, repository states, operations, preflight and redacted JSONL logs
${ui.lastDiagnosticBundle ? `
${icon('check')}
${escapeHtml(ui.lastDiagnosticBundle.size)} bundle created

SHA-256 ${escapeHtml(ui.lastDiagnosticBundle.sha256)}

` : ''}
+
`; +} + +function renderPipelineView() { + const operation = ui.activeDeployment; + if (!operation) return '

No deployment operation selected.

'; + const logs = (operation.logs || []).join('\n'); + return `

${escapeHtml(operation.status)}

${escapeHtml(operation.profileName || operation.workflowFile || '')} · ${escapeHtml(operation.shortSha || shortSha(operation.sha))}

${escapeHtml(operation.status)}
${(operation.stages || []).map((stage) => `
${stage.status === 'complete' ? icon('check') : stage.status === 'failed' ? icon('error') : stage.status === 'active' ? icon('pulse') : icon('clock')}${escapeHtml(stage.label)}
`).join('')}
${operation.jobs?.length ? `

Runner jobs

${operation.jobs.map((job) => ``).join('')}
JobStatusStartedCompleted
${escapeHtml(job.name)}${escapeHtml(job.conclusion || job.status)}${job.startedAt ? formatDate(job.startedAt) : '—'}${job.completedAt ? formatDate(job.completedAt) : '—'}
` : ''}
Deployment output
${escapeHtml(logs || 'Waiting for operation output…')}
${operation.failure ? `
${icon('error')}
${escapeHtml(operation.failure.stage)}

${escapeHtml(operation.failure.message)}

` : ''}
`; +} + +function renderStatusbar() { + const state = ui.boot?.state; + const repository = selectedRepository(); + const active = operations().filter((operation) => !isTerminalOperation(operation.status)).length; + return `
${icon('git')}${escapeHtml(ui.boot?.git?.version || 'Git unavailable')}${icon('folder')}${state?.workspaceRoots?.length || 0} roots${repository?.localStatus ? `${icon('branch')}${escapeHtml(repository.localStatus.branch.head)}` : ''}
${ui.autoRefreshPending ? `${icon('refresh')}Change detected` : ''}${active ? `${icon('pulse')}${active} active` : ''}ForgeFlow ${escapeHtml(ui.boot?.appVersion || '')}
`; +} + +function renderSetup() { + const steps = ['Readiness', 'Gitea', 'Folders', 'Discovery', 'Ready']; + let body = ''; + if (ui.setupStep === 0) { + body = `

Check this computer

ForgeFlow verifies Git, writable storage and protected credential support before you enter any connection details.

${icon('shield')}Your Gitea token is entered only inside this local desktop application. It is never included in diagnostic logs or support bundles.
${renderPreflightChecks(ui.systemPreflight, 'Run the readiness check to verify this computer.')}
Available even before Gitea is connected.
`; + } else if (ui.setupStep === 1) { + body = `

Connect your Gitea instance

Enter the URL and a personal access token created on your own Gitea server. ForgeFlow validates it locally and stores it using operating-system encryption when available.

${ui.setupValidation ? `
${icon('check')}Connected as ${escapeHtml(ui.setupValidation.user.login)} · ${ui.setupValidation.repositoryCount} repositories · Gitea ${escapeHtml(ui.setupValidation.version || 'version unknown')}
` : `
${icon('shield')}Use the narrowest permissions that allow repository reads and Actions workflow dispatch. The setup guide explains this without requiring you to share the token.
`}
`; + } else if (ui.setupStep === 2) { + body = `

Select development folders

Choose parent folders. ForgeFlow discovers Git working trees below them and matches their origin to Gitea.

${ui.setupDraft.roots.map((root,index) => `
`).join('')}
`; + } else if (ui.setupStep === 3) { + body = `

Discovering repositories

Inspecting local Git metadata. Generated folders and nested dependency trees are skipped.

Scanning configured folders…
`; + } else { + body = `

ForgeFlow is ready

${ui.setupDraft.discovered.length} local repositories were found. You can add deployment environments after opening a repository.

Gitea connected${escapeHtml(ui.setupDraft.baseUrl)} · ${escapeHtml(ui.setupDraft.user?.login || 'user')}
Workspace discovery${ui.setupDraft.roots.length} root folder(s), ${ui.setupDraft.discovered.length} repository/repositories
Safe diagnosticsStructured local logs with credential redaction are enabled by default.
${ui.setupDraft.discovered.length ? ui.setupDraft.discovered.slice(0, 8).map((item) => `
${icon(item.error ? 'error' : 'git')}
${escapeHtml(item.localPath.split(/[\\/]/).pop())}${escapeHtml(item.localPath)}
${item.error ? 'Unreadable' : 'Ready'}
`).join('') : '

No repositories found. You can link or clone repositories later.

'}
`; + } + const nextAction = ui.setupStep === 0 + ? (ui.systemPreflight?.summary?.ready ? '' : '') + : ui.setupStep === 1 ? '' + : ui.setupStep === 2 ? `` + : ui.setupStep === 4 ? '' : ''; + return `
${body}
${nextAction}
`; +} + +function renderModal() { + if (!ui.modal) return ''; + const repository = selectedRepository() || ui.repositories.find((repo) => repo.fullName === ui.modal.repositoryFullName); + if (ui.modal.type === 'deployment-config') { + const existing = repository?.deploymentProfiles?.find((profile) => profile.id === ui.modal.profileId) || {}; + const servers = ui.boot.state.servers || []; + const provider = ui.modal.provider || existing.provider || (servers.length ? 'ssh-unraid' : 'gitea-actions'); + const ssh = provider === 'ssh-unraid'; + const remoteFolder = existing.remoteFolder || safeCloneFolderName(repository); + return ``; + } + if (ui.modal.type === 'deployment-preflight') { + const profile = repository?.deploymentProfiles?.find((item) => item.id === ui.modal.profileId) || selectedProfile(repository); + const report = ui.deploymentPreflight; + return ``; + } + if (ui.modal.type === 'deploy-confirm') { + const profile = repository?.deploymentProfiles?.find((item) => item.id === ui.modal.profileId) || selectedProfile(repository); + return ``; + } + if (ui.modal.type === 'rollback-confirm') { + const profile = repository?.deploymentProfiles?.find((item) => item.id === ui.modal.profileId); + const target = profile?.state?.previousSha; + return ``; + } + if (ui.modal.type === 'server-config') { + const server = (ui.boot.state.servers || []).find((item) => item.id === ui.modal.serverId) || {}; + const authType = ui.modal.authType || server.authType || 'privateKey'; + return ``; + } + if (ui.modal.type === 'command-palette') return renderCommandPalette(); + return ''; +} + +function paletteCommands() { + const repository = selectedRepository(); + return [ + { id: 'overview', label: 'Go to release overview', detail: 'Workspace', icon: 'overview', enabled: true }, + { id: 'refresh', label: 'Refresh all repositories', detail: 'Local and Gitea', icon: 'refresh', enabled: true }, + { id: 'deployments', label: 'Open deployments', detail: 'Release history', icon: 'deploy', enabled: true }, + { id: 'diagnostics', label: 'Open diagnostics', detail: 'Logs, preflight and support bundle', icon: 'shield', enabled: true }, + { id: 'settings', label: 'Open settings', detail: 'Connections and awareness', icon: 'settings', enabled: true }, + { id: 'open-folder', label: 'Open selected project folder', detail: repository?.name || 'No repository selected', icon: 'folder', enabled: Boolean(repository?.localPath) }, + { id: 'git-tools', label: 'Open branch and stash tools', detail: repository?.name || 'No repository selected', icon: 'branch', enabled: Boolean(repository?.localPath) }, + { id: 'deploy-selected', label: 'Deploy selected repository', detail: repository?.readyToDeploy ? `${repository.name} ${repository.localStatus.shortHead}` : 'Not ready', icon: 'rocket', enabled: Boolean(repository?.readyToDeploy) } + ]; +} +function renderCommandPalette() { + const query = ui.paletteQuery.toLowerCase(); + const commands = paletteCommands().filter((command) => !query || `${command.label} ${command.detail}`.toLowerCase().includes(query)); + return ``; +} + +function render() { + if (!ui.boot) return; + const repository = selectedRepository(); + const main = ui.currentView === 'overview' ? renderOverview() : ui.currentView === 'deployments' ? renderDeployments() : ui.currentView === 'settings' ? renderSettings() : ui.currentView === 'diagnostics' ? renderDiagnostics() : ui.currentView === 'deployment-run' ? renderPipelineView() : repository ? renderRepositoryWorkspace(repository) : renderOverview(); + const withPanel = ui.currentView === 'repository' && repository; + app.innerHTML = `
${renderTitlebar()}
${renderSidebar()}
${main}
${withPanel ? renderActionPanel(repository) : ''}${ui.loading ? `
${escapeHtml(ui.loadingMessage || 'Working…')}
` : ''}
${renderStatusbar()}
${ui.boot.state.setupComplete ? '' : renderSetup()}${renderModal()}`; + if (ui.modal?.type === 'command-palette') requestAnimationFrame(() => document.querySelector('#palette-input')?.focus()); +} + +async function runOperation(message, operation, successMessage, { refresh = true } = {}) { + setLoading(true, message); + try { + const result = await operation(); + if (successMessage) showToast('Done', successMessage, 'success'); + if (refresh) await refreshRepositories(false); + return result; + } catch (error) { + showToast(error.code === 'PUSH_AFTER_COMMIT_FAILED' ? 'Commit created; push failed' : 'Operation failed', error.message, 'error'); + if (error.commitSha) await refreshRepositories(false, true); + return null; + } finally { setLoading(false); } +} + +async function executeDeployment(profileId) { + const repository = selectedRepository(); + const profile = repository?.deploymentProfiles?.find((item) => item.id === profileId) || selectedProfile(repository); + if (!repository || !profile) return; + ui.modal = null; + setLoading(true, profile.provider === 'ssh-unraid' ? `Deploying ${repository.name} to ${profile.remoteFolder} over SSH…` : `Dispatching ${profile.name} workflow…`); + try { + ui.activeDeployment = await window.forgeflow.deploy(repository, profile.id, repository.localStatus.head); + updateOperationInState(ui.activeDeployment); + ui.currentView = 'deployment-run'; + showToast('Deployment requested', `${repository.name} ${repository.localStatus.shortHead} → ${profile.environment}`, 'success'); + } catch (error) { showToast('Deployment failed to start', error.message, 'error'); } + setLoading(false); +} + +async function executeRollback(profileId) { + const repository = selectedRepository(); + const profile = repository?.deploymentProfiles?.find((item) => item.id === profileId); + const target = profile?.state?.previousSha; + if (!repository || !profile || !target) return; + ui.modal = null; + setLoading(true, profile?.provider === 'ssh-unraid' ? `Rolling back ${profile.remoteFolder} over SSH…` : `Dispatching rollback to ${shortSha(target)}…`); + try { + ui.activeDeployment = await window.forgeflow.rollback(repository, profile.id, target); + updateOperationInState(ui.activeDeployment); + ui.currentView = 'deployment-run'; + showToast('Rollback requested', `${profile.environment} → ${shortSha(target)}`, 'success'); + } catch (error) { showToast('Rollback failed to start', error.message, 'error'); } + setLoading(false); +} + +async function loadGitTools(repository) { + if (!repository?.localPath) return; + setLoading(true, 'Loading branches and stashes…'); + try { + [ui.branches, ui.stashes] = await Promise.all([window.forgeflow.branches(repository.localPath), window.forgeflow.stashList(repository.localPath)]); + ui.repositoryTab = 'gittools'; + } catch (error) { showToast('Git tools unavailable', error.message, 'error'); } + setLoading(false); +} + +function profileRepository(profileId) { + return ui.repositories.find((repository) => repository.deploymentProfiles?.some((profile) => profile.id === profileId)); +} + +async function runSystemPreflight({ setup = false } = {}) { + setLoading(true, 'Checking local readiness…'); + try { + ui.systemPreflight = await window.forgeflow.setupPreflight({ + baseUrl: ui.setupDraft.baseUrl, + token: ui.setupDraft.token, + roots: setup ? ui.setupDraft.roots : ui.boot.state.workspaceRoots + }); + if (!setup) ui.diagnosticsStatus = await window.forgeflow.diagnosticsStatus(); + showToast( + ui.systemPreflight.summary.ready ? 'Readiness checks passed' : 'Readiness needs attention', + ui.systemPreflight.summary.ready + ? `${ui.systemPreflight.summary.counts.pass} checks passed.` + : `${ui.systemPreflight.summary.blocking.length} blocking check(s) must be resolved.`, + ui.systemPreflight.summary.ready ? 'success' : 'error' + ); + return ui.systemPreflight; + } catch (error) { + showToast('Readiness check failed', error.message, 'error'); + return null; + } finally { setLoading(false); } +} + +async function runDeploymentPreflight(repository, profileId, { showModal = true } = {}) { + if (!repository || !profileId) return null; + if (String(repository.id) !== String(ui.selectedRepoId)) selectRepository(repository.id, false); + ui.selectedProfileId = profileId; + ui.deploymentPreflight = null; + setLoading(true, 'Verifying repository, workflow and server…'); + try { + const report = await window.forgeflow.deploymentPreflight(repository, profileId); + ui.deploymentPreflight = report; + if (showModal) ui.modal = { type: 'deployment-preflight', profileId, repositoryFullName: repository.fullName }; + return report; + } catch (error) { + showToast('Deployment preflight failed', error.message, 'error'); + return null; + } finally { setLoading(false); } +} + +app.addEventListener('click', async (event) => { + const target = event.target.closest('[data-action]'); + if (!target) return; + const action = target.dataset.action; + let repository = selectedRepository(); + if (target.dataset.repositoryId) { + const actionRepository = ui.repositories.find((item) => String(item.id) === String(target.dataset.repositoryId)); + if (actionRepository) repository = actionRepository; + } + + if (action === 'navigate') { ui.currentView = target.dataset.view; ui.modal = null; render(); } + else if (action === 'select-repo') selectRepository(target.dataset.id); + else if (action === 'refresh') await refreshRepositories(true); + else if (action === 'refresh-operations') { setLoading(true, 'Refreshing Gitea Actions runs…'); await refreshActiveOperations(); setLoading(false); } + else if (action === 'toggle-theme') { const appearance = document.documentElement.dataset.theme === 'dark' ? 'light' : 'dark'; applyTheme(appearance); ui.boot.state = await window.forgeflow.setAppearance(appearance); render(); } + else if (action === 'open-palette') { ui.paletteQuery = ''; ui.modal = { type: 'command-palette' }; render(); } + else if (action === 'repo-tab') { + ui.repositoryTab = target.dataset.tab; + if (ui.repositoryTab === 'gittools' && !ui.branches.length) await loadGitTools(repository); else render(); + } + else if (action === 'toggle-favorite') { ui.boot.state = await window.forgeflow.favoriteRepository(repository.fullName, !repository.favorite); repository.favorite = !repository.favorite; render(); } + else if (action === 'select-file') { if (event.target.matches('input[type=checkbox]')) return; ui.selectedFile = target.dataset.path; await loadDiff(repository, ui.selectedFile); } + else if (action === 'toggle-all-files') { const files = repository.localStatus?.files || []; ui.selectedFiles = ui.selectedFiles.size === files.length ? new Set() : new Set(files.map((file) => file.path)); render(); } + else if (action === 'copy-diff') { await navigator.clipboard.writeText(ui.diff || ''); showToast('Copied', 'Diff copied to clipboard.', 'success'); } + else if (action === 'copy-logs') { const text = (ui.activeDeployment?.logs || []).join('\n'); await navigator.clipboard.writeText(text); showToast('Copied', 'Safe operation output copied. Open Gitea for raw runner logs.', 'success'); } + else if (action === 'stage-selected') { if (!repository?.localPath || !ui.selectedFiles.size) return; await runOperation('Staging selected files…', () => window.forgeflow.stageFiles(repository.localPath, [...ui.selectedFiles]), 'Files staged.'); } + else if (action === 'unstage-selected') { if (!repository?.localPath || !ui.selectedFiles.size) return; await runOperation('Unstaging selected files…', () => window.forgeflow.unstageFiles(repository.localPath, [...ui.selectedFiles]), 'Files unstaged.'); } + else if (action === 'commit-push' || action === 'commit-only') { + if (!repository?.localPath || !ui.commitMessage.trim() || !ui.selectedFiles.size) return; + const result = await runOperation(action === 'commit-push' ? 'Committing and pushing…' : 'Creating local commit…', () => action === 'commit-push' ? window.forgeflow.commitAndPush(repository.localPath, ui.commitMessage, [...ui.selectedFiles]) : window.forgeflow.commit(repository.localPath, ui.commitMessage, [...ui.selectedFiles]), action === 'commit-push' ? 'Changes committed and pushed to Gitea.' : 'Local commit created.'); + if (result) { ui.commitMessage = ''; ui.selectedFiles.clear(); ui.selectedFile = null; ui.diff = ''; } + } + else if (action === 'push') await runOperation('Pushing local commits…', () => window.forgeflow.push(repository.localPath), 'Push completed.'); + else if (action === 'fetch') await runOperation('Fetching from Gitea…', () => window.forgeflow.fetch(repository.localPath), 'Remote state refreshed.'); + else if (action === 'pull') await runOperation('Synchronizing from Gitea…', () => window.forgeflow.pull(repository.localPath), 'Local branch fast-forwarded.'); + else if (action === 'load-history') { setLoading(true, 'Loading commit history…'); try { ui.history = await window.forgeflow.history(repository.localPath, 50); } catch (error) { showToast('History unavailable', error.message, 'error'); } setLoading(false); } + else if (action === 'load-git-tools') await loadGitTools(repository); + else if (action === 'create-branch') { const branch = document.querySelector('#new-branch-name')?.value.trim(); if (branch) await runOperation(`Creating ${branch}…`, () => window.forgeflow.createBranch(repository.localPath, branch), `Switched to ${branch}.`); await loadGitTools(selectedRepository()); } + else if (action === 'checkout-branch') { await runOperation(`Switching to ${target.dataset.branch}…`, () => window.forgeflow.checkoutBranch(repository.localPath, target.dataset.branch), `Switched to ${target.dataset.branch}.`); await loadGitTools(selectedRepository()); } + else if (action === 'stash-changes') { const result = await runOperation('Stashing local changes…', () => window.forgeflow.stash(repository.localPath, `ForgeFlow ${new Date().toLocaleString()}`), 'Local changes stashed.'); if (result) ui.stashes = result.stashes; } + else if (action === 'pop-stash') { const result = await runOperation(`Applying ${target.dataset.stashRef}…`, () => window.forgeflow.popStash(repository.localPath, target.dataset.stashRef), 'Stash applied.'); if (result) ui.stashes = result.stashes; } + else if (action === 'open-path') await window.forgeflow.openPath(repository.localPath).catch((error) => showToast('Could not open folder', error.message, 'error')); + else if (action === 'open-gitea') await window.forgeflow.openExternal(repository.htmlUrl).catch((error) => showToast('Could not open Gitea', error.message, 'error')); + else if (action === 'link-repo') { const localPath = await window.forgeflow.selectDirectory({ title: `Link local folder for ${repository.name}` }); if (localPath) { ui.repositories = await runOperation('Linking local repository…', () => window.forgeflow.linkRepository(repository.fullName, localPath), 'Local folder linked.', { refresh: false }) || ui.repositories; selectRepository(repository.id); } } + else if (action === 'unlink-repo') { ui.repositories = await runOperation('Removing local link…', () => window.forgeflow.unlinkRepository(repository.fullName), 'Repository link removed.', { refresh: false }) || ui.repositories; selectRepository(repository.id); } + else if (action === 'clone-repo' || action === 'clone-repo-custom') { + const mode = action === 'clone-repo-custom' ? 'custom' : 'default'; + const clone = await runOperation( + mode === 'custom' ? 'Choosing location and cloning repository…' : `Cloning ${repository.name} into the default project root…`, + () => window.forgeflow.cloneRepository(repository.fullName, mode), + null, + { refresh: false } + ); + if (clone?.cancelled) return; + if (clone?.target) { + if (clone.state) ui.boot.state = clone.state; + ui.repositories = clone.repositories || await window.forgeflow.refreshRepositories(); + selectRepository(repository.id); + showToast(clone.reused ? 'Existing repository linked' : 'Repository cloned', clone.target, 'success'); + } + } + else if (action === 'configure-deployment') { ui.modal = { type: 'deployment-config', profileId: null, provider: (ui.boot.state.servers || []).length ? 'ssh-unraid' : 'gitea-actions' }; render(); } + else if (action === 'edit-deployment-profile') { if (!repository) repository = profileRepository(target.dataset.profileId); if (repository && String(repository.id) !== String(ui.selectedRepoId)) selectRepository(repository.id, false); ui.modal = { type: 'deployment-config', profileId: target.dataset.profileId || null, provider: repository?.deploymentProfiles?.find((item) => item.id === target.dataset.profileId)?.provider }; render(); } + else if (action === 'close-modal') { ui.modal = null; render(); } + else if (action === 'save-deployment-profile') { + const provider = document.querySelector('#profile-provider').value; + const profile = { + id: target.dataset.profileId || undefined, + provider, + name: document.querySelector('#profile-name').value.trim(), + environment: document.querySelector('#profile-environment').value.trim(), + branch: document.querySelector('#profile-branch').value.trim(), + healthcheckUrl: document.querySelector('#profile-healthcheck')?.value.trim() || '', + confirmationRequired: document.querySelector('#profile-confirmation').checked, + ...(provider === 'ssh-unraid' ? { + serverId: document.querySelector('#profile-server').value, + remoteFolder: document.querySelector('#profile-remote-folder').value.trim(), + cloneUrl: document.querySelector('#profile-clone-url').value.trim(), + alignRemote: document.querySelector('#profile-align-remote').checked, + generatedCompose: document.querySelector('#profile-generated-compose').value === 'true', + composeFile: document.querySelector('#profile-compose-file').value.trim(), + composeService: document.querySelector('#profile-compose-service').value.trim(), + hostPort: Number(document.querySelector('#profile-host-port').value) || null, + containerPort: Number(document.querySelector('#profile-container-port').value) || null, + webUiUrl: document.querySelector('#profile-web-ui').value.trim(), + iconUrl: document.querySelector('#profile-icon-url').value.trim(), + preservePaths: document.querySelector('#profile-preserve-paths').value.split(',').map((item) => item.trim()).filter(Boolean) + } : { + workflowFile: document.querySelector('#profile-workflow').value.trim(), + rollbackWorkflowFile: document.querySelector('#profile-rollback-workflow').value.trim(), + statusUrl: document.querySelector('#profile-status-url').value.trim() + }) + }; + setLoading(true, 'Saving deployment environment…'); + try { const result = await window.forgeflow.saveDeploymentProfile(repository.fullName, profile); ui.boot.state = result.state; ui.modal = null; await refreshRepositories(false); ui.selectedProfileId = result.profile.id; showToast('Deployment configured', `${profile.name} targets ${profile.environment}.`, 'success'); } catch (error) { showToast('Could not save profile', error.message, 'error'); } + setLoading(false); + } + else if (action === 'delete-deployment-profile') { if (!confirm('Delete this deployment profile? Operation history is retained.')) return; setLoading(true, 'Deleting deployment profile…'); try { const result = await window.forgeflow.deleteDeploymentProfile(repository.fullName, target.dataset.profileId); ui.boot.state = result.state; ui.modal = null; await refreshRepositories(false); showToast('Profile deleted', 'Deployment environment removed.', 'success'); } catch (error) { showToast('Could not delete profile', error.message, 'error'); } setLoading(false); } + else if (action === 'run-deployment-preflight') { + if (!repository) repository = profileRepository(target.dataset.profileId); + await runDeploymentPreflight(repository, target.dataset.profileId); + } + else if (action === 'deploy-profile') { + if (repository && String(repository.id) !== String(ui.selectedRepoId)) selectRepository(repository.id, false); + const profile = repository?.deploymentProfiles?.find((item) => item.id === target.dataset.profileId) || selectedProfile(repository); + ui.selectedProfileId = profile?.id || null; + if (!profile) return; + const report = await runDeploymentPreflight(repository, profile.id, { showModal: true }); + if (!report) return; + } + else if (action === 'continue-after-preflight') { + const profile = selectedRepository()?.deploymentProfiles?.find((item) => item.id === target.dataset.profileId); + if (!profile || !ui.deploymentPreflight?.summary?.ready) return; + if (profile.confirmationRequired !== false) { ui.modal = { type: 'deploy-confirm', profileId: profile.id }; render(); } + else await executeDeployment(profile.id); + } + else if (action === 'confirm-deploy') await executeDeployment(target.dataset.profileId); + else if (action === 'rollback-profile') { if (!repository) repository = profileRepository(target.dataset.profileId); if (repository && String(repository.id) !== String(ui.selectedRepoId)) selectRepository(repository.id, false); ui.modal = { type: 'rollback-confirm', profileId: target.dataset.profileId }; render(); } + else if (action === 'confirm-rollback') await executeRollback(target.dataset.profileId); + else if (action === 'refresh-profile-state') { + if (!repository) repository = profileRepository(target.dataset.profileId); + const profile = repository?.deploymentProfiles?.find((item) => item.id === target.dataset.profileId); + setLoading(true, `Checking ${profile?.environment || 'environment'}…`); + try { const state = await window.forgeflow.refreshProfileState(repository.fullName, target.dataset.profileId); profile.state = state; showToast('Environment checked', state.healthy === false ? 'Healthcheck reports an unhealthy state.' : state.liveSha ? `Server reports ${shortSha(state.liveSha)}.` : 'Connection checked; no live SHA reported.', state.healthy === false ? 'error' : 'success'); } catch (error) { showToast('Status check failed', error.message, 'error'); } + setLoading(false); + } + else if (action === 'open-operation') { const operation = await window.forgeflow.getOperation(target.dataset.operationId); if (operation) { ui.activeDeployment = operation; ui.currentView = 'deployment-run'; render(); } } + else if (action === 'refresh-current-operation') { setLoading(true, 'Refreshing Actions run…'); try { const operation = await window.forgeflow.refreshOperations(ui.activeDeployment.id); updateOperationInState(operation); } catch (error) { showToast('Status refresh failed', error.message, 'error'); } setLoading(false); } + else if (action === 'open-run-url') await window.forgeflow.openExternal(ui.activeDeployment.runUrl); + else if (action === 'close-deployment') { ui.activeDeployment = null; ui.currentView = selectedRepository() ? 'repository' : 'deployments'; render(); } + else if (action === 'setup-run-preflight') await runSystemPreflight({ setup: true }); + else if (action === 'setup-continue') { if (ui.systemPreflight?.summary?.ready) { ui.setupStep = 1; render(); } } + else if (action === 'setup-validate') { setLoading(true, 'Validating Gitea connection…'); try { ui.setupValidation = await window.forgeflow.validateGitea(ui.setupDraft); ui.setupDraft.baseUrl = ui.setupValidation.baseUrl; ui.setupDraft.user = ui.setupValidation.user; ui.setupStep = 2; } catch (error) { showToast('Connection failed', error.message, 'error'); } setLoading(false); } + else if (action === 'setup-add-root') { const root = await window.forgeflow.selectDirectory({ title: 'Select a development folder' }); if (root && !ui.setupDraft.roots.includes(root)) ui.setupDraft.roots.push(root); render(); } + else if (action === 'setup-remove-root') { ui.setupDraft.roots.splice(Number(target.dataset.index), 1); render(); } + else if (action === 'setup-next') { if (ui.setupStep === 2) { ui.setupStep = 3; ui.setupDraft.discovered = []; render(); try { ui.setupDraft.discovered = await window.forgeflow.discoverRepositories(ui.setupDraft.roots); } catch (error) { showToast('Discovery failed', error.message, 'error'); } ui.setupStep = 4; render(); } } + else if (action === 'setup-back') { ui.setupStep = Math.max(0, ui.setupStep - 1); render(); } + else if (action === 'setup-finish') { setLoading(true, 'Saving configuration…'); try { const result = await window.forgeflow.completeSetup({ baseUrl: ui.setupDraft.baseUrl, token: ui.setupDraft.token, user: ui.setupDraft.user, workspaceRoots: ui.setupDraft.roots }); ui.boot.state = result.state; await refreshRepositories(false); showToast('Setup complete', result.tokenState.persistent ? 'Your token is stored securely.' : 'Your token is available for this session only.', 'success'); } catch (error) { showToast('Could not complete setup', error.message, 'error'); } setLoading(false); } + else if (action === 'check-updates') { + ui.updateChecking = true; render(); + try { ui.updateStatus = await window.forgeflow.checkForUpdates(); showToast(ui.updateStatus.available ? 'Update available' : 'ForgeFlow is current', ui.updateStatus.available ? `Version ${ui.updateStatus.remoteVersion} can be downloaded.` : `Version ${ui.updateStatus.currentVersion} is the newest release.`, ui.updateStatus.available ? 'success' : 'info'); } + catch (error) { showToast('Update check failed', error.message, 'error'); } + ui.updateChecking = false; render(); + } + else if (action === 'save-update-settings') { + const updates = { + owner: document.querySelector('#update-owner').value.trim(), + repo: document.querySelector('#update-repo').value.trim(), + branch: document.querySelector('#update-branch').value.trim(), + autoCheck: document.querySelector('#update-auto-check').value === 'true' + }; + try { ui.boot.state = await window.forgeflow.setUpdatePreferences(updates); ui.updateStatus = null; showToast('Update settings saved', 'The next check will use this repository and branch.', 'success'); } + catch (error) { showToast('Could not save update settings', error.message, 'error'); } + render(); + } + else if (action === 'download-update') { + setLoading(true, 'Downloading and verifying the exact ForgeFlow update…'); + try { ui.updateStatus = await window.forgeflow.downloadUpdate(); showToast('Update downloaded', `Version ${ui.updateStatus.remoteVersion} passed the archive check.`, 'success'); } + catch (error) { showToast('Update download failed', error.message, 'error'); } + setLoading(false); + } + else if (action === 'apply-update') { + if (!confirm(`Apply ForgeFlow ${ui.updateStatus?.remoteVersion || 'update'} now? ForgeFlow closes, validates the update and restarts automatically.`)) return; + setLoading(true, 'Launching safe updater…'); + try { await window.forgeflow.applyUpdate(); showToast('Update launched', 'ForgeFlow will close and restart after validation.', 'success'); } + catch (error) { showToast('Could not launch update', error.message, 'error'); setLoading(false); } + } + else if (action === 'open-add-server') { ui.modal = { type: 'server-config', serverId: null, authType: 'privateKey' }; render(); } + else if (action === 'edit-server') { const server = (ui.boot.state.servers || []).find((item) => item.id === target.dataset.serverId); ui.modal = { type: 'server-config', serverId: target.dataset.serverId, authType: server?.authType || 'privateKey' }; render(); } + else if (action === 'select-private-key') { + const keyPath = await window.forgeflow.selectKeyFile({ title: 'Select SSH private key', defaultPath: document.querySelector('#server-private-key')?.value || undefined }); + if (keyPath) document.querySelector('#server-private-key').value = keyPath; + } + else if (action === 'save-server') { + const authType = document.querySelector('#server-auth-type').value; + const server = { + id: target.dataset.serverId || undefined, + name: document.querySelector('#server-name').value.trim(), + host: document.querySelector('#server-host').value.trim(), + port: Number(document.querySelector('#server-port').value), + username: document.querySelector('#server-username').value.trim(), + authType, + basePath: document.querySelector('#server-base-path').value.trim(), + privateKeyPath: document.querySelector('#server-private-key')?.value.trim() || '', + hostFingerprint: document.querySelector('#server-fingerprint').value.trim() + }; + setLoading(true, 'Saving encrypted SSH configuration…'); + try { + const result = await window.forgeflow.saveServer(server, document.querySelector('#server-password')?.value || '', document.querySelector('#server-passphrase')?.value || ''); + ui.boot.state = result.state; ui.modal = null; showToast('Server saved', 'Run Test & trust before creating a deployment.', 'success'); + } catch (error) { showToast('Could not save server', error.message, 'error'); } + setLoading(false); + } + else if (action === 'test-server') { + setLoading(true, 'Connecting to Unraid and checking Git and Docker Compose…'); + try { const result = await window.forgeflow.testServer(target.dataset.serverId); ui.boot.state = result.state; showToast('SSH server ready', `${result.server.name} presented ${result.fingerprint}.`, 'success'); } + catch (error) { showToast('SSH test failed', error.message, 'error'); } + setLoading(false); + } + else if (action === 'delete-server') { + if (!confirm('Delete this server and all deployment profiles linked to it?')) return; + try { ui.boot.state = await window.forgeflow.deleteServer(target.dataset.serverId); ui.modal = null; await refreshRepositories(false); showToast('Server deleted', 'Linked SSH deployment profiles were removed.', 'success'); } + catch (error) { showToast('Could not delete server', error.message, 'error'); } + } + else if (action === 'add-root') { const root = await window.forgeflow.selectDirectory({ title: 'Add development folder' }); if (root && !ui.boot.state.workspaceRoots.includes(root)) ui.boot.state.workspaceRoots.push(root); render(); } + else if (action === 'remove-root') { ui.boot.state.workspaceRoots.splice(Number(target.dataset.index), 1); render(); } + else if (action === 'save-roots') { const roots = [...document.querySelectorAll('[data-root-index]')].map((input) => input.value.trim()).filter(Boolean); setLoading(true, 'Saving workspace folders…'); try { ui.boot.state = await window.forgeflow.setWorkspaceRoots(roots); await refreshRepositories(false); showToast('Folders saved', 'Repository discovery has been refreshed.', 'success'); } catch (error) { showToast('Could not save folders', error.message, 'error'); } setLoading(false); } + else if (action === 'save-gitea-settings') { const baseUrl = document.querySelector('#settings-gitea-url').value.trim(); const token = document.querySelector('#settings-gitea-token').value.trim(); setLoading(true, 'Validating Gitea…'); try { const result = await window.forgeflow.updateGitea({ baseUrl, token }); ui.boot.state = result.state; await refreshRepositories(false); showToast('Gitea connected', `Signed in as ${result.validation.user.login}.`, 'success'); } catch (error) { showToast('Connection failed', error.message, 'error'); } setLoading(false); } + else if (action === 'save-preferences') { const preferences = { autoRefresh: document.querySelector('#pref-auto-refresh').value === 'true', repositoryPollSeconds: Number(document.querySelector('#pref-repo-poll').value), operationPollSeconds: Number(document.querySelector('#pref-operation-poll').value), preferredCloneProtocol: document.querySelector('#pref-clone-protocol').value }; setLoading(true, 'Saving background settings…'); try { ui.boot.state = await window.forgeflow.setPreferences(preferences); await refreshRepositories(false); showToast('Settings saved', 'Background awareness has been updated.', 'success'); } catch (error) { showToast('Could not save settings', error.message, 'error'); } setLoading(false); } + else if (action === 'run-system-preflight') await runSystemPreflight(); + else if (action === 'save-diagnostics-preferences') { + const preferences = { + diagnosticsEnabled: document.querySelector('#diagnostics-enabled').value === 'true', + diagnosticLevel: document.querySelector('#diagnostic-level').value, + logRetentionDays: Number(document.querySelector('#diagnostic-retention').value), + maxLogFileMb: Number(document.querySelector('#diagnostic-max-file').value) + }; + setLoading(true, 'Saving diagnostic policy…'); + try { + ui.boot.state = await window.forgeflow.setPreferences(preferences); + ui.diagnosticsStatus = await window.forgeflow.diagnosticsStatus(); + showToast('Diagnostic policy saved', 'New events now use the updated retention and logging level.', 'success'); + } catch (error) { showToast('Could not save diagnostics', error.message, 'error'); } + setLoading(false); + } + else if (action === 'export-diagnostics') { + const privacyMode = document.querySelector('#diagnostic-privacy')?.value || 'standard'; + setLoading(true, 'Creating redacted diagnostic bundle…'); + try { + const bundle = await window.forgeflow.exportDiagnostics(privacyMode); + if (bundle) { + ui.lastDiagnosticBundle = bundle; + ui.diagnosticsStatus = await window.forgeflow.diagnosticsStatus(); + showToast('Diagnostic bundle created', `${bundle.size} · SHA-256 ${shortSha(bundle.sha256)}`, 'success'); + } + } catch (error) { showToast('Could not export diagnostics', error.message, 'error'); } + setLoading(false); + } + else if (action === 'show-diagnostic-bundle') { + if (!ui.lastDiagnosticBundle?.path) return; + await window.forgeflow.showDiagnosticBundle(ui.lastDiagnosticBundle.path).catch((error) => showToast('Could not show bundle', error.message, 'error')); + } + else if (action === 'open-diagnostics-folder') await window.forgeflow.openDiagnosticsFolder().catch((error) => showToast('Could not open diagnostic folder', error.message, 'error')); + else if (action === 'clear-diagnostics') { + if (!confirm('Clear local ForgeFlow diagnostic logs? This does not affect repositories or configuration.')) return; + try { ui.diagnosticsStatus = await window.forgeflow.clearDiagnostics(); showToast('Diagnostic logs cleared', 'A new session marker was created.', 'success'); render(); } + catch (error) { showToast('Could not clear logs', error.message, 'error'); } + } + else if (action === 'reset-app') { if (!confirm('Reset ForgeFlow configuration? Your Git repositories and Gitea data are not modified.')) return; ui.boot.state = await window.forgeflow.reset(); ui.repositories = []; ui.setupStep = 0; ui.setupValidation = null; ui.systemPreflight = null; ui.deploymentPreflight = null; ui.lastDiagnosticBundle = null; ui.setupDraft = { baseUrl: 'https://', token: '', user: null, roots: [], discovered: [] }; render(); } + else if (action === 'run-command') { + const command = target.dataset.command; ui.modal = null; + if (command === 'overview') ui.currentView = 'overview'; + else if (command === 'deployments') ui.currentView = 'deployments'; + else if (command === 'diagnostics') ui.currentView = 'diagnostics'; + else if (command === 'settings') ui.currentView = 'settings'; + else if (command === 'refresh') await refreshRepositories(true); + else if (command === 'open-folder' && repository?.localPath) await window.forgeflow.openPath(repository.localPath); + else if (command === 'git-tools' && repository) await loadGitTools(repository); + else if (command === 'deploy-selected' && repository?.readyToDeploy) { const profile = selectedProfile(repository); if (profile) await runDeploymentPreflight(repository, profile.id); } + render(); + } +}); + +app.addEventListener('input', (event) => { + if (event.target.id === 'global-search') { ui.search = event.target.value; render(); document.querySelector('#global-search')?.focus(); } + else if (event.target.id === 'repo-filter') { ui.repoSearch = event.target.value; render(); document.querySelector('#repo-filter')?.focus(); } + else if (event.target.id === 'commit-message') { ui.commitMessage = event.target.value; const position = event.target.selectionStart; render(); const next = document.querySelector('#commit-message'); if (next) { next.focus(); next.setSelectionRange(position, position); } } + else if (event.target.id === 'setup-url') ui.setupDraft.baseUrl = event.target.value; + else if (event.target.id === 'setup-token') ui.setupDraft.token = event.target.value; + else if (event.target.id === 'palette-input') { ui.paletteQuery = event.target.value; render(); } +}); + +app.addEventListener('change', async (event) => { + if (event.target.matches('[data-file-select]')) { const filePath = event.target.dataset.fileSelect; if (event.target.checked) ui.selectedFiles.add(filePath); else ui.selectedFiles.delete(filePath); render(); } + else if (event.target.id === 'appearance-select') { ui.boot.state = await window.forgeflow.setAppearance(event.target.value); applyTheme(event.target.value); render(); } + else if (event.target.id === 'action-profile-select') { ui.selectedProfileId = event.target.value; render(); } + else if (event.target.id === 'profile-provider') { ui.modal.provider = event.target.value; render(); } + else if (event.target.id === 'server-auth-type') { ui.modal.authType = event.target.value; render(); } +}); + +document.addEventListener('keydown', (event) => { + if ((event.key === 'Enter' || event.key === ' ') && event.target.matches('.file-row[data-action="select-file"]')) { event.preventDefault(); event.target.click(); return; } + if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'k') { event.preventDefault(); ui.paletteQuery = ''; ui.modal = { type: 'command-palette' }; render(); return; } + if ((event.ctrlKey || event.metaKey) && event.key === 'Enter' && ui.currentView === 'repository') { const button = document.querySelector('[data-action="commit-push"]:not(:disabled)'); if (button) button.click(); } + if (event.key === 'F5') { event.preventDefault(); refreshRepositories(true); } + if (event.key === 'Escape' && ui.modal) { ui.modal = null; render(); } +}); + +window.addEventListener('error', (event) => { + window.forgeflow.reportRendererEvent?.('error', 'uncaught-error', { + message: event.message, + filename: event.filename, + line: event.lineno, + column: event.colno, + stack: event.error?.stack + }).catch(() => {}); +}); + +window.addEventListener('unhandledrejection', (event) => { + const reason = event.reason; + window.forgeflow.reportRendererEvent?.('error', 'unhandled-rejection', { + message: reason?.message || String(reason || 'Unknown rejection'), + stack: reason?.stack + }).catch(() => {}); +}); + +bootstrap(); diff --git a/src/renderer/assets/itworx-mark.png b/src/renderer/assets/itworx-mark.png new file mode 100644 index 0000000000000000000000000000000000000000..4ae65f552f227de7204272d618e6e0d0100af898 GIT binary patch literal 31424 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 +
+ + + + diff --git a/src/renderer/mock-bridge.js b/src/renderer/mock-bridge.js new file mode 100644 index 0000000..c3ef7b6 --- /dev/null +++ b/src/renderer/mock-bridge.js @@ -0,0 +1,350 @@ +(() => { + if (window.forgeflow) return; + + const wait = (ms = 180) => new Promise((resolve) => setTimeout(resolve, ms)); + const clone = (value) => JSON.parse(JSON.stringify(value)); + const iso = (offset = 0) => new Date(Date.now() + offset).toISOString(); + const storage = { + get(key) { try { return localStorage.getItem(key); } catch { return null; } }, + set(key, value) { try { localStorage.setItem(key, value); } catch {} } + }; + const repositoryListeners = new Set(); + const operationListeners = new Set(); + const updateListeners = new Set(); + const emitRepositories = () => repositoryListeners.forEach((listener) => listener({ reason: 'demo-change' })); + const emitOperations = (operations) => operationListeners.forEach((listener) => listener({ operations: clone(operations) })); + const randomSha = () => `${Math.random().toString(16).slice(2)}${Date.now().toString(16)}`.padEnd(40, 'a').slice(0, 40); + + const makeStatus = ({ head, branch = 'main', ahead = 0, behind = 0, upstream = `origin/${branch}`, files = [] }) => ({ + branch: { oid: head, head: branch, upstream, ahead, behind }, + files, + counts: { + changed: files.length, + staged: files.filter((item) => item.staged).length, + unstaged: files.filter((item) => item.unstaged).length, + conflicts: files.filter((item) => item.conflict).length, + untracked: files.filter((item) => item.untracked).length + }, + clean: files.length === 0, + root: '', + remoteUrl: '', + head, + shortHead: head.slice(0, 7), + fingerprint: `${head}:${branch}:${ahead}:${behind}:${files.map((item) => `${item.path}:${item.indexCode}${item.worktreeCode}`).join('|')}` + }); + + const makeFile = (path, status = 'modified', options = {}) => ({ + path, + originalPath: options.originalPath || null, + indexCode: options.staged ? (status === 'added' ? 'A' : status === 'deleted' ? 'D' : 'M') : '.', + worktreeCode: options.staged ? '.' : status === 'untracked' ? '?' : status === 'deleted' ? 'D' : status === 'conflict' ? 'U' : 'M', + staged: Boolean(options.staged), + unstaged: !options.staged, + untracked: status === 'untracked', + conflict: status === 'conflict', + status + }); + + const profile = (id, name, environment, options = {}) => ({ + id, + name, + environment, + provider: 'gitea-actions', + branch: options.branch || 'main', + workflowFile: options.workflowFile || 'deploy.yml', + rollbackWorkflowFile: options.rollbackWorkflowFile ?? 'rollback.yml', + healthcheckUrl: options.healthcheckUrl || `https://${environment}.internal/health`, + statusUrl: options.statusUrl || `https://${environment}.internal/.well-known/forgeflow`, + confirmationRequired: options.confirmationRequired !== false, + inputs: {}, + state: { + liveSha: options.liveSha || null, + previousSha: options.previousSha || null, + healthy: options.healthy ?? null, + healthConfigured: true, + statusConfigured: true, + healthStatus: options.healthy === false ? 503 : 200, + healthLatencyMs: 42, + checkedAt: options.checkedAt || iso(-120000) + } + }); + + const now = iso(); + const defaultPreferences = { + autoRefresh: true, + repositoryPollSeconds: 4, + operationPollSeconds: 5, + fetchIntervalMinutes: 10, + preferredCloneProtocol: 'https', + diagnosticsEnabled: true, + diagnosticLevel: 'info', + logRetentionDays: 14, + maxLogFileMb: 8 + }; + + let state = { + schemaVersion: 5, + setupComplete: storage.get('forgeflow-demo-setup') !== 'false', + appearance: storage.get('forgeflow-theme') || 'dark', + gitea: { baseUrl: 'https://gitea.internal', user: { login: 'jens', full_name: 'Jens' }, hasToken: true }, + workspaceRoots: ['C:\\Development'], + repositoryMappings: {}, + deploymentProfiles: {}, + deploymentStates: {}, + favorites: ['jens/microsoft-cloud-operations-platform', 'jens/unraid-appops-gateway'], + updates: { owner: 'Jens', repo: 'ForgeFlow', branch: 'main', autoCheck: true, lastCheckedAt: null }, + servers: [{ id: 'server-unraid', name: 'Unraid', host: '192.168.1.10', port: 22, username: 'root', authType: 'privateKey', basePath: '/mnt/user/appdata', privateKeyPath: 'C:\\Users\\Jens\\.ssh\\id_ed25519', hostFingerprint: 'SHA256:demo', hasPassword: false, hasPassphrase: false }], + preferences: { ...defaultPreferences }, + operations: [ + { + id: 'op-success', type: 'deployment', action: 'deploy', status: 'success', + repository: 'jens/microsoft-cloud-operations-platform', profileId: 'profile-mcop-prod', profileName: 'Production', + environment: 'production', workflowFile: 'deploy.yml', branch: 'main', + sha: 'b82f91ab0173cd4346ca0f0f7dcc3e8182cc8fd0', shortSha: 'b82f91a', createdAt: now, updatedAt: now, + stages: [ + { id: 'requested', label: 'Requested', status: 'complete' }, { id: 'verified', label: 'Verified', status: 'complete' }, + { id: 'queued', label: 'Workflow queued', status: 'complete' }, { id: 'runner', label: 'Runner execution', status: 'complete' }, + { id: 'healthcheck', label: 'Healthcheck', status: 'complete' }, { id: 'complete', label: 'Complete', status: 'complete' } + ], + logs: ['[info] Exact commit verified.', '[job] deploy: success', '[ok] Server reports b82f91a and healthcheck returned 200.'], + run: { id: 48, runNumber: 48, status: 'completed', conclusion: 'success', name: 'ForgeFlow deployment' }, + runUrl: 'https://gitea.internal/jens/microsoft-cloud-operations-platform/actions/runs/48' + }, + { + id: 'op-failed', type: 'deployment', action: 'deploy', status: 'failed', + repository: 'jens/portfolio', profileId: 'profile-portfolio', profileName: 'Production', environment: 'production', + workflowFile: 'deploy.yml', branch: 'main', sha: 'a7f2e1c1bb6147fc8b6633d2b08500c93402a719', shortSha: 'a7f2e1c', + createdAt: iso(-86400000), updatedAt: iso(-86300000), + failure: { stage: 'healthcheck', message: 'Healthcheck returned 502.' }, + stages: [ + { id: 'requested', label: 'Requested', status: 'complete' }, { id: 'verified', label: 'Verified', status: 'complete' }, + { id: 'queued', label: 'Workflow queued', status: 'complete' }, { id: 'runner', label: 'Runner execution', status: 'complete' }, + { id: 'healthcheck', label: 'Healthcheck', status: 'failed' }, { id: 'complete', label: 'Complete', status: 'failed' } + ], + logs: ['[job] deploy: success', '[error] Healthcheck returned 502.'] + } + ] + }; + + let repositories = [ + { + id: 1, name: 'microsoft-cloud-operations-platform', fullName: 'jens/microsoft-cloud-operations-platform', owner: { login: 'jens' }, + description: 'Tenant-aware Microsoft cloud operations console.', private: true, defaultBranch: 'main', + htmlUrl: 'https://gitea.internal/jens/microsoft-cloud-operations-platform', cloneUrl: 'https://gitea.internal/jens/microsoft-cloud-operations-platform.git', sshUrl: 'git@gitea.internal:jens/microsoft-cloud-operations-platform.git', updatedAt: now, + localPath: 'C:\\Development\\Microsoft-Cloud-Operations-Platform', + localStatus: makeStatus({ head: 'b82f91ab0173cd4346ca0f0f7dcc3e8182cc8fd0' }), linkState: 'linked', + deploymentProfiles: [ + profile('profile-mcop-prod', 'Production', 'production', { liveSha: '72bd10eb0173cd4346ca0f0f7dcc3e8182cc8fd0', previousSha: '6ac991ab0173cd4346ca0f0f7dcc3e8182cc8fd0', healthy: true }), + profile('profile-mcop-stage', 'Staging', 'staging', { liveSha: 'b82f91ab0173cd4346ca0f0f7dcc3e8182cc8fd0', previousSha: '72bd10eb0173cd4346ca0f0f7dcc3e8182cc8fd0', healthy: true, confirmationRequired: false }) + ] + }, + { + id: 2, name: 'vacancyradar', fullName: 'jens/vacancyradar', owner: { login: 'jens' }, description: 'Local-first vacancy intelligence cockpit.', private: true, defaultBranch: 'main', + htmlUrl: 'https://gitea.internal/jens/vacancyradar', cloneUrl: 'https://gitea.internal/jens/vacancyradar.git', sshUrl: 'git@gitea.internal:jens/vacancyradar.git', updatedAt: now, + localPath: 'C:\\Development\\VacancyRadar', + localStatus: makeStatus({ head: 'c9182d0d28318c8cf0af109edc054732426aadf1', branch: 'feature/deployment-api', files: [makeFile('src/api/deploy.ts', 'added', { staged: true }), makeFile('src/main.tsx'), makeFile('src/components/Sidebar.tsx')] }), + linkState: 'linked', deploymentProfiles: [profile('profile-vr', 'Production', 'production', { liveSha: 'c117ab9d28318c8cf0af109edc054732426aadf1', previousSha: 'b1f57aad28318c8cf0af109edc054732426aadf1', healthy: true })] + }, + { + id: 3, name: 'unraid-appops-gateway', fullName: 'jens/unraid-appops-gateway', owner: { login: 'jens' }, description: 'Safe operations gateway for Unraid and Portainer.', private: true, defaultBranch: 'main', + htmlUrl: 'https://gitea.internal/jens/unraid-appops-gateway', cloneUrl: 'https://gitea.internal/jens/unraid-appops-gateway.git', sshUrl: 'git@gitea.internal:jens/unraid-appops-gateway.git', updatedAt: now, + localPath: 'C:\\Development\\Unraid-AppOps-Gateway', localStatus: makeStatus({ head: 'f2d1e0a1bb6147fc8b6633d2b08500c93402a719', ahead: 2 }), linkState: 'linked', + deploymentProfiles: [profile('profile-appops', 'Production', 'production', { liveSha: '8ac731b1bb6147fc8b6633d2b08500c93402a719', previousSha: '7bc198a1bb6147fc8b6633d2b08500c93402a719', healthy: true })] + }, + { + id: 4, name: 'support-bundle-collector', fullName: 'jens/support-bundle-collector', owner: { login: 'jens' }, description: 'Privacy-aware Windows support bundle collector.', private: true, defaultBranch: 'main', + htmlUrl: 'https://gitea.internal/jens/support-bundle-collector', cloneUrl: 'https://gitea.internal/jens/support-bundle-collector.git', sshUrl: 'git@gitea.internal:jens/support-bundle-collector.git', updatedAt: now, + localPath: null, localStatus: null, linkState: 'remote-only', deploymentProfiles: [] + }, + { + id: 5, name: 'portfolio', fullName: 'jens/portfolio', owner: { login: 'jens' }, description: 'Professional infrastructure and automation portfolio.', private: false, defaultBranch: 'main', + htmlUrl: 'https://gitea.internal/jens/portfolio', cloneUrl: 'https://gitea.internal/jens/portfolio.git', sshUrl: 'git@gitea.internal:jens/portfolio.git', updatedAt: now, + localPath: 'C:\\Development\\portfolio', localStatus: makeStatus({ head: 'a7f2e1c1bb6147fc8b6633d2b08500c93402a719', behind: 1 }), linkState: 'linked', + deploymentProfiles: [profile('profile-portfolio', 'Production', 'production', { liveSha: '4c20dd11bb6147fc8b6633d2b08500c93402a719', previousSha: '31adfe11bb6147fc8b6633d2b08500c93402a719', healthy: false })] + } + ]; + + const diffs = { + 'src/api/deploy.ts': `diff --git a/src/api/deploy.ts b/src/api/deploy.ts\nnew file mode 100644\n--- /dev/null\n+++ b/src/api/deploy.ts\n@@ -0,0 +1,18 @@\n+export interface DeploymentRequest {\n+ environment: 'staging' | 'production';\n+ commitSha: string;\n+}\n+\n+export async function deploy(request: DeploymentRequest) {\n+ return api.post('/deployments', request);\n+}`, + 'src/main.tsx': `diff --git a/src/main.tsx b/src/main.tsx\nindex 45ad1a2..939fc17 100644\n--- a/src/main.tsx\n+++ b/src/main.tsx\n@@ -24,8 +24,9 @@ import { Router } from './routes';\n-const API_ENDPOINT = 'http://localhost:3000';\n+const API_ENDPOINT = process.env.VITE_API_URL || '/api';\n+const DEPLOY_VERSION = '1.0.4-rc1';`, + 'src/components/Sidebar.tsx': `diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx\nindex a7bbd82..bf21e90 100644\n--- a/src/components/Sidebar.tsx\n+++ b/src/components/Sidebar.tsx\n@@ -31,6 +31,7 @@ export function Sidebar() {\n+ Deployments` + }; + + const findRepo = (localPath) => repositories.find((item) => item.localPath === localPath); + const findProfileRepo = (profileId) => repositories.find((item) => item.deploymentProfiles.some((entry) => entry.id === profileId)); + const syncState = () => { + state.deploymentProfiles = {}; + state.deploymentStates = {}; + state.repositoryMappings = {}; + for (const repository of repositories) { + if (repository.localPath) state.repositoryMappings[repository.fullName.toLowerCase()] = repository.localPath; + state.deploymentProfiles[repository.fullName.toLowerCase()] = repository.deploymentProfiles.map(({ state: profileState, ...entry }) => entry); + for (const entry of repository.deploymentProfiles) if (entry.state) state.deploymentStates[entry.id] = clone(entry.state); + } + }; + const recompute = (repository) => { + const status = repository.localStatus; + if (status) { + status.counts = { + changed: status.files.length, + staged: status.files.filter((item) => item.staged).length, + unstaged: status.files.filter((item) => item.unstaged).length, + conflicts: status.files.filter((item) => item.conflict).length, + untracked: status.files.filter((item) => item.untracked).length + }; + status.clean = status.files.length === 0; + status.shortHead = status.head.slice(0, 7); + status.branch.oid = status.head; + } + repository.favorite = state.favorites.includes(repository.fullName.toLowerCase()); + repository.readyToDeploy = Boolean(repository.localPath && status?.clean && status.branch.upstream && status.branch.ahead === 0 && status.branch.behind === 0 && repository.deploymentProfiles.some((entry) => entry.branch === status.branch.head)); + repository.attention = !repository.localPath || Boolean(status?.counts.conflicts || status?.branch.behind || status?.branch.ahead || status?.counts.changed); + repository.attentionReason = !repository.localPath ? 'No local folder linked' : status?.counts.conflicts ? `${status.counts.conflicts} conflict(s)` : status?.counts.changed ? `${status.counts.changed} local change(s)` : status?.branch.behind ? `${status.branch.behind} commit(s) behind remote` : status?.branch.ahead ? `${status.branch.ahead} unpushed commit(s)` : null; + repository.preferredCloneUrl = state.preferences.preferredCloneProtocol === 'ssh' ? repository.sshUrl : repository.cloneUrl; + }; + const snapshot = () => { repositories.forEach(recompute); syncState(); return clone(repositories); }; + syncState(); + + const commitHistory = [ + { sha: 'c9182d0d28318c8cf0af109edc054732426aadf1', shortSha: 'c9182d0', author: 'Jens', date: now, subject: 'feat: add deployment provider contract' }, + { sha: '1fa7399d28318c8cf0af109edc054732426aadf1', shortSha: '1fa7399', author: 'Jens', date: iso(-86400000), subject: 'refactor: consolidate repository state' }, + { sha: 'a251a11d28318c8cf0af109edc054732426aadf1', shortSha: 'a251a11', author: 'Jens', date: iso(-172800000), subject: 'docs: define deployment safety gates' } + ]; + const branchesByRepo = new Map(); + const stashesByRepo = new Map(); + + function updateOperation(operation) { + state.operations = [clone(operation), ...state.operations.filter((item) => item.id !== operation.id)].slice(0, 250); + emitOperations([operation]); + return clone(operation); + } + + function advanceOperation(operation) { + if (!operation || ['success', 'failed', 'cancelled', 'rolled-back'].includes(operation.status)) return operation; + operation.demoPolls = (operation.demoPolls || 0) + 1; + if (operation.demoPolls === 1) { + operation.status = 'running'; + operation.run = { id: 81, runNumber: 81, status: 'running', conclusion: null, name: operation.action === 'rollback' ? 'ForgeFlow rollback' : 'ForgeFlow deployment' }; + operation.runUrl = `https://gitea.internal/${operation.repository}/actions/runs/81`; + operation.stages.find((item) => item.id === 'queued').status = 'complete'; + operation.stages.find((item) => item.id === 'runner').status = 'active'; + operation.jobs = [{ id: 201, name: operation.action === 'rollback' ? 'rollback' : 'deploy', status: 'running', conclusion: null }]; + operation.logs.push(`[job] ${operation.jobs[0].name}: running`); + } else if (operation.demoPolls >= 2) { + operation.status = operation.action === 'rollback' ? 'rolled-back' : 'success'; + operation.stages.forEach((item) => { item.status = 'complete'; }); + operation.jobs = [{ id: 201, name: operation.action === 'rollback' ? 'rollback' : 'deploy', status: 'completed', conclusion: 'success' }]; + operation.logs.push('[ok] Runner completed successfully.', `[ok] Server status endpoint confirms ${operation.shortSha}.`); + const repository = repositories.find((item) => item.fullName === operation.repository); + const targetProfile = repository?.deploymentProfiles.find((item) => item.id === operation.profileId); + if (targetProfile) { + const oldLive = targetProfile.state.liveSha; + targetProfile.state.previousSha = oldLive; + targetProfile.state.liveSha = operation.sha; + targetProfile.state.healthy = true; + targetProfile.state.checkedAt = iso(); + } + } + operation.updatedAt = iso(); + return operation; + } + + window.forgeflow = Object.freeze({ + async bootstrap() { await wait(80); snapshot(); return { appVersion: '0.4.0-demo', platform: 'win32', state: clone(state), git: { available: true, version: 'git version 2.47.3' }, diagnostics: { enabled: true, level: state.preferences.diagnosticLevel, retentionDays: state.preferences.logRetentionDays, maxFileMb: state.preferences.maxLogFileMb, directory: '/AppData/Roaming/ForgeFlow/diagnostics', fileCount: 2, totalBytes: 18432, totalSize: '18.0 KB', latestAt: iso(-2000), lastWriteError: null } }; }, + async selectDirectory() { await wait(); return 'C:\\Development'; }, + async selectKeyFile() { await wait(); return 'C:\\Users\\Jens\\.ssh\\id_ed25519'; }, + async setupPreflight({ baseUrl, token, roots = [] }) { await wait(240); const checks = [ + { id: 'git.available', label: 'Git command line', status: 'pass', detail: 'git version 2.47.3', required: true }, + { id: 'git.identity', label: 'Git author identity', status: 'pass', detail: 'Jens ', required: false }, + { id: 'storage.userdata', label: 'Application data storage', status: 'pass', detail: 'ForgeFlow can write its local configuration.', required: true }, + { id: 'storage.diagnostics', label: 'Diagnostic log storage', status: 'pass', detail: 'The diagnostic directory is writable.', required: true }, + { id: 'storage.credentials', label: 'Protected credential storage', status: 'pass', detail: 'The operating system can encrypt the Gitea token at rest.', required: false }, + { id: 'workspace.roots', label: 'Development folders', status: roots.length ? 'pass' : 'warning', detail: roots.length ? `${roots.length} folder(s) selected.` : 'No development folder selected yet.', required: false }, + { id: 'gitea.connection', label: 'Gitea connection', status: baseUrl && token ? 'pass' : 'warning', detail: baseUrl && token ? 'Connection parameters are ready for validation.' : 'Enter the Gitea URL and token.', required: false } + ]; return { kind: 'system', startedAt: iso(-100), completedAt: iso(), checks, summary: { counts: { pass: checks.filter(i=>i.status==='pass').length, warning: checks.filter(i=>i.status==='warning').length, fail: 0, skipped: 0 }, blocking: [], ready: true } }; }, + async validateGitea({ baseUrl, token }) { await wait(320); if (!baseUrl || !token) throw new Error('Enter an instance URL and access token.'); return { baseUrl: baseUrl.replace(/\/$/, ''), user: { login: 'jens', full_name: 'Jens' }, repositoryCount: repositories.length, version: '1.26.0' }; }, + async completeSetup(payload) { await wait(300); state.setupComplete = true; state.gitea = { baseUrl: payload.baseUrl, user: payload.user, hasToken: true }; state.workspaceRoots = payload.workspaceRoots; storage.set('forgeflow-demo-setup', 'true'); return { state: clone(state), tokenState: { persistent: true } }; }, + async updateGitea(payload) { const validation = await this.validateGitea({ ...payload, token: payload.token || 'preserved-demo-token' }); state.gitea = { baseUrl: validation.baseUrl, user: validation.user, hasToken: true }; return { validation, tokenState: { persistent: true, preserved: !payload.token }, state: clone(state) }; }, + async setWorkspaceRoots(roots) { state.workspaceRoots = [...new Set(roots)]; return clone(state); }, + async setAppearance(appearance) { state.appearance = appearance; storage.set('forgeflow-theme', appearance); return clone(state); }, + async setPreferences(preferences) { state.preferences = { ...state.preferences, ...preferences }; snapshot(); return clone(state); }, + async setUpdatePreferences(updates) { state.updates = { ...state.updates, ...updates }; return clone(state); }, + async checkForUpdates() { await wait(300); return { checkedAt: iso(), owner: state.updates.owner, repo: state.updates.repo, branch: state.updates.branch, currentVersion: '0.4.0', remoteVersion: '0.4.1', remoteSha: 'a'.repeat(40), shortSha: 'aaaaaaa', available: true, mode: 'source' }; }, + async downloadUpdate() { await wait(500); return { ...(await this.checkForUpdates()), downloaded: true, archivePath: 'C:\\Temp\\ForgeFlow-0.4.1.zip', sha256: 'b'.repeat(64) }; }, + async applyUpdate() { await wait(200); return { launched: true, version: '0.4.1' }; }, + async saveServer(server) { const saved = { ...server, id: server.id || `server-${Date.now()}`, hasPassword: server.authType === 'password', hasPassphrase: false }; state.servers = [saved, ...state.servers.filter((item) => item.id !== saved.id)]; return { server: clone(saved), state: clone(state) }; }, + async deleteServer(serverId) { state.servers = state.servers.filter((item) => item.id !== serverId); return clone(state); }, + async testServer(serverId) { const server = state.servers.find((item) => item.id === serverId); server.hostFingerprint = server.hostFingerprint || 'SHA256:demo'; return { connected: true, fingerprint: server.hostFingerprint, server: clone(server), output: 'Linux\n/usr/bin/git\nDocker Compose version v2', state: clone(state) }; }, + async inspectServerProject() { return { exists: true, rootGit: true, head: 'd42d4a7'.padEnd(40,'0'), branch: 'main', trackedChanges: [], composeFiles: ['docker-compose.yml'], nestedGit: ['source'], dockerfile: true }; }, + async refreshRepositories() { await wait(260); return snapshot(); }, + async discoverRepositories() { await wait(360); return snapshot().filter((repo) => repo.localPath).map((repo) => ({ localPath: repo.localPath, remoteUrl: repo.cloneUrl, status: repo.localStatus })); }, + async favoriteRepository(fullName, favorite) { const key = fullName.toLowerCase(); state.favorites = favorite ? [...new Set([...state.favorites, key])] : state.favorites.filter((item) => item !== key); snapshot(); return clone(state); }, + async linkRepository(fullName, localPath) { const repo = repositories.find((item) => item.fullName === fullName); repo.localPath = localPath; repo.linkState = 'linked'; repo.localStatus = makeStatus({ head: randomSha() }); emitRepositories(); return snapshot(); }, + async unlinkRepository(fullName) { const repo = repositories.find((item) => item.fullName === fullName); repo.localPath = null; repo.localStatus = null; repo.linkState = 'remote-only'; emitRepositories(); return snapshot(); }, + async repositoryStatus(localPath) { return clone(findRepo(localPath)?.localStatus); }, + async repositoryDiff(localPath, filePath) { await wait(80); return diffs[filePath] || `diff --git a/${filePath} b/${filePath}\n--- a/${filePath}\n+++ b/${filePath}\n@@ -1 +1 @@\n-old\n+new`; }, + async stageFiles(localPath, files) { const repo = findRepo(localPath); repo.localStatus.files.forEach((item) => { if (!files?.length || files.includes(item.path)) { item.staged = true; item.unstaged = false; item.indexCode = item.untracked ? 'A' : 'M'; item.worktreeCode = '.'; } }); recompute(repo); emitRepositories(); return clone(repo.localStatus); }, + async unstageFiles(localPath, files) { const repo = findRepo(localPath); repo.localStatus.files.forEach((item) => { if (!files?.length || files.includes(item.path)) { item.staged = false; item.unstaged = true; item.indexCode = '.'; item.worktreeCode = item.untracked ? '?' : 'M'; } }); recompute(repo); emitRepositories(); return clone(repo.localStatus); }, + async commit(localPath, message, files) { await wait(520); if (!message?.trim()) throw new Error('Enter a commit message.'); const repo = findRepo(localPath); repo.localStatus.files = repo.localStatus.files.filter((item) => !files?.includes(item.path)); repo.localStatus.head = randomSha(); repo.localStatus.branch.ahead += 1; recompute(repo); emitRepositories(); return { commitOutput: `[${repo.localStatus.branch.head} ${repo.localStatus.shortHead}] ${message}`, commitSha: repo.localStatus.head, status: clone(repo.localStatus) }; }, + async commitAndPush(localPath, message, files) { const result = await this.commit(localPath, message, files); const repo = findRepo(localPath); await wait(240); repo.localStatus.branch.ahead = 0; recompute(repo); emitRepositories(); return { ...result, pushOutput: 'Push completed.', status: clone(repo.localStatus) }; }, + async push(localPath) { await wait(360); const repo = findRepo(localPath); repo.localStatus.branch.ahead = 0; recompute(repo); emitRepositories(); return { output: 'Push completed.', status: clone(repo.localStatus) }; }, + async fetch() { await wait(260); return { output: 'Fetch completed.' }; }, + async pull(localPath) { await wait(380); const repo = findRepo(localPath); repo.localStatus.branch.behind = 0; recompute(repo); emitRepositories(); return { output: 'Fast-forwarded.', status: clone(repo.localStatus) }; }, + async history() { await wait(100); return clone(commitHistory); }, + async branches(localPath) { const repo = findRepo(localPath); if (!branchesByRepo.has(localPath)) branchesByRepo.set(localPath, [{ name: repo.localStatus.branch.head, current: true, sha: repo.localStatus.head, shortSha: repo.localStatus.shortHead, upstream: repo.localStatus.branch.upstream }, { name: 'main', current: repo.localStatus.branch.head === 'main', sha: repo.localStatus.head, shortSha: repo.localStatus.shortHead, upstream: 'origin/main' }]); return clone(branchesByRepo.get(localPath)); }, + async checkoutBranch(localPath, branch) { const repo = findRepo(localPath); if (!repo.localStatus.clean) throw new Error('Commit or stash local changes before switching branches.'); const list = await this.branches(localPath); list.forEach((item) => { item.current = item.name === branch; }); branchesByRepo.set(localPath, list); repo.localStatus.branch.head = branch; repo.localStatus.branch.upstream = `origin/${branch}`; recompute(repo); emitRepositories(); return { status: clone(repo.localStatus), branches: clone(list) }; }, + async createBranch(localPath, branch) { const repo = findRepo(localPath); const list = await this.branches(localPath); list.forEach((item) => { item.current = false; }); list.unshift({ name: branch, current: true, sha: repo.localStatus.head, shortSha: repo.localStatus.shortHead, upstream: null }); branchesByRepo.set(localPath, list); repo.localStatus.branch.head = branch; repo.localStatus.branch.upstream = null; recompute(repo); emitRepositories(); return { status: clone(repo.localStatus), branches: clone(list) }; }, + async stash(localPath, message) { const repo = findRepo(localPath); const list = stashesByRepo.get(localPath) || []; list.unshift({ ref: `stash@{${list.length}}`, subject: message || 'ForgeFlow stash', date: iso() }); stashesByRepo.set(localPath, list); repo.localStatus.files = []; recompute(repo); emitRepositories(); return { output: 'Saved working directory and index state.', status: clone(repo.localStatus), stashes: clone(list) }; }, + async stashList(localPath) { return clone(stashesByRepo.get(localPath) || []); }, + async popStash(localPath, ref) { const repo = findRepo(localPath); const list = stashesByRepo.get(localPath) || []; const index = list.findIndex((item) => item.ref === ref); if (index < 0) throw new Error('Stash not found.'); list.splice(index, 1); stashesByRepo.set(localPath, list); repo.localStatus.files = [makeFile('src/restored-from-stash.ts')]; recompute(repo); emitRepositories(); return { output: 'Stash applied.', status: clone(repo.localStatus), stashes: clone(list) }; }, + async cloneRepository(fullName, mode = 'default') { + await wait(620); + const repository = repositories.find((item) => item.fullName === fullName); + if (!repository) throw new Error('Repository not found.'); + if (repository.localPath) throw new Error('This repository already has a linked local folder.'); + const root = mode === 'custom' ? 'D:\\OtherProjects' : state.workspaceRoots[0]; + if (!root) return { cancelled: true }; + const target = `${root.replace(/[\\/]+$/, '')}\\${repository.name}`; + const head = randomSha(); + repository.localPath = target; + repository.localStatus = makeStatus({ head, branch: repository.defaultBranch || 'main' }); + repository.localStatus.root = target; + repository.localStatus.remoteUrl = repository.preferredCloneUrl || repository.cloneUrl; + repository.linkState = 'linked'; + recompute(repository); + const current = snapshot(); + emitRepositories(); + return { target, status: clone(repository.localStatus), reused: false, repositories: current, state: clone(state) }; + }, + async openPath() { return true; }, + async openExternal() { return true; }, + async saveDeploymentProfile(fullName, input) { const repo = repositories.find((item) => item.fullName === fullName); const existing = repo.deploymentProfiles.find((item) => item.id === input.id); const saved = { ...(existing || profile(input.id || `profile-${Date.now()}`, input.name || input.environment, input.environment || 'production')), ...input, id: input.id || `profile-${Date.now()}`, provider: 'gitea-actions', inputs: existing?.inputs || {}, state: existing?.state || { liveSha: null, previousSha: null, healthy: null, healthConfigured: Boolean(input.healthcheckUrl), statusConfigured: Boolean(input.statusUrl), checkedAt: null } }; repo.deploymentProfiles = [...repo.deploymentProfiles.filter((item) => item.id !== saved.id), saved]; snapshot(); return { profile: clone(saved), state: clone(state) }; }, + async deleteDeploymentProfile(fullName, profileId) { const repo = repositories.find((item) => item.fullName === fullName); repo.deploymentProfiles = repo.deploymentProfiles.filter((item) => item.id !== profileId); snapshot(); return { profiles: clone(repo.deploymentProfiles), state: clone(state) }; }, + async deploymentPreflight(repository, profileId) { await wait(280); const profile = repository.deploymentProfiles.find((item) => item.id === profileId); const status = repository.localStatus; const checks = [ + { id: 'repository.linked', label: 'Local repository link', status: repository.localPath ? 'pass' : 'fail', detail: repository.localPath || 'No local folder linked.', required: true }, + { id: 'git.branch', label: 'Allowed branch', status: status?.branch.head === profile?.branch ? 'pass' : 'fail', detail: `Current: ${status?.branch.head || 'unknown'}; required: ${profile?.branch || 'unknown'}.`, required: true }, + { id: 'git.clean', label: 'Clean working tree', status: status?.clean ? 'pass' : 'fail', detail: status?.clean ? 'No uncommitted changes.' : `${status?.counts.changed || 0} changed file(s).`, required: true }, + { id: 'git.sync', label: 'Local and Gitea synchronized', status: !status?.branch.ahead && !status?.branch.behind ? 'pass' : 'fail', detail: `${status?.branch.ahead || 0} ahead, ${status?.branch.behind || 0} behind.`, required: true }, + { id: 'workflow.deploy.remote', label: 'Deploy workflow on Gitea branch', status: 'pass', detail: `${profile?.workflowFile || 'deploy.yml'} exists on ${profile?.branch || 'main'}.`, required: true }, + { id: 'gitea.actions', label: 'Gitea Actions API', status: 'pass', detail: 'The Actions runs endpoint is accessible.', required: true }, + { id: 'server.status', label: 'Server version endpoint', status: profile?.statusUrl ? 'pass' : 'warning', detail: profile?.statusUrl ? `Endpoint reachable; live ${profile.state?.liveSha?.slice(0,7) || 'unknown'}.` : 'No status URL configured.', required: false }, + { id: 'server.health', label: 'Application healthcheck', status: profile?.healthcheckUrl ? 'pass' : 'warning', detail: profile?.healthcheckUrl ? 'HTTP 200 in 42 ms.' : 'No healthcheck URL configured.', required: false } + ]; const blocking = checks.filter(i=>i.required && i.status==='fail').map(i=>i.id); return { kind: 'deployment', repository: repository.fullName, profileId, startedAt: iso(-100), completedAt: iso(), checks, summary: { counts: { pass: checks.filter(i=>i.status==='pass').length, warning: checks.filter(i=>i.status==='warning').length, fail: checks.filter(i=>i.status==='fail').length, skipped: 0 }, blocking, ready: blocking.length===0 }, head: status?.head || null }; }, + async deploy(repository, profileId, sha) { await wait(320); const selected = repository.deploymentProfiles.find((item) => item.id === profileId); const operation = { id: `deploy-${Date.now()}`, type: 'deployment', action: 'deploy', status: 'queued', repository: repository.fullName, profileId, profileName: selected.name, environment: selected.environment, workflowFile: selected.workflowFile, branch: selected.branch, sha, shortSha: sha.slice(0, 7), dispatchedAt: iso(), createdAt: iso(), updatedAt: iso(), demoPolls: 0, stages: [{ id: 'requested', label: 'Requested', status: 'complete' }, { id: 'verified', label: 'Verified', status: 'complete' }, { id: 'queued', label: 'Workflow queued', status: 'active' }, { id: 'runner', label: 'Runner execution', status: 'pending' }, { id: 'healthcheck', label: 'Healthcheck', status: 'pending' }, { id: 'complete', label: 'Complete', status: 'pending' }], logs: [`[info] Verified clean ${selected.branch} at ${sha}`, `[ok] Gitea accepted ${selected.workflowFile}.`] }; return updateOperation(operation); }, + async rollback(repository, profileId, targetSha) { await wait(320); const selected = repository.deploymentProfiles.find((item) => item.id === profileId); const operation = { id: `rollback-${Date.now()}`, type: 'deployment', action: 'rollback', status: 'queued', repository: repository.fullName, profileId, profileName: selected.name, environment: selected.environment, workflowFile: selected.rollbackWorkflowFile, branch: selected.branch, sha: targetSha, shortSha: targetSha.slice(0, 7), dispatchedAt: iso(), createdAt: iso(), updatedAt: iso(), demoPolls: 0, stages: [{ id: 'requested', label: 'Requested', status: 'complete' }, { id: 'verified', label: 'Verified', status: 'complete' }, { id: 'queued', label: 'Workflow queued', status: 'active' }, { id: 'runner', label: 'Runner execution', status: 'pending' }, { id: 'healthcheck', label: 'Healthcheck', status: 'pending' }, { id: 'complete', label: 'Complete', status: 'pending' }], logs: [`[warning] Rollback target verified: ${targetSha}`, `[ok] Gitea accepted ${selected.rollbackWorkflowFile}.`] }; return updateOperation(operation); }, + async healthcheck() { await wait(160); return { configured: true, healthy: true, status: 200, latencyMs: 42 }; }, + async refreshProfileState(fullName, profileId) { await wait(240); const repo = repositories.find((item) => item.fullName === fullName) || findProfileRepo(profileId); const target = repo?.deploymentProfiles.find((item) => item.id === profileId); if (!target) throw new Error('Deployment profile not found.'); target.state = { ...target.state, checkedAt: iso(), healthy: target.state.healthy !== false, healthConfigured: Boolean(target.healthcheckUrl), statusConfigured: Boolean(target.statusUrl) }; syncState(); return clone(target.state); }, + async refreshOperations(operationId = null) { await wait(300); if (operationId) { const operation = state.operations.find((item) => item.id === operationId); if (!operation) throw new Error('Operation not found.'); return updateOperation(advanceOperation(operation)); } const active = state.operations.filter((item) => !['success', 'failed', 'cancelled', 'rolled-back'].includes(item.status)).map(advanceOperation); if (active.length) emitOperations(active); state.operations = state.operations.map((item) => active.find((entry) => entry.id === item.id) || item); return clone(active); }, + async getOperation(operationId) { return clone(state.operations.find((item) => item.id === operationId) || null); }, + async diagnosticsStatus() { return { enabled: state.preferences.diagnosticsEnabled !== false, level: state.preferences.diagnosticLevel, retentionDays: state.preferences.logRetentionDays, maxFileMb: state.preferences.maxLogFileMb, directory: '/AppData/Roaming/ForgeFlow/diagnostics', fileCount: 2, totalBytes: 18432, totalSize: '18.0 KB', latestAt: iso(-2000), lastWriteError: null }; }, + async clearDiagnostics() { return { enabled: true, level: state.preferences.diagnosticLevel, retentionDays: state.preferences.logRetentionDays, maxFileMb: state.preferences.maxLogFileMb, directory: '/AppData/Roaming/ForgeFlow/diagnostics', fileCount: 1, totalBytes: 256, totalSize: '256 B', latestAt: iso(), lastWriteError: null }; }, + async openDiagnosticsFolder() { return true; }, + async exportDiagnostics(privacyMode = 'standard') { await wait(500); return { path: `C:\Users\Jens\Downloads\ForgeFlow-Diagnostics-demo.zip`, bytes: 38221, size: '37.3 KB', sha256: 'b'.repeat(64), privacyMode, generatedAt: iso() }; }, + async showDiagnosticBundle() { return true; }, + async reportRendererEvent() { return true; }, + onRepositoriesChanged(listener) { repositoryListeners.add(listener); return () => repositoryListeners.delete(listener); }, + onOperationsChanged(listener) { operationListeners.add(listener); return () => operationListeners.delete(listener); }, + onUpdatesChanged(listener) { updateListeners.add(listener); return () => updateListeners.delete(listener); }, + async reset() { state.setupComplete = false; storage.set('forgeflow-demo-setup', 'false'); return clone(state); } + }); +})(); diff --git a/src/renderer/styles.css b/src/renderer/styles.css new file mode 100644 index 0000000..a169175 --- /dev/null +++ b/src/renderer/styles.css @@ -0,0 +1,535 @@ +:root { + color-scheme: dark; + --bg: #0b0e14; + --surface-0: #0f131b; + --surface-1: #151a24; + --surface-2: #1b2130; + --surface-3: #252c3a; + --surface-hover: #202838; + --line: #30394a; + --line-soft: #222a38; + --text: #e7ebf4; + --text-muted: #9aa4b6; + --text-faint: #6f7a8d; + --primary: #8fb4ff; + --primary-strong: #5b8ff9; + --primary-soft: rgba(91, 143, 249, .15); + --success: #54ddb0; + --success-soft: rgba(84, 221, 176, .12); + --warning: #f2ba63; + --warning-soft: rgba(242, 186, 99, .13); + --danger: #ff817a; + --danger-soft: rgba(255, 129, 122, .13); + --shadow: 0 18px 70px rgba(0,0,0,.32); + --radius: 7px; + --sidebar: 286px; + --action-panel: 352px; + --font-ui: Inter, "Segoe UI", system-ui, -apple-system, BlinkMacSystemFont, sans-serif; + --font-mono: "Cascadia Code", "SFMono-Regular", Consolas, "Liberation Mono", monospace; +} + +html[data-theme="light"] { + color-scheme: light; + --bg: #eef2f7; + --surface-0: #f7f9fc; + --surface-1: #ffffff; + --surface-2: #f3f6fa; + --surface-3: #e8edf4; + --surface-hover: #edf2f8; + --line: #cdd5e1; + --line-soft: #e1e6ee; + --text: #172033; + --text-muted: #526078; + --text-faint: #7b879a; + --primary: #295fca; + --primary-strong: #326ee0; + --primary-soft: rgba(50, 110, 224, .10); + --success: #087a57; + --success-soft: rgba(8, 122, 87, .10); + --warning: #9b5b00; + --warning-soft: rgba(155, 91, 0, .10); + --danger: #c73737; + --danger-soft: rgba(199, 55, 55, .10); + --shadow: 0 18px 70px rgba(43, 55, 77, .15); +} + +* { box-sizing: border-box; } +html, body { width: 100%; height: 100%; margin: 0; overflow: hidden; } +body { background: var(--bg); color: var(--text); font-family: var(--font-ui); font-size: 13px; } +button, input, textarea, select { font: inherit; color: inherit; } +button { border: 0; } +button:focus-visible, input:focus-visible, textarea:focus-visible, select:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; } +::selection { background: rgba(91, 143, 249, .35); } +::-webkit-scrollbar { width: 9px; height: 9px; } +::-webkit-scrollbar-track { background: transparent; } +::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--text-faint) 38%, transparent); border: 3px solid transparent; background-clip: padding-box; border-radius: 20px; } + +.boot-screen { height: 100vh; display: grid; place-content: center; justify-items: center; gap: 10px; color: var(--text-muted); } +.boot-screen strong { color: var(--text); font-size: 16px; } +.brand-mark { width: 40px; height: 40px; display: grid; place-items: center; border-radius: 10px; background: linear-gradient(145deg, var(--primary), var(--primary-strong)); color: #07152e; font-weight: 800; font-size: 20px; box-shadow: 0 8px 30px rgba(91,143,249,.25); } + +.app-shell { height: 100vh; display: grid; grid-template-rows: 48px minmax(0,1fr) 25px; background: var(--bg); } +.titlebar { display: flex; align-items: center; justify-content: space-between; padding: 0 10px 0 14px; border-bottom: 1px solid var(--line); background: var(--surface-1); -webkit-app-region: drag; } +.titlebar-left, .titlebar-right { display: flex; align-items: center; gap: 9px; min-width: 0; } +.titlebar button, .titlebar input { -webkit-app-region: no-drag; } +.wordmark { display: flex; align-items: center; gap: 9px; font-size: 15px; font-weight: 720; letter-spacing: -.02em; } +.wordmark .brand-mark { width: 25px; height: 25px; border-radius: 6px; font-size: 13px; box-shadow: none; } +.brand-logo { width: 32px; height: 25px; object-fit: contain; display: block; } +.wordmark small { color: var(--text-faint); font-size: 9px; font-weight: 620; letter-spacing: .01em; margin-left: -4px; } +.workspace-name { color: var(--text-muted); border-left: 1px solid var(--line); padding-left: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 280px; } +.connection-chip { display: inline-flex; align-items: center; gap: 6px; color: var(--text-muted); font-size: 11px; font-weight: 650; padding: 5px 8px; border: 1px solid var(--line); border-radius: 5px; background: var(--surface-0); } +.connection-chip .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--success); box-shadow: 0 0 0 3px var(--success-soft); } +.global-search { width: clamp(180px, 23vw, 330px); height: 30px; padding: 0 10px 0 31px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface-0); color: var(--text); } +.search-wrap { position: relative; } +.search-wrap .icon { position: absolute; left: 9px; top: 7px; color: var(--text-faint); pointer-events: none; } + +.app-body { display: grid; grid-template-columns: var(--sidebar) minmax(0,1fr); min-height: 0; } +.sidebar { min-width: 0; display: flex; flex-direction: column; border-right: 1px solid var(--line); background: var(--surface-1); overflow: hidden; } +.primary-nav { padding: 10px 8px 8px; border-bottom: 1px solid var(--line-soft); } +.nav-button { width: 100%; height: 34px; display: flex; align-items: center; gap: 10px; padding: 0 10px; border-radius: 5px; background: transparent; color: var(--text-muted); cursor: pointer; text-align: left; font-weight: 560; } +.nav-button:hover { background: var(--surface-hover); color: var(--text); } +.nav-button.active { color: var(--primary); background: var(--primary-soft); } +.nav-button .nav-count { margin-left: auto; min-width: 20px; text-align: center; font-family: var(--font-mono); color: var(--text-faint); font-size: 10px; } +.sidebar-section { display: flex; align-items: center; justify-content: space-between; padding: 13px 12px 7px; color: var(--text-faint); font-size: 10px; font-weight: 750; letter-spacing: .09em; text-transform: uppercase; } +.sidebar-section button { background: none; color: inherit; cursor: pointer; padding: 2px; } +.repo-filter { margin: 0 9px 8px; width: calc(100% - 18px); height: 29px; border: 1px solid var(--line-soft); border-radius: 5px; background: var(--surface-0); padding: 0 9px; } +.repo-list { min-height: 0; overflow: auto; padding: 0 6px 10px; } +.repo-row { width: 100%; display: grid; grid-template-columns: 18px minmax(0,1fr) auto; gap: 8px; align-items: center; min-height: 44px; padding: 6px 8px; background: transparent; border-radius: 5px; color: var(--text-muted); cursor: pointer; text-align: left; border: 1px solid transparent; } +.repo-row:hover { background: var(--surface-hover); color: var(--text); } +.repo-row.active { background: var(--primary-soft); border-color: color-mix(in srgb, var(--primary) 26%, transparent); color: var(--text); } +.repo-row.attention .repo-icon { color: var(--warning); } +.repo-main { min-width: 0; } +.repo-name { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 620; } +.repo-sub { display: flex; gap: 6px; margin-top: 3px; color: var(--text-faint); font-family: var(--font-mono); font-size: 10px; overflow: hidden; } +.repo-sub span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.repo-badges { display: flex; gap: 3px; align-items: center; } +.mini-badge { min-width: 18px; height: 18px; display: inline-grid; place-items: center; padding: 0 5px; border-radius: 9px; font-family: var(--font-mono); font-size: 9px; font-weight: 700; color: var(--text-muted); background: var(--surface-3); } +.mini-badge.warning { background: var(--warning-soft); color: var(--warning); } +.mini-badge.success { background: var(--success-soft); color: var(--success); } +.mini-badge.danger { background: var(--danger-soft); color: var(--danger); } +.sidebar-footer { margin-top: auto; border-top: 1px solid var(--line-soft); padding: 10px 11px; } +.sidebar-diagnostic-state { display: grid; grid-template-columns: 9px minmax(0,1fr); gap: 9px; align-items: start; color: var(--text-muted); } +.sidebar-diagnostic-state .state-dot { margin-top: 4px; } +.sidebar-diagnostic-state strong, .sidebar-diagnostic-state span { display: block; } +.sidebar-diagnostic-state strong { color: var(--text); font-size: 10px; font-weight: 650; } +.sidebar-diagnostic-state span { margin-top: 2px; color: var(--text-faint); font-size: 9px; line-height: 1.35; } + +.workspace { min-width: 0; min-height: 0; display: grid; background: var(--surface-0); } +.workspace.with-panel { grid-template-columns: minmax(0,1fr) var(--action-panel); } +.main-canvas { min-width: 0; min-height: 0; overflow: auto; } +.main-canvas.repository-canvas { overflow: hidden; height: 100%; } +.action-panel { min-width: 0; border-left: 1px solid var(--line); background: var(--surface-1); overflow: auto; } +.page { min-height: 100%; padding: 22px 24px 40px; } +.page.nopad { padding: 0; } +.page-header { display: flex; justify-content: space-between; align-items: flex-start; gap: 20px; margin-bottom: 22px; } +.page-header h1, .repo-heading h1 { margin: 0; font-size: 20px; line-height: 1.3; letter-spacing: -.025em; } +.page-header p, .repo-heading p { margin: 5px 0 0; color: var(--text-muted); max-width: 720px; } +.eyebrow { color: var(--text-faint); font-size: 10px; font-weight: 760; letter-spacing: .09em; text-transform: uppercase; } + +.button { min-height: 32px; display: inline-flex; align-items: center; justify-content: center; gap: 7px; padding: 0 11px; border: 1px solid var(--line); border-radius: 5px; background: var(--surface-2); color: var(--text); cursor: pointer; font-weight: 620; white-space: nowrap; } +.button:hover { background: var(--surface-3); } +.button.primary { background: var(--primary-strong); border-color: var(--primary-strong); color: #fff; } +.button.primary:hover { filter: brightness(1.07); } +.button.success { background: var(--success); border-color: var(--success); color: #06251b; } +.button.danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 45%, var(--line)); background: var(--danger-soft); } +.button.ghost { background: transparent; border-color: transparent; color: var(--text-muted); } +.button.ghost:hover { color: var(--text); background: var(--surface-hover); } +.button.block { width: 100%; min-height: 38px; } +.button:disabled { opacity: .45; cursor: not-allowed; } +.icon-button { width: 30px; height: 30px; display: inline-grid; place-items: center; border-radius: 5px; border: 1px solid transparent; background: transparent; color: var(--text-muted); cursor: pointer; } +.icon-button:hover { color: var(--text); background: var(--surface-hover); border-color: var(--line-soft); } +.icon { width: 16px; height: 16px; display: inline-block; flex: 0 0 auto; } +.icon svg { width: 100%; height: 100%; display: block; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; } + +.summary-grid { display: grid; grid-template-columns: repeat(4,minmax(0,1fr)); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--surface-1); } +.summary-card { min-height: 118px; padding: 15px; border-right: 1px solid var(--line); position: relative; } +.summary-card:last-child { border-right: 0; } +.summary-value { margin-top: 18px; font-size: 28px; font-weight: 720; letter-spacing: -.04em; } +.summary-label { color: var(--text-muted); margin-top: 2px; } +.summary-card .icon { position: absolute; top: 14px; right: 14px; color: var(--text-faint); } +.summary-card.warning .summary-value, .summary-card.warning .icon { color: var(--warning); } +.summary-card.success .summary-value, .summary-card.success .icon { color: var(--success); } +.summary-card.danger .summary-value, .summary-card.danger .icon { color: var(--danger); } + +.section-block { margin-top: 24px; } +.section-heading { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 9px; } +.section-heading h2 { margin: 0; font-size: 13px; letter-spacing: -.01em; } +.section-heading .meta { color: var(--text-faint); font-size: 11px; } +.action-queue { border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--surface-1); } +.queue-row { display: grid; grid-template-columns: 28px minmax(160px,1.2fr) minmax(230px,2fr) auto; gap: 12px; align-items: center; min-height: 62px; padding: 10px 13px; border-bottom: 1px solid var(--line-soft); } +.queue-row:last-child { border-bottom: 0; } +.queue-row:hover { background: var(--surface-hover); } +.queue-icon { width: 28px; height: 28px; display: grid; place-items: center; border-radius: 6px; background: var(--surface-2); color: var(--text-muted); } +.queue-icon.warning { background: var(--warning-soft); color: var(--warning); } +.queue-icon.success { background: var(--success-soft); color: var(--success); } +.queue-icon.danger { background: var(--danger-soft); color: var(--danger); } +.queue-title { font-weight: 640; } +.queue-sub { color: var(--text-faint); margin-top: 3px; font-size: 11px; font-family: var(--font-mono); } +.queue-reason { color: var(--text-muted); } +.queue-reason strong { color: var(--text); display: block; font-weight: 600; } + +.two-column { display: grid; grid-template-columns: minmax(0,1.5fr) minmax(280px,1fr); gap: 16px; } +.panel { border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface-1); } +.panel-header { min-height: 42px; display: flex; align-items: center; justify-content: space-between; padding: 0 13px; border-bottom: 1px solid var(--line-soft); } +.panel-header h2, .panel-header h3 { font-size: 12px; margin: 0; } +.panel-body { padding: 14px; } +.activity-list { padding: 3px 0; } +.activity-item { display: grid; grid-template-columns: 10px minmax(0,1fr) auto; gap: 10px; padding: 10px 13px; align-items: start; } +.activity-dot { width: 7px; height: 7px; margin-top: 5px; border-radius: 50%; background: var(--text-faint); } +.activity-dot.success { background: var(--success); } +.activity-dot.warning { background: var(--warning); } +.activity-dot.danger { background: var(--danger); } +.activity-title { font-weight: 590; } +.activity-sub, .activity-time { color: var(--text-faint); font-size: 11px; } + +.repo-workspace { height: 100%; min-height: 0; display: grid; grid-template-rows: auto auto 39px minmax(0,1fr); } +.repo-header { padding: 16px 18px 13px; background: var(--surface-1); border-bottom: 1px solid var(--line); display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; } +.repo-heading { min-width: 0; } +.repo-heading h1 { display: flex; align-items: center; gap: 9px; font-size: 17px; } +.repo-heading p { font-family: var(--font-mono); font-size: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.repo-header-actions { display: flex; gap: 7px; } +.release-rail { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); background: var(--surface-0); border-bottom: 1px solid var(--line); } +.release-node { min-width: 0; padding: 10px 16px 11px; border-right: 1px solid var(--line-soft); position: relative; } +.release-node:last-child { border-right: 0; } +.release-node:not(:last-child)::after { content: '›'; position: absolute; right: -6px; top: 19px; z-index: 2; width: 12px; height: 12px; display: grid; place-items: center; border-radius: 50%; background: var(--surface-0); color: var(--text-faint); } +.release-label { color: var(--text-faint); font-size: 9px; font-weight: 760; letter-spacing: .08em; text-transform: uppercase; } +.release-value { display: flex; align-items: center; gap: 7px; margin-top: 4px; min-width: 0; } +.release-value strong { font-family: var(--font-mono); font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.release-value span { color: var(--text-muted); font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.state-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--text-faint); flex: 0 0 auto; } +.state-dot.success { background: var(--success); box-shadow: 0 0 0 3px var(--success-soft); } +.state-dot.warning { background: var(--warning); box-shadow: 0 0 0 3px var(--warning-soft); } +.state-dot.danger { background: var(--danger); box-shadow: 0 0 0 3px var(--danger-soft); } +.tabs { display: flex; align-items: flex-end; gap: 2px; padding: 0 12px; border-bottom: 1px solid var(--line); background: var(--surface-1); } +.tab { height: 38px; padding: 0 12px; background: transparent; color: var(--text-muted); border-bottom: 2px solid transparent; cursor: pointer; } +.tab:hover { color: var(--text); } +.tab.active { color: var(--primary); border-bottom-color: var(--primary); } +.repo-content { min-height: 0; overflow: hidden; } +.changes-layout { height: 100%; min-height: 0; display: grid; grid-template-columns: 290px minmax(0,1fr); } +.file-panel { min-width: 0; min-height: 0; overflow: hidden; border-right: 1px solid var(--line); background: var(--surface-1); display: flex; flex-direction: column; } +.file-panel-tools { min-height: 38px; padding: 0 9px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--line-soft); } +.file-list { flex: 1 1 auto; overflow-x: hidden; overflow-y: auto; min-height: 0; padding: 5px; overscroll-behavior: contain; scrollbar-gutter: stable; } +.file-row { width: 100%; min-height: 34px; display: grid; grid-template-columns: 17px 17px minmax(0,1fr) 16px; gap: 7px; align-items: center; padding: 3px 6px; border-radius: 4px; background: transparent; color: var(--text-muted); cursor: pointer; text-align: left; } +.file-row:hover { background: var(--surface-hover); color: var(--text); } +.file-row.active { background: var(--primary-soft); color: var(--text); } +.file-row input { margin: 0; accent-color: var(--primary-strong); } +.file-path { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-family: var(--font-mono); font-size: 11px; } +.file-status { font-family: var(--font-mono); font-size: 10px; font-weight: 750; color: var(--warning); } +.file-status.added, .file-status.untracked { color: var(--success); } +.file-status.deleted, .file-status.conflict { color: var(--danger); } +.diff-panel { min-width: 0; min-height: 0; display: grid; grid-template-rows: 38px minmax(0,1fr); background: var(--bg); } +.diff-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 0 11px; border-bottom: 1px solid var(--line-soft); background: var(--surface-0); } +.diff-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--font-mono); color: var(--text-muted); } +.diff-view { overflow: auto; padding: 8px 0 36px; font-family: var(--font-mono); font-size: 11px; line-height: 19px; white-space: pre; tab-size: 2; } +.diff-line { display: block; min-height: 19px; padding: 0 14px; } +.diff-line.add { background: rgba(38, 166, 115, .14); color: #8ef0c6; } +.diff-line.remove { background: rgba(229, 83, 75, .14); color: #ffaaa5; } +html[data-theme="light"] .diff-line.add { color: #006642; } +html[data-theme="light"] .diff-line.remove { color: #a31f1f; } +.diff-line.meta { color: var(--primary); } +.diff-line.hunk { color: #caa7ff; background: rgba(148, 97, 214, .08); } +.empty-state { height: 100%; min-height: 260px; display: grid; place-content: center; justify-items: center; text-align: center; padding: 30px; color: var(--text-muted); } +.empty-state .large-icon { width: 48px; height: 48px; display: grid; place-items: center; border-radius: 12px; background: var(--surface-2); color: var(--text-faint); margin-bottom: 12px; } +.empty-state h3 { margin: 0 0 6px; color: var(--text); font-size: 14px; } +.empty-state p { margin: 0; max-width: 420px; line-height: 1.55; } + +.inspector { padding: 16px; } +.inspector-header { margin-bottom: 16px; } +.inspector-header h2 { margin: 3px 0 0; font-size: 15px; } +.inspector-section { padding: 15px 0; border-top: 1px solid var(--line-soft); } +.inspector-section:first-of-type { border-top: 0; padding-top: 0; } +.inspector-label { color: var(--text-faint); font-size: 9px; font-weight: 760; letter-spacing: .09em; text-transform: uppercase; margin-bottom: 8px; } +.textarea, .input, .select { width: 100%; border: 1px solid var(--line); border-radius: 5px; background: var(--surface-0); color: var(--text); } +.input, .select { height: 33px; padding: 0 9px; } +.textarea { min-height: 98px; resize: vertical; padding: 9px 10px; line-height: 1.45; } +.field-hint { display: flex; justify-content: space-between; gap: 10px; margin-top: 6px; color: var(--text-faint); font-size: 10px; } +.context-summary { padding: 11px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface-0); } +.context-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 5px 0; color: var(--text-muted); } +.context-row strong { color: var(--text); font-family: var(--font-mono); font-size: 11px; text-align: right; overflow: hidden; text-overflow: ellipsis; } +.notice { display: flex; align-items: flex-start; gap: 9px; padding: 10px 11px; border-radius: 5px; border: 1px solid var(--line); background: var(--surface-0); color: var(--text-muted); line-height: 1.45; } +.notice.warning { border-color: color-mix(in srgb, var(--warning) 35%, var(--line)); background: var(--warning-soft); color: var(--warning); } +.notice.danger { border-color: color-mix(in srgb, var(--danger) 35%, var(--line)); background: var(--danger-soft); color: var(--danger); } +.notice.success { border-color: color-mix(in srgb, var(--success) 35%, var(--line)); background: var(--success-soft); color: var(--success); } +.stack { display: grid; gap: 8px; } +.divider-text { display: flex; align-items: center; gap: 9px; color: var(--text-faint); font-size: 10px; text-transform: uppercase; letter-spacing: .08em; font-weight: 700; } +.divider-text::before, .divider-text::after { content: ''; height: 1px; background: var(--line-soft); flex: 1; } + +.data-table { width: 100%; border-collapse: collapse; } +.data-table th { height: 34px; padding: 0 11px; color: var(--text-faint); text-align: left; font-size: 9px; letter-spacing: .07em; text-transform: uppercase; border-bottom: 1px solid var(--line); } +.data-table td { padding: 10px 11px; border-bottom: 1px solid var(--line-soft); vertical-align: middle; } +.data-table tr:last-child td { border-bottom: 0; } +.data-table tbody tr:hover { background: var(--surface-hover); } +.mono { font-family: var(--font-mono); } +.status-pill { display: inline-flex; align-items: center; gap: 6px; min-height: 22px; padding: 0 8px; border-radius: 11px; background: var(--surface-3); color: var(--text-muted); font-size: 10px; font-weight: 680; } +.status-pill.success { background: var(--success-soft); color: var(--success); } +.status-pill.warning { background: var(--warning-soft); color: var(--warning); } +.status-pill.danger { background: var(--danger-soft); color: var(--danger); } + +.settings-layout { display: grid; grid-template-columns: 210px minmax(0,1fr); min-height: 100%; } +.settings-nav { padding: 15px 8px; border-right: 1px solid var(--line); background: var(--surface-1); } +.settings-content { padding: 25px 30px 60px; overflow: auto; } +.settings-group { max-width: 860px; margin-bottom: 28px; } +.settings-group > h2 { font-size: 12px; margin: 0 0 12px; } +.form-grid { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: 14px; } +.field { display: grid; gap: 6px; } +.field label { color: var(--text-muted); font-size: 11px; font-weight: 650; } +.field.full { grid-column: 1 / -1; } +.connection-card { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 13px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface-1); } +.root-row { display: flex; align-items: center; gap: 8px; } +.root-row .input { flex: 1; font-family: var(--font-mono); font-size: 11px; } + +.deploy-card-grid { display: grid; grid-template-columns: repeat(auto-fill,minmax(310px,1fr)); gap: 12px; } +.deploy-card { border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface-1); overflow: hidden; } +.deploy-card-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 14px; border-bottom: 1px solid var(--line-soft); } +.deploy-card-body { padding: 13px 14px; } +.deploy-metadata { display: grid; grid-template-columns: 1fr auto; gap: 7px 15px; color: var(--text-muted); } +.deploy-metadata strong { font-family: var(--font-mono); font-size: 11px; color: var(--text); } + +.deployment-view { min-height: 100%; padding: 22px; } +.pipeline-card { border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface-1); overflow: hidden; } +.pipeline-head { display: flex; justify-content: space-between; gap: 15px; padding: 16px; border-bottom: 1px solid var(--line-soft); } +.pipeline-head h2 { margin: 0; font-size: 16px; } +.pipeline-head p { margin: 5px 0 0; color: var(--text-muted); font-family: var(--font-mono); font-size: 11px; } +.pipeline-stages { display: grid; grid-template-columns: repeat(5,1fr); padding: 25px 20px 20px; } +.pipeline-stage { position: relative; display: grid; justify-items: center; gap: 8px; color: var(--text-faint); text-align: center; font-size: 10px; font-weight: 680; } +.pipeline-stage::before { content: ''; position: absolute; height: 2px; left: -50%; right: 50%; top: 15px; background: var(--line); } +.pipeline-stage:first-child::before { display: none; } +.pipeline-stage.complete::before, .pipeline-stage.active::before { background: var(--success); } +.stage-icon { width: 32px; height: 32px; display: grid; place-items: center; border-radius: 9px; background: var(--surface-3); border: 1px solid var(--line); z-index: 1; } +.pipeline-stage.complete { color: var(--success); } +.pipeline-stage.complete .stage-icon { background: var(--success); border-color: var(--success); color: #06251b; } +.pipeline-stage.active { color: var(--primary); } +.pipeline-stage.active .stage-icon { background: var(--primary-strong); border-color: var(--primary); color: #fff; box-shadow: 0 0 0 5px var(--primary-soft); } +.log-view { margin-top: 14px; border: 1px solid var(--line); border-radius: var(--radius); background: #080b11; overflow: hidden; } +.log-toolbar { height: 35px; display: flex; align-items: center; justify-content: space-between; padding: 0 11px; border-bottom: 1px solid #262d3b; color: #9aa4b6; } +.log-lines { min-height: 290px; max-height: 500px; overflow: auto; padding: 12px 14px; font: 11px/19px var(--font-mono); color: #c3cada; white-space: pre-wrap; } +.log-lines .ok { color: #54ddb0; } +.log-lines .warn { color: #f2ba63; } +.log-lines .err { color: #ff817a; } + +.setup-backdrop { position: fixed; inset: 0; z-index: 50; display: grid; place-items: center; padding: 25px; background: rgba(4,7,12,.75); backdrop-filter: blur(8px); } +.setup-window { width: min(920px,96vw); min-height: 590px; max-height: 92vh; display: grid; grid-template-columns: 230px minmax(0,1fr); border: 1px solid var(--line); border-radius: 10px; overflow: hidden; background: var(--surface-1); box-shadow: var(--shadow); } +.setup-sidebar { padding: 24px 17px; border-right: 1px solid var(--line); background: var(--surface-0); } +.setup-sidebar h2 { margin: 16px 0 5px; font-size: 18px; } +.setup-sidebar p { margin: 0 0 22px; color: var(--text-muted); line-height: 1.5; } +.setup-step { min-height: 38px; display: flex; align-items: center; gap: 9px; padding: 0 9px; border-radius: 5px; color: var(--text-faint); margin-bottom: 3px; } +.setup-step .step-number { width: 22px; height: 22px; display: grid; place-items: center; border-radius: 50%; border: 1px solid var(--line); font-family: var(--font-mono); font-size: 9px; } +.setup-step.active { background: var(--primary-soft); color: var(--primary); } +.setup-step.complete { color: var(--success); } +.setup-content { min-width: 0; padding: 34px 38px 24px; display: grid; grid-template-rows: minmax(0,1fr) auto; overflow: auto; } +.setup-body h1 { margin: 0; font-size: 22px; } +.setup-body > p { color: var(--text-muted); line-height: 1.55; max-width: 620px; } +.setup-actions { display: flex; justify-content: space-between; gap: 10px; padding-top: 22px; border-top: 1px solid var(--line-soft); } +.discovery-list { border: 1px solid var(--line); border-radius: 6px; overflow: hidden; max-height: 260px; overflow-y: auto; } +.discovery-row { min-height: 45px; display: grid; grid-template-columns: 20px minmax(0,1fr) auto; gap: 10px; align-items: center; padding: 7px 10px; border-bottom: 1px solid var(--line-soft); } +.discovery-row:last-child { border-bottom: 0; } +.discovery-row strong { display: block; font-size: 12px; } +.discovery-row span { display: block; margin-top: 2px; color: var(--text-faint); font: 10px var(--font-mono); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } + +.modal-backdrop { position: fixed; inset: 0; z-index: 60; display: grid; place-items: center; padding: 20px; background: rgba(4,7,12,.7); backdrop-filter: blur(4px); } +.modal { width: min(520px,94vw); border: 1px solid var(--line); border-radius: 9px; background: var(--surface-1); box-shadow: var(--shadow); overflow: hidden; } +.modal-header { display: flex; justify-content: space-between; align-items: center; padding: 15px 17px; border-bottom: 1px solid var(--line); } +.modal-header h2 { margin: 0; font-size: 15px; } +.modal-body { padding: 17px; } +.modal-footer { display: flex; justify-content: flex-end; gap: 8px; padding: 12px 17px; border-top: 1px solid var(--line-soft); background: var(--surface-0); } + +.statusbar { display: flex; align-items: center; justify-content: space-between; gap: 15px; padding: 0 9px; border-top: 1px solid var(--line); background: var(--surface-1); color: var(--text-faint); font-size: 10px; font-weight: 620; } +.statusbar-left, .statusbar-right { display: flex; align-items: center; gap: 13px; min-width: 0; } +.statusbar-item { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; } +.statusbar .success { color: var(--success); } +.statusbar .warning { color: var(--warning); } +.statusbar .danger { color: var(--danger); } + +.toast-root { position: fixed; right: 15px; bottom: 38px; z-index: 90; display: grid; gap: 8px; pointer-events: none; } +.toast { width: min(380px,calc(100vw - 30px)); display: grid; grid-template-columns: 20px minmax(0,1fr); gap: 9px; padding: 11px 12px; border: 1px solid var(--line); border-radius: 7px; background: var(--surface-2); box-shadow: var(--shadow); pointer-events: auto; animation: toast-in .18s ease-out; } +.toast.success { border-color: color-mix(in srgb, var(--success) 35%, var(--line)); } +.toast.error { border-color: color-mix(in srgb, var(--danger) 40%, var(--line)); } +.toast strong { display: block; margin-bottom: 2px; } +.toast span { color: var(--text-muted); line-height: 1.4; } +@keyframes toast-in { from { transform: translateY(8px); opacity: 0; } } + +.loading-overlay { position: absolute; inset: 0; z-index: 20; display: grid; place-items: center; background: color-mix(in srgb, var(--surface-0) 72%, transparent); backdrop-filter: blur(2px); } +.spinner { width: 22px; height: 22px; border: 2px solid var(--line); border-top-color: var(--primary); border-radius: 50%; animation: spin .8s linear infinite; } +@keyframes spin { to { transform: rotate(360deg); } } + +@media (max-width: 1250px) { + :root { --sidebar: 250px; --action-panel: 320px; } + .summary-grid { grid-template-columns: repeat(2,1fr); } + .summary-card:nth-child(2) { border-right: 0; } + .summary-card:nth-child(-n+2) { border-bottom: 1px solid var(--line); } + .queue-row { grid-template-columns: 28px minmax(130px,1fr) minmax(180px,1.5fr) auto; } +} + +@media (max-width: 1120px) { + :root { --sidebar: 220px; --action-panel: 300px; } + .global-search { width: 190px; } + .changes-layout { grid-template-columns: 245px minmax(0,1fr); } + .page { padding-left: 18px; padding-right: 18px; } +} + +/* ForgeFlow v0.2 interaction and workflow refinements */ +.command-trigger { height: 30px; display: inline-flex; align-items: center; gap: 7px; padding: 0 8px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface-0); color: var(--text-muted); cursor: pointer; -webkit-app-region: no-drag; } +.command-trigger:hover { color: var(--text); background: var(--surface-hover); } +kbd { min-width: 24px; padding: 2px 5px; border: 1px solid var(--line); border-bottom-width: 2px; border-radius: 4px; background: var(--surface-2); color: var(--text-faint); font: 9px var(--font-mono); text-align: center; } +.repo-group-label { padding: 10px 8px 4px; color: var(--text-faint); font-size: 9px; font-weight: 750; letter-spacing: .08em; text-transform: uppercase; } +.favorite-button { width: 25px; height: 25px; display: inline-grid; place-items: center; margin-left: -5px; border-radius: 5px; background: transparent; color: var(--text-faint); cursor: pointer; } +.favorite-button:hover, .favorite-button.active { color: var(--warning); background: var(--warning-soft); } +.favorite-button.active svg { fill: currentColor; } +.repo-row .repo-icon .icon { width: 15px; height: 15px; } +.repo-row .repo-icon:has(svg path[d^="m12 3"]) { color: var(--warning); } +.button.small { min-height: 25px; padding: 0 7px; font-size: 10px; } +.stack.horizontal.compact { gap: 5px; } +.empty-state.compact { min-height: 105px; padding: 16px; } +.empty-state.full { height: 100%; min-height: 320px; } +.readiness-list { display: grid; gap: 3px; } +.readiness-row { display: grid; grid-template-columns: 12px minmax(0,1fr); gap: 9px; align-items: start; padding: 9px 4px; border-bottom: 1px solid var(--line-soft); } +.readiness-row:last-child { border-bottom: 0; } +.readiness-row strong { display: block; font-size: 11px; } +.readiness-row span:not(.state-dot) { display: block; margin-top: 2px; color: var(--text-faint); font-size: 10px; } +.action-panel-head { padding: 18px 17px 14px; border-bottom: 1px solid var(--line); } +.action-panel-head h2 { margin: 5px 0 5px; font-size: 16px; } +.action-panel-head p { margin: 0; color: var(--text-muted); line-height: 1.45; } +.action-panel-body { padding: 15px 16px; } +.action-panel-footer { display: grid; grid-template-columns: 1fr 1fr; gap: 5px; margin: auto 10px 10px; padding-top: 10px; border-top: 1px solid var(--line-soft); } +.action-panel { display: flex; flex-direction: column; } +.panel-callout { display: grid; gap: 9px; } +.panel-callout h2 { margin: 3px 0 0; font-size: 15px; } +.panel-callout p { margin: 0 0 5px; color: var(--text-muted); line-height: 1.5; } +.callout-icon { width: 38px; height: 38px; display: grid; place-items: center; border-radius: 9px; background: var(--primary-soft); color: var(--primary); } +.callout-icon.success { background: var(--success-soft); color: var(--success); } +.callout-icon.warning { background: var(--warning-soft); color: var(--warning); } +.callout-icon.danger { background: var(--danger-soft); color: var(--danger); } +.field-hint { display: flex; justify-content: space-between; gap: 10px; margin-top: 6px; color: var(--text-faint); font-size: 10px; } +.field-label { display: block; margin-top: 8px; color: var(--text-muted); font-size: 10px; font-weight: 650; } +.textarea { width: 100%; min-height: 92px; resize: vertical; padding: 9px 10px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface-0); line-height: 1.45; } +.input, .select { width: 100%; min-height: 34px; padding: 0 10px; border: 1px solid var(--line); border-radius: 5px; background: var(--surface-0); color: var(--text); } +.select { cursor: pointer; } +.stack { display: grid; gap: 8px; } +.stack.horizontal { display: flex; flex-wrap: wrap; align-items: center; } +.deploy-proof { display: grid; grid-template-columns: 1fr auto; gap: 7px 12px; margin: 7px 0 5px; padding: 11px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface-0); } +.deploy-proof span { color: var(--text-faint); } +.deploy-proof strong { font: 11px var(--font-mono); } +.tab-page { min-height: 100%; padding: 18px 19px 42px; overflow: auto; } +.git-tools-grid { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: 15px; align-items: start; } +.inline-form { display: grid; grid-template-columns: minmax(0,1fr) auto; gap: 8px; margin-bottom: 13px; } +.tool-list { display: grid; border: 1px solid var(--line-soft); border-radius: 6px; overflow: hidden; } +.tool-row { min-height: 51px; display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 8px 10px; border-bottom: 1px solid var(--line-soft); } +.tool-row:last-child { border-bottom: 0; } +.tool-row:hover { background: var(--surface-hover); } +.tool-row strong { display: block; } +.tool-row span { display: block; margin-top: 3px; color: var(--text-faint); font: 10px var(--font-mono); } +.deploy-card-header h3 { margin: 4px 0 3px; font-size: 14px; } +.deploy-card-header p { margin: 0; color: var(--text-faint); font: 10px var(--font-mono); } +.card-actions { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line-soft); } +.compact-card .deploy-card-body { padding-bottom: 11px; } +.check-field { display: flex; align-items: center; gap: 9px; padding: 8px 0; color: var(--text-muted); } +.check-field input { accent-color: var(--primary-strong); } +.wide-modal { width: min(650px,95vw); } +.modal-spacer { flex: 1; } +.confirm-hero { display: flex; align-items: center; gap: 12px; padding: 12px; border: 1px solid color-mix(in srgb, var(--success) 30%, var(--line)); border-radius: 7px; background: var(--success-soft); } +.confirm-hero.danger { border-color: color-mix(in srgb, var(--danger) 35%, var(--line)); background: var(--danger-soft); } +.confirm-hero > .icon { width: 27px; height: 27px; color: var(--success); } +.confirm-hero.danger > .icon { color: var(--danger); } +.confirm-hero strong, .confirm-hero span { display: block; } +.confirm-hero span { margin-top: 3px; color: var(--text-muted); } +.confirm-grid { display: grid; grid-template-columns: 120px minmax(0,1fr); gap: 9px 14px; margin-top: 16px; } +.confirm-grid span { color: var(--text-faint); } +.confirm-grid strong { overflow-wrap: anywhere; } +.notice.danger { border-color: color-mix(in srgb, var(--danger) 35%, var(--line)); background: var(--danger-soft); color: var(--danger); } +.notice.warning { border-color: color-mix(in srgb, var(--warning) 35%, var(--line)); background: var(--warning-soft); color: var(--warning); } +.danger-zone { padding: 15px; border: 1px solid color-mix(in srgb, var(--danger) 25%, var(--line)); border-radius: 7px; background: var(--danger-soft); } +.danger-zone p { color: var(--text-muted); line-height: 1.5; } +.pipeline-stages { grid-template-columns: repeat(6,1fr); } +.pipeline-stage.failed { color: var(--danger); } +.pipeline-stage.failed .stage-icon { background: var(--danger); border-color: var(--danger); color: #fff; } +.pipeline-stage.cancelled, .pipeline-stage.skipped { color: var(--text-faint); } +.log-lines { word-break: break-word; } +.palette-backdrop { align-items: start; padding-top: 12vh; } +.command-palette { width: min(650px,94vw); border: 1px solid var(--line); border-radius: 10px; background: var(--surface-1); box-shadow: var(--shadow); overflow: hidden; } +.palette-search { height: 54px; display: grid; grid-template-columns: 20px minmax(0,1fr); gap: 9px; align-items: center; padding: 0 15px; border-bottom: 1px solid var(--line); } +.palette-search input { height: 100%; border: 0; outline: 0; background: transparent; font-size: 15px; } +.palette-list { max-height: 380px; overflow: auto; padding: 6px; } +.palette-row { width: 100%; min-height: 52px; display: grid; grid-template-columns: 22px minmax(0,1fr) auto; gap: 10px; align-items: center; padding: 7px 10px; border-radius: 6px; background: transparent; color: var(--text-muted); text-align: left; cursor: pointer; } +.palette-row:hover, .palette-row:focus-visible { background: var(--primary-soft); color: var(--text); } +.palette-row:disabled { opacity: .4; cursor: not-allowed; } +.palette-row strong, .palette-row small { display: block; } +.palette-row small { margin-top: 3px; color: var(--text-faint); } +.palette-footer { padding: 8px 13px; border-top: 1px solid var(--line-soft); color: var(--text-faint); font-size: 10px; } +.discovery-progress { min-height: 260px; display: grid; place-content: center; justify-items: center; gap: 13px; color: var(--text-muted); } + +@media (max-width: 1240px) { + .command-trigger span { display: none; } + .command-trigger kbd { display: none; } + .git-tools-grid { grid-template-columns: 1fr; } + .pipeline-stages { grid-template-columns: repeat(3,1fr); row-gap: 18px; } + .pipeline-stage:nth-child(4)::before { display: none; } +} + +/* v0.3 diagnostics and preflight */ +.diagnostics-page { display: grid; gap: 18px; padding: 20px 22px 44px; overflow: auto; } +.diagnostic-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; align-items: start; } +.diagnostic-metrics { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); border: 1px solid var(--line-soft); border-radius: 7px; overflow: hidden; } +.diagnostic-metrics > div { min-height: 68px; display: grid; align-content: center; gap: 5px; padding: 11px 12px; border-right: 1px solid var(--line-soft); border-bottom: 1px solid var(--line-soft); background: var(--surface-0); } +.diagnostic-metrics > div:nth-child(2n) { border-right: 0; } +.diagnostic-metrics > div:nth-last-child(-n + 2) { border-bottom: 0; } +.diagnostic-metrics span { color: var(--text-faint); font-size: 9px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; } +.diagnostic-metrics strong { overflow-wrap: anywhere; font-size: 12px; } +.preflight-summary { min-height: 48px; display: flex; flex-wrap: wrap; align-items: center; gap: 9px; padding: 11px 13px; border-bottom: 1px solid var(--line-soft); color: var(--text-muted); } +.preflight-list { display: grid; } +.preflight-row { display: grid; grid-template-columns: 28px minmax(0, 1fr) auto; gap: 10px; align-items: start; padding: 12px 13px; border-bottom: 1px solid var(--line-soft); } +.preflight-row:last-child { border-bottom: 0; } +.preflight-row > div { min-width: 0; } +.preflight-row strong { display: block; margin-top: 1px; font-size: 11px; } +.preflight-row span:not(.preflight-state):not(.status-pill), .preflight-row small { display: block; margin-top: 3px; color: var(--text-faint); line-height: 1.4; overflow-wrap: anywhere; } +.preflight-row small { color: var(--text-muted); } +.preflight-state { width: 25px; height: 25px; display: grid; place-items: center; border-radius: 50%; background: var(--surface-2); color: var(--text-faint); } +.preflight-state .icon { width: 14px; height: 14px; } +.preflight-state.success { background: var(--success-soft); color: var(--success); } +.preflight-state.warning { background: var(--warning-soft); color: var(--warning); } +.preflight-state.danger { background: var(--danger-soft); color: var(--danger); } +.setup-preflight { max-height: 330px; margin-top: 17px; border: 1px solid var(--line); border-radius: 8px; overflow: auto; background: var(--surface-0); } +.setup-summary { display: grid; margin: 18px 0; border: 1px solid var(--line); border-radius: 8px; padding: 0 12px; background: var(--surface-0); } +.setup-support-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; } +.diagnostics-page .panel-body { padding: 14px; } +.diagnostics-page .section-block { margin: 0; } + +@media (max-width: 1100px) { + .diagnostic-grid { grid-template-columns: 1fr; } +} + +@media (max-width: 760px) { + .diagnostics-page { padding: 15px; } + .diagnostic-metrics { grid-template-columns: 1fr; } + .diagnostic-metrics > div { border-right: 0; } + .diagnostic-metrics > div:nth-last-child(-n + 2) { border-bottom: 1px solid var(--line-soft); } + .diagnostic-metrics > div:last-child { border-bottom: 0; } + .preflight-row { grid-template-columns: 28px minmax(0, 1fr); } + .preflight-row > .status-pill { grid-column: 2; justify-self: start; } +} + +.required-mark { color: var(--warning); font-size: 10px; font-weight: 700; text-transform: uppercase; margin-left: 5px; } +.commit-readiness { margin-top: 9px; padding: 8px 9px; display: flex; align-items: flex-start; gap: 7px; border: 1px solid var(--line); border-radius: 5px; color: var(--text-muted); background: var(--surface-0); font-size: 11px; line-height: 1.4; } +.commit-readiness.blocked { border-color: color-mix(in srgb, var(--warning) 35%, var(--line)); background: var(--warning-soft); color: var(--text); } +.commit-readiness.ready { border-color: color-mix(in srgb, var(--success) 35%, var(--line)); background: var(--success-soft); color: var(--text); } +.commit-readiness .icon { flex: 0 0 auto; margin-top: 1px; } +.stage-note { margin-top: 10px; color: var(--text-faint); font-size: 10px; line-height: 1.45; } + +.update-card { margin-top: 12px; padding: 12px; display: flex; align-items: center; justify-content: space-between; gap: 16px; border: 1px solid var(--line); border-radius: 7px; background: var(--surface-0); } +.update-card.available { border-color: color-mix(in srgb, var(--primary) 45%, var(--line)); background: var(--primary-soft); } +.update-card > div:first-child { display: flex; align-items: center; gap: 10px; min-width: 0; } +.update-card > div:first-child > span { min-width: 0; display: grid; gap: 2px; } +.update-card small, .server-card small { color: var(--text-faint); } +.server-list { display: grid; gap: 8px; } +.server-card { padding: 11px 12px; display: flex; align-items: center; justify-content: space-between; gap: 16px; border: 1px solid var(--line); border-radius: 7px; background: var(--surface-0); } +.server-card-main { min-width: 0; display: flex; align-items: center; gap: 10px; } +.server-card-main > div { min-width: 0; display: grid; gap: 2px; } +.server-card-main span { color: var(--text-muted); font-family: var(--font-mono); font-size: 10px; overflow: hidden; text-overflow: ellipsis; } +.provider-choice { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: 8px; margin-bottom: 14px; } +.provider-note { padding: 10px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface-0); color: var(--text-muted); font-size: 11px; } +.server-inspection { margin-top: 12px; } +@media (max-width: 1240px) { + .wordmark small { display: none; } + .update-card, .server-card { align-items: flex-start; flex-direction: column; } +} + +.setup-brand-logo { width: 150px; height: auto; display: block; margin-bottom: 12px; } diff --git a/src/shared/clone-target.cjs b/src/shared/clone-target.cjs new file mode 100644 index 0000000..ee1c058 --- /dev/null +++ b/src/shared/clone-target.cjs @@ -0,0 +1,24 @@ +'use strict'; + +const path = require('node:path'); + +function cloneDirectoryName(remoteUrl) { + const raw = String(remoteUrl || '').trim().replace(/[?#].*$/, '').replace(/[\\/]+$/, ''); + const segment = raw.split(/[\\/:]/).filter(Boolean).at(-1) || 'repository'; + return segment.replace(/\.git$/i, '').replace(/[^a-zA-Z0-9._-]/g, '-') || 'repository'; +} + +function resolveCloneTarget(workspaceRoot, remoteUrl) { + const root = path.resolve(String(workspaceRoot || '')); + if (!String(workspaceRoot || '').trim()) throw new Error('A project root is required.'); + const target = path.resolve(root, cloneDirectoryName(remoteUrl)); + const normalize = (value) => process.platform === 'win32' ? value.toLowerCase() : value; + const normalizedRoot = normalize(root); + const normalizedTarget = normalize(target); + if (normalizedTarget === normalizedRoot || !normalizedTarget.startsWith(`${normalizedRoot}${path.sep}`)) { + throw new Error('Clone target escapes the selected project root.'); + } + return { root, target, directoryName: path.basename(target) }; +} + +module.exports = { cloneDirectoryName, resolveCloneTarget }; diff --git a/src/shared/git-status.cjs b/src/shared/git-status.cjs new file mode 100644 index 0000000..630c938 --- /dev/null +++ b/src/shared/git-status.cjs @@ -0,0 +1,93 @@ +'use strict'; + +function parseBranchHeader(line, branch) { + if (line.startsWith('# branch.oid ')) branch.oid = line.slice(13).trim(); + if (line.startsWith('# branch.head ')) branch.head = line.slice(14).trim(); + if (line.startsWith('# branch.upstream ')) branch.upstream = line.slice(18).trim(); + if (line.startsWith('# branch.ab ')) { + const match = line.match(/\+(\d+)\s+-(\d+)/); + if (match) { + branch.ahead = Number(match[1]); + branch.behind = Number(match[2]); + } + } +} + +function statusLabel(code) { + const map = { + M: 'modified', A: 'added', D: 'deleted', R: 'renamed', C: 'copied', + U: 'conflict', T: 'type-changed', '?': 'untracked', '!': 'ignored', '.': 'clean', ' ': 'clean' + }; + return map[code] || 'changed'; +} + +function buildFile(path, originalPath, xy, kind) { + const indexCode = xy?.[0] || '.'; + const worktreeCode = xy?.[1] || '.'; + const conflict = kind === 'u' || indexCode === 'U' || worktreeCode === 'U'; + const untracked = kind === '?'; + return { + path, + originalPath: originalPath || null, + indexCode, + worktreeCode, + staged: !untracked && indexCode !== '.' && indexCode !== ' ', + unstaged: untracked || (worktreeCode !== '.' && worktreeCode !== ' '), + untracked, + conflict, + status: conflict ? 'conflict' : untracked ? 'untracked' : statusLabel(worktreeCode !== '.' ? worktreeCode : indexCode) + }; +} + +function parsePorcelainV2(output) { + const branch = { oid: null, head: null, upstream: null, ahead: 0, behind: 0 }; + const files = []; + const entries = String(output || '').split('\0'); + + for (let index = 0; index < entries.length; index += 1) { + const entry = entries[index]; + if (!entry) continue; + if (entry.startsWith('# ')) { + parseBranchHeader(entry, branch); + continue; + } + + const kind = entry[0]; + if (kind === '1') { + const parts = entry.split(' '); + const xy = parts[1]; + const path = parts.slice(8).join(' '); + files.push(buildFile(path, null, xy, kind)); + } else if (kind === '2') { + const parts = entry.split(' '); + const xy = parts[1]; + const path = parts.slice(9).join(' '); + const originalPath = entries[index + 1] || null; + index += 1; + files.push(buildFile(path, originalPath, xy, kind)); + } else if (kind === 'u') { + const parts = entry.split(' '); + const xy = parts[1]; + const path = parts.slice(10).join(' '); + files.push(buildFile(path, null, xy, kind)); + } else if (kind === '?' || kind === '!') { + const path = entry.slice(2); + if (kind === '?') files.push(buildFile(path, null, '??', kind)); + } + } + + return { + branch, + files, + counts: { + changed: files.length, + staged: files.filter((file) => file.staged).length, + unstaged: files.filter((file) => file.unstaged).length, + conflicts: files.filter((file) => file.conflict).length, + untracked: files.filter((file) => file.untracked).length + }, + clean: files.length === 0 + }; +} + +module.exports = { parsePorcelainV2, statusLabel }; diff --git a/src/shared/repository-match.cjs b/src/shared/repository-match.cjs new file mode 100644 index 0000000..6e47a15 --- /dev/null +++ b/src/shared/repository-match.cjs @@ -0,0 +1,39 @@ +'use strict'; + +function stripGitSuffix(value) { + return value.replace(/\.git$/i, '').replace(/^\/+|\/+$/g, ''); +} + +function normalizeRemoteUrl(remote) { + const raw = String(remote || '').trim(); + if (!raw) return null; + + const scp = raw.match(/^(?:[^@]+@)?([^:]+):(.+)$/); + if (scp && !raw.includes('://') && !/^[a-zA-Z]:[\\/]/.test(raw)) { + return { host: scp[1].toLowerCase(), path: stripGitSuffix(scp[2]).toLowerCase() }; + } + + try { + const url = new URL(raw); + return { host: url.hostname.toLowerCase(), path: stripGitSuffix(url.pathname).toLowerCase() }; + } catch { + return { host: '', path: stripGitSuffix(raw.replace(/\\/g, '/')).toLowerCase() }; + } +} + +function repositoryKey(repository) { + return String(repository?.full_name || `${repository?.owner?.login || repository?.owner || ''}/${repository?.name || ''}`) + .replace(/^\/+|\/+$/g, '') + .toLowerCase(); +} + +function matchRemoteToRepository(remote, repositories) { + const normalized = normalizeRemoteUrl(remote); + if (!normalized) return null; + return repositories.find((repository) => { + const key = repositoryKey(repository); + return normalized.path === key || normalized.path.endsWith(`/${key}`); + }) || null; +} + +module.exports = { normalizeRemoteUrl, repositoryKey, matchRemoteToRepository }; diff --git a/src/shared/semver.cjs b/src/shared/semver.cjs new file mode 100644 index 0000000..6a59cb3 --- /dev/null +++ b/src/shared/semver.cjs @@ -0,0 +1,32 @@ +'use strict'; + +function parseVersion(value) { + const match = String(value || '').trim().replace(/^v/i, '').match(/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/); + if (!match) return null; + return { + raw: String(value).trim(), + major: Number(match[1]), + minor: Number(match[2]), + patch: Number(match[3]), + prerelease: match[4] || '' + }; +} + +function compareVersions(leftValue, rightValue) { + const left = parseVersion(leftValue); + const right = parseVersion(rightValue); + if (!left || !right) throw new Error('Both versions must use semantic versioning (for example 1.2.3).'); + for (const key of ['major', 'minor', 'patch']) { + if (left[key] !== right[key]) return left[key] > right[key] ? 1 : -1; + } + if (left.prerelease === right.prerelease) return 0; + if (!left.prerelease) return 1; + if (!right.prerelease) return -1; + return left.prerelease.localeCompare(right.prerelease, undefined, { numeric: true }) > 0 ? 1 : -1; +} + +function isNewerVersion(candidate, current) { + return compareVersions(candidate, current) > 0; +} + +module.exports = { parseVersion, compareVersions, isNewerVersion }; diff --git a/src/shared/shell-verification.cjs b/src/shared/shell-verification.cjs new file mode 100644 index 0000000..208507a --- /dev/null +++ b/src/shared/shell-verification.cjs @@ -0,0 +1,36 @@ +const path = require('node:path'); + +function normalizeRelativePosixPath(value) { + if (typeof value !== 'string' || !value.trim()) { + throw new Error('Shell validation path must be a non-empty string.'); + } + const trimmed = value.trim(); + if (path.isAbsolute(trimmed) || /^[A-Za-z]:[\\/]/.test(trimmed)) { + throw new Error('Shell validation path must be relative to the project root.'); + } + const normalized = trimmed.replace(/\\/g, '/').replace(/^\.\//, ''); + if (normalized.split('/').some((segment) => segment === '..')) { + throw new Error('Shell validation path may not escape the project root.'); + } + return normalized; +} + +function bashSyntaxCheckInvocation(root, scriptPath = 'examples/server/forgeflow-deploy') { + if (typeof root !== 'string' || !root.trim()) { + throw new Error('Project root is required for shell validation.'); + } + return { + command: 'bash', + args: ['-n', normalizeRelativePosixPath(scriptPath)], + options: { + cwd: root, + encoding: 'utf8', + windowsHide: true + } + }; +} + +module.exports = { + bashSyntaxCheckInvocation, + normalizeRelativePosixPath +}; diff --git a/src/shared/tool-invocation.cjs b/src/shared/tool-invocation.cjs new file mode 100644 index 0000000..5463f0b --- /dev/null +++ b/src/shared/tool-invocation.cjs @@ -0,0 +1,42 @@ +'use strict'; + +function uniqueCandidates(candidates) { + const seen = new Set(); + return candidates.filter((candidate) => { + const key = JSON.stringify([candidate.file, candidate.args]); + if (seen.has(key)) return false; + seen.add(key); + return true; + }); +} + +function npmProbeCandidates(options = {}) { + const platform = options.platform || process.platform; + const env = options.env || process.env; + const execPath = options.execPath || process.execPath; + const candidates = []; + + // npm exposes the exact CLI entry point while running an npm script. Calling + // it through Node avoids Windows' inability to exec .cmd shims directly. + if (env.npm_execpath) { + candidates.push({ + file: env.npm_node_execpath || execPath, + args: [env.npm_execpath, '--version'], + source: 'npm_execpath' + }); + } + + if (platform === 'win32') { + candidates.push({ + file: env.ComSpec || env.COMSPEC || 'cmd.exe', + args: ['/d', '/s', '/c', 'npm --version'], + source: 'windows-command-shim' + }); + } else { + candidates.push({ file: 'npm', args: ['--version'], source: 'path' }); + } + + return uniqueCandidates(candidates); +} + +module.exports = { npmProbeCandidates }; diff --git a/src/shared/validation.cjs b/src/shared/validation.cjs new file mode 100644 index 0000000..3ca1bcd --- /dev/null +++ b/src/shared/validation.cjs @@ -0,0 +1,125 @@ +'use strict'; + +const path = require('node:path'); + +function normalizeBaseUrl(value) { + const raw = String(value || '').trim().replace(/\/+$/, ''); + if (!raw) throw new Error('Gitea URL is required.'); + const url = new URL(raw); + if (!['http:', 'https:'].includes(url.protocol)) throw new Error('Only HTTP and HTTPS URLs are supported.'); + if (url.username || url.password) throw new Error('Do not include credentials in the Gitea URL.'); + url.hash = ''; + url.search = ''; + return url.toString().replace(/\/$/, ''); +} + +function assertSafeRepositoryPath(value) { + if (!value || typeof value !== 'string') throw new Error('A repository path is required.'); + if (value.includes('\0')) throw new Error('Invalid repository path.'); + return path.resolve(value); +} + +function assertRepositoryRelativePath(value) { + const filePath = String(value || ''); + if (!filePath || filePath.includes('\0')) throw new Error('A repository-relative file path is required.'); + const normalized = filePath.replace(/\\/g, '/'); + if (path.posix.isAbsolute(normalized) || /^[a-zA-Z]:\//.test(normalized)) throw new Error('Absolute file paths are not allowed.'); + if (normalized.split('/').some((segment) => segment === '..')) throw new Error('File path may not escape the repository.'); + return normalized.replace(/^\.\//, ''); +} + +function assertRepositoryRelativePaths(values) { + if (!Array.isArray(values)) return []; + return [...new Set(values.filter(Boolean).map(assertRepositoryRelativePath))]; +} + +function assertCommitMessage(value) { + const message = String(value || '').trim(); + if (!message) throw new Error('Enter a commit message.'); + if (message.length > 5000) throw new Error('Commit message is too long.'); + if (message.includes('\0')) throw new Error('Commit message contains an invalid character.'); + return message; +} + +function assertFullCommitSha(value) { + const sha = String(value || '').trim(); + if (!/^[a-f0-9]{40,64}$/i.test(sha)) throw new Error('A full commit SHA is required.'); + return sha.toLowerCase(); +} + +function assertWorkflowFile(value) { + const workflow = assertRepositoryRelativePath(String(value || '').trim()); + if (!/^[a-zA-Z0-9._/-]+\.ya?ml$/i.test(workflow)) throw new Error('Workflow file must be a YAML filename.'); + return workflow; +} + + +function assertBranchName(value) { + const branch = String(value || '').trim(); + if (!branch) throw new Error('A branch name is required.'); + if (branch.length > 255) throw new Error('The branch name is too long.'); + if (branch === '@' || branch.startsWith('-') || branch.startsWith('/') || branch.endsWith('/') || branch.endsWith('.')) throw new Error('The branch name is invalid.'); + if (branch.includes('..') || branch.includes('@{') || branch.includes('//') || /[\x00-\x20\x7f~^:?*\[\\]/.test(branch)) throw new Error('The branch name is invalid.'); + if (branch.split('/').some((part) => !part || part.startsWith('.') || part.endsWith('.lock'))) throw new Error('The branch name is invalid.'); + return branch; +} + +function assertEnvironmentName(value) { + const environment = String(value || '').trim().toLowerCase(); + if (!/^[a-z0-9][a-z0-9._-]{0,63}$/.test(environment)) { + throw new Error('Environment must use 1-64 lowercase letters, numbers, dots, dashes or underscores.'); + } + return environment; +} + +function assertWorkflowFileName(value) { + const workflow = assertWorkflowFile(value); + if (workflow.includes('/')) throw new Error('Workflow must be a filename from .gitea/workflows, not a path.'); + return workflow; +} + +function assertDeploymentRequest(profile, sha) { + if (!profile) throw new Error('Deployment profile not found.'); + assertFullCommitSha(sha); + assertWorkflowFileName(profile.workflowFile); + assertBranchName(profile.branch); + assertEnvironmentName(profile.environment); + assertHttpUrl(profile.statusUrl, { label: 'Application status URL' }); +} + +function assertHttpUrl(value, { optional = false, label = 'URL' } = {}) { + const raw = String(value || '').trim(); + if (!raw && optional) return ''; + if (!raw) throw new Error(`${label} is required.`); + const url = new URL(raw); + if (!['http:', 'https:'].includes(url.protocol)) throw new Error(`${label} must use HTTP or HTTPS.`); + if (url.username || url.password) throw new Error(`${label} may not contain credentials.`); + return url.toString(); +} + +function assertCloneRemote(value) { + const remote = String(value || '').trim(); + if (!remote || remote.includes('\0')) throw new Error('Clone URL is required.'); + const scp = /^(?:[^@\s]+@)?[^:\s]+:[^\s]+$/.test(remote) && !remote.includes('://'); + if (scp) return remote; + const url = new URL(remote); + if (!['http:', 'https:', 'ssh:', 'git:'].includes(url.protocol)) throw new Error('Unsupported Git remote protocol.'); + if (url.password) throw new Error('Do not include a password in the clone URL.'); + return remote; +} + +module.exports = { + normalizeBaseUrl, + assertSafeRepositoryPath, + assertRepositoryRelativePath, + assertRepositoryRelativePaths, + assertCommitMessage, + assertFullCommitSha, + assertWorkflowFile, + assertWorkflowFileName, + assertBranchName, + assertEnvironmentName, + assertDeploymentRequest, + assertHttpUrl, + assertCloneRemote +}; diff --git a/src/shared/zip-writer.cjs b/src/shared/zip-writer.cjs new file mode 100644 index 0000000..63c6cb4 --- /dev/null +++ b/src/shared/zip-writer.cjs @@ -0,0 +1,91 @@ +'use strict'; + +const zlib = require('node:zlib'); + +const CRC_TABLE = (() => { + const table = new Uint32Array(256); + for (let n = 0; n < 256; n += 1) { + let c = n; + for (let k = 0; k < 8; k += 1) c = (c & 1) ? (0xedb88320 ^ (c >>> 1)) : (c >>> 1); + table[n] = c >>> 0; + } + return table; +})(); + +function crc32(buffer) { + let crc = 0xffffffff; + for (const byte of buffer) crc = CRC_TABLE[(crc ^ byte) & 0xff] ^ (crc >>> 8); + return (crc ^ 0xffffffff) >>> 0; +} + +function dosDateTime(date = new Date()) { + const year = Math.max(1980, date.getFullYear()); + const time = (date.getHours() << 11) | (date.getMinutes() << 5) | Math.floor(date.getSeconds() / 2); + const day = date.getDate(); + const month = date.getMonth() + 1; + const dosDate = ((year - 1980) << 9) | (month << 5) | day; + return { time, date: dosDate }; +} + +function createZip(entries) { + const localParts = []; + const centralParts = []; + let offset = 0; + const stamp = dosDateTime(); + + for (const entry of entries) { + const name = Buffer.from(String(entry.name).replace(/\\/g, '/').replace(/^\/+/, ''), 'utf8'); + const source = Buffer.isBuffer(entry.data) ? entry.data : Buffer.from(String(entry.data ?? ''), 'utf8'); + const compressed = zlib.deflateRawSync(source, { level: 6 }); + const checksum = crc32(source); + + const local = Buffer.alloc(30); + local.writeUInt32LE(0x04034b50, 0); + local.writeUInt16LE(20, 4); + local.writeUInt16LE(0x0800, 6); + local.writeUInt16LE(8, 8); + local.writeUInt16LE(stamp.time, 10); + local.writeUInt16LE(stamp.date, 12); + local.writeUInt32LE(checksum, 14); + local.writeUInt32LE(compressed.length, 18); + local.writeUInt32LE(source.length, 22); + local.writeUInt16LE(name.length, 26); + local.writeUInt16LE(0, 28); + localParts.push(local, name, compressed); + + const central = Buffer.alloc(46); + central.writeUInt32LE(0x02014b50, 0); + central.writeUInt16LE(20, 4); + central.writeUInt16LE(20, 6); + central.writeUInt16LE(0x0800, 8); + central.writeUInt16LE(8, 10); + central.writeUInt16LE(stamp.time, 12); + central.writeUInt16LE(stamp.date, 14); + central.writeUInt32LE(checksum, 16); + central.writeUInt32LE(compressed.length, 20); + central.writeUInt32LE(source.length, 24); + central.writeUInt16LE(name.length, 28); + central.writeUInt16LE(0, 30); + central.writeUInt16LE(0, 32); + central.writeUInt16LE(0, 34); + central.writeUInt16LE(0, 36); + central.writeUInt32LE(0, 38); + central.writeUInt32LE(offset, 42); + centralParts.push(central, name); + offset += local.length + name.length + compressed.length; + } + + const centralDirectory = Buffer.concat(centralParts); + const end = Buffer.alloc(22); + end.writeUInt32LE(0x06054b50, 0); + end.writeUInt16LE(0, 4); + end.writeUInt16LE(0, 6); + end.writeUInt16LE(entries.length, 8); + end.writeUInt16LE(entries.length, 10); + end.writeUInt32LE(centralDirectory.length, 12); + end.writeUInt32LE(offset, 16); + end.writeUInt16LE(0, 20); + return Buffer.concat([...localParts, centralDirectory, end]); +} + +module.exports = { createZip, crc32 }; diff --git a/tests/clone-target.test.mjs b/tests/clone-target.test.mjs new file mode 100644 index 0000000..6f24f64 --- /dev/null +++ b/tests/clone-target.test.mjs @@ -0,0 +1,91 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import os from 'node:os'; +import path from 'node:path'; +import fs from 'node:fs/promises'; +import { execFile } from 'node:child_process'; +import { promisify } from 'node:util'; +import cloneTargetModule from '../src/shared/clone-target.cjs'; +import gitModule from '../src/main/git-service.cjs'; + +const exec = promisify(execFile); +const { cloneDirectoryName, resolveCloneTarget } = cloneTargetModule; +const { GitService } = gitModule; + +test('derives a safe repository folder name from HTTPS and SSH clone URLs', () => { + assert.equal(cloneDirectoryName('https://gitea.example.test/jens/ForgeFlow.git'), 'ForgeFlow'); + assert.equal(cloneDirectoryName('git@gitea.example.test:jens/my-app.git'), 'my-app'); + assert.equal(cloneDirectoryName('ssh://git@gitea.example.test/jens/app.git?ref=main'), 'app'); +}); + +test('resolves the automatic clone target inside the configured project root', () => { + const root = path.join(os.tmpdir(), 'forgeflow-projects'); + const plan = resolveCloneTarget(root, 'https://gitea.example.test/jens/portfolio.git'); + assert.equal(plan.root, path.resolve(root)); + assert.equal(plan.target, path.join(path.resolve(root), 'portfolio')); + assert.equal(plan.directoryName, 'portfolio'); +}); + +test('clone target inspection accepts missing and empty destinations', async (t) => { + const root = await fs.mkdtemp(path.join(os.tmpdir(), 'forgeflow-clone-target-')); + t.after(() => fs.rm(root, { recursive: true, force: true })); + const service = new GitService(); + const remote = 'https://gitea.example.test/jens/app.git'; + + const missing = await service.inspectCloneTarget(remote, path.join(root, 'missing-app')); + assert.equal(missing.state, 'missing'); + + const emptyPath = path.join(root, 'empty-app'); + await fs.mkdir(emptyPath); + const empty = await service.inspectCloneTarget(remote, emptyPath); + assert.equal(empty.state, 'empty'); +}); + +test('clone target inspection reuses an existing checkout with the same origin', async (t) => { + const root = await fs.mkdtemp(path.join(os.tmpdir(), 'forgeflow-clone-reuse-')); + t.after(() => fs.rm(root, { recursive: true, force: true })); + const target = path.join(root, 'app'); + await fs.mkdir(target); + await exec('git', ['init'], { cwd: target, encoding: 'utf8' }); + await exec('git', ['remote', 'add', 'origin', 'git@gitea.example.test:jens/app.git'], { cwd: target, encoding: 'utf8' }); + + const service = new GitService(); + const assessment = await service.inspectCloneTarget('https://gitea.example.test/jens/app.git', target); + assert.equal(assessment.state, 'matching-repository'); +}); + +test('clone target inspection blocks a different repository and ordinary files', async (t) => { + const root = await fs.mkdtemp(path.join(os.tmpdir(), 'forgeflow-clone-conflict-')); + t.after(() => fs.rm(root, { recursive: true, force: true })); + const service = new GitService(); + + const otherRepository = path.join(root, 'repository'); + await fs.mkdir(otherRepository); + await exec('git', ['init'], { cwd: otherRepository, encoding: 'utf8' }); + await exec('git', ['remote', 'add', 'origin', 'https://gitea.example.test/jens/other.git'], { cwd: otherRepository, encoding: 'utf8' }); + await assert.rejects( + service.inspectCloneTarget('https://gitea.example.test/jens/app.git', otherRepository), + (error) => error.code === 'CLONE_TARGET_DIFFERENT_REPOSITORY' + ); + + const ordinaryFolder = path.join(root, 'ordinary'); + await fs.mkdir(ordinaryFolder); + await fs.writeFile(path.join(ordinaryFolder, 'notes.txt'), 'do not overwrite\n'); + await assert.rejects( + service.inspectCloneTarget('https://gitea.example.test/jens/app.git', ordinaryFolder), + (error) => error.code === 'CLONE_TARGET_NOT_EMPTY' + ); +}); + +test('clone target inspection blocks a file at the automatic destination', async (t) => { + const root = await fs.mkdtemp(path.join(os.tmpdir(), 'forgeflow-clone-file-')); + t.after(() => fs.rm(root, { recursive: true, force: true })); + const target = path.join(root, 'app'); + await fs.writeFile(target, 'not a directory'); + + const service = new GitService(); + await assert.rejects( + service.inspectCloneTarget('https://gitea.example.test/jens/app.git', target), + (error) => error.code === 'CLONE_TARGET_NOT_DIRECTORY' + ); +}); diff --git a/tests/deployment-status.test.mjs b/tests/deployment-status.test.mjs new file mode 100644 index 0000000..5406fad --- /dev/null +++ b/tests/deployment-status.test.mjs @@ -0,0 +1,154 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import deploymentModule from '../src/main/deployment-service.cjs'; + +const { DeploymentService, terminalRunConclusion, applicationVerificationFailure } = deploymentModule; + +test('maps runner conclusions to ForgeFlow terminal states', () => { + assert.equal(terminalRunConclusion({ conclusion: 'success' }), 'success'); + assert.equal(terminalRunConclusion({ conclusion: 'failure' }), 'failed'); + assert.equal(terminalRunConclusion({ status: 'timed_out' }), 'failed'); + assert.equal(terminalRunConclusion({ conclusion: 'cancelled' }), 'cancelled'); + assert.equal(terminalRunConclusion({ status: 'running' }), null); +}); + + +test('dispatches only controlled deployment inputs', async () => { + const sha = 'a'.repeat(40); + let dispatched = null; + const operations = new Map(); + const profile = { + id: 'staging', name: 'Staging', environment: 'staging', branch: 'main', + workflowFile: 'deploy.yml', rollbackWorkflowFile: 'rollback.yml', + statusUrl: 'https://app.example.test/.well-known/forgeflow', + inputs: { commit_sha: 'b'.repeat(40), request_id: 'forged', arbitrary: 'ignored' } + }; + const store = { + getDeploymentProfile: () => profile, + getToken: () => '', + addOperation: async (operation) => { operations.set(operation.id, structuredClone(operation)); return structuredClone(operation); } + }; + const service = new DeploymentService(store, { + dispatchWorkflow: async (payload) => { dispatched = payload; return { accepted: true, status: 204 }; } + }, { + status: async () => ({ head: sha, clean: true, counts: { changed: 0 }, branch: { head: 'main', upstream: 'origin/main', ahead: 0, behind: 0 } }), + verifyCommitOnRemoteBranch: async () => ({ valid: true }) + }, { info: async () => {}, error: async () => {} }); + + const operation = await service.deploy({ repository: { fullName: 'jens/app', localPath: '/repo' }, profileId: profile.id, sha }); + assert.deepEqual(Object.keys(dispatched.inputs).sort(), ['commit_sha', 'environment', 'request_id']); + assert.equal(dispatched.inputs.commit_sha, sha); + assert.equal(dispatched.inputs.environment, 'staging'); + assert.equal(dispatched.inputs.request_id, operation.id); + assert.equal(dispatched.inputs.arbitrary, undefined); +}); + +test('requires exact server SHA and matching request ID after a successful workflow', () => { + const operation = { id: 'request-1', repository: 'jens/app', environment: 'staging', sha: 'a'.repeat(40), shortSha: 'aaaaaaa' }; + assert.equal(applicationVerificationFailure(operation, { + statusConfigured: true, statusReachable: true, statusRepository: 'jens/app', statusEnvironment: 'staging', + liveSha: operation.sha, requestedSha: operation.sha, requestId: operation.id, lastExitCode: 0, healthy: true + }), null); + assert.match(applicationVerificationFailure(operation, { + statusConfigured: true, statusReachable: true, statusRepository: 'jens/app', statusEnvironment: 'staging', + liveSha: operation.sha, requestedSha: operation.sha, requestId: 'another-request', lastExitCode: 0, healthy: true + }).message, /different deployment request/i); + assert.match(applicationVerificationFailure(operation, { + statusConfigured: true, statusReachable: true, statusRepository: 'jens/app', statusEnvironment: 'staging', + liveSha: 'b'.repeat(40), requestedSha: operation.sha, requestId: operation.id, lastExitCode: 0, healthy: true + }).message, /server reports/i); + assert.match(applicationVerificationFailure(operation, { + statusConfigured: true, statusReachable: false, liveSha: null, + requestId: null, healthy: null + }).message, /not reachable/i); + assert.match(applicationVerificationFailure(operation, { + statusConfigured: true, statusReachable: true, statusRepository: 'other/app', statusEnvironment: 'staging', + liveSha: operation.sha, requestedSha: operation.sha, requestId: operation.id, lastExitCode: 0, healthy: true + }).message, /belongs to other\/app/i); + assert.match(applicationVerificationFailure(operation, { + statusConfigured: true, statusReachable: true, statusRepository: 'jens/app', statusEnvironment: 'staging', + liveSha: operation.sha, requestedSha: operation.sha, requestId: operation.id, lastExitCode: 70, healthy: false + }).message, /exit code 70/i); +}); + +test('rollback accepts only the currently reported previous SHA and dispatches controlled inputs', async () => { + const liveSha = 'a'.repeat(40); + const previousSha = 'b'.repeat(40); + let dispatched = null; + let verified = null; + const operations = new Map(); + const profile = { + id: 'production', name: 'Production', environment: 'production', branch: 'main', + workflowFile: 'deploy.yml', rollbackWorkflowFile: 'rollback.yml', + statusUrl: 'https://app.example.test/.well-known/forgeflow', + inputs: { target_sha: 'c'.repeat(40), request_id: 'forged', arbitrary: 'ignored' } + }; + const store = { + getDeploymentProfile: () => profile, + getToken: () => '', + addOperation: async (operation) => { operations.set(operation.id, structuredClone(operation)); return structuredClone(operation); } + }; + const service = new DeploymentService(store, { + listWorkflowRuns: async () => ({ runs: [] }), + dispatchWorkflow: async (payload) => { dispatched = payload; return { accepted: true, status: 204 }; } + }, { + verifyCommitOnRemoteBranch: async (...args) => { verified = args; return { valid: true }; } + }, { info: async () => {}, warning: async () => {}, error: async () => {} }); + service.refreshProfileState = async () => ({ + statusReachable: true, + statusRepository: 'jens/app', + statusEnvironment: 'production', + liveSha, + previousSha + }); + + const operation = await service.rollback({ + repository: { fullName: 'jens/app', localPath: '/repo' }, + profileId: profile.id, + targetSha: previousSha + }); + + assert.deepEqual(verified, ['/repo', previousSha, 'main']); + assert.deepEqual(Object.keys(dispatched.inputs).sort(), ['environment', 'request_id', 'target_sha']); + assert.equal(dispatched.inputs.environment, 'production'); + assert.equal(dispatched.inputs.target_sha, previousSha); + assert.equal(dispatched.inputs.request_id, operation.id); + assert.equal(dispatched.inputs.arbitrary, undefined); +}); + +test('rollback refuses a stale target that is no longer the server-reported previous SHA', async () => { + const previousSha = 'b'.repeat(40); + let dispatched = false; + const profile = { + id: 'production', name: 'Production', environment: 'production', branch: 'main', + workflowFile: 'deploy.yml', rollbackWorkflowFile: 'rollback.yml', + statusUrl: 'https://app.example.test/.well-known/forgeflow' + }; + const service = new DeploymentService({ + getDeploymentProfile: () => profile, + getToken: () => '', + addOperation: async (operation) => operation + }, { + dispatchWorkflow: async () => { dispatched = true; } + }, { + verifyCommitOnRemoteBranch: async () => ({ valid: true }) + }, { info: async () => {}, warning: async () => {}, error: async () => {} }); + service.refreshProfileState = async () => ({ + statusReachable: true, + statusRepository: 'jens/app', + statusEnvironment: 'production', + liveSha: 'a'.repeat(40), + previousSha + }); + + await assert.rejects( + service.rollback({ + repository: { fullName: 'jens/app', localPath: '/repo' }, + profileId: profile.id, + targetSha: 'c'.repeat(40) + }), + /no longer the previous server version/i + ); + assert.equal(dispatched, false); +}); + diff --git a/tests/diagnostics.test.mjs b/tests/diagnostics.test.mjs new file mode 100644 index 0000000..23b9abe --- /dev/null +++ b/tests/diagnostics.test.mjs @@ -0,0 +1,75 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { mkdtemp, readFile, rm } from 'node:fs/promises'; +import os from 'node:os'; +import path from 'node:path'; +import zlib from 'node:zlib'; +import diagnosticsModule from '../src/main/diagnostics-service.cjs'; + +const { DiagnosticsService } = diagnosticsModule; + +function unzipLocalEntries(buffer) { + const entries = new Map(); + let offset = 0; + while (offset + 4 <= buffer.length && buffer.readUInt32LE(offset) === 0x04034b50) { + const method = buffer.readUInt16LE(offset + 8); + const compressedSize = buffer.readUInt32LE(offset + 18); + const nameLength = buffer.readUInt16LE(offset + 26); + const extraLength = buffer.readUInt16LE(offset + 28); + const nameStart = offset + 30; + const dataStart = nameStart + nameLength + extraLength; + const name = buffer.subarray(nameStart, nameStart + nameLength).toString('utf8'); + const compressed = buffer.subarray(dataStart, dataStart + compressedSize); + entries.set(name, method === 8 ? zlib.inflateRawSync(compressed) : compressed); + offset = dataStart + compressedSize; + } + return entries; +} + +test('writes structured local diagnostics and exports a secret-free support bundle', async (t) => { + const root = await mkdtemp(path.join(os.tmpdir(), 'forgeflow-diagnostics-')); + t.after(() => rm(root, { recursive: true, force: true })); + const secret = ['gitea', 'TEST', 'ONLY', 'ULTRA', 'SECRET', '1234567890'].join('_'); + const service = new DiagnosticsService({ + userDataPath: root, + appInfo: { name: 'ForgeFlow', version: '0.3.0-test' }, + secretProvider: () => [secret], + preferencesProvider: () => ({ diagnosticsEnabled: true, diagnosticLevel: 'debug', logRetentionDays: 14, maxLogFileMb: 8 }) + }); + await service.initialize(); + await service.error('test.failure', { + authorization: `token ${secret}`, + password: 'unsafe-password', + message: `request failed with ${secret}`, + path: path.join(os.homedir(), 'private', 'repository') + }); + await service.flush(); + + const status = await service.getStatus(); + assert.equal(status.enabled, true); + assert.ok(status.fileCount >= 1); + const raw = (await Promise.all((await service.listLogFiles()).map((file) => readFile(file.path, 'utf8')))).join('\n'); + assert.doesNotMatch(raw, new RegExp(secret)); + assert.doesNotMatch(raw, /unsafe-password/); + assert.doesNotMatch(raw, new RegExp(os.homedir().replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))); + + const destination = path.join(root, 'support.zip'); + const result = await service.exportSupportBundle({ + destinationPath: destination, + privacyMode: 'strict', + publicState: { gitea: { baseUrl: 'https://gitea.example.test', hasToken: true, encryptedToken: 'ciphertext' }, preferences: {} }, + repositories: [{ id: 1, fullName: 'jens/private-repo', localPath: path.join(os.homedir(), 'private-repo'), localStatus: { head: 'a'.repeat(40), branch: { head: 'main' }, counts: {}, clean: true } }], + operations: [{ repository: 'jens/private-repo', status: 'failed', runnerLog: `Authorization: token ${secret}` }], + preflight: { checks: [] } + }); + assert.equal(service.isKnownBundlePath(result.path), true); + const entries = unzipLocalEntries(await readFile(destination)); + const bundleText = [...entries.values()].map((value) => value.toString('utf8')).join('\n'); + assert.doesNotMatch(bundleText, new RegExp(secret)); + assert.doesNotMatch(bundleText, /ciphertext|unsafe-password|jens\/private-repo/); + assert.match(entries.get('manifest.json').toString(), /"containsSecrets": false/); + assert.match(entries.get('repositories-sanitized.json').toString(), /fullname-[a-f0-9]{12}/); + + const cleared = await service.clear(); + assert.ok(cleared.fileCount >= 1, 'clear writes a new safe session marker'); +}); diff --git a/tests/git-integration.test.mjs b/tests/git-integration.test.mjs new file mode 100644 index 0000000..dde12d9 --- /dev/null +++ b/tests/git-integration.test.mjs @@ -0,0 +1,54 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import os from 'node:os'; +import path from 'node:path'; +import fs from 'node:fs/promises'; +import { execFile } from 'node:child_process'; +import { promisify } from 'node:util'; +import gitModule from '../src/main/git-service.cjs'; + +const exec = promisify(execFile); +const { GitService } = gitModule; + +async function git(args, cwd) { + return exec('git', args, { cwd, encoding: 'utf8' }); +} + +test('GitService reads changes and commits/pushes selected files to a real bare remote', async (t) => { + const root = await fs.mkdtemp(path.join(os.tmpdir(), 'forgeflow-git-')); + t.after(() => fs.rm(root, { recursive: true, force: true })); + + const remote = path.join(root, 'remote.git'); + const working = path.join(root, 'working'); + await git(['init', '--bare', remote], root); + await git(['clone', remote, working], root); + await git(['config', 'user.name', 'ForgeFlow Test'], working); + await git(['config', 'user.email', 'forgeflow@example.invalid'], working); + await fs.writeFile(path.join(working, 'README.md'), '# ForgeFlow\n'); + await git(['add', 'README.md'], working); + await git(['commit', '-m', 'Initial commit'], working); + await git(['branch', '-M', 'main'], working); + await git(['push', '-u', 'origin', 'main'], working); + + await fs.appendFile(path.join(working, 'README.md'), '\nDesktop release cockpit.\n'); + await fs.writeFile(path.join(working, 'feature.txt'), 'new file\n'); + + const service = new GitService(); + const before = await service.status(working); + assert.equal(before.branch.head, 'main'); + assert.equal(before.branch.ahead, 0); + assert.equal(before.branch.behind, 0); + assert.equal(before.counts.changed, 2); + assert.deepEqual(new Set(before.files.map((file) => file.path)), new Set(['README.md', 'feature.txt'])); + + const diff = await service.diff(working, 'README.md'); + assert.match(diff, /Desktop release cockpit/); + + const result = await service.commitAndPush(working, 'Add desktop cockpit copy', ['README.md', 'feature.txt']); + assert.equal(result.status.clean, true); + assert.equal(result.status.branch.ahead, 0); + assert.match(result.pushOutput, /main/); + + const remoteLog = await git(['--git-dir', remote, 'log', '-1', '--pretty=%s', 'refs/heads/main'], root); + assert.equal(remoteLog.stdout.trim(), 'Add desktop cockpit copy'); +}); diff --git a/tests/git-status.test.mjs b/tests/git-status.test.mjs new file mode 100644 index 0000000..1f19a7f --- /dev/null +++ b/tests/git-status.test.mjs @@ -0,0 +1,34 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import gitStatus from '../src/shared/git-status.cjs'; + +const { parsePorcelainV2 } = gitStatus; + +test('parses branch metadata and ordinary changes', () => { + const output = [ + '# branch.oid 0123456789abcdef', + '# branch.head main', + '# branch.upstream origin/main', + '# branch.ab +2 -1', + '1 .M N... 100644 100644 100644 abc def src/main.js', + '1 M. N... 100644 100644 100644 abc def README.md', + '? new file.txt', + '' + ].join('\0'); + const parsed = parsePorcelainV2(output); + assert.equal(parsed.branch.head, 'main'); + assert.equal(parsed.branch.ahead, 2); + assert.equal(parsed.branch.behind, 1); + assert.equal(parsed.counts.changed, 3); + assert.equal(parsed.counts.staged, 1); + assert.equal(parsed.counts.untracked, 1); + assert.equal(parsed.files[0].path, 'src/main.js'); +}); + +test('parses rename records with original path', () => { + const output = '2 R. N... 100644 100644 100644 abc def R100 src/new.js\0src/old.js\0'; + const parsed = parsePorcelainV2(output); + assert.equal(parsed.files[0].path, 'src/new.js'); + assert.equal(parsed.files[0].originalPath, 'src/old.js'); + assert.equal(parsed.files[0].status, 'renamed'); +}); diff --git a/tests/git-workflows.test.mjs b/tests/git-workflows.test.mjs new file mode 100644 index 0000000..738d2fb --- /dev/null +++ b/tests/git-workflows.test.mjs @@ -0,0 +1,46 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import os from 'node:os'; +import path from 'node:path'; +import fs from 'node:fs/promises'; +import { execFile } from 'node:child_process'; +import { promisify } from 'node:util'; +import gitModule from '../src/main/git-service.cjs'; + +const exec = promisify(execFile); +const { GitService } = gitModule; +const git = (args, cwd) => exec('git', args, { cwd, encoding: 'utf8' }); + +test('supports commit-only, branch creation, stash lifecycle and remote SHA verification', async (t) => { + const root = await fs.mkdtemp(path.join(os.tmpdir(), 'forgeflow-git-workflow-')); + t.after(() => fs.rm(root, { recursive: true, force: true })); + const remote = path.join(root, 'remote.git'); + const working = path.join(root, 'working'); + await git(['init', '--bare', remote], root); + await git(['clone', remote, working], root); + await git(['config', 'user.name', 'ForgeFlow Test'], working); + await git(['config', 'user.email', 'forgeflow@example.invalid'], working); + await fs.writeFile(path.join(working, 'README.md'), '# ForgeFlow\n'); + await git(['add', '.'], working); + await git(['commit', '-m', 'Initial'], working); + await git(['branch', '-M', 'main'], working); + await git(['push', '-u', 'origin', 'main'], working); + + const service = new GitService(); + const branchStatus = await service.createBranch(working, 'feature/release-flow'); + assert.equal(branchStatus.branch.head, 'feature/release-flow'); + await fs.writeFile(path.join(working, 'release.txt'), 'release cockpit\n'); + const committed = await service.commit(working, 'Add release flow', ['release.txt']); + assert.equal(committed.status.branch.ahead, 0, 'unpublished branches have no upstream-based ahead count'); + const pushed = await service.push(working); + assert.equal(pushed.status.branch.upstream, 'origin/feature/release-flow'); + await service.verifyCommitOnRemoteBranch(working, committed.sha, 'feature/release-flow'); + + await fs.appendFile(path.join(working, 'release.txt'), 'local draft\n'); + await fs.writeFile(path.join(working, 'untracked.txt'), 'draft\n'); + const stashed = await service.stash(working, 'Draft release work'); + assert.equal(stashed.status.clean, true); + assert.equal(stashed.stashes.length, 1); + const restored = await service.popStash(working, stashed.stashes[0].ref); + assert.equal(restored.status.counts.changed, 2); +}); diff --git a/tests/gitea-actions.test.mjs b/tests/gitea-actions.test.mjs new file mode 100644 index 0000000..680d9fb --- /dev/null +++ b/tests/gitea-actions.test.mjs @@ -0,0 +1,90 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import giteaModule from '../src/main/gitea-service.cjs'; + +const { GiteaService } = giteaModule; + +function makeStore() { + return { data: { gitea: { baseUrl: 'https://gitea.example.test' } }, getToken: () => 'demo-token' }; +} + +test('normalizes run payloads from different Actions API shapes', () => { + const service = new GiteaService(makeStore()); + const run = service.normalizeRun({ + task_id: 42, + index: 7, + workflow_name: 'Deploy', + status: 'success', + commit: { sha: 'a'.repeat(40) }, + ref: 'refs/heads/main', + workflow_file: '.gitea/workflows/deploy.yml', + start_time: '2026-07-24T12:00:00Z' + }); + assert.equal(run.id, 42); + assert.equal(run.runNumber, 7); + assert.equal(run.conclusion, 'success'); + assert.equal(run.headSha, 'a'.repeat(40)); + assert.equal(run.headBranch, 'refs/heads/main'); +}); + +test('retries Actions runs without optional filters when a server rejects them', async () => { + const service = new GiteaService(makeStore()); + const calls = []; + service.request = async (pathname) => { + calls.push(pathname); + if (calls.length === 1) { + const error = new Error('Unsupported query'); + error.status = 422; + throw error; + } + return { data: { workflow_runs: [{ id: 11, run_number: 11, status: 'queued', head_sha: 'b'.repeat(40), head_branch: 'main' }] } }; + }; + const result = await service.listWorkflowRuns({ owner: 'jens', repo: 'app', sha: 'b'.repeat(40), branch: 'main' }); + assert.equal(calls.length, 2); + assert.match(calls[0], /head_sha=/); + assert.doesNotMatch(calls[1], /head_sha=/); + assert.equal(result.source, 'runs'); + assert.equal(result.runs[0].runNumber, 11); +}); + +test('falls back to legacy Actions tasks endpoint when runs is unavailable', async () => { + const service = new GiteaService(makeStore()); + const calls = []; + service.request = async (pathname) => { + calls.push(pathname); + if (pathname.includes('/runs?')) { + const error = new Error('Not found'); + error.status = 404; + throw error; + } + return { data: [{ task_id: 9, index: 3, status: 'running', commit_sha: 'c'.repeat(40), branch: 'main' }] }; + }; + const result = await service.listWorkflowRuns({ owner: 'jens', repo: 'app' }); + assert.equal(result.source, 'tasks'); + assert.equal(result.runs[0].id, 9); + assert.ok(calls.some((pathname) => pathname.includes('/tasks?'))); +}); + +test('selects the newest matching workflow run', async () => { + const service = new GiteaService(makeStore()); + service.listWorkflowRuns = async () => ({ source: 'runs', runs: [ + { id: 1, headSha: 'd'.repeat(40), headBranch: 'main', workflowPath: 'deploy.yml', createdAt: '2026-07-24T10:00:00Z' }, + { id: 2, headSha: 'd'.repeat(40), headBranch: 'main', workflowPath: '.gitea/workflows/deploy.yml', createdAt: '2026-07-24T11:00:00Z' }, + { id: 3, headSha: 'e'.repeat(40), headBranch: 'main', workflowPath: 'deploy.yml', createdAt: '2026-07-24T12:00:00Z' } + ] }); + const found = await service.findWorkflowRun({ owner: 'jens', repo: 'app', sha: 'd'.repeat(40), branch: 'main', workflowFile: 'deploy.yml' }); + assert.equal(found.run.id, 2); + const excludingNewest = await service.findWorkflowRun({ owner: 'jens', repo: 'app', sha: 'd'.repeat(40), branch: 'main', workflowFile: 'deploy.yml', excludeRunIds: [2] }); + assert.equal(excludingNewest.run.id, 1); +}); + +test('checks repository workflow files through the contents API', async () => { + const service = new GiteaService(makeStore()); + const calls = []; + service.request = async (pathname) => { calls.push(pathname); return { data: { type: 'file' } }; }; + assert.equal(await service.repositoryFileExists({ owner: 'jens', repo: 'app', filePath: '.gitea/workflows/deploy.yml', ref: 'main' }), true); + assert.match(calls[0], /contents\/\.gitea\/workflows\/deploy\.yml\?ref=main/); + + service.request = async () => { const error = new Error('missing'); error.status = 404; throw error; }; + assert.equal(await service.repositoryFileExists({ owner: 'jens', repo: 'app', filePath: '.gitea/workflows/missing.yml', ref: 'main' }), false); +}); diff --git a/tests/log-redaction.test.mjs b/tests/log-redaction.test.mjs new file mode 100644 index 0000000..c131353 --- /dev/null +++ b/tests/log-redaction.test.mjs @@ -0,0 +1,47 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import redaction from '../src/main/log-redaction.cjs'; + +const { redactSecrets, sanitizeForDiagnostics, pathAlias, stableAlias } = redaction; + +test('redacts runtime credentials, structured secrets, private keys and URL credentials', () => { + const token = ['gitea', 'TEST', 'ONLY', 'SecretToken123456'].join('_'); + const input = [ + `Authorization: Bearer ${token}`, + `https://${['jens', 'p4ssw0rd'].join(':')}@gitea.example.test/api?access_token=${token}`, + 'client_secret=another-secret-value', + ['-----BEGIN', 'PRIVATE KEY-----\nsecret-key-material\n-----END PRIVATE KEY-----'].join(' ') + ].join('\n'); + const output = redactSecrets(input, [token]); + assert.doesNotMatch(output, /ThisIsARealisticSecret|p4ssw0rd|another-secret-value|secret-key-material/); + assert.match(output, /REDACTED/); +}); + +test('sanitizes nested sensitive keys and aliases user paths', () => { + const value = { + accessToken: 'do-not-keep', + nested: { password: 'do-not-keep-either', path: 'C:\\Users\\Jens\\Projects\\ForgeFlow' }, + home: '/home/jens/projects/forgeflow' + }; + const sanitized = sanitizeForDiagnostics(value, { homeDir: '/home/jens', cwd: '/work/ForgeFlow' }); + assert.equal(sanitized.accessToken, '[REDACTED]'); + assert.equal(sanitized.nested.password, '[REDACTED]'); + assert.doesNotMatch(JSON.stringify(sanitized), /do-not-keep|Users\\Jens|\/home\/jens/); + assert.match(JSON.stringify(sanitized), //); +}); + +test('strict privacy mode replaces stable identifiers deterministically', () => { + const first = sanitizeForDiagnostics({ fullName: 'jens/private-project', login: 'jens' }, { strictIdentifiers: true }); + const second = sanitizeForDiagnostics({ fullName: 'jens/private-project', login: 'jens' }, { strictIdentifiers: true }); + assert.equal(first.fullName, second.fullName); + assert.equal(first.login, second.login); + assert.notEqual(first.fullName, 'jens/private-project'); + assert.match(first.fullName, /^fullname-[a-f0-9]{12}$/); + assert.equal(stableAlias('same', 'repo'), stableAlias('same', 'repo')); +}); + +test('path aliasing handles slash variants', () => { + const result = pathAlias('C:\\Users\\Jens\\src and C:/Users/Jens/src', { homeDir: 'C:\\Users\\Jens', cwd: 'D:\\ForgeFlow' }); + assert.doesNotMatch(result, /Users[\\/]Jens/); + assert.match(result, //); +}); diff --git a/tests/preflight.test.mjs b/tests/preflight.test.mjs new file mode 100644 index 0000000..5129041 --- /dev/null +++ b/tests/preflight.test.mjs @@ -0,0 +1,65 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises'; +import os from 'node:os'; +import path from 'node:path'; +import preflightModule from '../src/main/preflight-service.cjs'; + +const { PreflightService, summarize, check } = preflightModule; + +test('only required failed checks block readiness', () => { + const summary = summarize([ + check('required-pass', 'Required pass', 'pass', 'ok', { required: true }), + check('optional-warning', 'Optional warning', 'warning', 'notice'), + check('optional-fail', 'Optional fail', 'fail', 'not blocking'), + check('required-fail', 'Required fail', 'fail', 'blocked', { required: true }) + ]); + assert.equal(summary.ready, false); + assert.deepEqual(summary.blocking, ['required-fail']); + assert.equal(summary.counts.warning, 1); +}); + +test('system preflight can pass before credentials are entered', async (t) => { + const root = await mkdtemp(path.join(os.tmpdir(), 'forgeflow-preflight-system-')); + t.after(() => rm(root, { recursive: true, force: true })); + const service = new PreflightService({ + store: { data: { gitea: { baseUrl: '' } }, getToken: () => '' }, + git: { isAvailable: async () => ({ available: true, version: 'git version test' }) }, + gitea: {}, deployments: {}, + diagnostics: { logDirectory: path.join(root, 'diagnostics'), info: async () => {} }, + userDataPath: path.join(root, 'data'), + secureStorageAvailable: () => true + }); + service.gitIdentity = async () => ({ name: 'Jens', email: 'jens@example.test' }); + const result = await service.runSystem({ roots: [root] }); + assert.equal(result.summary.ready, true); + assert.equal(result.checks.find((item) => item.id === 'gitea.connection').status, 'warning'); + assert.equal(result.checks.find((item) => item.id === 'storage.credentials').status, 'pass'); +}); + +test('deployment preflight verifies exact Git, workflow, Actions and server prerequisites', async (t) => { + const root = await mkdtemp(path.join(os.tmpdir(), 'forgeflow-preflight-deploy-')); + t.after(() => rm(root, { recursive: true, force: true })); + await mkdir(path.join(root, '.gitea', 'workflows'), { recursive: true }); + await writeFile(path.join(root, '.gitea', 'workflows', 'deploy.yml'), 'name: deploy\n'); + await writeFile(path.join(root, '.gitea', 'workflows', 'rollback.yml'), 'name: rollback\n'); + const sha = 'a'.repeat(40); + const profile = { id: 'production', name: 'Production', environment: 'production', branch: 'main', workflowFile: 'deploy.yml', rollbackWorkflowFile: 'rollback.yml', statusUrl: 'https://app.example.test/status', healthcheckUrl: 'https://app.example.test/health' }; + const service = new PreflightService({ + store: { getDeploymentProfile: () => profile }, + git: { + status: async () => ({ root, head: sha, clean: true, counts: { changed: 0 }, branch: { head: 'main', upstream: 'origin/main', ahead: 0, behind: 0 } }), + verifyCommitOnRemoteBranch: async () => true + }, + gitea: { repositoryFileExists: async () => true, listWorkflowRuns: async () => ({ runs: [] }) }, + deployments: { + readStatusEndpoint: async () => ({ configured: true, reachable: true, ok: true, liveSha: sha, status: 200 }), + checkHealth: async () => ({ configured: true, healthy: true, status: 200, latencyMs: 12 }) + }, + diagnostics: { info: async () => {} }, userDataPath: root + }); + const result = await service.runDeployment({ repository: { fullName: 'jens/app', localPath: root }, profileId: profile.id }); + assert.equal(result.summary.ready, true); + assert.equal(result.checks.filter((item) => item.status === 'fail').length, 0); + assert.equal(result.head, sha); +}); diff --git a/tests/renderer-workflow.test.mjs b/tests/renderer-workflow.test.mjs new file mode 100644 index 0000000..f177b3d --- /dev/null +++ b/tests/renderer-workflow.test.mjs @@ -0,0 +1,24 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { readFile } from 'node:fs/promises'; + +test('changed file list has an independently scrollable bounded layout', async () => { + const css = await readFile(new URL('../src/renderer/styles.css', import.meta.url), 'utf8'); + assert.match(css, /\.main-canvas\.repository-canvas\s*\{[^}]*overflow:\s*hidden/); + assert.match(css, /\.file-panel\s*\{[^}]*min-height:\s*0[^}]*overflow:\s*hidden/); + assert.match(css, /\.file-list\s*\{[^}]*flex:\s*1 1 auto[^}]*overflow-y:\s*auto/); +}); + +test('commit workflow explains every disabled prerequisite', async () => { + const renderer = await readFile(new URL('../src/renderer/app.js', import.meta.url), 'utf8'); + assert.match(renderer, /Commit message required/); + assert.match(renderer, /Enter a commit message to enable commit and push/); + assert.match(renderer, /ForgeFlow stages the selected files automatically/); + assert.match(renderer, /Commit selected & push to Gitea/); +}); + +test('ITWorx branding is integrated into titlebar and setup', async () => { + const renderer = await readFile(new URL('../src/renderer/app.js', import.meta.url), 'utf8'); + assert.match(renderer, /itworx-mark\.png/); + assert.match(renderer, /itworx-wordmark\.png/); +}); diff --git a/tests/repository-matching.test.mjs b/tests/repository-matching.test.mjs new file mode 100644 index 0000000..b382412 --- /dev/null +++ b/tests/repository-matching.test.mjs @@ -0,0 +1,18 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import matching from '../src/shared/repository-match.cjs'; + +const { normalizeRemoteUrl, matchRemoteToRepository } = matching; +const repositories = [{ full_name: 'jens/forgeflow', name: 'forgeflow', owner: { login: 'jens' } }]; + +test('normalizes HTTPS remotes', () => { + assert.deepEqual(normalizeRemoteUrl('https://gitea.internal/jens/forgeflow.git'), { host: 'gitea.internal', path: 'jens/forgeflow' }); +}); + +test('normalizes SCP-style SSH remotes', () => { + assert.deepEqual(normalizeRemoteUrl('git@gitea.internal:jens/forgeflow.git'), { host: 'gitea.internal', path: 'jens/forgeflow' }); +}); + +test('matches local remote to Gitea full name', () => { + assert.equal(matchRemoteToRepository('git@gitea.internal:jens/forgeflow.git', repositories)?.full_name, 'jens/forgeflow'); +}); diff --git a/tests/repository-monitor.test.mjs b/tests/repository-monitor.test.mjs new file mode 100644 index 0000000..f6a4368 --- /dev/null +++ b/tests/repository-monitor.test.mjs @@ -0,0 +1,30 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import monitorModule from '../src/main/repository-monitor.cjs'; + +const { RepositoryMonitor } = monitorModule; + +test('repository monitor establishes a baseline and emits only on later changes', async () => { + let revision = 1; + const changes = []; + const git = { + status: async (localPath) => ({ localPath, revision }), + statusFingerprint: (status) => String(status.revision) + }; + const store = { data: { preferences: { autoRefresh: true, repositoryPollSeconds: 2 } } }; + const monitor = new RepositoryMonitor({ store, git, onChange: (change) => changes.push(change) }); + monitor.setPaths(['/repo']); + await monitor.tick(); + assert.equal(changes.length, 0); + revision = 2; + await monitor.tick(); + assert.equal(changes.length, 1); + assert.equal(changes[0].reason, 'working-tree-changed'); + monitor.pause('/repo'); + revision = 3; + await monitor.tick(); + assert.equal(changes.length, 1); + monitor.resume('/repo'); + await monitor.tick(); + assert.equal(changes.length, 2); +}); diff --git a/tests/security-validation.test.mjs b/tests/security-validation.test.mjs new file mode 100644 index 0000000..f7baa57 --- /dev/null +++ b/tests/security-validation.test.mjs @@ -0,0 +1,66 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import validation from '../src/shared/validation.cjs'; +import redaction from '../src/main/log-redaction.cjs'; + +const { + normalizeBaseUrl, + assertRepositoryRelativePath, + assertRepositoryRelativePaths, + assertFullCommitSha, + assertWorkflowFile, + assertWorkflowFileName, + assertBranchName, + assertEnvironmentName, + assertHttpUrl, + assertCloneRemote +} = validation; +const { redactSecrets } = redaction; + +test('rejects credentials embedded in service URLs', () => { + assert.throws(() => normalizeBaseUrl(`https://${['jens', 'secret'].join(':')}@gitea.example.test`), /credentials/i); + assert.throws(() => assertHttpUrl(`https://${['user', 'secret'].join(':')}@app.example.test/health`), /credentials/i); +}); + +test('accepts repository-relative paths but blocks escapes and absolute paths', () => { + assert.equal(assertRepositoryRelativePath('./src/main.ts'), 'src/main.ts'); + assert.deepEqual(assertRepositoryRelativePaths(['src/main.ts', 'src/main.ts', 'docs/readme.md']), ['src/main.ts', 'docs/readme.md']); + assert.throws(() => assertRepositoryRelativePath('../secrets.txt'), /escape/i); + assert.throws(() => assertRepositoryRelativePath('/etc/passwd'), /absolute/i); + assert.throws(() => assertRepositoryRelativePath('C:\\Windows\\win.ini'), /absolute/i); +}); + +test('validates full commit SHAs and workflow filenames', () => { + const sha = 'A'.repeat(40); + assert.equal(assertFullCommitSha(sha), 'a'.repeat(40)); + assert.equal(assertWorkflowFile('.gitea/workflows/deploy.yml'), '.gitea/workflows/deploy.yml'); + assert.throws(() => assertFullCommitSha('abc1234'), /full commit SHA/i); + assert.throws(() => assertWorkflowFile('../deploy.yml'), /escape/i); + assert.throws(() => assertWorkflowFile('deploy.sh'), /YAML/i); + assert.equal(assertWorkflowFileName('deploy.yml'), 'deploy.yml'); + assert.throws(() => assertWorkflowFileName('.gitea/workflows/deploy.yml'), /filename/i); +}); + +test('allows supported Git remotes and rejects unsafe protocols/passwords', () => { + assert.equal(assertCloneRemote('git@gitea.example.test:jens/app.git'), 'git@gitea.example.test:jens/app.git'); + assert.equal(assertCloneRemote('ssh://git@gitea.example.test/jens/app.git'), 'ssh://git@gitea.example.test/jens/app.git'); + assert.throws(() => assertCloneRemote('file:///tmp/repo.git'), /unsupported/i); + assert.throws(() => assertCloneRemote(`https://${['jens', 'secret'].join(':')}@gitea.example.test/jens/app.git`), /password/i); +}); + +test('redacts known tokens, authorization headers, query tokens and URL passwords', () => { + const token = 'super-secret-token'; + const source = `Authorization: token ${token}\nhttps://gitea.test/api?access_token=${token}\nhttps://${['jens', 'password'].join(':')}@gitea.test\n${token}`; + const result = redactSecrets(source, [token]); + assert.doesNotMatch(result, /super-secret-token|password/); + assert.match(result, /\[REDACTED\]/); +}); + + +test('validates deployment branch and environment identifiers', () => { + assert.equal(assertBranchName('release/staging'), 'release/staging'); + assert.equal(assertEnvironmentName('Production-EU'), 'production-eu'); + assert.throws(() => assertBranchName('-dangerous'), /invalid/i); + assert.throws(() => assertBranchName('main..backup'), /invalid/i); + assert.throws(() => assertEnvironmentName('production eu'), /environment/i); +}); diff --git a/tests/semver.test.mjs b/tests/semver.test.mjs new file mode 100644 index 0000000..d544e60 --- /dev/null +++ b/tests/semver.test.mjs @@ -0,0 +1,13 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); +const { parseVersion, compareVersions, isNewerVersion } = require('../src/shared/semver.cjs'); + +test('semantic versions are compared without lexical mistakes', () => { + assert.equal(parseVersion('v0.4.0').minor, 4); + assert.equal(compareVersions('0.10.0', '0.9.9'), 1); + assert.equal(compareVersions('1.0.0', '1.0.0'), 0); + assert.equal(isNewerVersion('0.4.1', '0.4.0'), true); + assert.equal(isNewerVersion('0.4.0-beta.1', '0.4.0'), false); +}); diff --git a/tests/shell-verification.test.mjs b/tests/shell-verification.test.mjs new file mode 100644 index 0000000..2cabe5e --- /dev/null +++ b/tests/shell-verification.test.mjs @@ -0,0 +1,49 @@ +import assert from 'node:assert/strict'; +import { mkdtemp, mkdir, copyFile, rm } from 'node:fs/promises'; +import os from 'node:os'; +import path from 'node:path'; +import { spawnSync } from 'node:child_process'; +import test from 'node:test'; +import shellVerification from '../src/shared/shell-verification.cjs'; + +const { bashSyntaxCheckInvocation, normalizeRelativePosixPath } = shellVerification; + +test('Bash syntax validation keeps Windows project roots in cwd and passes a relative POSIX path', () => { + const invocation = bashSyntaxCheckInvocation('C:\\Projects\\ForgeFlow'); + assert.equal(invocation.command, 'bash'); + assert.deepEqual(invocation.args, ['-n', 'examples/server/forgeflow-deploy']); + assert.equal(invocation.options.cwd, 'C:\\Projects\\ForgeFlow'); + assert.equal(invocation.args[1].includes('\\'), false); + assert.equal(/^[A-Za-z]:/.test(invocation.args[1]), false); +}); + +test('Shell validation refuses absolute and escaping script paths', () => { + assert.throws(() => normalizeRelativePosixPath('C:\\Projects\\ForgeFlow\\script.sh'), /must be relative/); + assert.throws(() => normalizeRelativePosixPath('/tmp/script.sh'), /must be relative/); + assert.throws(() => normalizeRelativePosixPath('../script.sh'), /may not escape/); +}); + +test('Bash syntax validation works from a project root containing spaces', async (t) => { + if (spawnSync('bash', ['--version'], { encoding: 'utf8' }).status !== 0) { + t.skip('Bash is not available in this environment.'); + return; + } + const tempBase = await mkdtemp(path.join(os.tmpdir(), 'forge flow verify ')); + try { + const relativeDirectory = path.join(tempBase, 'examples', 'server'); + await mkdir(relativeDirectory, { recursive: true }); + await copyFile(new URL('../examples/server/forgeflow-deploy', import.meta.url), path.join(relativeDirectory, 'forgeflow-deploy')); + const invocation = bashSyntaxCheckInvocation(tempBase); + const result = spawnSync(invocation.command, invocation.args, invocation.options); + assert.equal(result.status, 0, result.stderr); + } finally { + try { + await rm(tempBase, { recursive: true, force: true, maxRetries: 20, retryDelay: 100 }); + } catch (error) { + // Git Bash on Windows can retain a short-lived working-directory handle + // after bash -n exits. Do not fail a successful syntax test solely because + // Windows delayed releasing that temporary directory. + if (!['EBUSY', 'EPERM', 'ENOTEMPTY'].includes(error?.code)) throw error; + } + } +}); diff --git a/tests/tool-invocation.test.mjs b/tests/tool-invocation.test.mjs new file mode 100644 index 0000000..fd4d76b --- /dev/null +++ b/tests/tool-invocation.test.mjs @@ -0,0 +1,41 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import toolInvocation from '../src/shared/tool-invocation.cjs'; + +const { npmProbeCandidates } = toolInvocation; + +test('uses npm CLI through Node when doctor is launched by npm on Windows', () => { + const candidates = npmProbeCandidates({ + platform: 'win32', + execPath: 'C:\\Program Files\\nodejs\\node.exe', + env: { + npm_execpath: 'C:\\Program Files\\nodejs\\node_modules\\npm\\bin\\npm-cli.js', + npm_node_execpath: 'C:\\Program Files\\nodejs\\node.exe', + ComSpec: 'C:\\Windows\\System32\\cmd.exe' + } + }); + assert.deepEqual(candidates[0], { + file: 'C:\\Program Files\\nodejs\\node.exe', + args: ['C:\\Program Files\\nodejs\\node_modules\\npm\\bin\\npm-cli.js', '--version'], + source: 'npm_execpath' + }); +}); + +test('falls back to cmd.exe for npm command shims on Windows', () => { + const candidates = npmProbeCandidates({ + platform: 'win32', + execPath: 'C:\\Program Files\\nodejs\\node.exe', + env: { ComSpec: 'C:\\Windows\\System32\\cmd.exe' } + }); + assert.deepEqual(candidates, [{ + file: 'C:\\Windows\\System32\\cmd.exe', + args: ['/d', '/s', '/c', 'npm --version'], + source: 'windows-command-shim' + }]); +}); + +test('uses npm directly on non-Windows systems', () => { + assert.deepEqual(npmProbeCandidates({ platform: 'linux', env: {}, execPath: '/usr/bin/node' }), [ + { file: 'npm', args: ['--version'], source: 'path' } + ]); +}); diff --git a/tests/unraid-deployment.test.mjs b/tests/unraid-deployment.test.mjs new file mode 100644 index 0000000..829cc52 --- /dev/null +++ b/tests/unraid-deployment.test.mjs @@ -0,0 +1,126 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); +const { UnraidDeploymentService, safeRemoteFolder, safeRelativeRemoteFile, parseInspection, dockerIgnoreHasPath, checksSummary, bash } = require('../src/main/unraid-deployment-service.cjs'); +const { fingerprintKey, shellQuote } = require('../src/main/ssh-service.cjs'); + +test('Unraid remote paths cannot escape appdata project folder', () => { + assert.equal(safeRemoteFolder('lumaops'), 'lumaops'); + assert.throws(() => safeRemoteFolder('../lumaops')); + assert.equal(safeRelativeRemoteFile('deploy/docker-compose.yml'), 'deploy/docker-compose.yml'); + assert.throws(() => safeRelativeRemoteFile('../../etc/passwd')); +}); + +test('server inspection key-value payload is decoded safely', () => { + const b64 = (value) => Buffer.from(value).toString('base64'); + const parsed = parseInspection(`noise\n__FORGEFLOW_KV__\nexists=true\nrootGit=true\nhead=${'a'.repeat(40)}\nbranch=main\nremote=${b64('ssh://git@gitea/Jens/LumaOps.git')}\ntrackedChanges=${b64(' M docker-compose.yml\n')}\ncomposeFiles=${b64('docker-compose.yml\n')}\nnestedGit=${b64('source\n')}\ndockerfile=true\ndockerignoreContent=${b64('.git\ndata/\n')}\nexistingPreservePaths=${b64('data\nlogs\n')}\n`); + assert.equal(parsed.rootGit, true); + assert.deepEqual(parsed.composeFiles, ['docker-compose.yml']); + assert.deepEqual(parsed.nestedGit, ['source']); + assert.equal(parsed.trackedChanges.length, 1); + assert.match(parsed.dockerignoreContent, /\.git/); + assert.deepEqual(parsed.existingPreservePaths, ['data', 'logs']); +}); + +test('Docker ignore checks identify exact runtime and Git context exclusions', () => { + const rules = '# build context\n.git\ndata/\nlogs/**\n!logs/keep.txt\n'; + assert.equal(dockerIgnoreHasPath(rules, '.git'), true); + assert.equal(dockerIgnoreHasPath(rules, 'data'), true); + assert.equal(dockerIgnoreHasPath(rules, 'logs'), true); + assert.equal(dockerIgnoreHasPath(rules, 'source'), false); +}); + +test('server inspection detects preserved runtime paths and missing Docker context exclusions', async () => { + const b64 = (value) => Buffer.from(value).toString('base64'); + let receivedCommand = ''; + const store = { + getDeploymentProfile: () => ({ + id: 'production', + provider: 'ssh-unraid', + serverId: 'unraid', + remoteFolder: 'lumaops', + preservePaths: ['data', 'logs'] + }), + getServer: () => ({ + id: 'unraid', + name: 'Unraid', + basePath: '/mnt/user/appdata' + }) + }; + const ssh = { + exec: async (_serverId, command) => { + receivedCommand = command; + return { + stdout: `__FORGEFLOW_KV__\nexists=true\nrootGit=true\nhead=${'a'.repeat(40)}\nbranch=main\nremote=${b64('ssh://git@gitea/Jens/LumaOps.git')}\ntrackedChanges=\ncomposeFiles=${b64('docker-compose.yml\n')}\nnestedGit=${b64('source\n')}\ndockerfile=true\ndockerignoreContent=${b64('.git\ndata/\n')}\nexistingPreservePaths=${b64('data\nlogs\n')}\n`, + stderr: '', + exitCode: 0 + }; + } + }; + const service = new UnraidDeploymentService({ store, ssh, git: {}, diagnostics: null }); + const inspection = await service.inspect({ repository: { fullName: 'Jens/LumaOps', name: 'LumaOps' }, profileId: 'production' }); + + assert.match(receivedCommand, /base64 -d \| bash$/); + assert.equal(inspection.remotePath, '/mnt/user/appdata/lumaops'); + assert.equal(inspection.dockerignoreGitExcluded, true); + assert.deepEqual(inspection.existingPreservePaths.sort(), ['data', 'logs']); + assert.deepEqual(inspection.dockerContextExclusionsMissing.sort(), ['logs', 'source']); +}); + +test('preflight summary blocks only failed checks', () => { + const result = checksSummary([{ id: 'a', status: 'pass' }, { id: 'b', status: 'warning' }, { id: 'c', status: 'fail' }]); + assert.equal(result.ready, false); + assert.deepEqual(result.blocking, ['c']); +}); + +test('SSH helpers produce pinned fingerprints and quoted commands', () => { + assert.match(fingerprintKey(Buffer.from('host-key')), /^SHA256:/); + assert.equal(shellQuote("a'b"), "'a'\\''b'"); + const wrapped = bash('git fetch origin main'); + assert.match(wrapped, /base64 -d \| bash$/); + assert.equal(wrapped.includes('\n'), false); + const encoded = wrapped.match(/printf '%s' '([A-Za-z0-9+/=]+)'/)[1]; + const decoded = Buffer.from(encoded, 'base64').toString('utf8'); + assert.match(decoded, /GIT_TERMINAL_PROMPT=0/); + assert.match(decoded, /BatchMode=yes/); + assert.match(decoded, /git fetch origin main/); +}); + +test('SSH rollback refuses any SHA other than the exact recorded previous deployment', async () => { + const previousSha = 'a'.repeat(40); + const store = { + getDeploymentProfile: () => ({ id: 'production', provider: 'ssh-unraid', serverId: 'unraid', remoteFolder: 'lumaops', composeFile: 'docker-compose.yml', branch: 'main', environment: 'production' }), + getServer: () => ({ id: 'unraid', basePath: '/mnt/user/appdata' }), + getDeploymentState: () => ({ liveSha: 'b'.repeat(40), previousSha }) + }; + const service = new UnraidDeploymentService({ store, ssh: {}, git: {}, diagnostics: null }); + await assert.rejects( + service.rollback({ repository: { fullName: 'Jens/LumaOps', name: 'LumaOps', localPath: '/tmp/lumaops' }, profileId: 'production', targetSha: 'c'.repeat(40) }), + (error) => error.code === 'ROLLBACK_TARGET_NOT_PREVIOUS_SHA' + ); +}); + +test('successful SSH rollback records the formerly live SHA as the new rollback target', async () => { + const previousSha = 'a'.repeat(40); + const liveSha = 'b'.repeat(40); + const savedStates = []; + const operations = []; + const store = { + getDeploymentProfile: () => ({ id: 'production', provider: 'ssh-unraid', serverId: 'unraid', remoteFolder: 'lumaops', composeFile: 'docker-compose.yml', branch: 'main', environment: 'production', healthcheckUrl: '' }), + getServer: () => ({ id: 'unraid', name: 'Unraid', basePath: '/mnt/user/appdata' }), + getDeploymentState: () => ({ liveSha, previousSha }), + addOperation: async (operation) => { operations.push(operation); return operation; }, + saveDeploymentState: async (_profileId, state) => { savedStates.push(state); return state; } + }; + const git = { verifyCommitOnRemoteBranch: async () => true }; + const ssh = { exec: async () => ({ stdout: '', stderr: '', exitCode: 0 }) }; + const service = new UnraidDeploymentService({ store, ssh, git, diagnostics: null }); + service.inspect = async () => ({ rootGit: true, trackedChanges: [], head: liveSha }); + service.checkHealth = async () => ({ configured: false, healthy: null, status: null, latencyMs: null }); + const result = await service.rollback({ repository: { fullName: 'Jens/LumaOps', name: 'LumaOps', localPath: '/tmp/lumaops' }, profileId: 'production', targetSha: previousSha }); + assert.equal(result.status, 'rolled-back'); + assert.equal(savedStates.at(-1).liveSha, previousSha); + assert.equal(savedStates.at(-1).previousSha, liveSha); + assert.equal(operations.at(-1).previousSha, liveSha); +}); diff --git a/tests/update-service.test.mjs b/tests/update-service.test.mjs new file mode 100644 index 0000000..de22cbf --- /dev/null +++ b/tests/update-service.test.mjs @@ -0,0 +1,52 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { mkdtemp, rm } from 'node:fs/promises'; +import os from 'node:os'; +import path from 'node:path'; +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); +const { UpdateService } = require('../src/main/update-service.cjs'); + +test('update check pins version to an exact branch commit', async () => { + const temp = await mkdtemp(path.join(os.tmpdir(), 'forgeflow-update-test-')); + const saved = []; + const store = { + data: { + gitea: { baseUrl: 'https://gitea.example.test' }, + updates: { owner: 'Jens', repo: 'ForgeFlow', branch: 'main', autoCheck: true } + }, + async save() { saved.push(true); } + }; + const calls = []; + const gitea = { + async getBranch(owner, repo, branch) { + calls.push(['branch', owner, repo, branch]); + return { commit: { id: 'a'.repeat(40) } }; + }, + async getRepositoryFile(input) { + calls.push(['file', input]); + return { decoded: JSON.stringify({ name: 'forgeflow', version: '0.4.1' }) }; + } + }; + const service = new UpdateService({ + store, gitea, diagnostics: null, + appInfo: { version: '0.4.0', packaged: false }, + sourcePath: temp, userDataPath: temp + }); + const result = await service.check(); + assert.equal(result.available, true); + assert.equal(result.remoteSha, 'a'.repeat(40)); + assert.equal(calls[1][1].ref, 'a'.repeat(40)); + assert.equal(saved.length, 1); + await rm(temp, { recursive: true, force: true }); +}); + +test('update repository parts reject path injection', async () => { + const temp = await mkdtemp(path.join(os.tmpdir(), 'forgeflow-update-test-')); + const service = new UpdateService({ + store: { data: { updates: { owner: '../Jens', repo: 'ForgeFlow', branch: 'main' } }, save: async () => {} }, + gitea: {}, diagnostics: null, appInfo: { version: '0.4.0', packaged: false }, sourcePath: temp, userDataPath: temp + }); + await assert.rejects(() => service.check(), /unsupported characters/); + await rm(temp, { recursive: true, force: true }); +}); diff --git a/tests/validation.test.mjs b/tests/validation.test.mjs new file mode 100644 index 0000000..c40e79c --- /dev/null +++ b/tests/validation.test.mjs @@ -0,0 +1,17 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import validation from '../src/shared/validation.cjs'; + +const { normalizeBaseUrl, assertCommitMessage, assertDeploymentRequest } = validation; + +test('normalizes Gitea base URL', () => { + assert.equal(normalizeBaseUrl('https://gitea.example.com/'), 'https://gitea.example.com'); +}); + +test('rejects blank commit messages', () => { + assert.throws(() => assertCommitMessage(' '), /commit message/i); +}); + +test('requires exact SHA and workflow profile', () => { + assert.throws(() => assertDeploymentRequest({ branch: 'main', workflowFile: 'deploy.yml' }, 'nope'), /commit SHA/i); +}); diff --git a/tests/zip-writer.test.mjs b/tests/zip-writer.test.mjs new file mode 100644 index 0000000..37464b4 --- /dev/null +++ b/tests/zip-writer.test.mjs @@ -0,0 +1,41 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import zlib from 'node:zlib'; +import zipModule from '../src/shared/zip-writer.cjs'; + +const { createZip, crc32 } = zipModule; + +function unzipLocalEntries(buffer) { + const entries = new Map(); + let offset = 0; + while (offset + 4 <= buffer.length && buffer.readUInt32LE(offset) === 0x04034b50) { + const method = buffer.readUInt16LE(offset + 8); + const expectedCrc = buffer.readUInt32LE(offset + 14); + const compressedSize = buffer.readUInt32LE(offset + 18); + const nameLength = buffer.readUInt16LE(offset + 26); + const extraLength = buffer.readUInt16LE(offset + 28); + const nameStart = offset + 30; + const dataStart = nameStart + nameLength + extraLength; + const name = buffer.subarray(nameStart, nameStart + nameLength).toString('utf8'); + const compressed = buffer.subarray(dataStart, dataStart + compressedSize); + const data = method === 8 ? zlib.inflateRawSync(compressed) : compressed; + assert.equal(crc32(data), expectedCrc); + entries.set(name, data); + offset = dataStart + compressedSize; + } + return entries; +} + +test('creates a valid deflated ZIP with UTF-8 entry names and CRCs', () => { + const archive = createZip([ + { name: 'manifest.json', data: '{"ok":true}\n' }, + { name: 'logs/diagnostics.jsonl', data: Buffer.from('hello diagnostics\n') }, + { name: 'unicode/één.txt', data: 'veilig' } + ]); + assert.equal(archive.readUInt32LE(0), 0x04034b50); + assert.equal(archive.readUInt32LE(archive.length - 22), 0x06054b50); + const entries = unzipLocalEntries(archive); + assert.equal(entries.size, 3); + assert.equal(entries.get('manifest.json').toString(), '{"ok":true}\n'); + assert.equal(entries.get('unicode/één.txt').toString(), 'veilig'); +}); diff --git a/update-windows.ps1 b/update-windows.ps1 new file mode 100644 index 0000000..f448d1d --- /dev/null +++ b/update-windows.ps1 @@ -0,0 +1,50 @@ +$ErrorActionPreference = "Stop" +Set-StrictMode -Version Latest +Set-Location $PSScriptRoot + +function Assert-Command { + param([Parameter(Mandatory = $true)][string]$Name) + if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) { + throw "Required command '$Name' was not found on PATH." + } +} + +function Invoke-Step { + param( + [Parameter(Mandatory = $true)][string]$Title, + [Parameter(Mandatory = $true)][scriptblock]$Action + ) + Write-Host "`n$Title" -ForegroundColor Yellow + & $Action + if ($LASTEXITCODE -ne 0) { + throw "$Title failed with exit code $LASTEXITCODE." + } +} + +Write-Host "ForgeFlow v0.4.2 source update" -ForegroundColor Cyan +Write-Host "Your Gitea token, repository mappings and deployment profiles are stored outside this source folder and are not reset." -ForegroundColor DarkGray + +Assert-Command node +Assert-Command npm +Assert-Command git + +$package = Get-Content ".\package.json" -Raw | ConvertFrom-Json +if ($package.version -ne "0.4.2") { + throw "This folder does not contain ForgeFlow v0.4.2. Detected version: $($package.version)" +} + +Invoke-Step "Updating project dependencies..." { + npm install --no-audit --no-fund +} + +Invoke-Step "Running the environment doctor..." { + npm run doctor +} + +Invoke-Step "Running source verification and all automated tests..." { + npm run check +} + +Write-Host "`nForgeFlow v0.4.2 is ready." -ForegroundColor Green +Write-Host "Starting ForgeFlow with your existing local configuration..." -ForegroundColor Green +npm start

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 literal 0 HcmV?d00001 diff --git a/src/renderer/assets/itworx-wordmark.png b/src/renderer/assets/itworx-wordmark.png new file mode 100644 index 0000000000000000000000000000000000000000..ac6acf6925eacf7a8fcb46677b11eb638093e000 GIT binary patch literal 64378 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¥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 AtN;K2 literal 0 HcmV?d00001 diff --git a/src/renderer/index.html b/src/renderer/index.html new file mode 100644 index 0000000..c27c592 --- /dev/null +++ b/src/renderer/index.html @@ -0,0 +1,22 @@ + + + + + + + ForgeFlow + + + +