已合并
支持 github镜像流水线 #843
SunBo创建于 3月19日
支持 github镜像流水线 #843
已合并
共 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 | ; | 16 | ; |
| 17 | const execSync = require('child_process').execSync; | 17 | const execSync = require('child_process').execSync; |
| 18 | const fs = require('fs'); | 18 | const 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 | ||
| 20 | async function execCommand(command) { | 41 | async 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 | ||
| 26 | function removeFolder(folderPath) { | 47 | function 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 | ||
| 32 | async function runCommands() { | 53 | async 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 | + | ||
| 51 | function printMemPerfInfo() { | 72 | function 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 | ||
| 56 | function printCPUPerfInfo() { | 83 | function printCPUPerfInfo() { |