CI & Build / Build now, or wait for Android? (push) Successful in 3s
CI & Build / Python lint (push) Successful in 3s
Android / Build, or is the channel already serving this? (push) Successful in 3s
Android / Core and FFI clippy and tests (push) Skipped
Android / Kotlin + Rust (APK) (push) Skipped
Android / Build the server image (push) Skipped
Desktop (Tauri) / Build, or is the channel already serving this? (push) Successful in 2s
CI & Build / Web typecheck and unit tests (push) Successful in 10s
CI & Build / Python tests (push) Successful in 10s
CI & Build / integration (push) Successful in 1m15s
Desktop (Tauri) / Web tests, clippy, Rust tests and rustfmt (push) Successful in 2m5s
CI & Build / Build & push image (push) Successful in 55s
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 2m15s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 2m57s
Desktop (Tauri) / Update manifest (push) Successful in 6s
Milestone 325 step 5 (Scribe #3253). curl -fsSL https://notes.example.com/install.sh | sh The server serves the installer at /install.sh with its own address written into it (installer.py). Settings → Public address when set, the request's own address otherwise. The substitution is one variable, given a value that has passed a strict shape check, and the script checks it again; an address that cannot pass makes the route refuse rather than serve a script pointed elsewhere. `public_url` joins the live settings cache so the route needs no database. From a server, the script: - resolves each Linux bundle from the public /api/client/<platform>, and builds the download URL from the platform id rather than reading it from the reply; - asks for a device token (from the terminal, since stdin is the script), or takes TS_TOKEN, and sends it from a file rather than the command line; - checks the sha256 the server published before anything installs; - revokes a prompted token once the download is done; - records `install-server` for the updater (step 6) instead of the channel. The forge path is unchanged, and stays the default for the copy the forge serves. The Account page's downloads card shows the one-line command whenever the server holds a Linux client. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
61 lines
2.5 KiB
Docker
61 lines
2.5 KiB
Docker
# syntax=docker/dockerfile:1
|
|
|
|
# Stage 1: build the Vue frontend (also type-checks via `vue-tsc` in the build script).
|
|
FROM node:22-alpine AS build-frontend
|
|
WORKDIR /build
|
|
COPY frontend/package.json frontend/package-lock.json* ./
|
|
RUN npm ci --quiet
|
|
COPY frontend/ .
|
|
RUN npm run build
|
|
|
|
# Stage 2: Python runtime.
|
|
FROM python:3.12-slim AS runtime
|
|
WORKDIR /app
|
|
|
|
COPY pyproject.toml .
|
|
COPY src/ src/
|
|
RUN --mount=type=cache,target=/root/.cache/pip \
|
|
pip install .
|
|
|
|
# Bake the built SPA into the package's static dir (served by app.py). PYTHONPATH
|
|
# points at /app/src so the runtime imports this source tree (with static/ present),
|
|
# not the pip-installed copy.
|
|
COPY --from=build-frontend /build/dist/ src/inkwell/static/
|
|
COPY alembic.ini .
|
|
COPY alembic/ alembic/
|
|
|
|
# The clients this server hands out — the APK and all four desktop bundles. CI
|
|
# fetches the newest published build of each into ./client immediately before this
|
|
# runs (packaging/fetch-clients.sh), so both image tags ship a full set and a
|
|
# `docker compose pull` delivers new ones with no file copying by hand.
|
|
#
|
|
# ~104 MB of this image is that set, almost all of it the AppImage.
|
|
#
|
|
# Fetched by the JOB rather than here on purpose: the releases are private, and a
|
|
# token used inside a build ends up in the build context or a layer.
|
|
#
|
|
# LAST of the COPYs, deliberately: this directory changes on every build, so
|
|
# putting it above the `pip install` layer would invalidate that layer every time.
|
|
#
|
|
# The directory is tracked (client/.keep) so this COPY cannot fail on a tree where
|
|
# that step never ran. An image with no clients — or with some and not others — is
|
|
# a supported state: the server advertises what it has and the web UI hides the
|
|
# rest (client_dist.py).
|
|
COPY client/ src/inkwell/client/
|
|
|
|
# The desktop installer, served at /install.sh with this server's own address
|
|
# written into it (installer.py). The SAME script the forge serves raw, copied rather
|
|
# than moved so the operator's documented forge URL keeps working.
|
|
COPY desktop/packaging/install.sh src/inkwell/install.sh
|
|
|
|
ENV PYTHONPATH=/app/src
|
|
|
|
ARG BUILD_VERSION=dev
|
|
ENV APP_VERSION=$BUILD_VERSION
|
|
|
|
EXPOSE 5000
|
|
# Wait for the database, run migrations, then serve. The DB wait keeps a briefly
|
|
# slow/unready database from crash-looping the container. Family convention
|
|
# (rule 82): schema is built by real migrations, never metadata.create_all.
|
|
CMD ["sh", "-c", "python -m inkwell.dbwait && alembic upgrade head && hypercorn 'inkwell.app:create_app()' --bind 0.0.0.0:5000 --keep-alive 600"]
|