@yunuseon/kayra 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +5 -5
  2. package/ansible/playbooks/apps-prune.yml +6 -6
  3. package/ansible/playbooks/platform.yml +5 -5
  4. package/ansible/roles/app/tasks/main.yml +26 -14
  5. package/ansible/roles/app/templates/app.env.j2 +1 -1
  6. package/ansible/roles/common/tasks/main.yml +1 -1
  7. package/ansible/roles/forgejo-config/tasks/main.yml +4 -9
  8. package/ansible/roles/platform/defaults/main.yml +8 -15
  9. package/ansible/roles/platform/tasks/main.yml +96 -63
  10. package/ansible/roles/platform/templates/platform.env.j2 +3 -11
  11. package/cli/dist/brand.js +0 -3
  12. package/cli/dist/cli.js +10 -1
  13. package/cli/dist/commands/server.js +16 -2
  14. package/cli/dist/deployer/app.js +47 -0
  15. package/cli/dist/deployer/brand.js +8 -0
  16. package/cli/dist/deployer/builder.js +196 -0
  17. package/cli/dist/deployer/bundle.js +49 -0
  18. package/cli/dist/deployer/compose.js +26 -0
  19. package/cli/dist/deployer/cron.js +44 -0
  20. package/cli/dist/deployer/ctl.js +77 -0
  21. package/cli/dist/deployer/deployer.js +282 -0
  22. package/cli/dist/deployer/forge.js +25 -0
  23. package/cli/dist/deployer/http.js +22 -0
  24. package/cli/dist/deployer/main.js +19 -0
  25. package/cli/dist/deployer/page.js +32 -0
  26. package/cli/dist/deployer/process.js +31 -0
  27. package/cli/dist/deployer/queue.js +28 -0
  28. package/cli/dist/deployer/records.js +46 -0
  29. package/cli/dist/deployer/run.js +76 -0
  30. package/cli/dist/deployer/store.js +31 -0
  31. package/cli/dist/deployer/webhook.js +31 -0
  32. package/cli/dist/fleet.js +1 -1
  33. package/cli/dist/main.js +11 -7
  34. package/cli/dist/settings.js +6 -10
  35. package/compose/authelia/configuration.yml +3 -3
  36. package/compose/deployer/Dockerfile.builder +37 -0
  37. package/compose/deployer/Dockerfile.deployer +40 -0
  38. package/compose/deployer/kayra-deployer +6 -0
  39. package/compose/docker-compose.ci.yml +71 -79
  40. package/hosts.conf +2 -2
  41. package/package.json +2 -3
  42. package/schema/defaults.json +2 -14
  43. package/schema/server.json +1 -1
  44. package/schema/vars-platform.json +2 -14
  45. package/scripts/local/require-main.sh +6 -2
  46. package/scripts/platform/compose-policy.sh +84 -0
  47. package/scripts/platform/registry-gc.sh +99 -0
  48. package/scripts/platform/resolve-images.sh +107 -0
  49. package/scripts/server/{jenkins-app-apply → app-apply} +9 -10
  50. package/scripts/server/app-purge +1 -1
  51. package/scripts/server/app-remove +8 -9
  52. package/ansible/roles/app/templates/casc-app.yaml.j2 +0 -68
  53. package/compose/jenkins/Dockerfile +0 -40
  54. package/compose/jenkins/casc/00-platform.yaml +0 -158
  55. package/compose/jenkins/plugins.txt +0 -97
  56. package/compose/jenkins-agent/Dockerfile +0 -24
  57. package/scripts/server/jenkins-api +0 -28
  58. package/scripts/server/jenkins-reload +0 -12
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env bash
2
+ # =============================================================================
3
+ # registry-gc.sh — keep the images apps actually shipped, delete the rest.
4
+ #
5
+ # Keep set: every image referenced by the newest KEEP_RELEASES release records
6
+ # of each app (the deployer writes one per deploy), plus every image any
7
+ # container on this host uses. A registered app (its apps/<app>.json exists)
8
+ # with no release record yet is not touched — it has not deployed for the first time. An app
9
+ # that is no longer registered has all its images collected. A deleted image
10
+ # is not lost for good: resolve-images.sh --ensure
11
+ # rebuilds whatever a deploy asks for and the registry no longer has.
12
+ #
13
+ # Then: registry garbage-collect (blobs), BuildKit cache and host images older
14
+ # than a week. Runs nightly in the deployer (`kayra job <server> gc` by hand).
15
+ # =============================================================================
16
+ set -euo pipefail
17
+
18
+ KEEP_RELEASES="${KEEP_RELEASES:-10}"
19
+ REGISTRY="${REGISTRY_PUSH_HOST:-registry:5000}"
20
+ platform_dir="${PLATFORM_DIR:?}"
21
+ releases="${platform_dir}/releases"
22
+
23
+ work="$(mktemp -d)"
24
+ trap 'rm -rf "$work"' EXIT
25
+
26
+ # ── Keep set (as repo:tag, registry host stripped) ──────────────────────────
27
+ for dir in "$releases"/*/; do
28
+ [ -d "$dir" ] || continue
29
+ ls -1 "$dir" | sort | tail -n "$KEEP_RELEASES" | while read -r record; do
30
+ cut -d= -f2- "${dir}${record}"
31
+ done
32
+ done > "$work/refs"
33
+ docker ps -a --format '{{.Image}}' >> "$work/refs"
34
+ sed -E 's#^[^/]+:[0-9]+/##' "$work/refs" | sort -u > "$work/keep"
35
+ ls -1 "$releases" 2>/dev/null > "$work/released" || true
36
+ ls -1 "${platform_dir}/apps" 2>/dev/null | sed -n 's/^\(.*\)\.json$/\1/p' > "$work/registered" || true
37
+
38
+ digest() {
39
+ curl -sfI \
40
+ -H 'Accept: application/vnd.oci.image.index.v1+json' \
41
+ -H 'Accept: application/vnd.oci.image.manifest.v1+json' \
42
+ -H 'Accept: application/vnd.docker.distribution.manifest.list.v2+json' \
43
+ -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' \
44
+ "http://${REGISTRY}/v2/$1/manifests/$2" \
45
+ | tr -d '\r' | sed -n 's/^[Dd]ocker-[Cc]ontent-[Dd]igest: //p'
46
+ }
47
+ json_list() { jq -r ".$1 // [] | .[]"; }
48
+
49
+ # ── Delete untracked manifests ──────────────────────────────────────────────
50
+ # By digest, and never a digest a kept tag also points at: deleting a manifest
51
+ # removes every tag on it.
52
+ # Fetched into variables first: a failure inside `for x in $(...)` would not
53
+ # trip set -e, and a GC that cannot list anything looks like one with nothing
54
+ # to delete.
55
+ repos=$(curl -sf "http://${REGISTRY}/v2/_catalog?n=1000" | json_list repositories)
56
+ deleted=0
57
+ for repo in $repos; do
58
+ app="${repo%%/*}"
59
+ if grep -qxF "$app" "$work/registered" && ! grep -qxF "$app" "$work/released"; then
60
+ echo "skip ${repo} (${app} has not deployed yet)"
61
+ continue
62
+ fi
63
+ : > "$work/kept-digests"; : > "$work/candidates"
64
+ tags=$(curl -sf "http://${REGISTRY}/v2/${repo}/tags/list" | json_list tags)
65
+ for tag in $tags; do
66
+ d=$(digest "$repo" "$tag")
67
+ [ -n "$d" ] || continue
68
+ if grep -qxF "${repo}:${tag}" "$work/keep"; then
69
+ echo "$d" >> "$work/kept-digests"
70
+ else
71
+ echo "$tag $d" >> "$work/candidates"
72
+ fi
73
+ done
74
+ while read -r tag d; do
75
+ grep -qxF "$d" "$work/kept-digests" && continue
76
+ curl -sf -X DELETE "http://${REGISTRY}/v2/${repo}/manifests/${d}" >/dev/null
77
+ echo "deleted ${repo}:${tag}"
78
+ deleted=$((deleted + 1))
79
+ done < "$work/candidates"
80
+ done
81
+ echo "Deleted ${deleted} manifest(s)."
82
+
83
+ # ── Reclaim blobs ───────────────────────────────────────────────────────────
84
+ # garbage-collect is only safe while nothing pushes, so the registry is stopped
85
+ # for the few seconds it takes; the trap brings it back whatever happens.
86
+ if [ "$deleted" -gt 0 ]; then
87
+ registry=$(docker ps -aq \
88
+ --filter label=com.docker.compose.project=platform \
89
+ --filter label=com.docker.compose.service=registry)
90
+ image=$(docker inspect -f '{{.Config.Image}}' "$registry")
91
+ docker stop "$registry" >/dev/null
92
+ trap 'docker start "$registry" >/dev/null; rm -rf "$work"' EXIT
93
+ docker run --rm --volumes-from "$registry" "$image" \
94
+ garbage-collect /etc/docker/registry/config.yml | tail -n 3
95
+ fi
96
+
97
+ # ── Build cache and host images ─────────────────────────────────────────────
98
+ buildctl --addr tcp://buildkitd:1234 prune --keep-duration 168h | tail -n 1
99
+ docker image prune -af --filter until=168h | tail -n 1
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env bash
2
+ # shellcheck disable=SC2154 # read_manifest assigns its variables via printf -v
3
+ # =============================================================================
4
+ # resolve-images.sh <app> <app-root> --plan | --ensure <artifact>
5
+ #
6
+ # Content-addressed image plan and build for any app. Every
7
+ # <app-root>/**/docker/image.json is an artifact, named after the directory
8
+ # that holds docker/:
9
+ # { "image": "<app>/<name>", "dockerfile": "...", "context": "...",
10
+ # "inputs": ["...", ...] } all paths relative to <app-root>
11
+ #
12
+ # Two modes, because the builder has to know what it is about to do before it can
13
+ # show progress through it:
14
+ # --plan One TSV row per artifact: name, env var, image ref.
15
+ # Pure git — no registry, no daemon, no side effects.
16
+ # --ensure <name> Build and push that artifact if its tag is missing from
17
+ # the registry. Prints CACHED or BUILT.
18
+ #
19
+ # Tag = SHA of the last commit touching any declared input: same inputs, same
20
+ # content, same tag, no rebuild — on any branch.
21
+ #
22
+ # Builds go through buildkitd (BUILDKIT_HOST), never a docker daemon. Two
23
+ # registry names for the same storage:
24
+ # REGISTRY_PUSH_HOST registry:5000 reachable from containers (buildkit)
25
+ # REGISTRY localhost:5000 reachable from the host daemon (compose)
26
+ #
27
+ # Requires: git, jq; --ensure also curl and buildctl.
28
+ # =============================================================================
29
+ set -euo pipefail
30
+
31
+ app="${1:?Usage: $0 <app> <app-root> --plan | --ensure <artifact>}"
32
+ root="${2:?}"
33
+ shift 2
34
+ REGISTRY="${REGISTRY:-localhost:5000}"
35
+ REGISTRY_PUSH_HOST="${REGISTRY_PUSH_HOST:-registry:5000}"
36
+
37
+ manifests() {
38
+ find "$root" -path '*/node_modules' -prune -o -path '*/docker/image.json' -print | sort
39
+ }
40
+
41
+ # Sets: image dockerfile context tag ref push_ref var_name
42
+ # (printf -v assigns them, which shellcheck cannot follow: see SC2154 above.)
43
+ read_manifest() {
44
+ local manifest="$1" field
45
+ for field in image dockerfile context; do
46
+ printf -v "$field" '%s' "$(jq -er ".${field}" "$manifest")"
47
+ done
48
+ # The registry has one namespace per app; an app may only name images in its own.
49
+ case "$image" in
50
+ "${app}/"*) ;;
51
+ *) echo "ERROR: ${manifest}: image ${image} is outside the ${app}/ namespace" >&2; return 1 ;;
52
+ esac
53
+
54
+ local inputs=() input
55
+ while IFS= read -r input; do inputs+=("$input"); done < <(jq -r '.inputs[]' "$manifest")
56
+ tag=$(git -C "$root" rev-list -1 HEAD -- "${inputs[@]}" | cut -c1-12)
57
+ [ -n "$tag" ] || { echo "ERROR: ${manifest}: no commit touches its inputs" >&2; return 1; }
58
+
59
+ ref="${REGISTRY}/${image}:${tag}"
60
+ push_ref="${REGISTRY_PUSH_HOST}/${image}:${tag}"
61
+ # shop/storefront -> STOREFRONT_IMAGE
62
+ var_name="$(basename "$image" | tr '[:lower:]-' '[:upper:]_')_IMAGE"
63
+ }
64
+
65
+ artifact_name() { basename "$(dirname "$(dirname "$1")")"; }
66
+
67
+ plan() {
68
+ local manifest
69
+ for manifest in $(manifests); do
70
+ read_manifest "$manifest"
71
+ printf '%s\t%s\t%s\n' "$(artifact_name "$manifest")" "$var_name" "$ref"
72
+ done
73
+ }
74
+
75
+ ensure() {
76
+ local manifest
77
+ # .../<artifact>/docker/image.json
78
+ manifest=$(manifests | awk -F/ -v name="$1" '$(NF-2) == name')
79
+ [ -n "$manifest" ] || { echo "ERROR: no artifact $1 under ${root}" >&2; return 1; }
80
+ read_manifest "$manifest"
81
+
82
+ local status
83
+ status=$(curl -s -o /dev/null -w '%{http_code}' \
84
+ -H 'Accept: application/vnd.oci.image.index.v1+json' \
85
+ -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' \
86
+ "http://${REGISTRY_PUSH_HOST}/v2/${image}/manifests/${tag}" || echo 000)
87
+ if [ "$status" = "200" ]; then
88
+ echo "CACHED ${ref}"
89
+ return 0
90
+ fi
91
+
92
+ : "${BUILDKIT_HOST:?BUILDKIT_HOST is required (e.g. tcp://buildkitd:1234)}"
93
+ echo "BUILDING ${image} -> ${push_ref}"
94
+ buildctl build \
95
+ --frontend dockerfile.v0 \
96
+ --local context="${root}/${context}" \
97
+ --local dockerfile="$(dirname "${root}/${dockerfile}")" \
98
+ --opt filename="$(basename "$dockerfile")" \
99
+ --output "type=image,name=${push_ref},push=true,registry.insecure=true"
100
+ echo "BUILT ${ref}"
101
+ }
102
+
103
+ case "${1:-}" in
104
+ --plan) plan ;;
105
+ --ensure) ensure "${2:?--ensure needs an artifact name}" ;;
106
+ *) echo "Usage: $0 <app> <app-root> --plan | --ensure <artifact>" >&2; exit 2 ;;
107
+ esac
@@ -1,18 +1,19 @@
1
1
  #!/usr/bin/env bash
