可用于自动化处理 GitCode 仓库的 PR 构建检查,通过发现 .xuanwu/ 目录下 Dockerfiles,创建 Jenkins Pipeline 任务,执行并行构建并通过 PR 评论和标签反馈状态。【此简介由AI生成】
当前访问频次受限,请登录后继续访问
xuanwu-ci-bot
GitCode PR webhook bot that:
- pr-check — discovers
.xuanwu/Dockerfiles, ensures a per-repo Jenkins Pipeline job, runs paralleldocker buildchecks, and reports status via PR comments and labels. - push-image — when a repo has
.xuanwu/push-image.yamland a push updates the branch named in that file, builds the configured Dockerfile and pushes to Harbor (does not changeci/*labels; no PR comment when triggered by push). - release — when a repo has
.xuanwu/release.yamland a tag is pushed, builds the configured Dockerfile, extractsartifactsfrom the image, and creates/uploads a GitCode Release. - nightly — bot daily tick (
NIGHTLY_*) for allowlisted repos that have.xuanwu/nightly/Dockerfiles on the latestv*tag; failure SMTP. Seedocs/nightly.md. - issue-notify — on Issue opened for repos in
ISSUE_NOTIFY_REPOS, emails configured recipients via SMTP (Tencent Exmail). - PR commands — slash commands in PR comments:
/retest,/cancel,/ci status,/help,/lgtm,/approve,/hold. Permissions follow GitCode collaborator roles. Seedocs/pr-commands.md.
Invite the bot user lxbot into each repository and set the role to Maintainer (comments and ci/* labels). See docs/onboarding.md.
Flow
GitCode MR open/sync → xuanwu-ci-bot → Jenkins …/pr-check → comment + ci/pass|fail
GitCode push to branch → if .xuanwu/push-image.yaml matches → Jenkins …/push-image
GitCode tag push → if .xuanwu/release.yaml present → Jenkins …/release → GitCode Release assets
Bot cron (NIGHTLY_*) → allowlist + .xuanwu/nightly/* on latest v* → Jenkins …/nightly
Manual POST /hooks/nightly may pass ref/sha (fork branch verify; not used by cron)
GitCode Issue opened → if repo in ISSUE_NOTIFY_REPOS → SMTP mail to ISSUE_NOTIFY_TO
GitCode PR comment → /retest | /cancel | /lgtm | /approve | /hold … → Jenkins / labels + reply
Shared Rust botgate L0
Org-wide base image (tools only): registry.xuanwu.openatom.cn/xuanwu/ci-cache/rust-botgate-base:<tag> — see ci-cache/. The tag is rust<channel>-gate<bot-gate>-metric<bot-metric>-audit<cargo-audit>-r<revision> (default rust1.99.1-gate1.4.0-metric1.3.0-audit0.22.1-r1). Yaml overrides any field; Jenkins builds and pushes a missing tag. New Rust repos copy examples/.xuanwu/botgate/.
pr-check
- Skip: no matching Dockerfiles /
botgate.yaml→ no comment and no label - Running: build triggered → comment with Jenkins URL +
ci/running - Done: callback → result table +
ci/passorci/fail - Checkout: MR builds clone the target (upstream) repo at
refs/merge-requests/<n>/head(GitLab-style; not GitHubrefs/pull/*). Private forks work as long as the CI git credential can read the target repo. Push-triggered pr-check still checks out by commit SHA. - Clone strategy (default: shallow depth=1, no tags): optional per-check Dockerfile directive
# xuanwu-ci-clone: shallow=false fetch_tags=true(alsodepth=N). Bot merges all checks with the most permissive settings for the single Checkout stage.
push-image
- Opt-in: push SHA must contain
.xuanwu/push-image.yaml(otherwise skip silently) - Trigger:
pushevent andevent.branchmatches YAMLbranch(defaultmain); optionaltrigger_pathspath filter - Job:
xuanwu-ci/{owner}/{repo}/push-image(separate frompr-check) - Image / Dockerfile / registry / tag: from YAML (
image_tag_prefix+SHA,image_tag_from: rust-toolchain→ channel,rust-toolchain-sha→ immutable<channel>-<sha7>, orl0-versions→ version tag) - Jenkins credentials: bot defaults only (not in repo YAML)
- Does not stop or share state with
pr-check; Redis claim prefix isxuanwu-ci:push-image:…
release
- Opt-in: tag SHA must contain
.xuanwu/release.yaml(otherwise skip silently) - Trigger:
pushwithrefs/tags/*(tag delete ignored) - Job:
xuanwu-ci/{owner}/{repo}/release - Dockerfile + artifact paths (+ optional
tag_pattern/ name / body / prerelease): from YAML - Pipeline creates the GitCode Release if missing, then uploads assets via
upload_url - Redis claim:
xuanwu-ci:release:result:{owner}/{repo}:{tag}
Jenkins build Console under xuanwu-ci/** is intended to be anonymously readable (see docs/onboarding.md). Website (and similar) can still pin tags via terraform (website_image) after a successful push.
Multi-replica: Redis SET NX trigger leases (plus existing result claims) serialize webhook intake across pods — see docs/onboarding.md § Redis.
Quick start
cp .env.example .env
# fill secrets — `cargo run` loads `.env` via dotenvy
cargo run
Usage docs (Chinese): GET /
Health: GET /healthz
Webhooks: POST /hooks/gitcode, POST /hooks/jenkins
Environment variables
| Variable | Required | Default | Description |
|---|---|---|---|
LISTEN_ADDR |
no | 0.0.0.0:8080 |
Bind address |
BOT_PUBLIC_BASE_URL |
yes | — | Public base URL (docs / external) |
JENKINS_CALLBACK_BASE_URL |
no | BOT_PUBLIC_BASE_URL |
URL Jenkins agents POST results to; on k3s use http://xuanwu-ci-bot.ci-bot.svc.cluster.local:8080 |
GITCODE_API_BASE |
no | https://api.gitcode.com/api/v5 |
GitCode API base |
GITCODE_TOKEN |
yes | — | PAT for tree/comment/labels |
GITCODE_WEBHOOK_SECRET |
yes | — | Webhook password or HMAC key |
GITCODE_WEBHOOK_ENCRYPTION_TYPE |
no | 0 |
0 password, 1 HMAC-SHA256 |
JENKINS_URL |
yes | — | Jenkins root URL |
JENKINS_USER |
yes | — | Jenkins user |
JENKINS_TOKEN |
yes | — | Jenkins API token |
JENKINS_CALLBACK_SECRET |
yes | — | Shared secret for /hooks/jenkins |
JENKINS_GIT_CREDENTIALS_ID |
no | d4cedbd2-… |
Git credentials id for checkout |
JENKINS_FOLDER_ROOT |
no | xuanwu-ci |
Root folder for generated jobs |
JENKINS_INSECURE_TLS |
no | false |
true to skip Jenkins TLS verify (private CA) |
PUSH_IMAGE_ENABLED |
no | true |
Global kill-switch for branch-push Harbor push |
RELEASE_ENABLED |
no | true |
Global kill-switch for tag → GitCode Release |
JENKINS_HARBOR_CREDENTIALS_ID |
no | 3cefd32b-… |
Harbor username/password credentials id |
JENKINS_GITCODE_TOKEN_CREDENTIALS_ID |
no | 29188ace-… |
String credentials for GITCODE_TOKEN build-arg |
JENKINS_GITEE_TOKEN_CREDENTIALS_ID |
no | d73f7f06-… |
String credentials for GITEE_TOKEN build-arg |
GITHUB_TOKEN |
no | empty | GitHub PAT passed as docker --build-arg GITHUB_TOKEN (website README fetch) |
REDIS_URL |
yes | — | Redis for PR-run state (redis://:pass@redis-master:6379) |
SMTP_HOST |
no | — | SMTP host (e.g. smtp.exmail.qq.com); omit to disable issue mail |
SMTP_PORT |
no | 465 |
SMTP port |
SMTP_USERNAME |
no* | — | SMTP username (* required with host/password) |
SMTP_PASSWORD |
no* | — | SMTP password / Exmail client password |
SMTP_FROM |
no | username | From header |
SMTP_IMPLICIT_TLS |
no | true if port 465 |
Implicit TLS wrapper |
ISSUE_NOTIFY_TO |
no | empty | Recipients (comma/;/whitespace); multiple To |
ISSUE_NOTIFY_REPOS |
no | empty | owner/repo allowlist for issue-open mail |
PR_COMMANDS_ENABLED |
no | true |
Global kill-switch for PR comment slash commands |
GITCODE_BOT_USERNAME |
no | empty | Bot account login; its comments are never parsed as commands |
PR_COMMAND_ALLOWLIST |
no | empty | GitCode logins that may run any PR command, bypassing role and author rules |
Webhook auth
- Password (
0): query?password=, headerX-GitCode-Token, or bodypassword/token - HMAC (
1): headerX-Gitcode-Signature(also acceptsX-Hub-Signature-256) assha256=<hex>of HMAC-SHA256(body, secret)
Callback auth
Header X-Xuanwu-Callback-Secret must match JENKINS_CALLBACK_SECRET.
.xuanwu convention
pr-check Dockerfiles
Recursive scan under .xuanwu/ for:
DockerfileDockerfile.**.Dockerfile
*.dockerignore is ignored by check discovery (not treated as a Dockerfile).
Each file is one check; all must succeed. See examples/.xuanwu/.
CI Rust Dockerfiles (and repo .cargo/config.toml) replace crates.io with:
[source.crates-io]
replace-with = "xuanwu"
[source.xuanwu]
registry = "sparse+https://crates.xuanwu.openatom.cn/api/v1/cratesio/"
push-image config
Add .xuanwu/push-image.yaml to opt in:
branch: main
harbor_registry: registry.xuanwu.openatom.cn
# Single image (legacy) or multi-target:
images:
- dockerfile: Dockerfile
harbor_repo: xuanwu/website # optional; default {owner}/{repo}
image_tag_prefix: "" # optional; e.g. beta-
# - dockerfile: ci-cache/Dockerfile.rust-botgate-base
# harbor_repo: xuanwu/ci-cache/rust-botgate-base
# image_tag_from: rust-toolchain
# trigger_paths: [ci-cache/]
When image_tag_from: rust-toolchain, default trigger_paths cover ci-cache/ / toolchain so routine app commits do not rebuild the base. Do not put secrets or Jenkins credentialsId in this file.
release config
Add .xuanwu/release.yaml to opt in on tag push:
dockerfile: .xuanwu/Dockerfile.release
artifacts:
- /out/hello.txt
# tag_pattern: "^v" # optional regex
# name: "" # default = tag
# body: "notes" # GitCode requires non-empty; omit → "Release {tag}"
# prerelease: false
The release Dockerfile must leave files at the listed absolute paths; Jenkins docker create + docker cp extracts them into Release assets. If the final stage is scratch, set an explicit CMD (e.g. the binary path) — otherwise docker create fails with no command specified. See examples/.xuanwu/Dockerfile.release.
Labels
Mutually exclusive: ci/running, ci/pass, ci/fail, ci/cancelled.
A head SHA with no .xuanwu checks is silent (no comment, no label).
A new run always sets exclusive ci/running (replacing any prior terminal label); the result callback then replaces it with ci/pass or ci/fail. /cancel sets ci/cancelled. A merge-request webhook does not do this for a head SHA that already has a posted result, or when the MR state is already merged or closed. Only /retest or a new head SHA replaces the terminal label with ci/running.
Review labels (set by PR commands, independent of ci/*): lgtm, approved, do-not-merge/hold. See docs/pr-commands.md.
Docker
docker build -f docker/Dockerfile -t registry.xuanwu.openatom.cn/xuanwu/xuanwu-ci-bot:local .
Harbor publish for this repo: push the branch named in .xuanwu/push-image.yaml (bot-managed push-image job). Pin the resulting tag in xuanwu-terraform/envs/onprem/ci-bot as app_image_tag.
Development
cargo test
cargo clippy --all-targets --locked -- -D warnings
cargo build --release
PR checks: .xuanwu/Dockerfile.clippy (via xuanwu-ci pr-check).