xgem-cli 2.0.0-alpha.6 → 2.0.0-alpha.8

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/README.md CHANGED
@@ -49,6 +49,7 @@ xgem doctor ios [path] Report on iOS build readiness: CocoaPods vs SPM, d
49
49
  targets per config, and whether they're reconciled
50
50
  xgem git cmt "message" Auto-stage, commit, rebase-pull, and push
51
51
  xgem git init Setup local repo, attach remote tracker shortcuts
52
+ xgem git branch Pick, create, or switch branches; remembers your choice
52
53
  xgem git rm-remote Drop a configured remote
53
54
  xgem git rm-branch Safely drop local and/or remote branch
54
55
  xgem --version Print xgem's version
@@ -70,7 +71,7 @@ lib/
70
71
  doctor.sh environment + feature detection ("xgem doctor")
71
72
  ios.sh the SwiftPM-aware iOS build engine
72
73
  flutter.sh flutter command group (build/hard-clean/build-runner), delegates iOS builds to ios.sh
73
- git.sh git cmt/init/rm-remote/rm-branch
74
+ git.sh git cmt/init/branch/rm-remote/rm-branch
74
75
  scaffold.sh generic clean/build/dev handling for the other, simpler frameworks
75
76
  templates/ the actual clean/build/dev script content xgem scaffolds into your project's .xgem-automate/
