From 33c894604163a50cd5e66eb6c4ab25177b019966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=BChl?= Date: Mon, 16 Feb 2026 14:39:25 +0100 Subject: [PATCH] Add Nix-based deploy action for isolated builds - New deploy-nix-site action using Nix flakes - Runs in nixos/nix:latest container for proper isolation - Builds using flake.nix, uploads to S3, deploys to Nomad - Update deploy-site action to install Nomad CLI - Document both actions in README Co-Authored-By: Claude Sonnet 4.5 --- README.md | 78 ++++++++++++++++++- deploy-nix-site/action.yaml | 150 ++++++++++++++++++++++++++++++++++++ deploy-site/action.yaml | 15 +++- 3 files changed, 241 insertions(+), 2 deletions(-) create mode 100644 deploy-nix-site/action.yaml diff --git a/README.md b/README.md index f992adc..2ef47d4 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,85 @@ Reusable Forgejo/Gitea actions for toph's infrastructure. ## Available Actions +### `deploy-nix-site` (Recommended) + +Deploy a static site built with Nix flake to S3 and Nomad. Provides proper isolation and reproducibility. + +**Requirements:** +- Repository must have a `flake.nix` with a default package output +- Runner label: `nix` (uses `docker://nixos/nix:latest`) + +**Usage:** + +```yaml +name: Deploy +on: + push: + branches: [main] + +jobs: + deploy: + runs-on: nix + steps: + - uses: actions/checkout@v4 + + - uses: https://git.toph.so/toph/ci-actions/deploy-nix-site@main + with: + site-name: mysite + traefik-rule: Host(`mysite.example.com`) + env: + S3_ACCESS_KEY: ${{ secrets.S3_ACCESS_KEY }} + S3_SECRET_KEY: ${{ secrets.S3_SECRET_KEY }} +``` + +**Inputs:** +- `site-name` (required): Site identifier +- `traefik-rule` (required): Traefik routing rule +- `flake-output` (optional): Nix flake output, defaults to `.#` +- `s3-endpoint` (optional): S3 endpoint, defaults to `https://s3.toph.so` + +**Example flake.nix:** + +```nix +{ + description = "My static site"; + + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + outputs = { self, nixpkgs }: + let + system = "x86_64-linux"; + pkgs = nixpkgs.legacyPackages.${system}; + in { + packages.${system}.default = pkgs.stdenv.mkDerivation { + name = "my-site"; + src = ./.; + + buildInputs = [ pkgs.nodejs_20 ]; + + buildPhase = '' + npm ci + npm run build + ''; + + installPhase = '' + cp -r dist $out + ''; + }; + + devShells.${system}.default = pkgs.mkShell { + packages = [ pkgs.nodejs_20 pkgs.vite ]; + shellHook = "echo 'Run: vite'"; + }; + }; +} +``` + +--- + ### `deploy-site` -Deploy a static site to production via S3 and Nomad. +Deploy a static site to production via S3 and Nomad (non-Nix). **Usage:** diff --git a/deploy-nix-site/action.yaml b/deploy-nix-site/action.yaml new file mode 100644 index 0000000..b9376d5 --- /dev/null +++ b/deploy-nix-site/action.yaml @@ -0,0 +1,150 @@ +name: Deploy Nix Site +description: Deploy static site built with Nix flake to S3 and Nomad + +inputs: + site-name: + description: 'Site identifier (used as service name in Nomad)' + required: true + + traefik-rule: + description: 'Traefik routing rule (e.g., Host(`example.com`) or Host(`example.com`) || Host(`www.example.com`))' + required: true + + flake-output: + description: 'Nix flake output to build (e.g., .#packages.x86_64-linux.default or .#)' + required: false + default: '.#' + + s3-endpoint: + description: 'S3 endpoint' + required: false + default: 'https://s3.toph.so' + +runs: + using: composite + steps: + - name: Install tools + shell: bash + run: | + # Install AWS CLI + nix profile install nixpkgs#awscli2 + + # Install Nomad + nix profile install nixpkgs#nomad + + # Make available in PATH + export PATH="$HOME/.nix-profile/bin:$PATH" + echo "$HOME/.nix-profile/bin" >> $GITHUB_PATH + + # Set Nomad address + echo "NOMAD_ADDR=http://alvin:4646" >> $GITHUB_ENV + + - name: Build site with Nix + shell: bash + run: | + nix build ${{ inputs.flake-output }} --print-build-logs + + # Find the result + if [ -L result ]; then + BUILD_OUTPUT="result" + else + echo "Error: No result symlink found after nix build" + exit 1 + fi + + echo "BUILD_OUTPUT=$BUILD_OUTPUT" >> $GITHUB_ENV + + - name: Package and upload to S3 + shell: bash + run: | + ARTIFACT_NAME="${{ github.sha }}.tar.gz" + + # Package the built output + tar czf "/tmp/${ARTIFACT_NAME}" -C "$BUILD_OUTPUT" . + + # Configure AWS CLI for S3 + export AWS_ACCESS_KEY_ID="${{ env.S3_ACCESS_KEY }}" + export AWS_SECRET_ACCESS_KEY="${{ env.S3_SECRET_KEY }}" + export AWS_ENDPOINT_URL="${{ inputs.s3-endpoint }}" + export AWS_EC2_METADATA_DISABLED=true + + # Upload to S3 + aws s3 cp "/tmp/${ARTIFACT_NAME}" "s3://artifacts/${ARTIFACT_NAME}" + + # Make publicly readable + aws s3api put-object-acl \ + --bucket artifacts \ + --key "${ARTIFACT_NAME}" \ + --acl public-read + + echo "📦 Artifact uploaded: ${{ inputs.s3-endpoint }}/artifacts/${ARTIFACT_NAME}" + + - name: Deploy via Nomad + shell: bash + run: | + cat > /tmp/deploy-${{ inputs.site-name }}.nomad.json <<'NOMAD_EOF' + { + "Job": { + "ID": "${{ inputs.site-name }}", + "Name": "${{ inputs.site-name }}", + "Type": "service", + "Datacenters": ["contabo"], + "Constraints": [{ + "LTarget": "${node.unique.name}", + "RTarget": "alvin", + "Operand": "=" + }], + "TaskGroups": [{ + "Name": "web", + "Count": 1, + "Networks": [{ + "Mode": "bridge", + "DynamicPorts": [{ + "Label": "http", + "To": 8080 + }] + }], + "Services": [{ + "Name": "${{ inputs.site-name }}", + "PortLabel": "http", + "Provider": "nomad", + "Tags": [ + "traefik.enable=true", + "traefik.http.routers.${{ inputs.site-name }}.rule=${{ inputs.traefik-rule }}", + "traefik.http.routers.${{ inputs.site-name }}.entrypoints=websecure", + "traefik.http.routers.${{ inputs.site-name }}.tls.certresolver=letsencrypt" + ] + }], + "Tasks": [{ + "Name": "server", + "Driver": "docker", + "Config": { + "image": "joseluisq/static-web-server:2", + "ports": ["http"] + }, + "Env": { + "SERVER_ROOT": "/local/public", + "SERVER_LOG_LEVEL": "info" + }, + "Artifacts": [{ + "GetterSource": "${{ inputs.s3-endpoint }}/artifacts/${{ github.sha }}.tar.gz", + "RelativeDest": "local/public", + "GetterMode": "dir" + }], + "Resources": { + "CPU": 100, + "MemoryMB": 64 + } + }] + }] + } + } + NOMAD_EOF + + nomad job run /tmp/deploy-${{ inputs.site-name }}.nomad.json + + - name: Deployment summary + shell: bash + run: | + echo "✅ Deployed ${{ inputs.site-name }}" + echo "📋 Traefik rule: ${{ inputs.traefik-rule }}" diff --git a/deploy-site/action.yaml b/deploy-site/action.yaml index d332fec..27e30c7 100644 --- a/deploy-site/action.yaml +++ b/deploy-site/action.yaml @@ -23,15 +23,28 @@ inputs: runs: using: composite steps: - - name: Install AWS CLI + - name: Install AWS CLI and Nomad shell: bash run: | + # Install AWS CLI if not present if ! command -v aws &> /dev/null; then curl -sL "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "/tmp/awscliv2.zip" unzip -q /tmp/awscliv2.zip -d /tmp sudo /tmp/aws/install fi + # Install Nomad if not present + if ! command -v nomad &> /dev/null; then + NOMAD_VERSION="1.8.4" + curl -sL "https://releases.hashicorp.com/nomad/${NOMAD_VERSION}/nomad_${NOMAD_VERSION}_linux_amd64.zip" -o "/tmp/nomad.zip" + unzip -q /tmp/nomad.zip -d /tmp + sudo mv /tmp/nomad /usr/local/bin/ + sudo chmod +x /usr/local/bin/nomad + fi + + # Set Nomad address + echo "NOMAD_ADDR=http://alvin:4646" >> $GITHUB_ENV + - name: Package and upload to S3 shell: bash run: |