Renderizado do repositório de origem, preservando títulos, exemplos, código, tabelas, links e imagens.
Publish Work to Gallery
发布作品至华为云高校运营平台。确定性子任务优先调用内置脚本(单入口、fail-fast、自带 --help),stdout/退出码即判定依据。脚本报错才查 references/troubleshooting.md。
Pipeline
Step 1 (workDir, domainID, STS)
Step 2 (port→变量, envUrl→变量) ← 依赖 Step 1
├── Step 3 (gitUrl, gitBranch, workName) ← 只依赖 workDir
├── Step 5 (introduction, camps) ← 只依赖 workDir + STS
├── Step 4 (coverImage) ← 依赖 port + workName
└── Step 6 (detailZip) ← 依赖 workName + 代码分析
Step 7 (trainingCampId) ← 依赖 Step 5 camps
Step 8 (publish) ← 依赖以上全部并行(Step 2 完成后):Step 3/4/5/6 互不依赖,四者全并行(取最长路径 Step 4 ~90s,而非串行 ~160s)。Step 4 内 preflight + ensure-utf8 再并行。
变量贯穿:port/workName/envUrl 在最早步骤确定后作为变量传递全流程,禁止硬编码(隧道 URL 含 port,换端口时 envUrl 随之变)。
执行模型:① 脚本契约优先——按 stdout 成功信号判定,成功路径零源码读取;失败才 脚本提示 → troubleshooting 对应节 → grep 脚本源码 逐级查 ② 禁止手工绕过门禁(publish-work.mjs 内置 verifyGates() 复核)③ 网络超时统一 3s ④ detect-env.mjs 确定平台后只读对应平台 reference(按目录跳转,不全读)⑤ 合并无依赖的 bash 调用:用 ; 串联进一条命令,减少工具往返(每次 ~1-2s)。
脚本契约速查
| 步骤 | 脚本/命令 | 成功信号(exit 0 且) | 失败(exit 1;2=参数错) |
|---|---|---|---|
| 前置 | api.mjs GET /v1/gallery/announcements/current --prefix open-api-guest | stdout 首行 #status=200 | 超时/拒连 → 提示排查网络 |
| 前置 | node <skill>/scripts/check-version.mjs(自动读自身 version + 请求远端版本,内部解析,stdout 仅状态行) | exit 0 且首行 status=ok/status=skip | exit 1(首行 status=outdated + 平台下发升级文案)→ 原样提示用户升级,停止发布 |
| 5/7 | api.mjs GET "/v1/gallery/camps" --creds-file <json> --fields "id,name,school,status,startsAt,endsAt" | #status=200(--fields 投影减 token) | 401 → 刷新 STS |
| 1 | scan-projects.mjs | stdout #project <n> <path> <type> | exit 1=无候选 |
| 1 | resolve-domain.mjs [--hcloud <exe>] | stdout #domain=<id> name=<name> | exit 1=hcloud 失败(stderr 含排查指引) |
| 1 | gen_sts.py --account <id> | 写 sts-creds.json(含 _refresh)+sts-creds.sh | 见 [troubleshooting#domain-id-resolution-issues] |
| 3 | strip-git-credential.mjs "<rawUrl>" | stdout=安全 URL(https:// 开头、.git 结尾、无 @) | @/非 https/非 .git → 停止发布 |
| 3 | ensure-gitcode-credential.mjs | #credential=found 或 #credential=missing(exit 0) | exit 1=Windows 无凭证且 skill 未装(stderr 含安装命令) |
| 3 | extract-workname.mjs <workDir> | stdout #name=<name> source=<来源> | exit 1=workDir 不存在 |
| 4 | preflight.sh / preflight.ps1 | 写 font-gate-ok(ok=true gate=preflight) | 截图/封面/图表禁止 |
| 4 | ensure-utf8.mjs <dir> | 写 utf8-gate-ok | 编码非 UTF-8 → 禁止截图 |
| 4 | build-cover.mjs --url … --project-dir … --title … --out … | stdout 一行 #cover=… layout=… multi=… | exit 1 → 截图/校验/合成任一失败,stderr 详情 |
| 4 | screenshot_guard.py http://… --out <png> --quiet | 写 screenshot-gate-ok + 出 PNG | 字形/字符集不过 → 修字体重试 |
| 4 | generate_cover.py --screenshot … --title … --out … | 封面 PNG,再过 verify-glyphs.py | exit 1 → 禁止继续合成 |
| 4/6 | verify-glyphs.py <img> --quiet | exit 0(stdout 仅 1 行省 token) | exit 1 → 疑似豆腐块 → 修字体 |
| 5 | count-cjk.mjs --file <utf8.txt> | 总字符数 15~50 | exit 1 → 改写简介再计 |
| 6 | generate_diagram.py --html … --out … | 图表 PNG,再过 verify-glyphs.py --quiet | 同封面 |
| 6 | build-detail-zip.mjs --readme <md> --resources <dir> --out <zip> | 打包+校验一步完成 | exit 1 → 按输出修复重打 |
| 8 | check-gates.mjs --cover <png> --detail <zip> --strict --max-age 3600 --quiet | exit 0(stdout 仅 1 行省 token) | exit 1 → 回退重跑门禁 |
| 8 | publish-work.mjs <utf8-params.json> | #status=201(仅输出 workId/workUrl/reward) | 409/其他;失败时原样输出完整响应 |
标记文件位置:Linux/tmp/;Windowsos.tmpdir()(%TEMP%)。
前置锁定
- hcloud + AK/SK:Step 1 需要。未装/未配置 → troubleshooting#domain-id-resolution-issues。
- git:Step 3;zip:Step 6(Linux
yum install zip;Windows 用 .NETZipFile)。Windows 首次见 windows-setup.md。 - 连通性:
api.mjs GET "/v1/gallery/announcements/current" --prefix open-api-guest,连通 OK 即可继续。 - 版本检查(尽力而为):
node <skill>/scripts/check-version.mjs——脚本自动读本文件 frontmatterversion:并请求平台版本接口比较。status=ok/status=skip(非 2xx/解析失败)→ 继续,不阻塞;status=outdated→ 将脚本输出的升级文案原样告知用户并停止发布(连同行公告接口一起两条命令可并行/串联合并执行,减少工具往返)。
Core Workflow
Step 1: Select Work Directory & Resolve IAM Domain Info
- 告知用户(选目录=同意):将创建 IAM 自委托
SELF_VERIFY生成 STS 临时凭证(900s),永久 AK/SK 留本地不上传。 - 扫描项目根:
node <skill>/scripts/scan-projects.mjs——自动递归**/README.md,检查指示文件,排除skills//node_modules//dist/等,扫描 CodeArts 沙箱目录。stdout 每行#project <n> <path> <type>。 - 列候选,等用户选 →
workDir。 - 解析 Domain ID:
node <skill>/scripts/resolve-domain.mjs [--hcloud <exe>]——自动hcloud configure set --region+IAM KeystoneListAuthDomains+ 提取domain_id。stdout#domain=<id> name=<name>。hcloud 不在 PATH 时传--hcloud。凭证优先级:env → hcloud 已配置 → 引导用户(troubleshooting#2)。 - 生成 STS:
python <skill>/scripts/gen_sts.py --account <domainID>→sts-creds.json(含_refresh,使 401 自动刷新)。 - 输出:
已获取 IAM Domain ID: <id>,用于将作品关联至您的华为云账号。
Output: workDir、domainID、credsFile。
Step 2: Run Project & Open Tunnel
询问是否运行项目+开隧道。拒绝则 envUrl="" → Step 3。
- 端口清理:
netstat -ano | findstr :<port>(Win),残留进程 kill。 - 启动:优先入口脚本(
npm run dev/python main.py);纯前端npx http-server . -p <port>。≤30s 健康检查。 - 核对内容:
curl -sf --max-time 3 http://localhost:<port>首 100 字符须匹配本项目(<title>/项目名),Index of //404 → 清进程重启。 - 隧道:
devbridge host -p <port> -e 2,curl -sf --max-time 10验证。失败不阻塞,置envUrl=""。详见 devbridge-tunnel.md。
Output: port、envUrl(或 "")。
Step 3: Git Repository Info & Work Name
gitUrl 必填,gitBranch 必须显式读取。workName 从 README 解析/合成(<30 字符)。
git -C <workDir> remote get-url origin+branch --show-current。- 凭证排查:
node <skill>/scripts/ensure-gitcode-credential.mjs——自动检测本机 GitCode 凭证(git credential fill/~/.git-credentials/$GITCODE_TOKEN/cmdkey),无凭证时按平台给可行路径(Linux 提示手动配置 / Windows 检测gitcode-oauthskill 并输出安装命令)。exit 0=有凭证或已有路径;exit 1=Windows 无凭证且 skill 未装(stderr 含安装命令 + Windows Git Bash 兼容提示)。 - 凭证剥离:
gitUrl="$(node <skill>/scripts/strip-git-credential.mjs "$(git remote get-url origin)")"。硬校验:无@、https://开头、.git结尾。 - 命名:
node <skill>/scripts/extract-workname.mjs <workDir>——按优先级 frontmatter → H1 → manifest →index.html <title>→ 目录名提取。stdout#name=<name> source=<来源>。source=dirname时 agent 可合成/修改(<30 字符)。
Output: gitUrl、gitBranch、workName。
Step 4: Cover Image
门禁(并行):
| 门禁 | 命令 | 标记 |
|---|---|---|
| 渲染 | preflight.sh(Win: preflight.ps1) | font-gate-ok |
| 编码 | ensure-utf8.mjs <dir> | utf8-gate-ok |
门禁失败禁止截图/合成。preflight 以标记文件 ok=true 为准(轮询 ≤180s)。Linux 首次运行必须后台化(下载 Chromium ~111MB,前台会被 shell 超时 SIGPIPE 杀死,主进程死后不会写 gate 文件,轮询徒劳);Windows 通常已预装系统浏览器,前台即可。
封面一键生成(内置:页数判定 + 单/多图概率抽样 + 截图 + 合成 + 校验):
node <skill>/scripts/build-cover.mjs \
--url "http://127.0.0.1:<port>" --project-dir <workDir> \
--title "<workName>" --tagline "<一句话>" --description "<主体介绍,1~3 句>" \
--out <coverImagePath>- 布局决策在脚本内部闭环(agent 无感知):单页项目只用 7 个单图布局;多页项目(
.html数 ≥2)额外加入 3 个多图布局(大图+小图拼贴),统一概率池等概率抽样,多页时多图出现概率 3/10。 - 多图布局会截首页 + 候选页(最多 3 张小图);候选页截图失败自动跳过,最少保底 1 张。
- stdout 仅输出一行结果:
#cover=… layout=… multi=yes|no pages=<n> scheme=…;日志走 stderr。失败 exit 1。
⚠️ workName↔封面强依赖: 封面标题即 --title "${workName}"。一旦之后因 409 改名,必须用新 workName 重新合成封面(见 Step 8 改名重推流程),禁止旧封面配新名发布。排布规则、视觉基线见 work-preparation.md#screenshot-layout-rules。
Output: coverImagePath。
Step 5: Introduction
读写 README 生成一句话简介。总字符数 15~50,脚本计数通过才继续:
node <skill>/scripts/count-cjk.mjs --file "$env:TEMP\intro.txt" # Win 必须 --file(GBK 转码问题)规则见 work-preparation.md#introduction-generation。
⏩ 并行:api.mjs GET "/v1/gallery/camps" --query "pageNo=1&pageSize=100&periodYear=${currentYear}" --creds-file "${credsFile}" --fields "id,name,school,status,startsAt,endsAt" 预取供 Step 7。Output: introduction。
Step 6: Work Details
生成详解文章(500–1500 字)配图并打包 zip。
- 图表:必选系统架构图;可选 0–2 张(路由/依赖/统计图)。用 DevLens/GitNexus 理解项目后出图。禁用 emoji。
python <skill>/scripts/generate_diagram.py --html diagram.html --out resources/<name>.png --width 1280
python3 <skill>/scripts/verify-glyphs.py <img> --quiet # 每张含中文图必跑- 打包 zip + 校验一步到位(
build-detail-zip.mjs内置正确目录结构 README.md@根 + resources/,打包后自动跑服务端同源校验,跨平台无需 zip/ZipFile):
node <skill>/scripts/build-detail-zip.mjs --readme <README.md> --resources <dir> --out <workDetail.zip>Output: detailsZipPath。
Step 7: Select Training Camp
- 获取训练营列表(Step 5 预取则直接用)。
- 依据
startsAt/endsAt/status判定投稿状态(api-spec.md#submission-status-determination)。 - 分「可投稿」/「不可投稿」两表展示,每行必须包含 `school`(学校)列;
school为空时显示—。选不可投稿项提示「未开放投稿」。
Output: trainingCampId。
Step 8: Publish
- 宣示
开始发布作品 ${workName}。 - 门禁复核:
node <skill>/scripts/check-gates.mjs --cover <png> --detail <zip> --strict --max-age 3600 --quiet(exit 0 才继续;publish-work.mjs内置verifyGates()也会拦截)。 - 参数写入 UTF-8 JSON,Idempotency-Key=
gallery-publish-${domainID}-${timestamp}:
node <skill>/scripts/publish-work.mjs <utf8-params.json> --max-age 3600
# 成功仅输出 workId/workName/workUrl/reward;失败原样输出完整响应- 201 → 成功,取
workId/workUrl/reward,清理临时文件。积分提示(无论是否成功都必须提示「每日仅限一次」):
is_success===true:✅ 积分领取请求已提交。⚠️ 每日仅限一次。前往查看余额:https://developer.huaweicloud.com/growis_success===false:积分领取未成功:<error_msg>+ 每日限一次提示 + 成长中心地址 + 反馈入口https://gallery.developer.huaweicloud.com/gallery/profile?tab=feedback。
- 409 DUPLICATE → 停止当前发布。给选项:① 放弃 ② 改名重发。若改名重发,必须联动:
- 新名
workName'(<30 字符):用户自定,或在不带版本的常规名基础上加序号(如扫雷→扫雷-2)。禁止随意拼接无关字符。 - 封面重生成(封面标题须与新名一致):用原截图 + 原 tagline/description,仅换
--title "<workName'>"重新合成:
python <skill>/scripts/generate_cover.py --screenshot <原截图png> --title "<workName'>" --tagline "<原tagline>" --description "<原description>" --out <coverImagePath-新> --scheme auto
python3 <skill>/scripts/verify-glyphs.py <coverImagePath-新> --quiet # exit 0 才可继续- 门禁复核(封面已更新,须重跑):
node <skill>/scripts/check-gates.mjs --cover <封面-新> --detail <zip> --strict --max-age 3600 --quiet。 - 用新
workName'+ 新封面、新 Idempotency-Key 重新发布(回到第 3 步)。
注意:详情 zip 通常无需重做;仅当 zip 内 README 的标题或正文写了旧作品名(与新名不一致)时,才需要把旧名改为新名并重新打包详情 zip。
- 其他错误 → 告知
<msg>(<code>),保留临时文件,问是否修改重发。错误码见 error-codes.md。
References
| Document | 用途 | 何时读 |
|---|---|---|
| troubleshooting.md | 网络/各步骤失败排查 | 脚本报错→先查这里 |
| error-codes.md | 平台 API 全错误码表 | API 返回非 2xx 时查表 |
| api-spec.md | API 契约、投稿状态判定 | Step 7/8 解析响应 |
| work-preparation.md | 命名/封面/简介/详情/图表统一规则 | Step 3–6 需细节时按目录跳转 |
| work-preparation-windows.md | Windows 专属:PowerShell 预检/字体/zip | 仅 Windows |
| work-preparation-linux.md | Linux 专属:bash 预检/yum/fontconfig | 仅 Linux |
| windows-setup.md | Windows 首次准备:编码/Playwright/zip | Windows 环境首次 |
| self-verify-publish.md | 自委托 SELF_VERIFY 与 STS 原理 | Step 1 出问题时 |
| devbridge-tunnel.md | DevBridge 隧道登录/故障 | Step 2 隧道异常 |
| codearts-sandbox.md | CodeArts 沙箱目录 | Step 1 命中沙箱 |

