xgem-cli 2.0.0-alpha.10 → 2.0.0-alpha.14

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.
Files changed (43) hide show
  1. package/README.md +37 -63
  2. package/bin/xgem +46 -15
  3. package/bin/xgem.js +22 -20
  4. package/lib/bootstrap.sh +165 -0
  5. package/lib/ci.sh +44 -0
  6. package/lib/create.sh +140 -71
  7. package/lib/flutter.sh +10 -8
  8. package/lib/git.sh +291 -29
  9. package/lib/registry.sh +45 -0
  10. package/lib/release.sh +185 -0
  11. package/lib/scaffold.sh +21 -2
  12. package/lib/status.sh +42 -0
  13. package/lib/utils.sh +11 -0
  14. package/lib/version.sh +1 -1
  15. package/lib-win/bootstrap.js +189 -0
  16. package/lib-win/ci.js +43 -0
  17. package/lib-win/git.js +275 -21
  18. package/lib-win/registry.js +43 -0
  19. package/lib-win/release.js +143 -0
  20. package/lib-win/scaffold.js +64 -2
  21. package/lib-win/status.js +53 -0
  22. package/lib-win/utils.js +6 -1
  23. package/package.json +1 -1
  24. package/templates/node/build.sh.tmpl +9 -1
  25. package/templates/node/hard-clean.sh.tmpl +34 -2
  26. package/templates/node/lint.sh.tmpl +10 -0
  27. package/templates/node/start.sh.tmpl +9 -1
  28. package/templates/node/test.sh.tmpl +10 -0
  29. package/templates/webframework/build.sh.tmpl +10 -1
  30. package/templates/webframework/dev.sh.tmpl +9 -1
  31. package/templates/webframework/hard-clean.sh.tmpl +35 -2
  32. package/templates/webframework/lint.sh.tmpl +10 -0
  33. package/templates/webframework/test.sh.tmpl +10 -0
  34. package/templates-win/node/build.mjs.tmpl +8 -2
  35. package/templates-win/node/hard-clean.mjs.tmpl +24 -5
  36. package/templates-win/node/lint.mjs.tmpl +10 -0
  37. package/templates-win/node/start.mjs.tmpl +8 -1
  38. package/templates-win/node/test.mjs.tmpl +10 -0
  39. package/templates-win/webframework/build.mjs.tmpl +8 -1
  40. package/templates-win/webframework/dev.mjs.tmpl +8 -1
  41. package/templates-win/webframework/hard-clean.mjs.tmpl +28 -5
  42. package/templates-win/webframework/lint.mjs.tmpl +10 -0
  43. package/templates-win/webframework/test.mjs.tmpl +10 -0
package/README.md CHANGED
@@ -13,7 +13,13 @@ your environment, and `xgem git` wraps a common stage → commit → rebase-pull
13
13
  Flutter's iOS builds go through a dedicated engine that detects whether your
14
14
  project uses CocoaPods or Swift Package Manager (SPM), figures out the actual
15
15
  deployment target your resolved SPM plugins require (instead of guessing),
