@sun-asterisk/sungen 3.2.30 → 3.2.32
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/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/audit.js +3 -1
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/capability.d.ts.map +1 -1
- package/dist/cli/commands/capability.js +11 -0
- package/dist/cli/commands/capability.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +2 -0
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/setup-mobile-env.d.ts +15 -0
- package/dist/cli/commands/setup-mobile-env.d.ts.map +1 -0
- package/dist/cli/commands/setup-mobile-env.js +65 -0
- package/dist/cli/commands/setup-mobile-env.js.map +1 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/all-contain-assertion.hbs +1 -2
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/all-contain-element.hbs +8 -5
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/count-assertion.hbs +5 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/disabled-with-filter-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/hidden-with-filter-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/list-item-count-assertion.hbs +5 -2
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/state-with-filter-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/visible-filtered-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/partials/await-elements.hbs +12 -0
- package/dist/generators/test-generator/adapters/appium/templates/test-file.hbs +26 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/capture-variable.hbs +7 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/row-scoped-column-assertion.hbs +23 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +4 -4
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/default.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/id.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +4 -4
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/url-assertion.hbs +13 -2
- package/dist/generators/test-generator/adapters/playwright/templates/test-file.hbs +14 -0
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +56 -13
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/generators/test-generator/diagnostics.d.ts +39 -2
- package/dist/generators/test-generator/diagnostics.d.ts.map +1 -1
- package/dist/generators/test-generator/diagnostics.js +38 -1
- package/dist/generators/test-generator/diagnostics.js.map +1 -1
- package/dist/generators/test-generator/patterns/index.d.ts +1 -1
- package/dist/generators/test-generator/patterns/index.js +1 -1
- package/dist/generators/test-generator/step-mapper.d.ts +35 -0
- package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.js +103 -4
- package/dist/generators/test-generator/step-mapper.js.map +1 -1
- package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
- package/dist/generators/test-generator/template-engine.js +18 -0
- package/dist/generators/test-generator/template-engine.js.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.d.ts +47 -0
- package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.js +17 -3
- package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
- package/dist/harness/audit.d.ts.map +1 -1
- package/dist/harness/audit.js +23 -4
- package/dist/harness/audit.js.map +1 -1
- package/dist/harness/next-step.d.ts.map +1 -1
- package/dist/harness/next-step.js +2 -1
- package/dist/harness/next-step.js.map +1 -1
- package/dist/harness/parse.d.ts.map +1 -1
- package/dist/harness/parse.js +42 -7
- package/dist/harness/parse.js.map +1 -1
- package/dist/harness/spec-branches.d.ts +7 -2
- package/dist/harness/spec-branches.d.ts.map +1 -1
- package/dist/harness/spec-branches.js +62 -11
- package/dist/harness/spec-branches.js.map +1 -1
- package/dist/harness/spec-coverage.d.ts +16 -0
- package/dist/harness/spec-coverage.d.ts.map +1 -1
- package/dist/harness/spec-coverage.js +119 -10
- package/dist/harness/spec-coverage.js.map +1 -1
- package/dist/orchestrator/appium-environment-setup.d.ts +24 -0
- package/dist/orchestrator/appium-environment-setup.d.ts.map +1 -0
- package/dist/orchestrator/appium-environment-setup.js +157 -0
- package/dist/orchestrator/appium-environment-setup.js.map +1 -0
- package/dist/orchestrator/mobile-runtime-scaffolder.d.ts +2 -0
- package/dist/orchestrator/mobile-runtime-scaffolder.d.ts.map +1 -1
- package/dist/orchestrator/mobile-runtime-scaffolder.js.map +1 -1
- package/dist/orchestrator/project-initializer.d.ts +1 -0
- package/dist/orchestrator/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +14 -0
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +5 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +55 -2
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +20 -5
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +3 -3
- package/dist/orchestrator/templates/appium-environment/README.md +83 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/install-appium.ps1 +44 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/install-appium.sh +50 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/lib.ps1 +108 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/lib.sh +141 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/verify-appium.ps1 +39 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/verify-appium.sh +43 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/verify-node.ps1 +13 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/verify-node.sh +16 -0
- package/dist/orchestrator/templates/appium-environment/scripts/macos/install-android.sh +110 -0
- package/dist/orchestrator/templates/appium-environment/scripts/macos/install-ios.sh +32 -0
- package/dist/orchestrator/templates/appium-environment/scripts/macos/verify-android.sh +32 -0
- package/dist/orchestrator/templates/appium-environment/scripts/macos/verify-ios.sh +15 -0
- package/dist/orchestrator/templates/appium-environment/scripts/windows/install-android.ps1 +153 -0
- package/dist/orchestrator/templates/appium-environment/scripts/windows/verify-android.ps1 +26 -0
- package/dist/orchestrator/templates/appium-environment/setup.ps1 +58 -0
- package/dist/orchestrator/templates/appium-environment/setup.sh +88 -0
- package/dist/orchestrator/templates/specs-checked-state.d.ts +1 -1
- package/dist/orchestrator/templates/specs-checked-state.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-checked-state.js +26 -3
- package/dist/orchestrator/templates/specs-checked-state.js.map +1 -1
- package/dist/orchestrator/templates/specs-checked-state.ts +31 -6
- package/dist/orchestrator/templates/specs-mock.d.ts +2 -1
- package/dist/orchestrator/templates/specs-mock.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-mock.js +10 -3
- package/dist/orchestrator/templates/specs-mock.js.map +1 -1
- package/dist/orchestrator/templates/specs-mock.ts +12 -4
- package/package.json +3 -3
- package/src/cli/commands/audit.ts +2 -1
- package/src/cli/commands/capability.ts +12 -1
- package/src/cli/commands/init.ts +3 -0
- package/src/cli/commands/setup-mobile-env.ts +64 -0
- package/src/cli/index.ts +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/all-contain-assertion.hbs +1 -2
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/all-contain-element.hbs +8 -5
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/count-assertion.hbs +5 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/disabled-with-filter-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/hidden-with-filter-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/list-item-count-assertion.hbs +5 -2
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/state-with-filter-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/visible-filtered-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/partials/await-elements.hbs +12 -0
- package/src/generators/test-generator/adapters/appium/templates/test-file.hbs +26 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/capture-variable.hbs +7 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/row-scoped-column-assertion.hbs +23 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +4 -4
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/default.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/id.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +4 -4
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/url-assertion.hbs +13 -2
- package/src/generators/test-generator/adapters/playwright/templates/test-file.hbs +14 -0
- package/src/generators/test-generator/code-generator.ts +66 -17
- package/src/generators/test-generator/diagnostics.ts +39 -2
- package/src/generators/test-generator/patterns/index.ts +1 -1
- package/src/generators/test-generator/step-mapper.ts +106 -5
- package/src/generators/test-generator/template-engine.ts +18 -0
- package/src/generators/test-generator/utils/selector-resolver.ts +18 -3
- package/src/harness/audit.ts +24 -5
- package/src/harness/next-step.ts +2 -1
- package/src/harness/parse.ts +42 -7
- package/src/harness/spec-branches.ts +67 -12
- package/src/harness/spec-coverage.ts +121 -11
- package/src/orchestrator/appium-environment-setup.ts +127 -0
- package/src/orchestrator/mobile-runtime-scaffolder.ts +2 -0
- package/src/orchestrator/project-initializer.ts +15 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +5 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +55 -2
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +20 -5
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +3 -3
- package/src/orchestrator/templates/appium-environment/README.md +83 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/install-appium.ps1 +44 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/install-appium.sh +50 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/lib.ps1 +108 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/lib.sh +141 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/verify-appium.ps1 +39 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/verify-appium.sh +43 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/verify-node.ps1 +13 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/verify-node.sh +16 -0
- package/src/orchestrator/templates/appium-environment/scripts/macos/install-android.sh +110 -0
- package/src/orchestrator/templates/appium-environment/scripts/macos/install-ios.sh +32 -0
- package/src/orchestrator/templates/appium-environment/scripts/macos/verify-android.sh +32 -0
- package/src/orchestrator/templates/appium-environment/scripts/macos/verify-ios.sh +15 -0
- package/src/orchestrator/templates/appium-environment/scripts/windows/install-android.ps1 +153 -0
- package/src/orchestrator/templates/appium-environment/scripts/windows/verify-android.ps1 +26 -0
- package/src/orchestrator/templates/appium-environment/setup.ps1 +58 -0
- package/src/orchestrator/templates/appium-environment/setup.sh +88 -0
- package/src/orchestrator/templates/specs-checked-state.ts +31 -6
- package/src/orchestrator/templates/specs-mock.ts +12 -4
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# appium-environment/ — one command to a working Appium machine
|
|
2
|
+
|
|
3
|
+
Emitted by `sungen init --mobile` (or `sungen capability add mobile`), which also runs it once.
|
|
4
|
+
It provisions everything **below** the Appium layer — the part QA used to do by hand — and ends with
|
|
5
|
+
`appium driver doctor`. Every step is **check → install → verify**: a satisfied step is skipped, so the
|
|
6
|
+
bundle is safe to re-run any time (a new machine, a teammate's clone, after an OS update).
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
sungen setup-mobile-env # any OS — emits this folder if missing, then runs it
|
|
10
|
+
./appium-environment/setup.sh # macOS, directly
|
|
11
|
+
powershell -NoProfile -ExecutionPolicy Bypass -File .\appium-environment\setup.ps1 # Windows, directly
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
| Flag (macOS) | Flag (Windows) | Effect |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| `--check` | `-Check` | report what is missing, install nothing (side-effect free) |
|
|
17
|
+
| `--skip-android` | `-SkipAndroid` | skip JDK / Android SDK / uiautomator2 |
|
|
18
|
+
| `--skip-ios` | — | skip Xcode CLT / applesimutils / xcuitest (iOS needs a Mac) |
|
|
19
|
+
| `--no-profile` | `-NoProfile` | do not write env vars to your shell profile / User environment |
|
|
20
|
+
|
|
21
|
+
Exit code: `0` ready (or only warnings) · `1` something failed · `2` unsupported OS / bad flag.
|
|
22
|
+
`sungen init` treats a non-zero exit as a warning — the project scaffold is already complete.
|
|
23
|
+
|
|
24
|
+
## What it installs (and what it only checks)
|
|
25
|
+
|
|
26
|
+
| Step | macOS (`setup.sh`) | Windows (`setup.ps1`) |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| Node.js ≥ 18 | check only (sungen needs it already) | check only |
|
|
29
|
+
| Package manager | Homebrew — installed if missing | winget — check only (App Installer, Microsoft Store) |
|
|
30
|
+
| JDK ≥ 17 | `brew install openjdk@21` (an existing JDK ≥ 17 is kept) | `winget install Microsoft.OpenJDK.21` (existing JDK ≥ 17 kept) |
|
|
31
|
+
| Android SDK root | existing `ANDROID_HOME` / Android Studio SDK / `brew install --cask android-commandlinetools` | existing / `%LOCALAPPDATA%\Android\Sdk` / Google's command-line tools zip |
|
|
32
|
+
| `platform-tools` (adb) + `emulator` | `sdkmanager` | `sdkmanager` |
|
|
33
|
+
| Env vars | marked block in `~/.zshrc` (or `~/.bash_profile`) | User environment (`ANDROID_HOME`, `ANDROID_SDK_ROOT`, `JAVA_HOME`, `Path`) |
|
|
34
|
+
| Android Studio | **check + hint only** (optional — AVD manager / on-screen emulator) | same |
|
|
35
|
+
| Xcode Command Line Tools | `xcode-select --install` (Apple's dialog — finish it, re-run) | — |
|
|
36
|
+
| Xcode.app | **check + hint only** (App Store; required for iOS) | — |
|
|
37
|
+
| Xcode license | `sudo xcodebuild -license accept` | — |
|
|
38
|
+
| `applesimutils` | `brew tap wix/brew && brew install applesimutils` | — |
|
|
39
|
+
| Appium | **project-local** (`node_modules/.bin/appium`, from `@sungen/driver-mobile`) — never global | same |
|
|
40
|
+
| Appium drivers | `uiautomator2` + `xcuitest` into `~/.appium` | `uiautomator2` |
|
|
41
|
+
| Health | `adb`, env vars, devices, Simulators, `appium driver doctor` | `adb`, env vars, devices, `appium driver doctor` |
|
|
42
|
+
|
|
43
|
+
Nothing is installed globally with npm. GUI apps (Xcode.app, Android Studio) are never installed for you —
|
|
44
|
+
the step tells you exactly where to get them and re-running the script picks up from there.
|
|
45
|
+
|
|
46
|
+
## The env-var block
|
|
47
|
+
|
|
48
|
+
macOS: the lines live between `# >>> sungen mobile env >>>` and `# <<< sungen mobile env <<<` in your
|
|
49
|
+
shell profile; re-running rewrites only that block, and it is skipped entirely when your shell already
|
|
50
|
+
exports matching values. To undo, delete the block. Windows: plain User-scope variables — undo in
|
|
51
|
+
*System Properties → Environment Variables*.
|
|
52
|
+
|
|
53
|
+
**Open a new terminal after the first run** so `PATH` / `JAVA_HOME` / `ANDROID_HOME` apply.
|
|
54
|
+
|
|
55
|
+
## Known limitations
|
|
56
|
+
|
|
57
|
+
- **Linux** is not provisioned (the entry point prints a notice and exits 0). Android on Linux: install a
|
|
58
|
+
JDK ≥ 17 and the Android command-line tools with your package manager, export the same variables, then
|
|
59
|
+
`sungen capability add mobile` for the project-local Appium + `uiautomator2`.
|
|
60
|
+
- **`sungen update` does not refresh this folder.** To pick up a newer bundle, delete `appium-environment/`
|
|
61
|
+
and run `sungen capability add mobile` again (all other files are skipped because they already exist).
|
|
62
|
+
- Windows downloads Google's command-line tools by a **pinned build number** (`$CmdlineToolsBuild` at the
|
|
63
|
+
top of `scripts/windows/install-android.ps1`). If the download 404s, bump it from
|
|
64
|
+
https://developer.android.com/studio#command-line-tools-only.
|
|
65
|
+
- No AVD / Simulator is created and no phone is paired — plug in a device (USB debugging on) or start an
|
|
66
|
+
emulator before `/sungen:run-test`.
|
|
67
|
+
|
|
68
|
+
## Manual fallback (the steps this bundle automates)
|
|
69
|
+
|
|
70
|
+
- Android: https://sungen.sun-asterisk.vn/docs/mobile/appium-setup-android
|
|
71
|
+
- iOS: https://sungen.sun-asterisk.vn/docs/mobile/appium-setup-ios
|
|
72
|
+
- Overview + troubleshooting: https://sungen.sun-asterisk.vn/docs/mobile/appium-setup ·
|
|
73
|
+
https://sungen.sun-asterisk.vn/docs/mobile/mobile-troubleshooting
|
|
74
|
+
|
|
75
|
+
## Layout
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
appium-environment/
|
|
79
|
+
├── setup.sh · setup.ps1 entry points (OS check, flags, step order, summary)
|
|
80
|
+
├── scripts/common/ lib.{sh,ps1} step runner · verify-node · install-appium · verify-appium
|
|
81
|
+
├── scripts/macos/ install-android · verify-android · install-ios · verify-ios
|
|
82
|
+
└── scripts/windows/ install-android · verify-android
|
|
83
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# install-appium.ps1 - Appium stays PROJECT-LOCAL (a devDependency pulled in by @sungen/driver-mobile),
|
|
2
|
+
# never `npm i -g appium`; the platform driver (uiautomator2) lives in ~\.appium and is installed
|
|
3
|
+
# through that local CLI. Mirrors what `sungen init --mobile` does in TypeScript, so this script also
|
|
4
|
+
# works on a fresh clone where a teammate ran init.
|
|
5
|
+
if (-not $script:SungenEnvLibLoaded) { . (Join-Path $PSScriptRoot 'lib.ps1') }
|
|
6
|
+
|
|
7
|
+
$AppiumCmd = $script:SungenEnvAppiumCmd
|
|
8
|
+
|
|
9
|
+
function Get-AppiumVersion { if (Test-Path $AppiumCmd) { ((& $AppiumCmd -v) 2>$null | Select-Object -First 1) } }
|
|
10
|
+
Invoke-Step -Title 'Appium (project-local)' -Check {
|
|
11
|
+
if (Test-Path $AppiumCmd) { "appium $(Get-AppiumVersion) (project-local: node_modules\.bin\appium)" }
|
|
12
|
+
} -Install {
|
|
13
|
+
# Only re-install when the project already declares the mobile driver - otherwise the fix is
|
|
14
|
+
# `sungen capability add mobile`, a consent-gated sungen command, not this script's call.
|
|
15
|
+
$pkg = Join-Path $script:ProjectDir 'package.json'
|
|
16
|
+
if ((Test-Path $pkg) -and ((Get-Content $pkg -Raw) -match '"@sungen/driver-mobile"')) {
|
|
17
|
+
Push-Location $script:ProjectDir; try { Invoke-Native npm @('install') } finally { Pop-Location }
|
|
18
|
+
} else { return 'needs-user' }
|
|
19
|
+
} -Verify {
|
|
20
|
+
if (Test-Path $AppiumCmd) { "appium $(Get-AppiumVersion)" }
|
|
21
|
+
} -Hint "Appium is project-local (never global). Add the mobile driver + runtime with:`n sungen capability add mobile"
|
|
22
|
+
|
|
23
|
+
# --- platform driver -------------------------------------------------------------------------------
|
|
24
|
+
function Get-InstalledDrivers {
|
|
25
|
+
$raw = (& $AppiumCmd driver list --installed --json) 2>$null | Out-String
|
|
26
|
+
if (-not $raw.Trim()) { return $null }
|
|
27
|
+
try { return ($raw | ConvertFrom-Json) } catch { return $null }
|
|
28
|
+
}
|
|
29
|
+
function Get-DriverDetail([string]$Name) {
|
|
30
|
+
$j = Get-InstalledDrivers
|
|
31
|
+
if ($j -and ($j.PSObject.Properties.Name -contains $Name)) { "$Name $($j.$Name.version) in ~\.appium" }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (Test-Path $AppiumCmd) {
|
|
35
|
+
if (-not $script:SungenEnvSkipAndroid) {
|
|
36
|
+
$script:CurrentDriver = 'uiautomator2'
|
|
37
|
+
Invoke-Step -Title "Appium driver: $($script:CurrentDriver)" -Check { Get-DriverDetail $script:CurrentDriver } `
|
|
38
|
+
-Install { Invoke-Native $AppiumCmd @('driver', 'install', $script:CurrentDriver) } `
|
|
39
|
+
-Verify { Get-DriverDetail $script:CurrentDriver } `
|
|
40
|
+
-Hint "retry: npx appium driver install $($script:CurrentDriver)"
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
Write-Warn 'Appium platform driver (uiautomator2)' 'skipped - Appium is not installed in this project yet (see the step above)'
|
|
44
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# install-appium.sh — Appium stays PROJECT-LOCAL (a devDependency pulled in by @sungen/driver-mobile),
|
|
3
|
+
# never `npm i -g appium`; the platform drivers (uiautomator2 / xcuitest) live in ~/.appium and are
|
|
4
|
+
# installed through that local CLI. Mirrors what `sungen init --mobile` does in TypeScript, so this
|
|
5
|
+
# script also works on a fresh clone where a teammate ran init.
|
|
6
|
+
[ -n "${SUNGEN_ENV_LIB_LOADED:-}" ] || . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib.sh"
|
|
7
|
+
|
|
8
|
+
APPIUM_BIN=$SUNGEN_ENV_APPIUM_BIN
|
|
9
|
+
|
|
10
|
+
appium_check() {
|
|
11
|
+
[ -x "$APPIUM_BIN" ] || return 1
|
|
12
|
+
printf 'appium %s (project-local: node_modules/.bin/appium)' "$("$APPIUM_BIN" -v 2>/dev/null)"
|
|
13
|
+
}
|
|
14
|
+
appium_install() {
|
|
15
|
+
# Only re-install when the project already declares the mobile driver — otherwise the fix is
|
|
16
|
+
# `sungen capability add mobile`, which is a consent-gated sungen command, not this script's call.
|
|
17
|
+
if grep -q '"@sungen/driver-mobile"' "$PROJECT_DIR/package.json" 2>/dev/null; then
|
|
18
|
+
(cd "$PROJECT_DIR" && npm install)
|
|
19
|
+
else
|
|
20
|
+
return 2
|
|
21
|
+
fi
|
|
22
|
+
}
|
|
23
|
+
appium_verify() {
|
|
24
|
+
appium_check || { printf 'Appium is project-local (never global). Add the mobile driver + runtime with:\n sungen capability add mobile'; return 1; }
|
|
25
|
+
}
|
|
26
|
+
run_step "Appium (project-local)" appium_check appium_install appium_verify
|
|
27
|
+
|
|
28
|
+
# ── platform drivers — one run_step per driver, CURRENT_DRIVER carries the name (bash 3.2: no closures)
|
|
29
|
+
installed_drivers_json() { "$APPIUM_BIN" driver list --installed --json 2>/dev/null; }
|
|
30
|
+
drv_check() {
|
|
31
|
+
local json ver
|
|
32
|
+
json=$(installed_drivers_json) || return 1
|
|
33
|
+
printf '%s' "$json" | grep -q "\"$CURRENT_DRIVER\"" || return 1
|
|
34
|
+
ver=$(printf '%s' "$json" | node -e "let s='';process.stdin.on('data',d=>s+=d).on('end',()=>{try{const j=JSON.parse(s);process.stdout.write((j['$CURRENT_DRIVER']||{}).version||'')}catch{}})" 2>/dev/null)
|
|
35
|
+
printf '%s %s in ~/.appium' "$CURRENT_DRIVER" "${ver:-}"
|
|
36
|
+
}
|
|
37
|
+
drv_install() { "$APPIUM_BIN" driver install "$CURRENT_DRIVER"; }
|
|
38
|
+
drv_verify() { drv_check || { printf 'retry: npx appium driver install %s' "$CURRENT_DRIVER"; return 1; }; }
|
|
39
|
+
|
|
40
|
+
if [ -x "$APPIUM_BIN" ]; then
|
|
41
|
+
APPIUM_DRIVERS="uiautomator2"
|
|
42
|
+
[ "$SUNGEN_ENV_SKIP_ANDROID" = 1 ] && APPIUM_DRIVERS=""
|
|
43
|
+
# xcuitest builds WebDriverAgent with Xcode at run time → only meaningful on a Mac.
|
|
44
|
+
if [ "$(uname -s)" = Darwin ] && [ "$SUNGEN_ENV_SKIP_IOS" != 1 ]; then APPIUM_DRIVERS="$APPIUM_DRIVERS xcuitest"; fi
|
|
45
|
+
for CURRENT_DRIVER in $APPIUM_DRIVERS; do
|
|
46
|
+
run_step "Appium driver: $CURRENT_DRIVER" drv_check drv_install drv_verify
|
|
47
|
+
done
|
|
48
|
+
else
|
|
49
|
+
log_warn "Appium platform drivers (uiautomator2 / xcuitest)" "skipped — Appium is not installed in this project yet (see the step above)"
|
|
50
|
+
fi
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# lib.ps1 - shared step runner for the sungen appium-environment bundle (Windows side).
|
|
2
|
+
# Dot-sourced by setup.ps1 and by every scripts\**\*.ps1; never run directly.
|
|
3
|
+
# Windows PowerShell 5.1 compatible: no ternary, no ??, ASCII only (5.1 reads BOM-less files as ANSI).
|
|
4
|
+
#
|
|
5
|
+
# Step model (see docs: Mobile -> Appium setup -> "One command"):
|
|
6
|
+
# Check -> returns a detail string when already satisfied, $null otherwise (side-effect free)
|
|
7
|
+
# Install -> idempotent; output streams to the console; returns 'needs-user' when the user must finish it
|
|
8
|
+
# Verify -> returns a detail string when satisfied, $null otherwise; -Hint is shown on failure
|
|
9
|
+
|
|
10
|
+
if ($script:SungenEnvLibLoaded) { return }
|
|
11
|
+
$script:SungenEnvLibLoaded = $true
|
|
12
|
+
$ErrorActionPreference = 'Continue'
|
|
13
|
+
|
|
14
|
+
# Flags - set by setup.ps1's parameters, read by every step.
|
|
15
|
+
if ($null -eq $script:SungenEnvCheckOnly) { $script:SungenEnvCheckOnly = $false }
|
|
16
|
+
if ($null -eq $script:SungenEnvSkipAndroid) { $script:SungenEnvSkipAndroid = $false }
|
|
17
|
+
if ($null -eq $script:SungenEnvNoProfile) { $script:SungenEnvNoProfile = $false }
|
|
18
|
+
# Flipped to $true when the User environment actually changed.
|
|
19
|
+
$script:SungenEnvProfileChanged = $false
|
|
20
|
+
|
|
21
|
+
# Paths: <project>\appium-environment\scripts\common\lib.ps1 -> bundle = ..\.., project = bundle\..
|
|
22
|
+
$script:SungenEnvBundleDir = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
|
|
23
|
+
if (-not $script:ProjectDir) { $script:ProjectDir = (Resolve-Path (Join-Path $script:SungenEnvBundleDir '..')).Path }
|
|
24
|
+
# Appium is project-local by design (never `npm i -g appium`) - see install-appium.ps1.
|
|
25
|
+
$script:SungenEnvAppiumCmd = Join-Path $script:ProjectDir 'node_modules\.bin\appium.cmd'
|
|
26
|
+
|
|
27
|
+
$script:CountOk = 0; $script:CountSkip = 0; $script:CountWarn = 0; $script:CountFail = 0
|
|
28
|
+
|
|
29
|
+
function Write-Section([string]$Title) { Write-Host ''; Write-Host $Title -ForegroundColor White }
|
|
30
|
+
function Write-Detail([string]$Text) {
|
|
31
|
+
if (-not $Text) { return }
|
|
32
|
+
foreach ($line in ($Text -split "`r?`n")) { if ($line) { Write-Host " $line" -ForegroundColor DarkGray } }
|
|
33
|
+
}
|
|
34
|
+
function Write-Ok([string]$Title, [string]$Detail) { $script:CountOk++; Write-Host "[ok] $Title" -ForegroundColor Green; Write-Detail $Detail }
|
|
35
|
+
function Write-Skip([string]$Title, [string]$Detail) { $script:CountSkip++; Write-Host "[skip] $Title" -ForegroundColor DarkGray; Write-Detail $Detail }
|
|
36
|
+
function Write-Warn([string]$Title, [string]$Detail) { $script:CountWarn++; Write-Host "[warn] $Title" -ForegroundColor Yellow; Write-Detail $Detail }
|
|
37
|
+
function Write-Fail([string]$Title, [string]$Detail) { $script:CountFail++; Write-Host "[fail] $Title" -ForegroundColor Red; Write-Detail $Detail }
|
|
38
|
+
function Write-Install([string]$Title) { Write-Host "[install] $Title" -ForegroundColor Cyan }
|
|
39
|
+
|
|
40
|
+
# Invoke-Native <exe> <args...> - run a native command, throw on a non-zero exit code.
|
|
41
|
+
function Invoke-Native([string]$File, [string[]]$Arguments) {
|
|
42
|
+
& $File @Arguments
|
|
43
|
+
if ($LASTEXITCODE -ne 0) { throw "$File exited with code $LASTEXITCODE" }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Invoke-Step - an install-type step.
|
|
47
|
+
function Invoke-Step {
|
|
48
|
+
param([string]$Title, [scriptblock]$Check, [scriptblock]$Install, [scriptblock]$Verify, [string]$Hint)
|
|
49
|
+
$found = $null; try { $found = & $Check } catch { $found = $null }
|
|
50
|
+
if ($found) { Write-Skip $Title "$found"; return }
|
|
51
|
+
if ($script:SungenEnvCheckOnly) { Write-Fail $Title 'not installed - run without -Check to install'; return }
|
|
52
|
+
Write-Install $Title
|
|
53
|
+
$needsUser = $false; $err = $null
|
|
54
|
+
try { $r = & $Install; if ("$r" -eq 'needs-user') { $needsUser = $true } } catch { $err = $_.Exception.Message }
|
|
55
|
+
$ok = $null; try { $ok = & $Verify } catch { $ok = $null }
|
|
56
|
+
if ($ok) { Write-Ok $Title "$ok"; return }
|
|
57
|
+
$msg = $Hint; if ($err) { $msg = "$err`n$Hint" }
|
|
58
|
+
if ($needsUser) { Write-Warn $Title $msg } else { Write-Fail $Title $msg }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# Invoke-Check - a verification-only step (ok / fail, or -Warn for informational checks).
|
|
62
|
+
function Invoke-Check {
|
|
63
|
+
param([string]$Title, [scriptblock]$Check, [string]$Hint, [switch]$Warn)
|
|
64
|
+
$out = $null; try { $out = & $Check } catch { $out = $null }
|
|
65
|
+
if ($out) { Write-Ok $Title "$out"; return }
|
|
66
|
+
if ($Warn) { Write-Warn $Title $Hint } else { Write-Fail $Title $Hint }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# Invoke-Guided - GUI installs this bundle never performs itself: skip when present, else warn with the next action.
|
|
70
|
+
function Invoke-Guided {
|
|
71
|
+
param([string]$Title, [scriptblock]$Check, [string]$Guidance)
|
|
72
|
+
$found = $null; try { $found = & $Check } catch { $found = $null }
|
|
73
|
+
if ($found) { Write-Skip $Title "$found" } else { Write-Warn $Title $Guidance }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# --- user environment (the Windows counterpart of the shell-profile block) ------------------------
|
|
77
|
+
# Update-SessionPath - pick up PATH changes made by installers (winget) without a new console.
|
|
78
|
+
function Update-SessionPath {
|
|
79
|
+
$env:Path = [Environment]::GetEnvironmentVariable('Path', 'Machine') + ';' + [Environment]::GetEnvironmentVariable('Path', 'User')
|
|
80
|
+
}
|
|
81
|
+
# Set-UserEnv <name> <value> - user-scope variable (what `setx` does, without its 1024-char truncation). $true when changed.
|
|
82
|
+
function Set-UserEnv([string]$Name, [string]$Value) {
|
|
83
|
+
if ([Environment]::GetEnvironmentVariable($Name, 'User') -eq $Value) { Set-Item -Path "env:$Name" -Value $Value; return $false }
|
|
84
|
+
[Environment]::SetEnvironmentVariable($Name, $Value, 'User')
|
|
85
|
+
Set-Item -Path "env:$Name" -Value $Value
|
|
86
|
+
$script:SungenEnvProfileChanged = $true
|
|
87
|
+
return $true
|
|
88
|
+
}
|
|
89
|
+
# Add-UserPath <dir> - append to the user-scope Path once; also to this session. $true when changed.
|
|
90
|
+
function Add-UserPath([string]$Dir) {
|
|
91
|
+
$current = [Environment]::GetEnvironmentVariable('Path', 'User')
|
|
92
|
+
$parts = @(); if ($current) { $parts = @($current -split ';' | Where-Object { $_ }) }
|
|
93
|
+
if (-not (($env:Path -split ';') -contains $Dir)) { $env:Path = "$env:Path;$Dir" }
|
|
94
|
+
if ($parts -contains $Dir) { return $false }
|
|
95
|
+
[Environment]::SetEnvironmentVariable('Path', (($parts + $Dir) -join ';'), 'User')
|
|
96
|
+
$script:SungenEnvProfileChanged = $true
|
|
97
|
+
return $true
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# Write-Summary - tallies + the "new terminal" notice. Returns 1 when anything failed, else 0.
|
|
101
|
+
function Write-Summary {
|
|
102
|
+
Write-Host ''
|
|
103
|
+
Write-Host "$($script:CountOk) ok, $($script:CountSkip) skipped, $($script:CountWarn) warned, $($script:CountFail) failed" -ForegroundColor White
|
|
104
|
+
if ($script:SungenEnvProfileChanged) { Write-Host '-> Open a NEW terminal so Path / JAVA_HOME / ANDROID_HOME apply.' -ForegroundColor Yellow }
|
|
105
|
+
if ($script:CountWarn -gt 0) { Write-Host ' warnings need a manual step - do it, then re-run this script.' }
|
|
106
|
+
if ($script:CountFail -gt 0) { return 1 }
|
|
107
|
+
return 0
|
|
108
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# lib.sh — shared step runner for the sungen appium-environment bundle (macOS / Linux side).
|
|
3
|
+
# Sourced by setup.sh and by every scripts/**/*.sh; never run directly.
|
|
4
|
+
# Bash 3.2 compatible (the /bin/bash that ships with macOS): no associative arrays, no ${var,,}.
|
|
5
|
+
#
|
|
6
|
+
# Step model (see docs: Mobile → Appium setup → "One command"):
|
|
7
|
+
# check → exit 0 when already satisfied (side-effect free — this is all `--check` runs)
|
|
8
|
+
# install → idempotent; output streams to the terminal; exit 2 = started but needs a user action
|
|
9
|
+
# verify → exit 0 when satisfied; stdout is the detail (ok) or the hint (fail)
|
|
10
|
+
|
|
11
|
+
[ -n "${SUNGEN_ENV_LIB_LOADED:-}" ] && return 0
|
|
12
|
+
SUNGEN_ENV_LIB_LOADED=1
|
|
13
|
+
|
|
14
|
+
# Flags — set by setup.sh's argument parser, read by every step.
|
|
15
|
+
SUNGEN_ENV_CHECK_ONLY=${SUNGEN_ENV_CHECK_ONLY:-0}
|
|
16
|
+
SUNGEN_ENV_SKIP_ANDROID=${SUNGEN_ENV_SKIP_ANDROID:-0}
|
|
17
|
+
SUNGEN_ENV_SKIP_IOS=${SUNGEN_ENV_SKIP_IOS:-0}
|
|
18
|
+
SUNGEN_ENV_NO_PROFILE=${SUNGEN_ENV_NO_PROFILE:-0}
|
|
19
|
+
# Flipped to 1 by write_profile_block when the shell profile actually changed.
|
|
20
|
+
SUNGEN_ENV_PROFILE_CHANGED=0
|
|
21
|
+
|
|
22
|
+
# Paths: <project>/appium-environment/scripts/common/lib.sh → bundle = ../.., project = bundle/..
|
|
23
|
+
SUNGEN_ENV_BUNDLE_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)
|
|
24
|
+
PROJECT_DIR=${PROJECT_DIR:-$(cd "$SUNGEN_ENV_BUNDLE_DIR/.." && pwd)}
|
|
25
|
+
# Appium is project-local by design (never `npm i -g appium`) — see install-appium.sh.
|
|
26
|
+
SUNGEN_ENV_APPIUM_BIN="$PROJECT_DIR/node_modules/.bin/appium"
|
|
27
|
+
|
|
28
|
+
# Environment as it was BEFORE this run — lets the profile step recognise a machine whose own
|
|
29
|
+
# shell profile already exports everything (then no block is written).
|
|
30
|
+
SUNGEN_ENV_INITIAL_ANDROID_HOME=${ANDROID_HOME:-}
|
|
31
|
+
SUNGEN_ENV_INITIAL_JAVA_HOME=${JAVA_HOME:-}
|
|
32
|
+
SUNGEN_ENV_INITIAL_ADB=$(command -v adb 2>/dev/null || true)
|
|
33
|
+
|
|
34
|
+
COUNT_OK=0; COUNT_SKIP=0; COUNT_WARN=0; COUNT_FAIL=0
|
|
35
|
+
|
|
36
|
+
if [ -t 1 ]; then
|
|
37
|
+
C_OK=$'\033[32m'; C_WARN=$'\033[33m'; C_FAIL=$'\033[31m'; C_DIM=$'\033[2m'; C_BOLD=$'\033[1m'; C_RESET=$'\033[0m'
|
|
38
|
+
else
|
|
39
|
+
C_OK=''; C_WARN=''; C_FAIL=''; C_DIM=''; C_BOLD=''; C_RESET=''
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
have() { command -v "$1" >/dev/null 2>&1; }
|
|
43
|
+
# version_major "v22.17.1" → 22
|
|
44
|
+
version_major() { printf '%s' "$1" | sed -E 's/^[^0-9]*([0-9]+).*/\1/'; }
|
|
45
|
+
# java_major_of <java binary> → 21 (empty when not runnable). "1.8.0_x" → 1, so JDK 8 fails a ≥17 gate.
|
|
46
|
+
java_major_of() { "$1" -version 2>&1 | head -n1 | sed -E 's/^[^"]*"([0-9]+)(\.[0-9]+)*.*$/\1/' | grep -E '^[0-9]+$'; }
|
|
47
|
+
# first_line <multi-line string>
|
|
48
|
+
first_line() { printf '%s' "${1%%$'\n'*}"; }
|
|
49
|
+
|
|
50
|
+
section() { printf '\n%s%s%s\n' "$C_BOLD" "$1" "$C_RESET"; }
|
|
51
|
+
_detail() { [ -n "$1" ] && printf ' %s%s%s\n' "$C_DIM" "$1" "$C_RESET"; return 0; }
|
|
52
|
+
_detail_lines() { printf '%s\n' "$1" | while IFS= read -r line; do [ -n "$line" ] && _detail "$line"; done; return 0; }
|
|
53
|
+
|
|
54
|
+
log_ok() { COUNT_OK=$((COUNT_OK+1)); printf '%s✓ ok%s %s\n' "$C_OK" "$C_RESET" "$1"; _detail_lines "${2:-}"; }
|
|
55
|
+
log_skip() { COUNT_SKIP=$((COUNT_SKIP+1)); printf '%s– skip%s %s\n' "$C_DIM" "$C_RESET" "$1"; _detail_lines "${2:-}"; }
|
|
56
|
+
log_warn() { COUNT_WARN=$((COUNT_WARN+1)); printf '%s⚠ warn%s %s\n' "$C_WARN" "$C_RESET" "$1"; _detail_lines "${2:-}"; }
|
|
57
|
+
log_fail() { COUNT_FAIL=$((COUNT_FAIL+1)); printf '%s✗ fail%s %s\n' "$C_FAIL" "$C_RESET" "$1"; _detail_lines "${2:-}"; }
|
|
58
|
+
log_install() { printf '📦 install %s\n' "$1"; }
|
|
59
|
+
|
|
60
|
+
# run_step <title> <check_fn> <install_fn> <verify_fn> — an install-type step.
|
|
61
|
+
run_step() {
|
|
62
|
+
local title=$1 check_fn=$2 install_fn=$3 verify_fn=$4 found out rc
|
|
63
|
+
if found=$("$check_fn" 2>/dev/null); then log_skip "$title" "${found:-already installed}"; return 0; fi
|
|
64
|
+
if [ "$SUNGEN_ENV_CHECK_ONLY" = 1 ]; then log_fail "$title" "not installed — run without --check to install"; return 0; fi
|
|
65
|
+
log_install "$title"
|
|
66
|
+
"$install_fn"; rc=$?
|
|
67
|
+
if out=$("$verify_fn" 2>&1); then log_ok "$title" "$out"; return 0; fi
|
|
68
|
+
if [ "$rc" -eq 2 ]; then log_warn "$title" "${out:-needs a manual step — re-run setup.sh afterwards}"
|
|
69
|
+
else log_fail "$title" "${out:-install did not complete}"; fi
|
|
70
|
+
return 0
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# run_check <title> <check_fn> <hint> [warn] — a verification-only step (ok / fail, or warn for informational checks).
|
|
74
|
+
run_check() {
|
|
75
|
+
local title=$1 check_fn=$2 hint=$3 level=${4:-fail} out msg
|
|
76
|
+
if out=$("$check_fn" 2>&1); then log_ok "$title" "$out"; return 0; fi
|
|
77
|
+
msg=$hint; [ -n "$out" ] && msg="$out"$'\n'"$hint"
|
|
78
|
+
if [ "$level" = warn ]; then log_warn "$title" "$msg"; else log_fail "$title" "$msg"; fi
|
|
79
|
+
return 0
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
# run_guided <title> <check_fn> <guidance> — GUI / App Store installs this bundle never performs itself.
|
|
83
|
+
run_guided() {
|
|
84
|
+
local title=$1 check_fn=$2 guidance=$3 found
|
|
85
|
+
if found=$("$check_fn" 2>/dev/null); then log_skip "$title" "${found:-already installed}"; return 0; fi
|
|
86
|
+
log_warn "$title" "$guidance"
|
|
87
|
+
return 0
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# ── shell profile block ────────────────────────────────────────────────────────────────────────
|
|
91
|
+
PROFILE_BEGIN='# >>> sungen mobile env >>>'
|
|
92
|
+
PROFILE_END='# <<< sungen mobile env <<<'
|
|
93
|
+
# zsh → ~/.zshrc, bash → ~/.bash_profile, anything else → ~/.profile
|
|
94
|
+
profile_file() {
|
|
95
|
+
case "${SHELL:-}" in
|
|
96
|
+
*zsh) printf '%s' "$HOME/.zshrc" ;;
|
|
97
|
+
*bash) printf '%s' "$HOME/.bash_profile" ;;
|
|
98
|
+
*) printf '%s' "$HOME/.profile" ;;
|
|
99
|
+
esac
|
|
100
|
+
}
|
|
101
|
+
profile_block_content() {
|
|
102
|
+
local file; file=$(profile_file); [ -f "$file" ] || return 0
|
|
103
|
+
awk -v b="$PROFILE_BEGIN" -v e="$PROFILE_END" '$0==b{f=1;next} $0==e{f=0} f' "$file"
|
|
104
|
+
}
|
|
105
|
+
# write_profile_block <content> — append the marked block, replace it when different, keep it when identical.
|
|
106
|
+
# Returns 0 = unchanged, 3 = written. Sets PROFILE_MSG. Must NOT be called in a subshell (it sets globals).
|
|
107
|
+
write_profile_block() {
|
|
108
|
+
local content=$1 file tmp
|
|
109
|
+
file=$(profile_file); [ -f "$file" ] || : > "$file"
|
|
110
|
+
if [ "$(profile_block_content)" = "$content" ]; then PROFILE_MSG="block already up to date in $file"; return 0; fi
|
|
111
|
+
tmp=$(mktemp)
|
|
112
|
+
awk -v b="$PROFILE_BEGIN" -v e="$PROFILE_END" '$0==b{f=1;next} $0==e{f=0;next} !f' "$file" > "$tmp"
|
|
113
|
+
[ -s "$tmp" ] && [ -n "$(tail -c1 "$tmp")" ] && printf '\n' >> "$tmp"
|
|
114
|
+
{ cat "$tmp"; printf '%s\n%s\n%s\n' "$PROFILE_BEGIN" "$content" "$PROFILE_END"; } > "$file"
|
|
115
|
+
rm -f "$tmp"
|
|
116
|
+
SUNGEN_ENV_PROFILE_CHANGED=1
|
|
117
|
+
PROFILE_MSG="written to $file — open a new terminal to apply"
|
|
118
|
+
return 3
|
|
119
|
+
}
|
|
120
|
+
# profile_step <title> <content> — the profile block as a step (honours --no-profile and --check).
|
|
121
|
+
profile_step() {
|
|
122
|
+
local title=$1 content=$2
|
|
123
|
+
if [ "$SUNGEN_ENV_NO_PROFILE" = 1 ]; then log_skip "$title" "--no-profile"; return 0; fi
|
|
124
|
+
if [ "$SUNGEN_ENV_CHECK_ONLY" = 1 ]; then
|
|
125
|
+
if [ "$(profile_block_content)" = "$content" ]; then log_skip "$title" "block already up to date in $(profile_file)"
|
|
126
|
+
else log_fail "$title" "block missing or outdated in $(profile_file) — run without --check to write it"; fi
|
|
127
|
+
return 0
|
|
128
|
+
fi
|
|
129
|
+
write_profile_block "$content"
|
|
130
|
+
case $? in 3) log_ok "$title" "$PROFILE_MSG" ;; *) log_skip "$title" "$PROFILE_MSG" ;; esac
|
|
131
|
+
return 0
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
# print_summary — tallies + the "new terminal" notice. Exit 1 when anything failed.
|
|
135
|
+
print_summary() {
|
|
136
|
+
printf '\n%s%d ok · %d skipped · %d warned · %d failed%s\n' "$C_BOLD" "$COUNT_OK" "$COUNT_SKIP" "$COUNT_WARN" "$COUNT_FAIL" "$C_RESET"
|
|
137
|
+
[ "$SUNGEN_ENV_PROFILE_CHANGED" = 1 ] && printf '%s→ Open a NEW terminal so PATH / JAVA_HOME / ANDROID_HOME apply.%s\n' "$C_WARN" "$C_RESET"
|
|
138
|
+
[ "$COUNT_WARN" -gt 0 ] && printf ' warnings need a manual step — do it, then re-run this script.\n'
|
|
139
|
+
[ "$COUNT_FAIL" -gt 0 ] && return 1
|
|
140
|
+
return 0
|
|
141
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# verify-appium.ps1 - the final gate: `appium driver doctor uiautomator2` checks the whole chain the
|
|
2
|
+
# driver needs at run time (JAVA_HOME, ANDROID_HOME, adb). Doctor problems are reported as warnings
|
|
3
|
+
# with the doctor's own output - it names the missing piece.
|
|
4
|
+
if (-not $script:SungenEnvLibLoaded) { . (Join-Path $PSScriptRoot 'lib.ps1') }
|
|
5
|
+
|
|
6
|
+
$AppiumCmd = $script:SungenEnvAppiumCmd
|
|
7
|
+
# Doctor prints a required failure as a heavy-ballot-x line; the character is referenced by code point
|
|
8
|
+
# so this file stays ASCII (Windows PowerShell 5.1 reads BOM-less scripts as ANSI).
|
|
9
|
+
$RequiredFailMark = [string][char]0x2716
|
|
10
|
+
|
|
11
|
+
Invoke-Check -Title 'Appium CLI' -Check {
|
|
12
|
+
if (Test-Path $AppiumCmd) { "appium $((& $AppiumCmd -v) 2>$null | Select-Object -First 1)" }
|
|
13
|
+
} -Hint 'not installed in this project - sungen capability add mobile'
|
|
14
|
+
|
|
15
|
+
if ((Test-Path $AppiumCmd) -and -not $script:SungenEnvSkipAndroid) {
|
|
16
|
+
$installed = (& $AppiumCmd driver list --installed --json) 2>$null | Out-String
|
|
17
|
+
if ($installed -match '"uiautomator2"') {
|
|
18
|
+
# Inline (not Invoke-Check) so the failure detail prints AFTER the step title, like every other step.
|
|
19
|
+
$title = 'appium driver doctor uiautomator2'
|
|
20
|
+
$hint = 'fix the required items above (open a NEW terminal first if the environment was just written), then re-run'
|
|
21
|
+
$out = (& $AppiumCmd driver doctor uiautomator2 2>&1) | Out-String
|
|
22
|
+
$rc = $LASTEXITCODE
|
|
23
|
+
# Doctor marks BOTH required and optional problems with the same mark, so read its summary line:
|
|
24
|
+
# "### Diagnostic completed, N required fixes needed, M optional fixes possible. ###"
|
|
25
|
+
$req = $null; if ($out -match 'Diagnostic completed, (\d+) required fix') { $req = [int]$Matches[1] }
|
|
26
|
+
$opt = 0; if ($out -match ', (\d+) optional fix') { $opt = [int]$Matches[1] }
|
|
27
|
+
if ($null -eq $req) {
|
|
28
|
+
# No summary line (older driver) - fall back to the exit code.
|
|
29
|
+
if ($rc -eq 0) { Write-Ok $title 'doctor exited 0' } else { Write-Warn $title "$out`n$hint" }
|
|
30
|
+
} elseif ($req -gt 0) {
|
|
31
|
+
$marks = @(($out -split "`r?`n") | Where-Object { $_.Contains($RequiredFailMark) } | ForEach-Object { $_ -replace '^.*Doctor\s+', '' })
|
|
32
|
+
Write-Warn $title ("$req required fix(es) needed:`n" + ($marks -join "`n") + "`nfull report: npx appium driver doctor uiautomator2`n$hint")
|
|
33
|
+
} else {
|
|
34
|
+
Write-Ok $title "0 required fixes, $opt optional (extras like bundletool / gstreamer - see: npx appium driver doctor uiautomator2)"
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
Write-Warn 'appium driver doctor uiautomator2' 'driver not installed - see the Appium step above'
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# verify-appium.sh — the final gate: `appium driver doctor <driver>` checks the whole chain the
|
|
3
|
+
# driver needs at run time (JAVA_HOME, ANDROID_HOME, adb; Xcode + simctl for xcuitest).
|
|
4
|
+
# Doctor problems are reported as warnings with the doctor's own output — it names the missing piece.
|
|
5
|
+
[ -n "${SUNGEN_ENV_LIB_LOADED:-}" ] || . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib.sh"
|
|
6
|
+
|
|
7
|
+
APPIUM_BIN=$SUNGEN_ENV_APPIUM_BIN
|
|
8
|
+
|
|
9
|
+
appium_cli_check() { [ -x "$APPIUM_BIN" ] || return 1; printf 'appium %s' "$("$APPIUM_BIN" -v 2>/dev/null)"; }
|
|
10
|
+
run_check "Appium CLI" appium_cli_check "not installed in this project — sungen capability add mobile"
|
|
11
|
+
|
|
12
|
+
doctor_check() {
|
|
13
|
+
local out rc req opt
|
|
14
|
+
out=$("$APPIUM_BIN" driver doctor "$CURRENT_DRIVER" 2>&1); rc=$?
|
|
15
|
+
# Doctor marks BOTH required and optional problems with "✖", so read its own summary line instead:
|
|
16
|
+
# "### Diagnostic completed, N required fixes needed, M optional fixes possible. ###"
|
|
17
|
+
req=$(printf '%s\n' "$out" | sed -n -E 's/.*Diagnostic completed, ([0-9]+) required fix.*/\1/p' | tail -n1)
|
|
18
|
+
opt=$(printf '%s\n' "$out" | sed -n -E 's/.*, ([0-9]+) optional fix.*/\1/p' | tail -n1)
|
|
19
|
+
if [ -z "$req" ]; then
|
|
20
|
+
# No summary line (older driver) — fall back to the exit code.
|
|
21
|
+
[ "$rc" -eq 0 ] && { printf 'doctor exited 0'; return 0; }
|
|
22
|
+
printf '%s' "$out"; return 1
|
|
23
|
+
fi
|
|
24
|
+
if [ "$req" -gt 0 ]; then
|
|
25
|
+
printf '%s required fix(es) needed:\n%s\nfull report: npx appium driver doctor %s' "$req" \
|
|
26
|
+
"$(printf '%s\n' "$out" | grep '✖' | sed -E 's/^.*Doctor +//')" "$CURRENT_DRIVER"
|
|
27
|
+
return 1
|
|
28
|
+
fi
|
|
29
|
+
printf '0 required fixes · %s optional (extras like bundletool / gstreamer — see: npx appium driver doctor %s)' "${opt:-0}" "$CURRENT_DRIVER"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if [ -x "$APPIUM_BIN" ]; then
|
|
33
|
+
DOCTOR_DRIVERS=""
|
|
34
|
+
[ "$SUNGEN_ENV_SKIP_ANDROID" != 1 ] && DOCTOR_DRIVERS="uiautomator2"
|
|
35
|
+
if [ "$(uname -s)" = Darwin ] && [ "$SUNGEN_ENV_SKIP_IOS" != 1 ]; then DOCTOR_DRIVERS="$DOCTOR_DRIVERS xcuitest"; fi
|
|
36
|
+
for CURRENT_DRIVER in $DOCTOR_DRIVERS; do
|
|
37
|
+
if printf '%s' "$("$APPIUM_BIN" driver list --installed --json 2>/dev/null)" | grep -q "\"$CURRENT_DRIVER\""; then
|
|
38
|
+
run_check "appium driver doctor $CURRENT_DRIVER" doctor_check "fix the required items above (open a NEW terminal first if the profile block was just written), then re-run" warn
|
|
39
|
+
else
|
|
40
|
+
log_warn "appium driver doctor $CURRENT_DRIVER" "driver not installed — see the Appium step above"
|
|
41
|
+
fi
|
|
42
|
+
done
|
|
43
|
+
fi
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# verify-node.ps1 - Node.js >= 18 (sungen's own engine floor). Never installs Node: sungen is already
|
|
2
|
+
# running on it when this bundle is invoked from `sungen init`; a standalone run gets the guide.
|
|
3
|
+
if (-not $script:SungenEnvLibLoaded) { . (Join-Path $PSScriptRoot 'lib.ps1') }
|
|
4
|
+
|
|
5
|
+
$NodeMin = 18
|
|
6
|
+
Invoke-Check -Title "Node.js >= $NodeMin" -Check {
|
|
7
|
+
if (-not (Get-Command node -ErrorAction SilentlyContinue)) { return $null }
|
|
8
|
+
$v = (& node -v) 2>$null
|
|
9
|
+
$major = 0; if ("$v" -match '(\d+)') { $major = [int]$Matches[1] }
|
|
10
|
+
if ($major -lt $NodeMin) { return $null }
|
|
11
|
+
$npm = (& npm -v) 2>$null
|
|
12
|
+
"node $v, npm $npm"
|
|
13
|
+
} -Hint 'install Node LTS: https://nodejs.org (or: winget install OpenJS.NodeJS.LTS), then open a new terminal'
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# verify-node.sh — Node.js ≥ 18 (sungen's own engine floor). Never installs Node: sungen is already
|
|
3
|
+
# running on it when this bundle is invoked from `sungen init`; a standalone run gets the guide.
|
|
4
|
+
[ -n "${SUNGEN_ENV_LIB_LOADED:-}" ] || . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib.sh"
|
|
5
|
+
|
|
6
|
+
NODE_MIN=18
|
|
7
|
+
|
|
8
|
+
node_check() {
|
|
9
|
+
local v major
|
|
10
|
+
have node || return 1
|
|
11
|
+
v=$(node -v 2>/dev/null) || return 1
|
|
12
|
+
major=$(version_major "$v")
|
|
13
|
+
[ "$major" -ge "$NODE_MIN" ] || { printf 'node %s is below %s' "$v" "$NODE_MIN"; return 1; }
|
|
14
|
+
printf 'node %s · npm %s' "$v" "$(npm -v 2>/dev/null || printf '?')"
|
|
15
|
+
}
|
|
16
|
+
run_check "Node.js ≥ $NODE_MIN" node_check "install Node LTS: https://nodejs.org (or: brew install node · nvm install --lts)"
|