8000 feat(packaging): self-contained QNAP QPKG, built for each release by adeelahmad · Pull Request #13 · adeelahmad/lense · GitHub
[go: up one dir, main page]

Skip to content

feat(packaging): self-contained QNAP QPKG, built for each release - #13

Merged
adeelahmad merged 6 commits into
mainfrom
claude/qnap-qpkg-a9a3mx
Oct 2, 2026
Merged

adeelahmad merged 6 commits into
mainfrom
claude/qnap-qpkg-a9a3mx

Conversation

@adeelahmad
@adeelahmad adeelahmad commented Oct 2, 2026 •
Copy link
Copy Markdown
Owner

Requested by Adeel · project thread

Before: running Lens on a QNAP NAS meant setting up the compose stack by hand in Container Station.

After: packaging/qnap/build.sh builds a .qpkg that installs from the App Center (Install Manually). The package carries the Lens images and SurrealDB, so the NAS pulls and builds nothing. It runs in Container Station, opens from the App Center's Open button, and puts the first-admin setup code in the QTS system log. The setup wizard from #19 then asks for the namespace, the model provider and storage. Publishing a GitHub release builds both packages and attaches them to the release.

How. The package is built with QDK v2.5.3, run in a small container so only Docker is needed. There is one package per architecture (x86_64, arm_64). build.sh builds the images for the NAS's platform, saves them into the package with a manifest, and runs qbuild. The backend uses the lean target by default (--target full adds the document converters), and the images are tagged qnap-<version>.

On the NAS, the service script lens.sh does the following:

  • waits for Container Station's Docker
  • on the first start, writes lens.env with fresh secrets to /share/Container/lens, a data folder that is kept when the app is removed
  • loads any missing image and deletes the image files to give the space back
  • runs docker compose up as the project lens

Upgrades and removal touch only this package's qnap-* image tags, so images like lens-backend:prod stay.

The package's own archive.yaml is mounted read-only. It defines no namespaces, so the wizard asks for one, or LENS_NAMESPACE names it. Its only local root is /audio, which maps to the NAS's Multimedia/Lens folder (created on the first start); the rest of the share isn't exposed. The wizard's storage step can watch that folder. The compose file passes LENS_ADMIN_*, LENS_NAMESPACE, LENS_LLM_* and LENS_SETUP_WIZARD through from lens.env. They are empty by default, which leaves the setup to the web app.

Releases. .github/workflows/qnap.yml is self-contained. It runs when a release is published, or by hand from the Actions tab with a tag. It builds both packages with the tag as the version and uploads only the QNAP assets, and fails clearly if a package is over GitHub's 2 GiB asset limit. The ARM package is built under QEMU unless the repository variable QNAP_ARM_RUNNER names an ARM runner. A draft made by release.yml must be published by a person, because releases published with GITHUB_TOKEN don't trigger other workflows.

Build

packaging/qnap/build.sh                 # Intel/AMD NAS -> packaging/qnap/build/Lens_0.3.0_x86_64.qpkg
packaging/qnap/build.sh --arch arm_64   # ARM NAS (QEMU when built on x86)

Install

  1. Install Container Station 3 from the App Center and open it once.
  2. In the App Center, click the gear icon, then Install Manually, and choose the .qpkg. Accept the warning that the app is unsigned.
  3. Wait for the first start, which loads the images and takes a few minutes. Then click Open, or go to http://<NAS>:3000.
  4. Create the admin account with the setup code. It is in the System Log entry from Lens, in lens.env, or printed by lens.sh setup-code.
  5. Follow the setup steps for the namespace, the model provider and storage. The storage step can watch /audio.

Tested

In a Linux container with the QTS tools (getcfg, setcfg, write_log) stubbed and stand-in Lens images (this sandbox can't reach Debian's apt mirrors, so the real images couldn't be built here):

  • qbuild packaged the files and the package's contents and metadata were checked
  • on the stub setup:
    • first start: images loaded, secrets written, the setup code logged, the data folder writable as uid 1000, the package's archive.yaml reached the API, and Multimedia/Lens was created
    • restart
    • an upgrade from 0.3.0 to 0.3.1: secrets kept, old images removed
    • stop and remove: the data folder and a lens-backend:prod image were kept
  • after the merge of main, docker compose config and shellcheck are clean
  • the workflow passes actionlint

Not yet tested: installing on a real NAS, the real images, and a run of the release workflow (there are no releases yet).

🤖 Generated with Claude Code

https://claude.ai/code/session_01KSuGMk2LX7Csu1f7pKqoFC

packaging/qnap/build.sh builds the images for the NAS's platform, saves them into the
package with SurrealDB, and runs QDK's qbuild (in a container, so only Docker is needed).
On the NAS, lens.sh loads the images into Container Station on the first start, writes
lens.env with fresh secrets in a data folder that outlasts removal, logs the first-admin
setup code to the QTS system log, and runs the stack with docker compose.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KSuGMk2LX7Csu1f7pKqoFC
@adeelahmad adeelahmad self-assigned this Oct 2, 2026
.github/workflows/qnap.yml builds the x86_64 and arm_64 QPKGs when a release is
published (or by hand for a tag) and attaches them to the release. ARM builds run
under QEMU unless the QNAP_ARM_RUNNER variable names an ARM runner.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KSuGMk2LX7Csu1f7pKqoFC
claude added 2 commits October 2, 2026 12:52
…nswers from lens.env

- A QNAP archive.yaml (mounted read-only) with a media namespace on /audio, and
  /audio is now just Multimedia/Lens, created on the first start, not the whole share.
- The package's images are tagged qnap-<version>; upgrades and removal touch only those,
  so Lens images built some other way (lens-backend:prod) stay.
- build.sh builds the lean backend by default (--target full for documents).
- The compose file passes the LENS_ADMIN_*, LENS_NAMESPACE, LENS_LLM_* and
  LENS_SETUP_WIZARD settings through from lens.env, empty by default.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KSuGMk2LX7Csu1f7pKqoFC
…or the namespace

The QNAP archive.yaml no longer defines a media namespace: the first-run setup asks for
one (or LENS_NAMESPACE names it) and can watch /audio, the NAS's Multimedia/Lens folder.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KSuGMk2LX7Csu1f7pKqoFC
@adeelahmad adeelahmad changed the title Lens for QNAP NAS: a self-contained QPKG feat(packaging): self-contained QNAP QPKG, built for each release Oct 2, 2026
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SNbRrpHTCqLewRKdkmd45X
@adeelahmad
adeelahmad marked this pull request as ready for review October 2, 2026 13:32
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SNbRrpHTCqLewRKdkmd45X
@adeelahmad
adeelahmad merged commit 73ea6c0 into main Oct 2, 2026
0 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0