Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
04af200
refactor(app): make the data pipeline self-contained for packaging
ApocDev Jun 30, 2026
6acb1a8
feat(app): show the data storage location in Settings
ApocDev Jun 30, 2026
b28a683
docs: document the in-process pipeline, data dir, and migration workflow
ApocDev Jun 30, 2026
01eb659
feat(desktop): add a Tauri desktop shell that runs the app in a window
ApocDev Jun 30, 2026
7dc1862
feat(desktop): bundle a vendored node sidecar so the app runs standalone
ApocDev Jun 30, 2026
92c3288
ci(desktop): add a GitHub Actions release workflow for the bundle
ApocDev Jun 30, 2026
a4db594
ci(desktop): build macOS and Windows bundles too
ApocDev Jun 30, 2026
755e51e
feat(desktop): polish the window — version title, icons, size, geometry
ApocDev Jun 30, 2026
8c98fcf
feat(desktop): open external links in the system browser
ApocDev Jun 30, 2026
bc94b4b
feat(desktop): enforce a single instance for stability
ApocDev Jun 30, 2026
cbe5406
ci: automate versioning and releases with release-please
ApocDev Jun 30, 2026
4ba57c6
fix(app): add a version field to package.json for release-please
ApocDev Jun 30, 2026
e8e5d6c
ci(desktop): sign update artifacts and publish latest.json for self-u…
ApocDev Jun 30, 2026
af78669
feat(desktop): check for updates on launch and prompt to install
ApocDev Jun 30, 2026
416c758
docs: document the desktop app, packaging, releases, and self-update
ApocDev Jun 30, 2026
ecbf147
Merge remote-tracking branch 'origin/main' into feat/desktop-packaging
ApocDev Jun 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Release

# release-please watches conventional commits on `main` and maintains a "release PR"
# that bumps the version (app/package.json + Cargo.toml + tauri.conf.json) and the
# changelog. Merging that PR cuts the GitHub release + tag, and the build job then
# attaches the platform bundles to it. workflow_dispatch runs a build-only smoke test
# (no release). Activates once this branch is merged to main.
on:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: write
pull-requests: write

jobs:
release-please:
runs-on: ubuntu-latest
outputs:
release_created: ${{ steps.rp.outputs.release_created }}
tag_name: ${{ steps.rp.outputs.tag_name }}
steps:
- uses: googleapis/release-please-action@v4
id: rp
with:
config-file: release-please-config.json
manifest-file: .release-please-manifest.json

build:
needs: release-please
if: ${{ needs.release-please.outputs.release_created == 'true' || github.event_name == 'workflow_dispatch' }}
strategy:
fail-fast: false
matrix:
include:
- platform: ubuntu-22.04 # Linux x64 (deb + AppImage)
bundles: 'deb,appimage'
- platform: macos-14 # macOS Apple Silicon
bundles: 'dmg'
- platform: macos-13 # macOS Intel
bundles: 'dmg'
- platform: windows-latest # Windows x64
bundles: 'nsis'
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4

# Tauri's webview + bundling deps (Linux only). The Ubuntu runner is Debian-
# based, so unlike an Arch host its linuxdeploy/AppImage tooling works.
- name: Install Linux build dependencies
if: startsWith(matrix.platform, 'ubuntu')
run: |
sudo apt-get update
sudo apt-get install -y \
libwebkit2gtk-4.1-dev libgtk-3-dev librsvg2-dev \
libayatana-appindicator3-dev patchelf file

# Rust toolchain for the Tauri shell.
- uses: dtolnay/rust-toolchain@stable
- uses: swatinem/rust-cache@v2
with:
workspaces: app/src-tauri

# vite-plus toolchain + app deps (same as ci.yml).
- uses: voidzero-dev/setup-vp@v1
with:
node-version: '24'
cache: true
- name: Install app dependencies
working-directory: app
run: vp install

# Vendor the node sidecar for this runner's platform (gitignored, so it isn't
# in the checkout). vendor-node.sh resolves the host triple itself.
- name: Vendor the node sidecar
working-directory: app/src-tauri
shell: bash
run: ./vendor-node.sh

