ci: add PaC-driven AgentRun JD01 v02 pipeline
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
json_escape() {
|
||||
sed 's/\\/\\\\/g; s/"/\\"/g; s/ /\\t/g' | tr '\n' ' '
|
||||
}
|
||||
|
||||
json_string() {
|
||||
printf '%s' "$1" | json_escape
|
||||
}
|
||||
|
||||
now_epoch() {
|
||||
date +%s
|
||||
}
|
||||
|
||||
duration_seconds() {
|
||||
start="$1"
|
||||
end="$2"
|
||||
if [ -n "$start" ] && [ -n "$end" ]; then
|
||||
s=$(date -d "$start" +%s 2>/dev/null || echo "")
|
||||
e=$(date -d "$end" +%s 2>/dev/null || echo "")
|
||||
if [ -n "$s" ] && [ -n "$e" ]; then
|
||||
echo $((e - s))
|
||||
return
|
||||
fi
|
||||
fi
|
||||
echo null
|
||||
}
|
||||
|
||||
api_url() {
|
||||
printf '%s/api/v1/%s' "$UNIDESK_PAC_GITEA_BASE_URL" "$1"
|
||||
}
|
||||
|
||||
gitea_api() {
|
||||
method="$1"
|
||||
path="$2"
|
||||
body="${3:-}"
|
||||
if [ -n "$body" ]; then
|
||||
curl -fsS -u "$UNIDESK_PAC_GITEA_ADMIN_USERNAME:$UNIDESK_PAC_GITEA_ADMIN_PASSWORD" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-X "$method" \
|
||||
--data "$body" \
|
||||
"$(api_url "$path")"
|
||||
else
|
||||
curl -fsS -u "$UNIDESK_PAC_GITEA_ADMIN_USERNAME:$UNIDESK_PAC_GITEA_ADMIN_PASSWORD" \
|
||||
-X "$method" \
|
||||
"$(api_url "$path")"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_token() {
|
||||
name="unidesk-pac-$(date +%Y%m%d%H%M%S)"
|
||||
body=$(printf '{"name":"%s"}' "$name")
|
||||
out=$(gitea_api POST "users/$UNIDESK_PAC_GITEA_API_USERNAME/tokens" "$body")
|
||||
printf '%s' "$out" | sed -n 's/.*"sha1"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p'
|
||||
}
|
||||
|
||||
ensure_webhook() {
|
||||
hooks=$(gitea_api GET "repos/$UNIDESK_PAC_GITEA_OWNER/$UNIDESK_PAC_GITEA_REPO/hooks")
|
||||
hook_id=$(printf '%s' "$hooks" | tr '{' '\n' | awk -v url="$UNIDESK_PAC_WEBHOOK_URL" 'index($0, url)>0 { if (match($0, /"id"[[:space:]]*:[[:space:]]*[0-9]+/)) { print substr($0, RSTART); exit } }' | sed -n 's/[^0-9]*\([0-9][0-9]*\).*/\1/p')
|
||||
body=$(printf '{"type":"gitea","config":{"url":"%s","content_type":"json","secret":"%s"},"events":["push"],"active":true}' "$UNIDESK_PAC_WEBHOOK_URL" "$UNIDESK_PAC_WEBHOOK_SECRET")
|
||||
if [ -n "$hook_id" ]; then
|
||||
gitea_api PATCH "repos/$UNIDESK_PAC_GITEA_OWNER/$UNIDESK_PAC_GITEA_REPO/hooks/$hook_id" "$body" >/dev/null
|
||||
else
|
||||
created=$(gitea_api POST "repos/$UNIDESK_PAC_GITEA_OWNER/$UNIDESK_PAC_GITEA_REPO/hooks" "$body")
|
||||
hook_id=$(printf '%s' "$created" | sed -n 's/.*"id"[[:space:]]*:[[:space:]]*\([0-9][0-9]*\).*/\1/p')
|
||||
fi
|
||||
printf '%s' "$hook_id"
|
||||
}
|
||||
|
||||
repository_manifest() {
|
||||
cat <<EOF
|
||||
apiVersion: pipelinesascode.tekton.dev/v1alpha1
|
||||
kind: Repository
|
||||
metadata:
|
||||
name: $UNIDESK_PAC_REPOSITORY_NAME
|
||||
namespace: $UNIDESK_PAC_TARGET_NAMESPACE
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: unidesk
|
||||
app.kubernetes.io/part-of: agentrun
|
||||
unidesk.ai/spec: GH-1552
|
||||
spec:
|
||||
url: $UNIDESK_PAC_REPOSITORY_URL
|
||||
git_provider:
|
||||
type: gitea
|
||||
url: $UNIDESK_PAC_GITEA_BASE_URL
|
||||
secret:
|
||||
name: $UNIDESK_PAC_SECRET_NAME
|
||||
key: $UNIDESK_PAC_TOKEN_KEY
|
||||
webhook_secret:
|
||||
name: $UNIDESK_PAC_SECRET_NAME
|
||||
key: $UNIDESK_PAC_WEBHOOK_SECRET_KEY
|
||||
concurrency_limit: $UNIDESK_PAC_CONCURRENCY_LIMIT
|
||||
params:
|
||||
$(printf '%s' "$UNIDESK_PAC_PARAMS_JSON" | node -e 'const fs=require("fs"); const p=JSON.parse(fs.readFileSync(0,"utf8")); for (const [name,value] of Object.entries(p)) console.log(` - name: ${name}\n value: ${JSON.stringify(String(value))}`);')
|
||||
EOF
|
||||
}
|
||||
|
||||
apply_release() {
|
||||
tmp=$(mktemp)
|
||||
printf '%s' "$UNIDESK_PAC_RELEASE_MANIFEST_B64" | base64 -d > "$tmp"
|
||||
kubectl apply --server-side --force-conflicts --field-manager="$UNIDESK_PAC_FIELD_MANAGER" -f "$tmp" >/dev/null
|
||||
rm -f "$tmp"
|
||||
}
|
||||
|
||||
wait_ready() {
|
||||
timeout="${UNIDESK_PAC_WAIT_TIMEOUT_SECONDS:-55}"
|
||||
end=$(( $(now_epoch) + timeout ))
|
||||
while [ "$(now_epoch)" -lt "$end" ]; do
|
||||
ready=$(kubectl -n "$UNIDESK_PAC_RELEASE_NAMESPACE" get deploy "$UNIDESK_PAC_CONTROLLER_SERVICE_NAME" -o jsonpath='{.status.readyReplicas}' 2>/dev/null || true)
|
||||
desired=$(kubectl -n "$UNIDESK_PAC_RELEASE_NAMESPACE" get deploy "$UNIDESK_PAC_CONTROLLER_SERVICE_NAME" -o jsonpath='{.spec.replicas}' 2>/dev/null || true)
|
||||
if [ "${ready:-0}" = "${desired:-1}" ] && [ "${ready:-0}" != "0" ]; then
|
||||
return 0
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
apply_action() {
|
||||
if [ "$UNIDESK_PAC_DRY_RUN" = "1" ]; then
|
||||
printf '{"ok":true,"mode":"dry-run","mutation":false,"valuesPrinted":false}\n'
|
||||
return
|
||||
fi
|
||||
apply_release
|
||||
kubectl create ns "$UNIDESK_PAC_TARGET_NAMESPACE" --dry-run=client -o yaml | kubectl apply -f - >/dev/null
|
||||
token=$(ensure_token)
|
||||
test -n "$token"
|
||||
kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" create secret generic "$UNIDESK_PAC_SECRET_NAME" \
|
||||
--from-literal="$UNIDESK_PAC_TOKEN_KEY=$token" \
|
||||
--from-literal="$UNIDESK_PAC_WEBHOOK_SECRET_KEY=$UNIDESK_PAC_WEBHOOK_SECRET" \
|
||||
--dry-run=client -o yaml | kubectl apply -f - >/dev/null
|
||||
repository_manifest | kubectl apply --server-side --force-conflicts --field-manager="$UNIDESK_PAC_FIELD_MANAGER" -f - >/dev/null
|
||||
hook_id=$(ensure_webhook)
|
||||
ready=false
|
||||
if wait_ready; then ready=true; fi
|
||||
printf '{"ok":%s,"mode":"confirmed","mutation":true,"controllerReady":%s,"webhook":{"present":true,"id":"%s","url":"%s"},"repository":"%s","namespace":"%s","valuesPrinted":false}\n' "$ready" "$ready" "$(json_string "$hook_id")" "$(json_string "$UNIDESK_PAC_WEBHOOK_URL")" "$(json_string "$UNIDESK_PAC_REPOSITORY_NAME")" "$(json_string "$UNIDESK_PAC_TARGET_NAMESPACE")"
|
||||
}
|
||||
|
||||
condition_status() {
|
||||
kubectl -n "$1" get "$2" "$3" -o jsonpath='{range .status.conditions[*]}{.type}={.status}:{.reason}{";"}{end}' 2>/dev/null || true
|
||||
}
|
||||
|
||||
pipeline_rows() {
|
||||
payload=$(kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get pipelinerun -o json 2>/dev/null || echo '{"items":[]}')
|
||||
PIPELINE_JSON="$payload" node <<'NODE'
|
||||
const input = process.env.PIPELINE_JSON || '{"items":[]}';
|
||||
const data = input ? JSON.parse(input) : { items: [] };
|
||||
function cond(item) {
|
||||
const c = (item.status?.conditions || []).find((x) => x.type === 'Succeeded') || {};
|
||||
return { status: c.status || '', reason: c.reason || '' };
|
||||
}
|
||||
function durationSeconds(item) {
|
||||
const start = item.status?.startTime;
|
||||
const done = item.status?.completionTime;
|
||||
if (!start) return null;
|
||||
return Math.max(0, Math.round(((done ? Date.parse(done) : Date.now()) - Date.parse(start)) / 1000));
|
||||
}
|
||||
const prefix = process.env.UNIDESK_PAC_PIPELINE_RUN_PREFIX;
|
||||
const rows = (data.items || [])
|
||||
.filter((item) => item.metadata?.name?.startsWith(prefix))
|
||||
.sort((a, b) => Date.parse(b.metadata?.creationTimestamp || 0) - Date.parse(a.metadata?.creationTimestamp || 0))
|
||||
.slice(0, 8)
|
||||
.map((item) => {
|
||||
const c = cond(item);
|
||||
return {
|
||||
name: item.metadata.name,
|
||||
status: c.status,
|
||||
reason: c.reason,
|
||||
createdAt: item.metadata.creationTimestamp || null,
|
||||
startTime: item.status?.startTime || null,
|
||||
completionTime: item.status?.completionTime || null,
|
||||
durationSeconds: durationSeconds(item),
|
||||
sourceCommit: item.metadata.labels?.['pipelinesascode.tekton.dev/sha'] || item.metadata.labels?.['agentrun.pikastech.local/source-commit'] || null,
|
||||
};
|
||||
});
|
||||
process.stdout.write(JSON.stringify(rows));
|
||||
NODE
|
||||
}
|
||||
|
||||
task_rows() {
|
||||
payload=$(kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get taskrun -o json 2>/dev/null || echo '{"items":[]}')
|
||||
TASK_JSON="$payload" node <<'NODE'
|
||||
const input = process.env.TASK_JSON || '{"items":[]}';
|
||||
const data = input ? JSON.parse(input) : { items: [] };
|
||||
const pr = process.env.UNIDESK_PAC_TARGET_PIPELINERUN || '';
|
||||
function cond(item) {
|
||||
const c = (item.status?.conditions || []).find((x) => x.type === 'Succeeded') || {};
|
||||
return { status: c.status || '', reason: c.reason || '' };
|
||||
}
|
||||
function durationSeconds(item) {
|
||||
const start = item.status?.startTime;
|
||||
const done = item.status?.completionTime;
|
||||
if (!start) return null;
|
||||
return Math.max(0, Math.round(((done ? Date.parse(done) : Date.now()) - Date.parse(start)) / 1000));
|
||||
}
|
||||
const rows = (data.items || [])
|
||||
.filter((item) => !pr || item.metadata?.labels?.['tekton.dev/pipelineRun'] === pr)
|
||||
.sort((a, b) => Date.parse(a.metadata?.creationTimestamp || 0) - Date.parse(b.metadata?.creationTimestamp || 0))
|
||||
.slice(0, 20)
|
||||
.map((item) => {
|
||||
const c = cond(item);
|
||||
return { name: item.metadata.name, pipelineRun: item.metadata.labels?.['tekton.dev/pipelineRun'] || null, status: c.status, reason: c.reason, startTime: item.status?.startTime || null, completionTime: item.status?.completionTime || null, durationSeconds: durationSeconds(item) };
|
||||
});
|
||||
process.stdout.write(JSON.stringify(rows));
|
||||
NODE
|
||||
}
|
||||
|
||||
hook_summary() {
|
||||
hooks=$(gitea_api GET "repos/$UNIDESK_PAC_GITEA_OWNER/$UNIDESK_PAC_GITEA_REPO/hooks" 2>/dev/null || echo '[]')
|
||||
HOOKS_JSON="$hooks" node <<'NODE'
|
||||
const data = JSON.parse(process.env.HOOKS_JSON || '[]');
|
||||
const url = process.env.UNIDESK_PAC_WEBHOOK_URL;
|
||||
const rows = data.filter((item) => item?.config?.url === url).map((item) => ({ id: item.id, active: item.active, type: item.type, events: item.events || [], url: item.config?.url || '' }));
|
||||
process.stdout.write(JSON.stringify(rows));
|
||||
NODE
|
||||
}
|
||||
|
||||
status_action() {
|
||||
crd=$(kubectl get crd repositories.pipelinesascode.tekton.dev -o name 2>/dev/null || true)
|
||||
controller_ready=$(kubectl -n "$UNIDESK_PAC_RELEASE_NAMESPACE" get deploy "$UNIDESK_PAC_CONTROLLER_SERVICE_NAME" -o jsonpath='{.status.readyReplicas}/{.spec.replicas}' 2>/dev/null || echo "0/0")
|
||||
repository_condition=$(condition_status "$UNIDESK_PAC_TARGET_NAMESPACE" repository "$UNIDESK_PAC_REPOSITORY_NAME")
|
||||
pipelines=$(pipeline_rows)
|
||||
latest=$(printf '%s' "$pipelines" | node -e 'const fs=require("fs"); const a=JSON.parse(fs.readFileSync(0,"utf8")||"[]"); process.stdout.write(a[0]?.name||"")')
|
||||
export UNIDESK_PAC_TARGET_PIPELINERUN="$latest"
|
||||
tasks=$(task_rows)
|
||||
hooks=$(hook_summary)
|
||||
argo=$(kubectl -n "$UNIDESK_PAC_ARGO_NAMESPACE" get application "$UNIDESK_PAC_ARGO_APPLICATION" -o json 2>/dev/null | node -e 'const fs=require("fs"); const s=fs.readFileSync(0,"utf8").trim(); if(!s){process.stdout.write("{}"); process.exit(0)} const a=JSON.parse(s); process.stdout.write(JSON.stringify({sync:a.status?.sync?.status||null, health:a.status?.health?.status||null, revision:a.status?.sync?.revision||null}))' || echo '{}')
|
||||
printf '{"ok":%s,"crdPresent":%s,"controllerReady":"%s","repositoryCondition":"%s","webhooks":%s,"pipelineRuns":%s,"taskRuns":%s,"argo":%s,"valuesPrinted":false}\n' \
|
||||
"$( [ -n "$crd" ] && [ "$controller_ready" != "0/0" ] && echo true || echo false )" \
|
||||
"$( [ -n "$crd" ] && echo true || echo false )" \
|
||||
"$(json_string "$controller_ready")" \
|
||||
"$(json_string "$repository_condition")" \
|
||||
"$hooks" "$pipelines" "$tasks" "$argo"
|
||||
}
|
||||
|
||||
webhook_test_action() {
|
||||
hooks=$(hook_summary)
|
||||
hook_id=$(printf '%s' "$hooks" | node -e 'const fs=require("fs"); const a=JSON.parse(fs.readFileSync(0,"utf8")||"[]"); process.stdout.write(String(a[0]?.id||""))')
|
||||
test -n "$hook_id"
|
||||
gitea_api POST "repos/$UNIDESK_PAC_GITEA_OWNER/$UNIDESK_PAC_GITEA_REPO/hooks/$hook_id/tests" '{}' >/dev/null
|
||||
printf '{"ok":true,"mutation":true,"webhookTestSent":true,"hookId":"%s","valuesPrinted":false}\n' "$(json_string "$hook_id")"
|
||||
}
|
||||
|
||||
case "$UNIDESK_PAC_ACTION" in
|
||||
apply) apply_action ;;
|
||||
status) status_action ;;
|
||||
webhook-test) webhook_test_action ;;
|
||||
*) printf '{"ok":false,"error":"unsupported-action","valuesPrinted":false}\n'; exit 2 ;;
|
||||
esac
|
||||
Reference in New Issue
Block a user