diff --git a/entry/README.md b/entry/README.md index 8c2dc8f..0be684f 100644 --- a/entry/README.md +++ b/entry/README.md @@ -23,11 +23,20 @@ Verified working on the local emulator (2026-06-13): `upload: POST /api/v2/rum - ## One-time setup -1. Open the repo root in **DevEco Studio**. -2. Configure automatic signing: `File → Project Structure → Signing Configs → +1. Build the hvigor plugin once. `entry/hvigorfile.ts` imports it from + `hvigor-plugin/dist/`, which is not checked in, and hvigor evaluates that file on + every invocation — DevEco project sync included, so skipping this fails the sync + with a module-not-found error: + + ```sh + (cd hvigor-plugin && npm ci && npm run build) + ``` + +2. Open the repo root in **DevEco Studio**. +3. Configure automatic signing: `File → Project Structure → Signing Configs → Automatically generate signature` (a free Huawei account works). The CLI build produces an **unsigned** HAP; running on a device needs a signature. -3. Edit `entry/src/main/resources/rawfile/demo_config.json`: +4. Edit `entry/src/main/resources/rawfile/demo_config.json`: - `clientToken` — your FlashCat client token - `applicationId` — your RUM application id - `service` — optional service name; defaults to `flashcat-harmony-demo` diff --git a/entry/hvigorfile.ts b/entry/hvigorfile.ts index 1d113a5..ee547c1 100644 --- a/entry/hvigorfile.ts +++ b/entry/hvigorfile.ts @@ -6,8 +6,11 @@ export default { plugins: [ flashcatSymbolUploadPlugin({ apiKey: process.env.FLASHCAT_API_KEY ?? '', - service: 'fc-sdk-harmony-demo', - version: '0.1.0' + // Must match what the demo reports at runtime, or symbolication cannot find + // these files: service is DemoConfig's default, version is AppScope versionName. + service: 'flashcat-harmony-demo', + version: '0.1.1', + enabled: process.env.FLASHCAT_UPLOAD === '1' }) ] }; diff --git a/hvigor-plugin/CHANGELOG.md b/hvigor-plugin/CHANGELOG.md index c8f88fb..a9f45ca 100644 --- a/hvigor-plugin/CHANGELOG.md +++ b/hvigor-plugin/CHANGELOG.md @@ -1,16 +1,14 @@ # Changelog -## 0.1.4 +## 0.1.5 - Fix `uploadFlashcatSymbols` breaking task-graph resolution: the task declared `dependencies: ['assembleHap','assembleHar']`, but a module has at most one of those, so the missing one failed the build. The task now declares no build dependencies — run it as its own hvigor invocation after a release build. -- **Breaking:** remove the `enabled` option. With no build dependencies the task - runs only when it is named on the command line, so naming it *is* the switch; a - second gate could only ever silently skip an upload that was explicitly asked - for. Drop `enabled` (and the `FLASHCAT_UPLOAD` variable that fed it) from - `hvigorfile.ts`. Every remaining skip path logs its reason. +- A disabled task now says so. `enabled: false` used to return without a word, which + in a build log is indistinguishable from a successful upload. Every path that skips + the upload now states its reason. - The build directory now follows the product being built (`-p product=beta` → `build/beta`), read from the project's OHOS app context. `buildDir` stays as an override for layouts that do not follow that convention; previously it defaulted @@ -23,6 +21,13 @@ environment when it is first started and refreshes only a fixed allowlist of variables, so a reused daemon can hand the plugin a stale or empty `process.env` — silently skipping the upload, or uploading under the previous version number. +- An empty `buildDir` now counts as unset instead of resolving to the module root. + An unassigned `FLASHCAT_BUILD_DIR=` in CI reaches the option as `''`, and scanning + the module root collects every product's sourcemap — uploading an arbitrary one + under the current version, the same class of bug the product-aware default fixes. +- The "skipping symbol upload" warning now names the `apiKey` option rather than only + the environment variable, and points at `--no-daemon` — the likeliest reason the + value arrived empty. - First tests for task registration and build-dir resolution (`plugin.ts` had none). ## 0.1.3 diff --git a/hvigor-plugin/README.md b/hvigor-plugin/README.md index a320d08..aa59fa7 100644 --- a/hvigor-plugin/README.md +++ b/hvigor-plugin/README.md @@ -19,7 +19,7 @@ The plugin is published on **npm**, not ohpm. Declare it in { "modelVersion": "5.0.0", "dependencies": { - "@flashcatcloud/hvigor-plugin": "0.1.4" + "@flashcatcloud/hvigor-plugin": "0.1.5" } } ``` @@ -54,7 +54,8 @@ export default { // (scheme + host, no path), or pass endpoint: 'https://rum.example.com'. apiKey: process.env.FLASHCAT_API_KEY ?? '', service: 'my-app', - version: '1.0.0' + version: '1.0.0', + enabled: process.env.FLASHCAT_UPLOAD === '1' // upload only when asked }) ] }; @@ -63,7 +64,7 @@ export default { Then, after a release build, run the task as its own hvigor invocation: ```sh -FLASHCAT_API_KEY=*** \ +FLASHCAT_UPLOAD=1 FLASHCAT_API_KEY=*** \ hvigorw uploadFlashcatSymbols --no-daemon \ --mode module -p module=entry@beta -p product=beta ``` @@ -74,9 +75,9 @@ environment once when it is *created* and afterwards refreshes only a fixed allowlist (`DEVECO_SDK_HOME`, `OHOS_BASE_SDK_HOME`, and two incremental-build flags). A reused daemon therefore sees the environment of whoever started it — an IDE build, or an earlier command — not the one you just typed. The failure is easy -to miss: `FLASHCAT_API_KEY` reads as unset and the task skips with only a warning, -or a stale version uploads the symbols under the wrong version number. Values -written directly into `hvigorfile.ts` are not affected. +to miss: `FLASHCAT_UPLOAD` or `FLASHCAT_API_KEY` reads as unset and the task skips +with only a warning, or a stale version uploads the symbols under the wrong version +number. Values written directly into `hvigorfile.ts` are not affected. Endpoint resolution (first match wins): @@ -104,7 +105,7 @@ logged (`flashcat: scanning (...)`) so a wrong guess is visible immediatel import { uploadAll } from '@flashcatcloud/hvigor-plugin'; const result = await uploadAll('entry/build/default', { endpoint: process.env.FLASHCAT_SOURCEMAP_INTAKE_URL || 'https://ci.flashcat.cloud', - apiKey, service, version, pluginVersion: '0.1.4' + apiKey, service, version, pluginVersion: '0.1.5' }, console.log); ``` diff --git a/hvigor-plugin/package-lock.json b/hvigor-plugin/package-lock.json index 128c5e7..c5801ab 100644 --- a/hvigor-plugin/package-lock.json +++ b/hvigor-plugin/package-lock.json @@ -1,12 +1,12 @@ { "name": "@flashcatcloud/hvigor-plugin", - "version": "0.1.4", + "version": "0.1.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@flashcatcloud/hvigor-plugin", - "version": "0.1.4", + "version": "0.1.5", "license": "Apache-2.0", "devDependencies": { "@types/node": "^26.0.0", diff --git a/hvigor-plugin/package.json b/hvigor-plugin/package.json index 2289b19..856773a 100644 --- a/hvigor-plugin/package.json +++ b/hvigor-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@flashcatcloud/hvigor-plugin", - "version": "0.1.4", + "version": "0.1.5", "description": "FlashCat hvigor plugin: upload HarmonyOS ArkTS sourcemaps + native .so debug symbols to fc-rum for crash symbolication.", "license": "Apache-2.0", "author": "FlashCat", diff --git a/hvigor-plugin/src/plugin.ts b/hvigor-plugin/src/plugin.ts index 29c2f69..4c89155 100644 --- a/hvigor-plugin/src/plugin.ts +++ b/hvigor-plugin/src/plugin.ts @@ -20,7 +20,15 @@ export interface HvigorNode { getNodePath(): string; getParentNode?(): HvigorNode | undefined; getContext?(pluginId: string): unknown; - registerTask(task: { name: string; run: () => void | Promise }): void; + // `dependencies`/`postDependencies` are part of hvigor's own registerTask API. This + // plugin does not use them (see below), but the interface must not describe less + // than hvigor offers, or it breaks anyone else typing a node against it. + registerTask(task: { + name: string; + run: () => void | Promise; + dependencies?: string[]; + postDependencies?: string[]; + }): void; } export interface HvigorPlugin { pluginId: string; @@ -38,8 +46,14 @@ export interface FlashcatPluginOptions { version: string; /** Module build dir, relative to the module root. Optional — by default it follows * the product being built (`-p product=beta` → `build/beta`). Set it only when the - * artifacts are somewhere that does not follow that layout. */ + * artifacts are somewhere that does not follow that layout. An empty string counts + * as unset: an unassigned `FLASHCAT_BUILD_DIR=` in CI must not turn into a scan of + * the whole module root, which would collect another product's sourcemap. */ buildDir?: string; + /** When false the task is registered but does nothing, and says so. Default true. + * Kept as an option because a pipeline variable is cheaper to flip than an edit to + * the build command — the same switch consumers otherwise hand-roll. */ + enabled?: boolean; pluginVersion?: string; } @@ -67,7 +81,7 @@ function currentProductName(node: HvigorNode): string | null { */ export function resolveBuildDir(node: HvigorNode, explicit?: string): { dir: string; how: string } { const moduleRoot = node.getNodePath(); - if (explicit !== undefined) { + if (explicit !== undefined && explicit !== '') { return { dir: `${moduleRoot}/${explicit}`, how: 'buildDir option' }; } const product = currentProductName(node); @@ -96,7 +110,8 @@ export function resolveBuildDir(node: HvigorNode, explicit?: string): { dir: str * system: hapTasks, * plugins: [flashcatSymbolUploadPlugin({ * apiKey: process.env.FLASHCAT_API_KEY ?? '', - * service: 'my-app', version: '1.0.0' + * service: 'my-app', version: '1.0.0', + * enabled: process.env.FLASHCAT_UPLOAD === '1' * })] * }; * ``` @@ -112,9 +127,19 @@ export function flashcatSymbolUploadPlugin(options: FlashcatPluginOptions): Hvig node.registerTask({ name: 'uploadFlashcatSymbols', run: async (): Promise => { + if (options.enabled === false) { + // Never return silently: in a build log, a deliberate skip and a + // successful upload would otherwise look exactly the same. + // eslint-disable-next-line no-console + console.warn('flashcat: upload disabled (enabled: false) — skipping symbol upload.'); + return; + } if (!options.apiKey) { // eslint-disable-next-line no-console - console.warn('flashcat: FLASHCAT_API_KEY not set — skipping symbol upload.'); + console.warn( + 'flashcat: apiKey is empty — skipping symbol upload. Set FLASHCAT_API_KEY, ' + + 'and pass --no-daemon so hvigor does not hand the plugin a cached environment.' + ); return; } const resolved = resolveUploadEndpoint(options.endpoint); diff --git a/hvigor-plugin/test/plugin.test.ts b/hvigor-plugin/test/plugin.test.ts index 964a264..de1ce96 100644 --- a/hvigor-plugin/test/plugin.test.ts +++ b/hvigor-plugin/test/plugin.test.ts @@ -65,7 +65,19 @@ test('explicit buildDir wins over the product', () => { assert.equal(r.how, 'buildDir option'); }); -test('missing api key is a no-op, never throws', async () => { +test('an empty buildDir counts as unset, not as the module root', () => { + // An unassigned FLASHCAT_BUILD_DIR= reaches the option as ''. Scanning the module + // root would collect every product's sourcemap and upload an arbitrary one. + const r = resolveBuildDir(fakeNode('/project/entry', 'beta'), ''); + assert.equal(r.dir, '/project/entry/build/beta'); + assert.match(r.how, /beta/); +}); + +test('a disabled task and a missing api key are no-ops, never throw', async () => { + const disabled = fakeNode('/project/entry', 'default'); + flashcatSymbolUploadPlugin({ ...options, enabled: false }).apply(disabled); + await disabled.tasks[0].run(); + const noKey = fakeNode('/project/entry', 'default'); flashcatSymbolUploadPlugin({ ...options, apiKey: '' }).apply(noKey); await noKey.tasks[0].run(); diff --git a/scripts/ci-check.sh b/scripts/ci-check.sh index 034c486..d548251 100755 --- a/scripts/ci-check.sh +++ b/scripts/ci-check.sh @@ -27,10 +27,18 @@ echo "==> build gate: demo HAP" # The only place the hvigor plugin runs against a real task graph — hosted Linux # CI cannot run hvigor, so a regression here is invisible to every other check. # The endpoint is deliberately unreachable: this gates task registration and -# build-dir resolution, not the network. +# build-dir resolution, not the network. Asserting the "(product '...')" suffix is +# what makes this a gate: the probe's fallback lands on the same build/default +# directory and an upload failure never fails the build, so without the assertion +# the step stays green even when the product probe is broken. echo "==> build gate: hvigor plugin task" -FLASHCAT_API_KEY=ci-smoke FLASHCAT_SOURCEMAP_INTAKE_URL=http://127.0.0.1:1 \ - "$HVIGORW" uploadFlashcatSymbols --no-daemon --mode module -p module=entry@default -p product=default +gate_out=$(FLASHCAT_UPLOAD=1 FLASHCAT_API_KEY=ci-smoke FLASHCAT_SOURCEMAP_INTAKE_URL=http://127.0.0.1:1 \ + "$HVIGORW" uploadFlashcatSymbols --no-daemon --mode module -p module=entry@default -p product=default 2>&1) +echo "$gate_out" +if ! echo "$gate_out" | grep -q "flashcat: scanning .*(product 'default')"; then + echo "FAILED: the plugin did not read the product from the hvigor context" >&2 + exit 1 +fi echo "==> build gate: unit-test compile (type check)" "$HVIGORW" --mode module -p module="$MODULES" UnitTestBuild --no-daemon