Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 0 additions & 61 deletions .github/dependabot.yml

This file was deleted.

31 changes: 23 additions & 8 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -1,24 +1,39 @@
name: CodeQL

# Run CodeQL only against `main` + on a weekly schedule. PR triggers
# are intentionally OFF — we're on the free GitHub Actions tier and a
# 2-job matrix (JS/TS + Rust) on every PR push was eating the bulk of
# our Action minutes for marginal additional coverage. Trade-off:
# CodeQL findings now surface post-merge rather than pre-merge.
# Run CodeQL against `main`, on a weekly schedule, and on PRs that touch
# dependency manifests. Broad PR scanning stays OFF — we're on the free
# GitHub Actions tier and a 2-job matrix (JS/TS + Rust) on *every* PR push
# was eating the bulk of our Action minutes for marginal coverage. The
# `pull_request` + `paths` filter below is the deliberate exception: it
# scans Renovate's single dependency-update PR (and any hand-made dep
# change) pre-merge, since a lockfile/manifest bump is exactly where a
# newly-pulled transitive vuln would land. Feature PRs that don't touch
# these paths still skip CodeQL and surface findings post-merge.
#
# - main: every merged push runs CodeQL so the latest tip is always
# scanned and the weekly cron has a fresh baseline.
# - schedule: weekly (Sun 18:24 UTC). CodeQL's upstream rule database
# updates regularly; a re-scan catches new findings even when the
# code hasn't changed.
# - pull_request (paths-filtered): dependency-manifest changes only —
# the paths mirror what Renovate edits across all four ecosystems.
#
# To re-enable PR scanning later, add a `pull_request:` block with the
# `paths:` filter that was here previously (see git blame on this
# file). Or drop the filter for full coverage at higher cost.
# To scan ALL PRs (full pre-merge coverage at higher cost), drop the
# `paths:` filter below.
on:
push:
branches:
- main
pull_request:
paths:
- 'package.json'
- 'pnpm-lock.yaml'
- 'src-tauri/Cargo.toml'
- 'src-tauri/Cargo.lock'
- 'Dockerfile'
- 'docker-compose*.yml'
- 'deploy/**/compose.yaml'
- '.github/workflows/**'
schedule:
- cron: '24 18 * * 0'

Expand Down
88 changes: 88 additions & 0 deletions .github/workflows/renovate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#
# Renovate — single-PR dependency updates.
#
# Replaces Dependabot (see the deleted .github/dependabot.yml). Renovate
# regenerates pnpm-lock.yaml *natively* — the whole reason for the switch —
# and, per renovate.json, bundles every non-major update across all four
# ecosystems (npm, cargo, docker, github-actions) into ONE PR on a stable
# branch. Major upgrades are parked on the Dependency Dashboard issue for
# manual, one-at-a-time review.
#
# Two ways to run:
# 1. Scheduled — Mondays; renovate.json's `schedule` is the effective
# time gate, so the job no-ops fast outside the window.
# 2. Manual — Actions ▸ Renovate ▸ "Run workflow". Defaults to a
# DRY RUN: it logs the exact PR it *would* open and
# changes nothing. Flip `dryRun` to `null` to go live.
# Manual runs ignore the Monday time-gate so you can
# test any day.
#
# Token (Settings ▸ Secrets and variables ▸ Actions ▸ RENOVATE_TOKEN):
# Optional for a DRY RUN — the built-in GITHUB_TOKEN is enough to preview.
# REQUIRED for live runs whose PR must trigger CI: PRs opened with the
# default GITHUB_TOKEN do NOT trigger `on: pull_request` workflows (GitHub's
# recursion guard), so the ci.yml checks would never run and the branch-
# protection gate would stay pending. Use a fine-grained PAT or a GitHub
# App installation token (contents:write + pull-requests:write + workflows)
# so the update PR is authored by a distinct identity and CI fires normally.
#
# Alternative to this workflow: install the hosted Mend Renovate GitHub App
# on the repo instead. It reads the same renovate.json, its PRs trigger CI
# automatically (no PAT to manage), and its onboarding PR doubles as the
# dry-run preview. If you go that route, delete this workflow so the two
# don't both run. Pick exactly one activation path.
#
name: Renovate

on:
workflow_dispatch:
inputs:
dryRun:
description: 'full = preview only, open no PR (default) · null = go live and open the PR'
type: choice
options:
- 'full'
- 'null'
default: 'full'
logLevel:
description: 'Renovate log level'
type: choice
options:
- 'info'
- 'debug'
default: 'info'
schedule:
# Sunday 21:00 UTC ≈ Monday 07:00–08:00 Australia/Melbourne, inside
# renovate.json's "before 9am on monday" window (robust across DST).
- cron: '0 21 * * 0'