2
2
  # =============================================================================
3
- # jenkins-app-apply <app> — the one way an app registers with Jenkins.
3
+ # app-apply <app> — the one way an app's secrets reach the deployer.
4
4
  #
5
- # Reads the app's secrets as KEY=VALUE lines on stdin, writes each to
6
- # jenkins-secrets/<app>/<KEY> (where its 10-<app>.yaml reads them via
7
- # ${readFile:/run/app-secrets/<app>/<KEY>}), then runs jenkins-reload.
5
+ # Reads the app's secrets as KEY=VALUE lines on stdin and writes each to
6
+ # app-secrets/<app>/<KEY>, which the deployer reads (mounted read-only at
7
+ # /run/app-secrets) into the environment of that app's compose commands, and
8
+ # nowhere else. The next deploy uses them; nothing is reloaded.
8
9
  #
9
10
  # Installed by the platform role at <platform_dir>/bin. Run as root.
10
11
  # =============================================================================
11
12
  set -euo pipefail
12
13
 
13
- app="${1:?Usage: jenkins-app-apply <app> < secrets.env}"
14
+ app="${1:?Usage: app-apply <app> < secrets.env}"
14
15
  platform_dir="$(cd "$(dirname "$0")/.." && pwd)"
15
- secrets_dir="${platform_dir}/jenkins-secrets/${app}"
16
+ secrets_dir="${platform_dir}/app-secrets/${app}"
16
17
 
