diff --git a/.forgejo/workflows/build-image.yml b/.forgejo/workflows/build-image.yml new file mode 100644 index 0000000..b537b6d --- /dev/null +++ b/.forgejo/workflows/build-image.yml @@ -0,0 +1,45 @@ +name: Build and Push OCI Image + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +jobs: + build: + runs-on: nix + steps: + - name: Checkout code + uses: https://code.forgejo.org/actions/checkout@v4 + + - name: Build OCI image + run: | + nix build .#solidhaus-image \ + --print-build-logs \ + --show-trace + + - name: Push to registry + if: github.ref == 'refs/heads/main' + run: | + image=$(nix build --no-link --print-out-paths .#solidhaus-image) + skopeo copy \ + --dest-tls-verify=false \ + "docker-archive:$image" \ + "docker://registry.toph.so/solidhaus:latest" + + # Also tag with commit SHA + skopeo copy \ + --dest-tls-verify=false \ + "docker-archive:$image" \ + "docker://registry.toph.so/solidhaus:${GITHUB_SHA:0:7}" + + - name: Build summary + if: github.ref == 'refs/heads/main' + run: | + echo "### ✅ Image Built and Pushed" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Image**: registry.toph.so/solidhaus:latest" >> $GITHUB_STEP_SUMMARY + echo "- **Tag**: ${GITHUB_SHA:0:7}" >> $GITHUB_STEP_SUMMARY + echo "- **Commit**: ${GITHUB_SHA}" >> $GITHUB_STEP_SUMMARY diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..84aac76 --- /dev/null +++ b/flake.lock @@ -0,0 +1,130 @@ +{ + "nodes": { + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1769996383, + "narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "57928607ea566b5db3ad13af0e57e921e6b12381", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1771903837, + "narHash": "sha256-sdaqdnsQCv3iifzxwB22tUwN/fSHoN7j2myFW5EIkGk=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e764fc9a405871f1f6ca3d1394fb422e0a0c3951", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-25.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1769909678, + "narHash": "sha256-cBEymOf4/o3FD5AZnzC3J9hLbiZ+QDT/KDuyHXVJOpM=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "72716169fe93074c333e8d0173151350670b824c", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1771848320, + "narHash": "sha256-0MAd+0mun3K/Ns8JATeHT1sX28faLII5hVLq0L3BdZU=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "2fc6539b481e1d2569f25f8799236694180c0993", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs", + "sd-card": "sd-card" + } + }, + "sd-card": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1772146011, + "narHash": "sha256-ZUj4zAAk9shxYOn5jh9Kri4q5O/Aq52OJF43otPMPBE=", + "ref": "refs/heads/main", + "rev": "307eb15035671c58f9145806067030b59c5d7792", + "revCount": 4, + "type": "git", + "url": "ssh://git@git.toph.so/toph/sd-card" + }, + "original": { + "type": "git", + "url": "ssh://git@git.toph.so/toph/sd-card" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix index 009ffc5..b352aef 100644 --- a/flake.nix +++ b/flake.nix @@ -1,26 +1,180 @@ { - description = "solidhaus dev shell"; + description = "SolidHaus — Local-first household inventory app"; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - flake-utils.url = "github:numtide/flake-utils"; - forgejo-workflow.url = "path:/nix/store/1kpd2vzj87rlraw81p4iy4ldw4dm8g6z-forgejo-workflow-setup-flake/bin"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; + flake-parts.url = "github:hercules-ci/flake-parts"; + sd-card.url = "git+ssh://git@git.toph.so/toph/sd-card"; }; - outputs = { self, nixpkgs, flake-utils, forgejo-workflow }: - flake-utils.lib.eachDefaultSystem (system: - let - pkgs = nixpkgs.legacyPackages.${system}; - in - { + outputs = inputs @ { + flake-parts, + nixpkgs, + sd-card, + ... + }: + flake-parts.lib.mkFlake {inherit inputs;} { + systems = ["x86_64-linux" "aarch64-linux"]; + + perSystem = { + config, + pkgs, + system, + ... + }: let + nodejs = pkgs.nodejs_22; + buildNpmPackage = pkgs.buildNpmPackage.override { + nodejs = nodejs; + }; + in { + packages = { + # Build the SvelteKit app + solidhaus = buildNpmPackage { + pname = "solidhaus"; + version = "0.0.1"; + + src = ./.; + + npmDepsHash = "sha256-d7k2YpmFt/Ba0j0SrhgoHQdhYjxHO46BliOzyecZgbY="; + + buildPhase = '' + npm run build + ''; + + installPhase = '' + mkdir -p $out + cp -r build/* $out/ + ''; + + meta = { + description = "Local-first household inventory app with barcode scanning"; + homepage = "https://git.toph.so/toph/solidhaus"; + }; + }; + + # OCI image with nginx serving the built app + solidhaus-image = pkgs.dockerTools.buildLayeredImage { + name = "registry.toph.so/solidhaus"; + tag = "latest"; + + contents = with pkgs; [ + fakeNss + nginx + ]; + + config = { + Cmd = [ + "${pkgs.nginx}/bin/nginx" + "-c" + "/etc/nginx/nginx.conf" + "-g" + "daemon off;" + ]; + ExposedPorts = { + "80/tcp" = {}; + }; + WorkingDir = "/usr/share/nginx/html"; + }; + + extraCommands = '' + # Create directory structure + mkdir -p var/log/nginx + mkdir -p var/cache/nginx + mkdir -p tmp + mkdir -p etc/nginx + + # Copy built app + mkdir -p usr/share/nginx/html + cp -r ${config.packages.solidhaus}/* usr/share/nginx/html/ + + # Create nginx config + cat > etc/nginx/nginx.conf <<'EOF' + user nobody nobody; + worker_processes auto; + error_log /var/log/nginx/error.log warn; + pid /tmp/nginx.pid; + + events { + worker_connections 1024; + } + + http { + include ${pkgs.nginx}/conf/mime.types; + default_type application/octet-stream; + + access_log /var/log/nginx/access.log; + + sendfile on; + keepalive_timeout 65; + + # Gzip compression + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_types text/plain text/css text/xml text/javascript application/javascript application/json application/xml+rss; + + server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + # SPA fallback + location / { + try_files $uri $uri/ /index.html; + } + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + + # Cache static assets + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + } + } + EOF + ''; + }; + + # nix run .#dev — start SvelteKit dev server with annotation proxy + dev = sd-card.lib.${system}.mkDevApp { + inherit pkgs; + devCmd = "${nodejs}/bin/npm run dev"; + devPort = 5173; # Vite default port + }; + + default = config.packages.solidhaus; + }; + + apps = { + # Push image to registry + push-solidhaus-image = { + type = "app"; + program = pkgs.lib.getExe (pkgs.writeShellApplication { + name = "push-solidhaus-image"; + runtimeInputs = [pkgs.skopeo]; + text = '' + image=$(nix build --no-link --print-out-paths .#solidhaus-image) + skopeo copy \ + --insecure-policy \ + "docker-archive:$image" \ + "docker://registry.toph.so/solidhaus:latest" + ''; + }); + }; + }; + devShells.default = pkgs.mkShell { - packages = - # Forgejo workflow tools: tea, jq, yq, git, curl - forgejo-workflow.packages.${system}.tools + packages = sd-card.packages.${system}.tools # tea, jq, nodejs, playwright ++ (with pkgs; [ - # add your project packages here + nodejs + nodePackages.npm ]); }; - } - ); + }; + }; }