Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add blog post about using a nix flake for development with Loco #907

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

charludo
Copy link

This PR adds a blog post describing how to get up and running with Loco on NixOS using a nix flake providing a development shell.

In #559, multiple people appeared interested in this.

I do not have a blog, so hope it's OK that I just added a new entry in the docs blog. If a different location is more fitting, I'm happy to move it there!

@charludo
Copy link
Author

Or maybe it would be nice to add the flake directly, just like the Devcontainer?

@NexVeridian
Copy link

Or maybe it would be nice to add the flake directly, just like the Devcontainer?

in #846 the Devcontainers is going to be moved to loco g deployment, which is also related to #864, so I think adding something like loco g nix eventually is a good idea

@NexVeridian
Copy link

NexVeridian commented Oct 26, 2024

I would also consider swapping to crane
since I think it's better for CI

I'll paste my current crane file here, if you decide to do that, notes/TODO:

  • postgres is working for cargoNextest, but not for dev
  • would be better if env was grabbed from the same place as config/development.yaml and config/test.yaml
flake.nix
{
  description = "Build a cargo project";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";

    crane.url = "github:ipetkov/crane";

    fenix = {
      url = "github:nix-community/fenix";
      inputs.nixpkgs.follows = "nixpkgs";
      inputs.rust-analyzer-src.follows = "";
    };

    flake-utils.url = "github:numtide/flake-utils";

    advisory-db = {
      url = "github:rustsec/advisory-db";
      flake = false;
    };
  };

  outputs =
    {
      self,
      nixpkgs,
      crane,
      fenix,
      flake-utils,
      advisory-db,
      ...
    }:
    flake-utils.lib.eachDefaultSystem (
      system:
      let
        pkgs = nixpkgs.legacyPackages.${system};

        inherit (pkgs) lib;

        craneLib = crane.mkLib pkgs;

        # src = craneLib.cleanCargoSource ./.;
        src =
          let
            yamlFilter = path: _type: builtins.match ".*yaml$" path != null;
            snapFilter = path: _type: builtins.match ".*snap$" path != null;
            tFilter = path: _type: builtins.match ".*t$" path != null;
            customFilter =
              path: type:
              (yamlFilter path type)
              || (snapFilter path type)
              || (tFilter path type)
              || (craneLib.filterCargoSources path type);
          in
          pkgs.lib.cleanSourceWith {
            src = ./.;
            filter = customFilter;
            name = "source";
          };

        # Common arguments can be set here to avoid repeating them later
        commonArgs = {
          inherit src;
          strictDeps = true;

          buildInputs =
            [
              # Add additional build inputs here
              pkgs.openssl
              pkgs.pkg-config
              pkgs.postgresql
            ]
            ++ lib.optionals pkgs.stdenv.isDarwin [
              # Additional darwin specific inputs can be set here
              pkgs.libiconv
              pkgs.darwin.apple_sdk.frameworks.SystemConfiguration
            ];

          # Additional environment variables can be set directly
          # MY_CUSTOM_VAR = "some value";
          POSTGRES_DB = "nextrack_test";
          POSTGRES_USER = "loco";
          POSTGRES_PASSWORD = "loco";
          DATABASE_URL = "postgres://loco:loco@localhost:5432/nextrack_test";
        };

        # Build *just* the cargo dependencies, so we can reuse
        # all of that work (e.g. via cachix) when running in CI
        cargoArtifacts = craneLib.buildDepsOnly commonArgs;

        # Build the actual crate itself, reusing the dependency
        # artifacts from above.
        my-crate = craneLib.buildPackage (
          commonArgs
          // {
            doCheck = false;
            inherit cargoArtifacts;
          }
        );

        # Define the Docker image build
        dockerImage = pkgs.dockerTools.buildImage {
          name = "nextrack";
          tag = "latest";
          copyToRoot = [ my-crate ];
          config = {
            Cmd = [ "${my-crate}/bin/nextrack-cli" ];
          };
        };

        postgresSetup = ''
          export PGDATA=$(mktemp -d /tmp/XXXXXX)
          ${pkgs.postgresql}/bin/initdb "$PGDATA"
          ${pkgs.postgresql}/bin/pg_ctl -D "$PGDATA" --options "-c unix_socket_directories=$PGDATA" start
          ${pkgs.postgresql}/bin/createdb -h localhost ${commonArgs.POSTGRES_DB}
          ${pkgs.postgresql}/bin/psql -h localhost -d ${commonArgs.POSTGRES_DB} -c "CREATE USER ${commonArgs.POSTGRES_USER} WITH PASSWORD '${commonArgs.POSTGRES_PASSWORD}';"
          ${pkgs.postgresql}/bin/psql -h localhost -d ${commonArgs.POSTGRES_DB} -c "ALTER USER ${commonArgs.POSTGRES_USER} WITH SUPERUSER;"
          echo "PostgreSQL started"
        '';

        postgresCleanup = ''
          ${pkgs.postgresql}/bin/pg_ctl -D "$PGDATA" stop
        '';
      in
      {
        checks = {
          # Build the crate as part of `nix flake check` for convenience
          inherit my-crate;

          # Run clippy (and deny all warnings) on the crate source,
          # again, reusing the dependency artifacts from above.
          #
          # Note that this is done as a separate derivation so that
          # we can block the CI if there are issues here, but not
          # prevent downstream consumers from building our crate by itself.
          my-crate-clippy = craneLib.cargoClippy (
            commonArgs
            // {
              inherit cargoArtifacts;
              cargoClippyExtraArgs = "--all-targets -- --deny warnings";
            }
          );

          # my-crate-doc = craneLib.cargoDoc (commonArgs // {
          #   inherit cargoArtifacts;
          # });

          # Check formatting
          my-crate-fmt = craneLib.cargoFmt {
            inherit src;
          };

          # # Audit dependencies
          # my-crate-audit = craneLib.cargoAudit {
          #   inherit src advisory-db;
          # };

          # # Audit licenses
          # my-crate-deny = craneLib.cargoDeny {
          #   inherit src;
          # };

          # Run tests with cargo-nextest
          # Consider setting `doCheck = false` on `my-crate` if you do not want
          # the tests to run twice
          my-crate-nextest = craneLib.cargoNextest (
            commonArgs
            // {
              inherit cargoArtifacts;
              partitions = 1;
              partitionType = "count";
              cargoNextestExtraArgs = " --test-threads=1";
              preCheck = postgresSetup;
              postCheck = postgresCleanup;
            }
          );
        };

        packages = {
          default = my-crate;
          inherit
            my-crate
            dockerImage
            ;
        };

        apps.default = flake-utils.lib.mkApp {
          drv = my-crate;
        };

        devShells.default = craneLib.devShell {
          # Inherit inputs from checks.
          checks = self.checks.${system};

          # Additional dev-shell environment variables can be set directly
          # MY_CUSTOM_DEVELOPMENT_VAR = "something else";

          # Extra inputs can be added here; cargo and rustc are provided by default.
          packages = with pkgs; [
            cargo-nextest
            sea-orm-cli
            cargo-insta
            cargo-edit
            nodejs
            pnpm
            biome
          ];
        };
      }
    );
}

@jondot
Copy link
Contributor

jondot commented Oct 26, 2024

I think both the blog and loco g deployment (and then select nix) make sense
LMK if you prefer for me to merge this PR (which is only the blog post)

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