mirror of
https://github.com/Drop-OSS/drop
synced 2026-08-27 14:23:05 -04:00
Merge 1b217805dd into 3e37863657
This commit is contained in:
commit
fdc3bd556c
12 changed files with 404 additions and 6 deletions
73
.github/workflows/security.yml
vendored
Normal file
73
.github/workflows/security.yml
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
name: Security
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [develop]
|
||||
pull_request:
|
||||
branches: [develop]
|
||||
schedule:
|
||||
# Weekly Monday 06:06 UTC — catches new advisories between PRs.
|
||||
- cron: "6 6 * * 1"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
gitleaks:
|
||||
name: Secret scan
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # full history for leak detection
|
||||
# Direct CLI invocation instead of gitleaks/gitleaks-action, which
|
||||
# requires a paid license key for organization repos.
|
||||
- name: Install gitleaks
|
||||
run: |
|
||||
curl -sSfL https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_linux_x64.tar.gz | tar -xz -C /usr/local/bin gitleaks
|
||||
- name: Run gitleaks
|
||||
run: gitleaks detect --redact --verbose
|
||||
|
||||
cargo-audit:
|
||||
name: Rust advisory audit
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
crate:
|
||||
- cli
|
||||
- torrential
|
||||
- desktop/src-tauri
|
||||
- libraries/droplet
|
||||
- libraries/droplet_types
|
||||
- libraries/libarchive
|
||||
- libraries/native_model
|
||||
steps:
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@7c8d7d138f5c09cef361f8214cf96882cd029cdb # dtolnay/rust-toolchain@nightly
|
||||
- uses: taiki-e/install-action@4b8992af23a2be34fd58f8d9b50d3f4939ad1891 # taiki-e/install-action@cargo-audit
|
||||
- name: Audit ${{ matrix.crate }}
|
||||
# Some vendored crates gitignore their Cargo.lock; generate one on the
|
||||
# fly so `cargo audit` has a manifest to scan.
|
||||
run: |
|
||||
if [ ! -f Cargo.lock ]; then cargo generate-lockfile; fi
|
||||
cargo audit --file Cargo.lock
|
||||
working-directory: ${{ matrix.crate }}
|
||||
|
||||
golangci:
|
||||
name: Go lint (backend)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # needed for --new-from-rev against origin/develop
|
||||
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: backend/go.work
|
||||
- uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # golangci/golangci-lint-action@v8
|
||||
with:
|
||||
working-directory: backend
|
||||
# Rollout mode: only report issues on changed lines. Remove once the
|
||||
# existing debt (2 issues in core/database.go) is cleaned up.
|
||||
args: --new-from-rev=origin/develop ./core/...
|
||||
27
.github/workflows/semgrep-scheduled.yml
vendored
Normal file
27
.github/workflows/semgrep-scheduled.yml
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
name: Semgrep (scheduled)
|
||||
|
||||
# Deep security scan on a weekly schedule — deliberately not wired into git
|
||||
# hooks or push/PR CI, where its runtime would be too disruptive.
|
||||
on:
|
||||
schedule:
|
||||
- cron: "30 5 * * 1" # weekly Monday 05:30 UTC
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
jobs:
|
||||
semgrep:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: semgrep/semgrep
|
||||
steps:
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # actions/checkout@v4
|
||||
- name: Scan
|
||||
run: semgrep scan --config p/default --sarif --output semgrep.sarif
|
||||
- name: Upload SARIF
|
||||
if: always()
|
||||
uses: github/codeql-action/upload-sarif@6d786de4d6f3531a740e445b53a42b622bbbace8 # github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: semgrep.sarif
|
||||
41
.github/workflows/server-ci.yml
vendored
41
.github/workflows/server-ci.yml
vendored
|
|
@ -29,13 +29,13 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # pnpm/action-setup@v4
|
||||
|
||||
- name: Setup Node.js environment
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: "pnpm"
|
||||
|
|
@ -52,13 +52,13 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # pnpm/action-setup@v4
|
||||
|
||||
- name: Setup Node.js environment
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: "pnpm"
|
||||
|
|
@ -69,3 +69,32 @@ jobs:
|
|||
- name: Lint
|
||||
working-directory: server
|
||||
run: pnpm run lint
|
||||
|
||||
knip:
|
||||
name: Dependency & export report (non-blocking)
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true # report-only rollout phase; remove once triaged
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # pnpm/action-setup@v4
|
||||
|
||||
- name: Setup Node.js environment
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Knip report
|
||||
run: pnpm exec knip --reporter json > knip-report.json || true
|
||||
|
||||
- name: Upload knip report
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # actions/upload-artifact@v4
|
||||
with:
|
||||
name: knip-report
|
||||
path: knip-report.json
|
||||
|
|
|
|||
5
.prettierrc.json
Normal file
5
.prettierrc.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"jsonRecursiveSort": true,
|
||||
"jsonSortOrder": "{\"/.*/\": \"lexical\"}",
|
||||
"plugins": ["prettier-plugin-sort-json"]
|
||||
}
|
||||
63
CLAUDE.md
Normal file
63
CLAUDE.md
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# Drop
|
||||
|
||||
Self-hosted game distribution platform. Polyglot monorepo:
|
||||
|
||||
- `server/` — Nuxt 3 + Vue 3 app (TypeScript, Prisma, Tailwind, buf/protobuf)
|
||||
- `backend/` — Go backend (go.work, module under `core/`)
|
||||
- Rust crates: `cli/` (downpour), `torrential/`, `desktop/src-tauri/`, `libraries/{droplet,droplet_types,libarchive,native_model}`
|
||||
- pnpm workspace: root + `server/`, `libraries/base/`, `sites/*`, `desktop/`
|
||||
|
||||
## Quality gates — what runs when
|
||||
|
||||
| Layer | When | What |
|
||||
| ------------------- | ---------- | ------------------------------------------------------------------------------------- |
|
||||
| Claude Code hooks | every edit | format-on-edit (advisory) |
|
||||
| lefthook pre-commit | commit | prettier + eslint --fix on staged files, ast-grep scan, gitleaks |
|
||||
| lefthook pre-push | push | server typecheck, clippy (changed crates), golangci-lint, knip report |
|
||||
| GitHub Actions | PR/push | typecheck/lint/clippy + gitleaks history scan + cargo-audit ×7 crates + golangci-lint |
|
||||
| GitHub Actions | weekly | semgrep deep scan → Code Scanning |
|
||||
|
||||
Hooks are early feedback; **CI is the authority**. Never disable a hook to make a
|
||||
failure go away — read the output and fix it.
|
||||
|
||||
### Escape hatches
|
||||
|
||||
```sh
|
||||
git commit --no-verify # skip pre-commit once
|
||||
git push --no-verify # skip pre-push once
|
||||
LEFTHOOK=0 git commit # same via env var
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
```sh
|
||||
pnpm exec lefthook run pre-commit --all-files # dry-run all pre-commit checks
|
||||
pnpm exec lefthook run pre-push --all-files # dry-run all pre-push checks
|
||||
pnpm exec ast-grep scan [paths] # structural lint (sgconfig.yml)
|
||||
pnpm exec ast-grep scan --json # machine-readable output
|
||||
pnpm exec knip --reporter compact # unused deps/exports/files report
|
||||
cd backend && golangci-lint run ./core/... # Go lint (.golangci.yml in backend/)
|
||||
gitleaks protect --staged # secret scan staged changes
|
||||
```
|
||||
|
||||
Native binaries NOT installable via pnpm:
|
||||
|
||||
- `gitleaks` — `brew install gitleaks`
|
||||
- `cargo-audit` — `cargo install cargo-audit`
|
||||
- `golangci-lint` — `go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest`
|
||||
(must be built with the same Go version as `backend/go.work` or typechecking fails)
|
||||
|
||||
## Rollout status (flip these when clean)
|
||||
|
||||
- **knip**: report-only (`continue-on-error` in CI, `|| true` in hook). Baseline:
|
||||
117 unused files / 31 unused exports / 4 unused deps. Remove the escape hatches
|
||||
to enforce.
|
||||
- **golangci-lint**: `--new-from-rev=origin/develop` (new issues only). Baseline:
|
||||
2 legacy issues in `core/database.go`. Remove flag when count is 0.
|
||||
- **ast-grep** rules: `severity: warning`. Promote per-rule to `error` after cleanup.
|
||||
|
||||
## Conventions
|
||||
|
||||
- Rust: nightly toolchain (matches CI); clippy with `-D warnings`.
|
||||
- Formatting: Prettier config at repo root (shared by all JS/TS/Vue packages).
|
||||
- Go: format with gofmt/goimports; keep modules inside `backend/` consistent with `go.work`.
|
||||
27
backend/.golangci.yml
Normal file
27
backend/.golangci.yml
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# golangci-lint v2 configuration.
|
||||
# Run: cd backend && golangci-lint run
|
||||
# CI: golangci-lint-action@v8 with --new-from-rev=origin/develop (rollout mode).
|
||||
version: "2"
|
||||
|
||||
run:
|
||||
timeout: 5m
|
||||
|
||||
linters:
|
||||
default: none
|
||||
enable:
|
||||
- gosec # security-focused linter
|
||||
- errcheck
|
||||
- govet
|
||||
- staticcheck
|
||||
- ineffassign
|
||||
- unused
|
||||
exclusions:
|
||||
rules:
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- gosec # test fixtures routinely trip G201/G404 etc.
|
||||
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- goimports
|
||||
31
knip.json
Normal file
31
knip.json
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"$schema": "./node_modules/knip/schema.json",
|
||||
"workspaces": {
|
||||
".": {
|
||||
"entry": ["lefthook.yml"],
|
||||
"ignoreDependencies": [
|
||||
"lefthook",
|
||||
"knip",
|
||||
"@ast-grep/cli",
|
||||
"prettier",
|
||||
"prettier-plugin-sort-json"
|
||||
]
|
||||
},
|
||||
"desktop": {},
|
||||
"libraries/base": {},
|
||||
"server": {
|
||||
"entry": [
|
||||
"app.vue",
|
||||
"error.vue",
|
||||
"nuxt.config.ts",
|
||||
"plugins/**/*.{ts,js}",
|
||||
"middleware/**/*.{ts,js}",
|
||||
"layouts/**/*.vue"
|
||||
],
|
||||
"ignore": [".nuxt/**", ".output/**", ".data/**", "build/**"],
|
||||
"project": ["**/*.{ts,js,mjs,vue}"]
|
||||
},
|
||||
"sites/docs": {},
|
||||
"sites/promo": {}
|
||||
}
|
||||
}
|
||||
74
lefthook.yml
Normal file
74
lefthook.yml
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# Lefthook git hooks for the Drop monorepo.
|
||||
# Docs: https://lefthook.dev
|
||||
# Escape hatches: git commit/push --no-verify, or LEFTHOOK=0
|
||||
#
|
||||
# Commands with a glob that matches no staged files are skipped entirely,
|
||||
# so e.g. a Markdown-only commit only runs prettier + gitleaks.
|
||||
|
||||
pre-commit:
|
||||
parallel: true
|
||||
commands:
|
||||
# Format only staged files, then re-stage them.
|
||||
prettier:
|
||||
glob: "*.{js,cjs,mjs,ts,tsx,vue,json,jsonc,css,scss,html,yaml,yml,md}"
|
||||
exclude:
|
||||
- "pnpm-lock.yaml"
|
||||
- "**/generated/**"
|
||||
run: pnpm exec prettier --write --ignore-unknown {staged_files}
|
||||
stage_fixed: true
|
||||
|
||||
# Scoped to server/ — the only package with an eslint flat config today.
|
||||
# root: makes {staged_files} relative to server/, so the config resolves.
|
||||
eslint:
|
||||
root: server/
|
||||
glob: "server/**/*.{js,cjs,mjs,ts,tsx,vue}"
|
||||
run: pnpm exec eslint --fix {staged_files}
|
||||
stage_fixed: true
|
||||
|
||||
# Structural lint rules (sgconfig.yml). Warnings only during rollout.
|
||||
ast-grep:
|
||||
glob: "*.{ts,tsx,vue,go}"
|
||||
run: pnpm exec ast-grep scan {staged_files}
|
||||
|
||||
# Secret scan on the diff about to be committed. Warns if the binary is
|
||||
# missing locally — CI enforces this authoritatively (security.yml).
|
||||
gitleaks:
|
||||
run: |
|
||||
if command -v gitleaks >/dev/null 2>&1; then
|
||||
gitleaks protect --staged --redact --verbose
|
||||
else
|
||||
echo "::warning:: gitleaks not installed locally; CI enforces. Install: brew install gitleaks"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
pre-push:
|
||||
parallel: true
|
||||
commands:
|
||||
# Whole-project typecheck; gated on any server TS/Vue change.
|
||||
typecheck:
|
||||
root: server/
|
||||
glob: "server/**/*.{ts,tsx,vue}"
|
||||
run: pnpm run typecheck
|
||||
|
||||
# Per-crate clippy over changed crates only (-D warnings, matching droplet-ci).
|
||||
# desktop/src-tauri is excluded locally (needs Tauri system libs); CI covers it.
|
||||
clippy:
|
||||
glob: "*.rs"
|
||||
run: ./scripts/clippy-changed.sh
|
||||
|
||||
# New-issues-only during rollout: backend/ has never been linted before.
|
||||
# Compares against origin/develop; remove --new-from-rev once clean.
|
||||
golangci-lint:
|
||||
root: backend/
|
||||
glob: "backend/**/*.go"
|
||||
run: |
|
||||
base="$(git rev-parse --verify origin/develop 2>/dev/null || echo "")"
|
||||
if [ -n "$base" ]; then
|
||||
golangci-lint run --new-from-rev="$base" ./...
|
||||
else
|
||||
golangci-lint run ./...
|
||||
fi
|
||||
|
||||
# Report-only by design (rollout phase): prints findings, never fails.
|
||||
knip:
|
||||
run: pnpm exec knip --reporter compact || true
|
||||
12
rules/no-console-log.yml
Normal file
12
rules/no-console-log.yml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
id: no-console-log
|
||||
message: Use the project logger (pino) instead of console.log
|
||||
severity: warning # promote to error once codebase is clean
|
||||
language: TypeScript
|
||||
rule:
|
||||
pattern: console.log($$$ARGS)
|
||||
files:
|
||||
- server/**
|
||||
ignores:
|
||||
- "**/*.d.ts"
|
||||
- "**/*.spec.ts"
|
||||
- "**/*.test.ts"
|
||||
6
rules/no-fmt-println.yml
Normal file
6
rules/no-fmt-println.yml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
id: no-fmt-println
|
||||
message: Use a structured logger instead of fmt.Println
|
||||
severity: warning
|
||||
language: Go
|
||||
rule:
|
||||
pattern: fmt.Println($$$ARGS)
|
||||
46
scripts/clippy-changed.sh
Executable file
46
scripts/clippy-changed.sh
Executable file
|
|
@ -0,0 +1,46 @@
|
|||
#!/usr/bin/env bash
|
||||
# Run `cargo clippy -D warnings` only over crates that contain changed .rs files.
|
||||
# Called by lefthook pre-push. desktop/src-tauri is intentionally excluded:
|
||||
# it needs Tauri system libraries that contributors on minimal setups won't have.
|
||||
# CI covers it in droplet-ci.yml / torrential-ci.yml.
|
||||
set -uo pipefail
|
||||
|
||||
# Resolve the repo root regardless of where the hook runs.
|
||||
repo_root="$(git rev-parse --show-toplevel)"
|
||||
cd "$repo_root" || exit 1
|
||||
|
||||
# :colon: curls are handled by set -e semantics below.
|
||||
crates=(
|
||||
cli
|
||||
torrential
|
||||
desktop/src-tauri
|
||||
libraries/droplet
|
||||
libraries/droplet_types
|
||||
libraries/libarchive
|
||||
libraries/native_model
|
||||
)
|
||||
|
||||
# Accumulate changed .rs files relative to repo root.
|
||||
changed_rs="$(git diff --name-only HEAD -- '*.rs' 2>/dev/null; git diff --cached --name-only -- '*.rs' 2>/dev/null)"
|
||||
# Falls back to previous-branch commit when @{push} isn't resolvable.
|
||||
if [ -z "$changed_rs" ]; then
|
||||
upstream="$(git rev-parse --verify '@{push}' 2>/dev/null || git rev-parse --verify 'HEAD~1' 2>/dev/null || echo "")"
|
||||
if [ -n "$upstream" ]; then
|
||||
changed_rs="$(git diff --name-only "$upstream..HEAD" -- '*.rs' 2>/dev/null)"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$changed_rs" ]; then
|
||||
echo "clippy-changed: no .rs changes; skipping"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
status=0
|
||||
for dir in "${crates[@]}"; do
|
||||
if printf '%s\n' "$changed_rs" | grep -q "^${dir}/"; then
|
||||
echo "==> cargo clippy (${dir})"
|
||||
(cd "$dir" && cargo clippy --all-targets --all-features -- -D warnings) || status=1
|
||||
fi
|
||||
done
|
||||
|
||||
exit "$status"
|
||||
5
sgconfig.yml
Normal file
5
sgconfig.yml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# ast-grep configuration. Rules live in ./rules.
|
||||
# Scan: pnpm exec ast-grep scan [paths]
|
||||
# Machine-readable: pnpm exec ast-grep scan --json
|
||||
ruleDirs:
|
||||
- rules
|
||||
Loading…
Add table
Add a link
Reference in a new issue