From 202db08cf131595623f81a9c780e35c8a4fc4cee Mon Sep 17 00:00:00 2001 From: thorsten Date: Mon, 17 Aug 2026 19:44:58 +0200 Subject: [PATCH] Run CI on every pull request, and keep agent tooling out of the statistics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `main` is about to stop accepting direct pushes, so it needs something to gate on. The workflow runs what a contributor would run: lint, types, tests and build on Linux, and fmt, clippy and the host's tests on Windows, where the app ships. Writing it found two things it would have failed on immediately. `cargo fmt --check` had a diff left from the last scan change, and clippy caught a `root` parameter in `collect` that was only ever passed to itself — a leftover from before the walker stopped needing the base path. Both fixed rather than tolerated, or the badge would be red on its first run. .gitattributes marks the agent folders vendored so GitHub stops counting their configuration as this project's source, and marks the three generated files as generated so a diff of them collapses. Both also carry export-ignore, so a release archive holds the program and not the tools that helped write it. The README gains what a public repository needs: badges, the versions you have to have installed, and a contributing section that says what the tests actually care about — every derived theme at 4.5:1, app.css held to the same bar, and nothing reaching the network at runtime. --- .gitattributes | 22 ++++++++++++++++++ .github/workflows/ci.yml | 46 ++++++++++++++++++++++++++++++++++++++ README.md | 48 +++++++++++++++++++++++++++++++--------- src-tauri/src/project.rs | 11 +++++---- 4 files changed, 113 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.gitattributes b/.gitattributes index 0190ccd..ef9b2ad 100644 --- a/.gitattributes +++ b/.gitattributes @@ -9,3 +9,25 @@ *.bat text eol=crlf *.sh text eol=lf + +############################################################################### +# Agent tooling. It is configuration for the machines that help write this, not +# part of the program: kept out of the language statistics, and out of the +# archives GitHub builds for a release. +############################################################################### +.claude/** linguist-vendored export-ignore +.agents/** linguist-vendored export-ignore +.github/** export-ignore + +############################################################################### +# Generated, and large enough to bury a diff. `registry-themes.ts` is written by +# `scripts/fetch-registry-themes.mjs` and `reference/theme.css` by +# `scripts/write-reference-theme.mjs`; neither is edited by hand. +############################################################################### +src/lib/theme/registry-themes.ts linguist-generated +reference/theme.css linguist-generated +package-lock.json linguist-generated + +# Binary, so git offers no line diff and no merge it could get wrong. +*.png binary +*.ico binary diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..892f815 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,46 @@ +name: CI + +# Every pull request, and every push to main so the badge reflects what is actually on the branch. +on: + pull_request: + push: + branches: [main] + +permissions: + contents: read + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + web: + name: Web + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-node@v6 + with: + node-version: 22 + cache: npm + - run: npm ci + - run: npm run lint + - run: npm run check + - run: npm test + - run: npm run build + + host: + name: Host + # The Tauri host is built for Windows, so its tests run where it ships. + runs-on: windows-latest + steps: + - uses: actions/checkout@v5 + - uses: dtolnay/rust-toolchain@stable + with: + components: clippy, rustfmt + - uses: Swatinem/rust-cache@v2 + with: + workspaces: src-tauri + - run: cargo fmt --manifest-path src-tauri/Cargo.toml -- --check + - run: cargo clippy --manifest-path src-tauri/Cargo.toml -- -D warnings + - run: cargo test --manifest-path src-tauri/Cargo.toml diff --git a/README.md b/README.md index 4668d7a..c4095e3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ + + # OpenThemeGenerator +[![CI](https://github.com/thorstenalpers/OpenThemeGenerator/actions/workflows/ci.yml/badge.svg)](https://github.com/thorstenalpers/OpenThemeGenerator/actions/workflows/ci.yml) +[![Licence: MIT](https://img.shields.io/badge/licence-MIT-blue.svg)](LICENSE) + A desktop theme generator for shadcn — and for anything else that reads CSS custom properties. Pick a seed colour, and the generator derives a complete light and dark palette: the shadcn token @@ -140,6 +145,11 @@ program is the hosted assistant, and the local binary is the default. ## Development +You need **Node 22**, a **Rust toolchain**, and on Windows the **WebView2 runtime** — which ships +with Windows 11 and every recent 10. Nothing else: the icons, the reference stylesheet and the +imported theme list are all produced by scripts in this repository rather than fetched at build +time. + ```bash npm install npm run dev @@ -155,16 +165,34 @@ starts drifting from the first. For the real thing: npm run start ``` -| Command | What it does | -| ------------------------------------------------- | -------------------------------------------------------- | -| `npm run check` | `svelte-check` against the app tsconfig | -| `npm run lint` | prettier and eslint | -| `npm test` | vitest — colour maths, the generator, and every exporter | -| `npm run app:build` | the NSIS installer | -| `npm run icons` | regenerates the icon set from `scripts/make-icons.mjs` | -| `npm run reference:theme` | rewrites `reference/theme.css` from a built-in preset | -| `cargo test --manifest-path src-tauri/Cargo.toml` | the host's own tests | +| Command | What it does | +| --------------------------------------------------- | -------------------------------------------------------- | +| `npm run check` | `svelte-check` against the app tsconfig | +| `npm run lint` | prettier and eslint | +| `npm run format` | writes the formatting rather than checking it | +| `npm test` | vitest — colour maths, the generator, and every exporter | +| `npm run build` | the static front end | +| `npm run app:build` | the NSIS installer | +| `npm run icons` | regenerates the icon set from `scripts/make-icons.mjs` | +| `npm run reference:theme` | rewrites `reference/theme.css` from a built-in preset | +| `cargo test --manifest-path src-tauri/Cargo.toml` | the host's own tests | +| `cargo clippy --manifest-path src-tauri/Cargo.toml` | the lints CI fails on | + +## Contributing + +`main` is protected: changes arrive as a pull request, and CI has to be green before it can merge. +The workflow runs the table above — lint, types, tests and build on Linux, and `cargo fmt`, +`clippy` and the host's tests on Windows, where the app actually ships. + +Two things the tests care about more than style: + +- **Every theme this app derives clears 4.5:1** on every surface/label pair, in both modes. So does + `app.css`, the window's own chrome, which has its own test because the generator cannot keep it + honest. Imported themes are exempt — they are other people's designs, carried as published, and + the gallery reports what they score instead of retinting them. +- **Nothing reaches the network at runtime.** The imported theme list is generated into the + repository by `scripts/fetch-registry-themes.mjs` and read from disk. ## Licence -MIT. +[MIT](LICENSE). diff --git a/src-tauri/src/project.rs b/src-tauri/src/project.rs index 870285c..de3188c 100644 --- a/src-tauri/src/project.rs +++ b/src-tauri/src/project.rs @@ -106,7 +106,7 @@ fn rank(name: &str) -> u8 { } } -fn collect(root: &Path, current: &Path, depth: usize, found: &mut Vec) { +fn collect(current: &Path, depth: usize, found: &mut Vec) { if depth > MAX_DEPTH { return; } @@ -125,7 +125,7 @@ fn collect(root: &Path, current: &Path, depth: usize, found: &mut Vec) // hold a git worktree of the whole project, and scanning that reads the same tokens a // second time under a different path — half the prompt budget spent on a copy. if !name.starts_with('.') && !SKIP.contains(&name) { - collect(root, &path, depth + 1, found); + collect(&path, depth + 1, found); } } else if is_wanted(&path, name) { found.push(path); @@ -207,7 +207,7 @@ pub fn scan(root: &str) -> AppResult { } let mut found = Vec::new(); - collect(&base, &base, 0, &mut found); + collect(&base, 0, &mut found); found.sort_by_key(|path| { let name = path .file_name() @@ -356,7 +356,10 @@ mod tests { write("src/app.css", "@import 'tailwindcss';"); write("components.json", "{}"); - write(".claude/worktrees/copy/src/app.css", "@import 'tailwindcss';"); + write( + ".claude/worktrees/copy/src/app.css", + "@import 'tailwindcss';", + ); write(".claude/skills/vendored/package.json", "{}"); write("node_modules/some-package/package.json", "{}");