generated from coulomb/repo-seed
feat: minimal IHP scaffold — T01-T05, T08 of IRP-WP-0001
- flake.nix adapted from inter-hub: appName=ihp-railiance-probe, stripped to core packages, GHC 9.10.3 Bug 1+2 overlays carried verbatim (pname check updated to ihp-railiance-probe-models) - IHP project scaffold: Main.hs, Config.hs, App.cabal, Setup.hs, Makefile - Schema: probes table (id, name, created_at) - Health endpoint: GET /healthz → "ok" (HealthController) - Probes CRUD: ProbesController + 4 views (Index, New, Show, Edit) - Hspec test suite: Test/ProbeControllerSpec covers /probes and /healthz - Helm chart in chart/: deployment, service, ingress, secret templates - devenv.nix, devenv.yaml, .ghci, tailwind config Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
7
.ghci
Normal file
7
.ghci
Normal file
@@ -0,0 +1,7 @@
|
||||
:set -XNoImplicitPrelude
|
||||
:def loadFromIHP \file -> (System.Environment.getEnv "IHP_LIB") >>= (\ihpLib -> readFile (ihpLib <> "/" <> file))
|
||||
:loadFromIHP applicationGhciConfig
|
||||
:set -j1
|
||||
:set -fkeep-going
|
||||
:set -fwrite-interface
|
||||
import IHP.Prelude
|
||||
57
App.cabal
Normal file
57
App.cabal
Normal file
@@ -0,0 +1,57 @@
|
||||
-- Stub cabal file — only for HLS/tooling support. Not used by the build.
|
||||
-- See flake.nix for actual package management.
|
||||
name: App
|
||||
version: 0.1.0.0
|
||||
license: AllRightsReserved
|
||||
license-file: LICENSE
|
||||
author: Developers
|
||||
maintainer: developers@example.com
|
||||
build-type: Simple
|
||||
cabal-version: >=1.10
|
||||
|
||||
executable App
|
||||
main-is: Main.hs
|
||||
build-depends:
|
||||
ihp,
|
||||
base,
|
||||
wai,
|
||||
text,
|
||||
hspec,
|
||||
ihp-hspec
|
||||
hs-source-dirs: .
|
||||
default-language: Haskell2010
|
||||
extensions:
|
||||
OverloadedStrings
|
||||
, NoImplicitPrelude
|
||||
, ImplicitParams
|
||||
, Rank2Types
|
||||
, DisambiguateRecordFields
|
||||
, NamedFieldPuns
|
||||
, DuplicateRecordFields
|
||||
, OverloadedLabels
|
||||
, FlexibleContexts
|
||||
, TypeSynonymInstances
|
||||
, FlexibleInstances
|
||||
, QuasiQuotes
|
||||
, TypeFamilies
|
||||
, PackageImports
|
||||
, ScopedTypeVariables
|
||||
, RecordWildCards
|
||||
, TypeApplications
|
||||
, DataKinds
|
||||
, InstanceSigs
|
||||
, DeriveGeneric
|
||||
, MultiParamTypeClasses
|
||||
, TypeOperators
|
||||
, DeriveDataTypeable
|
||||
, MultiWayIf
|
||||
, UndecidableInstances
|
||||
, BlockArguments
|
||||
, PartialTypeSignatures
|
||||
, LambdaCase
|
||||
, DefaultSignatures
|
||||
, EmptyDataDeriving
|
||||
, BangPatterns
|
||||
, FunctionalDependencies
|
||||
, StandaloneDeriving
|
||||
, DerivingVia
|
||||
1
Application/Fixtures.sql
Normal file
1
Application/Fixtures.sql
Normal file
@@ -0,0 +1 @@
|
||||
-- No fixtures for probe.
|
||||
7
Application/Schema.sql
Normal file
7
Application/Schema.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
||||
CREATE TABLE probes (
|
||||
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
|
||||
);
|
||||
9
Config/Config.hs
Normal file
9
Config/Config.hs
Normal file
@@ -0,0 +1,9 @@
|
||||
module Config where
|
||||
|
||||
import IHP.Prelude
|
||||
import IHP.Environment
|
||||
import IHP.FrameworkConfig
|
||||
|
||||
config :: ConfigBuilder
|
||||
config = do
|
||||
pure ()
|
||||
21
Main.hs
Normal file
21
Main.hs
Normal file
@@ -0,0 +1,21 @@
|
||||
module Main where
|
||||
import IHP.Prelude
|
||||
|
||||
import Config
|
||||
import qualified IHP.Server
|
||||
import IHP.RouterSupport
|
||||
import IHP.FrameworkConfig
|
||||
import IHP.Job.Types
|
||||
import Web.FrontController
|
||||
import Web.Types
|
||||
|
||||
instance FrontController RootApplication where
|
||||
controllers =
|
||||
[ mountFrontController WebApplication
|
||||
]
|
||||
|
||||
instance Worker RootApplication where
|
||||
workers _ = []
|
||||
|
||||
main :: IO ()
|
||||
main = IHP.Server.run config
|
||||
16
Makefile
Normal file
16
Makefile
Normal file
@@ -0,0 +1,16 @@
|
||||
CSS_FILES += ${IHP}/static/vendor/bootstrap.min.css
|
||||
CSS_FILES += ${IHP}/static/vendor/flatpickr.min.css
|
||||
CSS_FILES += static/app.css
|
||||
|
||||
JS_FILES += ${IHP}/static/vendor/jquery-3.6.0.slim.min.js
|
||||
JS_FILES += ${IHP}/static/vendor/timeago.js
|
||||
JS_FILES += ${IHP}/static/vendor/popper.min.js
|
||||
JS_FILES += ${IHP}/static/vendor/bootstrap.min.js
|
||||
JS_FILES += ${IHP}/static/vendor/flatpickr.js
|
||||
JS_FILES += ${IHP}/static/helpers.js
|
||||
JS_FILES += ${IHP}/static/vendor/morphdom-umd.min.js
|
||||
JS_FILES += ${IHP}/static/vendor/turbolinks.js
|
||||
JS_FILES += ${IHP}/static/vendor/turbolinksInstantClick.js
|
||||
JS_FILES += ${IHP}/static/vendor/turbolinksMorphdom.js
|
||||
|
||||
include ${IHP}/Makefile.dist
|
||||
4
PIPELINE_LOG.md
Normal file
4
PIPELINE_LOG.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Pipeline Validation Log
|
||||
|
||||
| Date | SHA | Build | Push | Deploy | Smoke |
|
||||
|------|-----|-------|------|--------|-------|
|
||||
13
Test/Main.hs
Normal file
13
Test/Main.hs
Normal file
@@ -0,0 +1,13 @@
|
||||
module Main where
|
||||
|
||||
import Test.Hspec
|
||||
import IHP.Prelude
|
||||
import qualified Test.ProbeControllerSpec as ProbeController
|
||||
|
||||
main :: IO ()
|
||||
main = hspec do
|
||||
describe "Sanity" do
|
||||
it "1 + 1 = 2" do
|
||||
1 + 1 `shouldBe` (2 :: Int)
|
||||
|
||||
ProbeController.spec
|
||||
14
Test/ProbeControllerSpec.hs
Normal file
14
Test/ProbeControllerSpec.hs
Normal file
@@ -0,0 +1,14 @@
|
||||
module Test.ProbeControllerSpec where
|
||||
|
||||
import Test.Hspec
|
||||
import IHP.HSpec
|
||||
|
||||
spec :: Spec
|
||||
spec = describe "ProbeController" do
|
||||
it "GET /probes returns 200" do
|
||||
response <- get "/probes"
|
||||
response `shouldRespondWith` 200
|
||||
|
||||
it "GET /healthz returns 200" do
|
||||
response <- get "/healthz"
|
||||
response `shouldRespondWith` 200
|
||||
7
Web/Controller/Health.hs
Normal file
7
Web/Controller/Health.hs
Normal file
@@ -0,0 +1,7 @@
|
||||
module Web.Controller.Health where
|
||||
|
||||
import Web.Controller.Prelude
|
||||
import Web.Types
|
||||
|
||||
instance Controller HealthController where
|
||||
action HealthAction = renderPlain "ok"
|
||||
12
Web/Controller/Prelude.hs
Normal file
12
Web/Controller/Prelude.hs
Normal file
@@ -0,0 +1,12 @@
|
||||
module Web.Controller.Prelude
|
||||
( module Web.Types
|
||||
, module Generated.Types
|
||||
, module IHP.Prelude
|
||||
, module IHP.ControllerPrelude
|
||||
) where
|
||||
|
||||
import Web.Types
|
||||
import Generated.Types
|
||||
import IHP.Prelude
|
||||
import IHP.ControllerPrelude
|
||||
import Web.Routes ()
|
||||
42
Web/Controller/Probes.hs
Normal file
42
Web/Controller/Probes.hs
Normal file
@@ -0,0 +1,42 @@
|
||||
module Web.Controller.Probes where
|
||||
|
||||
import Web.Controller.Prelude
|
||||
import Web.View.Probes.Index
|
||||
import Web.View.Probes.New
|
||||
import Web.View.Probes.Show
|
||||
import Web.View.Probes.Edit
|
||||
|
||||
instance Controller ProbesController where
|
||||
action ProbesAction = do
|
||||
probes <- query @Probe |> fetch
|
||||
render IndexView { .. }
|
||||
|
||||
action NewProbeAction = do
|
||||
let probe = newRecord @Probe
|
||||
render NewView { .. }
|
||||
|
||||
action ShowProbeAction { probeId } = do
|
||||
probe <- fetch probeId
|
||||
render ShowView { .. }
|
||||
|
||||
action CreateProbeAction = do
|
||||
newRecord @Probe
|
||||
|> fill @'["name"]
|
||||
|> createRecord
|
||||
redirectTo ProbesAction
|
||||
|
||||
action EditProbeAction { probeId } = do
|
||||
probe <- fetch probeId
|
||||
render EditView { .. }
|
||||
|
||||
action UpdateProbeAction { probeId } = do
|
||||
probe <- fetch probeId
|
||||
probe
|
||||
|> fill @'["name"]
|
||||
|> updateRecord
|
||||
redirectTo ProbesAction
|
||||
|
||||
action DeleteProbeAction { probeId } = do
|
||||
probe <- fetch probeId
|
||||
deleteRecord probe
|
||||
redirectTo ProbesAction
|
||||
36
Web/FrontController.hs
Normal file
36
Web/FrontController.hs
Normal file
@@ -0,0 +1,36 @@
|
||||
module Web.FrontController where
|
||||
|
||||
import IHP.RouterPrelude
|
||||
import IHP.ControllerPrelude
|
||||
import IHP.ViewPrelude (Html, hsx, Layout)
|
||||
import Generated.Types
|
||||
import Web.Types
|
||||
import Web.Routes ()
|
||||
|
||||
import Web.Controller.Health ()
|
||||
import Web.Controller.Probes ()
|
||||
|
||||
instance FrontController WebApplication where
|
||||
controllers =
|
||||
[ parseRoute @HealthController
|
||||
, parseRoute @ProbesController
|
||||
]
|
||||
|
||||
instance InitControllerContext WebApplication where
|
||||
initContext = do
|
||||
setLayout defaultLayout
|
||||
|
||||
defaultLayout :: (?context :: ControllerContext, ?request :: Request) => Layout
|
||||
defaultLayout inner = [hsx|
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>ihp-railiance-probe</title>
|
||||
</head>
|
||||
<body>
|
||||
{inner}
|
||||
</body>
|
||||
</html>
|
||||
|]
|
||||
16
Web/Routes.hs
Normal file
16
Web/Routes.hs
Normal file
@@ -0,0 +1,16 @@
|
||||
module Web.Routes where
|
||||
|
||||
import IHP.RouterPrelude
|
||||
import Generated.Types
|
||||
import Web.Types
|
||||
|
||||
instance CanRoute HealthController where
|
||||
parseRoute' = do
|
||||
pathPrefix "/healthz"
|
||||
endOfInput
|
||||
pure HealthAction
|
||||
|
||||
instance HasPath HealthController where
|
||||
pathTo HealthAction = "/healthz"
|
||||
|
||||
instance AutoRoute ProbesController
|
||||
21
Web/Types.hs
Normal file
21
Web/Types.hs
Normal file
@@ -0,0 +1,21 @@
|
||||
module Web.Types where
|
||||
|
||||
import IHP.Prelude
|
||||
import IHP.ModelSupport
|
||||
import Generated.Types
|
||||
|
||||
data WebApplication = WebApplication deriving (Eq, Show)
|
||||
|
||||
data HealthController
|
||||
= HealthAction
|
||||
deriving (Eq, Show, Data)
|
||||
|
||||
data ProbesController
|
||||
= ProbesAction
|
||||
| NewProbeAction
|
||||
| ShowProbeAction { probeId :: !(Id Probe) }
|
||||
| CreateProbeAction
|
||||
| EditProbeAction { probeId :: !(Id Probe) }
|
||||
| UpdateProbeAction { probeId :: !(Id Probe) }
|
||||
| DeleteProbeAction { probeId :: !(Id Probe) }
|
||||
deriving (Eq, Show, Data)
|
||||
12
Web/View/Prelude.hs
Normal file
12
Web/View/Prelude.hs
Normal file
@@ -0,0 +1,12 @@
|
||||
module Web.View.Prelude
|
||||
( module Web.Types
|
||||
, module Generated.Types
|
||||
, module IHP.Prelude
|
||||
, module IHP.ViewPrelude
|
||||
) where
|
||||
|
||||
import Web.Types
|
||||
import Generated.Types
|
||||
import IHP.Prelude
|
||||
import IHP.ViewPrelude
|
||||
import Web.Routes ()
|
||||
17
Web/View/Probes/Edit.hs
Normal file
17
Web/View/Probes/Edit.hs
Normal file
@@ -0,0 +1,17 @@
|
||||
module Web.View.Probes.Edit where
|
||||
|
||||
import Web.View.Prelude
|
||||
|
||||
data EditView = EditView { probe :: Probe }
|
||||
|
||||
instance View EditView where
|
||||
html EditView { .. } = [hsx|
|
||||
<h1>Edit Probe</h1>
|
||||
{renderForm probe}
|
||||
|]
|
||||
|
||||
renderForm :: Probe -> Html
|
||||
renderForm probe = formFor probe [hsx|
|
||||
{textField #name}
|
||||
{submitButton}
|
||||
|]
|
||||
22
Web/View/Probes/Index.hs
Normal file
22
Web/View/Probes/Index.hs
Normal file
@@ -0,0 +1,22 @@
|
||||
module Web.View.Probes.Index where
|
||||
|
||||
import Web.View.Prelude
|
||||
|
||||
data IndexView = IndexView { probes :: [Probe] }
|
||||
|
||||
instance View IndexView where
|
||||
html IndexView { .. } = [hsx|
|
||||
<h1>Probes</h1>
|
||||
<a href={NewProbeAction}>New Probe</a>
|
||||
<ul>
|
||||
{forEach probes renderProbe}
|
||||
</ul>
|
||||
|]
|
||||
|
||||
renderProbe :: Probe -> Html
|
||||
renderProbe probe = [hsx|
|
||||
<li>
|
||||
<a href={ShowProbeAction probe.id}>{probe.name}</a>
|
||||
<a href={DeleteProbeAction probe.id} class="js-delete">Delete</a>
|
||||
</li>
|
||||
|]
|
||||
17
Web/View/Probes/New.hs
Normal file
17
Web/View/Probes/New.hs
Normal file
@@ -0,0 +1,17 @@
|
||||
module Web.View.Probes.New where
|
||||
|
||||
import Web.View.Prelude
|
||||
|
||||
data NewView = NewView { probe :: Probe }
|
||||
|
||||
instance View NewView where
|
||||
html NewView { .. } = [hsx|
|
||||
<h1>New Probe</h1>
|
||||
{renderForm probe}
|
||||
|]
|
||||
|
||||
renderForm :: Probe -> Html
|
||||
renderForm probe = formFor probe [hsx|
|
||||
{textField #name}
|
||||
{submitButton}
|
||||
|]
|
||||
11
Web/View/Probes/Show.hs
Normal file
11
Web/View/Probes/Show.hs
Normal file
@@ -0,0 +1,11 @@
|
||||
module Web.View.Probes.Show where
|
||||
|
||||
import Web.View.Prelude
|
||||
|
||||
data ShowView = ShowView { probe :: Probe }
|
||||
|
||||
instance View ShowView where
|
||||
html ShowView { .. } = [hsx|
|
||||
<h1>{probe.name}</h1>
|
||||
<a href={ProbesAction}>Back</a>
|
||||
|]
|
||||
6
chart/Chart.yaml
Normal file
6
chart/Chart.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
apiVersion: v2
|
||||
name: ihp-railiance-probe
|
||||
description: Minimal IHP probe app for Railiance01 pipeline validation
|
||||
type: application
|
||||
version: 0.1.0
|
||||
appVersion: "0.1.0"
|
||||
38
chart/templates/deployment.yaml
Normal file
38
chart/templates/deployment.yaml
Normal file
@@ -0,0 +1,38 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Release.Name }}
|
||||
labels:
|
||||
app: {{ .Release.Name }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{ .Release.Name }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: {{ .Release.Name }}
|
||||
spec:
|
||||
containers:
|
||||
- name: {{ .Release.Name }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: {{ .Values.secretName }}
|
||||
resources:
|
||||
limits:
|
||||
memory: {{ .Values.resources.limits.memory }}
|
||||
cpu: {{ .Values.resources.limits.cpu }}
|
||||
requests:
|
||||
memory: {{ .Values.resources.requests.memory }}
|
||||
cpu: {{ .Values.resources.requests.cpu }}
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8000
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
20
chart/templates/ingress.yaml
Normal file
20
chart/templates/ingress.yaml
Normal file
@@ -0,0 +1,20 @@
|
||||
{{- if .Values.ingress.enabled }}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ .Release.Name }}
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: traefik
|
||||
spec:
|
||||
rules:
|
||||
- host: {{ .Values.ingress.host }}
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: {{ .Release.Name }}
|
||||
port:
|
||||
number: {{ .Values.service.port }}
|
||||
{{- end }}
|
||||
11
chart/templates/secret.yaml
Normal file
11
chart/templates/secret.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ .Values.secretName }}
|
||||
annotations:
|
||||
"helm.sh/resource-policy": keep
|
||||
type: Opaque
|
||||
stringData:
|
||||
IHP_SESSION_SECRET: "REPLACE_ME"
|
||||
DATABASE_URL: "REPLACE_ME"
|
||||
IHP_BASEURL: "http://{{ .Values.ingress.host }}"
|
||||
11
chart/templates/service.yaml
Normal file
11
chart/templates/service.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Release.Name }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
selector:
|
||||
app: {{ .Release.Name }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: {{ .Values.service.targetPort }}
|
||||
25
chart/values.yaml
Normal file
25
chart/values.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
image:
|
||||
repository: 92.205.130.254:32166/coulomb/ihp-railiance-probe
|
||||
tag: latest
|
||||
pullPolicy: Always
|
||||
|
||||
replicaCount: 1
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
targetPort: 8000
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
host: probe.coulomb.social
|
||||
|
||||
resources:
|
||||
limits:
|
||||
memory: 256Mi
|
||||
cpu: 200m
|
||||
requests:
|
||||
memory: 128Mi
|
||||
cpu: 50m
|
||||
|
||||
secretName: ihp-railiance-probe-env
|
||||
9
default.nix
Normal file
9
default.nix
Normal file
@@ -0,0 +1,9 @@
|
||||
# For backwards compatibility using flake.nix
|
||||
(import
|
||||
(
|
||||
fetchTarball {
|
||||
url = "https://github.com/edolstra/flake-compat/archive/35bb57c0c8d8b62bbfd284272c928ceb64ddbde9.tar.gz";
|
||||
sha256 = "sha256:1prd9b1xx8c0sfwnyzkspplh30m613j42l1k789s521f4kv4c2z2";
|
||||
}
|
||||
)
|
||||
{ src = ./.; }).defaultNix
|
||||
24
devenv.nix
Normal file
24
devenv.nix
Normal file
@@ -0,0 +1,24 @@
|
||||
{ pkgs, lib, config, inputs, ... }:
|
||||
{
|
||||
env.GHCRTS = "-A32m -M2g";
|
||||
|
||||
env.IHP_LIB = "${inputs.ihp.packages.${pkgs.stdenv.system}.ihp-env-var-backwards-compat}";
|
||||
env.IHP = "${inputs.ihp.packages.${pkgs.stdenv.system}.ihp-env-var-backwards-compat}";
|
||||
|
||||
languages.haskell.enable = true;
|
||||
languages.haskell.package = pkgs.ghc.ghc.withPackages (p: with p; [
|
||||
p.ihp
|
||||
cabal-install
|
||||
hlint
|
||||
hspec
|
||||
p.ihp-hspec
|
||||
]);
|
||||
languages.haskell.lsp.enable = false;
|
||||
|
||||
packages = [
|
||||
pkgs.ghc.ghcid
|
||||
pkgs.nodePackages.tailwindcss
|
||||
];
|
||||
|
||||
processes.tailwind.exec = "tailwindcss -c tailwind/tailwind.config.js -i ./tailwind/app.css -o static/app.css --watch=always";
|
||||
}
|
||||
5
devenv.yaml
Normal file
5
devenv.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
inputs:
|
||||
ihp:
|
||||
url: github:digitallyinduced/ihp/df3922d1a7166b131674efa3d3555ed7195ddf70
|
||||
overlays:
|
||||
- default
|
||||
118
flake.nix
Normal file
118
flake.nix
Normal file
@@ -0,0 +1,118 @@
|
||||
{
|
||||
inputs = {
|
||||
ihp.url = "github:digitallyinduced/ihp/v1.5";
|
||||
nixpkgs.follows = "ihp/nixpkgs";
|
||||
nixpkgs-nixos.follows = "ihp/nixpkgs-nixos";
|
||||
flake-parts.follows = "ihp/flake-parts";
|
||||
devenv.follows = "ihp/devenv";
|
||||
systems.follows = "ihp/systems";
|
||||
devenv-root = {
|
||||
url = "file+file:///dev/null";
|
||||
flake = false;
|
||||
};
|
||||
};
|
||||
|
||||
outputs = inputs@{ self, nixpkgs, nixpkgs-nixos, ihp, flake-parts, systems, ... }:
|
||||
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||
|
||||
systems = import systems;
|
||||
imports = [ ihp.flakeModules.default ];
|
||||
|
||||
perSystem = { pkgs, config, lib, ... }: {
|
||||
ihp = {
|
||||
appName = "ihp-railiance-probe";
|
||||
enable = true;
|
||||
projectPath = ./.;
|
||||
packages = with pkgs; [
|
||||
tailwindcss
|
||||
];
|
||||
haskellPackages = p: with p; [
|
||||
p.ihp
|
||||
base
|
||||
wai
|
||||
text
|
||||
];
|
||||
devHaskellPackages = p: with p; [
|
||||
cabal-install
|
||||
hlint
|
||||
hspec
|
||||
ihp-hspec
|
||||
];
|
||||
|
||||
withHoogle = false;
|
||||
};
|
||||
|
||||
# OCI container image for Kubernetes deployment.
|
||||
# Build: nix build .#docker
|
||||
# Push: skopeo copy docker-archive:result docker://92.205.130.254:32166/coulomb/ihp-railiance-probe:SHA
|
||||
packages.docker = config.packages.unoptimized-docker-image;
|
||||
|
||||
devenv.shells.default = {
|
||||
overlays = lib.mkAfter [
|
||||
(final: prev: {
|
||||
ghc = prev.ghc.extend (hfinal: hprev: {
|
||||
mkDerivation = args:
|
||||
let drv = hprev.mkDerivation args;
|
||||
in if (args.pname or "") == "ihp-railiance-probe-models"
|
||||
then drv.overrideAttrs (old: {
|
||||
# GHC 9.10.3 Bug 1: Generated.ActualTypes.hi overflow.
|
||||
# Bug 2: libHSghc-9.10.3-5702.a truncated — use ghc-iserv-dyn.
|
||||
configureFlags = (old.configureFlags or []) ++ [
|
||||
"--ghc-option=-O0"
|
||||
"--ghc-option=-fomit-interface-pragmas"
|
||||
"--disable-split-sections"
|
||||
"--ghc-option=-j1"
|
||||
"--ghc-option=-fexternal-interpreter"
|
||||
"--ghc-option=-pgmi"
|
||||
"--ghc-option=${hprev.ghc}/lib/ghc-9.10.3/bin/ghc-iserv-dyn"
|
||||
];
|
||||
postUnpack = (old.postUnpack or "") + ''
|
||||
_actual="$sourceRoot/build/Generated/ActualTypes.hs"
|
||||
_types=$(
|
||||
{
|
||||
awk '/^data [A-Z]|^newtype [A-Z]/{print $2"(..)"}
|
||||
/^type [A-Z]/{print $2}' \
|
||||
"$sourceRoot/build/Generated/Enums.hs"
|
||||
find "$sourceRoot/build/Generated/ActualTypes" -name "*.hs" | \
|
||||
sort | while IFS= read -r _m; do
|
||||
awk '/^data [A-Z]|^newtype [A-Z]/{print $2"(..)"}
|
||||
/^type [A-Z]/{print $2}' "$_m"
|
||||
done
|
||||
} | sort -u
|
||||
)
|
||||
_exports=$(echo "$_types" | \
|
||||
awk 'NR==1{printf " %s", $0; next} {printf "\n , %s", $0} END{printf "\n"}')
|
||||
_imports=$(awk '/^import Generated\./{print}' "$_actual")
|
||||
{
|
||||
printf 'module Generated.ActualTypes\n ( %s ) where\n' "$_exports"
|
||||
printf '%s\n' "$_imports"
|
||||
} > "$_actual.new" && mv "$_actual.new" "$_actual"
|
||||
'';
|
||||
})
|
||||
else drv;
|
||||
});
|
||||
})
|
||||
];
|
||||
|
||||
env.GHCRTS = "-A32m -M2g";
|
||||
|
||||
processes = {
|
||||
tailwind.exec = "tailwindcss -c tailwind/tailwind.config.js -i ./tailwind/app.css -o static/app.css --watch=always";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
nixConfig = {
|
||||
extra-substituters = [
|
||||
"https://devenv.cachix.org"
|
||||
"https://cachix.cachix.org"
|
||||
"https://digitallyinduced.cachix.org"
|
||||
];
|
||||
extra-trusted-public-keys = [
|
||||
"devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw="
|
||||
"cachix.cachix.org-1:eWNHQldwUO7G2VkjpnjDbWwy4KQ/HNxht7H4SSoMckM="
|
||||
"digitallyinduced.cachix.org-1:y+wQvrnxQ+PdEsCt91rmvv39qRCYzEgGQaldK26hCKE="
|
||||
];
|
||||
};
|
||||
}
|
||||
0
static/.gitkeep
Normal file
0
static/.gitkeep
Normal file
3
tailwind/app.css
Normal file
3
tailwind/app.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
11
tailwind/tailwind.config.js
Normal file
11
tailwind/tailwind.config.js
Normal file
@@ -0,0 +1,11 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./Web/View/**/*.hs",
|
||||
"./Web/FrontController.hs",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
Reference in New Issue
Block a user