diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index e0af74b..8182304 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -1,6 +1,20 @@ name: "Test RDE template" on: workflow_dispatch: # allows manual triggering + push: + paths: + - 'templates/rde/flake.lock' + - 'templates/rde/**/*.nix' + - 'templates/rde/**/*.sh' + - 'templates/rde/**/*.lua' + - '.github/workflows/check.yml' + pull_request: + paths: + - 'templates/rde/flake.lock' + - 'templates/rde/**/*.nix' + - 'templates/rde/**/*.sh' + - 'templates/rde/**/*.lua' + - '.github/workflows/check.yml' concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true diff --git a/.github/workflows/check_ed.yml b/.github/workflows/check_ed.yml index 497d060..350c013 100644 --- a/.github/workflows/check_ed.yml +++ b/.github/workflows/check_ed.yml @@ -1,16 +1,9 @@ -name: "Test ed template" +name: "Test ed template MacOS" on: - workflow_dispatch: + workflow_dispatch: # allows manual triggering push: - paths: - - 'templates/ed/**' - - 'tests/ed/**' - - '.github/workflows/check_ed.yml' - pull_request: - paths: - - 'templates/ed/**' - - 'tests/ed/**' - - '.github/workflows/check_ed.yml' + branches: + - 'update_rde' concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true @@ -19,105 +12,15 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest] + os: [macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v6 - uses: cachix/install-nix-action@v31 with: - github_access_token: ${{ github.token }} + github_access_token: ${{ secrets.GITHUB_TOKEN }} - uses: cachix/cachix-action@v17 with: name: rde authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' extraPullNames: rstats-on-nix, nix-community - - - name: Build template - run: nix build ./templates/ed - - - name: Test R script - env: - NO_NUSHELL: '1' - run: | - cd templates/ed - nix develop --command bash -c "Rscript ${{ github.workspace }}/tests/ed/test.R" - - - name: Test Python script (disabled by default) - env: - NO_NUSHELL: '1' - run: | - cd templates/ed - nix develop --command bash -c "python3 ${{ github.workspace }}/tests/ed/test.py" || echo "SKIP: Python packages not enabled" - - test-with-python: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: cachix/install-nix-action@v31 - with: - github_access_token: ${{ github.token }} - - uses: cachix/cachix-action@v17 - with: - name: rde - authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - extraPullNames: rstats-on-nix, nix-community - - - name: Setup test directory with Python enabled - run: | - TEST_DIR=$(mktemp -d) - cp -r templates/ed/. "$TEST_DIR/" - cd "$TEST_DIR" - sed -i 's/python[[:space:]]*=[[:space:]]*false;/python = true;/' flake.nix - grep -q "python[[:space:]]*=[[:space:]]*true;" flake.nix || { - echo "Error: Failed to enable Python in flake.nix" - exit 1 - } - echo "TEST_DIR=$TEST_DIR" >> $GITHUB_ENV - - - name: Build with Python enabled - run: | - cd "$TEST_DIR" - nix build - - - name: Test Python packages - env: - NO_NUSHELL: '1' - run: | - cd "$TEST_DIR" - nix develop --command bash -c "python3 ${{ github.workspace }}/tests/ed/test.py" - - test-with-julia: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: cachix/install-nix-action@v31 - with: - github_access_token: ${{ github.token }} - - uses: cachix/cachix-action@v17 - with: - name: rde - authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - extraPullNames: rstats-on-nix, nix-community - - - name: Setup test directory with Julia enabled - run: | - TEST_DIR=$(mktemp -d) - cp -r templates/ed/. "$TEST_DIR/" - cd "$TEST_DIR" - sed -i 's/julia[[:space:]]*=[[:space:]]*false;/julia = true;/' flake.nix - grep -q "julia[[:space:]]*=[[:space:]]*true;" flake.nix || { - echo "Error: Failed to enable Julia in flake.nix" - exit 1 - } - echo "TEST_DIR=$TEST_DIR" >> $GITHUB_ENV - - - name: Build with Julia enabled - run: | - cd "$TEST_DIR" - nix build - - - name: Test Julia packages - env: - NO_NUSHELL: '1' - run: | - cd "$TEST_DIR" - # nix develop --command bash -c "julia ${{ github.workspace }}/tests/ed/test.jl" + - run: nix build ./templates/ed diff --git a/.github/workflows/check_macos.yml b/.github/workflows/check_macos.yml index ad9d3ed..3b8e6f2 100644 --- a/.github/workflows/check_macos.yml +++ b/.github/workflows/check_macos.yml @@ -3,7 +3,7 @@ on: workflow_dispatch: # allows manual triggering push: branches: - - 'update_ed' + - 'update_rde' concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true @@ -23,4 +23,5 @@ jobs: name: rde authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' extraPullNames: rstats-on-nix, nix-community + - run: nix build ./templates/rde - run: nix build ./templates/ed diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml index 4b3cb00..a42ac30 100644 --- a/.github/workflows/update.yml +++ b/.github/workflows/update.yml @@ -22,11 +22,12 @@ jobs: name: rde authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' extraPullNames: rstats-on-nix, nix-community + - run: cd templates/rde/ && nix develop -c p-updateDeps - name: Update ed flake.lock uses: DeterminateSystems/update-flake-lock@v28 with: token: ${{ secrets.GH_TOKEN_FOR_UPDATES }} - branch: update_ed + branch: update_rde path-to-flake-dir: "templates/ed" pr-title: "Update RDE flake.lock" # Title of PR to be created pr-labels: | # Labels to be set on the PR diff --git a/templates/ed/.gitignore b/templates/ed/.gitignore index ffc0e1a..52e8a70 100644 --- a/templates/ed/.gitignore +++ b/templates/ed/.gitignore @@ -1,2 +1 @@ .nvimcom -.commandcode/ diff --git a/templates/ed/README.md b/templates/ed/README.md deleted file mode 100644 index 272d827..0000000 --- a/templates/ed/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# Project Editor - -A per-project Neovim wrapper built with [nix-wrapper-modules](https://birdeehub.github.io/nix-wrapper-modules/) and [nvimConfig](https://github.com/dwinkler1/nvimConfig). - -## Setup - -The flake provides two entry points: - -| Command | What you get | -|---|---| -| `direnv allow` / `nix develop` | **(Recommended)** A shell with `nv` and all enabled language toolchains (R, Python, Julia, Quarto) on `PATH`. | -| `nix run` | Launches only the `nv` editor. Language toolchains (R, Python, etc.) are **not** on `PATH`. | - -### With direnv (recommended) - -```bash -direnv allow # enter the devShell automatically -nv # launch the pre-configured Neovim -R # R REPL is available (if enabled in flake.nix) -``` - -### Without direnv - -```bash -nix develop # enter the devShell manually -nv # launch Neovim -R # R REPL is available (if enabled in flake.nix) -``` - -> **Why `nix run` only gives the editor:** The flake's `packages.default` is the wrapped Neovim binary. Language toolchains (R, radian, quarto, etc.) live in `devShells.default`. Use `nix develop` (or `direnv allow`) to get the full environment. - -## Configuration - -The `flake.nix` is the single source of truth. Key knobs: - -| Option | What it controls | -|---|---| -| `cats` | Toggle language support (nix, r, python, julia, etc.) | -| `settings.lang_packages.` | Language-specific packages installed in the wrapper | -| `settings.colorscheme` | Neovim colorscheme | -| `settings.background` | `"dark"` or `"light"` | -| `settings.wrapRc` | When `true`, init.lua is embedded (rebuild to change); when `false`, init.lua is external (reload without rebuild) | -| `binName` | The wrapper binary name (`nv` by default) | -| `env` | Environment variables set in the wrapper | -| `extraPackages` | Extra system packages available in the wrapper's PATH | -| `specs.extraLua` | Inject lazy.nvim plugin specs | - -### Adding packages without editing flake.nix - -Create any of these optional files in the project root to add dependencies without modifying `flake.nix`: - -- **`python-packages.nix`** — receives `python3Packages`, return a list of packages: - ```nix - p: with p; [ numpy scipy ] - ``` - -- **`r-packages.nix`** — receives `rpkgs` (includes `rPackages`), return a list: - ```nix - p: with p.rPackages; [ ggplot2 data.table ] - ``` - -- **`julia-packages.nix`** — no arguments, return a list of package name strings: - ```nix - [ "DataFrames" "Plots" ] - ``` - -## Formatting - -```bash -nix fmt -``` - -Uses `nixfmt-rfc-style` pinned via the flake's `formatter` output. diff --git a/templates/ed/flake.lock b/templates/ed/flake.lock index feb65be..8da8614 100644 --- a/templates/ed/flake.lock +++ b/templates/ed/flake.lock @@ -22,15 +22,15 @@ }, "nixpkgs": { "locked": { - "lastModified": 1779560665, - "narHash": "sha256-tpyBcxPpcQb8ukyNF7DoCwfSY3VPsxHoYwj00Cayv5o=", - "owner": "nixos", + "lastModified": 1776877367, + "narHash": "sha256-EHq1/OX139R1RvBzOJ0aMRT3xnWyqtHBRUBuO1gFzjI=", + "owner": "NixOS", "repo": "nixpkgs", - "rev": "64c08a7ca051951c8eae34e3e3cb1e202fe36786", + "rev": "0726a0ecb6d4e08f6adced58726b95db924cef57", "type": "github" }, "original": { - "owner": "nixos", + "owner": "NixOS", "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" @@ -45,18 +45,20 @@ "nixpkgs" ], "plugins-cmp-pandoc-references": "plugins-cmp-pandoc-references", - "r-nvim-nix": "r-nvim-nix", + "plugins-r": [ + "plugins-r" + ], "rixpkgs": [ "rixpkgs" ], "wrappers": "wrappers" }, "locked": { - "lastModified": 1779544872, - "narHash": "sha256-AnuNQ9nbXrDYmL97TOejAds/FQOgD6FEOHABp/8dmsU=", + "lastModified": 1776413627, + "narHash": "sha256-pU3u7T4MqpjUWxflNvd4i47FURp756L7nMRL8MV+QEw=", "owner": "dwinkler1", "repo": "nvimConfig", - "rev": "aec9110e709e1aa50d78f06293c4bde14974b23f", + "rev": "2617face3b1be40534d2cae7f0a374a062b3710d", "type": "github" }, "original": { @@ -81,26 +83,20 @@ "type": "github" } }, - "r-nvim-nix": { - "inputs": { - "nixpkgs": [ - "nvimConfig", - "rixpkgs" - ], - "rnvimsrc": "rnvimsrc" - }, + "plugins-r": { + "flake": false, "locked": { - "lastModified": 1779438909, - "narHash": "sha256-1lvv0bdvSVyeCIgeZ7Ws7ffbDFurA5LJscS9dRLHzC8=", - "owner": "dwinkler1", - "repo": "r_nvim_nix", - "rev": "ec17e22ab362a0ddfd6c2e9c5e95d43897a143be", + "lastModified": 1769881528, + "narHash": "sha256-oQSHHu6filJkAyH94yEvyTVuxA+5MU2dMOEAnsIjJKQ=", + "owner": "R-nvim", + "repo": "R.nvim", + "rev": "958b472d763cb258927c7ef69af4fd9945cc5469", "type": "github" }, "original": { - "owner": "dwinkler1", - "ref": "v0.99.4", - "repo": "r_nvim_nix", + "owner": "R-nvim", + "ref": "v0.99.3", + "repo": "R.nvim", "type": "github" } }, @@ -115,25 +111,8 @@ }, "original": { "owner": "dwinkler1", + "ref": "nixpkgs", "repo": "rixpkgs", - "rev": "af2dd3f7b4b172077747c0869d4e30702fb71b0e", - "type": "github" - } - }, - "rnvimsrc": { - "flake": false, - "locked": { - "lastModified": 1776905071, - "narHash": "sha256-dXox6qEs1VDE7vPNDoN8bY4g06uj1IEs6uki72w8lpA=", - "owner": "R-nvim", - "repo": "R.nvim", - "rev": "582f2af11290ac067e49018db38e12a511325556", - "type": "github" - }, - "original": { - "owner": "R-nvim", - "ref": "v0.99.4", - "repo": "R.nvim", "type": "github" } }, @@ -142,6 +121,7 @@ "fran": "fran", "nixpkgs": "nixpkgs", "nvimConfig": "nvimConfig", + "plugins-r": "plugins-r", "rixpkgs": "rixpkgs" } }, @@ -153,11 +133,11 @@ ] }, "locked": { - "lastModified": 1779297405, - "narHash": "sha256-VFoBwH7ZjVxCnvZTb5ODRXt70sLtWMxstive0N+RS50=", + "lastModified": 1776375800, + "narHash": "sha256-/SSAR77Brr9fbapsh1cb2K47JXCbvwS1GjM4yyDxle8=", "owner": "BirdeeHub", "repo": "nix-wrapper-modules", - "rev": "e7ed7a1205945befdf2e0d73ba7df91d935e5af1", + "rev": "f11469ca69068bac13d9e163b2bd268cc06dff57", "type": "github" }, "original": { diff --git a/templates/ed/flake.nix b/templates/ed/flake.nix index 07603fa..08a618d 100644 --- a/templates/ed/flake.nix +++ b/templates/ed/flake.nix @@ -6,36 +6,8 @@ nixpkgs, nvimConfig, ... - }: let - systems = ["aarch64-darwin" "x86_64-linux" "aarch64-linux"]; - forAllSystems = nixpkgs.lib.genAttrs systems; - - mkPkgs = system: - import nixpkgs { - inherit system; - config.allowUnfree = true; - overlays = [nvimConfig.overlays.dependencies]; - }; - - projectModule = pkgs: let - baseConfig = nvimConfig.wrapperConfigs.default {inherit pkgs;}; - extraRPackages = - if builtins.pathExists ./r-packages.nix - then import ./r-packages.nix pkgs.rpkgs - else []; - - extraPythonPackages = - if builtins.pathExists ./python-packages.nix - then import ./python-packages.nix pkgs.python3Packages - else []; - - extraJuliaPackages = - if builtins.pathExists ./julia-packages.nix - then import ./julia-packages.nix - else []; - - projectName = builtins.baseNameOf (toString self.outPath); - in { + } @ inputs: let + projectSettings = {pkgs}: { cats = { clickhouse = false; gitPlugins = false; @@ -45,117 +17,123 @@ nix = true; optional = false; python = false; - r = true; + r = false; }; - settings = { + settings = let + # With `replace` packages are replaced otherwise they are merged with base packages + replace = pkgs.lib.mkForce; + in { lang_packages = { - python = with pkgs.python3Packages; - [ - requests - ] - ++ extraPythonPackages; - - r = - (with pkgs.rpkgs.rPackages; [ - arrow - data_table - fixest - pkgs.nvimcom + python = replace ( + (with pkgs.python3Packages; [ + duckdb + polars ]) - ++ extraRPackages; + ++ ( + if builtins.pathExists ./python-packages.nix + # p: with p; [ ... ] + then import ./python-packages.nix pkgs.python3Packages + else [] + ) + ); - julia = - ["StatsBase"] - ++ extraJuliaPackages; + r = replace ( + (with pkgs.rpkgs.rPackages; [ + fixest + # pkgs.extraRPackages.musicMetadata + ]) + ++ ( + if builtins.pathExists ./r-packages.nix + # p: with p.rPackages; [ ... ] + then import ./r-packages.nix pkgs.rpkgs + else [] + ) + ); + + julia = replace ([ + "StatsBase" + ] + ++ ( + if builtins.pathExists ./julia-packages.nix + # [ ... ] + then import ./julia-packages.nix + else [] + )); }; - - colorscheme = "tokyonight"; #"kanagawa"; + colorscheme = "kanagawa"; background = "dark"; wrapRc = true; }; + binName = "vv"; - binName = "nv"; - - env.IS_PROJECT_EDITOR = "1"; - - catPkgs = { - always = [ - pkgs.git - pkgs.pre-commit - pkgs.cowsay - ]; - - nix = [ - pkgs.nil - pkgs.nixfmt - ]; + env = { + IS_PROJECT_EDITOR = "1"; + R_LIBS_USER = "./.nvimcom"; }; - specs.extraLua = { + extraPackages = with pkgs; [ + cowsay + ]; + + specs.extraLua = let + name = builtins.baseNameOf (builtins.toString ./.); + in { + data = pkgs.vimPlugins.mini-notify; before = ["INIT_MAIN"]; - data = pkgs.writeText "project-startup.lua" '' + config = '' require("mini.notify").setup() vim.notify = MiniNotify.make_notify() - vim.notify("Welcome to ${projectName}!") + vim.notify("Welcome to ${name}!") ''; }; }; - in { - formatter = forAllSystems (system: (mkPkgs system).nixfmt-tree); + systems = nixpkgs.lib.systems.flakeExposed; + forAllSystems = nixpkgs.lib.genAttrs systems; + overlays = [inputs.nvimConfig.overlays.dependencies]; + in { packages = forAllSystems (system: let - pkgs = mkPkgs system; + pkgs = import nixpkgs {inherit system overlays;}; + baseNvim = nvimConfig.packages.${system}.default; + + nvim = (baseNvim.eval (projectSettings {inherit pkgs;})).config.wrapper; + default = nvim; in { - default = nvimConfig.lib.mkWrapper { - inherit pkgs; - modules = [(projectModule pkgs)]; - }; + default = nvim; }); devShells = forAllSystems (system: let - pkgs = mkPkgs system; - evalResult = nvimConfig.lib.eval { - inherit pkgs; - modules = [(projectModule pkgs)]; - }; - nv = evalResult.config.wrap {inherit pkgs;}; + pkgs = import nixpkgs {inherit system overlays;}; + nv = self.packages.${system}.default; in { default = pkgs.mkShell { - packages = - [ - nv - pkgs.nushell - ] - ++ nvimConfig.lib.devShellPackages evalResult.config; - shellHook = - nvimConfig.lib.shellHook evalResult.config - + '' - if [ -z "''${NO_NUSHELL:-}" ]; then - exec nu - fi - ''; + packages = [nv pkgs.updateR]; }; }); }; inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - - rixpkgs.url = "github:dwinkler1/rixpkgs/af2dd3f7b4b172077747c0869d4e30702fb71b0e"; - + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + rixpkgs.url = "github:dwinkler1/rixpkgs/nixpkgs"; fran = { url = "github:dwinkler1/fran"; - inputs.nixpkgs.follows = "rixpkgs"; + inputs = { + nixpkgs.follows = "rixpkgs"; + }; }; - nvimConfig = { url = "github:dwinkler1/nvimConfig"; inputs = { - nixpkgs.follows = "nixpkgs"; rixpkgs.follows = "rixpkgs"; + nixpkgs.follows = "nixpkgs"; fran.follows = "fran"; + "plugins-r".follows = "plugins-r"; }; }; + "plugins-r" = { + url = "github:R-nvim/R.nvim/v0.99.3"; + flake = false; + }; }; } diff --git a/templates/rde/flake.nix b/templates/rde/flake.nix index 12ae4f0..27e3f5a 100644 --- a/templates/rde/flake.nix +++ b/templates/rde/flake.nix @@ -185,6 +185,10 @@ julia = config.enabledLanguages.julia; python = config.enabledLanguages.python; r = config.enabledLanguages.r; + # Enable markdown/quarto plugins whenever R is active so that + # .qmd buffers get proper syntax highlighting and chunk + # recognition via quarto-nvim and otter-nvim. + markdown = config.enabledLanguages.r; project = true; gitPlugins = config.enabledPackages.gitPlugins; background = config.theme.background; @@ -202,11 +206,22 @@ # Development shell configuration devShells = forSystems (system: let pkgs = import nixpkgs {inherit system;}; + # When R is enabled, build an overlaid pkgs that includes the R-enhanced + # quarto (with knitr and other R packages baked in via extraRPackages). + # This ensures `quarto render` from the terminal also works correctly. + rPkgs = + if config.enabledLanguages.r + then + import nixpkgs { + inherit system; + overlays = [rixOverlay rOverlay]; + } + else pkgs; # Language-specific packages that should be available in shell - languagePackages = with pkgs; + languagePackages = [] - ++ (if config.enabledLanguages.r then [quarto] else []) - ++ (if config.enabledLanguages.python then [uv] else []) + ++ (if config.enabledLanguages.r then [rPkgs.quarto] else []) + ++ (if config.enabledLanguages.python then [pkgs.uv] else []) ++ (if config.enabledLanguages.julia then [] else []); in { default = pkgs.mkShell { diff --git a/templates/rde/overlays/r.nix b/templates/rde/overlays/r.nix index 255079b..13415ba 100644 --- a/templates/rde/overlays/r.nix +++ b/templates/rde/overlays/r.nix @@ -18,8 +18,10 @@ final: prev: let broom # Tidy model outputs data_table # Fast data manipulation janitor # Data cleaning helpers + knitr # Required by Quarto to execute R code chunks languageserver # LSP for IDE support reprex # Reproducible examples + rmarkdown # Required by Quarto for R-based rendering styler # Code formatting tidyverse # Data science ecosystem ] diff --git a/tests/ed/test.R b/tests/ed/test.R deleted file mode 100755 index ad2967a..0000000 --- a/tests/ed/test.R +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env Rscript - -packages <- c("arrow", "data.table", "fixest", "nvimcom") -failed <- character(0) - -for (pkg in packages) { - ok <- tryCatch( - { - # nvimcom requires Neovim; skip loading but check it's installed - if (pkg == "nvimcom") { - requireNamespace(pkg, quietly = TRUE) - } else { - library(pkg, character.only = TRUE, quietly = TRUE) - } - TRUE - }, - error = function(e) FALSE - ) - if (!ok) { - failed <- c(failed, pkg) - } -} - -if (length(failed) > 0) { - cat(sprintf("FAIL: %s\n", paste(failed, collapse = ", "))) - quit(status = 1) -} - -cat("PASS: all packages loaded\n") diff --git a/tests/ed/test.jl b/tests/ed/test.jl deleted file mode 100755 index 5971f80..0000000 --- a/tests/ed/test.jl +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env julia - -packages = ["StatsBase"] -failed = String[] - -for pkg in packages - try - @eval using $pkg - catch - push!(failed, pkg) - end -end - -if !isempty(failed) - @error "FAIL: $(join(failed, ", "))" - exit(1) -end - -println("PASS: all packages loaded") diff --git a/tests/ed/test.py b/tests/ed/test.py deleted file mode 100755 index b893419..0000000 --- a/tests/ed/test.py +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env python3 -import sys - -packages = ["requests"] -failed = [] - -for pkg in packages: - try: - __import__(pkg) - except ImportError: - failed.append(pkg) - -if failed: - sys.stderr.write(f"FAIL: {', '.join(failed)}\n") - sys.exit(1) - -print("PASS: all packages loaded")