16
- and reconciles it automatically — see [Why the iOS build engine exists](#why-the-ios-build-engine-exists).
16
+ and reconciles it automatically.
17
+
18
+ **Full documentation: [docs/](https://github.com/Code4Hacker/x-gem-cli/blob/xgem/docs/README.md)** — getting started, every command in
19
+ depth, exactly what each framework's project-creation wizard does, the iOS build
20
+ engine explained, Windows support, and troubleshooting. This README is a summary.
21
+ (Absolute link on purpose — `docs/` isn't shipped inside the npm package itself,
22
+ so a relative link would 404 on npmjs.com's rendered README.)
17
23
 
18
24
  ## Install
19
25
 
@@ -56,6 +62,14 @@ xgem git init Setup local repo, attach remote tracker shortcuts
56
62
  xgem git branch Pick, create, or switch branches; remembers your choice
57
63
  xgem git rm-remote Drop a configured remote
58
64
  xgem git rm-branch Safely drop local and/or remote branch
65
+ xgem git pr Push current branch and open a GitHub PR (gh-backed)
66
+ xgem git sync Rebase current branch onto the repo's base branch
67
+ xgem git clean-branches Delete local branches already merged into the base branch
68
+ xgem git hooks <install|uninstall> Manage a pre-commit lint/test hook
69
+ xgem release [major|minor|patch] Bump version, update CHANGELOG.md, tag, push
70
+ xgem status Dashboard across every xgem-tracked project
71
+ xgem bootstrap Clone-to-running: detect framework, install, .env, migrate, launch
72
+ xgem ci Run lint/test/build for every configured framework, summarized
59
73
  xgem --version Print xgem's version
60
74
 
61
75
  Flags (any command): --yes (skip confirmations), --dry-run (show, don't apply), --verbose
@@ -68,31 +82,13 @@ flutter, node, python, react, vue, angular, next, go, rust, docker, swift
68
82
  ## Creating a new project
69
83
 
70
84
  `xgem init` asks, per framework, whether to create a brand-new project or use
71
- what's already in the directory:
72
-
73
- - **react**: Vite or Create React App ("the normal version"), then TypeScript
74
- or JavaScript, then current directory or a new folder.
75
- - **vue** / **next**: runs the official `create-vue`/`create-next-app` wizard
76
- directly, so you get their own real prompts (router, Tailwind, App Router,
77
- etc.) rather than a re-implementation of them.
78
- - **angular**: `ng new` (via a local Angular CLI if you have one, `npx
79
- @angular/cli` otherwise).
80
- - **flutter**: `flutter create`, then lists available devices/simulators
81
- (falling back to available-but-not-booted emulators you can launch) and
82
- runs on whichever you pick.
83
- - **node/python/go/rust/docker/swift**: the ecosystem's own minimal init
84
- command (`npm init`, a `.venv`, `cargo new`, `go mod init`, a starter
85
- Dockerfile, `swift package init`) — no sub-wizard, since these don't have
86
- an equivalent "dev server" to launch.
87
-
88
- For the frameworks with a dev server (react/vue/angular/next), after
89
- dependencies install xgem offers to start it and open your default browser —
90
- it tails the server's own log for the first `http://localhost:PORT` it
91
- prints rather than guessing a framework's default port, so it works even if
92
- that port's already taken and a different one gets picked.
93
-
94
- Answering "existing" (the default) skips all of this and behaves exactly
95
- like before — just scaffolds `.xgem-automate/` into the current directory.
85
+ what's already in the directory — real `create-vite`/`create-vue`/`ng new`/
86
+ `create-next-app`/`flutter create` scaffolding, with framework-specific
87
+ questions and a working dev-server/device launch at the end. Package manager
88
+ (npm/yarn/pnpm/bun) is auto-detected everywhere, never assumed. Answering
89
+ "existing" (the default) skips all of this and just scaffolds
90
+ `.xgem-automate/` into the current directory. Full detail, including exactly
91
+ what runs for each framework: **[docs/frameworks.md](https://github.com/Code4Hacker/x-gem-cli/blob/xgem/docs/frameworks.md)**.
96
92
 
97
93
  ## Architecture
98
94
 
@@ -118,47 +114,25 @@ of logic that can drift out of sync with two copies.
118
114
  ## Why the iOS build engine exists
119
115
 
120
116
  Flutter auto-generates a wrapper Swift package, `FlutterGeneratedPluginSwiftPackage`,
121
- that aggregates your plugins' SPM dependencies. Its declared minimum iOS platform can
122
- desync from your app's own `IPHONEOS_DEPLOYMENT_TARGET` — a confirmed, currently-open
123
- upstream Flutter bug ([flutter/flutter#186804](https://github.com/flutter/flutter/issues/186804),
117
+ whose declared minimum iOS platform can desync from your app's own
118
+ `IPHONEOS_DEPLOYMENT_TARGET` independently, even when your app's own target is
119
+ already high enough. This is a confirmed, currently-open upstream Flutter bug
120
+ ([flutter/flutter#186804](https://github.com/flutter/flutter/issues/186804),
124
121
  [#189422](https://github.com/flutter/flutter/issues/189422),
125
- [#162072](https://github.com/flutter/flutter/issues/162072)). A single `sed` patch to
126
- `project.pbxproj` doesn't reliably fix this: there are multiple deployment-target entries
127
- across Debug/Release/Profile × Runner/RunnerTests, and even a fully correct patch doesn't
128
- force Flutter to regenerate the generated package.
129
-
130
- `xgem run flutter build` (for the iOS target) instead:
131
-
132
- 1. Detects whether the project uses CocoaPods, SPM, or both.
133
- 2. Computes the deployment target actually required by your *resolved* SPM plugins
134
- (by reading their `ios/Package.swift` manifests via `.dart_tool/package_config.json`),
135
- instead of assuming a fixed value.
136
- 3. Shows a plan and asks for confirmation (unless `--yes`/`--dry-run`) before patching
137
- *every* `IPHONEOS_DEPLOYMENT_TARGET` occurrence in `project.pbxproj`.
138
- 4. Forces a clean regeneration (`flutter pub get` after clearing `ios/Flutter/ephemeral`)
139
- and verifies the regenerated package matches.
140
- 5. If it still doesn't (the upstream bug above), patches the generated package directly
141
- as a documented, loudly-logged last resort — never silently.
142
-
143
- Run `xgem doctor ios` any time for a standalone readiness report without doing a build.
122
+ [#162072](https://github.com/flutter/flutter/issues/162072)). `xgem run flutter build`
123
+ (iOS target) detects the actual requirement from your resolved SPM plugins, patches
124
+ every deployment-target occurrence, forces a clean regeneration, verifies it, and
125
+ falls back to a documented direct patch if the upstream bug is still biting — full
126
+ detail: **[docs/ios-build-engine.md](https://github.com/Code4Hacker/x-gem-cli/blob/xgem/docs/ios-build-engine.md)**. Run `xgem doctor ios`
127
+ any time for a standalone readiness report without doing a build.
144
128
 
145
129
  ## Windows support
146
130
 
147
- Homebrew and the curl installer are macOS/Linux only, same as any bash tool that's
148
- not going to change. npm is different: it's published with a small native Windows
149
- engine (`lib-win/`, driven by `bin/xgem.js`) so `npm install -g xgem-cli` actually works
150
- in `cmd.exe`/PowerShell, no WSL required.
151
-
152
- What works on Windows: `init`/`add`/`run`/`terminate`, `doctor`, the git workflow
153
- commands, and Flutter builds for **APK, App Bundle, and Windows desktop** targets. What doesn't:
154
- Flutter **iOS/macOS** builds — Xcode has no Windows equivalent, so `xgem doctor ios`
155
- explains that plainly instead of pretending. `swift` isn't offered as a framework
156
- choice on Windows for the same reason. If you're inside WSL, none of this applies —
157
- WSL reports itself as Linux, so you get the full bash engine automatically.
158
-
159
- On macOS/Linux, npm installs run the exact same bash engine as Homebrew/curl (`bin/xgem.js`
160
- is a thin passthrough that execs the real `bin/xgem` script) — there's only one
161
- implementation to trust on POSIX; Windows is the only platform with a second one.
131
+ Homebrew and the curl installer are macOS/Linux only, same as any bash tool. npm is
132
+ different: it ships a small native Windows engine (`lib-win/`) so `npm install -g
133
+ xgem-cli` actually works in `cmd.exe`/PowerShell, no WSL required everything except
134
+ Flutter iOS/macOS builds (Xcode has no Windows equivalent) and the project-creation
135
+ wizards (not ported yet). Full detail: **[docs/windows.md](https://github.com/Code4Hacker/x-gem-cli/blob/xgem/docs/windows.md)**.
162
136
 
163
137
  ## Roadmap
164
138
 
package/bin/xgem CHANGED
@@ -35,6 +35,16 @@ source "$XGEM_HOME/lib/flutter.sh"
35
35
  source "$XGEM_HOME/lib/scaffold.sh"
36
36
  # shellcheck source=lib/create.sh
37
37
  source "$XGEM_HOME/lib/create.sh"
38
+ # shellcheck source=lib/release.sh
39
+ source "$XGEM_HOME/lib/release.sh"
40
+ # shellcheck source=lib/registry.sh
41
+ source "$XGEM_HOME/lib/registry.sh"
42
+ # shellcheck source=lib/status.sh
43
+ source "$XGEM_HOME/lib/status.sh"
44
+ # shellcheck source=lib/bootstrap.sh
45
+ source "$XGEM_HOME/lib/bootstrap.sh"
46
+ # shellcheck source=lib/ci.sh
47
+ source "$XGEM_HOME/lib/ci.sh"
38
48
 
39
49
  CONFIG_DIR=".xgem-automate"
40
50
 
@@ -75,6 +85,14 @@ print_usage() {
75
85
  echo " xgem git branch - Pick, create, or switch branches; remembers your choice"
76
86
  echo " xgem git rm-remote - Drop specified target remote tracing rules"
77
87
  echo " xgem git rm-branch - Safely drop local and remote workspace branch states"
88
+ echo " xgem git pr - Push current branch and open a GitHub PR (gh-backed)"
89
+ echo " xgem git sync - Rebase current branch onto the repo's base branch"
90
+ echo " xgem git clean-branches - Delete local branches already merged into the base branch"
91
+ echo " xgem git hooks <install|uninstall> - Manage a pre-commit lint/test hook"
92
+ echo " xgem release [major|minor|patch] - Bump version, update CHANGELOG.md, tag, push"
93
+ echo " xgem status - Dashboard across every xgem-tracked project"
94
+ echo " xgem bootstrap - Clone-to-running: detect framework, install, .env, migrate, launch"
95
+ echo " xgem ci - Run lint/test/build for every configured framework, summarized"
78
96
  echo " xgem --version - Print xgem's version"
79
97
  echo ""
80
98
  echo "Flags (any command): --yes (skip confirmations), --dry-run (show, don't apply), --verbose"
@@ -91,26 +109,18 @@ case "${1:-}" in
91
109
  PS3="Enter target selection number: "
92
110
  select fw in "${ALL_FRAMEWORKS[@]}"; do
93
111
  if [ -n "$fw" ]; then
112
+ # create_project_wizard handles ALL of xgem's own
113
+ # bookkeeping (.xgem-automate + .gitignore) internally now,
114
+ # at the earliest safe point for each framework — never
115
+ # after a slow/interruptible step, so a Ctrl-C during (say)
116
+ # `ng new`'s install can no longer lose it. See lib/create.sh.
94
117
  create_project_wizard "$fw"
95
- mkdir -p "$CONFIG_DIR"
96
- scaffold_inject_templates "$fw" "$CONFIG_DIR"
97
- log_success "Successfully appended standard scripts for: $CONFIG_DIR/$fw"
98
118
  break
99
119
  else
100
120
  echo "Invalid selection."
101
121
  fi
102
122
  done
103
123
 
104
- if [ -f .gitignore ]; then
105
- if ! grep -q "$CONFIG_DIR/" .gitignore; then
106
- echo -e "\n$CONFIG_DIR/" >> .gitignore
107
- log_success "Added automation tracking to .gitignore"
108
- fi
109
- else
110
- echo "$CONFIG_DIR/" > .gitignore
111
- log_success "Created .gitignore and hidden tracking layer folder references."
112
- fi
113
-
114
124
  _ask_launch_dev_server
115
125
 
116
126
  # A child process (this script) can never change the parent
@@ -169,7 +179,7 @@ case "${1:-}" in
169
179
  else
170
180
  scaffold_run_script "$FRAMEWORK" "$SCRIPT_NAME" "$CONFIG_DIR"
171
181
  fi
172
- exit 0
182
+ exit $?
173
183
  ;;
174
184
 
175
185
  terminate)
@@ -186,6 +196,7 @@ case "${1:-}" in
186
196
  rm -rf "${CONFIG_DIR:?}/$fw"
187
197
  done
188
198
  rm -rf "$CONFIG_DIR"
199
+ registry_remove "$(pwd)"
189
200
 
190
201
  if [ -f .gitignore ]; then
191
202
  grep -v "^$CONFIG_DIR/" .gitignore > tmp_gitignore && mv tmp_gitignore .gitignore
@@ -197,7 +208,7 @@ case "${1:-}" in
197
208
  ;;
198
209
 
199
210
  git)
200
- [ -n "${2:-}" ] || die "Usage: xgem git <cmt|init|branch|rm-remote|rm-branch>"
211
+ [ -n "${2:-}" ] || die "Usage: xgem git <cmt|init|branch|rm-remote|rm-branch|pr|sync|clean-branches|hooks>"
201
212
  cmd_git "$2" "${3:-}"
202
213
  exit 0
203
214
  ;;
@@ -207,6 +218,26 @@ case "${1:-}" in
207
218
  exit 0
208
219
  ;;
209
220
 
221
+ release)
222
+ cmd_release "${2:-}"
223
+ exit 0
224
+ ;;
225
+
226
+ status)
227
+ cmd_status
228
+ exit 0
229
+ ;;
230
+
231
+ bootstrap)
232
+ cmd_bootstrap
233
+ exit 0
234
+ ;;
235
+
236
+ ci)
237
+ cmd_ci
238
+ exit $?
239
+ ;;
240
+
210
241
  --version|-V|version)
211
242
  print_version
212
243
  exit 0
package/bin/xgem.js CHANGED
@@ -21,6 +21,11 @@ const { cmdDoctor } = require('../lib-win/doctor');
21
21
  const { cmdGit } = require('../lib-win/git');
22
22
  const { cmdFlutter } = require('../lib-win/flutter');
23
23
  const scaffold = require('../lib-win/scaffold');
24
+ const { cmdRelease } = require('../lib-win/release');
25
+ const { registryAdd, registryRemove } = require('../lib-win/registry');
26
+ const { cmdStatus } = require('../lib-win/status');
27
+ const { cmdBootstrap } = require('../lib-win/bootstrap');
28
+ const { cmdCi } = require('../lib-win/ci');
24
29
 
25
30
  const CONFIG_DIR = '.xgem-automate';
26
31
 
@@ -48,6 +53,14 @@ function printUsage() {
48
53
  console.log(' xgem git branch - Pick, create, or switch branches; remembers your choice');
49
54
  console.log(' xgem git rm-remote - Drop specified target remote tracing rules');
50
55
  console.log(' xgem git rm-branch - Safely drop local and remote workspace branch states');
56
+ console.log(' xgem git pr - Push current branch and open a GitHub PR (gh-backed)');
57
+ console.log(' xgem git sync - Rebase current branch onto the repo\'s base branch');
58
+ console.log(' xgem git clean-branches - Delete local branches already merged into the base branch');
59
+ console.log(' xgem git hooks <install|uninstall> - Manage a pre-commit lint/test hook');
60
+ console.log(' xgem release [major|minor|patch] - Bump version, update CHANGELOG.md, tag, push');
61
+ console.log(' xgem status - Dashboard across every xgem-tracked project');
62
+ console.log(' xgem bootstrap - Clone-to-running: detect framework, install, .env, migrate, launch');
63
+ console.log(' xgem ci - Run lint/test/build for every configured framework, summarized');
51
64
  console.log(' xgem --version - Print xgem\'s version');
52
65
  console.log('');
53
66
  console.log('Flags (any command): --yes (skip confirmations), --dry-run (show, don\'t apply), --verbose');
@@ -61,20 +74,6 @@ async function selectFramework(options) {
61
74
  return options[idx];
62
75
  }
63
76
 
64
- function updateGitignore() {
65
- const gitignorePath = '.gitignore';
66
- if (fs.existsSync(gitignorePath)) {
67
- const content = fs.readFileSync(gitignorePath, 'utf8');
68
- if (!content.includes(`${CONFIG_DIR}/`)) {
69
- fs.appendFileSync(gitignorePath, `\n${CONFIG_DIR}/\n`);
70
- logSuccess('Added automation tracking to .gitignore');
71
- }
72
- } else {
73
- fs.writeFileSync(gitignorePath, `${CONFIG_DIR}/\n`);
74
- logSuccess('Created .gitignore and hidden tracking layer folder references.');
75
- }
76
- }
77
-
78
77
  async function cmdInit() {
79
78
  printBanner();
80
79
  if (fs.existsSync(CONFIG_DIR)) {
@@ -82,14 +81,12 @@ async function cmdInit() {
82
81
  return;
83
82
  }
84
83
  fs.mkdirSync(CONFIG_DIR, { recursive: true });
84
+ registryAdd(process.cwd());
85
85
 
86
86
  console.log('Select your starting framework architecture:');
87
87
  const fw = await selectFramework(scaffold.ALL_FRAMEWORKS);
88
88
  if (!fw) die('Invalid selection.');
89
- scaffold.injectTemplates(fw, CONFIG_DIR);
90
- logSuccess(`Successfully appended standard scripts for: ${CONFIG_DIR}/${fw}`);
91
-
92
- updateGitignore();
89
+ await scaffold.bookkeeping(fw, CONFIG_DIR);
93
90
  }
94
91
 
95
92
  async function cmdAdd() {
@@ -139,6 +136,7 @@ async function cmdTerminate() {
139
136
  fs.rmSync(path.join(CONFIG_DIR, fw), { recursive: true, force: true });
140
137
  }
141
138
  fs.rmSync(CONFIG_DIR, { recursive: true, force: true });
139
+ registryRemove(process.cwd());
142
140
 
143
141
  if (fs.existsSync('.gitignore')) {
144
142
  const lines = fs.readFileSync('.gitignore', 'utf8').split(/\r?\n/).filter((l) => l !== `${CONFIG_DIR}/`);
@@ -188,9 +186,13 @@ async function main() {
188
186
  case 'run': return cmdRun(a2, a3);
189
187
  case 'terminate': return cmdTerminate();
190
188
  case 'git':
191
- if (!a2) die('Usage: xgem git <cmt|init|branch|rm-remote|rm-branch>');
192
- return cmdGit(a2, a3);
189
+ if (!a2) die('Usage: xgem git <cmt|init|branch|rm-remote|rm-branch|pr|sync|clean-branches|hooks>');
190
+ return cmdGit(a2, a3, CONFIG_DIR);
193
191
  case 'doctor': return cmdDoctor(a2);
192
+ case 'release': return cmdRelease(a2);
193
+ case 'status': return cmdStatus(CONFIG_DIR);
194
+ case 'bootstrap': return cmdBootstrap(CONFIG_DIR);
195
+ case 'ci': return cmdCi(CONFIG_DIR);
194
196
  case '--version':
195
197
  case '-V':
196
198
  case 'version': {
@@ -0,0 +1,165 @@
1
+ #!/bin/bash
2
+ # xgem bootstrap: clone-to-running in one command, for a project you just
3
+ # checked out (not creating a new one — that's `xgem init`'s job).
4
+ # Depends on lib/logger.sh, lib/utils.sh, lib/scaffold.sh, lib/create.sh.
5
+
6
+ # _detect_framework -> a name from ALL_FRAMEWORKS, or empty if nothing
7
+ # recognizable was found. Never assumes; probes for marker files the same
8
+ # way lib/doctor.sh probes for tools instead of guessing.
9
+ _detect_framework() {
10
+ [ -f pubspec.yaml ] && { echo flutter; return 0; }
11
+ [ -f go.mod ] && { echo go; return 0; }
12
+ [ -f Cargo.toml ] && { echo rust; return 0; }
13
+ { [ -f pyproject.toml ] || [ -f requirements.txt ] || [ -f setup.py ]; } && { echo python; return 0; }
14
+ [ -f Package.swift ] && { echo swift; return 0; }
15
+ if [ -f package.json ]; then
16
+ if grep -q '"next"' package.json; then echo next
17
+ elif grep -q '"@angular/core"' package.json; then echo angular
18
+ elif grep -q '"vue"' package.json; then echo vue
19
+ elif grep -q '"react"' package.json; then echo react
20
+ else echo node
21
+ fi
22
+ return 0
23
+ fi
24
+ [ -f Dockerfile ] && { echo docker; return 0; }
25
+ return 1
26
+ }
27
+
28
+ _bootstrap_env_file() {
29
+ [ -f .env ] && { log_debug ".env already exists, leaving it as-is."; return 0; }
30
+ local example
31
+ for example in .env.example .env.sample; do
32
+ [ -f "$example" ] || continue
33
+ cp "$example" .env
34
+ log_success "Created .env from $example."
35
+ local missing
36
+ missing=$(grep -E '^[A-Za-z_][A-Za-z0-9_]*=[[:space:]]*$' .env | cut -d= -f1)
37
+ if [ -n "$missing" ]; then
38
+ log_warn "These .env keys are empty — fill them in:"
39
+ echo "$missing" | sed 's/^/ - /'
40
+ fi
41
+ return 0
42
+ done
43
+ }
44
+
45
+ _bootstrap_node_pm() {
46
+ if [ -f pnpm-lock.yaml ]; then echo pnpm
47
+ elif [ -f yarn.lock ]; then echo yarn
48
+ elif [ -f bun.lockb ] || [ -f bun.lock ]; then echo bun
49
+ else echo npm
50
+ fi
51
+ }
52
+
53
+ _bootstrap_install() {
54
+ local fw=$1
55
+ case "$fw" in
56
+ node|react|vue|angular|next)
57
+ local pm
58
+ pm=$(_bootstrap_node_pm)
59
+ log_info "Installing dependencies with $pm..."
60
+ case "$pm" in
61
+ yarn) yarn ;;
62
+ pnpm) pnpm install ;;
63
+ bun) bun install ;;
64
+ *) npm install ;;
65
+ esac
66
+ ;;
67
+ flutter)
68
+ has_cmd flutter || { log_warn "flutter not found — skipping 'flutter pub get'."; return 0; }
69
+ log_info "Running flutter pub get..."
70
+ flutter pub get
71
+ ;;
72
+ python)
73
+ if [ -f pyproject.toml ] && has_cmd poetry; then
74
+ log_info "Installing dependencies with poetry..."
75
+ poetry install
76
+ elif [ -f requirements.txt ]; then
77
+ has_cmd python3 || { log_warn "python3 not found — skipping install."; return 0; }
78
+ [ -d .venv ] || python3 -m venv .venv
79
+ log_info "Installing dependencies into .venv..."
80
+ .venv/bin/pip install -r requirements.txt
81
+ fi
82
+ ;;
83
+ go)
84
+ has_cmd go || { log_warn "go not found — skipping 'go mod download'."; return 0; }
85
+ log_info "Running go mod download..."
86
+ go mod download
87
+ ;;
88
+ rust)
89
+ has_cmd cargo || { log_warn "cargo not found — skipping 'cargo fetch'."; return 0; }
90
+ log_info "Running cargo fetch..."
91
+ cargo fetch
92
+ ;;
93
+ swift)
94
+ has_cmd swift || { log_warn "swift not found — skipping 'swift package resolve'."; return 0; }
95
+ log_info "Running swift package resolve..."
96
+ swift package resolve
97
+ ;;
98
+ docker)
99
+ log_debug "Docker project — nothing to install locally."
100
+ ;;
101
+ esac
102
+ }
103
+
104
+ _bootstrap_migrations() {
105
+ local fw=$1
106
+ if [ "$fw" = "node" ] || [ "$fw" = "react" ] || [ "$fw" = "vue" ] || [ "$fw" = "angular" ] || [ "$fw" = "next" ]; then
107
+ if _package_json_has_script migrate; then
108
+ local pm
109
+ pm=$(_bootstrap_node_pm)
110
+ confirm "Run the project's 'migrate' script (via $pm)?" || return 0
111
+ case "$pm" in
112
+ yarn) yarn migrate ;;
113
+ pnpm) pnpm run migrate ;;
114
+ bun) bun run migrate ;;
115
+ *) npm run migrate ;;
116
+ esac
117
+ return 0
118
+ fi
119
+ fi
120
+ if [ -f prisma/schema.prisma ]; then
121
+ confirm "Run 'npx prisma migrate dev'?" && npx prisma migrate dev
122
+ elif [ -f manage.py ]; then
123
+ has_cmd python3 && confirm "Run 'python3 manage.py migrate'?" && python3 manage.py migrate
124
+ fi
125
+ }
126
+
127
+ _bootstrap_offer_dev_server() {
128
+ local fw=$1
129
+ local script=""
130
+ case "$fw" in
131
+ node) script=start ;;
132
+ react|vue|angular|next) script=dev ;;
133
+ *) return 0 ;;
134
+ esac
135
+ [ -f "$CONFIG_DIR/$fw/$script.sh" ] || return 0
136
+ confirm "Launch the dev server now ('xgem run $fw $script')?" && scaffold_run_script "$fw" "$script" "$CONFIG_DIR"
137
+ }
138
+
139
+ cmd_bootstrap() {
140
+ log_info "Detecting project framework..."
141
+ local fw
142
+ fw=$(_detect_framework)
143
+ if [ -z "$fw" ]; then
144
+ log_warn "Could not auto-detect the framework from any known marker file."
145
+ echo "Pick one:"
146
+ PS3="Enter target selection number: "
147
+ select fw in "${ALL_FRAMEWORKS[@]}"; do
148
+ [ -n "$fw" ] && break
149
+ echo "Invalid selection."
150
+ done
151
+ else
152
+ log_success "Detected: $fw"
153
+ fi
154
+
155
+ _bootstrap_env_file
156
+ _bootstrap_install "$fw"
157
+ _bootstrap_migrations "$fw"
158
+
159
+ if [ ! -d "$CONFIG_DIR" ]; then
160
+ confirm "Scaffold xgem automation scripts (.xgem-automate) for $fw too?" && _xgem_bookkeeping "$fw"
161
+ fi
162
+
163
+ _bootstrap_offer_dev_server "$fw"
164
+ log_success "Bootstrap complete."
165
+ }
package/lib/ci.sh ADDED
@@ -0,0 +1,44 @@
1
+ #!/bin/bash
2
+ # xgem ci: run lint -> test -> build for every configured framework before
3
+ # you push, so a CI failure gets caught locally first.
4
+ # Depends on lib/logger.sh.
5
+
6
+ cmd_ci() {
7
+ [ -d "$CONFIG_DIR" ] || die "No $CONFIG_DIR found — run 'xgem init' or 'xgem add' first."
8
+
9
+ local -a steps=(lint test build)
10
+ local -a results=()
11
+ local overall_status=0
12
+ local fw_dir fw step
13
+
14
+ for fw_dir in "$CONFIG_DIR"/*/; do
15
+ [ -d "$fw_dir" ] || continue
16
+ fw=$(basename "$fw_dir")
17
+ for step in "${steps[@]}"; do
18
+ [ -f "$CONFIG_DIR/$fw/$step.sh" ] || continue
19
+ log_info "Running $fw $step..."
20
+ if bash "$CONFIG_DIR/$fw/$step.sh"; then
21
+ results+=("PASS $fw $step")
22
+ else
23
+ results+=("FAIL $fw $step")
24
+ overall_status=1
25
+ fi
26
+ done
27
+ done
28
+
29
+ if [ ${#results[@]} -eq 0 ]; then
30
+ log_warn "No lint/test/build scripts found under $CONFIG_DIR to run."
31
+ return 0
32
+ fi
33
+
34
+ echo ""
35
+ echo "=== xgem ci summary ==="
36
+ printf '%s\n' "${results[@]}"
37
+
38
+ if [ "$overall_status" -eq 0 ]; then
39
+ log_success "All checks passed."
40
+ else
41
+ log_error "Some checks failed."
42
+ fi
43
+ return "$overall_status"
44
+ }