flake.nix
1{
2 description = "AIP service";
3
4 inputs = {
5 nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
6 crane.url = "github:ipetkov/crane";
7 fenix = {
8 url = "github:nix-community/fenix";
9 inputs.nixpkgs.follows = "nixpkgs";
10 inputs.rust-analyzer-src.follows = "";
11 };
12 };
13
14 outputs = { self, nixpkgs, crane, fenix }:
15 let
16 systems = [ "x86_64-linux" ];
17 forAllSystems = nixpkgs.lib.genAttrs systems;
18
19 mkPackagesForSystem = system:
20 let
21 pkgs = import nixpkgs {
22 inherit system;
23 config = { allowUnfree = true; };
24 };
25
26 # Configure crane with stable Rust toolchain
27 craneLib = (crane.mkLib pkgs).overrideToolchain
28 fenix.packages.${system}.stable.toolchain;
29
30 # Project source for crane
31 src = pkgs.lib.cleanSourceWith {
32 src = ./.;
33 filter = path: type:
34 (craneLib.filterCargoSources path type) ||
35 (pkgs.lib.hasInfix "/templates/" path) ||
36 (pkgs.lib.hasInfix "/static/" path) ||
37 (pkgs.lib.hasSuffix "/templates" path) ||
38 (pkgs.lib.hasSuffix "/static" path) ||
39 (pkgs.lib.hasInfix "/migrations/" path) ||
40 (pkgs.lib.hasSuffix "/migrations" path);
41 };
42
43 commonArgs = {
44 inherit src;
45 version = "0.1.0";
46 strictDeps = true;
47 pname = "aip";
48 name = "aip";
49 buildInputs = with pkgs; [
50 openssl
51 pkg-config
52 ];
53 nativeBuildInputs = with pkgs; [
54 pkg-config
55 openssl.dev
56 # Add sqlx-cli for migrations
57 sqlx-cli
58 ];
59
60 # Environment variables for OpenSSL
61 OPENSSL_NO_VENDOR = 1;
62 PKG_CONFIG_PATH = "${pkgs.openssl.dev}/lib/pkgconfig";
63
64 # Pass arguments to cargo build
65 cargoExtraArgs = "--no-default-features --features embed,sqlite --bin aip";
66 };
67
68 cargoArtifacts = craneLib.buildDepsOnly commonArgs;
69
70 aip = craneLib.buildPackage (commonArgs // {
71 inherit cargoArtifacts;
72 doCheck = false;
73 CARGO_PROFILE = "release";
74 });
75
76 aip-client-management = craneLib.buildPackage (commonArgs // {
77 inherit cargoArtifacts;
78 doCheck = false;
79 CARGO_PROFILE = "release";
80 cargoExtraArgs = "--no-default-features --features embed,sqlite --bin aip-client-management";
81 });
82
83 # Copy migration files
84 migrationFiles = pkgs.stdenv.mkDerivation {
85 name = "aip-migrations";
86 src = ./migrations;
87 installPhase = ''
88 mkdir -p $out/migrations
89 cp -r * $out/migrations/
90 '';
91 };
92
93 # Copy static files
94 staticFiles = pkgs.stdenv.mkDerivation {
95 name = "aip-static";
96 src = ./static;
97 installPhase = ''
98 mkdir -p $out/static
99 cp -r * $out/static/
100 '';
101 };
102
103 # Migration runner script
104 migrationRunner = pkgs.writeShellScriptBin "run-migrations" ''
105 set -e
106
107 # Ensure /data directory exists and is writable
108 mkdir -p /data
109 chmod 755 /data
110
111 if [ -z "$DATABASE_URL" ]; then
112 echo "DATABASE_URL environment variable is required"
113 exit 1
114 fi
115
116 # Determine migration source based on database type
117 if [[ "$DATABASE_URL" == sqlite* ]]; then
118 MIGRATION_SOURCE="${migrationFiles}/migrations/sqlite"
119 elif [[ "$DATABASE_URL" == postgres* ]]; then
120 MIGRATION_SOURCE="${migrationFiles}/migrations/postgres"
121 else
122 echo "Unsupported database type in DATABASE_URL: $DATABASE_URL"
123 exit 1
124 fi
125
126 echo "Running migrations from $MIGRATION_SOURCE against $DATABASE_URL"
127 ${pkgs.sqlx-cli}/bin/sqlx database create
128 ${pkgs.sqlx-cli}/bin/sqlx migrate run --source "$MIGRATION_SOURCE"
129
130 # Ensure the database file is writable by all users (SQLite only)
131 if [[ "$DATABASE_URL" == sqlite* ]]; then
132 DB_FILE=$(echo "$DATABASE_URL" | sed 's/sqlite:\/\///')
133 if [ -f "$DB_FILE" ]; then
134 chmod 666 "$DB_FILE"
135 fi
136 fi
137 '';
138
139 # Docker image for deployment
140 aipImg = pkgs.dockerTools.buildImage {
141 name = "aip";
142 tag = "latest";
143 fromImage = pkgs.dockerTools.pullImage {
144 imageName = "alpine";
145 imageDigest = "sha256:beefdbd8a1da6d2915566fde36db9db0b524eb737fc57cd1367effd16dc0d06d";
146 sha256 = "sha256-Sfb0quuaHgzxA7paz5P51WhdA35to39HtOufceXixz0=";
147 };
148 copyToRoot = pkgs.buildEnv {
149 name = "image-root";
150 paths = [
151 aip
152 aip-client-management
153 migrationRunner
154 staticFiles
155 pkgs.cacert
156 pkgs.sqlx-cli
157 ];
158 pathsToLink = [ "/bin" "/etc" "/static" ];
159 };
160
161 config = {
162 Cmd = [ "/bin/sh" "-c" "if [ ! -f /data/aip.db ]; then /bin/run-migrations; fi && /bin/aip" ];
163 Env = [
164 "RUST_BACKTRACE=1"
165 "RUST_LOG=info"
166 "PORT=8080"
167 "HTTP_STATIC_PATH=/static"
168 ];
169 ExposedPorts = {
170 "8080/tcp" = {};
171 };
172 };
173 };
174 in
175 {
176 inherit aip aip-client-management aipImg migrationRunner;
177 default = aip;
178 };
179 in
180 {
181 packages = forAllSystems mkPackagesForSystem;
182
183 devShells = forAllSystems (system:
184 let
185 pkgs = import nixpkgs { inherit system; };
186 craneLib = (crane.mkLib pkgs).overrideToolchain
187 fenix.packages.${system}.stable.toolchain;
188 in
189 {
190 default = craneLib.devShell {
191 packages = with pkgs; [
192 nixpkgs-fmt
193 nil
194 dive
195 flyctl
196 sqlite
197 postgresql
198 sqlx-cli
199 ];
200
201 # Set up environment for development
202 RUST_LOG = "info";
203 };
204 });
205 };
206}