refactor: retire git mirror runtime

This commit is contained in:
root
2026-07-21 17:18:47 +02:00
parent a572e0c322
commit fac860fca9
41 changed files with 203 additions and 4683 deletions
-83
View File
@@ -1,83 +0,0 @@
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
generateName: hwlab-jd01-v03-ci-poll-
annotations:
pipelinesascode.tekton.dev/on-event: "[push]"
pipelinesascode.tekton.dev/on-target-branch: "[v0.3]"
pipelinesascode.tekton.dev/on-cel-expression: "event == 'push' && target_branch == 'v0.3' && node == 'JD01'"
pipelinesascode.tekton.dev/max-keep-runs: "8"
pipelinesascode.tekton.dev/pipeline: "ci/pipelines/hwlab-jd01-v03-ci-image-publish.yaml"
hwlab.pikastech.local/ci-contract: tekton-native-primitive-tasks
hwlab.pikastech.local/download-profile: jd01-node-default
hwlab.pikastech.local/gitops-branch: v0.3-gitops
hwlab.pikastech.local/network-profile: jd01-node-ci-egress
hwlab.pikastech.local/node: JD01
hwlab.pikastech.local/policy: native-per-service-taskrun-image-publish
hwlab.pikastech.local/runtime-path: deploy/gitops/node/jd01/runtime-v03
hwlab.pikastech.local/source-branch: v0.3
hwlab.pikastech.local/source-config: .tekton/hwlab-jd01-v03-pac.yaml
labels:
app.kubernetes.io/name: hwlab-jd01-v03-pac
app.kubernetes.io/part-of: hwlab
unidesk.ai/source-commit: "{{revision}}"
hwlab.pikastech.local/gitops-target: v03
hwlab.pikastech.local/source-commit: "{{revision}}"
hwlab.pikastech.local/trigger: pipelines-as-code
spec:
timeouts:
pipeline: 1h0m0s
taskRunTemplate:
serviceAccountName: hwlab-jd01-v03-tekton-runner
podTemplate:
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
securityContext:
fsGroup: 1000
pipelineRef:
name: hwlab-jd01-v03-ci-image-publish
workspaces:
- name: source
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 8Gi
- name: git-ssh
secret:
secretName: hwlab-git-ssh
params:
- name: git-url
value: git@github.com:pikasTech/HWLAB.git
- name: git-read-url
value: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git
- name: git-write-url
value: http://git-mirror-write.devops-infra.svc.cluster.local:8080/pikasTech/HWLAB.git
- name: source-branch
value: v0.3
- name: gitops-branch
value: v0.3-gitops
- name: lane
value: v03
- name: catalog-path
value: deploy/artifact-catalog.jd01-v03.json
- name: image-tag-mode
value: full
- name: runtime-path
value: deploy/gitops/node/jd01/runtime-v03
- name: source-commit
value: "{{revision}}"
- name: revision
value: "{{revision}}"
- name: registry-prefix
value: 127.0.0.1:5000/hwlab
- name: services
value: hwlab-cloud-api,hwlab-workbench-runtime,hwlab-user-billing,hwlab-project-management,hwlab-cloud-web,hwlab-gateway,hwlab-edge-proxy,hwlab-agent-skills
- name: base-image
value: 127.0.0.1:5000/hwlab/hwlab-node20-base:20-bookworm-slim
- name: build-cache-mode
value: registry
- name: runtime-ready-timeout-ms
value: "60000"
+10 -3
View File
@@ -9,7 +9,7 @@ metadata:
pipelinesascode.tekton.dev/on-cel-expression: "event == 'push' && target_branch == 'v0.3' && node == 'NC01'"
pipelinesascode.tekton.dev/max-keep-runs: "8"
unidesk.ai/owning-config-ref: "config/hwlab-node-lanes.yaml#lanes.v03.targets.NC01"
unidesk.ai/effective-config-sha256: sha256:01b954f394174302b5ebce2de98a24d091be1c5922fbc188ef5af72ff1bb569c
unidesk.ai/effective-config-sha256: sha256:f42181ba9c079806968bb8374967a2bc710c7a87b56030d512cc00c004be126b
unidesk.ai/source-artifact-renderer: hwlab-runtime-lane
unidesk.ai/source-artifact-mode: remote-pipeline-annotation
pipelinesascode.tekton.dev/pipeline: ci/pipelines/hwlab-nc01-v03-ci-image-publish.yaml
@@ -52,16 +52,23 @@ spec:
resources:
requests:
storage: 8Gi
- name: git-ssh
- name: gitea-auth
secret:
secretName: hwlab-git-ssh
secretName: pac-gitea-hwlab-nc01-v03
items:
- key: token
path: token
params:
- name: git-url
value: "{{ repo_url }}"
- name: git-read-url
value: "{{ git_read_url }}"
- name: gitops-read-url
value: "{{ gitops_read_url }}"
- name: git-write-url
value: "{{ git_write_url }}"
- name: gitops-username
value: "{{ gitops_username }}"
- name: source-branch
value: "{{ source_branch }}"
- name: gitops-branch
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+30 -76
View File
@@ -94,43 +94,20 @@ lanes:
node: G14
sourceBranch: v0.2
gitopsBranch: v0.2-gitops
gitMirror:
namespace: devops-infra
serviceReadName: git-mirror-http
serviceWriteName: git-mirror-write
cachePvcName: hwlab-git-mirror-cache
cachePvcStorage: 20Gi
cacheHostPath: /var/lib/rancher/k3s/storage/hwlab-d601-v02-git-mirror-cache
servicePort: 8080
syncConfigMapName: git-mirror-sync-script
egressProxy:
mode: node-global
required: true
namespace: platform-infra
serviceName: sub2api-egress-proxy
port: 10808
clientName: d601-global-proxy
noProxy:
- localhost
- 127.0.0.1
- ::1
- 127.0.0.1:5000
- localhost:5000
- .svc
- .svc.cluster.local
- .cluster.local
- kubernetes
- kubernetes.default
- kubernetes.default.svc
- 10.0.0.0/8
- 10.42.0.0/16
- 10.43.0.0/16
- 172.16.0.0/12
- 192.168.0.0/16
- 82.156.23.220
- 74.48.78.17
- hyueapi.com
- .hyueapi.com
egressProxy:
required: true
namespace: platform-infra
serviceName: sub2api-egress-proxy
port: 10808
noProxy:
- localhost
- 127.0.0.1
- ::1
- .svc
- .svc.cluster.local
- .cluster.local
- hyueapi.com
- .hyueapi.com
namespace: hwlab-v02
endpoint: https://hwlab.74-48-78-17.nip.io
publicEndpoints:
@@ -277,49 +254,26 @@ lanes:
AGENTRUN_API_KEY: secretRef:hwlab-v02-master-server-admin-api-key/api-key
HWLAB_CODE_AGENT_AGENTRUN_RUNNER_NAMESPACE: agentrun-v01
HWLAB_CODE_AGENT_AGENTRUN_SECRET_NAMESPACE: agentrun-v01
HWLAB_CODE_AGENT_AGENTRUN_REPO_URL: http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git
HWLAB_CODE_AGENT_AGENTRUN_REPO_URL: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git
v03:
name: v0.3
node: G14
sourceBranch: v0.3
gitopsBranch: v0.3-gitops
gitMirror:
namespace: devops-infra
serviceReadName: git-mirror-http
serviceWriteName: git-mirror-write
cachePvcName: hwlab-git-mirror-cache
cachePvcStorage: 20Gi
cacheHostPath: /var/lib/rancher/k3s/storage/hwlab-d601-v03-git-mirror-cache
servicePort: 8080
syncConfigMapName: git-mirror-sync-script
egressProxy:
mode: node-global
required: true
namespace: platform-infra
serviceName: sub2api-egress-proxy
port: 10808
clientName: d601-global-proxy
noProxy:
- localhost
- 127.0.0.1
- ::1
- 127.0.0.1:5000
- localhost:5000
- .svc
- .svc.cluster.local
- .cluster.local
- kubernetes
- kubernetes.default
- kubernetes.default.svc
- 10.0.0.0/8
- 10.42.0.0/16
- 10.43.0.0/16
- 172.16.0.0/12
- 192.168.0.0/16
- 82.156.23.220
- 74.48.78.17
- hyueapi.com
- .hyueapi.com
egressProxy:
required: true
namespace: platform-infra
serviceName: sub2api-egress-proxy
port: 10808
noProxy:
- localhost
- 127.0.0.1
- ::1
- .svc
- .svc.cluster.local
- .cluster.local
- hyueapi.com
- .hyueapi.com
namespace: hwlab-v03
endpoint: https://hwlab.pikapython.com
publicEndpoints:
@@ -1319,7 +1273,7 @@ lanes:
sourceBranch: release
gitopsBranch: release-gitops
gitReadUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git
gitWriteUrl: http://git-mirror-write.devops-infra.svc.cluster.local:8080/pikasTech/HWLAB.git
gitWriteUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git
artifactCatalog: deploy/artifact-catalog.nc01-production.json
runtimePath: runtime-production
namespace: hwlab-production
+3 -3
View File
@@ -4,7 +4,7 @@ set -eu
export HWLAB_SERVICE_ID="hwlab-agent-skills"
export HWLAB_ARTIFACT_KIND="skills-bundle"
export HWLAB_SERVICE_ENTRYPOINT="skills/hwlab-agent-runtime/SKILL.md"
export HWLAB_RUNTIME_MODE="${HWLAB_RUNTIME_MODE:-env-reuse-git-mirror-checkout}"
export HWLAB_RUNTIME_MODE="${HWLAB_RUNTIME_MODE:-env-reuse-gitea-checkout}"
export HWLAB_COMMIT_ID="${HWLAB_BOOT_COMMIT:?HWLAB_BOOT_COMMIT is required}"
export HWLAB_REVISION="${HWLAB_BOOT_COMMIT}"
export HWLAB_SKILLS_COMMIT_ID="${HWLAB_SKILLS_COMMIT_ID:-${HWLAB_BOOT_COMMIT}}"
@@ -81,11 +81,11 @@ function healthPayload() {
environment,
status: "ok",
artifactKind: "skills-bundle",
runtimeMode: process.env.HWLAB_RUNTIME_MODE || "env-reuse-git-mirror-checkout",
runtimeMode: process.env.HWLAB_RUNTIME_MODE || "env-reuse-gitea-checkout",
revision: process.env.HWLAB_REVISION || commitId,
commit: {
id: commitId,
source: process.env.HWLAB_BUILD_SOURCE || "git-mirror-runtime-checkout"
source: process.env.HWLAB_BUILD_SOURCE || "gitea-runtime-checkout"
},
image: {
reference: imageReference,
+2 -2
View File
@@ -4,7 +4,7 @@ set -eu
export HWLAB_SERVICE_ID="hwlab-cloud-api"
export HWLAB_ARTIFACT_KIND="bun-command"
export HWLAB_SERVICE_ENTRYPOINT="cmd/hwlab-cloud-api/main.ts"
export HWLAB_RUNTIME_MODE="${HWLAB_RUNTIME_MODE:-env-reuse-git-mirror-checkout}"
export HWLAB_RUNTIME_MODE="${HWLAB_RUNTIME_MODE:-env-reuse-gitea-checkout}"
export HWLAB_COMMIT_ID="${HWLAB_BOOT_COMMIT:?HWLAB_BOOT_COMMIT is required}"
export HWLAB_REVISION="${HWLAB_BOOT_COMMIT}"
export HWLAB_IMAGE="${HWLAB_ENVIRONMENT_IMAGE:-${HWLAB_IMAGE:-}}"
@@ -13,7 +13,7 @@ export PORT="${PORT:-${HWLAB_CLOUD_API_PORT:-${HWLAB_PORT:-6667}}}"
export HWLAB_PORT="$PORT"
export HWLAB_CLOUD_API_PORT="$PORT"
export HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT="${HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT:-${HWLAB_BOOT_COMMIT}}"
export HWLAB_CODE_AGENT_AGENTRUN_REPO_URL="${HWLAB_CODE_AGENT_AGENTRUN_REPO_URL:-${HWLAB_BOOT_READ_URL:-http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git}}"
export HWLAB_CODE_AGENT_AGENTRUN_REPO_URL="${HWLAB_CODE_AGENT_AGENTRUN_REPO_URL:-${HWLAB_BOOT_READ_URL:-http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git}}"
export HWLAB_CODE_AGENT_WORKSPACE="${HWLAB_CODE_AGENT_WORKSPACE:-$PWD}"
export HWLAB_CODE_AGENT_CODEX_WORKSPACE="${HWLAB_CODE_AGENT_CODEX_WORKSPACE:-$HWLAB_CODE_AGENT_WORKSPACE}"
export CODEX_HOME="${CODEX_HOME:-/codex-home}"
+3 -3
View File
@@ -4,7 +4,7 @@ set -eu
export HWLAB_SERVICE_ID="hwlab-cloud-web"
export HWLAB_ARTIFACT_KIND="cloud-web"
export HWLAB_SERVICE_ENTRYPOINT="web/hwlab-cloud-web/index.html"
export HWLAB_RUNTIME_MODE="${HWLAB_RUNTIME_MODE:-env-reuse-git-mirror-checkout}"
export HWLAB_RUNTIME_MODE="${HWLAB_RUNTIME_MODE:-env-reuse-gitea-checkout}"
export HWLAB_COMMIT_ID="${HWLAB_BOOT_COMMIT:?HWLAB_BOOT_COMMIT is required}"
export HWLAB_REVISION="${HWLAB_BOOT_COMMIT}"
export HWLAB_IMAGE="${HWLAB_ENVIRONMENT_IMAGE:-${HWLAB_IMAGE:-}}"
@@ -71,11 +71,11 @@ function healthPayload() {
environment,
status: "ok",
artifactKind: "cloud-web",
runtimeMode: process.env.HWLAB_RUNTIME_MODE || "env-reuse-git-mirror-checkout",
runtimeMode: process.env.HWLAB_RUNTIME_MODE || "env-reuse-gitea-checkout",
revision: process.env.HWLAB_REVISION || commitId,
commit: {
id: commitId,
source: process.env.HWLAB_BUILD_SOURCE || "git-mirror-runtime-checkout"
source: process.env.HWLAB_BUILD_SOURCE || "gitea-runtime-checkout"
},
image: {
reference: imageReference,
@@ -4,7 +4,7 @@ set -eu
export HWLAB_SERVICE_ID="hwlab-codex-api-responses-forwarder"
export HWLAB_ARTIFACT_KIND="bun-command"
export HWLAB_SERVICE_ENTRYPOINT="cmd/hwlab-codex-api-responses-forwarder/main.ts"
export HWLAB_RUNTIME_MODE="${HWLAB_RUNTIME_MODE:-env-reuse-git-mirror-checkout}"
export HWLAB_RUNTIME_MODE="${HWLAB_RUNTIME_MODE:-env-reuse-gitea-checkout}"
export HWLAB_COMMIT_ID="${HWLAB_BOOT_COMMIT:?HWLAB_BOOT_COMMIT is required}"
export HWLAB_REVISION="${HWLAB_BOOT_COMMIT}"
export HWLAB_IMAGE="${HWLAB_ENVIRONMENT_IMAGE:-${HWLAB_IMAGE:-}}"
@@ -4,7 +4,7 @@ set -eu
export HWLAB_SERVICE_ID="hwlab-deepseek-responses-bridge"
export HWLAB_ARTIFACT_KIND="bun-command"
export HWLAB_SERVICE_ENTRYPOINT="cmd/hwlab-deepseek-responses-bridge/main.ts"
export HWLAB_RUNTIME_MODE="${HWLAB_RUNTIME_MODE:-env-reuse-git-mirror-checkout}"
export HWLAB_RUNTIME_MODE="${HWLAB_RUNTIME_MODE:-env-reuse-gitea-checkout}"
export HWLAB_COMMIT_ID="${HWLAB_BOOT_COMMIT:?HWLAB_BOOT_COMMIT is required}"
export HWLAB_REVISION="${HWLAB_BOOT_COMMIT}"
export HWLAB_IMAGE="${HWLAB_ENVIRONMENT_IMAGE:-${HWLAB_IMAGE:-}}"
+1 -1
View File
@@ -4,7 +4,7 @@ set -eu
export HWLAB_SERVICE_ID="hwlab-edge-proxy"
export HWLAB_ARTIFACT_KIND="node-command"
export HWLAB_SERVICE_ENTRYPOINT="cmd/hwlab-edge-proxy/main.mjs"
export HWLAB_RUNTIME_MODE="${HWLAB_RUNTIME_MODE:-env-reuse-git-mirror-checkout}"
export HWLAB_RUNTIME_MODE="${HWLAB_RUNTIME_MODE:-env-reuse-gitea-checkout}"
export HWLAB_COMMIT_ID="${HWLAB_BOOT_COMMIT:?HWLAB_BOOT_COMMIT is required}"
export HWLAB_REVISION="${HWLAB_BOOT_COMMIT}"
export HWLAB_IMAGE="${HWLAB_ENVIRONMENT_IMAGE:-${HWLAB_IMAGE:-}}"
+1 -1
View File
@@ -4,7 +4,7 @@ set -eu
export HWLAB_SERVICE_ID="hwlab-gateway"
export HWLAB_ARTIFACT_KIND="bun-command"
export HWLAB_SERVICE_ENTRYPOINT="cmd/hwlab-gateway/main.ts"
export HWLAB_RUNTIME_MODE="${HWLAB_RUNTIME_MODE:-env-reuse-git-mirror-checkout}"
export HWLAB_RUNTIME_MODE="${HWLAB_RUNTIME_MODE:-env-reuse-gitea-checkout}"
export HWLAB_COMMIT_ID="${HWLAB_BOOT_COMMIT:?HWLAB_BOOT_COMMIT is required}"
export HWLAB_REVISION="${HWLAB_BOOT_COMMIT}"
export HWLAB_IMAGE="${HWLAB_ENVIRONMENT_IMAGE:-${HWLAB_IMAGE:-}}"
@@ -4,7 +4,7 @@ set -eu
export HWLAB_SERVICE_ID="hwlab-project-management"
export HWLAB_ARTIFACT_KIND="bun-command"
export HWLAB_SERVICE_ENTRYPOINT="cmd/hwlab-project-management/main.ts"
export HWLAB_RUNTIME_MODE="${HWLAB_RUNTIME_MODE:-env-reuse-git-mirror-checkout}"
export HWLAB_RUNTIME_MODE="${HWLAB_RUNTIME_MODE:-env-reuse-gitea-checkout}"
export HWLAB_COMMIT_ID="${HWLAB_BOOT_COMMIT:?HWLAB_BOOT_COMMIT is required}"
export HWLAB_REVISION="${HWLAB_BOOT_COMMIT}"
export HWLAB_IMAGE="${HWLAB_ENVIRONMENT_IMAGE:-${HWLAB_IMAGE:-}}"
@@ -17,7 +17,7 @@ if (
bootRef.includes("..")
) fail("HWLAB_BOOT_REF must be a safe branch or ref name");
const readUrl = process.env.HWLAB_BOOT_READ_URL || mirrorReadUrl(bootRepo);
const readUrl = process.env.HWLAB_BOOT_READ_URL || giteaReadUrl(bootRepo);
process.env.HWLAB_BUN_COMMAND ||= process.execPath;
mkdirSync(path.dirname(checkoutDir), { recursive: true });
rmSync(checkoutDir, { recursive: true, force: true });
@@ -37,11 +37,11 @@ function requiredEnv(name: string): string {
return value;
}
function mirrorReadUrl(repo: string): string {
function giteaReadUrl(repo: string): string {
if (/github\.com[:/]pikasTech\/HWLAB(?:\.git)?$/u.test(repo)) {
return "http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git";
return "http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git";
}
fail("no mirror resolver for HWLAB_BOOT_REPO; refusing direct runtime GitHub fallback");
fail("no Gitea resolver for HWLAB_BOOT_REPO; refusing direct runtime GitHub fallback");
}
function linkNodeModules(): void {
@@ -14,7 +14,7 @@ if (!/^[A-Za-z0-9._/-]+$/u.test(bootRef) || bootRef.startsWith("-") || bootRef.i
fail("HWLAB_BOOT_REF must be a safe branch or ref name");
}
const readUrl = process.env.HWLAB_BOOT_READ_URL || mirrorReadUrl(bootRepo);
const readUrl = process.env.HWLAB_BOOT_READ_URL || giteaReadUrl(bootRepo);
mkdirSync(path.dirname(checkoutDir), { recursive: true });
rmSync(checkoutDir, { recursive: true, force: true });
// Go env images avoid npm dependency installation; Go modules stay in Docker/BuildKit layers.
@@ -31,11 +31,11 @@ function requiredEnv(name) {
return value;
}
function mirrorReadUrl(repo) {
function giteaReadUrl(repo) {
if (/github\.com[:/]pikasTech\/HWLAB(?:\.git)?$/u.test(repo)) {
return "http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git";
return "http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git";
}
fail("no mirror resolver for HWLAB_BOOT_REPO; refusing direct runtime GitHub fallback");
fail("no Gitea resolver for HWLAB_BOOT_REPO; refusing direct runtime GitHub fallback");
}
function run(command, args, cwd, options = {}) {
+12 -12
View File
@@ -7,7 +7,7 @@ spec:
envArtifactGroups:
hwlab-go-runtime-env:
enabled: true
mode: env-reuse-git-mirror-checkout
mode: env-reuse-gitea-checkout
buildService: hwlab-user-billing
imageRepository: hwlab-go-runtime-env
cacheRepository: cache/hwlab-go-runtime-env
@@ -16,7 +16,7 @@ spec:
- hwlab-workbench-runtime
hwlab-ts-runtime-env:
enabled: true
mode: env-reuse-git-mirror-checkout
mode: env-reuse-gitea-checkout
buildService: hwlab-cloud-api
imageRepository: hwlab-ts-runtime-env
cacheRepository: cache/hwlab-ts-runtime-env
@@ -55,7 +55,7 @@ spec:
- internal/dev-entrypoint/artifact-runtime.mjs
envReuse:
enabled: true
mode: env-reuse-git-mirror-checkout
mode: env-reuse-gitea-checkout
nodeDepsPath: /opt/hwlab-env/node_modules
envIdentityFiles:
- package.json
@@ -81,7 +81,7 @@ spec:
- internal/dev-entrypoint/artifact-runtime.mjs
envReuse:
enabled: true
mode: env-reuse-git-mirror-checkout
mode: env-reuse-gitea-checkout
nodeDepsPath: /opt/hwlab-env/node_modules
envIdentityFiles:
- go.mod
@@ -108,7 +108,7 @@ spec:
- internal/dev-entrypoint/artifact-runtime.mjs
envReuse:
enabled: true
mode: env-reuse-git-mirror-checkout
mode: env-reuse-gitea-checkout
nodeDepsPath: /opt/hwlab-env/node_modules
envIdentityFiles:
- go.mod
@@ -133,7 +133,7 @@ spec:
- internal/dev-entrypoint/artifact-runtime.mjs
envReuse:
enabled: true
mode: env-reuse-git-mirror-checkout
mode: env-reuse-gitea-checkout
nodeDepsPath: /opt/hwlab-env/node_modules
envIdentityFiles:
- package.json
@@ -157,7 +157,7 @@ spec:
- internal/dev-entrypoint/artifact-runtime.mjs
envReuse:
enabled: true
mode: env-reuse-git-mirror-checkout
mode: env-reuse-gitea-checkout
nodeDepsPath: /opt/hwlab-env/node_modules
envIdentityFiles:
- package.json
@@ -180,7 +180,7 @@ spec:
- internal/dev-entrypoint/artifact-runtime.mjs
envReuse:
enabled: true
mode: env-reuse-git-mirror-checkout
mode: env-reuse-gitea-checkout
nodeDepsPath: /opt/hwlab-env/node_modules
envIdentityFiles:
- package.json
@@ -207,7 +207,7 @@ spec:
- internal/dev-entrypoint/artifact-runtime.mjs
envReuse:
enabled: true
mode: env-reuse-git-mirror-checkout
mode: env-reuse-gitea-checkout
nodeDepsPath: /opt/hwlab-env/node_modules
envIdentityFiles:
- package.json
@@ -230,7 +230,7 @@ spec:
- internal/dev-entrypoint/artifact-runtime.mjs
envReuse:
enabled: true
mode: env-reuse-git-mirror-checkout
mode: env-reuse-gitea-checkout
nodeDepsPath: /opt/hwlab-env/node_modules
envIdentityFiles:
- package.json
@@ -253,7 +253,7 @@ spec:
- internal/dev-entrypoint/artifact-runtime.mjs
envReuse:
enabled: true
mode: env-reuse-git-mirror-checkout
mode: env-reuse-gitea-checkout
nodeDepsPath: /opt/hwlab-env/node_modules
envIdentityFiles:
- package.json
@@ -275,7 +275,7 @@ spec:
- internal/dev-entrypoint/artifact-runtime.mjs
envReuse:
enabled: true
mode: env-reuse-git-mirror-checkout
mode: env-reuse-gitea-checkout
nodeDepsPath: /opt/hwlab-env/node_modules
envIdentityFiles:
- package.json
+2 -2
View File
@@ -32,7 +32,7 @@ const defaultRegistryPrefix =
const defaultBaseImage = process.env.HWLAB_DEV_BASE_IMAGE || null;
const defaultReportPath = tempReportPath("dev-artifacts.json");
const buildBackend = "buildkit";
const v02EnvReuseRuntimeMode = "env-reuse-git-mirror-checkout";
const v02EnvReuseRuntimeMode = "env-reuse-gitea-checkout";
const defaultBuildkitCommand = process.env.HWLAB_BUILDKIT_BUILDCTL || "buildctl-daemonless.sh";
const defaultBuildkitAddr = process.env.HWLAB_BUILDKIT_ADDR || null;
const defaultBuildCacheMode = process.env.HWLAB_BUILDKIT_CACHE_MODE || "registry";
@@ -1028,7 +1028,7 @@ function envReuseDockerfile(baseImage, labels = [], recipe, service = null) {
: `COPY ${normalizedRecipe.launcherPath} /usr/local/bin/hwlab-env-reuse-launcher.ts`,
`RUN ${stepTimingHelpers}; timed_stage "env-readiness" sh -c ${shellQuote(readinessScript)}`,
...labels.map(([name, value]) => `LABEL ${name}=${dockerfileQuote(value)}`),
"ENV HWLAB_RUNTIME_MODE=env-reuse-git-mirror-checkout",
"ENV HWLAB_RUNTIME_MODE=env-reuse-gitea-checkout",
...(goCacheEnabled ? [] : [`ENV HWLAB_RUNTIME_NODE_MODULES=${normalizedRecipe.runtimeNodeModulesPath}`]),
...goEnv,
"ENV PATH=/usr/local/bin:$PATH",
+1 -1
View File
@@ -97,7 +97,7 @@ function tryCreateNoDepsPlan(options) {
affected: false,
buildRequired: false,
skipImageBuild: true,
runtimeMode: "env-reuse-git-mirror-checkout",
runtimeMode: "env-reuse-gitea-checkout",
envReuse: true,
envChanged: false,
codeChanged: false,
+12 -12
View File
@@ -505,7 +505,7 @@ test("v02 planner rolls cloud-api service code changes without rebuilding servic
});
const cloudApi = plan.services.find((service) => service.serviceId === "hwlab-cloud-api");
assert.equal(cloudApi.affected, true);
assert.equal(cloudApi.runtimeMode, "env-reuse-git-mirror-checkout");
assert.equal(cloudApi.runtimeMode, "env-reuse-gitea-checkout");
assert.equal(cloudApi.envChanged, false);
assert.equal(cloudApi.codeChanged, true);
assert.equal(cloudApi.buildRequired, false);
@@ -818,7 +818,7 @@ test("artifact publish ignores stale external reports for envreuse services that
ciCdPlan: {},
services: [{
serviceId: "hwlab-cloud-web",
runtimeMode: "env-reuse-git-mirror-checkout",
runtimeMode: "env-reuse-gitea-checkout",
envReuse: true,
affected: false,
buildRequired: false,
@@ -846,7 +846,7 @@ test("artifact publish ignores stale external reports for envreuse services that
imageTag: "env-old",
digest,
repositoryDigest: `127.0.0.1:5000/hwlab/hwlab-cloud-web-env@${digest}`,
runtimeMode: "env-reuse-git-mirror-checkout",
runtimeMode: "env-reuse-gitea-checkout",
envReuse: true
}]
}
@@ -926,7 +926,7 @@ test("artifact publish accepts stale reused envreuse external reports for unchan
ciCdPlan: {},
services: [{
serviceId: "hwlab-cloud-web",
runtimeMode: "env-reuse-git-mirror-checkout",
runtimeMode: "env-reuse-gitea-checkout",
envReuse: true,
affected: false,
ciAffected: false,
@@ -955,7 +955,7 @@ test("artifact publish accepts stale reused envreuse external reports for unchan
imageTag: "env-stable",
digest,
repositoryDigest: `${image}@${digest}`,
runtimeMode: "env-reuse-git-mirror-checkout",
runtimeMode: "env-reuse-gitea-checkout",
envReuse: true,
affected: false,
ciAffected: false,
@@ -1290,7 +1290,7 @@ test("v02 planner marks cloud-web code-only change as env reuse rollout", async
services: ["hwlab-cloud-web"]
});
const cloudWeb = plan.services.find((service) => service.serviceId === "hwlab-cloud-web");
assert.equal(cloudWeb.runtimeMode, "env-reuse-git-mirror-checkout");
assert.equal(cloudWeb.runtimeMode, "env-reuse-gitea-checkout");
assert.equal(cloudWeb.envChanged, false);
assert.equal(cloudWeb.codeChanged, true);
assert.equal(cloudWeb.buildRequired, false);
@@ -1348,7 +1348,7 @@ test("v02 planner enables env reuse for all retained runtime services", async ()
assert.deepEqual(plan.buildServices, []);
assert.equal(plan.imageBuildRequired, false);
for (const service of plan.services) {
assert.equal(service.runtimeMode, "env-reuse-git-mirror-checkout", service.serviceId);
assert.equal(service.runtimeMode, "env-reuse-gitea-checkout", service.serviceId);
assert.equal(service.envReuse, true, service.serviceId);
assert.equal(service.envChanged, false, service.serviceId);
assert.equal(service.codeChanged, true, service.serviceId);
@@ -1541,7 +1541,7 @@ test("v03 planner reads env reuse declarations and catalog from v03 lane config"
assert.deepEqual(plan.compatibility.envReuseServiceIds, ["hwlab-cloud-api", "hwlab-cloud-web"]);
assert.equal(plan.services.every((service) => service.envReuseRecipe.downloadStack.npmFetchTimeoutMs === 300000), true);
for (const service of plan.services) {
assert.equal(service.runtimeMode, "env-reuse-git-mirror-checkout", service.serviceId);
assert.equal(service.runtimeMode, "env-reuse-gitea-checkout", service.serviceId);
assert.equal(service.envReuse, true, service.serviceId);
assert.equal(service.bootRepo, "git@github.com:pikasTech/HWLAB.git", service.serviceId);
assert.equal(service.bootSh, `deploy/runtime/boot/${service.serviceId}.sh`, service.serviceId);
@@ -1565,7 +1565,7 @@ test("v03 TaskTree API and worker share one TypeScript environment artifact", as
assert.equal(group.services?.includes(serviceId), true, `${serviceId} shared environment membership`);
assert.deepEqual(services[serviceId]?.runtimeReuse?.envIdentity?.paths, expectedEnvIdentity, `${serviceId} runtime env identity`);
assert.deepEqual(services[serviceId]?.envReuse?.envIdentityFiles, expectedEnvIdentity, `${serviceId} env reuse identity`);
assert.equal(services[serviceId]?.envReuse?.mode, "env-reuse-git-mirror-checkout", `${serviceId} runtime mode`);
assert.equal(services[serviceId]?.envReuse?.mode, "env-reuse-gitea-checkout", `${serviceId} runtime mode`);
}
assert.deepEqual(services["hwlab-tasktree-api"].runtimeReuse.codeIdentity.paths, [
"cmd/hwlab-tasktree-api",
@@ -1874,7 +1874,7 @@ async function addGoEnvReuseFixture(repo) {
envArtifactGroups: {
"hwlab-go-runtime-env": {
enabled: true,
mode: "env-reuse-git-mirror-checkout",
mode: "env-reuse-gitea-checkout",
buildService: "hwlab-user-billing",
imageRepository: "hwlab-go-runtime-env",
cacheRepository: "cache/hwlab-go-runtime-env",
@@ -1889,7 +1889,7 @@ async function addGoEnvReuseFixture(repo) {
},
envReuse: {
enabled: true,
mode: "env-reuse-git-mirror-checkout",
mode: "env-reuse-gitea-checkout",
envIdentityFiles: ["go.mod", "go.sum", "deploy/runtime/launcher/hwlab-go-env-reuse-launcher.mjs"]
}
}]))
@@ -2048,7 +2048,7 @@ function createCatalogFixture(mode, options = {}) {
componentInputHash: options.cloudApiComponentInputHash ?? options.componentInputHashes?.[serviceId]
} : {}),
...(envReuseCatalogServiceIds.has(serviceId) ? {
runtimeMode: "env-reuse-git-mirror-checkout",
runtimeMode: "env-reuse-gitea-checkout",
envReuse: true,
environmentImage: `127.0.0.1:5000/hwlab/${serviceId}-env:env-abc1234`,
environmentDigest: digest,
+1 -5
View File
@@ -47,10 +47,7 @@ import {
usage,
workbenchRuntimeRedisConfigForProfile
} from "./src/gitops-render/core.mjs";
import {
devopsInfraGitMirrorManifest,
registryManifest
} from "./src/gitops-render/infra-manifests.mjs";
import { registryManifest } from "./src/gitops-render/infra-manifests.mjs";
import {
argoApplication,
argoProject,
@@ -228,7 +225,6 @@ async function plannedFiles(args) {
putJson("source.json", sourceDescriptor);
putText("README.md", readme({ source, args, deploy }));
putJson("registry/registry.yaml", registryManifest());
if (isRuntimeLane(args.lane)) putJson("devops-infra/git-mirror.yaml", devopsInfraGitMirrorManifest(args, deploy));
putJson(`${settings.tektonDir}/rbac.yaml`, tektonRbac(args, deploy));
putJson(`${settings.tektonDir}/tasks.yaml`, { apiVersion: "v1", kind: "List", items: tektonTasks(args, deploy) });
putJson(`${settings.tektonDir}/pipeline.yaml`, tektonPipeline(args, deploy));
+27 -79
View File
@@ -180,11 +180,11 @@ async function runtimeNoopDecision(gitopsPromoteScript, { oldRuntime, newRuntime
}
}
test("opencode APK proxy prefers explicit gitMirror proxyUrl", async () => {
test("opencode APK proxy prefers the lane egress proxy URL", async () => {
assert.equal(configString(" value "), "value");
assert.equal(opencodeEgressProxyUrlForProfile({ lanes: { v03: { gitMirror: { egressProxy: { required: true, proxyUrl: "http://10.42.0.1:10808", namespace: "platform-infra", serviceName: "jd01-host-proxy", port: 10808 } } } } }, "v03"), "http://10.42.0.1:10808");
assert.equal(opencodeEgressProxyUrlForProfile({ lanes: { v03: { gitMirror: { egressProxy: { required: true, namespace: "platform-infra", serviceName: "sub2api-egress-proxy", port: 10808 } } } } }, "v03"), "http://sub2api-egress-proxy.platform-infra.svc.cluster.local:10808");
assert.equal(opencodeEgressProxyUrlForProfile({ lanes: { v03: { gitMirror: { egressProxy: { required: false, proxyUrl: "http://10.42.0.1:10808" } } } } }, "v03"), "");
assert.equal(opencodeEgressProxyUrlForProfile({ lanes: { v03: { egressProxy: { required: true, proxyUrl: "http://10.42.0.1:10808", namespace: "platform-infra", serviceName: "jd01-host-proxy", port: 10808 } } } }, "v03"), "http://10.42.0.1:10808");
assert.equal(opencodeEgressProxyUrlForProfile({ lanes: { v03: { egressProxy: { required: true, namespace: "platform-infra", serviceName: "sub2api-egress-proxy", port: 10808 } } } }, "v03"), "http://sub2api-egress-proxy.platform-infra.svc.cluster.local:10808");
assert.equal(opencodeEgressProxyUrlForProfile({ lanes: { v03: { egressProxy: { required: false, proxyUrl: "http://10.42.0.1:10808" } } } }, "v03"), "");
});
test("v02 render follows TypeScript runtime checks and does not self-patch bootstrap RBAC", async () => {
@@ -235,14 +235,14 @@ test("v02 render follows TypeScript runtime checks and does not self-patch boots
const planArtifacts = taskByName(pipelineJson, "plan-artifacts");
const planArtifactsScript = planArtifacts.taskSpec.steps[0].script;
assert.match(planArtifactsScript, /node scripts\/ci\/restore-artifact-catalog\.mjs/u);
assert.match(planArtifactsScript, /HWLAB_GIT_READ_URL="\$\(params\.git-read-url\)"/u);
assert.match(planArtifactsScript, /HWLAB_GIT_READ_URL="\$\(params\.gitops-read-url\)"/u);
assert.match(planArtifactsScript, /HWLAB_SERVICES="\$\(params\.services\)"/u);
assert.match(planArtifactsScript, /rolloutWithoutImageBuildServices/u);
assert.match(planArtifactsScript, /serviceReusedCount/u);
assert.match(planArtifactsScript, /artifactCatalog: plan\.artifactCatalog/u);
assert.match(planArtifactsScript, /--base-ref "\$\(params\.release-base-commit\)"/u);
assert.match(planArtifactsScript, /verify-reviewed-plan\.mjs/u);
assert.ok(planArtifacts.taskSpec.params.some((param) => param.name === "git-read-url"));
assert.ok(planArtifacts.taskSpec.params.some((param) => param.name === "gitops-read-url"));
assert.ok(planArtifacts.taskSpec.params.some((param) => param.name === "gitops-branch"));
assert.ok(planArtifacts.params.some((param) => param.name === "lane" && param.value === "$(params.lane)"));
assert.ok(pipelineJson.spec.params.some((param) => param.name === "runtime-ready-timeout-ms" && param.default === "60000"));
@@ -272,12 +272,13 @@ test("v02 render follows TypeScript runtime checks and does not self-patch boots
assert.match(gitopsPromoteScript, /ARGO_APPLICATION="hwlab-node-\$lane" ARGO_FIELD_MANAGER="hwlab-\$lane-gitops-promote"/u);
assert.match(gitopsPromoteScript, /argocd\.argoproj\.io\/refresh": "hard"/u);
assert.match(gitopsPromoteScript, /applications\/" \+ encodeURIComponent\(application\) \+ "\?fieldManager=" \+ encodeURIComponent\(fieldManager\)/u);
assert.match(gitopsPromoteScript, /BatchMode=yes/u);
assert.match(gitopsPromoteScript, /ConnectTimeout=10/u);
assert.match(gitopsPromoteScript, /GIT_ASKPASS_REQUIRE=force/u);
assert.match(gitopsPromoteScript, /test -s \/workspace\/gitea-auth\/token/u);
assert.match(gitopsPromoteScript, /git_timed "source-head-\$phase" 45 git ls-remote/u);
assert.match(gitopsPromoteScript, /"event":"git-ssh-setup","phase":"gitops-promote","status":"skipped"/u);
assert.doesNotMatch(gitopsPromoteScript, /git-ssh-setup/u);
assert.match(gitopsPromoteScript, /source_head_url="\$\(params\.git-read-url\)"/u);
assert.match(gitopsPromoteScript, /gitops_write_url="\$\(params\.git-write-url\)"/u);
assert.match(gitopsPromoteScript, /gitops_read_url="\$\(params\.gitops-read-url\)"/u);
assert.match(gitopsPromoteScript, /git clone --no-checkout "\$gitops_read_url"/u);
assert.match(gitopsPromoteScript, /git_timed gitops-push 120 git push origin/u);
assert.doesNotMatch(gitopsPromoteScript, /gitops-promote-proxy-preflight/u);
@@ -285,13 +286,14 @@ test("v02 render follows TypeScript runtime checks and does not self-patch boots
const prepareSource = taskByName(pipelineJson, "prepare-source");
assert.ok(prepareSource.taskSpec.params.some((param) => param.name === "git-read-url"));
assert.ok(prepareSource.taskSpec.params.some((param) => param.name === "gitops-read-url"));
assert.ok(prepareSource.params.some((param) => param.name === "git-read-url" && param.value === "$(params.git-read-url)"));
const prepareSourceScript = prepareSource.taskSpec.steps[0].script;
assert.match(prepareSourceScript, /git_read_url="\$\(params\.git-read-url\)"/u);
assert.match(prepareSourceScript, /"event":"git-ssh-setup","phase":"prepare-source","status":"skipped"/u);
assert.doesNotMatch(prepareSourceScript, /git-ssh-setup/u);
assert.match(prepareSourceScript, /git_timed source-clone 180 git clone --branch "\$\(params\.source-branch\)" "\$git_read_url"/u);
assert.match(prepareSourceScript, /git_timed catalog-ls-remote 45 git ls-remote --exit-code --heads "\$git_read_url"/u);
assert.match(prepareSourceScript, /git remote set-url gitops-catalog "\$git_read_url"/u);
assert.match(prepareSourceScript, /git_timed catalog-ls-remote 45 git ls-remote --exit-code --heads "\$gitops_read_url"/u);
assert.match(prepareSourceScript, /git remote set-url gitops-catalog "\$gitops_read_url"/u);
assert.match(prepareSourceScript, /"event":"prepare-source-dependencies","status":"skipped","reason":"renderer-dependency-install-disabled","dependency":"yaml"/u);
assert.doesNotMatch(prepareSourceScript, /require\.resolve\("yaml"\)/u);
assert.doesNotMatch(prepareSourceScript, /npm ci --ignore-scripts --no-audit --prefer-offline/u);
@@ -308,71 +310,17 @@ test("v02 render follows TypeScript runtime checks and does not self-patch boots
const pipelineGitReadParam = pipelineJson.spec.params.find((param) => param.name === "git-read-url");
assert.equal(pipelineGitReadParam?.default, "http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git");
const pipelineGitWriteParam = pipelineJson.spec.params.find((param) => param.name === "git-write-url");
assert.equal(pipelineGitWriteParam?.default, "http://git-mirror-write.devops-infra.svc.cluster.local/pikasTech/HWLAB.git");
assert.equal(pipelineGitWriteParam?.default, "http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git");
const pipelineGitopsReadParam = pipelineJson.spec.params.find((param) => param.name === "gitops-read-url");
assert.equal(pipelineGitopsReadParam?.default, pipelineGitReadParam?.default);
assert.ok(pipelineJson.spec.workspaces.some((workspace) => workspace.name === "gitea-auth"));
assert.ok(!pipelineJson.spec.workspaces.some((workspace) => workspace.name === "git-ssh"));
const sourceDescriptor = JSON.parse(await readFile(path.join(outDir, "source.json"), "utf8"));
assert.equal(sourceDescriptor.gitReadUrl, "http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git");
assert.equal(sourceDescriptor.gitWriteUrl, "http://git-mirror-write.devops-infra.svc.cluster.local/pikasTech/HWLAB.git");
assert.equal(sourceDescriptor.gitWriteUrl, "http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git");
const gitMirror = await readFile(path.join(outDir, "devops-infra", "git-mirror.yaml"), "utf8");
assert.match(gitMirror, /"name": "devops-infra"/u);
assert.match(gitMirror, /"name": "git-mirror-http"/u);
assert.match(gitMirror, /"name": "git-mirror-write"/u);
assert.match(gitMirror, /"name": "git-mirror-sync-script"/u);
assert.match(gitMirror, /git-receive-pack/u);
assert.match(gitMirror, /last-flush\.json/u);
assert.match(gitMirror, /last-write\.json/u);
assert.match(gitMirror, /ssh:\/\/git@ssh\.github\.com:443\/pikasTech\/HWLAB\.git/u);
assert.doesNotMatch(gitMirror, /"kind": "CronJob"/u);
assert.doesNotMatch(gitMirror, /"name": "git-mirror-hwlab-sync"/u);
const gitMirrorJson = JSON.parse(gitMirror);
const gitMirrorConfigMap = gitMirrorJson.items.find((item) => item.kind === "ConfigMap" && item.metadata?.name === "git-mirror-sync-script");
const gitMirrorScripts = gitMirrorConfigMap?.data ?? {};
const gitMirrorScript = gitMirrorScripts["sync.sh"] ?? "";
assert.match(gitMirrorScript, /uploadpack\.allowReachableSHA1InWant true/u);
assert.match(gitMirrorScript, /uploadpack\.allowAnySHA1InWant true/u);
assert.match(gitMirrorScript, /refs\/heads\/v0\.2:refs\/mirror-stage\/heads\/v0\.2/u);
assert.match(gitMirrorScript, /refs\/heads\/v0\.2-gitops:refs\/mirror-stage\/heads\/v0\.2-gitops/u);
assert.match(gitMirrorScript, /refs\/heads\/v0\.3:refs\/mirror-stage\/heads\/v0\.3/u);
assert.match(gitMirrorScript, /refs\/heads\/v0\.3-gitops:refs\/mirror-stage\/heads\/v0\.3-gitops/u);
assert.match(gitMirrorScript, /refs\/heads\/release:refs\/mirror-stage\/heads\/release/u);
assert.match(gitMirrorScript, /refs\/heads\/release-gitops:refs\/mirror-stage\/heads\/release-gitops/u);
assert.doesNotMatch(gitMirrorScript, /refs\/heads\/G14:refs\/mirror-stage\/heads\/G14/u);
assert.doesNotMatch(gitMirrorScript, /refs\/heads\/G14-gitops:refs\/mirror-stage\/heads\/G14-gitops/u);
assert.doesNotMatch(gitMirrorScript, /refs\/heads\/\*:refs\/mirror-stage\/heads\/\*/u);
assert.match(gitMirrorScript, /git -C "\$repo_path" update-ref "refs\/heads\/\$name" "\$sha"/u);
assert.match(gitMirrorScript, /source_snapshot_ref_prefix='refs\/unidesk\/snapshots\/hwlab-node-runtime'/u);
assert.match(gitMirrorScript, /source_stage_ref="\$\{source_snapshot_ref_prefix%\/\}\/\$source_branch\/\$source_sha"/u);
assert.match(gitMirrorScript, /git -C "\$repo_path" update-ref "\$source_stage_ref" "\$source_sha"/u);
assert.match(gitMirrorScript, /git-mirror-sync-timing/u);
assert.match(gitMirrorScript, /emit_timing fetch succeeded/u);
assert.match(gitMirrorScript, /emit_timing total succeeded/u);
assert.match(gitMirrorScript, /sourceInSync/u);
assert.match(gitMirrorScript, /sourceSnapshots/u);
assert.match(gitMirrorScript, /refs\/unidesk\/snapshots\/hwlab-node-runtime/u);
assert.match(gitMirrorScript, /refs\/mirror-stage\/heads\/v0\.2/u);
assert.match(gitMirrorScript, /refs\/mirror-stage\/heads\/v0\.3/u);
assert.match(gitMirrorScript, /for gitops_branch in 'release-gitops' 'v0\.2-gitops' 'v0\.3-gitops'/u);
assert.match(gitMirrorScript, /keeps local \$name ahead of GitHub/u);
assert.doesNotMatch(gitMirror, /not allowlisted/u);
assert.doesNotMatch(gitMirror, /path allowlist/u);
assert.doesNotMatch(gitMirror, /outside .* GitOps outputs/u);
assert.ok((gitMirrorJson.items || []).every((item) => item.kind !== "Secret"));
assert.ok((gitMirrorJson.items || []).every((item) => item.kind !== "CronJob"));
assert.ok((gitMirrorJson.items || []).every((item) => !item.data?.["ssh-privatekey"]));
const scriptCheckDir = path.join(outDir, "script-check");
await mkdir(scriptCheckDir, { recursive: true });
for (const scriptName of ["http.mjs", "sync.sh", "install-hooks.sh", "flush.sh"]) {
assert.ok(gitMirrorScripts[scriptName], `missing git mirror script ${scriptName}`);
await writeFile(path.join(scriptCheckDir, scriptName), gitMirrorScripts[scriptName]);
}
const httpCheck = spawnSync("node", ["--check", path.join(scriptCheckDir, "http.mjs")], { encoding: "utf8" });
assert.equal(httpCheck.status, 0, httpCheck.stderr || httpCheck.stdout);
for (const scriptName of ["sync.sh", "install-hooks.sh", "flush.sh"]) {
const shellCheck = spawnSync("sh", ["-n", path.join(scriptCheckDir, scriptName)], { encoding: "utf8" });
assert.equal(shellCheck.status, 0, `${scriptName}: ${shellCheck.stderr || shellCheck.stdout}`);
}
await assert.rejects(() => readFile(path.join(outDir, "devops-infra", "git-mirror.yaml"), "utf8"), { code: "ENOENT" });
const removedServiceIds = [
"hwlab-agent-mgr",
@@ -501,11 +449,11 @@ test("v02 render follows TypeScript runtime checks and does not self-patch boots
const cloudApiEnv = cloudApiContainer.env;
const cloudApiCommit = cloudApiEnv.find((entry) => entry.name === "HWLAB_COMMIT_ID")?.value;
const cloudApiBootEnv = new Map(cloudApiEnv.map((entry) => [entry.name, entry.value]));
assert.equal(cloudApiBootEnv.get("HWLAB_RUNTIME_MODE"), "env-reuse-git-mirror-checkout");
assert.equal(cloudApiBootEnv.get("HWLAB_RUNTIME_MODE"), "env-reuse-gitea-checkout");
assert.equal(cloudApiBootEnv.get("HWLAB_BOOT_SH"), "deploy/runtime/boot/hwlab-cloud-api.sh");
const codexForwarderEnv = new Map(codexForwarder.env.map((entry) => [entry.name, entry.value]));
assert.equal(codexForwarder.command, undefined);
assert.equal(codexForwarderEnv.get("HWLAB_RUNTIME_MODE"), "env-reuse-git-mirror-checkout");
assert.equal(codexForwarderEnv.get("HWLAB_RUNTIME_MODE"), "env-reuse-gitea-checkout");
assert.equal(codexForwarderEnv.get("HWLAB_BOOT_SH"), "deploy/runtime/boot/hwlab-codex-api-responses-forwarder.sh");
assert.equal(codexForwarderEnv.get("HWLAB_BOOT_COMMIT"), cloudApiCommit);
assert.equal(codexForwarderEnv.get("HWLAB_BOOT_READ_URL"), "http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git");
@@ -527,7 +475,7 @@ test("v02 render follows TypeScript runtime checks and does not self-patch boots
assert.ok(deepseekResponsesBridge, "expected DeepSeek responses bridge container");
const deepseekBridgeEnv = new Map(deepseekResponsesBridge.env.map((entry) => [entry.name, entry.value]));
assert.equal(deepseekResponsesBridge.command, undefined);
assert.equal(deepseekBridgeEnv.get("HWLAB_RUNTIME_MODE"), "env-reuse-git-mirror-checkout");
assert.equal(deepseekBridgeEnv.get("HWLAB_RUNTIME_MODE"), "env-reuse-gitea-checkout");
assert.equal(deepseekBridgeEnv.get("HWLAB_BOOT_SH"), "deploy/runtime/boot/hwlab-deepseek-responses-bridge.sh");
assert.equal(deepseekBridgeEnv.get("HWLAB_BOOT_COMMIT"), cloudApiCommit);
assert.equal(deepseekBridgeEnv.get("HWLAB_BOOT_READ_URL"), "http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git");
@@ -620,7 +568,7 @@ test("v02 render follows TypeScript runtime checks and does not self-patch boots
const pipelineRunSample = await readFile(path.join(outDir, "tekton-v02", "pipelinerun.sample.yaml"), "utf8");
assert.match(pipelineRunSample, /"name": "git-read-url"[\s\S]{0,180}gitea-http\.devops-infra\.svc\.cluster\.local:3000\/mirrors\/pikasTech-HWLAB\.git/u);
assert.match(pipelineRunSample, /"name": "git-write-url"[\s\S]{0,140}git-mirror-write\.devops-infra\.svc\.cluster\.local/u);
assert.match(pipelineRunSample, /"name": "git-write-url"[\s\S]{0,180}gitea-http\.devops-infra\.svc\.cluster\.local:3000\/mirrors\/pikasTech-HWLAB\.git/u);
assert.match(pipelineRunSample, /"name": "revision"[\s\S]{0,80}"value": "[0-9a-f]{40}"/u);
assert.match(pipelineRunSample, /"name": "source-branch"[\s\S]{0,80}"value": "v0\.2"/u);
} finally {
@@ -648,7 +596,7 @@ test("v03 render keeps node identity as data instead of generated structure", as
artifactState: "ready",
services: [{
serviceId: "hwlab-cloud-api",
runtimeMode: "env-reuse-git-mirror-checkout",
runtimeMode: "env-reuse-gitea-checkout",
envReuse: true,
sourceCommitId: staleRevision,
commitId: staleRevision,
@@ -848,7 +796,7 @@ test("v03 render includes JD01 secret-plane smoke on JD01 gitops root", { timeou
artifactState: "ready",
services: [{
serviceId: "hwlab-cloud-api",
runtimeMode: "env-reuse-git-mirror-checkout",
runtimeMode: "env-reuse-gitea-checkout",
envReuse: true,
sourceCommitId: staleRevision,
commitId: staleRevision,
+1 -1
View File
@@ -26,7 +26,7 @@ const commitPattern = /^[a-f0-9]{7,40}$/;
const runtimeArtifactLanePattern = /^[a-z][a-z0-9-]*$/u;
const publishReadyStatuses = new Set(["published", "reused"]);
const v02EnvReuseRuntimeMode = "env-reuse-git-mirror-checkout";
const v02EnvReuseRuntimeMode = "env-reuse-gitea-checkout";
function isRuntimeArtifactLane(lane) {
return runtimeArtifactLanePattern.test(String(lane ?? ""));
+2 -2
View File
@@ -431,7 +431,7 @@ test("v03 refresh accepts runtime lane config and env-reuse image tags", async (
serviceId,
status: "published",
artifactRequired: true,
runtimeMode: "env-reuse-git-mirror-checkout",
runtimeMode: "env-reuse-gitea-checkout",
envReuse: true,
commitId,
sourceCommitId: commitId,
@@ -476,7 +476,7 @@ test("v03 refresh accepts runtime lane config and env-reuse image tags", async (
assert.equal(payload.endpoint, "https://hwlab-v03.74-48-78-17.nip.io");
assert.equal(payload.requiredServiceCount, serviceIds.length);
assert.deepEqual(payload.services.map((service) => service.serviceId), serviceIds);
assert.equal(payload.services.every((service) => service.runtimeMode === "env-reuse-git-mirror-checkout"), true);
assert.equal(payload.services.every((service) => service.runtimeMode === "env-reuse-gitea-checkout"), true);
assert.equal(payload.services.every((service) => service.imageTag.startsWith("env-")), true);
});
+1 -1
View File
@@ -15,7 +15,7 @@ import { parseStructuredConfig, readStructuredFileIfPresent } from "./structured
const execFileAsync = promisify(execFile);
export const CI_PLAN_VERSION = "v1";
export const ENV_REUSE_RUNTIME_MODE = "env-reuse-git-mirror-checkout";
export const ENV_REUSE_RUNTIME_MODE = "env-reuse-gitea-checkout";
export const V02_ENV_REUSE_RUNTIME_MODE = ENV_REUSE_RUNTIME_MODE;
export const DEFAULT_RUNTIME_DEP_PATHS = Object.freeze([
+4 -4
View File
@@ -29,10 +29,10 @@ const defaultOutDir = "deploy/gitops/node";
const defaultRegistryPrefix = process.env.HWLAB_NODE_REGISTRY_PREFIX || "127.0.0.1:5000/hwlab";
const defaultSourceRepo = process.env.HWLAB_NODE_GIT_URL || "git@github.com:pikasTech/HWLAB.git";
const defaultGitReadUrl = process.env.HWLAB_NODE_GIT_READ_URL || null;
const defaultV02GitReadUrl = process.env.HWLAB_V02_GIT_READ_URL || "http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git";
const defaultV02GitWriteUrl = process.env.HWLAB_V02_GIT_WRITE_URL || "http://git-mirror-write.devops-infra.svc.cluster.local/pikasTech/HWLAB.git";
const defaultV02GitReadUrl = process.env.HWLAB_V02_GIT_READ_URL || "http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git";
const defaultV02GitWriteUrl = process.env.HWLAB_V02_GIT_WRITE_URL || defaultV02GitReadUrl;
const defaultRuntimeLaneGitReadUrl = process.env.HWLAB_RUNTIME_LANE_GIT_READ_URL || "http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git";
const defaultRuntimeLaneGitWriteUrl = process.env.HWLAB_RUNTIME_LANE_GIT_WRITE_URL || defaultV02GitWriteUrl;
const defaultRuntimeLaneGitWriteUrl = process.env.HWLAB_RUNTIME_LANE_GIT_WRITE_URL || defaultRuntimeLaneGitReadUrl;
const defaultBranch = process.env.HWLAB_NODE_BRANCH || "node";
const defaultGitopsBranch = process.env.HWLAB_NODE_GITOPS_BRANCH || "node-gitops";
const defaultCatalogPath = process.env.HWLAB_ARTIFACT_CATALOG_PATH || "deploy/artifact-catalog.dev.json";
@@ -44,7 +44,7 @@ const defaultV02RuntimeEndpoint = process.env.HWLAB_V02_RUNTIME_ENDPOINT || "htt
const defaultV02WebEndpoint = process.env.HWLAB_V02_WEB_ENDPOINT || "https://hwlab.74-48-78-17.nip.io";
const defaultProxyUrl = process.env.HWLAB_NODE_PROXY_URL || "http://127.0.0.1:10808";
const defaultAllProxyUrl = process.env.HWLAB_NODE_ALL_PROXY_URL || "socks5h://127.0.0.1:10808";
const defaultNoProxy = process.env.HWLAB_NODE_NO_PROXY || "hyueapi.com,.hyueapi.com,127.0.0.1,localhost,::1,10.0.0.0/8,10.42.0.0/16,10.43.0.0/16,192.168.0.0/16,.svc,.cluster.local,kubernetes.default.svc,registry.hwlab-ci.svc,hwlab-registry.hwlab-ci.svc,git-mirror-http,git-mirror-http.devops-infra,git-mirror-http.devops-infra.svc,git-mirror-http.devops-infra.svc.cluster.local,git-mirror-write,git-mirror-write.devops-infra,git-mirror-write.devops-infra.svc,git-mirror-write.devops-infra.svc.cluster.local";
const defaultNoProxy = process.env.HWLAB_NODE_NO_PROXY || "hyueapi.com,.hyueapi.com,127.0.0.1,localhost,::1,10.0.0.0/8,10.42.0.0/16,10.43.0.0/16,192.168.0.0/16,.svc,.cluster.local,kubernetes.default.svc,registry.hwlab-ci.svc,hwlab-registry.hwlab-ci.svc,gitea-http,gitea-http.devops-infra,gitea-http.devops-infra.svc,gitea-http.devops-infra.svc.cluster.local";
const ciToolsRunnerImage = process.env.HWLAB_NODE_CI_TOOLS_IMAGE || "127.0.0.1:5000/hwlab/hwlab-ci-node-tools:node22-alpine-bun-v1";
const buildkitRunnerImage = process.env.HWLAB_NODE_BUILDKIT_IMAGE || "moby/buildkit:rootless";
const defaultDevBaseImage = process.env.HWLAB_NODE_DEV_BASE_IMAGE || "127.0.0.1:5000/hwlab/hwlab-node20-base:20-bookworm-slim";
+1 -209
View File
@@ -1,17 +1,3 @@
import assert from "node:assert/strict";
import {
ciToolsRunnerImage,
configObject,
defaultOutDir,
label,
optionalConfigString,
requiredConfigPositiveInteger,
requiredConfigString,
runtimeLaneMirrorSpecs
} from "./core.mjs";
import { renderTemplate, shellSingleQuote } from "./tekton-scripts.mjs";
function registryManifest() {
return {
apiVersion: "v1",
@@ -55,200 +41,6 @@ function registryManifest() {
};
}
function uniqueStrings(values) {
return Array.from(new Set(values.filter((value) => typeof value === "string" && value.length > 0)));
}
function renderGitMirrorSshPrelude({ direct, proxySummary, proxyCommand, proxyUrl, proxyNoProxy }) {
if (direct) {
return renderTemplate("git-mirror-ssh-direct.sh", {
"__HWLAB_PROXY_SUMMARY_SHELL__": shellSingleQuote(proxySummary)
});
}
return renderTemplate("git-mirror-ssh-proxied.sh", {
"// __HWLAB_GIT_MIRROR_PROXY_CONNECT_MJS__": renderTemplate("git-mirror-proxy-connect.mjs"),
"__HWLAB_PROXY_SUMMARY_SHELL__": shellSingleQuote(proxySummary),
"__HWLAB_PROXY_COMMAND_SHELL__": shellSingleQuote(proxyCommand),
"__HWLAB_PROXY_URL_SHELL__": shellSingleQuote(proxyUrl),
"__HWLAB_PROXY_NO_PROXY_SHELL__": shellSingleQuote(proxyNoProxy)
});
}
function gitMirrorConfigScripts({
fetchConfigCommands,
gitMirrorSshPrelude,
gitopsBranchArgs,
gitopsBranchesJson,
mirrorBranchesJson,
requiredRefArgs,
sourceBranchArgs,
sourceBranchesJson,
sourceSnapshotRefPrefix
}) {
const gitopsBranchesJsonShell = shellSingleQuote(gitopsBranchesJson);
return {
"sync.sh": renderTemplate("git-mirror-sync.sh", {
"# __HWLAB_GIT_MIRROR_SSH_PRELUDE__": gitMirrorSshPrelude,
"__HWLAB_SOURCE_SNAPSHOT_REF_PREFIX_SHELL__": shellSingleQuote(sourceSnapshotRefPrefix),
"# __HWLAB_FETCH_CONFIG_COMMANDS__": fetchConfigCommands,
"__HWLAB_REQUIRED_REF_ARGS__": requiredRefArgs,
"__HWLAB_SOURCE_BRANCH_ARGS__": sourceBranchArgs,
"__HWLAB_GITOPS_BRANCH_ARGS__": gitopsBranchArgs,
"__HWLAB_MIRROR_BRANCHES_JSON_SHELL__": shellSingleQuote(mirrorBranchesJson),
"__HWLAB_SOURCE_BRANCHES_JSON_SHELL__": shellSingleQuote(sourceBranchesJson),
"__HWLAB_GITOPS_BRANCHES_JSON_SHELL__": gitopsBranchesJsonShell
}),
"install-hooks.sh": renderTemplate("git-mirror-install-hooks.sh", {
"__HWLAB_GITOPS_BRANCHES_JSON_SHELL__": gitopsBranchesJsonShell
}),
"flush.sh": renderTemplate("git-mirror-flush.sh", {
"# __HWLAB_GIT_MIRROR_SSH_PRELUDE__": gitMirrorSshPrelude,
"__HWLAB_GITOPS_BRANCH_ARGS__": gitopsBranchArgs,
"__HWLAB_GITOPS_BRANCHES_JSON_SHELL__": gitopsBranchesJsonShell
}),
"http.mjs": renderTemplate("git-mirror-http.mjs")
};
}
function devopsInfraGitMirrorManifest(args, deploy) {
const mirrorSpecs = runtimeLaneMirrorSpecs(deploy, { defaultNodeId: args.nodeId, defaultGitopsRoot: defaultOutDir });
assert.ok(mirrorSpecs.length > 0, "runtime lane git mirror requires at least one deploy.lanes entry");
const sourceBranches = uniqueStrings(mirrorSpecs.map((spec) => spec.sourceBranch));
const gitopsBranches = uniqueStrings(mirrorSpecs.map((spec) => spec.gitopsBranch));
const mirrorBranches = uniqueStrings([...sourceBranches, ...gitopsBranches]);
const fetchConfigCommands = mirrorBranches
.map((branch) => `git -C "$repo_path" config --add remote.origin.fetch '+refs/heads/${branch}:refs/mirror-stage/heads/${branch}'`)
.join("\n");
const requiredRefArgs = mirrorBranches.map((branch) => shellSingleQuote(`refs/mirror-stage/heads/${branch}`)).join(" ");
const sourceBranchArgs = sourceBranches.map((branch) => shellSingleQuote(branch)).join(" ");
const gitopsBranchArgs = gitopsBranches.map((branch) => shellSingleQuote(branch)).join(" ");
const mirrorBranchesJson = JSON.stringify(mirrorBranches);
const sourceBranchesJson = JSON.stringify(sourceBranches);
const gitopsBranchesJson = JSON.stringify(gitopsBranches);
const sourceSnapshotRefPrefix = "refs/unidesk/snapshots/hwlab-node-runtime";
const laneConfig = configObject(configObject(deploy.lanes, "deploy.lanes")[args.lane], `deploy.lanes.${args.lane}`);
const gitMirrorConfig = configObject(laneConfig.gitMirror, `deploy.lanes.${args.lane}.gitMirror`);
const gitMirrorProxy = configObject(gitMirrorConfig.egressProxy, `deploy.lanes.${args.lane}.gitMirror.egressProxy`);
const gitMirrorProxyMode = requiredConfigString(gitMirrorProxy, "mode", `deploy.lanes.${args.lane}.gitMirror.egressProxy`);
assert.ok(gitMirrorProxyMode === "node-global" || gitMirrorProxyMode === "host-proxy-client" || gitMirrorProxyMode === "direct", `deploy.lanes.${args.lane}.gitMirror.egressProxy.mode must be node-global, host-proxy-client, or direct`);
const useDirectGitMirrorProxy = gitMirrorProxyMode === "direct";
const useHostGitMirrorProxy = gitMirrorProxyMode === "host-proxy-client";
const gitMirrorNamespace = requiredConfigString(gitMirrorConfig, "namespace", `deploy.lanes.${args.lane}.gitMirror`);
const gitMirrorReadName = requiredConfigString(gitMirrorConfig, "serviceReadName", `deploy.lanes.${args.lane}.gitMirror`);
const gitMirrorWriteName = requiredConfigString(gitMirrorConfig, "serviceWriteName", `deploy.lanes.${args.lane}.gitMirror`);
const gitMirrorCachePvcName = requiredConfigString(gitMirrorConfig, "cachePvcName", `deploy.lanes.${args.lane}.gitMirror`);
const gitMirrorCachePvcStorage = requiredConfigString(gitMirrorConfig, "cachePvcStorage", `deploy.lanes.${args.lane}.gitMirror`);
const gitMirrorCacheHostPath = optionalConfigString(gitMirrorConfig, "cacheHostPath", `deploy.lanes.${args.lane}.gitMirror`);
const gitMirrorServicePort = requiredConfigPositiveInteger(gitMirrorConfig, "servicePort", `deploy.lanes.${args.lane}.gitMirror`);
const gitMirrorConfigMapName = requiredConfigString(gitMirrorConfig, "syncConfigMapName", `deploy.lanes.${args.lane}.gitMirror`);
const proxyNamespace = useDirectGitMirrorProxy || useHostGitMirrorProxy ? "" : requiredConfigString(gitMirrorProxy, "namespace", `deploy.lanes.${args.lane}.gitMirror.egressProxy`);
const proxyServiceName = useDirectGitMirrorProxy || useHostGitMirrorProxy ? "" : requiredConfigString(gitMirrorProxy, "serviceName", `deploy.lanes.${args.lane}.gitMirror.egressProxy`);
const proxyPort = useDirectGitMirrorProxy ? 0 : requiredConfigPositiveInteger(gitMirrorProxy, "port", `deploy.lanes.${args.lane}.gitMirror.egressProxy`);
const proxyClientName = useDirectGitMirrorProxy ? "" : requiredConfigString(gitMirrorProxy, "clientName", `deploy.lanes.${args.lane}.gitMirror.egressProxy`);
const proxyNoProxy = !useDirectGitMirrorProxy && Array.isArray(gitMirrorProxy.noProxy) ? gitMirrorProxy.noProxy.filter((value) => typeof value === "string" && value.length > 0).join(",") : "";
const proxyHost = useHostGitMirrorProxy
? requiredConfigString(gitMirrorProxy, "host", `deploy.lanes.${args.lane}.gitMirror.egressProxy`)
: `${proxyServiceName}.${proxyNamespace}.svc.cluster.local`;
const proxyUrl = `http://${proxyHost}:${proxyPort}`;
const proxySummary = useDirectGitMirrorProxy
? "git-mirror-egress-proxy mode=direct required=false transport=ssh source=yaml"
: `git-mirror-egress-proxy client=${proxyClientName} mode=${gitMirrorProxyMode} required=true host=${proxyHost} port=${proxyPort} ssh=GIT_SSH-wrapper source=yaml`;
const proxyCommand = useDirectGitMirrorProxy ? "" : `ProxyCommand=node /tmp/hwlab-github-proxy-connect.mjs ${proxyHost} ${proxyPort} %h %p`;
const gitMirrorSshPrelude = renderGitMirrorSshPrelude({
direct: useDirectGitMirrorProxy,
proxySummary,
proxyCommand,
proxyUrl,
proxyNoProxy
});
const cacheVolume = gitMirrorCacheHostPath === null
? { name: "cache", persistentVolumeClaim: { claimName: gitMirrorCachePvcName } }
: { name: "cache", hostPath: { path: gitMirrorCacheHostPath, type: "DirectoryOrCreate" } };
const labels = {
"app.kubernetes.io/part-of": "hwlab-node-control-plane",
"hwlab.pikastech.local/node": args.nodeId,
"hwlab.pikastech.local/lane": args.lane
};
const configLabels = { ...labels, "app.kubernetes.io/name": "git-mirror" };
const readLabels = { ...labels, "app.kubernetes.io/name": gitMirrorReadName, "hwlab.pikastech.local/git-mirror-mode": "read" };
const writeLabels = { ...labels, "app.kubernetes.io/name": gitMirrorWriteName, "hwlab.pikastech.local/git-mirror-mode": "write" };
return {
apiVersion: "v1",
kind: "List",
items: [
{ apiVersion: "v1", kind: "Namespace", metadata: { name: gitMirrorNamespace } },
...(gitMirrorCacheHostPath === null ? [{
apiVersion: "v1",
kind: "PersistentVolumeClaim",
metadata: { name: gitMirrorCachePvcName, namespace: gitMirrorNamespace, labels: configLabels },
spec: { accessModes: ["ReadWriteOnce"], resources: { requests: { storage: gitMirrorCachePvcStorage } } }
}] : []),
{
apiVersion: "v1",
kind: "ConfigMap",
metadata: { name: gitMirrorConfigMapName, namespace: gitMirrorNamespace, labels: configLabels },
data: gitMirrorConfigScripts({
fetchConfigCommands,
gitMirrorSshPrelude,
gitopsBranchArgs,
gitopsBranchesJson,
mirrorBranchesJson,
requiredRefArgs,
sourceBranchArgs,
sourceBranchesJson,
sourceSnapshotRefPrefix
})
},
{
apiVersion: "apps/v1",
kind: "Deployment",
metadata: { name: gitMirrorReadName, namespace: gitMirrorNamespace, labels: readLabels },
spec: {
replicas: 1,
selector: { matchLabels: { "app.kubernetes.io/name": gitMirrorReadName } },
template: {
metadata: { labels: readLabels },
spec: {
containers: [{
name: "git-mirror",
image: ciToolsRunnerImage,
imagePullPolicy: "IfNotPresent",
command: ["node"],
args: ["/etc/git-mirror/http.mjs"],
ports: [{ name: "http", containerPort: 8080 }],
readinessProbe: { httpGet: { path: "/pikasTech/HWLAB.git/HEAD", port: "http" }, initialDelaySeconds: 5, periodSeconds: 10 },
livenessProbe: { httpGet: { path: "/", port: "http" }, initialDelaySeconds: 10, periodSeconds: 30 },
volumeMounts: [
{ name: "cache", mountPath: "/cache" },
{ name: "config", mountPath: "/etc/git-mirror", readOnly: true }
]
}],
volumes: [
cacheVolume,
{ name: "config", configMap: { name: gitMirrorConfigMapName, defaultMode: 0o555 } }
]
}
}
}
},
{
apiVersion: "v1",
kind: "Service",
metadata: { name: gitMirrorReadName, namespace: gitMirrorNamespace, labels: readLabels },
spec: { selector: { "app.kubernetes.io/name": gitMirrorReadName }, ports: [{ name: "http", port: gitMirrorServicePort, targetPort: "http" }] }
},
{
apiVersion: "v1",
kind: "Service",
metadata: { name: gitMirrorWriteName, namespace: gitMirrorNamespace, labels: writeLabels },
spec: { selector: { "app.kubernetes.io/name": gitMirrorReadName }, ports: [{ name: "http", port: gitMirrorServicePort, targetPort: "http" }] }
}
]
};
}
export {
devopsInfraGitMirrorManifest,
registryManifest,
uniqueStrings
registryManifest
};
@@ -1237,7 +1237,7 @@ function opencodeConfigBoolean(value, fallback, label) {
}
function opencodeEgressProxyUrlForProfile(deploy, profile) {
const proxy = deploy?.lanes?.[profile]?.gitMirror?.egressProxy;
const proxy = deploy?.lanes?.[profile]?.egressProxy;
if (!proxy || proxy.required === false) return "";
const proxyUrl = configString(proxy.proxyUrl);
if (proxyUrl) return proxyUrl;
@@ -1249,7 +1249,7 @@ function opencodeEgressProxyUrlForProfile(deploy, profile) {
}
function opencodeEgressNoProxyForProfile(deploy, profile) {
const proxy = deploy?.lanes?.[profile]?.gitMirror?.egressProxy;
const proxy = deploy?.lanes?.[profile]?.egressProxy;
const entries = Array.isArray(proxy?.noProxy) ? proxy.noProxy.map(configString).filter(Boolean) : [];
return entries.length > 0 ? entries.join(",") : "localhost,127.0.0.1,::1,.svc,.svc.cluster.local,.cluster.local";
}
+22 -13
View File
@@ -307,7 +307,7 @@ function planArtifactsTask({ serviceIds = defaultServiceIds } = {}) {
taskSpec: {
params: [
{ name: "git-url" },
{ name: "git-read-url" },
{ name: "gitops-read-url" },
{ name: "gitops-branch" },
{ name: "lane" },
{ name: "revision" },
@@ -338,7 +338,7 @@ function planArtifactsTask({ serviceIds = defaultServiceIds } = {}) {
},
params: [
{ name: "git-url", value: "$(params.git-url)" },
{ name: "git-read-url", value: "$(params.git-read-url)" },
{ name: "gitops-read-url", value: "$(params.gitops-read-url)" },
{ name: "gitops-branch", value: "$(params.gitops-branch)" },
{ name: "lane", value: "$(params.lane)" },
{ name: "revision", value: "$(params.revision)" },
@@ -516,11 +516,11 @@ function tektonTasks(args = { lane: "node" }, deploy = null) {
name: "prepare-source",
taskSpec: {
params: [
{ name: "git-url" }, { name: "git-read-url" }, { name: "source-branch" },
{ name: "git-url" }, { name: "git-read-url" }, { name: "gitops-read-url" }, { name: "source-branch" },
{ name: "gitops-branch" }, { name: "lane" }, { name: "catalog-path" },
{ name: "image-tag-mode" }, { name: "registry-prefix" }, { name: "services" }, { name: "revision" }
],
workspaces: [{ name: "source" }, { name: "git-ssh" }],
workspaces: [{ name: "source" }],
steps: [{ name: "prepare-source", image: ciToolsRunnerImage, env: proxyEnv(), script: prepareSourceScript() }]
}
};
@@ -532,13 +532,13 @@ function tektonTasks(args = { lane: "node" }, deploy = null) {
name: "gitops-promote",
taskSpec: {
params: [
{ name: "git-url" }, { name: "git-read-url" }, { name: "git-write-url" },
{ name: "git-url" }, { name: "git-read-url" }, { name: "gitops-read-url" }, { name: "git-write-url" }, { name: "gitops-username" },
{ name: "source-branch" }, { name: "gitops-branch" }, { name: "lane" },
{ name: "catalog-path" }, { name: "image-tag-mode" }, { name: "runtime-path" },
{ name: "revision" }, { name: "registry-prefix" }
],
results: [{ name: "runtime-ready-required", description: "true when GitOps promotion changed runtime desired state and runtime readiness must be observed" }],
workspaces: [{ name: "source" }, { name: "git-ssh" }],
workspaces: [{ name: "source" }, { name: "gitea-auth" }],
steps: [{ name: "promote", image: ciToolsRunnerImage, env: proxyEnv(), script: gitopsPromoteScript() }]
}
},
@@ -576,7 +576,9 @@ function tektonPipeline(args = { lane: "node" }, deploy = null) {
params: [
{ name: "git-url", type: "string", default: defaultSourceRepo },
{ name: "git-read-url", type: "string", default: args.gitReadUrl },
{ name: "gitops-read-url", type: "string", default: args.gitReadUrl },
{ name: "git-write-url", type: "string", default: args.gitWriteUrl },
{ name: "gitops-username", type: "string", default: "unidesk-admin" },
{ name: "source-branch", type: "string", default: args.sourceBranch },
{ name: "gitops-branch", type: "string", default: args.gitopsBranch },
{ name: "lane", type: "string", default: settings.lane },
@@ -596,19 +598,19 @@ function tektonPipeline(args = { lane: "node" }, deploy = null) {
],
workspaces: [
{ name: "source" },
{ name: "git-ssh" }
{ name: "gitea-auth" }
],
tasks: [
{
name: "prepare-source",
workspaces: [
{ name: "source", workspace: "source" },
{ name: "git-ssh", workspace: "git-ssh" }
{ name: "source", workspace: "source" }
],
taskSpec: {
params: [
{ name: "git-url" },
{ name: "git-read-url" },
{ name: "gitops-read-url" },
{ name: "source-branch" },
{ name: "gitops-branch" },
{ name: "lane" },
@@ -618,12 +620,13 @@ function tektonPipeline(args = { lane: "node" }, deploy = null) {
{ name: "services" },
{ name: "revision" }
],
workspaces: [{ name: "source" }, { name: "git-ssh" }],
workspaces: [{ name: "source" }],
steps: [{ name: "prepare-source", image: ciToolsRunnerImage, env: proxyEnv(), script: prepareSourceScript() }]
},
params: [
{ name: "git-url", value: "$(params.git-url)" },
{ name: "git-read-url", value: "$(params.git-read-url)" },
{ name: "gitops-read-url", value: "$(params.gitops-read-url)" },
{ name: "source-branch", value: "$(params.source-branch)" },
{ name: "gitops-branch", value: "$(params.gitops-branch)" },
{ name: "lane", value: "$(params.lane)" },
@@ -641,13 +644,15 @@ function tektonPipeline(args = { lane: "node" }, deploy = null) {
runAfter: ["collect-artifacts"],
workspaces: [
{ name: "source", workspace: "source" },
{ name: "git-ssh", workspace: "git-ssh" }
{ name: "gitea-auth", workspace: "gitea-auth" }
],
taskSpec: {
params: [
{ name: "git-url" },
{ name: "git-read-url" },
{ name: "gitops-read-url" },
{ name: "git-write-url" },
{ name: "gitops-username" },
{ name: "source-branch" },
{ name: "gitops-branch" },
{ name: "lane" },
@@ -658,13 +663,15 @@ function tektonPipeline(args = { lane: "node" }, deploy = null) {
{ name: "registry-prefix" }
],
results: [{ name: "runtime-ready-required", description: "true when GitOps promotion changed runtime desired state and runtime readiness must be observed" }],
workspaces: [{ name: "source" }, { name: "git-ssh" }],
workspaces: [{ name: "source" }, { name: "gitea-auth" }],
steps: [{ name: "promote", image: ciToolsRunnerImage, env: proxyEnv(), script: gitopsPromoteScript() }]
},
params: [
{ name: "git-url", value: "$(params.git-url)" },
{ name: "git-read-url", value: "$(params.git-read-url)" },
{ name: "gitops-read-url", value: "$(params.gitops-read-url)" },
{ name: "git-write-url", value: "$(params.git-write-url)" },
{ name: "gitops-username", value: "$(params.gitops-username)" },
{ name: "source-branch", value: "$(params.source-branch)" },
{ name: "gitops-branch", value: "$(params.gitops-branch)" },
{ name: "lane", value: "$(params.lane)" },
@@ -709,7 +716,9 @@ function tektonPipelineRunTemplate({ source, args, deploy = null }) {
params: [
{ name: "git-url", value: args.sourceRepo },
{ name: "git-read-url", value: args.gitReadUrl },
{ name: "gitops-read-url", value: args.gitReadUrl },
{ name: "git-write-url", value: args.gitWriteUrl },
{ name: "gitops-username", value: "unidesk-admin" },
{ name: "source-branch", value: args.sourceBranch },
{ name: "gitops-branch", value: args.gitopsBranch },
{ name: "lane", value: settings.lane },
@@ -723,7 +732,7 @@ function tektonPipelineRunTemplate({ source, args, deploy = null }) {
],
workspaces: [
{ name: "source", volumeClaimTemplate: { spec: { accessModes: ["ReadWriteOnce"], resources: { requests: { storage: "8Gi" } } } } },
{ name: "git-ssh", secret: { secretName: "hwlab-git-ssh" } }
{ name: "gitea-auth", secret: { secretName: "pac-gitea-hwlab", items: [{ key: "token", path: "token" }] } }
]
}
};
@@ -63,7 +63,6 @@ function gitSshShellFunction() {
function prepareSourceScript() {
return renderTemplate("prepare-source.sh", {
"# __HWLAB_CI_TIMING_SHELL__\n": ciTimingShellFunction(),
"# __HWLAB_GIT_SSH_SHELL__\n": gitSshShellFunction(),
"__HWLAB_CI_TOOLS_IMAGE__": ciToolsRunnerImage,
"__HWLAB_V02_RUNTIME_ENDPOINT__": JSON.stringify(defaultV02RuntimeEndpoint).slice(1, -1),
"__HWLAB_VALIDATION_TASK_COUNT__": primitiveValidationTasks.length
@@ -140,7 +139,6 @@ function collectArtifactsScript() {
function gitopsPromoteScript() {
return renderTemplate("gitops-promote.sh", {
"# __HWLAB_CI_TIMING_SHELL__\n": ciTimingShellFunction(),
"# __HWLAB_GIT_SSH_SHELL__\n": gitSshShellFunction(),
"__HWLAB_CI_TOOLS_IMAGE__": ciToolsRunnerImage
});
}
@@ -1,76 +0,0 @@
#!/bin/sh
set -eu
repo_url="ssh://git@ssh.github.com:443/pikasTech/HWLAB.git"
repo_path="/cache/pikasTech/HWLAB.git"
lock_dir="/cache/.hwlab-flush.lock"
started_ms=$(node -e 'console.log(Date.now())')
now_ms() { node -e 'console.log(Date.now())'; }
emit_timing() {
phase="$1"
status="$2"
started="$3"
finished=$(now_ms)
duration=$((finished - started))
printf '{"event":"git-mirror-flush-timing","phase":"%s","status":"%s","durationMs":%s,"repository":"pikasTech/HWLAB"}
' "$phase" "$status" "$duration"
}
if ! mkdir "$lock_dir" 2>/dev/null; then
echo "git mirror flush already running"
exit 0
fi
trap 'rmdir "$lock_dir" 2>/dev/null || true' EXIT INT TERM
mkdir -p /root/.ssh
cp /git-ssh/ssh-privatekey /root/.ssh/id_rsa
chmod 0400 /root/.ssh/id_rsa
# __HWLAB_GIT_MIRROR_SSH_PRELUDE__
/script/install-hooks.sh
for gitops_branch in __HWLAB_GITOPS_BRANCH_ARGS__; do
local_head=$(git -C "$repo_path" show-ref --verify --hash "refs/heads/$gitops_branch" 2>/dev/null || true)
if [ -z "$local_head" ]; then
echo "git mirror flush skips missing local refs/heads/$gitops_branch"
continue
fi
fetch_started=$(now_ms)
timeout 90 git -C "$repo_path" fetch --quiet "$repo_url" "refs/heads/$gitops_branch:refs/mirror-stage/heads/$gitops_branch"
emit_timing "fetch-$gitops_branch" succeeded "$fetch_started"
github_head=$(git -C "$repo_path" show-ref --verify --hash "refs/mirror-stage/heads/$gitops_branch" 2>/dev/null || true)
if [ -n "$github_head" ] && [ "$github_head" != "$local_head" ] && ! git -C "$repo_path" merge-base --is-ancestor "$github_head" "$local_head"; then
echo "git mirror flush rejected divergent $gitops_branch local=$local_head github=$github_head" >&2
exit 52
fi
if [ "$local_head" != "$github_head" ]; then
push_started=$(now_ms)
timeout 120 git -C "$repo_path" push "$repo_url" "$local_head:refs/heads/$gitops_branch"
emit_timing "push-$gitops_branch" succeeded "$push_started"
else
echo "git mirror flush $gitops_branch already matches GitHub"
fi
git -C "$repo_path" update-ref "refs/mirror-stage/heads/$gitops_branch" "$local_head"
done
git -C "$repo_path" update-server-info
flushed_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)
export flushed_at
export gitops_branches_json=__HWLAB_GITOPS_BRANCHES_JSON_SHELL__
node - <<'NODE' | tee /cache/HWLAB.last-flush.json
const { execFileSync } = require("node:child_process");
const repo = "/cache/pikasTech/HWLAB.git";
const gitopsBranches = JSON.parse(process.env.gitops_branches_json || "[]");
function rev(ref) {
try { return execFileSync("git", ["-C", repo, "rev-parse", ref], { encoding: "utf8" }).trim() || null; } catch { return null; }
}
const refs = {};
for (const branch of gitopsBranches) {
refs["refs/heads/" + branch] = rev("refs/heads/" + branch);
refs["refs/mirror-stage/heads/" + branch] = rev("refs/mirror-stage/heads/" + branch);
}
const pendingFlush = gitopsBranches.some((branch) => refs["refs/heads/" + branch] && refs["refs/mirror-stage/heads/" + branch] && refs["refs/heads/" + branch] !== refs["refs/mirror-stage/heads/" + branch]);
console.log(JSON.stringify({
event: "git-mirror-flush",
status: "flushed",
repository: "pikasTech/HWLAB",
flushedAt: process.env.flushed_at,
pendingFlush,
refs
}));
NODE
emit_timing total succeeded "$started_ms"
@@ -1,196 +0,0 @@
import { createServer } from "node:http";
import { spawn } from "node:child_process";
import { createReadStream, statSync } from "node:fs";
import path from "node:path";
import { createGunzip, createInflate } from "node:zlib";
const cacheRoot = process.env.GIT_MIRROR_CACHE_ROOT || "/cache";
const port = Number(process.env.PORT || 8080);
createServer((req, res) => {
handle(req, res).catch((error) => {
if (!res.headersSent) res.writeHead(500, { "content-type": "application/json" });
res.end(JSON.stringify({ ok: false, error: error.message }));
});
}).listen(port, "0.0.0.0", () => {
console.log(JSON.stringify({ event: "git-mirror-smart-http-listening", port, cacheRoot }));
});
async function handle(req, res) {
const url = new URL(req.url || "/", "http://git-mirror-http");
const writeHost = isWriteHost(req.headers.host || "");
if (req.method === "GET" && (url.pathname === "/" || url.pathname === "/health")) {
res.writeHead(200, { "content-type": "application/json", "cache-control": "no-cache" });
res.end(JSON.stringify({ ok: true, service: "git-mirror-smart-http", writeHost }));
return;
}
if (req.method === "GET" && url.pathname.endsWith("/info/refs") && url.searchParams.get("service") === "git-upload-pack") {
await runUploadPackAdvertisement(repoPathFromGitServicePath(url.pathname, "/info/refs"), res);
return;
}
if (req.method === "POST" && url.pathname.endsWith("/git-upload-pack")) {
await runUploadPackRpc(repoPathFromGitServicePath(url.pathname, "/git-upload-pack"), req, res);
return;
}
if (req.method === "GET" && url.pathname.endsWith("/info/refs") && url.searchParams.get("service") === "git-receive-pack") {
if (!writeHost) return rejectReadOnly(res);
await runReceivePackAdvertisement(repoPathFromGitServicePath(url.pathname, "/info/refs"), res);
return;
}
if (req.method === "POST" && url.pathname.endsWith("/git-receive-pack")) {
if (!writeHost) return rejectReadOnly(res);
await runReceivePackRpc(repoPathFromGitServicePath(url.pathname, "/git-receive-pack"), req, res);
return;
}
if (req.method === "GET" || req.method === "HEAD") {
serveStaticFile(url.pathname, req, res);
return;
}
res.writeHead(405, { "content-type": "text/plain" });
res.end("method not allowed\n");
}
function isWriteHost(hostHeader) {
const host = String(hostHeader || "").split(":", 1)[0].toLowerCase();
return host === "git-mirror-write" || host.startsWith("git-mirror-write.");
}
function rejectReadOnly(res) {
res.writeHead(403, { "content-type": "application/json", "cache-control": "no-cache" });
res.end(JSON.stringify({ ok: false, error: "git mirror receive-pack is only available through git-mirror-write" }));
}
function repoPathFromGitServicePath(urlPath, suffix) {
const repoUrlPath = urlPath.slice(0, -suffix.length);
if (!repoUrlPath.endsWith(".git")) throw new Error(`unsupported git repo path: ${urlPath}`);
return safePath(repoUrlPath);
}
function safePath(urlPath) {
const decoded = decodeURIComponent(urlPath.replace(/^[/]+/u, ""));
const fullPath = path.resolve(cacheRoot, decoded);
const normalizedRoot = path.resolve(cacheRoot);
if (fullPath !== normalizedRoot && !fullPath.startsWith(`${normalizedRoot}${path.sep}`)) throw new Error("path escapes cache root");
return fullPath;
}
function smartHeaders(contentType) {
return {
"content-type": contentType,
"cache-control": "no-cache, max-age=0, must-revalidate",
expires: "Fri, 01 Jan 1980 00:00:00 GMT",
pragma: "no-cache"
};
}
function pktLine(text) {
const length = Buffer.byteLength(text) + 4;
return `${length.toString(16).padStart(4, "0")}${text}`;
}
function requestBodyStream(req) {
const encoding = String(req.headers["content-encoding"] || "identity").toLowerCase();
if (encoding === "identity" || encoding === "") return req;
if (encoding === "gzip" || encoding === "x-gzip") return req.pipe(createGunzip());
if (encoding === "deflate") return req.pipe(createInflate());
throw new Error(`unsupported git upload-pack content-encoding: ${encoding}`);
}
function runUploadPackAdvertisement(repoPath, res) {
return new Promise((resolve, reject) => {
const child = spawn("git-upload-pack", ["--stateless-rpc", "--advertise-refs", repoPath], { stdio: ["ignore", "pipe", "pipe"] });
let stderr = "";
child.stderr.on("data", (chunk) => { stderr += chunk; process.stderr.write(chunk); });
child.on("error", reject);
child.on("close", (code) => {
if (code !== 0 && !res.headersSent) reject(new Error(`git-upload-pack advertise failed: ${stderr.trim()}`));
else resolve();
});
res.writeHead(200, smartHeaders("application/x-git-upload-pack-advertisement"));
res.write(pktLine("# service=git-upload-pack\n"));
res.write("0000");
child.stdout.pipe(res, { end: false });
child.stdout.on("end", () => res.end());
});
}
function runUploadPackRpc(repoPath, req, res) {
return new Promise((resolve, reject) => {
const child = spawn("git-upload-pack", ["--stateless-rpc", repoPath], { stdio: ["pipe", "pipe", "pipe"] });
let stderr = "";
child.stderr.on("data", (chunk) => { stderr += chunk; process.stderr.write(chunk); });
child.on("error", reject);
child.on("close", (code) => {
if (code !== 0 && !res.headersSent) reject(new Error(`git-upload-pack rpc failed: ${stderr.trim()}`));
else resolve();
});
res.writeHead(200, smartHeaders("application/x-git-upload-pack-result"));
const body = requestBodyStream(req);
body.on("error", (error) => child.stdin.destroy(error));
body.pipe(child.stdin);
child.stdout.pipe(res);
});
}
function runReceivePackAdvertisement(repoPath, res) {
return new Promise((resolve, reject) => {
const child = spawn("git-receive-pack", ["--stateless-rpc", "--advertise-refs", repoPath], { stdio: ["ignore", "pipe", "pipe"] });
let stderr = "";
child.stderr.on("data", (chunk) => { stderr += chunk; process.stderr.write(chunk); });
child.on("error", reject);
child.on("close", (code) => {
if (code !== 0 && !res.headersSent) reject(new Error(`git-receive-pack advertise failed: ${stderr.trim()}`));
else resolve();
});
res.writeHead(200, smartHeaders("application/x-git-receive-pack-advertisement"));
res.write(pktLine("# service=git-receive-pack\n"));
res.write("0000");
child.stdout.pipe(res, { end: false });
child.stdout.on("end", () => res.end());
});
}
function runReceivePackRpc(repoPath, req, res) {
return new Promise((resolve, reject) => {
const child = spawn("git-receive-pack", ["--stateless-rpc", repoPath], { stdio: ["pipe", "pipe", "pipe"] });
let stderr = "";
child.stderr.on("data", (chunk) => { stderr += chunk; process.stderr.write(chunk); });
child.on("error", reject);
child.on("close", (code) => {
if (code !== 0 && !res.headersSent) reject(new Error(`git-receive-pack rpc failed: ${stderr.trim()}`));
else resolve();
});
res.writeHead(200, smartHeaders("application/x-git-receive-pack-result"));
const body = requestBodyStream(req);
body.on("error", (error) => child.stdin.destroy(error));
body.pipe(child.stdin);
child.stdout.pipe(res);
});
}
function serveStaticFile(urlPath, req, res) {
const fullPath = safePath(urlPath);
let stat;
try { stat = statSync(fullPath); } catch {
res.writeHead(404, { "content-type": "text/plain" });
res.end("not found\n");
return;
}
if (!stat.isFile()) {
res.writeHead(404, { "content-type": "text/plain" });
res.end("not found\n");
return;
}
res.writeHead(200, { "content-length": stat.size, "content-type": contentType(fullPath), "cache-control": "no-cache" });
if (req.method === "HEAD") {
res.end();
return;
}
createReadStream(fullPath).pipe(res);
}
function contentType(filePath) {
if (filePath.endsWith(".pack")) return "application/x-git-packed-objects";
if (filePath.endsWith(".idx")) return "application/x-git-packed-objects-toc";
return "text/plain";
}
@@ -1,65 +0,0 @@
#!/bin/sh
set -eu
repo_path="/cache/pikasTech/HWLAB.git"
mkdir -p "$repo_path/hooks"
cat > "$repo_path/hooks/pre-receive" <<'HOOK'
#!/bin/sh
set -eu
zero="0000000000000000000000000000000000000000"
status=0
while read -r old new ref; do
if [ "$new" = "$zero" ]; then
echo "git mirror write rejected: deleting $ref is forbidden" >&2
status=1
continue
fi
git cat-file -e "$new^{commit}" || status=1
git cat-file -e "$new^{tree}" || status=1
if git rev-list --objects --missing=print "$new" | grep -q '^?'; then
echo "git mirror write rejected: missing objects for $new" >&2
status=1
continue
fi
if [ "$old" != "$zero" ] && ! git merge-base --is-ancestor "$old" "$new"; then
echo "git mirror write rejected: non-fast-forward $ref" >&2
status=1
continue
fi
done
exit "$status"
HOOK
cat > "$repo_path/hooks/post-receive" <<'HOOK'
#!/bin/sh
set -eu
repo_path=$(git rev-parse --git-dir)
git -C "$repo_path" update-server-info
written_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)
export repo_path written_at
export gitops_branches_json=__HWLAB_GITOPS_BRANCHES_JSON_SHELL__
node - <<'NODE' > /cache/HWLAB.last-write.json
const { execFileSync } = require("node:child_process");
const repo = process.env.repo_path || "/cache/pikasTech/HWLAB.git";
const gitopsBranches = JSON.parse(process.env.gitops_branches_json || "[]");
function rev(ref) {
try { return execFileSync("git", ["-C", repo, "rev-parse", ref], { encoding: "utf8" }).trim() || null; } catch { return null; }
}
const refs = {};
for (const branch of gitopsBranches) {
refs["refs/heads/" + branch] = rev("refs/heads/" + branch);
refs["refs/mirror-stage/heads/" + branch] = rev("refs/mirror-stage/heads/" + branch);
}
const pendingFlush = gitopsBranches.some((branch) => refs["refs/heads/" + branch] && refs["refs/heads/" + branch] !== refs["refs/mirror-stage/heads/" + branch]);
console.log(JSON.stringify({
event: "git-mirror-write",
status: "received",
repository: "pikasTech/HWLAB",
writtenAt: process.env.written_at,
pendingFlush,
refs
}));
NODE
HOOK
chmod 0755 "$repo_path/hooks/pre-receive" "$repo_path/hooks/post-receive"
git -C "$repo_path" config http.receivepack true
git -C "$repo_path" config receive.denyDeletes true
git -C "$repo_path" config receive.denyNonFastForwards true
@@ -1,48 +0,0 @@
#!/usr/bin/env node
import net from "node:net";
const [proxyHost, proxyPortRaw, targetHost, targetPortRaw] = process.argv.slice(2);
const proxyPort = Number.parseInt(proxyPortRaw || "", 10);
const targetPort = Number.parseInt(targetPortRaw || "", 10);
if (!proxyHost || !Number.isInteger(proxyPort) || !targetHost || !Number.isInteger(targetPort)) {
console.error("hwlab git-mirror proxy-connect: invalid ProxyCommand arguments");
process.exit(64);
}
let settled = false;
let tunnelEstablished = false;
function finish(code, message) {
if (settled) return;
settled = true;
if (message) console.error("hwlab git-mirror proxy-connect: " + message);
process.exit(code);
}
const socket = net.createConnection({ host: proxyHost, port: proxyPort });
let buffer = Buffer.alloc(0);
socket.setTimeout(15000, () => { socket.destroy(); finish(65, "timeout connecting via " + proxyHost + ":" + proxyPort + " to " + targetHost + ":" + targetPort); });
socket.on("connect", () => socket.write("CONNECT " + targetHost + ":" + targetPort + " HTTP/1.1\r\nHost: " + targetHost + ":" + targetPort + "\r\nProxy-Connection: Keep-Alive\r\n\r\n"));
socket.on("error", (error) => finish(tunnelEstablished ? 69 : 66, (tunnelEstablished ? "tunnel socket error: " : "tcp error connecting to proxy: ") + (error && error.message ? error.message : String(error))));
socket.on("close", () => { if (!tunnelEstablished) finish(68, "proxy closed before CONNECT completed via " + proxyHost + ":" + proxyPort + " to " + targetHost + ":" + targetPort); else finish(0); });
function onData(chunk) {
buffer = Buffer.concat([buffer, chunk]);
const headerEnd = buffer.indexOf("\r\n\r\n");
if (headerEnd === -1 && buffer.length < 8192) return;
if (headerEnd === -1) { socket.destroy(); finish(68, "proxy response header exceeded 8192 bytes before CONNECT status via " + proxyHost + ":" + proxyPort + " to " + targetHost + ":" + targetPort); return; }
const head = buffer.slice(0, headerEnd + 4).toString("latin1");
const statusLine = head.split("\r\n", 1)[0] || "";
const statusCode = Number.parseInt(statusLine.split(" ")[1] || "", 10);
if (!statusLine.startsWith("HTTP/1.") || !Number.isInteger(statusCode) || statusCode < 200 || statusCode > 299) {
const safeStatus = statusLine.replace(/[^\x20-\x7e]/g, "?").slice(0, 160);
socket.destroy();
finish(67, "proxy CONNECT failed via " + proxyHost + ":" + proxyPort + " to " + targetHost + ":" + targetPort + ": " + safeStatus);
return;
}
socket.off("data", onData);
socket.setTimeout(0);
tunnelEstablished = true;
const rest = buffer.slice(headerEnd + 4);
if (rest.length) process.stdout.write(rest);
process.stdin.on("error", () => {});
process.stdout.on("error", () => {});
process.stdin.pipe(socket);
socket.pipe(process.stdout);
}
socket.on("data", onData);
@@ -1,11 +0,0 @@
printf '%s\n' __HWLAB_PROXY_SUMMARY_SHELL__ >&2
cat > /tmp/hwlab-git-ssh-proxy.sh <<'SH_PROXY'
#!/bin/sh
exec ssh -i /root/.ssh/id_rsa -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/root/.ssh/known_hosts -o ConnectTimeout=15 -o ServerAliveInterval=5 -o ServerAliveCountMax=1 "$@"
SH_PROXY
chmod 0700 /tmp/hwlab-git-ssh-proxy.sh
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY http_proxy https_proxy all_proxy
export NO_PROXY='*'
export no_proxy='*'
export GIT_SSH=/tmp/hwlab-git-ssh-proxy.sh
unset GIT_SSH_COMMAND
@@ -1,20 +0,0 @@
printf '%s\n' __HWLAB_PROXY_SUMMARY_SHELL__ >&2
cat > /tmp/hwlab-github-proxy-connect.mjs <<'NODE_PROXY'
// __HWLAB_GIT_MIRROR_PROXY_CONNECT_MJS__
NODE_PROXY
chmod 0700 /tmp/hwlab-github-proxy-connect.mjs
cat > /tmp/hwlab-git-ssh-proxy.sh <<'SH_PROXY'
#!/bin/sh
exec ssh -i /root/.ssh/id_rsa -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/root/.ssh/known_hosts -o ConnectTimeout=15 -o ServerAliveInterval=5 -o ServerAliveCountMax=1 -o __HWLAB_PROXY_COMMAND_SHELL__ "$@"
SH_PROXY
chmod 0700 /tmp/hwlab-git-ssh-proxy.sh
export HTTP_PROXY=__HWLAB_PROXY_URL_SHELL__
export HTTPS_PROXY=__HWLAB_PROXY_URL_SHELL__
export ALL_PROXY=__HWLAB_PROXY_URL_SHELL__
export http_proxy=__HWLAB_PROXY_URL_SHELL__
export https_proxy=__HWLAB_PROXY_URL_SHELL__
export all_proxy=__HWLAB_PROXY_URL_SHELL__
export NO_PROXY=__HWLAB_PROXY_NO_PROXY_SHELL__
export no_proxy=__HWLAB_PROXY_NO_PROXY_SHELL__
export GIT_SSH=/tmp/hwlab-git-ssh-proxy.sh
unset GIT_SSH_COMMAND
@@ -1,181 +0,0 @@
#!/bin/sh
set -eu
repo_url="ssh://git@ssh.github.com:443/pikasTech/HWLAB.git"
repo_path="/cache/pikasTech/HWLAB.git"
source_snapshot_ref_prefix=__HWLAB_SOURCE_SNAPSHOT_REF_PREFIX_SHELL__
lock_dir="/cache/.hwlab-sync.lock"
sync_started_ms=$(node -e 'console.log(Date.now())')
now_ms() { node -e 'console.log(Date.now())'; }
emit_timing() {
phase="$1"
status="$2"
started_ms="$3"
finished_ms=$(now_ms)
duration_ms=$((finished_ms - started_ms))
printf '{"event":"git-mirror-sync-timing","phase":"%s","status":"%s","durationMs":%s,"repository":"pikasTech/HWLAB"}
' "$phase" "$status" "$duration_ms"
}
mkdir -p /cache/pikasTech /root/.ssh
if ! mkdir "$lock_dir" 2>/dev/null; then
echo "git mirror sync already running"
exit 0
fi
trap 'rmdir "$lock_dir" 2>/dev/null || true' EXIT INT TERM
cp /git-ssh/ssh-privatekey /root/.ssh/id_rsa
chmod 0400 /root/.ssh/id_rsa
# __HWLAB_GIT_MIRROR_SSH_PRELUDE__
if [ -d "$repo_path/objects" ] && [ -f "$repo_path/HEAD" ]; then
git -C "$repo_path" remote set-url origin "$repo_url" || git -C "$repo_path" remote add origin "$repo_url"
else
rm -rf "$repo_path"
git init --bare "$repo_path"
git -C "$repo_path" remote add origin "$repo_url"
fi
/script/install-hooks.sh
git -C "$repo_path" config uploadpack.hideRefs refs/mirror-stage
git -C "$repo_path" config uploadpack.allowReachableSHA1InWant true
git -C "$repo_path" config uploadpack.allowAnySHA1InWant true
git -C "$repo_path" config --unset-all remote.origin.fetch 2>/dev/null || true
# __HWLAB_FETCH_CONFIG_COMMANDS__
fetch_started_ms=$(now_ms)
timeout 180 git -C "$repo_path" fetch --quiet --prune origin
emit_timing fetch succeeded "$fetch_started_ms"
git -C "$repo_path" for-each-ref --format='%(objectname) %(refname)' refs/mirror-stage/heads > /tmp/hwlab-stage-heads
git -C "$repo_path" for-each-ref --format='%(refname)' refs/mirror-stage/heads > /tmp/hwlab-stage-head-refs
git -C "$repo_path" for-each-ref --format='%(objectname) %(refname)' refs/mirror-stage/tags > /tmp/hwlab-stage-tags
git -C "$repo_path" for-each-ref --format='%(refname)' refs/mirror-stage/tags > /tmp/hwlab-stage-tag-refs
if [ ! -s /tmp/hwlab-stage-heads ]; then
echo "git mirror sync fetched no branch refs" >&2
exit 41
fi
for required_ref in __HWLAB_REQUIRED_REF_ARGS__; do
git -C "$repo_path" show-ref --verify --quiet "$required_ref" || { echo "git mirror sync missing required ref $required_ref" >&2; exit 43; }
done
validate_started_ms=$(now_ms)
while read -r sha ref; do
[ -n "$sha" ] || continue
git -C "$repo_path" cat-file -e "$sha^{commit}"
git -C "$repo_path" cat-file -e "$sha^{tree}"
if git -C "$repo_path" rev-list --objects --missing=print "$sha" | grep -q '^?'; then
echo "git mirror sync found missing objects for $ref $sha" >&2
exit 42
fi
done < /tmp/hwlab-stage-heads
emit_timing validate succeeded "$validate_started_ms"
publish_started_ms=$(now_ms)
while read -r sha ref; do
[ -n "$sha" ] || continue
name=$(printf '%s
' "$ref" | sed 's#^refs/mirror-stage/heads/##')
is_gitops_branch=false
for gitops_branch in __HWLAB_GITOPS_BRANCH_ARGS__; do
if [ "$name" = "$gitops_branch" ]; then is_gitops_branch=true; break; fi
done
if [ "$is_gitops_branch" = true ]; then
local_sha=$(git -C "$repo_path" show-ref --verify --hash "refs/heads/$name" 2>/dev/null || true)
if [ -n "$local_sha" ] && [ "$local_sha" != "$sha" ]; then
if git -C "$repo_path" merge-base --is-ancestor "$sha" "$local_sha"; then
echo "git mirror sync keeps local $name ahead of GitHub"
continue
fi
if ! git -C "$repo_path" merge-base --is-ancestor "$local_sha" "$sha"; then
echo "git mirror sync found divergent $name local=$local_sha github=$sha" >&2
exit 44
fi
fi
fi
git -C "$repo_path" update-ref "refs/heads/$name" "$sha"
done < /tmp/hwlab-stage-heads
for source_branch in __HWLAB_SOURCE_BRANCH_ARGS__; do
source_sha=$(git -C "$repo_path" show-ref --verify --hash "refs/heads/$source_branch" 2>/dev/null || true)
if [ -n "$source_sha" ]; then
source_stage_ref="${source_snapshot_ref_prefix%/}/$source_branch/$source_sha"
git -C "$repo_path" update-ref "$source_stage_ref" "$source_sha"
fi
done
while read -r sha ref; do
[ -n "$sha" ] || continue
name=$(printf '%s
' "$ref" | sed 's#^refs/mirror-stage/tags/##')
git -C "$repo_path" update-ref "refs/tags/$name" "$sha"
done < /tmp/hwlab-stage-tags
git -C "$repo_path" for-each-ref --format='%(refname)' refs/heads > /tmp/hwlab-public-heads
while read -r ref; do
[ -n "$ref" ] || continue
name=$(printf '%s
' "$ref" | sed 's#^refs/heads/##')
if ! grep -Fxq "refs/mirror-stage/heads/$name" /tmp/hwlab-stage-head-refs; then
git -C "$repo_path" update-ref -d "$ref"
fi
done < /tmp/hwlab-public-heads
git -C "$repo_path" for-each-ref --format='%(refname)' refs/tags > /tmp/hwlab-public-tags
while read -r ref; do
[ -n "$ref" ] || continue
name=$(printf '%s
' "$ref" | sed 's#^refs/tags/##')
if ! grep -Fxq "refs/mirror-stage/tags/$name" /tmp/hwlab-stage-tag-refs; then
git -C "$repo_path" update-ref -d "$ref"
fi
done < /tmp/hwlab-public-tags
emit_timing publish succeeded "$publish_started_ms"
for head_branch in __HWLAB_SOURCE_BRANCH_ARGS__; do
if git -C "$repo_path" show-ref --verify --quiet "refs/heads/$head_branch"; then
git -C "$repo_path" symbolic-ref HEAD "refs/heads/$head_branch"
break
fi
done
fsck_started_ms=$(now_ms)
git -C "$repo_path" fsck --connectivity-only --no-dangling >/tmp/hwlab-git-fsck.out
emit_timing fsck succeeded "$fsck_started_ms"
git -C "$repo_path" update-server-info
published_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)
for source_branch in __HWLAB_SOURCE_BRANCH_ARGS__; do
git -C "$repo_path" rev-parse "refs/heads/$source_branch" 2>/dev/null | tee "/cache/HWLAB-$source_branch.head" >/dev/null || true
done
printf '%s
' "$published_at" > /cache/HWLAB.last-sync
export published_at
export source_snapshot_ref_prefix
export mirror_branches_json=__HWLAB_MIRROR_BRANCHES_JSON_SHELL__
export source_branches_json=__HWLAB_SOURCE_BRANCHES_JSON_SHELL__
export gitops_branches_json=__HWLAB_GITOPS_BRANCHES_JSON_SHELL__
node - <<'NODE' | tee /cache/HWLAB.last-sync.json
const { execFileSync } = require("node:child_process");
const repo = "/cache/pikasTech/HWLAB.git";
const mirrorBranches = JSON.parse(process.env.mirror_branches_json || "[]");
const sourceBranches = JSON.parse(process.env.source_branches_json || "[]");
const gitopsBranches = JSON.parse(process.env.gitops_branches_json || "[]");
const sourceSnapshotRefPrefix = (process.env.source_snapshot_ref_prefix || "refs/unidesk/snapshots/hwlab-node-runtime").replace(//+$/u, "");
function rev(ref) {
try { return execFileSync("git", ["-C", repo, "rev-parse", ref], { encoding: "utf8" }).trim() || null; } catch { return null; }
}
const refs = {};
for (const branch of mirrorBranches) {
refs["refs/heads/" + branch] = rev("refs/heads/" + branch);
refs["refs/mirror-stage/heads/" + branch] = rev("refs/mirror-stage/heads/" + branch);
}
const sourceSnapshots = {};
for (const branch of sourceBranches) {
const source = refs["refs/heads/" + branch] || null;
const stageRef = source ? sourceSnapshotRefPrefix + "/" + branch + "/" + source : null;
const sourceSnapshot = stageRef ? rev(stageRef) : null;
if (stageRef) refs[stageRef] = sourceSnapshot;
sourceSnapshots[branch] = { stageRef, sourceSnapshot };
}
const sourceInSync = sourceBranches.every((branch) => refs["refs/heads/" + branch] && refs["refs/heads/" + branch] === refs["refs/mirror-stage/heads/" + branch] && sourceSnapshots[branch]?.sourceSnapshot === refs["refs/mirror-stage/heads/" + branch]);
const gitopsInSync = gitopsBranches.every((branch) => refs["refs/heads/" + branch] && refs["refs/heads/" + branch] === refs["refs/mirror-stage/heads/" + branch]);
const pendingFlush = gitopsBranches.some((branch) => refs["refs/heads/" + branch] && refs["refs/mirror-stage/heads/" + branch] && refs["refs/heads/" + branch] !== refs["refs/mirror-stage/heads/" + branch]);
const payload = {
event: "git-mirror-sync",
status: "published",
repository: "pikasTech/HWLAB",
publishedAt: process.env.published_at,
pendingFlush,
sourceInSync,
gitopsInSync,
sourceSnapshots,
refs
};
console.log(JSON.stringify(payload));
NODE
emit_timing total succeeded "$sync_started_ms"
@@ -8,9 +8,6 @@ export HWLAB_TEKTON_TASK="gitops-promote"
export HWLAB_SOURCE_REVISION="$(params.revision)"
echo '{"event":"ci-base-image","phase":"gitops-promote","image":"__HWLAB_CI_TOOLS_IMAGE__","policy":"no-runtime-apt"}'
for tool in node git timeout; do command -v "$tool" >/dev/null 2>&1 || { echo '{"event":"ci-base-image","phase":"gitops-promote","ok":false,"reason":"missing-tool","tool":"'"$tool"'"}'; exit 31; }; done
# __HWLAB_GIT_SSH_SHELL__
git_url_requires_ssh() { case "$1" in git@*|ssh://*) return 0 ;; *) return 1 ;; esac; }
lane="$(params.lane)"
case "$lane" in
node) runtime_lane=false ;;
@@ -21,29 +18,27 @@ if [ "$runtime_lane" = "true" ]; then
else
printf 'true' > /tekton/results/runtime-ready-required
fi
source_head_url_for_setup="$(params.git-url)"
gitops_read_url_for_setup="$(params.git-url)"
if [ "$runtime_lane" = "true" ]; then
source_head_url_for_setup="$(params.git-read-url)"
gitops_read_url_for_setup="$(params.git-read-url)"
fi
gitops_write_url_for_setup="$(params.git-write-url)"
if git_url_requires_ssh "$source_head_url_for_setup" || git_url_requires_ssh "$gitops_read_url_for_setup" || git_url_requires_ssh "$gitops_write_url_for_setup"; then
for tool in ssh ssh-keyscan; do command -v "$tool" >/dev/null 2>&1 || { echo '{"event":"ci-base-image","phase":"gitops-promote","ok":false,"reason":"missing-tool","tool":"'"$tool"'"}'; exit 31; }; done
git_ssh_setup
else
echo '{"event":"git-ssh-setup","phase":"gitops-promote","status":"skipped","reason":"non-ssh-git-url"}'
fi
test -s /workspace/gitea-auth/token || { echo '{"event":"gitea-auth","phase":"gitops-promote","status":"failed","reason":"token-missing"}'; exit 31; }
askpass_dir="$(mktemp -d)"
cat > "$askpass_dir/askpass.sh" <<'SH'
#!/bin/sh
case "$1" in
*sername*) printf '%s\n' "$GITEA_USERNAME" ;;
*) cat /workspace/gitea-auth/token ;;
esac
SH
chmod 0700 "$askpass_dir/askpass.sh"
export GITEA_USERNAME="$(params.gitops-username)"
export GIT_ASKPASS="$askpass_dir/askpass.sh"
export GIT_ASKPASS_REQUIRE=force
export GIT_TERMINAL_PROMPT=0
cd /workspace/source/repo
test "$(git rev-parse HEAD)" = "$(params.revision)"
check_source_head() {
phase="$1"
expected="${2:-$(params.revision)}"
source_head_url="$(params.git-url)"
if [ "$runtime_lane" = "true" ]; then
source_head_url="$(params.git-read-url)"
fi
source_head_url="$(params.git-read-url)"
latest_file="$(mktemp)"
if ! git_timed "source-head-$phase" 45 git ls-remote "$source_head_url" "refs/heads/$(params.source-branch)" > "$latest_file"; then
echo '{"status":"failed","reason":"source-head-check-failed","phase":"'"$phase"'","sourceBranch":"'"$(params.source-branch)"'","sourceRevision":"'"$(params.revision)"'"}'
@@ -182,11 +177,8 @@ node scripts/run-bun.mjs scripts/gitops-render.mjs --lane "$lane" --catalog-path
ci_timing_emit gitops-render succeeded "$gitops_render_started_ms"
check_source_head before-push
workdir="$(mktemp -d)"
gitops_read_url="$(params.git-url)"
gitops_read_url="$(params.gitops-read-url)"
gitops_write_url="$(params.git-write-url)"
if [ "$runtime_lane" = "true" ]; then
gitops_read_url="$(params.git-read-url)"
fi
gitops_clone_started_ms="$(ci_now_ms)"
git_timed gitops-clone 180 git clone --no-checkout "$gitops_read_url" "$workdir/gitops"
cd "$workdir/gitops"
@@ -28,7 +28,7 @@ const catalog = JSON.parse(fs.readFileSync(process.env.HWLAB_ARTIFACT_CATALOG_PA
const plan = fs.existsSync("/workspace/source/affected-services.json") ? JSON.parse(fs.readFileSync("/workspace/source/affected-services.json", "utf8")) : {};
const service = (catalog.services || []).find((item) => item.serviceId === serviceId) || {};
const planned = (plan.services || []).find((item) => item.serviceId === serviceId) || {};
const envReuse = planned.runtimeMode === "env-reuse-git-mirror-checkout" || service.runtimeMode === "env-reuse-git-mirror-checkout" || planned.envReuse === true || service.envReuse === true;
const envReuse = planned.runtimeMode === "env-reuse-gitea-checkout" || service.runtimeMode === "env-reuse-gitea-checkout" || planned.envReuse === true || service.envReuse === true;
const image = envReuse ? (service.environmentImage || service.image || "") : (service.image || "");
const digest = envReuse ? (service.environmentDigest || service.digest || "not_published") : (service.digest || "not_published");
const imageTag = service.imageTag || (image.includes(":") ? image.slice(image.lastIndexOf(":") + 1) : "");
@@ -48,7 +48,7 @@ const values = {
"component-input-hash": componentInputHash,
"environment-input-hash": environmentInputHash,
"code-input-hash": codeInputHash,
"runtime-mode": envReuse ? "env-reuse-git-mirror-checkout" : "service-image",
"runtime-mode": envReuse ? "env-reuse-gitea-checkout" : "service-image",
"boot-repo": planned.bootRepo || service.bootRepo || "",
"boot-commit": envReuse ? revision : (service.bootCommit || ""),
"boot-sh": planned.bootSh || service.bootSh || "",
@@ -9,7 +9,7 @@ if [ -d "$ci_node_deps/yaml" ] && [ ! -e node_modules/yaml ]; then
ln -s "$ci_node_deps/yaml" node_modules/yaml
fi
test "$(git rev-parse HEAD)" = "$(params.revision)"
HWLAB_CATALOG_PATH="$(params.catalog-path)" HWLAB_GIT_URL="$(params.git-url)" HWLAB_GIT_READ_URL="$(params.git-read-url)" HWLAB_GITOPS_BRANCH="$(params.gitops-branch)" HWLAB_SERVICES="$(params.services)" node scripts/ci/restore-artifact-catalog.mjs
HWLAB_CATALOG_PATH="$(params.catalog-path)" HWLAB_GIT_URL="$(params.git-url)" HWLAB_GIT_READ_URL="$(params.gitops-read-url)" HWLAB_GITOPS_BRANCH="$(params.gitops-branch)" HWLAB_SERVICES="$(params.services)" node scripts/ci/restore-artifact-catalog.mjs
node scripts/ci-plan.mjs --lane "$(params.lane)" --base-ref "$(params.release-base-commit)" --target-ref HEAD --deploy-config deploy/deploy.yaml --artifact-catalog "$(params.catalog-path)" --registry-prefix "$(params.registry-prefix)" --services "$(params.services)" --release-target "$(params.release-target)" --release-consumer "$(params.release-consumer)" --verify-reuse-registry > /workspace/source/ci-plan.json
node scripts/ci/verify-reviewed-plan.mjs --actual-plan /workspace/source/ci-plan.json --reviewed-plan "$(params.reviewed-plan)"
node - <<'NODE'
@@ -25,7 +25,7 @@ const entries = allServices.map((serviceId) => {
const service = byService.get(serviceId) || {};
const serviceSelected = selectedSet.has(serviceId);
const rolloutAffected = serviceSelected && affected.has(serviceId);
const envReuse = service.runtimeMode === "env-reuse-git-mirror-checkout" || service.envReuse === true;
const envReuse = service.runtimeMode === "env-reuse-gitea-checkout" || service.envReuse === true;
const buildRequired = serviceSelected && (plannedBuildServices ? plannedBuildServices.has(serviceId) : (envReuse ? service.envChanged === true : rolloutAffected));
return {
serviceId,
@@ -9,16 +9,8 @@ export HWLAB_SOURCE_REVISION="$(params.revision)"
echo '{"event":"ci-base-image","phase":"prepare-source","image":"__HWLAB_CI_TOOLS_IMAGE__","policy":"no-runtime-apk"}'
for tool in node git timeout; do command -v "$tool" >/dev/null 2>&1 || { echo '{"event":"ci-base-image","ok":false,"reason":"missing-tool","tool":"'"$tool"'"}'; exit 31; }; done
node -e 'console.log(JSON.stringify({event:"ci-base-image",phase:"prepare-source",ok:true,node:process.version}))'
# __HWLAB_GIT_SSH_SHELL__
git_url_requires_ssh() { case "$1" in git@*|ssh://*) return 0 ;; *) return 1 ;; esac; }
git_read_url="$(params.git-read-url)"
if git_url_requires_ssh "$git_read_url"; then
for tool in ssh ssh-keyscan; do command -v "$tool" >/dev/null 2>&1 || { echo '{"event":"ci-base-image","ok":false,"reason":"missing-tool","tool":"'"$tool"'"}'; exit 31; }; done
git_ssh_setup
else
echo '{"event":"git-ssh-setup","phase":"prepare-source","status":"skipped","reason":"non-ssh-git-read-url"}'
fi
gitops_read_url="$(params.gitops-read-url)"
rm -rf /workspace/source/repo
source_clone_started_ms="$(ci_now_ms)"
git_timed source-clone 180 git clone --branch "$(params.source-branch)" "$git_read_url" /workspace/source/repo
@@ -35,8 +27,8 @@ ci_timing_emit prepare-source-dependencies succeeded "$prepare_source_dependenci
catalog_path="$(params.catalog-path)"
mkdir -p "$(dirname "$catalog_path")"
catalog_fetch_started_ms="$(ci_now_ms)"
if git_timed catalog-ls-remote 45 git ls-remote --exit-code --heads "$git_read_url" "$(params.gitops-branch)" >/dev/null; then
git remote set-url gitops-catalog "$git_read_url" 2>/dev/null || git remote add gitops-catalog "$git_read_url"
if git_timed catalog-ls-remote 45 git ls-remote --exit-code --heads "$gitops_read_url" "$(params.gitops-branch)" >/dev/null; then
git remote set-url gitops-catalog "$gitops_read_url" 2>/dev/null || git remote add gitops-catalog "$gitops_read_url"
git_timed catalog-fetch 90 git fetch --depth 1 gitops-catalog "$(params.gitops-branch)" >/dev/null || true
if git cat-file -e FETCH_HEAD:"$catalog_path" 2>/dev/null; then
git show FETCH_HEAD:"$catalog_path" > /tmp/hwlab-gitops-artifact-catalog.json