Skip to content

Conversation

@0xgleb
Copy link
Owner

@0xgleb 0xgleb commented Nov 14, 2025

Create comprehensive implementation plan for setting up CI with GitHub Actions using the same Nix environment as development. Plan includes separate jobs for backend, frontend, and Nix checks with proper caching strategy.

Summary by CodeRabbit

  • Chores

    • Added a CI pipeline to run formatting, linting, builds, and tests for backend and frontend with staged jobs and shared artifacts.
    • Updated developer tooling/configuration to ensure consistent local builds and include additional packaging tooling.
  • Bug Fixes

    • Improved calculator input handling: clearer absent-error semantics and updated input mutation behavior for more reliable form submissions.

Create comprehensive implementation plan for setting up CI with GitHub Actions
using the same Nix environment as development. Plan includes separate jobs for
backend, frontend, and Nix checks with proper caching strategy.
@coderabbitai
Copy link

coderabbitai bot commented Nov 14, 2025

Walkthrough

Adds a new GitHub Actions CI workflow (.github/workflows/ci.yml) that orchestrates Nix checks, a Haskell backend job, and a TypeScript/Svelte frontend job sharing an OpenAPI artifact. Updates flake.nix to enable and order hpack relative to fourmolu, and changes a frontend Svelte component's CalculateMutation type: error from Error | null to Error | undefined and mutate() to mutate(input: unknown).

Changes

