-
Notifications
You must be signed in to change notification settings - Fork 1
feat(infra): test + CI + static-analysis infrastructure #200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
69c3e11
64f9297
62cee07
f382aaf
35deb2a
91d8969
3104914
8eccf21
0c958f4
c750120
ff35602
d530a12
fcefefb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| # NOTE: Baseline coverage is ~29% (27.85% on develop as of 2026-Q2). | ||
| # The project/patch range [60%, 80%] below is aspirational for Phase 3 | ||
| # coverage push (post-fast-check + property tests + Vitest utility tests). | ||
| # CI's ci.yml intentionally runs with "no thresholds, no gates", so this | ||
| # file does NOT currently block PRs — it only affects Codecov status | ||
| # badges. Update the range after Phase 2 coverage lands. | ||
| codecov: | ||
| notify: | ||
| wait_for_ci: true | ||
| require_ci_to_pass: true | ||
| comment: | ||
| behavior: default | ||
| layout: reach,diff,flags,tree | ||
| show_carryforward_flags: false | ||
| coverage: | ||
| precision: 2 | ||
| range: | ||
| - 60.0 | ||
| - 80.0 | ||
| round: down | ||
| status: | ||
| changes: false | ||
| default_rules: | ||
| flag_coverage_not_uploaded_behavior: include | ||
| patch: true | ||
| project: true | ||
| slack_app: true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| root = true | ||
|
|
||
| [*] | ||
| indent_style = space | ||
| indent_size = 2 | ||
| end_of_line = lf | ||
| charset = utf-8 | ||
| trim_trailing_whitespace = true | ||
| insert_final_newline = true | ||
|
|
||
| [*.rs] | ||
| indent_size = 4 | ||
|
|
||
| [*.md] | ||
| trim_trailing_whitespace = false | ||
|
|
||
| [*.{yml,yaml}] | ||
| indent_size = 2 | ||
|
|
||
| # Makefiles require tabs (syntax) | ||
| [Makefile] | ||
| indent_style = tab | ||
|
|
||
| # Nix files: prettier handles formatting, allow 2-space indent | ||
| [*.nix] | ||
| indent_style = space | ||
| indent_size = 2 | ||
|
|
||
| # JSON files: Prettier handles formatting with 2-space indent | ||
| [*.json] | ||
| indent_style = space | ||
| indent_size = 2 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # Security-sensitive routes — require review | ||
| # Auth: webauthn, passkey, MFA, OIDC, TOTP | ||
| /server/server/api/v1/auth/ @BillyOutlast | ||
| /server/server/internal/auth/ @BillyOutlast | ||
|
|
||
| # Metadata providers — external HTTP integration, complex fallthrough | ||
| /server/server/internal/metadata/ @BillyOutlast | ||
|
|
||
| # Nitro server core (server/ dir) | ||
| /server/server/ @BillyOutlast | ||
|
|
||
| # Build, deps, CI | ||
| /server/.env.example @BillyOutlast | ||
| /.github/workflows/ @BillyOutlast | ||
| /AGENTS.md @BillyOutlast | ||
| /CLAUDE.md @BillyOutlast | ||
| /CONTRIBUTING.md @BillyOutlast |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,154 @@ | ||
| name: Rust CI | ||
| description: > | ||
| Reusable Rust CI steps for Drop monorepo workspaces. | ||
| Handles checkout, toolchain, cache, system deps, fmt, clippy/check, | ||
| tests, coverage (llvm-cov + Codecov), and advisory cargo-audit. | ||
|
|
||
| inputs: | ||
| working-directory: | ||
| required: true | ||
| description: > | ||
| Working directory for cargo commands. | ||
| Example: libraries/droplet, cli, desktop/src-tauri | ||
|
|
||
| cache-workspaces: | ||
| required: true | ||
| description: > | ||
| Workspace mapping for swatinem/rust-cache. | ||
| Example: "./libraries/droplet -> target" | ||
|
|
||
| system-dependencies: | ||
| required: false | ||
| description: > | ||
| Shell commands to install system dependencies (apt-get etc.). | ||
| Omit or leave empty when no system deps are needed. | ||
| default: "" | ||
|
|
||
| lint-command: | ||
| required: true | ||
| description: > | ||
| Cargo lint/build command. | ||
| Example: cargo clippy --all-targets --all-features -- -D warnings | ||
|
|
||
| coverage-path: | ||
| required: true | ||
| description: > | ||
| Path to coverage.lcov relative to repo root (for Codecov upload). | ||
| Example: libraries/droplet/coverage.lcov | ||
|
|
||
| test-command: | ||
| required: false | ||
| description: Cargo test command. | ||
| default: cargo test --all-features --all --verbose | ||
|
|
||
| test-continue-on-error: | ||
| required: false | ||
| description: Whether to continue on test failure. | ||
| default: "false" | ||
|
|
||
| lint-continue-on-error: | ||
| required: false | ||
| description: Whether to continue on lint failure. | ||
| default: "false" | ||
|
|
||
| components: | ||
| required: false | ||
| description: Rust toolchain components (comma-separated). | ||
| default: rustfmt, clippy | ||
|
|
||
| runs: | ||
| using: composite | ||
| steps: | ||
| # ── Setup ────────────────────────────────────────────────────── | ||
| - name: Install Rust toolchain | ||
| uses: dtolnay/rust-toolchain@4fd1da8b0805d2d2e936788875a7d65dbd677dc2 | ||
| with: | ||
| toolchain: nightly | ||
| components: ${{ inputs.components }} | ||
|
Comment on lines
+63
to
+67
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== .github/actions/rust-ci/action.yml (excerpt) =="
nl -ba .github/actions/rust-ci/action.yml | sed -n '50,85p'
echo
echo "== rust-toolchain files =="
git ls-files | rg '(^|/)(rust-toolchain(\.toml)?|Cargo\.toml)$' || true
echo
echo "== nightly references =="
rg -n "toolchain:\s*nightly|nightly" .github/actions/rust-ci action . -g '!*target*' -g '!*.lock' || trueRepository: BillyOutlast/drop Length of output: 246 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== .github/actions/rust-ci/action.yml (excerpt) =="
sed -n '50,85p' .github/actions/rust-ci/action.yml | cat -n
echo
echo "== rust-toolchain files =="
git ls-files | rg '(^|/)(rust-toolchain(\.toml)?|Cargo\.toml)$' || true
echo
echo "== nightly references =="
rg -n "toolchain:\s*nightly|nightly" .github/actions/rust-ci . -g '!*target*' -g '!*.lock' || trueRepository: BillyOutlast/drop Length of output: 3075 🏁 Script executed: #!/bin/bash
set -euo pipefail
for f in cli/rust-toolchain.toml desktop/src-tauri/rust-toolchain.toml; do
echo "== $f =="
sed -n '1,40p' "$f" | cat -n
echo
done
echo "== action.yml context =="
sed -n '1,120p' .github/actions/rust-ci/action.yml | cat -nRepository: BillyOutlast/drop Length of output: 5041 Pin the nightly toolchain everywhere. 🤖 Prompt for AI Agents |
||
|
|
||
| - name: Rust cache | ||
| # pinned to v2 | ||
| uses: swatinem/rust-cache@42dc69e1aa15d09112580998cf2ef0119e2e91ae | ||
| with: | ||
| workspaces: ${{ inputs.cache-workspaces }} | ||
|
|
||
| # ── System dependencies ──────────────────────────────────────── | ||
| - name: Install system dependencies | ||
| if: ${{ inputs.system-dependencies != '' }} | ||
| shell: bash | ||
| run: ${{ inputs.system-dependencies }} | ||
|
|
||
| # ── Format ───────────────────────────────────────────────────── | ||
| - name: Check formatting | ||
| shell: bash | ||
| working-directory: ${{ inputs.working-directory }} | ||
| run: cargo fmt --all -- --check | ||
|
|
||
| # ── Lint / Build ─────────────────────────────────────────────── | ||
| - name: Lint / Build | ||
| shell: bash | ||
| working-directory: ${{ inputs.working-directory }} | ||
| continue-on-error: ${{ inputs.lint-continue-on-error == 'true' }} | ||
| run: ${{ inputs.lint-command }} | ||
|
|
||
| # ── Test ─────────────────────────────────────────────────────── | ||
| - name: Run tests | ||
| shell: bash | ||
| working-directory: ${{ inputs.working-directory }} | ||
| continue-on-error: ${{ inputs.test-continue-on-error == 'true' }} | ||
| run: ${{ inputs.test-command }} | ||
|
|
||
| # ── Coverage ─────────────────────────────────────────────────── | ||
| - name: Install cargo-llvm-cov | ||
| uses: taiki-e/install-action@cargo-llvm-cov | ||
|
Comment on lines
+102
to
+103
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: sed -n '90,135p' .github/actions/rust-ci/action.ymlRepository: BillyOutlast/drop Length of output: 2052 🏁 Script executed: set -euo pipefail
for ref in cargo-llvm-cov cargo-audit; do
echo "== $ref =="
curl -fsSL "https://api.github.com/repos/taiki-e/install-action/git/refs/tags/$ref" \
| python3 - <<'PY'
import sys, json
data = json.load(sys.stdin)
print("ref:", data.get("ref"))
print("type:", data.get("object", {}).get("type"))
print("sha:", data.get("object", {}).get("sha"))
PY
echo
doneRepository: BillyOutlast/drop Length of output: 830 🌐 Web query:
💡 Result: The GitHub Action taiki-e/install-action uses both Citations:
SHA-pin both 🤖 Prompt for AI Agents |
||
|
|
||
| - name: Generate code coverage | ||
| # Was continue-on-error: true — switched to false so coverage | ||
| # failures surface in CI. Codecov upload below still uses | ||
| # fail_ci_if_error: false, so generation failure is visible | ||
| # but won't block the pipeline. | ||
| continue-on-error: false | ||
| shell: bash | ||
| working-directory: ${{ inputs.working-directory }} | ||
| run: | | ||
| cargo llvm-cov --all-features --workspace \ | ||
| --codecov --output-path coverage.lcov | ||
|
|
||
| - name: Upload coverage to Codecov | ||
| # pinned to v5 | ||
| uses: codecov/codecov-action@04b047e8bb82a0c002c8312c1c880fbc6a999d45 | ||
| with: | ||
| files: ${{ inputs.coverage-path }} | ||
| fail_ci_if_error: false | ||
|
|
||
| # ── Security audit ───────────────────────────────────────────── | ||
| - name: Install cargo-audit | ||
| uses: taiki-e/install-action@cargo-audit | ||
|
|
||
| - name: Audit dependencies | ||
| # cargo audit exits 1 on ANY advisory. Keep non-blocking here; the | ||
| # follow-up step fails only when a NEW (un-ignored) advisory is found | ||
| # that is not already documented in security/risk-register.yaml. | ||
| continue-on-error: true | ||
| shell: bash | ||
| working-directory: ${{ inputs.working-directory }} | ||
| run: cargo audit --json > /tmp/cargo-audit.json 2>/dev/null || true | ||
|
|
||
| - name: Check for new Rust advisories | ||
| # Run on success or failure of the audit step, but not on cancel. | ||
| # Use --min-severity high for cargo to catch DoS-class advisories | ||
| # (RUSTSEC-2026-0194/0195 in quick-xml are severity "high"); the | ||
| # script handles missing/empty/malformed JSON and missing risk | ||
| # register gracefully (exits 0 with a warning in both cases). | ||
| # Resolve the script via $GITHUB_WORKSPACE because this composite | ||
| # action is invoked with working-directory set to a sub-crate | ||
| # (cli/, desktop/src-tauri/, libraries/droplet/), where a relative | ||
| # `scripts/check-new-vulns.cjs` would not exist. | ||
| if: success() || failure() | ||
| shell: bash | ||
| working-directory: ${{ inputs.working-directory }} | ||
| run: | | ||
| node "$GITHUB_WORKSPACE/scripts/check-new-vulns.cjs" \ | ||
| --format cargo \ | ||
| --json /tmp/cargo-audit.json \ | ||
| --min-severity high | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
| version: 2 | ||
|
|
||
| registries: | ||
| # Allow Dependabot to resolve private/skipped registry hosts from lockfiles | ||
| # (e.g. buf schema registry, GitHub Packages). Public registries need no entry. | ||
| npm-pkg-github: | ||
| type: "npm-registry" | ||
| url: "https://npm.pkg.github.com" | ||
| token: "${{secrets.GITHUB_TOKEN}}" | ||
|
Comment on lines
+3
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🌐 Web query:
💡 Result: The error message "registries key must be referenced by update entry" occurs in your dependabot.yml file because you have defined a private registry at the top level of your configuration, but have not explicitly instructed Dependabot to use it within your specific update ecosystem block [1][2][3]. To resolve this, ensure your dependabot.yml is structured as follows: 1. Top-Level Definition: Define your private registry details at the root level under the registries key [1][2]. 2. Reference in Updates: Within the updates block for your specific package-ecosystem, you must add the registries key and either list the specific registry names you defined or use "*" to include all defined private registries [1][2][3]. Example configuration: version: 2 registries: my-private-registry: type: maven-repository url: https://example.com/repo username: ${{secrets.MY_USERNAME}} password: ${{secrets.MY_PASSWORD}} updates: - package-ecosystem: "maven" directory: "/" registries: - my-private-registry # Required reference schedule: interval: "monthly" Regarding the GITHUB_TOKEN and private registries: As of June 2026, Dependabot can automatically authenticate to private GitHub Packages registries (e.g., ghcr.io or *.pkg.github.com) without requiring a Personal Access Token (PAT) or explicit configuration in the registries section of your dependabot.yml [4][5]. To enable this, ensure you have granted your repository "Read" access to the specific package via the "Manage Actions access" settings on the package's page in your organization or personal account [4][5]. If this is configured, Dependabot will automatically use its internal GITHUB_TOKEN to pull the dependencies, and you can remove any existing PAT-based registry entries from your dependabot.yml [4][5]. For non-GitHub private registries (e.g., Artifactory, Nexus, Azure Artifacts), you must continue to use the top-level registries key with appropriate credentials stored as repository or organization secrets [5]. Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
# Show the whole Dependabot config with line numbers, focusing on update entries.
sed -n '1,220p' .github/dependabot.yml | cat -n
printf '\n---\n'
# Search for registry references in update entries.
rg -n 'registries:|npm-pkg-github|GITHUB_TOKEN|DEPENDABOT' .github/dependabot.ymlRepository: BillyOutlast/drop Length of output: 5799 🏁 Script executed: #!/bin/bash
set -euo pipefail
if [ -f .github/dependabot.yml ]; then
cat -n .github/dependabot.yml
else
echo "missing .github/dependabot.yml"
fiRepository: BillyOutlast/drop Length of output: 5719 Reference 🤖 Prompt for AI Agents |
||
|
|
||
| updates: | ||
| # ----- Node / pnpm workspace (root, server, sites/*, desktop) ----- | ||
| - package-ecosystem: "npm" | ||
| directory: "/" | ||
| schedule: | ||
| interval: "daily" | ||
| time: "06:00" | ||
| timezone: "America/New_York" | ||
| open-pull-requests-limit: 10 | ||
| groups: | ||
| # Patch + minor bumps bundled together to avoid PR spam | ||
| node-minor: | ||
| update-types: ["minor", "patch"] | ||
| # Pin major bumps separately for explicit review | ||
| node-major: | ||
| update-types: ["major"] | ||
| commit-message: | ||
| prefix: "deps" | ||
| prefix-development: "chore(deps-dev)" | ||
| labels: ["dependencies", "javascript"] | ||
| reviewers: ["BillyOutlast"] | ||
| # Keep lockfile in sync; pnpm-workspace.yaml declares onlyBuiltDependencies | ||
| # — keep Dependabot from re-enabling builds that the workspace intentionally skips. | ||
| rebase-strategy: "auto" | ||
|
|
||
| # ----- Nuxt 4 desktop app (separate pnpm workspace) ----- | ||
| # desktop/main/ has its own pnpm-workspace.yaml and pnpm-lock.yaml, | ||
| # so root npm entry at "/" does not cover it. Scanned independently. | ||
| - package-ecosystem: "npm" | ||
| directory: "/desktop/main" | ||
| schedule: | ||
| interval: "weekly" | ||
| day: "monday" | ||
| time: "06:00" | ||
| timezone: "America/New_York" | ||
| open-pull-requests-limit: 5 | ||
| groups: | ||
| desktop-minor: | ||
| update-types: ["minor", "patch"] | ||
| desktop-major: | ||
| update-types: ["major"] | ||
| commit-message: | ||
| prefix: "deps(desktop)" | ||
| labels: ["dependencies", "javascript"] | ||
| rebase-strategy: "auto" | ||
|
|
||
| # ----- Rust workspace: CLI ----- | ||
| - package-ecosystem: "cargo" | ||
| directory: "/cli" | ||
| schedule: | ||
| interval: "weekly" | ||
| day: "monday" | ||
| time: "06:00" | ||
| timezone: "America/New_York" | ||
| open-pull-requests-limit: 5 | ||
| groups: | ||
| rust-minor: | ||
| update-types: ["minor", "patch"] | ||
| rust-major: | ||
| update-types: ["major"] | ||
| commit-message: | ||
| prefix: "deps(cli)" | ||
| labels: ["dependencies", "rust"] | ||
|
|
||
| # ----- Rust workspace: droplet library ----- | ||
| - package-ecosystem: "cargo" | ||
| directory: "/libraries/droplet" | ||
| schedule: | ||
| interval: "weekly" | ||
| day: "monday" | ||
| time: "06:00" | ||
| timezone: "America/New_York" | ||
| open-pull-requests-limit: 5 | ||
| groups: | ||
| rust-minor: | ||
| update-types: ["minor", "patch"] | ||
| rust-major: | ||
| update-types: ["major"] | ||
| commit-message: | ||
| prefix: "deps(droplet)" | ||
| labels: ["dependencies", "rust"] | ||
|
|
||
| # ----- Rust workspace: native_model library ----- | ||
| - package-ecosystem: "cargo" | ||
| directory: "/libraries/native_model" | ||
| schedule: | ||
| interval: "weekly" | ||
| day: "monday" | ||
| time: "06:00" | ||
| timezone: "America/New_York" | ||
| open-pull-requests-limit: 5 | ||
| groups: | ||
| rust-minor: | ||
| update-types: ["minor", "patch"] | ||
| rust-major: | ||
| update-types: ["major"] | ||
| commit-message: | ||
| prefix: "deps(native_model)" | ||
| labels: ["dependencies", "rust"] | ||
|
|
||
| # ----- Rust workspace: desktop (Tauri) ----- | ||
| - package-ecosystem: "cargo" | ||
| directory: "/desktop/src-tauri" | ||
| schedule: | ||
| interval: "weekly" | ||
| day: "monday" | ||
| time: "06:00" | ||
| timezone: "America/New_York" | ||
| open-pull-requests-limit: 5 | ||
| groups: | ||
| rust-minor: | ||
| update-types: ["minor", "patch"] | ||
| rust-major: | ||
| update-types: ["major"] | ||
| commit-message: | ||
| prefix: "deps(desktop)" | ||
| labels: ["dependencies", "rust"] | ||
|
|
||
| # ----- Dockerfile (root image) ----- | ||
| - package-ecosystem: "docker" | ||
| directory: "/" | ||
| schedule: | ||
| interval: "weekly" | ||
| day: "monday" | ||
| time: "06:00" | ||
| timezone: "America/New_York" | ||
| open-pull-requests-limit: 5 | ||
| commit-message: | ||
| prefix: "deps(docker)" | ||
| labels: ["dependencies", "docker"] | ||
|
|
||
| # ----- GitHub Actions ----- | ||
| - package-ecosystem: "github-actions" | ||
| directory: "/" | ||
| schedule: | ||
| interval: "weekly" | ||
| day: "monday" | ||
| time: "06:00" | ||
| timezone: "America/New_York" | ||
| open-pull-requests-limit: 10 | ||
| groups: | ||
| actions-minor: | ||
| update-types: ["minor", "patch"] | ||
| actions-major: | ||
| update-types: ["major"] | ||
| commit-message: | ||
| prefix: "deps(ci)" | ||
| labels: ["dependencies", "github-actions"] | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Default
cargo testcommand uses deprecated--allflag and may not always match workspace intentThe default
test-commandrunscargo test --all-features --all --verbose.--allis deprecated (now equivalent to--workspace) and triggers warnings in newer Cargo versions, and some workspaces may not want--all-featuresby default. Consider changing the default tocargo test --workspace --all-features --verbose(or justcargo test --workspace), and let callers override it when they need different feature or target scopes.