# Never let two Renovate runs race on the same branch/lockfile.
concurrency:
group: renovate
cancel-in-progress: false

permissions:
contents: write
pull-requests: write

jobs:
renovate:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

- name: Run Renovate
uses: renovatebot/github-action@b50d2ba2bd928235abdcc14d06dfafc217f1c565 # v46.1.18
with:
configurationFile: renovate.json
token: ${{ secrets.RENOVATE_TOKEN || github.token }}
env:
RENOVATE_REPOSITORIES: ${{ github.repository }}
LOG_LEVEL: ${{ inputs.logLevel || 'info' }}
# Scheduled runs go live ('null' = dry-run disabled). Manual runs
# honour the dropdown, which defaults to 'full' (preview only).
RENOVATE_DRY_RUN: ${{ github.event_name == 'schedule' && 'null' || inputs.dryRun }}
# Manual runs bypass renovate.json's Monday time-gate so testing
# works any day; scheduled runs leave it in force.
RENOVATE_FORCE: ${{ github.event_name == 'workflow_dispatch' && '{"schedule":null}' || '' }}
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ The test suite is intentionally small and focused (`pnpm test`). Container healt

Separate Python companion script in `scripts/ios-app-import/` (stdlib-only, Python 3.9+): `python3 scripts/ios-app-import/export_ios_apps.py --mode backup|device`. It is *not* wired into the Node app — it produces a `.txt`/`.csv` that the user feeds back into the web onboarding flow.

## Dependency updates (Renovate, not Dependabot)

Dependency bumps are driven by **Renovate**, not Dependabot — `.github/dependabot.yml` was removed because its pnpm support left `pnpm-lock.yaml` stale (needing a manual regen) and it fanned each ecosystem out into separate, mutually-conflicting PRs. Renovate regenerates the lockfile natively and, per `renovate.json`, bundles every **non-major** update across all four ecosystems (npm, cargo, docker, github-actions) into a **single** PR on a stable branch. **Major** upgrades are held on the Dependency Dashboard issue (`dependencyDashboardApproval`) for one-at-a-time review — tick one there to let Renovate raise its PR. Do NOT reintroduce a `dependabot.yml`; that would duplicate Renovate's PRs.

Activation is one of two mutually-exclusive paths (pick one): the self-hosted `.github/workflows/renovate.yml` (weekly cron + a `workflow_dispatch` **dry-run** button that previews the PR without opening it — needs a `RENOVATE_TOKEN` secret for live-run PRs to trigger CI, since GITHUB_TOKEN-authored PRs don't), **or** the hosted Mend Renovate GitHub App (its PRs trigger CI automatically; delete the workflow if you install the app). Both read the same `renovate.json`. See the header comment in the workflow for the token rationale.

## Architecture

This is a Next.js 16 App Router app (TypeScript, React 19) backed by a single local SQLite file. All scraping, parsing, diffing, and AI calls happen server-side inside API routes that import helpers from `lib/`.
Expand Down
45 changes: 45 additions & 0 deletions renovate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":dependencyDashboard",
":semanticCommits",
"helpers:pinGitHubActionDigests"
],
"timezone": "Australia/Melbourne",
"schedule": ["before 9am on monday"],
"labels": ["dependencies"],
"postUpdateOptions": ["pnpmDedupe"],
"lockFileMaintenance": {
"enabled": true,
"schedule": ["before 9am on monday"]
},
"vulnerabilityAlerts": {
"labels": ["dependencies", "security"]
},
"packageRules": [
{
"description": "Bundle every non-major update (all four ecosystems: npm, cargo, docker, github-actions), plus digest bumps and weekly lockfile maintenance, into ONE reviewable PR on a stable branch. pnpm-lock.yaml is regenerated natively by Renovate as part of the same PR.",
"matchUpdateTypes": [
"minor",
"patch",
"pin",
"digest",
"lockFileMaintenance"
],
"groupName": "all non-major dependencies",
"groupSlug": "all-non-major"
},
{
"description": "Hold major upgrades (e.g. Next 16 to 17, Tauri 2 to 3, caddy:2 to :3) back as individually reviewable entries on the Dependency Dashboard rather than auto-opening PRs. Tick one there to let Renovate raise its own PR when you're ready to take the breaking change.",
"matchUpdateTypes": ["major"],
"dependencyDashboardApproval": true
},
{
"description": "Never try to bump privacytracker's own published image referenced in the deploy compose files.",
"matchDatasources": ["docker"],
"matchPackageNames": ["ghcr.io/privacykey/privacytracker"],
"enabled": false
}
]
}
Loading