create-dwy 0.14.6 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/bin/index.js +4 -0
  3. package/package.json +1 -1
  4. package/src/skills-install.js +113 -0
  5. package/src/sync-all.js +47 -3
  6. package/templates/ai-tools/CLAUDE.md +18 -1
  7. package/templates/ai-tools/hooks/Git/pre-git-commit-sensitive-check.sh +10 -2
  8. package/templates/ai-tools/rules/Android/dwy-android-core.md +8 -0
  9. package/templates/ai-tools/rules/Flutter/dwy-flutter-core.md +8 -0
  10. package/templates/ai-tools/rules/HarmonyOS/dwy-arkts-strict.md +221 -0
  11. package/templates/ai-tools/rules/HarmonyOS/dwy-arkts-ui-build.md +193 -0
  12. package/templates/ai-tools/rules/iOS/dwy-ios-pitfalls.md +159 -0
  13. package/templates/ai-tools/rules/iOS/dwy-swift-core.md +35 -0
  14. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-acceptance/SKILL.md +23 -0
  15. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-architecture/SKILL.md +28 -0
  16. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-competitor/SKILL.md +22 -0
  17. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-ideate/SKILL.md +21 -0
  18. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-mvp/SKILL.md +23 -0
  19. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-poc/SKILL.md +24 -0
  20. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-prd/SKILL.md +22 -0
  21. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/SKILL.md +68 -0
  22. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/references/state-and-contract.md +103 -0
  23. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-prototype/SKILL.md +23 -0
  24. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-release/SKILL.md +26 -0
  25. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-design/SKILL.md +23 -0
  26. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-dev/SKILL.md +22 -0
  27. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-launch/SKILL.md +25 -0
  28. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-prd/SKILL.md +22 -0
  29. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-ship/SKILL.md +21 -0
  30. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-tasks/SKILL.md +22 -0
  31. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-tdd-dev/SKILL.md +25 -0
  32. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-validate/SKILL.md +28 -0
  33. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-version/SKILL.md +23 -0
  34. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish/SKILL.md +365 -0
  35. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish/references/electron-release-workflow-template.yml +98 -0
  36. package/templates/ai-tools/skills//{350/277/220/347/273/264/345/217/221/345/270/203/dwy-cibuildwheel → 345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish}/references/release-workflow-template.yml +3 -3
  37. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/SKILL.md +31 -0
  38. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/android.md +31 -0
  39. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/backend-deploy.md +23 -0
  40. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/changelog.md +37 -0
  41. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/frontend-deploy.md +23 -0
  42. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/generic-release.md +27 -0
  43. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/harmony.md +25 -0
  44. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/ios.md +32 -0
  45. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/monitor-notify.md +36 -0
  46. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/publish-config.md +65 -0
  47. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/version-sync.md +30 -0
  48. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/scripts/release_config.sh +115 -0
  49. package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/SKILL.md +31 -21
  50. package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/references/python-commercial-guide.md +1 -1
  51. package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/references/sensitive-patterns.md +1 -1
  52. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/scripts/init_config.sh +91 -0
  53. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-whiteboard-prototype/SKILL.md +70 -0
  54. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-whiteboard-prototype/assets/template.html +353 -0
  55. package/templates/ai-tools/rules/Global/dwy-common-code-baseline.md +0 -27
  56. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-sdk-spec/scripts/init_config.sh +0 -97
  57. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-publish/SKILL.md +0 -50
  58. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-publish/references/application-release.md +0 -106
  59. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-publish/references/sdk-release.md +0 -93
  60. package/templates/ai-tools/skills//350/277/220/347/273/264/345/217/221/345/270/203/dwy-cibuildwheel/SKILL.md +0 -356
  61. /package/templates/ai-tools/rules/{Global → Docker}/dwy-docker.md +0 -0
  62. /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/assets/files-whitelist-snippet.json" +0 -0
  63. /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/assets/js-commercial.npmignore" +0 -0
  64. /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/assets/python-commercial-MANIFEST.in" +0 -0
  65. /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/assets/python-commercial.gitignore" +0 -0
  66. /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/references/js-commercial-guide.md" +0 -0
  67. /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/references/publish-blacklist.md" +0 -0
  68. /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/scripts/check_comments.py" +0 -0
  69. /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/scripts/check_js_publish.sh" +0 -0
  70. /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/scripts/check_python_publish.sh" +0 -0
  71. /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/scripts/detect_sdk.sh" +0 -0
  72. /package/templates/ai-tools/skills//345/{274/200/345/217/221/346/265/201/347/250/213 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-semver/SKILL.md" +0 -0
  73. /package/templates/ai-tools/skills//345/{274/200/345/217/221/346/265/201/347/250/213 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-semver/references/semver-spec.md" +0 -0
  74. /package/templates/ai-tools/skills//345/{274/200/345/217/221/346/265/201/347/250/213 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-semver/scripts/bump.py" +0 -0