17
18
  # Staged next to the target and swapped in whole, so keys the app no longer
18
19
  # declares disappear instead of lingering.
@@ -27,10 +28,8 @@ done
27
28
  rm -rf "$secrets_dir"
28
29
  mv "$staging" "$secrets_dir"
29
30
  trap - EXIT
30
- # The controller runs as uid 1000 and reads these through a read-only mount.
31
+ # The deployer runs as uid 1000 and reads these through a read-only mount.
31
32
  chown -R 1000:1000 "$secrets_dir"
32
33
  find "$secrets_dir" -type f -exec chmod 0400 {} +
33
34
  chmod 0500 "$secrets_dir"
34
-
35
- "${platform_dir}/bin/jenkins-reload"
36
- echo "Applied Jenkins config for ${app}."
35
+ echo "Applied ${app}'s secrets."
@@ -12,7 +12,7 @@ set -euo pipefail
12
12
  app="${1:?Usage: app-purge <app>}"
13
13
  platform_dir="$(cd "$(dirname "$0")/.." && pwd)"
14
14
 
15
- if [ -e "${platform_dir}/jenkins/casc/10-${app}.yaml" ]; then
15
+ if [ -e "${platform_dir}/apps/${app}.json" ]; then
16
16
  echo "ERROR: ${app} is still registered on this server — remove it from server.yml and run \`kayra apps <server>\` first." >&2
