已合并
支持 github镜像流水线 #843
支持 github镜像流水线 #843
已合并
SunBo创建于 3月19日
6 个文件变更+189-11
@@ -0,0 +1,24 @@
1+# GitHub Actions 流水线说明
2+ 
3+## npm pack & publish
4+ 
5+### 触发条件
6+ 
7+- **push**`mirror`:执行 `npm pack`,产物上传为 artifact
8+- **Release 发布**:在 pack 基础上执行 `npm publish` 发布到 npm
9+- **手动触发**`workflow_dispatch` 可手动运行
10+ 
11+### 发布到 npm 前置条件
12+ 
13+1. 在 [npmjs.com](https://www.npmjs.com/) 创建账号并登录
14+2. 生成 **Automation** Token(重要:若账号开启 2FA,必须用 Automation 类型,否则会报 EOTP 错误)
15+ - Account → Access Tokens → Generate New Token
16+ - 选择 **Bypass tow-factor authentication(2FA)** 类型(CI 发布无需 OTP)
17+3. 在 GitHub 仓库设置中添加 Secret:`NPM_TOKEN` = 上述 token
18+ 
19+### 发布流程
20+ 
21+1. **GitCode 代码镜像到 GitHub**:执行 `./script/mirror-to-github.sh`,将主分支推送到 GitHub 的 mirror 分支
22+2. 在 GitHub 创建 Release(Tag 建议与 `package.json``version` 一致)
23+3. 流水线自动执行 pack → publish
24+4. 在 npm 上查看发布结果:https://www.npmjs.com/package/arkanalyzer
@@ -0,0 +1,53 @@
1+name: npm pack & publish
2+ 
3+on:
4+ push:
5+ branches: [mirror]
6+ release:
7+ types: [published]
8+ workflow_dispatch:
9+ 
10+jobs:
11+ pack:
12+ runs-on: ubuntu-latest
13+ steps:
14+ - uses: actions/checkout@v4
15+ 
16+ - name: Setup Node.js
17+ uses: actions/setup-node@v4
18+ with:
19+ node-version: '24'
20+ registry-url: 'https://registry.npmjs.org'
21+ 
22+ - name: Install dependencies
23+ run: npm install
24+ 
25+ - name: npm pack
26+ run: npm pack
27+ 
28+ - name: Upload pack artifact
29+ uses: actions/upload-artifact@v4
30+ with:
31+ name: arkanalyzer-tarball
32+ path: 'arkanalyzer-*.tgz'
33+ 
34+ publish:
35+ needs: pack
36+ runs-on: ubuntu-latest
37+ if: github.event_name == 'release' && github.event.action == 'published'
38+ steps:
39+ - name: Download pack artifact
40+ uses: actions/download-artifact@v4
41+ with:
42+ name: arkanalyzer-tarball
43+ 
44+ - name: Setup Node.js
45+ uses: actions/setup-node@v4
46+ with:
47+ node-version: '24'
48+ registry-url: 'https://registry.npmjs.org'
49+ 
50+ - name: Publish to npm
51+ run: npm publish ./arkanalyzer-*.tgz --access public
52+ env:
53+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -1,6 +1,6 @@
1{1{
2 "name": "arkanalyzer",2 "name": "arkanalyzer",
3- "version": "1.0.8",3+ "version": "1.0.86",
4 "files": [4 "files": [
5 "docs",5 "docs",
6 "lib",6 "lib",
@@ -8,14 +8,14 @@
8 ],8 ],
9 "main": "lib/index.js",9 "main": "lib/index.js",
10 "scripts": {10 "scripts": {
11+ "prebuild": "node script/npmInstall.js",
11 "build": "tsc",12 "build": "tsc",
12- "prepack": "tsc -p ./tsconfig.prod.json",13+ "prepack": "npm run build && tsc -p ./tsconfig.prod.json",
13- "test": "vitest",14+ "test": "npm run build && vitest",
14- "testonce": "vitest --no-color run",15+ "testonce": "npm run build && vitest --no-color run",
15- "coverage": "vitest run --coverage",16+ "coverage": "npm run build && vitest run --coverage",
16 "heapdump": "npm run build && node --expose-gc out/tests/HeapDumpTest.js",17 "heapdump": "npm run build && node --expose-gc out/tests/HeapDumpTest.js",
17- "postinstall": "node script/npmInstall.js",18+ "gendoc": "npm run build && npx typedoc"
18- "gendoc": "npx typedoc"
19 },19 },
20 "dependencies": {20 "dependencies": {
21 "commander": "13.1.0",21 "commander": "13.1.0",
@@ -0,0 +1,48 @@
1+#!/bin/bash
2+#
3+# Copyright (c) 2024-2026 Huawei Device Co., Ltd.
4+# Licensed under the Apache License, Version 2.0 (the "License");
5+# you may not use this file except in compliance with the License.
6+# You may obtain a copy of the License at
7+#
8+# http://www.apache.org/licenses/LICENSE-2.0
9+#
10+# Unless required by applicable law or agreed to in writing, software
11+# distributed under the License is distributed on an "AS IS" BASIS,
12+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+# See the License for the specific language governing permissions and
14+# limitations under the License.
15+#
16+# 将 GitCode 的 main/master 分支镜像到 GitHub,并创建 mirror 分支
17+# 使用前请先在 GitHub 创建仓库,并替换下方 GITHUB_REPO 为实际地址
18+#
19+# 用法: ./script/mirror-to-github.sh [github_repo_url]
20+# 示例: ./script/mirror-to-github.sh
21+# 示例: ./script/mirror-to-github.sh git@github.com:SMAT-Lab/ArkAnalyzer.git
22+#
23+ 
24+set -e
25+ 
26+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
27+REPO_DIR="$(dirname "$SCRIPT_DIR")"
28+DEFAULT_GITHUB_REPO="git@github.com:SMAT-Lab/ArkAnalyzer.git"
29+GITHUB_REPO="${1:-$DEFAULT_GITHUB_REPO}"
30+ 
31+cd "$REPO_DIR"
32+ 
33+# 检查是否已有 github 远程
34+if git remote | grep -q '^github$'; then
35+ git remote set-url github "$GITHUB_REPO"
36+else
37+ git remote add github "$GITHUB_REPO"
38+fi
39+ 
40+# 获取当前主分支(master 或 main)
41+MAIN_BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo "master")
42+ 
43+echo "Mirroring $MAIN_BRANCH to GitHub (mirror branch)..."
44+ 
45+# 推送主分支到 GitHub 的 mirror 分支
46+git push github "${MAIN_BRANCH}:mirror"
47+ 
48+echo "Done. GitHub mirror branch updated."
@@ -16,24 +16,50 @@
16'use strict';16'use strict';
17const execSync = require('child_process').execSync;17const execSync = require('child_process').execSync;
18const fs = require('fs');18const fs = require('fs');
19+const path = require('path');
20+ 
21+const OHOS_TS_MARKER = '.ohos-typescript-version';
22+ 
23+/**
24+ * 检查 ohos-typescript 是否已由本脚本安装完成。
25+ * 通过 node_modules/ohos-typescript 下的标识文件判断,避免仅依赖版本号(版本不变但内容可能变更)。
26+ * 若标识存在则跳过安装,包括:1)开发环境已安装 2)bundledDependencies 打包产物。
27+ */
28+function isOhosTypescriptInstalled() {
29+ const markerPath = path.join(__dirname, '../node_modules/ohos-typescript', OHOS_TS_MARKER);
30+ return fs.existsSync(markerPath);
31+}
32+ 
33+function writeInstallMarker() {
34+ const ohosTsPath = path.join(__dirname, '../node_modules/ohos-typescript');
35+ const markerPath = path.join(ohosTsPath, OHOS_TS_MARKER);
36+ if (fs.existsSync(ohosTsPath)) {
37+ fs.writeFileSync(markerPath, `ohos-typescript-4.9.5-r4-OpenHarmony-6.0-Release`, 'utf-8');
38+ }
39+}
19 40 
20async function execCommand(command) {41async function execCommand(command) {
21 console.log(command);42 console.log(command);
22- let result = await execSync(command, {encoding: 'utf-8'});43+ let result = await execSync(command, { encoding: 'utf-8' });
23 console.log(result);44 console.log(result);
24}45}
25 46 
26function removeFolder(folderPath) {47function removeFolder(folderPath) {
27 console.log(`start to remove '${folderPath}'`);48 console.log(`start to remove '${folderPath}'`);
28- fs.rmSync(folderPath, {recursive: true, force: true});49+ fs.rmSync(folderPath, { recursive: true, force: true });
29 console.log();50 console.log();
30}51}
31 52 
32async function runCommands() {53async function runCommands() {
54+ if (isOhosTypescriptInstalled()) {
55+ console.log('ohos-typescript already installed, skipping npmInstall.');
56+ return;
57+ }
33 try {58 try {
34 removeFolder('arktools');59 removeFolder('arktools');
35 await execCommand('git clone https://gitee.com/yifei-xue/arktools.git');60 await execCommand('git clone https://gitee.com/yifei-xue/arktools.git');
36 await execCommand('npm install arktools/lib/ohos-typescript-4.9.5-r4-OpenHarmony-6.0-Release.tgz --no-save');61 await execCommand('npm install arktools/lib/ohos-typescript-4.9.5-r4-OpenHarmony-6.0-Release.tgz --no-save');
62+ writeInstallMarker();
37 removeFolder('arktools');63 removeFolder('arktools');
38 } catch (error) {64 } catch (error) {
39 console.error(error);65 console.error(error);
@@ -48,9 +48,36 @@ function runPerfTest(): void {
48 performance.mark('end');48 performance.mark('end');
49}49}
50 50 
51+const RSS_SAMPLE_COUNT = 7;
52+const RSS_SAMPLE_INTERVAL_MS = 50;
53+ 
54+function collectRssSamples(): number[] {
55+ const samples: number[] = [];
56+ for (let i = 0; i < RSS_SAMPLE_COUNT; i++) {
57+ samples.push(process.memoryUsage().rss);
58+ if (i < RSS_SAMPLE_COUNT - 1) {
59+ const deadline = Date.now() + RSS_SAMPLE_INTERVAL_MS;
60+ while (Date.now() < deadline) { /* spin wait */ }
61+ }
62+ }
63+ return samples;
64+}
65+ 
66+function median(values: number[]): number {
67+ const sorted = [...values].sort((a, b) => a - b);
68+ const mid = Math.floor(sorted.length / 2);
69+ return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
70+}
71+ 
51function printMemPerfInfo() {72function printMemPerfInfo() {
52- const usedRss = process.memoryUsage().rss;73+ const g = typeof globalThis !== 'undefined' ? globalThis : (typeof global !== 'undefined' ? global : undefined);
53- logger.info(`RSS Memory Size: ${Math.round(usedRss / 1024 / 1024 * 100) / 100} MB.`);74+ if (g && typeof (g as { gc?: () => void }).gc === 'function') {
75+ (g as { gc: () => void }).gc();
76+ }
77+ const samples = collectRssSamples();
78+ const rssMedian = median(samples);
79+ const rssMb = rssMedian / 1024 / 1024;
80+ logger.info(`RSS Memory Size: ${Math.round(rssMb * 100) / 100} MB.`);
54}81}
55 82 
56function printCPUPerfInfo() {83function printCPUPerfInfo() {