@1aboveio/skills 1.0.15 → 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +1 -1
- package/.codex-plugin/resources.json +156 -152
- package/package.json +4 -4
- package/skills/app-dev/airflow/SKILL.md +1 -1
- package/skills/app-dev/android-development/SKILL.md +30 -1
- package/skills/app-dev/android-development/references/diagnosis.md +50 -0
- package/skills/app-dev/android-development/references/feature-work.md +3 -2
- package/skills/app-dev/android-development/references/gotchas.md +13 -0
- package/skills/app-dev/android-development/references/init.md +31 -2
- package/skills/app-dev/android-development/references/setup.md +16 -7
- package/skills/app-dev/android-development/references/ui-journeys.md +130 -0
- package/skills/app-dev/android-development/scripts/device-run.sh +93 -2
- package/skills/app-dev/android-development/scripts/failure_capture.py +331 -0
- package/skills/app-dev/android-development/scripts/init.mjs +129 -9
- package/skills/app-dev/android-development/scripts/maestro_failure.py +401 -0
- package/skills/app-dev/android-development/scripts/test_failure_capture.py +378 -0
- package/skills/app-dev/android-development/scripts/test_maestro_failure.py +462 -0
- package/skills/app-dev/app-debug/SKILL.md +1 -1
- package/skills/app-dev/better-auth/SKILL.md +1 -1
- package/skills/app-dev/data-catalog/SKILL.md +1 -1
- package/skills/app-dev/data-quality/SKILL.md +1 -1
- package/skills/app-dev/ios-foundation/SKILL.md +1 -1
- package/skills/app-dev/monorepo/SKILL.md +1 -1
- package/skills/app-dev/nextjs-fullstack/SKILL.md +1 -1
- package/skills/app-dev/prisma-setup/SKILL.md +1 -1
- package/skills/app-dev/pyspark/SKILL.md +1 -1
- package/skills/app-dev/python-backend/SKILL.md +1 -1
- package/skills/app-dev/shadcn/SKILL.md +1 -1
- package/skills/app-dev/zod-v4/SKILL.md +1 -1
- package/skills/cicd/cloud-build/SKILL.md +1 -1
- package/skills/cicd/cloud-debug/SKILL.md +1 -1
- package/skills/cicd/cloud-deploy/SKILL.md +1 -1
- package/skills/cicd/docker/SKILL.md +1 -1
- package/skills/cicd/github-actions/SKILL.md +1 -1
- package/skills/cicd/github-flow/SKILL.md +1 -1
- package/skills/cicd/google-cloud/SKILL.md +1 -1
- package/skills/cicd/mergify/SKILL.md +1 -1
- package/skills/cicd/podman/SKILL.md +1 -1
- package/skills/content/article-review/SKILL.md +1 -1
- package/skills/content/article-to-image/SKILL.md +1 -1
- package/skills/content/flowchart-svg/SKILL.md +1 -1
- package/skills/content/image-gen/SKILL.md +1 -1
- package/skills/content/mermaid/SKILL.md +1 -1
- package/skills/content/wechat-article-writer/SKILL.md +1 -1
- package/skills/content/wechat-publisher/SKILL.md +1 -1
- package/skills/content/wechat-search/SKILL.md +1 -1
- package/skills/content/xai-search/SKILL.md +1 -1
- package/skills/core/distill-writing/SKILL.md +1 -1
- package/skills/core/doc-to-md/SKILL.md +1 -1
- package/skills/core/editorial-review/SKILL.md +1 -1
- package/skills/core/house-style/SKILL.md +1 -1
- package/skills/core/plain-language/SKILL.md +1 -1
- package/skills/core/polish-cn/SKILL.md +1 -1
- package/skills/core/reg-parser/SKILL.md +1 -1
- package/skills/core/terminology/SKILL.md +1 -1
- package/skills/core/write-doc/SKILL.md +1 -1
- package/skills/core/write-report/SKILL.md +1 -1
- package/skills/engineering/better-review/SKILL.md +1 -1
- package/skills/engineering/e2e-test/SKILL.md +1 -1
- package/skills/engineering/engineering/SKILL.md +1 -1
- package/skills/engineering/engineering-runtime/SKILL.md +1 -1
- package/skills/engineering/engineering-runtime/coherence/workflow.json +13 -13
- package/skills/engineering/ensure-coverage/SKILL.md +1 -1
- package/skills/engineering/golden-path/SKILL.md +1 -1
- package/skills/engineering/harness-runtime/SKILL.md +4 -3
- package/skills/engineering/harness-runtime/agent-catalog.mjs +929 -0
- package/skills/engineering/harness-runtime/contracts.md +2 -0
- package/skills/engineering/harness-runtime/references/model-catalog.md +7 -1
- package/skills/engineering/resolve-issues/SKILL.md +18 -13
- package/skills/engineering/resolve-issues/references/delivery-progress.md +2 -0
- package/skills/engineering/resolve-issues/references/expire.md +6 -3
- package/skills/engineering/resolve-issues/references/exploration.md +60 -18
- package/skills/engineering/resolve-issues/references/implementation.md +7 -2
- package/skills/engineering/resolve-issues/references/preflight.md +65 -31
- package/skills/engineering/resolve-issues/references/review.md +22 -9
- package/skills/engineering/resolve-issues/references/shared-modules.md +142 -20
- package/skills/engineering/resolve-issues/references/task-plan.md +62 -8
- package/skills/engineering/resolve-issues/scripts/findings-reuse.mjs +1 -1
- package/skills/engineering/resolve-issues/scripts/next-actions.mjs +7 -7
- package/skills/engineering/resolve-issues/scripts/plan-sync.mjs +1 -1
- package/skills/engineering/resolve-issues/scripts/preflight-models.mjs +382 -592
- package/skills/engineering/resolve-issues/scripts/size-check.mjs +6 -5
- package/skills/engineering/resolve-release/SKILL.md +1 -1
- package/skills/engineering/rush-issues/SKILL.md +4 -4
- package/skills/engineering/rush-release/SKILL.md +1 -1
- package/skills/engineering/test-quality/SKILL.md +1 -1
- package/skills/risk-compliance/compliance-audit/SKILL.md +1 -1
- package/skills/risk-compliance/contract-compare/SKILL.md +1 -1
- package/skills/risk-compliance/contract-review/SKILL.md +1 -1
- package/skills/risk-compliance/doc-authenticity/SKILL.md +1 -1
- package/skills/risk-compliance/fraud-detection/SKILL.md +1 -1
- package/skills/risk-compliance/payment-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/portfolio-payment-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/similar-domain/SKILL.md +1 -1
- package/skills/risk-compliance/tld-detection/SKILL.md +1 -1
- package/skills/engineering/resolve-issues/scripts/discover-models.mjs +0 -9
- package/skills/engineering/resolve-issues/scripts/model-catalog.mjs +0 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1aboveio/skills",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.17",
|
|
4
4
|
"description": "Every 1aboveio skill package in one: install once, then choose which packages Pi loads with npx @1aboveio/skills select.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"nativeCodex": {
|
|
29
29
|
"schemaVersion": 1,
|
|
30
30
|
"metadataPath": ".codex-plugin/resources.json",
|
|
31
|
-
"metadataDigest": "
|
|
32
|
-
"releaseIdentity": "
|
|
31
|
+
"metadataDigest": "4ae04aa79316f8a7cc9df45582d19e332321316012dac6843be1eb21d2b1a385",
|
|
32
|
+
"releaseIdentity": "d4a51368dcc414b3136d3b6f51a6aeeeea77429a101eb65f36f7878e00032494"
|
|
33
33
|
},
|
|
34
34
|
"pi": {
|
|
35
35
|
"skills": [
|
|
@@ -260,7 +260,7 @@
|
|
|
260
260
|
]
|
|
261
261
|
}
|
|
262
262
|
],
|
|
263
|
-
"digest": "
|
|
263
|
+
"digest": "ae9b2ca5b33484b68e892b41976bcc054562385ef7dc7db94bf86ab42e369d0d"
|
|
264
264
|
},
|
|
265
265
|
"publishConfig": {
|
|
266
266
|
"registry": "https://registry.npmjs.org/",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: airflow
|
|
3
3
|
description: Airflow DAG architecture and conventions. Use when designing, writing, reviewing, testing, or debugging Airflow DAGs and tasks, including TaskGroups, data intervals, timezone-aware date parameters, CLI validation, environment-based scheduling, and pytest integrity tests.
|
|
4
4
|
user-invocable: true
|
|
5
|
-
release-version: 0.
|
|
5
|
+
release-version: 0.48.1
|
|
6
6
|
---
|
|
7
7
|
**Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
|
|
8
8
|
# Airflow DAG Development Patterns
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: android-development
|
|
3
3
|
description: "Prove and debug Expo / React Native apps on an owned Android emulator. Use for Android startup/rendering failures, Metro or ADB problems, UiAutomator/harness failures, APK builds, SDK/NDK/JDK workspace setup, native journey validation, and choosing Kotlin/Compose versus React Native for Android flows. Reproduce failed Android CI checkpoints off CI before fixing and redispatching. Standalone invocation offers Project or Session; init persists repo conventions. NOT for workflow YAML, CI speed or runners (github-actions), browser tests (e2e-test), iOS (ios-foundation), plain Jest tests, or signing/store release."
|
|
4
|
-
release-version: 0.
|
|
4
|
+
release-version: 0.48.1
|
|
5
5
|
---
|
|
6
6
|
**Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
|
|
7
7
|
# Android development
|
|
@@ -43,6 +43,33 @@ Choose the task, not a fresh environment for every attempt:
|
|
|
43
43
|
repo-specific stages in order and record proof. Do not add Expo or a new harness merely
|
|
44
44
|
to match the examples, or repeat cold setup for ordinary fixes.
|
|
45
45
|
|
|
46
|
+
## Device failure record
|
|
47
|
+
|
|
48
|
+
A failed device case leaves one record and one line, whichever layer saw the failure:
|
|
49
|
+
|
|
50
|
+
- `failure.json` holding `case`, `step`, `error`, `screenshot`, `uiDump`, `logcatTail`,
|
|
51
|
+
`evidenceDir` and `cleanupErrors`, captured at the failing step before teardown.
|
|
52
|
+
- One printed line `NATIVE_FAILURE {"case":…,"step":…,"error":…,"evidenceDir":…}`.
|
|
53
|
+
|
|
54
|
+
Journey code wraps its `wait_for`/`tap` helpers in
|
|
55
|
+
[failure_capture.py](scripts/failure_capture.py), `with recorder.step("tap Pay"):`. Without
|
|
56
|
+
that, [device-run.sh](scripts/device-run.sh) writes a partial record with `step: null` when
|
|
57
|
+
the journey fails and left none. The first failure is never replaced by a cleanup error.
|
|
58
|
+
The contract, other languages and the Maestro/Detox mapping are in
|
|
59
|
+
[diagnosis](references/diagnosis.md#record-the-failed-device-case).
|
|
60
|
+
|
|
61
|
+
## UI journey tool
|
|
62
|
+
|
|
63
|
+
The skill references Maestro; it does not bundle or install it. Offer Maestro for new
|
|
64
|
+
single-device journeys when the repo has no journey tool. Keep an existing sufficient
|
|
65
|
+
harness; adding Maestro needs a concrete benefit and repo authorization, not just an
|
|
66
|
+
installed CLI. Maestro uses the same work loop and validation ladder. Run it on the
|
|
67
|
+
session's own emulator (`maestro --device <serial>`) and convert its output with
|
|
68
|
+
[maestro_failure.py](scripts/maestro_failure.py), which writes the record above. Read
|
|
69
|
+
[UI journeys](references/ui-journeys.md) before selecting or running Maestro flows, for
|
|
70
|
+
starting state, permissions, device ownership, execution evidence and the cases that
|
|
71
|
+
stay custom.
|
|
72
|
+
|
|
46
73
|
## Validation ladder
|
|
47
74
|
|
|
48
75
|
Read [feature work](references/feature-work.md) for selection, ownership and handoff.
|
|
@@ -123,5 +150,7 @@ higher levels still pending rather than claiming acceptance.
|
|
|
123
150
|
| Parallel warm servers and test-first provenance | [Parallel sessions](references/workspace.md#parallel-development-sessions) and [test-first provenance](references/feature-work.md#run-tests-before-chasing-commit-labels) |
|
|
124
151
|
| Builds, provisioning, device execution, cleanup and setup proof | [setup.md](references/setup.md); use [device-run.sh](scripts/device-run.sh) only without an existing resource owner |
|
|
125
152
|
| Feature/fix validation and handoff | [feature-work.md](references/feature-work.md) |
|
|
153
|
+
| Device failure record and evidence capture | [diagnosis.md](references/diagnosis.md#record-the-failed-device-case) |
|
|
154
|
+
| UI journey tool (Maestro) and its evidence converter | [UI journeys](references/ui-journeys.md) |
|
|
126
155
|
| Failure reproduction and correction | [diagnosis.md](references/diagnosis.md), then the relevant [gotcha](references/gotchas.md) |
|
|
127
156
|
| CI integration | [ci.md](references/ci.md), [template](assets/android-ci-job.yml), and `github-actions` |
|
|
@@ -94,6 +94,56 @@ reproduction command, controlled change, observations, evidence locations, prima
|
|
|
94
94
|
cleanup outcomes, and remaining uncertainty. Do not require an APK hash for a compiler
|
|
95
95
|
failure that produced no APK.
|
|
96
96
|
|
|
97
|
+
## Record the failed device case
|
|
98
|
+
|
|
99
|
+
Capture the failing step where it fails, not after cleanup. One contract covers every
|
|
100
|
+
harness, so a coordinator, a fix brief and a later diagnosis read the same thing.
|
|
101
|
+
|
|
102
|
+
`failure.json` in the evidence directory holds `case`, `step`, `error`, `screenshot`,
|
|
103
|
+
`uiDump`, `logcatTail`, `evidenceDir` and `cleanupErrors`. The three artifact fields are
|
|
104
|
+
file names relative to `evidenceDir`, or `null` when that capture failed; the reason then
|
|
105
|
+
goes to `cleanupErrors` and never replaces `error`. Write the record once: the first
|
|
106
|
+
failure is kept, and neither a later failure nor a cleanup problem overwrites it. Print
|
|
107
|
+
exactly one line, `NATIVE_FAILURE {"case":…,"step":…,"error":…,"evidenceDir":…}`, so a
|
|
108
|
+
reader that only sees the run output still learns the cause and where the evidence is.
|
|
109
|
+
|
|
110
|
+
In Python journeys wrap the step helpers in [failure_capture.py](../scripts/failure_capture.py):
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from failure_capture import FailureRecorder
|
|
114
|
+
|
|
115
|
+
recorder = FailureRecorder(case=os.environ["DEVICE_CASE"], evidence_dir=os.environ["LOG_DIR"])
|
|
116
|
+
|
|
117
|
+
with recorder.step("tap Pay"):
|
|
118
|
+
tap("Pay")
|
|
119
|
+
with recorder.step("wait for receipt"):
|
|
120
|
+
wait_for("Thank you")
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`recorder.step` is a context manager and a decorator. On the first exception in a step it
|
|
124
|
+
captures `adb exec-out screencap -p`, `uiautomator dump` and a bounded logcat tail on the
|
|
125
|
+
injected serial, writes the record, prints the line and re-raises the original exception.
|
|
126
|
+
Captures are bounded; a capture failure is a `cleanupErrors` entry. A cancelled run
|
|
127
|
+
(`KeyboardInterrupt`, `SystemExit`) is not a failed case.
|
|
128
|
+
|
|
129
|
+
Other languages implement the same two shapes. When the journey harness cannot report a
|
|
130
|
+
step, fix the harness in the PR so every case records one; do not wrap it in a run-only
|
|
131
|
+
script, and do not add a second failure format.
|
|
132
|
+
|
|
133
|
+
Without an in-journey helper, [device-run.sh](../scripts/device-run.sh) falls back: after a
|
|
134
|
+
non-zero journey exit with no `failure.json` in `LOG_DIR` it captures the screen, hierarchy
|
|
135
|
+
and logcat tail before tearing the device down, writes a partial record with `step: null`,
|
|
136
|
+
and prints `NATIVE_FAILURE`. Its `original_stage_exit=<n> cleanup_exit=<n>` line and exit
|
|
137
|
+
status are unchanged, and a record the journey already wrote is left untouched.
|
|
138
|
+
|
|
139
|
+
A repo that already runs Maestro or Detox maps its failing artifacts to this record instead
|
|
140
|
+
of adding the helper: Maestro's failing-step screenshot, view hierarchy and logcat plus its
|
|
141
|
+
`commands.json` step, and Detox's `failing` artifact preset output. For Maestro, run
|
|
142
|
+
[maestro_failure.py](../scripts/maestro_failure.py) over the flow folder or the session folder
|
|
143
|
+
it sits in; it writes the record and prints the line, and [UI journeys](ui-journeys.md#evidence)
|
|
144
|
+
has the evidence layout. A record is written even when the source is the framework's artifact
|
|
145
|
+
set, and the printed line still has the four fields.
|
|
146
|
+
|
|
97
147
|
## Bound work and distinguish progress from waiting
|
|
98
148
|
|
|
99
149
|
Give commands and child waits explicit deadlines appropriate to their purpose. Reserve
|
|
@@ -173,8 +173,9 @@ at L2. Required CI and L3 obligations remain assigned, not silently waived or ma
|
|
|
173
173
|
rule for unreproduced or intermittent failures; report uncertainty until evidence
|
|
174
174
|
supports the cause.
|
|
175
175
|
- Report source SHA, commands, results and remaining gaps at review handoff. For device
|
|
176
|
-
checks, include the selected scenarios, `PASS` markers
|
|
177
|
-
|
|
176
|
+
checks, include the selected scenarios, per-scenario results (`PASS` markers or the
|
|
177
|
+
journey tool's report) and cleanup exit. For a failure, name the earliest failing
|
|
178
|
+
checkpoint and link retained evidence; preserve failed attempts.
|
|
178
179
|
Separate build duration, device-check duration and total worker time when discussing cost.
|
|
179
180
|
Reuse existing logs to distinguish cold/warm builds, build count, ABI, test packaging,
|
|
180
181
|
runtime startup and edit-to-feedback time. Do not infer a task's cost from total job time,
|
|
@@ -20,8 +20,10 @@ the product.
|
|
|
20
20
|
| Backend tool, CI only | a Backend-backed step gets a 5xx in CI only; the log names a missing program (`Browser was not found at the configured executablePath`) |
|
|
21
21
|
| adb transport, mid-journey | `connection terminated: read failed` in the adb trace after boot, then lost `adb reverse`: usually guest RAM |
|
|
22
22
|
| UiAutomator server | `RemoteDisconnected` while the adb transport stays up: the on-device server was relaunched |
|
|
23
|
+
| Maestro driver | driver readiness fails while a setup uiautomator2 process is still alive: see [device ownership](ui-journeys.md#device-ownership) |
|
|
23
24
|
| Journey harness | a wait right after a navigation times out while the destination is on screen |
|
|
24
25
|
| Journey harness, short screen | a wait for a label times out only on a short or landscape screen, which shows the content above it |
|
|
26
|
+
| Journey adapter | an offscreen target stays hidden while scrolls move away from it |
|
|
25
27
|
| Product assertion | a screen or state mismatch with a screenshot |
|
|
26
28
|
|
|
27
29
|
Then match the signal against [gotchas.md](gotchas.md). If nothing
|
|
@@ -143,6 +145,17 @@ matches, [confirm the suspected cause](#confirm-a-suspected-cause) before changi
|
|
|
143
145
|
|
|
144
146
|
## Journey harness
|
|
145
147
|
|
|
148
|
+
### A scroll adapter moves away from the target
|
|
149
|
+
|
|
150
|
+
- **Signal:** a custom comparison adapter times out on an offscreen target even though it
|
|
151
|
+
sends scroll gestures; a fake UI that exposes all targets can still pass.
|
|
152
|
+
- **Cause:** Maestro's requested scroll direction describes the content to reach, not the
|
|
153
|
+
finger movement. Mapping `DOWN` to a downward finger swipe reverses the intended scroll.
|
|
154
|
+
- **Fix:** for `DOWN` toward later content, swipe the finger up; for `UP` toward earlier
|
|
155
|
+
content, swipe the finger down. In the adapter test, keep the target offscreen until the
|
|
156
|
+
correct gesture moves it into view. Wrong-direction gestures must not reveal it merely
|
|
157
|
+
because a gesture count was reached. Check both directions where the adapter supports them.
|
|
158
|
+
|
|
146
159
|
### A navigation check passes on the previous screen
|
|
147
160
|
|
|
148
161
|
- **Signal:** intermittently, a wait right after the next tap times out even though the
|
|
@@ -7,12 +7,14 @@ whether anything is persisted.
|
|
|
7
7
|
## Usage
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
|
-
node {baseDir}/scripts/init.mjs [--repo-root <dir>] [--write]
|
|
10
|
+
node {baseDir}/scripts/init.mjs [--repo-root <dir>] [--install-scripts [<dir>]] [--write]
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
- Preview is the default: it prints the planned action per file and writes nothing.
|
|
14
14
|
- `--write` applies the plan. `--repo-root` selects the project; without it the nearest Git root
|
|
15
15
|
from the current directory is used, falling back to the current directory when there is none.
|
|
16
|
+
- `--install-scripts` additionally copies the generic script layer into the repo; see
|
|
17
|
+
[Skill scripts](#skill-scripts-reference-or-copy) for the target and the rules.
|
|
16
18
|
- `--help` prints the usage line.
|
|
17
19
|
- An unknown argument, an unresolvable root, a malformed marker, an unsafe target or a file that
|
|
18
20
|
ends inside an unclosed fenced code block or HTML comment exits non-zero with a message and no
|
|
@@ -29,6 +31,7 @@ to a newly created Android section; report unresolved commands instead of guessi
|
|
|
29
31
|
| --- | --- |
|
|
30
32
|
| `CONTRIBUTING.md` | The `## Android development` section, wrapped in `<!-- conventions:android:start -->` / `<!-- conventions:android:end -->`, with `references/android-conventions.md` as its body. |
|
|
31
33
|
| `AGENTS.md` | An `Android task entry` skill-loading block between `conventions:android-task-entry` markers, plus a pointer to the selected `CONTRIBUTING.md` when missing. Check these independently: a contributor-guide link alone is not a skill loader. |
|
|
34
|
+
| `<dir>/device-run.sh`, `<dir>/failure_capture.py` | Only with `--install-scripts`: the generic layer copied with a version header. |
|
|
32
35
|
|
|
33
36
|
Each file is created only when it is absent. The helper never edits `CLAUDE.md`, never writes into
|
|
34
37
|
a parent project, a home directory or global agent configuration, and never reaches outside the
|
|
@@ -107,6 +110,31 @@ Existing guidance wins over the template:
|
|
|
107
110
|
and a new pointer is written relative to the real location of `AGENTS.md`. A link to some other
|
|
108
111
|
file, such as a nested package guide, does not count as this project's loader.
|
|
109
112
|
|
|
113
|
+
## Skill scripts: reference or copy
|
|
114
|
+
|
|
115
|
+
`device-run.sh` and `failure_capture.py` are referenced from the installed skill by
|
|
116
|
+
default: nothing is copied, and the run reads them from the skill directory. Copy them
|
|
117
|
+
into the repo only on an explicit request, with `--install-scripts` (default target
|
|
118
|
+
`scripts/android/`) or `--install-scripts <dir>` for another relative directory inside
|
|
119
|
+
the project.
|
|
120
|
+
|
|
121
|
+
A copy carries one header line naming the skill release and the shipped body's hash, for
|
|
122
|
+
example `# android-development skill 0.47.0 sha256:<64 hex> device-run.sh`. The header
|
|
123
|
+
follows a shebang or a module docstring, so neither stops being first; the rest of the file
|
|
124
|
+
is the shipped script byte for byte. That makes the three outcomes unambiguous:
|
|
125
|
+
|
|
126
|
+
- Missing file: `create`. An unmodified copy of the current skill: `unchanged`, so a rerun
|
|
127
|
+
writes nothing. An unmodified copy of an older skill release: `update` re-stamps the
|
|
128
|
+
header and rewrites nothing a caller added.
|
|
129
|
+
- Any other content, including a copy without the header, is `drift`. It is reported in
|
|
130
|
+
preview and with `--write`, the file is preserved, and the run still succeeds. Refresh it
|
|
131
|
+
by hand; initialization never replaces a local edit.
|
|
132
|
+
- Unsafe targets are refused before anything is written: an absolute path, a `..` segment,
|
|
133
|
+
or a directory whose nearest existing ancestor is a file or resolves outside the root.
|
|
134
|
+
|
|
135
|
+
This is the contract's preservation rule, not an update path: a new skill release never
|
|
136
|
+
rewrites project files, and drift is reported rather than resolved.
|
|
137
|
+
|
|
110
138
|
## What it does not do
|
|
111
139
|
|
|
112
140
|
Initialization is not installation or delivery. It does not install dependencies, SDK components
|
|
@@ -114,7 +142,8 @@ or devices, provision an emulator, generate CI, open issues, push or release. It
|
|
|
114
142
|
discover commands automatically: the initializer follows the command map above and reports
|
|
115
143
|
what it could not resolve. Adding verified repository-specific bindings to the new section is
|
|
116
144
|
part of that explicit initialization; a
|
|
117
|
-
conflicting existing rule is reported rather than overwritten.
|
|
145
|
+
conflicting existing rule is reported rather than overwritten. Copying the generic scripts
|
|
146
|
+
does not make the repo's existing harness or CI use them.
|
|
118
147
|
|
|
119
148
|
## Scope and overrides
|
|
120
149
|
|
|
@@ -211,7 +211,12 @@ Pass each command as separate words, not as one quoted string. The script:
|
|
|
211
211
|
uses the separate `JOURNEY_SECONDS` deadline. Leave time for cleanup after that deadline.
|
|
212
212
|
5. Exports `$DB_URL_VAR`, `$DB_NAME_VAR` (`$DB_NAME_PREFIX` plus a unique suffix) and
|
|
213
213
|
`ANDROID_EMULATOR_SERIAL`, then runs the journey.
|
|
214
|
-
6.
|
|
214
|
+
6. On a failing journey that left no record, captures the screenshot, UI dump and logcat
|
|
215
|
+
tail into `LOG_DIR` while the device is still up, writes `failure.json` with `step: null`
|
|
216
|
+
and prints one `NATIVE_FAILURE {...}` line. A record the journey's own helper wrote is
|
|
217
|
+
kept; capture problems are listed in its `cleanupErrors`. See the
|
|
218
|
+
[failure record contract](diagnosis.md#record-the-failed-device-case).
|
|
219
|
+
7. Drops the database, stops the emulator PID (`TERM`, then `KILL` after 10 seconds),
|
|
215
220
|
removes the container by its CID, and prints `original_stage_exit=<n> cleanup_exit=<n>`.
|
|
216
221
|
It exits with the journey's status if the journey failed, else the cleanup status.
|
|
217
222
|
|
|
@@ -220,11 +225,14 @@ group; a wrapper that deliberately creates detached services must stop those ser
|
|
|
220
225
|
through its own owner. Send TERM to the launcher and allow its cleanup to finish rather
|
|
221
226
|
than killing it immediately. The generic runner inherits the caller's ADB endpoint and
|
|
222
227
|
does not stop that server: use a verified private endpoint on a shared host and close it
|
|
223
|
-
through the caller's owner. Capture the command's exit status
|
|
228
|
+
through the caller's owner. Capture the command's exit status, the final summary line and,
|
|
229
|
+
when the journey failed, the `NATIVE_FAILURE` line and its `failure.json`.
|
|
224
230
|
|
|
225
|
-
Expect every selected scenario's `PASS` marker
|
|
226
|
-
|
|
227
|
-
|
|
231
|
+
Expect every selected scenario's result (`PASS` marker or the journey tool's report),
|
|
232
|
+
required screenshots, and backend and Metro logs when the selected variant uses those
|
|
233
|
+
services. For Maestro's result and screenshot rules, read [UI journeys](ui-journeys.md#evidence).
|
|
234
|
+
Preserve the failed checkpoint's evidence before cleanup. A release build with embedded
|
|
235
|
+
assets does not require Metro by default.
|
|
228
236
|
|
|
229
237
|
## Owned development session
|
|
230
238
|
|
|
@@ -379,8 +387,9 @@ For setup proof only. Record:
|
|
|
379
387
|
- Each stage's exit status and duration.
|
|
380
388
|
- The APK's SHA-256 (`shasum -a 256` or `sha256sum`) and its per-ABI `lib/` entries
|
|
381
389
|
(`unzip -l`), checked on the APK itself rather than inferred from Gradle flags.
|
|
382
|
-
- The journey's `PASS` markers
|
|
383
|
-
|
|
390
|
+
- The journey's per-scenario results (`PASS` markers or the journey tool's report) and
|
|
391
|
+
cleanup results: database dropped, emulator exited, container removed, no listeners
|
|
392
|
+
left on the backend or Metro ports.
|
|
384
393
|
- Earlier failed or incomplete attempts. A later pass does not erase them.
|
|
385
394
|
|
|
386
395
|
State what the run does not prove: tablet, accessibility (TalkBack), physical devices,
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# UI journey tool: Maestro
|
|
2
|
+
|
|
3
|
+
Maestro writes a single-device UI flow as YAML and records per-step failure evidence. The
|
|
4
|
+
skill references it; it does not bundle or install it, and adding it is the repo's decision.
|
|
5
|
+
|
|
6
|
+
## When to use it
|
|
7
|
+
|
|
8
|
+
| Situation | Recommendation |
|
|
9
|
+
| --- | --- |
|
|
10
|
+
| Repo has no UI journey tool | Offer Maestro for new single-device journeys. It replaces hand-written adb/UiAutomator launch, poll, capture and assertion code. |
|
|
11
|
+
| Repo has a working harness | Reuse it when sufficient. Add Maestro only for a concrete benefit with repo authorization; an installed CLI alone is not a reason to add or migrate journeys. |
|
|
12
|
+
| Coordinated multi-device case | Keep the custom harness. Maestro sharding runs independent flows, and a shard cannot depend on another. |
|
|
13
|
+
|
|
14
|
+
Maestro changes the UI journey driver, not the [work loop or validation ladder](../SKILL.md#work-loop).
|
|
15
|
+
Use focused flows at the applicable L0/L1 check, candidate-APK journeys at L2, and assigned
|
|
16
|
+
acceptance journeys at L3. Source tests and required backend/native checks keep their roles.
|
|
17
|
+
|
|
18
|
+
## Starting state and permissions
|
|
19
|
+
|
|
20
|
+
Declare each flow's intended starting app state and permissions in its YAML or owned
|
|
21
|
+
fixture setup, and check included subflows and hooks for resets:
|
|
22
|
+
|
|
23
|
+
- For a warm-runtime check, keep the running app or use `launchApp: {stopApp: false}`
|
|
24
|
+
to foreground it. Plain `launchApp` stops and relaunches the app by default, so it
|
|
25
|
+
cannot preserve the process whose state the check needs.
|
|
26
|
+
- For restart, upgrade or fresh-install checks, use the checkpoint's required setup.
|
|
27
|
+
`clearState: true` and `clearState` clear Android app data; they do not install the
|
|
28
|
+
candidate APK or prove a fresh installation or upgrade. Clear only authorized disposable
|
|
29
|
+
state. If a reset removes debug endpoint preferences, restore them through the existing
|
|
30
|
+
owner and verify the app's actual code source before continuing.
|
|
31
|
+
- Set permissions for permission-sensitive journeys. `launchApp` allows all permissions
|
|
32
|
+
by default; use the required denied or unset state to exercise denial or prompts.
|
|
33
|
+
An auto-granted run does not prove the permission-request journey.
|
|
34
|
+
|
|
35
|
+
## Device ownership
|
|
36
|
+
|
|
37
|
+
Run against the emulator the session owns and name it with `maestro --device <serial>`.
|
|
38
|
+
Use the status handling under [Evidence](#evidence) inside the existing journey owner.
|
|
39
|
+
|
|
40
|
+
Each session installs and starts Maestro's driver packages (`dev.mobile.maestro`,
|
|
41
|
+
`dev.mobile.maestro.test`) through `am instrument`. That cost lands on the session's first
|
|
42
|
+
flow, not on every step, so run every flow of the case in one session instead of one session
|
|
43
|
+
per flow.
|
|
44
|
+
|
|
45
|
+
Keep mutating checks exclusive on that device, including existing harness and Maestro
|
|
46
|
+
runs. Reuse the existing runtime owner; Maestro is not a second emulator, backend or
|
|
47
|
+
cleanup owner.
|
|
48
|
+
|
|
49
|
+
If setup started a `setupUiAutomator` uiautomator2 process, have its owner verify ownership,
|
|
50
|
+
stop it and wait for it to exit before starting Maestro; a live setup driver can block
|
|
51
|
+
Maestro driver readiness. Restart that setup driver only after Maestro has fully exited,
|
|
52
|
+
if it is still needed. Never stop a peer's process; coordinate with its owner instead.
|
|
53
|
+
|
|
54
|
+
When comparing a custom journey adapter with Maestro, check scroll direction and prove
|
|
55
|
+
offscreen targets with the [scroll-direction gotcha](gotchas.md#a-scroll-adapter-moves-away-from-the-target).
|
|
56
|
+
|
|
57
|
+
## Evidence
|
|
58
|
+
|
|
59
|
+
Record the selected flows, executed assertions, run exit status, app/source identity and
|
|
60
|
+
cleanup outcome. Accept the harness's `PASS` markers or Maestro's per-flow results/JUnit
|
|
61
|
+
report as scenario results; a zero exit with no selected flows is not a pass. For JUnit,
|
|
62
|
+
use `--format junit --output <owned-report-path>`; the report is separate from
|
|
63
|
+
`--test-output-dir`. Capture successful checkpoints with `takeScreenshot` when the
|
|
64
|
+
assignment requires visual evidence; normal passing flows have no automatic screenshots.
|
|
65
|
+
|
|
66
|
+
`--test-output-dir` receives one session folder holding one self-contained folder per flow:
|
|
67
|
+
`manifest.json`, `commands.json` (one entry per executed step), the failing step's
|
|
68
|
+
`screenshots/` image and `screen-hierarchy/` JSON, and `logs/device-logcat.txt`. Convert that
|
|
69
|
+
into the [device failure record](diagnosis.md#record-the-failed-device-case) instead of
|
|
70
|
+
adding the journey helper:
|
|
71
|
+
|
|
72
|
+
Give each case/attempt a new owned `LOG_DIR`, shared with its existing launcher so its
|
|
73
|
+
partial-record fallback looks in the same directory. Retain earlier attempts separately;
|
|
74
|
+
reusing a runtime does not mean reusing a failure-record destination. Confirm the pinned
|
|
75
|
+
Maestro supports these flags and output layout before using the example.
|
|
76
|
+
|
|
77
|
+
Run this body as the existing owner's journey command. Guard both commands so `set -e`
|
|
78
|
+
cannot skip conversion or cleanup after a failure. The owner retains its bounded cleanup
|
|
79
|
+
after the journey exits; this is not a new launcher or an acceptance checker.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
maestro_status=0
|
|
83
|
+
maestro --device "$ANDROID_SERIAL" test --test-output-dir "$LOG_DIR/maestro" \
|
|
84
|
+
--format junit --output "$LOG_DIR/report.xml" ./maestro-flows || maestro_status=$?
|
|
85
|
+
conversion_status=0
|
|
86
|
+
python3 <skill-base-dir>/scripts/maestro_failure.py --maestro-dir "$LOG_DIR/maestro" \
|
|
87
|
+
--evidence-dir "$LOG_DIR" --case "$DEVICE_CASE" || conversion_status=$?
|
|
88
|
+
printf 'maestro_exit=%s conversion_exit=%s\n' "$maestro_status" "$conversion_status"
|
|
89
|
+
if [ "$maestro_status" -ne 0 ]; then
|
|
90
|
+
exit "$maestro_status"
|
|
91
|
+
fi
|
|
92
|
+
exit "$conversion_status"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Convert this invocation's finalized output before teardown. If startup or driver failure
|
|
96
|
+
left no usable failed-step output, retain raw CLI logs and the original status and use
|
|
97
|
+
the existing owner's partial-record fallback before cleanup. Conversion errors are
|
|
98
|
+
evidence-processing failures, not replacements for the original test failure.
|
|
99
|
+
|
|
100
|
+
It takes the session folder or one flow folder, writes `failure.json` into `--evidence-dir`
|
|
101
|
+
(the flow folder by default) and prints the one `NATIVE_FAILURE` line for the first failed
|
|
102
|
+
step. A passing flow writes nothing, and an existing record is never replaced. The artifact
|
|
103
|
+
fields name the Maestro files (`screenshots/…`, `screen-hierarchy/…`, `logs/…`) rather than
|
|
104
|
+
copying them into the adb names, so the failing frame and its hierarchy stay paired.
|
|
105
|
+
|
|
106
|
+
Converter exit zero means processing completed, not that tests passed: a recorded failure
|
|
107
|
+
also returns zero, and empty or incomplete step output may produce no record. Establish
|
|
108
|
+
success from the executed assertions and per-flow results above, never from the converter
|
|
109
|
+
or absence of `NATIVE_FAILURE`.
|
|
110
|
+
|
|
111
|
+
## Agents
|
|
112
|
+
|
|
113
|
+
`maestro mcp` gives an agent `list_devices`, `inspect_screen`, `take_screenshot` and `run`.
|
|
114
|
+
Explore the screen and draft a repeatable YAML flow with it. Proof comes from executing
|
|
115
|
+
the flow against the identified candidate with assertions that establish the claimed
|
|
116
|
+
behavior and retained results. Committing YAML or finding selectors in MCP does not prove
|
|
117
|
+
the journey passed.
|
|
118
|
+
|
|
119
|
+
## Limits
|
|
120
|
+
|
|
121
|
+
- Coordinated multi-device cases, exact data or payment proofs, and process-death checks stay
|
|
122
|
+
on custom harness code.
|
|
123
|
+
- Authentication codes still come from the harness's disposable database, passed into a flow
|
|
124
|
+
with `maestro test -e` or resolved by its `runScript`; do not add a second fixture path.
|
|
125
|
+
|
|
126
|
+
## Sources
|
|
127
|
+
|
|
128
|
+
- [Launch state and permissions](https://docs.maestro.dev/reference/commands-available/launchapp)
|
|
129
|
+
- [Android state clearing](https://docs.maestro.dev/reference/commands-available/clearstate)
|
|
130
|
+
- [Reports and artifacts](https://docs.maestro.dev/maestro-flows/workspace-management/test-reports-and-artifacts)
|
|
@@ -17,6 +17,13 @@
|
|
|
17
17
|
# POSTGRES_IMAGE (postgres:17) DB_URL_VAR (SHARD_ADMIN_URL) DB_NAME_VAR (SHARD_DB_NAME)
|
|
18
18
|
# LOG_DIR (.cache/logs) BOUNDED_SECONDS (30) BOOT_ATTEMPTS (60) KILL_GRACE_SECONDS (10)
|
|
19
19
|
# JOURNEY_SECONDS (0 = no deadline; caller chooses a positive whole-journey limit)
|
|
20
|
+
# DEVICE_CASE (unset) LOGCAT_LINES (400)
|
|
21
|
+
#
|
|
22
|
+
# A journey that fails and left no record of its own gets one before teardown:
|
|
23
|
+
# $LOG_DIR/screenshot.png, $LOG_DIR/ui-dump.xml, $LOG_DIR/logcat-tail.txt and a
|
|
24
|
+
# partial $LOG_DIR/failure.json with step null, plus one `NATIVE_FAILURE {json}`
|
|
25
|
+
# line. A record the journey's own helper wrote is kept, and capture problems go
|
|
26
|
+
# to that record's cleanupErrors instead of replacing the original failure.
|
|
20
27
|
#
|
|
21
28
|
# ADB_SERVER_SOCKET / ANDROID_ADB_SERVER_PORT are inherited unchanged. The caller
|
|
22
29
|
# selects a private ADB endpoint before entry; this script never stops that server.
|
|
@@ -55,6 +62,7 @@ BOUNDED_SECONDS="${BOUNDED_SECONDS:-30}"
|
|
|
55
62
|
BOOT_ATTEMPTS="${BOOT_ATTEMPTS:-60}"
|
|
56
63
|
KILL_GRACE_SECONDS="${KILL_GRACE_SECONDS:-10}"
|
|
57
64
|
JOURNEY_SECONDS="${JOURNEY_SECONDS:-0}"
|
|
65
|
+
LOGCAT_LINES="${LOGCAT_LINES:-400}"
|
|
58
66
|
|
|
59
67
|
# Reject invalid limits before creating any owned resources.
|
|
60
68
|
python3 -c 'import math, sys
|
|
@@ -62,8 +70,9 @@ for value in sys.argv[1:4]:
|
|
|
62
70
|
number = float(value)
|
|
63
71
|
if not math.isfinite(number) or number < 0: sys.exit("limits must be finite and non-negative")
|
|
64
72
|
if float(sys.argv[1]) == 0: sys.exit("BOUNDED_SECONDS must be positive")
|
|
65
|
-
if not sys.argv[4].isdigit() or int(sys.argv[4]) < 1: sys.exit("BOOT_ATTEMPTS must be a positive integer")
|
|
66
|
-
|
|
73
|
+
if not sys.argv[4].isdigit() or int(sys.argv[4]) < 1: sys.exit("BOOT_ATTEMPTS must be a positive integer")
|
|
74
|
+
if not sys.argv[5].isdigit() or int(sys.argv[5]) < 1: sys.exit("LOGCAT_LINES must be a positive integer")' \
|
|
75
|
+
"$BOUNDED_SECONDS" "$KILL_GRACE_SECONDS" "$JOURNEY_SECONDS" "$BOOT_ATTEMPTS" "$LOGCAT_LINES" || exit 2
|
|
67
76
|
for variable in "$DB_URL_VAR" "$DB_NAME_VAR"; do
|
|
68
77
|
if ! [[ "$variable" =~ ^[a-zA-Z_][a-zA-Z_0-9]*$ ]]; then
|
|
69
78
|
echo "invalid database environment variable: $variable" >&2
|
|
@@ -88,6 +97,8 @@ db_ready=""
|
|
|
88
97
|
container_started=""
|
|
89
98
|
launch_in_progress=0
|
|
90
99
|
pending_signal=0
|
|
100
|
+
journey_started=0
|
|
101
|
+
cleanup_errors=()
|
|
91
102
|
handle_signal() {
|
|
92
103
|
pending_signal="$1"
|
|
93
104
|
# Do not abandon a spawned supervisor before its PID has been recorded.
|
|
@@ -174,8 +185,86 @@ managed() {
|
|
|
174
185
|
|
|
175
186
|
bounded() { managed "$BOUNDED_SECONDS" "$@"; }
|
|
176
187
|
|
|
188
|
+
capture_artifact() {
|
|
189
|
+
# capture_artifact <file-name> <adb args...>; an empty capture is not evidence.
|
|
190
|
+
local name="$1" file="$LOG_DIR/$1"
|
|
191
|
+
shift
|
|
192
|
+
if bounded adb -s "$ANDROID_EMULATOR_SERIAL" "$@" > "$file" 2>/dev/null && test -s "$file"; then
|
|
193
|
+
return 0
|
|
194
|
+
fi
|
|
195
|
+
cleanup_errors+=("$name: adb -s $ANDROID_EMULATOR_SERIAL $* produced no evidence")
|
|
196
|
+
rm -f "$file"
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
capture_ui_dump() {
|
|
200
|
+
local remote=/sdcard/android-failure.xml file="$LOG_DIR/ui-dump.xml"
|
|
201
|
+
if bounded adb -s "$ANDROID_EMULATOR_SERIAL" shell uiautomator dump "$remote" >/dev/null 2>&1 \
|
|
202
|
+
&& bounded adb -s "$ANDROID_EMULATOR_SERIAL" exec-out cat "$remote" > "$file" 2>/dev/null \
|
|
203
|
+
&& test -s "$file"; then
|
|
204
|
+
return 0
|
|
205
|
+
fi
|
|
206
|
+
cleanup_errors+=("ui-dump.xml: adb -s $ANDROID_EMULATOR_SERIAL shell uiautomator dump failed")
|
|
207
|
+
rm -f "$file"
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
capture_logcat() {
|
|
211
|
+
# An empty tail is still a capture; a failed one is not.
|
|
212
|
+
local file="$LOG_DIR/logcat-tail.txt"
|
|
213
|
+
if bounded adb -s "$ANDROID_EMULATOR_SERIAL" logcat -d -t "$LOGCAT_LINES" > "$file" 2>/dev/null; then
|
|
214
|
+
return 0
|
|
215
|
+
fi
|
|
216
|
+
cleanup_errors+=("logcat-tail.txt: adb -s $ANDROID_EMULATOR_SERIAL logcat -d -t $LOGCAT_LINES failed")
|
|
217
|
+
rm -f "$file"
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
write_failure_record() {
|
|
221
|
+
local error
|
|
222
|
+
case "$1" in
|
|
223
|
+
124) error="journey exceeded ${JOURNEY_SECONDS}s (status 124): ${journey_cmd[*]}" ;;
|
|
224
|
+
*) error="journey exited with status $1: ${journey_cmd[*]}" ;;
|
|
225
|
+
esac
|
|
226
|
+
# One writer for the record and its line, so their shapes cannot drift.
|
|
227
|
+
python3 - "$LOG_DIR" "${DEVICE_CASE:-}" "$error" \
|
|
228
|
+
${cleanup_errors[@]+"${cleanup_errors[@]}"} >&3 <<'PY' || {
|
|
229
|
+
import json, os, sys
|
|
230
|
+
evidence, case, error = os.path.abspath(sys.argv[1]), sys.argv[2] or None, sys.argv[3]
|
|
231
|
+
record = {"case": case, "step": None, "error": error}
|
|
232
|
+
for key, name in (("screenshot", "screenshot.png"), ("uiDump", "ui-dump.xml"), ("logcatTail", "logcat-tail.txt")):
|
|
233
|
+
path = os.path.join(evidence, name)
|
|
234
|
+
record[key] = name if os.path.isfile(path) and os.path.getsize(path) else None
|
|
235
|
+
record["evidenceDir"] = evidence
|
|
236
|
+
record["cleanupErrors"] = sys.argv[4:]
|
|
237
|
+
path = os.path.join(evidence, "failure.json")
|
|
238
|
+
temporary = path + ".tmp"
|
|
239
|
+
with open(temporary, "w", encoding="utf-8") as stream:
|
|
240
|
+
stream.write(json.dumps(record, indent=2) + "\n")
|
|
241
|
+
os.replace(temporary, path)
|
|
242
|
+
print("NATIVE_FAILURE " + json.dumps(
|
|
243
|
+
{key: record[key] for key in ("case", "step", "error", "evidenceDir")}, separators=(",", ":")))
|
|
244
|
+
PY
|
|
245
|
+
echo "failure record not written; keep the evidence in $LOG_DIR" >&3
|
|
246
|
+
return 0
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
capture_failure() {
|
|
251
|
+
# Called before teardown: the emulator and app are still in the failing state.
|
|
252
|
+
local status="$1" cancelled="$2"
|
|
253
|
+
test "$status" -ne 0 || return 0
|
|
254
|
+
test "$journey_started" -eq 1 || return 0
|
|
255
|
+
# A requested stop is not a failed case.
|
|
256
|
+
test "$cancelled" -eq 0 || return 0
|
|
257
|
+
# The journey's own helper already recorded the failing step; keep that record.
|
|
258
|
+
test ! -e "$LOG_DIR/failure.json" || return 0
|
|
259
|
+
capture_artifact screenshot.png exec-out screencap -p
|
|
260
|
+
capture_ui_dump
|
|
261
|
+
capture_logcat
|
|
262
|
+
write_failure_record "$status"
|
|
263
|
+
}
|
|
264
|
+
|
|
177
265
|
cleanup() {
|
|
178
266
|
original_status=$?
|
|
267
|
+
cancelled_signal="$pending_signal"
|
|
179
268
|
trap - EXIT
|
|
180
269
|
trap '' INT TERM
|
|
181
270
|
pending_signal=0
|
|
@@ -187,6 +276,7 @@ cleanup() {
|
|
|
187
276
|
wait "$active_pid" || :
|
|
188
277
|
active_pid=""
|
|
189
278
|
fi
|
|
279
|
+
capture_failure "$original_status" "$cancelled_signal"
|
|
190
280
|
if test -n "$db_ready"; then
|
|
191
281
|
bounded "${drop_cmd[@]}" || cleanup_status=1
|
|
192
282
|
fi
|
|
@@ -279,4 +369,5 @@ export "$DB_URL_VAR=postgresql://postgres:postgres@127.0.0.1:$db_port/postgres"
|
|
|
279
369
|
export "$DB_NAME_VAR=${DB_NAME_PREFIX}$(date +%s)_$$"
|
|
280
370
|
db_ready=1
|
|
281
371
|
|
|
372
|
+
journey_started=1
|
|
282
373
|
managed "$JOURNEY_SECONDS" "${journey_cmd[@]}"
|