17
17
  exit 1
18
18
  fi
@@ -3,9 +3,9 @@
3
3
  # app-remove <app> — take an app off this server, keeping its data.
4
4
  #
5
5
  # Stops its containers and removes everything the platform set up for it:
6
- # JCasC fragment, secret files, .env, the Jenkins folder with its jobs and
7
- # history, the release records (so Rollback no longer offers it). Its images
8
- # are collected by the next registry GC, since the app is no longer registered.
6
+ # its registration, secret files, .env, runs and release records (so rollback
7
+ # no longer offers it). Its images are collected by the next registry GC,
8
+ # since the app is no longer registered.
9
9
  #
10
10
  # Volumes stay: dropping a line from server.yml must never silently destroy a
11
11
  # database. `app-purge <app>` deletes them deliberately.
@@ -19,12 +19,11 @@ app="${1:?Usage: app-remove <app>}"
19
19
  platform_dir="$(cd "$(dirname "$0")/.." && pwd)"
20
20
  apps_dir="$(sed -n 's/^APPS_DIR=//p' "${platform_dir}/.env")"
21
21
 
22
+ # Unregistered first: from here on no push, poll or rollback starts a run.
23
+ rm -f "${platform_dir}/apps/${app}.json"
22
24
  docker compose -p "$app" down --remove-orphans
