xgem-cli 2.0.0-alpha.5 → 2.0.0-alpha.7
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 +1 -1
- package/bin/xgem.js +1 -1
- package/lib/flutter.sh +42 -15
- package/lib/ios.sh +47 -9
- package/lib/version.sh +1 -1
- package/lib-win/flutter.js +6 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -115,7 +115,7 @@ engine (`lib-win/`, driven by `bin/xgem.js`) so `npm install -g xgem-cli` actual
|
|
|
115
115
|
in `cmd.exe`/PowerShell, no WSL required.
|
|
116
116
|
|
|
117
117
|
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:
|
|
118
|
+
commands, and Flutter builds for **APK, App Bundle, and Windows desktop** targets. What doesn't:
|
|
119
119
|
Flutter **iOS/macOS** builds — Xcode has no Windows equivalent, so `xgem doctor ios`
|
|
120
120
|
explains that plainly instead of pretending. `swift` isn't offered as a framework
|
|
121
121
|
choice on Windows for the same reason. If you're inside WSL, none of this applies —
|
package/bin/xgem.js
CHANGED
|
@@ -164,7 +164,7 @@ function printFrameworkHelp(fw) {
|
|
|
164
164
|
}
|
|
165
165
|
if (fw === 'flutter') {
|
|
166
166
|
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.");
|
|
167
|
+
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
168
|
}
|
|
169
169
|
console.log('');
|
|
170
170
|
}
|
package/lib/flutter.sh
CHANGED
|
@@ -105,7 +105,7 @@ _flutter_build_ios() {
|
|
|
105
105
|
mkdir -p "$archive_dir"
|
|
106
106
|
archive_path="$archive_dir/Runner $archive_time.xcarchive"
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
local -a archive_cmd=(xcodebuild -workspace ios/Runner.xcworkspace \
|
|
109
109
|
-scheme Runner \
|
|
110
110
|
-sdk iphoneos \
|
|
111
111
|
-configuration Release \
|
|
@@ -113,10 +113,35 @@ _flutter_build_ios() {
|
|
|
113
113
|
-archivePath "$archive_path" \
|
|
114
114
|
"BUILD_NUMBER=$build_number" \
|
|
115
115
|
"MARKETING_VERSION=$build_name" \
|
|
116
|
-
-allowProvisioningUpdates
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
-allowProvisioningUpdates)
|
|
117
|
+
|
|
118
|
+
if ! "${archive_cmd[@]}" > "$log_file" 2>&1; then
|
|
119
|
+
# Static pre-checks can miss cases the real SwiftPM resolution
|
|
120
|
+
# catches (different plugin manifest layouts, symlinked package
|
|
121
|
+
# dirs, etc.) — Xcode's own error is authoritative, so parse it
|
|
122
|
+
# directly and retry once before giving up.
|
|
123
|
+
local retry_required
|
|
124
|
+
retry_required=$(ios_parse_required_from_build_log "$log_file")
|
|
125
|
+
if [ -n "$retry_required" ]; then
|
|
126
|
+
log_warn "Archive failed on a SwiftPM platform-version mismatch Xcode reports directly (requires iOS $retry_required). Retrying with that applied..."
|
|
127
|
+
ios_pbxproj_patch_deployment_target "$retry_required" "."
|
|
128
|
+
ios_regenerate_generated_package "."
|
|
129
|
+
local regenerated
|
|
130
|
+
regenerated=$(ios_generated_package_target ".")
|
|
131
|
+
if [ -z "$regenerated" ] || _ios_ver_lt "$regenerated" "$retry_required"; then
|
|
132
|
+
ios_patch_generated_package_target "$retry_required" "."
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
if ! "${archive_cmd[@]}" > "$log_file" 2>&1; then
|
|
136
|
+
log_error "Archive FAILED again after retry"
|
|
137
|
+
cat "$log_file"
|
|
138
|
+
die "xcodebuild archive failed" "$?"
|
|
139
|
+
fi
|
|
140
|
+
else
|
|
141
|
+
log_error "Archive FAILED"
|
|
142
|
+
cat "$log_file"
|
|
143
|
+
die "xcodebuild archive failed" "$?"
|
|
144
|
+
fi
|
|
120
145
|
fi
|
|
121
146
|
log_success "Archive created at: $archive_path"
|
|
122
147
|
|
|
@@ -164,20 +189,21 @@ cmd_flutter_build() {
|
|
|
164
189
|
echo ""
|
|
165
190
|
echo "Select Target Platform:"
|
|
166
191
|
echo "1) APK (Android)"
|
|
167
|
-
echo "2)
|
|
168
|
-
echo "3)
|
|
169
|
-
echo "4)
|
|
170
|
-
echo "5)
|
|
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"
|
|
171
197
|
local platform_choice
|
|
172
|
-
read -r -p "Choose [1-
|
|
173
|
-
if ! [[ "$platform_choice" =~ ^[1-
|
|
198
|
+
read -r -p "Choose [1-6]: " platform_choice
|
|
199
|
+
if ! [[ "$platform_choice" =~ ^[1-6]$ ]]; then
|
|
174
200
|
die "Invalid platform choice '$platform_choice'."
|
|
175
201
|
fi
|
|
176
202
|
|
|
177
203
|
local mode="--release"
|
|
178
204
|
[[ "$is_release" == "n" || "$is_release" == "N" ]] && mode="--debug"
|
|
179
205
|
|
|
180
|
-
if [ "$platform_choice" -eq
|
|
206
|
+
if [ "$platform_choice" -eq 3 ]; then
|
|
181
207
|
_flutter_build_ios "$build_name" "$build_number" "$mode"
|
|
182
208
|
return
|
|
183
209
|
fi
|
|
@@ -185,9 +211,10 @@ cmd_flutter_build() {
|
|
|
185
211
|
log_info "Initializing Flutter Build ($mode) for version $build_name+$build_number..."
|
|
186
212
|
case $platform_choice in
|
|
187
213
|
1) flutter build apk "$mode" "--build-name=$build_name" "--build-number=$build_number" ;;
|
|
188
|
-
|
|
189
|
-
4) flutter build
|
|
190
|
-
5) flutter build
|
|
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" ;;
|
|
191
218
|
esac
|
|
192
219
|
}
|
|
193
220
|
|
package/lib/ios.sh
CHANGED
|
@@ -103,9 +103,11 @@ ios_required_spm_deployment_target() {
|
|
|
103
103
|
local packages_dir="$project_dir/ios/Flutter/ephemeral/Packages/.packages"
|
|
104
104
|
|
|
105
105
|
if [ -d "$packages_dir" ]; then
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
# -L: per-plugin dirs here are frequently symlinks into pub-cache;
|
|
107
|
+
# without -L, find silently skips descending into them.
|
|
108
|
+
find -L "$packages_dir" -maxdepth 4 -name "Package.swift" -print0 2>/dev/null \
|
|
109
|
+
| xargs -0 grep -ohE '\.iOS\(\.v[0-9_]+\)|\.iOS\("[0-9.]+"\)' 2>/dev/null \
|
|
110
|
+
| grep -oE '[0-9]+(\.[0-9]+)?(_[0-9]+)?' | tr '_' '.' \
|
|
109
111
|
| sort -g | tail -1
|
|
110
112
|
return 0
|
|
111
113
|
fi
|
|
@@ -142,9 +144,12 @@ for pkg in data.get("packages", []):
|
|
|
142
144
|
except OSError:
|
|
143
145
|
continue
|
|
144
146
|
|
|
145
|
-
|
|
147
|
+
# Both SPM syntaxes: enum .iOS(.v15) and string .iOS("15.0") (the form
|
|
148
|
+
# Flutter's own generator uses).
|
|
149
|
+
for m in re.finditer(r'\.iOS\(\.v(\d+(?:_\d+)?)\)|\.iOS\("(\d+(?:\.\d+)?)"\)', content):
|
|
150
|
+
raw = m.group(1) or m.group(2)
|
|
146
151
|
try:
|
|
147
|
-
fv = float(
|
|
152
|
+
fv = float(raw.replace("_", "."))
|
|
148
153
|
except ValueError:
|
|
149
154
|
continue
|
|
150
155
|
max_target = max(max_target, fv)
|
|
@@ -188,21 +193,40 @@ ios_generated_package_swift_path() {
|
|
|
188
193
|
echo "$guess"
|
|
189
194
|
return 0
|
|
190
195
|
fi
|
|
191
|
-
|
|
196
|
+
# -L: plugin package directories under ephemeral/Packages/.packages are
|
|
197
|
+
# frequently symlinks into the pub-cache; a plain find silently skips
|
|
198
|
+
# symlinked directories instead of descending into them.
|
|
199
|
+
find -L "$project_dir/ios" -maxdepth 6 -type f -name "Package.swift" \
|
|
192
200
|
-path "*FlutterGeneratedPluginSwiftPackage*" 2>/dev/null | head -1
|
|
193
201
|
}
|
|
194
202
|
|
|
203
|
+
# SPM's SupportedPlatform.iOS accepts two syntaxes: the enum form .iOS(.v15)
|
|
204
|
+
# for the fixed known-version cases, and a string form .iOS("15.0") for
|
|
205
|
+
# arbitrary versions — Flutter's own code generator uses the *string* form
|
|
206
|
+
# for FlutterGeneratedPluginSwiftPackage, not the enum form. Matching only
|
|
207
|
+
# the enum form (as an earlier version of this function did) means the
|
|
208
|
+
# patch step silently never matches anything on a real generated package:
|
|
209
|
+
# sed finds no match, writes back an identical file, and "succeeds" while
|
|
210
|
+
# doing nothing.
|
|
211
|
+
_ios_extract_platform_version() {
|
|
212
|
+
grep -oE '\.iOS\(\.v[0-9_]+\)|\.iOS\("[0-9.]+"\)' | head -1 \
|
|
213
|
+
| grep -oE '[0-9]+(\.[0-9]+)?(_[0-9]+)?' | tr '_' '.'
|
|
214
|
+
}
|
|
215
|
+
|
|
195
216
|
ios_generated_package_target() {
|
|
196
217
|
local project_dir=${1:-.}
|
|
197
218
|
local pkg
|
|
198
219
|
pkg=$(ios_generated_package_swift_path "$project_dir")
|
|
199
220
|
[ -n "$pkg" ] && [ -f "$pkg" ] || return 1
|
|
200
|
-
|
|
221
|
+
_ios_extract_platform_version < "$pkg"
|
|
201
222
|
}
|
|
202
223
|
|
|
203
224
|
# Last-resort workaround for the still-open upstream desync bug: directly
|
|
204
225
|
# patch the generated package's platform declaration. Always logged loudly
|
|
205
226
|
# — this is a documented workaround for a known bug, never a silent hack.
|
|
227
|
+
# Writes the string form (.iOS("15.0")) since that's what Flutter's own
|
|
228
|
+
# generator uses in this file — matching its existing convention rather
|
|
229
|
+
# than introducing a different syntax.
|
|
206
230
|
ios_patch_generated_package_target() {
|
|
207
231
|
local new_target=$1
|
|
208
232
|
local project_dir=${2:-.}
|
|
@@ -214,10 +238,9 @@ ios_patch_generated_package_target() {
|
|
|
214
238
|
fi
|
|
215
239
|
|
|
216
240
|
log_warn "Working around known Flutter SPM bug (flutter/flutter#186804): the regenerated package still doesn't match your deployment target, so patching it directly."
|
|
217
|
-
local token=${new_target//./_}
|
|
218
241
|
local tmp
|
|
219
242
|
tmp=$(mktemp)
|
|
220
|
-
sed -E "s/\.iOS\(\.v[0-9_]+\)/.iOS(.
|
|
243
|
+
sed -E "s/\.iOS\(\.v[0-9_]+\)/.iOS(\"${new_target}\")/g; s/\.iOS\(\"[0-9.]+\"\)/.iOS(\"${new_target}\")/g" "$pkg" > "$tmp"
|
|
221
244
|
mv "$tmp" "$pkg"
|
|
222
245
|
log_success "Patched generated package platform to iOS $new_target."
|
|
223
246
|
}
|
|
@@ -229,6 +252,21 @@ ios_regenerate_generated_package() {
|
|
|
229
252
|
( cd "$project_dir" && flutter pub get )
|
|
230
253
|
}
|
|
231
254
|
|
|
255
|
+
# ios_parse_required_from_build_log <log_file>
|
|
256
|
+
# Statically scanning plugin manifests to predict the required deployment
|
|
257
|
+
# target has proven unreliable in practice (plugin authors structure SPM
|
|
258
|
+
# manifests differently, package dirs can be symlinks, etc.). Xcode itself
|
|
259
|
+
# always computes this correctly and states it plainly in its own error:
|
|
260
|
+
# "requires minimum platform version X for the iOS platform". Parsing that
|
|
261
|
+
# directly is the authoritative fallback when the static pre-check misses
|
|
262
|
+
# something.
|
|
263
|
+
ios_parse_required_from_build_log() {
|
|
264
|
+
local log_file=$1
|
|
265
|
+
grep -oE "requires minimum platform version [0-9]+(\.[0-9]+)?" "$log_file" 2>/dev/null \
|
|
266
|
+
| grep -oE '[0-9]+(\.[0-9]+)?' \
|
|
267
|
+
| sort -g | tail -1
|
|
268
|
+
}
|
|
269
|
+
|
|
232
270
|
ios_known_issue_check() {
|
|
233
271
|
log_warn "Known upstream issue (flutter/flutter#186804, #189422, #162072): FlutterGeneratedPluginSwiftPackage's deployment target can desync from your app's IPHONEOS_DEPLOYMENT_TARGET — independently, even when the app's own target is already high enough. Currently open — xgem detects and works around it automatically below."
|
|
234
272
|
}
|
package/lib/version.sh
CHANGED
package/lib-win/flutter.js
CHANGED
|
@@ -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)
|
|
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-
|
|
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xgem-cli",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.7",
|
|
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"
|