ci!: deploy workflow
This commit is contained in:
@@ -0,0 +1,62 @@
|
|||||||
|
---
|
||||||
|
# yamllint disable rule:line-length
|
||||||
|
name: deploy neuro
|
||||||
|
|
||||||
|
on: # yamllint disable-line rule:truthy
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: deploy-neuro
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
name: deploy neuro from performance node
|
||||||
|
if: ${{ gitea.ref == 'refs/heads/master' }}
|
||||||
|
runs-on: gross-nix-x86-perf
|
||||||
|
timeout-minutes: 30
|
||||||
|
env:
|
||||||
|
NIX_CONFIG: |
|
||||||
|
extra-substituters = https://cache.nixos.org https://cache.hectic-lab.com/hectic
|
||||||
|
extra-trusted-public-keys = hectic:KMQsKow4SoA9K2vOJlOljmx7/Zpf91Yy+5qEtxDDCzA=
|
||||||
|
DEPLOY_REVISION: ${{ gitea.sha }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout dispatched revision
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af6832 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ gitea.sha }}
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Show runner and cache configuration
|
||||||
|
run: |
|
||||||
|
set -eu
|
||||||
|
uname -a
|
||||||
|
nix --version
|
||||||
|
nix config show substituters
|
||||||
|
nix config show trusted-public-keys
|
||||||
|
|
||||||
|
- name: Deploy neuro
|
||||||
|
env:
|
||||||
|
NEURO_SSH_PRIVATE_KEY: ${{ secrets.NEURO_SSH_PRIVATE_KEY }}
|
||||||
|
NEURO_SSH_KNOWN_HOSTS: ${{ secrets.NEURO_SSH_KNOWN_HOSTS }}
|
||||||
|
run: |
|
||||||
|
set -eu
|
||||||
|
test -n "$NEURO_SSH_PRIVATE_KEY"
|
||||||
|
test -n "$NEURO_SSH_KNOWN_HOSTS"
|
||||||
|
ssh_home=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$ssh_home"' EXIT
|
||||||
|
install -d -m 700 "$ssh_home/.ssh"
|
||||||
|
printf '%s\n' "$NEURO_SSH_PRIVATE_KEY" > "$ssh_home/.ssh/id_ed25519"
|
||||||
|
printf '%s\n' "$NEURO_SSH_KNOWN_HOSTS" > "$ssh_home/.ssh/known_hosts"
|
||||||
|
chmod 600 "$ssh_home/.ssh/id_ed25519" "$ssh_home/.ssh/known_hosts"
|
||||||
|
export HOME="$ssh_home"
|
||||||
|
export NIX_SSHOPTS="-o BatchMode=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile=$HOME/.ssh/known_hosts -o IdentitiesOnly=yes -i $HOME/.ssh/id_ed25519"
|
||||||
|
ssh -o BatchMode=yes -o ConnectTimeout=10 \
|
||||||
|
-o StrictHostKeyChecking=yes \
|
||||||
|
-o UserKnownHostsFile="$HOME/.ssh/known_hosts" \
|
||||||
|
-o IdentitiesOnly=yes \
|
||||||
|
-i "$HOME/.ssh/id_ed25519" \
|
||||||
|
neuro true
|
||||||
|
# Deploy tool intentionally uses requested GitHub flake; revision matches checkout.
|
||||||
|
nix run --refresh "github:hectic-lab/util.nix/${DEPLOY_REVISION}#deploy" -- \
|
||||||
|
push -- --flake '.#neuro|x86_64-linux' --target-host neuro --use-remote-sudo
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
{ ... }: {
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
cfg = config.hectic.services.dify;
|
||||||
|
|
||||||
|
difySource = pkgs.fetchFromGitHub {
|
||||||
|
owner = "langgenius";
|
||||||
|
repo = "dify";
|
||||||
|
rev = "00e578606715a9da34488608edee8c68d4ef4893";
|
||||||
|
hash = "sha256-kLxMdmt1FtOl39C9SS7ZGY6C78e3hPiVI5Q3EdbkMPU=";
|
||||||
|
};
|
||||||
|
|
||||||
|
composeOverride = pkgs.writeText "dify-compose.override.yaml" ''
|
||||||
|
services:
|
||||||
|
nginx:
|
||||||
|
ports: !override
|
||||||
|
- "127.0.0.1:${toString cfg.port}:80"
|
||||||
|
plugin_daemon:
|
||||||
|
ports: !override
|
||||||
|
- "127.0.0.1:${toString cfg.pluginPort}:5003"
|
||||||
|
'';
|
||||||
|
in {
|
||||||
|
options.hectic.services.dify = {
|
||||||
|
enable = lib.mkEnableOption "Dify self-hosted AI platform";
|
||||||
|
|
||||||
|
dataDir = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
default = "/var/lib/dify";
|
||||||
|
description = "Persistent directory for Dify compose state and volumes.";
|
||||||
|
};
|
||||||
|
|
||||||
|
environmentFile = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
description = ''
|
||||||
|
Environment file for Dify. Keep secrets here, including SECRET_KEY,
|
||||||
|
DB_PASSWORD, REDIS_PASSWORD, and plugin daemon credentials.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
port = lib.mkOption {
|
||||||
|
type = lib.types.port;
|
||||||
|
default = 80;
|
||||||
|
description = "Loopback HTTP port forwarded to Dify through SSH.";
|
||||||
|
};
|
||||||
|
|
||||||
|
pluginPort = lib.mkOption {
|
||||||
|
type = lib.types.port;
|
||||||
|
default = 5003;
|
||||||
|
description = "Loopback plugin daemon port forwarded through SSH when needed.";
|
||||||
|
};
|
||||||
|
|
||||||
|
composeProfiles = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "weaviate,postgresql,collaboration";
|
||||||
|
description = "Dify Docker Compose profiles to start.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
virtualisation.docker.enable = true;
|
||||||
|
|
||||||
|
systemd.services.dify = {
|
||||||
|
description = "Dify Docker Compose stack";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
wants = [ "docker.service" ];
|
||||||
|
after = [ "docker.service" ];
|
||||||
|
requires = [ "docker.service" ];
|
||||||
|
restartIfChanged = true;
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
WorkingDirectory = cfg.dataDir;
|
||||||
|
ExecStartPre = pkgs.writeShellScript "dify-prepare" ''
|
||||||
|
set -eu
|
||||||
|
install -d -m 0750 ${lib.escapeShellArg cfg.dataDir}
|
||||||
|
cp -R ${difySource}/docker/. ${lib.escapeShellArg cfg.dataDir}/
|
||||||
|
install -m 0600 ${lib.escapeShellArg cfg.environmentFile} ${lib.escapeShellArg cfg.dataDir}/.env
|
||||||
|
'';
|
||||||
|
ExecStart = "${pkgs.docker-compose}/bin/docker-compose --project-directory ${cfg.dataDir} --file ${cfg.dataDir}/docker-compose.yaml --file ${composeOverride} --env-file ${cfg.dataDir}/.env up --detach";
|
||||||
|
ExecStop = "${pkgs.docker-compose}/bin/docker-compose --project-directory ${cfg.dataDir} --file ${cfg.dataDir}/docker-compose.yaml --file ${composeOverride} --env-file ${cfg.dataDir}/.env down";
|
||||||
|
};
|
||||||
|
environment = {
|
||||||
|
COMPOSE_PROFILES = cfg.composeProfiles;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d ${cfg.dataDir} 0750 root root -"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -194,6 +194,13 @@ in {
|
|||||||
openFirewall = false;
|
openFirewall = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
hectic.services.dify = {
|
||||||
|
enable = true;
|
||||||
|
environmentFile = config.sops.secrets."dify/environment".path;
|
||||||
|
port = 8080;
|
||||||
|
pluginPort = 5003;
|
||||||
|
};
|
||||||
|
|
||||||
networking = {
|
networking = {
|
||||||
networkmanager.enable = true;
|
networkmanager.enable = true;
|
||||||
useDHCP = lib.mkDefault true;
|
useDHCP = lib.mkDefault true;
|
||||||
@@ -229,6 +236,7 @@ in {
|
|||||||
# group = "turnserver";
|
# group = "turnserver";
|
||||||
# mode = "0400";
|
# mode = "0400";
|
||||||
#};
|
#};
|
||||||
|
secrets."dify/environment" = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
boot.loader.systemd-boot.enable = true;
|
boot.loader.systemd-boot.enable = true;
|
||||||
|
|||||||
@@ -7,8 +7,12 @@ HECTIC_NAMESPACE="deploy"
|
|||||||
|
|
||||||
# ssh that not saves the host in ~/.ssh/know_hosts
|
# ssh that not saves the host in ~/.ssh/know_hosts
|
||||||
puressh() {
|
puressh() {
|
||||||
|
local known_hosts="${HECTIC_DEPLOY_KNOWN_HOSTS:-$HOME/.ssh/known_hosts}"
|
||||||
|
local identity_file="${HECTIC_DEPLOY_IDENTITY_FILE:-$HOME/.ssh/id_ed25519}"
|
||||||
# shellcheck disable=SC2068
|
# shellcheck disable=SC2068
|
||||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $@
|
ssh -o BatchMode=yes -o StrictHostKeyChecking=yes \
|
||||||
|
-o UserKnownHostsFile="$known_hosts" -o IdentitiesOnly=yes \
|
||||||
|
-i "$identity_file" $@
|
||||||
}
|
}
|
||||||
|
|
||||||
# echo <gens_list> | find_older_gen(gen)
|
# echo <gens_list> | find_older_gen(gen)
|
||||||
|
|||||||
+4
-2
@@ -2,6 +2,8 @@ init-postgresql: ENC[AES256_GCM,data:JUoZk/A3b3xCDWPKt08Uy7PjSmBd3BQqn+uymiBKTA7
|
|||||||
matrix:
|
matrix:
|
||||||
secrets: ENC[AES256_GCM,data:vL5opJTCzQMQwhxGZm1a2K4lTFySYHAj92EW4JQSKpt3yWQAu3QHLHdt9YGOvb9/c/0APouoc+NeYWKNC5oYcih2H8z4wGfYPImn+bH7OCzr/fAGNxgOlG27cWRbkzkIpbNE7qtll0GPpEL+BsdAklzbZcgMp9s2LvQKYEovrWronBksuTI5sbOtZtoAY40yuYmnuH/Sjp+6az9KlX3fuCrb/HF6UUsPoRz98cYVPcpGiNImb64WwPGUkvDemDzKSLHB9ClUMLWuWJHumameMAKE236bz+Jqlrz5rVhbL9E1oadyw1JgQWlgeA3axKw3Ju+sThalwxQXiW3pZJPGFAmwOoNY6LiH4WBkXnfPlTY7m5f/+6FjFALUFTKHWr+iZ3S1ykUrwJGxCsK6ARBUH7U1CY6fU+fLRxjuyDogGpe7O+TcntxCKHqB/UpNjX1BDbpakOoa,iv:JR2pVrakbhDakRQDC2dfdtB1C2HneJcAtFuHClTw5gU=,tag:OYQYO22o6eKpyMrfZ/VmvA==,type:str]
|
secrets: ENC[AES256_GCM,data:vL5opJTCzQMQwhxGZm1a2K4lTFySYHAj92EW4JQSKpt3yWQAu3QHLHdt9YGOvb9/c/0APouoc+NeYWKNC5oYcih2H8z4wGfYPImn+bH7OCzr/fAGNxgOlG27cWRbkzkIpbNE7qtll0GPpEL+BsdAklzbZcgMp9s2LvQKYEovrWronBksuTI5sbOtZtoAY40yuYmnuH/Sjp+6az9KlX3fuCrb/HF6UUsPoRz98cYVPcpGiNImb64WwPGUkvDemDzKSLHB9ClUMLWuWJHumameMAKE236bz+Jqlrz5rVhbL9E1oadyw1JgQWlgeA3axKw3Ju+sThalwxQXiW3pZJPGFAmwOoNY6LiH4WBkXnfPlTY7m5f/+6FjFALUFTKHWr+iZ3S1ykUrwJGxCsK6ARBUH7U1CY6fU+fLRxjuyDogGpe7O+TcntxCKHqB/UpNjX1BDbpakOoa,iv:JR2pVrakbhDakRQDC2dfdtB1C2HneJcAtFuHClTw5gU=,tag:OYQYO22o6eKpyMrfZ/VmvA==,type:str]
|
||||||
turn-secret: ENC[AES256_GCM,data:d2KxHlbeE1dehmbWy9KLKCT6UuC3bu7LaOeEQTIRTSk/etMpNL9gv2rHBmKQ/35en30PIjsAbQr3nMHLuI6uDA==,iv:Cjyb6LfdVbmlKqO16Cn7jSEy2j/7+xAz1y9UJjcpiuo=,tag:pX6RdGMxWLdP5QzqmS4tow==,type:str]
|
turn-secret: ENC[AES256_GCM,data:d2KxHlbeE1dehmbWy9KLKCT6UuC3bu7LaOeEQTIRTSk/etMpNL9gv2rHBmKQ/35en30PIjsAbQr3nMHLuI6uDA==,iv:Cjyb6LfdVbmlKqO16Cn7jSEy2j/7+xAz1y9UJjcpiuo=,tag:pX6RdGMxWLdP5QzqmS4tow==,type:str]
|
||||||
|
dify:
|
||||||
|
environment: ENC[AES256_GCM,data:MPK6v5qeNve6sZo/OVRlPJ52+m+vHd/L96BUUX18h7JO9ErJT5kAMbu86pp3WC3g8j1MOEOvt6ccciR2wmaerzABDR+2KANRKJeDii4BWKbBssFLwRMXKn8qPlDxenTE08UsieDEyW+iF+797+CniMGPpQuynSbhV5/7QWjT1fkOImbmqslNpo2x4QgQ0gLpZCAXHMpotDlrr25MgP9AHWBBjI42o2cFz8Rfz1gBb7opu6lhbmGwt+4VL8itxmlKxPgt5iyCrL9s9TR9Kyz1dPZQzrCWcqepD8IbjyeMi3F/RGEBKsll1NM=,iv:YvSK40NV95/xoEbVERDdT3LfY2mpvguvceKvxzcqBE4=,tag:UYGTojOoh0SYe2QmOx8Avw==,type:str]
|
||||||
sops:
|
sops:
|
||||||
age:
|
age:
|
||||||
- recipient: age1r25zdeqq8nac6dgca9en28r57ffyz9u9d8z5yc25gc8xqz747vaqmdtk0h
|
- recipient: age1r25zdeqq8nac6dgca9en28r57ffyz9u9d8z5yc25gc8xqz747vaqmdtk0h
|
||||||
@@ -22,7 +24,7 @@ sops:
|
|||||||
YWNZWGhKNnF6VUNsYnB6UlZFeis0dGsKVFEdSbZdnJhFrUTFrrXza43GcoCk/6m2
|
YWNZWGhKNnF6VUNsYnB6UlZFeis0dGsKVFEdSbZdnJhFrUTFrrXza43GcoCk/6m2
|
||||||
2qQyFPc/cQQ2xB7ygLfNDK4xKDkvEbKmmwAIttnDbX13qIWWxvblZw==
|
2qQyFPc/cQQ2xB7ygLfNDK4xKDkvEbKmmwAIttnDbX13qIWWxvblZw==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2026-03-02T20:48:15Z"
|
lastmodified: "2026-09-06T21:54:19Z"
|
||||||
mac: ENC[AES256_GCM,data:PYuxQRG0VIq6XmsdiH6JeC9XPPE084/keH7j/Os9qCtBZRP2b4d8yUcIFPYXpP3G9uLS55YvAOWMw9zlxpG2QM/TNwRxJ/3kziaA9sB6uA3o1DDxfhwZFiRcB5sGPB5Sh8bHYMMdk/s3KcipJ7FdSYrNn+/dGguDGAlSyruEMME=,iv:MJlGblf/VHvAr3jGRVvt0Nvr2hHRK724PiZ0wqdBtDY=,tag:Pg5mbqLG/nlvuS7Yse9iFg==,type:str]
|
mac: ENC[AES256_GCM,data:9Z7r0jp6hB54cO1/plMLx6Dmj3MDLTbnocQGv22g+HO/dF/rwxPXW+FOhamGjOTSyncAX6h9Pg2oIGVeFbKwP0dgoYloi5eq3XOsVdn2obNn7/1PNSGmqTqw1y1N8p29f/Iv+rRl9itofE974TvpCG2Jydz1HhQ8yx/YPDC5tTY=,iv:v1DDKxJX8R+vPCXO8jCxZcbgIRizxVUY3DPUmeaWKs0=,tag:vXYmW7kbXFYuHekGEzgmoA==,type:str]
|
||||||
unencrypted_suffix: _unencrypted
|
unencrypted_suffix: _unencrypted
|
||||||
version: 3.10.2
|
version: 3.10.2
|
||||||
|
|||||||
Reference in New Issue
Block a user