mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-14 10:08:06 -10:00
Compare commits
462
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2be2536654 | ||
|
|
bd0a696b4e | ||
|
|
230e364379 | ||
|
|
eaebb72813 | ||
|
|
cf39201de3 | ||
|
|
bb3e815716 | ||
|
|
beec6ad305 | ||
|
|
84b47924b7 | ||
|
|
3ff62a9c79 | ||
|
|
2751a7e0de | ||
|
|
7139e4f576 | ||
|
|
ecbe49abaa | ||
|
|
6af86b545f | ||
|
|
a9a77b6fcb | ||
|
|
82fccc8193 | ||
|
|
5f800b5be6 | ||
|
|
3817e2d387 | ||
|
|
eb4c5e073d | ||
|
|
eefc133eda | ||
|
|
7f9969429d | ||
|
|
26ff083b7c | ||
|
|
6414361d4c | ||
|
|
28ce6b7dc7 | ||
|
|
4772f1aea5 | ||
|
|
2a1fd290b9 | ||
|
|
e83565c518 | ||
|
|
f38a739858 | ||
|
|
3fcbcf5f6b | ||
|
|
30936911ad | ||
|
|
a0606adde9 | ||
|
|
f0e3b648a1 | ||
|
|
3ed5e7821e | ||
|
|
466339b90e | ||
|
|
1d07e08a70 | ||
|
|
c21270e062 | ||
|
|
afb548599e | ||
|
|
0b54169631 | ||
|
|
af13f3987e | ||
|
|
3ea71e57cb | ||
|
|
261dbb4be4 | ||
|
|
5dd02e2b40 | ||
|
|
9447cbc478 | ||
|
|
562bd3fe9c | ||
|
|
d6840de401 | ||
|
|
d9bd20a47b | ||
|
|
13da8d4bfc | ||
|
|
a99fe9aef5 | ||
|
|
61109ea2c1 | ||
|
|
f889929f5f | ||
|
|
4bb314192e | ||
|
|
1a170283db | ||
|
|
ca803eed3f | ||
|
|
4b26472975 | ||
|
|
70b854abb1 | ||
|
|
2576a97704 | ||
|
|
15e50b1536 | ||
|
|
d6f3784cc0 | ||
|
|
a6f7d37d71 | ||
|
|
675c36a157 | ||
|
|
b2961a275b | ||
|
|
57928d1fd6 | ||
|
|
ff9d533248 | ||
|
|
e1ebcc0b8a | ||
|
|
628a7262cf | ||
|
|
05be162ec1 | ||
|
|
ff2fa425bd | ||
|
|
a621e395fc | ||
|
|
2260f74434 | ||
|
|
6dc9253d2d | ||
|
|
b6570bdfcf | ||
|
|
4d1675c96f | ||
|
|
00d9009967 | ||
|
|
b68840b76b | ||
|
|
6d8be295bf | ||
|
|
40c1cbe9f6 | ||
|
|
efae679089 | ||
|
|
6a2a3fe037 | ||
|
|
917987a41e | ||
|
|
bbe8fb9698 | ||
|
|
d656103357 | ||
|
|
4b7ad9f23a | ||
|
|
4db3979ab5 | ||
|
|
63e0bf2a24 | ||
|
|
a740aa174b | ||
|
|
f9ef83ba52 | ||
|
|
de8956c2ec | ||
|
|
41622c0cd3 | ||
|
|
d54abee7c3 | ||
|
|
cdd046b647 | ||
|
|
4577cf957d | ||
|
|
84b56f86eb | ||
|
|
b2580d0cc2 | ||
|
|
0f18ea01a9 | ||
|
|
fe9927862c | ||
|
|
bc2f6e60f4 | ||
|
|
6ac1e54f67 | ||
|
|
479e8d0b5d | ||
|
|
7ed62ec7f1 | ||
|
|
87000c36c1 | ||
|
|
a9d08f39ad | ||
|
|
55c156dd00 | ||
|
|
736d414cd6 | ||
|
|
85de580d84 | ||
|
|
1a65b2cc4e | ||
|
|
b3fca978d0 | ||
|
|
ba4c4e5954 | ||
|
|
383caa0354 | ||
|
|
2e43d4bba0 | ||
|
|
b995d1d713 | ||
|
|
d307f2343b | ||
|
|
9eeaee544a | ||
|
|
d9092e15f2 | ||
|
|
f90f085dcc | ||
|
|
61bb001059 | ||
|
|
9ef86f51fa | ||
|
|
44cd3b2669 | ||
|
|
0c706048be | ||
|
|
211a3fb6bf | ||
|
|
baeb88f5be | ||
|
|
ec3c27f744 | ||
|
|
054732c245 | ||
|
|
70c876776d | ||
|
|
0f45f938b1 | ||
|
|
c8313f36e7 | ||
|
|
a8cde5e81d | ||
|
|
d05142f9fc | ||
|
|
8e26c7c508 | ||
|
|
c29018df20 | ||
|
|
8b7d977b5b | ||
|
|
12f98c2471 | ||
|
|
d6186edb69 | ||
|
|
b20cf472bf | ||
|
|
0ab64d5eaa | ||
|
|
40afed198f | ||
|
|
5337929696 | ||
|
|
4c97fc31f8 | ||
|
|
42252cd355 | ||
|
|
784b52bddb | ||
|
|
d85b3283f0 | ||
|
|
0c332eb6d3 | ||
|
|
26bb7052ef | ||
|
|
8333d3361a | ||
|
|
f62a8cad27 | ||
|
|
b52145c0a7 | ||
|
|
e06fc189aa | ||
|
|
b346f96c63 | ||
|
|
26ba0719ce | ||
|
|
c33dff99d2 | ||
|
|
3d7d851d07 | ||
|
|
fad3f1346c | ||
|
|
dece2e5242 | ||
|
|
f579441e05 | ||
|
|
e40634297b | ||
|
|
17ea5d4f01 | ||
|
|
d803e52085 | ||
|
|
e400515fa7 | ||
|
|
e6233b3028 | ||
|
|
eec975c171 | ||
|
|
08ea07bf5c | ||
|
|
e5d94ad15c | ||
|
|
a51fbd37ca | ||
|
|
1c78e09676 | ||
|
|
d1edec6549 | ||
|
|
ab6483f7a5 | ||
|
|
32848f6255 | ||
|
|
7e2aae333d | ||
|
|
191cb2396c | ||
|
|
33fb5fad03 | ||
|
|
3ec6e5dd14 | ||
|
|
b6acbf05d8 | ||
|
|
957874d8cc | ||
|
|
1a5fbc2342 | ||
|
|
68cd7e6fba | ||
|
|
628a3f708a | ||
|
|
3ac45f08ba | ||
|
|
73ba19de66 | ||
|
|
ef6623cd15 | ||
|
|
d78917b947 | ||
|
|
2b59a5e2ee | ||
|
|
e6465cd3e5 | ||
|
|
561e3bbb5a | ||
|
|
64a6e3a0f7 | ||
|
|
33c965b236 | ||
|
|
831e69e5c6 | ||
|
|
7d47060b12 | ||
|
|
b6b22b79c3 | ||
|
|
6770cd99c9 | ||
|
|
704745b829 | ||
|
|
3780e2ae1c | ||
|
|
e93854d039 | ||
|
|
89354f5932 | ||
|
|
a074ce0cff | ||
|
|
31f583442a | ||
|
|
9bf1f383e5 | ||
|
|
e157473f41 | ||
|
|
ba87bb6e29 | ||
|
|
efd9213fab | ||
|
|
a3d9bc0eca | ||
|
|
39dc6299d2 | ||
|
|
37573bed50 | ||
|
|
2e49f6348e | ||
|
|
3a7ddbd025 | ||
|
|
a5c1100ce1 | ||
|
|
0b71812694 | ||
|
|
caf2403dfd | ||
|
|
09fe5dd1a3 | ||
|
|
048460f323 | ||
|
|
69b0edae8d | ||
|
|
9ee9ea39bb | ||
|
|
204a8b7eb4 | ||
|
|
a6e281b6c8 | ||
|
|
315b5b23f8 | ||
|
|
ceb561a4af | ||
|
|
63743cae65 | ||
|
|
e8e5eb622c | ||
|
|
fe8f1b32b3 | ||
|
|
3a1e5298a4 | ||
|
|
92c1256e8b | ||
|
|
61543c7983 | ||
|
|
62d149ae60 | ||
|
|
bbf14e2ee8 | ||
|
|
126c5627eb | ||
|
|
b5f2f5286a | ||
|
|
8ea05793dd | ||
|
|
02c8dcaa5b | ||
|
|
fe1a1f14d1 | ||
|
|
df7b18ca03 | ||
|
|
41142a9486 | ||
|
|
72c28d4259 | ||
|
|
da42962151 | ||
|
|
bbebe90876 | ||
|
|
ea57d6383a | ||
|
|
f5a99cea6b | ||
|
|
e9064b7d25 | ||
|
|
10ab6724e1 | ||
|
|
ab8115b6d3 | ||
|
|
ccc8d5ac02 | ||
|
|
4c07b89eb6 | ||
|
|
189d163a41 | ||
|
|
098b1665bc | ||
|
|
51a01d10e9 | ||
|
|
28042ef5a6 | ||
|
|
14dd3d29ea | ||
|
|
b1fd4e8e3a | ||
|
|
bcc5f2b3ec | ||
|
|
0aea5a3dc6 | ||
|
|
493cf56c32 | ||
|
|
eeb143af8a | ||
|
|
2dab7846be | ||
|
|
32b8d7dc77 | ||
|
|
183fe8712b | ||
|
|
06466e5967 | ||
|
|
b5c905734a | ||
|
|
7e74829f8f | ||
|
|
655f6c8a84 | ||
|
|
43180c0ebe | ||
|
|
1a5df5ce55 | ||
|
|
533bde2442 | ||
|
|
899a563c1a | ||
|
|
f0012f59a4 | ||
|
|
b44ba6b22b | ||
|
|
0cd0c8c5bd | ||
|
|
ffa45bbefa | ||
|
|
8c8b354bee | ||
|
|
5b46476175 | ||
|
|
8817842b72 | ||
|
|
1db76a1b2d | ||
|
|
50d78b04d8 | ||
|
|
a541a2e534 | ||
|
|
97c6df81af | ||
|
|
e300d7a118 | ||
|
|
7c48b2bd35 | ||
|
|
fea791300d | ||
|
|
54b1028cd6 | ||
|
|
8e7c668f47 | ||
|
|
cd6a0c49f3 | ||
|
|
5e31a9d55d | ||
|
|
cd3a62ec80 | ||
|
|
abda123286 | ||
|
|
5815b8b3a8 | ||
|
|
1bdab63799 | ||
|
|
c33334bbee | ||
|
|
9f4e39b21f | ||
|
|
f8cfa009c1 | ||
|
|
2600805da2 | ||
|
|
335fb1f55e | ||
|
|
d2bb189cf3 | ||
|
|
dc25b207b9 | ||
|
|
5bf7f279fd | ||
|
|
fe710d03fa | ||
|
|
7c7d6ec118 | ||
|
|
b9064287fc | ||
|
|
239ad4a88e | ||
|
|
7374a93f03 | ||
|
|
b2bab3a523 | ||
|
|
e8df9cdeb6 | ||
|
|
14980fe6e8 | ||
|
|
743ae7c648 | ||
|
|
5ed155e67a | ||
|
|
aa14f21597 | ||
|
|
91787e1f76 | ||
|
|
474c95a7b7 | ||
|
|
0e4f00df2f | ||
|
|
c5d75568ef | ||
|
|
4982e12bf6 | ||
|
|
41c039c428 | ||
|
|
606ce9a1ff | ||
|
|
8df2a08b20 | ||
|
|
1948fd6e81 | ||
|
|
49b6e1f9e4 | ||
|
|
a56abe33dd | ||
|
|
feaedbe634 | ||
|
|
0fba735f7a | ||
|
|
480bc30468 | ||
|
|
c1c9e06f76 | ||
|
|
5fb68a652b | ||
|
|
056d0b1017 | ||
|
|
cc99794bbd | ||
|
|
04049a8d1f | ||
|
|
5bdd46a2a9 | ||
|
|
a7e88bd7cb | ||
|
|
78b75c6224 | ||
|
|
52f54fc7ae | ||
|
|
7ddd630cfd | ||
|
|
8751a2835f | ||
|
|
da9829a853 | ||
|
|
cc822b18f3 | ||
|
|
bcab3bcb09 | ||
|
|
a5dc0bd27c | ||
|
|
54127c4e37 | ||
|
|
9097ab2537 | ||
|
|
4e2029a5c5 | ||
|
|
ad02a056d7 | ||
|
|
ea9f734b11 | ||
|
|
7db9d79c55 | ||
|
|
2e31b42b76 | ||
|
|
66d02d9724 | ||
|
|
7c96cc10cf | ||
|
|
523a781ba5 | ||
|
|
6cf8bd5c5a | ||
|
|
67e9ea706b | ||
|
|
c22f410889 | ||
|
|
9ff47af4fa | ||
|
|
324dce8fc0 | ||
|
|
b30c86f0c0 | ||
|
|
008279e1dd | ||
|
|
d00814f84b | ||
|
|
81aa127e28 | ||
|
|
d321a0bccb | ||
|
|
a1d86549ed | ||
|
|
f7e9af913c | ||
|
|
137f9d4ac7 | ||
|
|
2ac863a9e3 | ||
|
|
11be9eba49 | ||
|
|
b36db15601 | ||
|
|
93c5dc1a32 | ||
|
|
c86550c0c8 | ||
|
|
36b4296d3c | ||
|
|
8c08541ca3 | ||
|
|
5a1406d39c | ||
|
|
78ea88cc5f | ||
|
|
6cb02d0ccd | ||
|
|
d6c7623718 | ||
|
|
32142b4fe8 | ||
|
|
6bdee0f395 | ||
|
|
baf94a21c3 | ||
|
|
162d64bce6 | ||
|
|
d16c1fc757 | ||
|
|
e42ea7e2ec | ||
|
|
5fea1ad16e | ||
|
|
2a57a2eb3d | ||
|
|
5628b78523 | ||
|
|
19d1e6bffd | ||
|
|
88b068511c | ||
|
|
842efcf3ab | ||
|
|
f5f08aa506 | ||
|
|
9f7f412cfb | ||
|
|
e369ec1e25 | ||
|
|
b353441f31 | ||
|
|
4db35e73c6 | ||
|
|
10bc872dc9 | ||
|
|
fedc39ea48 | ||
|
|
6046723769 | ||
|
|
afaee9df38 | ||
|
|
de39d6f00d | ||
|
|
401751fffa | ||
|
|
7f7f1c6a9a | ||
|
|
279d6716d2 | ||
|
|
92977d9a53 | ||
|
|
31871a9eda | ||
|
|
f122f42149 | ||
|
|
a9a49d6b97 | ||
|
|
1d94414334 | ||
|
|
0b73dc32f6 | ||
|
|
ef8ad24639 | ||
|
|
24dbe6e7cc | ||
|
|
aad026febb | ||
|
|
6ae8125c0e | ||
|
|
87d143b310 | ||
|
|
346dee9ea5 | ||
|
|
4308d21927 | ||
|
|
207f623338 | ||
|
|
601101844c | ||
|
|
7027eaa974 | ||
|
|
b3203345ea | ||
|
|
5ed2d01146 | ||
|
|
6fc64d337f | ||
|
|
f7ed4cfa20 | ||
|
|
47968a1b8b | ||
|
|
7c779b3dc0 | ||
|
|
50474ec5e9 | ||
|
|
46ff97f173 | ||
|
|
b263dffbde | ||
|
|
8d57828f7e | ||
|
|
fd9561cfc3 | ||
|
|
93ce451bc7 | ||
|
|
554e433548 | ||
|
|
12a1cb7011 | ||
|
|
360de90eea | ||
|
|
ea02fcebda | ||
|
|
0b5194b1a5 | ||
|
|
05799aba9c | ||
|
|
2cece65dbf | ||
|
|
17b6b4ec45 | ||
|
|
4f892c66e7 | ||
|
|
fd99365655 | ||
|
|
7dca2c8021 | ||
|
|
55bce8d82a | ||
|
|
1f5533e5e3 | ||
|
|
51d0555baa | ||
|
|
2137176a65 | ||
|
|
3fda52c8d7 | ||
|
|
b252b5b582 | ||
|
|
2befe410cf | ||
|
|
1463e0de74 | ||
|
|
e963558ef6 | ||
|
|
fe57c055b6 | ||
|
|
045840111e | ||
|
|
9a6068c1f2 | ||
|
|
d06a77bd6d | ||
|
|
7bcf83d141 | ||
|
|
cd9323e115 | ||
|
|
34145c38ca | ||
|
|
57dd372957 | ||
|
|
5232641217 | ||
|
|
3c3d3150c9 | ||
|
|
fd322d4189 | ||
|
|
77254f6be1 | ||
|
|
44fcbbaf17 | ||
|
|
db841b45ee | ||
|
|
568669d384 | ||
|
|
aa50bf11ce | ||
|
|
119133360b | ||
|
|
3f0f25dd46 | ||
|
|
e821ae95e1 | ||
|
|
f4e51b4fbc | ||
|
|
963bea9b03 | ||
|
|
9bf2020b2b | ||
|
|
50e695b5a3 | ||
|
|
f3577deac4 | ||
|
|
f11c0b4d34 | ||
|
|
88411e8fc0 |
No files matched your search
@@ -0,0 +1,5 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Expo Developers Discord
|
||||||
|
url: https://chat.expo.dev/
|
||||||
|
about: Join other developers in discussions regarding Expo and EAS.
|
||||||
@@ -14,9 +14,9 @@ jobs:
|
|||||||
token: ${{ secrets.EXPO_BOT_PAT }}
|
token: ${{ secrets.EXPO_BOT_PAT }}
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 20
|
||||||
- name: Checkout Pull Request
|
- name: Checkout Pull Request
|
||||||
run: hub pr checkout ${{ github.event.issue.number }}
|
run: gh pr checkout ${{ github.event.issue.number }}
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ on:
|
|||||||
- labeled
|
- labeled
|
||||||
- unlabeled
|
- unlabeled
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: changelog-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pr-changelog-entry:
|
pr-changelog-entry:
|
||||||
name: Check CHANGELOG.md updated
|
name: Check CHANGELOG.md updated
|
||||||
@@ -16,7 +20,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Check if CHANGELOG.md was updated
|
- name: Check if CHANGELOG.md was updated
|
||||||
id: changelog-updated
|
id: changelog-updated
|
||||||
uses: tj-actions/changed-files@v35
|
uses: tj-actions/changed-files@v41
|
||||||
with:
|
with:
|
||||||
files: CHANGELOG.md
|
files: CHANGELOG.md
|
||||||
- name: Fail if CHANGELOG.md was not updated and the "no changelog" label is absent
|
- name: Fail if CHANGELOG.md was not updated and the "no changelog" label is absent
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 20
|
||||||
- run: yarn install --frozen-lockfile --check-files
|
- run: yarn install --frozen-lockfile --check-files
|
||||||
- run: yarn build
|
- run: yarn build
|
||||||
- name: Build tarballs
|
- name: Build tarballs
|
||||||
@@ -68,17 +68,23 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 20
|
||||||
|
- run: brew install python-setuptools
|
||||||
|
env:
|
||||||
|
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||||
- run: yarn install --frozen-lockfile --check-files
|
- run: yarn install --frozen-lockfile --check-files
|
||||||
- run: yarn build
|
- run: yarn build
|
||||||
- name: Build tarballs
|
- name: Build tarballs
|
||||||
working-directory: ${{ github.workspace }}/packages/eas-cli
|
working-directory: ${{ github.workspace }}/packages/eas-cli
|
||||||
run: |
|
run: |
|
||||||
yarn pretarball-ci
|
yarn pretarball-ci
|
||||||
yarn oclif pack:tarballs --no-xz --targets darwin-x64
|
yarn oclif pack:tarballs --no-xz --targets darwin-x64,darwin-arm64
|
||||||
- id: x64
|
- id: x64
|
||||||
run: echo "filename=$(ls eas-*-x64.tar.gz)" >> $GITHUB_OUTPUT
|
run: echo "filename=$(ls eas-*-x64.tar.gz)" >> $GITHUB_OUTPUT
|
||||||
working-directory: ${{ github.workspace }}/packages/eas-cli/dist
|
working-directory: ${{ github.workspace }}/packages/eas-cli/dist
|
||||||
|
- id: arm64
|
||||||
|
run: echo "filename=$(ls eas-*-arm64.tar.gz)" >> $GITHUB_OUTPUT
|
||||||
|
working-directory: ${{ github.workspace }}/packages/eas-cli/dist
|
||||||
- name: Upload darwin-x64 tarball
|
- name: Upload darwin-x64 tarball
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
@@ -88,6 +94,15 @@ jobs:
|
|||||||
asset_path: packages/eas-cli/dist/${{ steps.x64.outputs.filename }}
|
asset_path: packages/eas-cli/dist/${{ steps.x64.outputs.filename }}
|
||||||
asset_name: ${{ steps.x64.outputs.filename }}
|
asset_name: ${{ steps.x64.outputs.filename }}
|
||||||
asset_content_type: application/gzip
|
asset_content_type: application/gzip
|
||||||
|
- name: Upload darwin-arm64 tarball
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ needs.release.outputs.upload_url }}
|
||||||
|
asset_path: packages/eas-cli/dist/${{ steps.arm64.outputs.filename }}
|
||||||
|
asset_name: ${{ steps.arm64.outputs.filename }}
|
||||||
|
asset_content_type: application/gzip
|
||||||
build-windows:
|
build-windows:
|
||||||
name: Build for Windows
|
name: Build for Windows
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -96,7 +111,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 20
|
||||||
- run: yarn install --frozen-lockfile --check-files
|
- run: yarn install --frozen-lockfile --check-files
|
||||||
- run: sudo apt-get install nsis
|
- run: sudo apt-get install nsis
|
||||||
- run: yarn build
|
- run: yarn build
|
||||||
@@ -138,7 +153,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
registry-url: 'https://registry.npmjs.org/'
|
registry-url: 'https://registry.npmjs.org/'
|
||||||
scope: 'expo'
|
scope: 'expo'
|
||||||
node-version: 18
|
node-version: 20
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install --frozen-lockfile --check-files
|
run: yarn install --frozen-lockfile --check-files
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -152,7 +167,13 @@ jobs:
|
|||||||
VERSION=$(cat lerna.json | jq -r .version)
|
VERSION=$(cat lerna.json | jq -r .version)
|
||||||
echo //registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN > ~/.npmrc
|
echo //registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN > ~/.npmrc
|
||||||
npm dist-tag add eas-cli@$VERSION latest-eas-build-staging
|
npm dist-tag add eas-cli@$VERSION latest-eas-build-staging
|
||||||
echo "Run \"npm dist-tag add eas-cli@$VERSION latest-eas-build\" to promote it to production."
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
|
||||||
|
- name: Add latest-eas-build tag
|
||||||
|
run: |
|
||||||
|
VERSION=$(cat lerna.json | jq -r .version)
|
||||||
|
echo //registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN > ~/.npmrc
|
||||||
|
npm dist-tag add eas-cli@$VERSION latest-eas-build
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
|
||||||
release-changelog:
|
release-changelog:
|
||||||
@@ -165,7 +186,7 @@ jobs:
|
|||||||
token: ${{ secrets.EXPO_BOT_PAT }}
|
token: ${{ secrets.EXPO_BOT_PAT }}
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 20
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
working-directory: ./scripts
|
working-directory: ./scripts
|
||||||
run: yarn install --frozen-lockfile --check-files
|
run: yarn install --frozen-lockfile --check-files
|
||||||
@@ -175,7 +196,7 @@ jobs:
|
|||||||
working-directory: ./scripts
|
working-directory: ./scripts
|
||||||
run: yarn --silent release-changelog $EAS_CLI_VERSION > /tmp/current-changelog
|
run: yarn --silent release-changelog $EAS_CLI_VERSION > /tmp/current-changelog
|
||||||
- name: Publish release
|
- name: Publish release
|
||||||
run: hub release edit v$EAS_CLI_VERSION --draft=false -F /tmp/current-changelog
|
run: gh release edit v$EAS_CLI_VERSION --draft=false -F /tmp/current-changelog
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Commit and push
|
- name: Commit and push
|
||||||
@@ -205,3 +226,16 @@ jobs:
|
|||||||
SLACK_USERNAME: 'EAS CLI CI'
|
SLACK_USERNAME: 'EAS CLI CI'
|
||||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||||
SLACK_FOOTER: ''
|
SLACK_FOOTER: ''
|
||||||
|
open-pr-in-universe:
|
||||||
|
name: Open a PR with `@expo/eas-json` bump in universe
|
||||||
|
needs: publish-to-npm
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Open a PR with `@expo/eas-json` version bump in universe
|
||||||
|
uses: benc-uk/workflow-dispatch@v1
|
||||||
|
with:
|
||||||
|
repo: expo/universe
|
||||||
|
ref: main
|
||||||
|
workflow: update-eas-json.yml
|
||||||
|
token: ${{ secrets.EXPO_BOT_PAT }}
|
||||||
@@ -2,6 +2,9 @@ name: Size
|
|||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [synchronize, opened]
|
types: [synchronize, opened]
|
||||||
|
concurrency:
|
||||||
|
group: size-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
jobs:
|
jobs:
|
||||||
compare:
|
compare:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -9,7 +12,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 20
|
||||||
- uses: preactjs/compressed-size-action@v2
|
- uses: preactjs/compressed-size-action@v2
|
||||||
with:
|
with:
|
||||||
build-script: 'build:tarballs:linux'
|
build-script: 'build:tarballs:linux'
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- 'scripts/**'
|
- 'scripts/**'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: test-scripts-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -21,6 +25,6 @@ jobs:
|
|||||||
- name: Setup node
|
- name: Setup node
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 20
|
||||||
- run: yarn install --frozen-lockfile --check-files
|
- run: yarn install --frozen-lockfile --check-files
|
||||||
- run: yarn typecheck
|
- run: yarn typecheck
|
||||||
@@ -6,14 +6,18 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, synchronize]
|
types: [opened, synchronize]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: test-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node: ['14', '16', '18']
|
node: ['18', '20', '22']
|
||||||
include:
|
include:
|
||||||
- node: '18'
|
- node: '20'
|
||||||
coverage: true
|
coverage: true
|
||||||
name: Test with Node ${{ matrix.node }}
|
name: Test with Node ${{ matrix.node }}
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
name: Open a PR bumping eas-build modules
|
||||||
|
|
||||||
|
on: workflow_dispatch
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
open-pr:
|
||||||
|
name: Open a PR bumping eas-build modules
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: main
|
||||||
|
token: ${{ secrets.EXPO_BOT_PAT }}
|
||||||
|
|
||||||
|
- name: Setup node
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
|
||||||
|
- run: yarn install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Configure git
|
||||||
|
run: |
|
||||||
|
git config --global user.email "[email protected]"
|
||||||
|
git config --global user.name "Expo Bot"
|
||||||
|
|
||||||
|
- name: Upgrade eas-build modules
|
||||||
|
run: |
|
||||||
|
for DIR in packages/eas-cli packages/eas-json; do
|
||||||
|
pushd $DIR >/dev/null 2>&1
|
||||||
|
# packages are ordered according to output of `lerna list --toposort`
|
||||||
|
# https://github.com/expo/turtle-v2/pull/568
|
||||||
|
for PACKAGE in "@expo/downloader" "@expo/eas-build-job" "@expo/logger" "@expo/template-file" "@expo/steps" "@expo/turtle-spawn" "@expo/build-tools"; do
|
||||||
|
if grep -q $PACKAGE package.json; then
|
||||||
|
echo "[$DIR] yarn --exact add $PACKAGE"
|
||||||
|
yarn add --exact $PACKAGE
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
popd >/dev/null 2>&1
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Set a branch name
|
||||||
|
id: bump-branch-name
|
||||||
|
run: |
|
||||||
|
HASH="$(echo $RANDOM | md5sum | head -c 10)"
|
||||||
|
BRANCH_NAME="@expo-bot/eas-build-bump-$HASH"
|
||||||
|
|
||||||
|
echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Create a branch, commit, and push
|
||||||
|
run: |
|
||||||
|
BRANCH_NAME="${{ steps.bump-branch-name.outputs.BRANCH_NAME }}"
|
||||||
|
|
||||||
|
git checkout -b $BRANCH_NAME
|
||||||
|
git add .
|
||||||
|
git commit -m "Upgrade eas-build dependencies"
|
||||||
|
git push origin $BRANCH_NAME
|
||||||
|
|
||||||
|
- name: Open a PR to main
|
||||||
|
uses: repo-sync/pull-request@v2
|
||||||
|
id: open-pr
|
||||||
|
with:
|
||||||
|
source_branch: '${{ steps.bump-branch-name.outputs.BRANCH_NAME }}'
|
||||||
|
destination_branch: 'main'
|
||||||
|
pr_title: 'Upgrade eas-build dependencies'
|
||||||
|
pr_body: ':arrow_up: Upgrades [`eas-build`](https://github.com/expo/eas-build) dependencies.'
|
||||||
|
pr_reviewer: 'szdziedzic,radoslawkrzemien,sjchmiela,khamilowicz'
|
||||||
|
github_token: ${{ secrets.EXPO_BOT_PAT }}
|
||||||
|
|
||||||
|
- uses: actions/github-script@v3
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.EXPO_BOT_PAT }}
|
||||||
|
script: |
|
||||||
|
github.issues.createComment({
|
||||||
|
issue_number: ${{ steps.open-pr.outputs.pr_number }},
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
body: "/changelog-entry chore Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies."
|
||||||
|
})
|
||||||
Vendored
+10
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"eslint.enable": true,
|
||||||
|
"search.exclude": {
|
||||||
|
"**/node_modules": true,
|
||||||
|
"**/bower_components": true,
|
||||||
|
"**/*.code-search": true,
|
||||||
|
"**/src/**/build/**": false,
|
||||||
|
"**/src/build/**": false,
|
||||||
|
}
|
||||||
|
}
|
||||||
+691
-3
@@ -8,16 +8,704 @@ This is the log of notable changes to EAS CLI and related packages.
|
|||||||
|
|
||||||
### 🎉 New features
|
### 🎉 New features
|
||||||
|
|
||||||
- Show build profile when selecting a build for eas build:run. ([#1901](https://github.com/expo/eas-cli/pull/1901) by [@keith-kurak](https://github.com/keith-kurak))
|
### 🐛 Bug fixes
|
||||||
- Adds -m alias to --message in update/republish and removed README comment. ([#1905](https://github.com/expo/eas-cli/pull/1905) by [@pusongqi](https://github.com/pusongqi))
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Remove any mentions of deleted Xcode < 15 images. ([#2438](https://github.com/expo/eas-cli/pull/2438) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [10.0.2](https://github.com/expo/eas-cli/releases/tag/v10.0.2) - 2024-06-17
|
||||||
|
|
||||||
### 🐛 Bug fixes
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Fix parsing of `build.gradle` file by `gradle-to-js` parser by filtering out empty single line comments. ([#2435](https://github.com/expo/eas-cli/pull/2435) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [10.0.1](https://github.com/expo/eas-cli/releases/tag/v10.0.1) - 2024-06-17
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Add clarification to private-key flag in command line help. ([#2432](https://github.com/expo/eas-cli/pull/2432) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
## [10.0.0](https://github.com/expo/eas-cli/releases/tag/v10.0.0) - 2024-06-13
|
||||||
|
|
||||||
|
### 🛠 Breaking changes
|
||||||
|
|
||||||
|
- Drop support for Node 16. ([#2413](https://github.com/expo/eas-cli/pull/2413) by [@byCedric](https://github.com/byCedric))
|
||||||
|
- Update [`eas-build`](https://github.com/expo/eas-build) dependencies to the version requiring Node 18 as minimal Node version. ([#2416](https://github.com/expo/eas-cli/pull/2416) by [@expo-bot](https://github.com/expo-bot))
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Resolve correct submit profile configuration for `eas build` command with `--auto-submit-with-profile` flag. ([#2425](https://github.com/expo/eas-cli/pull/2425) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
- Correctly parse the EXPO_APPLE_PROVIER_ID environment variable. ([#2349](https://github.com/expo/eas-cli/pull/2349) by [@louix](https://github.com/louix))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Update lockfile to only include `@types/[email protected]`. ([#2412](https://github.com/expo/eas-cli/pull/2412) by [@byCedric](https://github.com/byCedric))
|
||||||
|
- Update test workflow Node versions to 18, 20, and 22. ([#2413](https://github.com/expo/eas-cli/pull/2413) by [@byCedric](https://github.com/byCedric))
|
||||||
|
|
||||||
|
## [9.2.0](https://github.com/expo/eas-cli/releases/tag/v9.2.0) - 2024-06-06
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Add `target-profile` and `source-profile` flags to the `eas build:resign` command. ([#2410](https://github.com/expo/eas-cli/pull/2410) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
- Display build profile in the output of `eas build:list`. ([#2408](https://github.com/expo/eas-cli/pull/2408) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Use the correct app config for no GitHub flow in `init:onboarding`. ([#2397](https://github.com/expo/eas-cli/pull/2397) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
- Disallow picking expired builds as submit archive source. ([#2406](https://github.com/expo/eas-cli/pull/2406) by [@sjchmiela](https://github.com/sjchmiela))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Print network error message if present. ([#2407](https://github.com/expo/eas-cli/pull/2407) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
- Make flags for `eas build:list` command more aligned with flags for rest of the commands. ([#2409](https://github.com/expo/eas-cli/pull/2409) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [9.1.0](https://github.com/expo/eas-cli/releases/tag/v9.1.0) - 2024-05-23
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Make `eas init:onboarding` command public. ([#2399](https://github.com/expo/eas-cli/pull/2399) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [9.0.10](https://github.com/expo/eas-cli/releases/tag/v9.0.10) - 2024-05-22
|
||||||
|
|
||||||
|
## [9.0.9](https://github.com/expo/eas-cli/releases/tag/v9.0.9) - 2024-05-22
|
||||||
|
|
||||||
|
## [9.0.8](https://github.com/expo/eas-cli/releases/tag/v9.0.8) - 2024-05-21
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2387](https://github.com/expo/eas-cli/pull/2387) by [@expo-bot](https://github.com/expo-bot))
|
||||||
|
- Improve displaying of device registration QR code. ([#2391](https://github.com/expo/eas-cli/pull/2391) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [9.0.7](https://github.com/expo/eas-cli/releases/tag/v9.0.7) - 2024-05-17
|
||||||
|
|
||||||
|
## [9.0.6](https://github.com/expo/eas-cli/releases/tag/v9.0.6) - 2024-05-15
|
||||||
|
|
||||||
|
## [9.0.5](https://github.com/expo/eas-cli/releases/tag/v9.0.5) - 2024-05-13
|
||||||
|
|
||||||
|
## [9.0.4](https://github.com/expo/eas-cli/releases/tag/v9.0.4) - 2024-05-13
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Add loader and progress information to the `diagnostics` command. ([#2378](https://github.com/expo/eas-cli/pull/2378) by [@simek](https://github.com/simek))
|
||||||
|
|
||||||
|
## [9.0.3](https://github.com/expo/eas-cli/releases/tag/v9.0.3) - 2024-05-10
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2372](https://github.com/expo/eas-cli/pull/2372) by [@expo-bot](https://github.com/expo-bot))
|
||||||
|
|
||||||
|
## [9.0.2](https://github.com/expo/eas-cli/releases/tag/v9.0.2) - 2024-05-09
|
||||||
|
|
||||||
|
## [9.0.1](https://github.com/expo/eas-cli/releases/tag/v9.0.1) - 2024-05-09
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Update image tags in `eas.schema.json`. ([#2363](https://github.com/expo/eas-cli/pull/2363) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [9.0.0](https://github.com/expo/eas-cli/releases/tag/v9.0.0) - 2024-05-08
|
||||||
|
|
||||||
|
### 🛠 Breaking changes
|
||||||
|
|
||||||
|
- Allow modification of provisioning profile in CI, add --freeze-credentials flag. ([#2347](https://github.com/expo/eas-cli/pull/2347) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Pass env from process.env and build profile to expo-updates CLI calls where applicable. ([#2359](https://github.com/expo/eas-cli/pull/2359) by [@wschurman](https://github.com/wschurman))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Remove more classic updates code. ([#2357](https://github.com/expo/eas-cli/pull/2357) by [@wschurman](https://github.com/wschurman))
|
||||||
|
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2360](https://github.com/expo/eas-cli/pull/2360) by [@expo-bot](https://github.com/expo-bot))
|
||||||
|
- Don't pass custom global `"expoCli"` version set in `eas.json` to EAS Build process and warn users when setting it. ([#2361](https://github.com/expo/eas-cli/pull/2361) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [8.0.0](https://github.com/expo/eas-cli/releases/tag/v8.0.0) - 2024-05-01
|
||||||
|
|
||||||
|
### 🛠 Breaking changes
|
||||||
|
|
||||||
|
- Stop creating a channel on `eas update` and `eas update:roll-back-to-embedded` unless the `--channel` flag is specified. ([#2346](https://github.com/expo/eas-cli/pull/2346) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Improve login info message for other login options. ([#2352](https://github.com/expo/eas-cli/pull/2352) by [@wschurman](https://github.com/wschurman))
|
||||||
|
- Show the -s, --sso option in the login command help. ([#2353](https://github.com/expo/eas-cli/pull/2353) by [@lzkb](https://github.com/lzkb))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2351](https://github.com/expo/eas-cli/pull/2351) by [@expo-bot](https://github.com/expo-bot))
|
||||||
|
- Don't prompt users to set push notifications by default if they don't have the `expo-notifications` installed. ([#2343](https://github.com/expo/eas-cli/pull/2343) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [7.8.5](https://github.com/expo/eas-cli/releases/tag/v7.8.5) - 2024-04-26
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Add explicit workflow arg to expo-update CLI calls. ([#2340](https://github.com/expo/eas-cli/pull/2340) by [@wschurman](https://github.com/wschurman))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Improve error message if the server returns `UNAUTHORIZED_ERROR`. ([#2345](https://github.com/expo/eas-cli/pull/2345) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
- Fill in min expo-updates version for expo-updates CLI. ([#2344](https://github.com/expo/eas-cli/pull/2344) by [@wschurman](https://github.com/wschurman))
|
||||||
|
|
||||||
|
## [7.8.4](https://github.com/expo/eas-cli/releases/tag/v7.8.4) - 2024-04-24
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Update the list of available Android images. ([#2337](https://github.com/expo/eas-cli/pull/2337) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||||
|
- Make multi-select for revoking distribution certificates more readable. ([#2342](https://github.com/expo/eas-cli/pull/2342) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
- Improve error message displayed when EAS CLI version doesn't satisfy minimal version required specified in eas.json. ([#2341](https://github.com/expo/eas-cli/pull/2341) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [7.8.3](https://github.com/expo/eas-cli/releases/tag/v7.8.3) - 2024-04-23
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Don't prompt to set `android.package` and `ios.bundleIdentifier` values when running in non-interactive mode. ([#2336](https://github.com/expo/eas-cli/pull/2336) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Amend credential removal wording. ([#2334](https://github.com/expo/eas-cli/pull/2334) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
## [7.8.2](https://github.com/expo/eas-cli/releases/tag/v7.8.2) - 2024-04-15
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Fix display of errors when expo-updates CLI command fails. ([#2324](https://github.com/expo/eas-cli/pull/2324) by [@wschurman](https://github.com/wschurman))
|
||||||
|
- Move credentials endpoints to paginated counterparts. ([#2327](https://github.com/expo/eas-cli/pull/2327) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Add progress bar for fetching paginated datasets. ([#2326](https://github.com/expo/eas-cli/pull/2326) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
## [7.8.1](https://github.com/expo/eas-cli/releases/tag/v7.8.1) - 2024-04-11
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Fix command source files URLs in autogenerated `README`. ([#2318](https://github.com/expo/eas-cli/pull/2318) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2316](https://github.com/expo/eas-cli/pull/2316) by [@expo-bot](https://github.com/expo-bot))
|
||||||
|
- Stop querying `Build.resourceClass` field. ([#2320](https://github.com/expo/eas-cli/pull/2320) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [7.8.0](https://github.com/expo/eas-cli/releases/tag/v7.8.0) - 2024-04-08
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Add `auto`, `sdk-50` and `sdk-49` image tags. ([#2298](https://github.com/expo/eas-cli/pull/2298) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
- Add `--build-logger-level` flag to `eas build` command. ([#2313](https://github.com/expo/eas-cli/pull/2313) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Deprecate the `default` image tag. ([#2298](https://github.com/expo/eas-cli/pull/2298) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
- Deprecate iOS images with Xcode version lower then 15. ([#2298](https://github.com/expo/eas-cli/pull/2298) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [7.7.0](https://github.com/expo/eas-cli/releases/tag/v7.7.0) - 2024-04-05
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Generate metadata file for project archive ([#2149](https://github.com/expo/eas-cli/pull/2149) by [@khamilowicz](https://github.com/khamilowicz))
|
||||||
|
- Add --verbose-fastlane flag to eas submit command for more robust fastlane pilot logs. ([#2276](https://github.com/expo/eas-cli/pull/2276) by [@khamilowicz](https://github.com/khamilowicz))
|
||||||
|
- Add `eas credentials:configure-build` subcommand. ([#2282](https://github.com/expo/eas-cli/pull/2282) by [@fiberjw](https://github.com/fiberjw))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Add info about the Xcode 15.3 image to `eas.schema.json`. ([#2312](https://github.com/expo/eas-cli/pull/2312) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [7.6.2](https://github.com/expo/eas-cli/releases/tag/v7.6.2) - 2024-03-27
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2301](https://github.com/expo/eas-cli/pull/2301) by [@expo-bot](https://github.com/expo-bot))
|
||||||
|
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2304](https://github.com/expo/eas-cli/pull/2304) by [@expo-bot](https://github.com/expo-bot))
|
||||||
|
|
||||||
|
## [7.6.1](https://github.com/expo/eas-cli/releases/tag/v7.6.1) - 2024-03-25
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2291](https://github.com/expo/eas-cli/pull/2291) by [@expo-bot](https://github.com/expo-bot))
|
||||||
|
- Fix asset limit punctuation. ([#2296](https://github.com/expo/eas-cli/pull/2296) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2293](https://github.com/expo/eas-cli/pull/2293) by [@expo-bot](https://github.com/expo-bot))
|
||||||
|
|
||||||
|
## [7.6.0](https://github.com/expo/eas-cli/releases/tag/v7.6.0) - 2024-03-18
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Print uncommitted files in non-interactive mode if they fail the execution. ([#2288](https://github.com/expo/eas-cli/pull/2288) by [@sjchmiela](https://github.com/sjchmiela))
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Use a custom build config path with POSIX separator when sending data to the EAS Build server. ([#2285](https://github.com/expo/eas-cli/pull/2285) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
- Improve resolving vcsClient as a part of the project context. ([#2295](https://github.com/expo/eas-cli/pull/2295) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2277](https://github.com/expo/eas-cli/pull/2277) by [@expo-bot](https://github.com/expo-bot))
|
||||||
|
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2283](https://github.com/expo/eas-cli/pull/2283) by [@expo-bot](https://github.com/expo-bot))
|
||||||
|
- Bump the `@expo/apple-utils` version to switch between the `developer.apple.com` and `developer-mdn.apple.com` domains when one of them doesn't work. ([#2290](https://github.com/expo/eas-cli/pull/2290) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [7.5.0](https://github.com/expo/eas-cli/releases/tag/v7.5.0) - 2024-03-11
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Add `--auto-submit` option to `eas build:internal` command. ([#2271](https://github.com/expo/eas-cli/pull/2271) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2274](https://github.com/expo/eas-cli/pull/2274) by [@expo-bot](https://github.com/expo-bot))
|
||||||
|
|
||||||
|
## [7.4.0](https://github.com/expo/eas-cli/releases/tag/v7.4.0) - 2024-03-10
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Use new expo-updates configuration:syncnative for versioned native sync. ([#2269](https://github.com/expo/eas-cli/pull/2269) by [@wschurman](https://github.com/wschurman))
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Fix expo-updates package version detection for canaries. ([#2243](https://github.com/expo/eas-cli/pull/2243) by [@wschurman](https://github.com/wschurman))
|
||||||
|
- Add missing `config` property to `eas.json` schema. ([#2248](https://github.com/expo/eas-cli/pull/2248) by [@sjchmiela](https://github.com/sjchmiela))
|
||||||
|
- Use expo-updates runtime version CLI to generate runtime versions. ([#2251](https://github.com/expo/eas-cli/pull/2251) by [@wschurman](https://github.com/wschurman))
|
||||||
|
- Update @expo/apple-utils to handle changes in API. ([73ba19de6662763cc6bff9fac6b7700ffbd0e88a](https://github.com/expo/eas-cli/commit/73ba19de6662763cc6bff9fac6b7700ffbd0e88a) by [@brentvatne](https://github.com/brentvatne))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2237](https://github.com/expo/eas-cli/pull/2237) by [@expo-bot](https://github.com/expo-bot))
|
||||||
|
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2240](https://github.com/expo/eas-cli/pull/2240) by [@expo-bot](https://github.com/expo-bot))
|
||||||
|
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2253](https://github.com/expo/eas-cli/pull/2253) by [@expo-bot](https://github.com/expo-bot))
|
||||||
|
- Include src/\*\*/build directories in vscode search and replace. ([#2250](https://github.com/expo/eas-cli/pull/2250) by [@wschurman](https://github.com/wschurman))
|
||||||
|
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2259](https://github.com/expo/eas-cli/pull/2259) by [@expo-bot](https://github.com/expo-bot))
|
||||||
|
|
||||||
|
## [7.3.0](https://github.com/expo/eas-cli/releases/tag/v7.3.0) - 2024-02-19
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Fix expo-updates fingerprinting during update. ([#2231](https://github.com/expo/eas-cli/pull/2231) by [@wschurman](https://github.com/wschurman))
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Don't require expo on fresh react-native project. ([#2235](https://github.com/expo/eas-cli/pull/2235) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2229](https://github.com/expo/eas-cli/pull/2229) by [@expo-bot](https://github.com/expo-bot))
|
||||||
|
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2230](https://github.com/expo/eas-cli/pull/2230) by [@expo-bot](https://github.com/expo-bot))
|
||||||
|
- Reword update configuration warning. ([#2234](https://github.com/expo/eas-cli/pull/2234) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
## [7.2.0](https://github.com/expo/eas-cli/releases/tag/v7.2.0) - 2024-02-11
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Support configuring a Google Service Account Key via eas credentials, for sending Android Notifications via FCM V1. ([#2197](https://github.com/expo/eas-cli/pull/2197) by [@christopherwalter](https://github.com/christopherwalter))
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Revert expose expo export dev flag as an option in eas update. ([#2214](https://github.com/expo/eas-cli/pull/2214) by [@wschurman](https://github.com/wschurman))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2223](https://github.com/expo/eas-cli/pull/2223) by [@expo-bot](https://github.com/expo-bot))
|
||||||
|
|
||||||
|
## [7.1.3](https://github.com/expo/eas-cli/releases/tag/v7.1.3) - 2024-02-07
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Remove duplicated log message when creating ASC API key. ([#2208](https://github.com/expo/eas-cli/pull/2208) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||||
|
- Add simulator flag to metadata. ([#2073](https://github.com/expo/eas-cli/pull/2073) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||||
|
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2220](https://github.com/expo/eas-cli/pull/2220) by [@expo-bot](https://github.com/expo-bot))
|
||||||
|
|
||||||
|
## [7.1.2](https://github.com/expo/eas-cli/releases/tag/v7.1.2) - 2024-01-30
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Add better validation for EAS Submit inputs. ([#2202](https://github.com/expo/eas-cli/pull/2202) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [7.1.1](https://github.com/expo/eas-cli/releases/tag/v7.1.1) - 2024-01-26
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Revert incorrect EAS Submit input validation changes. ([#2200](https://github.com/expo/eas-cli/pull/2200) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [7.1.0](https://github.com/expo/eas-cli/releases/tag/v7.1.0) - 2024-01-26
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Support requireCommit for EAS Update. ([#2196](https://github.com/expo/eas-cli/pull/2196) by [@wschurman](https://github.com/wschurman))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Remove support for classic updates release channel in 50+. ([#2189](https://github.com/expo/eas-cli/pull/2189) by [@wschurman](https://github.com/wschurman))
|
||||||
|
- Validate EAS Submit inputs better. ([#2198](https://github.com/expo/eas-cli/pull/2198) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [7.0.0](https://github.com/expo/eas-cli/releases/tag/v7.0.0) - 2024-01-19
|
||||||
|
|
||||||
|
### 🛠 Breaking changes
|
||||||
|
|
||||||
|
- Stop generating eas-update-metadata.json on publish. ([#2187](https://github.com/expo/eas-cli/pull/2187) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Use branch mapping utility fn. ([#2186](https://github.com/expo/eas-cli/pull/2186) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
## [6.1.0](https://github.com/expo/eas-cli/releases/tag/v6.1.0) - 2024-01-18
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Add `build:delete` command. ([#2178](https://github.com/expo/eas-cli/pull/2178) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||||
|
- Add filter flags for `platform` and `profile` to `build:cancel` and `build:delete` commands. ([#2178](https://github.com/expo/eas-cli/pull/2178) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Remove "bare"-specific **eas.json** template. ([#2179](https://github.com/expo/eas-cli/pull/2179) by [@sjchmiela](https://github.com/sjchmiela))
|
||||||
|
- Prompt users if they want to continue if EAS CLI fails to provision the devices. ([#2181](https://github.com/expo/eas-cli/pull/2181) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
- Update `eas-cli` and `@expo/eas-json` dependencies. ([#2176](https://github.com/expo/eas-cli/pull/2176) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
- Update `eas.schema.json` to after adding Xcode 15.2 image. ([#2184](https://github.com/expo/eas-cli/pull/2184) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
- Upgrade packages from the expo/expo repo. ([#2145](https://github.com/expo/eas-cli/pull/2145) by [@wschurman](https://github.com/wschurman))
|
||||||
|
|
||||||
|
## [6.0.0](https://github.com/expo/eas-cli/releases/tag/v6.0.0) - 2024-01-12
|
||||||
|
|
||||||
|
### 🛠 Breaking changes
|
||||||
|
|
||||||
|
- Drop support for Node 14. ([#2175](https://github.com/expo/eas-cli/pull/2175) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Allow undefined update message for EAS Update publishing when no VCS. ([#2148](https://github.com/expo/eas-cli/pull/2148) by [@wschurman](https://github.com/wschurman))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Upgrade `@expo/eas-build-job` to `1.0.56`, thus removing unused (since [#1524](https://github.com/expo/eas-cli/pull/1524)) support for S3 project archives. ([#2165](https://github.com/expo/eas-cli/pull/2165) by [@sjchmiela](https://github.com/sjchmiela))
|
||||||
|
|
||||||
|
## [5.9.3](https://github.com/expo/eas-cli/releases/tag/v5.9.3) - 2023-12-19
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Rename getUpdateGroupJsonInfo. ([#2157](https://github.com/expo/eas-cli/pull/2157) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Add new Xcode 15.1 image to `eas.schema.json`. ([#2155](https://github.com/expo/eas-cli/pull/2155) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [5.9.2](https://github.com/expo/eas-cli/releases/tag/v5.9.2) - 2023-12-15
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Throw error if custom build config is gitignored. ([#2123](https://github.com/expo/eas-cli/pull/2123) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
- Update `@expo/steps` library to `1.0.51`. ([#2130](https://github.com/expo/eas-cli/pull/2130) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
- Update `eas.schema.json` to include changes after some of our images were migrated from Ubuntu 18.04 to Ubuntu 20.04. ([#2137](https://github.com/expo/eas-cli/pull/2137) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
- Update oclif dependencies. ([#2008](https://github.com/expo/eas-cli/pull/2008) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||||
|
- Upgrade `eas-build-job` and unify how we're handling `buildMode`. ([#2138](https://github.com/expo/eas-cli/pull/2138) by [@sjchmiela](https://github.com/sjchmiela))
|
||||||
|
|
||||||
|
## [5.9.1](https://github.com/expo/eas-cli/releases/tag/v5.9.1) - 2023-11-20
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Don't ask a user to install the dev client if running in non-interactive mode. ([#2124](https://github.com/expo/eas-cli/pull/2124) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
- Always refresh existing provisioning profile before use. ([#2125](https://github.com/expo/eas-cli/pull/2125) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||||
|
|
||||||
|
## [5.9.0](https://github.com/expo/eas-cli/releases/tag/v5.9.0) - 2023-11-15
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Add `--profile` flag to `eas build:run` command. ([#2035](https://github.com/expo/eas-cli/pull/2035) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [5.8.0](https://github.com/expo/eas-cli/releases/tag/v5.8.0) - 2023-11-13
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Move channel:rollout out of developer preview. ([#2114](https://github.com/expo/eas-cli/pull/2114) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Fixed provisioning of new devices into an existing profile. ([#2119](https://github.com/expo/eas-cli/pull/2119) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Update `@expo/package-manager` to `1.1.2` to change package manager resolution order. ([#2118](https://github.com/expo/eas-cli/pull/2118) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [5.7.0](https://github.com/expo/eas-cli/releases/tag/v5.7.0) - 2023-11-08
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Add `EXPO_APPLE_TEAM_ID` and `EXPO_APPLE_PROVIDER_ID` support. ([#2091](https://github.com/expo/eas-cli/pull/2091) by [@EvanBacon](https://github.com/EvanBacon))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Add link to SDK upgrade page for SDK-gated command error. ([#2106](https://github.com/expo/eas-cli/pull/2106) by [@wschurman](https://github.com/wschurman))
|
||||||
|
- Add `selectedImage` and `customNodeVersion` information to build metadata. ([#2113](https://github.com/expo/eas-cli/pull/2113) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [5.6.0](https://github.com/expo/eas-cli/releases/tag/v5.6.0) - 2023-10-27
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Use corresponding submit profile when selecting build from EAS. ([#2101](https://github.com/expo/eas-cli/pull/2101) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Added `buildArtifactsUrl` to `eas-cli build:view --json` output. ([#2102](https://github.com/expo/eas-cli/pull/2102) by [@sjchmiela](https://github.com/sjchmiela))
|
||||||
|
|
||||||
|
## [5.5.0](https://github.com/expo/eas-cli/releases/tag/v5.5.0) - 2023-10-25
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Add account type to the items in the prompt to select project owner. ([#2083](https://github.com/expo/eas-cli/pull/2083) by [@alanjhughes](https://github.com/alanjhughes))
|
||||||
|
- Gate roll back to embedded to expo-updates >= 0.19.0. ([#2094](https://github.com/expo/eas-cli/pull/2094) by [@wschurman](https://github.com/wschurman))
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- EAS Update: Increase asset upload timeout to 90s and reset on upload retry for slow connections. ([#2085](https://github.com/expo/eas-cli/pull/2085) by [@wschurman](https://github.com/wschurman))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Add `requiredPackageManager` to metadata. ([#2067](https://github.com/expo/eas-cli/pull/2067) by [@kadikraman](https://github.com/kadikraman))
|
||||||
|
- Move `getVcsClient` into command context. ([#2086](https://github.com/expo/eas-cli/pull/2086) by [@Josh-McFarlin](https://github.com/Josh-McFarlin))
|
||||||
|
- Display Apple device creation date when listing devices. ([#2092](https://github.com/expo/eas-cli/pull/2092) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||||
|
- Clean up Intel resource classes code after their deletion. ([#2093](https://github.com/expo/eas-cli/pull/2093) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
- Update images descriptions in `eas.schema.json` and add info about the new JDK 17 image. ([#2099](https://github.com/expo/eas-cli/pull/2099) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [5.4.0](https://github.com/expo/eas-cli/releases/tag/v5.4.0) - 2023-09-28
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Add support for the Tap to Pay on iPhone iOS entitlement. ([#2069](https://github.com/expo/eas-cli/pull/2069) by [@fobos531](https://github.com/fobos531))
|
||||||
|
|
||||||
|
## [5.3.1](https://github.com/expo/eas-cli/releases/tag/v5.3.1) - 2023-09-28
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Update EAS Build images description in our VSCode plugin. Add new `macos-ventura-13.6-xcode-15.0` image. ([#2068](https://github.com/expo/eas-cli/pull/2068) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [5.3.0](https://github.com/expo/eas-cli/releases/tag/v5.3.0) - 2023-09-25
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Update: expose expo cli `--dev` flag as an argument. ([#2050](https://github.com/expo/eas-cli/pull/2050) by [@nderscore](https://github.com/nderscore))
|
||||||
|
- Support `bun` option in eas.json. ([#2055](https://github.com/expo/eas-cli/pull/2055) by [@kadikraman](https://github.com/kadikraman))
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Support node aliases in .nvmrc. ([#2052](https://github.com/expo/eas-cli/pull/2052) by [@khamilowicz](https://github.com/khamilowicz))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Rollouts: more robust printing function. ([#2047](https://github.com/expo/eas-cli/pull/2047) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
## [5.2.0](https://github.com/expo/eas-cli/releases/tag/v5.2.0) - 2023-09-05
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Rollouts: json output for ci. ([#2037](https://github.com/expo/eas-cli/pull/2037) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Update: only print channel-branch pairing if we created a channel. ([#2036](https://github.com/expo/eas-cli/pull/2036) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Added rollout tests. ([#2042](https://github.com/expo/eas-cli/pull/2042) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Remove unreachable codesigning option. ([#2041](https://github.com/expo/eas-cli/pull/2041) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Fix generated graphql tsc errors. ([#2039](https://github.com/expo/eas-cli/pull/2039) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Rollouts: view action for CI. ([#2040](https://github.com/expo/eas-cli/pull/2040) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
## [5.1.0](https://github.com/expo/eas-cli/releases/tag/v5.1.0) - 2023-09-01
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Support `pnpm` option in eas.json. ([#1988](https://github.com/expo/eas-cli/pull/1988) by [@khamilowicz](https://github.com/khamilowicz))
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Make app config error not repeat indefinitely. ([#2020](https://github.com/expo/eas-cli/pull/2020) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||||
|
|
||||||
|
## [5.0.2](https://github.com/expo/eas-cli/releases/tag/v5.0.2) - 2023-08-29
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Add proper expo cli `--platform` flag handling when exporting updates. ([#1939](https://github.com/expo/eas-cli/pull/1939) by [@byCedric](https://github.com/byCedric))
|
||||||
|
|
||||||
|
## [5.0.1](https://github.com/expo/eas-cli/releases/tag/v5.0.1) - 2023-08-28
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Pass `platform` argument to expo-cli correctly when using the `eas update` command. ([#2028](https://github.com/expo/eas-cli/pull/2028) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [5.0.0](https://github.com/expo/eas-cli/releases/tag/v5.0.0) - 2023-08-28
|
||||||
|
|
||||||
|
### 🛠 Breaking changes
|
||||||
|
|
||||||
|
- Only export at most ios and android dist for EAS updates. ([#2002](https://github.com/expo/eas-cli/pull/2002) by [@wschurman](https://github.com/wschurman))
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Add rollback disambiguation command. ([#2004](https://github.com/expo/eas-cli/pull/2004) by [@wschurman](https://github.com/wschurman))
|
||||||
|
- Detect devices that fail to be provisioned, list them to the user and show the explanation message with the link to the devices page to check actual status. ([#2011](https://github.com/expo/eas-cli/pull/2011) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||||
|
- Add info to EAS Update asset upload process about asset counts and limits. ([#2013](https://github.com/expo/eas-cli/pull/2013) by [@wschurman](https://github.com/wschurman))
|
||||||
|
- .nvmrc support for setting node version. ([#1954](https://github.com/expo/eas-cli/pull/1954) by [@khamilowicz](https://github.com/khamilowicz))
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Support republishing roll back to embedded updates. ([#2006](https://github.com/expo/eas-cli/pull/2006) by [@wschurman](https://github.com/wschurman))
|
||||||
|
- Configure updates as well when somebody tries to run a build with channel set. ([#2016](https://github.com/expo/eas-cli/pull/2016) by [@wschurman](https://github.com/wschurman))
|
||||||
|
- Fix printing bug: branch with no update. ([#2023](https://github.com/expo/eas-cli/pull/2023) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- More branch map utility functions. ([#2001](https://github.com/expo/eas-cli/pull/2001) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- More branch mapping utils. ([#2003](https://github.com/expo/eas-cli/pull/2003) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Create a linked channel on build if not exists. ([#2017](https://github.com/expo/eas-cli/pull/2017) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Add `developmentClient` to metadata. ([#2015](https://github.com/expo/eas-cli/pull/2015) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [4.1.2](https://github.com/expo/eas-cli/releases/tag/v4.1.2) - 2023-08-10
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Make branch mapping utility files have no dependencies. ([#1993](https://github.com/expo/eas-cli/pull/1993) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
## [4.1.1](https://github.com/expo/eas-cli/releases/tag/v4.1.1) - 2023-08-08
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Logger to say website support is coming soon for rollouts. ([#1997](https://github.com/expo/eas-cli/pull/1997) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
## [4.1.0](https://github.com/expo/eas-cli/releases/tag/v4.1.0) - 2023-08-08
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Pass credentials to custom iOS builds. ([#1989](https://github.com/expo/eas-cli/pull/1989) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
- Add the `withoutCredentials` option as a common build profile field in `eas.json`. ([#1994](https://github.com/expo/eas-cli/pull/1994) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [4.0.1](https://github.com/expo/eas-cli/releases/tag/v4.0.1) - 2023-08-08
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Fix `eas channel:*` to work with empty branch mappings. ([#1992](https://github.com/expo/eas-cli/pull/1992) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
## [4.0.0](https://github.com/expo/eas-cli/releases/tag/v4.0.0) - 2023-08-07
|
||||||
|
|
||||||
|
### 🛠 Breaking changes
|
||||||
|
|
||||||
|
- Release redesigned `eas channel:rollout` into developer preview. The set of flag arguments are different, in addition to the workflow. ([#1986](https://github.com/expo/eas-cli/pull/1986) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Option to add current Apple Silicon device without the need to manually provide the provisioning UDID. ([#1943](https://github.com/expo/eas-cli/pull/1943) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Fix rollout-preview ending with republish with code signing. ([#1978](https://github.com/expo/eas-cli/pull/1978) by [@wschurman](https://github.com/wschurman))
|
||||||
|
- Rollouts: fix fp precision. ([#1985](https://github.com/expo/eas-cli/pull/1985) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Fixing more grammar errors. ([#1980](https://github.com/expo/eas-cli/pull/1980) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Handle rollout edit edge case. ([#1981](https://github.com/expo/eas-cli/pull/1981) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Linked expo.fyi rollout article. ([#1991](https://github.com/expo/eas-cli/pull/1991) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Rollout ux improvements. ([#1984](https://github.com/expo/eas-cli/pull/1984) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
## [3.18.3](https://github.com/expo/eas-cli/releases/tag/v3.18.3) - 2023-08-03
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Fix republishing with code signing. ([#1973](https://github.com/expo/eas-cli/pull/1973) by [@wschurman](https://github.com/wschurman))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Add relationships flag to rollouts-preview. ([#1972](https://github.com/expo/eas-cli/pull/1972) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Get channel:{view,list,edit} to play nice with rollouts. ([#1974](https://github.com/expo/eas-cli/pull/1974) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Use 'roll out' instead of rollout for verbs. ([#1979](https://github.com/expo/eas-cli/pull/1979) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
## [3.18.2](https://github.com/expo/eas-cli/releases/tag/v3.18.2) - 2023-08-03
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Revert adding `.nvmrc` support for setting node version. ([#1976](https://github.com/expo/eas-cli/pull/1976) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Use just a comma instead of `, ` when concatenating `keywords` in `eas metadata`. ([#1967](https://github.com/expo/eas-cli/pull/1967) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [3.18.1](https://github.com/expo/eas-cli/releases/tag/v3.18.1) - 2023-08-03
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Pass correct group into `update:republish`. ([#1971](https://github.com/expo/eas-cli/pull/1971) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Make new rollouts version available for internal dogfooding. ([#1966](https://github.com/expo/eas-cli/pull/1966) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Change default runtime version policy for EAS Update to appVersion. ([#1968](https://github.com/expo/eas-cli/pull/1968) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
## [3.18.0](https://github.com/expo/eas-cli/releases/tag/v3.18.0) - 2023-08-02
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- .nvmrc support for setting node version. ([#1954](https://github.com/expo/eas-cli/pull/1954) by [@khamilowicz](https://github.com/khamilowicz))
|
||||||
|
- Provide credentials for custom Android builds. ([#1969](https://github.com/expo/eas-cli/pull/1969) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- More branch mapping utility. ([#1957](https://github.com/expo/eas-cli/pull/1957) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Utility classes to select existing rollouts and channels. ([#1958](https://github.com/expo/eas-cli/pull/1958) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
## [3.17.1](https://github.com/expo/eas-cli/releases/tag/v3.17.1) - 2023-07-27
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Unify channel graphql query types. ([#1949](https://github.com/expo/eas-cli/pull/1949) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Revert UpdateBranchWithCurrentGroupFragment. ([#1952](https://github.com/expo/eas-cli/pull/1952) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Fetch entire relay compliant dataset. ([#1953](https://github.com/expo/eas-cli/pull/1953) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
## [3.17.0](https://github.com/expo/eas-cli/releases/tag/v3.17.0) - 2023-07-24
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Add `rollout` option for configuring Android submissions. ([#1938](https://github.com/expo/eas-cli/pull/1938) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Added branch mapping utility functions. ([#1944](https://github.com/expo/eas-cli/pull/1944) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Amend branch mapping utility functions. ([#1945](https://github.com/expo/eas-cli/pull/1945) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Handle error thrown when `intel-medium` resource class is not available as server-side defined error. ([#1947](https://github.com/expo/eas-cli/pull/1947) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
- Remove `intel-medium` from `eas.schema.json`, so it's not suggested as a valid value by our VSCode plugin. ([#1947](https://github.com/expo/eas-cli/pull/1947) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [3.16.0](https://github.com/expo/eas-cli/releases/tag/v3.16.0) - 2023-07-18
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Add styling to SSO auth redirect completion page. ([#1929](https://github.com/expo/eas-cli/pull/1929) by [@wschurman](https://github.com/wschurman))
|
||||||
|
- Ignore entitlements from native template when `/ios` is gitignored. ([#1906](https://github.com/expo/eas-cli/pull/1906) by [@byCedric](https://github.com/byCedric))
|
||||||
|
- Use node server default port selection for SSO login server. ([#1930](https://github.com/expo/eas-cli/pull/1930) by [@wschurman](https://github.com/wschurman))
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Fix incorrect handling of valid `inProgress` Android submission release status. ([#1934](https://github.com/expo/eas-cli/pull/1934) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Print friendly error msg in case account doesn't have required permission. ([#1867](https://github.com/expo/eas-cli/pull/1867) by [@firasrg](https://github.com/firasrg))
|
||||||
|
- Limit project file upload size to 2GB. ([#1928](https://github.com/expo/eas-cli/pull/1928) by [@khamilowicz](https://github.com/khamilowicz))
|
||||||
|
- Bump urql graphql client major version. ([#1936](https://github.com/expo/eas-cli/pull/1936) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
## [3.15.1](https://github.com/expo/eas-cli/releases/tag/v3.15.1) - 2023-07-11
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Ensure useClassicUpdates is not set when using EAS Update commands. ([#1915](https://github.com/expo/eas-cli/pull/1915) by [@ide](https://github.com/ide))
|
||||||
|
- Handle multiple GraphQLErrors when receiving a CombinedError. ([#1924](https://github.com/expo/eas-cli/pull/1924) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Bump Expo package dependencies. ([#1911](https://github.com/expo/eas-cli/pull/1911) by [@brentvatne](https://github.com/brentvatne))
|
||||||
|
- Better bare workflow runtimeVersion error. ([#1910](https://github.com/expo/eas-cli/pull/1910) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Fix runtime version print logs. ([#1925](https://github.com/expo/eas-cli/pull/1925) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Add info about new `macos-ventura-13.4-xcode-14.3.1` image to `eas.schema.json`. ([#1920](https://github.com/expo/eas-cli/pull/1920) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [3.15.0](https://github.com/expo/eas-cli/releases/tag/v3.15.0) - 2023-06-30
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Show build profile when selecting a build for eas build:run. ([#1901](https://github.com/expo/eas-cli/pull/1901) by [@keith-kurak](https://github.com/keith-kurak))
|
||||||
|
- Adds -m alias to --message in update/republish and removed README comment. ([#1905](https://github.com/expo/eas-cli/pull/1905) by [@pusongqi](https://github.com/pusongqi))
|
||||||
|
|
||||||
### 🧹 Chores
|
### 🧹 Chores
|
||||||
|
|
||||||
- Better runtimeVersion output. ([#1894](https://github.com/expo/eas-cli/pull/1894) by [@quinlanj](https://github.com/quinlanj))
|
- Better runtimeVersion output. ([#1894](https://github.com/expo/eas-cli/pull/1894) by [@quinlanj](https://github.com/quinlanj))
|
||||||
- Print better error message when uploading project archive tarball fails. ([#1897](https://github.com/expo/eas-cli/pull/1897) by [@szdziedzic](https://github.com/szdziedzic))
|
- Print better error message when uploading project archive tarball fails. ([#1897](https://github.com/expo/eas-cli/pull/1897) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
- Set useClassicUpdates to false on `eas update` and `eas update:configure`. ([#1914](https://github.com/expo/eas-cli/pull/1914) by [@quinlanj](https://github.com/quinlanj))
|
- Adds support for 2 new server-side errors related to build limits. ([#1921](https://github.com/expo/eas-cli/pull/1921) by [@sundeeppeswani](https://github.com/sundeeppeswani))
|
||||||
|
|
||||||
## [3.14.0](https://github.com/expo/eas-cli/releases/tag/v3.14.0) - 2023-06-20
|
## [3.14.0](https://github.com/expo/eas-cli/releases/tag/v3.14.0) - 2023-06-20
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -1,10 +1,10 @@
|
|||||||
* @szdziedzic @khamilowicz
|
* @szdziedzic @khamilowicz @sjchmiela
|
||||||
|
|
||||||
packages/eas-cli/src/build @szdziedzic @khamilowicz
|
packages/eas-cli/src/build @szdziedzic @khamilowicz @sjchmiela
|
||||||
packages/eas-cli/src/commands/build @szdziedzic @khamilowicz
|
packages/eas-cli/src/commands/build @szdziedzic @khamilowicz @sjchmiela
|
||||||
|
|
||||||
packages/eas-cli/src/submit @szdziedzic @khamilowicz
|
packages/eas-cli/src/submit @szdziedzic @khamilowicz @sjchmiela
|
||||||
packages/eas-cli/src/commands/submit.ts @szdziedzic @khamilowicz
|
packages/eas-cli/src/commands/submit.ts @szdziedzic @khamilowicz @sjchmiela
|
||||||
|
|
||||||
packages/eas-cli/src/update @EvanBacon @byCedric @wschurman
|
packages/eas-cli/src/update @EvanBacon @byCedric @wschurman
|
||||||
packages/eas-cli/src/commands/update @EvanBacon @byCedric @wschurman
|
packages/eas-cli/src/commands/update @EvanBacon @byCedric @wschurman
|
||||||
@@ -13,4 +13,4 @@ packages/eas-cli/schema @bycedric
|
|||||||
packages/eas-cli/src/metadata @bycedric
|
packages/eas-cli/src/metadata @bycedric
|
||||||
packages/eas-cli/src/commands/metadata @bycedric
|
packages/eas-cli/src/commands/metadata @bycedric
|
||||||
|
|
||||||
packages/eas-json @szdziedzic @khamilowicz
|
packages/eas-json @szdziedzic @khamilowicz @sjchmiela
|
||||||
@@ -18,6 +18,25 @@ If you're making changes to `packages/eas-json` or prefer/need to work with prod
|
|||||||
yarn start
|
yarn start
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If it is easier for you to work with the code locally with compiler options set to:
|
||||||
|
|
||||||
|
```json
|
||||||
|
...
|
||||||
|
"compilerOptions": {
|
||||||
|
...
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
...
|
||||||
|
}
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
you can consider using:
|
||||||
|
|
||||||
|
```
|
||||||
|
yarn start-allow-unused
|
||||||
|
```
|
||||||
|
|
||||||
## Format
|
## Format
|
||||||
|
|
||||||
- Be sure to update the [`CHANGELOG.md`](./CHANGELOG.md) with changes for every PR. There is a changelog bot that can generate the proper entry for you. The instructions on how to use it are in the PR description placeholder.
|
- Be sure to update the [`CHANGELOG.md`](./CHANGELOG.md) with changes for every PR. There is a changelog bot that can generate the proper entry for you. The instructions on how to use it are in the PR description placeholder.
|
||||||
|
|||||||
@@ -12,7 +12,3 @@ The algorithm works as follows:
|
|||||||
- If there are any entries in the "🛠 Breaking changes" section, bump the MAJOR version.
|
- If there are any entries in the "🛠 Breaking changes" section, bump the MAJOR version.
|
||||||
- Otherwise, if there are any entries in the "🎉 New features", bump the MINOR version.
|
- Otherwise, if there are any entries in the "🎉 New features", bump the MINOR version.
|
||||||
- Otherwise, bump the PATCH version.
|
- Otherwise, bump the PATCH version.
|
||||||
|
|
||||||
## Updating EAS CLI version on EAS Build
|
|
||||||
|
|
||||||
The release process promotes the new version to staging. After verifying it works there, you can promote it to production with `npm dist-tag add eas-cli@{version} latest-eas-build`.
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
Instead of opening a GitHub issue for security vulnerabilities, send an email to our secure inbox: secure@expo.io.
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
projects: [
|
|
||||||
require('./packages/eas-cli/jest.config.js'),
|
|
||||||
require('./packages/eas-json/jest.config.js'),
|
|
||||||
],
|
|
||||||
testPathIgnorePatterns: ['.*'],
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import type { Config } from 'jest';
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-relative-packages
|
||||||
|
import easCliConfig from './packages/eas-cli/jest.config';
|
||||||
|
// eslint-disable-next-line import/no-relative-packages
|
||||||
|
import easJsonConfig from './packages/eas-json/jest.config';
|
||||||
|
|
||||||
|
const config: Config = {
|
||||||
|
projects: [easCliConfig, easJsonConfig],
|
||||||
|
testPathIgnorePatterns: ['.*'],
|
||||||
|
watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'],
|
||||||
|
collectCoverageFrom: ['<rootDir>/src/**/*.{ts,tsx,js,jsx}'],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
preset: 'ts-jest',
|
|
||||||
testEnvironment: 'node',
|
|
||||||
testRegex: '/__tests__/.*(test|spec)\\.[jt]sx?$',
|
|
||||||
watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'],
|
|
||||||
collectCoverageFrom: ['<rootDir>/src/**/*.{ts,tsx,js,jsx}'],
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import type { Config } from 'jest';
|
||||||
|
|
||||||
|
const config: Config = {
|
||||||
|
preset: 'ts-jest',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
testRegex: '/__tests__/.*(test|spec)\\.[jt]sx?$',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
+1
-2
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"useWorkspaces": true,
|
"version": "10.0.2",
|
||||||
"version": "3.14.0",
|
|
||||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
|
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
|
||||||
}
|
}
|
||||||
+15
-14
@@ -10,6 +10,7 @@
|
|||||||
"build-for-size-check": "yarn build && yarn workspace eas-cli pretarball-ci && CLI_SIZE_CHECK=1 yarn workspace eas-cli oclif pack:tarballs --no-xz --targets linux-x64",
|
"build-for-size-check": "yarn build && yarn workspace eas-cli pretarball-ci && CLI_SIZE_CHECK=1 yarn workspace eas-cli oclif pack:tarballs --no-xz --targets linux-x64",
|
||||||
"typecheck": "lerna run typecheck",
|
"typecheck": "lerna run typecheck",
|
||||||
"start": "lerna run watch --stream --parallel",
|
"start": "lerna run watch --stream --parallel",
|
||||||
|
"start-allow-unused": "lerna run watch-allow-unused --stream --parallel",
|
||||||
"watch": "yarn start",
|
"watch": "yarn start",
|
||||||
"eas": "packages/eas-cli/bin/run",
|
"eas": "packages/eas-cli/bin/run",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
@@ -19,22 +20,22 @@
|
|||||||
"clean": "lerna run clean && rm -rf node_modules coverage"
|
"clean": "lerna run clean && rm -rf node_modules coverage"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "29.2.5",
|
"@types/jest": "29.5.11",
|
||||||
"@types/node": "18.11.18",
|
"@types/node": "20.11.0",
|
||||||
"eslint": "8.31.0",
|
"eslint": "8.56.0",
|
||||||
"eslint-config-universe": "11.1.1",
|
"eslint-config-universe": "12.0.0",
|
||||||
"eslint-plugin-async-protect": "3.0.0",
|
"eslint-plugin-async-protect": "3.1.0",
|
||||||
"eslint-plugin-node": "11.1.0",
|
"eslint-plugin-node": "11.1.0",
|
||||||
"jest": "29.3.1",
|
"jest": "29.7.0",
|
||||||
"jest-watch-typeahead": "2.2.1",
|
"jest-watch-typeahead": "2.2.2",
|
||||||
"lerna": "6.4.1",
|
"lerna": "7.4.2",
|
||||||
"oclif": "3.4.3",
|
"oclif": "^3.9.0",
|
||||||
"prettier": "2.8.2",
|
"prettier": "3.1.1",
|
||||||
"ts-jest": "29.0.3",
|
"ts-jest": "29.1.1",
|
||||||
"typescript": "4.9.4"
|
"typescript": "5.3.3"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "18.6.0",
|
"node": "20.11.0",
|
||||||
"yarn": "1.22.19"
|
"yarn": "1.22.21"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+362
-172
File diff suppressed because it is too large.
Load diff
Generated
+8792
-1433
File diff suppressed because it is too large.
Load diff
@@ -1,7 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
...require('../../jest/jest.config.js'),
|
|
||||||
rootDir: __dirname,
|
|
||||||
roots: ['src', '__mocks__'],
|
|
||||||
globalSetup: './jest/global-setup.ts',
|
|
||||||
setupFilesAfterEnv: ['./jest/setup-after-env.ts'],
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import type { Config } from 'jest';
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-relative-packages
|
||||||
|
import baseConfig from '../../jest/jest.config';
|
||||||
|
|
||||||
|
const config: Config = {
|
||||||
|
...baseConfig,
|
||||||
|
rootDir: __dirname,
|
||||||
|
roots: ['src', '__mocks__'],
|
||||||
|
globalSetup: './jest/global-setup.ts',
|
||||||
|
setupFilesAfterEnv: ['./jest/setup-after-env.ts'],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
@@ -1,64 +1,65 @@
|
|||||||
{
|
{
|
||||||
"name": "eas-cli",
|
"name": "eas-cli",
|
||||||
"description": "EAS command line tool",
|
"description": "EAS command line tool",
|
||||||
"version": "3.14.0",
|
"version": "10.0.2",
|
||||||
"author": "Expo <[email protected]>",
|
"author": "Expo <[email protected]>",
|
||||||
"bin": {
|
"bin": {
|
||||||
"eas": "./bin/run"
|
"eas": "./bin/run"
|
||||||
},
|
},
|
||||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@expo/apple-utils": "1.3.1",
|
"@expo/apple-utils": "1.7.0",
|
||||||
"@expo/code-signing-certificates": "0.0.5",
|
"@expo/code-signing-certificates": "0.0.5",
|
||||||
"@expo/config": "8.1.2",
|
"@expo/config": "8.5.4",
|
||||||
"@expo/config-plugins": "7.2.3",
|
"@expo/config-plugins": "7.8.4",
|
||||||
"@expo/config-types": "49.0.0",
|
"@expo/config-types": "50.0.0",
|
||||||
"@expo/eas-build-job": "1.0.13",
|
"@expo/eas-build-job": "1.0.117",
|
||||||
"@expo/eas-json": "3.13.0",
|
"@expo/eas-json": "10.0.0",
|
||||||
"@expo/json-file": "8.2.37",
|
"@expo/json-file": "8.2.37",
|
||||||
|
"@expo/logger": "1.0.117",
|
||||||
"@expo/multipart-body-parser": "1.1.0",
|
"@expo/multipart-body-parser": "1.1.0",
|
||||||
"@expo/osascript": "2.0.33",
|
"@expo/osascript": "2.0.33",
|
||||||
"@expo/package-manager": "0.0.57",
|
"@expo/package-manager": "1.1.2",
|
||||||
"@expo/pkcs12": "0.0.8",
|
"@expo/pkcs12": "0.0.8",
|
||||||
"@expo/plist": "0.0.20",
|
"@expo/plist": "0.0.20",
|
||||||
"@expo/plugin-help": "5.1.22",
|
"@expo/plugin-help": "5.1.23",
|
||||||
"@expo/plugin-warn-if-update-available": "2.5.0",
|
"@expo/plugin-warn-if-update-available": "2.5.1",
|
||||||
"@expo/prebuild-config": "5.0.7",
|
"@expo/prebuild-config": "6.7.3",
|
||||||
"@expo/results": "1.0.0",
|
"@expo/results": "1.0.0",
|
||||||
"@expo/rudder-sdk-node": "1.1.1",
|
"@expo/rudder-sdk-node": "1.1.1",
|
||||||
"@expo/spawn-async": "1.7.0",
|
"@expo/spawn-async": "1.7.0",
|
||||||
"@expo/steps": "1.0.23",
|
"@expo/steps": "1.0.118",
|
||||||
"@expo/timeago.js": "1.0.0",
|
"@expo/timeago.js": "1.0.0",
|
||||||
"@oclif/core": "1.23.2",
|
"@oclif/core": "^1.26.2",
|
||||||
"@oclif/plugin-autocomplete": "1.3.10",
|
"@oclif/plugin-autocomplete": "^2.3.10",
|
||||||
"@segment/ajv-human-errors": "^2.1.2",
|
"@segment/ajv-human-errors": "^2.1.2",
|
||||||
"@urql/core": "3.1.1",
|
"@urql/core": "4.0.11",
|
||||||
"@urql/exchange-retry": "1.0.0",
|
"@urql/exchange-retry": "1.2.0",
|
||||||
"ajv": "8.11.0",
|
"ajv": "8.11.0",
|
||||||
"ajv-formats": "2.1.1",
|
"ajv-formats": "2.1.1",
|
||||||
"better-opn": "3.0.2",
|
"better-opn": "3.0.2",
|
||||||
"chalk": "4.1.2",
|
"chalk": "4.1.2",
|
||||||
"cli-progress": "3.11.2",
|
"cli-progress": "3.12.0",
|
||||||
"dateformat": "4.6.3",
|
"dateformat": "4.6.3",
|
||||||
"dotenv": "16.0.3",
|
"dotenv": "16.3.1",
|
||||||
"env-paths": "2.2.0",
|
"env-paths": "2.2.0",
|
||||||
"envinfo": "7.8.1",
|
"envinfo": "7.11.0",
|
||||||
"fast-deep-equal": "3.1.3",
|
"fast-deep-equal": "3.1.3",
|
||||||
"fast-glob": "3.2.12",
|
"fast-glob": "3.3.2",
|
||||||
"figures": "3.2.0",
|
"figures": "3.2.0",
|
||||||
"form-data": "4.0.0",
|
"form-data": "4.0.0",
|
||||||
"freeport-async": "2.0.0",
|
"fs-extra": "11.2.0",
|
||||||
"fs-extra": "10.1.0",
|
|
||||||
"getenv": "1.0.0",
|
"getenv": "1.0.0",
|
||||||
"gradle-to-js": "2.0.1",
|
"gradle-to-js": "2.0.1",
|
||||||
"graphql": "16.6.0",
|
"graphql": "16.8.1",
|
||||||
"graphql-tag": "2.12.6",
|
"graphql-tag": "2.12.6",
|
||||||
"https-proxy-agent": "5.0.1",
|
"https-proxy-agent": "5.0.1",
|
||||||
"ignore": "5.2.4",
|
"ignore": "5.3.0",
|
||||||
"indent-string": "4.0.0",
|
"indent-string": "4.0.0",
|
||||||
"jks-js": "1.1.0",
|
"jks-js": "1.1.0",
|
||||||
"joi": "17.7.0",
|
"joi": "17.11.0",
|
||||||
"keychain": "1.3.0",
|
"jsonwebtoken": "9.0.0",
|
||||||
|
"keychain": "1.5.0",
|
||||||
"log-symbols": "4.1.0",
|
"log-symbols": "4.1.0",
|
||||||
"mime": "3.0.0",
|
"mime": "3.0.0",
|
||||||
"minimatch": "5.1.2",
|
"minimatch": "5.1.2",
|
||||||
@@ -68,58 +69,59 @@
|
|||||||
"nullthrows": "1.1.1",
|
"nullthrows": "1.1.1",
|
||||||
"ora": "5.1.0",
|
"ora": "5.1.0",
|
||||||
"pkg-dir": "4.2.0",
|
"pkg-dir": "4.2.0",
|
||||||
"pngjs": "6.0.0",
|
"pngjs": "7.0.0",
|
||||||
"promise-limit": "2.7.0",
|
"promise-limit": "2.7.0",
|
||||||
"promise-retry": "2.0.1",
|
"promise-retry": "2.0.1",
|
||||||
"prompts": "2.4.2",
|
"prompts": "2.4.2",
|
||||||
"qrcode-terminal": "0.12.0",
|
"qrcode-terminal": "0.12.0",
|
||||||
"resolve-from": "5.0.0",
|
"resolve-from": "5.0.0",
|
||||||
"semver": "7.3.8",
|
"semver": "7.5.4",
|
||||||
"slash": "3.0.0",
|
"slash": "3.0.0",
|
||||||
"tar": "6.1.13",
|
"tar": "6.2.1",
|
||||||
"terminal-link": "2.1.1",
|
"terminal-link": "2.1.1",
|
||||||
"tslib": "2.4.1",
|
"tslib": "2.6.2",
|
||||||
"turndown": "7.1.1",
|
"turndown": "7.1.2",
|
||||||
"untildify": "4.0.0",
|
"untildify": "4.0.0",
|
||||||
"uuid": "9.0.0",
|
"uuid": "9.0.1",
|
||||||
"wrap-ansi": "7.0.0"
|
"wrap-ansi": "7.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@graphql-codegen/cli": "2.16.3",
|
"@graphql-codegen/cli": "5.0.0",
|
||||||
"@graphql-codegen/introspection": "2.2.3",
|
"@graphql-codegen/introspection": "4.0.0",
|
||||||
"@graphql-codegen/typescript": "2.8.7",
|
"@graphql-codegen/typescript": "4.0.1",
|
||||||
"@graphql-codegen/typescript-operations": "2.5.12",
|
"@graphql-codegen/typescript-operations": "4.0.1",
|
||||||
"@types/cli-progress": "3.11.0",
|
"@types/cli-progress": "3.11.5",
|
||||||
"@types/dateformat": "3.0.1",
|
"@types/dateformat": "3.0.1",
|
||||||
"@types/envinfo": "7.8.1",
|
"@types/envinfo": "7.8.3",
|
||||||
"@types/express": "4.17.15",
|
"@types/express": "4.17.15",
|
||||||
"@types/fs-extra": "9.0.13",
|
"@types/fs-extra": "11.0.4",
|
||||||
"@types/getenv": "^1.0.0",
|
"@types/getenv": "^1.0.0",
|
||||||
|
"@types/jsonwebtoken": "8.5.1",
|
||||||
"@types/mime": "3.0.1",
|
"@types/mime": "3.0.1",
|
||||||
"@types/node-fetch": "2.6.2",
|
"@types/node-fetch": "2.6.2",
|
||||||
"@types/node-forge": "1.3.1",
|
"@types/node-forge": "1.3.1",
|
||||||
"@types/pngjs": "6.0.1",
|
"@types/pngjs": "6.0.4",
|
||||||
"@types/promise-retry": "1.1.3",
|
"@types/promise-retry": "1.1.3",
|
||||||
"@types/prompts": "2.4.2",
|
"@types/prompts": "2.4.2",
|
||||||
"@types/semver": "7.3.13",
|
"@types/semver": "7.5.6",
|
||||||
"@types/tar": "6.1.3",
|
"@types/tar": "6.1.10",
|
||||||
"@types/tough-cookie": "4.0.2",
|
"@types/tough-cookie": "4.0.2",
|
||||||
"@types/uuid": "9.0.0",
|
"@types/uuid": "9.0.7",
|
||||||
"@types/wrap-ansi": "3.0.0",
|
"@types/wrap-ansi": "3.0.0",
|
||||||
"axios": "0.27.2",
|
"axios": "0.27.2",
|
||||||
"eslint-plugin-graphql": "4.0.0",
|
"eslint-plugin-graphql": "4.0.0",
|
||||||
"express": "4.18.2",
|
"express": "4.19.2",
|
||||||
"memfs": "3.4.13",
|
"memfs": "3.4.13",
|
||||||
"mockdate": "3.0.5",
|
"mockdate": "3.0.5",
|
||||||
"nock": "13.2.9",
|
"nock": "13.4.0",
|
||||||
"rimraf": "3.0.2",
|
"rimraf": "3.0.2",
|
||||||
"ts-deepmerge": "4.0.0",
|
"ts-deepmerge": "6.2.0",
|
||||||
"ts-mockito": "2.6.1",
|
"ts-mockito": "2.6.1",
|
||||||
"ts-node": "10.9.1",
|
"ts-node": "10.9.2",
|
||||||
"typescript": "4.9.4"
|
"typescript": "5.3.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.0.0"
|
"node": ">=18.0.0"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"/bin",
|
"/bin",
|
||||||
@@ -208,7 +210,9 @@
|
|||||||
"rebuild": "rimraf build && yarn build",
|
"rebuild": "rimraf build && yarn build",
|
||||||
"pretarball-ci": "./scripts/pretarball-ci.sh",
|
"pretarball-ci": "./scripts/pretarball-ci.sh",
|
||||||
"build": "tsc --project tsconfig.build.json",
|
"build": "tsc --project tsconfig.build.json",
|
||||||
|
"build-allow-unused": "tsc --project tsconfig.allowUnused.json",
|
||||||
"watch": "yarn build --watch --preserveWatchOutput",
|
"watch": "yarn build --watch --preserveWatchOutput",
|
||||||
|
"watch-allow-unused": "yarn build-allow-unused --watch --preserveWatchOutput",
|
||||||
"typecheck": "tsc",
|
"typecheck": "tsc",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"version": "yarn oclif readme && node scripts/patch-readme && git add README.md",
|
"version": "yarn oclif readme && node scripts/patch-readme && git add README.md",
|
||||||
@@ -216,7 +220,7 @@
|
|||||||
"clean": "rimraf dist build tmp node_modules yarn-error.log"
|
"clean": "rimraf dist build tmp node_modules yarn-error.log"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "18.6.0",
|
"node": "20.11.0",
|
||||||
"yarn": "1.22.19"
|
"yarn": "1.22.21"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,8 +3,7 @@ const fs = require('fs/promises');
|
|||||||
(async () => {
|
(async () => {
|
||||||
// Patch `oclif readme` path and link generation
|
// Patch `oclif readme` path and link generation
|
||||||
let readmeContent = await fs.readFile('README.md', 'utf8');
|
let readmeContent = await fs.readFile('README.md', 'utf8');
|
||||||
readmeContent = readmeContent.replace(/\[build\//g, '[src/');
|
readmeContent = readmeContent.replace(/src\/commands/g, 'packages/eas-cli/src/commands');
|
||||||
readmeContent = readmeContent.replace(/build\/commands/g, 'packages/eas-cli/src/commands');
|
|
||||||
await fs.writeFile('README.md', readmeContent);
|
await fs.writeFile('README.md', readmeContent);
|
||||||
|
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { AppVersionSource, EasJson } from '@expo/eas-json';
|
import { AppVersionSource, EasJson } from '@expo/eas-json';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
|
||||||
|
import { getMockEasJson, mockCommandContext, mockProjectId, mockTestCommand } from './utils';
|
||||||
import BuildVersionGetView from '../../commands/build/version/get';
|
import BuildVersionGetView from '../../commands/build/version/get';
|
||||||
import { AppVersionQuery } from '../../graphql/queries/AppVersionQuery';
|
import { AppVersionQuery } from '../../graphql/queries/AppVersionQuery';
|
||||||
import Log from '../../log';
|
import Log from '../../log';
|
||||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||||
import { getMockEasJson, mockCommandContext, mockProjectId, mockTestCommand } from './utils';
|
|
||||||
|
|
||||||
jest.mock('../../project/applicationIdentifier');
|
jest.mock('../../project/applicationIdentifier');
|
||||||
jest.mock('fs');
|
jest.mock('fs');
|
||||||
|
|||||||
@@ -2,12 +2,6 @@ import { AppVersionSource, EasJson } from '@expo/eas-json';
|
|||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
import BuildVersionSetView from '../../commands/build/version/set';
|
|
||||||
import { AppVersionMutation } from '../../graphql/mutations/AppVersionMutation';
|
|
||||||
import { AppQuery } from '../../graphql/queries/AppQuery';
|
|
||||||
import { AppVersionQuery } from '../../graphql/queries/AppVersionQuery';
|
|
||||||
import Log from '../../log';
|
|
||||||
import * as prompts from '../../prompts';
|
|
||||||
import {
|
import {
|
||||||
getMockAppFragment,
|
getMockAppFragment,
|
||||||
getMockEasJson,
|
getMockEasJson,
|
||||||
@@ -15,6 +9,12 @@ import {
|
|||||||
mockProjectId,
|
mockProjectId,
|
||||||
mockTestCommand,
|
mockTestCommand,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
import BuildVersionSetView from '../../commands/build/version/set';
|
||||||
|
import { AppVersionMutation } from '../../graphql/mutations/AppVersionMutation';
|
||||||
|
import { AppQuery } from '../../graphql/queries/AppQuery';
|
||||||
|
import { AppVersionQuery } from '../../graphql/queries/AppVersionQuery';
|
||||||
|
import Log from '../../log';
|
||||||
|
import * as prompts from '../../prompts';
|
||||||
|
|
||||||
jest.mock('../../project/applicationIdentifier');
|
jest.mock('../../project/applicationIdentifier');
|
||||||
jest.mock('../../graphql/queries/AppVersionQuery');
|
jest.mock('../../graphql/queries/AppVersionQuery');
|
||||||
|
|||||||
@@ -3,6 +3,13 @@ import { AppVersionSource, EasJson } from '@expo/eas-json';
|
|||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getMockAppFragment,
|
||||||
|
getMockEasJson,
|
||||||
|
mockCommandContext,
|
||||||
|
mockProjectId,
|
||||||
|
mockTestCommand,
|
||||||
|
} from './utils';
|
||||||
import { updateNativeVersionsAsync as updateAndroidNativeVersionsAsync } from '../../build/android/version';
|
import { updateNativeVersionsAsync as updateAndroidNativeVersionsAsync } from '../../build/android/version';
|
||||||
import { updateNativeVersionsAsync as updateIosNativeVersionsAsync } from '../../build/ios/version';
|
import { updateNativeVersionsAsync as updateIosNativeVersionsAsync } from '../../build/ios/version';
|
||||||
import BuildVersionSyncView from '../../commands/build/version/sync';
|
import BuildVersionSyncView from '../../commands/build/version/sync';
|
||||||
@@ -14,13 +21,6 @@ import { getAppBuildGradleAsync } from '../../project/android/gradleUtils';
|
|||||||
import { resolveTargetsAsync } from '../../project/ios/target';
|
import { resolveTargetsAsync } from '../../project/ios/target';
|
||||||
import { resolveWorkflowAsync } from '../../project/workflow';
|
import { resolveWorkflowAsync } from '../../project/workflow';
|
||||||
import * as prompts from '../../prompts';
|
import * as prompts from '../../prompts';
|
||||||
import {
|
|
||||||
getMockAppFragment,
|
|
||||||
getMockEasJson,
|
|
||||||
mockCommandContext,
|
|
||||||
mockProjectId,
|
|
||||||
mockTestCommand,
|
|
||||||
} from './utils';
|
|
||||||
|
|
||||||
jest.mock('../../build/android/version');
|
jest.mock('../../build/android/version');
|
||||||
jest.mock('../../build/ios/version');
|
jest.mock('../../build/ios/version');
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export function getMockAppFragment(): AppFragment {
|
|||||||
return {
|
return {
|
||||||
id: mockProjectId,
|
id: mockProjectId,
|
||||||
slug: 'testapp',
|
slug: 'testapp',
|
||||||
|
name: 'testapp',
|
||||||
fullName: '@testuser/testpp',
|
fullName: '@testuser/testpp',
|
||||||
ownerAccount: {
|
ownerAccount: {
|
||||||
id: 'test-account-id',
|
id: 'test-account-id',
|
||||||
@@ -59,7 +60,7 @@ export interface LocalProjectContext {
|
|||||||
export function mockCommandContext<
|
export function mockCommandContext<
|
||||||
C extends {
|
C extends {
|
||||||
[name: string]: any;
|
[name: string]: any;
|
||||||
} = object
|
} = object,
|
||||||
>(
|
>(
|
||||||
commandClass: { contextDefinition: ContextInput<C> },
|
commandClass: { contextDefinition: ContextInput<C> },
|
||||||
overrides: {
|
overrides: {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { JSONValue } from '@expo/json-file';
|
|||||||
|
|
||||||
import { ApiV2Error } from './ApiV2Error';
|
import { ApiV2Error } from './ApiV2Error';
|
||||||
import fetch, { RequestError, RequestInit } from './fetch';
|
import fetch, { RequestError, RequestInit } from './fetch';
|
||||||
import { getFreePortAsync } from './utils/port';
|
|
||||||
|
|
||||||
interface RequestOptions {
|
interface RequestOptions {
|
||||||
body: JSONValue;
|
body: JSONValue;
|
||||||
@@ -105,14 +104,3 @@ export function getEASUpdateURL(projectId: string): string {
|
|||||||
return new URL(projectId, `https://u.expo.dev`).href;
|
return new URL(projectId, `https://u.expo.dev`).href;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getSsoLocalServerPortAsync(): Promise<number> {
|
|
||||||
let startPort: number;
|
|
||||||
if (process.env.SSO_LOCAL_SERVER_PORT) {
|
|
||||||
startPort = Number(process.env.SSO_LOCAL_SERVER_PORT);
|
|
||||||
} else {
|
|
||||||
startPort = 19300;
|
|
||||||
}
|
|
||||||
const port = await getFreePortAsync(startPort);
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { UpdateBranchBasicInfoFragment } from '../../graphql/generated';
|
||||||
|
|
||||||
|
export const testBasicBranchInfo: UpdateBranchBasicInfoFragment = {
|
||||||
|
id: 'c4fec95d-7ab1-4d35-bfe6-fe7ff05beda9',
|
||||||
|
name: 'development',
|
||||||
|
__typename: 'UpdateBranch',
|
||||||
|
};
|
||||||
|
export const testBasicBranchInfo2: UpdateBranchBasicInfoFragment = {
|
||||||
|
id: '43ccb8a4-8aaa-47f0-9790-dcc80933441b',
|
||||||
|
name: 'preview',
|
||||||
|
__typename: 'UpdateBranch',
|
||||||
|
};
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
import { EASUpdateAction, EASUpdateContext, NonInteractiveError } from '../../eas-update/utils';
|
||||||
|
import { UpdateBranchBasicInfoFragment } from '../../graphql/generated';
|
||||||
|
import { BranchQuery } from '../../graphql/queries/BranchQuery';
|
||||||
|
import {
|
||||||
|
Connection,
|
||||||
|
Edge,
|
||||||
|
FilterPagination,
|
||||||
|
QueryParams,
|
||||||
|
selectPaginatedAsync,
|
||||||
|
} from '../../utils/relay';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select a branch for the project.
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {function} options.filterPredicate - A predicate to filter the branches that are shown to the user. It takes a branchInfo object as a parameter and returns a boolean.
|
||||||
|
* @param {string} options.printedType - The type of branch printed to the user. Defaults to 'branch'.
|
||||||
|
* @param {number} options.pageSize - The number of branches to show per page. Defaults to 100.
|
||||||
|
* @param {function} options.beforeEachFilterQuery Optional. If a filter predicate was specified, this callback function will be called before each query.
|
||||||
|
* @args externalQueryParams The query params for the pagination.
|
||||||
|
* @args totalNodesFetched The total number of nodes fetched so far.
|
||||||
|
* @args dataset The dataset so far.
|
||||||
|
* @param {function} options.afterEachFilterQuery Optional. If a filter predicate was specified, this callback function will be called after each query.
|
||||||
|
* @args externalQueryParams The query params for the pagination.
|
||||||
|
* @args totalNodesFetched The total number of nodes fetched so far.
|
||||||
|
* @args dataset The dataset so far.
|
||||||
|
* @args willFetchAgain If the query will fetch again to get a complete page.
|
||||||
|
*/
|
||||||
|
export class SelectBranch implements EASUpdateAction<UpdateBranchBasicInfoFragment | null> {
|
||||||
|
constructor(
|
||||||
|
private options: {
|
||||||
|
filterPredicate?: (branchInfo: UpdateBranchBasicInfoFragment) => boolean;
|
||||||
|
printedType?: string;
|
||||||
|
pageSize?: number;
|
||||||
|
beforeEachFilterQuery?: (
|
||||||
|
externalQueryParams: QueryParams,
|
||||||
|
totalNodesFetched: number,
|
||||||
|
dataset: Edge<UpdateBranchBasicInfoFragment>[]
|
||||||
|
) => void;
|
||||||
|
afterEachFilterQuery?: (
|
||||||
|
externalQueryParams: QueryParams,
|
||||||
|
totalNodesFetched: number,
|
||||||
|
dataset: Edge<UpdateBranchBasicInfoFragment>[],
|
||||||
|
willFetchAgain: boolean
|
||||||
|
) => void;
|
||||||
|
} = {}
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async queryAsync(
|
||||||
|
ctx: EASUpdateContext,
|
||||||
|
queryParams: QueryParams
|
||||||
|
): Promise<Connection<UpdateBranchBasicInfoFragment>> {
|
||||||
|
const { graphqlClient, app } = ctx;
|
||||||
|
const { projectId } = app;
|
||||||
|
return await BranchQuery.listBranchesBasicInfoPaginatedOnAppAsync(graphqlClient, {
|
||||||
|
appId: projectId,
|
||||||
|
...queryParams,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async filterQueryAsync(
|
||||||
|
ctx: EASUpdateContext,
|
||||||
|
queryParams: QueryParams,
|
||||||
|
filterPredicate: (branchInfo: UpdateBranchBasicInfoFragment) => boolean
|
||||||
|
): Promise<Connection<UpdateBranchBasicInfoFragment>> {
|
||||||
|
const queryAsync = async (
|
||||||
|
queryParams: QueryParams
|
||||||
|
): Promise<Connection<UpdateBranchBasicInfoFragment>> =>
|
||||||
|
await this.queryAsync(ctx, queryParams);
|
||||||
|
return await FilterPagination.getPageAsync({
|
||||||
|
queryParams,
|
||||||
|
queryAsync,
|
||||||
|
filterPredicate,
|
||||||
|
beforeEachQuery: this.options.beforeEachFilterQuery,
|
||||||
|
afterEachQuery: this.options.afterEachFilterQuery,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async runAsync(ctx: EASUpdateContext): Promise<UpdateBranchBasicInfoFragment | null> {
|
||||||
|
const { nonInteractive } = ctx;
|
||||||
|
const { filterPredicate } = this.options;
|
||||||
|
const printedType = this.options.printedType ?? 'branch';
|
||||||
|
const pageSize = this.options.pageSize ?? 100;
|
||||||
|
if (nonInteractive) {
|
||||||
|
throw new NonInteractiveError(
|
||||||
|
`${printedType} selection cannot be run in non-interactive mode.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryAsync = async (
|
||||||
|
queryParams: QueryParams
|
||||||
|
): Promise<Connection<UpdateBranchBasicInfoFragment>> =>
|
||||||
|
filterPredicate
|
||||||
|
? this.filterQueryAsync(ctx, queryParams, filterPredicate)
|
||||||
|
: this.queryAsync(ctx, queryParams);
|
||||||
|
|
||||||
|
const getTitleAsync = async (branchInfo: UpdateBranchBasicInfoFragment): Promise<string> =>
|
||||||
|
branchInfo.name;
|
||||||
|
|
||||||
|
return await selectPaginatedAsync({
|
||||||
|
queryAsync,
|
||||||
|
getTitleAsync,
|
||||||
|
printedType,
|
||||||
|
pageSize,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import { createCtxMock } from '../../../eas-update/__tests__/fixtures';
|
||||||
|
import { NonInteractiveError } from '../../../eas-update/utils';
|
||||||
|
import { UpdateBranchBasicInfoFragment } from '../../../graphql/generated';
|
||||||
|
import { BranchQuery } from '../../../graphql/queries/BranchQuery';
|
||||||
|
import { promptAsync } from '../../../prompts';
|
||||||
|
import { testBasicBranchInfo, testBasicBranchInfo2 } from '../../__tests__/fixtures';
|
||||||
|
import { SelectBranch } from '../SelectBranch';
|
||||||
|
|
||||||
|
jest.mock('../../../prompts');
|
||||||
|
jest.mock('../../../graphql/queries/BranchQuery');
|
||||||
|
describe(SelectBranch, () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
it('returns null if project has no branchs', async () => {
|
||||||
|
const ctx = createCtxMock();
|
||||||
|
jest.mocked(BranchQuery.listBranchesBasicInfoPaginatedOnAppAsync).mockResolvedValue({
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
hasPreviousPage: false,
|
||||||
|
hasNextPage: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const selectBranch = new SelectBranch();
|
||||||
|
const branch = await selectBranch.runAsync(ctx);
|
||||||
|
expect(branch).toBeNull();
|
||||||
|
});
|
||||||
|
it('still prompts if project has only one branch', async () => {
|
||||||
|
const ctx = createCtxMock();
|
||||||
|
jest.mocked(BranchQuery.listBranchesBasicInfoPaginatedOnAppAsync).mockResolvedValue({
|
||||||
|
edges: [{ node: testBasicBranchInfo, cursor: 'cursor' }],
|
||||||
|
pageInfo: {
|
||||||
|
hasPreviousPage: false,
|
||||||
|
hasNextPage: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
jest.mocked(promptAsync).mockImplementation(async () => ({
|
||||||
|
item: testBasicBranchInfo,
|
||||||
|
}));
|
||||||
|
const selectBranch = new SelectBranch();
|
||||||
|
const branch = await selectBranch.runAsync(ctx);
|
||||||
|
expect(branch).toBe(testBasicBranchInfo);
|
||||||
|
expect(promptAsync).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
it('returns the selected branch', async () => {
|
||||||
|
jest.mocked(BranchQuery.listBranchesBasicInfoPaginatedOnAppAsync).mockResolvedValue({
|
||||||
|
edges: [
|
||||||
|
{ node: testBasicBranchInfo, cursor: 'cursor' },
|
||||||
|
{ node: testBasicBranchInfo2, cursor: 'cursor' },
|
||||||
|
],
|
||||||
|
pageInfo: {
|
||||||
|
hasPreviousPage: false,
|
||||||
|
hasNextPage: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
jest.mocked(promptAsync).mockImplementation(async () => ({
|
||||||
|
item: testBasicBranchInfo,
|
||||||
|
}));
|
||||||
|
const ctx = createCtxMock();
|
||||||
|
const selectBranch = new SelectBranch();
|
||||||
|
const branch = await selectBranch.runAsync(ctx);
|
||||||
|
expect(branch).toBe(testBasicBranchInfo);
|
||||||
|
});
|
||||||
|
it('returns the only available branch with a filter', async () => {
|
||||||
|
jest.mocked(BranchQuery.listBranchesBasicInfoPaginatedOnAppAsync).mockResolvedValue({
|
||||||
|
edges: [
|
||||||
|
{ node: testBasicBranchInfo, cursor: 'cursor' },
|
||||||
|
{ node: testBasicBranchInfo2, cursor: 'cursor' },
|
||||||
|
],
|
||||||
|
pageInfo: {
|
||||||
|
hasPreviousPage: false,
|
||||||
|
hasNextPage: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const ctx = createCtxMock();
|
||||||
|
const selectBranch = new SelectBranch({
|
||||||
|
filterPredicate: (branchInfo: UpdateBranchBasicInfoFragment) =>
|
||||||
|
branchInfo === testBasicBranchInfo,
|
||||||
|
});
|
||||||
|
const branch = await selectBranch.runAsync(ctx);
|
||||||
|
expect(branch).toBe(testBasicBranchInfo);
|
||||||
|
});
|
||||||
|
it('throws an error in Non-Interactive Mode', async () => {
|
||||||
|
const ctx = createCtxMock({
|
||||||
|
nonInteractive: true,
|
||||||
|
});
|
||||||
|
const selectBranch = new SelectBranch();
|
||||||
|
|
||||||
|
// dont fail if users are running in non-interactive mode
|
||||||
|
await expect(selectBranch.runAsync(ctx)).rejects.toThrowError(NonInteractiveError);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import gql from 'graphql-tag';
|
import gql from 'graphql-tag';
|
||||||
|
|
||||||
|
import { BranchNotFoundError } from './utils';
|
||||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||||
import { PaginatedQueryOptions } from '../commandUtils/pagination';
|
import { PaginatedQueryOptions } from '../commandUtils/pagination';
|
||||||
import { withErrorHandlingAsync } from '../graphql/client';
|
import { withErrorHandlingAsync } from '../graphql/client';
|
||||||
@@ -19,7 +20,6 @@ import {
|
|||||||
paginatedQueryWithConfirmPromptAsync,
|
paginatedQueryWithConfirmPromptAsync,
|
||||||
paginatedQueryWithSelectPromptAsync,
|
paginatedQueryWithSelectPromptAsync,
|
||||||
} from '../utils/queries';
|
} from '../utils/queries';
|
||||||
import { BranchNotFoundError } from './utils';
|
|
||||||
|
|
||||||
export const BRANCHES_LIMIT = 50;
|
export const BRANCHES_LIMIT = 50;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { getVcsClient } from '../vcs';
|
import { Client } from '../vcs/vcs';
|
||||||
|
|
||||||
export async function getDefaultBranchNameAsync(): Promise<string> {
|
export async function getDefaultBranchNameAsync(vcsClient: Client): Promise<string> {
|
||||||
return (
|
return (
|
||||||
(await getVcsClient().getBranchNameAsync()) ||
|
(await vcsClient.getBranchNameAsync()) || `branch-${Math.random().toString(36).substring(2, 4)}`
|
||||||
`branch-${Math.random().toString(36).substring(2, 4)}`
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,7 @@ import { v4 as uuid } from 'uuid';
|
|||||||
|
|
||||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||||
import { selectBuildToCancelAsync } from '../../commands/build/cancel';
|
import { selectBuildToCancelAsync } from '../../commands/build/cancel';
|
||||||
import {
|
import { AppPlatform, BuildFragment, BuildPriority, BuildStatus } from '../../graphql/generated';
|
||||||
AppPlatform,
|
|
||||||
BuildFragment,
|
|
||||||
BuildPriority,
|
|
||||||
BuildResourceClass,
|
|
||||||
BuildStatus,
|
|
||||||
} from '../../graphql/generated';
|
|
||||||
import { BuildQuery } from '../../graphql/queries/BuildQuery';
|
import { BuildQuery } from '../../graphql/queries/BuildQuery';
|
||||||
import { confirmAsync, selectAsync } from '../../prompts';
|
import { confirmAsync, selectAsync } from '../../prompts';
|
||||||
|
|
||||||
@@ -91,6 +85,6 @@ function createMockBuildFragment({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
status: BuildStatus.InQueue,
|
status: BuildStatus.InQueue,
|
||||||
resourceClass: BuildResourceClass.AndroidMedium,
|
isForIosSimulator: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
import { instance, mock } from 'ts-mockito';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||||
|
import { selectBuildToDeleteAsync } from '../../commands/build/delete';
|
||||||
|
import { AppPlatform, BuildFragment, BuildPriority, BuildStatus } from '../../graphql/generated';
|
||||||
|
import { BuildQuery } from '../../graphql/queries/BuildQuery';
|
||||||
|
import { confirmAsync, selectAsync } from '../../prompts';
|
||||||
|
|
||||||
|
jest.mock('../../ora', () => ({
|
||||||
|
ora: jest.fn().mockImplementation(() => ({
|
||||||
|
start: jest.fn().mockImplementation(() => ({
|
||||||
|
stop: jest.fn(),
|
||||||
|
fail: jest.fn(),
|
||||||
|
})),
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
jest.mock('../../prompts', () => {
|
||||||
|
return {
|
||||||
|
selectAsync: jest.fn(),
|
||||||
|
confirmAsync: jest.fn(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.mock('../../graphql/queries/BuildQuery', () => {
|
||||||
|
const actual = jest.requireActual('../../graphql/queries/BuildQuery');
|
||||||
|
return {
|
||||||
|
BuildQuery: {
|
||||||
|
...actual.BuildQuery,
|
||||||
|
viewBuildsOnAppAsync: jest.fn(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(selectBuildToDeleteAsync.name, () => {
|
||||||
|
const selectedBuildId = uuid();
|
||||||
|
const projectId = uuid();
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest
|
||||||
|
.mocked(BuildQuery.viewBuildsOnAppAsync)
|
||||||
|
.mockImplementation(async () => [
|
||||||
|
createMockBuildFragment({ projectId, buildId: selectedBuildId }),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not return build id when confirmation is rejected', async () => {
|
||||||
|
const graphqlClient = instance(mock<ExpoGraphqlClient>());
|
||||||
|
jest.mocked(confirmAsync).mockResolvedValueOnce(false);
|
||||||
|
expect(selectBuildToDeleteAsync(graphqlClient, projectId, 'blah')).resolves.toEqual(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns build id when confirmation is confirmed', async () => {
|
||||||
|
const graphqlClient = instance(mock<ExpoGraphqlClient>());
|
||||||
|
jest.mocked(selectAsync).mockResolvedValueOnce(selectedBuildId);
|
||||||
|
jest.mocked(confirmAsync).mockResolvedValueOnce(true);
|
||||||
|
expect(selectBuildToDeleteAsync(graphqlClient, projectId, 'blah')).resolves.toEqual(
|
||||||
|
selectedBuildId
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function createMockBuildFragment({
|
||||||
|
projectId,
|
||||||
|
buildId,
|
||||||
|
}: {
|
||||||
|
projectId: string;
|
||||||
|
buildId?: string;
|
||||||
|
}): BuildFragment {
|
||||||
|
return {
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
platform: AppPlatform.Android,
|
||||||
|
id: buildId ?? uuid(),
|
||||||
|
priority: BuildPriority.Normal,
|
||||||
|
project: {
|
||||||
|
__typename: 'App',
|
||||||
|
slug: 'test-project',
|
||||||
|
id: projectId,
|
||||||
|
name: 'test-project',
|
||||||
|
ownerAccount: {
|
||||||
|
__typename: 'Account',
|
||||||
|
id: uuid(),
|
||||||
|
name: 'test-account',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
status: BuildStatus.InQueue,
|
||||||
|
isForIosSimulator: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -13,6 +13,8 @@ import {
|
|||||||
EasBuildFreeTierDisabledAndroidError,
|
EasBuildFreeTierDisabledAndroidError,
|
||||||
EasBuildFreeTierDisabledError,
|
EasBuildFreeTierDisabledError,
|
||||||
EasBuildFreeTierDisabledIOSError,
|
EasBuildFreeTierDisabledIOSError,
|
||||||
|
EasBuildFreeTierIosLimitExceededError,
|
||||||
|
EasBuildFreeTierLimitExceededError,
|
||||||
EasBuildResourceClassNotAvailableInFreeTierError,
|
EasBuildResourceClassNotAvailableInFreeTierError,
|
||||||
EasBuildTooManyPendingBuildsError,
|
EasBuildTooManyPendingBuildsError,
|
||||||
RequestValidationError,
|
RequestValidationError,
|
||||||
@@ -25,12 +27,18 @@ beforeEach(() => {
|
|||||||
|
|
||||||
const EXPECTED_STRINGIFIED_GRAPHQL_ERROR_JSON = `[
|
const EXPECTED_STRINGIFIED_GRAPHQL_ERROR_JSON = `[
|
||||||
{
|
{
|
||||||
|
"name": "GraphQLError",
|
||||||
|
"extensions": {},
|
||||||
"message": "Error 1"
|
"message": "Error 1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"name": "GraphQLError",
|
||||||
|
"extensions": {},
|
||||||
"message": "Error 2"
|
"message": "Error 2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"name": "GraphQLError",
|
||||||
|
"extensions": {},
|
||||||
"message": "Error 3"
|
"message": "Error 3"
|
||||||
}
|
}
|
||||||
]`;
|
]`;
|
||||||
@@ -280,6 +288,45 @@ describe(Build.name, () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('for EAS_BUILD_FREE_TIER_LIMIT_EXCEEDED', () => {
|
||||||
|
it('throws correct EasCommandError subclass', async () => {
|
||||||
|
const platform = Platform.ANDROID;
|
||||||
|
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_FREE_TIER_LIMIT_EXCEEDED');
|
||||||
|
const graphQLErrors = [graphQLError];
|
||||||
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
|
||||||
|
const handleBuildRequestErrorThrownError = getError<EasBuildFreeTierLimitExceededError>(
|
||||||
|
() => {
|
||||||
|
handleBuildRequestError(error, platform);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assertReThrownError(
|
||||||
|
handleBuildRequestErrorThrownError,
|
||||||
|
EasBuildFreeTierLimitExceededError,
|
||||||
|
'Error 1'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('for EAS_BUILD_FREE_TIER_IOS_LIMIT_EXCEEDED', () => {
|
||||||
|
it('throws correct EasCommandError subclass', async () => {
|
||||||
|
const platform = Platform.ANDROID;
|
||||||
|
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_FREE_TIER_IOS_LIMIT_EXCEEDED');
|
||||||
|
const graphQLErrors = [graphQLError];
|
||||||
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
|
||||||
|
const handleBuildRequestErrorThrownError =
|
||||||
|
getError<EasBuildFreeTierIosLimitExceededError>(() => {
|
||||||
|
handleBuildRequestError(error, platform);
|
||||||
|
});
|
||||||
|
|
||||||
|
assertReThrownError(
|
||||||
|
handleBuildRequestErrorThrownError,
|
||||||
|
EasBuildFreeTierIosLimitExceededError,
|
||||||
|
'Error 1'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('for VALIDATION_ERROR', () => {
|
describe('for VALIDATION_ERROR', () => {
|
||||||
describe('with Android', () => {
|
describe('with Android', () => {
|
||||||
@@ -424,6 +471,29 @@ describe(Build.name, () => {
|
|||||||
|
|
||||||
assertReThrownError(handleBuildRequestErrorThrownError, Error, expectedMessage);
|
assertReThrownError(handleBuildRequestErrorThrownError, Error, expectedMessage);
|
||||||
});
|
});
|
||||||
|
it('throws base Error class with message including all error messages if combined error has multiple GraphQL errors', async () => {
|
||||||
|
const platform = Platform.ANDROID;
|
||||||
|
const graphQLErrors = [
|
||||||
|
getGraphQLError('Error 1', 'UNKNOWN_GRAPHQL_ERROR'),
|
||||||
|
getGraphQLError('Error 2', 'OTHER_GRAPHQL_ERROR'),
|
||||||
|
getGraphQLError('Error 3', 'YET_ANOTHER_GRAPHQL_ERROR'),
|
||||||
|
];
|
||||||
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
const expectedMessage =
|
||||||
|
`${EXPECTED_GENERIC_MESSAGE}\n` +
|
||||||
|
`Request ID: ${mockRequestId}\n` +
|
||||||
|
'Error message: Error 1\n' +
|
||||||
|
`Request ID: ${mockRequestId}\n` +
|
||||||
|
'Error message: Error 2\n' +
|
||||||
|
`Request ID: ${mockRequestId}\n` +
|
||||||
|
'Error message: Error 3';
|
||||||
|
|
||||||
|
const handleBuildRequestErrorThrownError = getError<Error>(() => {
|
||||||
|
handleBuildRequestError(error, platform);
|
||||||
|
});
|
||||||
|
|
||||||
|
assertReThrownError(handleBuildRequestErrorThrownError, Error, expectedMessage);
|
||||||
|
});
|
||||||
describe('without request ID', () => {
|
describe('without request ID', () => {
|
||||||
it('throws base Error class with custom message without request ID line', async () => {
|
it('throws base Error class with custom message without request ID line', async () => {
|
||||||
const platform = Platform.ANDROID;
|
const platform = Platform.ANDROID;
|
||||||
@@ -455,6 +525,29 @@ describe(Build.name, () => {
|
|||||||
|
|
||||||
assertReThrownError(handleBuildRequestErrorThrownError, Error, expectedMessage);
|
assertReThrownError(handleBuildRequestErrorThrownError, Error, expectedMessage);
|
||||||
});
|
});
|
||||||
|
it('throws base Error class with message including all error messages if combined error has multiple GraphQL errors', async () => {
|
||||||
|
const platform = Platform.IOS;
|
||||||
|
const graphQLErrors = [
|
||||||
|
getGraphQLError('Error 1', 'UNKNOWN_GRAPHQL_ERROR'),
|
||||||
|
getGraphQLError('Error 2', 'OTHER_GRAPHQL_ERROR'),
|
||||||
|
getGraphQLError('Error 3', 'YET_ANOTHER_GRAPHQL_ERROR'),
|
||||||
|
];
|
||||||
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
const expectedMessage =
|
||||||
|
`${EXPECTED_GENERIC_MESSAGE}\n` +
|
||||||
|
`Request ID: ${mockRequestId}\n` +
|
||||||
|
'Error message: Error 1\n' +
|
||||||
|
`Request ID: ${mockRequestId}\n` +
|
||||||
|
'Error message: Error 2\n' +
|
||||||
|
`Request ID: ${mockRequestId}\n` +
|
||||||
|
'Error message: Error 3';
|
||||||
|
|
||||||
|
const handleBuildRequestErrorThrownError = getError<Error>(() => {
|
||||||
|
handleBuildRequestError(error, platform);
|
||||||
|
});
|
||||||
|
|
||||||
|
assertReThrownError(handleBuildRequestErrorThrownError, Error, expectedMessage);
|
||||||
|
});
|
||||||
describe('without request ID', () => {
|
describe('without request ID', () => {
|
||||||
it('throws base Error class with custom message without request ID line', async () => {
|
it('throws base Error class with custom message without request ID line', async () => {
|
||||||
const platform = Platform.IOS;
|
const platform = Platform.IOS;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import os from 'os';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
import { getAppBuildGradleAsync, resolveConfigValue } from '../../../project/android/gradleUtils';
|
import { getAppBuildGradleAsync, resolveConfigValue } from '../../../project/android/gradleUtils';
|
||||||
|
import { resolveVcsClient } from '../../../vcs';
|
||||||
import {
|
import {
|
||||||
BumpStrategy,
|
BumpStrategy,
|
||||||
bumpVersionAsync,
|
bumpVersionAsync,
|
||||||
@@ -18,6 +19,8 @@ import {
|
|||||||
const fsReal = jest.requireActual('fs').promises as typeof fs;
|
const fsReal = jest.requireActual('fs').promises as typeof fs;
|
||||||
jest.mock('fs');
|
jest.mock('fs');
|
||||||
|
|
||||||
|
const vcsClient = resolveVcsClient();
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
// do not remove the following line
|
// do not remove the following line
|
||||||
// this fixes a weird error with tempy in @expo/image-utils
|
// this fixes a weird error with tempy in @expo/image-utils
|
||||||
@@ -191,7 +194,8 @@ describe(maybeResolveVersionsAsync, () => {
|
|||||||
const { appVersion, appBuildVersion } = await maybeResolveVersionsAsync(
|
const { appVersion, appBuildVersion } = await maybeResolveVersionsAsync(
|
||||||
'/app',
|
'/app',
|
||||||
exp,
|
exp,
|
||||||
{} as BuildProfile<Platform.ANDROID>
|
{} as BuildProfile<Platform.ANDROID>,
|
||||||
|
vcsClient
|
||||||
);
|
);
|
||||||
expect(appVersion).toBe('3.0.0');
|
expect(appVersion).toBe('3.0.0');
|
||||||
expect(appBuildVersion).toBe('123');
|
expect(appBuildVersion).toBe('123');
|
||||||
@@ -203,7 +207,8 @@ describe(maybeResolveVersionsAsync, () => {
|
|||||||
const { appVersion, appBuildVersion } = await maybeResolveVersionsAsync(
|
const { appVersion, appBuildVersion } = await maybeResolveVersionsAsync(
|
||||||
'/app',
|
'/app',
|
||||||
exp,
|
exp,
|
||||||
{} as BuildProfile<Platform.ANDROID>
|
{} as BuildProfile<Platform.ANDROID>,
|
||||||
|
vcsClient
|
||||||
);
|
);
|
||||||
expect(appVersion).toBe('5.0.0');
|
expect(appVersion).toBe('5.0.0');
|
||||||
expect(appBuildVersion).toBe('126');
|
expect(appBuildVersion).toBe('126');
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
import { Android, Job, Metadata, Platform, Workflow } from '@expo/eas-build-job';
|
import { Android, Metadata, Platform, Workflow } from '@expo/eas-build-job';
|
||||||
import { AppVersionSource } from '@expo/eas-json';
|
import { AppVersionSource } from '@expo/eas-json';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import nullthrows from 'nullthrows';
|
import nullthrows from 'nullthrows';
|
||||||
|
|
||||||
|
import { transformJob } from './graphql';
|
||||||
|
import { prepareJobAsync } from './prepareJob';
|
||||||
|
import { syncProjectConfigurationAsync } from './syncProjectConfiguration';
|
||||||
|
import { resolveRemoteVersionCodeAsync } from './version';
|
||||||
import AndroidCredentialsProvider, {
|
import AndroidCredentialsProvider, {
|
||||||
AndroidCredentials,
|
AndroidCredentials,
|
||||||
} from '../../credentials/android/AndroidCredentialsProvider';
|
} from '../../credentials/android/AndroidCredentialsProvider';
|
||||||
@@ -29,10 +33,6 @@ import {
|
|||||||
checkNodeEnvVariable,
|
checkNodeEnvVariable,
|
||||||
validatePNGsForManagedProjectAsync,
|
validatePNGsForManagedProjectAsync,
|
||||||
} from '../validate';
|
} from '../validate';
|
||||||
import { transformJob } from './graphql';
|
|
||||||
import { prepareJobAsync } from './prepareJob';
|
|
||||||
import { syncProjectConfigurationAsync } from './syncProjectConfiguration';
|
|
||||||
import { resolveRemoteVersionCodeAsync } from './version';
|
|
||||||
|
|
||||||
export async function createAndroidContextAsync(
|
export async function createAndroidContextAsync(
|
||||||
ctx: CommonContext<Platform.ANDROID>
|
ctx: CommonContext<Platform.ANDROID>
|
||||||
@@ -59,13 +59,22 @@ This means that it will most likely produce an AAB and you will not be able to i
|
|||||||
await checkGoogleServicesFileAsync(ctx);
|
await checkGoogleServicesFileAsync(ctx);
|
||||||
await validatePNGsForManagedProjectAsync(ctx);
|
await validatePNGsForManagedProjectAsync(ctx);
|
||||||
|
|
||||||
const gradleContext = await resolveGradleBuildContextAsync(ctx.projectDir, buildProfile);
|
const gradleContext = await resolveGradleBuildContextAsync(
|
||||||
|
ctx.projectDir,
|
||||||
|
buildProfile,
|
||||||
|
ctx.vcsClient
|
||||||
|
);
|
||||||
|
|
||||||
if (ctx.workflow === Workflow.MANAGED) {
|
if (ctx.workflow === Workflow.MANAGED) {
|
||||||
await ensureApplicationIdIsDefinedForManagedProjectAsync(ctx);
|
await ensureApplicationIdIsDefinedForManagedProjectAsync(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
const applicationId = await getApplicationIdAsync(ctx.projectDir, ctx.exp, gradleContext);
|
const applicationId = await getApplicationIdAsync(
|
||||||
|
ctx.projectDir,
|
||||||
|
ctx.exp,
|
||||||
|
ctx.vcsClient,
|
||||||
|
gradleContext
|
||||||
|
);
|
||||||
const versionCodeOverride =
|
const versionCodeOverride =
|
||||||
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
|
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
|
||||||
? await resolveRemoteVersionCodeAsync(ctx.graphqlClient, {
|
? await resolveRemoteVersionCodeAsync(ctx.graphqlClient, {
|
||||||
@@ -74,6 +83,7 @@ This means that it will most likely produce an AAB and you will not be able to i
|
|||||||
exp: ctx.exp,
|
exp: ctx.exp,
|
||||||
applicationId,
|
applicationId,
|
||||||
buildProfile,
|
buildProfile,
|
||||||
|
vcsClient: ctx.vcsClient,
|
||||||
})
|
})
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
@@ -89,20 +99,21 @@ export async function prepareAndroidBuildAsync(
|
|||||||
return await ensureAndroidCredentialsAsync(ctx);
|
return await ensureAndroidCredentialsAsync(ctx);
|
||||||
},
|
},
|
||||||
syncProjectConfigurationAsync: async () => {
|
syncProjectConfigurationAsync: async () => {
|
||||||
await syncProjectConfigurationAsync(ctx.graphqlClient, {
|
await syncProjectConfigurationAsync({
|
||||||
projectDir: ctx.projectDir,
|
projectDir: ctx.projectDir,
|
||||||
exp: ctx.exp,
|
exp: ctx.exp,
|
||||||
localAutoIncrement:
|
localAutoIncrement:
|
||||||
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
|
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
|
||||||
? false
|
? false
|
||||||
: ctx.buildProfile.autoIncrement,
|
: ctx.buildProfile.autoIncrement,
|
||||||
projectId: ctx.projectId,
|
vcsClient: ctx.vcsClient,
|
||||||
|
env: ctx.buildProfile.env,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
prepareJobAsync: async (
|
prepareJobAsync: async (
|
||||||
ctx: BuildContext<Platform.ANDROID>,
|
ctx: BuildContext<Platform.ANDROID>,
|
||||||
jobData: JobData<AndroidCredentials>
|
jobData: JobData<AndroidCredentials>
|
||||||
): Promise<Job> => {
|
): Promise<Android.Job> => {
|
||||||
return await prepareJobAsync(ctx, jobData);
|
return await prepareJobAsync(ctx, jobData);
|
||||||
},
|
},
|
||||||
sendBuildRequestAsync: async (
|
sendBuildRequestAsync: async (
|
||||||
@@ -124,8 +135,7 @@ export async function prepareAndroidBuildAsync(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function shouldProvideCredentials(ctx: BuildContext<Platform.ANDROID>): boolean {
|
function shouldProvideCredentials(ctx: BuildContext<Platform.ANDROID>): boolean {
|
||||||
const isCustomBuild = ctx.buildProfile.config !== undefined;
|
return !ctx.buildProfile.withoutCredentials;
|
||||||
return !ctx.buildProfile.withoutCredentials && !isCustomBuild;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ensureAndroidCredentialsAsync(
|
async function ensureAndroidCredentialsAsync(
|
||||||
@@ -137,6 +147,7 @@ async function ensureAndroidCredentialsAsync(
|
|||||||
const androidApplicationIdentifier = await getApplicationIdAsync(
|
const androidApplicationIdentifier = await getApplicationIdAsync(
|
||||||
ctx.projectDir,
|
ctx.projectDir,
|
||||||
ctx.exp,
|
ctx.exp,
|
||||||
|
ctx.vcsClient,
|
||||||
ctx.android.gradleContext
|
ctx.android.gradleContext
|
||||||
);
|
);
|
||||||
const provider = new AndroidCredentialsProvider(ctx.credentialsCtx, {
|
const provider = new AndroidCredentialsProvider(ctx.credentialsCtx, {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Android } from '@expo/eas-build-job';
|
|||||||
|
|
||||||
import { AndroidBuildType, AndroidJobInput } from '../../graphql/generated';
|
import { AndroidBuildType, AndroidJobInput } from '../../graphql/generated';
|
||||||
import {
|
import {
|
||||||
|
loggerLevelToGraphQLWorkerLoggerLevel,
|
||||||
transformBuildMode,
|
transformBuildMode,
|
||||||
transformBuildTrigger,
|
transformBuildTrigger,
|
||||||
transformProjectArchive,
|
transformProjectArchive,
|
||||||
@@ -14,7 +15,6 @@ export function transformJob(job: Android.Job): AndroidJobInput {
|
|||||||
triggeredBy: transformBuildTrigger(job.triggeredBy),
|
triggeredBy: transformBuildTrigger(job.triggeredBy),
|
||||||
projectArchive: transformProjectArchive(job.projectArchive),
|
projectArchive: transformProjectArchive(job.projectArchive),
|
||||||
projectRootDirectory: job.projectRootDirectory,
|
projectRootDirectory: job.projectRootDirectory,
|
||||||
releaseChannel: job.releaseChannel,
|
|
||||||
updates: job.updates,
|
updates: job.updates,
|
||||||
secrets: job.secrets,
|
secrets: job.secrets,
|
||||||
builderEnvironment: job.builderEnvironment,
|
builderEnvironment: job.builderEnvironment,
|
||||||
@@ -29,6 +29,9 @@ export function transformJob(job: Android.Job): AndroidJobInput {
|
|||||||
experimental: job.experimental,
|
experimental: job.experimental,
|
||||||
mode: transformBuildMode(job.mode),
|
mode: transformBuildMode(job.mode),
|
||||||
customBuildConfig: job.customBuildConfig,
|
customBuildConfig: job.customBuildConfig,
|
||||||
|
loggerLevel: job.loggerLevel
|
||||||
|
? loggerLevelToGraphQLWorkerLoggerLevel[job.loggerLevel]
|
||||||
|
: undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,18 +3,16 @@ import {
|
|||||||
ArchiveSource,
|
ArchiveSource,
|
||||||
BuildMode,
|
BuildMode,
|
||||||
BuildTrigger,
|
BuildTrigger,
|
||||||
Job,
|
|
||||||
Platform,
|
Platform,
|
||||||
sanitizeJob,
|
sanitizeBuildJob,
|
||||||
} from '@expo/eas-build-job';
|
} from '@expo/eas-build-job';
|
||||||
import { BuildProfile } from '@expo/eas-json';
|
import { BuildProfile } from '@expo/eas-json';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import slash from 'slash';
|
import slash from 'slash';
|
||||||
|
|
||||||
import { AndroidCredentials } from '../../credentials/android/AndroidCredentialsProvider';
|
import { AndroidCredentials } from '../../credentials/android/AndroidCredentialsProvider';
|
||||||
import { getCustomBuildConfigPath } from '../../project/customBuildConfig';
|
import { getCustomBuildConfigPathForJob } from '../../project/customBuildConfig';
|
||||||
import { getUsername } from '../../project/projectUtils';
|
import { getUsername } from '../../project/projectUtils';
|
||||||
import { getVcsClient } from '../../vcs';
|
|
||||||
import { BuildContext } from '../context';
|
import { BuildContext } from '../context';
|
||||||
|
|
||||||
interface JobData {
|
interface JobData {
|
||||||
@@ -30,11 +28,11 @@ const cacheDefaults = {
|
|||||||
export async function prepareJobAsync(
|
export async function prepareJobAsync(
|
||||||
ctx: BuildContext<Platform.ANDROID>,
|
ctx: BuildContext<Platform.ANDROID>,
|
||||||
jobData: JobData
|
jobData: JobData
|
||||||
): Promise<Job> {
|
): Promise<Android.Job> {
|
||||||
const username = getUsername(ctx.exp, ctx.user);
|
const username = getUsername(ctx.exp, ctx.user);
|
||||||
const buildProfile: BuildProfile<Platform.ANDROID> = ctx.buildProfile;
|
const buildProfile: BuildProfile<Platform.ANDROID> = ctx.buildProfile;
|
||||||
const projectRootDirectory =
|
const projectRootDirectory =
|
||||||
slash(path.relative(await getVcsClient().getRootPathAsync(), ctx.projectDir)) || '.';
|
slash(path.relative(await ctx.vcsClient.getRootPathAsync(), ctx.projectDir)) || '.';
|
||||||
const { credentials } = jobData;
|
const { credentials } = jobData;
|
||||||
const buildCredentials = credentials
|
const buildCredentials = credentials
|
||||||
? {
|
? {
|
||||||
@@ -55,9 +53,18 @@ export async function prepareJobAsync(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const maybeCustomBuildConfigPath = buildProfile.config
|
const maybeCustomBuildConfigPath = buildProfile.config
|
||||||
? getCustomBuildConfigPath(buildProfile.config)
|
? getCustomBuildConfigPathForJob(buildProfile.config)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
let buildMode;
|
||||||
|
if (ctx.repack) {
|
||||||
|
buildMode = BuildMode.REPACK;
|
||||||
|
} else if (buildProfile.config) {
|
||||||
|
buildMode = BuildMode.CUSTOM;
|
||||||
|
} else {
|
||||||
|
buildMode = BuildMode.BUILD;
|
||||||
|
}
|
||||||
|
|
||||||
const job: Android.Job = {
|
const job: Android.Job = {
|
||||||
type: ctx.workflow,
|
type: ctx.workflow,
|
||||||
platform: Platform.ANDROID,
|
platform: Platform.ANDROID,
|
||||||
@@ -66,9 +73,10 @@ export async function prepareJobAsync(
|
|||||||
builderEnvironment: {
|
builderEnvironment: {
|
||||||
image: buildProfile.image,
|
image: buildProfile.image,
|
||||||
node: buildProfile.node,
|
node: buildProfile.node,
|
||||||
|
pnpm: buildProfile.pnpm,
|
||||||
|
bun: buildProfile.bun,
|
||||||
yarn: buildProfile.yarn,
|
yarn: buildProfile.yarn,
|
||||||
ndk: buildProfile.ndk,
|
ndk: buildProfile.ndk,
|
||||||
expoCli: buildProfile.expoCli,
|
|
||||||
env: buildProfile.env,
|
env: buildProfile.env,
|
||||||
},
|
},
|
||||||
cache: {
|
cache: {
|
||||||
@@ -79,7 +87,6 @@ export async function prepareJobAsync(
|
|||||||
secrets: {
|
secrets: {
|
||||||
...buildCredentials,
|
...buildCredentials,
|
||||||
},
|
},
|
||||||
releaseChannel: buildProfile.releaseChannel,
|
|
||||||
updates: { channel: buildProfile.channel },
|
updates: { channel: buildProfile.channel },
|
||||||
developmentClient: buildProfile.developmentClient,
|
developmentClient: buildProfile.developmentClient,
|
||||||
gradleCommand: buildProfile.gradleCommand,
|
gradleCommand: buildProfile.gradleCommand,
|
||||||
@@ -95,14 +102,20 @@ export async function prepareJobAsync(
|
|||||||
experimental: {
|
experimental: {
|
||||||
prebuildCommand: buildProfile.prebuildCommand,
|
prebuildCommand: buildProfile.prebuildCommand,
|
||||||
},
|
},
|
||||||
mode: buildProfile.config ? BuildMode.CUSTOM : BuildMode.BUILD,
|
mode: buildMode,
|
||||||
triggeredBy: BuildTrigger.EAS_CLI,
|
triggeredBy: BuildTrigger.EAS_CLI,
|
||||||
...(maybeCustomBuildConfigPath && {
|
...(maybeCustomBuildConfigPath && {
|
||||||
customBuildConfig: {
|
customBuildConfig: {
|
||||||
path: maybeCustomBuildConfigPath,
|
path: maybeCustomBuildConfigPath,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
...(ctx.repack && {
|
||||||
|
customBuildConfig: {
|
||||||
|
path: '__eas/repack.yml',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
loggerLevel: ctx.loggerLevel,
|
||||||
};
|
};
|
||||||
|
|
||||||
return sanitizeJob(job);
|
return sanitizeBuildJob(job) as Android.Job;
|
||||||
}
|
}
|
||||||
@@ -1,39 +1,38 @@
|
|||||||
import { ExpoConfig } from '@expo/config';
|
import { ExpoConfig } from '@expo/config';
|
||||||
import { AndroidConfig } from '@expo/config-plugins';
|
import { AndroidConfig } from '@expo/config-plugins';
|
||||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
import { Env, Platform, Workflow } from '@expo/eas-build-job';
|
||||||
import { AndroidVersionAutoIncrement } from '@expo/eas-json';
|
import { AndroidVersionAutoIncrement } from '@expo/eas-json';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version';
|
||||||
import Log from '../../log';
|
import Log from '../../log';
|
||||||
import { isExpoUpdatesInstalled } from '../../project/projectUtils';
|
import { isExpoUpdatesInstalled } from '../../project/projectUtils';
|
||||||
import { resolveWorkflowAsync } from '../../project/workflow';
|
import { resolveWorkflowAsync } from '../../project/workflow';
|
||||||
import { syncUpdatesConfigurationAsync } from '../../update/android/UpdatesModule';
|
import { syncUpdatesConfigurationAsync } from '../../update/android/UpdatesModule';
|
||||||
import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version';
|
import { Client } from '../../vcs/vcs';
|
||||||
|
|
||||||
export async function syncProjectConfigurationAsync(
|
export async function syncProjectConfigurationAsync({
|
||||||
graphqlClient: ExpoGraphqlClient,
|
|
||||||
{
|
|
||||||
projectDir,
|
projectDir,
|
||||||
exp,
|
exp,
|
||||||
localAutoIncrement,
|
localAutoIncrement,
|
||||||
projectId,
|
vcsClient,
|
||||||
|
env,
|
||||||
}: {
|
}: {
|
||||||
projectDir: string;
|
projectDir: string;
|
||||||
exp: ExpoConfig;
|
exp: ExpoConfig;
|
||||||
localAutoIncrement?: AndroidVersionAutoIncrement;
|
localAutoIncrement?: AndroidVersionAutoIncrement;
|
||||||
projectId: string;
|
vcsClient: Client;
|
||||||
}
|
env: Env | undefined;
|
||||||
): Promise<void> {
|
}): Promise<void> {
|
||||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
|
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient);
|
||||||
const versionBumpStrategy = resolveVersionBumpStrategy(localAutoIncrement ?? false);
|
const versionBumpStrategy = resolveVersionBumpStrategy(localAutoIncrement ?? false);
|
||||||
|
|
||||||
if (workflow === Workflow.GENERIC) {
|
if (workflow === Workflow.GENERIC) {
|
||||||
await cleanUpOldEasBuildGradleScriptAsync(projectDir);
|
await cleanUpOldEasBuildGradleScriptAsync(projectDir);
|
||||||
if (isExpoUpdatesInstalled(projectDir)) {
|
if (isExpoUpdatesInstalled(projectDir)) {
|
||||||
await syncUpdatesConfigurationAsync(graphqlClient, projectDir, exp, projectId);
|
await syncUpdatesConfigurationAsync({ projectDir, exp, workflow, env });
|
||||||
}
|
}
|
||||||
await bumpVersionAsync({ projectDir, exp, bumpStrategy: versionBumpStrategy });
|
await bumpVersionAsync({ projectDir, exp, bumpStrategy: versionBumpStrategy });
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
} from '../../project/android/gradleUtils';
|
} from '../../project/android/gradleUtils';
|
||||||
import { getNextVersionCode } from '../../project/android/versions';
|
import { getNextVersionCode } from '../../project/android/versions';
|
||||||
import { resolveWorkflowAsync } from '../../project/workflow';
|
import { resolveWorkflowAsync } from '../../project/workflow';
|
||||||
|
import { Client } from '../../vcs/vcs';
|
||||||
import { updateAppJsonConfigAsync } from '../utils/appJson';
|
import { updateAppJsonConfigAsync } from '../utils/appJson';
|
||||||
import { bumpAppVersionAsync, ensureStaticConfigExists } from '../utils/version';
|
import { bumpAppVersionAsync, ensureStaticConfigExists } from '../utils/version';
|
||||||
|
|
||||||
@@ -96,9 +97,10 @@ export async function bumpVersionInAppJsonAsync({
|
|||||||
export async function maybeResolveVersionsAsync(
|
export async function maybeResolveVersionsAsync(
|
||||||
projectDir: string,
|
projectDir: string,
|
||||||
exp: ExpoConfig,
|
exp: ExpoConfig,
|
||||||
buildProfile: BuildProfile<Platform.ANDROID>
|
buildProfile: BuildProfile<Platform.ANDROID>,
|
||||||
|
vcsClient: Client
|
||||||
): Promise<{ appVersion?: string; appBuildVersion?: string }> {
|
): Promise<{ appVersion?: string; appBuildVersion?: string }> {
|
||||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
|
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient);
|
||||||
if (workflow === Workflow.GENERIC) {
|
if (workflow === Workflow.GENERIC) {
|
||||||
const buildGradle = await getAppBuildGradleAsync(projectDir);
|
const buildGradle = await getAppBuildGradleAsync(projectDir);
|
||||||
try {
|
try {
|
||||||
@@ -183,12 +185,14 @@ export async function resolveRemoteVersionCodeAsync(
|
|||||||
exp,
|
exp,
|
||||||
applicationId,
|
applicationId,
|
||||||
buildProfile,
|
buildProfile,
|
||||||
|
vcsClient,
|
||||||
}: {
|
}: {
|
||||||
projectDir: string;
|
projectDir: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
exp: ExpoConfig;
|
exp: ExpoConfig;
|
||||||
applicationId: string;
|
applicationId: string;
|
||||||
buildProfile: BuildProfile<Platform.ANDROID>;
|
buildProfile: BuildProfile<Platform.ANDROID>;
|
||||||
|
vcsClient: Client;
|
||||||
}
|
}
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const remoteVersions = await AppVersionQuery.latestVersionAsync(
|
const remoteVersions = await AppVersionQuery.latestVersionAsync(
|
||||||
@@ -198,7 +202,7 @@ export async function resolveRemoteVersionCodeAsync(
|
|||||||
applicationId
|
applicationId
|
||||||
);
|
);
|
||||||
|
|
||||||
const localVersions = await maybeResolveVersionsAsync(projectDir, exp, buildProfile);
|
const localVersions = await maybeResolveVersionsAsync(projectDir, exp, buildProfile, vcsClient);
|
||||||
let currentBuildVersion: string;
|
let currentBuildVersion: string;
|
||||||
if (remoteVersions?.buildVersion) {
|
if (remoteVersions?.buildVersion) {
|
||||||
currentBuildVersion = remoteVersions.buildVersion;
|
currentBuildVersion = remoteVersions.buildVersion;
|
||||||
@@ -228,7 +232,9 @@ export async function resolveRemoteVersionCodeAsync(
|
|||||||
applicationIdentifier: applicationId,
|
applicationIdentifier: applicationId,
|
||||||
storeVersion: localVersions.appVersion ?? exp.version ?? '1.0.0',
|
storeVersion: localVersions.appVersion ?? exp.version ?? '1.0.0',
|
||||||
buildVersion: currentBuildVersion,
|
buildVersion: currentBuildVersion,
|
||||||
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.ANDROID) ?? undefined,
|
runtimeVersion:
|
||||||
|
(await Updates.getRuntimeVersionNullableAsync(projectDir, exp, Platform.ANDROID)) ??
|
||||||
|
undefined,
|
||||||
});
|
});
|
||||||
spinner.succeed(`Initialized versionCode with ${chalk.bold(currentBuildVersion)}.`);
|
spinner.succeed(`Initialized versionCode with ${chalk.bold(currentBuildVersion)}.`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -250,7 +256,9 @@ export async function resolveRemoteVersionCodeAsync(
|
|||||||
applicationIdentifier: applicationId,
|
applicationIdentifier: applicationId,
|
||||||
storeVersion: localVersions.appVersion ?? exp.version ?? '1.0.0',
|
storeVersion: localVersions.appVersion ?? exp.version ?? '1.0.0',
|
||||||
buildVersion: String(nextBuildVersion),
|
buildVersion: String(nextBuildVersion),
|
||||||
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.ANDROID) ?? undefined,
|
runtimeVersion:
|
||||||
|
(await Updates.getRuntimeVersionNullableAsync(projectDir, exp, Platform.ANDROID)) ??
|
||||||
|
undefined,
|
||||||
});
|
});
|
||||||
spinner.succeed(
|
spinner.succeed(
|
||||||
`Incremented versionCode from ${chalk.bold(currentBuildVersion)} to ${chalk.bold(
|
`Incremented versionCode from ${chalk.bold(currentBuildVersion)} to ${chalk.bold(
|
||||||
|
|||||||
+113
-121
@@ -1,11 +1,43 @@
|
|||||||
import { ArchiveSource, ArchiveSourceType, Job, Metadata, Platform } from '@expo/eas-build-job';
|
import {
|
||||||
import { CredentialsSource, EasJsonAccessor, EasJsonUtils, ResourceClass } from '@expo/eas-json';
|
ArchiveSource,
|
||||||
|
ArchiveSourceType,
|
||||||
|
BuildJob,
|
||||||
|
Metadata,
|
||||||
|
Platform,
|
||||||
|
} from '@expo/eas-build-job';
|
||||||
|
import { CredentialsSource } from '@expo/eas-json';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import cliProgress from 'cli-progress';
|
import cliProgress from 'cli-progress';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
|
import { GraphQLError } from 'graphql/error';
|
||||||
import nullthrows from 'nullthrows';
|
import nullthrows from 'nullthrows';
|
||||||
|
|
||||||
|
import { BuildContext } from './context';
|
||||||
|
import {
|
||||||
|
EasBuildDownForMaintenanceError,
|
||||||
|
EasBuildFreeTierDisabledAndroidError,
|
||||||
|
EasBuildFreeTierDisabledError,
|
||||||
|
EasBuildFreeTierDisabledIOSError,
|
||||||
|
EasBuildFreeTierIosLimitExceededError,
|
||||||
|
EasBuildFreeTierLimitExceededError,
|
||||||
|
EasBuildLegacyResourceClassNotAvailableError,
|
||||||
|
EasBuildProjectArchiveUploadError,
|
||||||
|
EasBuildResourceClassNotAvailableInFreeTierError,
|
||||||
|
EasBuildTooManyPendingBuildsError,
|
||||||
|
RequestValidationError,
|
||||||
|
TurtleDeprecatedJobFormatError,
|
||||||
|
} from './errors';
|
||||||
|
import { transformMetadata } from './graphql';
|
||||||
|
import { LocalBuildMode, runLocalBuildAsync } from './local';
|
||||||
|
import { collectMetadataAsync } from './metadata';
|
||||||
|
import { printDeprecationWarnings } from './utils/printBuildInfo';
|
||||||
|
import {
|
||||||
|
LocalFile,
|
||||||
|
makeProjectMetadataFileAsync,
|
||||||
|
makeProjectTarballAsync,
|
||||||
|
reviewAndCommitChangesAsync,
|
||||||
|
} from './utils/repository';
|
||||||
import { BuildEvent } from '../analytics/AnalyticsManager';
|
import { BuildEvent } from '../analytics/AnalyticsManager';
|
||||||
import { withAnalyticsAsync } from '../analytics/common';
|
import { withAnalyticsAsync } from '../analytics/common';
|
||||||
import { getExpoWebsiteBaseUrl } from '../api';
|
import { getExpoWebsiteBaseUrl } from '../api';
|
||||||
@@ -16,7 +48,6 @@ import {
|
|||||||
BuildFragment,
|
BuildFragment,
|
||||||
BuildParamsInput,
|
BuildParamsInput,
|
||||||
BuildPriority,
|
BuildPriority,
|
||||||
BuildResourceClass,
|
|
||||||
BuildStatus,
|
BuildStatus,
|
||||||
UploadSessionType,
|
UploadSessionType,
|
||||||
} from '../graphql/generated';
|
} from '../graphql/generated';
|
||||||
@@ -29,30 +60,11 @@ import {
|
|||||||
appPlatformEmojis,
|
appPlatformEmojis,
|
||||||
requestedPlatformDisplayNames,
|
requestedPlatformDisplayNames,
|
||||||
} from '../platform';
|
} from '../platform';
|
||||||
import { confirmAsync } from '../prompts';
|
|
||||||
import { uploadFileAtPathToGCSAsync } from '../uploads';
|
import { uploadFileAtPathToGCSAsync } from '../uploads';
|
||||||
import { formatBytes } from '../utils/files';
|
import { formatBytes } from '../utils/files';
|
||||||
import { printJsonOnlyOutput } from '../utils/json';
|
import { printJsonOnlyOutput } from '../utils/json';
|
||||||
import { createProgressTracker } from '../utils/progress';
|
import { createProgressTracker } from '../utils/progress';
|
||||||
import { sleepAsync } from '../utils/promise';
|
import { sleepAsync } from '../utils/promise';
|
||||||
import { getVcsClient } from '../vcs';
|
|
||||||
import { BuildContext } from './context';
|
|
||||||
import {
|
|
||||||
EasBuildDownForMaintenanceError,
|
|
||||||
EasBuildFreeTierDisabledAndroidError,
|
|
||||||
EasBuildFreeTierDisabledError,
|
|
||||||
EasBuildFreeTierDisabledIOSError,
|
|
||||||
EasBuildProjectArchiveUploadError,
|
|
||||||
EasBuildResourceClassNotAvailableInFreeTierError,
|
|
||||||
EasBuildTooManyPendingBuildsError,
|
|
||||||
RequestValidationError,
|
|
||||||
TurtleDeprecatedJobFormatError,
|
|
||||||
} from './errors';
|
|
||||||
import { transformMetadata } from './graphql';
|
|
||||||
import { LocalBuildMode, runLocalBuildAsync } from './local';
|
|
||||||
import { collectMetadataAsync } from './metadata';
|
|
||||||
import { printDeprecationWarnings } from './utils/printBuildInfo';
|
|
||||||
import { makeProjectTarballAsync, reviewAndCommitChangesAsync } from './utils/repository';
|
|
||||||
|
|
||||||
export interface CredentialsResult<Credentials> {
|
export interface CredentialsResult<Credentials> {
|
||||||
source: CredentialsSource.LOCAL | CredentialsSource.REMOTE;
|
source: CredentialsSource.LOCAL | CredentialsSource.REMOTE;
|
||||||
@@ -64,14 +76,14 @@ export interface JobData<Credentials> {
|
|||||||
projectArchive: ArchiveSource;
|
projectArchive: ArchiveSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Builder<TPlatform extends Platform, Credentials, TJob extends Job> {
|
interface Builder<TPlatform extends Platform, Credentials, TJob extends BuildJob> {
|
||||||
ctx: BuildContext<TPlatform>;
|
ctx: BuildContext<TPlatform>;
|
||||||
|
|
||||||
ensureCredentialsAsync(
|
ensureCredentialsAsync(
|
||||||
ctx: BuildContext<TPlatform>
|
ctx: BuildContext<TPlatform>
|
||||||
): Promise<CredentialsResult<Credentials> | undefined>;
|
): Promise<CredentialsResult<Credentials> | undefined>;
|
||||||
syncProjectConfigurationAsync(ctx: BuildContext<TPlatform>): Promise<void>;
|
syncProjectConfigurationAsync(ctx: BuildContext<TPlatform>): Promise<void>;
|
||||||
prepareJobAsync(ctx: BuildContext<TPlatform>, jobData: JobData<Credentials>): Promise<Job>;
|
prepareJobAsync(ctx: BuildContext<TPlatform>, jobData: JobData<Credentials>): Promise<BuildJob>;
|
||||||
sendBuildRequestAsync(
|
sendBuildRequestAsync(
|
||||||
appId: string,
|
appId: string,
|
||||||
job: TJob,
|
job: TJob,
|
||||||
@@ -96,7 +108,7 @@ function resolveBuildParamsInput<T extends Platform>(
|
|||||||
export async function prepareBuildRequestForPlatformAsync<
|
export async function prepareBuildRequestForPlatformAsync<
|
||||||
TPlatform extends Platform,
|
TPlatform extends Platform,
|
||||||
Credentials,
|
Credentials,
|
||||||
TJob extends Job
|
TJob extends BuildJob,
|
||||||
>(builder: Builder<TPlatform, Credentials, TJob>): Promise<BuildRequestSender> {
|
>(builder: Builder<TPlatform, Credentials, TJob>): Promise<BuildRequestSender> {
|
||||||
const { ctx } = builder;
|
const { ctx } = builder;
|
||||||
const credentialsResult = await withAnalyticsAsync(
|
const credentialsResult = await withAnalyticsAsync(
|
||||||
@@ -121,9 +133,10 @@ export async function prepareBuildRequestForPlatformAsync<
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (await getVcsClient().isCommitRequiredAsync()) {
|
if (await ctx.vcsClient.isCommitRequiredAsync()) {
|
||||||
Log.addNewLineIfNone();
|
Log.addNewLineIfNone();
|
||||||
await reviewAndCommitChangesAsync(
|
await reviewAndCommitChangesAsync(
|
||||||
|
ctx.vcsClient,
|
||||||
`[EAS Build] Run EAS Build for ${requestedPlatformDisplayNames[ctx.platform as Platform]}`,
|
`[EAS Build] Run EAS Build for ${requestedPlatformDisplayNames[ctx.platform as Platform]}`,
|
||||||
{ nonInteractive: ctx.nonInteractive }
|
{ nonInteractive: ctx.nonInteractive }
|
||||||
);
|
);
|
||||||
@@ -131,9 +144,10 @@ export async function prepareBuildRequestForPlatformAsync<
|
|||||||
|
|
||||||
let projectArchive: ArchiveSource | undefined;
|
let projectArchive: ArchiveSource | undefined;
|
||||||
if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN) {
|
if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN) {
|
||||||
|
const projectPath = (await makeProjectTarballAsync(ctx.vcsClient)).path;
|
||||||
projectArchive = {
|
projectArchive = {
|
||||||
type: ArchiveSourceType.PATH,
|
type: ArchiveSourceType.PATH,
|
||||||
path: (await makeProjectTarballAsync()).path,
|
path: projectPath,
|
||||||
};
|
};
|
||||||
} else if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.INTERNAL) {
|
} else if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.INTERNAL) {
|
||||||
projectArchive = {
|
projectArchive = {
|
||||||
@@ -143,7 +157,7 @@ export async function prepareBuildRequestForPlatformAsync<
|
|||||||
} else if (!ctx.localBuildOptions.localBuildMode) {
|
} else if (!ctx.localBuildOptions.localBuildMode) {
|
||||||
projectArchive = {
|
projectArchive = {
|
||||||
type: ArchiveSourceType.GCS,
|
type: ArchiveSourceType.GCS,
|
||||||
bucketKey: await uploadProjectAsync(ctx),
|
...(await uploadProjectAsync(ctx)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
assert(projectArchive);
|
assert(projectArchive);
|
||||||
@@ -183,8 +197,11 @@ const SERVER_SIDE_DEFINED_ERRORS: Record<string, typeof EasCommandError> = {
|
|||||||
EAS_BUILD_FREE_TIER_DISABLED: EasBuildFreeTierDisabledError,
|
EAS_BUILD_FREE_TIER_DISABLED: EasBuildFreeTierDisabledError,
|
||||||
EAS_BUILD_FREE_TIER_DISABLED_IOS: EasBuildFreeTierDisabledIOSError,
|
EAS_BUILD_FREE_TIER_DISABLED_IOS: EasBuildFreeTierDisabledIOSError,
|
||||||
EAS_BUILD_FREE_TIER_DISABLED_ANDROID: EasBuildFreeTierDisabledAndroidError,
|
EAS_BUILD_FREE_TIER_DISABLED_ANDROID: EasBuildFreeTierDisabledAndroidError,
|
||||||
|
EAS_BUILD_FREE_TIER_LIMIT_EXCEEDED: EasBuildFreeTierLimitExceededError,
|
||||||
|
EAS_BUILD_FREE_TIER_IOS_LIMIT_EXCEEDED: EasBuildFreeTierIosLimitExceededError,
|
||||||
EAS_BUILD_RESOURCE_CLASS_NOT_AVAILABLE_IN_FREE_TIER:
|
EAS_BUILD_RESOURCE_CLASS_NOT_AVAILABLE_IN_FREE_TIER:
|
||||||
EasBuildResourceClassNotAvailableInFreeTierError,
|
EasBuildResourceClassNotAvailableInFreeTierError,
|
||||||
|
EAS_BUILD_LEGACY_RESOURCE_CLASS_NOT_AVAILABLE: EasBuildLegacyResourceClassNotAvailableError,
|
||||||
VALIDATION_ERROR: RequestValidationError,
|
VALIDATION_ERROR: RequestValidationError,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -206,15 +223,19 @@ export function handleBuildRequestError(error: any, platform: Platform): never {
|
|||||||
`You have already reached the maximum number of pending ${requestedPlatformDisplayNames[platform]} builds for your account. Try again later.`
|
`You have already reached the maximum number of pending ${requestedPlatformDisplayNames[platform]} builds for your account. Try again later.`
|
||||||
);
|
);
|
||||||
} else if (error?.graphQLErrors) {
|
} else if (error?.graphQLErrors) {
|
||||||
const graphQLError = error.graphQLErrors[0];
|
const errorMessage = error.graphQLErrors
|
||||||
|
.map((graphQLError: GraphQLError) => {
|
||||||
const requestIdLine = graphQLError?.extensions?.requestId
|
const requestIdLine = graphQLError?.extensions?.requestId
|
||||||
? `\nRequest ID: ${graphQLError.extensions.requestId}`
|
? `\nRequest ID: ${graphQLError.extensions.requestId}`
|
||||||
: '';
|
: '';
|
||||||
const originalErrorMessage = graphQLError?.message
|
const errorMessageLine = graphQLError?.message
|
||||||
? `\nError message: ${graphQLError.message}`
|
? `\nError message: ${graphQLError.message}`
|
||||||
: '';
|
: '';
|
||||||
|
return `${requestIdLine}${errorMessageLine}`;
|
||||||
|
})
|
||||||
|
.join('');
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Build request failed. Make sure you are using the latest eas-cli version. If the problem persists, report the issue.${requestIdLine}${originalErrorMessage}`
|
`Build request failed. Make sure you are using the latest eas-cli version. If the problem persists, report the issue.${errorMessage}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
@@ -222,7 +243,10 @@ export function handleBuildRequestError(error: any, platform: Platform): never {
|
|||||||
|
|
||||||
async function uploadProjectAsync<TPlatform extends Platform>(
|
async function uploadProjectAsync<TPlatform extends Platform>(
|
||||||
ctx: BuildContext<TPlatform>
|
ctx: BuildContext<TPlatform>
|
||||||
): Promise<string> {
|
): Promise<{
|
||||||
|
bucketKey: string;
|
||||||
|
metadataLocation?: string;
|
||||||
|
}> {
|
||||||
let projectTarballPath;
|
let projectTarballPath;
|
||||||
try {
|
try {
|
||||||
return await withAnalyticsAsync(
|
return await withAnalyticsAsync(
|
||||||
@@ -234,7 +258,7 @@ async function uploadProjectAsync<TPlatform extends Platform>(
|
|||||||
'https://expo.fyi/eas-build-archive'
|
'https://expo.fyi/eas-build-archive'
|
||||||
)}`
|
)}`
|
||||||
);
|
);
|
||||||
const projectTarball = await makeProjectTarballAsync();
|
const projectTarball = await makeProjectTarballAsync(ctx.vcsClient);
|
||||||
|
|
||||||
if (projectTarball.size > 1024 * 1024 * 100) {
|
if (projectTarball.size > 1024 * 1024 * 100) {
|
||||||
Log.warn(
|
Log.warn(
|
||||||
@@ -246,22 +270,32 @@ async function uploadProjectAsync<TPlatform extends Platform>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
projectTarballPath = projectTarball.path;
|
if (projectTarball.size > 2 * 1024 * 1024 * 1024) {
|
||||||
|
throw new Error('Project archive is too big. Maximum allowed size is 2GB.');
|
||||||
|
}
|
||||||
|
|
||||||
const bucketKey = await uploadFileAtPathToGCSAsync(
|
projectTarballPath = projectTarball.path;
|
||||||
|
const [bucketKey, { metadataLocation }] = await Promise.all([
|
||||||
|
uploadFileAtPathToGCSAsync(
|
||||||
ctx.graphqlClient,
|
ctx.graphqlClient,
|
||||||
UploadSessionType.EasBuildGcsProjectSources,
|
UploadSessionType.EasBuildGcsProjectSources,
|
||||||
projectTarball.path,
|
projectTarball.path,
|
||||||
createProgressTracker({
|
createProgressTracker({
|
||||||
total: projectTarball.size,
|
total: projectTarball.size,
|
||||||
message: ratio =>
|
message: ratio =>
|
||||||
`Uploading to EAS Build (${formatBytes(projectTarball.size * ratio)} / ${formatBytes(
|
`Uploading to EAS Build (${formatBytes(
|
||||||
projectTarball.size
|
projectTarball.size * ratio
|
||||||
)})`,
|
)} / ${formatBytes(projectTarball.size)})`,
|
||||||
completedMessage: (duration: string) => `Uploaded to EAS ${chalk.dim(duration)}`,
|
completedMessage: (duration: string) => `Uploaded to EAS ${chalk.dim(duration)}`,
|
||||||
})
|
})
|
||||||
);
|
),
|
||||||
return bucketKey;
|
uploadMetadataFileAsync<TPlatform>(projectTarball, ctx),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (metadataLocation) {
|
||||||
|
return { bucketKey, metadataLocation };
|
||||||
|
}
|
||||||
|
return { bucketKey };
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
attemptEvent: BuildEvent.PROJECT_UPLOAD_ATTEMPT,
|
attemptEvent: BuildEvent.PROJECT_UPLOAD_ATTEMPT,
|
||||||
@@ -272,9 +306,11 @@ async function uploadProjectAsync<TPlatform extends Platform>(
|
|||||||
);
|
);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
let errMessage = 'Failed to upload the project tarball to EAS Build';
|
let errMessage = 'Failed to upload the project tarball to EAS Build';
|
||||||
|
|
||||||
if (err.message) {
|
if (err.message) {
|
||||||
errMessage += `\n\nReason: ${err.message}`;
|
errMessage += `\n\nReason: ${err.message}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new EasBuildProjectArchiveUploadError(errMessage);
|
throw new EasBuildProjectArchiveUploadError(errMessage);
|
||||||
} finally {
|
} finally {
|
||||||
if (projectTarballPath) {
|
if (projectTarballPath) {
|
||||||
@@ -283,7 +319,41 @@ async function uploadProjectAsync<TPlatform extends Platform>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendBuildRequestAsync<TPlatform extends Platform, Credentials, TJob extends Job>(
|
async function uploadMetadataFileAsync<TPlatform extends Platform>(
|
||||||
|
projectTarball: LocalFile,
|
||||||
|
ctx: BuildContext<TPlatform>
|
||||||
|
): Promise<{ metadataLocation: string | null }> {
|
||||||
|
let projectMetadataFile: LocalFile | null = null;
|
||||||
|
try {
|
||||||
|
projectMetadataFile = await makeProjectMetadataFileAsync(projectTarball.path);
|
||||||
|
|
||||||
|
const metadataLocation = await uploadFileAtPathToGCSAsync(
|
||||||
|
ctx.graphqlClient,
|
||||||
|
UploadSessionType.EasBuildGcsProjectMetadata,
|
||||||
|
projectMetadataFile.path
|
||||||
|
);
|
||||||
|
return { metadataLocation };
|
||||||
|
} catch (err: any) {
|
||||||
|
let errMessage = 'Failed to upload metadata to EAS Build';
|
||||||
|
|
||||||
|
if (err.message) {
|
||||||
|
errMessage += `\n\nReason: ${err.message}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.warn(errMessage);
|
||||||
|
return { metadataLocation: null };
|
||||||
|
} finally {
|
||||||
|
if (projectMetadataFile) {
|
||||||
|
await fs.remove(projectMetadataFile.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendBuildRequestAsync<
|
||||||
|
TPlatform extends Platform,
|
||||||
|
Credentials,
|
||||||
|
TJob extends BuildJob,
|
||||||
|
>(
|
||||||
builder: Builder<TPlatform, Credentials, TJob>,
|
builder: Builder<TPlatform, Credentials, TJob>,
|
||||||
job: TJob,
|
job: TJob,
|
||||||
metadata: Metadata,
|
metadata: Metadata,
|
||||||
@@ -320,12 +390,7 @@ export type MaybeBuildFragment = BuildFragment | null;
|
|||||||
|
|
||||||
export async function waitForBuildEndAsync(
|
export async function waitForBuildEndAsync(
|
||||||
graphqlClient: ExpoGraphqlClient,
|
graphqlClient: ExpoGraphqlClient,
|
||||||
{
|
{ buildIds, accountName }: { buildIds: string[]; accountName: string },
|
||||||
buildIds,
|
|
||||||
accountName,
|
|
||||||
projectDir,
|
|
||||||
nonInteractive,
|
|
||||||
}: { buildIds: string[]; accountName: string; projectDir: string; nonInteractive: boolean },
|
|
||||||
{ intervalSec = 10 } = {}
|
{ intervalSec = 10 } = {}
|
||||||
): Promise<MaybeBuildFragment[]> {
|
): Promise<MaybeBuildFragment[]> {
|
||||||
let spinner;
|
let spinner;
|
||||||
@@ -342,10 +407,7 @@ export async function waitForBuildEndAsync(
|
|||||||
const builds = await getBuildsSafelyAsync(graphqlClient, buildIds);
|
const builds = await getBuildsSafelyAsync(graphqlClient, buildIds);
|
||||||
const { refetch } =
|
const { refetch } =
|
||||||
builds.length === 1
|
builds.length === 1
|
||||||
? await handleSingleBuildProgressAsync(
|
? await handleSingleBuildProgressAsync({ build: builds[0], accountName }, { spinner })
|
||||||
{ build: builds[0], accountName, projectDir, nonInteractive },
|
|
||||||
{ spinner }
|
|
||||||
)
|
|
||||||
: await handleMultipleBuildsProgressAsync({ builds }, { spinner, originalSpinnerText });
|
: await handleMultipleBuildsProgressAsync({ builds }, { spinner, originalSpinnerText });
|
||||||
if (!refetch) {
|
if (!refetch) {
|
||||||
return builds;
|
return builds;
|
||||||
@@ -383,13 +445,9 @@ async function handleSingleBuildProgressAsync(
|
|||||||
{
|
{
|
||||||
build,
|
build,
|
||||||
accountName,
|
accountName,
|
||||||
projectDir,
|
|
||||||
nonInteractive,
|
|
||||||
}: {
|
}: {
|
||||||
build: MaybeBuildFragment;
|
build: MaybeBuildFragment;
|
||||||
accountName: string;
|
accountName: string;
|
||||||
projectDir: string;
|
|
||||||
nonInteractive: boolean;
|
|
||||||
},
|
},
|
||||||
{ spinner }: { spinner: Ora }
|
{ spinner }: { spinner: Ora }
|
||||||
): Promise<BuildProgressResult> {
|
): Promise<BuildProgressResult> {
|
||||||
@@ -443,54 +501,6 @@ async function handleSingleBuildProgressAsync(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
build.platform === AppPlatform.Ios &&
|
|
||||||
[BuildResourceClass.IosIntelLarge, BuildResourceClass.IosIntelMedium].includes(
|
|
||||||
build.resourceClass
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
let askToSwitchToM1 = false;
|
|
||||||
if (
|
|
||||||
build.priority === BuildPriority.High &&
|
|
||||||
build.estimatedWaitTimeLeftSeconds >= /* 10 minutes */ 10 * 60
|
|
||||||
) {
|
|
||||||
Log.newLine();
|
|
||||||
Log.warn(
|
|
||||||
`Warning: Priority queue wait time for legacy iOS Intel workers is longer than usual (more than 10 minutes).`
|
|
||||||
);
|
|
||||||
Log.warn(`We recommend switching to the new, faster M1 workers for iOS builds.`);
|
|
||||||
Log.warn(
|
|
||||||
learnMore('https://blog.expo.dev/m1-workers-on-eas-build-dcaa2c1333ad', {
|
|
||||||
learnMoreMessage: 'Learn more on switching to M1 workers.',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
askToSwitchToM1 = true;
|
|
||||||
} else if (
|
|
||||||
build.priority !== BuildPriority.High &&
|
|
||||||
build.estimatedWaitTimeLeftSeconds >= /* 120 minutes */ 120 * 60
|
|
||||||
) {
|
|
||||||
Log.newLine();
|
|
||||||
Log.warn(
|
|
||||||
`Warning: Free tier queue wait time for legacy iOS Intel workers is longer than usual.`
|
|
||||||
);
|
|
||||||
Log.warn(`We recommend switching to the new, faster M1 workers for iOS builds.`);
|
|
||||||
Log.warn(
|
|
||||||
learnMore('https://blog.expo.dev/m1-workers-on-eas-build-dcaa2c1333ad', {
|
|
||||||
learnMoreMessage: 'Learn more on switching to M1 workers.',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
askToSwitchToM1 = true;
|
|
||||||
}
|
|
||||||
if (!nonInteractive && askToSwitchToM1) {
|
|
||||||
const shouldSwitchToM1 = await confirmAsync({
|
|
||||||
message: `Switch iOS builds to M1 workers (modifies build profiles in eas.json)?`,
|
|
||||||
});
|
|
||||||
if (shouldSwitchToM1) {
|
|
||||||
await updateIosBuildProfilesToUseM1WorkersAsync(projectDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.newLine();
|
Log.newLine();
|
||||||
Log.log(`Waiting in ${priorityToQueueDisplayName[build.priority]}`);
|
Log.log(`Waiting in ${priorityToQueueDisplayName[build.priority]}`);
|
||||||
queueProgressBar.start(
|
queueProgressBar.start(
|
||||||
@@ -639,21 +649,3 @@ function formatAccountSubscriptionsUrl(accountName: string): string {
|
|||||||
getExpoWebsiteBaseUrl()
|
getExpoWebsiteBaseUrl()
|
||||||
).toString();
|
).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateIosBuildProfilesToUseM1WorkersAsync(projectDir: string): Promise<void> {
|
|
||||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
|
||||||
await easJsonAccessor.readRawJsonAsync();
|
|
||||||
|
|
||||||
const profileNames = await EasJsonUtils.getBuildProfileNamesAsync(easJsonAccessor);
|
|
||||||
easJsonAccessor.patch(easJsonRawObject => {
|
|
||||||
for (const profileName of profileNames) {
|
|
||||||
easJsonRawObject.build[profileName].ios = {
|
|
||||||
...easJsonRawObject.build[profileName].ios,
|
|
||||||
resourceClass: ResourceClass.M_MEDIUM,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return easJsonRawObject;
|
|
||||||
});
|
|
||||||
await easJsonAccessor.writeAsync();
|
|
||||||
Log.withTick('Updated eas.json. Your next builds will run on M1 workers.');
|
|
||||||
}
|
|
||||||
@@ -1,17 +1,16 @@
|
|||||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
|
||||||
import { EasJson, EasJsonAccessor } from '@expo/eas-json';
|
import { EasJson, EasJsonAccessor } from '@expo/eas-json';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
|
|
||||||
import Log, { learnMore } from '../log';
|
|
||||||
import { resolveWorkflowAsync } from '../project/workflow';
|
|
||||||
import { easCliVersion } from '../utils/easCli';
|
|
||||||
import { getVcsClient } from '../vcs';
|
|
||||||
import { maybeBailOnRepoStatusAsync, reviewAndCommitChangesAsync } from './utils/repository';
|
import { maybeBailOnRepoStatusAsync, reviewAndCommitChangesAsync } from './utils/repository';
|
||||||
|
import Log, { learnMore } from '../log';
|
||||||
|
import { easCliVersion } from '../utils/easCli';
|
||||||
|
import { Client } from '../vcs/vcs';
|
||||||
|
|
||||||
interface ConfigureParams {
|
interface ConfigureParams {
|
||||||
projectDir: string;
|
projectDir: string;
|
||||||
nonInteractive: boolean;
|
nonInteractive: boolean;
|
||||||
|
vcsClient: Client;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function easJsonExistsAsync(projectDir: string): Promise<boolean> {
|
export async function easJsonExistsAsync(projectDir: string): Promise<boolean> {
|
||||||
@@ -36,20 +35,24 @@ export async function ensureProjectConfiguredAsync(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function configureAsync({ projectDir, nonInteractive }: ConfigureParams): Promise<void> {
|
async function configureAsync({
|
||||||
await maybeBailOnRepoStatusAsync();
|
projectDir,
|
||||||
|
nonInteractive,
|
||||||
|
vcsClient,
|
||||||
|
}: ConfigureParams): Promise<void> {
|
||||||
|
await maybeBailOnRepoStatusAsync(vcsClient, nonInteractive);
|
||||||
|
|
||||||
await createEasJsonAsync(projectDir);
|
await createEasJsonAsync(projectDir, vcsClient);
|
||||||
|
|
||||||
if (await getVcsClient().isCommitRequiredAsync()) {
|
if (await vcsClient.isCommitRequiredAsync()) {
|
||||||
Log.newLine();
|
Log.newLine();
|
||||||
await reviewAndCommitChangesAsync('Configure EAS Build', {
|
await reviewAndCommitChangesAsync(vcsClient, 'Configure EAS Build', {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const EAS_JSON_MANAGED_DEFAULT: EasJson = {
|
const EAS_JSON_DEFAULT: EasJson = {
|
||||||
cli: {
|
cli: {
|
||||||
version: `>= ${easCliVersion}`,
|
version: `>= ${easCliVersion}`,
|
||||||
},
|
},
|
||||||
@@ -68,44 +71,11 @@ const EAS_JSON_MANAGED_DEFAULT: EasJson = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const EAS_JSON_BARE_DEFAULT: EasJson = {
|
async function createEasJsonAsync(projectDir: string, vcsClient: Client): Promise<void> {
|
||||||
cli: {
|
|
||||||
version: `>= ${easCliVersion}`,
|
|
||||||
},
|
|
||||||
build: {
|
|
||||||
development: {
|
|
||||||
distribution: 'internal',
|
|
||||||
android: {
|
|
||||||
gradleCommand: ':app:assembleDebug',
|
|
||||||
},
|
|
||||||
ios: {
|
|
||||||
buildConfiguration: 'Debug',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
preview: {
|
|
||||||
distribution: 'internal',
|
|
||||||
},
|
|
||||||
production: {},
|
|
||||||
},
|
|
||||||
submit: {
|
|
||||||
production: {},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
async function createEasJsonAsync(projectDir: string): Promise<void> {
|
|
||||||
const easJsonPath = EasJsonAccessor.formatEasJsonPath(projectDir);
|
const easJsonPath = EasJsonAccessor.formatEasJsonPath(projectDir);
|
||||||
|
|
||||||
const hasAndroidNativeProject =
|
await fs.writeFile(easJsonPath, `${JSON.stringify(EAS_JSON_DEFAULT, null, 2)}\n`);
|
||||||
(await resolveWorkflowAsync(projectDir, Platform.ANDROID)) === Workflow.GENERIC;
|
await vcsClient.trackFileAsync(easJsonPath);
|
||||||
const hasIosNativeProject =
|
|
||||||
(await resolveWorkflowAsync(projectDir, Platform.IOS)) === Workflow.GENERIC;
|
|
||||||
const easJson =
|
|
||||||
hasAndroidNativeProject || hasIosNativeProject
|
|
||||||
? EAS_JSON_BARE_DEFAULT
|
|
||||||
: EAS_JSON_MANAGED_DEFAULT;
|
|
||||||
|
|
||||||
await fs.writeFile(easJsonPath, `${JSON.stringify(easJson, null, 2)}\n`);
|
|
||||||
await getVcsClient().trackFileAsync(easJsonPath);
|
|
||||||
Log.withTick(
|
Log.withTick(
|
||||||
`Generated ${chalk.bold('eas.json')}. ${learnMore(
|
`Generated ${chalk.bold('eas.json')}. ${learnMore(
|
||||||
'https://docs.expo.dev/build-reference/eas-json/'
|
'https://docs.expo.dev/build-reference/eas-json/'
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { ExpoConfig } from '@expo/config';
|
import { ExpoConfig } from '@expo/config';
|
||||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||||
import { BuildProfile, EasJson } from '@expo/eas-json';
|
import { BuildProfile, EasJson } from '@expo/eas-json';
|
||||||
|
import { LoggerLevel } from '@expo/logger';
|
||||||
|
import { NodePackageManager } from '@expo/package-manager';
|
||||||
|
|
||||||
|
import { LocalBuildOptions } from './local';
|
||||||
import { Analytics, AnalyticsEventProperties } from '../analytics/AnalyticsManager';
|
import { Analytics, AnalyticsEventProperties } from '../analytics/AnalyticsManager';
|
||||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||||
import { CredentialsContext } from '../credentials/context';
|
import { CredentialsContext } from '../credentials/context';
|
||||||
@@ -11,7 +14,7 @@ import { GradleBuildContext } from '../project/android/gradle';
|
|||||||
import { CustomBuildConfigMetadata } from '../project/customBuildConfig';
|
import { CustomBuildConfigMetadata } from '../project/customBuildConfig';
|
||||||
import { XcodeBuildContext } from '../project/ios/scheme';
|
import { XcodeBuildContext } from '../project/ios/scheme';
|
||||||
import { Actor } from '../user/User';
|
import { Actor } from '../user/User';
|
||||||
import { LocalBuildOptions } from './local';
|
import { Client } from '../vcs/vcs';
|
||||||
|
|
||||||
export type CommonContext<T extends Platform> = Omit<BuildContext<T>, 'android' | 'ios'>;
|
export type CommonContext<T extends Platform> = Omit<BuildContext<T>, 'android' | 'ios'>;
|
||||||
|
|
||||||
@@ -55,4 +58,9 @@ export interface BuildContext<T extends Platform> {
|
|||||||
customBuildConfigMetadata?: CustomBuildConfigMetadata;
|
customBuildConfigMetadata?: CustomBuildConfigMetadata;
|
||||||
android: T extends Platform.ANDROID ? AndroidBuildContext : undefined;
|
android: T extends Platform.ANDROID ? AndroidBuildContext : undefined;
|
||||||
ios: T extends Platform.IOS ? IosBuildContext : undefined;
|
ios: T extends Platform.IOS ? IosBuildContext : undefined;
|
||||||
|
developmentClient: boolean;
|
||||||
|
requiredPackageManager: NodePackageManager['name'] | null;
|
||||||
|
vcsClient: Client;
|
||||||
|
loggerLevel?: LoggerLevel;
|
||||||
|
repack: boolean;
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,17 @@
|
|||||||
import { Platform } from '@expo/eas-build-job';
|
import { Platform } from '@expo/eas-build-job';
|
||||||
import { BuildProfile, EasJson, ResourceClass } from '@expo/eas-json';
|
import { BuildProfile, EasJson, ResourceClass } from '@expo/eas-json';
|
||||||
import JsonFile from '@expo/json-file';
|
import JsonFile from '@expo/json-file';
|
||||||
|
import { LoggerLevel } from '@expo/logger';
|
||||||
|
import { resolvePackageManager } from '@expo/package-manager';
|
||||||
import getenv from 'getenv';
|
import getenv from 'getenv';
|
||||||
import resolveFrom from 'resolve-from';
|
import resolveFrom from 'resolve-from';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
import { createAndroidContextAsync } from './android/build';
|
||||||
|
import { BuildContext, CommonContext } from './context';
|
||||||
|
import { createIosContextAsync } from './ios/build';
|
||||||
|
import { LocalBuildOptions } from './local';
|
||||||
|
import { resolveBuildResourceClassAsync } from './utils/resourceClass';
|
||||||
import { Analytics, AnalyticsEventProperties, BuildEvent } from '../analytics/AnalyticsManager';
|
import { Analytics, AnalyticsEventProperties, BuildEvent } from '../analytics/AnalyticsManager';
|
||||||
import { DynamicConfigContextFn } from '../commandUtils/context/DynamicProjectConfigContextField';
|
import { DynamicConfigContextFn } from '../commandUtils/context/DynamicProjectConfigContextField';
|
||||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||||
@@ -13,11 +20,7 @@ import { CustomBuildConfigMetadata } from '../project/customBuildConfig';
|
|||||||
import { getOwnerAccountForProjectIdAsync } from '../project/projectUtils';
|
import { getOwnerAccountForProjectIdAsync } from '../project/projectUtils';
|
||||||
import { resolveWorkflowAsync } from '../project/workflow';
|
import { resolveWorkflowAsync } from '../project/workflow';
|
||||||
import { Actor } from '../user/User';
|
import { Actor } from '../user/User';
|
||||||
import { createAndroidContextAsync } from './android/build';
|
import { Client } from '../vcs/vcs';
|
||||||
import { BuildContext, CommonContext } from './context';
|
|
||||||
import { createIosContextAsync } from './ios/build';
|
|
||||||
import { LocalBuildOptions } from './local';
|
|
||||||
import { resolveBuildResourceClassAsync } from './utils/resourceClass';
|
|
||||||
|
|
||||||
export async function createBuildContextAsync<T extends Platform>({
|
export async function createBuildContextAsync<T extends Platform>({
|
||||||
buildProfileName,
|
buildProfileName,
|
||||||
@@ -34,8 +37,12 @@ export async function createBuildContextAsync<T extends Platform>({
|
|||||||
actor,
|
actor,
|
||||||
graphqlClient,
|
graphqlClient,
|
||||||
analytics,
|
analytics,
|
||||||
|
vcsClient,
|
||||||
getDynamicPrivateProjectConfigAsync,
|
getDynamicPrivateProjectConfigAsync,
|
||||||
customBuildConfigMetadata,
|
customBuildConfigMetadata,
|
||||||
|
buildLoggerLevel,
|
||||||
|
freezeCredentials,
|
||||||
|
repack,
|
||||||
}: {
|
}: {
|
||||||
buildProfileName: string;
|
buildProfileName: string;
|
||||||
buildProfile: BuildProfile<T>;
|
buildProfile: BuildProfile<T>;
|
||||||
@@ -51,15 +58,27 @@ export async function createBuildContextAsync<T extends Platform>({
|
|||||||
actor: Actor;
|
actor: Actor;
|
||||||
graphqlClient: ExpoGraphqlClient;
|
graphqlClient: ExpoGraphqlClient;
|
||||||
analytics: Analytics;
|
analytics: Analytics;
|
||||||
|
vcsClient: Client;
|
||||||
getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn;
|
getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn;
|
||||||
customBuildConfigMetadata?: CustomBuildConfigMetadata;
|
customBuildConfigMetadata?: CustomBuildConfigMetadata;
|
||||||
|
buildLoggerLevel?: LoggerLevel;
|
||||||
|
freezeCredentials: boolean;
|
||||||
|
repack: boolean;
|
||||||
}): Promise<BuildContext<T>> {
|
}): Promise<BuildContext<T>> {
|
||||||
const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({ env: buildProfile.env });
|
const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({ env: buildProfile.env });
|
||||||
const projectName = exp.slug;
|
const projectName = exp.slug;
|
||||||
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
|
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
|
||||||
const workflow = await resolveWorkflowAsync(projectDir, platform);
|
const workflow = await resolveWorkflowAsync(projectDir, platform, vcsClient);
|
||||||
const accountId = account.id;
|
const accountId = account.id;
|
||||||
const runFromCI = getenv.boolish('CI', false);
|
const runFromCI = getenv.boolish('CI', false);
|
||||||
|
const developmentClient =
|
||||||
|
buildProfile.developmentClient ??
|
||||||
|
(platform === Platform.ANDROID
|
||||||
|
? (buildProfile as BuildProfile<Platform.ANDROID>)?.gradleCommand === ':app:assembleDebug'
|
||||||
|
: (buildProfile as BuildProfile<Platform.IOS>)?.buildConfiguration === 'Debug') ??
|
||||||
|
false;
|
||||||
|
|
||||||
|
const requiredPackageManager = resolvePackageManager(projectDir);
|
||||||
|
|
||||||
const credentialsCtx = new CredentialsContext({
|
const credentialsCtx = new CredentialsContext({
|
||||||
projectInfo: { exp, projectId },
|
projectInfo: { exp, projectId },
|
||||||
@@ -70,6 +89,8 @@ export async function createBuildContextAsync<T extends Platform>({
|
|||||||
analytics,
|
analytics,
|
||||||
env: buildProfile.env,
|
env: buildProfile.env,
|
||||||
easJsonCliConfig,
|
easJsonCliConfig,
|
||||||
|
vcsClient,
|
||||||
|
freezeCredentials,
|
||||||
});
|
});
|
||||||
|
|
||||||
const devClientProperties = getDevClientEventProperties({
|
const devClientProperties = getDevClientEventProperties({
|
||||||
@@ -116,10 +137,15 @@ export async function createBuildContextAsync<T extends Platform>({
|
|||||||
user: actor,
|
user: actor,
|
||||||
graphqlClient,
|
graphqlClient,
|
||||||
analytics,
|
analytics,
|
||||||
|
vcsClient,
|
||||||
workflow,
|
workflow,
|
||||||
message,
|
message,
|
||||||
runFromCI,
|
runFromCI,
|
||||||
customBuildConfigMetadata,
|
customBuildConfigMetadata,
|
||||||
|
developmentClient,
|
||||||
|
requiredPackageManager,
|
||||||
|
loggerLevel: buildLoggerLevel,
|
||||||
|
repack,
|
||||||
};
|
};
|
||||||
if (platform === Platform.ANDROID) {
|
if (platform === Platform.ANDROID) {
|
||||||
const common = commonContext as CommonContext<Platform.ANDROID>;
|
const common = commonContext as CommonContext<Platform.ANDROID>;
|
||||||
|
|||||||
@@ -8,8 +8,14 @@ export class EasBuildFreeTierDisabledIOSError extends EasCommandError {}
|
|||||||
|
|
||||||
export class EasBuildFreeTierDisabledAndroidError extends EasCommandError {}
|
export class EasBuildFreeTierDisabledAndroidError extends EasCommandError {}
|
||||||
|
|
||||||
|
export class EasBuildFreeTierLimitExceededError extends EasCommandError {}
|
||||||
|
|
||||||
|
export class EasBuildFreeTierIosLimitExceededError extends EasCommandError {}
|
||||||
|
|
||||||
export class EasBuildResourceClassNotAvailableInFreeTierError extends EasCommandError {}
|
export class EasBuildResourceClassNotAvailableInFreeTierError extends EasCommandError {}
|
||||||
|
|
||||||
|
export class EasBuildLegacyResourceClassNotAvailableError extends EasCommandError {}
|
||||||
|
|
||||||
export class RequestValidationError extends EasCommandError {}
|
export class RequestValidationError extends EasCommandError {}
|
||||||
|
|
||||||
export class EasBuildDownForMaintenanceError extends EasCommandError {}
|
export class EasBuildDownForMaintenanceError extends EasCommandError {}
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
import {
|
import {
|
||||||
ArchiveSource,
|
ArchiveSource,
|
||||||
ArchiveSourceType,
|
ArchiveSourceType,
|
||||||
|
BuildMode,
|
||||||
BuildTrigger,
|
BuildTrigger,
|
||||||
Metadata,
|
Metadata,
|
||||||
Workflow,
|
Workflow,
|
||||||
} from '@expo/eas-build-job';
|
} from '@expo/eas-build-job';
|
||||||
|
import { LoggerLevel } from '@expo/logger';
|
||||||
|
import assert from 'assert';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BuildCredentialsSource,
|
BuildCredentialsSource,
|
||||||
BuildIosEnterpriseProvisioning,
|
BuildIosEnterpriseProvisioning,
|
||||||
BuildMetadataInput,
|
BuildMetadataInput,
|
||||||
BuildMode,
|
|
||||||
BuildWorkflow,
|
BuildWorkflow,
|
||||||
DistributionType,
|
DistributionType,
|
||||||
|
BuildMode as GraphQLBuildMode,
|
||||||
BuildTrigger as GraphQLBuildTrigger,
|
BuildTrigger as GraphQLBuildTrigger,
|
||||||
ProjectArchiveSourceInput,
|
ProjectArchiveSourceInput,
|
||||||
ProjectArchiveSourceType,
|
ProjectArchiveSourceType,
|
||||||
|
WorkerLoggerLevel,
|
||||||
} from '../graphql/generated';
|
} from '../graphql/generated';
|
||||||
|
|
||||||
export function transformProjectArchive(archiveSource: ArchiveSource): ProjectArchiveSourceInput {
|
export function transformProjectArchive(archiveSource: ArchiveSource): ProjectArchiveSourceInput {
|
||||||
if (archiveSource.type === ArchiveSourceType.S3) {
|
if (archiveSource.type === ArchiveSourceType.GCS) {
|
||||||
return {
|
|
||||||
type: ProjectArchiveSourceType.S3,
|
|
||||||
bucketKey: archiveSource.bucketKey,
|
|
||||||
};
|
|
||||||
} else if (archiveSource.type === ArchiveSourceType.GCS) {
|
|
||||||
return {
|
return {
|
||||||
type: ProjectArchiveSourceType.Gcs,
|
type: ProjectArchiveSourceType.Gcs,
|
||||||
bucketKey: archiveSource.bucketKey,
|
bucketKey: archiveSource.bucketKey,
|
||||||
|
metadataLocation: archiveSource.metadataLocation,
|
||||||
};
|
};
|
||||||
} else if (archiveSource.type === ArchiveSourceType.URL) {
|
} else if (archiveSource.type === ArchiveSourceType.URL) {
|
||||||
return {
|
return {
|
||||||
@@ -42,7 +42,6 @@ export function transformProjectArchive(archiveSource: ArchiveSource): ProjectAr
|
|||||||
export function transformMetadata(metadata: Metadata): BuildMetadataInput {
|
export function transformMetadata(metadata: Metadata): BuildMetadataInput {
|
||||||
return {
|
return {
|
||||||
...metadata,
|
...metadata,
|
||||||
buildMode: metadata.buildMode && transformBuildMode(metadata.buildMode),
|
|
||||||
credentialsSource:
|
credentialsSource:
|
||||||
metadata.credentialsSource && transformCredentialsSource(metadata.credentialsSource),
|
metadata.credentialsSource && transformCredentialsSource(metadata.credentialsSource),
|
||||||
distribution: metadata.distribution && transformDistribution(metadata.distribution),
|
distribution: metadata.distribution && transformDistribution(metadata.distribution),
|
||||||
@@ -91,17 +90,17 @@ export function transformIosEnterpriseProvisioning(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check what in metadata
|
const buildModeToGraphQLBuildMode: Record<BuildMode, GraphQLBuildMode> = {
|
||||||
export function transformBuildMode(buildMode: string): BuildMode {
|
[BuildMode.BUILD]: GraphQLBuildMode.Build,
|
||||||
if (buildMode === 'build') {
|
[BuildMode.CUSTOM]: GraphQLBuildMode.Custom,
|
||||||
return BuildMode.Build;
|
[BuildMode.RESIGN]: GraphQLBuildMode.Resign,
|
||||||
} else if (buildMode === 'resign') {
|
[BuildMode.REPACK]: GraphQLBuildMode.Repack,
|
||||||
return BuildMode.Resign;
|
};
|
||||||
} else if (buildMode === 'custom') {
|
|
||||||
return BuildMode.Custom;
|
export function transformBuildMode(buildMode: BuildMode): GraphQLBuildMode {
|
||||||
} else {
|
const graphQLBuildMode = buildModeToGraphQLBuildMode[buildMode];
|
||||||
throw new Error(`Unsupported build mode: ${buildMode}`);
|
assert(graphQLBuildMode, `Unsupported build mode: ${buildMode}`);
|
||||||
}
|
return graphQLBuildMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function transformBuildTrigger(buildTrigger: BuildTrigger): GraphQLBuildTrigger {
|
export function transformBuildTrigger(buildTrigger: BuildTrigger): GraphQLBuildTrigger {
|
||||||
@@ -112,3 +111,12 @@ export function transformBuildTrigger(buildTrigger: BuildTrigger): GraphQLBuildT
|
|||||||
}
|
}
|
||||||
throw new Error('Unknown build trigger');
|
throw new Error('Unknown build trigger');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const loggerLevelToGraphQLWorkerLoggerLevel: Record<LoggerLevel, WorkerLoggerLevel> = {
|
||||||
|
[LoggerLevel.TRACE]: WorkerLoggerLevel.Trace,
|
||||||
|
[LoggerLevel.DEBUG]: WorkerLoggerLevel.Debug,
|
||||||
|
[LoggerLevel.INFO]: WorkerLoggerLevel.Info,
|
||||||
|
[LoggerLevel.WARN]: WorkerLoggerLevel.Warn,
|
||||||
|
[LoggerLevel.ERROR]: WorkerLoggerLevel.Error,
|
||||||
|
[LoggerLevel.FATAL]: WorkerLoggerLevel.Fatal,
|
||||||
|
};
|
||||||
@@ -6,6 +6,7 @@ import os from 'os';
|
|||||||
import type { XCBuildConfiguration } from 'xcode';
|
import type { XCBuildConfiguration } from 'xcode';
|
||||||
|
|
||||||
import { readPlistAsync } from '../../../utils/plist';
|
import { readPlistAsync } from '../../../utils/plist';
|
||||||
|
import { resolveVcsClient } from '../../../vcs';
|
||||||
import {
|
import {
|
||||||
BumpStrategy,
|
BumpStrategy,
|
||||||
bumpVersionAsync,
|
bumpVersionAsync,
|
||||||
@@ -18,6 +19,8 @@ import {
|
|||||||
|
|
||||||
jest.mock('fs');
|
jest.mock('fs');
|
||||||
|
|
||||||
|
const vcsClient = resolveVcsClient();
|
||||||
|
|
||||||
const getXCBuildConfigurationFromPbxproj = jest.spyOn(
|
const getXCBuildConfigurationFromPbxproj = jest.spyOn(
|
||||||
IOSConfig.Target,
|
IOSConfig.Target,
|
||||||
'getXCBuildConfigurationFromPbxproj'
|
'getXCBuildConfigurationFromPbxproj'
|
||||||
@@ -59,7 +62,7 @@ describe(evaluateTemplateString, () => {
|
|||||||
// bare workflow
|
// bare workflow
|
||||||
describe(bumpVersionAsync, () => {
|
describe(bumpVersionAsync, () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
getPbxproj.mockImplementation(() => ({} as any));
|
getPbxproj.mockImplementation((): any => {});
|
||||||
});
|
});
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
getXCBuildConfigurationFromPbxproj.mockReset();
|
getXCBuildConfigurationFromPbxproj.mockReset();
|
||||||
@@ -73,7 +76,7 @@ describe(bumpVersionAsync, () => {
|
|||||||
buildSettings: {
|
buildSettings: {
|
||||||
INFOPLIST_FILE: 'myproject/Info.plist',
|
INFOPLIST_FILE: 'myproject/Info.plist',
|
||||||
},
|
},
|
||||||
} as XCBuildConfiguration)
|
}) as XCBuildConfiguration
|
||||||
);
|
);
|
||||||
await bumpVersionAsync({
|
await bumpVersionAsync({
|
||||||
bumpStrategy: BumpStrategy.BUILD_NUMBER,
|
bumpStrategy: BumpStrategy.BUILD_NUMBER,
|
||||||
@@ -109,7 +112,7 @@ describe(bumpVersionAsync, () => {
|
|||||||
buildSettings: {
|
buildSettings: {
|
||||||
INFOPLIST_FILE: '$(SRCROOT)/myproject/Info2.plist',
|
INFOPLIST_FILE: '$(SRCROOT)/myproject/Info2.plist',
|
||||||
},
|
},
|
||||||
} as XCBuildConfiguration)
|
}) as XCBuildConfiguration
|
||||||
);
|
);
|
||||||
await bumpVersionAsync({
|
await bumpVersionAsync({
|
||||||
bumpStrategy: BumpStrategy.BUILD_NUMBER,
|
bumpStrategy: BumpStrategy.BUILD_NUMBER,
|
||||||
@@ -144,7 +147,7 @@ describe(bumpVersionAsync, () => {
|
|||||||
buildSettings: {
|
buildSettings: {
|
||||||
INFOPLIST_FILE: 'myproject/Info.plist',
|
INFOPLIST_FILE: 'myproject/Info.plist',
|
||||||
},
|
},
|
||||||
} as XCBuildConfiguration)
|
}) as XCBuildConfiguration
|
||||||
);
|
);
|
||||||
await bumpVersionAsync({
|
await bumpVersionAsync({
|
||||||
bumpStrategy: BumpStrategy.APP_VERSION,
|
bumpStrategy: BumpStrategy.APP_VERSION,
|
||||||
@@ -179,7 +182,7 @@ describe(bumpVersionAsync, () => {
|
|||||||
buildSettings: {
|
buildSettings: {
|
||||||
INFOPLIST_FILE: 'myproject/Info.plist',
|
INFOPLIST_FILE: 'myproject/Info.plist',
|
||||||
},
|
},
|
||||||
} as XCBuildConfiguration)
|
}) as XCBuildConfiguration
|
||||||
);
|
);
|
||||||
await bumpVersionAsync({
|
await bumpVersionAsync({
|
||||||
bumpStrategy: BumpStrategy.NOOP,
|
bumpStrategy: BumpStrategy.NOOP,
|
||||||
@@ -262,7 +265,7 @@ describe(readBuildNumberAsync, () => {
|
|||||||
describe('bare project', () => {
|
describe('bare project', () => {
|
||||||
it('reads the build number from native code', async () => {
|
it('reads the build number from native code', async () => {
|
||||||
const exp = initBareWorkflowProject();
|
const exp = initBareWorkflowProject();
|
||||||
const buildNumber = await readBuildNumberAsync('/app', exp, {});
|
const buildNumber = await readBuildNumberAsync('/app', exp, {}, vcsClient);
|
||||||
expect(buildNumber).toBe('1');
|
expect(buildNumber).toBe('1');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -270,7 +273,7 @@ describe(readBuildNumberAsync, () => {
|
|||||||
describe('managed project', () => {
|
describe('managed project', () => {
|
||||||
it('reads the build number from expo config', async () => {
|
it('reads the build number from expo config', async () => {
|
||||||
const exp = initManagedProject();
|
const exp = initManagedProject();
|
||||||
const buildNumber = await readBuildNumberAsync('/app', exp, {});
|
const buildNumber = await readBuildNumberAsync('/app', exp, {}, vcsClient);
|
||||||
expect(buildNumber).toBe('1');
|
expect(buildNumber).toBe('1');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -280,16 +283,21 @@ describe(readShortVersionAsync, () => {
|
|||||||
describe('bare project', () => {
|
describe('bare project', () => {
|
||||||
it('reads the short version from native code', async () => {
|
it('reads the short version from native code', async () => {
|
||||||
const exp = initBareWorkflowProject();
|
const exp = initBareWorkflowProject();
|
||||||
const appVersion = await readShortVersionAsync('/app', exp, {});
|
const appVersion = await readShortVersionAsync('/app', exp, {}, vcsClient);
|
||||||
expect(appVersion).toBe('1.0.0');
|
expect(appVersion).toBe('1.0.0');
|
||||||
});
|
});
|
||||||
it('evaluates interpolated build number', async () => {
|
it('evaluates interpolated build number', async () => {
|
||||||
const exp = initBareWorkflowProject({
|
const exp = initBareWorkflowProject({
|
||||||
appVersion: '$(CURRENT_PROJECT_VERSION)',
|
appVersion: '$(CURRENT_PROJECT_VERSION)',
|
||||||
});
|
});
|
||||||
const buildNumber = await readShortVersionAsync('/app', exp, {
|
const buildNumber = await readShortVersionAsync(
|
||||||
|
'/app',
|
||||||
|
exp,
|
||||||
|
{
|
||||||
CURRENT_PROJECT_VERSION: '1.0.0',
|
CURRENT_PROJECT_VERSION: '1.0.0',
|
||||||
});
|
},
|
||||||
|
vcsClient
|
||||||
|
);
|
||||||
expect(buildNumber).toBe('1.0.0');
|
expect(buildNumber).toBe('1.0.0');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -299,9 +307,14 @@ describe(readShortVersionAsync, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
readShortVersionAsync('/app', exp, {
|
readShortVersionAsync(
|
||||||
|
'/app',
|
||||||
|
exp,
|
||||||
|
{
|
||||||
CURRENT_PROJECT_VERSION: '0.0.7.1.028',
|
CURRENT_PROJECT_VERSION: '0.0.7.1.028',
|
||||||
})
|
},
|
||||||
|
vcsClient
|
||||||
|
)
|
||||||
).rejects.toThrowError(
|
).rejects.toThrowError(
|
||||||
'CFBundleShortVersionString (version field in app.json/app.config.js) must be a period-separated list of three non-negative integers. Current value: 0.0.7.1.028'
|
'CFBundleShortVersionString (version field in app.json/app.config.js) must be a period-separated list of three non-negative integers. Current value: 0.0.7.1.028'
|
||||||
);
|
);
|
||||||
@@ -311,7 +324,7 @@ describe(readShortVersionAsync, () => {
|
|||||||
describe('managed project', () => {
|
describe('managed project', () => {
|
||||||
it('reads the version from app config', async () => {
|
it('reads the version from app config', async () => {
|
||||||
const exp = initBareWorkflowProject();
|
const exp = initBareWorkflowProject();
|
||||||
const appVersion = await readShortVersionAsync('/app', exp, {});
|
const appVersion = await readShortVersionAsync('/app', exp, {}, vcsClient);
|
||||||
expect(appVersion).toBe('1.0.0');
|
expect(appVersion).toBe('1.0.0');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import { Ios, Job, Metadata, Platform, Workflow } from '@expo/eas-build-job';
|
import { Ios, Metadata, Platform, Workflow } from '@expo/eas-build-job';
|
||||||
import { AppVersionSource } from '@expo/eas-json';
|
import { AppVersionSource } from '@expo/eas-json';
|
||||||
|
|
||||||
|
import { ensureIosCredentialsAsync } from './credentials';
|
||||||
|
import { transformJob } from './graphql';
|
||||||
|
import { prepareJobAsync } from './prepareJob';
|
||||||
|
import { syncProjectConfigurationAsync } from './syncProjectConfiguration';
|
||||||
|
import { resolveRemoteBuildNumberAsync } from './version';
|
||||||
import { IosCredentials } from '../../credentials/ios/types';
|
import { IosCredentials } from '../../credentials/ios/types';
|
||||||
import { BuildParamsInput } from '../../graphql/generated';
|
import { BuildParamsInput } from '../../graphql/generated';
|
||||||
import { BuildMutation, BuildResult } from '../../graphql/mutations/BuildMutation';
|
import { BuildMutation, BuildResult } from '../../graphql/mutations/BuildMutation';
|
||||||
@@ -15,11 +20,6 @@ import {
|
|||||||
checkNodeEnvVariable,
|
checkNodeEnvVariable,
|
||||||
validatePNGsForManagedProjectAsync,
|
validatePNGsForManagedProjectAsync,
|
||||||
} from '../validate';
|
} from '../validate';
|
||||||
import { ensureIosCredentialsAsync } from './credentials';
|
|
||||||
import { transformJob } from './graphql';
|
|
||||||
import { prepareJobAsync } from './prepareJob';
|
|
||||||
import { syncProjectConfigurationAsync } from './syncProjectConfiguration';
|
|
||||||
import { resolveRemoteBuildNumberAsync } from './version';
|
|
||||||
|
|
||||||
export async function createIosContextAsync(
|
export async function createIosContextAsync(
|
||||||
ctx: CommonContext<Platform.IOS>
|
ctx: CommonContext<Platform.IOS>
|
||||||
@@ -39,6 +39,7 @@ export async function createIosContextAsync(
|
|||||||
projectDir: ctx.projectDir,
|
projectDir: ctx.projectDir,
|
||||||
nonInteractive: ctx.nonInteractive,
|
nonInteractive: ctx.nonInteractive,
|
||||||
exp: ctx.exp,
|
exp: ctx.exp,
|
||||||
|
vcsClient: ctx.vcsClient,
|
||||||
},
|
},
|
||||||
buildProfile
|
buildProfile
|
||||||
);
|
);
|
||||||
@@ -47,6 +48,7 @@ export async function createIosContextAsync(
|
|||||||
exp: ctx.exp,
|
exp: ctx.exp,
|
||||||
xcodeBuildContext,
|
xcodeBuildContext,
|
||||||
env: buildProfile.env,
|
env: buildProfile.env,
|
||||||
|
vcsClient: ctx.vcsClient,
|
||||||
});
|
});
|
||||||
const applicationTarget = findApplicationTarget(targets);
|
const applicationTarget = findApplicationTarget(targets);
|
||||||
const buildNumberOverride =
|
const buildNumberOverride =
|
||||||
@@ -57,6 +59,7 @@ export async function createIosContextAsync(
|
|||||||
exp: ctx.exp,
|
exp: ctx.exp,
|
||||||
applicationTarget,
|
applicationTarget,
|
||||||
buildProfile,
|
buildProfile,
|
||||||
|
vcsClient: ctx.vcsClient,
|
||||||
})
|
})
|
||||||
: undefined;
|
: undefined;
|
||||||
return {
|
return {
|
||||||
@@ -77,7 +80,7 @@ export async function prepareIosBuildAsync(
|
|||||||
return ensureIosCredentialsAsync(ctx, ctx.ios.targets);
|
return ensureIosCredentialsAsync(ctx, ctx.ios.targets);
|
||||||
},
|
},
|
||||||
syncProjectConfigurationAsync: async () => {
|
syncProjectConfigurationAsync: async () => {
|
||||||
await syncProjectConfigurationAsync(ctx.graphqlClient, {
|
await syncProjectConfigurationAsync({
|
||||||
projectDir: ctx.projectDir,
|
projectDir: ctx.projectDir,
|
||||||
exp: ctx.exp,
|
exp: ctx.exp,
|
||||||
targets: ctx.ios.targets,
|
targets: ctx.ios.targets,
|
||||||
@@ -85,13 +88,14 @@ export async function prepareIosBuildAsync(
|
|||||||
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
|
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
|
||||||
? false
|
? false
|
||||||
: ctx.buildProfile.autoIncrement,
|
: ctx.buildProfile.autoIncrement,
|
||||||
projectId: ctx.projectId,
|
vcsClient: ctx.vcsClient,
|
||||||
|
env: ctx.buildProfile.env,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
prepareJobAsync: async (
|
prepareJobAsync: async (
|
||||||
ctx: BuildContext<Platform.IOS>,
|
ctx: BuildContext<Platform.IOS>,
|
||||||
jobData: JobData<IosCredentials>
|
jobData: JobData<IosCredentials>
|
||||||
): Promise<Job> => {
|
): Promise<Ios.Job> => {
|
||||||
return await prepareJobAsync(ctx, {
|
return await prepareJobAsync(ctx, {
|
||||||
...jobData,
|
...jobData,
|
||||||
buildScheme: ctx.ios.xcodeBuildContext.buildScheme,
|
buildScheme: ctx.ios.xcodeBuildContext.buildScheme,
|
||||||
|
|||||||
@@ -55,6 +55,5 @@ export async function ensureIosCredentialsForBuildResignAsync(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function shouldProvideCredentials(buildCtx: BuildContext<Platform.IOS>): boolean {
|
function shouldProvideCredentials(buildCtx: BuildContext<Platform.IOS>): boolean {
|
||||||
const isCustomBuild = buildCtx.buildProfile.config !== undefined;
|
return !buildCtx.buildProfile.simulator && !buildCtx.buildProfile.withoutCredentials;
|
||||||
return !buildCtx.buildProfile.simulator && !isCustomBuild;
|
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@ import nullthrows from 'nullthrows';
|
|||||||
|
|
||||||
import { IosJobInput, IosJobSecretsInput } from '../../graphql/generated';
|
import { IosJobInput, IosJobSecretsInput } from '../../graphql/generated';
|
||||||
import {
|
import {
|
||||||
|
loggerLevelToGraphQLWorkerLoggerLevel,
|
||||||
transformBuildMode,
|
transformBuildMode,
|
||||||
transformBuildTrigger,
|
transformBuildTrigger,
|
||||||
transformProjectArchive,
|
transformProjectArchive,
|
||||||
@@ -15,7 +16,6 @@ export function transformJob(job: Ios.Job): IosJobInput {
|
|||||||
triggeredBy: transformBuildTrigger(job.triggeredBy),
|
triggeredBy: transformBuildTrigger(job.triggeredBy),
|
||||||
projectArchive: transformProjectArchive(job.projectArchive),
|
projectArchive: transformProjectArchive(job.projectArchive),
|
||||||
projectRootDirectory: nullthrows(job.projectRootDirectory),
|
projectRootDirectory: nullthrows(job.projectRootDirectory),
|
||||||
releaseChannel: job.releaseChannel,
|
|
||||||
updates: job.updates,
|
updates: job.updates,
|
||||||
secrets: job.secrets ? transformIosSecrets(job.secrets) : undefined,
|
secrets: job.secrets ? transformIosSecrets(job.secrets) : undefined,
|
||||||
builderEnvironment: job.builderEnvironment,
|
builderEnvironment: job.builderEnvironment,
|
||||||
@@ -31,6 +31,9 @@ export function transformJob(job: Ios.Job): IosJobInput {
|
|||||||
experimental: job.experimental,
|
experimental: job.experimental,
|
||||||
mode: transformBuildMode(job.mode),
|
mode: transformBuildMode(job.mode),
|
||||||
customBuildConfig: job.customBuildConfig,
|
customBuildConfig: job.customBuildConfig,
|
||||||
|
loggerLevel: job.loggerLevel
|
||||||
|
? loggerLevelToGraphQLWorkerLoggerLevel[job.loggerLevel]
|
||||||
|
: undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,8 @@ import {
|
|||||||
BuildMode,
|
BuildMode,
|
||||||
BuildTrigger,
|
BuildTrigger,
|
||||||
Ios,
|
Ios,
|
||||||
Job,
|
|
||||||
Platform,
|
Platform,
|
||||||
sanitizeJob,
|
sanitizeBuildJob,
|
||||||
} from '@expo/eas-build-job';
|
} from '@expo/eas-build-job';
|
||||||
import { BuildProfile } from '@expo/eas-json';
|
import { BuildProfile } from '@expo/eas-json';
|
||||||
import nullthrows from 'nullthrows';
|
import nullthrows from 'nullthrows';
|
||||||
@@ -14,9 +13,8 @@ import slash from 'slash';
|
|||||||
|
|
||||||
import { IosCredentials, TargetCredentials } from '../../credentials/ios/types';
|
import { IosCredentials, TargetCredentials } from '../../credentials/ios/types';
|
||||||
import { IosJobSecretsInput } from '../../graphql/generated';
|
import { IosJobSecretsInput } from '../../graphql/generated';
|
||||||
import { getCustomBuildConfigPath } from '../../project/customBuildConfig';
|
import { getCustomBuildConfigPathForJob } from '../../project/customBuildConfig';
|
||||||
import { getUsername } from '../../project/projectUtils';
|
import { getUsername } from '../../project/projectUtils';
|
||||||
import { getVcsClient } from '../../vcs';
|
|
||||||
import { BuildContext } from '../context';
|
import { BuildContext } from '../context';
|
||||||
|
|
||||||
interface JobData {
|
interface JobData {
|
||||||
@@ -33,11 +31,11 @@ const cacheDefaults = {
|
|||||||
export async function prepareJobAsync(
|
export async function prepareJobAsync(
|
||||||
ctx: BuildContext<Platform.IOS>,
|
ctx: BuildContext<Platform.IOS>,
|
||||||
jobData: JobData
|
jobData: JobData
|
||||||
): Promise<Job> {
|
): Promise<Ios.Job> {
|
||||||
const username = getUsername(ctx.exp, ctx.user);
|
const username = getUsername(ctx.exp, ctx.user);
|
||||||
const buildProfile: BuildProfile<Platform.IOS> = ctx.buildProfile;
|
const buildProfile: BuildProfile<Platform.IOS> = ctx.buildProfile;
|
||||||
const projectRootDirectory =
|
const projectRootDirectory =
|
||||||
slash(path.relative(await getVcsClient().getRootPathAsync(), ctx.projectDir)) || '.';
|
slash(path.relative(await ctx.vcsClient.getRootPathAsync(), ctx.projectDir)) || '.';
|
||||||
const buildCredentials: Ios.BuildSecrets['buildCredentials'] = {};
|
const buildCredentials: Ios.BuildSecrets['buildCredentials'] = {};
|
||||||
if (jobData.credentials) {
|
if (jobData.credentials) {
|
||||||
const targetNames = Object.keys(jobData.credentials);
|
const targetNames = Object.keys(jobData.credentials);
|
||||||
@@ -47,9 +45,18 @@ export async function prepareJobAsync(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const maybeCustomBuildConfigPath = buildProfile.config
|
const maybeCustomBuildConfigPath = buildProfile.config
|
||||||
? getCustomBuildConfigPath(buildProfile.config)
|
? getCustomBuildConfigPathForJob(buildProfile.config)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
let buildMode;
|
||||||
|
if (ctx.repack) {
|
||||||
|
buildMode = BuildMode.REPACK;
|
||||||
|
} else if (buildProfile.config) {
|
||||||
|
buildMode = BuildMode.CUSTOM;
|
||||||
|
} else {
|
||||||
|
buildMode = BuildMode.BUILD;
|
||||||
|
}
|
||||||
|
|
||||||
const job: Ios.Job = {
|
const job: Ios.Job = {
|
||||||
type: ctx.workflow,
|
type: ctx.workflow,
|
||||||
platform: Platform.IOS,
|
platform: Platform.IOS,
|
||||||
@@ -58,11 +65,12 @@ export async function prepareJobAsync(
|
|||||||
builderEnvironment: {
|
builderEnvironment: {
|
||||||
image: buildProfile.image,
|
image: buildProfile.image,
|
||||||
node: buildProfile.node,
|
node: buildProfile.node,
|
||||||
|
pnpm: buildProfile.pnpm,
|
||||||
|
bun: buildProfile.bun,
|
||||||
yarn: buildProfile.yarn,
|
yarn: buildProfile.yarn,
|
||||||
bundler: buildProfile.bundler,
|
bundler: buildProfile.bundler,
|
||||||
cocoapods: buildProfile.cocoapods,
|
cocoapods: buildProfile.cocoapods,
|
||||||
fastlane: buildProfile.fastlane,
|
fastlane: buildProfile.fastlane,
|
||||||
expoCli: buildProfile.expoCli,
|
|
||||||
env: buildProfile.env,
|
env: buildProfile.env,
|
||||||
},
|
},
|
||||||
cache: {
|
cache: {
|
||||||
@@ -73,7 +81,6 @@ export async function prepareJobAsync(
|
|||||||
secrets: {
|
secrets: {
|
||||||
buildCredentials,
|
buildCredentials,
|
||||||
},
|
},
|
||||||
releaseChannel: buildProfile.releaseChannel,
|
|
||||||
updates: { channel: buildProfile.channel },
|
updates: { channel: buildProfile.channel },
|
||||||
developmentClient: buildProfile.developmentClient,
|
developmentClient: buildProfile.developmentClient,
|
||||||
simulator: buildProfile.simulator,
|
simulator: buildProfile.simulator,
|
||||||
@@ -90,15 +97,21 @@ export async function prepareJobAsync(
|
|||||||
experimental: {
|
experimental: {
|
||||||
prebuildCommand: buildProfile.prebuildCommand,
|
prebuildCommand: buildProfile.prebuildCommand,
|
||||||
},
|
},
|
||||||
mode: buildProfile.config ? BuildMode.CUSTOM : BuildMode.BUILD,
|
mode: buildMode,
|
||||||
triggeredBy: BuildTrigger.EAS_CLI,
|
triggeredBy: BuildTrigger.EAS_CLI,
|
||||||
...(maybeCustomBuildConfigPath && {
|
...(maybeCustomBuildConfigPath && {
|
||||||
customBuildConfig: {
|
customBuildConfig: {
|
||||||
path: maybeCustomBuildConfigPath,
|
path: maybeCustomBuildConfigPath,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
...(ctx.repack && {
|
||||||
|
customBuildConfig: {
|
||||||
|
path: '__eas/repack.yml',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
loggerLevel: ctx.loggerLevel,
|
||||||
};
|
};
|
||||||
return sanitizeJob(job);
|
return sanitizeBuildJob(job) as Ios.Job;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function prepareCredentialsToResign(credentials: IosCredentials): IosJobSecretsInput {
|
export function prepareCredentialsToResign(credentials: IosCredentials): IosJobSecretsInput {
|
||||||
|
|||||||
@@ -1,36 +1,35 @@
|
|||||||
import { ExpoConfig } from '@expo/config';
|
import { ExpoConfig } from '@expo/config';
|
||||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
import { Env, Platform, Workflow } from '@expo/eas-build-job';
|
||||||
import { IosVersionAutoIncrement } from '@expo/eas-json';
|
import { IosVersionAutoIncrement } from '@expo/eas-json';
|
||||||
|
|
||||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version';
|
||||||
import { Target } from '../../credentials/ios/types';
|
import { Target } from '../../credentials/ios/types';
|
||||||
import { isExpoUpdatesInstalled } from '../../project/projectUtils';
|
import { isExpoUpdatesInstalled } from '../../project/projectUtils';
|
||||||
import { resolveWorkflowAsync } from '../../project/workflow';
|
import { resolveWorkflowAsync } from '../../project/workflow';
|
||||||
import { syncUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule';
|
import { syncUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule';
|
||||||
import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version';
|
import { Client } from '../../vcs/vcs';
|
||||||
|
|
||||||
export async function syncProjectConfigurationAsync(
|
export async function syncProjectConfigurationAsync({
|
||||||
graphqlClient: ExpoGraphqlClient,
|
|
||||||
{
|
|
||||||
projectDir,
|
projectDir,
|
||||||
exp,
|
exp,
|
||||||
targets,
|
targets,
|
||||||
localAutoIncrement,
|
localAutoIncrement,
|
||||||
projectId,
|
vcsClient,
|
||||||
|
env,
|
||||||
}: {
|
}: {
|
||||||
projectDir: string;
|
projectDir: string;
|
||||||
exp: ExpoConfig;
|
exp: ExpoConfig;
|
||||||
targets: Target[];
|
targets: Target[];
|
||||||
localAutoIncrement?: IosVersionAutoIncrement;
|
localAutoIncrement?: IosVersionAutoIncrement;
|
||||||
projectId: string;
|
vcsClient: Client;
|
||||||
}
|
env: Env | undefined;
|
||||||
): Promise<void> {
|
}): Promise<void> {
|
||||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
|
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient);
|
||||||
const versionBumpStrategy = resolveVersionBumpStrategy(localAutoIncrement ?? false);
|
const versionBumpStrategy = resolveVersionBumpStrategy(localAutoIncrement ?? false);
|
||||||
|
|
||||||
if (workflow === Workflow.GENERIC) {
|
if (workflow === Workflow.GENERIC) {
|
||||||
if (isExpoUpdatesInstalled(projectDir)) {
|
if (isExpoUpdatesInstalled(projectDir)) {
|
||||||
await syncUpdatesConfigurationAsync(graphqlClient, projectDir, exp, projectId);
|
await syncUpdatesConfigurationAsync({ vcsClient, projectDir, exp, workflow, env });
|
||||||
}
|
}
|
||||||
await bumpVersionAsync({ projectDir, exp, bumpStrategy: versionBumpStrategy, targets });
|
await bumpVersionAsync({ projectDir, exp, bumpStrategy: versionBumpStrategy, targets });
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { resolveWorkflowAsync } from '../../project/workflow';
|
|||||||
import { promptAsync } from '../../prompts';
|
import { promptAsync } from '../../prompts';
|
||||||
import uniqBy from '../../utils/expodash/uniqBy';
|
import uniqBy from '../../utils/expodash/uniqBy';
|
||||||
import { readPlistAsync, writePlistAsync } from '../../utils/plist';
|
import { readPlistAsync, writePlistAsync } from '../../utils/plist';
|
||||||
|
import { Client } from '../../vcs/vcs';
|
||||||
import { updateAppJsonConfigAsync } from '../utils/appJson';
|
import { updateAppJsonConfigAsync } from '../utils/appJson';
|
||||||
import { bumpAppVersionAsync, ensureStaticConfigExists } from '../utils/version';
|
import { bumpAppVersionAsync, ensureStaticConfigExists } from '../utils/version';
|
||||||
|
|
||||||
@@ -110,9 +111,10 @@ function validateShortVersion(shortVersion: string | undefined): void {
|
|||||||
export async function readShortVersionAsync(
|
export async function readShortVersionAsync(
|
||||||
projectDir: string,
|
projectDir: string,
|
||||||
exp: ExpoConfig,
|
exp: ExpoConfig,
|
||||||
buildSettings: XCBuildConfiguration['buildSettings']
|
buildSettings: XCBuildConfiguration['buildSettings'],
|
||||||
|
vcsClient: Client
|
||||||
): Promise<string | undefined> {
|
): Promise<string | undefined> {
|
||||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
|
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient);
|
||||||
if (workflow === Workflow.GENERIC) {
|
if (workflow === Workflow.GENERIC) {
|
||||||
const infoPlist = await readInfoPlistAsync(projectDir, buildSettings);
|
const infoPlist = await readInfoPlistAsync(projectDir, buildSettings);
|
||||||
|
|
||||||
@@ -131,9 +133,10 @@ export async function readShortVersionAsync(
|
|||||||
export async function readBuildNumberAsync(
|
export async function readBuildNumberAsync(
|
||||||
projectDir: string,
|
projectDir: string,
|
||||||
exp: ExpoConfig,
|
exp: ExpoConfig,
|
||||||
buildSettings: XCBuildConfiguration['buildSettings']
|
buildSettings: XCBuildConfiguration['buildSettings'],
|
||||||
|
vcsClient: Client
|
||||||
): Promise<string | undefined> {
|
): Promise<string | undefined> {
|
||||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
|
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient);
|
||||||
if (workflow === Workflow.GENERIC) {
|
if (workflow === Workflow.GENERIC) {
|
||||||
const infoPlist = await readInfoPlistAsync(projectDir, buildSettings);
|
const infoPlist = await readInfoPlistAsync(projectDir, buildSettings);
|
||||||
return (
|
return (
|
||||||
@@ -147,7 +150,8 @@ export async function readBuildNumberAsync(
|
|||||||
export async function maybeResolveVersionsAsync(
|
export async function maybeResolveVersionsAsync(
|
||||||
projectDir: string,
|
projectDir: string,
|
||||||
exp: ExpoConfig,
|
exp: ExpoConfig,
|
||||||
targets: Target[]
|
targets: Target[],
|
||||||
|
vcsClient: Client
|
||||||
): Promise<{ appVersion?: string; appBuildVersion?: string }> {
|
): Promise<{ appVersion?: string; appBuildVersion?: string }> {
|
||||||
const applicationTarget = findApplicationTarget(targets);
|
const applicationTarget = findApplicationTarget(targets);
|
||||||
try {
|
try {
|
||||||
@@ -155,12 +159,14 @@ export async function maybeResolveVersionsAsync(
|
|||||||
appBuildVersion: await readBuildNumberAsync(
|
appBuildVersion: await readBuildNumberAsync(
|
||||||
projectDir,
|
projectDir,
|
||||||
exp,
|
exp,
|
||||||
applicationTarget.buildSettings ?? {}
|
applicationTarget.buildSettings ?? {},
|
||||||
|
vcsClient
|
||||||
),
|
),
|
||||||
appVersion: await readShortVersionAsync(
|
appVersion: await readShortVersionAsync(
|
||||||
projectDir,
|
projectDir,
|
||||||
exp,
|
exp,
|
||||||
applicationTarget.buildSettings ?? {}
|
applicationTarget.buildSettings ?? {},
|
||||||
|
vcsClient
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
@@ -278,12 +284,14 @@ export async function resolveRemoteBuildNumberAsync(
|
|||||||
exp,
|
exp,
|
||||||
applicationTarget,
|
applicationTarget,
|
||||||
buildProfile,
|
buildProfile,
|
||||||
|
vcsClient,
|
||||||
}: {
|
}: {
|
||||||
projectDir: string;
|
projectDir: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
exp: ExpoConfig;
|
exp: ExpoConfig;
|
||||||
applicationTarget: Target;
|
applicationTarget: Target;
|
||||||
buildProfile: BuildProfile<Platform.IOS>;
|
buildProfile: BuildProfile<Platform.IOS>;
|
||||||
|
vcsClient: Client;
|
||||||
}
|
}
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const remoteVersions = await AppVersionQuery.latestVersionAsync(
|
const remoteVersions = await AppVersionQuery.latestVersionAsync(
|
||||||
@@ -296,12 +304,14 @@ export async function resolveRemoteBuildNumberAsync(
|
|||||||
const localBuildNumber = await readBuildNumberAsync(
|
const localBuildNumber = await readBuildNumberAsync(
|
||||||
projectDir,
|
projectDir,
|
||||||
exp,
|
exp,
|
||||||
applicationTarget.buildSettings ?? {}
|
applicationTarget.buildSettings ?? {},
|
||||||
|
vcsClient
|
||||||
);
|
);
|
||||||
const localShortVersion = await readShortVersionAsync(
|
const localShortVersion = await readShortVersionAsync(
|
||||||
projectDir,
|
projectDir,
|
||||||
exp,
|
exp,
|
||||||
applicationTarget.buildSettings ?? {}
|
applicationTarget.buildSettings ?? {},
|
||||||
|
vcsClient
|
||||||
);
|
);
|
||||||
let currentBuildVersion: string;
|
let currentBuildVersion: string;
|
||||||
if (remoteVersions?.buildVersion) {
|
if (remoteVersions?.buildVersion) {
|
||||||
@@ -332,7 +342,9 @@ export async function resolveRemoteBuildNumberAsync(
|
|||||||
applicationIdentifier: applicationTarget.bundleIdentifier,
|
applicationIdentifier: applicationTarget.bundleIdentifier,
|
||||||
storeVersion: localShortVersion ?? '1.0.0',
|
storeVersion: localShortVersion ?? '1.0.0',
|
||||||
buildVersion: currentBuildVersion,
|
buildVersion: currentBuildVersion,
|
||||||
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.IOS) ?? undefined,
|
runtimeVersion:
|
||||||
|
(await Updates.getRuntimeVersionNullableAsync(projectDir, exp, Platform.IOS)) ??
|
||||||
|
undefined,
|
||||||
});
|
});
|
||||||
spinner.succeed(`Initialized buildNumber with ${chalk.bold(currentBuildVersion)}.`);
|
spinner.succeed(`Initialized buildNumber with ${chalk.bold(currentBuildVersion)}.`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -354,7 +366,9 @@ export async function resolveRemoteBuildNumberAsync(
|
|||||||
applicationIdentifier: applicationTarget.bundleIdentifier,
|
applicationIdentifier: applicationTarget.bundleIdentifier,
|
||||||
storeVersion: localShortVersion ?? '1.0.0',
|
storeVersion: localShortVersion ?? '1.0.0',
|
||||||
buildVersion: nextBuildVersion,
|
buildVersion: nextBuildVersion,
|
||||||
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.IOS) ?? undefined,
|
runtimeVersion:
|
||||||
|
(await Updates.getRuntimeVersionNullableAsync(projectDir, exp, Platform.IOS)) ??
|
||||||
|
undefined,
|
||||||
});
|
});
|
||||||
spinner.succeed(
|
spinner.succeed(
|
||||||
`Incremented buildNumber from ${chalk.bold(currentBuildVersion)} to ${chalk.bold(
|
`Incremented buildNumber from ${chalk.bold(currentBuildVersion)} to ${chalk.bold(
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import semver from 'semver';
|
|||||||
import { ora } from '../ora';
|
import { ora } from '../ora';
|
||||||
|
|
||||||
const PLUGIN_PACKAGE_NAME = 'eas-cli-local-build-plugin';
|
const PLUGIN_PACKAGE_NAME = 'eas-cli-local-build-plugin';
|
||||||
const PLUGIN_PACKAGE_VERSION = '1.0.24';
|
const PLUGIN_PACKAGE_VERSION = '1.0.118';
|
||||||
|
|
||||||
export enum LocalBuildMode {
|
export enum LocalBuildMode {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,35 +1,25 @@
|
|||||||
import { Updates } from '@expo/config-plugins';
|
import { Metadata, Platform, sanitizeMetadata } from '@expo/eas-build-job';
|
||||||
import { BuildMode, Metadata, Platform, sanitizeMetadata } from '@expo/eas-build-job';
|
|
||||||
import { IosEnterpriseProvisioning } from '@expo/eas-json';
|
import { IosEnterpriseProvisioning } from '@expo/eas-json';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import resolveFrom from 'resolve-from';
|
import resolveFrom from 'resolve-from';
|
||||||
|
|
||||||
import Log from '../log';
|
|
||||||
import { getUsername, isExpoUpdatesInstalled } from '../project/projectUtils';
|
|
||||||
import {
|
|
||||||
readChannelSafelyAsync as readAndroidChannelSafelyAsync,
|
|
||||||
readReleaseChannelSafelyAsync as readAndroidReleaseChannelSafelyAsync,
|
|
||||||
} from '../update/android/UpdatesModule';
|
|
||||||
import {
|
|
||||||
readChannelSafelyAsync as readIosChannelSafelyAsync,
|
|
||||||
readReleaseChannelSafelyAsync as readIosReleaseChannelSafelyAsync,
|
|
||||||
} from '../update/ios/UpdatesModule';
|
|
||||||
import { easCliVersion } from '../utils/easCli';
|
|
||||||
import { getVcsClient } from '../vcs';
|
|
||||||
import { maybeResolveVersionsAsync as maybeResolveAndroidVersionsAsync } from './android/version';
|
import { maybeResolveVersionsAsync as maybeResolveAndroidVersionsAsync } from './android/version';
|
||||||
import { BuildContext } from './context';
|
import { BuildContext } from './context';
|
||||||
import { maybeResolveVersionsAsync as maybeResolveIosVersionsAsync } from './ios/version';
|
import { maybeResolveVersionsAsync as maybeResolveIosVersionsAsync } from './ios/version';
|
||||||
import { LocalBuildMode } from './local';
|
import { LocalBuildMode } from './local';
|
||||||
|
import { BuildDistributionType } from './types';
|
||||||
|
import Log from '../log';
|
||||||
|
import { getUsername, isExpoUpdatesInstalled } from '../project/projectUtils';
|
||||||
|
import { resolveRuntimeVersionAsync } from '../project/resolveRuntimeVersionAsync';
|
||||||
|
import { readChannelSafelyAsync as readAndroidChannelSafelyAsync } from '../update/android/UpdatesModule';
|
||||||
|
import { readChannelSafelyAsync as readIosChannelSafelyAsync } from '../update/ios/UpdatesModule';
|
||||||
|
import { easCliVersion } from '../utils/easCli';
|
||||||
|
|
||||||
export async function collectMetadataAsync<T extends Platform>(
|
export async function collectMetadataAsync<T extends Platform>(
|
||||||
ctx: BuildContext<T>
|
ctx: BuildContext<T>
|
||||||
): Promise<Metadata> {
|
): Promise<Metadata> {
|
||||||
const vcsClient = getVcsClient();
|
const channelObject = await resolveChannelAsync(ctx);
|
||||||
const channelOrReleaseChannel = await resolveChannelOrReleaseChannelAsync(ctx);
|
const distribution = ctx.buildProfile.distribution ?? BuildDistributionType.STORE;
|
||||||
const distribution =
|
|
||||||
('simulator' in ctx.buildProfile && ctx.buildProfile.simulator
|
|
||||||
? 'simulator'
|
|
||||||
: ctx.buildProfile.distribution) ?? 'store';
|
|
||||||
const metadata: Metadata = {
|
const metadata: Metadata = {
|
||||||
trackingContext: ctx.analyticsEventProperties,
|
trackingContext: ctx.analyticsEventProperties,
|
||||||
...(await maybeResolveVersionsAsync(ctx)),
|
...(await maybeResolveVersionsAsync(ctx)),
|
||||||
@@ -37,21 +27,29 @@ export async function collectMetadataAsync<T extends Platform>(
|
|||||||
workflow: ctx.workflow,
|
workflow: ctx.workflow,
|
||||||
credentialsSource: ctx.buildProfile.credentialsSource,
|
credentialsSource: ctx.buildProfile.credentialsSource,
|
||||||
sdkVersion: ctx.exp.sdkVersion,
|
sdkVersion: ctx.exp.sdkVersion,
|
||||||
runtimeVersion: Updates.getRuntimeVersionNullable(ctx.exp, ctx.platform) ?? undefined,
|
runtimeVersion:
|
||||||
|
(await resolveRuntimeVersionAsync({
|
||||||
|
exp: ctx.exp,
|
||||||
|
platform: ctx.platform,
|
||||||
|
workflow: ctx.workflow,
|
||||||
|
projectDir: ctx.projectDir,
|
||||||
|
env: ctx.buildProfile.env,
|
||||||
|
cwd: ctx.projectDir,
|
||||||
|
})) ?? undefined,
|
||||||
reactNativeVersion: await getReactNativeVersionAsync(ctx.projectDir),
|
reactNativeVersion: await getReactNativeVersionAsync(ctx.projectDir),
|
||||||
...channelOrReleaseChannel,
|
...channelObject,
|
||||||
distribution,
|
distribution,
|
||||||
appName: ctx.exp.name,
|
appName: ctx.exp.name,
|
||||||
appIdentifier: resolveAppIdentifier(ctx),
|
appIdentifier: resolveAppIdentifier(ctx),
|
||||||
buildProfile: ctx.buildProfileName,
|
buildProfile: ctx.buildProfileName,
|
||||||
gitCommitHash: await vcsClient.getCommitHashAsync(),
|
gitCommitHash: await ctx.vcsClient.getCommitHashAsync(),
|
||||||
gitCommitMessage: truncateGitCommitMessage(
|
gitCommitMessage: truncateGitCommitMessage(
|
||||||
(await vcsClient.getLastCommitMessageAsync()) ?? undefined
|
(await ctx.vcsClient.getLastCommitMessageAsync()) ?? undefined
|
||||||
),
|
),
|
||||||
isGitWorkingTreeDirty:
|
isGitWorkingTreeDirty:
|
||||||
ctx.localBuildOptions.localBuildMode === LocalBuildMode.INTERNAL
|
ctx.localBuildOptions.localBuildMode === LocalBuildMode.INTERNAL
|
||||||
? false
|
? false
|
||||||
: await vcsClient.hasUncommittedChangesAsync(),
|
: await ctx.vcsClient.hasUncommittedChangesAsync(),
|
||||||
username: getUsername(ctx.exp, ctx.user),
|
username: getUsername(ctx.exp, ctx.user),
|
||||||
message: ctx.message,
|
message: ctx.message,
|
||||||
...(ctx.platform === Platform.IOS && {
|
...(ctx.platform === Platform.IOS && {
|
||||||
@@ -61,8 +59,12 @@ export async function collectMetadataAsync<T extends Platform>(
|
|||||||
}),
|
}),
|
||||||
runWithNoWaitFlag: ctx.noWait,
|
runWithNoWaitFlag: ctx.noWait,
|
||||||
runFromCI: ctx.runFromCI,
|
runFromCI: ctx.runFromCI,
|
||||||
buildMode: ctx.buildProfile.config ? BuildMode.CUSTOM : BuildMode.BUILD,
|
|
||||||
customWorkflowName: ctx.customBuildConfigMetadata?.workflowName,
|
customWorkflowName: ctx.customBuildConfigMetadata?.workflowName,
|
||||||
|
developmentClient: ctx.developmentClient,
|
||||||
|
requiredPackageManager: ctx.requiredPackageManager ?? undefined,
|
||||||
|
selectedImage: ctx.buildProfile.image,
|
||||||
|
customNodeVersion: ctx.buildProfile.node,
|
||||||
|
simulator: 'simulator' in ctx.buildProfile && ctx.buildProfile.simulator,
|
||||||
};
|
};
|
||||||
return sanitizeMetadata(metadata);
|
return sanitizeMetadata(metadata);
|
||||||
}
|
}
|
||||||
@@ -75,7 +77,8 @@ async function maybeResolveVersionsAsync<T extends Platform>(
|
|||||||
const resolvedVersion = await maybeResolveIosVersionsAsync(
|
const resolvedVersion = await maybeResolveIosVersionsAsync(
|
||||||
ctx.projectDir,
|
ctx.projectDir,
|
||||||
ctx.exp,
|
ctx.exp,
|
||||||
iosContext.ios.targets
|
iosContext.ios.targets,
|
||||||
|
ctx.vcsClient
|
||||||
);
|
);
|
||||||
if (iosContext.ios.buildNumberOverride) {
|
if (iosContext.ios.buildNumberOverride) {
|
||||||
return {
|
return {
|
||||||
@@ -89,7 +92,8 @@ async function maybeResolveVersionsAsync<T extends Platform>(
|
|||||||
const resolvedVersion = await maybeResolveAndroidVersionsAsync(
|
const resolvedVersion = await maybeResolveAndroidVersionsAsync(
|
||||||
ctx.projectDir,
|
ctx.projectDir,
|
||||||
ctx.exp,
|
ctx.exp,
|
||||||
androidCtx.buildProfile
|
androidCtx.buildProfile,
|
||||||
|
ctx.vcsClient
|
||||||
);
|
);
|
||||||
if (androidCtx.android.versionCodeOverride) {
|
if (androidCtx.android.versionCodeOverride) {
|
||||||
return {
|
return {
|
||||||
@@ -111,39 +115,21 @@ function resolveAppIdentifier<T extends Platform>(ctx: BuildContext<T>): string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resolveChannelOrReleaseChannelAsync<T extends Platform>(
|
async function resolveChannelAsync<T extends Platform>(
|
||||||
ctx: BuildContext<T>
|
ctx: BuildContext<T>
|
||||||
): Promise<{ channel: string } | { releaseChannel: string } | null> {
|
): Promise<{ channel: string } | null> {
|
||||||
if (!isExpoUpdatesInstalled(ctx.projectDir)) {
|
if (!isExpoUpdatesInstalled(ctx.projectDir)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (ctx.buildProfile.channel) {
|
if (ctx.buildProfile.channel) {
|
||||||
return { channel: ctx.buildProfile.channel };
|
return { channel: ctx.buildProfile.channel };
|
||||||
}
|
}
|
||||||
if (ctx.buildProfile.releaseChannel) {
|
|
||||||
return { releaseChannel: ctx.buildProfile.releaseChannel };
|
|
||||||
}
|
|
||||||
const channel = await getNativeChannelAsync(ctx);
|
const channel = await getNativeChannelAsync(ctx);
|
||||||
if (channel) {
|
if (channel) {
|
||||||
return { channel };
|
return { channel };
|
||||||
}
|
}
|
||||||
const releaseChannel = await getNativeReleaseChannelAsync(ctx);
|
|
||||||
return { releaseChannel };
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getNativeReleaseChannelAsync<T extends Platform>(
|
return null;
|
||||||
ctx: BuildContext<T>
|
|
||||||
): Promise<string> {
|
|
||||||
switch (ctx.platform) {
|
|
||||||
case Platform.ANDROID: {
|
|
||||||
return (await readAndroidReleaseChannelSafelyAsync(ctx.projectDir)) ?? 'default';
|
|
||||||
}
|
|
||||||
case Platform.IOS: {
|
|
||||||
return (await readIosReleaseChannelSafelyAsync(ctx.projectDir)) ?? 'default';
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return 'default';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getNativeChannelAsync<T extends Platform>(
|
async function getNativeChannelAsync<T extends Platform>(
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
|
||||||
|
import { formatGraphQLBuild } from './utils/formatBuild';
|
||||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||||
import { PaginatedQueryOptions } from '../commandUtils/pagination';
|
import { PaginatedQueryOptions } from '../commandUtils/pagination';
|
||||||
import { AppPlatform, BuildFilter, BuildFragment } from '../graphql/generated';
|
import { AppPlatform, BuildFilter, BuildFragment } from '../graphql/generated';
|
||||||
@@ -11,7 +12,6 @@ import {
|
|||||||
paginatedQueryWithConfirmPromptAsync,
|
paginatedQueryWithConfirmPromptAsync,
|
||||||
paginatedQueryWithSelectPromptAsync,
|
paginatedQueryWithSelectPromptAsync,
|
||||||
} from '../utils/queries';
|
} from '../utils/queries';
|
||||||
import { formatGraphQLBuild } from './utils/formatBuild';
|
|
||||||
|
|
||||||
export const BUILDS_LIMIT = 50;
|
export const BUILDS_LIMIT = 50;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
import { ExpoConfig } from '@expo/config-types';
|
||||||
|
import { Env, Platform, Workflow } from '@expo/eas-build-job';
|
||||||
import {
|
import {
|
||||||
AppVersionSource,
|
AppVersionSource,
|
||||||
BuildProfile,
|
BuildProfile,
|
||||||
@@ -8,11 +9,23 @@ import {
|
|||||||
ResourceClass,
|
ResourceClass,
|
||||||
SubmitProfile,
|
SubmitProfile,
|
||||||
} from '@expo/eas-json';
|
} from '@expo/eas-json';
|
||||||
|
import { LoggerLevel } from '@expo/logger';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import nullthrows from 'nullthrows';
|
import nullthrows from 'nullthrows';
|
||||||
|
|
||||||
|
import { prepareAndroidBuildAsync } from './android/build';
|
||||||
|
import { BuildRequestSender, MaybeBuildFragment, waitForBuildEndAsync } from './build';
|
||||||
|
import { ensureProjectConfiguredAsync } from './configure';
|
||||||
|
import { BuildContext } from './context';
|
||||||
|
import { createBuildContextAsync } from './createContext';
|
||||||
|
import { prepareIosBuildAsync } from './ios/build';
|
||||||
|
import { LocalBuildMode, LocalBuildOptions } from './local';
|
||||||
|
import { ensureExpoDevClientInstalledForDevClientBuildsAsync } from './utils/devClient';
|
||||||
|
import { printBuildResults, printLogsUrls } from './utils/printBuildInfo';
|
||||||
|
import { ensureRepoIsCleanAsync } from './utils/repository';
|
||||||
import { Analytics } from '../analytics/AnalyticsManager';
|
import { Analytics } from '../analytics/AnalyticsManager';
|
||||||
|
import { createAndLinkChannelAsync, doesChannelExistAsync } from '../channel/queries';
|
||||||
import { DynamicConfigContextFn } from '../commandUtils/context/DynamicProjectConfigContextField';
|
import { DynamicConfigContextFn } from '../commandUtils/context/DynamicProjectConfigContextField';
|
||||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||||
import {
|
import {
|
||||||
@@ -24,7 +37,7 @@ import {
|
|||||||
} from '../graphql/generated';
|
} from '../graphql/generated';
|
||||||
import { BuildQuery } from '../graphql/queries/BuildQuery';
|
import { BuildQuery } from '../graphql/queries/BuildQuery';
|
||||||
import { toAppPlatform, toPlatform } from '../graphql/types/AppPlatform';
|
import { toAppPlatform, toPlatform } from '../graphql/types/AppPlatform';
|
||||||
import Log from '../log';
|
import Log, { learnMore } from '../log';
|
||||||
import {
|
import {
|
||||||
RequestedPlatform,
|
RequestedPlatform,
|
||||||
appPlatformDisplayNames,
|
appPlatformDisplayNames,
|
||||||
@@ -38,9 +51,9 @@ import {
|
|||||||
import { checkExpoSdkIsSupportedAsync } from '../project/expoSdk';
|
import { checkExpoSdkIsSupportedAsync } from '../project/expoSdk';
|
||||||
import { validateMetroConfigForManagedWorkflowAsync } from '../project/metroConfig';
|
import { validateMetroConfigForManagedWorkflowAsync } from '../project/metroConfig';
|
||||||
import {
|
import {
|
||||||
installExpoUpdatesAsync,
|
|
||||||
isExpoUpdatesInstalledAsDevDependency,
|
isExpoUpdatesInstalledAsDevDependency,
|
||||||
isExpoUpdatesInstalledOrAvailable,
|
isExpoUpdatesInstalledOrAvailable,
|
||||||
|
isUsingEASUpdate,
|
||||||
validateAppVersionRuntimePolicySupportAsync,
|
validateAppVersionRuntimePolicySupportAsync,
|
||||||
} from '../project/projectUtils';
|
} from '../project/projectUtils';
|
||||||
import {
|
import {
|
||||||
@@ -57,23 +70,13 @@ import {
|
|||||||
waitToCompleteAsync as waitForSubmissionsToCompleteAsync,
|
waitToCompleteAsync as waitForSubmissionsToCompleteAsync,
|
||||||
} from '../submit/submit';
|
} from '../submit/submit';
|
||||||
import { printSubmissionDetailsUrls } from '../submit/utils/urls';
|
import { printSubmissionDetailsUrls } from '../submit/utils/urls';
|
||||||
import { validateBuildProfileConfigMatchesProjectConfigAsync } from '../update/utils';
|
import { ensureEASUpdateIsConfiguredAsync } from '../update/configure';
|
||||||
import { Actor } from '../user/User';
|
import { Actor } from '../user/User';
|
||||||
import { downloadAndMaybeExtractAppAsync } from '../utils/download';
|
import { downloadAndMaybeExtractAppAsync } from '../utils/download';
|
||||||
import { truthy } from '../utils/expodash/filter';
|
import { truthy } from '../utils/expodash/filter';
|
||||||
import { printJsonOnlyOutput } from '../utils/json';
|
import { printJsonOnlyOutput } from '../utils/json';
|
||||||
import { ProfileData, getProfilesAsync } from '../utils/profiles';
|
import { ProfileData, getProfilesAsync } from '../utils/profiles';
|
||||||
import { getVcsClient } from '../vcs';
|
import { Client } from '../vcs/vcs';
|
||||||
import { prepareAndroidBuildAsync } from './android/build';
|
|
||||||
import { BuildRequestSender, MaybeBuildFragment, waitForBuildEndAsync } from './build';
|
|
||||||
import { ensureProjectConfiguredAsync } from './configure';
|
|
||||||
import { BuildContext } from './context';
|
|
||||||
import { createBuildContextAsync } from './createContext';
|
|
||||||
import { prepareIosBuildAsync } from './ios/build';
|
|
||||||
import { LocalBuildOptions } from './local';
|
|
||||||
import { ensureExpoDevClientInstalledForDevClientBuildsAsync } from './utils/devClient';
|
|
||||||
import { printBuildResults, printLogsUrls } from './utils/printBuildInfo';
|
|
||||||
import { ensureRepoIsCleanAsync } from './utils/repository';
|
|
||||||
|
|
||||||
let metroConfigValidated = false;
|
let metroConfigValidated = false;
|
||||||
let sdkVersionChecked = false;
|
let sdkVersionChecked = false;
|
||||||
@@ -90,22 +93,29 @@ export interface BuildFlags {
|
|||||||
localBuildOptions: LocalBuildOptions;
|
localBuildOptions: LocalBuildOptions;
|
||||||
resourceClass?: ResourceClass;
|
resourceClass?: ResourceClass;
|
||||||
message?: string;
|
message?: string;
|
||||||
|
buildLoggerLevel?: LoggerLevel;
|
||||||
|
freezeCredentials: boolean;
|
||||||
|
repack: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function runBuildAndSubmitAsync(
|
export async function runBuildAndSubmitAsync(
|
||||||
graphqlClient: ExpoGraphqlClient,
|
graphqlClient: ExpoGraphqlClient,
|
||||||
analytics: Analytics,
|
analytics: Analytics,
|
||||||
|
vcsClient: Client,
|
||||||
projectDir: string,
|
projectDir: string,
|
||||||
flags: BuildFlags,
|
flags: BuildFlags,
|
||||||
actor: Actor,
|
actor: Actor,
|
||||||
getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn
|
getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn
|
||||||
): Promise<void> {
|
): Promise<{
|
||||||
await getVcsClient().ensureRepoExistsAsync();
|
buildIds: string[];
|
||||||
await ensureRepoIsCleanAsync(flags.nonInteractive);
|
}> {
|
||||||
|
await vcsClient.ensureRepoExistsAsync();
|
||||||
|
await ensureRepoIsCleanAsync(vcsClient, flags.nonInteractive);
|
||||||
|
|
||||||
await ensureProjectConfiguredAsync({
|
await ensureProjectConfiguredAsync({
|
||||||
projectDir,
|
projectDir,
|
||||||
nonInteractive: flags.nonInteractive,
|
nonInteractive: flags.nonInteractive,
|
||||||
|
vcsClient,
|
||||||
});
|
});
|
||||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||||
const easJsonCliConfig: EasJson['cli'] =
|
const easJsonCliConfig: EasJson['cli'] =
|
||||||
@@ -117,19 +127,40 @@ export async function runBuildAndSubmitAsync(
|
|||||||
easJsonAccessor,
|
easJsonAccessor,
|
||||||
platforms,
|
platforms,
|
||||||
profileName: flags.profile ?? undefined,
|
profileName: flags.profile ?? undefined,
|
||||||
|
projectDir,
|
||||||
});
|
});
|
||||||
|
Log.log(
|
||||||
|
`Loaded "env" configuration for the "${buildProfiles[0].profileName}" profile: ${
|
||||||
|
buildProfiles[0].profile.env
|
||||||
|
? Object.keys(buildProfiles[0].profile.env).join(', ')
|
||||||
|
: 'no environment variables specified'
|
||||||
|
}. ${learnMore('https://docs.expo.dev/build-reference/variables/')}`
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const buildProfile of buildProfiles) {
|
||||||
|
if (buildProfile.profile.image && ['default', 'stable'].includes(buildProfile.profile.image)) {
|
||||||
|
Log.warn(
|
||||||
|
`The "image" field in the build profile "${buildProfile.profileName}" is set to "${buildProfile.profile.image}". This tag is deprecated and will be removed in the future. Use other images or tags listed here: https://docs.expo.dev/build-reference/infrastructure/`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await ensureExpoDevClientInstalledForDevClientBuildsAsync({
|
await ensureExpoDevClientInstalledForDevClientBuildsAsync({
|
||||||
projectDir,
|
projectDir,
|
||||||
nonInteractive: flags.nonInteractive,
|
nonInteractive: flags.nonInteractive,
|
||||||
buildProfiles,
|
buildProfiles,
|
||||||
|
vcsClient,
|
||||||
});
|
});
|
||||||
|
|
||||||
const customBuildConfigMetadataByPlatform: { [p in AppPlatform]?: CustomBuildConfigMetadata } =
|
const customBuildConfigMetadataByPlatform: { [p in AppPlatform]?: CustomBuildConfigMetadata } =
|
||||||
{};
|
{};
|
||||||
for (const buildProfile of buildProfiles) {
|
for (const buildProfile of buildProfiles) {
|
||||||
validateBuildProfileVersionSettings(buildProfile, easJsonCliConfig);
|
validateBuildProfileVersionSettings(buildProfile, easJsonCliConfig);
|
||||||
const maybeMetadata = await validateCustomBuildConfigAsync(projectDir, buildProfile.profile);
|
const maybeMetadata = await validateCustomBuildConfigAsync({
|
||||||
|
projectDir,
|
||||||
|
profile: buildProfile.profile,
|
||||||
|
vcsClient,
|
||||||
|
});
|
||||||
if (maybeMetadata) {
|
if (maybeMetadata) {
|
||||||
customBuildConfigMetadataByPlatform[toAppPlatform(buildProfile.platform)] = maybeMetadata;
|
customBuildConfigMetadataByPlatform[toAppPlatform(buildProfile.platform)] = maybeMetadata;
|
||||||
}
|
}
|
||||||
@@ -152,6 +183,7 @@ export async function runBuildAndSubmitAsync(
|
|||||||
actor,
|
actor,
|
||||||
graphqlClient,
|
graphqlClient,
|
||||||
analytics,
|
analytics,
|
||||||
|
vcsClient,
|
||||||
getDynamicPrivateProjectConfigAsync,
|
getDynamicPrivateProjectConfigAsync,
|
||||||
customBuildConfigMetadata: customBuildConfigMetadataByPlatform[platform],
|
customBuildConfigMetadata: customBuildConfigMetadataByPlatform[platform],
|
||||||
});
|
});
|
||||||
@@ -161,13 +193,25 @@ export async function runBuildAndSubmitAsync(
|
|||||||
buildCtxByPlatform[platform] = buildCtx;
|
buildCtxByPlatform[platform] = buildCtx;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags.localBuildOptions.localBuildMode) {
|
if (flags.localBuildOptions.localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN) {
|
||||||
return;
|
return {
|
||||||
|
buildIds: startedBuilds.map(({ build }) => build.id),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (flags.localBuildOptions.localBuildMode === LocalBuildMode.INTERNAL) {
|
||||||
|
const startedBuild = await BuildQuery.byIdAsync(
|
||||||
|
graphqlClient,
|
||||||
|
nullthrows(process.env.EAS_BUILD_ID, 'EAS_BUILD_ID is not defined')
|
||||||
|
);
|
||||||
|
startedBuilds.push({ build: startedBuild, buildProfile: buildProfiles[0] });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!flags.localBuildOptions.localBuildMode) {
|
||||||
Log.newLine();
|
Log.newLine();
|
||||||
printLogsUrls(startedBuilds.map(startedBuild => startedBuild.build));
|
printLogsUrls(startedBuilds.map(startedBuild => startedBuild.build));
|
||||||
Log.newLine();
|
Log.newLine();
|
||||||
|
}
|
||||||
|
|
||||||
const submissions: SubmissionFragment[] = [];
|
const submissions: SubmissionFragment[] = [];
|
||||||
if (flags.autoSubmit) {
|
if (flags.autoSubmit) {
|
||||||
@@ -176,6 +220,7 @@ export async function runBuildAndSubmitAsync(
|
|||||||
platforms,
|
platforms,
|
||||||
profileName: flags.submitProfile,
|
profileName: flags.submitProfile,
|
||||||
type: 'submit',
|
type: 'submit',
|
||||||
|
projectDir,
|
||||||
});
|
});
|
||||||
for (const startedBuild of startedBuilds) {
|
for (const startedBuild of startedBuilds) {
|
||||||
const submitProfile = nullthrows(
|
const submitProfile = nullthrows(
|
||||||
@@ -191,6 +236,7 @@ export async function runBuildAndSubmitAsync(
|
|||||||
buildProfile: startedBuild.buildProfile.profile,
|
buildProfile: startedBuild.buildProfile.profile,
|
||||||
submitProfile,
|
submitProfile,
|
||||||
nonInteractive: flags.nonInteractive,
|
nonInteractive: flags.nonInteractive,
|
||||||
|
selectedSubmitProfileName: flags.submitProfile,
|
||||||
});
|
});
|
||||||
startedBuild.build = await BuildQuery.withSubmissionsByIdAsync(
|
startedBuild.build = await BuildQuery.withSubmissionsByIdAsync(
|
||||||
graphqlClient,
|
graphqlClient,
|
||||||
@@ -199,24 +245,32 @@ export async function runBuildAndSubmitAsync(
|
|||||||
submissions.push(submission);
|
submissions.push(submission);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!flags.localBuildOptions.localBuildMode) {
|
||||||
Log.newLine();
|
Log.newLine();
|
||||||
printSubmissionDetailsUrls(submissions);
|
printSubmissionDetailsUrls(submissions);
|
||||||
Log.newLine();
|
Log.newLine();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags.localBuildOptions.localBuildMode) {
|
||||||
|
return {
|
||||||
|
buildIds: startedBuilds.map(({ build }) => build.id),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (!flags.wait) {
|
if (!flags.wait) {
|
||||||
if (flags.json) {
|
if (flags.json) {
|
||||||
printJsonOnlyOutput(startedBuilds.map(buildInfo => buildInfo.build));
|
printJsonOnlyOutput(startedBuilds.map(buildInfo => buildInfo.build));
|
||||||
}
|
}
|
||||||
return;
|
return {
|
||||||
|
buildIds: startedBuilds.map(({ build }) => build.id),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const { accountName } = Object.values(buildCtxByPlatform)[0];
|
const { accountName } = Object.values(buildCtxByPlatform)[0];
|
||||||
const builds = await waitForBuildEndAsync(graphqlClient, {
|
const builds = await waitForBuildEndAsync(graphqlClient, {
|
||||||
buildIds: startedBuilds.map(({ build }) => build.id),
|
buildIds: startedBuilds.map(({ build }) => build.id),
|
||||||
accountName,
|
accountName,
|
||||||
projectDir,
|
|
||||||
nonInteractive: flags.nonInteractive,
|
|
||||||
});
|
});
|
||||||
if (!flags.json) {
|
if (!flags.json) {
|
||||||
printBuildResults(builds);
|
printBuildResults(builds);
|
||||||
@@ -251,6 +305,9 @@ export async function runBuildAndSubmitAsync(
|
|||||||
}
|
}
|
||||||
exitWithNonZeroCodeIfSomeSubmissionsDidntFinish(completedSubmissions);
|
exitWithNonZeroCodeIfSomeSubmissionsDidntFinish(completedSubmissions);
|
||||||
}
|
}
|
||||||
|
return {
|
||||||
|
buildIds: startedBuilds.map(({ build }) => build.id),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function prepareAndStartBuildAsync({
|
async function prepareAndStartBuildAsync({
|
||||||
@@ -262,6 +319,7 @@ async function prepareAndStartBuildAsync({
|
|||||||
actor,
|
actor,
|
||||||
graphqlClient,
|
graphqlClient,
|
||||||
analytics,
|
analytics,
|
||||||
|
vcsClient,
|
||||||
getDynamicPrivateProjectConfigAsync,
|
getDynamicPrivateProjectConfigAsync,
|
||||||
customBuildConfigMetadata,
|
customBuildConfigMetadata,
|
||||||
}: {
|
}: {
|
||||||
@@ -273,6 +331,7 @@ async function prepareAndStartBuildAsync({
|
|||||||
actor: Actor;
|
actor: Actor;
|
||||||
graphqlClient: ExpoGraphqlClient;
|
graphqlClient: ExpoGraphqlClient;
|
||||||
analytics: Analytics;
|
analytics: Analytics;
|
||||||
|
vcsClient: Client;
|
||||||
getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn;
|
getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn;
|
||||||
customBuildConfigMetadata?: CustomBuildConfigMetadata;
|
customBuildConfigMetadata?: CustomBuildConfigMetadata;
|
||||||
}): Promise<{ build: BuildFragment | undefined; buildCtx: BuildContext<Platform> }> {
|
}): Promise<{ build: BuildFragment | undefined; buildCtx: BuildContext<Platform> }> {
|
||||||
@@ -291,8 +350,12 @@ async function prepareAndStartBuildAsync({
|
|||||||
actor,
|
actor,
|
||||||
graphqlClient,
|
graphqlClient,
|
||||||
analytics,
|
analytics,
|
||||||
|
vcsClient,
|
||||||
getDynamicPrivateProjectConfigAsync,
|
getDynamicPrivateProjectConfigAsync,
|
||||||
customBuildConfigMetadata,
|
customBuildConfigMetadata,
|
||||||
|
buildLoggerLevel: flags.buildLoggerLevel,
|
||||||
|
freezeCredentials: flags.freezeCredentials,
|
||||||
|
repack: flags.repack,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (moreBuilds) {
|
if (moreBuilds) {
|
||||||
@@ -305,19 +368,29 @@ async function prepareAndStartBuildAsync({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await validateBuildProfileConfigMatchesProjectConfigAsync(
|
|
||||||
buildCtx.exp,
|
|
||||||
buildProfile,
|
|
||||||
buildCtx.projectId,
|
|
||||||
flags.nonInteractive
|
|
||||||
);
|
|
||||||
if (buildProfile.profile.channel) {
|
if (buildProfile.profile.channel) {
|
||||||
await validateExpoUpdatesInstalledAsProjectDependencyAsync({
|
await validateExpoUpdatesInstalledAsProjectDependencyAsync({
|
||||||
|
exp: buildCtx.exp,
|
||||||
|
projectId: buildCtx.projectId,
|
||||||
projectDir,
|
projectDir,
|
||||||
|
vcsClient: buildCtx.vcsClient,
|
||||||
sdkVersion: buildCtx.exp.sdkVersion,
|
sdkVersion: buildCtx.exp.sdkVersion,
|
||||||
nonInteractive: flags.nonInteractive,
|
nonInteractive: flags.nonInteractive,
|
||||||
buildProfile,
|
buildProfile,
|
||||||
|
env: buildProfile.profile.env,
|
||||||
});
|
});
|
||||||
|
if (isUsingEASUpdate(buildCtx.exp, buildCtx.projectId)) {
|
||||||
|
const doesChannelExist = await doesChannelExistAsync(graphqlClient, {
|
||||||
|
appId: buildCtx.projectId,
|
||||||
|
channelName: buildProfile.profile.channel,
|
||||||
|
});
|
||||||
|
if (!doesChannelExist) {
|
||||||
|
await createAndLinkChannelAsync(graphqlClient, {
|
||||||
|
appId: buildCtx.projectId,
|
||||||
|
channelName: buildProfile.profile.channel,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await validateAppVersionRuntimePolicySupportAsync(buildCtx.projectDir, buildCtx.exp);
|
await validateAppVersionRuntimePolicySupportAsync(buildCtx.projectDir, buildCtx.exp);
|
||||||
@@ -359,6 +432,7 @@ async function prepareAndStartSubmissionAsync({
|
|||||||
projectDir,
|
projectDir,
|
||||||
buildProfile,
|
buildProfile,
|
||||||
submitProfile,
|
submitProfile,
|
||||||
|
selectedSubmitProfileName,
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
}: {
|
}: {
|
||||||
build: BuildFragment;
|
build: BuildFragment;
|
||||||
@@ -367,6 +441,7 @@ async function prepareAndStartSubmissionAsync({
|
|||||||
projectDir: string;
|
projectDir: string;
|
||||||
buildProfile: BuildProfile;
|
buildProfile: BuildProfile;
|
||||||
submitProfile: SubmitProfile;
|
submitProfile: SubmitProfile;
|
||||||
|
selectedSubmitProfileName?: string;
|
||||||
nonInteractive: boolean;
|
nonInteractive: boolean;
|
||||||
}): Promise<SubmissionFragment> {
|
}): Promise<SubmissionFragment> {
|
||||||
const platform = toPlatform(build.platform);
|
const platform = toPlatform(build.platform);
|
||||||
@@ -384,6 +459,9 @@ async function prepareAndStartSubmissionAsync({
|
|||||||
analytics: buildCtx.analytics,
|
analytics: buildCtx.analytics,
|
||||||
projectId: buildCtx.projectId,
|
projectId: buildCtx.projectId,
|
||||||
exp: buildCtx.exp,
|
exp: buildCtx.exp,
|
||||||
|
vcsClient: buildCtx.vcsClient,
|
||||||
|
isVerboseFastlaneEnabled: false,
|
||||||
|
specifiedProfile: selectedSubmitProfileName,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (moreBuilds) {
|
if (moreBuilds) {
|
||||||
@@ -440,15 +518,23 @@ async function maybeDownloadAndRunSimulatorBuildsAsync(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function validateExpoUpdatesInstalledAsProjectDependencyAsync({
|
async function validateExpoUpdatesInstalledAsProjectDependencyAsync({
|
||||||
|
exp,
|
||||||
|
projectId,
|
||||||
projectDir,
|
projectDir,
|
||||||
|
vcsClient,
|
||||||
buildProfile,
|
buildProfile,
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
sdkVersion,
|
sdkVersion,
|
||||||
|
env,
|
||||||
}: {
|
}: {
|
||||||
|
exp: ExpoConfig;
|
||||||
|
projectId: string;
|
||||||
projectDir: string;
|
projectDir: string;
|
||||||
|
vcsClient: Client;
|
||||||
buildProfile: ProfileData<'build'>;
|
buildProfile: ProfileData<'build'>;
|
||||||
nonInteractive: boolean;
|
nonInteractive: boolean;
|
||||||
sdkVersion?: string;
|
sdkVersion?: string;
|
||||||
|
env: Env | undefined;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
if (isExpoUpdatesInstalledOrAvailable(projectDir, sdkVersion)) {
|
if (isExpoUpdatesInstalledOrAvailable(projectDir, sdkVersion)) {
|
||||||
return;
|
return;
|
||||||
@@ -460,18 +546,26 @@ async function validateExpoUpdatesInstalledAsProjectDependencyAsync({
|
|||||||
);
|
);
|
||||||
} else if (nonInteractive) {
|
} else if (nonInteractive) {
|
||||||
Log.warn(
|
Log.warn(
|
||||||
`The build profile "${buildProfile.profileName}" has specified the channel "${buildProfile.profile.channel}", but the "expo-updates" package hasn't been installed. To use channels for your builds, install the "expo-updates" package by running "npx expo install expo-updates".`
|
`The build profile "${buildProfile.profileName}" has specified the channel "${buildProfile.profile.channel}", but the "expo-updates" package hasn't been installed. To use channels for your builds, install the "expo-updates" package by running "npx expo install expo-updates" followed by "eas update:configure".`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
Log.warn(
|
Log.warn(
|
||||||
`The build profile "${buildProfile.profileName}" specifies the channel "${buildProfile.profile.channel}", but the "expo-updates" package is missing. To use channels in your builds, install the "expo-updates" package.`
|
`The build profile "${buildProfile.profileName}" specifies the channel "${buildProfile.profile.channel}", but the "expo-updates" package is missing. To use channels in your builds, install the "expo-updates" package and run "eas update:configure".`
|
||||||
);
|
);
|
||||||
const installExpoUpdates = await confirmAsync({
|
const installExpoUpdates = await confirmAsync({
|
||||||
message: `Would you like to install the "expo-updates" package?`,
|
message: `Would you like to install the "expo-updates" package and configure EAS Update now?`,
|
||||||
});
|
});
|
||||||
if (installExpoUpdates) {
|
if (installExpoUpdates) {
|
||||||
await installExpoUpdatesAsync(projectDir, { silent: false });
|
await ensureEASUpdateIsConfiguredAsync({
|
||||||
Log.withTick('Installed expo-updates');
|
exp,
|
||||||
|
projectId,
|
||||||
|
projectDir,
|
||||||
|
platform: RequestedPlatform.All,
|
||||||
|
vcsClient,
|
||||||
|
env,
|
||||||
|
});
|
||||||
|
Log.withTick('Installed expo-updates and configured EAS Update.');
|
||||||
|
throw new Error('Command must be re-run to pick up new updates configuration.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,5 +11,6 @@ export enum BuildStatus {
|
|||||||
export enum BuildDistributionType {
|
export enum BuildDistributionType {
|
||||||
STORE = 'store',
|
STORE = 'store',
|
||||||
INTERNAL = 'internal',
|
INTERNAL = 'internal',
|
||||||
|
/** @deprecated Use simulator flag instead */
|
||||||
SIMULATOR = 'simulator',
|
SIMULATOR = 'simulator',
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@ import { Errors } from '@oclif/core';
|
|||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import resolveFrom from 'resolve-from';
|
import resolveFrom from 'resolve-from';
|
||||||
|
|
||||||
|
import { reviewAndCommitChangesAsync } from './repository';
|
||||||
import { toAppPlatform } from '../../graphql/types/AppPlatform';
|
import { toAppPlatform } from '../../graphql/types/AppPlatform';
|
||||||
import Log, { learnMore } from '../../log';
|
import Log, { learnMore } from '../../log';
|
||||||
import { appPlatformDisplayNames } from '../../platform';
|
import { appPlatformDisplayNames } from '../../platform';
|
||||||
@@ -10,15 +11,16 @@ import { resolveWorkflowAsync } from '../../project/workflow';
|
|||||||
import { confirmAsync } from '../../prompts';
|
import { confirmAsync } from '../../prompts';
|
||||||
import { expoCommandAsync } from '../../utils/expoCli';
|
import { expoCommandAsync } from '../../utils/expoCli';
|
||||||
import { ProfileData } from '../../utils/profiles';
|
import { ProfileData } from '../../utils/profiles';
|
||||||
import { getVcsClient } from '../../vcs';
|
import { Client } from '../../vcs/vcs';
|
||||||
import { reviewAndCommitChangesAsync } from './repository';
|
|
||||||
|
|
||||||
export async function ensureExpoDevClientInstalledForDevClientBuildsAsync({
|
export async function ensureExpoDevClientInstalledForDevClientBuildsAsync({
|
||||||
projectDir,
|
projectDir,
|
||||||
|
vcsClient,
|
||||||
nonInteractive = false,
|
nonInteractive = false,
|
||||||
buildProfiles = [],
|
buildProfiles = [],
|
||||||
}: {
|
}: {
|
||||||
projectDir: string;
|
projectDir: string;
|
||||||
|
vcsClient: Client;
|
||||||
nonInteractive?: boolean;
|
nonInteractive?: boolean;
|
||||||
buildProfiles?: ProfileData<'build'>[];
|
buildProfiles?: ProfileData<'build'>[];
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
@@ -41,7 +43,7 @@ export async function ensureExpoDevClientInstalledForDevClientBuildsAsync({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const workflowPerPlatformList = await Promise.all(
|
const workflowPerPlatformList = await Promise.all(
|
||||||
platformsToCheck.map(platform => resolveWorkflowAsync(projectDir, platform))
|
platformsToCheck.map(platform => resolveWorkflowAsync(projectDir, platform, vcsClient))
|
||||||
);
|
);
|
||||||
|
|
||||||
Log.newLine();
|
Log.newLine();
|
||||||
@@ -55,6 +57,19 @@ export async function ensureExpoDevClientInstalledForDevClientBuildsAsync({
|
|||||||
'expo-dev-client'
|
'expo-dev-client'
|
||||||
)} installed for your project.`
|
)} installed for your project.`
|
||||||
);
|
);
|
||||||
|
if (nonInteractive) {
|
||||||
|
Log.error(`You'll need to install ${chalk.bold('expo-dev-client')} manually.`);
|
||||||
|
Log.error(
|
||||||
|
learnMore('https://docs.expo.dev/clients/installation/', {
|
||||||
|
learnMoreMessage: 'See installation instructions on how to do it.',
|
||||||
|
dim: false,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
Errors.error(`Install ${chalk.bold('expo-dev-client')} manually and try again later.`, {
|
||||||
|
exit: 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const areAllManaged = workflowPerPlatformList.every(i => i === Workflow.MANAGED);
|
const areAllManaged = workflowPerPlatformList.every(i => i === Workflow.MANAGED);
|
||||||
if (areAllManaged) {
|
if (areAllManaged) {
|
||||||
const install = await confirmAsync({
|
const install = await confirmAsync({
|
||||||
@@ -62,7 +77,7 @@ export async function ensureExpoDevClientInstalledForDevClientBuildsAsync({
|
|||||||
instructions: 'The command will abort unless you agree.',
|
instructions: 'The command will abort unless you agree.',
|
||||||
});
|
});
|
||||||
if (install) {
|
if (install) {
|
||||||
await installExpoDevClientAsync(projectDir, { nonInteractive });
|
await installExpoDevClientAsync(projectDir, vcsClient, { nonInteractive });
|
||||||
} else {
|
} else {
|
||||||
Errors.error(`Install ${chalk.bold('expo-dev-client')} manually and come back later.`, {
|
Errors.error(`Install ${chalk.bold('expo-dev-client')} manually and come back later.`, {
|
||||||
exit: 1,
|
exit: 1,
|
||||||
@@ -100,6 +115,7 @@ async function isExpoDevClientInstalledAsync(projectDir: string): Promise<boolea
|
|||||||
|
|
||||||
async function installExpoDevClientAsync(
|
async function installExpoDevClientAsync(
|
||||||
projectDir: string,
|
projectDir: string,
|
||||||
|
vcsClient: Client,
|
||||||
{ nonInteractive }: { nonInteractive: boolean }
|
{ nonInteractive }: { nonInteractive: boolean }
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
Log.newLine();
|
Log.newLine();
|
||||||
@@ -107,8 +123,8 @@ async function installExpoDevClientAsync(
|
|||||||
Log.newLine();
|
Log.newLine();
|
||||||
await expoCommandAsync(projectDir, ['install', 'expo-dev-client']);
|
await expoCommandAsync(projectDir, ['install', 'expo-dev-client']);
|
||||||
Log.newLine();
|
Log.newLine();
|
||||||
if (await getVcsClient().isCommitRequiredAsync()) {
|
if (await vcsClient.isCommitRequiredAsync()) {
|
||||||
await reviewAndCommitChangesAsync('Install expo-dev-client', {
|
await reviewAndCommitChangesAsync(vcsClient, 'Install expo-dev-client', {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
|
||||||
|
import { getBuildLogsUrl } from './url';
|
||||||
import {
|
import {
|
||||||
AppPlatform,
|
AppPlatform,
|
||||||
BuildFragment,
|
BuildFragment,
|
||||||
@@ -7,7 +8,6 @@ import {
|
|||||||
} from '../../graphql/generated';
|
} from '../../graphql/generated';
|
||||||
import { appPlatformDisplayNames } from '../../platform';
|
import { appPlatformDisplayNames } from '../../platform';
|
||||||
import formatFields from '../../utils/formatFields';
|
import formatFields from '../../utils/formatFields';
|
||||||
import { getBuildLogsUrl } from './url';
|
|
||||||
|
|
||||||
export function formatGraphQLBuild(build: BuildFragment): string {
|
export function formatGraphQLBuild(build: BuildFragment): string {
|
||||||
const actor = getActorName(build);
|
const actor = getActorName(build);
|
||||||
@@ -39,6 +39,10 @@ export function formatGraphQLBuild(build: BuildFragment): string {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Profile',
|
||||||
|
value: build.buildProfile,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Message',
|
label: 'Message',
|
||||||
value: build.message,
|
value: build.message,
|
||||||
@@ -51,10 +55,6 @@ export function formatGraphQLBuild(build: BuildFragment): string {
|
|||||||
label: 'Enterprise Provisioning',
|
label: 'Enterprise Provisioning',
|
||||||
value: build.iosEnterpriseProvisioning?.toLowerCase(),
|
value: build.iosEnterpriseProvisioning?.toLowerCase(),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: 'Release Channel',
|
|
||||||
value: build.releaseChannel,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: 'Channel',
|
label: 'Channel',
|
||||||
value: build.channel,
|
value: build.channel,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import assert from 'assert';
|
|||||||
import indentString from 'indent-string';
|
import indentString from 'indent-string';
|
||||||
import qrcodeTerminal from 'qrcode-terminal';
|
import qrcodeTerminal from 'qrcode-terminal';
|
||||||
|
|
||||||
|
import { getBuildLogsUrl, getInternalDistributionInstallUrl } from './url';
|
||||||
import {
|
import {
|
||||||
AppPlatform,
|
AppPlatform,
|
||||||
BuildError,
|
BuildError,
|
||||||
@@ -13,7 +14,6 @@ import {
|
|||||||
} from '../../graphql/generated';
|
} from '../../graphql/generated';
|
||||||
import Log, { learnMore, link } from '../../log';
|
import Log, { learnMore, link } from '../../log';
|
||||||
import { appPlatformDisplayNames, appPlatformEmojis } from '../../platform';
|
import { appPlatformDisplayNames, appPlatformEmojis } from '../../platform';
|
||||||
import { getBuildLogsUrl, getInternalDistributionInstallUrl } from './url';
|
|
||||||
|
|
||||||
function terminalLinkFallback(url: string, text: string): string {
|
function terminalLinkFallback(url: string, text: string): string {
|
||||||
return `${text} (${url})`;
|
return `${text} (${url})`;
|
||||||
|
|||||||
@@ -10,10 +10,13 @@ import { confirmAsync, promptAsync } from '../../prompts';
|
|||||||
import { formatBytes } from '../../utils/files';
|
import { formatBytes } from '../../utils/files';
|
||||||
import { getTmpDirectory } from '../../utils/paths';
|
import { getTmpDirectory } from '../../utils/paths';
|
||||||
import { endTimer, formatMilliseconds, startTimer } from '../../utils/timer';
|
import { endTimer, formatMilliseconds, startTimer } from '../../utils/timer';
|
||||||
import { getVcsClient } from '../../vcs';
|
import { Client } from '../../vcs/vcs';
|
||||||
|
|
||||||
export async function maybeBailOnRepoStatusAsync(): Promise<void> {
|
export async function maybeBailOnRepoStatusAsync(
|
||||||
if (!(await getVcsClient().isCommitRequiredAsync())) {
|
vcsClient: Client,
|
||||||
|
nonInteractive: boolean
|
||||||
|
): Promise<void> {
|
||||||
|
if (!(await vcsClient.isCommitRequiredAsync())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Log.addNewLineIfNone();
|
Log.addNewLineIfNone();
|
||||||
@@ -28,12 +31,20 @@ export async function maybeBailOnRepoStatusAsync(): Promise<void> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!answer) {
|
if (!answer) {
|
||||||
|
if (nonInteractive) {
|
||||||
|
Log.log('The following files need to be committed:');
|
||||||
|
await vcsClient.showChangedFilesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
throw new Error('Commit all changes. Aborting...');
|
throw new Error('Commit all changes. Aborting...');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function ensureRepoIsCleanAsync(nonInteractive = false): Promise<void> {
|
export async function ensureRepoIsCleanAsync(
|
||||||
if (!(await getVcsClient().isCommitRequiredAsync())) {
|
vcsClient: Client,
|
||||||
|
nonInteractive = false
|
||||||
|
): Promise<void> {
|
||||||
|
if (!(await vcsClient.isCommitRequiredAsync())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Log.addNewLineIfNone();
|
Log.addNewLineIfNone();
|
||||||
@@ -44,25 +55,31 @@ export async function ensureRepoIsCleanAsync(nonInteractive = false): Promise<vo
|
|||||||
)}.`
|
)}.`
|
||||||
);
|
);
|
||||||
if (nonInteractive) {
|
if (nonInteractive) {
|
||||||
|
Log.log('The following files need to be committed:');
|
||||||
|
await vcsClient.showChangedFilesAsync();
|
||||||
|
|
||||||
throw new Error('Commit all changes. Aborting...');
|
throw new Error('Commit all changes. Aborting...');
|
||||||
}
|
}
|
||||||
const answer = await confirmAsync({
|
const answer = await confirmAsync({
|
||||||
message: `Commit changes to git?`,
|
message: `Commit changes to git?`,
|
||||||
});
|
});
|
||||||
if (answer) {
|
if (answer) {
|
||||||
await commitPromptAsync({ commitAllFiles: true });
|
await commitPromptAsync(vcsClient, { commitAllFiles: true });
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Commit all changes. Aborting...');
|
throw new Error('Commit all changes. Aborting...');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function commitPromptAsync({
|
export async function commitPromptAsync(
|
||||||
|
vcsClient: Client,
|
||||||
|
{
|
||||||
initialCommitMessage,
|
initialCommitMessage,
|
||||||
commitAllFiles,
|
commitAllFiles,
|
||||||
}: {
|
}: {
|
||||||
initialCommitMessage?: string;
|
initialCommitMessage?: string;
|
||||||
commitAllFiles?: boolean;
|
commitAllFiles?: boolean;
|
||||||
} = {}): Promise<void> {
|
} = {}
|
||||||
|
): Promise<void> {
|
||||||
const { message } = await promptAsync({
|
const { message } = await promptAsync({
|
||||||
type: 'text',
|
type: 'text',
|
||||||
name: 'message',
|
name: 'message',
|
||||||
@@ -70,14 +87,39 @@ export async function commitPromptAsync({
|
|||||||
initial: initialCommitMessage,
|
initial: initialCommitMessage,
|
||||||
validate: (input: string) => input !== '',
|
validate: (input: string) => input !== '',
|
||||||
});
|
});
|
||||||
await getVcsClient().commitAsync({
|
await vcsClient.commitAsync({
|
||||||
commitAllFiles,
|
commitAllFiles,
|
||||||
commitMessage: message,
|
commitMessage: message,
|
||||||
nonInteractive: false,
|
nonInteractive: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function makeProjectTarballAsync(): Promise<{ path: string; size: number }> {
|
export type LocalFile = {
|
||||||
|
path: string;
|
||||||
|
size: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function makeProjectMetadataFileAsync(archivePath: string): Promise<LocalFile> {
|
||||||
|
const metadataLocation = path.join(getTmpDirectory(), `${uuidv4()}-eas-build-metadata.json`);
|
||||||
|
const archiveContent: string[] = [];
|
||||||
|
|
||||||
|
await tar.list({
|
||||||
|
file: archivePath,
|
||||||
|
onentry: (entry: tar.ReadEntry) => {
|
||||||
|
if (entry.type === 'File' && !entry.path.includes('.git/')) {
|
||||||
|
archiveContent.push(entry.path);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await fs.writeJSON(metadataLocation, {
|
||||||
|
archiveContent,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { path: metadataLocation, size: await fs.stat(metadataLocation).then(stat => stat.size) };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function makeProjectTarballAsync(vcsClient: Client): Promise<LocalFile> {
|
||||||
const spinner = ora('Compressing project files');
|
const spinner = ora('Compressing project files');
|
||||||
|
|
||||||
await fs.mkdirp(getTmpDirectory());
|
await fs.mkdirp(getTmpDirectory());
|
||||||
@@ -99,7 +141,7 @@ export async function makeProjectTarballAsync(): Promise<{ path: string; size: n
|
|||||||
startTimer(compressTimerLabel);
|
startTimer(compressTimerLabel);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await getVcsClient().makeShallowCopyAsync(shallowClonePath);
|
await vcsClient.makeShallowCopyAsync(shallowClonePath);
|
||||||
await tar.create({ cwd: shallowClonePath, file: tarPath, prefix: 'project', gzip: true }, [
|
await tar.create({ cwd: shallowClonePath, file: tarPath, prefix: 'project', gzip: true }, [
|
||||||
'.',
|
'.',
|
||||||
]);
|
]);
|
||||||
@@ -133,11 +175,12 @@ enum ShouldCommitChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function reviewAndCommitChangesAsync(
|
export async function reviewAndCommitChangesAsync(
|
||||||
|
vcsClient: Client,
|
||||||
initialCommitMessage: string,
|
initialCommitMessage: string,
|
||||||
{ nonInteractive, askedFirstTime = true }: { nonInteractive: boolean; askedFirstTime?: boolean }
|
{ nonInteractive, askedFirstTime = true }: { nonInteractive: boolean; askedFirstTime?: boolean }
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (process.env.EAS_BUILD_AUTOCOMMIT) {
|
if (process.env.EAS_BUILD_AUTOCOMMIT) {
|
||||||
await getVcsClient().commitAsync({
|
await vcsClient.commitAsync({
|
||||||
commitMessage: initialCommitMessage,
|
commitMessage: initialCommitMessage,
|
||||||
commitAllFiles: false,
|
commitAllFiles: false,
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
@@ -171,11 +214,11 @@ export async function reviewAndCommitChangesAsync(
|
|||||||
"Aborting, run the command again once you're ready. Make sure to commit any changes you've made."
|
"Aborting, run the command again once you're ready. Make sure to commit any changes you've made."
|
||||||
);
|
);
|
||||||
} else if (selected === ShouldCommitChanges.Yes) {
|
} else if (selected === ShouldCommitChanges.Yes) {
|
||||||
await commitPromptAsync({ initialCommitMessage });
|
await commitPromptAsync(vcsClient, { initialCommitMessage });
|
||||||
Log.withTick('Committed changes.');
|
Log.withTick('Committed changes.');
|
||||||
} else if (selected === ShouldCommitChanges.ShowDiffFirst) {
|
} else if (selected === ShouldCommitChanges.ShowDiffFirst) {
|
||||||
await getVcsClient().showDiffAsync();
|
await vcsClient.showDiffAsync();
|
||||||
await reviewAndCommitChangesAsync(initialCommitMessage, {
|
await reviewAndCommitChangesAsync(vcsClient, initialCommitMessage, {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
askedFirstTime: false,
|
askedFirstTime: false,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,21 +7,13 @@ import Log, { link } from '../../log';
|
|||||||
|
|
||||||
type AndroidResourceClass = Exclude<
|
type AndroidResourceClass = Exclude<
|
||||||
ResourceClass,
|
ResourceClass,
|
||||||
| ResourceClass.M1_EXPERIMENTAL
|
ResourceClass.M1_MEDIUM | ResourceClass.M_MEDIUM | ResourceClass.M_LARGE
|
||||||
| ResourceClass.M1_MEDIUM
|
|
||||||
| ResourceClass.M1_LARGE
|
|
||||||
| ResourceClass.INTEL_MEDIUM
|
|
||||||
| ResourceClass.M_MEDIUM
|
|
||||||
| ResourceClass.M_LARGE
|
|
||||||
>;
|
>;
|
||||||
|
|
||||||
const iosResourceClassToBuildResourceClassMapping: Record<ResourceClass, BuildResourceClass> = {
|
const iosResourceClassToBuildResourceClassMapping: Record<ResourceClass, BuildResourceClass> = {
|
||||||
[ResourceClass.DEFAULT]: BuildResourceClass.IosDefault,
|
[ResourceClass.DEFAULT]: BuildResourceClass.IosDefault,
|
||||||
[ResourceClass.LARGE]: BuildResourceClass.IosLarge,
|
[ResourceClass.LARGE]: BuildResourceClass.IosLarge,
|
||||||
[ResourceClass.M1_EXPERIMENTAL]: BuildResourceClass.IosMMedium,
|
|
||||||
[ResourceClass.M1_MEDIUM]: BuildResourceClass.IosMMedium,
|
[ResourceClass.M1_MEDIUM]: BuildResourceClass.IosMMedium,
|
||||||
[ResourceClass.M1_LARGE]: BuildResourceClass.IosMLarge,
|
|
||||||
[ResourceClass.INTEL_MEDIUM]: BuildResourceClass.IosIntelMedium,
|
|
||||||
[ResourceClass.MEDIUM]: BuildResourceClass.IosMedium,
|
[ResourceClass.MEDIUM]: BuildResourceClass.IosMedium,
|
||||||
[ResourceClass.M_MEDIUM]: BuildResourceClass.IosMMedium,
|
[ResourceClass.M_MEDIUM]: BuildResourceClass.IosMMedium,
|
||||||
[ResourceClass.M_LARGE]: BuildResourceClass.IosMLarge,
|
[ResourceClass.M_LARGE]: BuildResourceClass.IosMLarge,
|
||||||
@@ -57,15 +49,7 @@ export async function resolveBuildResourceClassAsync<T extends Platform>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resolveAndroidResourceClass(selectedResourceClass?: ResourceClass): BuildResourceClass {
|
function resolveAndroidResourceClass(selectedResourceClass?: ResourceClass): BuildResourceClass {
|
||||||
if (
|
if (selectedResourceClass && ResourceClass.M1_MEDIUM === selectedResourceClass) {
|
||||||
selectedResourceClass &&
|
|
||||||
[
|
|
||||||
ResourceClass.M1_EXPERIMENTAL,
|
|
||||||
ResourceClass.M1_MEDIUM,
|
|
||||||
ResourceClass.M1_LARGE,
|
|
||||||
ResourceClass.INTEL_MEDIUM,
|
|
||||||
].includes(selectedResourceClass)
|
|
||||||
) {
|
|
||||||
throw new Error(`Resource class ${selectedResourceClass} is only available for iOS builds`);
|
throw new Error(`Resource class ${selectedResourceClass} is only available for iOS builds`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +72,7 @@ function resolveIosResourceClass(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([ResourceClass.M1_EXPERIMENTAL, ResourceClass.M1_MEDIUM].includes(resourceClass)) {
|
if (ResourceClass.M1_MEDIUM === resourceClass) {
|
||||||
Log.warn(
|
Log.warn(
|
||||||
`Resource class ${chalk.bold(resourceClass)} is deprecated. Use ${chalk.bold(
|
`Resource class ${chalk.bold(resourceClass)} is deprecated. Use ${chalk.bold(
|
||||||
'm-medium'
|
'm-medium'
|
||||||
@@ -96,7 +80,7 @@ function resolveIosResourceClass(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([ResourceClass.M_LARGE, ResourceClass.M1_LARGE].includes(resourceClass)) {
|
if (ResourceClass.M_LARGE === resourceClass) {
|
||||||
Log.warn(
|
Log.warn(
|
||||||
`Resource class ${chalk.bold(resourceClass)} is deprecated. Use ${chalk.bold(
|
`Resource class ${chalk.bold(resourceClass)} is deprecated. Use ${chalk.bold(
|
||||||
'large'
|
'large'
|
||||||
|
|||||||
@@ -50,3 +50,31 @@ export function getUpdateGroupUrl(
|
|||||||
getExpoWebsiteBaseUrl()
|
getExpoWebsiteBaseUrl()
|
||||||
).toString();
|
).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getUpdatePreviewUrl({
|
||||||
|
message,
|
||||||
|
updateRuntimeVersion,
|
||||||
|
createdAt,
|
||||||
|
slug,
|
||||||
|
projectId,
|
||||||
|
group,
|
||||||
|
}: {
|
||||||
|
message?: string;
|
||||||
|
updateRuntimeVersion: string;
|
||||||
|
createdAt: string;
|
||||||
|
slug: string;
|
||||||
|
projectId: string;
|
||||||
|
group: string;
|
||||||
|
}): string {
|
||||||
|
const previewUrl = new URL(`/preview/update`, getExpoWebsiteBaseUrl());
|
||||||
|
if (message) {
|
||||||
|
//previewUrl.searchParams.append('message', message);
|
||||||
|
}
|
||||||
|
|
||||||
|
previewUrl.searchParams.append('updateRuntimeVersion', updateRuntimeVersion);
|
||||||
|
//previewUrl.searchParams.append('createdAt', createdAt);
|
||||||
|
previewUrl.searchParams.append('slug', slug);
|
||||||
|
previewUrl.searchParams.append('projectId', projectId);
|
||||||
|
previewUrl.searchParams.append('group', group);
|
||||||
|
return previewUrl.toString();
|
||||||
|
}
|
||||||
@@ -3,9 +3,9 @@ import chalk from 'chalk';
|
|||||||
import nullthrows from 'nullthrows';
|
import nullthrows from 'nullthrows';
|
||||||
import semver from 'semver';
|
import semver from 'semver';
|
||||||
|
|
||||||
|
import { updateAppJsonConfigAsync } from './appJson';
|
||||||
import Log from '../../log';
|
import Log from '../../log';
|
||||||
import { promptAsync } from '../../prompts';
|
import { promptAsync } from '../../prompts';
|
||||||
import { updateAppJsonConfigAsync } from './appJson';
|
|
||||||
|
|
||||||
export function ensureStaticConfigExists(projectDir: string): void {
|
export function ensureStaticConfigExists(projectDir: string): void {
|
||||||
const paths = getConfigFilePaths(projectDir);
|
const paths = getConfigFilePaths(projectDir);
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ import fs from 'fs-extra';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import semver from 'semver';
|
import semver from 'semver';
|
||||||
|
|
||||||
|
import { CommonContext } from './context';
|
||||||
import Log, { learnMore } from '../log';
|
import Log, { learnMore } from '../log';
|
||||||
import { isPNGAsync } from '../utils/image';
|
import { isPNGAsync } from '../utils/image';
|
||||||
import { getVcsClient } from '../vcs';
|
|
||||||
import { CommonContext } from './context';
|
|
||||||
|
|
||||||
export function checkNodeEnvVariable(ctx: CommonContext<Platform>): void {
|
export function checkNodeEnvVariable(ctx: CommonContext<Platform>): void {
|
||||||
if (ctx.buildProfile.env?.NODE_ENV === 'production') {
|
if (ctx.buildProfile.env?.NODE_ENV === 'production') {
|
||||||
@@ -28,12 +27,12 @@ export async function checkGoogleServicesFileAsync<T extends Platform>(
|
|||||||
if (!googleServicesFilePath) {
|
if (!googleServicesFilePath) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const rootDir = path.normalize(await getVcsClient().getRootPathAsync());
|
const rootDir = path.normalize(await ctx.vcsClient.getRootPathAsync());
|
||||||
const absGoogleServicesFilePath = path.resolve(ctx.projectDir, googleServicesFilePath);
|
const absGoogleServicesFilePath = path.resolve(ctx.projectDir, googleServicesFilePath);
|
||||||
if (
|
if (
|
||||||
(await fs.pathExists(absGoogleServicesFilePath)) &&
|
(await fs.pathExists(absGoogleServicesFilePath)) &&
|
||||||
(!isInsideDirectory(absGoogleServicesFilePath, rootDir) ||
|
(!isInsideDirectory(absGoogleServicesFilePath, rootDir) ||
|
||||||
(await getVcsClient().isFileIgnoredAsync(path.relative(rootDir, absGoogleServicesFilePath))))
|
(await ctx.vcsClient.isFileIgnoredAsync(path.relative(rootDir, absGoogleServicesFilePath))))
|
||||||
) {
|
) {
|
||||||
Log.warn(
|
Log.warn(
|
||||||
`File specified via "${ctx.platform}.googleServicesFile" field in your app.json is not checked in to your repository and won't be uploaded to the builder.`
|
`File specified via "${ctx.platform}.googleServicesFile" field in your app.json is not checked in to your repository and won't be uploaded to the builder.`
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { BranchBasicInfo, ChannelBasicInfo, UpdateChannelInfoWithBranches } from '../utils';
|
||||||
|
|
||||||
|
export const testChannelBasicInfo: ChannelBasicInfo = {
|
||||||
|
id: '9309afc2-9752-40db-8ef7-4abc10744c61',
|
||||||
|
name: 'production',
|
||||||
|
branchMapping:
|
||||||
|
'{"data":[{"branchId":"754bf17f-efc0-46ab-8a59-a03f20e53e9b","branchMappingLogic":{"operand":0.15,"clientKey":"rolloutToken","branchMappingOperator":"hash_lt"}},{"branchId":"6941a8dd-5c0a-48bc-8876-f49c88ed419f","branchMappingLogic":"true"}],"version":0}',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const testBasicBranchInfo1: BranchBasicInfo = {
|
||||||
|
id: '754bf17f-efc0-46ab-8a59-a03f20e53e9b',
|
||||||
|
name: 'wrong-channel',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const testBasicBranchInfo2: BranchBasicInfo = {
|
||||||
|
id: '6941a8dd-5c0a-48bc-8876-f49c88ed419f',
|
||||||
|
name: 'production',
|
||||||
|
};
|
||||||
|
export const channelInfoWithBranches: UpdateChannelInfoWithBranches<BranchBasicInfo> = {
|
||||||
|
...testChannelBasicInfo,
|
||||||
|
updateBranches: [testBasicBranchInfo1, testBasicBranchInfo2],
|
||||||
|
};
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import { testChannelBasicInfo } from './branch-mapping-fixtures';
|
||||||
|
import {
|
||||||
|
emptyBranchMapping,
|
||||||
|
rolloutBranchMapping,
|
||||||
|
standardBranchMapping,
|
||||||
|
} from '../../rollout/__tests__/fixtures';
|
||||||
|
import {
|
||||||
|
BranchMappingValidationError,
|
||||||
|
assertVersion,
|
||||||
|
getAlwaysTrueBranchMapping,
|
||||||
|
getBranchMapping,
|
||||||
|
getEmptyBranchMapping,
|
||||||
|
getStandardBranchId,
|
||||||
|
isAlwaysTrueBranchMapping,
|
||||||
|
isEmptyBranchMapping,
|
||||||
|
} from '../branch-mapping';
|
||||||
|
|
||||||
|
describe(assertVersion, () => {
|
||||||
|
it('throws if the branch mapping is not the correct version', () => {
|
||||||
|
expect(() => assertVersion(testChannelBasicInfo, 5)).toThrowError(BranchMappingValidationError);
|
||||||
|
});
|
||||||
|
it('asserts the correct version', () => {
|
||||||
|
assertVersion(testChannelBasicInfo, 0);
|
||||||
|
expect(getBranchMapping(testChannelBasicInfo.branchMapping).version).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(getEmptyBranchMapping, () => {
|
||||||
|
it('gets an empty branch mapping', () => {
|
||||||
|
expect(isEmptyBranchMapping(getEmptyBranchMapping())).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(isEmptyBranchMapping, () => {
|
||||||
|
it('detects empty branch mappings', () => {
|
||||||
|
expect(isEmptyBranchMapping(emptyBranchMapping)).toBe(true);
|
||||||
|
expect(isEmptyBranchMapping(standardBranchMapping)).toBe(false);
|
||||||
|
expect(isEmptyBranchMapping(rolloutBranchMapping)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(isAlwaysTrueBranchMapping, () => {
|
||||||
|
it('detects always true branch mappings', () => {
|
||||||
|
expect(isAlwaysTrueBranchMapping(standardBranchMapping)).toBe(true);
|
||||||
|
expect(isAlwaysTrueBranchMapping(rolloutBranchMapping)).toBe(false);
|
||||||
|
expect(isAlwaysTrueBranchMapping(emptyBranchMapping)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(getAlwaysTrueBranchMapping, () => {
|
||||||
|
it('gets an always true branch mapping', () => {
|
||||||
|
const alwaysTrueBranchMapping = getAlwaysTrueBranchMapping('test-id');
|
||||||
|
expect(isAlwaysTrueBranchMapping(alwaysTrueBranchMapping)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(getStandardBranchId, () => {
|
||||||
|
it('throws if the branch mapping is not a standard mapping', () => {
|
||||||
|
expect(() => getStandardBranchId(testChannelBasicInfo)).toThrowError(
|
||||||
|
BranchMappingValidationError
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('gets a standard branch id', () => {
|
||||||
|
const channelObjectWithStandardMapping = { ...testChannelBasicInfo };
|
||||||
|
channelObjectWithStandardMapping.branchMapping = JSON.stringify(standardBranchMapping);
|
||||||
|
expect(getStandardBranchId(channelObjectWithStandardMapping)).toBe(
|
||||||
|
standardBranchMapping.data[0].branchId
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
import { UpdateChannelBasicInfoFragment } from '../../graphql/generated';
|
||||||
|
import { UpdateBranchObject, UpdateChannelObject } from '../../graphql/queries/ChannelQuery';
|
||||||
|
|
||||||
|
export const testUpdateBranch1: UpdateBranchObject = {
|
||||||
|
id: '754bf17f-efc0-46ab-8a59-a03f20e53e9b',
|
||||||
|
name: 'wrong-channel',
|
||||||
|
updateGroups: [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: 'bbe3e63a-f620-4603-8c4d-258f3e51590d',
|
||||||
|
group: '16ca6dba-e63b-48b0-baa3-15a894ee9434',
|
||||||
|
message: 'fix bug',
|
||||||
|
createdAt: '2023-07-17T22:48:59.278Z',
|
||||||
|
runtimeVersion: 'exposdk:48.0.0',
|
||||||
|
platform: 'ios',
|
||||||
|
manifestFragment:
|
||||||
|
'{"extra":{"expoClient":{"ios":{"supportsTablet":true,"bundleIdentifier":"com.quintest113.updatestest4"},"web":{"favicon":"./assets/favicon.png"},"icon":"./assets/icon.png","name":"updates-test-4","slug":"updates-test-4","extra":{"eas":{"projectId":"789a8784-0186-4f41-8bd2-7f219291a2b8"}},"splash":{"image":"./assets/splash.png","resizeMode":"contain","backgroundColor":"#ffffff"},"android":{"package":"com.quintest113.updatestest4","adaptiveIcon":{"backgroundColor":"#ffffff","foregroundImage":"./assets/adaptive-icon.png"}},"updates":{"url":"https://u.expo.dev/789a8784-0186-4f41-8bd2-7f219291a2b8"},"version":"1.0.0","platforms":["ios","android"],"sdkVersion":"48.0.0","orientation":"portrait","runtimeVersion":{"policy":"sdkVersion"},"currentFullName":"@quintest113/updates-test-4","originalFullName":"@quintest113/updates-test-4","userInterfaceStyle":"light","assetBundlePatterns":["**/*"]}},"assets":[{"bundleKey":"7d40544b395c5949f4646f5e150fe020","fileSHA256":"LN_rjlzN55dvcBL7jM5zryIpAp7iHU8VCf7RFIccbNg","storageKey":"Pi65ns2w9xLI5G_m9aGrJ5wK5FpWcGcInSSFwnA_0aA","contentType":"image/png","fileExtension":".png"},{"bundleKey":"cdd04e13d4ec83ff0cd13ec8dabdc341","fileSHA256":"irHji01LPI9giDYxtJo9bTPHaEHzuUcCMTlb4xn90Ks","storageKey":"oJZ9JX1m8IKNDgnIqcKOo5KUFOoa_NCaxruigZu65E0","contentType":"image/png","fileExtension":".png"},{"bundleKey":"a132ecc4ba5c1517ff83c0fb321bc7fc","fileSHA256":"K3RDqaWOksoRpXW9wRWYYVpX3eMFzYznDWAmBrMCzZg","storageKey":"X3OAkUu5P_LqyKFx1bNkA_qDQFLlSjCYNAlVEaOamHE","contentType":"image/png","fileExtension":".png"},{"bundleKey":"0ea69b5077e7c4696db85dbcba75b0e1","fileSHA256":"Pd0Hc-0n4j0leJ8wFzGzrEVa2L5aonoKG4OPicbSciU","storageKey":"FwJkAbrRNahBGX1KXnTAgiiwyzENu3D7DeEzCwmInyo","contentType":"image/png","fileExtension":".png"},{"bundleKey":"f5b790e2ac193b3d41015edb3551f9b8","fileSHA256":"vCfIss7SLYKt1AAi0hxjsgOMEDceLJB6ZWjQf6gTU4k","storageKey":"GQtYm7r0G9N3WJyrymjZ26p8eNMIb7rzqwSVgGco5EU","contentType":"image/png","fileExtension":".png"},{"bundleKey":"5223c8d9b0d08b82a5670fb5f71faf78","fileSHA256":"LJXowmdeeZM1_QBGuwfb2gbLfs_1HtN5IsYwnWGcxNM","storageKey":"jeqOrdQBNY4hgl04B3wtGs8QNWMDmjvbjIMUXPtOBpw","contentType":"image/png","fileExtension":".png"},{"bundleKey":"02d5848f62ade9617cf96894727d32e7","fileSHA256":"u1-8zHOj9oxS8maH-7JbShJIq1-KEV7FXnrGpEUcR-4","storageKey":"LMUvUZZpdGFb7KqaB_l9N4TXTkFn1Pw92ws9fWDNZPw","contentType":"model/gltf-binary","fileExtension":".glb"},{"bundleKey":"6013c27e421ab23c826713af3ebec891","fileSHA256":"dutQNnvzTCS4NgwvbeMbvhro5QnW0GJUeY_4rrvlcLY","storageKey":"85KB9Jr7eqz1Jwt1mYQGRzTy6ZXyO0j7ZcaLImCbzkU","contentType":"image/jpeg","fileExtension":".jpg"}],"launchAsset":{"bundleKey":"3d0ac172fe41ab941a8c68cd7e3a17ee","fileSHA256":"jjnar-ZPrb_g0Bx5tcFfjJrP3v8HdeAlXMk__xN7I1U","storageKey":"bimf-UXq5IFH5nNHCIHdcQ2MyJwTnwDVBb-P8hr2CZw","contentType":"application/javascript","fileExtension":".bundle"}}',
|
||||||
|
isRollBackToEmbedded: false,
|
||||||
|
manifestPermalink: 'https://u.expo.dev/update/bbe3e63a-f620-4603-8c4d-258f3e51590d',
|
||||||
|
gitCommitHash: 'e91c5bb733eff675dd8a3722ed5d4f885a4b10ce',
|
||||||
|
actor: {
|
||||||
|
__typename: 'User',
|
||||||
|
id: '026ace2b-c1f8-4a02-818d-6b52c100bdf2',
|
||||||
|
username: 'quintest113',
|
||||||
|
},
|
||||||
|
branch: {
|
||||||
|
id: '754bf17f-efc0-46ab-8a59-a03f20e53e9b',
|
||||||
|
name: 'wrong-channel',
|
||||||
|
__typename: 'UpdateBranch',
|
||||||
|
},
|
||||||
|
codeSigningInfo: null,
|
||||||
|
__typename: 'Update',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'db22401b-d321-4629-a068-21d6d1f19c02',
|
||||||
|
group: '16ca6dba-e63b-48b0-baa3-15a894ee9434',
|
||||||
|
message: 'fix bug',
|
||||||
|
createdAt: '2023-07-17T22:48:59.278Z',
|
||||||
|
runtimeVersion: 'exposdk:48.0.0',
|
||||||
|
platform: 'android',
|
||||||
|
manifestFragment:
|
||||||
|
'{"extra":{"expoClient":{"ios":{"supportsTablet":true,"bundleIdentifier":"com.quintest113.updatestest4"},"web":{"favicon":"./assets/favicon.png"},"icon":"./assets/icon.png","name":"updates-test-4","slug":"updates-test-4","extra":{"eas":{"projectId":"789a8784-0186-4f41-8bd2-7f219291a2b8"}},"splash":{"image":"./assets/splash.png","resizeMode":"contain","backgroundColor":"#ffffff"},"android":{"package":"com.quintest113.updatestest4","adaptiveIcon":{"backgroundColor":"#ffffff","foregroundImage":"./assets/adaptive-icon.png"}},"updates":{"url":"https://u.expo.dev/789a8784-0186-4f41-8bd2-7f219291a2b8"},"version":"1.0.0","platforms":["ios","android"],"sdkVersion":"48.0.0","orientation":"portrait","runtimeVersion":{"policy":"sdkVersion"},"currentFullName":"@quintest113/updates-test-4","originalFullName":"@quintest113/updates-test-4","userInterfaceStyle":"light","assetBundlePatterns":["**/*"]}},"assets":[{"bundleKey":"778ffc9fe8773a878e9c30a6304784de","fileSHA256":"i2Gkx-9w3JJ1PwSUl2SC9m_UFQ7CPfx3KrZeEDc6-lU","storageKey":"idnX8z03q4vGLzhYMPaQGBADjhqvWe67_afPqJ1vGzM","contentType":"image/png","fileExtension":".png"},{"bundleKey":"376d6a4c7f622917c39feb23671ef71d","fileSHA256":"QJsG0VpGM-5viirUgoKq0M4zQ_TYyPfLhAhN5mrM54I","storageKey":"46DQ95-UdSKiRkQgt5FyXkE1FrKOT9tBZP3nS7auKlg","contentType":"image/png","fileExtension":".png"},{"bundleKey":"c79c3606a1cf168006ad3979763c7e0c","fileSHA256":"kGZm3WiTRq2iMs42ia3vkHtCV_obWT8DY40rlJf2SIQ","storageKey":"k-3xHp3vP8mR36WdccUZVPTRJKpP-zlyboWdkXCIQpQ","contentType":"image/png","fileExtension":".png"},{"bundleKey":"02bc1fa7c0313217bde2d65ccbff40c9","fileSHA256":"_6fuRbdkBbpzkhSVAI99aMneY5X0tpQdsGNGX244IyA","storageKey":"lfqeUjiWFXwowhxVDBEzxsbTOEzYxMLebljy5Bh1mpU","contentType":"image/png","fileExtension":".png"},{"bundleKey":"35ba0eaec5a4f5ed12ca16fabeae451d","fileSHA256":"hM9es7ICUPaeDkczvrTaX0F_BoKJKlrRteYlcHU8IbE","storageKey":"fMoMrsUeB5xWw_Ugn8FqGJf-M2pld2hkXKR704z0MM8","contentType":"image/png","fileExtension":".png"},{"bundleKey":"5223c8d9b0d08b82a5670fb5f71faf78","fileSHA256":"LJXowmdeeZM1_QBGuwfb2gbLfs_1HtN5IsYwnWGcxNM","storageKey":"jeqOrdQBNY4hgl04B3wtGs8QNWMDmjvbjIMUXPtOBpw","contentType":"image/png","fileExtension":".png"},{"bundleKey":"02d5848f62ade9617cf96894727d32e7","fileSHA256":"u1-8zHOj9oxS8maH-7JbShJIq1-KEV7FXnrGpEUcR-4","storageKey":"LMUvUZZpdGFb7KqaB_l9N4TXTkFn1Pw92ws9fWDNZPw","contentType":"model/gltf-binary","fileExtension":".glb"},{"bundleKey":"6013c27e421ab23c826713af3ebec891","fileSHA256":"dutQNnvzTCS4NgwvbeMbvhro5QnW0GJUeY_4rrvlcLY","storageKey":"85KB9Jr7eqz1Jwt1mYQGRzTy6ZXyO0j7ZcaLImCbzkU","contentType":"image/jpeg","fileExtension":".jpg"}],"launchAsset":{"bundleKey":"4cc2ee49988e438ac066827b809721de","fileSHA256":"ctfK72f8EdS1IQmDvJLya38RMSma16beJcMoQToZIe0","storageKey":"QdEoKuCQPvVPA71gY52wqJ9vmtV4ObnCcPE2FP4SIdo","contentType":"application/javascript","fileExtension":".bundle"}}',
|
||||||
|
isRollBackToEmbedded: false,
|
||||||
|
manifestPermalink: 'https://u.expo.dev/update/db22401b-d321-4629-a068-21d6d1f19c02',
|
||||||
|
gitCommitHash: 'e91c5bb733eff675dd8a3722ed5d4f885a4b10ce',
|
||||||
|
actor: {
|
||||||
|
__typename: 'User',
|
||||||
|
id: '026ace2b-c1f8-4a02-818d-6b52c100bdf2',
|
||||||
|
username: 'quintest113',
|
||||||
|
},
|
||||||
|
branch: {
|
||||||
|
id: '754bf17f-efc0-46ab-8a59-a03f20e53e9b',
|
||||||
|
name: 'wrong-channel',
|
||||||
|
__typename: 'UpdateBranch',
|
||||||
|
},
|
||||||
|
codeSigningInfo: null,
|
||||||
|
__typename: 'Update',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
__typename: 'UpdateBranch',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const testUpdateBranch2: UpdateBranchObject = {
|
||||||
|
id: '6941a8dd-5c0a-48bc-8876-f49c88ed419f',
|
||||||
|
name: 'production',
|
||||||
|
updateGroups: [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: 'f04aafc1-0bf5-4548-8c35-726b467e7c40',
|
||||||
|
group: 'e40ad156-e9af-4cc2-8e9d-c7b5c328db48',
|
||||||
|
message: 'fix bug',
|
||||||
|
createdAt: '2023-06-23T23:37:10.004Z',
|
||||||
|
runtimeVersion: 'exposdk:48.0.0',
|
||||||
|
platform: 'ios',
|
||||||
|
manifestFragment:
|
||||||
|
'{"extra":{"expoClient":{"ios":{"supportsTablet":true,"bundleIdentifier":"com.quintest113.updatestest4"},"web":{"favicon":"./assets/favicon.png"},"icon":"./assets/icon.png","name":"updates-test-4","slug":"updates-test-4","extra":{"eas":{"projectId":"789a8784-0186-4f41-8bd2-7f219291a2b8"}},"splash":{"image":"./assets/splash.png","resizeMode":"contain","backgroundColor":"#ffffff"},"android":{"package":"com.quintest113.updatestest4","adaptiveIcon":{"backgroundColor":"#ffffff","foregroundImage":"./assets/adaptive-icon.png"}},"updates":{"url":"https://u.expo.dev/789a8784-0186-4f41-8bd2-7f219291a2b8"},"version":"1.0.0","platforms":["ios","android"],"sdkVersion":"48.0.0","orientation":"portrait","runtimeVersion":{"policy":"sdkVersion"},"currentFullName":"@quintest113/updates-test-4","originalFullName":"@quintest113/updates-test-4","userInterfaceStyle":"light","assetBundlePatterns":["**/*"]}},"assets":[{"bundleKey":"7d40544b395c5949f4646f5e150fe020","fileSHA256":"LN_rjlzN55dvcBL7jM5zryIpAp7iHU8VCf7RFIccbNg","storageKey":"Pi65ns2w9xLI5G_m9aGrJ5wK5FpWcGcInSSFwnA_0aA","contentType":"image/png","fileExtension":".png"},{"bundleKey":"cdd04e13d4ec83ff0cd13ec8dabdc341","fileSHA256":"irHji01LPI9giDYxtJo9bTPHaEHzuUcCMTlb4xn90Ks","storageKey":"oJZ9JX1m8IKNDgnIqcKOo5KUFOoa_NCaxruigZu65E0","contentType":"image/png","fileExtension":".png"},{"bundleKey":"a132ecc4ba5c1517ff83c0fb321bc7fc","fileSHA256":"K3RDqaWOksoRpXW9wRWYYVpX3eMFzYznDWAmBrMCzZg","storageKey":"X3OAkUu5P_LqyKFx1bNkA_qDQFLlSjCYNAlVEaOamHE","contentType":"image/png","fileExtension":".png"},{"bundleKey":"0ea69b5077e7c4696db85dbcba75b0e1","fileSHA256":"Pd0Hc-0n4j0leJ8wFzGzrEVa2L5aonoKG4OPicbSciU","storageKey":"FwJkAbrRNahBGX1KXnTAgiiwyzENu3D7DeEzCwmInyo","contentType":"image/png","fileExtension":".png"},{"bundleKey":"f5b790e2ac193b3d41015edb3551f9b8","fileSHA256":"vCfIss7SLYKt1AAi0hxjsgOMEDceLJB6ZWjQf6gTU4k","storageKey":"GQtYm7r0G9N3WJyrymjZ26p8eNMIb7rzqwSVgGco5EU","contentType":"image/png","fileExtension":".png"},{"bundleKey":"5223c8d9b0d08b82a5670fb5f71faf78","fileSHA256":"LJXowmdeeZM1_QBGuwfb2gbLfs_1HtN5IsYwnWGcxNM","storageKey":"jeqOrdQBNY4hgl04B3wtGs8QNWMDmjvbjIMUXPtOBpw","contentType":"image/png","fileExtension":".png"},{"bundleKey":"02d5848f62ade9617cf96894727d32e7","fileSHA256":"u1-8zHOj9oxS8maH-7JbShJIq1-KEV7FXnrGpEUcR-4","storageKey":"LMUvUZZpdGFb7KqaB_l9N4TXTkFn1Pw92ws9fWDNZPw","contentType":"model/gltf-binary","fileExtension":".glb"},{"bundleKey":"6013c27e421ab23c826713af3ebec891","fileSHA256":"dutQNnvzTCS4NgwvbeMbvhro5QnW0GJUeY_4rrvlcLY","storageKey":"85KB9Jr7eqz1Jwt1mYQGRzTy6ZXyO0j7ZcaLImCbzkU","contentType":"image/jpeg","fileExtension":".jpg"}],"launchAsset":{"bundleKey":"76ec49faddaef30ba78fe50edc5ab3d6","fileSHA256":"OLUdgP-fZI2shwGS5qlIk7nWnybL_13RJl5xR85CDBU","storageKey":"OCvI0-34zApbdo3eSGfIaQUbWdZeLeKH-nJSeedKTFs","contentType":"application/javascript","fileExtension":".bundle"}}',
|
||||||
|
isRollBackToEmbedded: false,
|
||||||
|
manifestPermalink: 'https://u.expo.dev/update/f04aafc1-0bf5-4548-8c35-726b467e7c40',
|
||||||
|
gitCommitHash: 'e91c5bb733eff675dd8a3722ed5d4f885a4b10ce',
|
||||||
|
actor: {
|
||||||
|
__typename: 'User',
|
||||||
|
id: '026ace2b-c1f8-4a02-818d-6b52c100bdf2',
|
||||||
|
username: 'quintest113',
|
||||||
|
},
|
||||||
|
branch: {
|
||||||
|
id: '6941a8dd-5c0a-48bc-8876-f49c88ed419f',
|
||||||
|
name: 'production',
|
||||||
|
__typename: 'UpdateBranch',
|
||||||
|
},
|
||||||
|
codeSigningInfo: null,
|
||||||
|
__typename: 'Update',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'd8d15786-2727-407f-bf3e-f4bda7e397d9',
|
||||||
|
group: 'e40ad156-e9af-4cc2-8e9d-c7b5c328db48',
|
||||||
|
message: 'fix bug',
|
||||||
|
createdAt: '2023-06-23T23:37:10.004Z',
|
||||||
|
runtimeVersion: 'exposdk:48.0.0',
|
||||||
|
platform: 'android',
|
||||||
|
manifestFragment:
|
||||||
|
'{"extra":{"expoClient":{"ios":{"supportsTablet":true,"bundleIdentifier":"com.quintest113.updatestest4"},"web":{"favicon":"./assets/favicon.png"},"icon":"./assets/icon.png","name":"updates-test-4","slug":"updates-test-4","extra":{"eas":{"projectId":"789a8784-0186-4f41-8bd2-7f219291a2b8"}},"splash":{"image":"./assets/splash.png","resizeMode":"contain","backgroundColor":"#ffffff"},"android":{"package":"com.quintest113.updatestest4","adaptiveIcon":{"backgroundColor":"#ffffff","foregroundImage":"./assets/adaptive-icon.png"}},"updates":{"url":"https://u.expo.dev/789a8784-0186-4f41-8bd2-7f219291a2b8"},"version":"1.0.0","platforms":["ios","android"],"sdkVersion":"48.0.0","orientation":"portrait","runtimeVersion":{"policy":"sdkVersion"},"currentFullName":"@quintest113/updates-test-4","originalFullName":"@quintest113/updates-test-4","userInterfaceStyle":"light","assetBundlePatterns":["**/*"]}},"assets":[{"bundleKey":"778ffc9fe8773a878e9c30a6304784de","fileSHA256":"i2Gkx-9w3JJ1PwSUl2SC9m_UFQ7CPfx3KrZeEDc6-lU","storageKey":"idnX8z03q4vGLzhYMPaQGBADjhqvWe67_afPqJ1vGzM","contentType":"image/png","fileExtension":".png"},{"bundleKey":"376d6a4c7f622917c39feb23671ef71d","fileSHA256":"QJsG0VpGM-5viirUgoKq0M4zQ_TYyPfLhAhN5mrM54I","storageKey":"46DQ95-UdSKiRkQgt5FyXkE1FrKOT9tBZP3nS7auKlg","contentType":"image/png","fileExtension":".png"},{"bundleKey":"c79c3606a1cf168006ad3979763c7e0c","fileSHA256":"kGZm3WiTRq2iMs42ia3vkHtCV_obWT8DY40rlJf2SIQ","storageKey":"k-3xHp3vP8mR36WdccUZVPTRJKpP-zlyboWdkXCIQpQ","contentType":"image/png","fileExtension":".png"},{"bundleKey":"02bc1fa7c0313217bde2d65ccbff40c9","fileSHA256":"_6fuRbdkBbpzkhSVAI99aMneY5X0tpQdsGNGX244IyA","storageKey":"lfqeUjiWFXwowhxVDBEzxsbTOEzYxMLebljy5Bh1mpU","contentType":"image/png","fileExtension":".png"},{"bundleKey":"35ba0eaec5a4f5ed12ca16fabeae451d","fileSHA256":"hM9es7ICUPaeDkczvrTaX0F_BoKJKlrRteYlcHU8IbE","storageKey":"fMoMrsUeB5xWw_Ugn8FqGJf-M2pld2hkXKR704z0MM8","contentType":"image/png","fileExtension":".png"},{"bundleKey":"5223c8d9b0d08b82a5670fb5f71faf78","fileSHA256":"LJXowmdeeZM1_QBGuwfb2gbLfs_1HtN5IsYwnWGcxNM","storageKey":"jeqOrdQBNY4hgl04B3wtGs8QNWMDmjvbjIMUXPtOBpw","contentType":"image/png","fileExtension":".png"},{"bundleKey":"02d5848f62ade9617cf96894727d32e7","fileSHA256":"u1-8zHOj9oxS8maH-7JbShJIq1-KEV7FXnrGpEUcR-4","storageKey":"LMUvUZZpdGFb7KqaB_l9N4TXTkFn1Pw92ws9fWDNZPw","contentType":"model/gltf-binary","fileExtension":".glb"},{"bundleKey":"6013c27e421ab23c826713af3ebec891","fileSHA256":"dutQNnvzTCS4NgwvbeMbvhro5QnW0GJUeY_4rrvlcLY","storageKey":"85KB9Jr7eqz1Jwt1mYQGRzTy6ZXyO0j7ZcaLImCbzkU","contentType":"image/jpeg","fileExtension":".jpg"}],"launchAsset":{"bundleKey":"f4c6981626dbabcf51078d07a1611f4e","fileSHA256":"q-k1hSWl49aXpSg4jOSaVVnYFSFL6xf7HVl_TQE-ObM","storageKey":"FwK1PaZqtpL0QZIgaHsxU1AV93JhMdu9K1wZizXROAc","contentType":"application/javascript","fileExtension":".bundle"}}',
|
||||||
|
isRollBackToEmbedded: false,
|
||||||
|
manifestPermalink: 'https://u.expo.dev/update/d8d15786-2727-407f-bf3e-f4bda7e397d9',
|
||||||
|
gitCommitHash: 'e91c5bb733eff675dd8a3722ed5d4f885a4b10ce',
|
||||||
|
actor: {
|
||||||
|
__typename: 'User',
|
||||||
|
id: '026ace2b-c1f8-4a02-818d-6b52c100bdf2',
|
||||||
|
username: 'quintest113',
|
||||||
|
},
|
||||||
|
branch: {
|
||||||
|
id: '6941a8dd-5c0a-48bc-8876-f49c88ed419f',
|
||||||
|
name: 'production',
|
||||||
|
__typename: 'UpdateBranch',
|
||||||
|
},
|
||||||
|
codeSigningInfo: null,
|
||||||
|
__typename: 'Update',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
__typename: 'UpdateBranch',
|
||||||
|
};
|
||||||
|
export const testChannelObject: UpdateChannelObject = {
|
||||||
|
id: '9309afc2-9752-40db-8ef7-4abc10744c61',
|
||||||
|
name: 'production',
|
||||||
|
createdAt: '2023-05-16T21:26:16.946Z',
|
||||||
|
updatedAt: '2023-05-16T21:26:16.946Z',
|
||||||
|
branchMapping:
|
||||||
|
'{"data":[{"branchId":"754bf17f-efc0-46ab-8a59-a03f20e53e9b","branchMappingLogic":{"operand":0.15,"clientKey":"rolloutToken","branchMappingOperator":"hash_lt"}},{"branchId":"6941a8dd-5c0a-48bc-8876-f49c88ed419f","branchMappingLogic":"true"}],"version":0}',
|
||||||
|
updateBranches: [testUpdateBranch1, testUpdateBranch2],
|
||||||
|
__typename: 'UpdateChannel',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const testBasicChannelInfo: UpdateChannelBasicInfoFragment = {
|
||||||
|
id: '9309afc2-9752-40db-8ef7-4abc10744c61',
|
||||||
|
name: 'production',
|
||||||
|
branchMapping:
|
||||||
|
'{"data":[{"branchId":"754bf17f-efc0-46ab-8a59-a03f20e53e9b","branchMappingLogic":{"operand":0.1,"clientKey":"rolloutToken","branchMappingOperator":"hash_lt"}},{"branchId":"6941a8dd-5c0a-48bc-8876-f49c88ed419f","branchMappingLogic":"true"}],"version":0}',
|
||||||
|
__typename: 'UpdateChannel',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const testBasicChannelInfo2: UpdateChannelBasicInfoFragment = {
|
||||||
|
id: '9d5c7bf0-d52d-474c-9140-c1bad7f0de9d',
|
||||||
|
name: 'staging',
|
||||||
|
branchMapping:
|
||||||
|
'{"data":[{"branchId":"d7d68e32-d9c9-4a8d-8d1b-21e53100a5e8","branchMappingLogic":{"operand":0.1,"clientKey":"rolloutToken","branchMappingOperator":"hash_lt"}},{"branchId":"f9f708c2-0c91-4360-b2a4-0b61834aef4a","branchMappingLogic":"true"}],"version":0}',
|
||||||
|
__typename: 'UpdateChannel',
|
||||||
|
};
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { format } from '@expo/timeago.js';
|
||||||
|
|
||||||
|
import { testChannelObject, testUpdateBranch1, testUpdateBranch2 } from './fixtures';
|
||||||
|
import { getAlwaysTrueBranchMapping, getEmptyBranchMapping } from '../branch-mapping';
|
||||||
|
import { getDescriptionByBranchId } from '../print-utils';
|
||||||
|
|
||||||
|
describe(getDescriptionByBranchId, () => {
|
||||||
|
it('should get descriptions for multiple branches with multiple update groups', () => {
|
||||||
|
const descriptionByBranchId = getDescriptionByBranchId(testChannelObject);
|
||||||
|
expect(Object.values(descriptionByBranchId)).toHaveLength(2);
|
||||||
|
expect(descriptionByBranchId[testUpdateBranch1.id]).toEqual({
|
||||||
|
branch: 'wrong-channel',
|
||||||
|
branchRolloutPercentage: 15,
|
||||||
|
update: {
|
||||||
|
codeSigningKey: undefined,
|
||||||
|
group: '16ca6dba-e63b-48b0-baa3-15a894ee9434',
|
||||||
|
isRollBackToEmbedded: false,
|
||||||
|
message: `"fix bug" (${format('2023-07-17T22:48:59.278Z', 'en_US')} by quintest113)`,
|
||||||
|
platforms: 'android, ios',
|
||||||
|
runtimeVersion: 'exposdk:48.0.0',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(descriptionByBranchId[testUpdateBranch2.id]).toEqual({
|
||||||
|
branch: 'production',
|
||||||
|
branchRolloutPercentage: 85,
|
||||||
|
update: {
|
||||||
|
codeSigningKey: undefined,
|
||||||
|
group: 'e40ad156-e9af-4cc2-8e9d-c7b5c328db48',
|
||||||
|
isRollBackToEmbedded: false,
|
||||||
|
message: `"fix bug" (${format('2023-06-23T23:37:10.004Z', 'en_US')} by quintest113)`,
|
||||||
|
platforms: 'android, ios',
|
||||||
|
runtimeVersion: 'exposdk:48.0.0',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should get descriptions for branches with no updates', () => {
|
||||||
|
const noUpdatesBranch = { ...testUpdateBranch1, updateGroups: [] };
|
||||||
|
const oneBranchChannel = {
|
||||||
|
...testChannelObject,
|
||||||
|
updateBranches: [noUpdatesBranch],
|
||||||
|
branchMapping: JSON.stringify(getAlwaysTrueBranchMapping(noUpdatesBranch.id)),
|
||||||
|
};
|
||||||
|
const descriptionByBranchId = getDescriptionByBranchId(oneBranchChannel);
|
||||||
|
expect(Object.values(descriptionByBranchId)).toHaveLength(1);
|
||||||
|
expect(descriptionByBranchId[testUpdateBranch1.id]).toEqual({ branch: 'wrong-channel' });
|
||||||
|
});
|
||||||
|
it('should get descriptions for no branches', () => {
|
||||||
|
const noBranchChannel = {
|
||||||
|
...testChannelObject,
|
||||||
|
updateBranches: [],
|
||||||
|
branchMapping: JSON.stringify(getEmptyBranchMapping()),
|
||||||
|
};
|
||||||
|
const descriptionByBranchId = getDescriptionByBranchId(noBranchChannel);
|
||||||
|
expect(Object.values(descriptionByBranchId)).toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { channelInfoWithBranches, testBasicBranchInfo1 } from './branch-mapping-fixtures';
|
||||||
|
import { getUpdateBranch } from '../utils';
|
||||||
|
|
||||||
|
describe(getUpdateBranch, () => {
|
||||||
|
it('gets the update branch', () => {
|
||||||
|
expect(getUpdateBranch(channelInfoWithBranches, '754bf17f-efc0-46ab-8a59-a03f20e53e9b')).toBe(
|
||||||
|
testBasicBranchInfo1
|
||||||
|
);
|
||||||
|
expect(() => getUpdateBranch(channelInfoWithBranches, 'not-a-branch-ID')).toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
import { EASUpdateAction, EASUpdateContext, NonInteractiveError } from '../../eas-update/utils';
|
||||||
|
import { UpdateChannelBasicInfoFragment } from '../../graphql/generated';
|
||||||
|
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
|
||||||
|
import {
|
||||||
|
Connection,
|
||||||
|
Edge,
|
||||||
|
FilterPagination,
|
||||||
|
QueryParams,
|
||||||
|
selectPaginatedAsync,
|
||||||
|
} from '../../utils/relay';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select a channel for the project.
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {function} options.filterPredicate - A predicate to filter the channels that are shown to the user. It takes a channelInfo object as a parameter and returns a boolean.
|
||||||
|
* @param {string} options.printedType - The type of channel printed to the user. Defaults to 'channel'.
|
||||||
|
* @param {number} options.pageSize - The number of channels to show per page. Defaults to 100.
|
||||||
|
* @param {function} options.beforeEachFilterQuery Optional. If a filter predicate was specified, this callback function will be called before each query.
|
||||||
|
* @args externalQueryParams The query params for the pagination.
|
||||||
|
* @args totalNodesFetched The total number of nodes fetched so far.
|
||||||
|
* @args dataset The dataset so far.
|
||||||
|
* @param {function} options.afterEachFilterQuery Optional. If a filter predicate was specified, this callback function will be called after each query.
|
||||||
|
* @args externalQueryParams The query params for the pagination.
|
||||||
|
* @args totalNodesFetched The total number of nodes fetched so far.
|
||||||
|
* @args dataset The dataset so far.
|
||||||
|
* @args willFetchAgain If the query will fetch again to get a complete page.
|
||||||
|
*/
|
||||||
|
export class SelectChannel implements EASUpdateAction<UpdateChannelBasicInfoFragment | null> {
|
||||||
|
constructor(
|
||||||
|
private options: {
|
||||||
|
filterPredicate?: (channelInfo: UpdateChannelBasicInfoFragment) => boolean;
|
||||||
|
printedType?: string;
|
||||||
|
pageSize?: number;
|
||||||
|
beforeEachFilterQuery?: (
|
||||||
|
externalQueryParams: QueryParams,
|
||||||
|
totalNodesFetched: number,
|
||||||
|
dataset: Edge<UpdateChannelBasicInfoFragment>[]
|
||||||
|
) => void;
|
||||||
|
afterEachFilterQuery?: (
|
||||||
|
externalQueryParams: QueryParams,
|
||||||
|
totalNodesFetched: number,
|
||||||
|
dataset: Edge<UpdateChannelBasicInfoFragment>[],
|
||||||
|
willFetchAgain: boolean
|
||||||
|
) => void;
|
||||||
|
} = {}
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async queryAsync(
|
||||||
|
ctx: EASUpdateContext,
|
||||||
|
queryParams: QueryParams
|
||||||
|
): Promise<Connection<UpdateChannelBasicInfoFragment>> {
|
||||||
|
const { graphqlClient, app } = ctx;
|
||||||
|
const { projectId } = app;
|
||||||
|
return await ChannelQuery.viewUpdateChannelsBasicInfoPaginatedOnAppAsync(graphqlClient, {
|
||||||
|
appId: projectId,
|
||||||
|
...queryParams,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async filterQueryAsync(
|
||||||
|
ctx: EASUpdateContext,
|
||||||
|
queryParams: QueryParams,
|
||||||
|
filterPredicate: (channelInfo: UpdateChannelBasicInfoFragment) => boolean
|
||||||
|
): Promise<Connection<UpdateChannelBasicInfoFragment>> {
|
||||||
|
const queryAsync = async (
|
||||||
|
queryParams: QueryParams
|
||||||
|
): Promise<Connection<UpdateChannelBasicInfoFragment>> =>
|
||||||
|
await this.queryAsync(ctx, queryParams);
|
||||||
|
return await FilterPagination.getPageAsync({
|
||||||
|
queryParams,
|
||||||
|
queryAsync,
|
||||||
|
filterPredicate,
|
||||||
|
beforeEachQuery: this.options.beforeEachFilterQuery,
|
||||||
|
afterEachQuery: this.options.afterEachFilterQuery,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async runAsync(ctx: EASUpdateContext): Promise<UpdateChannelBasicInfoFragment | null> {
|
||||||
|
const { nonInteractive } = ctx;
|
||||||
|
const { filterPredicate } = this.options;
|
||||||
|
const printedType = this.options.printedType ?? 'channel';
|
||||||
|
const pageSize = this.options.pageSize ?? 100;
|
||||||
|
if (nonInteractive) {
|
||||||
|
throw new NonInteractiveError(
|
||||||
|
`${printedType} selection cannot be run in non-interactive mode.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryAsync = async (
|
||||||
|
queryParams: QueryParams
|
||||||
|
): Promise<Connection<UpdateChannelBasicInfoFragment>> =>
|
||||||
|
filterPredicate
|
||||||
|
? this.filterQueryAsync(ctx, queryParams, filterPredicate)
|
||||||
|
: this.queryAsync(ctx, queryParams);
|
||||||
|
|
||||||
|
const getTitleAsync = async (channelInfo: UpdateChannelBasicInfoFragment): Promise<string> =>
|
||||||
|
channelInfo.name;
|
||||||
|
|
||||||
|
return await selectPaginatedAsync({
|
||||||
|
queryAsync,
|
||||||
|
getTitleAsync,
|
||||||
|
printedType,
|
||||||
|
pageSize,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import { createCtxMock } from '../../../eas-update/__tests__/fixtures';
|
||||||
|
import { NonInteractiveError } from '../../../eas-update/utils';
|
||||||
|
import { UpdateChannelBasicInfoFragment } from '../../../graphql/generated';
|
||||||
|
import { ChannelQuery } from '../../../graphql/queries/ChannelQuery';
|
||||||
|
import { promptAsync } from '../../../prompts';
|
||||||
|
import { testBasicChannelInfo, testBasicChannelInfo2 } from '../../__tests__/fixtures';
|
||||||
|
import { SelectChannel } from '../SelectChannel';
|
||||||
|
|
||||||
|
jest.mock('../../../prompts');
|
||||||
|
jest.mock('../../../graphql/queries/ChannelQuery');
|
||||||
|
describe(SelectChannel, () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
it('returns null if project has no channels', async () => {
|
||||||
|
const ctx = createCtxMock();
|
||||||
|
jest.mocked(ChannelQuery.viewUpdateChannelsBasicInfoPaginatedOnAppAsync).mockResolvedValue({
|
||||||
|
edges: [],
|
||||||
|
pageInfo: {
|
||||||
|
hasPreviousPage: false,
|
||||||
|
hasNextPage: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const selectChannel = new SelectChannel();
|
||||||
|
const channel = await selectChannel.runAsync(ctx);
|
||||||
|
expect(channel).toBeNull();
|
||||||
|
});
|
||||||
|
it('still prompts if project has only one channel', async () => {
|
||||||
|
const ctx = createCtxMock();
|
||||||
|
jest.mocked(ChannelQuery.viewUpdateChannelsBasicInfoPaginatedOnAppAsync).mockResolvedValue({
|
||||||
|
edges: [{ node: testBasicChannelInfo, cursor: 'cursor' }],
|
||||||
|
pageInfo: {
|
||||||
|
hasPreviousPage: false,
|
||||||
|
hasNextPage: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
jest.mocked(promptAsync).mockImplementation(async () => ({
|
||||||
|
item: testBasicChannelInfo,
|
||||||
|
}));
|
||||||
|
const selectChannel = new SelectChannel();
|
||||||
|
const channel = await selectChannel.runAsync(ctx);
|
||||||
|
expect(channel).toBe(testBasicChannelInfo);
|
||||||
|
expect(promptAsync).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
it('returns the selected channel', async () => {
|
||||||
|
jest.mocked(ChannelQuery.viewUpdateChannelsBasicInfoPaginatedOnAppAsync).mockResolvedValue({
|
||||||
|
edges: [
|
||||||
|
{ node: testBasicChannelInfo, cursor: 'cursor' },
|
||||||
|
{ node: testBasicChannelInfo2, cursor: 'cursor' },
|
||||||
|
],
|
||||||
|
pageInfo: {
|
||||||
|
hasPreviousPage: false,
|
||||||
|
hasNextPage: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
jest.mocked(promptAsync).mockImplementation(async () => ({
|
||||||
|
item: testBasicChannelInfo,
|
||||||
|
}));
|
||||||
|
const ctx = createCtxMock();
|
||||||
|
const selectChannel = new SelectChannel();
|
||||||
|
const channel = await selectChannel.runAsync(ctx);
|
||||||
|
expect(channel).toBe(testBasicChannelInfo);
|
||||||
|
});
|
||||||
|
it('returns the only available channel with a filter', async () => {
|
||||||
|
jest.mocked(ChannelQuery.viewUpdateChannelsBasicInfoPaginatedOnAppAsync).mockResolvedValue({
|
||||||
|
edges: [
|
||||||
|
{ node: testBasicChannelInfo, cursor: 'cursor' },
|
||||||
|
{ node: testBasicChannelInfo2, cursor: 'cursor' },
|
||||||
|
],
|
||||||
|
pageInfo: {
|
||||||
|
hasPreviousPage: false,
|
||||||
|
hasNextPage: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const ctx = createCtxMock();
|
||||||
|
const selectChannel = new SelectChannel({
|
||||||
|
filterPredicate: (channelInfo: UpdateChannelBasicInfoFragment) =>
|
||||||
|
channelInfo === testBasicChannelInfo,
|
||||||
|
});
|
||||||
|
const channel = await selectChannel.runAsync(ctx);
|
||||||
|
expect(channel).toBe(testBasicChannelInfo);
|
||||||
|
});
|
||||||
|
it('throws an error in Non-Interactive Mode', async () => {
|
||||||
|
const ctx = createCtxMock({
|
||||||
|
nonInteractive: true,
|
||||||
|
});
|
||||||
|
const selectChannel = new SelectChannel();
|
||||||
|
|
||||||
|
// dont fail if users are running in non-interactive mode
|
||||||
|
await expect(selectChannel.runAsync(ctx)).rejects.toThrowError(NonInteractiveError);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,214 @@
|
|||||||
|
import { ChannelBasicInfo } from './utils';
|
||||||
|
|
||||||
|
// TODO(quin): move this into a common package with www
|
||||||
|
export type BranchMappingOperator =
|
||||||
|
| '=='
|
||||||
|
| '!='
|
||||||
|
| '<'
|
||||||
|
| '>'
|
||||||
|
| '<='
|
||||||
|
| '>='
|
||||||
|
| 'in'
|
||||||
|
| 'regex'
|
||||||
|
| 'hash_lt'
|
||||||
|
| 'hash_lte'
|
||||||
|
| 'hash_gt'
|
||||||
|
| 'hash_gte';
|
||||||
|
export type BranchMappingObject = {
|
||||||
|
clientKey: string;
|
||||||
|
branchMappingOperator: BranchMappingOperator;
|
||||||
|
operand: number | string | string[];
|
||||||
|
};
|
||||||
|
export type BranchMappingAlwaysTrue = 'true';
|
||||||
|
export type BranchMappingStatement =
|
||||||
|
| ['and' | 'or', ...BranchMappingNode[]]
|
||||||
|
| ['not', BranchMappingNode];
|
||||||
|
export type BranchMappingNode =
|
||||||
|
| BranchMappingAlwaysTrue
|
||||||
|
| BranchMappingObject
|
||||||
|
| BranchMappingStatement;
|
||||||
|
export type BranchMapping = {
|
||||||
|
version: number;
|
||||||
|
data: {
|
||||||
|
branchId: string;
|
||||||
|
branchMappingLogic: BranchMappingNode;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AlwaysTrueBranchMapping = {
|
||||||
|
version: number;
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
branchId: string;
|
||||||
|
branchMappingLogic: BranchMappingAlwaysTrue;
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EmptyBranchMapping = {
|
||||||
|
version: number;
|
||||||
|
data: [];
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getEmptyBranchMapping(): EmptyBranchMapping {
|
||||||
|
return {
|
||||||
|
version: 0,
|
||||||
|
data: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAlwaysTrueBranchMapping(branchId: string): AlwaysTrueBranchMapping {
|
||||||
|
return {
|
||||||
|
version: 0,
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
branchId,
|
||||||
|
branchMappingLogic: 'true',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasEmptyBranchMap(channelInfo: ChannelBasicInfo): boolean {
|
||||||
|
const branchMapping = getBranchMapping(channelInfo.branchMapping);
|
||||||
|
return isEmptyBranchMapping(branchMapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasStandardBranchMap(channelInfo: ChannelBasicInfo): boolean {
|
||||||
|
const branchMapping = getBranchMapping(channelInfo.branchMapping);
|
||||||
|
return isAlwaysTrueBranchMapping(branchMapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getStandardBranchId(channelInfo: ChannelBasicInfo): string {
|
||||||
|
const branchMapping = getBranchMapping(channelInfo.branchMapping);
|
||||||
|
assertAlwaysTrueBranchMapping(branchMapping);
|
||||||
|
return getBranchIdFromStandardMapping(branchMapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isEmptyBranchMapping(
|
||||||
|
branchMapping: BranchMapping
|
||||||
|
): branchMapping is EmptyBranchMapping {
|
||||||
|
return branchMapping.data.length === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isAlwaysTrueBranchMapping(
|
||||||
|
branchMapping: BranchMapping
|
||||||
|
): branchMapping is AlwaysTrueBranchMapping {
|
||||||
|
const numBranches = branchMapping.data.length;
|
||||||
|
if (numBranches !== 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const branchMappingLogic = branchMapping.data[0].branchMappingLogic;
|
||||||
|
return isAlwaysTrue(branchMappingLogic);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBranchIdFromStandardMapping(branchMapping: AlwaysTrueBranchMapping): string {
|
||||||
|
return branchMapping.data[0].branchId;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBranchIds(branchMapping: BranchMapping): string[] {
|
||||||
|
return branchMapping.data.map(data => data.branchId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBranchMapping(branchMappingString: string): BranchMapping {
|
||||||
|
try {
|
||||||
|
return JSON.parse(branchMappingString);
|
||||||
|
} catch {
|
||||||
|
throw new Error(`Could not parse branchMapping string into a JSON: "${branchMappingString}"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNodesFromStatement(statement: BranchMappingStatement): BranchMappingNode[] {
|
||||||
|
return statement.slice(1) as BranchMappingNode[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isAndStatement(
|
||||||
|
statement: BranchMappingStatement
|
||||||
|
): statement is BranchMappingStatement {
|
||||||
|
return statement[0] === 'and';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isStatement(node: BranchMappingNode): node is BranchMappingStatement {
|
||||||
|
return Array.isArray(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isNodeObject(node: BranchMappingNode): node is BranchMappingObject {
|
||||||
|
return typeof node === 'object' && !isStatement(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function andStatement(nodes: BranchMappingNode[]): ['and', ...BranchMappingNode[]] {
|
||||||
|
return ['and', ...nodes];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isAlwaysTrue(node: BranchMappingNode): boolean {
|
||||||
|
return node === 'true';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function alwaysTrue(): BranchMappingAlwaysTrue {
|
||||||
|
return 'true';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function equalsOperator(): BranchMappingOperator {
|
||||||
|
return '==';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hashLtOperator(): BranchMappingOperator {
|
||||||
|
return 'hash_lt';
|
||||||
|
}
|
||||||
|
|
||||||
|
function isVersion(branchMapping: BranchMapping, version: number): boolean {
|
||||||
|
return branchMapping.version === version;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assertVersion(channelInfo: ChannelBasicInfo, version: number): void {
|
||||||
|
const branchMapping = getBranchMapping(channelInfo.branchMapping);
|
||||||
|
if (!isVersion(branchMapping, version)) {
|
||||||
|
throw new BranchMappingValidationError(
|
||||||
|
`Expected branch mapping version ${version}. Received: ${JSON.stringify(branchMapping)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assertStatement(node: BranchMappingNode): asserts node is BranchMappingStatement {
|
||||||
|
if (!isStatement(node)) {
|
||||||
|
throw new BranchMappingValidationError(
|
||||||
|
'Branch mapping node must be a statement. Received: ' + JSON.stringify(node)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assertNodeObject(node: BranchMappingNode): asserts node is BranchMappingObject {
|
||||||
|
if (!isNodeObject(node)) {
|
||||||
|
throw new BranchMappingValidationError(
|
||||||
|
'Branch mapping node must be an object. Received: ' + JSON.stringify(node)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assertNumber(operand: string | number | string[]): asserts operand is number {
|
||||||
|
if (typeof operand !== 'number') {
|
||||||
|
throw new BranchMappingValidationError(
|
||||||
|
'Expected a number. Received: ' + JSON.stringify(operand)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assertString(operand: string | number | string[]): asserts operand is string {
|
||||||
|
if (typeof operand !== 'string') {
|
||||||
|
throw new BranchMappingValidationError(
|
||||||
|
'Expected a string. Received: ' + JSON.stringify(operand)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertAlwaysTrueBranchMapping(
|
||||||
|
branchMapping: BranchMapping
|
||||||
|
): asserts branchMapping is AlwaysTrueBranchMapping {
|
||||||
|
if (!isAlwaysTrueBranchMapping(branchMapping)) {
|
||||||
|
throw new BranchMappingValidationError(
|
||||||
|
'Expected standard branch mapping. Received: ' + JSON.stringify(branchMapping)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BranchMappingValidationError extends Error {}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import chalk from 'chalk';
|
||||||
|
|
||||||
|
import { assertVersion, hasEmptyBranchMap, hasStandardBranchMap } from './branch-mapping';
|
||||||
|
import { UpdateChannelObject } from '../graphql/queries/ChannelQuery';
|
||||||
|
import Log from '../log';
|
||||||
|
import { getRollout, isRollout } from '../rollout/branch-mapping';
|
||||||
|
import {
|
||||||
|
FormattedBranchDescription,
|
||||||
|
formatBranch,
|
||||||
|
getUpdateGroupDescriptionsWithBranch,
|
||||||
|
} from '../update/utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log all the branches associated with the channel and their most recent update group
|
||||||
|
*/
|
||||||
|
export function logChannelDetails(channel: UpdateChannelObject): void {
|
||||||
|
assertVersion(channel, 0);
|
||||||
|
const doesSupportMapping =
|
||||||
|
hasEmptyBranchMap(channel) || hasStandardBranchMap(channel) || isRollout(channel);
|
||||||
|
if (!doesSupportMapping) {
|
||||||
|
Log.log(chalk.dim('Custom branch mapping detected.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const branchDescriptionByBranchId = getDescriptionByBranchId(channel);
|
||||||
|
const entries = Object.entries(branchDescriptionByBranchId);
|
||||||
|
if (entries.length === 0) {
|
||||||
|
Log.log(chalk.dim('No branches are pointed to this channel.'));
|
||||||
|
} else {
|
||||||
|
Log.log(
|
||||||
|
entries
|
||||||
|
.map(([_branchId, description]) => formatBranch(description))
|
||||||
|
.join(`\n\n${chalk.dim('———')}\n\n`)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDescriptionByBranchId(
|
||||||
|
channel: UpdateChannelObject
|
||||||
|
): Record<string, FormattedBranchDescription> {
|
||||||
|
return channel.updateBranches.reduce(
|
||||||
|
(acc, branch) => {
|
||||||
|
const maybeRollout = isRollout(channel) ? getRollout(channel) : null;
|
||||||
|
let maybePercentOnBranch: number | undefined = undefined;
|
||||||
|
if (maybeRollout) {
|
||||||
|
maybePercentOnBranch =
|
||||||
|
maybeRollout.rolledOutBranchId === branch.id
|
||||||
|
? maybeRollout.percentRolledOut
|
||||||
|
: 100 - maybeRollout.percentRolledOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (branch.updateGroups.length === 0) {
|
||||||
|
acc[branch.id] = { branch: branch.name, branchRolloutPercentage: maybePercentOnBranch };
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
const updateGroupsWithBranchDescriptions = getUpdateGroupDescriptionsWithBranch(
|
||||||
|
branch.updateGroups
|
||||||
|
);
|
||||||
|
// display the most recent update group
|
||||||
|
const updateGroupWithBranchDescription = updateGroupsWithBranchDescriptions[0];
|
||||||
|
const { branch: branchName, ...updateGroup } = updateGroupWithBranchDescription;
|
||||||
|
acc[branch.id] = {
|
||||||
|
branch: branchName,
|
||||||
|
branchRolloutPercentage: maybePercentOnBranch,
|
||||||
|
update: updateGroup,
|
||||||
|
};
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<string, FormattedBranchDescription>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,12 @@
|
|||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
import { print } from 'graphql';
|
||||||
import gql from 'graphql-tag';
|
import gql from 'graphql-tag';
|
||||||
|
|
||||||
|
import { ChannelNotFoundError } from './errors';
|
||||||
|
import { logChannelDetails } from './print-utils';
|
||||||
|
import { ChannelBasicInfo } from './utils';
|
||||||
|
import { createUpdateBranchOnAppAsync } from '../branch/queries';
|
||||||
|
import { BranchNotFoundError } from '../branch/utils';
|
||||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||||
import { PaginatedQueryOptions } from '../commandUtils/pagination';
|
import { PaginatedQueryOptions } from '../commandUtils/pagination';
|
||||||
import { withErrorHandlingAsync } from '../graphql/client';
|
import { withErrorHandlingAsync } from '../graphql/client';
|
||||||
@@ -12,14 +18,15 @@ import {
|
|||||||
} from '../graphql/generated';
|
} from '../graphql/generated';
|
||||||
import { BranchQuery, UpdateBranchOnChannelObject } from '../graphql/queries/BranchQuery';
|
import { BranchQuery, UpdateBranchOnChannelObject } from '../graphql/queries/BranchQuery';
|
||||||
import { ChannelQuery, UpdateChannelObject } from '../graphql/queries/ChannelQuery';
|
import { ChannelQuery, UpdateChannelObject } from '../graphql/queries/ChannelQuery';
|
||||||
|
import { UpdateChannelBasicInfoFragmentNode } from '../graphql/types/UpdateChannelBasicInfo';
|
||||||
import Log from '../log';
|
import Log from '../log';
|
||||||
|
import { getDisplayNameForProjectIdAsync } from '../project/projectUtils';
|
||||||
import formatFields from '../utils/formatFields';
|
import formatFields from '../utils/formatFields';
|
||||||
import { printJsonOnlyOutput } from '../utils/json';
|
import { printJsonOnlyOutput } from '../utils/json';
|
||||||
import {
|
import {
|
||||||
paginatedQueryWithConfirmPromptAsync,
|
paginatedQueryWithConfirmPromptAsync,
|
||||||
paginatedQueryWithSelectPromptAsync,
|
paginatedQueryWithSelectPromptAsync,
|
||||||
} from '../utils/queries';
|
} from '../utils/queries';
|
||||||
import { logChannelDetails } from './utils';
|
|
||||||
|
|
||||||
export const CHANNELS_LIMIT = 25;
|
export const CHANNELS_LIMIT = 25;
|
||||||
|
|
||||||
@@ -227,11 +234,11 @@ export async function createChannelOnAppAsync(
|
|||||||
updateChannel {
|
updateChannel {
|
||||||
createUpdateChannelForApp(appId: $appId, name: $name, branchMapping: $branchMapping) {
|
createUpdateChannelForApp(appId: $appId, name: $name, branchMapping: $branchMapping) {
|
||||||
id
|
id
|
||||||
name
|
...UpdateChannelBasicInfoFragment
|
||||||
branchMapping
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${print(UpdateChannelBasicInfoFragmentNode)}
|
||||||
`,
|
`,
|
||||||
{
|
{
|
||||||
appId,
|
appId,
|
||||||
@@ -262,3 +269,101 @@ export async function ensureChannelExistsAsync(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function doesChannelExistAsync(
|
||||||
|
graphqlClient: ExpoGraphqlClient,
|
||||||
|
{ appId, channelName }: { appId: string; channelName: string }
|
||||||
|
): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
|
||||||
|
appId,
|
||||||
|
channelName,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof ChannelNotFoundError) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Creates a channel and links it to a branch with the same name.
|
||||||
|
*
|
||||||
|
* @param appId the app ID, also known as the project ID
|
||||||
|
* @param channelName the name of the channel to create
|
||||||
|
* @param shouldPrintJson print only the JSON output
|
||||||
|
*/
|
||||||
|
export async function createAndLinkChannelAsync(
|
||||||
|
graphqlClient: ExpoGraphqlClient,
|
||||||
|
{
|
||||||
|
appId,
|
||||||
|
channelName,
|
||||||
|
shouldPrintJson,
|
||||||
|
}: { appId: string; channelName: string; shouldPrintJson?: boolean }
|
||||||
|
): Promise<ChannelBasicInfo> {
|
||||||
|
let branchId: string;
|
||||||
|
let branchMessage: string;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const branch = await BranchQuery.getBranchByNameAsync(graphqlClient, {
|
||||||
|
appId,
|
||||||
|
name: channelName,
|
||||||
|
});
|
||||||
|
branchId = branch.id;
|
||||||
|
branchMessage = `We found a branch with the same name`;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof BranchNotFoundError) {
|
||||||
|
const newBranch = await createUpdateBranchOnAppAsync(graphqlClient, {
|
||||||
|
appId,
|
||||||
|
name: channelName,
|
||||||
|
});
|
||||||
|
branchId = newBranch.id;
|
||||||
|
branchMessage = `We also went ahead and made a branch with the same name`;
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
updateChannel: { createUpdateChannelForApp: newChannel },
|
||||||
|
} = await createChannelOnAppAsync(graphqlClient, {
|
||||||
|
appId,
|
||||||
|
channelName,
|
||||||
|
branchId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!newChannel) {
|
||||||
|
throw new Error(
|
||||||
|
`Could not create channel with name ${channelName} on project with id ${appId}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldPrintJson) {
|
||||||
|
printJsonOnlyOutput(newChannel);
|
||||||
|
} else {
|
||||||
|
Log.addNewLineIfNone();
|
||||||
|
Log.withTick(
|
||||||
|
`Created a new channel on project ${chalk.bold(
|
||||||
|
await getDisplayNameForProjectIdAsync(graphqlClient, appId)
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
Log.log(
|
||||||
|
formatFields([
|
||||||
|
{ label: 'Name', value: newChannel.name },
|
||||||
|
{ label: 'ID', value: newChannel.id },
|
||||||
|
])
|
||||||
|
);
|
||||||
|
Log.addNewLineIfNone();
|
||||||
|
Log.withTick(`${branchMessage} and have pointed the channel at it.`);
|
||||||
|
Log.log(
|
||||||
|
formatFields([
|
||||||
|
{ label: 'Name', value: newChannel.name },
|
||||||
|
{ label: 'ID', value: branchId },
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return newChannel;
|
||||||
|
}
|
||||||
@@ -1,117 +1,35 @@
|
|||||||
import assert from 'assert';
|
export type ChannelBasicInfo = {
|
||||||
import chalk from 'chalk';
|
id: string;
|
||||||
|
name: string;
|
||||||
import { UpdateChannelObject } from '../graphql/queries/ChannelQuery';
|
branchMapping: string;
|
||||||
import Log from '../log';
|
};
|
||||||
import {
|
export type BranchBasicInfo = {
|
||||||
FormattedBranchDescription,
|
id: string;
|
||||||
formatBranch,
|
name: string;
|
||||||
getUpdateGroupDescriptionsWithBranch,
|
|
||||||
} from '../update/utils';
|
|
||||||
|
|
||||||
export type BranchMapping = {
|
|
||||||
version: number;
|
|
||||||
data: {
|
|
||||||
branchId: string;
|
|
||||||
branchMappingLogic: {
|
|
||||||
operand: number;
|
|
||||||
clientKey: string;
|
|
||||||
branchMappingOperator: string;
|
|
||||||
} & string;
|
|
||||||
}[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
export type UpdateChannelInfoWithBranches<Branch extends BranchBasicInfo> = ChannelBasicInfo & {
|
||||||
* Get the branch mapping and determine whether it is a rollout.
|
updateBranches: Branch[];
|
||||||
* Ensure that the branch mapping is properly formatted.
|
};
|
||||||
*/
|
|
||||||
export function getBranchMapping(branchMappingString?: string): {
|
|
||||||
branchMapping: BranchMapping;
|
|
||||||
isRollout: boolean;
|
|
||||||
rolloutPercent?: number;
|
|
||||||
} {
|
|
||||||
if (!branchMappingString) {
|
|
||||||
throw new Error('Missing branch mapping.');
|
|
||||||
}
|
|
||||||
let branchMapping: BranchMapping;
|
|
||||||
try {
|
|
||||||
branchMapping = JSON.parse(branchMappingString);
|
|
||||||
} catch {
|
|
||||||
throw new Error(`Could not parse branchMapping string into a JSON: "${branchMappingString}"`);
|
|
||||||
}
|
|
||||||
assert(branchMapping, 'Branch Mapping must be defined.');
|
|
||||||
|
|
||||||
if (branchMapping.version !== 0) {
|
function getUpdateBranchNullable<Branch extends BranchBasicInfo>(
|
||||||
throw new Error('Branch mapping must be version 0.');
|
channel: UpdateChannelInfoWithBranches<Branch>,
|
||||||
|
branchId: string
|
||||||
|
): Branch | null {
|
||||||
|
const updateBranches = channel.updateBranches;
|
||||||
|
const updateBranch = updateBranches.find(branch => branch.id === branchId);
|
||||||
|
return updateBranch ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isRollout = branchMapping.data.length === 2;
|
export function getUpdateBranch<Branch extends BranchBasicInfo>(
|
||||||
const rolloutPercent = branchMapping.data[0]?.branchMappingLogic.operand;
|
channel: UpdateChannelInfoWithBranches<Branch>,
|
||||||
|
branchId: string
|
||||||
switch (branchMapping.data.length) {
|
): Branch {
|
||||||
case 0:
|
const updateBranch = getUpdateBranchNullable(channel, branchId);
|
||||||
break;
|
if (!updateBranch) {
|
||||||
case 1:
|
throw new Error(
|
||||||
if (branchMapping.data[0].branchMappingLogic !== 'true') {
|
`Could not find branch with id "${branchId}" in branch-mapping of channel "${channel.name}"`
|
||||||
throw new Error('Branch mapping logic for a single branch must be "true"');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
if (branchMapping.data[0].branchMappingLogic.clientKey !== 'rolloutToken') {
|
|
||||||
throw new Error('Client key of initial branch mapping must be "rolloutToken"');
|
|
||||||
}
|
|
||||||
if (branchMapping.data[0].branchMappingLogic.branchMappingOperator !== 'hash_lt') {
|
|
||||||
throw new Error('Branch mapping operator of initial branch mapping must be "hash_lt"');
|
|
||||||
}
|
|
||||||
if (rolloutPercent == null) {
|
|
||||||
throw new Error('Branch mapping is missing a "rolloutPercent"');
|
|
||||||
}
|
|
||||||
if (branchMapping.data[1].branchMappingLogic !== 'true') {
|
|
||||||
throw new Error('Branch mapping logic for a the second branch of a rollout must be "true"');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error('Branch mapping data must have length less than or equal to 2.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return { branchMapping, isRollout, rolloutPercent };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function logChannelDetails(channel: UpdateChannelObject): void {
|
|
||||||
const { branchMapping, isRollout, rolloutPercent } = getBranchMapping(channel.branchMapping);
|
|
||||||
if (branchMapping.data.length > 2) {
|
|
||||||
throw new Error('Branch Mapping data must have length less than or equal to 2.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const rolloutBranchIds = branchMapping.data.map(data => data.branchId);
|
|
||||||
const branchDescription = channel.updateBranches.flatMap(branch => {
|
|
||||||
const updateGroupWithBranchDescriptions = getUpdateGroupDescriptionsWithBranch(
|
|
||||||
branch.updateGroups
|
|
||||||
);
|
|
||||||
|
|
||||||
const isRolloutBranch = isRollout && rolloutBranchIds.includes(branch.id);
|
|
||||||
const isBaseBranch = rolloutBranchIds.length > 0 && rolloutBranchIds[0] === branch.id;
|
|
||||||
let rolloutPercentNumber: number | undefined = undefined;
|
|
||||||
if (isRolloutBranch) {
|
|
||||||
rolloutPercentNumber = isBaseBranch ? rolloutPercent! * 100 : (1 - rolloutPercent!) * 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
return updateGroupWithBranchDescriptions.map(
|
|
||||||
({ branch, ...updateGroup }): FormattedBranchDescription => ({
|
|
||||||
branch,
|
|
||||||
branchRolloutPercentage: rolloutPercentNumber,
|
|
||||||
update: updateGroup,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (branchDescription.length === 0) {
|
|
||||||
Log.log(chalk.dim('No branches are pointed to this channel.'));
|
|
||||||
} else {
|
|
||||||
Log.log(
|
|
||||||
branchDescription
|
|
||||||
.map(description => formatBranch(description))
|
|
||||||
.join(`\n\n${chalk.dim('———')}\n\n`)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return updateBranch;
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,9 @@
|
|||||||
import { Command } from '@oclif/core';
|
import { Command } from '@oclif/core';
|
||||||
import { CombinedError } from '@urql/core';
|
import { CombinedError } from '@urql/core';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
import { GraphQLError } from 'graphql/error';
|
||||||
import nullthrows from 'nullthrows';
|
import nullthrows from 'nullthrows';
|
||||||
|
|
||||||
import {
|
|
||||||
AnalyticsWithOrchestration,
|
|
||||||
CommandEvent,
|
|
||||||
createAnalyticsAsync,
|
|
||||||
} from '../analytics/AnalyticsManager';
|
|
||||||
import Log from '../log';
|
|
||||||
import SessionManager from '../user/SessionManager';
|
|
||||||
import AnalyticsContextField from './context/AnalyticsContextField';
|
import AnalyticsContextField from './context/AnalyticsContextField';
|
||||||
import ContextField from './context/ContextField';
|
import ContextField from './context/ContextField';
|
||||||
import {
|
import {
|
||||||
@@ -21,12 +16,21 @@ import { OptionalPrivateProjectConfigContextField } from './context/OptionalPriv
|
|||||||
import { PrivateProjectConfigContextField } from './context/PrivateProjectConfigContextField';
|
import { PrivateProjectConfigContextField } from './context/PrivateProjectConfigContextField';
|
||||||
import ProjectDirContextField from './context/ProjectDirContextField';
|
import ProjectDirContextField from './context/ProjectDirContextField';
|
||||||
import SessionManagementContextField from './context/SessionManagementContextField';
|
import SessionManagementContextField from './context/SessionManagementContextField';
|
||||||
|
import VcsClientContextField from './context/VcsClientContextField';
|
||||||
import { EasCommandError } from './errors';
|
import { EasCommandError } from './errors';
|
||||||
|
import {
|
||||||
|
AnalyticsWithOrchestration,
|
||||||
|
CommandEvent,
|
||||||
|
createAnalyticsAsync,
|
||||||
|
} from '../analytics/AnalyticsManager';
|
||||||
|
import Log from '../log';
|
||||||
|
import SessionManager from '../user/SessionManager';
|
||||||
|
import { Client } from '../vcs/vcs';
|
||||||
|
|
||||||
export type ContextInput<
|
export type ContextInput<
|
||||||
T extends {
|
T extends {
|
||||||
[name: string]: any;
|
[name: string]: any;
|
||||||
} = object
|
} = object,
|
||||||
> = {
|
> = {
|
||||||
[P in keyof T]: ContextField<T[P]>;
|
[P in keyof T]: ContextField<T[P]>;
|
||||||
};
|
};
|
||||||
@@ -34,7 +38,7 @@ export type ContextInput<
|
|||||||
export type ContextOutput<
|
export type ContextOutput<
|
||||||
T extends {
|
T extends {
|
||||||
[name: string]: any;
|
[name: string]: any;
|
||||||
} = object
|
} = object,
|
||||||
> = {
|
> = {
|
||||||
[P in keyof T]: T[P];
|
[P in keyof T]: T[P];
|
||||||
};
|
};
|
||||||
@@ -100,6 +104,9 @@ export default abstract class EasCommand extends Command {
|
|||||||
Analytics: {
|
Analytics: {
|
||||||
analytics: new AnalyticsContextField(),
|
analytics: new AnalyticsContextField(),
|
||||||
},
|
},
|
||||||
|
Vcs: {
|
||||||
|
vcsClient: new VcsClientContextField(),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -131,10 +138,10 @@ export default abstract class EasCommand extends Command {
|
|||||||
protected async getContextAsync<
|
protected async getContextAsync<
|
||||||
C extends {
|
C extends {
|
||||||
[name: string]: any;
|
[name: string]: any;
|
||||||
} = object
|
} = object,
|
||||||
>(
|
>(
|
||||||
commandClass: { contextDefinition: ContextInput<C> },
|
commandClass: { contextDefinition: ContextInput<C> },
|
||||||
{ nonInteractive }: { nonInteractive: boolean }
|
{ nonInteractive, vcsClientOverride }: { nonInteractive: boolean; vcsClientOverride?: Client }
|
||||||
): Promise<ContextOutput<C>> {
|
): Promise<ContextOutput<C>> {
|
||||||
const contextDefinition = commandClass.contextDefinition;
|
const contextDefinition = commandClass.contextDefinition;
|
||||||
|
|
||||||
@@ -147,6 +154,7 @@ export default abstract class EasCommand extends Command {
|
|||||||
nonInteractive,
|
nonInteractive,
|
||||||
sessionManager: this.sessionManager,
|
sessionManager: this.sessionManager,
|
||||||
analytics: this.analytics,
|
analytics: this.analytics,
|
||||||
|
vcsClientOverride,
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -193,10 +201,31 @@ export default abstract class EasCommand extends Command {
|
|||||||
if (err instanceof EasCommandError) {
|
if (err instanceof EasCommandError) {
|
||||||
Log.error(err.message);
|
Log.error(err.message);
|
||||||
} else if (err instanceof CombinedError && err?.graphQLErrors) {
|
} else if (err instanceof CombinedError && err?.graphQLErrors) {
|
||||||
let cleanMessage = err.message.replace('[GraphQL] ', '');
|
const cleanGQLErrorsMessage = err?.graphQLErrors
|
||||||
cleanMessage += err?.graphQLErrors?.[0]?.extensions?.requestId
|
.map((graphQLError: GraphQLError) => {
|
||||||
|
const messageLine = graphQLError.message.replace('[GraphQL] ', '');
|
||||||
|
const requestIdLine = graphQLError.extensions?.requestId
|
||||||
? `\nRequest ID: ${err.graphQLErrors[0].extensions.requestId}`
|
? `\nRequest ID: ${err.graphQLErrors[0].extensions.requestId}`
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
|
const defaultMsg = `${messageLine}${requestIdLine}`;
|
||||||
|
|
||||||
|
if (graphQLError.extensions?.errorCode === 'UNAUTHORIZED_ERROR') {
|
||||||
|
return `${chalk.bold(
|
||||||
|
`You don't have the required permissions to perform this operation.`
|
||||||
|
)}\n\nThis can sometimes happen if you are logged in as incorrect user.\nRun ${chalk.bold(
|
||||||
|
'eas whoami'
|
||||||
|
)} to check the username you are logged in as.\nRun ${chalk.bold(
|
||||||
|
'eas login'
|
||||||
|
)} to change the account.\n\nOriginal error message: ${defaultMsg}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultMsg;
|
||||||
|
})
|
||||||
|
.join('\n');
|
||||||
|
const cleanMessage = err.networkError
|
||||||
|
? `${cleanGQLErrorsMessage}\n${err.networkError.message}`
|
||||||
|
: cleanGQLErrorsMessage;
|
||||||
Log.error(cleanMessage);
|
Log.error(cleanMessage);
|
||||||
baseMessage = BASE_GRAPHQL_ERROR_MESSAGE;
|
baseMessage = BASE_GRAPHQL_ERROR_MESSAGE;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -172,6 +172,44 @@ describe(EasCommand.name, () => {
|
|||||||
expect(logDebugSpy).toBeCalledWith(error);
|
expect(logDebugSpy).toBeCalledWith(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('logs the cleaned messages with request IDs if multiple GraphQL errors present', async () => {
|
||||||
|
const TestEasCommand = createTestEasCommand();
|
||||||
|
const logErrorSpy = jest.spyOn(Log, 'error');
|
||||||
|
const logDebugSpy = jest.spyOn(Log, 'debug');
|
||||||
|
const runAsyncMock = jest.spyOn(TestEasCommand.prototype as any, 'runAsync');
|
||||||
|
const graphQLErrors = [
|
||||||
|
new GraphQLError('Unexpected GraphQL error message', null, null, null, null, null, {
|
||||||
|
errorCode: 'UNKNOWN_GRAPHQL_ERROR',
|
||||||
|
requestId: mockRequestId,
|
||||||
|
}),
|
||||||
|
new GraphQLError('Other GraphQL error message', null, null, null, null, null, {
|
||||||
|
errorCode: 'OTHER_GRAPHQL_ERROR',
|
||||||
|
requestId: mockRequestId,
|
||||||
|
}),
|
||||||
|
new GraphQLError('Yet another GraphQL error message', null, null, null, null, null, {
|
||||||
|
errorCode: 'YET_ANOTHER_GRAPHQL_ERROR',
|
||||||
|
requestId: mockRequestId,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
runAsyncMock.mockImplementation(() => {
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await TestEasCommand.run();
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
expect(logErrorSpy).toBeCalledWith(
|
||||||
|
'Unexpected GraphQL error message\n' +
|
||||||
|
`Request ID: ${mockRequestId}\n` +
|
||||||
|
'Other GraphQL error message\n' +
|
||||||
|
`Request ID: ${mockRequestId}\n` +
|
||||||
|
'Yet another GraphQL error message\n' +
|
||||||
|
`Request ID: ${mockRequestId}`
|
||||||
|
);
|
||||||
|
expect(logDebugSpy).toBeCalledWith(error);
|
||||||
|
});
|
||||||
|
|
||||||
it('re-throws the error with default base message', async () => {
|
it('re-throws the error with default base message', async () => {
|
||||||
const TestEasCommand = createTestEasCommand();
|
const TestEasCommand = createTestEasCommand();
|
||||||
const runAsyncMock = jest.spyOn(TestEasCommand.prototype as any, 'runAsync');
|
const runAsyncMock = jest.spyOn(TestEasCommand.prototype as any, 'runAsync');
|
||||||
@@ -186,7 +224,7 @@ describe(EasCommand.name, () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('re-throws the error with a different default base message in case of graphQLError', async () => {
|
it('re-throws the error with a different default base message in case of CombinedError', async () => {
|
||||||
const TestEasCommand = createTestEasCommand();
|
const TestEasCommand = createTestEasCommand();
|
||||||
const runAsyncMock = jest.spyOn(TestEasCommand.prototype as any, 'runAsync');
|
const runAsyncMock = jest.spyOn(TestEasCommand.prototype as any, 'runAsync');
|
||||||
runAsyncMock.mockImplementation(() => {
|
runAsyncMock.mockImplementation(() => {
|
||||||
@@ -200,6 +238,34 @@ describe(EasCommand.name, () => {
|
|||||||
expect((caughtError as Error).message).toEqual('GraphQL request failed.');
|
expect((caughtError as Error).message).toEqual('GraphQL request failed.');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('re-throws the error with a different default base message in case of CombinedError with multiple GraphQLErrors', async () => {
|
||||||
|
const TestEasCommand = createTestEasCommand();
|
||||||
|
const runAsyncMock = jest.spyOn(TestEasCommand.prototype as any, 'runAsync');
|
||||||
|
runAsyncMock.mockImplementation(() => {
|
||||||
|
const graphQLErrors = [
|
||||||
|
new GraphQLError('Unexpected GraphQL error message', null, null, null, null, null, {
|
||||||
|
errorCode: 'UNEXPECTED_GRAPHQL_ERROR',
|
||||||
|
requestId: mockRequestId,
|
||||||
|
}),
|
||||||
|
new GraphQLError('Other GraphQL error message', null, null, null, null, null, {
|
||||||
|
errorCode: 'OTHER_GRAPHQL_ERROR',
|
||||||
|
requestId: mockRequestId,
|
||||||
|
}),
|
||||||
|
new GraphQLError('Yet another GraphQL error message', null, null, null, null, null, {
|
||||||
|
errorCode: 'YET_ANOTHER_GRAPHQL_ERROR',
|
||||||
|
requestId: mockRequestId,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
throw new CombinedError({ graphQLErrors });
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await TestEasCommand.run();
|
||||||
|
} catch (caughtError) {
|
||||||
|
expect(caughtError).toBeInstanceOf(Error);
|
||||||
|
expect((caughtError as Error).message).toEqual('GraphQL request failed.');
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import chalk from 'chalk';
|
||||||
|
|
||||||
|
import { ExpoGraphqlClient } from './context/contextUtils/createGraphqlClient';
|
||||||
|
import {
|
||||||
|
AppPlatform,
|
||||||
|
Build,
|
||||||
|
BuildFilter,
|
||||||
|
BuildFragment,
|
||||||
|
BuildStatus,
|
||||||
|
InputMaybe,
|
||||||
|
} from '../graphql/generated';
|
||||||
|
import { BuildQuery } from '../graphql/queries/BuildQuery';
|
||||||
|
import { RequestedPlatform, appPlatformEmojis } from '../platform';
|
||||||
|
|
||||||
|
const platformToAppPlatform: Record<
|
||||||
|
Exclude<RequestedPlatform, RequestedPlatform.All>,
|
||||||
|
AppPlatform
|
||||||
|
> = {
|
||||||
|
android: AppPlatform.Android,
|
||||||
|
ios: AppPlatform.Ios,
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function ensureBuildExistsAsync(
|
||||||
|
graphqlClient: ExpoGraphqlClient,
|
||||||
|
buildId: string
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
await BuildQuery.byIdAsync(graphqlClient, buildId);
|
||||||
|
} catch {
|
||||||
|
throw new Error(`Couldn't find a build matching the id ${buildId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchBuildsAsync({
|
||||||
|
graphqlClient,
|
||||||
|
projectId,
|
||||||
|
filters,
|
||||||
|
}: {
|
||||||
|
graphqlClient: ExpoGraphqlClient;
|
||||||
|
projectId: string;
|
||||||
|
filters?: { statuses?: BuildStatus[]; platform?: RequestedPlatform; profile?: string };
|
||||||
|
}): Promise<BuildFragment[]> {
|
||||||
|
let builds: BuildFragment[];
|
||||||
|
const queryFilters: InputMaybe<BuildFilter> = {};
|
||||||
|
if (filters?.platform && filters.platform !== 'all') {
|
||||||
|
queryFilters['platform'] = platformToAppPlatform[filters.platform];
|
||||||
|
}
|
||||||
|
if (filters?.profile) {
|
||||||
|
queryFilters['buildProfile'] = filters.profile;
|
||||||
|
}
|
||||||
|
if (!filters?.statuses) {
|
||||||
|
builds = await BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
|
||||||
|
appId: projectId,
|
||||||
|
offset: 0,
|
||||||
|
limit: 10,
|
||||||
|
filter: queryFilters ? queryFilters : undefined,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
builds = (
|
||||||
|
await Promise.all(
|
||||||
|
filters.statuses.map(status =>
|
||||||
|
BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
|
||||||
|
appId: projectId,
|
||||||
|
offset: 0,
|
||||||
|
limit: 10,
|
||||||
|
filter: queryFilters ? { ...queryFilters, status } : { status },
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).flat();
|
||||||
|
}
|
||||||
|
builds.sort((buildA, buildB) => (buildA.createdAt > buildB.createdAt ? -1 : 1));
|
||||||
|
return builds;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatBuild(
|
||||||
|
build: Pick<Build, 'id' | 'platform' | 'status' | 'createdAt'>
|
||||||
|
): string {
|
||||||
|
const platform = appPlatformEmojis[build.platform];
|
||||||
|
const startTime = new Date(build.createdAt).toLocaleString();
|
||||||
|
let statusText: string;
|
||||||
|
if (build.status === BuildStatus.New) {
|
||||||
|
statusText = 'new';
|
||||||
|
} else if (build.status === BuildStatus.InQueue) {
|
||||||
|
statusText = 'in queue';
|
||||||
|
} else if (build.status === BuildStatus.InProgress) {
|
||||||
|
statusText = 'in progress';
|
||||||
|
} else if (build.status === BuildStatus.Finished) {
|
||||||
|
statusText = 'finished';
|
||||||
|
} else if (build.status === BuildStatus.Errored) {
|
||||||
|
statusText = 'errored';
|
||||||
|
} else if ([BuildStatus.PendingCancel, BuildStatus.Canceled].includes(build.status)) {
|
||||||
|
statusText = 'canceled';
|
||||||
|
} else {
|
||||||
|
statusText = 'unknown';
|
||||||
|
}
|
||||||
|
const status = chalk.blue(statusText);
|
||||||
|
return `${platform} Started at: ${startTime}, Status: ${status}, Id: ${build.id}`;
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Analytics } from '../../analytics/AnalyticsManager';
|
|
||||||
import ContextField, { ContextOptions } from './ContextField';
|
import ContextField, { ContextOptions } from './ContextField';
|
||||||
|
import { Analytics } from '../../analytics/AnalyticsManager';
|
||||||
|
|
||||||
export default class AnalyticsContextField extends ContextField<Analytics> {
|
export default class AnalyticsContextField extends ContextField<Analytics> {
|
||||||
async getValueAsync({ analytics }: ContextOptions): Promise<Analytics> {
|
async getValueAsync({ analytics }: ContextOptions): Promise<Analytics> {
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { Analytics } from '../../analytics/AnalyticsManager';
|
import { Analytics } from '../../analytics/AnalyticsManager';
|
||||||
import SessionManager from '../../user/SessionManager';
|
import SessionManager from '../../user/SessionManager';
|
||||||
|
import { Client } from '../../vcs/vcs';
|
||||||
|
|
||||||
export interface ContextOptions {
|
export interface ContextOptions {
|
||||||
sessionManager: SessionManager;
|
sessionManager: SessionManager;
|
||||||
analytics: Analytics;
|
analytics: Analytics;
|
||||||
nonInteractive: boolean;
|
nonInteractive: boolean;
|
||||||
|
vcsClientOverride?: Client;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default abstract class ContextField<T> {
|
export default abstract class ContextField<T> {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { ExpoConfig } from '@expo/config-types';
|
import { ExpoConfig } from '@expo/config-types';
|
||||||
|
|
||||||
|
import ContextField, { ContextOptions } from './ContextField';
|
||||||
|
import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync';
|
||||||
|
import { getProjectIdAsync } from './contextUtils/getProjectIdAsync';
|
||||||
import {
|
import {
|
||||||
ExpoConfigOptions,
|
ExpoConfigOptions,
|
||||||
getPrivateExpoConfig,
|
getPrivateExpoConfig,
|
||||||
getPublicExpoConfig,
|
getPublicExpoConfig,
|
||||||
} from '../../project/expoConfig';
|
} from '../../project/expoConfig';
|
||||||
import ContextField, { ContextOptions } from './ContextField';
|
|
||||||
import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync';
|
|
||||||
import { getProjectIdAsync } from './contextUtils/getProjectIdAsync';
|
|
||||||
|
|
||||||
export type DynamicConfigContextFn = (options?: ExpoConfigOptions) => Promise<{
|
export type DynamicConfigContextFn = (options?: ExpoConfigOptions) => Promise<{
|
||||||
projectId: string;
|
projectId: string;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
import ContextField, { ContextOptions } from './ContextField';
|
||||||
|
import { ExpoGraphqlClient, createGraphqlClient } from './contextUtils/createGraphqlClient';
|
||||||
import { Actor } from '../../user/User';
|
import { Actor } from '../../user/User';
|
||||||
import FeatureGateEnvOverrides from '../gating/FeatureGateEnvOverrides';
|
import FeatureGateEnvOverrides from '../gating/FeatureGateEnvOverrides';
|
||||||
import FeatureGating from '../gating/FeatureGating';
|
import FeatureGating from '../gating/FeatureGating';
|
||||||
import ContextField, { ContextOptions } from './ContextField';
|
|
||||||
import { ExpoGraphqlClient, createGraphqlClient } from './contextUtils/createGraphqlClient';
|
|
||||||
|
|
||||||
export default class LoggedInContextField extends ContextField<{
|
export default class LoggedInContextField extends ContextField<{
|
||||||
actor: Actor;
|
actor: Actor;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
import ContextField, { ContextOptions } from './ContextField';
|
||||||
|
import { ExpoGraphqlClient, createGraphqlClient } from './contextUtils/createGraphqlClient';
|
||||||
import { Actor } from '../../user/User';
|
import { Actor } from '../../user/User';
|
||||||
import FeatureGateEnvOverrides from '../gating/FeatureGateEnvOverrides';
|
import FeatureGateEnvOverrides from '../gating/FeatureGateEnvOverrides';
|
||||||
import FeatureGating from '../gating/FeatureGating';
|
import FeatureGating from '../gating/FeatureGating';
|
||||||
import ContextField, { ContextOptions } from './ContextField';
|
|
||||||
import { ExpoGraphqlClient, createGraphqlClient } from './contextUtils/createGraphqlClient';
|
|
||||||
|
|
||||||
export default class MaybeLoggedInContextField extends ContextField<{
|
export default class MaybeLoggedInContextField extends ContextField<{
|
||||||
actor: Actor | null;
|
actor: Actor | null;
|
||||||
|
|||||||
+1
-1
@@ -1,10 +1,10 @@
|
|||||||
import { ExpoConfig } from '@expo/config-types';
|
import { ExpoConfig } from '@expo/config-types';
|
||||||
import { InvalidEasJsonError } from '@expo/eas-json/build/errors';
|
import { InvalidEasJsonError } from '@expo/eas-json/build/errors';
|
||||||
|
|
||||||
import { getPrivateExpoConfig } from '../../project/expoConfig';
|
|
||||||
import ContextField, { ContextOptions } from './ContextField';
|
import ContextField, { ContextOptions } from './ContextField';
|
||||||
import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync';
|
import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync';
|
||||||
import { getProjectIdAsync } from './contextUtils/getProjectIdAsync';
|
import { getProjectIdAsync } from './contextUtils/getProjectIdAsync';
|
||||||
|
import { getPrivateExpoConfig } from '../../project/expoConfig';
|
||||||
|
|
||||||
export class OptionalPrivateProjectConfigContextField extends ContextField<
|
export class OptionalPrivateProjectConfigContextField extends ContextField<
|
||||||
| {
|
| {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { ExpoConfig } from '@expo/config-types';
|
import { ExpoConfig } from '@expo/config-types';
|
||||||
|
|
||||||
import { getPrivateExpoConfig } from '../../project/expoConfig';
|
|
||||||
import ContextField, { ContextOptions } from './ContextField';
|
import ContextField, { ContextOptions } from './ContextField';
|
||||||
import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync';
|
import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync';
|
||||||
import { getProjectIdAsync } from './contextUtils/getProjectIdAsync';
|
import { getProjectIdAsync } from './contextUtils/getProjectIdAsync';
|
||||||
|
import { getPrivateExpoConfig } from '../../project/expoConfig';
|
||||||
|
|
||||||
export class PrivateProjectConfigContextField extends ContextField<{
|
export class PrivateProjectConfigContextField extends ContextField<{
|
||||||
projectId: string;
|
projectId: string;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import SessionManager from '../../user/SessionManager';
|
|
||||||
import ContextField, { ContextOptions } from './ContextField';
|
import ContextField, { ContextOptions } from './ContextField';
|
||||||
|
import SessionManager from '../../user/SessionManager';
|
||||||
|
|
||||||
export default class SessionManagementContextField extends ContextField<SessionManager> {
|
export default class SessionManagementContextField extends ContextField<SessionManager> {
|
||||||
async getValueAsync({ sessionManager }: ContextOptions): Promise<SessionManager> {
|
async getValueAsync({ sessionManager }: ContextOptions): Promise<SessionManager> {
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { EasJsonAccessor, EasJsonUtils } from '@expo/eas-json';
|
||||||
|
|
||||||
|
import ContextField from './ContextField';
|
||||||
|
import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync';
|
||||||
|
import { resolveVcsClient } from '../../vcs';
|
||||||
|
import { Client } from '../../vcs/vcs';
|
||||||
|
|
||||||
|
export default class VcsClientContextField extends ContextField<Client> {
|
||||||
|
async getValueAsync({ vcsClientOverride }: { vcsClientOverride?: Client }): Promise<Client> {
|
||||||
|
if (vcsClientOverride) {
|
||||||
|
return vcsClientOverride;
|
||||||
|
}
|
||||||
|
const projectDir = await findProjectDirAndVerifyProjectSetupAsync();
|
||||||
|
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||||
|
const config = await EasJsonUtils.getCliConfigAsync(easJsonAccessor);
|
||||||
|
return resolveVcsClient(config?.requireCommit);
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
-1
@@ -1,4 +1,4 @@
|
|||||||
import { getConfig, modifyConfigAsync } from '@expo/config';
|
import { getConfig, getConfigFilePaths, modifyConfigAsync } from '@expo/config';
|
||||||
import { vol } from 'memfs';
|
import { vol } from 'memfs';
|
||||||
import { anything, instance, mock, when } from 'ts-mockito';
|
import { anything, instance, mock, when } from 'ts-mockito';
|
||||||
|
|
||||||
@@ -26,6 +26,10 @@ describe(getProjectIdAsync, () => {
|
|||||||
let sessionManager: SessionManager;
|
let sessionManager: SessionManager;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
jest
|
||||||
|
.mocked(getConfigFilePaths)
|
||||||
|
.mockReturnValue({ staticConfigPath: null, dynamicConfigPath: null });
|
||||||
|
|
||||||
const sessionManagerMock = mock<SessionManager>();
|
const sessionManagerMock = mock<SessionManager>();
|
||||||
when(sessionManagerMock.ensureLoggedInAsync(anything())).thenResolve({
|
when(sessionManagerMock.ensureLoggedInAsync(anything())).thenResolve({
|
||||||
actor: {
|
actor: {
|
||||||
@@ -51,6 +55,7 @@ describe(getProjectIdAsync, () => {
|
|||||||
],
|
],
|
||||||
isExpoAdmin: false,
|
isExpoAdmin: false,
|
||||||
featureGates: {},
|
featureGates: {},
|
||||||
|
preferences: {},
|
||||||
},
|
},
|
||||||
authenticationInfo: { accessToken: 'fake', sessionSecret: null },
|
authenticationInfo: { accessToken: 'fake', sessionSecret: null },
|
||||||
});
|
});
|
||||||
@@ -75,6 +80,7 @@ describe(getProjectIdAsync, () => {
|
|||||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||||
id: '1234',
|
id: '1234',
|
||||||
fullName: '@notnotbrent/test',
|
fullName: '@notnotbrent/test',
|
||||||
|
name: 'test',
|
||||||
slug: 'test',
|
slug: 'test',
|
||||||
ownerAccount: { name: 'notnotbrent' } as any,
|
ownerAccount: { name: 'notnotbrent' } as any,
|
||||||
});
|
});
|
||||||
@@ -95,6 +101,7 @@ describe(getProjectIdAsync, () => {
|
|||||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||||
id: '1234',
|
id: '1234',
|
||||||
fullName: '@notnotbrent/test',
|
fullName: '@notnotbrent/test',
|
||||||
|
name: 'test',
|
||||||
slug: 'test',
|
slug: 'test',
|
||||||
ownerAccount: { name: 'notnotbrent' } as any,
|
ownerAccount: { name: 'notnotbrent' } as any,
|
||||||
});
|
});
|
||||||
@@ -119,6 +126,7 @@ describe(getProjectIdAsync, () => {
|
|||||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||||
id: '1234',
|
id: '1234',
|
||||||
fullName: '@totallybrent/test',
|
fullName: '@totallybrent/test',
|
||||||
|
name: 'test',
|
||||||
slug: 'test',
|
slug: 'test',
|
||||||
ownerAccount: { name: 'totallybrent' } as any,
|
ownerAccount: { name: 'totallybrent' } as any,
|
||||||
});
|
});
|
||||||
@@ -167,6 +175,7 @@ describe(getProjectIdAsync, () => {
|
|||||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||||
id: '1234',
|
id: '1234',
|
||||||
fullName: '@totallybrent/test',
|
fullName: '@totallybrent/test',
|
||||||
|
name: 'test',
|
||||||
slug: 'test',
|
slug: 'test',
|
||||||
ownerAccount: { name: 'totallybrent' } as any,
|
ownerAccount: { name: 'totallybrent' } as any,
|
||||||
});
|
});
|
||||||
@@ -191,6 +200,7 @@ describe(getProjectIdAsync, () => {
|
|||||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||||
id: '1234',
|
id: '1234',
|
||||||
fullName: '@notnotbrent/test',
|
fullName: '@notnotbrent/test',
|
||||||
|
name: 'test',
|
||||||
slug: 'test',
|
slug: 'test',
|
||||||
ownerAccount: { name: 'notnotbrent' } as any,
|
ownerAccount: { name: 'notnotbrent' } as any,
|
||||||
});
|
});
|
||||||
@@ -260,6 +270,7 @@ describe(getProjectIdAsync, () => {
|
|||||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||||
id: '1234',
|
id: '1234',
|
||||||
fullName: '@notnotbrent/test',
|
fullName: '@notnotbrent/test',
|
||||||
|
name: 'test',
|
||||||
slug: 'test',
|
slug: 'test',
|
||||||
ownerAccount: { name: 'notnotbrent' } as any,
|
ownerAccount: { name: 'notnotbrent' } as any,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,11 +3,10 @@ import {
|
|||||||
Client,
|
Client,
|
||||||
OperationContext,
|
OperationContext,
|
||||||
OperationResult,
|
OperationResult,
|
||||||
PromisifiedSource,
|
OperationResultSource,
|
||||||
TypedDocumentNode,
|
TypedDocumentNode,
|
||||||
cacheExchange,
|
cacheExchange,
|
||||||
createClient as createUrqlClient,
|
createClient as createUrqlClient,
|
||||||
dedupExchange,
|
|
||||||
fetchExchange,
|
fetchExchange,
|
||||||
} from '@urql/core';
|
} from '@urql/core';
|
||||||
import { retryExchange } from '@urql/exchange-retry';
|
import { retryExchange } from '@urql/exchange-retry';
|
||||||
@@ -22,7 +21,7 @@ export interface ExpoGraphqlClient extends Client {
|
|||||||
query: DocumentNode | TypedDocumentNode<Data, Variables> | string,
|
query: DocumentNode | TypedDocumentNode<Data, Variables> | string,
|
||||||
variables: Variables,
|
variables: Variables,
|
||||||
context: Partial<OperationContext> & { additionalTypenames: string[] }
|
context: Partial<OperationContext> & { additionalTypenames: string[] }
|
||||||
): PromisifiedSource<OperationResult<Data, Variables>>;
|
): OperationResultSource<OperationResult<Data, Variables>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createGraphqlClient(authInfo: {
|
export function createGraphqlClient(authInfo: {
|
||||||
@@ -32,7 +31,6 @@ export function createGraphqlClient(authInfo: {
|
|||||||
return createUrqlClient({
|
return createUrqlClient({
|
||||||
url: getExpoApiBaseUrl() + '/graphql',
|
url: getExpoApiBaseUrl() + '/graphql',
|
||||||
exchanges: [
|
exchanges: [
|
||||||
dedupExchange,
|
|
||||||
cacheExchange,
|
cacheExchange,
|
||||||
retryExchange({
|
retryExchange({
|
||||||
maxDelayMs: 4000,
|
maxDelayMs: 4000,
|
||||||
|
|||||||
+7
-7
@@ -8,20 +8,20 @@ import semver from 'semver';
|
|||||||
|
|
||||||
import { learnMore } from '../../../log';
|
import { learnMore } from '../../../log';
|
||||||
import { easCliVersion } from '../../../utils/easCli';
|
import { easCliVersion } from '../../../utils/easCli';
|
||||||
import { getVcsClient, setVcsClient } from '../../../vcs';
|
import { resolveVcsClient } from '../../../vcs';
|
||||||
import GitClient from '../../../vcs/clients/git';
|
|
||||||
|
|
||||||
async function applyCliConfigAsync(projectDir: string): Promise<void> {
|
async function applyCliConfigAsync(projectDir: string): Promise<void> {
|
||||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||||
const config = await EasJsonUtils.getCliConfigAsync(easJsonAccessor);
|
const config = await EasJsonUtils.getCliConfigAsync(easJsonAccessor);
|
||||||
if (config?.version && !semver.satisfies(easCliVersion, config.version)) {
|
if (config?.version && !semver.satisfies(easCliVersion, config.version)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`You are on eas-cli@${easCliVersion} which does not satisfy the CLI version constraint in eas.json (${config.version})`
|
`You are on eas-cli@${easCliVersion} which does not satisfy the CLI version constraint defined in eas.json (${
|
||||||
|
config.version
|
||||||
|
}).\n\nThis error probably means that you need update your eas-cli to a newer version.\nRun ${chalk.bold(
|
||||||
|
'npm install -g eas-cli'
|
||||||
|
)} to update the eas-cli to the latest version.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (config?.requireCommit) {
|
|
||||||
setVcsClient(new GitClient());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ensureEasCliIsNotInDependenciesAsync(projectDir: string): Promise<void> {
|
async function ensureEasCliIsNotInDependenciesAsync(projectDir: string): Promise<void> {
|
||||||
@@ -92,7 +92,7 @@ export async function findProjectRootAsync({
|
|||||||
} else {
|
} else {
|
||||||
let vcsRoot;
|
let vcsRoot;
|
||||||
try {
|
try {
|
||||||
vcsRoot = path.normalize(await getVcsClient().getRootPathAsync());
|
vcsRoot = path.normalize(await resolveVcsClient().getRootPathAsync());
|
||||||
} catch {}
|
} catch {}
|
||||||
if (vcsRoot && vcsRoot.startsWith(projectRootDir) && vcsRoot !== projectRootDir) {
|
if (vcsRoot && vcsRoot.startsWith(projectRootDir) && vcsRoot !== projectRootDir) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { ExpoConfig } from '@expo/config-types';
|
|||||||
import { Env } from '@expo/eas-build-job';
|
import { Env } from '@expo/eas-build-job';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
|
||||||
|
import { ExpoGraphqlClient, createGraphqlClient } from './createGraphqlClient';
|
||||||
|
import { findProjectRootAsync } from './findProjectDirAndVerifyProjectSetupAsync';
|
||||||
import { AppQuery } from '../../../graphql/queries/AppQuery';
|
import { AppQuery } from '../../../graphql/queries/AppQuery';
|
||||||
import Log, { learnMore } from '../../../log';
|
import Log, { learnMore } from '../../../log';
|
||||||
import { ora } from '../../../ora';
|
import { ora } from '../../../ora';
|
||||||
@@ -11,8 +13,6 @@ import { fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync } from '../
|
|||||||
import { toAppPrivacy } from '../../../project/projectUtils';
|
import { toAppPrivacy } from '../../../project/projectUtils';
|
||||||
import SessionManager from '../../../user/SessionManager';
|
import SessionManager from '../../../user/SessionManager';
|
||||||
import { Actor, getActorUsername } from '../../../user/User';
|
import { Actor, getActorUsername } from '../../../user/User';
|
||||||
import { createGraphqlClient } from './createGraphqlClient';
|
|
||||||
import { findProjectRootAsync } from './findProjectDirAndVerifyProjectSetupAsync';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save an EAS project ID to the appropriate field in the app config.
|
* Save an EAS project ID to the appropriate field in the app config.
|
||||||
@@ -87,6 +87,23 @@ export async function getProjectIdAsync(
|
|||||||
});
|
});
|
||||||
const graphqlClient = createGraphqlClient(authenticationInfo);
|
const graphqlClient = createGraphqlClient(authenticationInfo);
|
||||||
|
|
||||||
|
const projectId = await validateOrSetProjectIdAsync({ exp, graphqlClient, actor, options });
|
||||||
|
return projectId;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function validateOrSetProjectIdAsync({
|
||||||
|
exp,
|
||||||
|
graphqlClient,
|
||||||
|
actor,
|
||||||
|
options,
|
||||||
|
cwd,
|
||||||
|
}: {
|
||||||
|
exp: ExpoConfig;
|
||||||
|
graphqlClient: ExpoGraphqlClient;
|
||||||
|
actor: Actor;
|
||||||
|
options: { env?: Env; nonInteractive: boolean };
|
||||||
|
cwd?: string;
|
||||||
|
}): Promise<string> {
|
||||||
const localProjectId = exp.extra?.eas?.projectId;
|
const localProjectId = exp.extra?.eas?.projectId;
|
||||||
if (localProjectId) {
|
if (localProjectId) {
|
||||||
if (typeof localProjectId !== 'string') {
|
if (typeof localProjectId !== 'string') {
|
||||||
@@ -146,7 +163,9 @@ export async function getProjectIdAsync(
|
|||||||
return localProjectId;
|
return localProjectId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const projectDir = await findProjectRootAsync();
|
const projectDir = await findProjectRootAsync({
|
||||||
|
cwd,
|
||||||
|
});
|
||||||
if (!projectDir) {
|
if (!projectDir) {
|
||||||
throw new Error('This command must be run inside a project directory.');
|
throw new Error('This command must be run inside a project directory.');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import nullthrows from 'nullthrows';
|
import nullthrows from 'nullthrows';
|
||||||
|
|
||||||
import env from '../../env';
|
|
||||||
import { FeatureGateKey } from './FeatureGateKey';
|
import { FeatureGateKey } from './FeatureGateKey';
|
||||||
|
import env from '../../env';
|
||||||
|
|
||||||
function parseFeatureGateEnvVariableValue(value: string | undefined): string[] {
|
function parseFeatureGateEnvVariableValue(value: string | undefined): string[] {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ export default class AccountLogin extends EasCommand {
|
|||||||
description: 'Login with SSO',
|
description: 'Login with SSO',
|
||||||
char: 's',
|
char: 's',
|
||||||
default: false,
|
default: false,
|
||||||
hidden: true,
|
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loaded 100 of 410 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user