23
- rm -rf "${platform_dir}/jenkins/casc/10-${app}.yaml" \
24
- "${platform_dir}/jenkins-secrets/${app}" \
25
+ rm -rf "${platform_dir}/app-secrets/${app}" \
26
+ "${platform_dir}/runs/${app}" \
27
+ "${platform_dir}/releases/${app}" \
25
28
  "${apps_dir:?}/${app}"
26
- # Without its fragment the reload drops the app's credentials.
27
- "${platform_dir}/bin/jenkins-reload"
28
- "${platform_dir}/bin/jenkins-api" POST "/job/${app}/doDelete" -o /dev/null || true
29
- (cd "$platform_dir" && docker compose exec -T jenkins-controller rm -rf "/var/jenkins_home/releases/${app}")
30
29
  echo "Removed ${app}. Its volumes are kept — app-purge ${app} deletes them."
@@ -1,68 +0,0 @@
1
- {#- Rendered once per app by the platform app role. The jobs run the platform
2
- library's pipelines; the app's repo is only ever checked out as data. -#}
3
- {% set coords = {
4
- 'name': app.name, 'repo': app_repo, 'ref': app_ref, 'path': app.path | default('.'),
5
- 'hosts': manifest.subdomains | map('regex_replace', '$', '.' ~ domain) | list,
6
- 'secrets': manifest.secrets | default([]),
7
- } -%}
8
- {% set literal = coords | to_json | regex_replace('^\\{', '[') | regex_replace('\\}$', ']') | regex_replace('"(\\w+)": ', '\\1: ') %}
9
- # =============================================================================
10
- # {{ app.name }} — generated by the platform app role. Do not edit on the server.
11
- # =============================================================================
12
-
13
- credentials:
14
- system:
15
- domainCredentials:
16
- - credentials:
17
- {% for key in manifest.secrets | default([]) %}
18
- - string:
19
- scope: GLOBAL
20
- id: "{{ app.name }}-{{ key | lower | replace('_', '-') }}"
21
- description: "{{ app.name }}: {{ key }}"
22
- secret: "${readFile:/run/app-secrets/{{ app.name }}/{{ key }}}"
23
- {% endfor %}
24
-
25
- jobs:
26
- - script: |
27
- folder("{{ app.name }}") {
28
- displayName("{{ app.name }}")
29
- }
30
-
31
- pipelineJob("{{ app.name }}/build") {
32
- displayName("Build")
33
- description("Builds {{ app.name }}'s images from {{ app_ref }} on every push (GitHub webhook, hourly poll as fallback); any other branch on demand. A green {{ app_ref }} build deploys that exact commit.")
34
- logRotator { numToKeep(20) }
35
- // Declared here as well as in the pipeline: a JCasC reload regenerates
36
- // the job from this DSL, and parameters only the pipeline declared
37
- // would be gone — Jenkins then silently drops the ones passed in.
38
- parameters {
39
- stringParam("BRANCH", "{{ app_ref }}", "Branch, tag or commit to build")
40
- }
41
- definition {
42
- cps {
43
- script('''@Library('platform') _
44
- appBuild({{ literal }})''')
45
- sandbox(true)
46
- }
47
- }
48
- triggers {
49
- githubPush()
50
- scm("H * * * *")
51
- }
52
- }
53
-
54
- pipelineJob("{{ app.name }}/deploy") {
55
- displayName("Deploy")
56
- description("Deploys any branch, tag or commit of {{ app.name }}: builds missing images, admits the compose config against the platform policy, runs it, records the release. Rollback starts it with an older SHA.")
57
- logRotator { numToKeep(50) }
58
- parameters {
59
- stringParam("REF", "{{ app_ref }}", "Branch, tag or commit SHA to deploy")
60
- }
61
- definition {
62
- cps {
63
- script('''@Library('platform') _
64
- appDeploy({{ literal }})''')
65
- sandbox(true)
66
- }
67
- }
68
- }
@@ -1,40 +0,0 @@
1
- # =============================================================================
2
- # Jenkins Controller — LTS with Docker CLI, Compose, buildctl and JCasC
3
- # =============================================================================
4
-
5
- FROM jenkins/jenkins:2.580.1-lts-jdk21
6
-
7
- USER root
8
-
9
- # renovate: datasource=github-releases depName=moby/buildkit
10
- ARG BUILDKIT_VERSION=v0.34.0
11
-
12
- # Install Docker CLI + Compose plugin
13
- RUN apt-get update && \
14
- apt-get install -y --no-install-recommends \
15
- ca-certificates curl gnupg jq && \
16
- install -m 0755 -d /etc/apt/keyrings && \
17
- curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg && \
18
- chmod a+r /etc/apt/keyrings/docker.gpg && \
19
- echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
20
- https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
21
- > /etc/apt/sources.list.d/docker.list && \
22
- apt-get update && \
23
- apt-get install -y --no-install-recommends \
24
- docker-ce-cli docker-compose-plugin && \
25
- arch="$(dpkg --print-architecture)" && \
26
- curl -fsSL "https://github.com/moby/buildkit/releases/download/${BUILDKIT_VERSION}/buildkit-${BUILDKIT_VERSION}.linux-${arch}.tar.gz" \
27
- | tar -xz -C /usr/local bin/buildctl && \
28
- rm -rf /var/lib/apt/lists/*
29
-
30
- USER jenkins
31
-
32
- # Install plugins
33
- COPY plugins.txt /usr/share/jenkins/ref/plugins.txt
34
- RUN jenkins-plugin-cli --plugin-file /usr/share/jenkins/ref/plugins.txt
35
-
36
- # JCasC fragments are bind-mounted, not baked in; CASC_JENKINS_CONFIG is set in
37
- # compose. That keeps config out of the image and out of the jenkins-home volume.
38
- # notifyCommit without a token, but only to trigger polling: how Forgejo tells
39
- # Jenkins about a push, over platform-internal (Traefik does not route it).
40
- ENV JAVA_OPTS="-Djenkins.install.runSetupWizard=false -Dhudson.plugins.git.GitStatus.NOTIFY_COMMIT_ACCESS_CONTROL=disabled-for-polling"
@@ -1,158 +0,0 @@
1
- # =============================================================================
2
- # Jenkins Configuration as Code — platform fragment (app-agnostic).
3
- #
4
- # Owns:
5
- # - Jenkins setup, security realm, authorization
6
- # - The agent node
7
- # - Git credentials (for cloning the app repo)
8
- # - GitHub: push-webhook secret + API token for commit statuses
9
- # - Jenkins URL
10
- #
11
- # Also owns the app-agnostic jobs: platform-registry-gc and rollback work off
12
- # the release records every app's <app>/deploy job writes (the platform
13
- # library's appDeploy) and start that job with a REF. platform-renovate exists
14
- # only where the server has repos to keep up to date (PLATFORM_RENOVATE).
15
- #
16
- # Does NOT own any app-specific credentials (Postgres, ...) or app pipelines.
17
- # Each app renders its own 10-<app>.yaml next to this file and applies it with
18
- # bin/jenkins-app-apply. JCasC merges every YAML in this directory.
19
- #
20
- # Git remote comes from the platform .env (rendered by Ansible).
21
- # =============================================================================
22
-
23
- jenkins:
24
- systemMessage: "Platform CI/CD — Managed by JCasC"
25
- numExecutors: 1
26
-
27
- securityRealm:
28
- local:
29
- allowsSignup: false
30
- users:
31
- - id: "${JENKINS_ADMIN_USER:-admin}"
32
- password: "${JENKINS_ADMIN_PASSWORD:-admin}"
33
-
34
- authorizationStrategy:
35
- loggedInUsersCanDoAnything:
36
- allowAnonymousRead: false
37
-
38
- nodes:
39
- - permanent:
40
- name: "builder"
41
- labelString: "buildkit linux"
42
- numExecutors: 2
43
- remoteFS: "/home/jenkins/agent"
44
- launcher:
45
- inbound:
46
- workDirSettings:
47
- disabled: false
48
- internalDir: "remoting"
49
- retentionStrategy: "always"
50
-
51
- credentials:
52
- system:
53
- domainCredentials:
54
- - credentials:
55
- - usernamePassword:
56
- scope: GLOBAL
57
- id: "git-credentials"
58
- description: "Git credentials for cloning repositories"
59
- username: "git"
60
- password: "${readFile:/run/platform-secrets/GIT_TOKEN}"
61
- - string:
62
- scope: GLOBAL
63
- id: "github-token"
64
- description: "Same PAT as git-credentials; needs Commit statuses: write"
65
- secret: "${readFile:/run/platform-secrets/GIT_TOKEN}"
66
- - string:
67
- scope: GLOBAL
68
- id: "renovate-token"
69
- description: "PAT for Renovate: Contents + Pull requests read & write"
70
- secret: "${readFile:/run/platform-secrets/RENOVATE_TOKEN}"
71
- - string:
72
- scope: GLOBAL
73
- id: "github-webhook-secret"
74
- description: "HMAC secret GitHub signs push webhooks with"
75
- secret: "${GITHUB_WEBHOOK_SECRET:-}"
76
-
77
- unclassified:
78
- # The pipeline every app runs. Trusted (outside the sandbox), loaded from
79
- # PLATFORM_REF (the release tag, v<version>; a test server may set a branch) and jobs cannot
80
- # pick another version, so a pushed app branch never changes what a pipeline
81
- # does. See ci/jenkins-lib/.
82
- globalLibraries:
83
- libraries:
84
- - name: "platform"
85
- defaultVersion: "${PLATFORM_REF}"
86
- allowVersionOverride: false
87
- implicit: false
88
- retriever:
89
- modernSCM:
90
- libraryPath: "ci/jenkins-lib/"
91
- scm:
92
- git:
93
- remote: "${JENKINS_GIT_REPO}"
94
- credentialsId: "git-credentials"
95
- location:
96
- url: "https://jenkins.${DOMAIN:-localhost}/"
97
- # Webhook lands on /github-webhook/. Hooks are registered by hand in the
98
- # repo settings, not managed from here.
99
- gitHubPluginConfig:
100
- configs:
101
- - name: "GitHub"
102
- apiUrl: "https://api.github.com"
103
- credentialsId: "github-token"
104
- manageHooks: false
105
- hookSecretConfigs:
106
- - credentialsId: "github-webhook-secret"
107
-
108
- # Platform jobs run pipelines from PLATFORM_REF of this repo, never from a pushed branch.
109
- jobs:
110
- - script: |
111
- def fromPlatform = { String path ->
112
- return {
113
- cpsScm {
114
- scm {
115
- git {
116
- remote {
117
- url("${JENKINS_GIT_REPO}")
118
- credentials("git-credentials")
119
- }
120
- branch("${PLATFORM_REF}")
121
- }
122
- }
123
- scriptPath(path)
124
- lightweight(false)
125
- }
126
- }
127
- }
128
-
129
- pipelineJob("platform-registry-gc") {
130
- displayName("Platform Registry GC")
131
- description("Nightly: deletes registry images no recent release of any app references, then prunes the BuildKit cache and host images. See ci/registry-gc.sh.")
132
- definition(fromPlatform("ci/registry-gc.Jenkinsfile"))
133
- triggers {
134
- cron("H 3 * * *")
135
- }
136
- }
137
-
138
- pipelineJob("rollback") {
139
- displayName("Rollback")
140
- description("Redeploys an earlier release of an app: pick the app, pick a release, done. Hands the SHA to <app>/deploy.")
141
- definition(fromPlatform("ci/rollback.Jenkinsfile"))
142
- }
143
-
144
- if ("${PLATFORM_RENOVATE:-false}" == "true") {
145
- pipelineJob("platform-renovate") {
146
- displayName("Platform Renovate")
147
- description("On renovate_schedule (weekly by default): opens PRs that bump pinned versions. Config: renovate.json. See ci/renovate.Jenkinsfile.")
148
- // Declared here as well: a JCasC reload regenerates the job and drops
149
- // parameters only the pipeline declares.
150
- parameters {
151
- booleanParam("DRY_RUN", false, "Log what would change, open no PRs")
152
- }
153
- definition(fromPlatform("ci/renovate.Jenkinsfile"))
154
- triggers {
155
- cron("${RENOVATE_SCHEDULE:-H 4 * * 1}")
156
- }
157
- }
158
- }
@@ -1,97 +0,0 @@
1
- # Every plugin, dependencies included, at the version prod runs: without the
2
- # dependencies pinned, jenkins-plugin-cli resolves them to whatever is newest
3
- # on the day the image is built. Renovate proposes updates.
4
- antisamy-markup-formatter:173.v680e3a_b_69ff3
5
- apache-httpcomponents-client-4-api:4.5.14-269.vfa_2321039a_83
6
- apache-httpcomponents-client-5-api:5.6.4-207.vd5b_a_c15e4de5
7
- asm-api:9.10.1-216.va_9256d3b_844b_
8
- authentication-tokens:1.144.v5ff4a_5ec5c33
9
- bootstrap5-api:5.3.8-1048.va_c299057e35c
10
- bouncycastle-api:2.30.1.86.1-306.vfc711fa_36d5f
11
- branch-api:2.1303.v9f3b_95dc329d
12
- caffeine-api:3.3.0-210.v71b_2985ce530
13
- checks-api:422.vc7e8b_51488c6
14
- cloud-stats:423.v63f017a_f582c
15
- cloudbees-folder:6.1106.v3a_d9a_6d2465e
16
- commons-compress-api:1.28.0-95.vb_e5e24f83645
17
- commons-lang3-api:3.21.0-122.v6a_ea_4fd34049
18
- commons-text-api:1.15.0-218.va_61573470393
19
- configuration-as-code:2133.ve570217cdb_cd
20
- credentials-binding:728.v902a_273b_8947
21
- credentials:1511.v2e3cb_0008ef0
22
- display-url-api:2.217.va_6b_de84cc74b_
23
- docker-commons:477.v289085a_b_6896
24
- docker-java-api:3.7.1-136.v5d70f77a_c3d6
25
- docker-plugin:1331.v2c036a_dc63fc
26
- docker-workflow:653.v2f2c08eff0ec
27
- durable-task:686.v80ff80875b_82
28
- echarts-api:6.1.0-1321.v86c4c5114494
29
- eddsa-api:0.3.0.1-29.v67e9a_1c969b_b_
30
- font-awesome-api:7.3.1-1013.v0835a_879ec6d
31
- git-client:6.6.1
32
- git:5.10.1
33
- github-api:1.330-492.v3941a_032db_2a_
34
- github:1.47.0
35
- gson-api:2.14.0-201.v8eefe5515533
36
- instance-identity:203.v15e81a_1b_7a_38
37
- ionicons-api:94.vcc3065403257
38
- jackson-annotations2-api:2.22-19.v10a_a_582ea_26e
39
- jackson2-api:2.22.3-447.v6a_93ce7d2c82
40
- jackson3-api:3.2.3-101.v69fc84453a_8e
41
- jakarta-activation-api:2.1.4-1
42
- jakarta-mail-api:2.1.5-1
43
- jakarta-xml-bind-api:4.0.9-19.v2b_a_5b_44d9a_1c
44
- javax-activation-api:1.2.0-8
45
- jaxb:2.3.9-143.v5979df3304e6
46
- job-dsl:3732.v9a_c49a_61a_313
47
- joda-time-api:2.15.0-208.v6b_084e7a_d9f0
48
- jquery3-api:3.7.1-687.v68d468e40b_30
49
- json-api:20260814-226.v20f9685d642c
50
- json-path-api:3.0.0-218.vcd4dd1355de2
51
- junit:1434.vde2d24df0c4c
52
- mailer:534.v1b_36f5864073
53
- matrix-project:928.v1effc3f3a_236
54
- mina-sshd-api-common:2.19.0-192.v2b_a_7b_2c1dc71
55
- mina-sshd-api-core:2.19.0-192.v2b_a_7b_2c1dc71
56
- okhttp-api:5.3.2-200.vedb_720a_cf1f8
57
- pipeline-build-step:601.v6d4c6d1a_9dc7
58
- pipeline-graph-analysis:254.v0f63a_a_447dca_
59
- pipeline-graph-view:1053.v9df19e6ceed2
60
- pipeline-groovy-lib:806.v408277b_33d1d
61
- pipeline-input-step:560.v56198a_642157
62
- pipeline-milestone-step:152.v6e22b_8cfc66c
63
- pipeline-model-api:2.2293.v6e7193cec599
64
- pipeline-model-definition:2.2293.v6e7193cec599
65
- pipeline-model-extensions:2.2293.v6e7193cec599
66
- pipeline-rest-api:2.41
67
- pipeline-stage-step:345.va_96187909426
68
- pipeline-stage-tags-metadata:2.2293.v6e7193cec599
69
- pipeline-stage-view:2.41
70
- pipeline-utility-steps:3.810.va_7672d206740
71
- plain-credentials:199.v9f8e1f741799
72
- plugin-util-api:7.1341.v039f146993d9
73
- prism-api:1.30.0-741.v034eb_0b_0a_a_fa_
74
- resource-disposer:0.25
75
- scm-api:728.vc30dcf7a_0df5
76
- script-security:1433.v17c58a_e45cc1
77
- snakeyaml-api:2.7-152.vc672728cf536
78
- snakeyaml-engine-api:3.2-18.v885ed7ff698f
79
- ssh-credentials:372.va_250881b_08cd
80
- ssh-slaves:3.1097.v868116049892
81
- structs:362.va_b_695ef4fdf9
82
- timestamper:1.30
83
- token-macro:477.vd4f0dc3cb_cf1
84
- trilead-api:2.284.v1974ea_324382
85
- variant:70.va_d9f17f859e0
86
- woodstox-core-api:7.3.0-16.v017f30ed004c
87
- workflow-aggregator:608.v67378e9d3db_1
88
- workflow-api:1413.v2ff1a_5e720fa_
89
- workflow-basic-steps:1098.v808b_fd7f8cf4
90
- workflow-cps:4391.v652b_b_b_1b_7c00
91
- workflow-durable-task-step:1479.v56e587f413a_7
92
- workflow-job:1609.v8926e7962024
93
- workflow-multibranch:842.v3a_b_59b_57b_e6e
94
- workflow-scm-step:487.v41313a_96b_65d
95
- workflow-step-api:724.v538c2362b_dfb_
96
- workflow-support:1015.v785e5a_b_b_8b_22
97
- ws-cleanup:0.49