From 4afee7626ed71973b03505590aa488390e9b12d1 Mon Sep 17 00:00:00 2001 From: charlieroth Date: Fri, 22 Aug 2025 19:45:47 +0200 Subject: [PATCH 1/6] feat: add comprehensive GitHub Actions CI workflow - Add CI workflow with format, lint, test, audit, and deny checks - Configure PostgreSQL service for database-dependent tests - Set up Rust and SQLx dependency caching - Add matrix testing for stable and beta Rust compilers - Add CI status badge to README Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-b79731c7-828b-4935-897f-696188919afc --- .github/workflows/ci.yml | 91 ++++++++++++++++++++++++++++++++++++++++ README.md | 2 + 2 files changed, 93 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..bf2d532 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,91 @@ +name: CI + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +env: + CARGO_TERM_COLOR: always + DATABASE_URL: postgres://capsule:capsule_password@localhost:5432/capsule_dev + +jobs: + ci: + name: CI + runs-on: ubuntu-latest + strategy: + matrix: + rust: [stable, beta] + + services: + postgres: + image: postgres:16 + env: + POSTGRES_USER: capsule + POSTGRES_PASSWORD: capsule_password + POSTGRES_DB: capsule_dev + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + components: rustfmt, clippy + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + with: + shared-key: "ci-cache" + + - name: Install tools + run: | + cargo install sqlx-cli --no-default-features --features native-tls,postgres + cargo install cargo-audit + cargo install cargo-deny + + - name: Cache SQLx + uses: actions/cache@v4 + with: + path: | + ~/.cargo/bin/sqlx + .sqlx + key: sqlx-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} + + - name: Wait for PostgreSQL + run: | + until pg_isready -h localhost -p 5432 -U capsule; do + echo "Waiting for PostgreSQL..." + sleep 2 + done + + - name: Run database migrations + run: sqlx migrate run + + - name: Check formatting + run: cargo fmt --all -- --check + + - name: Run clippy + run: cargo clippy --all-targets --all-features -- -D warnings + + - name: Run tests + run: cargo test --all-features + + - name: SQLx prepare check + run: cargo sqlx prepare --check --workspace -- --all-features + + - name: Security audit + run: cargo audit + + - name: License/dependency check + run: cargo deny check diff --git a/README.md b/README.md index ba88fac..46f3bba 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # capsule +[![CI](https://github.com/charlieroth/capsule/actions/workflows/ci.yml/badge.svg)](https://github.com/charlieroth/capsule/actions/workflows/ci.yml) + A pragmatic "read later" service built in Rust to explore production-grade web service patterns: - Authentication From 5e4c60918d965eea0254a140e8b7e21aa6e3acb3 Mon Sep 17 00:00:00 2001 From: charlieroth Date: Fri, 22 Aug 2025 19:46:31 +0200 Subject: [PATCH 2/6] chore: update Rust container version in Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index d4e7df9..3d17e9b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # build -FROM rust:1.85-bookworm AS build +FROM rust:1.89-bookworm AS build WORKDIR /app # cache deps COPY Cargo.toml Cargo.lock ./ From 1288c330b118bd8c4663b571fea4d9ec033a505e Mon Sep 17 00:00:00 2001 From: charlieroth Date: Fri, 22 Aug 2025 19:59:11 +0200 Subject: [PATCH 3/6] fix: add SQLx metadata for CI prepare check - Remove .sqlx from .gitignore to commit offline metadata - Generate SQLx prepare files for compile-time query verification - Fixes CI SQLx prepare check step Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-b79731c7-828b-4935-897f-696188919afc --- .gitignore | 3 +- ...17e90171f7e80eef83d0e9c38dc2cbd252da8.json | 15 +++++++ ...bbc9f6698d2e3d860666b6c8d83af939305c3.json | 40 ++++++++++++++++++ ...f86e7293b8a88699a34fcd5ba65ddcf965a3c.json | 41 +++++++++++++++++++ ...91327ee418b14d2fb11ea1656045a0ec4857f.json | 40 ++++++++++++++++++ ...528466926789ff31e9ed2591bb175527ec169.json | 14 +++++++ 6 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 .sqlx/query-5ab1cfb901456f7e2e492036bd517e90171f7e80eef83d0e9c38dc2cbd252da8.json create mode 100644 .sqlx/query-8775da07a2fa84d481563b36fb5bbc9f6698d2e3d860666b6c8d83af939305c3.json create mode 100644 .sqlx/query-8a1835877a71f3c1436c35e06c2f86e7293b8a88699a34fcd5ba65ddcf965a3c.json create mode 100644 .sqlx/query-b4cdfbecfc47b5ea7d8a523659b91327ee418b14d2fb11ea1656045a0ec4857f.json create mode 100644 .sqlx/query-b69a6f42965b3e7103fcbf46e39528466926789ff31e9ed2591bb175527ec169.json diff --git a/.gitignore b/.gitignore index fb87fc0..7473c5f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ /target .envrc -/erd -/.sqlx \ No newline at end of file +/erd \ No newline at end of file diff --git a/.sqlx/query-5ab1cfb901456f7e2e492036bd517e90171f7e80eef83d0e9c38dc2cbd252da8.json b/.sqlx/query-5ab1cfb901456f7e2e492036bd517e90171f7e80eef83d0e9c38dc2cbd252da8.json new file mode 100644 index 0000000..0791adc --- /dev/null +++ b/.sqlx/query-5ab1cfb901456f7e2e492036bd517e90171f7e80eef83d0e9c38dc2cbd252da8.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE users\n SET pw_hash = $1\n WHERE id = $2\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "5ab1cfb901456f7e2e492036bd517e90171f7e80eef83d0e9c38dc2cbd252da8" +} diff --git a/.sqlx/query-8775da07a2fa84d481563b36fb5bbc9f6698d2e3d860666b6c8d83af939305c3.json b/.sqlx/query-8775da07a2fa84d481563b36fb5bbc9f6698d2e3d860666b6c8d83af939305c3.json new file mode 100644 index 0000000..be86905 --- /dev/null +++ b/.sqlx/query-8775da07a2fa84d481563b36fb5bbc9f6698d2e3d860666b6c8d83af939305c3.json @@ -0,0 +1,40 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT id, email, pw_hash, created_at\n FROM users\n WHERE id = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "email", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "pw_hash", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "created_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "8775da07a2fa84d481563b36fb5bbc9f6698d2e3d860666b6c8d83af939305c3" +} diff --git a/.sqlx/query-8a1835877a71f3c1436c35e06c2f86e7293b8a88699a34fcd5ba65ddcf965a3c.json b/.sqlx/query-8a1835877a71f3c1436c35e06c2f86e7293b8a88699a34fcd5ba65ddcf965a3c.json new file mode 100644 index 0000000..a510310 --- /dev/null +++ b/.sqlx/query-8a1835877a71f3c1436c35e06c2f86e7293b8a88699a34fcd5ba65ddcf965a3c.json @@ -0,0 +1,41 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO users (email, pw_hash)\n VALUES ($1, $2)\n RETURNING id, email, pw_hash, created_at\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "email", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "pw_hash", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "created_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "8a1835877a71f3c1436c35e06c2f86e7293b8a88699a34fcd5ba65ddcf965a3c" +} diff --git a/.sqlx/query-b4cdfbecfc47b5ea7d8a523659b91327ee418b14d2fb11ea1656045a0ec4857f.json b/.sqlx/query-b4cdfbecfc47b5ea7d8a523659b91327ee418b14d2fb11ea1656045a0ec4857f.json new file mode 100644 index 0000000..4223fc1 --- /dev/null +++ b/.sqlx/query-b4cdfbecfc47b5ea7d8a523659b91327ee418b14d2fb11ea1656045a0ec4857f.json @@ -0,0 +1,40 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT id, email, pw_hash, created_at\n FROM users\n WHERE email = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "email", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "pw_hash", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "created_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "b4cdfbecfc47b5ea7d8a523659b91327ee418b14d2fb11ea1656045a0ec4857f" +} diff --git a/.sqlx/query-b69a6f42965b3e7103fcbf46e39528466926789ff31e9ed2591bb175527ec169.json b/.sqlx/query-b69a6f42965b3e7103fcbf46e39528466926789ff31e9ed2591bb175527ec169.json new file mode 100644 index 0000000..5dd9ec9 --- /dev/null +++ b/.sqlx/query-b69a6f42965b3e7103fcbf46e39528466926789ff31e9ed2591bb175527ec169.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "\n DELETE FROM users\n WHERE id = $1\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "b69a6f42965b3e7103fcbf46e39528466926789ff31e9ed2591bb175527ec169" +} From e07cc9e1f3ce66bcfd625420c4d982d0d0a60f8d Mon Sep 17 00:00:00 2001 From: charlieroth Date: Sat, 23 Aug 2025 11:10:33 +0200 Subject: [PATCH 4/6] skip audit and dep check in ci workflow for now --- .github/workflows/ci.yml | 116 +++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf2d532..de32536 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] env: CARGO_TERM_COLOR: always @@ -17,7 +17,7 @@ jobs: strategy: matrix: rust: [stable, beta] - + services: postgres: image: postgres:16 @@ -34,58 +34,58 @@ jobs: --health-retries 5 steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@v1 - with: - toolchain: ${{ matrix.rust }} - components: rustfmt, clippy - - - name: Cache Rust dependencies - uses: Swatinem/rust-cache@v2 - with: - shared-key: "ci-cache" - - - name: Install tools - run: | - cargo install sqlx-cli --no-default-features --features native-tls,postgres - cargo install cargo-audit - cargo install cargo-deny - - - name: Cache SQLx - uses: actions/cache@v4 - with: - path: | - ~/.cargo/bin/sqlx - .sqlx - key: sqlx-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} - - - name: Wait for PostgreSQL - run: | - until pg_isready -h localhost -p 5432 -U capsule; do - echo "Waiting for PostgreSQL..." - sleep 2 - done - - - name: Run database migrations - run: sqlx migrate run - - - name: Check formatting - run: cargo fmt --all -- --check - - - name: Run clippy - run: cargo clippy --all-targets --all-features -- -D warnings - - - name: Run tests - run: cargo test --all-features - - - name: SQLx prepare check - run: cargo sqlx prepare --check --workspace -- --all-features - - - name: Security audit - run: cargo audit - - - name: License/dependency check - run: cargo deny check + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + components: rustfmt, clippy + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + with: + shared-key: "ci-cache" + + - name: Install tools + run: | + cargo install sqlx-cli --no-default-features --features native-tls,postgres + cargo install cargo-audit + cargo install cargo-deny + + - name: Cache SQLx + uses: actions/cache@v4 + with: + path: | + ~/.cargo/bin/sqlx + .sqlx + key: sqlx-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} + + - name: Wait for PostgreSQL + run: | + until pg_isready -h localhost -p 5432 -U capsule; do + echo "Waiting for PostgreSQL..." + sleep 2 + done + + - name: Run database migrations + run: sqlx migrate run + + - name: Check formatting + run: cargo fmt --all -- --check + + - name: Run clippy + run: cargo clippy --all-targets --all-features -- -D warnings + + - name: Run tests + run: cargo test --all-features + + - name: SQLx prepare check + run: cargo sqlx prepare --check --workspace -- --all-features + + # - name: Security audit + # run: cargo audit + + # - name: License/dependency check + # run: cargo deny check From 3b2ff17dd87cdc8cddaeaa44c059e72b746ce138 Mon Sep 17 00:00:00 2001 From: charlieroth Date: Sat, 23 Aug 2025 11:30:43 +0200 Subject: [PATCH 5/6] Add JWT_SECRET environment variable for CI --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index de32536..375a567 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,7 @@ on: env: CARGO_TERM_COLOR: always DATABASE_URL: postgres://capsule:capsule_password@localhost:5432/capsule_dev + JWT_SECRET: dev-secret-change-me jobs: ci: From eecb0e6ad6ab2c83c5ba59d1aa42544ed33ad304 Mon Sep 17 00:00:00 2001 From: charlieroth Date: Sat, 23 Aug 2025 11:51:51 +0200 Subject: [PATCH 6/6] Remove matrix strategy from CI workflow, use stable Rust only Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-9bcae4d6-cf4a-45a5-918e-50773377e1a4 --- .github/workflows/ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 375a567..dec0470 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,9 +15,6 @@ jobs: ci: name: CI runs-on: ubuntu-latest - strategy: - matrix: - rust: [stable, beta] services: postgres: @@ -41,7 +38,7 @@ jobs: - name: Install Rust toolchain uses: dtolnay/rust-toolchain@v1 with: - toolchain: ${{ matrix.rust }} + toolchain: stable components: rustfmt, clippy - name: Cache Rust dependencies