@@ -0,0 +1,353 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <!-- ============================================================
7
+ 白板原型模板 · 复制本文件后只改三处:
8
+ ① 画布区(.stage 列表)—— 每个状态一个画布,含左手机框 + 右标注栏
9
+ ② 脚本里的 EDGES —— 声明白板内状态跳转拓扑(画连线)
10
+ ③ 左侧 .sidenav —— 把当前页设为 .on,其余指向对应白板 html
11
+ 引擎(平移缩放/连线/选中/复位/拖拽防误触)勿改。
12
+ ============================================================ -->
13
+ <title>页面名 · 白板(含全状态)</title>
14
+ <style>
15
+ /* 可改①:配色 — 按产品定位重填下方色值(变量名/结构勿改),默认深色蓝调一套 */
16
+ :root{
17
+ --bg:#0A0A0B; --device:#111113; --surface:#18181B; --surface2:#1F1F23; --surface3:#27272B;
18
+ --ink:#F4F4F5; --sub:#8E8E96; --faint:#5C5C63;
19
+ --gold:#3B82F6; --goldHi:#60A5FA; --gold2:#2563EB;
20
+ --red:#FB7185; --green:#34D399; --hairline:rgba(255,255,255,.08);
21
+ --pro:#F5B85C; /* 付费锁牌金色(按需) */
22
+ --spec-bg:#0d0d0f; --spec-line:#222;
23
+ }
24
+ *{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
25
+ html,body{height:100%}
26
+ body{margin:0;background:#000;font-family:-apple-system,"SF Pro Text","PingFang SC",sans-serif;
27
+ color:var(--ink);overflow:hidden;user-select:none} /* 禁选中,拖拽不选画框文字 */
28
+ input{user-select:text}
29
+
30
+ /* ===== 左侧悬浮页面导航(fixed,不随白板移动)===== */
31
+ .sidenav{position:fixed;left:14px;top:50%;transform:translateY(-50%);z-index:30;
32
+ display:flex;flex-direction:column;gap:4px;background:rgba(10,11,14,.92);
33
+ backdrop-filter:blur(12px);border:1px solid var(--spec-line);border-radius:18px;padding:9px}
34
+ .sidenav .ov{display:flex;align-items:center;gap:9px;text-decoration:none;color:var(--sub);
35
+ padding:7px 12px 7px 8px;font-size:11px;font-weight:600;margin-bottom:3px;
36
+ border-bottom:1px solid var(--hairline);padding-bottom:9px}
37
+ .sidenav .ov:hover{color:var(--ink)}
38
+ .navitem{display:flex;align-items:center;gap:9px;text-decoration:none;color:var(--sub);
39
+ padding:6px 12px 6px 7px;border-radius:12px;transition:background .15s,color .15s;white-space:nowrap}
40
+ .navitem .nic{width:30px;height:30px;border-radius:50%;display:flex;align-items:center;justify-content:center;
41
+ font-size:15px;background:var(--surface2);flex-shrink:0}
42
+ .navitem .nlb{font-size:12.5px;font-weight:600}
43
+ .navitem .nlb .todo{font-size:9px;color:var(--faint);margin-left:5px;font-weight:500}
44
+ .navitem.ready{cursor:pointer}
45
+ .navitem.ready:hover{background:var(--surface2);color:var(--ink)}
46
+ .navitem.on{background:rgba(59,130,246,.16);color:#fff}
47
+ .navitem.on .nic{background:linear-gradient(135deg,var(--goldHi),var(--gold));color:#fff}
48
+ .navitem.todo{opacity:.42;cursor:not-allowed}
49
+
50
+ /* ===== 白板视口 ===== */
51
+ #viewport{position:fixed;inset:0;overflow:hidden;cursor:grab;
52
+ background:radial-gradient(circle at 1px 1px,rgba(255,255,255,.05) 1px,transparent 0) 0 0/26px 26px,#050506}
53
+ #viewport.grabbing{cursor:grabbing}
54
+ #world{position:absolute;top:0;left:0;transform-origin:0 0;will-change:transform}
55
+ #wires{position:absolute;top:0;left:0;overflow:visible;pointer-events:none;z-index:1}
56
+ #wires path{fill:none;stroke:rgba(96,165,250,.5);stroke-width:3}
57
+ #wires path.hot{stroke:#7DB5FF;stroke-width:5;filter:drop-shadow(0 0 4px rgba(96,165,250,.7))}
58
+ #wires .wlabel{fill:#cfe0ff;font-size:13px;font-weight:600;font-family:-apple-system,"PingFang SC",sans-serif}
59
+ #wires .wlabel-bg{fill:#10141c;stroke:rgba(96,165,250,.35);stroke-width:1;rx:6}
60
+
61
+ .pagehead{position:fixed;top:14px;left:50%;transform:translateX(-50%);z-index:20;text-align:center;
62
+ background:rgba(10,11,14,.82);backdrop-filter:blur(12px);border:1px solid var(--spec-line);
63
+ border-radius:14px;padding:10px 22px}
64
+ .pagehead h1{font-size:16px;font-weight:800;margin:0;letter-spacing:.3px}
65
+ .pagehead p{color:var(--sub);font-size:11.5px;line-height:1.5;margin:3px 0 0}
66
+ .toolbar{position:fixed;bottom:18px;left:50%;transform:translateX(-50%);z-index:20;display:flex;gap:6px;
67
+ background:rgba(10,11,14,.9);backdrop-filter:blur(12px);border:1px solid var(--spec-line);
68
+ border-radius:12px;padding:6px}
69
+ .toolbar button{border:none;background:var(--surface2);color:var(--ink);font-size:13px;font-weight:600;
70
+ padding:8px 13px;border-radius:8px;cursor:pointer;transition:background .15s}
71
+ .toolbar button:hover{background:var(--surface3)}
72
+ .toolbar .zlabel{display:flex;align-items:center;padding:0 8px;color:var(--sub);font-size:12px;min-width:48px;justify-content:center}
73
+
74
+ /* ===== 画布:白板上绝对定位(data-x/data-y)===== */
75
+ .stage{position:absolute;display:flex;gap:22px;align-items:flex-start;z-index:2}
76
+ .frame{display:flex;flex-direction:column;gap:10px;flex-shrink:0}
77
+ /* 编号 = 选中入口(只点它才选中+复位展示),画框本身不响应选中 */
78
+ .canvas-no{font-size:13px;font-weight:700;color:var(--goldHi);letter-spacing:.3px;cursor:pointer}
79
+ .canvas-no:hover{color:#fff}
80
+ .canvas-no .n{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;
81
+ border-radius:7px;background:rgba(59,130,246,.16);margin-right:7px;font-size:12px}
82
+
83
+ /* 可改②:设备框尺寸 — 按目标端改 width/height(iOS 390×844 / 鸿蒙 vp / iPad / Web 各自比例),默认 320×680 */
84
+ .phone{width:320px;height:680px;background:var(--bg);border-radius:44px;
85
+ border:9px solid #1c1c1e;box-shadow:0 24px 60px rgba(0,0,0,.6);position:relative;overflow:hidden;
86
+ transition:box-shadow .25s,transform .25s}
87
+ /* 选中:双层光圈 + 微放大,明显区别于按钮自带样式 */
88
+ .phone.selected{box-shadow:0 0 0 3px #0A0A0B,0 0 0 6px var(--gold),0 0 30px rgba(59,130,246,.6),0 24px 60px rgba(0,0,0,.6);transform:scale(1.015)}
89
+ .canvas-no.selected{color:#fff}
90
+ .canvas-no.selected .n{background:var(--gold);color:#fff}
91
+ .canvas-no.selected::after{content:'当前';margin-left:8px;font-size:10px;font-weight:700;
92
+ color:#0A1A33;background:var(--goldHi);padding:2px 7px;border-radius:6px;letter-spacing:.5px}
93
+ .notch{position:absolute;top:0;left:50%;transform:translateX(-50%);width:112px;height:28px;
94
+ background:#1c1c1e;border-radius:0 0 17px 17px;z-index:50}
95
+ .screen{position:absolute;inset:0;padding:50px 18px 22px;overflow-y:auto;background:var(--device)}
96
+ .screen::-webkit-scrollbar{display:none}
97
+
98
+ /* ===== 右侧标注栏 ===== */
99
+ .spec{width:300px;background:var(--spec-bg);border:1px solid var(--spec-line);border-radius:16px;padding:18px 18px 20px;flex-shrink:0}
100
+ .spec h2{font-size:15px;font-weight:700;margin:0 0 4px;color:var(--ink)}
101
+ .spec .use{color:var(--sub);font-size:12.5px;line-height:1.55;margin:0 0 16px}
102
+ .spec .grp{margin-bottom:15px}
103
+ .spec .grp:last-child{margin-bottom:0}
104
+ .spec .gt{font-size:11px;font-weight:700;letter-spacing:.6px;text-transform:uppercase;color:var(--faint);margin:0 0 8px}
105
+ .spec .gt.ix{color:var(--goldHi)}
106
+ .spec .gt.bd{color:var(--red)}
107
+ .spec .gt.pay{color:var(--pro)}
108
+ .spec ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:7px}
109
+ .spec li{font-size:12px;line-height:1.5;color:#c9c9cf;display:flex;gap:7px;align-items:flex-start}
110
+ .spec li .dot{color:var(--faint);flex-shrink:0}
111
+ .spec li .dot.ix{color:var(--goldHi)}
112
+ .spec li .dot.bd{color:var(--red)}
113
+ .spec li .dot.pay{color:var(--pro)}
114
+ /* 白板内跳转链接(data-go):点了 select+复位展示 */
115
+ .jump{color:var(--goldHi);font-weight:600;cursor:pointer;text-decoration:none;border-bottom:1px dashed rgba(96,165,250,.4)}
116
+ .jump:hover{color:#fff;border-bottom-color:#fff}
117
+ /* 跨白板跳转链接(href,绿色↗):交给浏览器切文件 */
118
+ .xjump{color:var(--green);font-weight:600;text-decoration:none;border-bottom:1px dashed rgba(52,211,153,.45)}
119
+ .xjump:hover{color:#fff;border-bottom-color:#fff}
120
+ .xjump::after{content:'↗';font-size:10px;margin-left:2px}
121
+ .spec code{font-family:ui-monospace,"SF Mono",monospace;font-size:11px;color:var(--goldHi);background:rgba(59,130,246,.1);padding:1px 5px;border-radius:4px}
122
+
123
+ /* ===== 画框内常用 UI(按需取用,可自行加)===== */
124
+ .hdr{display:flex;align-items:center;gap:10px;margin-bottom:18px}
125
+ .hdr .back{width:32px;height:32px;border-radius:10px;background:var(--surface);display:flex;align-items:center;justify-content:center;color:var(--sub);font-size:17px;cursor:pointer}
126
+ .hdr h1{font-size:18px;font-weight:700;margin:0;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
127
+ .sub{color:var(--sub);font-size:13px;line-height:1.5}
128
+ .faint{color:var(--faint);font-size:12px}
129
+ .sectiontitle{font-size:12px;color:var(--sub);font-weight:600;letter-spacing:.5px;text-transform:uppercase;margin:0 0 10px 2px}
130
+ .field{width:100%;background:var(--surface);border:1.5px solid var(--hairline);border-radius:12px;padding:11px 14px;color:var(--ink);font-size:14px;outline:none;margin-bottom:12px}
131
+ .field::placeholder{color:var(--faint)}
132
+ .btn{width:100%;border:none;border-radius:14px;padding:14px;font-size:15px;font-weight:700;
133
+ background:linear-gradient(135deg,var(--goldHi),var(--gold),var(--gold2));color:#fff;cursor:pointer;
134
+ box-shadow:0 8px 20px rgba(59,130,246,.4);transition:transform .15s cubic-bezier(.34,1.2,.64,1);display:flex;align-items:center;justify-content:center;gap:7px}
135
+ .btn:active{transform:scale(.93)}
136
+ .btn.ghost{background:transparent;color:var(--gold);box-shadow:none;border:1.5px solid rgba(59,130,246,.4)}
137
+ .btn.danger{background:linear-gradient(135deg,#FB7185,#E11D48);box-shadow:0 8px 20px rgba(225,29,72,.35)}
138
+ .btn.mini{padding:11px;font-size:14px;border-radius:11px}
139
+ .card{background:var(--surface2);border:1px solid var(--hairline);border-radius:14px;padding:15px;margin-bottom:13px}
140
+ .pill{display:inline-flex;align-items:center;gap:4px;background:rgba(59,130,246,.15);color:var(--gold);font-size:10.5px;font-weight:700;padding:3px 8px;border-radius:7px}
141
+ .pill.ok{background:rgba(52,211,153,.15);color:var(--green)}
142
+ .pill.warn{background:rgba(251,113,133,.14);color:var(--red)}
143
+ .pill.lock{background:linear-gradient(135deg,rgba(245,184,92,.22),rgba(224,153,47,.22));color:var(--pro);border:1px solid rgba(245,184,92,.35)} /* 付费锁牌 */
144
+ .note{display:flex;gap:7px;align-items:flex-start;margin-top:10px;color:var(--faint);font-size:11.5px;line-height:1.5}
145
+ .note .d{color:var(--gold)}
146
+ .spin{width:42px;height:42px;border:4px solid rgba(59,130,246,.2);border-top-color:var(--gold);border-radius:50%;animation:rot 1s linear infinite;margin:30px auto 18px}
147
+ @keyframes rot{to{transform:rotate(360deg)}}
148
+ .centerblk{text-align:center;padding-top:30px}
149
+ .centerblk h2{font-size:18px;margin:0 0 8px}
150
+ /* 列表行:单行截断示范(min-width:0 是省略号生效前提)*/
151
+ .listrow{display:flex;align-items:center;gap:12px;background:var(--surface2);border:1px solid var(--hairline);border-radius:14px;padding:12px;margin-bottom:10px;cursor:pointer}
152
+ .listrow .ic{width:42px;height:42px;border-radius:11px;background:var(--surface3);display:flex;align-items:center;justify-content:center;font-size:21px;flex-shrink:0}
153
+ .listrow .meta{flex:1;min-width:0}
154
+ .listrow .meta .n{font-size:15px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
155
+ .listrow .meta .s{font-size:12px;color:var(--sub);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
156
+ .listrow .arr{color:var(--faint);font-size:17px;flex-shrink:0}
157
+ /* 空态 */
158
+ .empty{text-align:center;padding:70px 20px 40px;color:var(--faint)}
159
+ .empty .ei{font-size:48px;margin-bottom:16px;opacity:.6}
160
+ .empty .et{font-size:16px;color:var(--sub);font-weight:600}
161
+ .empty .ed{font-size:13px;margin-top:8px;line-height:1.6}
162
+ /* 加载骨架 */
163
+ .skel{height:64px;border-radius:14px;margin-bottom:10px;background:linear-gradient(90deg,var(--surface2) 25%,var(--surface3) 50%,var(--surface2) 75%);background-size:200% 100%;animation:sh 1.3s infinite}
164
+ @keyframes sh{to{background-position:-200% 0}}
165
+ </style>
166
+ </head>
167
+ <body>
168
+
169
+ <div class="pagehead">
170
+ <h1>页面名 · 全状态白板</h1>
171
+ <p>一句话页面定位。<span style="color:var(--green)">绿色 ↗</span> = 跨白板跳转</p>
172
+ </div>
173
+
174
+ <!-- ③ 左侧导航:把当前页设 .on(去掉 href),其余指向对应白板 html;待做的用 .todo + span -->
175
+ <nav class="sidenav">
176
+ <a class="ov" href="index-spec.html"><span>◳</span>原型总览</a>
177
+ <a class="navitem on"><span class="nic">📄</span><span class="nlb">本页</span></a>
178
+ <a class="navitem ready" href="other-spec.html"><span class="nic">📄</span><span class="nlb">其他页</span></a>
179
+ <span class="navitem todo"><span class="nic">📄</span><span class="nlb">待做页<span class="todo">待做</span></span></span>
180
+ </nav>
181
+
182
+ <div id="viewport">
183
+ <div id="world">
184
+ <svg id="wires"></svg>
185
+
186
+ <!-- ① 画布区:每个状态一个 .stage。data-id 唯一;data-x/data-y 是白板坐标(列距~760 行距~820)。
187
+ 编号 .canvas-no 的 id 必须等于 data-id(选中靠它)。左 .phone 放 UI,右 .spec 放标注。 -->
188
+
189
+ <!-- 占位画布 A -->
190
+ <div class="stage" data-id="stateA" data-x="40" data-y="60">
191
+ <div class="frame">
192
+ <div class="canvas-no" id="stateA"><span class="n">1</span>状态 A(默认)</div>
193
+ <div class="phone"><div class="notch"></div>
194
+ <div class="screen">
195
+ <div class="hdr"><h1>页面标题</h1></div>
196
+ <!-- 列表单行截断示范 -->
197
+ <div class="listrow" data-go="stateB">
198
+ <div class="ic">📄</div>
199
+ <div class="meta"><div class="n">某条目超长名字会被省略号截断不撑破布局</div><div class="s">副信息行也截断</div></div>
200
+ <span class="arr">›</span>
201
+ </div>
202
+ <button class="btn" data-go="stateB">主操作 → 状态 B</button>
203
+ </div>
204
+ </div>
205
+ </div>
206
+ <div class="spec">
207
+ <h2>状态 A(默认)</h2>
208
+ <p class="use">一句话说明本状态的职责。</p>
209
+ <div class="grp"><p class="gt ix">交互跳转</p><ul>
210
+ <li><span class="dot ix">→</span><span>主操作 → <a class="jump" data-go="stateB">② 状态 B</a></span></li>
211
+ <li><span class="dot ix">→</span><span>跨白板示例 → <a class="xjump" href="other-spec.html">其他页</a></span></li>
212
+ </ul></div>
213
+ <div class="grp"><p class="gt bd">边界处理</p><ul>
214
+ <li><span class="dot bd">·</span><span>单行长文 → 省略号截断(<code>min-width:0</code>)</span></li>
215
+ <li><span class="dot bd">·</span><span>空态 / 加载 / 错误 / 禁用 按需补画布</span></li>
216
+ </ul></div>
217
+ </div>
218
+ </div>
219
+
220
+ <!-- 占位画布 B -->
221
+ <div class="stage" data-id="stateB" data-x="820" data-y="60">
222
+ <div class="frame">
223
+ <div class="canvas-no" id="stateB"><span class="n">2</span>状态 B</div>
224
+ <div class="phone"><div class="notch"></div>
225
+ <div class="screen">
226
+ <div class="hdr"><div class="back" data-go="stateA">‹</div><h1>状态 B</h1></div>
227
+ <div class="centerblk"><h2>这里放状态 B 的 UI</h2><p class="sub">返回 ‹ 回状态 A</p></div>
228
+ </div>
229
+ </div>
230
+ </div>
231
+ <div class="spec">
232
+ <h2>状态 B</h2>
233
+ <p class="use">一句话说明。</p>
234
+ <div class="grp"><p class="gt ix">交互跳转</p><ul>
235
+ <li><span class="dot ix">→</span><span>返回 ‹ → <a class="jump" data-go="stateA">① 状态 A</a></span></li>
236
+ </ul></div>
237
+ <div class="grp"><p class="gt bd">边界处理</p><ul>
238
+ <li><span class="dot bd">·</span><span>按需填写</span></li>
239
+ </ul></div>
240
+ </div>
241
+ </div>
242
+
243
+ </div><!-- /#world -->
244
+ </div><!-- /#viewport -->
245
+
246
+ <div class="toolbar">
247
+ <button id="btnFit">适应全部</button>
248
+ <button id="btnReset">复位</button>
249
+ <button id="btnZoomOut">−</button>
250
+ <span class="zlabel" id="zlabel">100%</span>
251
+ <button id="btnZoomIn">+</button>
252
+ </div>
253
+
254
+ <script>
255
+ // ② EDGES:白板内状态跳转拓扑,决定画哪些连线。from/to = .stage 的 data-id;label = 触发条件。
256
+ const EDGES = [
257
+ {from:'stateA', to:'stateB', label:'主操作'},
258
+ {from:'stateB', to:'stateA', label:'返回'},
259
+ ];
260
+ const START='stateA'; // 初始选中/复位的画布
261
+
262
+ /* ===== 以下为白板引擎,勿改 ===== */
263
+ const viewport=document.getElementById('viewport'), world=document.getElementById('world'), wires=document.getElementById('wires');
264
+ const stages=[...document.querySelectorAll('.stage')];
265
+ const byId=id=>document.querySelector(`.stage[data-id="${id}"]`);
266
+ stages.forEach(s=>{ s.style.left=s.dataset.x+'px'; s.style.top=s.dataset.y+'px'; });
267
+
268
+ let scale=1,tx=0,ty=0;
269
+ const NS='http://www.w3.org/2000/svg';
270
+ function apply(){ world.style.transform=`translate(${tx}px,${ty}px) scale(${scale})`; document.getElementById('zlabel').textContent=Math.round(scale*100)+'%'; }
271
+
272
+ function drawWires(){
273
+ wires.innerHTML='';
274
+ let maxX=0,maxY=0; stages.forEach(s=>{ maxX=Math.max(maxX,s.offsetLeft+s.offsetWidth); maxY=Math.max(maxY,s.offsetTop+s.offsetHeight); });
275
+ wires.setAttribute('width',maxX+200); wires.setAttribute('height',maxY+200);
276
+ const defs=document.createElementNS(NS,'defs');
277
+ defs.innerHTML=`<marker id="arrow" viewBox="0 0 12 12" refX="10" refY="6" markerWidth="11" markerHeight="11" orient="auto-start-reverse"><path d="M1,1 L11,6 L1,11 L4,6 z" fill="rgba(96,165,250,.85)"/></marker><marker id="arrowHot" viewBox="0 0 12 12" refX="10" refY="6" markerWidth="13" markerHeight="13" orient="auto-start-reverse"><path d="M1,1 L11,6 L1,11 L4,6 z" fill="#7DB5FF"/></marker>`;
278
+ wires.appendChild(defs);
279
+ function edgeAnchor(stage,tX,tY){ // 从画框最近的边进出,连线不被框体盖住
280
+ const p=stage.querySelector('.phone');
281
+ const x=stage.offsetLeft+p.offsetLeft,y=stage.offsetTop+p.offsetTop,w=p.offsetWidth,h=p.offsetHeight,cx=x+w/2,cy=y+h/2,dx=tX-cx,dy=tY-cy;
282
+ if(Math.abs(dx)>=Math.abs(dy)) return dx>=0?{x:x+w,y:cy,side:'r'}:{x:x,y:cy,side:'l'};
283
+ return dy>=0?{x:cx,y:y+h,side:'b'}:{x:cx,y:y,side:'t'};
284
+ }
285
+ EDGES.forEach(e=>{
286
+ const a=byId(e.from),b=byId(e.to); if(!a||!b)return;
287
+ const pa=a.querySelector('.phone'),pb=b.querySelector('.phone');
288
+ const acx=a.offsetLeft+pa.offsetLeft+pa.offsetWidth/2,acy=a.offsetTop+pa.offsetTop+pa.offsetHeight/2;
289
+ const bcx=b.offsetLeft+pb.offsetLeft+pb.offsetWidth/2,bcy=b.offsetTop+pb.offsetTop+pb.offsetHeight/2;
290
+ const A=edgeAnchor(a,bcx,bcy),B=edgeAnchor(b,acx,acy),pull=120;
291
+ const cp=pt=>({x:pt.x+(pt.side==='r'?pull:pt.side==='l'?-pull:0),y:pt.y+(pt.side==='b'?pull:pt.side==='t'?-pull:0)});
292
+ const c1=cp(A),c2=cp(B);
293
+ const path=document.createElementNS(NS,'path');
294
+ path.setAttribute('d',`M${A.x},${A.y} C${c1.x},${c1.y} ${c2.x},${c2.y} ${B.x},${B.y}`);
295
+ path.setAttribute('marker-end','url(#arrow)'); path.dataset.from=e.from; path.dataset.to=e.to; wires.appendChild(path);
296
+ const mx=(A.x+B.x)/2,my=(A.y+B.y)/2,tw=e.label.length*13+14;
297
+ const bg=document.createElementNS(NS,'rect'); bg.setAttribute('class','wlabel-bg'); bg.setAttribute('x',mx-tw/2); bg.setAttribute('y',my-13); bg.setAttribute('width',tw); bg.setAttribute('height',24); bg.setAttribute('rx',6); bg.dataset.from=e.from; bg.dataset.to=e.to; wires.appendChild(bg);
298
+ const t=document.createElementNS(NS,'text'); t.setAttribute('class','wlabel'); t.setAttribute('x',mx); t.setAttribute('y',my+4); t.setAttribute('text-anchor','middle'); t.textContent=e.label; t.dataset.from=e.from; t.dataset.to=e.to; wires.appendChild(t);
299
+ });
300
+ }
301
+
302
+ // 选中:高亮画框+标题+「当前」标记 + 相关连线高亮
303
+ function select(id){
304
+ document.querySelectorAll('.phone.selected,.canvas-no.selected').forEach(el=>el.classList.remove('selected'));
305
+ wires.querySelectorAll('path.hot').forEach(p=>{ p.classList.remove('hot'); p.setAttribute('marker-end','url(#arrow)'); });
306
+ const stage=byId(id); if(!stage)return;
307
+ stage.querySelector('.canvas-no')?.classList.add('selected'); stage.querySelector('.phone')?.classList.add('selected');
308
+ wires.querySelectorAll(`path[data-from="${id}"],path[data-to="${id}"]`).forEach(p=>{ p.classList.add('hot'); p.setAttribute('marker-end','url(#arrowHot)'); });
309
+ return stage;
310
+ }
311
+ // 复位展示:缩放到单画布刚好填满 + 居中(不管之前缩放多少都看清)
312
+ function focusOn(id){
313
+ const stage=byId(id); if(!stage)return;
314
+ const NAV=210,pad=70,w=stage.offsetWidth+pad*2,h=stage.offsetHeight+pad*2,availW=viewport.clientWidth-NAV;
315
+ scale=Math.min(availW/w,viewport.clientHeight/h,2.2);
316
+ const cx=stage.offsetLeft+stage.offsetWidth/2,cy=stage.offsetTop+stage.offsetHeight/2;
317
+ tx=NAV+availW/2-cx*scale; ty=viewport.clientHeight/2-cy*scale; apply();
318
+ }
319
+
320
+ let dragging=false,sx=0,sy=0,stx=0,sty=0,moved=false;
321
+ viewport.addEventListener('pointerdown',e=>{
322
+ // 可点元素不启动拖拽:否则真实鼠标微小抖动会把点击判成拖拽吞掉(点编号/按钮失效)
323
+ if(e.target.closest('input,a,[data-go],.canvas-no'))return;
324
+ dragging=true;moved=false;sx=e.clientX;sy=e.clientY;stx=tx;sty=ty;viewport.classList.add('grabbing');try{viewport.setPointerCapture(e.pointerId)}catch{}
325
+ });
326
+ viewport.addEventListener('pointermove',e=>{ if(!dragging)return; const ddx=e.clientX-sx,ddy=e.clientY-sy; if(Math.abs(ddx)+Math.abs(ddy)>4)moved=true; tx=stx+ddx;ty=sty+ddy;apply(); });
327
+ viewport.addEventListener('pointerup',()=>{ dragging=false;viewport.classList.remove('grabbing'); });
328
+ viewport.addEventListener('wheel',e=>{ e.preventDefault(); const r=viewport.getBoundingClientRect(),mx=e.clientX-r.left,my=e.clientY-r.top,wx=(mx-tx)/scale,wy=(my-ty)/scale,f=e.deltaY<0?1.12:1/1.12; scale=Math.min(2.2,Math.max(.18,scale*f)); tx=mx-wx*scale;ty=my-wy*scale;apply(); },{passive:false});
329
+ viewport.addEventListener('click',e=>{
330
+ if(moved)return;
331
+ if(e.target.closest('a[href]'))return; // 跨白板链接交给浏览器
332
+ const go=e.target.closest('[data-go]'); // 画框内按钮 / 标注 jump → 白板内跳转
333
+ if(go){ const id=go.getAttribute('data-go'); select(id); focusOn(id); return; }
334
+ const no=e.target.closest('.canvas-no'); // 只点编号才选中(画框不响应)
335
+ if(no){ select(no.id); focusOn(no.id); }
336
+ });
337
+
338
+ function fitAll(){
339
+ let minX=1e9,minY=1e9,maxX=0,maxY=0;
340
+ stages.forEach(s=>{ minX=Math.min(minX,s.offsetLeft);minY=Math.min(minY,s.offsetTop);maxX=Math.max(maxX,s.offsetLeft+s.offsetWidth);maxY=Math.max(maxY,s.offsetTop+s.offsetHeight); });
341
+ const NAV=210,pad=80,w=maxX-minX+pad*2,h=maxY-minY+pad*2,availW=viewport.clientWidth-NAV;
342
+ scale=Math.min(availW/w,viewport.clientHeight/h,1);
343
+ tx=NAV+availW/2-((minX+maxX)/2)*scale; ty=viewport.clientHeight/2-((minY+maxY)/2)*scale; apply();
344
+ }
345
+ document.getElementById('btnFit').onclick=fitAll;
346
+ document.getElementById('btnReset').onclick=()=>{ select(START);focusOn(START); };
347
+ document.getElementById('btnZoomIn').onclick=()=>{ scale=Math.min(2.2,scale*1.15);apply(); };
348
+ document.getElementById('btnZoomOut').onclick=()=>{ scale=Math.max(.18,scale/1.15);apply(); };
349
+
350
+ drawWires(); select(START); fitAll();
351
+ </script>
352
+ </body>
353
+ </html>
@@ -1,27 +0,0 @@
1
- ---
2
- description: 跨语言通用代码基础规则(注释、图标、全面屏适配、发布与打包)
3
- ---
4
-
5
- # 跨语言通用代码基础规则
6
-
7
- ## 1. 代码注释
8
-
9
- - 所有代码都必须有注释。
10
- - 类、接口、私有方法、函数、重要参数与重要字段都必须写明用途、约束与边界。
11
- - 每个类顶部要有简短功能说明。
12
- - 注释重点写“为什么”和“约束”,避免仅复述代码实现。
13
-
14
- ## 2. 图标规范
15
-
16
- - 图标优先使用成熟稳定的图标库,不使用 emoji 作为功能性图标。
17
- - 优先选择项目已有成熟依赖;无可用依赖时再补充稳定方案。
18
-
19
- ## 3. iOS / Android 全面屏适配
20
-
21
- - iOS 与 Android 端默认按全面屏/刘海屏适配实现,不使用固定上下左右边距的“硬编码”方案。
22
- - 优先使用安全区域(Safe Area / Window Insets)与响应式布局约束。
23
-
24
- ## 4. 发布与打包基础准则
25
-
26
- - PyPI 与 npm 的发布必须走 GitHub Actions,使用 OIDC 完成鉴权,禁止使用长期静态发布凭据。
27
- - 多平台打包必须走 GitHub Actions 工作流执行,不使用本地手工打包作为唯一流程。
@@ -1,97 +0,0 @@
1
- #!/usr/bin/env bash
2
- # dwy-sdk-spec 配置缓存读写
3
- # 用法:
4
- # init_config.sh get <project_path>
5
- # init_config.sh set <project_path> <edition> <languages_json> <sdk_paths_json>
6
- # init_config.sh reset <project_path>
7
- # init_config.sh list
8
-
9
- set -euo pipefail
10
-
11
- CONFIG_DIR="${HOME}/.config/dwy-sdk-spec"
12
- CONFIG_FILE="${CONFIG_DIR}/projects.json"
13
-
14
- ensure_config() {
15
- mkdir -p "$CONFIG_DIR"
16
- if [[ ! -f "$CONFIG_FILE" ]]; then
17
- echo '{"version":"1","projects":{}}' > "$CONFIG_FILE"
18
- fi
19
- # 检测损坏
20
- if ! jq empty "$CONFIG_FILE" 2>/dev/null; then
21
- echo "ERROR: $CONFIG_FILE is corrupted. Back it up and delete to reset." >&2
22
- exit 2
23
- fi
24
- }
25
-
26
- cmd_get() {
27
- local project_path="$1"
28
- ensure_config
29
- jq --arg p "$project_path" '.projects[$p] // null' "$CONFIG_FILE"
30
- }
31
-
32
- cmd_set() {
33
- local project_path="$1"
34
- local edition="$2"
35
- local languages_json="$3"
36
- local sdk_paths_json="$4"
37
-
38
- if [[ "$edition" != "commercial" && "$edition" != "opensource" ]]; then
39
- echo "ERROR: edition must be 'commercial' or 'opensource', got: $edition" >&2
40
- exit 2
41
- fi
42
-
43
- ensure_config
44
- local now
45
- now=$(date -u +%Y-%m-%dT%H:%M:%SZ)
46
-
47
- local tmp
48
- tmp=$(mktemp)
49
- jq \
50
- --arg p "$project_path" \
51
- --arg e "$edition" \
52
- --argjson l "$languages_json" \
53
- --argjson s "$sdk_paths_json" \
54
- --arg t "$now" \
55
- '.projects[$p] = {edition: $e, languages: $l, sdk_paths: $s, configured_at: $t}' \
56
- "$CONFIG_FILE" > "$tmp"
57
- mv "$tmp" "$CONFIG_FILE"
58
-
59
- echo "OK: cached $project_path -> $edition"
60
- }
61
-
62
- cmd_reset() {
63
- local project_path="$1"
64
- ensure_config
65
- local tmp
66
- tmp=$(mktemp)
67
- jq --arg p "$project_path" 'del(.projects[$p])' "$CONFIG_FILE" > "$tmp"
68
- mv "$tmp" "$CONFIG_FILE"
69
- echo "OK: removed $project_path from cache"
70
- }
71
-
72
- cmd_list() {
73
- ensure_config
74
- jq '.projects | to_entries | map({path: .key, edition: .value.edition, languages: .value.languages})' "$CONFIG_FILE"
75
- }
76
-
77
- main() {
78
- if [[ $# -lt 1 ]]; then
79
- echo "Usage: $0 {get|set|reset|list} [args...]" >&2
80
- exit 2
81
- fi
82
- local action="$1"
83
- shift
84
-
85
- case "$action" in
86
- get) cmd_get "$@" ;;
87
- set) cmd_set "$@" ;;
88
- reset) cmd_reset "$@" ;;
89
- list) cmd_list ;;
90
- *)
91
- echo "ERROR: unknown action: $action" >&2
92
- exit 2
93
- ;;
94
- esac
95
- }
96
-
97
- main "$@"
@@ -1,50 +0,0 @@
1
- ---
2
- name: dwy-publish
3
- description: "部署应用、发布版本、发布 SDK 的通用发版入口。触发条件:用户说“部署”“发版”“发布”“release”“publish”“bump version”时。先根据仓库上下文判断目标是应用部署还是 SDK 发版,再分别读取对应流程文件执行。"
4
- ---
5
-
6
- # 发布入口
7
-
8
- 通用发布 skill。默认进入本 skill 前,测试已经完成。
9
-
10
- ## 第 0 步:读取仓库发布上下文
11
-
12
- 执行前先从仓库里定位发布相关信息。
13
-
14
- 优先查看这些信息源:
15
-
16
- - `package.json`、`pyproject.toml`、`Cargo.toml`、`Makefile`、发布脚本
17
- - `.github/workflows/`、CI 配置、Docker 构建文件
18
- - `README.md`、发布文档、变更日志
19
- - 现有 tag 格式、历史 release commit、历史 workflow 运行方式
20
-
21
- 如果仍然无法判断发布方式或版本来源,停止并要求用户明确,不猜命令。
22
-
23
- ## 第 1 步:先判断发布类型
24
-
25
- 先确认:
26
-
27
- 1. 发布哪个应用或包
28
- 2. 目标是应用部署还是 SDK 发版
29
- 3. 目标版本号是什么;如果用户没给,判断是 `major` / `minor` / `patch`
30
-
31
- 归类规则:
32
-
33
- - **应用部署**:目标是某个服务、站点、容器、镜像、二进制或线上环境
34
- - **SDK 发版**:目标是 npm、PyPI、私有包仓库或对外库版本
35
-
36
- 多包场景按仓库已有依赖关系、workspace 拓扑、历史发布顺序执行,不自定顺序。
37
-
38
- ## 第 2 步:读取对应流程文件
39
-
40
- - **应用部署**:读取 `references/application-release.md`
41
- - **SDK 发版**:读取 `references/sdk-release.md`
42
-
43
- 不要把两条流程混在一起执行。先判类型,再只读对应流程文件。
44
-
45
- ## 通用约束
46
-
47
- - 禁止在未读清仓库发布上下文时猜发布命令
48
- - 禁止测试未完成就进入本发布流程
49
- - 禁止构建失败后继续发布
50
- - 禁止发布失败后对当前状态含糊表述
@@ -1,106 +0,0 @@
1
- # 应用部署流程
2
-
3
- 适用:发布目标是某个服务、站点、容器、镜像、二进制或线上环境。
4
-
5
- ## 主流程
6
-
7
- 流程骨架:
8
-
9
- 1. `bump`
10
- 2. `CHANGELOG`
11
- 3. `build`
12
- 4. `deploy`
13
- 5. `verify env`
14
-
15
- ## 1. 判断是否需要 bump
16
-
17
- 不要一上来改版本号。先判断这次部署是否需要 bump:
18
-
19
- - **需要 bump**:带显式版本号的应用发布、镜像 tag 升级、二进制版本升级、对外可见版本变化
20
- - **可不 bump**:纯重发、仅重跑 workflow、仅补环境配置、仅回滚到已有构建
21
- - **拿不准**:调用 `dwy-semver` 决定升级位和新版本号
22
-
23
- 如果需要 bump:
24
-
25
- 1. 读取当前版本
26
- 2. 算出新版本号
27
- 3. 修改版本文件、镜像 tag 或其他版本来源
28
-
29
- 不要用会自动 commit 的版本命令。直接编辑版本来源,保持提交边界可控。
30
-
31
- ## 2. 写 CHANGELOG
32
-
33
- 判断完 bump 后,再处理 `CHANGELOG`。
34
-
35
- - **需要正式版本发布**:必须写
36
- - **仅部署且无版本变化**:按项目约定决定是否跳过
37
-
38
- 处理顺序:
39
-
40
- 1. 找上一个 release tag
41
- 2. 查看 `git log <last-tag>..HEAD --oneline`
42
- 3. 按 `feat` / `fix` / `refactor` / `chore` 分组
43
- 4. 写入 `CHANGELOG.md` 或项目指定的 release notes 文件
44
-
45
- 如果仓库已有自动生成命令,优先使用;没有再手写。
46
-
47
- `CHANGELOG` 未完成,不继续。
48
-
49
- ## 3. 确认测试已完成并执行 build
50
-
51
- 本流程不重复跑测试,只确认当前变更已经过项目要求的测试。
52
-
53
- 然后执行 build,确保发布产物可生成。
54
-
55
- 构建失败则停止。
56
-
57
- ## 4. 决定 deploy 入口
58
-
59
- 不要把 deploy 写死成某种命令。根据仓库上下文自行判断:
60
-
61
- - 是推镜像、发二进制、跑部署脚本,还是触发 CI/CD
62
- - 是 GitHub Actions、其他 CI,还是本地脚本
63
- - 是否需要先 commit、tag、push 才能触发部署
64
-
65
- 判断标准示例:
66
-
67
- - 存在明确 workflow、触发方式、部署 job,且部署由 CI 完成 → 走 CI
68
- - 仓库里有明确部署脚本、命令或运维入口 → 走本地部署流程
69
-
70
- ## 5. 执行 deploy
71
-
72
- 如果是 CI 驱动:
73
-
74
- 1. 提交 release 相关文件
75
- 2. 按项目规则创建 tag;如果不是 tag 触发,则按配置触发 workflow
76
- 3. 推送分支和 tag
77
- 4. 观察部署运行状态直到成功或失败
78
-
79
- 如果是本地驱动:
80
-
81
- 1. 提交 release 相关文件
82
- 2. 按项目规则创建 tag
83
- 3. 推送分支和 tag
84
- 4. 执行本地部署命令
85
-
86
- 失败则停止,并给出失败阶段和关键报错。
87
-
88
- ## 6. verify env
89
-
90
- 部署完成后,按仓库约定验证环境已生效。可用信号包括:
91
-
92
- - 健康检查
93
- - 版本接口
94
- - 页面访问
95
- - 运行日志
96
- - 镜像 tag
97
- - 发布平台状态
98
- - 其他项目约定信号
99
-
100
- 验证失败,部署不算完成。
101
-
102
- ## 禁止事项
103
-
104
- - 禁止跳过“是否需要 bump”的判断
105
- - 禁止先部署后补 `CHANGELOG`
106
- - 禁止把环境验证替换成包仓库验证