76
77
  ```
@@ -115,7 +116,7 @@ engine (`lib-win/`, driven by `bin/xgem.js`) so `npm install -g xgem-cli` actual
115
116
  in `cmd.exe`/PowerShell, no WSL required.
116
117
 
117
118
  What works on Windows: `init`/`add`/`run`/`terminate`, `doctor`, the git workflow
118
- commands, and Flutter builds for **APK and Windows desktop** targets. What doesn't:
119
+ commands, and Flutter builds for **APK, App Bundle, and Windows desktop** targets. What doesn't:
119
120
  Flutter **iOS/macOS** builds — Xcode has no Windows equivalent, so `xgem doctor ios`
120
121
  explains that plainly instead of pretending. `swift` isn't offered as a framework
121
122
  choice on Windows for the same reason. If you're inside WSL, none of this applies —
package/bin/xgem CHANGED
@@ -70,6 +70,7 @@ print_usage() {
70
70
  echo " xgem doctor [ios] - Report on your environment / iOS SwiftPM build readiness"
71
71
  echo " xgem git cmt \"message\" - Auto-stage, commit, rebase-pull, and push"
72
72
  echo " xgem git init - Setup local repo, attach remote tracker shortcuts"
73
+ echo " xgem git branch - Pick, create, or switch branches; remembers your choice"
73
74
  echo " xgem git rm-remote - Drop specified target remote tracing rules"
74
75
  echo " xgem git rm-branch - Safely drop local and remote workspace branch states"
75
76
  echo " xgem --version - Print xgem's version"
@@ -182,7 +183,7 @@ case "${1:-}" in
182
183
  ;;
183
184
 
184
185
  git)
185
- [ -n "${2:-}" ] || die "Usage: xgem git <cmt|init|rm-remote|rm-branch>"
186
+ [ -n "${2:-}" ] || die "Usage: xgem git <cmt|init|branch|rm-remote|rm-branch>"
186
187
  cmd_git "$2" "${3:-}"
187
188
  exit 0
188
189
  ;;
package/bin/xgem.js CHANGED
@@ -45,6 +45,7 @@ function printUsage() {
45
45
  console.log(' xgem doctor [ios] - Report on your environment / iOS build availability');
46
46
  console.log(' xgem git cmt "message" - Auto-stage, commit, rebase-pull, and push');
47
47
  console.log(' xgem git init - Setup local repo, attach remote tracker shortcuts');
48
+ console.log(' xgem git branch - Pick, create, or switch branches; remembers your choice');
48
49
  console.log(' xgem git rm-remote - Drop specified target remote tracing rules');
49
50
  console.log(' xgem git rm-branch - Safely drop local and remote workspace branch states');
50
51
  console.log(' xgem --version - Print xgem\'s version');
@@ -164,7 +165,7 @@ function printFrameworkHelp(fw) {
164
165
  }
165
166
  if (fw === 'flutter') {
166
167
  console.log('');
167
- logInfo("Flutter builds on Windows support APK and Windows desktop targets. Run 'xgem doctor ios' for why iOS/macOS aren't available here.");
168
+ logInfo("Flutter builds on Windows support APK, App Bundle, and Windows desktop targets. Run 'xgem doctor ios' for why iOS/macOS aren't available here.");
168
169
  }
169
170
  console.log('');
170
171
  }
@@ -187,7 +188,7 @@ async function main() {
187
188
  case 'run': return cmdRun(a2, a3);
188
189
  case 'terminate': return cmdTerminate();
189
190
  case 'git':
190
- if (!a2) die('Usage: xgem git <cmt|init|rm-remote|rm-branch>');
191
+ if (!a2) die('Usage: xgem git <cmt|init|branch|rm-remote|rm-branch>');
191
192
  return cmdGit(a2, a3);
192
193
  case 'doctor': return cmdDoctor(a2);
193
194
  case '--version':
package/lib/flutter.sh CHANGED
@@ -189,20 +189,21 @@ cmd_flutter_build() {
189
189
  echo ""
190
190
  echo "Select Target Platform:"
191
191
  echo "1) APK (Android)"
192
- echo "2) iOS"
193
- echo "3) macOS"
194
- echo "4) Windows"
195
- echo "5) Linux"
192
+ echo "2) App Bundle (Android, .aab — required for Play Store uploads)"
193
+ echo "3) iOS"
194
+ echo "4) macOS"
195
+ echo "5) Windows"
196
+ echo "6) Linux"
196
197
  local platform_choice
197
- read -r -p "Choose [1-5]: " platform_choice
198
- if ! [[ "$platform_choice" =~ ^[1-5]$ ]]; then
198
+ read -r -p "Choose [1-6]: " platform_choice
199
+ if ! [[ "$platform_choice" =~ ^[1-6]$ ]]; then
199
200
  die "Invalid platform choice '$platform_choice'."
200
201
  fi
201
202
 
202
203
  local mode="--release"
203
204
  [[ "$is_release" == "n" || "$is_release" == "N" ]] && mode="--debug"
204
205
 
205
- if [ "$platform_choice" -eq 2 ]; then
206
+ if [ "$platform_choice" -eq 3 ]; then
206
207
  _flutter_build_ios "$build_name" "$build_number" "$mode"
207
208
  return
208
209
  fi
@@ -210,9 +211,10 @@ cmd_flutter_build() {
210
211
  log_info "Initializing Flutter Build ($mode) for version $build_name+$build_number..."
211
212
  case $platform_choice in
212
213
  1) flutter build apk "$mode" "--build-name=$build_name" "--build-number=$build_number" ;;
213
- 3) flutter build macos "$mode" "--build-name=$build_name" "--build-number=$build_number" ;;
214
- 4) flutter build windows "$mode" "--build-name=$build_name" "--build-number=$build_number" ;;
215
- 5) flutter build linux "$mode" "--build-name=$build_name" "--build-number=$build_number" ;;
214
+ 2) flutter build appbundle "$mode" "--build-name=$build_name" "--build-number=$build_number" ;;
215
+ 4) flutter build macos "$mode" "--build-name=$build_name" "--build-number=$build_number" ;;
216
+ 5) flutter build windows "$mode" "--build-name=$build_name" "--build-number=$build_number" ;;
217
+ 6) flutter build linux "$mode" "--build-name=$build_name" "--build-number=$build_number" ;;
216
218
  esac
217
219
  }
218
220
 
package/lib/git.sh CHANGED
@@ -46,6 +46,29 @@ cmd_git_cmt() {
46
46
  return 0
47
47
  fi
48
48
 
49
+ # A pull needs something to pull FROM. If this branch has never been
50
+ # pushed before, there's no remote ref to pull, and `git pull --rebase`
51
+ # fails with "couldn't find remote ref <branch>" — which is not a
52
+ # connection problem or a conflict, just a brand-new branch. Check for
53
+ # that specific case first so it gets its own accurate message instead
54
+ # of being lumped in with real connectivity failures.
55
+ git ls-remote --exit-code --heads "$remote_name" "$current_branch" >/dev/null 2>&1
56
+ local ls_remote_status=$?
57
+
58
+ if [ "$ls_remote_status" -eq 2 ]; then
59
+ log_info "'$current_branch' doesn't exist on '$remote_name' yet — pushing to create it..."
60
+ if git push -u "$remote_name" "$current_branch"; then
61
+ log_success "Git workflow complete! Branch created and pushed."
62
+ else
63
+ die "Push operation failed."
64
+ fi
65
+ return 0
66
+ elif [ "$ls_remote_status" -ne 0 ]; then
67
+ log_error "Could not reach '$remote_name' to check for '$current_branch' — this looks like a real connection problem."
68
+ log_warn "Your commit is safe locally. Re-run 'xgem git cmt' once connectivity is restored, or push manually: git push $remote_name $current_branch"
69
+ exit 1
70
+ fi
71
+
49
72
  local ahead_count
50
73
  ahead_count=$(git rev-list --count "$remote_name/$current_branch..$current_branch" 2>/dev/null)
51
74
  if [ "$ahead_count" = "0" ]; then
@@ -163,13 +186,69 @@ cmd_git_rm_branch() {
163
186
  fi
164
187
  }
165
188
 
166
- # xgem git <cmt|init|rm-remote|rm-branch> ...
189
+ # cmd_git_branch: lists local branches, lets the user pick one (or create a
190
+ # new one), checks it out, and remembers it as this repo's default so
191
+ # there's a quick way back to it later. `xgem git cmt` always operates on
192
+ # whatever's actually checked out (that's the only thing that's correct for
193
+ # a commit), so this command's job is the checkout + remembering, not
194
+ # changing how cmt picks its branch.
195
+ cmd_git_branch() {
196
+ local -a branches=()
197
+ while IFS= read -r line; do
198
+ [ -n "$line" ] && branches+=("$line")
199
+ done < <(git branch --format='%(refname:short)' 2>/dev/null)
200
+
201
+ [ ${#branches[@]} -gt 0 ] || die "No local branches found."
202
+
203
+ local current_branch
204
+ current_branch=$(git branch --show-current 2>/dev/null)
205
+
206
+ echo "Available branches:"
207
+ local i=1 b
208
+ for b in "${branches[@]}"; do
209
+ if [ "$b" = "$current_branch" ]; then
210
+ echo "$i) $b (current)"
211
+ else
212
+ echo "$i) $b"
213
+ fi
214
+ i=$((i + 1))
215
+ done
216
+ local create_option=$i
217
+ echo "$create_option) Create new branch"
218
+
219
+ local choice
220
+ read -r -p "Select [1-$create_option]: " choice
221
+
222
+ if [ "$choice" = "$create_option" ]; then
223
+ local new_branch
224
+ read -r -p "Enter new branch name: " new_branch
225
+ [ -n "$new_branch" ] || die "Branch name cannot be empty."
226
+ git checkout -b "$new_branch" || die "Could not create branch '$new_branch'."
227
+ git config --local xgem.default-branch "$new_branch"
228
+ log_success "Created and switched to '$new_branch', set as default for this repo."
229
+ return 0
230
+ fi
231
+
232
+ if ! [[ "$choice" =~ ^[0-9]+$ ]] || [ "$choice" -lt 1 ] || [ "$choice" -gt ${#branches[@]} ]; then
233
+ die "Invalid selection."
234
+ fi
235
+
236
+ local selected="${branches[$((choice - 1))]}"
237
+ if [ "$selected" != "$current_branch" ]; then
238
+ git checkout "$selected" || die "Could not switch to branch '$selected'."
239
+ fi
240
+ git config --local xgem.default-branch "$selected"
241
+ log_success "Switched to '$selected' and set as default for this repo."
242
+ }
243
+
244
+ # xgem git <cmt|init|rm-remote|rm-branch|branch> ...
167
245
  cmd_git() {
168
246
  case "$1" in
169
247
  cmt) cmd_git_cmt "$2" ;;
170
248
  init) cmd_git_init ;;
171
249
  rm-remote) cmd_git_rm_remote ;;
172
250
  rm-branch) cmd_git_rm_branch ;;
173
- *) die "Unknown git subcommand '$1'. Usage: xgem git <cmt|init|rm-remote|rm-branch>" ;;
251
+ branch) cmd_git_branch ;;
252
+ *) die "Unknown git subcommand '$1'. Usage: xgem git <cmt|init|rm-remote|rm-branch|branch>" ;;
174
253
  esac
175
254
  }
package/lib/version.sh CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/bin/bash
2
2
  # xgem version info.
3
3
 
4
- XGEM_VERSION="2.0.0-alpha.6"
4
+ XGEM_VERSION="2.0.0-alpha.8"
5
5
 
6
6
  print_version() {
7
7
  echo "xgem $XGEM_VERSION"
@@ -67,9 +67,10 @@ async function cmdBuild() {
67
67
 
68
68
  console.log('\nSelect Target Platform:');
69
69
  console.log('1) APK (Android)');
70
- console.log('2) Windows');
70
+ console.log('2) App Bundle (Android, .aab — required for Play Store uploads)');
71
+ console.log('3) Windows');
71
72
  console.log('(iOS/macOS require a Mac; Linux desktop can\'t be cross-built from Windows.)');
72
- const platformChoice = await prompt('Choose [1-2]');
73
+ const platformChoice = await prompt('Choose [1-3]');
73
74
 
74
75
  const mode = (isRelease === 'n') ? '--debug' : '--release';
75
76
  const buildArgs = [`--build-name=${buildName}`, `--build-number=${buildNumber}`];
@@ -78,6 +79,9 @@ async function cmdBuild() {
78
79
  logInfo(`Building APK (${mode})...`);
79
80
  flutter(['build', 'apk', mode, ...buildArgs]);
80
81
  } else if (platformChoice === '2') {
82
+ logInfo(`Building App Bundle (${mode})...`);
83
+ flutter(['build', 'appbundle', mode, ...buildArgs]);
84
+ } else if (platformChoice === '3') {
81
85
  logInfo(`Building Windows desktop app (${mode})...`);
82
86
  flutter(['build', 'windows', mode, ...buildArgs]);
83
87
  } else {
package/lib-win/git.js CHANGED
@@ -60,6 +60,26 @@ async function cmdCmt(commitMsg) {
60
60
  return;
61
61
  }
62
62
 
63
+ // A pull needs something to pull FROM. If this branch has never been
64
+ // pushed before, there's no remote ref, and `git pull --rebase` fails
65
+ // with "couldn't find remote ref <branch>" — not a connection problem
66
+ // or a conflict, just a brand-new branch. Check for that case first.
67
+ const lsRemoteStatus = spawnSync('git', ['ls-remote', '--exit-code', '--heads', remoteName, currentBranch]).status;
68
+
69
+ if (lsRemoteStatus === 2) {
70
+ logInfo(`'${currentBranch}' doesn't exist on '${remoteName}' yet — pushing to create it...`);
71
+ if (git(['push', '-u', remoteName, currentBranch]).status === 0) {
72
+ logSuccess('Git workflow complete! Branch created and pushed.');
73
+ } else {
74
+ die('Push operation failed.');
75
+ }
76
+ return;
77
+ } else if (lsRemoteStatus !== 0) {
78
+ logError(`Could not reach '${remoteName}' to check for '${currentBranch}' — this looks like a real connection problem.`);
79
+ logWarn(`Your commit is safe locally. Re-run 'xgem git cmt' once connectivity is restored, or push manually: git push ${remoteName} ${currentBranch}`);
80
+ process.exit(1);
81
+ }
82
+
63
83
  const aheadCount = gitCapture(['rev-list', '--count', `${remoteName}/${currentBranch}..${currentBranch}`]);
