M12 — the Android client, end to end #2
@@ -0,0 +1,54 @@
|
||||
# ThoughtSync production settings. Copy to `.env` and edit:
|
||||
#
|
||||
# cp .env.example .env
|
||||
#
|
||||
# Only POSTGRES_PASSWORD has no default — compose refuses to start without it.
|
||||
# Everything else here is optional. Anything NOT in this file (site name, signups,
|
||||
# attachment limits, trash retention, link previews) is configured in the admin
|
||||
# Settings UI and stored in the database, not here.
|
||||
|
||||
# --- required ---------------------------------------------------------------
|
||||
|
||||
# Generate one and keep it: changing it later means also changing it inside the
|
||||
# database, or Postgres will reject the app's connection.
|
||||
#
|
||||
# openssl rand -base64 24 | tr -d '/+=' | head -c 32
|
||||
#
|
||||
# Stick to letters and digits. This value goes into a connection URL, so a `@`,
|
||||
# `/`, `:` or `#` in it will be misparsed as URL structure rather than password.
|
||||
POSTGRES_PASSWORD=
|
||||
|
||||
# --- optional ---------------------------------------------------------------
|
||||
|
||||
# Which build to run.
|
||||
#
|
||||
# latest tracks the `main` branch — the production line (default)
|
||||
# dev tracks the `dev` branch — newer, less settled
|
||||
# <commit sha> pins one exact build; every push publishes one, and this is
|
||||
# the rollback lever when an upgrade misbehaves
|
||||
#
|
||||
# NOTE: `main` can sit well behind `dev`. If a feature you expect is missing,
|
||||
# check which branch it actually landed on before assuming a bug.
|
||||
#THOUGHTSYNC_TAG=latest
|
||||
|
||||
# The host port the app is published on.
|
||||
#THOUGHTSYNC_PORT=5000
|
||||
|
||||
# Which interface to bind. The default (all interfaces) is what lets desktop
|
||||
# clients on your network reach the server. Behind a reverse proxy, set this to
|
||||
# 127.0.0.1 so only the proxy can talk to it.
|
||||
#THOUGHTSYNC_BIND=0.0.0.0
|
||||
|
||||
# Database identity. Changing these AFTER the first start does not rename anything
|
||||
# that already exists — the volume keeps whatever the first run created.
|
||||
#POSTGRES_USER=thoughtsync
|
||||
#POSTGRES_DB=thoughtsync
|
||||
|
||||
# --- a note on HTTPS --------------------------------------------------------
|
||||
#
|
||||
# The app marks its session cookie Secure automatically when a request arrives over
|
||||
# HTTPS, directly or via a proxy setting X-Forwarded-Proto — no setting needed.
|
||||
#
|
||||
# Worth knowing if you use the desktop app: typing a bare hostname there defaults to
|
||||
# https://, deliberately, so a device token never crosses the wire in cleartext by
|
||||
# accident. Serving over plain HTTP means typing the `http://` yourself.
|
||||
@@ -91,8 +91,11 @@ jobs:
|
||||
else
|
||||
echo "No TAURI_SIGNING_PRIVATE_KEY — building unsigned, no updater artifacts."
|
||||
fi
|
||||
version="$(sh ../packaging/build-version.sh)"
|
||||
echo "Building version $version"
|
||||
cargo tauri build \
|
||||
--config '{"build":{"beforeBuildCommand":""}}' \
|
||||
--config "{\"version\":\"$version\"}" \
|
||||
--config "$updater"
|
||||
working-directory: desktop/src-tauri
|
||||
|
||||
@@ -217,12 +220,23 @@ jobs:
|
||||
# the MSVC CRT/SDK, pre-warmed into the image, and links with lld-link).
|
||||
# Frontend already built above; skip the beforeBuildCommand rebuild.
|
||||
- name: Tauri build (NSIS installer)
|
||||
env:
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
run: |
|
||||
version="$(sh ../packaging/build-version.sh)"
|
||||
echo "Building version $version"
|
||||
updater='{}'
|
||||
if [ -n "${TAURI_SIGNING_PRIVATE_KEY:-}" ]; then
|
||||
updater='{"bundle":{"createUpdaterArtifacts":true}}'
|
||||
fi
|
||||
cargo tauri build \
|
||||
--runner cargo-xwin \
|
||||
--target x86_64-pc-windows-msvc \
|
||||
--bundles nsis \
|
||||
--config '{"build":{"beforeBuildCommand":""}}'
|
||||
--config '{"build":{"beforeBuildCommand":""}}' \
|
||||
--config "{\"version\":\"$version\"}" \
|
||||
--config "$updater"
|
||||
working-directory: desktop/src-tauri
|
||||
|
||||
- name: Upload installer
|
||||
@@ -292,8 +306,10 @@ jobs:
|
||||
echo "manifest to write. Add the secret to enable in-app updates."
|
||||
exit 0
|
||||
fi
|
||||
# The version the bundles carry, read from the crate rather than guessed.
|
||||
version="$(grep -m1 '^version' desktop/src-tauri/Cargo.toml | sed -E 's/.*"([^"]+)".*/\1/')"
|
||||
# The SAME helper the bundles were built with — a second derivation here
|
||||
# could drift, and a manifest whose version doesn't match the binary it
|
||||
# points at is an updater that never settles.
|
||||
version="$(sh desktop/packaging/build-version.sh)"
|
||||
if [ "${GITHUB_REF_NAME}" = "dev" ]; then
|
||||
export RELEASE_TAG=dev
|
||||
export RELEASE_NOTES="Development build from ${GITHUB_SHA}"
|
||||
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env sh
|
||||
#
|
||||
# Echo the version this build should carry. One definition, used in three places
|
||||
# (both bundle jobs and the manifest writer) — if they ever disagreed, the app would
|
||||
# compare its own version against a manifest describing a different build, and the
|
||||
# updater would either offer nothing or loop forever offering the same thing.
|
||||
#
|
||||
# WHY DEV BUILDS NEED THEIR OWN VERSION AT ALL:
|
||||
# an updater decides by comparing semver. Every dev build carries the version in
|
||||
# Cargo.toml, so without this they'd all be `0.1.0` — an installed build would see a
|
||||
# manifest advertising the version it already has, conclude it was current, and never
|
||||
# update. The rolling channel needs a number that actually rises.
|
||||
#
|
||||
# The CI run number is that number: monotonic, already unique per build, and it needs
|
||||
# no state carried between runs. `0.1.0` + run 2932 becomes `0.1.2932`.
|
||||
#
|
||||
# Plain semver on purpose, NOT a `-dev.N` prerelease tag: prerelease versions sort
|
||||
# BELOW the release they qualify (`0.1.0-dev.5` < `0.1.0`), so a tagged build would
|
||||
# never update to a newer dev one, and Windows installer metadata wants a numeric
|
||||
# X.Y.Z anyway. Bumping the minor in Cargo.toml still wins over any dev build on the
|
||||
# old line, which is the ordering you want: 0.2.0 > 0.1.2932.
|
||||
set -eu
|
||||
|
||||
CARGO_TOML="$(dirname "$0")/../src-tauri/Cargo.toml"
|
||||
base="$(grep -m1 '^version' "$CARGO_TOML" | sed -E 's/.*"([^"]+)".*/\1/')"
|
||||
|
||||
# Dev builds only. Anything else (a v* tag, main) ships the version as written.
|
||||
if [ "${GITHUB_REF_NAME:-}" = "dev" ] && [ -n "${GITHUB_RUN_NUMBER:-}" ]; then
|
||||
printf '%s.%s\n' "${base%.*}" "$GITHUB_RUN_NUMBER"
|
||||
else
|
||||
printf '%s\n' "$base"
|
||||
fi
|
||||
@@ -32,7 +32,7 @@
|
||||
"endpoints": [
|
||||
"https://git.fabledsword.com/bvandeusen/thoughtsync/releases/download/stable/latest.json"
|
||||
],
|
||||
"pubkey": "",
|
||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDkwRTk2RkVBMkY2RDlCNkEKUldScW0yMHY2bS9wa0VBdWFpM3c1d2trQnlNVUJXUUtwZXBzQjduM3FRVzdGa3dXNGxObkZFV28K",
|
||||
"windows": {
|
||||
"installMode": "passive"
|
||||
}
|
||||
|
||||
@@ -5,7 +5,11 @@
|
||||
# docker compose -f docker-compose.dev.yml up
|
||||
#
|
||||
# Then open http://localhost:5173 (the Vite dev server proxies /api to the backend).
|
||||
# (docker-compose.yml, by contrast, builds + runs the production image on :5000.)
|
||||
#
|
||||
# docker-compose.yml — the DEFAULT file — is the production stack instead: it pulls
|
||||
# the published image, keeps Postgres off the host network, and expects a .env. This
|
||||
# one builds nothing and is deliberately insecure-by-convenience (weak password,
|
||||
# Postgres published on 5432) because it is meant for a laptop, not a deployment.
|
||||
services:
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
|
||||
+76
-12
@@ -1,34 +1,98 @@
|
||||
# Local two-service stack (app + Postgres). Provided for convenience — per family
|
||||
# rule 12 the agent does NOT start this; run it yourself with `docker compose up`.
|
||||
# ThoughtSync — PRODUCTION stack (app + Postgres).
|
||||
#
|
||||
# This is the default compose file: `docker compose up -d` runs a real deployment
|
||||
# from the published image. Development lives in docker-compose.dev.yml (hot-reload,
|
||||
# builds from source).
|
||||
#
|
||||
# cp .env.example .env # then set POSTGRES_PASSWORD
|
||||
# docker compose up -d
|
||||
#
|
||||
# Per family rule 12 the agent does NOT start this — run it yourself.
|
||||
#
|
||||
# Upgrades: docker compose pull && docker compose up -d
|
||||
# Rollback: set THOUGHTSYNC_TAG to a commit sha in .env, then the same two commands.
|
||||
# Every push publishes an immutable :<sha> image for exactly this.
|
||||
#
|
||||
# Schema migrations run automatically at container start (see the Dockerfile CMD),
|
||||
# so an upgrade is just a pull and a restart. Take a backup first anyway:
|
||||
#
|
||||
# docker compose exec -T db pg_dump -U thoughtsync thoughtsync > backup.sql
|
||||
#
|
||||
# Attachments are files, not rows — they live in the `thoughtsync-data` volume and
|
||||
# a pg_dump does NOT contain them. Back up both or you'll restore notes whose images
|
||||
# are gone.
|
||||
|
||||
services:
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: thoughtsync
|
||||
POSTGRES_PASSWORD: thoughtsync
|
||||
POSTGRES_DB: thoughtsync
|
||||
POSTGRES_USER: ${POSTGRES_USER:-thoughtsync}
|
||||
# No default on purpose. A production compose that ships a known password is
|
||||
# how self-hosted databases end up in search engines; compose fails fast here
|
||||
# instead, with the message below.
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env — see .env.example}
|
||||
POSTGRES_DB: ${POSTGRES_DB:-thoughtsync}
|
||||
volumes:
|
||||
# Volume names kept from the previous compose file so an existing deployment
|
||||
# upgrades in place. Renaming them would silently start against an empty
|
||||
# database while the old one sat there, orphaned and looking like data loss.
|
||||
- thoughtsync-db:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "5432:5432"
|
||||
# Deliberately NOT published to the host. The app reaches Postgres over the
|
||||
# compose network; exposing 5432 only widens the attack surface. If you need
|
||||
# psql, `docker compose exec db psql -U thoughtsync` gets you there without it.
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U thoughtsync"]
|
||||
interval: 5s
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-thoughtsync} -d ${POSTGRES_DB:-thoughtsync}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 30s
|
||||
logging: &logging
|
||||
driver: json-file
|
||||
options:
|
||||
# Unbounded container logs are a slow-motion disk-full outage on a
|
||||
# long-running self-hosted box.
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
|
||||
app:
|
||||
build: .
|
||||
# :latest tracks `main`. Set THOUGHTSYNC_TAG=dev in .env to follow the
|
||||
# development line instead, or a commit sha to pin exactly.
|
||||
image: git.fabledsword.com/bvandeusen/thoughtsync:${THOUGHTSYNC_TAG:-latest}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
THOUGHTSYNC_DATABASE_URL: postgresql+asyncpg://thoughtsync:thoughtsync@db:5432/thoughtsync
|
||||
# The only required application setting. Everything else a person might want
|
||||
# to tune lives in the admin Settings UI, backed by the database (rule 25).
|
||||
THOUGHTSYNC_DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-thoughtsync}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-thoughtsync}
|
||||
volumes:
|
||||
# Uploaded attachments. /var/thoughtsync is fixed in the app (Config.DATA_DIR),
|
||||
# not configurable — mount it or lose every image on container recreation.
|
||||
- thoughtsync-data:/var/thoughtsync
|
||||
ports:
|
||||
- "5000:5000"
|
||||
# Default binds every interface, which is what lets desktop clients on the LAN
|
||||
# reach it. Behind a reverse proxy, set THOUGHTSYNC_BIND=127.0.0.1 so only the
|
||||
# proxy can talk to it.
|
||||
- "${THOUGHTSYNC_BIND:-0.0.0.0}:${THOUGHTSYNC_PORT:-5000}:5000"
|
||||
healthcheck:
|
||||
# python rather than curl: the runtime image is python:3.12-slim and carries no
|
||||
# HTTP client binary. Hits the app's own /api/health.
|
||||
test:
|
||||
- CMD
|
||||
- python
|
||||
- -c
|
||||
- |
|
||||
import sys, urllib.request
|
||||
sys.exit(0 if urllib.request.urlopen("http://127.0.0.1:5000/api/health", timeout=5).status == 200 else 1)
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
# Generous: the container waits for Postgres and runs migrations before it
|
||||
# serves anything, and a first boot builds the whole schema.
|
||||
start_period: 60s
|
||||
logging: *logging
|
||||
|
||||
volumes:
|
||||
thoughtsync-db:
|
||||
|
||||
Reference in New Issue
Block a user