Cohort / File(s) Summary
CI/CD Pipeline
\.github/workflows/ci\.yml
New GitHub Actions workflow "CI" with three jobs: nix-checks (flake validation, formatting, Nix/Cachix setup), backend (Haskell lint/build/test, Stack caching, generates & uploads OpenAPI spec), and frontend (needs backend, downloads spec, pnpm caching, generates TypeScript client, formatting/lint/type-check/tests/build).
Development Environment Configuration
flake\.nix
Adjusted Haskell hooks: replaced fourmolu.enable = true; with fourmolu = { enable = true; after = [ "hpack" ]; };, added hpack.enable = true;, and added hpack to devShell.packages under haskellPackages.
Frontend API / Types
frontend/src/lib/components/calculator/OptionParametersForm.svelte
Updated CalculateMutation.error type from `Error

Sequence Diagram

sequenceDiagram
    participant GH as GitHub Actions
    participant NC as nix-checks
    participant BE as backend (Haskell)
    participant ART as Artifact Storage
    participant FE as frontend (TS/Svelte)

    GH->>NC: start nix-checks (parallel)
    GH->>BE: start backend (parallel)
    NC->>NC: validate flake\ncheck formatting
    BE->>BE: restore Stack cache\nlint, build, test\ngenerate OpenAPI spec
    BE->>ART: upload OpenAPI spec

    rect rgba(120,180,240,0.08)
    Note over FE: waits for backend artifact
    end

    BE-->>FE: backend done (needs)
    FE->>ART: download OpenAPI spec
    FE->>FE: setup Nix/Cachix\nrestore pnpm cache\ninstall deps\ngenerate TS client\nformat/lint/typecheck/test/build
    NC-->>GH: pass
    BE-->>GH: pass
    FE-->>GH: pass
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Areas to focus on:
    • Workflow artifact upload/download steps and permissions for the OpenAPI spec.
    • Cache key correctness and restore/save behavior for Stack and pnpm.
    • Nix/Cachix setup reproducibility in CI.
    • The fourmolu ordering relative to hpack and any effect on formatting hooks.
    • The Svelte component signature change (error and mutate) and call sites that may need updating.

Poem

🐰 I hopped through CI with cheer,
uploaded specs for all to near,
hpack now leads fourmolu's song,
frontend waits and builds along,
carrots toasted — tests pass clear! 🥕

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Set up GitHub Actions CI with Nix' directly aligns with the primary change: introducing a new CI workflow at .github/workflows/ci.yml that uses Nix for environment configuration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/setup-github-actions-ci-011Ho26tCs3ZsG8jgy5eoukz

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7666578 and ee7f524.

📒 Files selected for processing (1)
  • frontend/src/lib/components/calculator/OptionParametersForm.svelte (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/lib/components/calculator/OptionParametersForm.svelte
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Nix Checks
  • GitHub Check: Backend (Haskell)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

claude and others added 16 commits November 14, 2025 22:28
Implement comprehensive CI pipeline with three parallel jobs:
- Nix checks: flake check and nixfmt
- Backend: fourmolu, hlint, stack build/test
- Frontend: prettier, eslint, svelte-check, vitest, build

Uses same Nix environment as development via cachix/install-nix-action.
Includes caching for Stack artifacts and pnpm store for faster builds.
Change workflow triggers to run on:
- Push to master branch only
- All pull requests

This prevents duplicate CI runs on feature branches.
- Generate .cabal file before running fourmolu (needed for language extensions)
- Use prettier --check instead of pnpm format --check for proper validation
Fourmolu needs the .cabal file to recognize language extensions like
OverloadedRecordDot. Generate it using 'stack build --dry-run' before
running checks in both nix-checks and backend jobs.

Changes:
- Use stack instead of hpack (stack includes hpack built-in)
- Generate .cabal in nix-checks job before 'nix flake check'
- Add stack caching to nix-checks job for faster builds
Fourmolu needs the .cabal file to read language extensions like
OverloadedRecordDot. Committing the .cabal file ensures it's always
available for pre-commit hooks and CI checks.
Instead of relying on .cabal file (which is gitignored), configure
fourmolu.yaml with ghc-opt flags for all language extensions from
package.yaml. This allows fourmolu to recognize OverloadedRecordDot
and other extensions without needing the .cabal file.

Changes:
- Add ghc-opt section to fourmolu.yaml with all default-extensions
- Revert .gitignore to keep *.cabal gitignored
- Remove unnecessary cabal generation steps from CI workflow
Based on fourmolu documentation, fourmolu requires the .cabal file to
detect language extensions like OverloadedRecordDot. The ghc-opt
configuration in fourmolu.yaml doesn't work for language extensions.

Solution:
- Enable hpack pre-commit hook to generate .cabal from package.yaml
- Configure fourmolu to run after hpack using after = ["hpack"]
- Add hpack to devenv packages
- Remove incorrect ghc-opt configuration from fourmolu.yaml

References:
- https://fourmolu.github.io/config/
- https://flake.parts/options/git-hooks-nix
The fourmolu formatting check is already run in the nix-checks job via
'nix flake check', which executes all git-hooks.nix pre-commit hooks
including fourmolu. Running it separately in the backend job is
redundant and was causing CI failures.
Removed working-directory overrides that were causing pnpm install to run
in the root directory instead of frontend/. The job already has
defaults.run.working-directory set to frontend, so the overrides were
unnecessary and causing pnpm to fail.
The 'nix develop' command enters the nix shell at the root directory where
flake.nix is located, not in the frontend/ subdirectory. This caused prettier
to check root files including .devenv/ which contains invalid JSON.

Solution: Explicitly cd into frontend directory before running each command.
Use 'nix develop ..' to reference the parent flake while keeping the
working directory in frontend/. This allows pnpm and prettier to run
in the correct directory without needing cd commands.
Remove working-directory default and explicitly cd into frontend for each
command. The nix develop command executes from the repo root, so we need
to cd into frontend to ensure pnpm and prettier run in the correct directory.
@0xgleb 0xgleb marked this pull request as ready for review November 15, 2025 03:46
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
.github/workflows/ci.yml (3)

31-32: Consider checking all Nix files for formatting consistency.

Currently, only flake.nix is checked for formatting. If there are other .nix files in the repository, they should also be checked for consistent formatting.

Apply this diff to check all Nix files:

-      - name: Check Nix formatting
-        run: nix develop --impure --command nixfmt --check flake.nix
+      - name: Check Nix formatting
+        run: nix develop --impure --command bash -c 'find . -name "*.nix" -exec nixfmt --check {} +'

63-73: Consider batching commands for better performance.

Multiple separate nix develop --impure --command invocations add overhead. Consider batching related commands in a single shell session.

Example refactor:

-      - name: Lint Haskell code
-        run: nix develop --impure --command bash -c 'find src app test -name "*.hs" -exec hlint {} +'
-
-      - name: Build Haskell project
-        run: nix develop --impure --command stack build --fast
-
-      - name: Run Haskell tests
-        run: nix develop --impure --command stack test --fast
-
-      - name: Generate OpenAPI spec
-        run: nix develop --impure --command stack exec generate-openapi
+      - name: Build and test Haskell project
+        run: |
+          nix develop --impure --command bash -c '
+            find src app test -name "*.hs" -exec hlint {} + &&
+            stack build --fast &&
+            stack test --fast &&
+            stack exec generate-openapi
+          '

119-138: Consider using working-directory or batching commands.

The repeated cd frontend && pattern in every step adds verbosity. Consider using the working-directory key or batching related commands.

Option 1: Use working-directory (cleaner but separate steps):

       - name: Install dependencies
+        working-directory: frontend
-        run: nix develop --impure --command bash -c 'cd frontend && pnpm install'
+        run: nix develop --impure --command pnpm install

Option 2: Batch related commands (faster but less granular error reporting):

-      - name: Install dependencies
-        run: nix develop --impure --command bash -c 'cd frontend && pnpm install'
-
-      - name: Generate TypeScript client
-        run: nix develop --impure --command bash -c 'cd frontend && pnpm generate-client'
-
-      - name: Check TypeScript formatting
-        run: nix develop --impure --command bash -c 'cd frontend && prettier --check .'
-
-      - name: Lint TypeScript code
-        run: nix develop --impure --command bash -c 'cd frontend && pnpm lint'
-
-      - name: Type check
-        run: nix develop --impure --command bash -c 'cd frontend && pnpm check'
-
-      - name: Run tests
-        run: nix develop --impure --command bash -c 'cd frontend && pnpm test:run'
-
-      - name: Build frontend
-        run: nix develop --impure --command bash -c 'cd frontend && pnpm build'
+      - name: Build and test frontend
+        run: |
+          nix develop --impure --command bash -c '
+            cd frontend &&
+            pnpm install &&
+            pnpm generate-client &&
+            prettier --check . &&
+            pnpm lint &&
+            pnpm check &&
+            pnpm test:run &&
+            pnpm build
+          '
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6e6973d and ccf1e63.

📒 Files selected for processing (2)
  • .github/workflows/ci.yml (1 hunks)
  • flake.nix (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-26T04:01:46.339Z
Learnt from: CR
Repo: 0xgleb/quanty PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-26T04:01:46.339Z
Learning: Applies to **/*.hs : Do not enable language extensions that are already enabled project-wide (GHC2024/package.yaml); only add module-specific pragmas when necessary

Applied to files:

  • flake.nix
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Backend (Haskell)
  • GitHub Check: Nix Checks
🔇 Additional comments (6)
flake.nix (2)

54-54: LGTM!

Adding hpack to the development shell packages is consistent with enabling it as a git hook, making it available for both automated and manual use.


27-31: git-hooks.nix supports the after field — configuration is correct. The after field is a documented feature that allows referencing other hooks by id, and the configuration correctly uses after = [ "hpack" ] to ensure fourmolu runs after hpack generates the .cabal file.

.github/workflows/ci.yml (4)

81-91: LGTM! Correct job dependency and artifact sharing.

The frontend job correctly depends on the backend job and downloads the OpenAPI spec artifact for client generation. This ensures the TypeScript client is generated from the latest backend specification.


106-117: Good caching strategy for pnpm store.

The workflow correctly retrieves the pnpm store path from within the Nix environment and caches it based on the lockfile hash. This should provide good cache hit rates and speed up dependency installation.


72-79: Both workflow assumptions are correct and properly implemented.

The verification confirms:

  • The generate-openapi executable is properly defined in package.yaml with the executable configured in app/GenerateOpenApi.hs
  • The executable writes openapi.json to the repository root (line 12 of app/GenerateOpenApi.hs), matching the workflow's artifact path expectation
  • The frontend job's dependency on this artifact is justified and will work correctly

63-64: No issues found—directory structure exists and supports hlint.

Verification confirms that src, app, and test directories all exist in the repository and contain Haskell source files. The hlint command on line 64 of .github/workflows/ci.yml will execute correctly against the project's actual structure.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
frontend/src/lib/components/calculator/OptionParametersForm.svelte (1)

222-230: Consider type-safe error handling.

The current pattern relies on isError to gate error display, but TypeScript doesn't narrow the error type based on the isError flag. The error could still be undefined even when isError is true.

For better type safety and alignment with the coding guidelines, consider refactoring to use Effect's error handling (Effect.catchAll/catchTag) with explicitly typed error variants. This would eliminate the need for separate isError and error fields and provide compile-time guarantees about error handling.

As per coding guidelines.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ccf1e63 and 7666578.

📒 Files selected for processing (1)
  • frontend/src/lib/components/calculator/OptionParametersForm.svelte (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
frontend/**/*.{ts,tsx,svelte}

📄 CodeRabbit inference engine (AGENTS.md)

frontend/**/*.{ts,tsx,svelte}: Use Effect for all async operations and error handling; prefer Effect.gen over Promise/async-await
Model errors explicitly with tagged unions and handle errors via Effect.catchAll/catchTag; do not silently swallow errors
Validate external data with @effect/schema (Schema.decode) and use refinements for business rules; no any or unknown without validation
All external API calls must include a timeout using Effect.timeout
Use Layers for dependency injection in TypeScript services
Do not use try/catch in new TypeScript code; use Effect.try and Effect error-handling instead

Files:

  • frontend/src/lib/components/calculator/OptionParametersForm.svelte
frontend/src/**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use Svelte 5 runes for reactivity ($state, $derived, $effect) and execute Effect programs with Effect.runPromise in components

Files:

  • frontend/src/lib/components/calculator/OptionParametersForm.svelte
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Nix Checks
  • GitHub Check: Backend (Haskell)
🔇 Additional comments (1)
frontend/src/lib/components/calculator/OptionParametersForm.svelte (1)

18-24: <!-- [scratchpad_start]
[task review_file_1/1 review_verification_2/2]
Verify getErrorMessage handles undefined

[observations]

  • Found blackScholes.ts at frontend/src/lib/services/blackScholes.ts
  • getErrorMessage signature shows it ALREADY accepts undefined in parameter:
    error: BlackScholesError | Error | null | undefined
  • Function returns object with title and message fields
  • Need to see full implementation to verify it handles all cases

[analysis]

  • Good news: getErrorMessage signature already includes undefined in its parameter type
  • This means the change from Error | null to Error | undefined in CalculateMutation is safe
  • The function is designed to handle undefined values

[pending]
Get full implementation of getErrorMessage to verify it handles undefined without errors

[actions]
Display full getErrorMessage implementation from blackScholes.ts

@0xgleb 0xgleb merged commit 3795ff9 into master Nov 15, 2025
4 checks passed
@0xgleb 0xgleb deleted the claude/setup-github-actions-ci-011Ho26tCs3ZsG8jgy5eoukz branch November 15, 2025 04:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants