mars: give L.U.N.A. direct git+tea access to the homelab repo
Provisions a dedicated PR-tier gitea account (luna) with branch protection restricting master push/merge/approve to darman only, then wires git and tea directly into the hermes-agent container (mounted from the host's Nix store, credential-store + tea login set up by a host-side prepare oneshot, repo cloned inside Hermes's own writable sandbox root at /opt/data/workspace/homelab). Replaces an earlier standalone MCP-server approach, scrapped in favor of direct CLI access for simplicity. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011FHr5ug9pu8q4XPrRkFnzJ
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
{ config, ... }:
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
# Hermes Agent — moved here from jupiter (hosts/jupiter/hermes-agent.nix,
|
# Hermes Agent — moved here from jupiter (hosts/jupiter/hermes-agent.nix,
|
||||||
# see its git history / b5fa599 / 713d91d for the terra->jupiter->mars
|
# see its git history / b5fa599 / 713d91d for the terra->jupiter->mars
|
||||||
@@ -15,10 +15,13 @@
|
|||||||
# runtime — no redeploy needed except to bump the pinned digest below.
|
# runtime — no redeploy needed except to bump the pinned digest below.
|
||||||
#
|
#
|
||||||
# Security posture:
|
# Security posture:
|
||||||
# - Only two paths reachable: its own local state dir, and the small
|
# - Reachable paths: its own local state dir, the small shared "dropbox"
|
||||||
# shared "dropbox" below (via the jupiter samba mount) for darman to
|
# (via the jupiter samba mount) for darman to hand files to Hermes, and
|
||||||
# hand files to Hermes — nothing else on jupiter's array is reachable
|
# — new — a clone of THIS repo at ${workspaceDir}/homelab plus `git`/
|
||||||
# if a command goes wrong or gets injected via Telegram/tool output.
|
# `tea` (logged in as the `luna` gitea account, PR-tier only — see
|
||||||
|
# services/dev/gitea.nix). Nothing else on jupiter's array or the host
|
||||||
|
# is reachable if a command goes wrong or gets injected via
|
||||||
|
# Telegram/tool output.
|
||||||
# - Its own Telegram bot (own token, in secrets.nix) with an EXPLICIT
|
# - Its own Telegram bot (own token, in secrets.nix) with an EXPLICIT
|
||||||
# TELEGRAM_ALLOWED_USERS.
|
# TELEGRAM_ALLOWED_USERS.
|
||||||
# - Runs as a rootful podman container (services/containers.nix) with its
|
# - Runs as a rootful podman container (services/containers.nix) with its
|
||||||
@@ -26,6 +29,12 @@
|
|||||||
# host-level debugging only (`hermes ...` alias below, needs sudo since
|
# host-level debugging only (`hermes ...` alias below, needs sudo since
|
||||||
# the container itself runs under root's podman, not darman's rootless
|
# the container itself runs under root's podman, not darman's rootless
|
||||||
# one).
|
# one).
|
||||||
|
# - git/tea access is direct CLI, not a narrow wrapper: darman explicitly
|
||||||
|
# chose this over a purpose-built MCP server (tried first, scrapped —
|
||||||
|
# see git history) in favor of simplicity. The backstop is entirely
|
||||||
|
# server-side: gitea's branch protection on `master` (only darman can
|
||||||
|
# push/merge/approve there) is what actually keeps a bad or injected
|
||||||
|
# command from reaching the base branch, not anything client-side here.
|
||||||
#
|
#
|
||||||
# Dashboard (HERMES_DASHBOARD=1) is gated behind Authentik, same setup as on
|
# Dashboard (HERMES_DASHBOARD=1) is gated behind Authentik, same setup as on
|
||||||
# jupiter. Its default bind (0.0.0.0:9119) fails closed without an auth
|
# jupiter. Its default bind (0.0.0.0:9119) fails closed without an auth
|
||||||
@@ -69,6 +78,16 @@ let
|
|||||||
# change if state ever gets migrated over.
|
# change if state ever gets migrated over.
|
||||||
hermesUid = "986";
|
hermesUid = "986";
|
||||||
hermesGid = "983";
|
hermesGid = "983";
|
||||||
|
|
||||||
|
# luna's own working copy of this repo (git+PR account provisioned in
|
||||||
|
# services/dev/gitea.nix). Lives under hermesHome specifically so it falls
|
||||||
|
# inside HERMES_WRITE_SAFE_ROOT=/opt/data — Hermes's own file-editing
|
||||||
|
# tools can reach it the same way they reach anything else it manages,
|
||||||
|
# without a separate bind mount or sandbox root.
|
||||||
|
workspaceDir = "${hermesHome}/workspace";
|
||||||
|
repoDir = "${workspaceDir}/homelab";
|
||||||
|
giteaHost = "git.mgaction.town";
|
||||||
|
giteaRepo = "darman/homelab";
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
# Browsing convenience (ssh access to the bind-mounted local state) — does
|
# Browsing convenience (ssh access to the bind-mounted local state) — does
|
||||||
@@ -91,15 +110,55 @@ in
|
|||||||
# podman requires the bind-mount source to already exist (no auto-create),
|
# podman requires the bind-mount source to already exist (no auto-create),
|
||||||
# and the dropbox lives on the CIFS mount below — mkdir there works fine
|
# and the dropbox lives on the CIFS mount below — mkdir there works fine
|
||||||
# over cifs, no server-side (jupiter) config needed.
|
# over cifs, no server-side (jupiter) config needed.
|
||||||
|
#
|
||||||
|
# Also provisions luna's git/tea access: writes a git credential-store file
|
||||||
|
# and runs `tea logins add` INTO hermesHome (i.e. paths that appear at
|
||||||
|
# /opt/data/... once the container is up), and clones this repo if it
|
||||||
|
# isn't already there. All of this runs on the HOST as root, before the
|
||||||
|
# container starts — the container's own entrypoint is what fixes
|
||||||
|
# ownership to HERMES_UID/HERMES_GID on first boot (same mechanism
|
||||||
|
# already relied on for the rest of hermesHome; nothing new here).
|
||||||
|
#
|
||||||
|
# Delete-then-add for the tea login (not a "does it exist" check): tea can
|
||||||
|
# leave a login entry behind even when `add` reports failure (e.g. a token
|
||||||
|
# missing a scope errors out AFTER the entry is written — observed
|
||||||
|
# directly against the real instance during the first version of this
|
||||||
|
# setup). Delete-then-add is idempotent either way and picks up a rotated
|
||||||
|
# token for free.
|
||||||
systemd.services.hermes-agent-prepare-dirs = {
|
systemd.services.hermes-agent-prepare-dirs = {
|
||||||
description = "Create Hermes state dirs before the container starts";
|
description = "Create Hermes state dirs + luna's git/tea access before the container starts";
|
||||||
before = [ "podman-hermes-agent.service" ];
|
before = [ "podman-hermes-agent.service" ];
|
||||||
wantedBy = [ "podman-hermes-agent.service" ];
|
wantedBy = [ "podman-hermes-agent.service" ];
|
||||||
unitConfig.RequiresMountsFor = [ "/mnt/jupiter" ];
|
unitConfig.RequiresMountsFor = [ "/mnt/jupiter" ];
|
||||||
|
path = [ pkgs.git pkgs.tea ];
|
||||||
serviceConfig.Type = "oneshot";
|
serviceConfig.Type = "oneshot";
|
||||||
script = ''
|
script = ''
|
||||||
mkdir -p ${hermesHome}
|
mkdir -p ${hermesHome}
|
||||||
mkdir -p ${dropboxDir}
|
mkdir -p ${dropboxDir}
|
||||||
|
mkdir -p ${workspaceDir}
|
||||||
|
|
||||||
|
export HOME=${hermesHome}
|
||||||
|
export GIT_CONFIG_GLOBAL=${hermesHome}/.gitconfig
|
||||||
|
export XDG_CONFIG_HOME=${hermesHome}/.config
|
||||||
|
token_file=${config.sops.secrets.gitea_luna_token.path}
|
||||||
|
|
||||||
|
# Never embed the token in the remote URL (would land in
|
||||||
|
# repoDir/.git/config in plaintext) — the credential helper reads it
|
||||||
|
# from this file instead.
|
||||||
|
install -m 0600 /dev/null ${hermesHome}/.git-credentials
|
||||||
|
printf 'https://luna:%s@${giteaHost}\n' "$(cat "$token_file")" \
|
||||||
|
> ${hermesHome}/.git-credentials
|
||||||
|
git config --global credential.helper "store --file=${hermesHome}/.git-credentials"
|
||||||
|
git config --global user.name "luna"
|
||||||
|
git config --global user.email "luna@${giteaHost}"
|
||||||
|
|
||||||
|
if [ ! -d ${repoDir}/.git ]; then
|
||||||
|
git clone "https://${giteaHost}/${giteaRepo}.git" ${repoDir}
|
||||||
|
fi
|
||||||
|
|
||||||
|
tea logins delete luna 2>/dev/null || true
|
||||||
|
GITEA_SERVER_TOKEN="$(cat "$token_file")" tea logins add \
|
||||||
|
--name luna --url "https://${giteaHost}" --no-version-check
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -114,11 +173,30 @@ in
|
|||||||
volumes = [
|
volumes = [
|
||||||
"${hermesHome}:/opt/data"
|
"${hermesHome}:/opt/data"
|
||||||
"${dropboxDir}:/opt/data/dropbox"
|
"${dropboxDir}:/opt/data/dropbox"
|
||||||
|
|
||||||
|
# git/tea for luna: the image doesn't ship `tea` (and shouldn't be
|
||||||
|
# trusted to have a known-good `git` either), so both come from this
|
||||||
|
# host's Nix store instead — mounted read-only at fixed PATH-visible
|
||||||
|
# locations. /nix/store itself has to come along too since both
|
||||||
|
# binaries are dynamically linked against paths inside it; the store
|
||||||
|
# is read-only content-addressed build output, not a source of
|
||||||
|
# secrets, so mounting the whole thing read-only costs nothing beyond
|
||||||
|
# the two specific binaries actually being reachable.
|
||||||
|
"/nix/store:/nix/store:ro"
|
||||||
|
"${pkgs.git}/bin/git:/usr/local/bin/git:ro"
|
||||||
|
"${pkgs.tea}/bin/tea:/usr/local/bin/tea:ro"
|
||||||
];
|
];
|
||||||
environment = {
|
environment = {
|
||||||
HERMES_UID = hermesUid;
|
HERMES_UID = hermesUid;
|
||||||
HERMES_GID = hermesGid;
|
HERMES_GID = hermesGid;
|
||||||
TZ = "Europe/Berlin";
|
TZ = "Europe/Berlin";
|
||||||
|
|
||||||
|
# Point git/tea at the config the prepare-dirs oneshot wrote into
|
||||||
|
# hermesHome (visible here as /opt/data/...) — the credential-store
|
||||||
|
# helper, the luna gitea login, and (implicitly, via HOME not being
|
||||||
|
# overridden) darman's Hermes state stays wherever it already was.
|
||||||
|
GIT_CONFIG_GLOBAL = "/opt/data/.gitconfig";
|
||||||
|
XDG_CONFIG_HOME = "/opt/data/.config";
|
||||||
# HERMES_TIMEZONE is the highest-priority source hermes_time.py checks
|
# HERMES_TIMEZONE is the highest-priority source hermes_time.py checks
|
||||||
# (ahead of config.yaml's `timezone` key) — the container has no host
|
# (ahead of config.yaml's `timezone` key) — the container has no host
|
||||||
# /etc/localtime bind-mount, so it defaults to UTC otherwise (fixed in
|
# /etc/localtime bind-mount, so it defaults to UTC otherwise (fixed in
|
||||||
|
|||||||
@@ -35,4 +35,16 @@
|
|||||||
TELEGRAM_ALLOWED_USERS=15151223
|
TELEGRAM_ALLOWED_USERS=15151223
|
||||||
HERMES_DASHBOARD_OIDC_CLIENT_SECRET=${config.sops.placeholder.hermes_dashboard_oidc_client_secret}
|
HERMES_DASHBOARD_OIDC_CLIENT_SECRET=${config.sops.placeholder.hermes_dashboard_oidc_client_secret}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
# luna's own gitea push token (services/dev/gitea.nix provisions the
|
||||||
|
# account + PR-tier repo access on jupiter; this is the per-user token
|
||||||
|
# generated once via `gitea admin user generate-access-token --username
|
||||||
|
# luna --scopes write:repository,read:user` on jupiter — read:user is
|
||||||
|
# required, `tea logins add` fails without it). Read directly by
|
||||||
|
# hermes-agent.nix's prepare-dirs oneshot (default root:root owner is
|
||||||
|
# fine — that oneshot already runs as root) to set up a git
|
||||||
|
# credential-store file and a `tea` login, both written into hermesHome
|
||||||
|
# so they're visible inside the container at /opt/data/....
|
||||||
|
# restartUnits re-provisions both on rotation, without a full mars deploy.
|
||||||
|
sops.secrets.gitea_luna_token.restartUnits = [ "hermes-agent-prepare-dirs.service" ];
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -4,6 +4,7 @@ tailscale_authkey: ENC[AES256_GCM,data:An+OPDZF9kmemzoDhZPo7yMljksCz3yE/W9I1EAwt
|
|||||||
opencode_go_api_key: ENC[AES256_GCM,data:x7V6iRrP6UMvMAYh/25bcrE10MHhL9lasCYRHiQ3PIDI6aL+uXP0/YpfrRPY+60m5Yv+Bd7+9aWTWdAVu1laSNjJGg==,iv:EmEAig+fSMYX+g77UpkiQ0USxUYOfFWX4WjIj9NA9N8=,tag:Pr+EZW6uDTSGjng8iG2SZw==,type:str]
|
opencode_go_api_key: ENC[AES256_GCM,data:x7V6iRrP6UMvMAYh/25bcrE10MHhL9lasCYRHiQ3PIDI6aL+uXP0/YpfrRPY+60m5Yv+Bd7+9aWTWdAVu1laSNjJGg==,iv:EmEAig+fSMYX+g77UpkiQ0USxUYOfFWX4WjIj9NA9N8=,tag:Pr+EZW6uDTSGjng8iG2SZw==,type:str]
|
||||||
telegram_bot_token: ENC[AES256_GCM,data:WX+KFtoqFodkoWNwd7EXUrUJakZ9oaMZgg4OnCeL/JVXcsdQesD1PLmKp6vK9g==,iv:m1oqKlcesvhMLtndyp/XxsUAy0YpEsSulPDK0V+Wh0A=,tag:zvLcxcQ+A4fQUht5GkL2Qw==,type:str]
|
telegram_bot_token: ENC[AES256_GCM,data:WX+KFtoqFodkoWNwd7EXUrUJakZ9oaMZgg4OnCeL/JVXcsdQesD1PLmKp6vK9g==,iv:m1oqKlcesvhMLtndyp/XxsUAy0YpEsSulPDK0V+Wh0A=,tag:zvLcxcQ+A4fQUht5GkL2Qw==,type:str]
|
||||||
hermes_dashboard_oidc_client_secret: ENC[AES256_GCM,data:IMPNTPMKO+b7eyV4hyGfnvH1/i+W4IPDNjncoyB1oIV8WaB6nOJn0sSEuTUCKB94K+Y7bsVQU0zpbKdIYOdGqgmPzwMCsScxMt4SewTmiiqWxv6SQFf4EzMxgXqjMvH8PWDzLcI2C2tI/KcVS251iqRViOTFe1/tkm+mV8sJmEI=,iv:F/rOUDmJZoGPS9fObAni5ntyOqbbhMWDPdHGLTexwlA=,tag:ALf98DmB0JziGspZMiLCiw==,type:str]
|
hermes_dashboard_oidc_client_secret: ENC[AES256_GCM,data:IMPNTPMKO+b7eyV4hyGfnvH1/i+W4IPDNjncoyB1oIV8WaB6nOJn0sSEuTUCKB94K+Y7bsVQU0zpbKdIYOdGqgmPzwMCsScxMt4SewTmiiqWxv6SQFf4EzMxgXqjMvH8PWDzLcI2C2tI/KcVS251iqRViOTFe1/tkm+mV8sJmEI=,iv:F/rOUDmJZoGPS9fObAni5ntyOqbbhMWDPdHGLTexwlA=,tag:ALf98DmB0JziGspZMiLCiw==,type:str]
|
||||||
|
gitea_luna_token: ENC[AES256_GCM,data:EgSgzXFlYHN1yAlpjBBjSxacVYO9mhe1TBtAjNMZDEPxkeizB5O8Bw==,iv:pKN6bz7mBV3HxqBdnJi6ah17bukhd+sXeItojngT0HE=,tag:1wCfPK+MJb+P/S/kq2czeQ==,type:str]
|
||||||
sops:
|
sops:
|
||||||
age:
|
age:
|
||||||
- enc: |
|
- enc: |
|
||||||
@@ -24,7 +25,7 @@ sops:
|
|||||||
oyJ7PS3lW+PxH5AZkeeU7gXO/pz2oDku0aDOds7kaD3n0+qSWicQ+Q==
|
oyJ7PS3lW+PxH5AZkeeU7gXO/pz2oDku0aDOds7kaD3n0+qSWicQ+Q==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
recipient: age1eapjg6tdrr0fuvmgs3q3nlvnjkaxez298qynqqqxt0lpcv0lrsyq7ayxjk
|
recipient: age1eapjg6tdrr0fuvmgs3q3nlvnjkaxez298qynqqqxt0lpcv0lrsyq7ayxjk
|
||||||
lastmodified: "2026-08-21T23:24:48Z"
|
lastmodified: "2026-08-22T18:28:46Z"
|
||||||
mac: ENC[AES256_GCM,data:p/vtMVfVWfgP7+xa8sbbdYLeLCYbjvthHoexmKU0RyIl2jv5bp9ldD1F1G8U+v+8+Re44DClcDtoNXrIHlreFx3hbH9sF2tnL4+oI2os7Yn+nmWZ6J1ngS2Hz+qspWtFt+up/TkP2BsTzoas5ZstJckOW0AkeKEYIT1zQmzcBtE=,iv:O2VoZK1DmRSJIR/bC538Ley1fkZqjI0L80YpjUU9mPo=,tag:xO9wzAocmxoxrIHcnBRGXQ==,type:str]
|
mac: ENC[AES256_GCM,data:Y/QbEoRG2pJ+tz919+kSEfCs6HsjTHmiaO5xWuDhuVXO71Sm+8vx2OQwCnEHWA1FnFoQgBJWJFlAA4yMiFyjtE3Ark9Uxxi07DXYfXJ/B64DBbrxJMQSKVfCxi8KlExbKyL87FSuUwmSeYgE2DIydmOGDv0P+Q0kn5GJ1T6lJOU=,iv:iX9OJMJK3xTsGh8ZLXzZWUj5mZg7jGR3GnvMeR2lXvA=,tag:CvsoVF1vdf4fQmLE3NR9hw==,type:str]
|
||||||
unencrypted_suffix: _unencrypted
|
unencrypted_suffix: _unencrypted
|
||||||
version: 3.13.3
|
version: 3.13.3
|
||||||
|
|||||||
@@ -14,6 +14,12 @@ let
|
|||||||
# and whitelisted to push past branch protection. Add a repo here and
|
# and whitelisted to push past branch protection. Add a repo here and
|
||||||
# redeploy — no manual UI clicking needed.
|
# redeploy — no manual UI clicking needed.
|
||||||
ciBotRepos = [ "darman/hypr-chrome" ];
|
ciBotRepos = [ "darman/hypr-chrome" ];
|
||||||
|
|
||||||
|
# Repos where luna (Hermes Agent's own gitea identity — see below) gets PR-tier
|
||||||
|
# access: Write collaborator (so she can push feature branches and open PRs)
|
||||||
|
# but explicitly walled off `master`'s push/merge/approve whitelists so
|
||||||
|
# nothing she does lands without darman clicking merge.
|
||||||
|
lunaRepos = [ "darman/homelab" ];
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
services.gitea = {
|
services.gitea = {
|
||||||
@@ -166,4 +172,96 @@ in
|
|||||||
'') ciBotRepos}
|
'') ciBotRepos}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# luna: Hermes Agent's own gitea identity (Hermes was renamed L.U.N.A.,
|
||||||
|
# 2026-08-22). Deliberately PR-tier only, not push-tier like ci-bot:
|
||||||
|
# Hermes runs on mars, takes instructions over Telegram, and can be
|
||||||
|
# prompt-injected via tool output — a dedicated account with its own
|
||||||
|
# scoped, revocable token keeps that blast radius off darman's own
|
||||||
|
# credentials, and the branch-protection whitelists below keep it off
|
||||||
|
# `master` entirely regardless of what the token can technically do.
|
||||||
|
# She gets Write collaborator access (needed to push a branch and open a
|
||||||
|
# PR against the same repo — this instance has no fork workflow), but:
|
||||||
|
# - enable_push + enable_push_whitelist(darman only): nobody but darman
|
||||||
|
# can push straight to master; luna can only land on a side branch.
|
||||||
|
# - enable_merge_whitelist(darman only): opening a PR is not the same
|
||||||
|
# as merging one — only darman can click merge.
|
||||||
|
# - required_approvals=1 + enable_approvals_whitelist(darman only):
|
||||||
|
# an approval has to come from darman specifically, not luna
|
||||||
|
# rubber-stamping her own PR from a second identity.
|
||||||
|
# This is provisioning parity with ci-bot only (account + collaborator +
|
||||||
|
# branch protection) — it does NOT wire a token into mars/hermes-agent.nix
|
||||||
|
# yet; that's a separate step once luna actually has git tooling to call.
|
||||||
|
#
|
||||||
|
# luna's own push token (used by whatever git tooling gets wired into
|
||||||
|
# hermes-agent.nix later) is generated once, the same way ci-bot's was:
|
||||||
|
# su gitea -s /bin/sh -c \
|
||||||
|
# 'GITEA_WORK_DIR=/mnt/data/AppData/gitea gitea admin user generate-access-token \
|
||||||
|
# --username luna --scopes write:repository'
|
||||||
|
# then stored as a secret (e.g. secrets/mars.yaml's gitea_luna_token) —
|
||||||
|
# NOT pushed into gitea itself as an Actions secret like ci-bot's is,
|
||||||
|
# since luna isn't a CI workflow running inside gitea, she's an external
|
||||||
|
# agent calling out to it.
|
||||||
|
systemd.services.gitea-luna-provision = {
|
||||||
|
description = "Provision luna (Hermes Agent) gitea account + PR-tier repo access";
|
||||||
|
after = [ "gitea.service" ];
|
||||||
|
requires = [ "gitea.service" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
path = [ pkgs.curl pkgs.jq config.services.gitea.package ];
|
||||||
|
environment = {
|
||||||
|
TOKEN_FILE = config.sops.secrets.gitea_provisioning_token.path;
|
||||||
|
};
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
User = config.services.gitea.user;
|
||||||
|
};
|
||||||
|
script = ''
|
||||||
|
set -euo pipefail
|
||||||
|
api=http://127.0.0.1:${toString config.services.gitea.settings.server.HTTP_PORT}/api/v1
|
||||||
|
admin_token="$(cat "$TOKEN_FILE")"
|
||||||
|
auth=(-H "Authorization: token $admin_token")
|
||||||
|
|
||||||
|
for _ in $(seq 1 30); do
|
||||||
|
curl -fs "$api/version" >/dev/null 2>&1 && break
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
if ! curl -fs "''${auth[@]}" "$api/users/luna" >/dev/null 2>&1; then
|
||||||
|
GITEA_WORK_DIR=${config.services.gitea.stateDir} gitea admin user create \
|
||||||
|
--username luna \
|
||||||
|
--email luna@${config.services.gitea.settings.server.DOMAIN} \
|
||||||
|
--random-password --must-change-password=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
${lib.concatMapStringsSep "\n" (repo: ''
|
||||||
|
curl -fsS "''${auth[@]}" -H 'Content-Type: application/json' \
|
||||||
|
-X PUT "$api/repos/${repo}/collaborators/luna" \
|
||||||
|
-d '{"permission":"write"}'
|
||||||
|
|
||||||
|
default_branch="$(curl -fs "''${auth[@]}" "$api/repos/${repo}" | jq -r .default_branch)"
|
||||||
|
|
||||||
|
protect_body="$(jq -n '{
|
||||||
|
enable_push: true,
|
||||||
|
enable_push_whitelist: true,
|
||||||
|
push_whitelist_usernames: ["darman"],
|
||||||
|
enable_merge_whitelist: true,
|
||||||
|
merge_whitelist_usernames: ["darman"],
|
||||||
|
required_approvals: 1,
|
||||||
|
enable_approvals_whitelist: true,
|
||||||
|
approvals_whitelist_username: ["darman"]
|
||||||
|
}')"
|
||||||
|
|
||||||
|
if curl -fs "''${auth[@]}" "$api/repos/${repo}/branch_protections/$default_branch" >/dev/null 2>&1; then
|
||||||
|
curl -fsS "''${auth[@]}" -H 'Content-Type: application/json' \
|
||||||
|
-X PATCH "$api/repos/${repo}/branch_protections/$default_branch" \
|
||||||
|
-d "$protect_body"
|
||||||
|
else
|
||||||
|
curl -fsS "''${auth[@]}" -H 'Content-Type: application/json' \
|
||||||
|
-X POST "$api/repos/${repo}/branch_protections" \
|
||||||
|
-d "$(echo "$protect_body" | jq --arg b "$default_branch" '. + {branch_name: $b}')"
|
||||||
|
fi
|
||||||
|
'') lunaRepos}
|
||||||
|
'';
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user