# Build, sign the updater artifacts, attach bundles to the release-please
# release, and generate/aggregate latest.json across the matrix. tauri-action
# runs `vp build` (beforeBuildCommand) then the tauri build via the CLI vp
# installed. With no tag (workflow_dispatch), it builds only — a smoke test.
- name: Build, sign, and publish
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
with:
projectPath: app
tagName: ${{ needs.release-please.outputs.tag_name }}
args: --bundles ${{ matrix.bundles }}
3 changes: 3 additions & 0 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"app": "0.1.0"
}
26 changes: 18 additions & 8 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,20 @@ This is a single repo with three cooperating parts:
in one process. Hosts the SQLite store, the block solver, the Factorio UDP
bridge, the data-dump + icon pipeline, and the AI assistant. This is where almost
all work happens. It has its own `AGENTS.md`/`CLAUDE.md` (Vite+ toolchain notes).
`app/src-tauri/` is the Tauri **desktop shell** that wraps this server in a native
window and packages it into a self-contained bundle (vendored Node + bundled
resources) with self-update — see [`docs/desktop.md`](docs/desktop.md).
- **`mod/`** — the Factorio mod (`pyops`, Factorio 2.0): in-game panel, UDP link to
the app, data-dump trigger, Helmod-style production-block view, and the
request-combinator planner. Pure Lua, no build step.
- **`scripts/`** — one-off dev/test scripts (icon-atlas builder, atlas preview
server, a Factorio reload helper). Not part of the app build; has its own
`package.json` (uses `sharp`).
- **`scripts/`** — dev-only helpers (currently `tunnel-dev`, to expose the dev
server through a tunnel). Not part of the app build.

Per-project SQLite stores live in `projects/*.db` (each self-describes its name in
its own `meta`); `app-config.json` holds app-level config (active project + the
OpenRouter key/model). All are generated/gitignored. A legacy root `dev.db` is
migrated to `projects/default.db` on first run, and any old `projects.json`
registry is imported into the dbs' `meta` and backed up to `.bak`.
OpenRouter key/model). Both live under the app's **data dir** — the working dir in
dev, a per-OS user-data dir for a packaged build, overridable via `PYOPS_DATA_DIR`
(see `app/src/server/paths.ts`). All are generated/gitignored.

## App architecture (`app/src/`)

Expand Down Expand Up @@ -67,8 +69,16 @@ from inside `app/`:
the UDP-bridge round-trip, and `responsive.e2e.ts`, which screenshots every route
across a desktop/tablet/phone matrix and **asserts no route scrolls sideways** at
tablet/phone widths. See `app/e2e/README.md`.
- DB: `pnpm db:generate` / `db:migrate` / `db:push` / `db:studio` (drizzle-kit),
`pnpm db:import` to load the Factorio dump.
- DB schema: edit `src/db/schema.ts`, then `vp run db:generate <name>` to write a
migration under `drizzle/` (a name is required, so files stay meaningful).
`server/provision.ts` applies the migrations **in-process** on first connect to
each project db. `db:studio` opens the drizzle DB browser. A Factorio dump loads
through the in-app data sync.
- Desktop shell: `vp run tauri dev` runs the app in a native window; `vp run tauri
build` packages a bundle (run `src-tauri/vendor-node.sh` first). Releases are
automated by **release-please** — **don't hand-edit the version** in
`package.json` / `Cargo.toml` / `tauri.conf.json`; it bumps all three from
conventional commits. See [`docs/desktop.md`](docs/desktop.md).

If setup/runtime/package-manager behavior looks wrong, run `vp env doctor`.

Expand Down
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,29 @@ set, with produced-by / used-in (here: Iron plate — 14 producers, 160 consumer

---

## Desktop app

PyOps also ships as a self-contained **desktop app** — a [Tauri](https://tauri.app)
window around the same server, bundling its own Node runtime so it needs no toolchain
to run. Grab the build for your OS from the
[Releases](https://github.com/ApocDev/pyops/releases) page:

- **Linux** — `.AppImage` (`chmod +x` it and run; self-updates) or `.deb`
- **macOS** — `.dmg`
- **Windows** — the `-setup.exe` installer

It still needs **Factorio + the Py mods** installed locally (it reads your recipe
data via a data sync), and a fresh install starts empty — open **⚙ Settings › Game
data** and run a sync on first launch, same as below. Bundled builds **check for
updates on launch** and prompt to install. For how it's built and released, see
[`docs/desktop.md`](docs/desktop.md).

---

## Requirements

_To run from source (below). The desktop app needs none of this._

- **Node.js** (current LTS) and **pnpm** — the app's toolchain ([Vite+](https://viteplus.dev/),
the `vp` CLI) handles the rest.
- **Factorio 2.0** installed locally, with the **Pyanodons** mod suite and
Expand Down
Loading
Loading