chore: update skills
23763b65
1 file(s) · +163 −44
| 9 | 9 | ||
| 10 | 10 | Scaffold and build Rust CRUD web applications in the andromeda workspace using Axum + SQLite + Askama templates + `andromeda-auth` for authentication. The result is a single binary web server with HTML pages, a JSON API, optional session or API key auth, and Docker deployment. |
|
| 11 | 11 | ||
| 12 | - | All apps live under `apps/` in the workspace and share dependencies via the root `Cargo.toml`. The `andromeda-auth` crate at `crates/auth/` provides shared authentication primitives. |
|
| 12 | + | All apps live under `apps/` in the workspace and share dependencies via the root `Cargo.toml`. Shared crates live under `crates/`: |
|
| 13 | + | ||
| 14 | + | - `andromeda-auth` — password verify, session token, cookie helpers |
|
| 15 | + | - `andromeda-db` — DB connection helpers, optional `session` feature |
|
| 16 | + | - `andromeda-darkmatter-css` — shared CSS theme |
|
| 13 | 17 | ||
| 14 | 18 | ## Project Structure |
|
| 15 | 19 | ||
| 58 | 62 | tracing = { workspace = true } |
|
| 59 | 63 | tracing-subscriber = { workspace = true } |
|
| 60 | 64 | andromeda-auth = { workspace = true } |
|
| 65 | + | andromeda-db = { workspace = true, features = ["session"] } # if using session auth |
|
| 66 | + | andromeda-darkmatter-css = { workspace = true } # if using shared theme |
|
| 61 | 67 | askama = "0.15" |
|
| 62 | 68 | askama_web = { version = "0.15", features = ["axum-0.8"] } |
|
| 63 | 69 | ``` |
|
| 64 | 70 | ||
| 65 | - | After creating the app, register it in the workspace root `Cargo.toml` under `[workspace] members`. |
|
| 71 | + | After creating the app, register it in the workspace root `Cargo.toml` under `[workspace] members`. See **Wiring up a new app** below for the full set of workspace files to update. |
|
| 66 | 72 | ||
| 67 | 73 | Only add additional crates when the specific app requires them. Do NOT include TUI crates (ratatui, crossterm), CLI crates (clap), or HTTP client crates (reqwest) unless explicitly requested. |
|
| 68 | 74 | ||
| 527 | 533 | ||
| 528 | 534 | ## Dockerfile |
|
| 529 | 535 | ||
| 530 | - | Multi-stage workspace build. Must be built from the repo root with `docker build -f apps/APP_NAME/Dockerfile .`: |
|
| 536 | + | Multi-stage workspace build using `cargo-chef` for dep caching. Built from repo root with `docker build -f apps/APP_NAME/Dockerfile .`: |
|
| 531 | 537 | ||
| 532 | 538 | ```dockerfile |
|
| 533 | 539 | # Build from repo root: docker build -t APP_NAME -f apps/APP_NAME/Dockerfile . |
|
| 534 | - | FROM rust:1-slim-bookworm AS builder |
|
| 535 | - | RUN apt-get update && apt-get install -y pkg-config libssl-dev && rm -rf /var/lib/apt/lists/* |
|
| 540 | + | FROM lukemathwalker/cargo-chef:latest-rust-1-slim-bookworm AS chef |
|
| 536 | 541 | WORKDIR /app |
|
| 537 | 542 | ||
| 538 | - | # Copy workspace manifests for dependency caching |
|
| 539 | - | COPY Cargo.toml Cargo.lock . |
|
| 540 | - | COPY crates/auth/Cargo.toml crates/auth/ |
|
| 541 | - | # Copy all app Cargo.tomls (needed for workspace resolution) |
|
| 542 | - | COPY apps/sipp/Cargo.toml apps/sipp/ |
|
| 543 | - | COPY apps/feeds/Cargo.toml apps/feeds/ |
|
| 544 | - | COPY apps/parcels/Cargo.toml apps/parcels/ |
|
| 545 | - | COPY apps/jotts/Cargo.toml apps/jotts/ |
|
| 546 | - | COPY apps/og/Cargo.toml apps/og/ |
|
| 547 | - | COPY apps/shrink/Cargo.toml apps/shrink/ |
|
| 548 | - | COPY apps/APP_NAME/Cargo.toml apps/APP_NAME/ |
|
| 549 | - | ||
| 550 | - | # Create stubs for dependency caching |
|
| 551 | - | RUN mkdir -p crates/auth/src && echo '' > crates/auth/src/lib.rs \ |
|
| 552 | - | && for app in sipp feeds parcels jotts og shrink APP_NAME; do \ |
|
| 553 | - | mkdir -p apps/$app/src && echo 'fn main() {}' > apps/$app/src/main.rs; \ |
|
| 554 | - | done |
|
| 543 | + | FROM chef AS planner |
|
| 544 | + | COPY . . |
|
| 545 | + | RUN cargo chef prepare --recipe-path recipe.json |
|
| 555 | 546 | ||
| 547 | + | FROM chef AS builder |
|
| 548 | + | RUN apt-get update && apt-get install -y pkg-config libssl-dev && rm -rf /var/lib/apt/lists/* |
|
| 549 | + | COPY --from=planner /app/recipe.json recipe.json |
|
| 550 | + | RUN cargo chef cook --release --recipe-path recipe.json -p APP_NAME |
|
| 551 | + | COPY . . |
|
| 556 | 552 | RUN cargo build --release -p APP_NAME |
|
| 557 | - | ||
| 558 | - | # Copy real source |
|
| 559 | - | COPY crates/auth/src crates/auth/src |
|
| 560 | - | COPY apps/APP_NAME/src apps/APP_NAME/src |
|
| 561 | - | COPY apps/APP_NAME/static apps/APP_NAME/static |
|
| 562 | - | COPY apps/APP_NAME/templates apps/APP_NAME/templates |
|
| 563 | - | ||
| 564 | - | RUN touch apps/APP_NAME/src/*.rs crates/auth/src/*.rs && cargo build --release -p APP_NAME |
|
| 565 | 553 | ||
| 566 | 554 | FROM debian:bookworm-slim |
|
| 567 | 555 | COPY --from=builder /app/target/release/APP_NAME /usr/local/bin/APP_NAME |
|
| 572 | 560 | CMD ["APP_NAME"] |
|
| 573 | 561 | ``` |
|
| 574 | 562 | ||
| 575 | - | Replace all `APP_NAME` with the actual binary/package name. If the app makes HTTPS requests (e.g., uses reqwest), add `RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*` to the final stage. |
|
| 563 | + | Replace all `APP_NAME` with actual binary/package name. If app makes HTTPS requests (uses reqwest etc.), add `RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*` to final stage. |
|
| 576 | 564 | ||
| 577 | 565 | ## docker-compose.yml |
|
| 578 | 566 | ||
| 607 | 595 | ||
| 608 | 596 | Always create one with all configurable env vars and sensible comments. |
|
| 609 | 597 | ||
| 598 | + | ## Wiring up a new app |
|
| 599 | + | ||
| 600 | + | A new app is not done when `cargo run -p APP_NAME` works. Several workspace-level files list every app explicitly and must be updated, or CI/release/deploy break silently. |
|
| 601 | + | ||
| 602 | + | ### 1. Workspace root `Cargo.toml` |
|
| 603 | + | ||
| 604 | + | Add to `[workspace] members`: |
|
| 605 | + | ||
| 606 | + | ```toml |
|
| 607 | + | [workspace] |
|
| 608 | + | members = [ |
|
| 609 | + | "apps/sipp", |
|
| 610 | + | # ... |
|
| 611 | + | "apps/APP_NAME", |
|
| 612 | + | "crates/auth", |
|
| 613 | + | "crates/db", |
|
| 614 | + | "crates/darkmatter-css", |
|
| 615 | + | ] |
|
| 616 | + | ``` |
|
| 617 | + | ||
| 618 | + | ### 2. `dist-workspace.toml` |
|
| 619 | + | ||
| 620 | + | Add to `[workspace] members` so `cargo dist` builds release binaries + Homebrew formula: |
|
| 621 | + | ||
| 622 | + | ```toml |
|
| 623 | + | [workspace] |
|
| 624 | + | members = [ |
|
| 625 | + | "cargo:apps/sipp", |
|
| 626 | + | # ... |
|
| 627 | + | "cargo:apps/APP_NAME", |
|
| 628 | + | ] |
|
| 629 | + | ``` |
|
| 630 | + | ||
| 631 | + | ### 3. `.github/workflows/docker.yml` |
|
| 632 | + | ||
| 633 | + | Two spots — both list every app: |
|
| 634 | + | ||
| 635 | + | ```yaml |
|
| 636 | + | ALL='["cellar","sipp","feeds","parcels","jotts","og","shrink","backup","posts","library","APP_NAME"]' |
|
| 637 | + | ``` |
|
| 638 | + | ||
| 639 | + | ```yaml |
|
| 640 | + | for app in cellar sipp feeds parcels jotts og shrink backup posts library APP_NAME; do |
|
| 641 | + | ``` |
|
| 642 | + | ||
| 643 | + | If the cargo package name differs from directory name, add a case to `pkg_to_dir()`: |
|
| 644 | + | ||
| 645 | + | ```bash |
|
| 646 | + | pkg_to_dir() { |
|
| 647 | + | case "$1" in |
|
| 648 | + | sipp-so) echo "sipp" ;; |
|
| 649 | + | APP_PKG) echo "APP_NAME" ;; |
|
| 650 | + | *) echo "$1" ;; |
|
| 651 | + | esac |
|
| 652 | + | } |
|
| 653 | + | ``` |
|
| 654 | + | ||
| 655 | + | ### 4. `.github/workflows/docker-test.yml` |
|
| 656 | + | ||
| 657 | + | Same two lists — keep in sync with `docker.yml`: |
|
| 658 | + | ||
| 659 | + | ```yaml |
|
| 660 | + | ALL='["cellar","sipp","feeds","parcels","jotts","og","shrink","backup","posts","library","APP_NAME"]' |
|
| 661 | + | ``` |
|
| 662 | + | ||
| 663 | + | ```yaml |
|
| 664 | + | for app in cellar sipp feeds parcels jotts og shrink backup posts library APP_NAME; do |
|
| 665 | + | ``` |
|
| 666 | + | ||
| 667 | + | ### 5. Root `docker-compose.yml` |
|
| 668 | + | ||
| 669 | + | Production compose at repo root pulls images from GHCR. Add a service + volume: |
|
| 670 | + | ||
| 671 | + | ```yaml |
|
| 672 | + | services: |
|
| 673 | + | APP_NAME: |
|
| 674 | + | image: ghcr.io/stevedylandev/andromeda/APP_NAME:latest |
|
| 675 | + | restart: unless-stopped |
|
| 676 | + | ports: |
|
| 677 | + | - "PORT:3000" |
|
| 678 | + | volumes: |
|
| 679 | + | - APP_NAME_data:/data |
|
| 680 | + | env_file: apps/APP_NAME/.env |
|
| 681 | + | ||
| 682 | + | volumes: |
|
| 683 | + | APP_NAME_data: |
|
| 684 | + | external: true |
|
| 685 | + | name: APP_NAME_APP_NAME-data |
|
| 686 | + | ``` |
|
| 687 | + | ||
| 688 | + | Pick a unique host port (existing: 3000 sipp, 3030 jotts, 3456 og, 3535 posts, 3565 shrink, 4555 feeds, 4646 library, 6754 cellar). Drop `volumes`/`env_file` if app is stateless (see `shrink`, `og`). |
|
| 689 | + | ||
| 690 | + | If the app holds data worth backing up, add a read-only mount to the `backup` service: |
|
| 691 | + | ||
| 692 | + | ```yaml |
|
| 693 | + | backup: |
|
| 694 | + | volumes: |
|
| 695 | + | - APP_NAME_data:/data/APP_NAME:ro |
|
| 696 | + | ``` |
|
| 697 | + | ||
| 698 | + | ### 6. App-local `docker-compose.yml` |
|
| 699 | + | ||
| 700 | + | Per-app compose for local dev — builds from source: |
|
| 701 | + | ||
| 702 | + | ```yaml |
|
| 703 | + | services: |
|
| 704 | + | app: |
|
| 705 | + | build: |
|
| 706 | + | context: ../.. |
|
| 707 | + | dockerfile: apps/APP_NAME/Dockerfile |
|
| 708 | + | ports: |
|
| 709 | + | - "${PORT:-3000}:${PORT:-3000}" |
|
| 710 | + | environment: |
|
| 711 | + | - APP_PASSWORD=${APP_PASSWORD:-changeme} |
|
| 712 | + | - APP_DB_PATH=/data/APP_NAME.sqlite |
|
| 713 | + | - COOKIE_SECURE=false |
|
| 714 | + | - HOST=0.0.0.0 |
|
| 715 | + | - PORT=${PORT:-3000} |
|
| 716 | + | volumes: |
|
| 717 | + | - APP_NAME-data:/data |
|
| 718 | + | restart: unless-stopped |
|
| 719 | + | ||
| 720 | + | volumes: |
|
| 721 | + | APP_NAME-data: |
|
| 722 | + | ``` |
|
| 723 | + | ||
| 610 | 724 | ## Checklist |
|
| 611 | 725 | ||
| 612 | - | When scaffolding a new app with this pattern: |
|
| 726 | + | When scaffolding a new app: |
|
| 613 | 727 | ||
| 614 | - | 1. Create `apps/app-name/` with `cargo init` |
|
| 615 | - | 2. Register in workspace root `Cargo.toml` under `[workspace] members` |
|
| 616 | - | 3. Set up `Cargo.toml` with workspace dependencies + `andromeda-auth` |
|
| 617 | - | 4. Write `db.rs` — schema, model struct, CRUD functions, session table if needed |
|
| 618 | - | 5. Write `auth.rs` — wrap `andromeda-auth` with `AuthSession` extractor (if auth needed) |
|
| 619 | - | 6. Write `server.rs` — config, state, templates, handlers, routes |
|
| 620 | - | 7. Write `main.rs` — minimal entry point |
|
| 621 | - | 8. Create `templates/` with at least a `base.html` and index page |
|
| 622 | - | 9. Create `static/styles.css` |
|
| 623 | - | 10. Create `.env.example` |
|
| 624 | - | 11. Create `Dockerfile` (workspace-aware multi-stage) and `docker-compose.yml` |
|
| 625 | - | 12. Test: `cargo run -p app-name`, verify routes work |
|
| 728 | + | 1. Create `apps/APP_NAME/` with `cargo init` |
|
| 729 | + | 2. Set up `apps/APP_NAME/Cargo.toml` with workspace deps + `andromeda-auth` (and `andromeda-db`, `andromeda-darkmatter-css` if used) |
|
| 730 | + | 3. Register app in workspace root `Cargo.toml` under `[workspace] members` |
|
| 731 | + | 4. Register app in `dist-workspace.toml` under `[workspace] members` (with `cargo:` prefix) |
|
| 732 | + | 5. Add app to `ALL` array + `for app in ...` loop in `.github/workflows/docker.yml` |
|
| 733 | + | 6. Add app to `ALL` array + `for app in ...` loop in `.github/workflows/docker-test.yml` |
|
| 734 | + | 7. Write `db.rs` — schema, model, CRUD, session table if needed |
|
| 735 | + | 8. Write `auth.rs` — wrap `andromeda-auth` with `AuthSession` extractor (if auth needed) |
|
| 736 | + | 9. Write `server.rs` — config, state, templates, handlers, routes |
|
| 737 | + | 10. Write `main.rs` — minimal entry point |
|
| 738 | + | 11. Create `templates/` with `base.html` + index page |
|
| 739 | + | 12. Create `static/styles.css` |
|
| 740 | + | 13. Create `.env.example` |
|
| 741 | + | 14. Create `Dockerfile` (cargo-chef multi-stage) + app-local `docker-compose.yml` |
|
| 742 | + | 15. Add service entry + volume to root `docker-compose.yml` (and `backup` mount if stateful) |
|
| 743 | + | 16. Test: `cargo run -p APP_NAME`, hit routes |
|
| 744 | + | 17. Test: `docker build -f apps/APP_NAME/Dockerfile .` from repo root |
|
| 626 | 745 | ||
| 627 | 746 | ## What NOT to include |
|
| 628 | 747 | ||