dev
sh 624 lines 17.8 KB
Raw
1 #!/bin/bash
2
3 # Hello! o/
4 # If you are think there's a chance that you
5 # 1) want to update flutter sdk
6 # 2) want to update some dependency
7 # 3) want to add some dependency
8 # 4) are forking a dependency
9 # 5) something in the build process broke and you want to blame dependency pinning
10 # 6) the script is failing
11 #
12 # In cases 1, 2, 3 you don't need to edit this script at all. Just add the dependency
13 # to either pubspec_base.yaml or cw_*/pubspec.yaml. and re-run this script.
14 # In case 4 (or 6), do the same and add the dependency to
15 # package_repo_redirects - to redirect the dependency to your fork.
16 # repo_redirects - if the dependency was moved between git repos.
17 # version_ref_maps / dart_sdk_packages - if the dependency cannot be found upstream.
18 # In case 5 remove pubspec_overrides.yaml, and just build the app. There is a non-zero
19 # chance that it won't work. And if it works look for the diff between whatever is on git
20 # and whatever is published on pub.dev tarball.
21 # The script is designed in a way in which it is optional (so not using it should
22 # still result in a working build).
23 # In case of doubt git blame and ask the author ^^
24
25 set -e
26
27 cd "$(dirname "$0")/.."
28
29 yq() {
30 go run github.com/mikefarah/yq/v4@latest "$@"
31 }
32
33 lockfile="$(pwd)/pubspec.lock"
34 pubspec_base="$(pwd)/pubspec_base.yaml"
35 overrides_file="$(pwd)/pubspec_overrides.yaml"
36 sdk_git_url=https://github.com/dart-lang/sdk
37
38 resolved_git_path=""
39 resolved_ref=""
40 git_url=""
41 override_url=""
42 override_path=""
43 version_ref_map_skip_verify=0
44
45 # old git url|new git url
46 repo_redirects=(
47 "https://github.com/cmdrootaccess/another-flushbar|https://github.com/ideployed/another-flushbar"
48 "https://github.com/anicdh|https://github.com/MrCyjaneK/bs58check.git"
49 "https://github.com/MedzikUser/libcrypto-dart|https://github.com/MrCyjaneK/libcrypto.git"
50 "https://github.com/rive-app/rive-flutter|https://github.com/MrCyjaneK/rive-flutter-no-lfs"
51 "https://github.com/dart-lang/ffi|https://github.com/dart-lang/native"
52 )
53
54 # package|old git url|new git url (to move only one package out of a shared repo)
55 package_repo_redirects=(
56 "rive_common|https://github.com/rive-app/rive-flutter|https://github.com/MrCyjaneK/rive_common"
57 "fixnum_nanodart|https://github.com/dart-lang/fixnum|https://github.com/MrCyjaneK/fixnum_nanodart"
58 )
59
60 # package-version|git hash (exact match, or package-* to skip version check)
61 version_ref_maps=(
62 "ffi-2.1.0|6bd0935f29c7294ebb9aab21c06b47b513def933"
63 "flutter_rust_bridge-2.11.1|f19087cd0cac019b82d52cfa5153ce81b1b6e902"
64 )
65
66 dart_sdk_ref=$(cat $(dirname $(dirname $(which flutter)))/bin/cache/dart-sdk/revision)
67 dart_sdk_packages=(
68 js vm_service
69 )
70
71 # Kept on pub.dev
72 skip_git_pin_packages=(
73 _fe_analyzer_shared
74 analyzer
75 meta
76 )
77
78 lookup_version_ref_map() {
79 local pkg=$1 ver=$2 entry key ref sdk_pkg
80 version_ref_map_skip_verify=0
81 for sdk_pkg in "${dart_sdk_packages[@]}"; do
82 [[ "$pkg" == "$sdk_pkg" ]] || continue
83 version_ref_map_skip_verify=1
84 echo "$dart_sdk_ref"
85 return 0
86 done
87 for entry in "${version_ref_maps[@]}"; do
88 key="${entry%%|*}"
89 ref="${entry#*|}"
90 if [[ "$key" == "${pkg}-${ver}" ]]; then
91 echo "$ref"
92 return 0
93 fi
94 if [[ "$key" == "${pkg}-*" ]]; then
95 version_ref_map_skip_verify=1
96 echo "$ref"
97 return 0
98 fi
99 done
100 return 1
101 }
102
103 is_dart_sdk_package() {
104 local pkg=$1 sdk_pkg
105 for sdk_pkg in "${dart_sdk_packages[@]}"; do
106 [[ "$pkg" == "$sdk_pkg" ]] && return 0
107 done
108 return 1
109 }
110
111 is_skip_git_pin_package() {
112 local pkg=$1 skip_pkg
113 for skip_pkg in "${skip_git_pin_packages[@]}"; do
114 [[ "$pkg" == "$skip_pkg" ]] && return 0
115 done
116 return 1
117 }
118
119 apply_default_overrides() {
120 [[ -f "$pubspec_base" ]] || return 0
121
122 awk '
123 /^# dependency_overrides:/ {
124 in_block = 1
125 print "dependency_overrides:"
126 next
127 }
128 in_block && /^#[[:space:]]/ {
129 sub(/^#[[:space:]]?/, "")
130 print
131 next
132 }
133 in_block && /^#/ {
134 sub(/^#/, "")
135 print
136 next
137 }
138 in_block { exit }
139 ' "$pubspec_base" > "$overrides_file"
140
141 if [[ ! -s "$overrides_file" ]]; then
142 rm -f "$overrides_file"
143 return 0
144 fi
145
146 echo "applied default overrides from ${pubspec_base}"
147 }
148
149 normalize_url() {
150 echo "$1" | sed 's|\.git$||'
151 }
152
153 normalize_git_path() {
154 local path=$1
155 [[ -z "$path" || "$path" == "." ]] && return 0
156 [[ "$path" == ./* ]] && { echo "$path"; return 0; }
157 echo "./${path}"
158 }
159
160 redirect_git_url() {
161 local url=$1 package=${2:-} entry rest old new
162 url=$(normalize_url "$url")
163
164 if [[ -n "$package" ]]; then
165 for entry in "${package_repo_redirects[@]}"; do
166 [[ "${entry%%|*}" != "$package" ]] && continue
167 rest="${entry#*|}"
168 old=$(normalize_url "${rest%%|*}")
169 new=$(normalize_url "${rest#*|}")
170 [[ "$url" == "$old" ]] && { echo "$new"; return 0; }
171 done
172 fi
173
174 for entry in "${repo_redirects[@]}"; do
175 old=$(normalize_url "${entry%%|*}")
176 new=$(normalize_url "${entry#*|}")
177 [[ "$url" == "$old" ]] && { echo "$new"; return 0; }
178 done
179 echo "$1"
180 }
181
182 parse_github_repo_url() {
183 local url=$1
184 if [[ "$url" =~ github\.com/([^/]+)/([^/?#]+) ]]; then
185 git_owner="${BASH_REMATCH[1]}"
186 git_repo="${BASH_REMATCH[2]%.git}"
187 git_url="https://github.com/${git_owner}/${git_repo}"
188 if [[ "$url" =~ github\.com/[^/]+/[^/]+/(tree|blob)/[^/]+/(.+) ]]; then
189 git_path="./${BASH_REMATCH[2]}"
190 else
191 git_path=""
192 fi
193 return 0
194 fi
195 return 1
196 }
197
198 pubdev_cache_file() {
199 echo "$(pwd)/scripts/.cache/pubdev/${1}/${2}.json"
200 }
201
202 pubdev_repo_url() {
203 local pkg=$1 ver=$2 cache_file
204 cache_file=$(pubdev_cache_file "$pkg" "$ver")
205 if [[ -f "$cache_file" ]]; then
206 jq -r '.pubspec.repository // .pubspec.homepage // empty' "$cache_file"
207 return 0
208 fi
209 mkdir -p "$(dirname "$cache_file")"
210 curl -sf "https://pub.dev/api/packages/${pkg}/versions/${ver}" >"$cache_file"
211 jq -r '.pubspec.repository // .pubspec.homepage // empty' "$cache_file"
212 }
213
214 repo_cache_dir() {
215 echo "scripts/.cache/repos/$(normalize_url "$1" | sed 's|https://github.com/||; s|/|_|g')"
216 }
217
218 locks_dir="$(pwd)/scripts/.cache/locks"
219 rm -rf $locks_dir
220 mkdir -p $locks_dir
221
222 repo_lock_file() {
223 local key=$1
224 if [[ "$key" == */* || "$key" == scripts/* ]]; then
225 echo "${locks_dir}/repo-$(basename "$key").lock"
226 else
227 echo "${locks_dir}/repo-$(normalize_url "$key" | sed 's|https://github.com/||; s|/|_|g').lock"
228 fi
229 }
230
231 with_lock() {
232 local lockpath=$1; shift
233 mkdir -p "$(dirname "$lockpath")"
234 while ! mkdir "$lockpath" 2>/dev/null; do
235 sleep 0.05
236 done
237 local rc=0
238 "$@" || rc=$?
239 rmdir "$lockpath" 2>/dev/null || true
240 return "$rc"
241 }
242
243 with_repo_lock() {
244 with_lock "$(repo_lock_file "$1")" "${@:2}"
245 }
246
247 with_overrides_lock() {
248 with_lock "${locks_dir}/overrides.lock" "$@"
249 }
250
251 _ensure_repo_clone() {
252 local url=$1 cache_dir=$2
253 [[ -d "${cache_dir}/.git" ]] && return 0
254 mkdir -p "$(dirname "$cache_dir")"
255 if [[ "$(normalize_url "$url")" == "$sdk_git_url" ]]; then
256 git clone --quiet --filter=blob:none "$url" "$cache_dir" || return 1
257 else
258 git clone --quiet "$url" "$cache_dir" || return 1
259 fi
260 }
261
262 ensure_repo_clone() {
263 local url=$1 cache_dir=$2
264 with_repo_lock "$cache_dir" _ensure_repo_clone "$url" "$cache_dir"
265 }
266
267 _ensure_git_ref() {
268 local cache_dir=$1 ref=$2
269 git -C "$cache_dir" cat-file -e "${ref}^{commit}" 2>/dev/null
270 }
271
272 ensure_git_ref() {
273 with_repo_lock "$1" _ensure_git_ref "$@"
274 }
275
276 pubspec_field() {
277 local content=$1 field=$2
278 echo "$content" |
279 grep -E "^${field}:[[:space:]]*" |
280 head -1 |
281 sed "s/^${field}:[[:space:]]*//" |
282 tr -d "\"'" |
283 sed 's/[[:space:]]*$//'
284 }
285
286 pubspec_matches() {
287 local content=$1 package=$2 ver=${3:-}
288 [[ "$(pubspec_field "$content" name)" == "$package" ]] || return 1
289 [[ -z "$ver" || "$(pubspec_field "$content" version)" == "$ver" ]]
290 }
291
292 build_path_candidates() {
293 local url=$1 package=$2 path=$3
294 local candidate candidates=() seen=""
295
296 add_candidate() {
297 local c=$1
298 if [[ -z "$c" ]]; then
299 [[ " $seen " == *" __ROOT__ "* ]] && return
300 seen="$seen __ROOT__"
301 candidates+=("")
302 return
303 fi
304 [[ " $seen " == *" $c "* ]] && return
305 seen="$seen $c"
306 candidates+=("$c")
307 }
308
309 if [[ -n "$path" ]]; then
310 local normalized="${path#./}"
311 normalized="${normalized%/}"
312 if [[ "$normalized" != "packages" ]]; then
313 local cache_dir content
314 cache_dir=$(repo_cache_dir "$url")
315 if ensure_repo_clone "$url" "$cache_dir"; then
316 content=$(git -C "$cache_dir" show "HEAD:${normalized}/pubspec.yaml" 2>/dev/null) || content=""
317 pubspec_matches "$content" "$package" && add_candidate "$normalized"
318 fi
319 fi
320 fi
321
322 local cache_dir pubspec_path dir content
323 cache_dir=$(repo_cache_dir "$url")
324 if ensure_repo_clone "$url" "$cache_dir"; then
325 while IFS= read -r pubspec_path; do
326 dir=$(dirname "$pubspec_path")
327 content=$(git -C "$cache_dir" show "HEAD:${pubspec_path}" 2>/dev/null) || continue
328 pubspec_matches "$content" "$package" || continue
329 add_candidate "$dir"
330 done < <(git -C "$cache_dir" ls-files | grep '/pubspec.yaml$')
331 fi
332
333 add_candidate "pkgs/${package}"
334 add_candidate "packages/${package}"
335 add_candidate "${package}"
336 add_candidate ""
337 printf '%s\n' "${candidates[@]}"
338 }
339
340 find_path_at_ref() {
341 local url=$1 ref=$2 package=$3 ver=$4 hint_path=$5
342 local cache_dir candidate pubspec_file content
343
344 cache_dir=$(repo_cache_dir "$url")
345 ensure_repo_clone "$url" "$cache_dir" || return 1
346 ensure_git_ref "$cache_dir" "$ref" || return 1
347
348 while IFS= read -r candidate; do
349 pubspec_file=$([[ -n "$candidate" ]] && echo "${candidate}/pubspec.yaml" || echo "pubspec.yaml")
350 content=$(git -C "$cache_dir" show "${ref}:${pubspec_file}" 2>/dev/null) || continue
351 pubspec_matches "$content" "$package" "$ver" || continue
352 [[ -n "$candidate" ]] && echo "./${candidate}"
353 return 0
354 done < <(build_path_candidates "$url" "$package" "$hint_path")
355 return 1
356 }
357
358 _resolve_tag_ref() {
359 local url=$1 pkg=$2 ver=$3 cache_dir ref tag
360
361 cache_dir=$(repo_cache_dir "$url")
362 _ensure_repo_clone "$url" "$cache_dir" || return 1
363
364 for tag in "${ver}" "v${ver}" "${pkg}-${ver}" "${pkg}-v${ver}"; do
365 ref=$(git -C "$cache_dir" rev-parse -q "refs/tags/${tag}^{commit}" 2>/dev/null) && {
366 echo "$ref"
367 return 0
368 }
369 ref=$(git -C "$cache_dir" rev-parse -q "refs/tags/${tag}" 2>/dev/null) && {
370 echo "$ref"
371 return 0
372 }
373 done
374 return 1
375 }
376
377 resolve_tag_ref() {
378 with_repo_lock "$1" _resolve_tag_ref "$@"
379 }
380
381 find_commit_for_package_version() {
382 local url=$1 package=$2 ver=$3 hint_path=$4
383 local cache_dir candidate pubspec_file ref content ver_try log_args
384 local -a version_attempts=("$ver")
385
386 [[ "$ver" != *-dev ]] && version_attempts+=("${ver}-dev")
387
388 cache_dir=$(repo_cache_dir "$url")
389 ensure_repo_clone "$url" "$cache_dir" || return 1
390
391 while IFS= read -r candidate; do
392 pubspec_file=$([[ -n "$candidate" ]] && echo "${candidate}/pubspec.yaml" || echo "pubspec.yaml")
393 for ver_try in "${version_attempts[@]}"; do
394 [[ "$ver_try" == "$ver" ]] && log_args=(--reverse) || log_args=()
395 for ref in $(git -C "$cache_dir" log "${log_args[@]}" --format=%H -- "$pubspec_file" 2>/dev/null); do
396 content=$(git -C "$cache_dir" show "${ref}:${pubspec_file}" 2>/dev/null) || continue
397 pubspec_matches "$content" "$package" "$ver_try" || continue
398 resolved_git_path=""
399 [[ -n "$candidate" ]] && resolved_git_path="./${candidate}"
400 resolved_ref="$ref"
401 return 0
402 done
403 done
404 done < <(build_path_candidates "$url" "$package" "$hint_path")
405 return 1
406 }
407
408 resolve_git_override_for_package() {
409 local package=$1 version=$2 repo_url=$3
410 local original_git_url hint_path tag_ref path
411
412 repo_url=$(redirect_git_url "$repo_url" "$package")
413 parse_github_repo_url "$repo_url" || return 1
414
415 original_git_url="$git_url"
416 hint_path="$git_path"
417
418 path=$(find_path_at_ref "$git_url" HEAD "$package" "" "$hint_path" || true)
419 if [[ -n "$path" ]]; then
420 hint_path="$path"
421 elif [[ -z "$hint_path" && "$git_url" != "$original_git_url" ]]; then
422 path=$(find_path_at_ref "$original_git_url" HEAD "$package" "" "" || true)
423 if [[ -n "$path" ]]; then
424 git_url="$original_git_url"
425 hint_path="$path"
426 fi
427 fi
428
429 resolved_git_path=""
430 resolved_ref=""
431
432 tag_ref=$(resolve_tag_ref "$git_url" "$package" "$version" || true)
433 if [[ -n "$tag_ref" ]] &&
434 path=$(find_path_at_ref "$git_url" "$tag_ref" "$package" "$version" "$hint_path"); then
435 resolved_git_path="$path"
436 resolved_ref="$tag_ref"
437 return 0
438 fi
439
440 find_commit_for_package_version "$git_url" "$package" "$version" "$hint_path"
441 }
442
443 resolve_mapped_override() {
444 local package=$1 version=$2 source=$3 lock_git_url=$4 lock_git_path=$5 mapped_ref=$6
445 local repo_url path_ver
446
447 if is_dart_sdk_package "$package"; then
448 override_url="$sdk_git_url"
449 override_path="./pkg/${package}"
450 return 0
451 fi
452
453 if [[ "$source" == "git" ]]; then
454 [[ -n "$lock_git_url" ]] || return 1
455 override_url=$(redirect_git_url "$lock_git_url" "$package")
456 override_path=$(normalize_git_path "$lock_git_path")
457 return 0
458 fi
459
460 [[ "$source" == "hosted" ]] || return 1
461 repo_url=$(pubdev_repo_url "$package" "$version")
462 [[ -n "$repo_url" ]] || return 1
463 repo_url=$(redirect_git_url "$repo_url" "$package")
464 parse_github_repo_url "$repo_url" || return 1
465 override_url="$git_url"
466 path_ver="$version"
467 [[ "$version_ref_map_skip_verify" == 1 ]] && path_ver=""
468 override_path=$(find_path_at_ref "$git_url" "$mapped_ref" "$package" "$path_ver" "" || true)
469 [[ -n "$override_path" ]]
470 }
471
472 has_git_override_in_yaml() {
473 local pkg=$1
474 [[ -f "$overrides_file" ]] || return 1
475 yq -e ".dependency_overrides.\"${pkg}\".git" "$overrides_file" >/dev/null 2>&1
476 }
477
478 _add_git_override_to_yaml() {
479 local pkg=$1 url=$2 path=$3 ref=$4 yq_expr
480
481 [[ -f "$overrides_file" ]] || printf 'dependency_overrides: {}\n' > "$overrides_file"
482 if has_git_override_in_yaml "$pkg"; then
483 echo "$pkg: OK (default override)"
484 return 0
485 fi
486
487 yq -i "del(.dependency_overrides.\"${pkg}\")" "$overrides_file"
488 yq_expr=".dependency_overrides.\"${pkg}\".git.url = \"${url}\" | .dependency_overrides.\"${pkg}\".git.ref = \"${ref}\""
489 [[ -n "$path" ]] && yq_expr+=" | .dependency_overrides.\"${pkg}\".git.path = \"${path}\""
490
491 yq -i "$yq_expr" "$overrides_file" || {
492 echo "$pkg: failed to update ${overrides_file}" >&2
493 return 1
494 }
495
496 echo "added to ${overrides_file}:"
497 echo " ${pkg}:"
498 echo " git:"
499 echo " url: ${url}"
500 [[ -n "$path" ]] && echo " path: ${path}"
501 echo " ref: ${ref}"
502 }
503
504 add_git_override_to_yaml() {
505 with_overrides_lock _add_git_override_to_yaml "$@"
506 }
507
508 sort_overrides_yaml() {
509 yq -i \
510 '.dependency_overrides = (.dependency_overrides | to_entries | sort_by(.key) | from_entries)' \
511 "$overrides_file"
512 }
513
514 rm -f "$overrides_file"
515 apply_default_overrides
516 ./configure_cake_wallet.sh android
517 flutter pub get
518
519 apply_default_overrides
520
521 mkdir -p "$locks_dir"
522
523 process_package() {
524 local pkg=$1 package source version git_url git_ref git_path mapped_ref repo_url
525
526 eval "$(jq -r '@sh "
527 package=\(.package)
528 source=\(.source)
529 version=\(.version)
530 git_url=\(.git_url)
531 git_ref=\(.git_ref)
532 git_path=\(.git_path)
533 "' <<< "$pkg")"
534
535 if has_git_override_in_yaml "$package"; then
536 echo "$package: OK (default override)"
537 return 0
538 fi
539
540 if is_skip_git_pin_package "$package"; then
541 echo "$package: OK (version pinned, skipped)"
542 return 0
543 fi
544
545 mapped_ref=$(lookup_version_ref_map "$package" "$version" || true)
546 if [[ -n "$mapped_ref" ]]; then
547 if ! resolve_mapped_override "$package" "$version" "$source" "$git_url" "$git_path" "$mapped_ref"; then
548 echo "$package: NOT OK - could not apply version_ref_maps entry for ${version}" >&2
549 return 1
550 fi
551 add_git_override_to_yaml "$package" "$override_url" "$override_path" "$mapped_ref" || return 1
552 return 0
553 fi
554
555 case "$source" in
556 git)
557 if [[ -z "$git_url" || -z "$git_ref" ]]; then
558 echo "$package: NOT OK - git dependency missing url or ref in ${lockfile}" >&2
559 return 1
560 fi
561 add_git_override_to_yaml \
562 "$package" \
563 "$(redirect_git_url "$git_url" "$package")" \
564 "$(normalize_git_path "$git_path")" \
565 "$git_ref" || return 1
566 ;;
567 sdk)
568 echo "$package: OK (sdk, skipped)"
569 ;;
570 hosted)
571 repo_url=$(pubdev_repo_url "$package" "$version")
572 if [[ -z "$repo_url" ]]; then
573 echo "$package: NOT OK - could not find repository or homepage on pub.dev for ${version}"
574 return 1
575 fi
576 if ! resolve_git_override_for_package "$package" "$version" "$repo_url"; then
577 echo "$package: NOT OK - could not resolve git ref for version ${version} in ${git_url}"
578 echo "# add manual mapping to version_ref_maps in scripts/create_overrides.sh:"
579 echo "# \"${package}-${version}|GIT_HASH\""
580 echo "# repository: ${repo_url}"
581 return 1
582 fi
583 add_git_override_to_yaml "$package" "$git_url" "$resolved_git_path" "$resolved_ref" || return 1
584 ;;
585 path)
586 echo "$package: OK (path, skipped)"
587 ;;
588 *)
589 echo "$package: NOT OK - source=${source} (cannot auto-generate git override)"
590 return 1
591 ;;
592 esac
593 }
594
595 max_jobs=16
596 failed=0
597 batch=()
598 while IFS= read -r pkg; do
599 process_package "$pkg" &
600 batch+=($!)
601 if ((${#batch[@]} >= max_jobs)); then
602 for pid in "${batch[@]}"; do
603 wait "$pid" || failed=1
604 done
605 batch=()
606 fi
607 done < <(
608 yq -o json '.packages' "$lockfile" | jq -c '
609 to_entries[] | {
610 package: .key,
611 source: (.value.source // ""),
612 version: (.value.version // ""),
613 git_url: (if (.value.description | type) == "object" then .value.description.url // "" else "" end),
614 git_ref: (if (.value.description | type) == "object" then (.value.description["resolved-ref"] // .value.description.ref // "") else "" end),
615 git_path: (if (.value.description | type) == "object" then .value.description.path // "" else "" end)
616 }
617 '
618 )
619 for pid in "${batch[@]}"; do
620 wait "$pid" || failed=1
621 done
622 [[ "$failed" -eq 0 ]] || exit 1
623
624 sort_overrides_yaml