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