├── .github
├── FUNDING.yml
└── workflows
│ └── nightly.yaml
├── .gitignore
├── CHANGELOG.md
├── LICENSE.txt
├── README.md
├── flutterw
├── install.sh
├── test
├── .gitignore
├── analysis_options.yaml
├── pubspec.yaml
└── test
│ ├── install_test.dart
│ ├── multi_packages_test.dart
│ ├── submodule_test.dart
│ └── update_test.dart
├── uninstall.sh
└── version
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [passsy]
2 |
--------------------------------------------------------------------------------
/.github/workflows/nightly.yaml:
--------------------------------------------------------------------------------
1 | name: nightly
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | workflow_dispatch:
9 | schedule:
10 | # Every night at 03:00
11 | - cron: '0 3 * * *'
12 |
13 | jobs:
14 | test:
15 | runs-on: ubuntu-latest
16 |
17 | container:
18 | image: passsy/flutterw:base-0.4.1
19 |
20 | steps:
21 | - uses: actions/checkout@v2.4.0
22 | - name: Preload Flutter
23 | run: |
24 | mkdir -p /flutter
25 | git clone https://github.com/flutter/flutter.git -b stable /flutter
26 | export PATH="$PATH:/flutter/bin"
27 | echo "/flutter/bin" >> $GITHUB_PATH
28 | cat /flutter/.git/config
29 | # TODO fetch this in flutterw script
30 | git -C /flutter fetch origin
31 | git -C /flutter fetch --all
32 | flutter precache --linux --web
33 |
34 | git config --global user.email "dash@flutter.io"
35 | git config --global user.name "Dash"
36 | - name: Get dependencies
37 | run: |
38 | cd test
39 | dart pub get
40 |
41 | - name: Run tests
42 | run: |
43 | cd test
44 | dart run test -j 1
45 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | test/.idea
2 | *.iml
3 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 1.3.1
4 |
5 | - Allow flutterw update when placed in repo subfolder [#34](https://github.com/passsy/flutter_wrapper/pull/34)
6 |
7 | ## 1.3.0
8 |
9 | - Don't clear .gitmodules when calling `flutter channel` without a channel [#26](https://github.com/passsy/flutter_wrapper/pull/26)
10 | - Move echos to `stderr`, so that the output of `stdout` is not changed for scripts consuming it [#24](https://github.com/passsy/flutter_wrapper/pull/24)
11 | - Don't error `flutter upgrade` when `pubspec.lock` doesn't exist [#28](https://github.com/passsy/flutter_wrapper/pull/28)
12 | - This project is now tested! This gives me personally confidence to change things without breaking your project
13 |
14 | ## 1.2.0
15 |
16 | - Fix submodule initialization detection #22
17 | - Support for multi-package repositories #18
18 | - Support placing flutterw in a sub folder #17
19 | - Added tests so I don't accidentally break your project
20 |
21 | ## 1.1.2
22 |
23 | - Always switch to defined channel to fix `./flutterw upgrade` #19
24 | - Escape all git arguments
25 |
26 | ## 1.1.1
27 |
28 | - Escape arguments in post flutterw section 7701728ffab0053db5a5f5b647c2c38ea0e0b27e
29 | - Fix `./flutterw upgrade` when branch (`stable`) is not fetched ba5729987c10d62cd860b3b81ebc8ae62484b86c
30 |
31 | ## 1.1.0
32 |
33 | - `./flutterw upgrade` and `./flutterw channel X` now works without manual adjustments inside the submodule #15
34 | - The channel (master|dev|beta|stable) is now synced with what's defined in `.gitmodules`
35 |
36 | ## 1.0.3
37 |
38 | - Fix submodule matching in install script 605854d1db5053fd36814d4a9733e9d1b182bcd3
39 |
40 | ## 1.0.2
41 |
42 | - Improve `.flutter` submodule matching 5008757479b29c7296fb9143e6adb859a392ba7e a1c7c7fa8903c5bf0b3344e921dfbd6c45cedcc7
43 |
44 | ## 1.0.1
45 |
46 | - Fix `.flutter` submodule existence check, allow other submodules containing `flutter` as name #7
47 |
48 | ## 1.0.0
49 |
50 | - Use flutters stable branch as default channel
51 |
52 | ## 0.8.0
53 |
54 | - #4 Use `https` instead of `ssh` to clone flutter
55 | - fc7d105 fail fast on download error
56 |
57 | ## 0.7.1
58 |
59 | - Fix: Download flutterw from latest tag defined in `version` file 053fc8a4aeb219d70c6793fb71bd334e1d860f37
60 |
61 | ## 0.7.0
62 |
63 | - Linux support (Hello PixelBook 😁) #3
64 |
65 | ## 0.6.1
66 |
67 | - Fix: Abort install script when git clone fails 8ec7c79748e11ab8e04356e5ca574ad9de3b4140
68 | - Fix: '.flutter' already exists in the index during update d02f505be2cf2648d67fc260c812a387d5e05cf1
69 |
70 | ## 0.6.0
71 |
72 | - Download version which is defined in `version` on master branch. Prevents downloading of preview versions
73 | - Adds version and date to `flutterw` header
74 | - `install.sh` does now support the argument `--tag|-t` for the git tag to download
75 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright 2018 Pascal Welsch
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Flutter Wrapper
2 |
3 | `flutterw` is a tiny, open source shell script which downloads and executes the Flutter SDK with the exact version defined in your project respository.
4 | It encourages the idea that upgrading Flutter should happen per project, not per developer.
5 | Thus upgrading Flutter with `flutterw` automatically upgrades Flutter for your co-workers and on the CI servers.
6 |
7 | The Flutter Wrapper will add the Flutter SDK as a git submodule to your project.
8 | It pins the version and the channel.
9 |
10 | This project is inspired by the gradle wrapper.
11 |
12 | Read more on [Medium](https://medium.com/grandcentrix/flutter-wrapper-bind-your-project-to-an-explicit-flutter-release-4062cfe6dcaf)
13 |
14 |
15 | # Install flutterw
16 |
17 | ```bash
18 | sh -c "$(curl -fsSL https://raw.githubusercontent.com/passsy/flutter_wrapper/master/install.sh)"
19 | ```
20 | _Open the Terminal, navigate to your project root and execute the line above._
21 |
22 | From now on use `./flutterw` instead of `flutter`
23 |
24 | 
25 |
26 | ## IDE Setup
27 | ### Use with VScode
28 |
29 | If you're a VScode user link the new Flutter SDK path in your settings
30 | `$projectRoot/.vscode/settings.json` (create if it doesn't exists yet)
31 |
32 | ```json
33 | {
34 | "dart.flutterSdkPath": ".flutter",
35 | }
36 | ```
37 |
38 | Commit this file to your git repo and your coworkers will automatically use `flutterw` from now on
39 |
40 | ### Use with IntelliJ / Android Studio
41 |
42 | Go to `File > Settings > Languages & Frameworks > Flutter` and set the Flutter SDK path to `$projectRoot/.flutter`
43 |
44 |
45 |
46 | Add this step to the onboarding guidelines of your projects because this has to be done for every developer for every project using `flutterw`.
47 |
48 |
49 | ## Tips and Tricks
50 | ### Upgrading Flutter
51 |
52 | Flutter Wrapper doesn't require any special command to update Flutter.
53 | Run `./flutterw channel ` to change the channel or update to the lastest version of a given channel.
54 |
55 | ```
56 | ./flutterw channel beta
57 | ./flutterw upgrade
58 | ```
59 |
60 | Don't forget to commit the submodule changes.
61 |
62 |
63 | ### Updating flutterw
64 |
65 | To update the flutter wrapper to the latest version run the install command again:
66 |
67 | ```bash
68 | sh -c "$(curl -fsSL https://raw.githubusercontent.com/passsy/flutter_wrapper/master/install.sh)"
69 | ```
70 |
71 | To update the flutter wrapper to a specific verssion, use the `-t ` (i.e. `v1.0.0`)
72 |
73 | ```bash
74 | sh -c "curl -fsSL https://raw.githubusercontent.com/passsy/flutter_wrapper/master/install.sh" | bash /dev/stdin -t v1.0.0
75 | ```
76 |
77 |
78 | ### Uninstall flutterw
79 |
80 | Sorry to let you go!
81 | Removing submodules is hard, that's why I did the hard work for you.
82 | Simply run this command from the root of your flutter project and the uninstall script will cleanup everything.
83 |
84 | ```bash
85 | sh -c "$(curl -fsSL https://raw.githubusercontent.com/passsy/flutter_wrapper/master/uninstall.sh)"
86 | ```
87 |
88 | Bye :wave:
89 |
90 | ### Create a new project using the flutter wrapper
91 |
92 | You can create a new Flutter project without installing Flutter globally on your machine.
93 |
94 | ```bash
95 | # 1. Create an empty git repo
96 | mkdir flutter_wrapper_project && cd "$_"
97 | git init
98 |
99 | # 2. Install flutterw
100 | sh -c "$(curl -fsSL https://raw.githubusercontent.com/passsy/flutter_wrapper/master/install.sh)"
101 |
102 | # 3. Create Flutter project
103 | ./flutterw create .
104 | ./flutterw run
105 | ```
106 |
107 | ### Using flutterw on CI
108 |
109 | Here is an example of using flutterw on a CI server.
110 |
111 | ```bash
112 | jobs:
113 | linux:
114 | runs-on: ubuntu-latest
115 |
116 | container:
117 | # Use container with pre-installed Flutter dependencies like unzip, libglu1-mesa
118 | image: passsy/flutterw:base-0.4.1
119 |
120 | steps:
121 | - uses: actions/checkout@v2.4.0
122 | - run: ./flutterw doctor -v
123 | - run: ./flutterw pub get
124 | - run: ./flutterw test
125 | - run: ./flutterw analyze --fatal-infos --fatal-warnings .
126 |
127 | macos:
128 | runs-on: macos-latest
129 | steps:
130 | - uses: actions/checkout@v2.4.0
131 | - run: ./flutterw doctor -v
132 | - run: ./flutterw pub get
133 | - run: ./flutterw test
134 | - run: ./flutterw analyze --fatal-infos --fatal-warnings .
135 | ```
136 |
137 | ## License
138 |
139 | ```
140 | Copyright 2019 Pascal Welsch
141 |
142 | Licensed under the Apache License, Version 2.0 (the "License");
143 | you may not use this file except in compliance with the License.
144 | You may obtain a copy of the License at
145 |
146 | http://www.apache.org/licenses/LICENSE-2.0
147 |
148 | Unless required by applicable law or agreed to in writing, software
149 | distributed under the License is distributed on an "AS IS" BASIS,
150 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
151 | See the License for the specific language governing permissions and
152 | limitations under the License.
153 | ```
154 |
--------------------------------------------------------------------------------
/flutterw:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Flutter start up script for UN*X
6 | ## Version: VERSION_PLACEHOLDER
7 | ## Date: DATE_PLACEHOLDER
8 | ##
9 | ## Use this flutter wrapper to bundle Flutter within your project to make
10 | ## sure everybody builds with the same version.
11 | ##
12 | ## Read about the install and uninstall process in the README on GitHub
13 | ## https://github.com/passsy/flutter_wrapper
14 | ##
15 | ## Inspired by gradle-wrapper.
16 | ##
17 | ##############################################################################
18 |
19 | echoerr() { echo "$@" 1>&2; }
20 |
21 | # Attempt to set APP_HOME
22 | # Resolve links: $0 may be a link
23 | PRG="$0"
24 | # Need this for relative symlinks.
25 | while [ -h "$PRG" ]; do
26 | ls=$(ls -ld "$PRG")
27 | link=$(expr "$ls" : '.*-> \(.*\)$')
28 | if expr "$link" : '/.*' >/dev/null; then
29 | PRG="$link"
30 | else
31 | PRG=$(dirname "$PRG")"/$link"
32 | fi
33 | done
34 | SAVED="$(pwd)"
35 | cd "$(dirname "$PRG")/" >/dev/null
36 | APP_HOME="$(pwd -P)"
37 | cd "$SAVED" >/dev/null
38 |
39 | FLUTTER_SUBMODULE_NAME='.flutter'
40 | GIT_HOME=$(git -C "${APP_HOME}" rev-parse --show-toplevel)
41 | FLUTTER_DIR="${GIT_HOME}/${FLUTTER_SUBMODULE_NAME}"
42 |
43 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
44 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
45 | cd "$(dirname "$0")"
46 | fi
47 |
48 | # Fix not initialized flutter submodule
49 | if [ ! -f "${FLUTTER_DIR}/bin/flutter" ]; then
50 | echoerr "$FLUTTER_SUBMODULE_NAME submodule not initialized. Initializing..."
51 | git submodule update --init "${FLUTTER_DIR}"
52 | fi
53 |
54 | # Detect detach HEAD and fix it. commands like upgrade expect a valid branch, not a detached HEAD
55 | FLUTTER_SYMBOLIC_REF=$(git -C "${FLUTTER_DIR}" symbolic-ref -q HEAD)
56 | if [ -z "${FLUTTER_SYMBOLIC_REF}" ]; then
57 | FLUTTER_REV=$(git -C "${FLUTTER_DIR}" rev-parse HEAD)
58 | FLUTTER_CHANNEL=$(git -C "${GIT_HOME}" config -f .gitmodules submodule.${FLUTTER_SUBMODULE_NAME}.branch)
59 |
60 | if [ -z "${FLUTTER_CHANNEL}" ]; then
61 | echoerr "Warning: Submodule '$FLUTTER_SUBMODULE_NAME' doesn't point to an official Flutter channel \
62 | (one of stable|beta|dev|master). './flutterw upgrade' will fail without a channel."
63 | echoerr "Fix this by adding a specific channel with:"
64 | echoerr " - './flutterw channel ' or"
65 | echoerr " - Add 'branch = ' to '$FLUTTER_SUBMODULE_NAME' submodule in .gitmodules"
66 | else
67 | echoerr "Fixing detached HEAD: '$FLUTTER_SUBMODULE_NAME' submodule points to a specific commit $FLUTTER_REV, not channel '$FLUTTER_CHANNEL' (as defined in .gitmodules)."
68 | # Make sure channel is fetched
69 | # Remove old channel branch because it might be moved to an unrelated commit where fast-forward pull isn't possible
70 | git -C "${FLUTTER_DIR}" branch -q -D "${FLUTTER_CHANNEL}" 2> /dev/null || true
71 | git -C "${FLUTTER_DIR}" fetch -q origin
72 |
73 | # bind current HEAD to channel defined in .gitmodules
74 | git -C "${FLUTTER_DIR}" checkout -q -b "${FLUTTER_CHANNEL}" "${FLUTTER_REV}"
75 | git -C "${FLUTTER_DIR}" branch -q -u "origin/${FLUTTER_CHANNEL}" "${FLUTTER_CHANNEL}"
76 | echoerr "Fixed! Migrated to channel '$FLUTTER_CHANNEL' while staying at commit $FLUTTER_REV. './flutterw upgrade' now works without problems!"
77 | git -C "${FLUTTER_DIR}" status -bs
78 | fi
79 | fi
80 |
81 | # Wrapper tasks done, call flutter binary with all args
82 | set -e
83 | "$FLUTTER_DIR/bin/flutter" "$@"
84 | set +e
85 |
86 | # Post flutterw tasks. exit code from /bin/flutterw will be used as final exit
87 | FLUTTER_EXIT_STATUS=$?
88 | if [ ${FLUTTER_EXIT_STATUS} -eq 0 ]; then
89 |
90 | # ./flutterw channel CHANNEL
91 | if echo "$@" | grep -q "channel"; then
92 | if [ -n "$2" ]; then
93 | # make sure .gitmodules is updated as well
94 | CHANNEL=${2} # second arg
95 | git config -f "${GIT_HOME}/.gitmodules" "submodule.${FLUTTER_SUBMODULE_NAME}.branch" "${CHANNEL}"
96 | # makes sure nobody forgets to do commit all changed files
97 | git add "${GIT_HOME}/.gitmodules"
98 | git add "${FLUTTER_DIR}"
99 | fi
100 | fi
101 |
102 | # ./flutterw upgrade
103 | if echo "$@" | grep -q "upgrade"; then
104 | # makes sure nobody forgets to do commit the changed submodule
105 | git add "${FLUTTER_DIR}"
106 | # flutter packages get runs automatically. Stage those changes as well
107 | if [ -f pubspec.lock ]; then
108 | git add pubspec.lock
109 | fi
110 | fi
111 | fi
112 |
113 | exit ${FLUTTER_EXIT_STATUS}
114 |
--------------------------------------------------------------------------------
/install.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ###
4 | # Check preconditions
5 | ###
6 |
7 | # Verify flutter project is a git repo
8 | inside_git_repo="$(git rev-parse --is-inside-work-tree 2>/dev/null)"
9 | if ! [ "$inside_git_repo" ]; then
10 | printf "Error: Not a git repository, to fix this run: git init\n"
11 | exit 1
12 | fi
13 |
14 | # Make sure this is the root of the flutter dir (search for pubspec.yaml)
15 | # flutterw should be placed next to
16 | if ! [ -f pubspec.yaml ]; then
17 | printf "Warning: Not executed in flutter root. Couldn't find pubspec.yaml.\n"
18 | printf "Continuing in case this flutter wrapper is used to create a new project. If so continue with './flutterw create .'\n\n"
19 | fi
20 |
21 | ###
22 | # Parse arguments
23 | ###
24 |
25 | # Parse arguments
26 | while [ "$1" != "" ]; do
27 | case $1 in
28 |
29 | # version tag which should be used for downloading
30 | -t | --tag)
31 | shift
32 | VERSION_TAG="$1"
33 | ;;
34 |
35 | *)
36 | echo "Unknown option '$key'"
37 | ;;
38 | esac
39 | shift
40 | done
41 |
42 | if [ -z "$VERSION_TAG" ]; then
43 | # Get latest version from master in git
44 | VERSION_TAG=$(curl -s "https://raw.githubusercontent.com/passsy/flutter_wrapper/master/version")
45 |
46 | starts_with_v=$(echo "$VERSION_TAG" | cut -c 1)
47 | if [ "$starts_with_v" != "v" ]; then
48 | # add v prefix for tag if not present
49 | VERSION_TAG="v$VERSION_TAG"
50 | fi
51 | fi
52 |
53 | printf "Installing Flutter Wrapper %s\n" "$VERSION_TAG"
54 |
55 | ###
56 | # Add .flutter submodule
57 | ###
58 | FLUTTER_SUBMODULE_NAME='.flutter'
59 | GIT_HOME=$(git rev-parse --show-toplevel)
60 |
61 | # Check if submodule already exists (when updating flutter wrapper)
62 | HAS_SUBMODULE=$(git -C "${GIT_HOME}" submodule | grep "\ ${FLUTTER_SUBMODULE_NAME}")
63 | if [ -z "${HAS_SUBMODULE}" ]; then
64 | printf "adding '%s' submodule\n" "${FLUTTER_SUBMODULE_NAME}"
65 | UPDATED=false
66 |
67 | # create relative to /.flutter
68 | source=$PWD
69 | target=${GIT_HOME}/${FLUTTER_SUBMODULE_NAME}
70 | common_part=$source
71 | back=
72 | while [ "${target#$common_part}" = "${target}" ]; do
73 | common_part=$(dirname "$common_part")
74 | back="../${back}"
75 | done
76 | CLONE_TO=${back}${target#$common_part/}
77 |
78 | # add the flutter submodule
79 | git submodule add -b stable https://github.com/flutter/flutter.git "${CLONE_TO}"
80 |
81 | # When submodule failed, abort
82 | if [ ! $? -eq 0 ]; then
83 | echo "Abort installation of flutterw, couldn't clone flutter" >&2
84 | exit 1
85 | fi
86 | else
87 | # update url to https
88 | printf "Upgrading existing flutter wrapper\n"
89 | UPDATED=true
90 |
91 | # Update old ssh url to https
92 | SUBMODULE_PATH=$(git config -f "${GIT_HOME}/.gitmodules" "submodule.$FLUTTER_SUBMODULE_NAME.path" )
93 |
94 | USES_SSH=$(git config -f "${GIT_HOME}/.gitmodules" "submodule.${SUBMODULE_PATH}.url" | cut -c 1-4)
95 | if [ "$USES_SSH" = "git@" ]; then
96 | printf "Update %s submodule url to https\n" "$FLUTTER_SUBMODULE_NAME"
97 | git config -f "${GIT_HOME}/.gitmodules submodule.${SUBMODULE_PATH}.url" https://github.com/flutter/flutter.git
98 | git add "${GIT_HOME}/.gitmodules"
99 | git submodule sync "${FLUTTER_SUBMODULE_NAME}"
100 | fi
101 | fi
102 |
103 | ###
104 | # Download flutterw executable
105 | ###
106 | printf "Downloading new flutterw\n"
107 | # Download latest flutterw version
108 | FLUTTERW_URL="https://raw.githubusercontent.com/passsy/flutter_wrapper/$VERSION_TAG/flutterw"
109 | curl -sfO "$FLUTTERW_URL"
110 | if [ "$?" != "0" ]; then
111 | printf "Couldn't download flutterw from '%s'\n" "$FLUTTERW_URL"
112 | exit 1
113 | fi
114 |
115 | # make it executable
116 | chmod 755 flutterw
117 |
118 | # Replace version string in wrapper
119 | sed -i.bak "s/VERSION_PLACEHOLDER/$VERSION_TAG/g" flutterw && rm flutterw.bak
120 |
121 | # Replace date placeholder in wrapper
122 | DATE=$(date '+%Y-%m-%d %H:%M:%S')
123 | sed -i.bak "s/DATE_PLACEHOLDER/$DATE/g" flutterw && rm flutterw.bak
124 |
125 | # add it to git
126 | git add flutterw
127 |
128 | ###
129 | # Run flutterw
130 | ###
131 |
132 | # bind this flutter instance to the project (update .packages file)
133 | if [ -f pubspec.yaml ]; then
134 | ./flutterw packages get
135 | fi
136 |
137 | if $UPDATED; then
138 | printf "\nFlutter Wrapper updated to version %s\n\n" "$VERSION_TAG"
139 | else
140 | printf "\nFlutter Wrapper installed (version %s), initialized with channel stable.\n\n" "$VERSION_TAG"
141 | fi
142 | printf "Run your app with: ./flutterw run\n"
143 | printf "Switch channel: ./flutterw channel beta\n"
144 |
--------------------------------------------------------------------------------
/test/.gitignore:
--------------------------------------------------------------------------------
1 | # Files and directories created by pub
2 | .dart_tool/
3 | .packages
4 | # Remove the following pattern if you wish to check in your lock file
5 | pubspec.lock
6 |
7 | # Conventional directory for build outputs
8 | build/
9 |
10 | # Directory created by dartdoc
11 | doc/api/
12 |
--------------------------------------------------------------------------------
/test/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | include: package:lint/analysis_options.yaml
--------------------------------------------------------------------------------
/test/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_wrapper_test
2 |
3 | environment:
4 | sdk: '>=3.0.0 <4.0.0'
5 |
6 | dependencies:
7 | cli_script: ^1.0.0
8 | file: ^7.0.0
9 |
10 | dev_dependencies:
11 | lint: ^2.0.0
12 | synchronized: ^3.1.0
13 | test: ^1.24.2
14 |
--------------------------------------------------------------------------------
/test/test/install_test.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io' as io;
3 |
4 | import 'package:cli_script/cli_script.dart';
5 | import 'package:file/file.dart';
6 | import 'package:file/local.dart';
7 | import 'package:synchronized/synchronized.dart';
8 | import 'package:test/test.dart';
9 |
10 | void main() {
11 | group('install', () {
12 | test(
13 | 'report missing git',
14 | () async {
15 | final dir = io.Directory.systemTemp.createTempSync('root');
16 | addTearDown(() {
17 | dir.deleteSync(recursive: true);
18 | });
19 |
20 | final script = Script.capture((_) async {
21 | await runInstallScript(
22 | appDir: dir.absolute.path, gitRootDir: dir.absolute.path);
23 | });
24 | // access fields before accessing them or they crash
25 | final exitCodeFuture = script.exitCode;
26 | final outFuture = script.stdout.text;
27 |
28 | final err = await outFuture;
29 | expect(
30 | err, contains("Not a git repository, to fix this run: git init"));
31 | final code = await exitCodeFuture;
32 | expect(code, 1);
33 | },
34 | timeout: const Timeout(Duration(minutes: 5)),
35 | );
36 |
37 | group("install in git root", () {
38 | late Directory gitRootDir;
39 | late Directory appDir;
40 |
41 | tearDownAll(() {
42 | appDir.parent.deleteSync(recursive: true);
43 | });
44 | setUpAll(() async {
45 | final dir =
46 | const LocalFileSystem().systemTempDirectory.createTempSync('root');
47 | gitRootDir = appDir = dir.childDirectory('myApp');
48 | assert(gitRootDir == appDir);
49 |
50 | // create git in appDir
51 | appDir.createSync();
52 | await run("git init", workingDirectory: appDir.absolute.path);
53 |
54 | await runInstallScript(
55 | appDir: appDir.absolute.path, gitRootDir: gitRootDir.absolute.path);
56 | });
57 |
58 | test('flutterw was downloaded', () async {
59 | expect(appDir.childFile('flutterw').existsSync(), isTrue);
60 | });
61 |
62 | test('flutterw is executable', () async {
63 | final flutterw = appDir.childFile('flutterw');
64 | final script =
65 | Script.capture((_) async => run("stat ${flutterw.absolute.path}"));
66 | expect(await script.stdout.text, contains("-rwxr-xr-x"));
67 | });
68 |
69 | test('created .flutter submodule in appDir', () async {
70 | final flutterw = appDir.childFile('flutterw');
71 | expect(flutterw.existsSync(), isTrue);
72 | });
73 |
74 | test('downloaded dart tools', () async {
75 | expect(
76 | gitRootDir
77 | .childFile(".flutter/bin/cache/dart-sdk/bin/dart")
78 | .existsSync(),
79 | isTrue);
80 | });
81 |
82 | test('flutterw contains version', () async {
83 | final flutterw = appDir.childFile('flutterw');
84 | final text = flutterw.readAsStringSync();
85 | expect(text, isNot(contains("VERSION_PLACEHOLDER")));
86 | expect(text, isNot(contains("DATE_PLACEHOLDER")));
87 | });
88 | });
89 |
90 | group("install in subdir", () {
91 | late Directory gitRootDir;
92 | late Directory appDir;
93 |
94 | tearDownAll(() {
95 | gitRootDir.deleteSync(recursive: true);
96 | });
97 |
98 | setUpAll(() async {
99 | gitRootDir =
100 | const LocalFileSystem().systemTempDirectory.createTempSync('root');
101 | // git repo in root, flutterw in appDir
102 | appDir = gitRootDir.childDirectory('myApp')..createSync();
103 |
104 | await run("git init", workingDirectory: gitRootDir.absolute.path);
105 | await runInstallScript(
106 | appDir: appDir.absolute.path, gitRootDir: gitRootDir.absolute.path);
107 | });
108 |
109 | test('subdir flutterw was downloaded', () async {
110 | final flutterw = appDir.childFile('flutterw');
111 | expect(flutterw.existsSync(), isTrue);
112 | });
113 |
114 | test('subdir flutterw is executable', () async {
115 | final flutterw = appDir.childFile('flutterw');
116 | final script =
117 | Script.capture((_) async => run("stat ${flutterw.absolute.path}"));
118 | expect(await script.stdout.text, contains("-rwxr-xr-x"));
119 | });
120 |
121 | test('subdir created .flutter submodule', () async {
122 | final flutterDir = gitRootDir.childDirectory('.flutter');
123 | expect(flutterDir.existsSync(), isTrue);
124 | });
125 |
126 | test('subdir downloaded dart tools', () async {
127 | final dartBinary =
128 | gitRootDir.childFile(".flutter/bin/cache/dart-sdk/bin/dart");
129 | expect(dartBinary.existsSync(), isTrue);
130 | });
131 |
132 | test('subdir flutterw contains version', () async {
133 | final flutterw = appDir.childFile('flutterw');
134 | final text = flutterw.readAsStringSync();
135 | expect(text, isNot(contains("VERSION_PLACEHOLDER")));
136 | expect(text, isNot(contains("DATE_PLACEHOLDER")));
137 | });
138 | });
139 | });
140 | }
141 |
142 | bool _precached = false;
143 | Lock precacheLock = Lock();
144 |
145 | /// Allow to clone from file system to speed up tests
146 | Future allowCloneFromFile() async {
147 | // get current allow setting
148 | String? allowValue;
149 | try {
150 | allowValue = await output('git config --global protocol.file.allow');
151 | } catch (e) {
152 | // ignore, not set
153 | }
154 | if (allowValue?.trim() != 'always') {
155 | await run('git config --global protocol.file.allow always');
156 |
157 | // always restore to previous value
158 | addTearDown(() async {
159 | if (allowValue == null) {
160 | await run('git config --global --unset protocol.file.allow');
161 | } else {
162 | await run('git config --global protocol.file.allow $allowValue');
163 | }
164 | });
165 | }
166 | }
167 |
168 | Future runInstallScript({
169 | required String appDir,
170 | required String gitRootDir,
171 | }) async {
172 | await allowCloneFromFile();
173 | const fs = LocalFileSystem();
174 | final repoRoot = fs.currentDirectory.parent;
175 | // Get path from line
176 | // • Flutter version 2.2.0-10.1.pre at /usr/local/Caskroom/flutter/latest/flutter
177 | final doctor = Script.capture((_) async => run('flutter doctor -v'));
178 | final lines = await doctor.stdout.lines.toList();
179 | final flutterRepoPath = lines
180 | .firstWhere((line) => line.contains("Flutter version"))
181 | .split(" ")
182 | .last;
183 | final localFlutterExists = fs.directory(flutterRepoPath).existsSync();
184 | if (!localFlutterExists) {
185 | throw "Did not find a flutter repo on your system";
186 | }
187 |
188 | await precacheLock.synchronized(() async {
189 | if (!_precached) {
190 | // make sure remote is setup correctly
191 | final remotes =
192 | await output('git remote', workingDirectory: flutterRepoPath);
193 | if (!remotes.contains('origin')) {
194 | await run(
195 | 'git remote add origin https://github.com/flutter/flutter.git',
196 | workingDirectory: flutterRepoPath);
197 | }
198 | // make sure all branches are available as in the remote
199 | await run('git fetch --all', workingDirectory: flutterRepoPath);
200 | await run('git checkout beta', workingDirectory: flutterRepoPath);
201 | await run('git checkout master', workingDirectory: flutterRepoPath);
202 | await run('git checkout stable', workingDirectory: flutterRepoPath);
203 |
204 | await run('flutter channel stable');
205 | await run('flutter upgrade');
206 | await run('flutter precache');
207 | _precached = true;
208 | }
209 | });
210 |
211 | final buildDir = fs.directory(appDir).childDirectory('build')
212 | ..createSync(recursive: true);
213 |
214 | final File testableInstall = repoRoot
215 | .childFile('install.sh')
216 | .copySync(buildDir.childFile('testable_install.sh').path);
217 |
218 | await run('chmod 755 ${testableInstall.path}');
219 | {
220 | // modify install script to use local dependencies
221 | var modified = testableInstall.readAsStringSync();
222 |
223 | // Close local repo instead of remote
224 | modified = modified.replaceFirst(
225 | 'https://github.com/flutter/flutter.git', flutterRepoPath);
226 |
227 | // Instead of getting dependencies, preload flutter dependencies
228 | modified = modified.replaceFirst(
229 | '''if [ -f pubspec.yaml ]; then
230 | ./flutterw packages get
231 | fi''',
232 | // copy over precached files
233 | 'mkdir -p $appDir/.flutter/bin/cache/ \n'
234 | 'cp -R -L -f "$flutterRepoPath/bin/" "$gitRootDir/.flutter/bin/" \n'
235 | 'cp -R -L -f "$flutterRepoPath/packages/flutter_tools/" "$gitRootDir/.flutter/packages/flutter_tools/" \n'
236 | './flutterw \n',
237 | );
238 |
239 | // Don't load version from repo
240 | modified = modified.replaceFirst(
241 | 'VERSION_TAG=\$(curl -s "https://raw.githubusercontent.com/passsy/flutter_wrapper/master/version")',
242 | 'VERSION_TAG=T.E.S.T',
243 | );
244 |
245 | // Local local flutterw file
246 | modified = modified.replaceFirst(
247 | 'https://raw.githubusercontent.com/passsy/flutter_wrapper/\$VERSION_TAG/flutterw',
248 | 'file://${repoRoot.childFile('flutterw').path}',
249 | );
250 |
251 | // add set -x for debugging
252 | modified = modified.replaceFirst(
253 | '#!/usr/bin/env sh',
254 | '#!/usr/bin/env sh\nset -x',
255 | );
256 |
257 | testableInstall.writeAsStringSync(modified);
258 | }
259 |
260 | final script = Script(
261 | "${testableInstall.absolute.path}",
262 | name: 'install.sh (testable)',
263 | workingDirectory: appDir,
264 | );
265 | await script.done;
266 | }
267 |
--------------------------------------------------------------------------------
/test/test/multi_packages_test.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: avoid_print
2 |
3 | import 'package:cli_script/cli_script.dart';
4 | import 'package:file/local.dart';
5 | import 'package:test/test.dart';
6 |
7 | import 'install_test.dart';
8 |
9 | void main() {
10 | test(
11 | 'Calling ./../../fluttew from packages/xyz/',
12 | () async {
13 | // setup repo
14 | final repo =
15 | const LocalFileSystem().systemTempDirectory.createTempSync('repo');
16 | addTearDown(() {
17 | repo.deleteSync(recursive: true);
18 | });
19 | await run('git init', workingDirectory: repo.absolute.path);
20 | await runInstallScript(
21 | appDir: repo.absolute.path, gitRootDir: repo.absolute.path);
22 | await run('git commit -a -m "initial commit"',
23 | workingDirectory: repo.absolute.path);
24 |
25 | // create package
26 | final package = repo.childDirectory('packages/xyz')
27 | ..createSync(recursive: true);
28 | await run('./flutterw create packages/xyz',
29 | workingDirectory: repo.absolute.path);
30 |
31 | // Make sure flutterw can be executed in package
32 | final script = Script.capture((_) async =>
33 | run('./../../flutterw', workingDirectory: package.absolute.path));
34 | final output = await script.combineOutput().text;
35 | print(output);
36 | expect(
37 | output,
38 | isNot(contains(
39 | 'fatal: cannot change to \'\.flutter\': No such file or directory')));
40 | },
41 | timeout: const Timeout(Duration(minutes: 5)),
42 | );
43 | }
44 |
--------------------------------------------------------------------------------
/test/test/submodule_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:cli_script/cli_script.dart';
2 | import 'package:file/local.dart';
3 | import 'package:test/test.dart';
4 |
5 | import 'install_test.dart';
6 |
7 | void main() {
8 | test(
9 | 'populate submodule when uninitialized',
10 | () async {
11 | final origin =
12 | const LocalFileSystem().systemTempDirectory.createTempSync('origin');
13 | addTearDown(() {
14 | origin.deleteSync(recursive: true);
15 | });
16 | await run('git init', workingDirectory: origin.absolute.path);
17 | await runInstallScript(
18 | appDir: origin.absolute.path, gitRootDir: origin.absolute.path);
19 | await run('git commit -a -m "initial commit"',
20 | workingDirectory: origin.absolute.path);
21 |
22 | final clone =
23 | const LocalFileSystem().systemTempDirectory.createTempSync('clone');
24 | addTearDown(() {
25 | clone.deleteSync(recursive: true);
26 | });
27 | await run('git clone ${origin.absolute.path} ${clone.absolute.path}');
28 | // calling flutterw should now automatically initialize the submodule and build the flutter tool
29 | await run('./flutterw', workingDirectory: clone.absolute.path);
30 |
31 | expect(clone.childFile('.flutter/bin/flutter').existsSync(), isTrue);
32 | expect(
33 | clone.childFile('.flutter/bin/cache/dart-sdk/bin/dart').existsSync(),
34 | isTrue);
35 | },
36 | timeout: const Timeout(Duration(minutes: 5)),
37 | );
38 |
39 | group('gitmodules update', () {
40 | test(
41 | 'flutter channel updates gitmodules (single module)',
42 | () async {
43 | final repo =
44 | const LocalFileSystem().systemTempDirectory.createTempSync('repo');
45 | addTearDown(() {
46 | repo.deleteSync(recursive: true);
47 | });
48 | await run('git init', workingDirectory: repo.absolute.path);
49 | await runInstallScript(
50 | appDir: repo.absolute.path, gitRootDir: repo.absolute.path);
51 | await run('git commit -a -m "initial commit"',
52 | workingDirectory: repo.absolute.path);
53 | expect(repo.childFile('.gitmodules').readAsStringSync(),
54 | contains('branch = stable'));
55 |
56 | await run('./flutterw channel beta',
57 | workingDirectory: repo.absolute.path);
58 | expect(repo.childFile('.gitmodules').readAsStringSync(),
59 | contains('branch = beta'));
60 | },
61 | timeout: const Timeout(Duration(minutes: 5)),
62 | );
63 |
64 | test(
65 | 'flutter channel without second arg does not update gitmodules (single module)',
66 | () async {
67 | final repo =
68 | const LocalFileSystem().systemTempDirectory.createTempSync('repo');
69 | addTearDown(() {
70 | repo.deleteSync(recursive: true);
71 | });
72 | await run('git init', workingDirectory: repo.absolute.path);
73 | await runInstallScript(
74 | appDir: repo.absolute.path, gitRootDir: repo.absolute.path);
75 | await run('git commit -a -m "initial commit"',
76 | workingDirectory: repo.absolute.path);
77 | expect(repo.childFile('.gitmodules').readAsStringSync(),
78 | contains('branch = stable'));
79 |
80 | await run('./flutterw channel', workingDirectory: repo.absolute.path);
81 | expect(repo.childFile('.gitmodules').readAsStringSync(),
82 | contains('branch = stable'));
83 | },
84 | timeout: const Timeout(Duration(minutes: 5)),
85 | );
86 |
87 | test(
88 | 'flutter channel called from package updates gitmodules in root',
89 | () async {
90 | final repo =
91 | const LocalFileSystem().systemTempDirectory.createTempSync('repo');
92 | addTearDown(() {
93 | repo.deleteSync(recursive: true);
94 | });
95 | await run('git init', workingDirectory: repo.absolute.path);
96 | await runInstallScript(
97 | appDir: repo.absolute.path, gitRootDir: repo.absolute.path);
98 | await run('git commit -a -m "initial commit"',
99 | workingDirectory: repo.absolute.path);
100 | expect(repo.childFile('.gitmodules').readAsStringSync(),
101 | contains('branch = stable'));
102 |
103 | // create package
104 | final package = repo.childDirectory('packages/xyz')
105 | ..createSync(recursive: true);
106 |
107 | await run('./../../flutterw channel beta',
108 | workingDirectory: package.absolute.path);
109 | // doesn't accidentally create a .gitmodules file in package
110 | expect(package.childFile('.gitmodules').existsSync(), isFalse);
111 | // updates .gitmodules in root
112 | expect(repo.childFile('.gitmodules').readAsStringSync(),
113 | contains('branch = beta'));
114 | },
115 | timeout: const Timeout(Duration(minutes: 5)),
116 | );
117 |
118 | test(
119 | 'flutter channel without second arg called from package does not update gitmodules in root',
120 | () async {
121 | final repo =
122 | const LocalFileSystem().systemTempDirectory.createTempSync('repo');
123 | addTearDown(() {
124 | repo.deleteSync(recursive: true);
125 | });
126 | await run('git init', workingDirectory: repo.absolute.path);
127 | await runInstallScript(
128 | appDir: repo.absolute.path, gitRootDir: repo.absolute.path);
129 | await run('git commit -a -m "initial commit"',
130 | workingDirectory: repo.absolute.path);
131 | expect(repo.childFile('.gitmodules').readAsStringSync(),
132 | contains('branch = stable'));
133 |
134 | // create package
135 | final package = repo.childDirectory('packages/xyz')
136 | ..createSync(recursive: true);
137 |
138 | await run('./../../flutterw channel',
139 | workingDirectory: package.absolute.path);
140 | // doesn't accidentally create a .gitmodules file in package
141 | expect(package.childFile('.gitmodules').existsSync(), isFalse);
142 | // Doesn't update .gitmodules in root
143 | expect(repo.childFile('.gitmodules').readAsStringSync(),
144 | contains('branch = stable'));
145 | },
146 | timeout: const Timeout(Duration(minutes: 5)),
147 | );
148 | });
149 | }
150 |
--------------------------------------------------------------------------------
/test/test/update_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:cli_script/cli_script.dart';
2 | import 'package:file/file.dart';
3 | import 'package:file/local.dart';
4 | import 'package:test/test.dart';
5 |
6 | import 'install_test.dart';
7 |
8 | void main() {
9 | group('update', () {
10 | group("Update flutterw in git root", () {
11 | late Directory gitRootDir;
12 | late Directory appDir;
13 |
14 | tearDownAll(() {
15 | appDir.parent.deleteSync(recursive: true);
16 | });
17 | setUpAll(() async {
18 | final dir =
19 | const LocalFileSystem().systemTempDirectory.createTempSync('root');
20 | gitRootDir = appDir = dir.childDirectory('myApp');
21 | assert(gitRootDir == appDir);
22 |
23 | // create git in appDir
24 | appDir.createSync();
25 | await run("git init", workingDirectory: appDir.absolute.path);
26 |
27 | await runInstallScript(
28 | appDir: appDir.absolute.path, gitRootDir: gitRootDir.absolute.path);
29 | });
30 |
31 | test('updates flutterw', () async {
32 | final flutterw = appDir.childFile('flutterw');
33 | final content = flutterw.readAsStringSync();
34 | await runInstallScript(
35 | appDir: appDir.absolute.path, gitRootDir: gitRootDir.absolute.path);
36 |
37 | final update = flutterw.readAsStringSync();
38 | expect(update, isNot(content));
39 | });
40 | });
41 |
42 | group("update in subdir", () {
43 | late Directory gitRootDir;
44 | late Directory appDir;
45 |
46 | tearDownAll(() {
47 | gitRootDir.deleteSync(recursive: true);
48 | });
49 |
50 | setUpAll(() async {
51 | gitRootDir =
52 | const LocalFileSystem().systemTempDirectory.createTempSync('root');
53 | // git repo in root, flutterw in appDir
54 | appDir = gitRootDir.childDirectory('myApp')..createSync();
55 |
56 | await run("git init", workingDirectory: gitRootDir.absolute.path);
57 | await runInstallScript(
58 | appDir: appDir.absolute.path, gitRootDir: gitRootDir.absolute.path);
59 | });
60 |
61 | test('updates flutterw', () async {
62 | final flutterw = appDir.childFile('flutterw');
63 | final content = flutterw.readAsStringSync();
64 | await runInstallScript(
65 | appDir: appDir.absolute.path, gitRootDir: gitRootDir.absolute.path);
66 |
67 | final update = flutterw.readAsStringSync();
68 | expect(update, isNot(content));
69 | });
70 | });
71 | });
72 | }
73 |
--------------------------------------------------------------------------------
/uninstall.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | echo "Uninstalling Flutter Wrapper..."
4 |
5 | FLUTTER_DIR_NAME='.flutter'
6 |
7 | # remove wrapper executable via git or fallback just the wrapper file when not
8 | # known to git
9 | git rm -f flutterw >>/dev/null 2>&1 || rm flutterw
10 |
11 | # remove submodule
12 | git submodule deinit -f $FLUTTER_DIR_NAME
13 |
14 | # remove submodule directory
15 | git rm -rf $FLUTTER_DIR_NAME
16 |
17 | # remove submodule history
18 | rm -rf .git/modules/$FLUTTER_DIR_NAME
19 |
20 | # remove empty .gitmodules file
21 | if ! [ -s .gitmodules ]; then
22 | # try via git first, fallback to just rm when not added to git
23 | git rm -f .gitmodules >>/dev/null 2>&1 || rm .gitmodules
24 | fi
25 |
--------------------------------------------------------------------------------
/version:
--------------------------------------------------------------------------------
1 | 1.3.1
2 |
--------------------------------------------------------------------------------