diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..dec0470 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,89 @@ +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 + JWT_SECRET: dev-secret-change-me + +jobs: + ci: + name: CI + runs-on: ubuntu-latest + + 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: stable + 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/.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" +} 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 ./ 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