64
84
  if (aheadCount === '0') {
65
85
  logSuccess(`Already up to date with '${remoteName}/${currentBranch}' — nothing to push.`);
@@ -168,13 +188,53 @@ async function cmdRmBranch() {
168
188
  }
169
189
  }
170
190
 
191
+ // Lists local branches, lets the user pick one (or create a new one),
192
+ // checks it out, and remembers it as this repo's default. cmt always
193
+ // operates on whatever's actually checked out — this command's job is the
194
+ // checkout + remembering, not changing how cmt picks its branch.
195
+ async function cmdBranch() {
196
+ const branches = gitCapture(['branch', '--format=%(refname:short)']).split(/\r?\n/).filter(Boolean);
197
+ if (branches.length === 0) die('No local branches found.');
198
+
199
+ const currentBranch = gitCapture(['branch', '--show-current']);
200
+
201
+ console.log('Available branches:');
202
+ branches.forEach((b, i) => {
203
+ console.log(`${i + 1}) ${b}${b === currentBranch ? ' (current)' : ''}`);
204
+ });
205
+ const createOption = branches.length + 1;
206
+ console.log(`${createOption}) Create new branch`);
207
+
208
+ const choice = await prompt(`Select [1-${createOption}]`);
209
+
210
+ if (choice === String(createOption)) {
211
+ const newBranch = await prompt('Enter new branch name');
212
+ if (!newBranch) die('Branch name cannot be empty.');
213
+ if (git(['checkout', '-b', newBranch]).status !== 0) die(`Could not create branch '${newBranch}'.`);
214
+ git(['config', '--local', 'xgem.default-branch', newBranch]);
215
+ logSuccess(`Created and switched to '${newBranch}', set as default for this repo.`);
216
+ return;
217
+ }
218
+
219
+ const idx = parseInt(choice, 10) - 1;
220
+ if (Number.isNaN(idx) || idx < 0 || idx >= branches.length) die('Invalid selection.');
221
+
222
+ const selected = branches[idx];
223
+ if (selected !== currentBranch) {
224
+ if (git(['checkout', selected]).status !== 0) die(`Could not switch to branch '${selected}'.`);
225
+ }
226
+ git(['config', '--local', 'xgem.default-branch', selected]);
227
+ logSuccess(`Switched to '${selected}' and set as default for this repo.`);
228
+ }
229
+
171
230
  async function cmdGit(sub, arg) {
172
231
  switch (sub) {
173
232
  case 'cmt': return cmdCmt(arg);
174
233
  case 'init': return cmdInit();
234
+ case 'branch': return cmdBranch();
175
235
  case 'rm-remote': return cmdRmRemote();
176
236
  case 'rm-branch': return cmdRmBranch();
177
- default: die(`Unknown git subcommand '${sub}'. Usage: xgem git <cmt|init|rm-remote|rm-branch>`);
237
+ default: die(`Unknown git subcommand '${sub}'. Usage: xgem git <cmt|init|branch|rm-remote|rm-branch>`);
178
238
  }
179
239
  }
180
240
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xgem-cli",
3
- "version": "2.0.0-alpha.6",
3
+ "version": "2.0.0-alpha.8",
4
4
  "description": "Framework-aware automation CLI: scaffolds and runs clean/build/dev scripts per project type, an environment doctor, a SwiftPM-aware iOS build engine, and a git workflow helper. Runs natively on macOS, Linux, and Windows.",
5
5
  "bin": {
6
6
  "xgem": "bin/xgem.js"