xgem-cli 2.0.0-alpha.4 → 2.0.0-alpha.6
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/lib/doctor.sh +4 -1
- package/lib/flutter.sh +30 -5
- package/lib/ios.sh +75 -13
- package/lib/version.sh +1 -1
- package/package.json +1 -1
package/lib/doctor.sh
CHANGED
|
@@ -26,7 +26,10 @@ flutter_supports_flag() {
|
|
|
26
26
|
flutter_config_spm_enabled() {
|
|
27
27
|
has_cmd flutter || { echo "unknown (flutter not found)"; return; }
|
|
28
28
|
local line
|
|
29
|
-
|
|
29
|
+
# Anchored to "enable-swift-package-manager:" (the current-value line)
|
|
30
|
+
# so this doesn't instead match the "--[no-]enable-swift-package-manager"
|
|
31
|
+
# flag-syntax help line that `flutter config` also prints.
|
|
32
|
+
line=$(flutter config 2>/dev/null | grep -im1 -E '^[[:space:]]*enable-swift-package-manager:')
|
|
30
33
|
if [ -z "$line" ]; then
|
|
31
34
|
echo "unknown (not reported by this Flutter version)"
|
|
32
35
|
else
|
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
|
|
package/lib/ios.sh
CHANGED
|
@@ -60,8 +60,13 @@ ios_deployment_target_for_config() {
|
|
|
60
60
|
|
|
61
61
|
if has_cmd xcodebuild; then
|
|
62
62
|
local value
|
|
63
|
+
# Anchored: newer Xcode also emits DEPLOYMENT_TARGET_SETTING_NAME =
|
|
64
|
+
# IPHONEOS_DEPLOYMENT_TARGET (a meta-setting whose *value* is that
|
|
65
|
+
# string), which an unanchored grep matches before the real
|
|
66
|
+
# "IPHONEOS_DEPLOYMENT_TARGET = 15.6" line — anchoring to the key
|
|
67
|
+
# position avoids grabbing that decoy.
|
|
63
68
|
value=$(xcodebuild -showBuildSettings "${xcb_args[@]}" -configuration "$config" 2>/dev/null \
|
|
64
|
-
| grep -m1 'IPHONEOS_DEPLOYMENT_TARGET' | awk '{print $NF}')
|
|
69
|
+
| grep -m1 -E '^[[:space:]]*IPHONEOS_DEPLOYMENT_TARGET[[:space:]]*=' | awk '{print $NF}')
|
|
65
70
|
[ -n "$value" ] && { echo "$value"; return 0; }
|
|
66
71
|
fi
|
|
67
72
|
|
|
@@ -80,15 +85,36 @@ ios_pbxproj_current_max_target() {
|
|
|
80
85
|
}
|
|
81
86
|
|
|
82
87
|
# ios_required_spm_deployment_target [project_dir]
|
|
83
|
-
#
|
|
84
|
-
#
|
|
85
|
-
#
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
+
# Returns the highest .iOS(.vNN) platform floor declared across every
|
|
89
|
+
# resolved SwiftPM plugin, instead of hardcoding a guessed value.
|
|
90
|
+
#
|
|
91
|
+
# Primary source: ios/Flutter/ephemeral/Packages/.packages/<name>-<version>/,
|
|
92
|
+
# which `flutter pub get` itself populates with each SPM-enabled plugin as
|
|
93
|
+
# part of dependency resolution — this is the actual, already-resolved
|
|
94
|
+
# location Xcode/SPM uses, not a path we have to reconstruct. Scanning it
|
|
95
|
+
# directly sidesteps having to guess each plugin's on-disk manifest layout
|
|
96
|
+
# (which varies — not every plugin puts Package.swift at <root>/ios/).
|
|
97
|
+
#
|
|
98
|
+
# Falls back to parsing .dart_tool/package_config.json's rootUri entries
|
|
99
|
+
# (assuming the common <root>/ios/Package.swift convention) only if that
|
|
100
|
+
# ephemeral directory doesn't exist yet — e.g. before any pub get has run.
|
|
88
101
|
ios_required_spm_deployment_target() {
|
|
89
102
|
local project_dir=${1:-.}
|
|
90
|
-
local
|
|
103
|
+
local packages_dir="$project_dir/ios/Flutter/ephemeral/Packages/.packages"
|
|
104
|
+
|
|
105
|
+
if [ -d "$packages_dir" ]; then
|
|
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 '_' '.' \
|
|
111
|
+
| sort -g | tail -1
|
|
112
|
+
return 0
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
log_debug "No resolved SwiftPM packages directory at $packages_dir yet; falling back to package_config.json."
|
|
91
116
|
|
|
117
|
+
local pkg_config="$project_dir/.dart_tool/package_config.json"
|
|
92
118
|
[ -f "$pkg_config" ] || { log_debug "No .dart_tool/package_config.json — run flutter pub get first."; return 1; }
|
|
93
119
|
has_cmd python3 || { log_debug "python3 not found; cannot parse package_config.json."; return 1; }
|
|
94
120
|
|
|
@@ -118,9 +144,12 @@ for pkg in data.get("packages", []):
|
|
|
118
144
|
except OSError:
|
|
119
145
|
continue
|
|
120
146
|
|
|
121
|
-
|
|
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)
|
|
122
151
|
try:
|
|
123
|
-
fv = float(
|
|
152
|
+
fv = float(raw.replace("_", "."))
|
|
124
153
|
except ValueError:
|
|
125
154
|
continue
|
|
126
155
|
max_target = max(max_target, fv)
|
|
@@ -164,21 +193,40 @@ ios_generated_package_swift_path() {
|
|
|
164
193
|
echo "$guess"
|
|
165
194
|
return 0
|
|
166
195
|
fi
|
|
167
|
-
|
|
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" \
|
|
168
200
|
-path "*FlutterGeneratedPluginSwiftPackage*" 2>/dev/null | head -1
|
|
169
201
|
}
|
|
170
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
|
+
|
|
171
216
|
ios_generated_package_target() {
|
|
172
217
|
local project_dir=${1:-.}
|
|
173
218
|
local pkg
|
|
174
219
|
pkg=$(ios_generated_package_swift_path "$project_dir")
|
|
175
220
|
[ -n "$pkg" ] && [ -f "$pkg" ] || return 1
|
|
176
|
-
|
|
221
|
+
_ios_extract_platform_version < "$pkg"
|
|
177
222
|
}
|
|
178
223
|
|
|
179
224
|
# Last-resort workaround for the still-open upstream desync bug: directly
|
|
180
225
|
# patch the generated package's platform declaration. Always logged loudly
|
|
181
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.
|
|
182
230
|
ios_patch_generated_package_target() {
|
|
183
231
|
local new_target=$1
|
|
184
232
|
local project_dir=${2:-.}
|
|
@@ -190,10 +238,9 @@ ios_patch_generated_package_target() {
|
|
|
190
238
|
fi
|
|
191
239
|
|
|
192
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."
|
|
193
|
-
local token=${new_target//./_}
|
|
194
241
|
local tmp
|
|
195
242
|
tmp=$(mktemp)
|
|
196
|
-
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"
|
|
197
244
|
mv "$tmp" "$pkg"
|
|
198
245
|
log_success "Patched generated package platform to iOS $new_target."
|
|
199
246
|
}
|
|
@@ -205,6 +252,21 @@ ios_regenerate_generated_package() {
|
|
|
205
252
|
( cd "$project_dir" && flutter pub get )
|
|
206
253
|
}
|
|
207
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
|
+
|
|
208
270
|
ios_known_issue_check() {
|
|
209
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."
|
|
210
272
|
}
|
package/lib/version.sh
CHANGED
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.6",
|
|
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"
|