@sun-asterisk/sungen 3.2.31 → 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,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)"
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# install-android.sh — macOS: JDK ≥ 17, Android SDK root, platform-tools + emulator, shell profile block.
|
|
3
|
+
# Headless via Homebrew (decision D2): `openjdk@21` + the `android-commandlinetools` cask + sdkmanager.
|
|
4
|
+
# Android Studio is optional (only for an on-screen emulator) and is never installed here.
|
|
5
|
+
# Sourced by setup.sh (shares the runner + counters); runnable standalone for debugging.
|
|
6
|
+
[ -n "${SUNGEN_ENV_LIB_LOADED:-}" ] || . "$(cd "$(dirname "${BASH_SOURCE[0]}")/../common" && pwd)/lib.sh"
|
|
7
|
+
|
|
8
|
+
JDK_MIN=17
|
|
9
|
+
BREW_SDK_ROOT=$( { have brew && printf '%s/share/android-commandlinetools' "$(brew --prefix)"; } 2>/dev/null || true)
|
|
10
|
+
|
|
11
|
+
# ── JDK ────────────────────────────────────────────────────────────────────────────────────────
|
|
12
|
+
# Any working JDK ≥ 17 is accepted (Temurin, Corretto, Oracle, asdf…) — we never replace a good one.
|
|
13
|
+
# resolve_* run in THIS shell (not a subshell) so their exports survive; *_check run captured.
|
|
14
|
+
resolve_java_home() {
|
|
15
|
+
local major home
|
|
16
|
+
if [ -n "${JAVA_HOME:-}" ] && [ -x "$JAVA_HOME/bin/java" ]; then
|
|
17
|
+
major=$(java_major_of "$JAVA_HOME/bin/java"); [ -n "$major" ] && [ "$major" -ge "$JDK_MIN" ] && return 0
|
|
18
|
+
fi
|
|
19
|
+
if have java; then
|
|
20
|
+
major=$(java_major_of java)
|
|
21
|
+
if [ -n "$major" ] && [ "$major" -ge "$JDK_MIN" ]; then
|
|
22
|
+
home=$(java -XshowSettings:properties -version 2>&1 | awk -F' = ' '/java\.home/{print $2}')
|
|
23
|
+
[ -d "$home" ] && { export JAVA_HOME=$home; return 0; }
|
|
24
|
+
fi
|
|
25
|
+
fi
|
|
26
|
+
# Homebrew openjdk@21 present but not on PATH (brew keeps it keg-only).
|
|
27
|
+
if have brew && [ -x "$(brew --prefix openjdk@21 2>/dev/null)/bin/java" ]; then export JAVA_HOME; JAVA_HOME=$(brew --prefix openjdk@21); return 0; fi
|
|
28
|
+
return 1
|
|
29
|
+
}
|
|
30
|
+
jdk_check() {
|
|
31
|
+
local major
|
|
32
|
+
[ -n "${JAVA_HOME:-}" ] && [ -x "$JAVA_HOME/bin/java" ] || return 1
|
|
33
|
+
major=$(java_major_of "$JAVA_HOME/bin/java"); [ -n "$major" ] && [ "$major" -ge "$JDK_MIN" ] || return 1
|
|
34
|
+
printf 'java %s · JAVA_HOME=%s' "$major" "$JAVA_HOME"
|
|
35
|
+
}
|
|
36
|
+
jdk_install() { brew install openjdk@21 && { export JAVA_HOME; JAVA_HOME=$(brew --prefix openjdk@21); }; }
|
|
37
|
+
jdk_verify() { jdk_check || { printf 'no JDK ≥ %s — brew install openjdk@21 && export JAVA_HOME=$(brew --prefix openjdk@21)' "$JDK_MIN"; return 1; }; }
|
|
38
|
+
resolve_java_home || true
|
|
39
|
+
run_step "JDK ≥ $JDK_MIN" jdk_check jdk_install jdk_verify
|
|
40
|
+
|
|
41
|
+
# ── Android SDK root ───────────────────────────────────────────────────────────────────────────
|
|
42
|
+
# find_sdkmanager <sdk root> → path of sdkmanager (cmdline-tools/latest, any cmdline-tools/*, legacy tools/bin)
|
|
43
|
+
find_sdkmanager() {
|
|
44
|
+
local root=$1 c
|
|
45
|
+
[ -n "$root" ] || return 1
|
|
46
|
+
for c in "$root/cmdline-tools/latest/bin/sdkmanager" "$root"/cmdline-tools/*/bin/sdkmanager "$root/tools/bin/sdkmanager"; do
|
|
47
|
+
[ -x "$c" ] && { printf '%s' "$c"; return 0; }
|
|
48
|
+
done
|
|
49
|
+
return 1
|
|
50
|
+
}
|
|
51
|
+
sdk_root_usable() { [ -n "$1" ] && [ -d "$1" ] && { [ -x "$1/platform-tools/adb" ] || find_sdkmanager "$1" >/dev/null; }; }
|
|
52
|
+
# Precedence: what the user already exports → Android Studio's default → the Homebrew cask root.
|
|
53
|
+
resolve_android_home() {
|
|
54
|
+
local c
|
|
55
|
+
for c in "${ANDROID_HOME:-}" "${ANDROID_SDK_ROOT:-}" "$HOME/Library/Android/sdk" "$BREW_SDK_ROOT"; do
|
|
56
|
+
if sdk_root_usable "$c"; then export ANDROID_HOME=$c ANDROID_SDK_ROOT=$c; return 0; fi
|
|
57
|
+
done
|
|
58
|
+
return 1
|
|
59
|
+
}
|
|
60
|
+
sdk_root_check() { sdk_root_usable "${ANDROID_HOME:-}" || return 1; printf 'ANDROID_HOME=%s' "$ANDROID_HOME"; }
|
|
61
|
+
sdk_root_install() { brew install --cask android-commandlinetools && export ANDROID_HOME=$BREW_SDK_ROOT ANDROID_SDK_ROOT=$BREW_SDK_ROOT; }
|
|
62
|
+
sdk_root_verify() {
|
|
63
|
+
find_sdkmanager "${ANDROID_HOME:-}" >/dev/null || { printf 'sdkmanager not found under %s — brew install --cask android-commandlinetools' "${ANDROID_HOME:-<unset>}"; return 1; }
|
|
64
|
+
printf 'ANDROID_HOME=%s' "$ANDROID_HOME"
|
|
65
|
+
}
|
|
66
|
+
resolve_android_home || true
|
|
67
|
+
run_step "Android SDK root (cmdline-tools)" sdk_root_check sdk_root_install sdk_root_verify
|
|
68
|
+
|
|
69
|
+
# ── platform-tools (adb) + emulator ────────────────────────────────────────────────────────────
|
|
70
|
+
sdk_pkgs_check() {
|
|
71
|
+
local v
|
|
72
|
+
[ -x "${ANDROID_HOME:-}/platform-tools/adb" ] && [ -x "$ANDROID_HOME/emulator/emulator" ] || return 1
|
|
73
|
+
v=$("$ANDROID_HOME/platform-tools/adb" version 2>/dev/null) || v='adb'
|
|
74
|
+
printf 'platform-tools + emulator · %s' "$(first_line "$v")"
|
|
75
|
+
}
|
|
76
|
+
sdk_pkgs_install() {
|
|
77
|
+
local sm
|
|
78
|
+
sm=$(find_sdkmanager "${ANDROID_HOME:-}") || { printf 'no sdkmanager — the "Android SDK root" step must succeed first\n' >&2; return 1; }
|
|
79
|
+
# sdkmanager needs a JDK; --sdk_root is mandatory for the cask layout. Licenses are accepted
|
|
80
|
+
# non-interactively (they are Google's SDK terms — the same prompt Android Studio shows).
|
|
81
|
+
yes 2>/dev/null | JAVA_HOME=${JAVA_HOME:-} "$sm" --sdk_root="$ANDROID_HOME" --licenses >/dev/null 2>&1 || true
|
|
82
|
+
JAVA_HOME=${JAVA_HOME:-} "$sm" --sdk_root="$ANDROID_HOME" "platform-tools" "emulator"
|
|
83
|
+
}
|
|
84
|
+
sdk_pkgs_verify() { sdk_pkgs_check || { printf 'adb / emulator still missing — sdkmanager --sdk_root="%s" platform-tools emulator' "${ANDROID_HOME:-}"; return 1; }; }
|
|
85
|
+
run_step "Android platform-tools (adb) + emulator" sdk_pkgs_check sdk_pkgs_install sdk_pkgs_verify
|
|
86
|
+
|
|
87
|
+
# Make the tools reachable for the rest of THIS run (the profile block covers new terminals).
|
|
88
|
+
if [ -n "${ANDROID_HOME:-}" ]; then
|
|
89
|
+
export PATH="$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator:$ANDROID_HOME/cmdline-tools/latest/bin:$PATH"
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
# ── shell profile block (decision D3) ─────────────────────────────────────────────────────────
|
|
93
|
+
PROFILE_TITLE="Shell profile (ANDROID_HOME, JAVA_HOME, PATH)"
|
|
94
|
+
if [ -z "${ANDROID_HOME:-}" ] || [ -z "${JAVA_HOME:-}" ]; then
|
|
95
|
+
log_warn "$PROFILE_TITLE" "skipped — fix the JDK / SDK steps above first, then re-run"
|
|
96
|
+
elif [ "$SUNGEN_ENV_INITIAL_ANDROID_HOME" = "$ANDROID_HOME" ] && [ "$SUNGEN_ENV_INITIAL_JAVA_HOME" = "$JAVA_HOME" ] \
|
|
97
|
+
&& [ -n "$SUNGEN_ENV_INITIAL_ADB" ] && grep -qs 'ANDROID_HOME' "$(profile_file)"; then
|
|
98
|
+
# The user's own profile already exports the right values (and adb was on PATH before we touched it) —
|
|
99
|
+
# nothing to add. A value exported only in this terminal session does not count: the profile must name it.
|
|
100
|
+
log_skip "$PROFILE_TITLE" "already exported by your shell profile (ANDROID_HOME, JAVA_HOME, adb on PATH) — no block written"
|
|
101
|
+
else
|
|
102
|
+
profile_step "$PROFILE_TITLE" "$(printf 'export ANDROID_HOME="%s"\nexport ANDROID_SDK_ROOT="$ANDROID_HOME"\nexport JAVA_HOME="%s"\nexport PATH="$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator:$ANDROID_HOME/cmdline-tools/latest/bin:$PATH"' "$ANDROID_HOME" "$JAVA_HOME")"
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
# ── Android Studio — optional, GUI, never installed here ───────────────────────────────────────
|
|
106
|
+
if [ -d "/Applications/Android Studio.app" ]; then
|
|
107
|
+
log_skip "Android Studio (optional)" "/Applications/Android Studio.app"
|
|
108
|
+
else
|
|
109
|
+
log_skip "Android Studio (optional)" "not installed — only needed for the AVD manager / an on-screen emulator: https://developer.android.com/studio"
|
|
110
|
+
fi
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# install-ios.sh — macOS: Xcode Command Line Tools, Xcode.app (checked + guided), license, applesimutils.
|
|
3
|
+
# Xcode.app comes from the App Store and is never installed here (decision D2); xcuitest needs it
|
|
4
|
+
# because WebDriverAgent is built by Xcode at run time.
|
|
5
|
+
[ -n "${SUNGEN_ENV_LIB_LOADED:-}" ] || . "$(cd "$(dirname "${BASH_SOURCE[0]}")/../common" && pwd)/lib.sh"
|
|
6
|
+
|
|
7
|
+
# ── Command Line Tools ─────────────────────────────────────────────────────────────────────────
|
|
8
|
+
clt_check() { xcode-select -p 2>/dev/null; }
|
|
9
|
+
# `xcode-select --install` opens Apple's dialog and returns immediately — the install finishes
|
|
10
|
+
# outside this script, so report "needs a manual step" (exit 2) rather than success or failure.
|
|
11
|
+
clt_install() { xcode-select --install >/dev/null 2>&1; return 2; }
|
|
12
|
+
clt_verify() { clt_check || { printf 'finish the "Install Command Line Developer Tools" dialog, then re-run setup.sh'; return 1; }; }
|
|
13
|
+
run_step "Xcode Command Line Tools" clt_check clt_install clt_verify
|
|
14
|
+
|
|
15
|
+
# ── Xcode.app (guided) ─────────────────────────────────────────────────────────────────────────
|
|
16
|
+
# Only a full Xcode answers `xcodebuild -version`; a CLT-only machine errors out here.
|
|
17
|
+
xcode_check() { local v; v=$(xcodebuild -version 2>/dev/null) || return 1; first_line "$v"; }
|
|
18
|
+
run_guided "Xcode.app" xcode_check "not installed — required for iOS (Xcode builds WebDriverAgent). Install Xcode from the App Store, open it once, then re-run setup.sh"
|
|
19
|
+
|
|
20
|
+
# ── license — only meaningful once Xcode.app is there ──────────────────────────────────────────
|
|
21
|
+
if xcode_check >/dev/null 2>&1; then
|
|
22
|
+
license_check() { xcodebuild -license check >/dev/null 2>&1 && printf 'accepted'; }
|
|
23
|
+
license_install() { sudo xcodebuild -license accept; }
|
|
24
|
+
license_verify() { license_check || { printf 'run: sudo xcodebuild -license accept'; return 1; }; }
|
|
25
|
+
run_step "Xcode license" license_check license_install license_verify
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
# ── applesimutils — Simulator permission steps (`grant permission …`) ──────────────────────────
|
|
29
|
+
asu_check() { have applesimutils && printf 'applesimutils at %s' "$(command -v applesimutils)"; }
|
|
30
|
+
asu_install() { brew tap wix/brew && brew install applesimutils; }
|
|
31
|
+
asu_verify() { asu_check || { printf 'brew tap wix/brew && brew install applesimutils'; return 1; }; }
|
|
32
|
+
run_step "applesimutils (Simulator permission steps)" asu_check asu_install asu_verify
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# verify-android.sh — macOS: is the Android side usable right now? (adb, env vars, connected devices)
|
|
3
|
+
[ -n "${SUNGEN_ENV_LIB_LOADED:-}" ] || . "$(cd "$(dirname "${BASH_SOURCE[0]}")/../common" && pwd)/lib.sh"
|
|
4
|
+
|
|
5
|
+
# Prefer the adb under ANDROID_HOME (this run may have just installed it) over a stale one on PATH.
|
|
6
|
+
android_adb() {
|
|
7
|
+
if [ -x "${ANDROID_HOME:-}/platform-tools/adb" ]; then printf '%s/platform-tools/adb' "$ANDROID_HOME"
|
|
8
|
+
else command -v adb 2>/dev/null; fi
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
adb_check() {
|
|
12
|
+
local adb v
|
|
13
|
+
adb=$(android_adb) && [ -x "$adb" ] || return 1
|
|
14
|
+
v=$("$adb" version 2>/dev/null) || return 1
|
|
15
|
+
printf '%s' "$(first_line "$v")"
|
|
16
|
+
}
|
|
17
|
+
run_check "adb" adb_check "adb not found — re-run without --skip-android, or open a NEW terminal if the profile block was just written"
|
|
18
|
+
|
|
19
|
+
env_check() {
|
|
20
|
+
[ -n "${ANDROID_HOME:-}" ] && [ -n "${JAVA_HOME:-}" ] || return 1
|
|
21
|
+
printf 'ANDROID_HOME=%s · JAVA_HOME=%s' "$ANDROID_HOME" "$JAVA_HOME"
|
|
22
|
+
}
|
|
23
|
+
run_check "ANDROID_HOME / JAVA_HOME" env_check "not set in this shell — open a NEW terminal (profile block) or export them"
|
|
24
|
+
|
|
25
|
+
devices_check() {
|
|
26
|
+
local adb n
|
|
27
|
+
adb=$(android_adb) && [ -x "$adb" ] || return 1
|
|
28
|
+
n=$("$adb" devices 2>/dev/null | awk 'NR>1 && $2=="device"{n++} END{print n+0}')
|
|
29
|
+
if [ "$n" -gt 0 ]; then printf '%s device(s) connected' "$n"
|
|
30
|
+
else printf 'no device connected — plug in a phone (USB debugging on) or start an emulator before /sungen:run-test'; fi
|
|
31
|
+
}
|
|
32
|
+
run_check "Android devices" devices_check "adb is not usable yet" warn
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# verify-ios.sh — macOS: is the iOS side usable right now? (Xcode selected, Simulator runtimes visible)
|
|
3
|
+
[ -n "${SUNGEN_ENV_LIB_LOADED:-}" ] || . "$(cd "$(dirname "${BASH_SOURCE[0]}")/../common" && pwd)/lib.sh"
|
|
4
|
+
|
|
5
|
+
xcode_version_check() { local v; v=$(xcodebuild -version 2>/dev/null) || return 1; printf '%s · %s' "$(first_line "$v")" "$(xcode-select -p 2>/dev/null)"; }
|
|
6
|
+
run_check "Xcode" xcode_version_check "Xcode.app not installed or not selected — App Store, then: sudo xcode-select -s /Applications/Xcode.app" warn
|
|
7
|
+
|
|
8
|
+
simulators_check() {
|
|
9
|
+
local json n
|
|
10
|
+
json=$(xcrun simctl list devices available -j 2>/dev/null) || return 1
|
|
11
|
+
n=$(printf '%s' "$json" | grep -c '"udid"' || true)
|
|
12
|
+
if [ "${n:-0}" -gt 0 ]; then printf '%s simulator(s) available (xcrun simctl list devices available)' "$n"
|
|
13
|
+
else printf 'no Simulator runtime — Xcode → Settings → Components, add an iOS Simulator runtime'; return 1; fi
|
|
14
|
+
}
|
|
15
|
+
run_check "iOS Simulators" simulators_check "xcrun simctl is not usable — needs Xcode.app (real-iPhone runs need it too: WebDriverAgent signing)" warn
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# install-android.ps1 - Windows: JDK >= 17, Android SDK root, platform-tools + emulator, User environment.
|
|
2
|
+
# Headless via winget (decision D2): Microsoft OpenJDK 21 + Google's command-line tools zip + sdkmanager.
|
|
3
|
+
# Android Studio is optional (only for an on-screen emulator) and is never installed here.
|
|
4
|
+
# Dot-sourced by setup.ps1 (shares the runner + counters); runnable standalone for debugging.
|
|
5
|
+
if (-not $script:SungenEnvLibLoaded) { . (Join-Path $PSScriptRoot '..\common\lib.ps1') }
|
|
6
|
+
|
|
7
|
+
$JdkMin = 17
|
|
8
|
+
# Google rotates the command-line tools zip name; bump this ONE value when the download 404s.
|
|
9
|
+
# Current builds: https://developer.android.com/studio#command-line-tools-only
|
|
10
|
+
$CmdlineToolsBuild = '13114758'
|
|
11
|
+
$DefaultSdkRoot = Join-Path $env:LOCALAPPDATA 'Android\Sdk'
|
|
12
|
+
|
|
13
|
+
# --- JDK ------------------------------------------------------------------------------------------
|
|
14
|
+
# Any working JDK >= 17 is accepted (Microsoft, Temurin, Oracle, Corretto...) - we never replace a good one.
|
|
15
|
+
function Get-JavaMajor([string]$JavaExe) {
|
|
16
|
+
try { $out = (& $JavaExe -version 2>&1) | Out-String; if ($out -match '"(\d+)(\.\d+)*') { return [int]$Matches[1] } } catch {}
|
|
17
|
+
return 0
|
|
18
|
+
}
|
|
19
|
+
function Get-JavaHomeFromExe([string]$JavaExe) {
|
|
20
|
+
$out = (& $JavaExe -XshowSettings:properties -version 2>&1) | Out-String
|
|
21
|
+
if ($out -match 'java\.home = (.+)') { return $Matches[1].Trim() }
|
|
22
|
+
return $null
|
|
23
|
+
}
|
|
24
|
+
function Test-JdkHome([string]$JdkHome) {
|
|
25
|
+
if (-not $JdkHome) { return $false }
|
|
26
|
+
$exe = Join-Path $JdkHome 'bin\java.exe'
|
|
27
|
+
return ((Test-Path $exe) -and ((Get-JavaMajor $exe) -ge $JdkMin))
|
|
28
|
+
}
|
|
29
|
+
# Resolve-JavaHome - sets $env:JAVA_HOME when a usable JDK is found (env -> PATH -> vendor default folders).
|
|
30
|
+
function Resolve-JavaHome {
|
|
31
|
+
if (Test-JdkHome $env:JAVA_HOME) { return $true }
|
|
32
|
+
$java = Get-Command java -ErrorAction SilentlyContinue
|
|
33
|
+
if ($java -and ((Get-JavaMajor $java.Source) -ge $JdkMin)) {
|
|
34
|
+
$home = Get-JavaHomeFromExe $java.Source
|
|
35
|
+
if ($home -and (Test-Path $home)) { $env:JAVA_HOME = $home; return $true }
|
|
36
|
+
}
|
|
37
|
+
foreach ($root in @((Join-Path $env:ProgramFiles 'Microsoft'), (Join-Path $env:ProgramFiles 'Eclipse Adoptium'), (Join-Path $env:ProgramFiles 'Java'))) {
|
|
38
|
+
if (-not (Test-Path $root)) { continue }
|
|
39
|
+
$cand = Get-ChildItem $root -Directory -Filter 'jdk-*' -ErrorAction SilentlyContinue | Sort-Object Name -Descending |
|
|
40
|
+
Where-Object { Test-JdkHome $_.FullName } | Select-Object -First 1
|
|
41
|
+
if ($cand) { $env:JAVA_HOME = $cand.FullName; return $true }
|
|
42
|
+
}
|
|
43
|
+
return $false
|
|
44
|
+
}
|
|
45
|
+
function Get-JdkDetail { if (Resolve-JavaHome) { "java $(Get-JavaMajor (Join-Path $env:JAVA_HOME 'bin\java.exe')), JAVA_HOME=$env:JAVA_HOME" } }
|
|
46
|
+
|
|
47
|
+
Invoke-Step -Title "JDK >= $JdkMin" -Check { Get-JdkDetail } -Install {
|
|
48
|
+
Invoke-Native winget @('install', '--id', 'Microsoft.OpenJDK.21', '-e', '--silent', '--accept-source-agreements', '--accept-package-agreements')
|
|
49
|
+
Update-SessionPath
|
|
50
|
+
} -Verify { Get-JdkDetail } -Hint 'winget install --id Microsoft.OpenJDK.21 -e (then open a NEW terminal and re-run)'
|
|
51
|
+
|
|
52
|
+
# --- Android SDK root -----------------------------------------------------------------------------
|
|
53
|
+
function Find-SdkManager([string]$Root) {
|
|
54
|
+
if (-not $Root) { return $null }
|
|
55
|
+
foreach ($c in @((Join-Path $Root 'cmdline-tools\latest\bin\sdkmanager.bat'), (Join-Path $Root 'tools\bin\sdkmanager.bat'))) { if (Test-Path $c) { return $c } }
|
|
56
|
+
$any = Get-ChildItem (Join-Path $Root 'cmdline-tools') -Directory -ErrorAction SilentlyContinue |
|
|
57
|
+
ForEach-Object { Join-Path $_.FullName 'bin\sdkmanager.bat' } | Where-Object { Test-Path $_ } | Select-Object -First 1
|
|
58
|
+
return $any
|
|
59
|
+
}
|
|
60
|
+
function Test-SdkRoot([string]$Root) {
|
|
61
|
+
if (-not $Root -or -not (Test-Path $Root)) { return $false }
|
|
62
|
+
return ((Test-Path (Join-Path $Root 'platform-tools\adb.exe')) -or [bool](Find-SdkManager $Root))
|
|
63
|
+
}
|
|
64
|
+
# Precedence: what the user already set -> Android Studio's default (%LOCALAPPDATA%\Android\Sdk).
|
|
65
|
+
function Resolve-AndroidHome {
|
|
66
|
+
foreach ($c in @($env:ANDROID_HOME, $env:ANDROID_SDK_ROOT, $DefaultSdkRoot)) {
|
|
67
|
+
if (Test-SdkRoot $c) { $env:ANDROID_HOME = $c; $env:ANDROID_SDK_ROOT = $c; return $true }
|
|
68
|
+
}
|
|
69
|
+
return $false
|
|
70
|
+
}
|
|
71
|
+
function Install-CmdlineTools {
|
|
72
|
+
# Cmdlet errors (download 404 when Google rotates the zip, unpack failures) must become terminating so
|
|
73
|
+
# Invoke-Step's try/catch reports the real cause instead of only the generic hint.
|
|
74
|
+
$ErrorActionPreference = 'Stop'
|
|
75
|
+
$root = $DefaultSdkRoot
|
|
76
|
+
$url = "https://dl.google.com/android/repository/commandlinetools-win-${CmdlineToolsBuild}_latest.zip"
|
|
77
|
+
$zip = Join-Path $env:TEMP "commandlinetools-win-$CmdlineToolsBuild.zip"
|
|
78
|
+
$staging = Join-Path $env:TEMP "sungen-cmdline-tools-$CmdlineToolsBuild"
|
|
79
|
+
New-Item -ItemType Directory -Force -Path (Join-Path $root 'cmdline-tools') | Out-Null
|
|
80
|
+
Write-Host " downloading $url" -ForegroundColor DarkGray
|
|
81
|
+
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
|
82
|
+
Invoke-WebRequest -Uri $url -OutFile $zip -UseBasicParsing
|
|
83
|
+
if (Test-Path $staging) { Remove-Item $staging -Recurse -Force }
|
|
84
|
+
Expand-Archive -Path $zip -DestinationPath $staging -Force
|
|
85
|
+
# The zip unpacks as cmdline-tools\ but sdkmanager only works from cmdline-tools\latest\.
|
|
86
|
+
$latest = Join-Path $root 'cmdline-tools\latest'
|
|
87
|
+
if (Test-Path $latest) { Remove-Item $latest -Recurse -Force }
|
|
88
|
+
Move-Item (Join-Path $staging 'cmdline-tools') $latest
|
|
89
|
+
Remove-Item $zip -Force -ErrorAction SilentlyContinue
|
|
90
|
+
Remove-Item $staging -Recurse -Force -ErrorAction SilentlyContinue
|
|
91
|
+
$env:ANDROID_HOME = $root; $env:ANDROID_SDK_ROOT = $root
|
|
92
|
+
}
|
|
93
|
+
[void](Resolve-AndroidHome)
|
|
94
|
+
Invoke-Step -Title 'Android SDK root (cmdline-tools)' -Check {
|
|
95
|
+
if (Test-SdkRoot $env:ANDROID_HOME) { "ANDROID_HOME=$env:ANDROID_HOME" }
|
|
96
|
+
} -Install { Install-CmdlineTools } -Verify {
|
|
97
|
+
if (Find-SdkManager $env:ANDROID_HOME) { "ANDROID_HOME=$env:ANDROID_HOME" }
|
|
98
|
+
} -Hint "download the command-line tools zip from https://developer.android.com/studio#command-line-tools-only and unpack it to $DefaultSdkRoot\cmdline-tools\latest"
|
|
99
|
+
|
|
100
|
+
# --- platform-tools (adb) + emulator --------------------------------------------------------------
|
|
101
|
+
function Get-SdkPackagesDetail {
|
|
102
|
+
if (-not $env:ANDROID_HOME) { return $null }
|
|
103
|
+
$adb = Join-Path $env:ANDROID_HOME 'platform-tools\adb.exe'; $emu = Join-Path $env:ANDROID_HOME 'emulator\emulator.exe'
|
|
104
|
+
if ((Test-Path $adb) -and (Test-Path $emu)) { $v = (& $adb version) 2>$null | Select-Object -First 1; "platform-tools + emulator, $v" }
|
|
105
|
+
}
|
|
106
|
+
Invoke-Step -Title 'Android platform-tools (adb) + emulator' -Check { Get-SdkPackagesDetail } -Install {
|
|
107
|
+
$sm = Find-SdkManager $env:ANDROID_HOME
|
|
108
|
+
if (-not $sm) { throw 'no sdkmanager - the "Android SDK root" step must succeed first' }
|
|
109
|
+
# sdkmanager needs JAVA_HOME (set above). Licenses are Google's SDK terms - the same prompt Android
|
|
110
|
+
# Studio shows - accepted non-interactively here.
|
|
111
|
+
$yes = ('y' + "`n") * 40
|
|
112
|
+
$yes | & $sm "--sdk_root=$env:ANDROID_HOME" --licenses 2>&1 | Out-Null
|
|
113
|
+
Invoke-Native $sm @("--sdk_root=$env:ANDROID_HOME", 'platform-tools', 'emulator')
|
|
114
|
+
} -Verify { Get-SdkPackagesDetail } -Hint "sdkmanager --sdk_root=$env:ANDROID_HOME platform-tools emulator"
|
|
115
|
+
|
|
116
|
+
# Make the tools reachable for the rest of THIS run (the User environment covers new consoles).
|
|
117
|
+
if ($env:ANDROID_HOME) {
|
|
118
|
+
foreach ($d in @('platform-tools', 'emulator', 'cmdline-tools\latest\bin')) {
|
|
119
|
+
$p = Join-Path $env:ANDROID_HOME $d
|
|
120
|
+
if (-not (($env:Path -split ';') -contains $p)) { $env:Path = "$env:Path;$p" }
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# --- User environment (decision D3) ---------------------------------------------------------------
|
|
125
|
+
$EnvTitle = 'User environment (ANDROID_HOME, ANDROID_SDK_ROOT, JAVA_HOME, Path)'
|
|
126
|
+
$PathDirs = @('platform-tools', 'emulator', 'cmdline-tools\latest\bin')
|
|
127
|
+
if (-not $env:ANDROID_HOME -or -not $env:JAVA_HOME) {
|
|
128
|
+
Write-Warn $EnvTitle 'skipped - fix the JDK / SDK steps above first, then re-run'
|
|
129
|
+
} elseif ($script:SungenEnvNoProfile) {
|
|
130
|
+
Write-Skip $EnvTitle '-NoProfile'
|
|
131
|
+
} elseif ($script:SungenEnvCheckOnly) {
|
|
132
|
+
$missing = @()
|
|
133
|
+
foreach ($pair in @(@('ANDROID_HOME', $env:ANDROID_HOME), @('ANDROID_SDK_ROOT', $env:ANDROID_HOME), @('JAVA_HOME', $env:JAVA_HOME))) {
|
|
134
|
+
if ([Environment]::GetEnvironmentVariable($pair[0], 'User') -ne $pair[1]) { $missing += $pair[0] }
|
|
135
|
+
}
|
|
136
|
+
$userPath = @(([Environment]::GetEnvironmentVariable('Path', 'User')) -split ';')
|
|
137
|
+
foreach ($d in $PathDirs) { if (-not ($userPath -contains (Join-Path $env:ANDROID_HOME $d))) { $missing += "Path+=$d" } }
|
|
138
|
+
if ($missing.Count -gt 0) { Write-Fail $EnvTitle "not set for your user: $($missing -join ', ') - run without -Check to write them" }
|
|
139
|
+
else { Write-Skip $EnvTitle 'already set for your user' }
|
|
140
|
+
} else {
|
|
141
|
+
$changed = @()
|
|
142
|
+
if (Set-UserEnv 'ANDROID_HOME' $env:ANDROID_HOME) { $changed += 'ANDROID_HOME' }
|
|
143
|
+
if (Set-UserEnv 'ANDROID_SDK_ROOT' $env:ANDROID_HOME) { $changed += 'ANDROID_SDK_ROOT' }
|
|
144
|
+
if (Set-UserEnv 'JAVA_HOME' $env:JAVA_HOME) { $changed += 'JAVA_HOME' }
|
|
145
|
+
foreach ($d in $PathDirs) { if (Add-UserPath (Join-Path $env:ANDROID_HOME $d)) { $changed += "Path+=$d" } }
|
|
146
|
+
if ($changed.Count -gt 0) { Write-Ok $EnvTitle "set for your user: $($changed -join ', ') - open a NEW terminal to apply" }
|
|
147
|
+
else { Write-Skip $EnvTitle 'already set for your user' }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
# --- Android Studio - optional, GUI, never installed here ------------------------------------------
|
|
151
|
+
$studio = @((Join-Path $env:LOCALAPPDATA 'Programs\Android Studio'), (Join-Path $env:ProgramFiles 'Android\Android Studio')) | Where-Object { Test-Path $_ } | Select-Object -First 1
|
|
152
|
+
if ($studio) { Write-Skip 'Android Studio (optional)' $studio }
|
|
153
|
+
else { Write-Skip 'Android Studio (optional)' 'not installed - only needed for the AVD manager / an on-screen emulator: https://developer.android.com/studio' }
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# verify-android.ps1 - Windows: is the Android side usable right now? (adb, env vars, connected devices)
|
|
2
|
+
if (-not $script:SungenEnvLibLoaded) { . (Join-Path $PSScriptRoot '..\common\lib.ps1') }
|
|
3
|
+
|
|
4
|
+
# Prefer the adb under ANDROID_HOME (this run may have just installed it) over a stale one on PATH.
|
|
5
|
+
function Get-AndroidAdb {
|
|
6
|
+
if ($env:ANDROID_HOME) { $p = Join-Path $env:ANDROID_HOME 'platform-tools\adb.exe'; if (Test-Path $p) { return $p } }
|
|
7
|
+
$cmd = Get-Command adb -ErrorAction SilentlyContinue
|
|
8
|
+
if ($cmd) { return $cmd.Source }
|
|
9
|
+
return $null
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
Invoke-Check -Title 'adb' -Check {
|
|
13
|
+
$adb = Get-AndroidAdb; if (-not $adb) { return $null }
|
|
14
|
+
(& $adb version) 2>$null | Select-Object -First 1
|
|
15
|
+
} -Hint 'adb not found - re-run without -SkipAndroid, or open a NEW terminal if the environment was just written'
|
|
16
|
+
|
|
17
|
+
Invoke-Check -Title 'ANDROID_HOME / JAVA_HOME' -Check {
|
|
18
|
+
if ($env:ANDROID_HOME -and $env:JAVA_HOME) { "ANDROID_HOME=$env:ANDROID_HOME, JAVA_HOME=$env:JAVA_HOME" }
|
|
19
|
+
} -Hint 'not set in this console - open a NEW terminal (User environment) or set them'
|
|
20
|
+
|
|
21
|
+
Invoke-Check -Title 'Android devices' -Check {
|
|
22
|
+
$adb = Get-AndroidAdb; if (-not $adb) { return $null }
|
|
23
|
+
$lines = @((& $adb devices) 2>$null | Select-Object -Skip 1 | Where-Object { $_ -match '\sdevice$' })
|
|
24
|
+
if ($lines.Count -gt 0) { "$($lines.Count) device(s) connected" }
|
|
25
|
+
else { 'no device connected - plug in a phone (USB debugging on) or start an emulator before /sungen:run-test' }
|
|
26
|
+
} -Hint 'adb is not usable yet' -Warn
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
setup.ps1 - Windows entry point of the sungen Appium environment bundle.
|
|
4
|
+
.DESCRIPTION
|
|
5
|
+
.\appium-environment\setup.ps1 [-Check] [-SkipAndroid] [-NoProfile]
|
|
6
|
+
|
|
7
|
+
Emitted by `sungen init --mobile` / `sungen capability add mobile`, which also run it once.
|
|
8
|
+
Re-run it any time: every step is check -> install -> verify and a satisfied step is skipped.
|
|
9
|
+
-Check report what is missing, install nothing (side-effect free)
|
|
10
|
+
-SkipAndroid skip JDK / Android SDK / uiautomator2
|
|
11
|
+
-NoProfile do not write ANDROID_HOME / JAVA_HOME / Path to your User environment
|
|
12
|
+
iOS (xcuitest) needs Xcode and therefore a Mac - there is no iOS step on Windows.
|
|
13
|
+
Exit: 0 ready (or only warnings) - 1 something failed - 2 unsupported host
|
|
14
|
+
Run as: powershell -NoProfile -ExecutionPolicy Bypass -File .\appium-environment\setup.ps1
|
|
15
|
+
#>
|
|
16
|
+
[CmdletBinding()]
|
|
17
|
+
param([switch]$Check, [switch]$SkipAndroid, [switch]$NoProfile)
|
|
18
|
+
|
|
19
|
+
$ErrorActionPreference = 'Continue'
|
|
20
|
+
if ($PSVersionTable.PSVersion.Major -lt 5) { Write-Host 'Windows PowerShell 5.1 or newer is required.' -ForegroundColor Red; exit 2 }
|
|
21
|
+
if ($env:OS -ne 'Windows_NT') { Write-Host 'setup.ps1 is for Windows - on macOS run ./appium-environment/setup.sh' -ForegroundColor Red; exit 2 }
|
|
22
|
+
|
|
23
|
+
$script:SungenEnvCheckOnly = [bool]$Check
|
|
24
|
+
$script:SungenEnvSkipAndroid = [bool]$SkipAndroid
|
|
25
|
+
$script:SungenEnvNoProfile = [bool]$NoProfile
|
|
26
|
+
$script:ProjectDir = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
|
|
27
|
+
. (Join-Path $PSScriptRoot 'scripts\common\lib.ps1')
|
|
28
|
+
|
|
29
|
+
Set-Location $script:ProjectDir
|
|
30
|
+
if ($script:SungenEnvCheckOnly) { Write-Section 'sungen - Appium environment - check only (Windows)' }
|
|
31
|
+
else { Write-Section 'sungen - Appium environment setup (Windows)' }
|
|
32
|
+
Write-Host "project: $($script:ProjectDir)" -ForegroundColor DarkGray
|
|
33
|
+
|
|
34
|
+
# --- 1/4 prerequisites -----------------------------------------------------------------------------
|
|
35
|
+
Write-Section '1/4 Prerequisites'
|
|
36
|
+
. (Join-Path $PSScriptRoot 'scripts\common\verify-node.ps1')
|
|
37
|
+
Invoke-Check -Title 'winget (Windows Package Manager)' -Check {
|
|
38
|
+
if (Get-Command winget -ErrorAction SilentlyContinue) { $v = (& winget --version) 2>$null | Select-Object -First 1; "winget $v" }
|
|
39
|
+
} -Hint 'winget missing - install "App Installer" from the Microsoft Store (Windows 10 1809+ / 11), then re-run'
|
|
40
|
+
|
|
41
|
+
# --- 2/4 Android -----------------------------------------------------------------------------------
|
|
42
|
+
if (-not $script:SungenEnvSkipAndroid) {
|
|
43
|
+
Write-Section '2/4 Android (JDK, SDK, adb, emulator)'
|
|
44
|
+
. (Join-Path $PSScriptRoot 'scripts\windows\install-android.ps1')
|
|
45
|
+
} else {
|
|
46
|
+
Write-Section '2/4 Android'; Write-Skip 'Android toolchain' '-SkipAndroid'
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# --- 3/4 Appium ------------------------------------------------------------------------------------
|
|
50
|
+
Write-Section '3/4 Appium (project-local) + platform driver'
|
|
51
|
+
. (Join-Path $PSScriptRoot 'scripts\common\install-appium.ps1')
|
|
52
|
+
|
|
53
|
+
# --- 4/4 health ------------------------------------------------------------------------------------
|
|
54
|
+
Write-Section '4/4 Health check'
|
|
55
|
+
if (-not $script:SungenEnvSkipAndroid) { . (Join-Path $PSScriptRoot 'scripts\windows\verify-android.ps1') }
|
|
56
|
+
. (Join-Path $PSScriptRoot 'scripts\common\verify-appium.ps1')
|
|
57
|
+
|
|
58
|
+
exit (Write-Summary)
|