Add Nomad deployment configuration and CI/CD pipeline
Some checks failed
Deploy / deploy (push) Failing after 1m43s
Some checks failed
Deploy / deploy (push) Failing after 1m43s
- Add docker.nix for Nix-based Docker image builds - Update flake.nix with dockerImage package output - Add output: standalone to next.config.ts for production builds - Add /metrics endpoint for Prometheus scraping - Add Gitea Actions workflow calling shared deploy-nomad.yaml Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
16
.gitea/workflows/deploy.yaml
Normal file
16
.gitea/workflows/deploy.yaml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# ABOUTME: Gitea Actions workflow for deploying PhaseFlow to Nomad.
|
||||||
|
# ABOUTME: Builds Nix Docker image and deploys using shared workflow.
|
||||||
|
|
||||||
|
name: Deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
uses: alo/alo-cluster/.gitea/workflows/deploy-nomad.yaml@master
|
||||||
|
with:
|
||||||
|
service_name: phaseflow
|
||||||
|
secrets: inherit
|
||||||
111
docker.nix
Normal file
111
docker.nix
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
# ABOUTME: Nix expression for building PhaseFlow Docker image.
|
||||||
|
# ABOUTME: Creates standalone Next.js production bundle with minimal dependencies.
|
||||||
|
{ pkgs }:
|
||||||
|
|
||||||
|
let
|
||||||
|
src = pkgs.lib.cleanSource ./.;
|
||||||
|
|
||||||
|
# Build the Next.js application using pnpm
|
||||||
|
# Note: This builds with network access. For fully reproducible builds,
|
||||||
|
# consider using pnpm.fetchDeps or dream2nix in the future.
|
||||||
|
phaseflow = pkgs.stdenv.mkDerivation {
|
||||||
|
pname = "phaseflow";
|
||||||
|
version = "0.1.0";
|
||||||
|
inherit src;
|
||||||
|
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
nodejs_24
|
||||||
|
pnpm
|
||||||
|
cacert
|
||||||
|
];
|
||||||
|
|
||||||
|
# Allow network access for pnpm install
|
||||||
|
__noChroot = true;
|
||||||
|
|
||||||
|
# Enable network during build (requires trusted-users in nix.conf)
|
||||||
|
# Alternative: use sandbox = false for this derivation
|
||||||
|
impureEnvVars = pkgs.lib.fetchers.proxyImpureEnvVars ++ [
|
||||||
|
"HOME"
|
||||||
|
"npm_config_cache"
|
||||||
|
];
|
||||||
|
|
||||||
|
buildPhase = ''
|
||||||
|
export HOME=$TMPDIR
|
||||||
|
export NEXT_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
|
# Provide dummy env vars for build (actual values injected at runtime)
|
||||||
|
export RESEND_API_KEY="re_build_placeholder"
|
||||||
|
export ENCRYPTION_KEY="build_placeholder_32_chars_long!"
|
||||||
|
export CRON_SECRET="build_placeholder_secret"
|
||||||
|
export POCKETBASE_URL="http://localhost:8090"
|
||||||
|
export APP_URL="https://phaseflow.v.paler.net"
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
# Build the Next.js app with standalone output
|
||||||
|
pnpm build
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Disable broken symlink check - pnpm creates internal symlinks we don't need
|
||||||
|
dontCheckForBrokenSymlinks = true;
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out
|
||||||
|
|
||||||
|
# Copy standalone server (self-contained with minimal node_modules)
|
||||||
|
cp -r .next/standalone/* $out/
|
||||||
|
|
||||||
|
# Copy static assets (Next.js standalone requires these separately)
|
||||||
|
mkdir -p $out/.next
|
||||||
|
cp -r .next/static $out/.next/static
|
||||||
|
|
||||||
|
# Copy public assets
|
||||||
|
if [ -d public ]; then
|
||||||
|
cp -r public $out/public
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in
|
||||||
|
pkgs.dockerTools.buildImage {
|
||||||
|
name = "gitea.v.paler.net/alo/phaseflow";
|
||||||
|
tag = "latest";
|
||||||
|
|
||||||
|
copyToRoot = pkgs.buildEnv {
|
||||||
|
name = "phaseflow-env";
|
||||||
|
paths = with pkgs; [
|
||||||
|
# System utilities
|
||||||
|
busybox
|
||||||
|
bash
|
||||||
|
|
||||||
|
# Node.js runtime
|
||||||
|
nodejs_24
|
||||||
|
|
||||||
|
# Docker filesystem helpers
|
||||||
|
dockerTools.usrBinEnv
|
||||||
|
dockerTools.binSh
|
||||||
|
dockerTools.fakeNss
|
||||||
|
dockerTools.caCertificates
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Copy the built application
|
||||||
|
extraCommands = ''
|
||||||
|
mkdir -p -m 1777 tmp
|
||||||
|
mkdir -p app
|
||||||
|
cp -r ${phaseflow}/* app/
|
||||||
|
'';
|
||||||
|
|
||||||
|
config = {
|
||||||
|
Env = [
|
||||||
|
"NODE_ENV=production"
|
||||||
|
"PORT=3000"
|
||||||
|
"HOSTNAME=0.0.0.0"
|
||||||
|
];
|
||||||
|
ExposedPorts = {
|
||||||
|
"3000/tcp" = {};
|
||||||
|
};
|
||||||
|
Cmd = [ "${pkgs.nodejs_24}/bin/node" "/app/server.js" ];
|
||||||
|
WorkingDir = "/app";
|
||||||
|
};
|
||||||
|
}
|
||||||
10
flake.nix
10
flake.nix
@@ -1,5 +1,5 @@
|
|||||||
# ABOUTME: Nix flake for PhaseFlow development environment.
|
# ABOUTME: Nix flake for PhaseFlow development environment and Docker build.
|
||||||
# ABOUTME: Provides Node.js 24, pnpm, turbo, lefthook, and Ralph sandbox shell.
|
# ABOUTME: Provides Node.js 24, pnpm, turbo, lefthook, and Docker image output.
|
||||||
{
|
{
|
||||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
|
||||||
@@ -16,6 +16,12 @@
|
|||||||
pocketbase
|
pocketbase
|
||||||
];
|
];
|
||||||
in {
|
in {
|
||||||
|
# Docker image for production deployment
|
||||||
|
packages.${system} = {
|
||||||
|
dockerImage = import ./docker.nix { inherit pkgs; };
|
||||||
|
default = import ./docker.nix { inherit pkgs; };
|
||||||
|
};
|
||||||
|
|
||||||
devShells.${system} = {
|
devShells.${system} = {
|
||||||
# Default development shell with all tools
|
# Default development shell with all tools
|
||||||
default = pkgs.mkShell {
|
default = pkgs.mkShell {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { NextConfig } from "next";
|
import type { NextConfig } from "next";
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
/* config options here */
|
output: "standalone",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
|
|||||||
16
src/app/metrics/route.ts
Normal file
16
src/app/metrics/route.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// ABOUTME: Prometheus metrics endpoint at /metrics for standard scraping path.
|
||||||
|
// ABOUTME: Re-exports the same metrics as /api/metrics for Prometheus autodiscovery.
|
||||||
|
|
||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import { metricsRegistry } from "@/lib/metrics";
|
||||||
|
|
||||||
|
export async function GET(): Promise<NextResponse> {
|
||||||
|
const metrics = await metricsRegistry.metrics();
|
||||||
|
|
||||||
|
return new NextResponse(metrics, {
|
||||||
|
status: 200,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": metricsRegistry.contentType,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user