Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions entry/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
7 changes: 5 additions & 2 deletions entry/hvigorfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
})
]
};
17 changes: 11 additions & 6 deletions hvigor-plugin/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down
15 changes: 8 additions & 7 deletions hvigor-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
```
Expand Down Expand Up @@ -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
})
]
};
Expand All @@ -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
```
Expand All @@ -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):

Expand Down Expand Up @@ -104,7 +105,7 @@ logged (`flashcat: scanning <dir> (...)`) 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);
```

Expand Down
4 changes: 2 additions & 2 deletions hvigor-plugin/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion hvigor-plugin/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
35 changes: 30 additions & 5 deletions hvigor-plugin/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,15 @@ export interface HvigorNode {
getNodePath(): string;
getParentNode?(): HvigorNode | undefined;
getContext?(pluginId: string): unknown;
registerTask(task: { name: string; run: () => void | Promise<void> }): 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<void>;
dependencies?: string[];
postDependencies?: string[];
}): void;
}
export interface HvigorPlugin {
pluginId: string;
Expand All @@ -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;
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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'
* })]
* };
* ```
Expand All @@ -112,9 +127,19 @@ export function flashcatSymbolUploadPlugin(options: FlashcatPluginOptions): Hvig
node.registerTask({
name: 'uploadFlashcatSymbols',
run: async (): Promise<void> => {
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);
Expand Down
14 changes: 13 additions & 1 deletion hvigor-plugin/test/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
14 changes: 11 additions & 3 deletions scripts/ci-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading