Skip to content

Commit

Permalink
tools/upgrade: Add script to upgrade our dependencies
Browse files Browse the repository at this point in the history
This leaves open one significant gap, which is upgrades to
our Flutter version.  I'm leaving that for another time
because it involves making changes in another repository
(the user/developer's Flutter checkout), and so requires care
to avoid clobbering any local changes they have there.

For updating the CocoaPods lockfiles after an upgrade to our
Dart dependencies in pubspec.lock, we'd previously been making
an actual build, or using `flutter build ios --config-only`
(and ditto for macOS) to strip that process down somewhat; but
then we've sometimes also had to run `pod update POD...` to
unwedge CocoaPods on certain pods, as in 105727e and several
later commits like 0dbc0c0 and most recently 1b0061e.

That last step would have been annoying to automate.  But
fortunately it can be subsumed by simply `pod update` with no
list of pods, which causes CocoaPods to update all the pods.
Moreover, if we do that, it turns out to update some other
pods that we hadn't been updating: our transitive dependencies
that don't directly correspond to any Flutter plugin and
therefore don't get upgraded by changes in our pubspec.lock.
So start doing `pod update` on its own, too, even apart from
`flutter pub upgrade`.

And then in fact it turns out that a full `pod update` also
subsumes all the changes that `flutter build` had been making,
so it replaces that in the followup to `flutter pub upgrade`.
  • Loading branch information
gnprice committed Feb 22, 2024
1 parent fa886f5 commit 742de99
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 3 deletions.
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,20 @@ To update the version bounds:
When adding or upgrading dependencies, try to keep our generated files
updated atomically with them.

In particular the files `ios/Podfile.lock` and `macos/Podfile.lock`
In particular the CocoaPods lockfiles
`ios/Podfile.lock` and `macos/Podfile.lock`
frequently need an update when dependencies change.
To update those, run (on a Mac) the commands
`flutter build ios --config-only && flutter build macos --config-only`.
This can only be done in a macOS development environment.

If you have access to a Mac,
then for upgrading dependencies, use the script `tools/upgrade`.
Or after adding a new dependency, run the commands
`(cd ios && pod update) && (cd macos && pod update)`
to apply any needed updates to the CocoaPods lockfiles.

If you don't have convenient access to a Mac, then just mention
clearly in your PR that the upgrade needs syncing for CocoaPods,
and someone else can do it before merging the PR.

(Ideally we would validate these automatically in CI: [#329][].
Several other kinds of generated files are already validated in CI.)
Expand Down
1 change: 1 addition & 0 deletions tools/lib/git.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ git_status_short()
git status --short --untracked-files=normal -- "$@"
}

# shellcheck disable=SC2120 # parameters are all optional
check_no_uncommitted_or_untracked()
{
local problem=""
Expand Down
209 changes: 209 additions & 0 deletions tools/upgrade
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
#!/usr/bin/env bash
set -euo pipefail

this_dir=${BASH_SOURCE[0]%/*}

# shellcheck source=tools/lib/git.sh
. "${this_dir}"/lib/git.sh

# shellcheck source=tools/lib/cli.sh
. "${this_dir}"/lib/cli.sh


## CLI PARSING

default_steps=(pod pub pub-major)

usage() {
cat <<EOF
usage: tools/upgrade [OPTION]... [STEP]...
Upgrade our dependencies.
By default, run all upgrade steps:
${default_steps[*]}
Each step produces a Git commit if there were any changes.
The steps are:
pod Upgrade CocoaPods pods.
pub Upgrade pub packages within the constraints expressed
in pubspec.yaml, then upgrade pods to match.
pub-major Upgrade pub packages to latest, editing pubspec.yaml,
then upgrade pods to match. If there are any changes
here, the resulting commit is only a draft and requires
human editing and review.
EOF
}

opt_steps=()
while (( $# )); do
case "$1" in
pod|pub|pub-major)
opt_steps+=("$1"); shift;;
--help) usage; exit 0;;
*) usage >&2; exit 2;;
esac
done

if (( ! "${#opt_steps[@]}" )); then
opt_steps=( "${default_steps[@]}" )
fi


## EXECUTION

rootdir=$(git rev-parse --show-toplevel)
cd "$rootdir"

check_have_cocoapods() {
if ! type pod >/dev/null; then
echo >&2 "No \`pod\` command found."
echo >&2
echo >&2 "This script requires CocoaPods, in order to keep"
echo >&2 "the CocoaPods lockfiles in sync with our \`pubspec.lock\`."
echo >&2 "Try running it in a development environment on macOS."
return 1
fi
}

# Memoized result of check_pub_get_clean.
#
# (Useful because `flutter pub get` takes a couple of seconds to run.)
pub_get_known_clean=

# Check there are no changes that would be snuck in by the
# locally-installed Flutter version.
# TODO automate upgrading Flutter, too
check_pub_get_clean() {
if [ -n "${pub_get_known_clean}" ]; then
return 0
fi

run_visibly flutter pub get
if ! no_uncommitted_changes; then
echo >&2 "There were changes caused by running \`flutter pub get\`:"
echo >&2
git_status_short
echo >&2
echo >&2 "Typically this means your local Flutter install is newer"
echo >&2 "than the version reflected in our \`pubspec.lock\`."
echo >&2 "Follow the \"Upgrading Flutter\" steps in our README,"
echo >&2 "and then try \`tools/upgrade\` again."
return 1
fi

pub_get_known_clean=1
}

just_pod_update() {
run_visibly pod update --project-directory=ios/
run_visibly pod update --project-directory=macos/
}

upgrade_pod() {
check_no_uncommitted_or_untracked
check_pub_get_clean

just_pod_update
if no_uncommitted_changes; then
echo >&2 "pod update: No changes."
return
fi

git commit -a -m "\
deps: Update CocoaPods pods (tools/upgrade pod)
"
}

upgrade_pub() {
check_no_uncommitted_or_untracked
check_pub_get_clean

run_visibly flutter pub upgrade
if no_uncommitted_changes; then
echo >&2 "flutter pub upgrade: No changes."
return
fi

just_pod_update

# Some upgrades also cause various "generated_plugin_registrant"
# or "generated_plugins" files to get updated: see commits
# bf09824bd and b8b72723a. From the latter, it sounds like those
# changes are made automatically by `flutter pub upgrade`, though,
# so no action needed here.

# TODO rerun build_runner, at least if Drift was updated;
# cf commits db7932244, 9400c8561, and 5dbf1e635.
# If that does change anything, probably flag for closer human review.

git commit -a -m "\
deps: Upgrade packages within constraints (tools/upgrade pub)
"
}

upgrade_pub_major() {
check_no_uncommitted_or_untracked
check_pub_get_clean

run_visibly flutter pub upgrade --major-versions
if no_uncommitted_changes; then
echo >&2 "flutter pub upgrade --major-versions: No changes."
return
fi

just_pod_update

# TODO rerun build_runner; see upgrade_pub

git commit -a -m "\
WIP deps: Upgrade packages to latest, namely TODO:(which packages?)
This is the result of \`tools/upgrade pod-major\`, and
TODO:(describe any other changes you had to make).
Changelogs:
TODO:(link https://pub.dev/packages/PACKAGE_NAME/changelog)
"

cat <<EOF
There were upgrades beyond the constraints we had in \`pubspec.yaml\`.
This typically means the package maintainers identified the changes
as potentially breaking.
The \`tools/upgrade\` script created a draft commit, but this type
of upgrade cannot be fully automated because it requires review.
To finish the upgrade:
* Identify which packages were updated.
* Locate their changelogs: https://pub.dev/packages/PACKAGE_NAME/changelog .
* Review the changelogs and determine if any of the breaking changes
look like they could affect us.
* Test any relevant areas of the app, and make any changes needed
for our code to go along with the updates.
* Amend the commit, and fill in the TODO items in the commit message.
If several unrelated packages were upgraded, and any of them require
changes in our code, consider upgrading them in separate commits.
EOF
}

check_have_cocoapods

divider_line='================================================================'

for step in "${opt_steps[@]}"; do
echo
echo "${divider_line}"
echo "======== tools/upgrade ${step}"
case "${step}" in
pod) upgrade_pod ;;
pub) upgrade_pub ;;
pub-major) upgrade_pub_major ;;
*) echo >&2 "Internal error: unknown step ${step}" ;;
esac
done

0 comments on commit 742de99

Please sign in to comment.