Compare commits
808 Commits
10.0.0-rc.
...
9.0.0-rc.1
Author | SHA1 | Date | |
---|---|---|---|
b890d10fc6 | |||
2e1a16bf95 | |||
3bbe1d9f30 | |||
49e8028f26 | |||
1ccf3e54f7 | |||
36268a7f4c | |||
4e3eb25edc | |||
904363528f | |||
6b20883c84 | |||
aa7f05f90a | |||
4f3e6e0faf | |||
d566621fef | |||
239c602f69 | |||
b57f7c7776 | |||
622737cee3 | |||
62e1186140 | |||
65354fb4ac | |||
bca432c60f | |||
ef70a89c7f | |||
c9db7bdb8a | |||
37727ce898 | |||
080a8bfd43 | |||
8f5c920474 | |||
c80392bcd2 | |||
92d26e26ea | |||
326c2ed15f | |||
43e2fd2c9f | |||
adc663e43b | |||
1e7851cf7c | |||
a95ae5d68e | |||
cc7fca4dc1 | |||
6a4186c017 | |||
28e9d0a2a6 | |||
81af3ed183 | |||
e69d02c69f | |||
2cf7d0fd05 | |||
1bccb7d403 | |||
bf01b665bc | |||
ba7f2f1617 | |||
b04d4ba9ef | |||
4eeb6cf24d | |||
a10d2a8dc4 | |||
8215b345e3 | |||
c99a4f8b3e | |||
2f2396cac0 | |||
73adfa23a1 | |||
3999d7adf0 | |||
b6dfb4da2d | |||
7b77b3d62e | |||
3897fb9014 | |||
6dd51f1cc2 | |||
d243d851a5 | |||
aa4f6c699d | |||
a4cbb86360 | |||
34c30ae0aa | |||
10ed25d335 | |||
7e8aac128b | |||
e83fe875a0 | |||
6fbc72f7da | |||
20dc436585 | |||
d4c1ab54a0 | |||
d8c29ffb28 | |||
41df82e60a | |||
f566705fba | |||
82f8d07b0f | |||
516c531207 | |||
065c788875 | |||
c1823f83cc | |||
a651fb98c5 | |||
81c114e8ef | |||
641c7cf35c | |||
05c4c0ccbc | |||
da693353f3 | |||
4eec1a94c7 | |||
aafd823669 | |||
86add54e39 | |||
92c17fe560 | |||
4090bb9461 | |||
4242c43545 | |||
a357e4c809 | |||
01dcb1d542 | |||
820e43289d | |||
c1fd5a9861 | |||
e5a352c3c6 | |||
4ace602ace | |||
7f93684f33 | |||
7643913298 | |||
21a9a41c5b | |||
93a035f7f6 | |||
f6fbe3a4a9 | |||
47a547da10 | |||
aff9f5f992 | |||
251d548a95 | |||
e689ef0313 | |||
55790663ce | |||
66d32acb28 | |||
0b6987e355 | |||
762c531e30 | |||
0945f2808f | |||
04ebe125b2 | |||
2f88faf5d9 | |||
484af4699e | |||
ae38e1e57c | |||
51a9f6c540 | |||
8560f016cd | |||
449a9fccc2 | |||
cdf5bd76e7 | |||
7fb43d0d02 | |||
fa30e31cd5 | |||
1cf28929f1 | |||
d1bac3bbbe | |||
f7415731cf | |||
bf695d7526 | |||
7f7d3584b3 | |||
c8ba576021 | |||
fb7a6ce586 | |||
32d4d97111 | |||
1268f77937 | |||
b6bc3887f0 | |||
76a84bfffc | |||
ad3d964955 | |||
7e98c5fc07 | |||
d05c5b1461 | |||
c8a9ef2fe5 | |||
26b8a639a2 | |||
331a59f48a | |||
72e075d570 | |||
99f7ac99f5 | |||
80521e4739 | |||
40fdb01108 | |||
f1eecd75ba | |||
373157970a | |||
fa39a8c026 | |||
c4d035e886 | |||
a7ca658d68 | |||
3113bb7ad2 | |||
e100bbc696 | |||
995bafd007 | |||
c9c0113a46 | |||
b1d213bece | |||
84fc2b09d8 | |||
4bffaf63da | |||
d16d1d8d3b | |||
7e10366977 | |||
a15f20b208 | |||
1ec9515f4f | |||
4928f1a492 | |||
6212bab745 | |||
a8c789d98d | |||
a7cd7d4df6 | |||
5b3e0d6d49 | |||
c18a3fe455 | |||
965f5575c7 | |||
d318249125 | |||
2c0b9ea310 | |||
b3af2202b9 | |||
76d7aa7b19 | |||
1f05929c22 | |||
eb5c20cf64 | |||
fe19327d14 | |||
7305b02023 | |||
b0164c6a29 | |||
f1cdb8f15d | |||
ff02ddfd9b | |||
f48c81c1ca | |||
8ef1c60bcc | |||
a0eb57fb81 | |||
00675269cc | |||
f33ec38182 | |||
f8d4ce74fc | |||
bd85c0d9a6 | |||
71f94afe48 | |||
9801fc3c0a | |||
06fed476e7 | |||
80315b517f | |||
08c25818d8 | |||
a7d8dcd2ea | |||
31841d29fb | |||
c3490f7092 | |||
42d7039caa | |||
4955c256d2 | |||
0e01d5c5de | |||
368ea28e40 | |||
c9fdc661d6 | |||
cdf1b5f985 | |||
0bdaac7b77 | |||
30d4605000 | |||
38750e3f0b | |||
bb6d033a9c | |||
27a552d98e | |||
2d30e7dd28 | |||
64b807e201 | |||
d71c1215f6 | |||
d530dfc747 | |||
96941c7300 | |||
80a7bc8168 | |||
a5cdad57fe | |||
87d2c0739f | |||
613367508f | |||
58cc83ef1f | |||
c4e9cef314 | |||
3e72381bb0 | |||
d83071c3b1 | |||
68e6db5d7a | |||
f7dc3a7b64 | |||
e175878480 | |||
d35451327e | |||
3a5147bc7a | |||
1ec2eb4376 | |||
1bb0fbd733 | |||
e03c0f6ae5 | |||
9f051752af | |||
a812880054 | |||
94e6452bac | |||
a9f13015b2 | |||
0d9c7ba6b3 | |||
2ef0b500ef | |||
a88dc17e5b | |||
56a19f833e | |||
cb7dcb3d23 | |||
2b878f4a18 | |||
f312848c2e | |||
f7a2ed7d66 | |||
cbb175f010 | |||
d63572e87c | |||
937d3fd112 | |||
180d214ff4 | |||
7a5c892557 | |||
417682bbea | |||
05c31fbbc6 | |||
8804e422e4 | |||
16a799d28d | |||
bbeb0605ec | |||
f2952403dd | |||
66a1db7c6d | |||
e2e030d5cd | |||
83868be713 | |||
4def99ed38 | |||
7df28388c9 | |||
8a295ac012 | |||
1ea82785d9 | |||
8e6c5213dc | |||
98ed7c620f | |||
d9ae70e883 | |||
7c3172a469 | |||
7ddf794274 | |||
619e68b95b | |||
6274b02e99 | |||
79be3544c1 | |||
7bbfccfcd8 | |||
ba4aeed2eb | |||
186d310f6b | |||
10acac07e1 | |||
e5498460d1 | |||
6b68f4019c | |||
67a5460c14 | |||
4f3215d281 | |||
a53f510c63 | |||
c42b90b81d | |||
17801c3076 | |||
56be6288a0 | |||
15c8efd5ef | |||
a56b11ca9a | |||
a980551e93 | |||
37912362a2 | |||
0f11bfec77 | |||
e6850a3c51 | |||
c692757029 | |||
fa43184c0f | |||
8573677528 | |||
0be9456b0a | |||
4ce67d7c3e | |||
ba4c31cd95 | |||
925a861ca1 | |||
22533fb5b4 | |||
963ed711d8 | |||
7a0d6e7cba | |||
166009584f | |||
284559614e | |||
5f640c47bd | |||
32d321d1da | |||
532a911325 | |||
d5e0073efd | |||
ac67c5b1f8 | |||
88adc3036e | |||
8a25cd4e96 | |||
4414fce46e | |||
381b895a34 | |||
ca8b584097 | |||
fde5067996 | |||
6c33b7040d | |||
f563c7c656 | |||
0638e65ab1 | |||
197620c24b | |||
c1fd6293fe | |||
e18c679880 | |||
d6edeab1a6 | |||
82297645a2 | |||
3e695da964 | |||
76219f6e4b | |||
2ef633ef8b | |||
44034888fc | |||
c0c2ab30cb | |||
4bffb6bd6e | |||
84a7d8a4e4 | |||
9ca5faab47 | |||
b2a8466e45 | |||
5ec4fb2f2a | |||
1ebb405ef0 | |||
2ea26de2b6 | |||
4e41bf9e30 | |||
7ea39849ff | |||
82dce68e13 | |||
deeb87dc24 | |||
c58edd3216 | |||
4063b1dbf7 | |||
03e236a9f4 | |||
f48e807d9a | |||
e5d992c175 | |||
b1d4c58b81 | |||
76e4870a3f | |||
11257b85fa | |||
ab614802e4 | |||
7916b1e595 | |||
6607fb496a | |||
ee1eebd5d8 | |||
05b4f8eb9d | |||
da425b7553 | |||
931cb5ecd4 | |||
c9172cf1df | |||
caae6738c2 | |||
dfed492214 | |||
d2f40b9595 | |||
199d7da343 | |||
c511d3d09b | |||
3de87d7150 | |||
ff06c05494 | |||
60d1d5efa1 | |||
d83599d8b1 | |||
65fb2fd765 | |||
08ce0262c3 | |||
3c3c07e40e | |||
0ff54f2369 | |||
9bff8e73c5 | |||
bed62b15a2 | |||
4d99dfe6da | |||
8c8eaa460f | |||
4779d24e75 | |||
d661f1e993 | |||
5e075ae96c | |||
35ccf5a101 | |||
fb4a11a859 | |||
adb0663d58 | |||
940e62ba11 | |||
5d871b56c6 | |||
82442c588d | |||
eb9a8ac52c | |||
ce9419220b | |||
45b73c2a7d | |||
bfc575139c | |||
418d586908 | |||
c387952739 | |||
e6ead8a066 | |||
631e70b6ae | |||
25b210a269 | |||
c26738df12 | |||
241f948358 | |||
de71f9152c | |||
8c01446b68 | |||
cfa135d4e6 | |||
382631b20e | |||
5ea93196ac | |||
81c75cf560 | |||
63366af648 | |||
1489e5e969 | |||
d55370e004 | |||
305fecf879 | |||
5d48dbc162 | |||
b490f0d7fb | |||
7ca4f763db | |||
184b83911c | |||
d7c459a81a | |||
b4d49eafc1 | |||
7fe9145f1f | |||
ad82ed891e | |||
bcebc5f4f3 | |||
676aca108f | |||
4b95ba43a5 | |||
7ed984b30c | |||
c66fd060c0 | |||
d3ec306d98 | |||
bbb9412a17 | |||
edfaab62bc | |||
059206b7b7 | |||
74bbe3e413 | |||
46f9201e6f | |||
7b59159443 | |||
b0d5784b63 | |||
7c2bb37e4e | |||
8c53de2cce | |||
3b3610164b | |||
0d07d8379b | |||
24d1f9e87c | |||
3cd43c1173 | |||
599dcd0d85 | |||
60051ebb59 | |||
e3d829fc66 | |||
1347064342 | |||
d0160e71f9 | |||
eb8cd6cb57 | |||
10a33efc3e | |||
26dba2180f | |||
24bbcafe78 | |||
af36bc6ba3 | |||
6e944acaae | |||
8288f8f0a4 | |||
5dcc7e4524 | |||
7dfd327099 | |||
6c70ba7376 | |||
9a43749854 | |||
0e911f87b0 | |||
99745009a8 | |||
46393379e1 | |||
b4680a64c5 | |||
72a5a8cab7 | |||
5f0703de16 | |||
178016b69e | |||
ab85c39589 | |||
50e115e609 | |||
5aeb16f422 | |||
81c8b83a8f | |||
b4600cadbf | |||
83ccaa6cec | |||
b5f2824913 | |||
f87f011e7c | |||
4bf1bdc69a | |||
69dd516666 | |||
aa2b5789a2 | |||
a97d2c06c5 | |||
a616c429ab | |||
1183075d16 | |||
a02bde74d2 | |||
848018f5d3 | |||
95557318bc | |||
f1737ff578 | |||
4d025562c4 | |||
25462614db | |||
c92c9f7e21 | |||
668692598b | |||
41ea3c214a | |||
eae541b6e2 | |||
b712065726 | |||
00f7372554 | |||
5a98410a7d | |||
65cd811de8 | |||
0044c6621e | |||
91f4b81f79 | |||
383be62afa | |||
6123261ad6 | |||
d2538cab5a | |||
dd6da21068 | |||
d516d5253b | |||
c89017a286 | |||
65755edfa0 | |||
4836fe08b5 | |||
1bab8c2757 | |||
f4d714ca0b | |||
5cf5843be3 | |||
64317c680d | |||
93ac362848 | |||
62d384dbbd | |||
33d3340bac | |||
7414ece2fa | |||
54bfd8a0c1 | |||
cba37734b4 | |||
f11c6b1d42 | |||
60c350cd95 | |||
3516213632 | |||
396779f6e6 | |||
fb22f18694 | |||
db4789bf91 | |||
354f66b904 | |||
716fc845f5 | |||
ce79cacd03 | |||
7504543962 | |||
b640d38fd1 | |||
c3c363fef9 | |||
d5a48b2cc0 | |||
69d27f3a08 | |||
daa403fb61 | |||
572e731b63 | |||
0d102bb918 | |||
bbfca62b66 | |||
08c0e6a002 | |||
9995d87f96 | |||
3224d7e79c | |||
a5fe8501c4 | |||
7a7e999fb5 | |||
29de8d3ab1 | |||
bfeb4d2324 | |||
75f4757173 | |||
12e4aa0bb6 | |||
127baa0d38 | |||
075f01341d | |||
2f8bbec6af | |||
4e766b8e0c | |||
846ff256c3 | |||
325e2665ff | |||
bf16b0e4be | |||
ca88f7d2ef | |||
7385e9d06d | |||
ef8b95abae | |||
fc2db27446 | |||
b05ce85391 | |||
7faa9bbc09 | |||
7402430d06 | |||
5a8d25dfc6 | |||
955a3129f0 | |||
e059df3bad | |||
a7b298a4fd | |||
00f8d6ac64 | |||
1b4fac192a | |||
7e6350ce2e | |||
9e5065a778 | |||
1eddb1ae15 | |||
9255dc3468 | |||
c5584b2dbc | |||
60db3505f6 | |||
6904319847 | |||
dda9b9ea7e | |||
e8ec296fdd | |||
4745aef9e8 | |||
a122311773 | |||
6bf258178d | |||
fec9f7dce5 | |||
d2784c7894 | |||
5cc362fbbf | |||
dee0c33a1d | |||
1d14f85dd6 | |||
5e530e917b | |||
781003fd2f | |||
cbc28f255d | |||
8a565c8814 | |||
d228801af4 | |||
978b500961 | |||
b659aa32c9 | |||
1d513e8978 | |||
d22f3d6f85 | |||
bbc8b0f142 | |||
ebe3229da5 | |||
caf5cffd53 | |||
cf420194ed | |||
420750bd05 | |||
7d8b112a91 | |||
abb8c90df9 | |||
efd626c4bc | |||
8c2ff79d04 | |||
457ac3ac46 | |||
688b188484 | |||
f63fca1e3a | |||
60570d0cb2 | |||
00f1ffbbb4 | |||
9d41b5bdd4 | |||
83989b879b | |||
290739f301 | |||
8ed7b73dc1 | |||
7b77ead88f | |||
27f775a65d | |||
f2bb9cc95e | |||
9248dba8fa | |||
1ffbde14ab | |||
4946be0b35 | |||
274ed2ca74 | |||
21ec7d92dd | |||
85a4a1ac60 | |||
e24ed8d0e2 | |||
229bfd507f | |||
af77e5289d | |||
b4c839ccb9 | |||
99e68d0318 | |||
971228b4f4 | |||
bda3c2245d | |||
63c9123924 | |||
aa4d2b785b | |||
719ca1d23c | |||
18574cfed1 | |||
ad9b9a3534 | |||
a54096ab5b | |||
e456e5833a | |||
4a42be27ee | |||
9719047df8 | |||
f1bf5b26d1 | |||
a528006d7a | |||
c1c04dc8ad | |||
90a9043abb | |||
a172644c6c | |||
d3825c20d5 | |||
c01fa1823f | |||
9a5334c75e | |||
250e6fdd39 | |||
196808eebb | |||
c6695faf47 | |||
3814674f9a | |||
e17f051601 | |||
ca5d7723ff | |||
1394781cef | |||
197f042fb1 | |||
fc6ad19089 | |||
90cd9682d2 | |||
80ecf12b4b | |||
b12fde4ceb | |||
94257ac8d3 | |||
fbd2133610 | |||
a09d60cb50 | |||
61cc7a3437 | |||
4e0eb3627d | |||
05a18cc4cb | |||
6da5230003 | |||
b8ba6b036c | |||
b31afc2aa9 | |||
3b5e1668ed | |||
7c7d432085 | |||
213e3c3e49 | |||
5267c92a93 | |||
595375f7a3 | |||
d92da1335d | |||
30ddadc4d7 | |||
dc8e300029 | |||
a7e2327212 | |||
a7d6829cde | |||
bc97c25efe | |||
49571bf11a | |||
14156bd262 | |||
e42fa81aa9 | |||
c7a3694eb9 | |||
839ac3c0a7 | |||
105616cd2b | |||
edd624b254 | |||
56801c74b9 | |||
01af94ccd7 | |||
456ea5cce4 | |||
1e78fafd37 | |||
83140f7926 | |||
b08bb411b9 | |||
1b4d264ccf | |||
05d5c4fc76 | |||
c126a6e3fc | |||
97ff04a7bd | |||
b744505620 | |||
b707c48817 | |||
2d7b015ed7 | |||
b53a1ac999 | |||
bc28ca7b0c | |||
b7c1a5e45f | |||
f136ddad0d | |||
5be23a309e | |||
6d06d9d3c6 | |||
b7c012f91b | |||
1b7aa05699 | |||
1218ce4f5f | |||
7ffbe54ef0 | |||
2f8c9e1990 | |||
bde55ecc91 | |||
aeaf530de6 | |||
66ecab6e8c | |||
f3d8f6adca | |||
a05e42ad09 | |||
0854dc8688 | |||
5926b2897f | |||
0405ddb521 | |||
1a9904d2f1 | |||
37243ce680 | |||
59a4b76b57 | |||
80cd7d7a40 | |||
e47638ec7c | |||
7567f2b8c8 | |||
ebf3afb7f0 | |||
d562625960 | |||
e57a8350e1 | |||
dafdf9a87b | |||
0e20453273 | |||
5bfcd8231a | |||
e641eaaae0 | |||
3074cdfea9 | |||
c182dea146 | |||
502fa906cd | |||
430293abe2 | |||
cba6b7dd20 | |||
09480d3a9d | |||
ca633535c5 | |||
9761ebe7bd | |||
9882a8215a | |||
0688a28be3 | |||
af2cca0c7a | |||
4a4888b276 | |||
d45bbccb58 | |||
d49c830064 | |||
12d67e0d31 | |||
d39b643553 | |||
84524583e9 | |||
a61fb76d51 | |||
b1d0a4f84d | |||
63c3569e2a | |||
aaa38cf3f3 | |||
a9d834fe69 | |||
987d2bd181 | |||
a16a57e52a | |||
1860a9edbc | |||
5fd0d15d86 | |||
8e5ed203bc | |||
06e36e5972 | |||
21bd8c9a2e | |||
1c1240c21a | |||
4c706086a7 | |||
93ba4c2418 | |||
3f6d9a9870 | |||
ba4f171fac | |||
744a3854f3 | |||
ac9fa2f2eb | |||
de8cf75ff5 | |||
5ad1e5f5ec | |||
b4d7a684f8 | |||
49e517d049 | |||
e1df98be17 | |||
1b489083bc | |||
9f10aa0d8c | |||
ecf38d48cf | |||
5f9a2d1cf8 | |||
8ed32e5e71 | |||
d67a38b7f5 | |||
f750f187d5 | |||
4e027fe3e4 | |||
1b0255c3b0 | |||
29852960c3 | |||
508bbfd92f | |||
e4cb91d373 | |||
71238a95d9 | |||
c22f15812c | |||
da01dbc459 | |||
37ae45e2ec | |||
4ec079f852 | |||
b75a21294f | |||
faaa96c737 | |||
c315881d04 | |||
bb9b8030d0 | |||
20e27a86d4 | |||
7c4366dce8 | |||
f8e9c1e6f1 | |||
4988094e7d | |||
9ab49def61 | |||
66157436f8 | |||
72796b98b1 | |||
ea83125df3 | |||
83626962cf | |||
5b292bf125 | |||
3160193719 | |||
9045e3e495 | |||
c5400616f8 | |||
f67802ddc0 | |||
ffb010d3ab | |||
f45d5dc331 | |||
9396b19631 | |||
343483cae0 | |||
682eaef690 | |||
011ecdf939 | |||
cdceb60bd0 | |||
4613639a02 | |||
f32ce01d93 | |||
3d13a6d318 | |||
46b5e4ae69 | |||
e6f383fe20 | |||
86d656bc67 | |||
c5737f4a19 | |||
0b998844c8 | |||
fc24a69a6b | |||
b789c891bb | |||
eb9ed8cc13 | |||
139b2e4f40 | |||
290ae43db3 | |||
c7f913d1ec | |||
ba68b560bc | |||
69fdcbd71c | |||
a6ddda8834 | |||
2910d1a65f | |||
811275cd15 | |||
c8c51a9879 | |||
33f6cd4799 | |||
c1bd3bc76a | |||
0248d6b8a7 | |||
4f69ff7e85 | |||
9df1178fe7 | |||
76e52c5297 | |||
83b635cb3f | |||
09cf0cd878 | |||
cb55f60c74 | |||
cd8333cf0d | |||
854a377232 | |||
8164d28b6c | |||
59b25da76b | |||
68380e4af2 | |||
4029c98f16 | |||
99ead47bff | |||
d09ad82293 | |||
4924d73b8e | |||
7bccef516f | |||
204620291e | |||
3419b5002f | |||
687582fd99 |
91
.bazelignore
91
.bazelignore
@ -1,97 +1,10 @@
|
|||||||
# Bazel does not yet support wildcards or other .gitignore semantics for
|
|
||||||
# .bazelignore. Two issues for this feature request are outstanding:
|
|
||||||
# https://github.com/bazelbuild/bazel/issues/7093
|
|
||||||
# https://github.com/bazelbuild/bazel/issues/8106
|
|
||||||
.git
|
.git
|
||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
aio/content
|
aio/content
|
||||||
aio/node_modules
|
aio/node_modules
|
||||||
aio/tools/examples/shared/node_modules
|
aio/tools/examples/shared/node_modules
|
||||||
packages/bazel/node_modules
|
integration/bazel
|
||||||
integration/bazel/bazel-bazel
|
|
||||||
integration/bazel/bazel-bin
|
|
||||||
integration/bazel/bazel-out
|
|
||||||
integration/bazel/bazel-testlogs
|
|
||||||
integration/bazel-schematics/demo
|
integration/bazel-schematics/demo
|
||||||
# All integration test node_modules folders
|
|
||||||
integration/bazel/node_modules
|
|
||||||
integration/bazel-schematics/node_modules
|
|
||||||
integration/cli-hello-world/node_modules
|
|
||||||
integration/cli-hello-world-ivy-compat/node_modules
|
|
||||||
integration/cli-hello-world-ivy-i18n/node_modules
|
|
||||||
integration/cli-hello-world-ivy-minimal/node_modules
|
|
||||||
integration/cli-hello-world-lazy/node_modules
|
|
||||||
integration/cli-hello-world-lazy-rollup/node_modules
|
|
||||||
integration/dynamic-compiler/node_modules
|
|
||||||
integration/hello_world__closure/node_modules
|
|
||||||
integration/hello_world__systemjs_umd/node_modules
|
|
||||||
integration/i18n/node_modules
|
|
||||||
integration/injectable-def/node_modules
|
|
||||||
integration/ivy-i18n/node_modules
|
|
||||||
integration/language_service_plugin/node_modules
|
|
||||||
integration/ng_elements/node_modules
|
|
||||||
integration/ng_elements_schematics/node_modules
|
|
||||||
integration/ng_update/node_modules
|
|
||||||
integration/ng_update_migrations/node_modules
|
|
||||||
integration/ngcc/node_modules
|
|
||||||
integration/platform-server/node_modules
|
integration/platform-server/node_modules
|
||||||
integration/service-worker-schema/node_modules
|
packages/bazel/node_modules
|
||||||
integration/side-effects/node_modules
|
|
||||||
integration/terser/node_modules
|
|
||||||
integration/typings_test_ts36/node_modules
|
|
||||||
integration/typings_test_ts37/node_modules
|
|
||||||
# All integration test .yarn_local_cache folders
|
|
||||||
integration/bazel/.yarn_local_cache
|
|
||||||
integration/bazel-schematics/.yarn_local_cache
|
|
||||||
integration/cli-hello-world/.yarn_local_cache
|
|
||||||
integration/cli-hello-world-ivy-compat/.yarn_local_cache
|
|
||||||
integration/cli-hello-world-ivy-i18n/.yarn_local_cache
|
|
||||||
integration/cli-hello-world-ivy-minimal/.yarn_local_cache
|
|
||||||
integration/cli-hello-world-lazy/.yarn_local_cache
|
|
||||||
integration/cli-hello-world-lazy-rollup/.yarn_local_cache
|
|
||||||
integration/dynamic-compiler/.yarn_local_cache
|
|
||||||
integration/hello_world__closure/.yarn_local_cache
|
|
||||||
integration/hello_world__systemjs_umd/.yarn_local_cache
|
|
||||||
integration/i18n/.yarn_local_cache
|
|
||||||
integration/injectable-def/.yarn_local_cache
|
|
||||||
integration/ivy-i18n/.yarn_local_cache
|
|
||||||
integration/language_service_plugin/.yarn_local_cache
|
|
||||||
integration/ng_elements/.yarn_local_cache
|
|
||||||
integration/ng_elements_schematics/.yarn_local_cache
|
|
||||||
integration/ng_update/.yarn_local_cache
|
|
||||||
integration/ng_update_migrations/.yarn_local_cache
|
|
||||||
integration/ngcc/.yarn_local_cache
|
|
||||||
integration/platform-server/.yarn_local_cache
|
|
||||||
integration/service-worker-schema/.yarn_local_cache
|
|
||||||
integration/side-effects/.yarn_local_cache
|
|
||||||
integration/terser/.yarn_local_cache
|
|
||||||
integration/typings_test_ts36/.yarn_local_cache
|
|
||||||
integration/typings_test_ts37/.yarn_local_cache
|
|
||||||
# All integration test NPM_PACKAGE_MANIFEST.json folders
|
|
||||||
integration/bazel/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/bazel-schematics/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/cli-hello-world/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/cli-hello-world-ivy-compat/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/cli-hello-world-ivy-i18n/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/cli-hello-world-ivy-minimal/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/cli-hello-world-lazy/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/cli-hello-world-lazy-rollup/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/dynamic-compiler/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/hello_world__closure/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/hello_world__systemjs_umd/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/i18n/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/injectable-def/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/ivy-i18n/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/language_service_plugin/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/ng_elements/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/ng_elements_schematics/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/ng_update/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/ng_update_migrations/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/ngcc/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/platform-server/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/service-worker-schema/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/side-effects/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/terser/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/typings_test_ts36/NPM_PACKAGE_MANIFEST.json
|
|
||||||
integration/typings_test_ts37/NPM_PACKAGE_MANIFEST.json
|
|
||||||
|
28
.bazelrc
28
.bazelrc
@ -33,11 +33,6 @@ build --incompatible_strict_action_env
|
|||||||
run --incompatible_strict_action_env
|
run --incompatible_strict_action_env
|
||||||
test --incompatible_strict_action_env
|
test --incompatible_strict_action_env
|
||||||
|
|
||||||
# Do not build runfile trees by default. If an execution strategy relies on runfile
|
|
||||||
# symlink teee, the tree is created on-demand. See: https://github.com/bazelbuild/bazel/issues/6627
|
|
||||||
# and https://github.com/bazelbuild/bazel/commit/03246077f948f2790a83520e7dccc2625650e6df
|
|
||||||
build --nobuild_runfile_links
|
|
||||||
|
|
||||||
###############################
|
###############################
|
||||||
# Release support #
|
# Release support #
|
||||||
# Turn on these settings with #
|
# Turn on these settings with #
|
||||||
@ -47,7 +42,7 @@ build --nobuild_runfile_links
|
|||||||
# Releases should always be stamped with version control info
|
# Releases should always be stamped with version control info
|
||||||
# This command assumes node on the path and is a workaround for
|
# This command assumes node on the path and is a workaround for
|
||||||
# https://github.com/bazelbuild/bazel/issues/4802
|
# https://github.com/bazelbuild/bazel/issues/4802
|
||||||
build:release --workspace_status_command="yarn -s ng-dev release build-env-stamp"
|
build:release --workspace_status_command="node ./tools/bazel_stamp_vars.js"
|
||||||
build:release --stamp
|
build:release --stamp
|
||||||
|
|
||||||
###############################
|
###############################
|
||||||
@ -67,16 +62,6 @@ test --test_output=errors
|
|||||||
|
|
||||||
# Bazel flags for CircleCI are in /.circleci/bazel.linux.rc and /.circleci/bazel.windows.rc
|
# Bazel flags for CircleCI are in /.circleci/bazel.linux.rc and /.circleci/bazel.windows.rc
|
||||||
|
|
||||||
##################################
|
|
||||||
# Settings for integration tests #
|
|
||||||
##################################
|
|
||||||
|
|
||||||
# Trick bazel into treating BUILD files under integration/bazel as being regular files
|
|
||||||
# This lets us glob() up all the files inside this integration test to make them inputs to tests
|
|
||||||
# (Note, we cannot use common --deleted_packages because the bazel version command doesn't support it)
|
|
||||||
build --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/test/e2e
|
|
||||||
query --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/test/e2e
|
|
||||||
|
|
||||||
################################
|
################################
|
||||||
# Temporary Settings for Ivy #
|
# Temporary Settings for Ivy #
|
||||||
################################
|
################################
|
||||||
@ -115,6 +100,7 @@ build:remote --javabase=@rbe_ubuntu1604_angular//java:jdk
|
|||||||
build:remote --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8
|
build:remote --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8
|
||||||
build:remote --java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8
|
build:remote --java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8
|
||||||
build:remote --crosstool_top=@rbe_ubuntu1604_angular//cc:toolchain
|
build:remote --crosstool_top=@rbe_ubuntu1604_angular//cc:toolchain
|
||||||
|
build:remote --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1
|
||||||
build:remote --extra_toolchains=@rbe_ubuntu1604_angular//config:cc-toolchain
|
build:remote --extra_toolchains=@rbe_ubuntu1604_angular//config:cc-toolchain
|
||||||
build:remote --extra_execution_platforms=//tools:rbe_ubuntu1604-angular
|
build:remote --extra_execution_platforms=//tools:rbe_ubuntu1604-angular
|
||||||
build:remote --host_platform=//tools:rbe_ubuntu1604-angular
|
build:remote --host_platform=//tools:rbe_ubuntu1604-angular
|
||||||
@ -126,16 +112,6 @@ build:remote --project_id=internal-200822
|
|||||||
build:remote --remote_cache=remotebuildexecution.googleapis.com
|
build:remote --remote_cache=remotebuildexecution.googleapis.com
|
||||||
build:remote --remote_executor=remotebuildexecution.googleapis.com
|
build:remote --remote_executor=remotebuildexecution.googleapis.com
|
||||||
|
|
||||||
##################################
|
|
||||||
# Saucelabs tests settings #
|
|
||||||
# Turn on these settings with #
|
|
||||||
# --config=saucelabs #
|
|
||||||
##################################
|
|
||||||
|
|
||||||
# For saucelabs tests we don't want to enable flaky test attempts. Karma has its own integrated
|
|
||||||
# retry mechanism and we do not want to retry unnecessarily if Karma already tried multiple times.
|
|
||||||
test:saucelabs --flaky_test_attempts=1
|
|
||||||
|
|
||||||
###############################
|
###############################
|
||||||
# NodeJS rules settings
|
# NodeJS rules settings
|
||||||
# These settings are required for rules_nodejs
|
# These settings are required for rules_nodejs
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
3.2.0
|
|
||||||
# [NB: this comment has to be after the first line, see https://github.com/bazelbuild/bazelisk/issues/117]
|
|
||||||
# When updating the Bazel version you also need to update the RBE toolchains version in package.bzl
|
|
@ -12,7 +12,7 @@ We use this as a symmetric AES encryption key to encrypt tokens like
|
|||||||
a GitHub token that enables publishing snapshots.
|
a GitHub token that enables publishing snapshots.
|
||||||
|
|
||||||
To create the github_token file, we take this approach:
|
To create the github_token file, we take this approach:
|
||||||
- Find the angular-builds:token in the internal pw database
|
- Find the angular-builds:token in http://valentine
|
||||||
- Go inside the CircleCI default docker image so you use the same version of openssl as we will at runtime: `docker run --rm -it circleci/node:10.12`
|
- Go inside the CircleCI default docker image so you use the same version of openssl as we will at runtime: `docker run --rm -it circleci/node:10.12`
|
||||||
- echo "https://[token]:@github.com" > credentials
|
- echo "https://[token]:@github.com" > credentials
|
||||||
- openssl aes-256-cbc -e -in credentials -out .circleci/github_token -k $KEY
|
- openssl aes-256-cbc -e -in credentials -out .circleci/github_token -k $KEY
|
||||||
|
@ -13,3 +13,7 @@ test --flaky_test_attempts=2
|
|||||||
|
|
||||||
# More details on failures
|
# More details on failures
|
||||||
build --verbose_failures=true
|
build --verbose_failures=true
|
||||||
|
|
||||||
|
# For saucelabs tests we don't want to enable flaky test attempts. Karma has its own integrated
|
||||||
|
# retry mechanism and we do not want to retry unnecessarily if Karma already tried multiple times.
|
||||||
|
test:saucelabs --flaky_test_attempts=1
|
||||||
|
@ -14,17 +14,8 @@ build --repository_cache=/home/circleci/bazel_repository_cache
|
|||||||
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
|
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
|
||||||
# Limit Bazel to consuming resources that fit in CircleCI "xlarge" class
|
# Limit Bazel to consuming resources that fit in CircleCI "xlarge" class
|
||||||
# https://circleci.com/docs/2.0/configuration-reference/#resource_class
|
# https://circleci.com/docs/2.0/configuration-reference/#resource_class
|
||||||
build --local_cpu_resources=8
|
build --local_resources=14336,8.0,1.0
|
||||||
build --local_ram_resources=14336
|
|
||||||
|
|
||||||
# All build executed remotely should be done using our RBE configuration.
|
# All build executed remotely should be done using our RBE configuration.
|
||||||
build:remote --google_default_credentials
|
build:remote --google_default_credentials
|
||||||
|
|
||||||
# Upload to GCP's Build Status viewer to allow for us to have better viewing of execution/build
|
|
||||||
# logs. This is only done on CI as the BES (GCP's Build Status viewer) API requires credentials
|
|
||||||
# from service accounts, rather than end user accounts.
|
|
||||||
build:remote --bes_backend=buildeventservice.googleapis.com
|
|
||||||
build:remote --bes_timeout=30s
|
|
||||||
build:remote --bes_results_url="https://source.cloud.google.com/results/invocations/"
|
|
||||||
|
|
||||||
build --config=remote
|
build --config=remote
|
||||||
|
@ -10,10 +10,6 @@ try-import %workspace%/.circleci/bazel.common.rc
|
|||||||
# speeding up the analysis time significantly with Bazel managed node dependencies on the CI.
|
# speeding up the analysis time significantly with Bazel managed node dependencies on the CI.
|
||||||
build --repository_cache=C:/Users/circleci/bazel_repository_cache
|
build --repository_cache=C:/Users/circleci/bazel_repository_cache
|
||||||
|
|
||||||
# Manually set the local resources used in windows CI runs
|
|
||||||
build --local_ram_resources=13500
|
|
||||||
build --local_cpu_resources=4
|
|
||||||
|
|
||||||
# All windows jobs run on master and should use http caching
|
# All windows jobs run on master and should use http caching
|
||||||
build --remote_http_cache=https://storage.googleapis.com/angular-team-cache
|
build --remote_http_cache=https://storage.googleapis.com/angular-team-cache
|
||||||
build --remote_accept_cached=true
|
build --remote_accept_cached=true
|
||||||
|
@ -22,18 +22,15 @@ version: 2.1
|
|||||||
# **NOTE 1 **: If you change the cache key prefix, also sync the cache_key_fallback to match.
|
# **NOTE 1 **: If you change the cache key prefix, also sync the cache_key_fallback to match.
|
||||||
# **NOTE 2 **: Keep the static part of the cache key as prefix to enable correct fallbacks.
|
# **NOTE 2 **: Keep the static part of the cache key as prefix to enable correct fallbacks.
|
||||||
# See https://circleci.com/docs/2.0/caching/#restoring-cache for how prefixes work in CircleCI.
|
# See https://circleci.com/docs/2.0/caching/#restoring-cache for how prefixes work in CircleCI.
|
||||||
var_3: &cache_key v7-angular-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
|
var_3: &cache_key v3-angular-node-10.16-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
|
||||||
# We invalidate the cache if the Bazel version changes because otherwise the `bazelisk` cache
|
var_4: &cache_key_fallback v3-angular-node-10.16-
|
||||||
# folder will contain all previously used versions and ultimately cause the cache restoring to
|
var_3_win: &cache_key_win v5-angular-win-node-12.0-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
|
||||||
# be slower due to its growing size.
|
var_4_win: &cache_key_win_fallback v5-angular-win-node-12.0-
|
||||||
var_4: &cache_key_fallback v7-angular-node-12-{{ checksum ".bazelversion" }}
|
|
||||||
var_3_win: &cache_key_win v7-angular-win-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
|
|
||||||
var_4_win: &cache_key_win_fallback v7-angular-win-node-12-{{ checksum ".bazelversion" }}
|
|
||||||
|
|
||||||
# Cache key for the `components-repo-unit-tests` job. **Note** when updating the SHA in the
|
# Cache key for the `components-repo-unit-tests` job. **Note** when updating the SHA in the
|
||||||
# cache keys also update the SHA for the "COMPONENTS_REPO_COMMIT" environment variable.
|
# cache keys also update the SHA for the "COMPONENTS_REPO_COMMIT" environment variable.
|
||||||
var_5: &components_repo_unit_tests_cache_key v7-angular-components-189d98e8b01b33974328255f085de04251d61567
|
var_5: &components_repo_unit_tests_cache_key v5-angular-components-97a7e2babbccd3dc58e7b3364004e45ed5bd9968
|
||||||
var_6: &components_repo_unit_tests_cache_key_fallback v7-angular-components-
|
var_6: &components_repo_unit_tests_cache_key_fallback v5-angular-components-
|
||||||
|
|
||||||
# Workspace initially persisted by the `setup` job, and then enhanced by `build-npm-packages` and
|
# Workspace initially persisted by the `setup` job, and then enhanced by `build-npm-packages` and
|
||||||
# `build-ivy-npm-packages`.
|
# `build-ivy-npm-packages`.
|
||||||
@ -67,6 +64,9 @@ var_10: &only_on_master
|
|||||||
# **NOTE 1**: Pin to exact images using an ID (SHA). See https://circleci.com/docs/2.0/circleci-images/#using-a-docker-image-id-to-pin-an-image-to-a-fixed-version.
|
# **NOTE 1**: Pin to exact images using an ID (SHA). See https://circleci.com/docs/2.0/circleci-images/#using-a-docker-image-id-to-pin-an-image-to-a-fixed-version.
|
||||||
# (Using the tag in not necessary when pinning by ID, but include it anyway for documentation purposes.)
|
# (Using the tag in not necessary when pinning by ID, but include it anyway for documentation purposes.)
|
||||||
# **NOTE 2**: If you change the version of the docker images, also change the `cache_key` suffix.
|
# **NOTE 2**: If you change the version of the docker images, also change the `cache_key` suffix.
|
||||||
|
# **NOTE 3**: If you change the version of the `*-browsers` docker image, make sure the
|
||||||
|
# `CI_CHROMEDRIVER_VERSION_ARG` env var (in `.circleci/env.sh`) points to a ChromeDriver
|
||||||
|
# version that is compatible with the Chrome version in the image.
|
||||||
executors:
|
executors:
|
||||||
default-executor:
|
default-executor:
|
||||||
parameters:
|
parameters:
|
||||||
@ -74,7 +74,21 @@ executors:
|
|||||||
type: string
|
type: string
|
||||||
default: medium
|
default: medium
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:12.14.1@sha256:f9de24fc0017059cc42ef7d07db060008af65a98b1f0cdd1ef3339213226bf6d
|
- image: circleci/node:10.16@sha256:75c05084fff4afa3683a03c5a04a4a3ad95c536ff2439d8fe14e7e1f5c58b09a
|
||||||
|
resource_class: << parameters.resource_class >>
|
||||||
|
working_directory: ~/ng
|
||||||
|
|
||||||
|
browsers-executor:
|
||||||
|
parameters:
|
||||||
|
resource_class:
|
||||||
|
type: string
|
||||||
|
default: medium
|
||||||
|
docker:
|
||||||
|
# The browser docker image comes with Chrome and Firefox preinstalled. This is just
|
||||||
|
# needed for jobs that run tests without Bazel. Bazel runs tests with browsers that will be
|
||||||
|
# fetched by the Webtesting rules. Therefore for jobs that run tests with Bazel, we don't need a
|
||||||
|
# docker image with browsers pre-installed.
|
||||||
|
- image: circleci/node:10.16-browsers@sha256:d2a96fe1cbef51257ee626b5f645e64dade3e886f00ba9cb7e8ea65b4efe8db1
|
||||||
resource_class: << parameters.resource_class >>
|
resource_class: << parameters.resource_class >>
|
||||||
working_directory: ~/ng
|
working_directory: ~/ng
|
||||||
|
|
||||||
@ -106,45 +120,25 @@ commands:
|
|||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: *workspace_location
|
at: *workspace_location
|
||||||
|
|
||||||
# Install shared libs used by Chrome that is either provisioned by
|
# Overwrite the yarn installed in the docker container with our own version.
|
||||||
# rules_webtesting or by puppeteer.
|
overwrite_yarn:
|
||||||
install_chrome_libs:
|
description: Overwrite yarn with our own version
|
||||||
description: Install shared Chrome libs
|
|
||||||
steps:
|
steps:
|
||||||
- run:
|
- run:
|
||||||
name: Install shared Chrome libs
|
name: Overwrite yarn
|
||||||
command: |
|
command: |
|
||||||
sudo apt-get update
|
localYarnPath=`node ./.circleci/get-vendored-yarn-path.js`
|
||||||
# Install GTK+ graphical user interface (libgtk-3-0), advanced linux sound architecture (libasound2)
|
sudo chmod a+x $localYarnPath
|
||||||
# and network security service libraries (libnss3) & X11 Screen Saver extension library (libssx1)
|
sudo ln -fs $localYarnPath /usr/local/bin/yarn
|
||||||
# which are dependencies of chrome & needed for karma & protractor headless chrome tests.
|
- run: node --version
|
||||||
# This is a very small install which takes around 7s in comparing to using the full
|
- run: yarn --version
|
||||||
# circleci/node:x.x.x-browsers image.
|
|
||||||
sudo apt-get -y install libgtk-3-0 libasound2 libnss3 libxss1
|
|
||||||
|
|
||||||
# Install java runtime which is required by some integration tests such as
|
|
||||||
# //integration:hello_world__closure_test, //integration:i18n_test and
|
|
||||||
# //integration:ng_elements_test to run the closure compiler
|
|
||||||
install_java:
|
|
||||||
description: Install java
|
|
||||||
steps:
|
|
||||||
- run:
|
|
||||||
name: Install java
|
|
||||||
command: |
|
|
||||||
sudo apt-get update
|
|
||||||
# Install java runtime
|
|
||||||
sudo apt-get install default-jre
|
|
||||||
|
|
||||||
# Initializes the CI environment by setting up common environment variables.
|
# Initializes the CI environment by setting up common environment variables.
|
||||||
init_environment:
|
init_environment:
|
||||||
description: Initializing environment (setting up variables)
|
description: Initializing environment (setting up variables, overwriting Yarn)
|
||||||
steps:
|
steps:
|
||||||
- run:
|
- run: ./.circleci/env.sh
|
||||||
name: Set up environment
|
- overwrite_yarn
|
||||||
environment:
|
|
||||||
CIRCLE_GIT_BASE_REVISION: << pipeline.git.base_revision >>
|
|
||||||
CIRCLE_GIT_REVISION: << pipeline.git.revision >>
|
|
||||||
command: ./.circleci/env.sh
|
|
||||||
- run:
|
- run:
|
||||||
# Configure git as the CircleCI `checkout` command does.
|
# Configure git as the CircleCI `checkout` command does.
|
||||||
# This is needed because we only checkout on the setup job.
|
# This is needed because we only checkout on the setup job.
|
||||||
@ -156,29 +150,8 @@ commands:
|
|||||||
git config --global url."ssh://git@github.com".insteadOf "https://github.com" || true
|
git config --global url."ssh://git@github.com".insteadOf "https://github.com" || true
|
||||||
git config --global gc.auto 0 || true
|
git config --global gc.auto 0 || true
|
||||||
|
|
||||||
init_saucelabs_environment:
|
|
||||||
description: Sets up a domain that resolves to the local host.
|
|
||||||
steps:
|
|
||||||
- run:
|
|
||||||
name: Preparing environment for running tests on Sauce Labs.
|
|
||||||
command: |
|
|
||||||
# For SauceLabs jobs, we set up a domain which resolves to the machine which launched
|
|
||||||
# the tunnel. We do this because devices are sometimes not able to properly resolve
|
|
||||||
# `localhost` or `127.0.0.1` through the SauceLabs tunnel. Using a domain that does not
|
|
||||||
# resolve to anything on SauceLabs VMs ensures that such requests are always resolved
|
|
||||||
# through the tunnel, and resolve to the actual tunnel host machine (i.e. the CircleCI VM).
|
|
||||||
# More context can be found in: https://github.com/angular/angular/pull/35171.
|
|
||||||
setPublicVar SAUCE_LOCALHOST_ALIAS_DOMAIN "angular-ci.local"
|
|
||||||
setSecretVar SAUCE_ACCESS_KEY $(echo $SAUCE_ACCESS_KEY | rev)
|
|
||||||
- run:
|
|
||||||
# Sets up a local domain in the machine's host file that resolves to the local
|
|
||||||
# host. This domain is helpful in Sauce Labs tests where devices are not able to
|
|
||||||
# properly resolve `localhost` or `127.0.0.1` through the sauce-connect tunnel.
|
|
||||||
name: Setting up alias domain for local host.
|
|
||||||
command: echo "127.0.0.1 $SAUCE_LOCALHOST_ALIAS_DOMAIN" | sudo tee -a /etc/hosts
|
|
||||||
|
|
||||||
# Normally this would be an individual job instead of a command.
|
# Normally this would be an individual job instead of a command.
|
||||||
# But startup and setup time for each individual windows job are high enough to discourage
|
# But startup and setup time for each invidual windows job are high enough to discourage
|
||||||
# many small jobs, so instead we use a command for setup unless the gain becomes significant.
|
# many small jobs, so instead we use a command for setup unless the gain becomes significant.
|
||||||
setup_win:
|
setup_win:
|
||||||
description: Setup windows node environment
|
description: Setup windows node environment
|
||||||
@ -187,6 +160,10 @@ commands:
|
|||||||
- custom_attach_workspace
|
- custom_attach_workspace
|
||||||
# Install Bazel pre-requisites that aren't in the preconfigured CircleCI Windows VM.
|
# Install Bazel pre-requisites that aren't in the preconfigured CircleCI Windows VM.
|
||||||
- run: ./.circleci/windows-env.ps1
|
- run: ./.circleci/windows-env.ps1
|
||||||
|
- run:
|
||||||
|
# Overwrite the yarn installed in the docker container with our own version.
|
||||||
|
name: Overwrite yarn with our own version
|
||||||
|
command: ./.circleci/windows-yarn-setup.ps1
|
||||||
- run: node --version
|
- run: node --version
|
||||||
- run: yarn --version
|
- run: yarn --version
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
@ -195,11 +172,10 @@ commands:
|
|||||||
- *cache_key_win_fallback
|
- *cache_key_win_fallback
|
||||||
# Reinstall to get windows binaries.
|
# Reinstall to get windows binaries.
|
||||||
- run: yarn install --frozen-lockfile --non-interactive
|
- run: yarn install --frozen-lockfile --non-interactive
|
||||||
# Install @bazel/bazelisk globally and use that for the first run.
|
# Install @bazel/bazel globally and use that for the first run.
|
||||||
# Workaround for https://github.com/bazelbuild/rules_nodejs/issues/894
|
# Workaround for https://github.com/bazelbuild/rules_nodejs/issues/894
|
||||||
# NB: the issue was for @bazel/bazel but the same problem applies to @bazel/bazelisk
|
- run: yarn global add @bazel/bazel@$env:BAZEL_VERSION
|
||||||
- run: yarn global add @bazel/bazelisk@$env:BAZELISK_VERSION
|
- run: bazel info
|
||||||
- run: bazelisk info
|
|
||||||
|
|
||||||
notify_webhook_on_fail:
|
notify_webhook_on_fail:
|
||||||
description: Notify a webhook about failure
|
description: Notify a webhook about failure
|
||||||
@ -223,7 +199,6 @@ jobs:
|
|||||||
executor: default-executor
|
executor: default-executor
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- init_environment
|
|
||||||
- run:
|
- run:
|
||||||
name: Rebase PR on target branch
|
name: Rebase PR on target branch
|
||||||
# After checkout, rebase on top of target branch.
|
# After checkout, rebase on top of target branch.
|
||||||
@ -233,7 +208,7 @@ jobs:
|
|||||||
git config user.name "angular-ci"
|
git config user.name "angular-ci"
|
||||||
git config user.email "angular-ci"
|
git config user.email "angular-ci"
|
||||||
# Rebase PR on top of target branch.
|
# Rebase PR on top of target branch.
|
||||||
node tools/rebase-pr.js
|
node tools/rebase-pr.js angular/angular ${CIRCLE_PR_NUMBER}
|
||||||
else
|
else
|
||||||
echo "This build is not over a PR, nothing to do."
|
echo "This build is not over a PR, nothing to do."
|
||||||
fi
|
fi
|
||||||
@ -242,6 +217,7 @@ jobs:
|
|||||||
keys:
|
keys:
|
||||||
- *cache_key
|
- *cache_key
|
||||||
- *cache_key_fallback
|
- *cache_key_fallback
|
||||||
|
- init_environment
|
||||||
- run:
|
- run:
|
||||||
name: Running Yarn install
|
name: Running Yarn install
|
||||||
command: yarn install --frozen-lockfile --non-interactive
|
command: yarn install --frozen-lockfile --non-interactive
|
||||||
@ -269,25 +245,22 @@ jobs:
|
|||||||
- custom_attach_workspace
|
- custom_attach_workspace
|
||||||
- init_environment
|
- init_environment
|
||||||
|
|
||||||
- run: yarn -s tslint
|
- run: 'yarn bazel:format -mode=check ||
|
||||||
- run: yarn -s ng-dev format changed $CI_GIT_BASE_REVISION --check
|
(echo "BUILD files not formatted. Please run ''yarn bazel:format''" ; exit 1)'
|
||||||
- run: yarn -s ts-circular-deps:check
|
# Run the skylark linter to check our Bazel rules
|
||||||
- run: yarn -s ng-dev pullapprove verify
|
- run: 'yarn bazel:lint ||
|
||||||
- run: yarn -s ng-dev commit-message validate-range --range $CI_COMMIT_RANGE
|
(echo -e "\n.bzl files have lint errors. Please run ''yarn bazel:lint-fix''"; exit 1)'
|
||||||
|
|
||||||
|
- run: yarn gulp lint
|
||||||
|
- run: node tools/verify-codeownership
|
||||||
|
|
||||||
test:
|
test:
|
||||||
executor:
|
executor:
|
||||||
name: default-executor
|
name: default-executor
|
||||||
# Now that large integration tests are running locally in parallel (they can't run on RBE yet
|
resource_class: xlarge
|
||||||
# as they require network access for yarn install), this test is running out of memory
|
|
||||||
# consistently with the xlarge machine.
|
|
||||||
# TODO: switch back to xlarge once integration tests are running on remote-exec
|
|
||||||
resource_class: 2xlarge+
|
|
||||||
steps:
|
steps:
|
||||||
- custom_attach_workspace
|
- custom_attach_workspace
|
||||||
- init_environment
|
- init_environment
|
||||||
- install_chrome_libs
|
|
||||||
- install_java
|
|
||||||
- run:
|
- run:
|
||||||
command: yarn bazel test //... --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only
|
command: yarn bazel test //... --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only
|
||||||
no_output_timeout: 20m
|
no_output_timeout: 20m
|
||||||
@ -300,7 +273,6 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- custom_attach_workspace
|
- custom_attach_workspace
|
||||||
- init_environment
|
- init_environment
|
||||||
- install_chrome_libs
|
|
||||||
# We need to explicitly specify the --symlink_prefix option because otherwise we would
|
# We need to explicitly specify the --symlink_prefix option because otherwise we would
|
||||||
# not be able to easily find the output bin directory when uploading artifacts for size
|
# not be able to easily find the output bin directory when uploading artifacts for size
|
||||||
# measurements.
|
# measurements.
|
||||||
@ -327,7 +299,11 @@ jobs:
|
|||||||
path: dist/bin/packages/core/test/bundling/todo/bundle.min.js.br
|
path: dist/bin/packages/core/test/bundling/todo/bundle.min.js.br
|
||||||
destination: core/todo/bundle.br
|
destination: core/todo/bundle.br
|
||||||
|
|
||||||
# NOTE: This is currently limited to master builds only. See the `monitoring` configuration.
|
# This job is currently a PoC for running tests on SauceLabs via bazel. It runs a subset of the
|
||||||
|
# tests in `legacy-unit-tests-saucelabs` (see
|
||||||
|
# [BUILD.bazel](https://github.com/angular/angular/blob/ef44f51d5/BUILD.bazel#L66-L92)).
|
||||||
|
#
|
||||||
|
# NOTE: This is currently limited to master builds only. See the `default_workflow` configuration.
|
||||||
saucelabs_view_engine:
|
saucelabs_view_engine:
|
||||||
executor:
|
executor:
|
||||||
name: default-executor
|
name: default-executor
|
||||||
@ -338,20 +314,24 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- custom_attach_workspace
|
- custom_attach_workspace
|
||||||
- init_environment
|
- init_environment
|
||||||
- init_saucelabs_environment
|
|
||||||
- run:
|
- run:
|
||||||
name: Run Bazel tests on Saucelabs with ViewEngine
|
name: Run Bazel tests in saucelabs
|
||||||
# See /tools/saucelabs/README.md for more info
|
# All web tests are contained within a single //:saucelabs_unit_tests_poc target
|
||||||
command: |
|
# for Saucelabs as running each set of tests as a separate target will attempt to acquire
|
||||||
yarn bazel run //tools/saucelabs:sauce_service_setup
|
# too many browsers on Saucelabs (7 per target currently) and some tests will always
|
||||||
TESTS=$(./node_modules/.bin/bazelisk query --output label '(kind(karma_web_test, ...) intersect attr("tags", "saucelabs", ...)) except attr("tags", "ivy-only", ...) except attr("tags", "fixme-saucelabs-ve", ...)')
|
# fail to acquire browsers. For example:
|
||||||
yarn bazel test --config=saucelabs ${TESTS}
|
# 14 02 2019 19:52:33.170:WARN [launcher]: chrome beta on SauceLabs have not captured in 180000 ms, killing.
|
||||||
yarn bazel run //tools/saucelabs:sauce_service_stop
|
# //packages/forms/test:web_test_sauce TIMEOUT in 315.0s
|
||||||
no_output_timeout: 40m
|
command: |
|
||||||
|
./scripts/saucelabs/run-bazel-via-tunnel.sh \
|
||||||
|
--tunnel-id angular-${CIRCLE_BUILD_NUM}-${CIRCLE_NODE_INDEX} \
|
||||||
|
--username $SAUCE_USERNAME \
|
||||||
|
--key $(echo $SAUCE_ACCESS_KEY | rev) \
|
||||||
|
-- yarn bazel test //:saucelabs_unit_tests_poc --config=saucelabs
|
||||||
|
no_output_timeout: 20m
|
||||||
- notify_webhook_on_fail:
|
- notify_webhook_on_fail:
|
||||||
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
|
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
|
||||||
|
|
||||||
# NOTE: This is currently limited to master builds only. See the `monitoring` configuration.
|
|
||||||
saucelabs_ivy:
|
saucelabs_ivy:
|
||||||
executor:
|
executor:
|
||||||
name: default-executor
|
name: default-executor
|
||||||
@ -362,25 +342,27 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- custom_attach_workspace
|
- custom_attach_workspace
|
||||||
- init_environment
|
- init_environment
|
||||||
- init_saucelabs_environment
|
|
||||||
- run:
|
- run:
|
||||||
name: Run Bazel tests on Saucelabs with Ivy
|
name: Run Bazel tests on Saucelabs
|
||||||
# See /tools/saucelabs/README.md for more info
|
# Runs the //:saucelabs_tests target with Saucelabs and Ivy.
|
||||||
command: |
|
command: |
|
||||||
yarn bazel run //tools/saucelabs:sauce_service_setup
|
./scripts/saucelabs/run-bazel-via-tunnel.sh \
|
||||||
TESTS=$(./node_modules/.bin/bazelisk query --output label '(kind(karma_web_test, ...) intersect attr("tags", "saucelabs", ...)) except attr("tags", "no-ivy-aot", ...) except attr("tags", "fixme-saucelabs-ivy", ...)')
|
--tunnel-id angular-${CIRCLE_BUILD_NUM}-${CIRCLE_NODE_INDEX} \
|
||||||
yarn bazel test --config=saucelabs --config=ivy ${TESTS}
|
--username $SAUCE_USERNAME \
|
||||||
yarn bazel run //tools/saucelabs:sauce_service_stop
|
--key $(echo $SAUCE_ACCESS_KEY | rev) \
|
||||||
no_output_timeout: 40m
|
-- yarn bazel test //:saucelabs_unit_tests --config=ivy --config=saucelabs
|
||||||
- notify_webhook_on_fail:
|
no_output_timeout: 20m
|
||||||
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
|
|
||||||
|
|
||||||
test_aio:
|
test_aio:
|
||||||
executor: default-executor
|
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
|
||||||
|
executor: browsers-executor
|
||||||
steps:
|
steps:
|
||||||
- custom_attach_workspace
|
- custom_attach_workspace
|
||||||
- init_environment
|
- init_environment
|
||||||
- install_chrome_libs
|
# Compile dependencies to ivy
|
||||||
|
# Running `ngcc` here (instead of implicitly via `ng build`) allows us to take advantage of
|
||||||
|
# the parallel, async mode speed-up (~20-25s on CI).
|
||||||
|
- run: yarn --cwd aio ngcc --properties es2015
|
||||||
# Build aio
|
# Build aio
|
||||||
- run: yarn --cwd aio build --progress=false
|
- run: yarn --cwd aio build --progress=false
|
||||||
# Lint the code
|
# Lint the code
|
||||||
@ -399,11 +381,11 @@ jobs:
|
|||||||
- run: yarn --cwd aio redirects-test
|
- run: yarn --cwd aio redirects-test
|
||||||
|
|
||||||
deploy_aio:
|
deploy_aio:
|
||||||
executor: default-executor
|
# Needed because before deploying the deploy-production script runs the PWA score tests.
|
||||||
|
executor: browsers-executor
|
||||||
steps:
|
steps:
|
||||||
- custom_attach_workspace
|
- custom_attach_workspace
|
||||||
- init_environment
|
- init_environment
|
||||||
- install_chrome_libs
|
|
||||||
# Deploy angular.io to production (if necessary)
|
# Deploy angular.io to production (if necessary)
|
||||||
- run: setPublicVar_CI_STABLE_BRANCH
|
- run: setPublicVar_CI_STABLE_BRANCH
|
||||||
- run: yarn --cwd aio deploy-production
|
- run: yarn --cwd aio deploy-production
|
||||||
@ -413,11 +395,11 @@ jobs:
|
|||||||
viewengine:
|
viewengine:
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
executor: default-executor
|
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
|
||||||
|
executor: browsers-executor
|
||||||
steps:
|
steps:
|
||||||
- custom_attach_workspace
|
- custom_attach_workspace
|
||||||
- init_environment
|
- init_environment
|
||||||
- install_chrome_libs
|
|
||||||
# Build aio (with local Angular packages)
|
# Build aio (with local Angular packages)
|
||||||
- run: yarn --cwd aio build-local<<# parameters.viewengine >>-with-viewengine<</ parameters.viewengine >>-ci
|
- run: yarn --cwd aio build-local<<# parameters.viewengine >>-with-viewengine<</ parameters.viewengine >>-ci
|
||||||
# Run unit tests
|
# Run unit tests
|
||||||
@ -443,23 +425,32 @@ jobs:
|
|||||||
|
|
||||||
test_docs_examples:
|
test_docs_examples:
|
||||||
parameters:
|
parameters:
|
||||||
viewengine:
|
ivy:
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
executor:
|
executor:
|
||||||
name: default-executor
|
# Needed because the example e2e tests depend on Chrome.
|
||||||
|
name: browsers-executor
|
||||||
resource_class: xlarge
|
resource_class: xlarge
|
||||||
parallelism: 5
|
parallelism: 5
|
||||||
steps:
|
steps:
|
||||||
- custom_attach_workspace
|
- custom_attach_workspace
|
||||||
- init_environment
|
- init_environment
|
||||||
- install_chrome_libs
|
|
||||||
# Install aio
|
# Install aio
|
||||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
||||||
|
- when:
|
||||||
|
condition: << parameters.ivy >>
|
||||||
|
steps:
|
||||||
|
# Rename the Ivy packages dist folder to "dist/packages-dist" as the AIO
|
||||||
|
# package installer picks up the locally built packages from that location.
|
||||||
|
# *Note*: We could also adjust the packages installer, but given we won't have
|
||||||
|
# two different folders of Angular distributions in the future, we should keep
|
||||||
|
# the packages installer unchanged.
|
||||||
|
- run: mv dist/packages-dist-ivy-aot dist/packages-dist
|
||||||
# Run examples tests. The "CIRCLE_NODE_INDEX" will be set if "parallelism" is enabled.
|
# Run examples tests. The "CIRCLE_NODE_INDEX" will be set if "parallelism" is enabled.
|
||||||
# Since the parallelism is set to "5", there will be five parallel CircleCI containers.
|
# Since the parallelism is set to "5", there will be five parallel CircleCI containers.
|
||||||
# with either "0", "1", etc as node index. This can be passed to the "--shard" argument.
|
# with either "0", "1", etc as node index. This can be passed to the "--shard" argument.
|
||||||
- run: yarn --cwd aio example-e2e --setup --local <<# parameters.viewengine >>--viewengine<</ parameters.viewengine >> --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} --retry 2
|
- run: yarn --cwd aio example-e2e --setup --local <<# parameters.ivy >>--ivy<</ parameters.ivy >> --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} --retry 2
|
||||||
|
|
||||||
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
|
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
|
||||||
aio_preview:
|
aio_preview:
|
||||||
@ -479,11 +470,11 @@ jobs:
|
|||||||
|
|
||||||
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
|
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
|
||||||
test_aio_preview:
|
test_aio_preview:
|
||||||
executor: default-executor
|
# Needed because the test-preview script runs e2e tests and the PWA score test with Chrome.
|
||||||
|
executor: browsers-executor
|
||||||
steps:
|
steps:
|
||||||
- custom_attach_workspace
|
- custom_attach_workspace
|
||||||
- init_environment
|
- init_environment
|
||||||
- install_chrome_libs
|
|
||||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
||||||
- run:
|
- run:
|
||||||
name: Wait for preview and run tests
|
name: Wait for preview and run tests
|
||||||
@ -505,7 +496,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- custom_attach_workspace
|
- custom_attach_workspace
|
||||||
- init_environment
|
- init_environment
|
||||||
- run: node scripts/build/build-packages-dist.js
|
- run: node scripts/build-packages-dist.js
|
||||||
|
|
||||||
# Save the npm packages from //packages/... for other workflow jobs to read
|
# Save the npm packages from //packages/... for other workflow jobs to read
|
||||||
- persist_to_workspace:
|
- persist_to_workspace:
|
||||||
@ -521,7 +512,6 @@ jobs:
|
|||||||
- "node_modules"
|
- "node_modules"
|
||||||
- "aio/node_modules"
|
- "aio/node_modules"
|
||||||
- "~/bazel_repository_cache"
|
- "~/bazel_repository_cache"
|
||||||
- "~/.cache/bazelisk"
|
|
||||||
|
|
||||||
# Build the ivy npm packages.
|
# Build the ivy npm packages.
|
||||||
build-ivy-npm-packages:
|
build-ivy-npm-packages:
|
||||||
@ -531,14 +521,35 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- custom_attach_workspace
|
- custom_attach_workspace
|
||||||
- init_environment
|
- init_environment
|
||||||
- run: node scripts/build/build-ivy-npm-packages.js
|
- run: node scripts/build-ivy-npm-packages.js
|
||||||
|
|
||||||
# Save the npm packages from //packages/... for other workflow jobs to read
|
# Save the npm packages from //packages/... for other workflow jobs to read
|
||||||
- persist_to_workspace:
|
- persist_to_workspace:
|
||||||
root: *workspace_location
|
root: *workspace_location
|
||||||
paths:
|
paths:
|
||||||
- ng/dist/packages-dist-ivy-aot
|
- ng/dist/packages-dist-ivy-aot
|
||||||
- ng/dist/zone.js-dist-ivy-aot
|
|
||||||
|
# We run the integration tests outside of Bazel for now.
|
||||||
|
# They are a separate workflow job so that they can be easily re-run.
|
||||||
|
# When the tests are ported to bazel test targets, they should move to the "test"
|
||||||
|
# job above, as part of the bazel test command. That has flaky_test_attempts so the
|
||||||
|
# need to re-run manually should be alleviated.
|
||||||
|
# See comments inside the integration/run_tests.sh script.
|
||||||
|
integration_test:
|
||||||
|
executor:
|
||||||
|
# Needed because the integration tests expect Chrome to be installed (e.g cli-hello-world)
|
||||||
|
name: browsers-executor
|
||||||
|
# Note: we run Bazel in one of the integration tests, and it can consume >2G
|
||||||
|
# of memory. Together with the system under test, this can exhaust the RAM
|
||||||
|
# on a 4G worker so we use a larger machine here too.
|
||||||
|
resource_class: xlarge
|
||||||
|
parallelism: 4
|
||||||
|
steps:
|
||||||
|
- custom_attach_workspace
|
||||||
|
- init_environment
|
||||||
|
# Runs the integration tests in parallel across multiple CircleCI container instances. The
|
||||||
|
# amount of container nodes for this job is controlled by the "parallelism" option.
|
||||||
|
- run: ./integration/run_tests.sh ${CIRCLE_NODE_INDEX} ${CIRCLE_NODE_TOTAL}
|
||||||
|
|
||||||
# This job creates compressed tarballs (`.tgz` files) for all Angular packages and stores them as
|
# This job creates compressed tarballs (`.tgz` files) for all Angular packages and stores them as
|
||||||
# build artifacts. This makes it easy to try out changes from a PR build for testing purposes.
|
# build artifacts. This makes it easy to try out changes from a PR build for testing purposes.
|
||||||
@ -596,26 +607,26 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Decrypt github credentials
|
name: Decrypt github credentials
|
||||||
# We need ensure that the same default digest is used for encoding and decoding with
|
# We need ensure that the same default digest is used for encoding and decoding with
|
||||||
# OpenSSL. OpenSSL versions might have different default digests which can cause
|
# openssl. Openssl versions might have different default digests which can cause
|
||||||
# decryption failures based on the installed OpenSSL version. https://stackoverflow.com/a/39641378/4317734
|
# decryption failures based on the installed openssl version. https://stackoverflow.com/a/39641378/4317734
|
||||||
command: 'openssl aes-256-cbc -d -in .circleci/github_token -md md5 -k "${KEY}" -out ~/.git_credentials'
|
command: 'openssl aes-256-cbc -d -in .circleci/github_token -md md5 -k "${KEY}" -out ~/.git_credentials'
|
||||||
- run: ./scripts/ci/publish-build-artifacts.sh
|
- run: ./scripts/ci/publish-build-artifacts.sh
|
||||||
|
|
||||||
aio_monitoring_stable:
|
aio_monitoring_stable:
|
||||||
executor: default-executor
|
# This job needs Chrome to be globally installed because the tests run with Protractor
|
||||||
|
# which does not load the browser through the Bazel webtesting rules.
|
||||||
|
executor: browsers-executor
|
||||||
steps:
|
steps:
|
||||||
- custom_attach_workspace
|
- custom_attach_workspace
|
||||||
- init_environment
|
- init_environment
|
||||||
- install_chrome_libs
|
|
||||||
- run: setPublicVar_CI_STABLE_BRANCH
|
- run: setPublicVar_CI_STABLE_BRANCH
|
||||||
- run:
|
- run:
|
||||||
name: Check out `aio/` and yarn from the stable branch
|
name: Check out `aio/` and yarn from the stable branch
|
||||||
command: |
|
command: |
|
||||||
git fetch origin $CI_STABLE_BRANCH
|
git fetch origin $CI_STABLE_BRANCH
|
||||||
git checkout --force origin/$CI_STABLE_BRANCH -- aio/ .yarn/ .yarnrc
|
git checkout --force origin/$CI_STABLE_BRANCH -- aio/ .yarn/ .yarnrc
|
||||||
# Ignore yarn's engines check, because we checked out `aio/package.json` from the stable
|
# Overwrite yarn again to use the version from the checked out branch.
|
||||||
# branch and there could be a node version skew, which is acceptable in this monitoring job.
|
- overwrite_yarn
|
||||||
- run: yarn config set ignore-engines true
|
|
||||||
- run:
|
- run:
|
||||||
name: Run tests against https://angular.io/
|
name: Run tests against https://angular.io/
|
||||||
command: ./aio/scripts/test-production.sh https://angular.io/ $CI_AIO_MIN_PWA_SCORE
|
command: ./aio/scripts/test-production.sh https://angular.io/ $CI_AIO_MIN_PWA_SCORE
|
||||||
@ -625,11 +636,12 @@ jobs:
|
|||||||
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
|
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
|
||||||
|
|
||||||
aio_monitoring_next:
|
aio_monitoring_next:
|
||||||
executor: default-executor
|
# This job needs Chrome to be globally installed because the tests run with Protractor
|
||||||
|
# which does not load the browser through the Bazel webtesting rules.
|
||||||
|
executor: browsers-executor
|
||||||
steps:
|
steps:
|
||||||
- custom_attach_workspace
|
- custom_attach_workspace
|
||||||
- init_environment
|
- init_environment
|
||||||
- install_chrome_libs
|
|
||||||
- run:
|
- run:
|
||||||
name: Run tests against https://next.angular.io/
|
name: Run tests against https://next.angular.io/
|
||||||
command: ./aio/scripts/test-production.sh https://next.angular.io/ $CI_AIO_MIN_PWA_SCORE
|
command: ./aio/scripts/test-production.sh https://next.angular.io/ $CI_AIO_MIN_PWA_SCORE
|
||||||
@ -648,26 +660,23 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- custom_attach_workspace
|
- custom_attach_workspace
|
||||||
- init_environment
|
- init_environment
|
||||||
- init_saucelabs_environment
|
|
||||||
- run:
|
- run:
|
||||||
name: Starting Saucelabs tunnel service
|
name: Preparing environment for running tests on Saucelabs.
|
||||||
command: ./tools/saucelabs/sauce-service.sh run
|
command: |
|
||||||
|
setPublicVar KARMA_JS_BROWSERS $(node -e 'console.log(require("./browser-providers.conf").sauceAliases.CI_REQUIRED.join(","))')
|
||||||
|
setSecretVar SAUCE_ACCESS_KEY $(echo $SAUCE_ACCESS_KEY | rev)
|
||||||
|
- run:
|
||||||
|
name: Starting Saucelabs tunnel
|
||||||
|
command: ./scripts/saucelabs/start-tunnel.sh
|
||||||
background: true
|
background: true
|
||||||
- run: yarn tsc -p packages
|
- run: yarn tsc -p packages
|
||||||
- run: yarn tsc -p modules
|
- run: yarn tsc -p modules
|
||||||
- run: yarn bazel build //packages/zone.js:npm_package
|
- run: yarn bazel build //packages/zone.js:npm_package
|
||||||
- run:
|
# Waits for the Saucelabs tunnel to be ready. This ensures that we don't run tests
|
||||||
# Waiting on ready ensures that we don't run tests too early without Saucelabs not being ready.
|
# too early without Saucelabs not being ready.
|
||||||
name: Waiting for Saucelabs tunnel to connect
|
- run: ./scripts/saucelabs/wait-for-tunnel.sh
|
||||||
command: ./tools/saucelabs/sauce-service.sh ready-wait
|
- run: yarn karma start ./karma-js.conf.js --single-run --browsers=${KARMA_JS_BROWSERS}
|
||||||
- run:
|
- run: ./scripts/saucelabs/stop-tunnel.sh
|
||||||
name: Running tests on Saucelabs.
|
|
||||||
command: |
|
|
||||||
browsers=$(node -e 'console.log(require("./browser-providers.conf").sauceAliases.CI_REQUIRED.join(","))')
|
|
||||||
yarn karma start ./karma-js.conf.js --single-run --browsers=${browsers}
|
|
||||||
- run:
|
|
||||||
name: Stop Saucelabs tunnel service
|
|
||||||
command: ./tools/saucelabs/sauce-service.sh stop
|
|
||||||
|
|
||||||
# Job that runs all unit tests of the `angular/components` repository.
|
# Job that runs all unit tests of the `angular/components` repository.
|
||||||
components-repo-unit-tests:
|
components-repo-unit-tests:
|
||||||
@ -734,7 +743,6 @@ jobs:
|
|||||||
cp dist/bin/packages/zone.js/npm_package/dist/zone-mix.js ./packages/zone.js/test/extra/ &&
|
cp dist/bin/packages/zone.js/npm_package/dist/zone-mix.js ./packages/zone.js/test/extra/ &&
|
||||||
cp dist/bin/packages/zone.js/npm_package/dist/zone-patch-electron.js ./packages/zone.js/test/extra/ &&
|
cp dist/bin/packages/zone.js/npm_package/dist/zone-patch-electron.js ./packages/zone.js/test/extra/ &&
|
||||||
yarn --cwd packages/zone.js electrontest
|
yarn --cwd packages/zone.js electrontest
|
||||||
- run: yarn --cwd packages/zone.js jesttest
|
|
||||||
|
|
||||||
# Windows jobs
|
# Windows jobs
|
||||||
# Docs: https://circleci.com/docs/2.0/hello-world-windows/
|
# Docs: https://circleci.com/docs/2.0/hello-world-windows/
|
||||||
@ -790,6 +798,17 @@ workflows:
|
|||||||
- legacy-unit-tests-saucelabs:
|
- legacy-unit-tests-saucelabs:
|
||||||
requires:
|
requires:
|
||||||
- setup
|
- setup
|
||||||
|
- saucelabs_ivy:
|
||||||
|
requires:
|
||||||
|
- test_ivy_aot
|
||||||
|
- saucelabs_view_engine:
|
||||||
|
# This job is currently a PoC and a subset of `legacy-unit-tests-saucelabs`. Running on
|
||||||
|
# master only to avoid wasting resources.
|
||||||
|
# TODO: Run this job on all branches (including PRs) as soon as it is not a PoC and
|
||||||
|
# we can remove the legacy saucelabs job.
|
||||||
|
<<: *only_on_master
|
||||||
|
requires:
|
||||||
|
- setup
|
||||||
- test_aio:
|
- test_aio:
|
||||||
requires:
|
requires:
|
||||||
- setup
|
- setup
|
||||||
@ -811,10 +830,10 @@ workflows:
|
|||||||
requires:
|
requires:
|
||||||
- build-npm-packages
|
- build-npm-packages
|
||||||
- test_docs_examples:
|
- test_docs_examples:
|
||||||
name: test_docs_examples_viewengine
|
name: test_docs_examples_ivy
|
||||||
viewengine: true
|
ivy: true
|
||||||
requires:
|
requires:
|
||||||
- build-npm-packages
|
- build-ivy-npm-packages
|
||||||
- aio_preview:
|
- aio_preview:
|
||||||
# Only run on PR builds. (There can be no previews for non-PR builds.)
|
# Only run on PR builds. (There can be no previews for non-PR builds.)
|
||||||
<<: *only_on_pull_requests
|
<<: *only_on_pull_requests
|
||||||
@ -823,6 +842,9 @@ workflows:
|
|||||||
- test_aio_preview:
|
- test_aio_preview:
|
||||||
requires:
|
requires:
|
||||||
- aio_preview
|
- aio_preview
|
||||||
|
- integration_test:
|
||||||
|
requires:
|
||||||
|
- build-npm-packages
|
||||||
- publish_packages_as_artifacts:
|
- publish_packages_as_artifacts:
|
||||||
requires:
|
requires:
|
||||||
- build-npm-packages
|
- build-npm-packages
|
||||||
@ -835,11 +857,13 @@ workflows:
|
|||||||
# Only publish if tests and integration tests pass
|
# Only publish if tests and integration tests pass
|
||||||
- test
|
- test
|
||||||
- test_ivy_aot
|
- test_ivy_aot
|
||||||
|
- integration_test
|
||||||
|
- saucelabs_ivy
|
||||||
# Only publish if `aio`/`docs` tests using the locally built Angular packages pass
|
# Only publish if `aio`/`docs` tests using the locally built Angular packages pass
|
||||||
- test_aio_local
|
- test_aio_local
|
||||||
- test_aio_local_viewengine
|
- test_aio_local_viewengine
|
||||||
- test_docs_examples
|
- test_docs_examples
|
||||||
- test_docs_examples_viewengine
|
- test_docs_examples_ivy
|
||||||
# Get the artifacts to publish from the build-packages-dist job
|
# Get the artifacts to publish from the build-packages-dist job
|
||||||
# since the publishing script expects the legacy outputs layout.
|
# since the publishing script expects the legacy outputs layout.
|
||||||
- build-npm-packages
|
- build-npm-packages
|
||||||
@ -866,7 +890,7 @@ workflows:
|
|||||||
requires:
|
requires:
|
||||||
- test_ivy_aot
|
- test_ivy_aot
|
||||||
|
|
||||||
monitoring:
|
aio_monitoring:
|
||||||
jobs:
|
jobs:
|
||||||
- setup
|
- setup
|
||||||
- aio_monitoring_stable:
|
- aio_monitoring_stable:
|
||||||
@ -875,26 +899,8 @@ workflows:
|
|||||||
- aio_monitoring_next:
|
- aio_monitoring_next:
|
||||||
requires:
|
requires:
|
||||||
- setup
|
- setup
|
||||||
- saucelabs_ivy:
|
|
||||||
# Testing saucelabs via Bazel currently taking longer than the legacy saucelabs job as it
|
|
||||||
# each karma_web_test target is provisioning and tearing down browsers which is adding
|
|
||||||
# a lot of overhead. Running once daily on master only to avoid wasting resources and
|
|
||||||
# slowing down CI for PRs.
|
|
||||||
# TODO: Run this job on all branches (including PRs) once karma_web_test targets can
|
|
||||||
# share provisioned browsers and we can remove the legacy saucelabs job.
|
|
||||||
requires:
|
|
||||||
- setup
|
|
||||||
- saucelabs_view_engine:
|
|
||||||
# Testing saucelabs via Bazel currently taking longer than the legacy saucelabs job as it
|
|
||||||
# each karma_web_test target is provisioning and tearing down browsers which is adding
|
|
||||||
# a lot of overhead. Running once daily on master only to avoid wasting resources and
|
|
||||||
# slowing down CI for PRs.
|
|
||||||
# TODO: Run this job on all branches (including PRs) once karma_web_test targets can
|
|
||||||
# share provisioned browsers and we can remove the legacy saucelabs job.
|
|
||||||
requires:
|
|
||||||
- setup
|
|
||||||
triggers:
|
triggers:
|
||||||
- schedule:
|
- schedule:
|
||||||
<<: *only_on_master
|
<<: *only_on_master
|
||||||
# Runs monitoring jobs at 10:00AM every day.
|
# Runs AIO monitoring jobs at 10:00AM every day.
|
||||||
cron: "0 10 * * *"
|
cron: "0 10 * * *"
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
# Variables
|
# Variables
|
||||||
readonly projectDir=$(realpath "$(dirname ${BASH_SOURCE[0]})/..")
|
readonly projectDir=$(realpath "$(dirname ${BASH_SOURCE[0]})/..")
|
||||||
readonly envHelpersPath="$projectDir/.circleci/env-helpers.inc.sh";
|
readonly envHelpersPath="$projectDir/.circleci/env-helpers.inc.sh";
|
||||||
readonly bashEnvCachePath="$projectDir/.circleci/bash_env_cache";
|
|
||||||
|
|
||||||
# Load helpers and make them available everywhere (through `$BASH_ENV`).
|
# Load helpers and make them available everywhere (through `$BASH_ENV`).
|
||||||
source $envHelpersPath;
|
source $envHelpersPath;
|
||||||
@ -15,23 +14,27 @@ echo "source $envHelpersPath;" >> $BASH_ENV;
|
|||||||
####################################################################################################
|
####################################################################################################
|
||||||
# See https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables for more info.
|
# See https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables for more info.
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
setPublicVar CI "$CI"
|
|
||||||
setPublicVar PROJECT_ROOT "$projectDir";
|
setPublicVar PROJECT_ROOT "$projectDir";
|
||||||
setPublicVar CI_AIO_MIN_PWA_SCORE "95";
|
setPublicVar CI_AIO_MIN_PWA_SCORE "95";
|
||||||
# This is the branch being built; e.g. `pull/12345` for PR builds.
|
# This is the branch being built; e.g. `pull/12345` for PR builds.
|
||||||
setPublicVar CI_BRANCH "$CIRCLE_BRANCH";
|
setPublicVar CI_BRANCH "$CIRCLE_BRANCH";
|
||||||
setPublicVar CI_BUILD_URL "$CIRCLE_BUILD_URL";
|
setPublicVar CI_BUILD_URL "$CIRCLE_BUILD_URL";
|
||||||
|
# ChromeDriver version compatible with the Chrome version included in the docker image used in
|
||||||
|
# `.circleci/config.yml`. See http://chromedriver.chromium.org/downloads for a list of versions.
|
||||||
|
# This variable is intended to be passed as an arg to the `webdriver-manager update` command (e.g.
|
||||||
|
# `"postinstall": "webdriver-manager update $CI_CHROMEDRIVER_VERSION_ARG"`).
|
||||||
|
setPublicVar CI_CHROMEDRIVER_VERSION_ARG "--versions.chrome 75.0.3770.90";
|
||||||
setPublicVar CI_COMMIT "$CIRCLE_SHA1";
|
setPublicVar CI_COMMIT "$CIRCLE_SHA1";
|
||||||
# `CI_COMMIT_RANGE` is only used on push builds (a.k.a. non-PR, non-scheduled builds and rerun
|
# `CI_COMMIT_RANGE` is only used on push builds (a.k.a. non-PR, non-scheduled builds and rerun
|
||||||
# workflows of such builds).
|
# workflows of such builds).
|
||||||
setPublicVar CI_GIT_BASE_REVISION "${CIRCLE_GIT_BASE_REVISION}";
|
# NOTE: With [CircleCI Pipelines](https://circleci.com/docs/2.0/build-processing) enabled,
|
||||||
setPublicVar CI_GIT_REVISION "${CIRCLE_GIT_REVISION}";
|
# `CIRCLE_COMPARE_URL` is no longer available and the commit range cannot be reliably
|
||||||
setPublicVar CI_COMMIT_RANGE "$CIRCLE_GIT_BASE_REVISION..$CIRCLE_GIT_REVISION";
|
# detected. Fall back to only considering the last commit (which is accurate in the majority
|
||||||
|
# of cases for push builds).
|
||||||
|
setPublicVar CI_COMMIT_RANGE "`[[ ${CIRCLE_PR_NUMBER:-false} != false ]] && echo "" || echo "$CIRCLE_SHA1~1...$CIRCLE_SHA1"`";
|
||||||
setPublicVar CI_PULL_REQUEST "${CIRCLE_PR_NUMBER:-false}";
|
setPublicVar CI_PULL_REQUEST "${CIRCLE_PR_NUMBER:-false}";
|
||||||
setPublicVar CI_REPO_NAME "$CIRCLE_PROJECT_REPONAME";
|
setPublicVar CI_REPO_NAME "$CIRCLE_PROJECT_REPONAME";
|
||||||
setPublicVar CI_REPO_OWNER "$CIRCLE_PROJECT_USERNAME";
|
setPublicVar CI_REPO_OWNER "$CIRCLE_PROJECT_USERNAME";
|
||||||
setPublicVar CI_PR_REPONAME "$CIRCLE_PR_REPONAME";
|
|
||||||
setPublicVar CI_PR_USERNAME "$CIRCLE_PR_USERNAME";
|
|
||||||
|
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
@ -62,7 +65,6 @@ setPublicVar SAUCE_TUNNEL_IDENTIFIER "angular-framework-${CIRCLE_BUILD_NUM}-${CI
|
|||||||
# acquire CircleCI instances for too long if sauceconnect failed, we need a connect timeout.
|
# acquire CircleCI instances for too long if sauceconnect failed, we need a connect timeout.
|
||||||
setPublicVar SAUCE_READY_FILE_TIMEOUT 120
|
setPublicVar SAUCE_READY_FILE_TIMEOUT 120
|
||||||
|
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# Define environment variables for the `angular/components` repo unit tests job.
|
# Define environment variables for the `angular/components` repo unit tests job.
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
@ -74,7 +76,7 @@ setPublicVar COMPONENTS_REPO_TMP_DIR "/tmp/angular-components-repo"
|
|||||||
setPublicVar COMPONENTS_REPO_URL "https://github.com/angular/components.git"
|
setPublicVar COMPONENTS_REPO_URL "https://github.com/angular/components.git"
|
||||||
setPublicVar COMPONENTS_REPO_BRANCH "master"
|
setPublicVar COMPONENTS_REPO_BRANCH "master"
|
||||||
# **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI `config.yml`.
|
# **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI `config.yml`.
|
||||||
setPublicVar COMPONENTS_REPO_COMMIT "189d98e8b01b33974328255f085de04251d61567"
|
setPublicVar COMPONENTS_REPO_COMMIT "97a7e2babbccd3dc58e7b3364004e45ed5bd9968"
|
||||||
|
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
@ -88,22 +90,6 @@ openssl aes-256-cbc -d -in "${projectDir}/.circleci/gcp_token" \
|
|||||||
####################################################################################################
|
####################################################################################################
|
||||||
cp "${projectDir}/.circleci/bazel.linux.rc" "$HOME/.bazelrc";
|
cp "${projectDir}/.circleci/bazel.linux.rc" "$HOME/.bazelrc";
|
||||||
|
|
||||||
####################################################################################################
|
|
||||||
# Create shell script in /tmp for Bazel actions to access CI envs without
|
|
||||||
# busting the cache. Used by payload-size.sh script in integration tests.
|
|
||||||
####################################################################################################
|
|
||||||
readonly bazelVarEnv="/tmp/bazel-ci-env.sh"
|
|
||||||
echo "# Setup by /.circle/env.sh" > $bazelVarEnv
|
|
||||||
echo "export PROJECT_ROOT=\"${PROJECT_ROOT}\";" >> $bazelVarEnv
|
|
||||||
echo "export CI_BRANCH=\"${CI_BRANCH}\";" >> $bazelVarEnv
|
|
||||||
echo "export CI_BUILD_URL=\"${CI_BUILD_URL}\";" >> $bazelVarEnv
|
|
||||||
echo "export CI_COMMIT=\"${CI_COMMIT}\";" >> $bazelVarEnv
|
|
||||||
echo "export CI_COMMIT_RANGE=\"${CI_COMMIT_RANGE}\";" >> $bazelVarEnv
|
|
||||||
echo "export CI_PULL_REQUEST=\"${CI_PULL_REQUEST}\";" >> $bazelVarEnv
|
|
||||||
echo "export CI_REPO_NAME=\"${CI_REPO_NAME}\";" >> $bazelVarEnv
|
|
||||||
echo "export CI_REPO_OWNER=\"${CI_REPO_OWNER}\";" >> $bazelVarEnv
|
|
||||||
echo "export CI_SECRET_PAYLOAD_FIREBASE_TOKEN=\"${CI_SECRET_PAYLOAD_FIREBASE_TOKEN}\";" >> $bazelVarEnv
|
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
## Source `$BASH_ENV` to make the variables available immediately. ##
|
## Source `$BASH_ENV` to make the variables available immediately. ##
|
||||||
|
166
.circleci/get-commit-range.js
Normal file
166
.circleci/get-commit-range.js
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **Usage:**
|
||||||
|
* ```
|
||||||
|
* node get-commit-range <build-number> [<compare-url> [<circle-token>]]
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Returns the commit range, either extracting it from `compare-url` (if defined), which is of the
|
||||||
|
* format of the `CIRCLE_COMPARE_URL` environment variable, or by retrieving the equivalent of
|
||||||
|
* `CIRCLE_COMPARE_URL` for jobs that are part of a rerun workflow and extracting it from there.
|
||||||
|
*
|
||||||
|
* > !!! WARNING !!!
|
||||||
|
* > !!
|
||||||
|
* > !! When [CircleCI Pipelines](https://circleci.com/docs/2.0/build-processing) is enabled, the
|
||||||
|
* > !! `CIRCLE_COMPARE_URL` environment variable is not available at all and this script does not
|
||||||
|
* > !! work.
|
||||||
|
* > !!!!!!!!!!!!!!!
|
||||||
|
*
|
||||||
|
* **Context:**
|
||||||
|
* CircleCI sets the `CIRCLE_COMPARE_URL` environment variable (from which we can extract the commit
|
||||||
|
* range) on push builds (a.k.a. non-PR, non-scheduled builds). Yet, when a workflow is rerun
|
||||||
|
* (either from the beginning or from failed jobs) - e.g. when a job flakes - CircleCI does not set
|
||||||
|
* the `CIRCLE_COMPARE_URL`.
|
||||||
|
*
|
||||||
|
* **Implementation details:**
|
||||||
|
* This script relies on the fact that all rerun workflows share the same CircleCI workspace and the
|
||||||
|
* (undocumented) fact that the workspace ID happens to be the same as the workflow ID that first
|
||||||
|
* created it.
|
||||||
|
*
|
||||||
|
* For example, for a job on push build workflows, the CircleCI API will return data that look like:
|
||||||
|
* ```js
|
||||||
|
* {
|
||||||
|
* compare: 'THE_COMPARE_URL_WE_ARE_LOOKING_FOR',
|
||||||
|
* //...
|
||||||
|
* previous: {
|
||||||
|
* // ...
|
||||||
|
* build_num: 12345,
|
||||||
|
* },
|
||||||
|
* //...
|
||||||
|
* workflows: {
|
||||||
|
* //...
|
||||||
|
* workflow_id: 'SOME_ID_A',
|
||||||
|
* workspace_id: 'SOME_ID_A', // Same as `workflow_id`.
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* If the workflow is rerun, the data for jobs on the new workflow will look like:
|
||||||
|
* ```js
|
||||||
|
* {
|
||||||
|
* compare: null, // ¯\_(ツ)_/¯
|
||||||
|
* //...
|
||||||
|
* previous: {
|
||||||
|
* // ...
|
||||||
|
* build_num: 23456,
|
||||||
|
* },
|
||||||
|
* //...
|
||||||
|
* workflows: {
|
||||||
|
* //...
|
||||||
|
* workflow_id: 'SOME_ID_B',
|
||||||
|
* workspace_id: 'SOME_ID_A', // Different from current `workflow_id`.
|
||||||
|
* // Same as original `workflow_id`. \o/
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* This script uses the `previous.build_num` (which points to the previous build number on the same
|
||||||
|
* branch) to traverse the jobs backwards, until it finds a job from the original workflow. Such a
|
||||||
|
* job (if found) should also contain the compare URL.
|
||||||
|
*
|
||||||
|
* **NOTE 1:**
|
||||||
|
* This is only useful on workflows which are created by rerunning a workflow for which
|
||||||
|
* `CIRCLE_COMPARE_URL` was defined.
|
||||||
|
*
|
||||||
|
* **NOTE 2:**
|
||||||
|
* The `circleToken` will be used for CircleCI API requests if provided, but it is not needed for
|
||||||
|
* accessing the read-only endpoints that we need (as long as the current project is FOSS and the
|
||||||
|
* corresponding setting is turned on in "Advanced Settings" in the project dashboard).
|
||||||
|
*
|
||||||
|
* ---
|
||||||
|
* Inspired by https://circleci.com/orbs/registry/orb/iynere/compare-url
|
||||||
|
* (source code: https://github.com/iynere/compare-url-orb).
|
||||||
|
*
|
||||||
|
* We are not using the `compare-url` orb for the following reasons:
|
||||||
|
* 1. (By looking at the code) it would only work if the rerun workflow is the latest workflow on
|
||||||
|
* the branch (which is not guaranteed to be true).
|
||||||
|
* 2. It is less efficient (e.g. makes unnecessary CircleCI API requests for builds on different
|
||||||
|
* branches, installs extra dependencies, persists files to the workspace (as a means of passing
|
||||||
|
* the result to the calling job), etc.).
|
||||||
|
* 3. It is slightly more complicated to setup and consume than our own script.
|
||||||
|
* 4. Its implementation is more complicated than needed for our usecase (e.g. handles different git
|
||||||
|
* providers, handles newly created branches, etc.).
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Imports
|
||||||
|
const {get: httpsGet} = require('https');
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
const API_URL_BASE = 'https://circleci.com/api/v1.1/project/github/angular/angular';
|
||||||
|
const COMPARE_URL_RE = /^.*\/([0-9a-f]+\.\.\.[0-9a-f]+)$/i;
|
||||||
|
|
||||||
|
// Run
|
||||||
|
_main(process.argv.slice(2));
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
async function _main([buildNumber, compareUrl = '', circleToken = '']) {
|
||||||
|
try {
|
||||||
|
if (!buildNumber || isNaN(buildNumber)) {
|
||||||
|
throw new Error(
|
||||||
|
'Missing or invalid arguments.\n' +
|
||||||
|
'Expected: buildNumber (number), compareUrl? (string), circleToken? (string)');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!compareUrl) {
|
||||||
|
compareUrl = await getCompareUrl(buildNumber, circleToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
const commitRangeMatch = COMPARE_URL_RE.exec(compareUrl)
|
||||||
|
const commitRange = commitRangeMatch ? commitRangeMatch[1] : '';
|
||||||
|
|
||||||
|
console.log(commitRange);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBuildInfo(buildNumber, circleToken) {
|
||||||
|
console.error(`BUILD ${buildNumber}`);
|
||||||
|
const url = `${API_URL_BASE}/${buildNumber}?circle-token=${circleToken}`;
|
||||||
|
return getJson(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCompareUrl(buildNumber, circleToken) {
|
||||||
|
let info = await getBuildInfo(buildNumber, circleToken);
|
||||||
|
const targetWorkflowId = info.workflows.workspace_id;
|
||||||
|
|
||||||
|
while (info.workflows.workflow_id !== targetWorkflowId) {
|
||||||
|
info = await getBuildInfo(info.previous.build_num, circleToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
return info.compare || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getJson(url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const opts = {headers: {Accept: 'application/json'}};
|
||||||
|
const onResponse = res => {
|
||||||
|
const statusCode = res.statusCode || -1;
|
||||||
|
const isSuccess = (200 <= statusCode) && (statusCode < 400);
|
||||||
|
let responseText = '';
|
||||||
|
|
||||||
|
res.
|
||||||
|
on('error', reject).
|
||||||
|
on('data', d => responseText += d).
|
||||||
|
on('end', () => isSuccess ?
|
||||||
|
resolve(JSON.parse(responseText)) :
|
||||||
|
reject(`Error getting '${url}' (status ${statusCode}):\n${responseText}`));
|
||||||
|
};
|
||||||
|
|
||||||
|
httpsGet(url, opts, onResponse).
|
||||||
|
on('error', reject).
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
}
|
36
.circleci/get-vendored-yarn-path.js
Normal file
36
.circleci/get-vendored-yarn-path.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **Usage:**
|
||||||
|
* ```
|
||||||
|
* node get-vendored-yarn-path
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Returns the path to the vendored `yarn.js` script, so that it can be used for setting up yarn
|
||||||
|
* aliases/symlinks and use the local, vendored yarn script instead of a globally installed one.
|
||||||
|
*
|
||||||
|
* **Context:**
|
||||||
|
* We keep a version of yarn in the repo, at `third_party/github.com/yarnpkg/`. All CI jobs should
|
||||||
|
* use that version for consistency (and easier updates). The path to the actual `yarn.js` script,
|
||||||
|
* however, changes depending on the version (e.g. `third_party/github.com/yarnpkg/v1.21.1/...`).
|
||||||
|
*
|
||||||
|
* This script infers the correct path, so that we don't have to update the path in several places,
|
||||||
|
* when we update the version of yarn in `third_party/github.com/yarnpkg/`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const {readdirSync} = require('fs');
|
||||||
|
const {normalize} = require('path');
|
||||||
|
|
||||||
|
const yarnDownloadDir = `${__dirname}/../third_party/github.com/yarnpkg/yarn/releases/download`;
|
||||||
|
const yarnVersionSubdirs = readdirSync(yarnDownloadDir);
|
||||||
|
|
||||||
|
// Based on our current process, there should be exactly one sub-directory inside
|
||||||
|
// `vendoredYarnDownloadDir` at all times. Throw, if that is not the case.
|
||||||
|
if (yarnVersionSubdirs.length !== 1) {
|
||||||
|
throw new Error(
|
||||||
|
`Expected exactly 1 yarn version in '${yarnDownloadDir}', but found ` +
|
||||||
|
`${yarnVersionSubdirs.length}: ${yarnVersionSubdirs.join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(normalize(`${yarnDownloadDir}/${yarnVersionSubdirs[0]}/bin/yarn.js`));
|
@ -60,15 +60,14 @@ if (require.resolve === module) {
|
|||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
function _main(args) {
|
function _main(args) {
|
||||||
triggerWebhook(...args)
|
triggerWebhook(...args).
|
||||||
.then(
|
then(({statusCode, responseText}) => (200 <= statusCode && statusCode < 400) ?
|
||||||
({statusCode, responseText}) => (200 <= statusCode && statusCode < 400) ?
|
console.log(`Status: ${statusCode}\n${responseText}`) :
|
||||||
console.log(`Status: ${statusCode}\n${responseText}`) :
|
Promise.reject(new Error(`Request failed (status: ${statusCode}): ${responseText}`))).
|
||||||
Promise.reject(new Error(`Request failed (status: ${statusCode}): ${responseText}`)))
|
catch(err => {
|
||||||
.catch(err => {
|
console.error(err);
|
||||||
console.error(err);
|
process.exit(1);
|
||||||
process.exit(1);
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function postJson(url, data) {
|
function postJson(url, data) {
|
||||||
@ -78,12 +77,15 @@ function postJson(url, data) {
|
|||||||
const statusCode = res.statusCode || -1;
|
const statusCode = res.statusCode || -1;
|
||||||
let responseText = '';
|
let responseText = '';
|
||||||
|
|
||||||
res.on('error', reject)
|
res.
|
||||||
.on('data', d => responseText += d)
|
on('error', reject).
|
||||||
.on('end', () => resolve({statusCode, responseText}));
|
on('data', d => responseText += d).
|
||||||
|
on('end', () => resolve({statusCode, responseText}));
|
||||||
};
|
};
|
||||||
|
|
||||||
request(url, opts, onResponse).on('error', reject).end(JSON.stringify(data));
|
request(url, opts, onResponse).
|
||||||
|
on('error', reject).
|
||||||
|
end(JSON.stringify(data));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,12 +14,12 @@ Add-Content $profile '$Env:path = "${Env:ProgramFiles}\nodejs\;C:\Users\circleci
|
|||||||
# Environment variables for Bazel
|
# Environment variables for Bazel
|
||||||
Add-Content $profile '$Env:BAZEL_SH = "C:\tools\msys64\usr\bin\bash.exe"'
|
Add-Content $profile '$Env:BAZEL_SH = "C:\tools\msys64\usr\bin\bash.exe"'
|
||||||
|
|
||||||
# Get the bazelisk version devdep and store it in a global var for use in the circleci job.
|
# Get the bazel version devdep and store it in a global var for use in the circleci job.
|
||||||
$bazeliskVersion = & ${Env:ProgramFiles}\nodejs\node.exe -e "console.log(require('./package.json').devDependencies['@bazel/bazelisk'])"
|
$bazelVersion = & ${Env:ProgramFiles}\nodejs\node.exe -e "console.log(require('./package.json').devDependencies['@bazel/bazel'])"
|
||||||
# This is a tricky situation: we want $bazeliskVersion to be evaluated but not $Env:BAZELISK_VERSION.
|
# This is a tricky situation: we want $bazelVersion to be evaluated but not $Env:BAZEL_VERSION.
|
||||||
# Formatting works https://stackoverflow.com/questions/32127583/expand-variable-inside-single-quotes
|
# Formatting works https://stackoverflow.com/questions/32127583/expand-variable-inside-single-quotes
|
||||||
$bazeliskVersionGlobalVar = '$Env:BAZELISK_VERSION = "{0}"' -f $bazeliskVersion
|
$bazelVersionGlobalVar = '$Env:BAZEL_VERSION = "{0}"' -f $bazelVersion
|
||||||
Add-Content $profile $bazeliskVersionGlobalVar
|
Add-Content $profile $bazelVersionGlobalVar
|
||||||
|
|
||||||
# Remove the CircleCI checkout SSH override, because it breaks cloning repositories through Bazel.
|
# Remove the CircleCI checkout SSH override, because it breaks cloning repositories through Bazel.
|
||||||
# See https://circleci.com/gh/angular/angular/401454 for an example.
|
# See https://circleci.com/gh/angular/angular/401454 for an example.
|
||||||
@ -38,12 +38,9 @@ openssl aes-256-cbc -d -in .circleci\gcp_token -md md5 -out "$env:APPDATA\gcloud
|
|||||||
####################################################################################################
|
####################################################################################################
|
||||||
copy .circleci\bazel.windows.rc ${Env:USERPROFILE}\.bazelrc
|
copy .circleci\bazel.windows.rc ${Env:USERPROFILE}\.bazelrc
|
||||||
|
|
||||||
####################################################################################################
|
|
||||||
# Install specific version of node.
|
|
||||||
####################################################################################################
|
|
||||||
choco install nodejs --version 12.14.1 --no-progress
|
|
||||||
|
|
||||||
# These Bazel prereqs aren't needed because the CircleCI image already includes them.
|
# These Bazel prereqs aren't needed because the CircleCI image already includes them.
|
||||||
|
# choco install nodejs --version 10.16.0 --no-progress
|
||||||
# choco install yarn --version 1.16.0 --no-progress
|
# choco install yarn --version 1.16.0 --no-progress
|
||||||
# choco install vcredist2015 --version 14.0.24215.20170201
|
# choco install vcredist2015 --version 14.0.24215.20170201
|
||||||
|
|
||||||
|
14
.circleci/windows-yarn-setup.ps1
Normal file
14
.circleci/windows-yarn-setup.ps1
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Use our local, vendored yarn in the global `yarn` command.
|
||||||
|
$globalYarnDir = "$HOME\AppData\Roaming\yarn"
|
||||||
|
$localYarnPath = & ${Env:ProgramFiles}\nodejs\node.exe ".\.circleci\get-vendored-yarn-path.js"
|
||||||
|
|
||||||
|
# Create a directory to put the yarn PowerShell script.
|
||||||
|
New-Item -Path "$globalYarnDir" -ItemType "directory" >$null
|
||||||
|
|
||||||
|
# Create the yarn PowerShell script (using the inferred path to the local yarn script).
|
||||||
|
Get-Content -Path ".\.circleci\windows-yarn.ps1.template" |
|
||||||
|
%{$_ -replace "{{ LOCAL_YARN_PATH_PLACEHOLDER }}", "$localYarnPath"} |
|
||||||
|
Add-Content -Path "$globalYarnDir\yarn.ps1"
|
||||||
|
|
||||||
|
# Add the directory containing the yarn PowerShell script to `PATH`.
|
||||||
|
Add-Content -Path $profile -Value ('$Env:path = "{0};" + $Env:path' -f $globalYarnDir)
|
15
.circleci/windows-yarn.ps1.template
Normal file
15
.circleci/windows-yarn.ps1.template
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
$exe=""
|
||||||
|
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||||
|
# Fix case when both the Windows and Linux builds of Node
|
||||||
|
# are installed in the same directory
|
||||||
|
$exe=".exe"
|
||||||
|
}
|
||||||
|
$ret=0
|
||||||
|
if (Test-Path "$basedir/node$exe") {
|
||||||
|
& "$basedir/node$exe" "{{ LOCAL_YARN_PATH_PLACEHOLDER }}" $args
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
} else {
|
||||||
|
& "node$exe" "{{ LOCAL_YARN_PATH_PLACEHOLDER }}" $args
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
}
|
||||||
|
exit $ret
|
981
.github/CODEOWNERS
vendored
Normal file
981
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1,981 @@
|
|||||||
|
# ==================================================================================
|
||||||
|
# ==================================================================================
|
||||||
|
# Angular CODEOWNERS
|
||||||
|
# ==================================================================================
|
||||||
|
# ==================================================================================
|
||||||
|
#
|
||||||
|
# Configuration of code ownership and review approvals for the angular/angular repo.
|
||||||
|
#
|
||||||
|
# More info: https://help.github.com/articles/about-codeowners/
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# General rules / philosophy
|
||||||
|
# ================================================
|
||||||
|
#
|
||||||
|
# - we trust that people do the right thing and not approve changes they don't feel confident reviewing
|
||||||
|
# - we use github teams so that we funnel code reviews to the most appropriate reviewer, this is why the team structure is fine-grained
|
||||||
|
# - we enforce that only approved PRs get merged to ensure that unreviewed code doesn't get accidentally merged
|
||||||
|
# - we delegate approval rights as much as possible so that we can scale better
|
||||||
|
# - each group must have at least one person, but several people are preferable to avoid a single point of failure issues
|
||||||
|
# - most file groups have one or two global approvers groups as fallbacks:
|
||||||
|
# - @angular/fw-global-approvers: for approving minor changes, large-scale refactorings, and emergency situations.
|
||||||
|
# - @angular/fw-global-approvers-for-docs-only-changes: for approving minor documentation-only changes that don't require engineering review
|
||||||
|
# - a small number of file groups have very limited number of reviewers because incorrect changes to the files they guard would have serious consequences (e.g. security, public api)
|
||||||
|
#
|
||||||
|
# Configuration nuances:
|
||||||
|
#
|
||||||
|
# - This configuration works in conjunction with the protected branch settings that require all changes to be made via pull requests with at least one approval.
|
||||||
|
# - This approval can come from an appropriate codeowner, or any repo collaborator (person with write access) if the PR is authored by a codeowner.
|
||||||
|
# - Each codeowners team must have write access to the repo, otherwise their reviews won't count.
|
||||||
|
#
|
||||||
|
# In the case of emergency, the repo administrators which include angular-caretaker can bypass this requirement.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# GitHub username registry
|
||||||
|
# (just to make this file easier to understand)
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
# alan-agius4 - Alan Agius
|
||||||
|
# alexeagle - Alex Eagle
|
||||||
|
# alxhub - Alex Rickabaugh
|
||||||
|
# AndrewKushnir - Andrew Kushnir
|
||||||
|
# andrewseguin - Andrew Seguin
|
||||||
|
# atscott - Andrew Scott
|
||||||
|
# devversion - Paul Gschwendtner
|
||||||
|
# filipesilva - Filipe Silva
|
||||||
|
# gkalpak - George Kalpakas
|
||||||
|
# IgorMinar - Igor Minar
|
||||||
|
# JiaLiPassion - Jia Li
|
||||||
|
# JoostK - Joost Koehoorn
|
||||||
|
# josephperrott - Joey Perrott
|
||||||
|
# kapunahelewong - Kapunahele Wong
|
||||||
|
# kara - Kara Erickson
|
||||||
|
# kyliau - Keen Yee Liau
|
||||||
|
# matsko - Matias Niemelä
|
||||||
|
# mgechev - Minko Gechev
|
||||||
|
# mhevery - Misko Hevery
|
||||||
|
# petebacondarwin - Pete Bacon Darwin
|
||||||
|
# pkozlowski-opensource - Pawel Kozlowski
|
||||||
|
# robwormald - Rob Wormald
|
||||||
|
# stephenfluin - Stephen Fluin
|
||||||
|
# vikerman - Vikram Subramanian
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
######################################################################################################
|
||||||
|
#
|
||||||
|
# Team structure and memberships
|
||||||
|
# ------------------------------
|
||||||
|
#
|
||||||
|
# This section is here just because the GitHub UI is too hard to navigate and audit.
|
||||||
|
#
|
||||||
|
# Any changes to team structure or memberships must first be made in this file and only then
|
||||||
|
# implemented in the GitHub UI.
|
||||||
|
#######################################################################################################
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/framework-global-approvers
|
||||||
|
# ===========================================================
|
||||||
|
# Used for approving minor changes, large-scale refactorings, and emergency situations.
|
||||||
|
# (secret team to avoid review requests, it also doesn't inherit from @angular/framework because nested teams can't be secret)
|
||||||
|
#
|
||||||
|
# - IgorMinar
|
||||||
|
# - josephperrott
|
||||||
|
# - kara
|
||||||
|
# - mhevery
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
# ===========================================================
|
||||||
|
# Used for approving minor documentation-only changes that don't require engineering review.
|
||||||
|
# (secret team to avoid review requests, it also doesn't inherit from @angular/framework because nested teams can't be secret)
|
||||||
|
#
|
||||||
|
# - gkalpak
|
||||||
|
# - kapunahelewong
|
||||||
|
# - petebacondarwin
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/fw-animations
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - matsko
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/tools-bazel
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - alexeagle
|
||||||
|
# - kyliau
|
||||||
|
# - IgorMinar
|
||||||
|
# - mgechev
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/tools-cli
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - filipesilva
|
||||||
|
# - mgechev
|
||||||
|
# - vikerman
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/fw-compiler
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - alxhub
|
||||||
|
# - AndrewKushnir
|
||||||
|
# - kara
|
||||||
|
# - JoostK
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/fw-ngcc
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - alxhub
|
||||||
|
# - gkalpak
|
||||||
|
# - petebacondarwin
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/fw-core
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - alxhub
|
||||||
|
# - AndrewKushnir
|
||||||
|
# - kara
|
||||||
|
# - mhevery
|
||||||
|
# - pkozlowski-opensource
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/fw-http
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - alxhub
|
||||||
|
# - IgorMinar
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/fw-elements
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - andrewseguin
|
||||||
|
# - gkalpak
|
||||||
|
# - robwormald
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/fw-forms
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - AndrewKushnir
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/tools-language-service
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - kyliau
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/fw-server
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - alxhub
|
||||||
|
# - vikerman
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/fw-router
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - atscott
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/fw-service-worker
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - alxhub
|
||||||
|
# - gkalpak
|
||||||
|
# - IgorMinar
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/fw-upgrade
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - gkalpak
|
||||||
|
# - petebacondarwin
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/fw-testing
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - vikerman
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/fw-i18n
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - AndrewKushnir
|
||||||
|
# - mhevery
|
||||||
|
# - petebacondarwin
|
||||||
|
# - vikerman
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/fw-security
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - IgorMinar
|
||||||
|
# - mhevery
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/fw-zones
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - JiaLiPassion
|
||||||
|
# - mhevery
|
||||||
|
# - vikerman
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/tools-benchpress
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - alxhub
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/fw-integration
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - IgorMinar
|
||||||
|
# - josephperrott
|
||||||
|
# - mhevery
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/docs-infra
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - gkalpak
|
||||||
|
# - IgorMinar
|
||||||
|
# - petebacondarwin
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/fw-docs-intro
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - IgorMinar
|
||||||
|
# - stephenfluin
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/fw-docs-observables
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - alxhub
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/fw-docs-packaging
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - IgorMinar
|
||||||
|
# - vikerman
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/tools-docs-libraries
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - alan-agius4
|
||||||
|
# - IgorMinar
|
||||||
|
# - mgechev
|
||||||
|
# - vikerman
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/tools-docs-schematics
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - alan-agius4
|
||||||
|
# - IgorMinar
|
||||||
|
# - mgechev
|
||||||
|
# - vikerman
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/fw-docs-marketing
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - IgorMinar
|
||||||
|
# - stephenfluin
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/fw-public-api
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - IgorMinar
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/dev-infra-framework
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - devversion
|
||||||
|
# - filipesilva
|
||||||
|
# - gkalpak
|
||||||
|
# - IgorMinar
|
||||||
|
# - josephperrott
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================
|
||||||
|
# @angular/fw-size-tracking
|
||||||
|
# ===========================================================
|
||||||
|
#
|
||||||
|
# - IgorMinar
|
||||||
|
# - kara
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
######################################################################################################
|
||||||
|
#
|
||||||
|
# CODEOWNERS rules
|
||||||
|
# -----------------
|
||||||
|
#
|
||||||
|
# All the following rules are applied in the order specified in this file.
|
||||||
|
# The last rule that matches wins!
|
||||||
|
#
|
||||||
|
# See https://git-scm.com/docs/gitignore#_pattern_format for pattern syntax docs.
|
||||||
|
#
|
||||||
|
######################################################################################################
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# Default Owners
|
||||||
|
# (in case no pattern matches a path in a PR - this should be treated as a bug and result in adding the path to CODEOWNERS)
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
* @IgorMinar @angular/framework-global-approvers
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# Build, CI & Dev-infra Owners
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/* @angular/dev-infra-framework
|
||||||
|
/.buildkite/** @angular/dev-infra-framework
|
||||||
|
/.circleci/** @angular/dev-infra-framework
|
||||||
|
/.devcontainer/** @angular/dev-infra-framework
|
||||||
|
/.github/** @angular/dev-infra-framework
|
||||||
|
/.vscode/** @angular/dev-infra-framework
|
||||||
|
/docs/BAZEL.md @angular/dev-infra-framework
|
||||||
|
/packages/* @angular/dev-infra-framework
|
||||||
|
/packages/examples/test-utils/** @angular/dev-infra-framework
|
||||||
|
/packages/private/** @angular/dev-infra-framework
|
||||||
|
/scripts/** @angular/dev-infra-framework
|
||||||
|
/third_party/** @angular/dev-infra-framework
|
||||||
|
/tools/build/** @angular/dev-infra-framework
|
||||||
|
/tools/cjs-jasmine/** @angular/dev-infra-framework
|
||||||
|
/tools/gulp-tasks/** @angular/dev-infra-framework
|
||||||
|
/tools/ngcontainer/** @angular/dev-infra-framework
|
||||||
|
/tools/npm/** @angular/dev-infra-framework
|
||||||
|
/tools/npm_workspace/** @angular/dev-infra-framework
|
||||||
|
/tools/public_api_guard/** @angular/dev-infra-framework
|
||||||
|
/tools/rxjs/** @angular/dev-infra-framework
|
||||||
|
/tools/size-tracking/** @angular/dev-infra-framework
|
||||||
|
/tools/source-map-test/** @angular/dev-infra-framework
|
||||||
|
/tools/symbol-extractor/** @angular/dev-infra-framework
|
||||||
|
/tools/testing/** @angular/dev-infra-framework
|
||||||
|
/tools/ts-api-guardian/** @angular/dev-infra-framework
|
||||||
|
/tools/tslint/** @angular/dev-infra-framework
|
||||||
|
/tools/validate-commit-message/** @angular/dev-infra-framework
|
||||||
|
/tools/yarn/** @angular/dev-infra-framework
|
||||||
|
/tools/* @angular/dev-infra-framework
|
||||||
|
*.BAZEL @angular/dev-infra-framework
|
||||||
|
*.bzl @angular/dev-infra-framework
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# @angular/animations
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/packages/animations/** @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/platform-browser/animations/** @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/animations.md @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/animations/** @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/animations/** @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/complex-animation-sequences.md @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/reusable-animations.md @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/route-animations.md @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/transition-and-triggers.md @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# @angular/bazel
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/packages/bazel/** @angular/tools-bazel @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/bazel.md @angular/tools-bazel @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# @angular/compiler
|
||||||
|
# @angular/compiler-cli
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/packages/compiler/** @angular/fw-compiler @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/examples/compiler/** @angular/fw-compiler @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/compiler-cli/** @angular/fw-compiler @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/angular-compiler-options.md @angular/fw-compiler @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/aot-compiler.md @angular/fw-compiler @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/aot-metadata-errors.md @angular/fw-compiler @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/template-typecheck.md @angular/fw-compiler @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# packages/compiler-cli/ngcc/
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/packages/compiler-cli/ngcc/** @angular/fw-ngcc @angular/framework-global-approvers
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# Framework/cli integration
|
||||||
|
#
|
||||||
|
# a rule to control API changes between @angular/compiler-cli and @angular/cli
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/packages/compiler-cli/src/ngtools/** @angular/tools-cli @angular/framework-global-approvers
|
||||||
|
/aio/content/guide/cli-builder.md @angular/tools-cli @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/cli-builder/** @angular/tools-cli @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/ivy.md @angular/tools-cli @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/web-worker.md @angular/tools-cli @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# @angular/core
|
||||||
|
# @angular/common (except @angular/common/http)
|
||||||
|
# @angular/platform-browser
|
||||||
|
# @angular/platform-browser-dynamic
|
||||||
|
# @angular/platform-webworker
|
||||||
|
# @angular/platform-webworker-dynamic
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/packages/core/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/examples/core/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/common/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/platform-browser/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/examples/platform-browser/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/platform-browser-dynamic/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/platform-webworker/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/platform-webworker-dynamic/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/examples/common/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/docs/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/accessibility.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/accessibility/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/architecture-components.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/architecture-modules.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/architecture-next-steps.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/architecture-services.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/architecture.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/architecture/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/architecture/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/attribute-directives.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/attribute-directives/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/attribute-directives/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/bootstrapping.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/bootstrapping/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/cheatsheet.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/component-interaction.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/component-interaction/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/component-interaction/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/component-styles.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/component-styles/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/dependency-injection.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/dependency-injection/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/dependency-injection/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/dependency-injection-in-action.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/dependency-injection-in-action/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/dependency-injection-in-action/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/dependency-injection-navtree.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/dependency-injection-providers.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/displaying-data.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/displaying-data/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/displaying-data/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/dynamic-component-loader.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/dynamic-component-loader/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/dynamic-component-loader/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/entry-components.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/feature-modules.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/feature-modules/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/feature-modules/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/frequent-ngmodules.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/frequent-ngmodules/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/hierarchical-dependency-injection.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/hierarchical-dependency-injection/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/providers-viewproviders/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/resolution-modifiers/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/lazy-loading-ngmodules.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/lazy-loading-ngmodules/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/lazy-loading-ngmodules/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/lifecycle-hooks.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/lifecycle-hooks/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/lifecycle-hooks/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/examples/ngcontainer/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/ngmodules.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/ngmodules/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/ngmodule-api.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/ngmodule-faq.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/ngmodule-faq/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/ngmodule-vs-jsmodule.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/module-types.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/template-syntax.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/built-in-template-functions/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/event-binding/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/interpolation/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/template-syntax/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/template-syntax/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/binding-syntax/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/property-binding/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/attribute-binding/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/two-way-binding/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/built-in-directives/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/built-in-directives/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/template-reference-variables/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/inputs-outputs/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/inputs-outputs/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/template-expression-operators/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/pipes.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/pipes/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/pipes/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/providers.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/providers/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/singleton-services.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/set-document-title.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/set-document-title/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/set-document-title/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/sharing-ngmodules.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/structural-directives.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/structural-directives/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/structural-directives/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
/aio/content/guide/user-input.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/user-input/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/user-input/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# @angular/common/http
|
||||||
|
# @angular/http
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/packages/common/http/** @angular/fw-http @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/http/** @angular/fw-http @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/examples/http/** @angular/fw-http @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/http.md @angular/fw-http @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/http/** @angular/fw-http @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/http/** @angular/fw-http @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# @angular/elements
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/packages/elements/** @angular/fw-elements @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/elements/** @angular/fw-elements @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/elements/** @angular/fw-elements @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/elements.md @angular/fw-elements @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# @angular/forms
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/packages/forms/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/examples/forms/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/forms.md @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/forms/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/forms/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/forms-overview.md @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/forms-overview/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/forms-overview/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/form-validation.md @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/form-validation/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/form-validation/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/dynamic-form.md @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/dynamic-form/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/dynamic-form/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/reactive-forms.md @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/reactive-forms/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/reactive-forms/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# @angular/language-service
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/packages/language-service/** @angular/tools-language-service @angular/framework-global-approvers
|
||||||
|
/aio/content/guide/language-service.md @angular/tools-language-service @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/language-service/** @angular/tools-language-service @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# @angular/platform-server
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/packages/platform-server/** @angular/fw-server @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/universal.md @angular/fw-server @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/universal/** @angular/fw-server @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# @angular/router
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/packages/router/** @angular/fw-router @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/examples/router/** @angular/fw-router @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/router.md @angular/fw-router @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/router/** @angular/fw-router @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/router/** @angular/fw-router @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# @angular/service-worker
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/packages/service-worker/** @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/examples/service-worker/** @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/service-worker-getting-started.md @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/service-worker-getting-started/** @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/app-shell.md @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/service-worker-communications.md @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/service-worker-config.md @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/service-worker-devops.md @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/service-worker-intro.md @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/service-worker/** @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# @angular/upgrade
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/packages/upgrade/** @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/common/upgrade/** @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/examples/upgrade/** @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/upgrade.md @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/upgrade-lazy-load-ajs/** @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/upgrade-module/** @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/upgrade/** @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/upgrade-phonecat-1-typescript/** @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/upgrade-phonecat-2-hybrid/** @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/upgrade-phonecat-3-final/** @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/upgrade-performance.md @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/upgrade-setup.md @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/ajs-quick-reference.md @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/ajs-quick-reference/** @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# @angular/**/testing
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
testing/** @angular/fw-testing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/testing.md @angular/fw-testing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/testing/** @angular/fw-testing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/testing/** @angular/fw-testing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# @angular i18n
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/packages/core/src/i18n/** @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/core/src/render3/i18n.ts @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/core/src/render3/i18n.md @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/core/src/render3/interfaces/i18n.ts @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/common/locales/** @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/common/src/i18n/** @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/common/src/pipes/date_pipe.ts @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/common/src/pipes/i18n_plural_pipe.ts @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/common/src/pipes/i18n_select_pipe.ts @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/common/src/pipes/number_pipe.ts @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/compiler/src/i18n/** @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/compiler/src/render3/view/i18n/** @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/compiler-cli/src/extract_i18n.ts @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/packages/localize/** @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/i18n.md @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/i18n/** @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# @angular security
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/packages/core/src/sanitization/** @angular/fw-security
|
||||||
|
/packages/core/test/linker/security_integration_spec.ts @angular/fw-security
|
||||||
|
/packages/compiler/src/schema/** @angular/fw-security
|
||||||
|
/packages/platform-browser/src/security/** @angular/fw-security
|
||||||
|
/aio/content/guide/security.md @angular/fw-security @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/security/** @angular/fw-security @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/security/** @angular/fw-security @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# zone.js
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/packages/zone.js/** @angular/fw-zones @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# benchpress
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/packages/benchpress/** @angular/tools-benchpress @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# /integration/*
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/integration/** @angular/fw-integration @angular/framework-global-approvers
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# docs-infra
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/aio/* @angular/docs-infra @angular/framework-global-approvers
|
||||||
|
/aio/aio-builds-setup/** @angular/docs-infra @angular/framework-global-approvers
|
||||||
|
/aio/content/examples/* @angular/docs-infra @angular/framework-global-approvers
|
||||||
|
/aio/scripts/** @angular/docs-infra @angular/framework-global-approvers
|
||||||
|
/aio/src/** @angular/docs-infra @angular/framework-global-approvers
|
||||||
|
/aio/tests/** @angular/docs-infra @angular/framework-global-approvers
|
||||||
|
/aio/tools/** @angular/docs-infra @angular/framework-global-approvers
|
||||||
|
|
||||||
|
# Hidden docs
|
||||||
|
/aio/content/guide/docs-style-guide.md @angular/docs-infra @angular/framework-global-approvers
|
||||||
|
/aio/content/examples/docs-style-guide/** @angular/docs-infra @angular/framework-global-approvers
|
||||||
|
/aio/content/images/guide/docs-style-guide/** @angular/docs-infra @angular/framework-global-approvers
|
||||||
|
/aio/content/guide/visual-studio-2015.md @angular/docs-infra @angular/framework-global-approvers
|
||||||
|
/aio/content/examples/visual-studio-2015/** @angular/docs-infra @angular/framework-global-approvers
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# Docs: getting started & tutorial
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/aio/content/guide/setup-local.md @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/setup-local/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/tutorial/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/toh/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/toh-pt0/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/toh-pt1/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/toh-pt2/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/toh-pt3/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/toh-pt4/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/toh-pt5/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/toh-pt6/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/getting-started-v0/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/getting-started/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/start/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/start/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# Docs: observables
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/aio/content/guide/observables.md @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/observables/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/comparing-observables.md @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/comparing-observables/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/observables-in-angular.md @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/observables-in-angular/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/practical-observable-usage.md @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/practical-observable-usage/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/rx-library.md @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/rx-library/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# Docs: packaging, tooling, releasing
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/aio/content/guide/npm-packages.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/browser-support.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/typescript-configuration.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/setup/** @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/build.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/build/** @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/deployment.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/deployment/** @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/file-structure.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/releases.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/updating.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/workspace-config.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/deprecations.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/migration-renderer.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/migration-undecorated-classes.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/migration-dynamic-flag.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/migration-injectable.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/migration-localize.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/migration-module-with-providers.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/updating-to-version-9.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/ivy-compatibility.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/ivy-compatibility-examples.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# Docs: libraries
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/aio/content/guide/creating-libraries.md @angular/tools-docs-libraries @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/libraries.md @angular/tools-docs-libraries @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/using-libraries.md @angular/tools-docs-libraries @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# Docs: schematics
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/aio/content/guide/schematics.md @angular/tools-docs-schematics @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/schematics-authoring.md @angular/tools-docs-schematics @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/guide/schematics-for-libraries.md @angular/tools-docs-schematics @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/guide/schematics/** @angular/tools-docs-schematics @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/examples/schematics-for-libraries/** @angular/tools-docs-schematics @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# Docs: marketing
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/aio/content/marketing/** @angular/fw-docs-marketing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/bios/** @angular/fw-docs-marketing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/images/marketing/** @angular/fw-docs-marketing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/navigation.json @angular/fw-docs-marketing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
/aio/content/license.md @angular/fw-docs-marketing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# angular/components CI
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/tools/components-repo-ci/** @angular/fw-core @angular/framework-global-approvers
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# Public API
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/tools/public_api_guard/** @angular/fw-public-api
|
||||||
|
/aio/content/guide/glossary.md @angular/fw-public-api
|
||||||
|
/aio/content/guide/styleguide.md @angular/fw-public-api
|
||||||
|
/aio/content/examples/styleguide/** @angular/fw-public-api
|
||||||
|
/aio/content/images/guide/styleguide/** @angular/fw-public-api
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# Size tracking
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/aio/scripts/_payload-limits.json @angular/fw-size-tracking
|
||||||
|
/integration/_payload-limits.json @angular/fw-size-tracking
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# Special cases
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/aio/content/guide/static-query-migration.md @kara @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# CODEOWNERS Owners owners ...
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
/.github/CODEOWNERS @IgorMinar @angular/framework-global-approvers
|
2
.github/ISSUE_TEMPLATE/1-bug-report.md
vendored
2
.github/ISSUE_TEMPLATE/1-bug-report.md
vendored
@ -32,7 +32,7 @@ Existing issues often contain information about workarounds, resolution, or prog
|
|||||||
|
|
||||||
## 🔬 Minimal Reproduction
|
## 🔬 Minimal Reproduction
|
||||||
<!--
|
<!--
|
||||||
Please create and share minimal reproduction of the issue starting with this template: https://stackblitz.com/fork/angular-ivy
|
Please create and share minimal reproduction of the issue starting with this template: https://stackblitz.com/fork/angular-issue-repro2
|
||||||
-->
|
-->
|
||||||
<!-- ✍️--> https://stackblitz.com/...
|
<!-- ✍️--> https://stackblitz.com/...
|
||||||
|
|
||||||
|
1
.github/angular-robot.yml
vendored
1
.github/angular-robot.yml
vendored
@ -115,7 +115,6 @@ merge:
|
|||||||
- "ci/angular: size"
|
- "ci/angular: size"
|
||||||
- "cla/google"
|
- "cla/google"
|
||||||
- "google3"
|
- "google3"
|
||||||
- "pullapprove"
|
|
||||||
|
|
||||||
|
|
||||||
# the comment that will be added when the merge label is added despite failing checks, leave empty or set to false to disable
|
# the comment that will be added when the merge label is added despite failing checks, leave empty or set to false to disable
|
||||||
|
1
.github/workflows/lock-closed.yml
vendored
1
.github/workflows/lock-closed.yml
vendored
@ -7,7 +7,6 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lock_closed:
|
lock_closed:
|
||||||
if: github.repository == 'angular/angular'
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: angular/dev-infra/github-actions/lock-closed@66462f6
|
- uses: angular/dev-infra/github-actions/lock-closed@66462f6
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,8 +3,10 @@
|
|||||||
/dist/
|
/dist/
|
||||||
/bazel-out
|
/bazel-out
|
||||||
/integration/bazel/bazel-*
|
/integration/bazel/bazel-*
|
||||||
|
e2e_test.*
|
||||||
*.log
|
*.log
|
||||||
node_modules
|
node_modules
|
||||||
|
tools/gulp-tasks/cldr/cldr-data/
|
||||||
|
|
||||||
# Include when developing application packages.
|
# Include when developing application packages.
|
||||||
pubspec.lock
|
pubspec.lock
|
||||||
|
@ -1,119 +0,0 @@
|
|||||||
import {MergeConfig} from '../dev-infra/pr/merge/config';
|
|
||||||
|
|
||||||
// The configuration for `ng-dev commit-message` commands.
|
|
||||||
const commitMessage = {
|
|
||||||
'maxLength': 120,
|
|
||||||
'minBodyLength': 100,
|
|
||||||
'types': [
|
|
||||||
'build',
|
|
||||||
'ci',
|
|
||||||
'docs',
|
|
||||||
'feat',
|
|
||||||
'fix',
|
|
||||||
'perf',
|
|
||||||
'refactor',
|
|
||||||
'release',
|
|
||||||
'style',
|
|
||||||
'test',
|
|
||||||
],
|
|
||||||
'scopes': [
|
|
||||||
'animations',
|
|
||||||
'bazel',
|
|
||||||
'benchpress',
|
|
||||||
'changelog',
|
|
||||||
'common',
|
|
||||||
'compiler',
|
|
||||||
'compiler-cli',
|
|
||||||
'core',
|
|
||||||
'dev-infra',
|
|
||||||
'docs-infra',
|
|
||||||
'elements',
|
|
||||||
'forms',
|
|
||||||
'http',
|
|
||||||
'language-service',
|
|
||||||
'localize',
|
|
||||||
'migrations',
|
|
||||||
'ngcc',
|
|
||||||
'packaging',
|
|
||||||
'platform-browser',
|
|
||||||
'platform-browser-dynamic',
|
|
||||||
'platform-server',
|
|
||||||
'platform-webworker',
|
|
||||||
'platform-webworker-dynamic',
|
|
||||||
'router',
|
|
||||||
'service-worker',
|
|
||||||
'upgrade',
|
|
||||||
've',
|
|
||||||
'zone.js',
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
// The configuration for `ng-dev format` commands.
|
|
||||||
const format = {
|
|
||||||
'clang-format': {
|
|
||||||
'matchers': [
|
|
||||||
'**/*.{js,ts}',
|
|
||||||
// TODO: burn down format failures and remove aio and integration exceptions.
|
|
||||||
'!aio/**',
|
|
||||||
'!integration/**',
|
|
||||||
// TODO: remove this exclusion as part of IE deprecation.
|
|
||||||
'!shims_for_IE.js',
|
|
||||||
// Both third_party and .yarn are directories containing copied code which should
|
|
||||||
// not be modified.
|
|
||||||
'!third_party/**',
|
|
||||||
'!.yarn/**',
|
|
||||||
// Do not format d.ts files as they are generated
|
|
||||||
'!**/*.d.ts',
|
|
||||||
]
|
|
||||||
},
|
|
||||||
'buildifier': true
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Github metadata information for `ng-dev` commands. */
|
|
||||||
const github = {
|
|
||||||
owner: 'angular',
|
|
||||||
name: 'angular',
|
|
||||||
};
|
|
||||||
|
|
||||||
// Configuration for the `ng-dev pr merge` command. The command can be used
|
|
||||||
// for merging upstream pull requests into branches based on a PR target label.
|
|
||||||
const merge = () => {
|
|
||||||
// TODO: resume dynamically determining patch branch
|
|
||||||
const patch = '10.0.x';
|
|
||||||
const config: MergeConfig = {
|
|
||||||
githubApiMerge: false,
|
|
||||||
claSignedLabel: 'cla: yes',
|
|
||||||
mergeReadyLabel: /^PR action: merge(-assistance)?/,
|
|
||||||
commitMessageFixupLabel: 'commit message fixup',
|
|
||||||
labels: [
|
|
||||||
{
|
|
||||||
pattern: 'PR target: master-only',
|
|
||||||
branches: ['master'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
pattern: 'PR target: patch-only',
|
|
||||||
branches: [patch],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
pattern: 'PR target: master & patch',
|
|
||||||
branches: ['master', patch],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
requiredBaseCommits: {
|
|
||||||
// PRs that target either `master` or the patch branch, need to be rebased
|
|
||||||
// on top of the latest commit message validation fix.
|
|
||||||
// These SHAs are the commits that update the required license text in the header.
|
|
||||||
'master': '5aeb9a4124922d8ac08eb73b8f322905a32b0b3a',
|
|
||||||
[patch]: '27b95ba64a5d99757f4042073fd1860e20e3ed24'
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return config;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Export function to build ng-dev configuration object.
|
|
||||||
module.exports = {
|
|
||||||
commitMessage,
|
|
||||||
format,
|
|
||||||
github,
|
|
||||||
merge,
|
|
||||||
};
|
|
1156
.pullapprove.yml
1156
.pullapprove.yml
File diff suppressed because it is too large
Load Diff
6
.vscode/recommended-launch.json
vendored
6
.vscode/recommended-launch.json
vendored
@ -34,7 +34,7 @@
|
|||||||
"name": "IVY:packages/core/test/acceptance",
|
"name": "IVY:packages/core/test/acceptance",
|
||||||
"type": "node",
|
"type": "node",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
"program": "${workspaceFolder}/node_modules/.bin/bazel",
|
||||||
"args": [
|
"args": [
|
||||||
"test",
|
"test",
|
||||||
"--config=ivy",
|
"--config=ivy",
|
||||||
@ -51,7 +51,7 @@
|
|||||||
"name": "IVY:packages/core/test/render3",
|
"name": "IVY:packages/core/test/render3",
|
||||||
"type": "node",
|
"type": "node",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
"program": "${workspaceFolder}/node_modules/.bin/bazel",
|
||||||
"args": [
|
"args": [
|
||||||
"test",
|
"test",
|
||||||
"--config=ivy",
|
"--config=ivy",
|
||||||
@ -68,7 +68,7 @@
|
|||||||
"name": "IVY:packages/core/test",
|
"name": "IVY:packages/core/test",
|
||||||
"type": "node",
|
"type": "node",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
"program": "${workspaceFolder}/node_modules/.bin/bazel",
|
||||||
"args": [
|
"args": [
|
||||||
"test",
|
"test",
|
||||||
"--config=ivy",
|
"--config=ivy",
|
||||||
|
14
.vscode/recommended-tasks.json
vendored
14
.vscode/recommended-tasks.json
vendored
@ -6,7 +6,7 @@
|
|||||||
{
|
{
|
||||||
"label": "IVY:packages/core/test/...",
|
"label": "IVY:packages/core/test/...",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
"command": "${workspaceFolder}/node_modules/.bin/bazel",
|
||||||
"args": [
|
"args": [
|
||||||
"test",
|
"test",
|
||||||
"--config=ivy",
|
"--config=ivy",
|
||||||
@ -23,7 +23,7 @@
|
|||||||
{
|
{
|
||||||
"label": "VE:packages/core/test/...",
|
"label": "VE:packages/core/test/...",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
"command": "${workspaceFolder}/node_modules/.bin/bazel",
|
||||||
"args": [
|
"args": [
|
||||||
"test",
|
"test",
|
||||||
"packages/core/test",
|
"packages/core/test",
|
||||||
@ -39,7 +39,7 @@
|
|||||||
{
|
{
|
||||||
"label": "IVY:packages/core/test/acceptance",
|
"label": "IVY:packages/core/test/acceptance",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
"command": "${workspaceFolder}/node_modules/.bin/bazel",
|
||||||
"args": [
|
"args": [
|
||||||
"test",
|
"test",
|
||||||
"--config=ivy",
|
"--config=ivy",
|
||||||
@ -54,7 +54,7 @@
|
|||||||
{
|
{
|
||||||
"label": "VE:packages/core/test/acceptance",
|
"label": "VE:packages/core/test/acceptance",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
"command": "${workspaceFolder}/node_modules/.bin/bazel",
|
||||||
"args": [
|
"args": [
|
||||||
"test",
|
"test",
|
||||||
"packages/core/test/acceptance",
|
"packages/core/test/acceptance",
|
||||||
@ -68,7 +68,7 @@
|
|||||||
{
|
{
|
||||||
"label": "IVY:packages/core/test",
|
"label": "IVY:packages/core/test",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
"command": "${workspaceFolder}/node_modules/.bin/bazel",
|
||||||
"args": [
|
"args": [
|
||||||
"test",
|
"test",
|
||||||
"--config=ivy",
|
"--config=ivy",
|
||||||
@ -83,7 +83,7 @@
|
|||||||
{
|
{
|
||||||
"label": "VE:packages/core/test",
|
"label": "VE:packages/core/test",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
"command": "${workspaceFolder}/node_modules/.bin/bazel",
|
||||||
"args": [
|
"args": [
|
||||||
"test",
|
"test",
|
||||||
"packages/core/test",
|
"packages/core/test",
|
||||||
@ -97,7 +97,7 @@
|
|||||||
{
|
{
|
||||||
"label": "IVY:packages/core/test/render3",
|
"label": "IVY:packages/core/test/render3",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
"command": "${workspaceFolder}/node_modules/.bin/bazel",
|
||||||
"args": [
|
"args": [
|
||||||
"test",
|
"test",
|
||||||
"--config=ivy",
|
"--config=ivy",
|
||||||
|
@ -34805,27 +34805,15 @@ function hasMergeConflicts(str) {
|
|||||||
function parse(str, fileLoc) {
|
function parse(str, fileLoc) {
|
||||||
const parser = new Parser(str, fileLoc);
|
const parser = new Parser(str, fileLoc);
|
||||||
parser.next();
|
parser.next();
|
||||||
|
try {
|
||||||
if (!fileLoc.endsWith(`.yml`)) {
|
return parser.parse();
|
||||||
|
} catch (error1) {
|
||||||
try {
|
try {
|
||||||
return parser.parse();
|
return safeLoad(str, {
|
||||||
} catch (error1) {
|
schema: FAILSAFE_SCHEMA
|
||||||
try {
|
});
|
||||||
return safeLoad(str, {
|
} catch (error2) {
|
||||||
schema: FAILSAFE_SCHEMA
|
throw error1;
|
||||||
});
|
|
||||||
} catch (error2) {
|
|
||||||
throw error1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const result = safeLoad(str, {
|
|
||||||
schema: FAILSAFE_SCHEMA
|
|
||||||
});
|
|
||||||
if (typeof result === 'object') {
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46678,7 +46666,7 @@ function mkdirfix (name, opts, cb) {
|
|||||||
/* 194 */
|
/* 194 */
|
||||||
/***/ (function(module, exports) {
|
/***/ (function(module, exports) {
|
||||||
|
|
||||||
module.exports = {"name":"yarn","installationMethod":"unknown","version":"1.22.4","license":"BSD-2-Clause","preferGlobal":true,"description":"📦🐈 Fast, reliable, and secure dependency management.","dependencies":{"@zkochan/cmd-shim":"^3.1.0","babel-runtime":"^6.26.0","bytes":"^3.0.0","camelcase":"^4.0.0","chalk":"^2.1.0","cli-table3":"^0.4.0","commander":"^2.9.0","death":"^1.0.0","debug":"^3.0.0","deep-equal":"^1.0.1","detect-indent":"^5.0.0","dnscache":"^1.0.1","glob":"^7.1.1","gunzip-maybe":"^1.4.0","hash-for-dep":"^1.2.3","imports-loader":"^0.8.0","ini":"^1.3.4","inquirer":"^6.2.0","invariant":"^2.2.0","is-builtin-module":"^2.0.0","is-ci":"^1.0.10","is-webpack-bundle":"^1.0.0","js-yaml":"^3.13.1","leven":"^2.0.0","loud-rejection":"^1.2.0","micromatch":"^2.3.11","mkdirp":"^0.5.1","node-emoji":"^1.6.1","normalize-url":"^2.0.0","npm-logical-tree":"^1.2.1","object-path":"^0.11.2","proper-lockfile":"^2.0.0","puka":"^1.0.0","read":"^1.0.7","request":"^2.87.0","request-capture-har":"^1.2.2","rimraf":"^2.5.0","semver":"^5.1.0","ssri":"^5.3.0","strip-ansi":"^4.0.0","strip-bom":"^3.0.0","tar-fs":"^1.16.0","tar-stream":"^1.6.1","uuid":"^3.0.1","v8-compile-cache":"^2.0.0","validate-npm-package-license":"^3.0.4","yn":"^2.0.0"},"devDependencies":{"babel-core":"^6.26.0","babel-eslint":"^7.2.3","babel-loader":"^6.2.5","babel-plugin-array-includes":"^2.0.3","babel-plugin-inline-import":"^3.0.0","babel-plugin-transform-builtin-extend":"^1.1.2","babel-plugin-transform-inline-imports-commonjs":"^1.0.0","babel-plugin-transform-runtime":"^6.4.3","babel-preset-env":"^1.6.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.0.0","babylon":"^6.5.0","commitizen":"^2.9.6","cz-conventional-changelog":"^2.0.0","eslint":"^4.3.0","eslint-config-fb-strict":"^22.0.0","eslint-plugin-babel":"^5.0.0","eslint-plugin-flowtype":"^2.35.0","eslint-plugin-jasmine":"^2.6.2","eslint-plugin-jest":"^21.0.0","eslint-plugin-jsx-a11y":"^6.0.2","eslint-plugin-prefer-object-spread":"^1.2.1","eslint-plugin-prettier":"^2.1.2","eslint-plugin-react":"^7.1.0","eslint-plugin-relay":"^0.0.28","eslint-plugin-yarn-internal":"file:scripts/eslint-rules","execa":"^0.11.0","fancy-log":"^1.3.2","flow-bin":"^0.66.0","git-release-notes":"^3.0.0","gulp":"^4.0.0","gulp-babel":"^7.0.0","gulp-if":"^2.0.1","gulp-newer":"^1.0.0","gulp-plumber":"^1.0.1","gulp-sourcemaps":"^2.2.0","jest":"^22.4.4","jsinspect":"^0.12.6","minimatch":"^3.0.4","mock-stdin":"^0.3.0","prettier":"^1.5.2","string-replace-loader":"^2.1.1","temp":"^0.8.3","webpack":"^2.1.0-beta.25","yargs":"^6.3.0"},"resolutions":{"sshpk":"^1.14.2"},"engines":{"node":">=4.0.0"},"repository":"yarnpkg/yarn","bin":{"yarn":"./bin/yarn.js","yarnpkg":"./bin/yarn.js"},"scripts":{"build":"gulp build","build-bundle":"node ./scripts/build-webpack.js","build-chocolatey":"powershell ./scripts/build-chocolatey.ps1","build-deb":"./scripts/build-deb.sh","build-dist":"bash ./scripts/build-dist.sh","build-win-installer":"scripts\\build-windows-installer.bat","changelog":"git-release-notes $(git describe --tags --abbrev=0 $(git describe --tags --abbrev=0)^)..$(git describe --tags --abbrev=0) scripts/changelog.md","dupe-check":"yarn jsinspect ./src","lint":"eslint . && flow check","pkg-tests":"yarn --cwd packages/pkg-tests jest yarn.test.js","prettier":"eslint src __tests__ --fix","release-branch":"./scripts/release-branch.sh","test":"yarn lint && yarn test-only","test-only":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --verbose","test-only-debug":"node --inspect-brk --max_old_space_size=4096 node_modules/jest/bin/jest.js --runInBand --verbose","test-coverage":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --coverage --verbose","watch":"gulp watch","commit":"git-cz"},"jest":{"collectCoverageFrom":["src/**/*.js"],"testEnvironment":"node","modulePathIgnorePatterns":["__tests__/fixtures/","packages/pkg-tests/pkg-tests-fixtures","dist/"],"testPathIgnorePatterns":["__tests__/(fixtures|__mocks__)/","updates/","_(temp|mock|install|init|helpers).js$","packages/pkg-tests"]},"config":{"commitizen":{"path":"./node_modules/cz-conventional-changelog"}}}
|
module.exports = {"name":"yarn","installationMethod":"unknown","version":"1.21.1","license":"BSD-2-Clause","preferGlobal":true,"description":"📦🐈 Fast, reliable, and secure dependency management.","dependencies":{"@zkochan/cmd-shim":"^3.1.0","babel-runtime":"^6.26.0","bytes":"^3.0.0","camelcase":"^4.0.0","chalk":"^2.1.0","cli-table3":"^0.4.0","commander":"^2.9.0","death":"^1.0.0","debug":"^3.0.0","deep-equal":"^1.0.1","detect-indent":"^5.0.0","dnscache":"^1.0.1","glob":"^7.1.1","gunzip-maybe":"^1.4.0","hash-for-dep":"^1.2.3","imports-loader":"^0.8.0","ini":"^1.3.4","inquirer":"^6.2.0","invariant":"^2.2.0","is-builtin-module":"^2.0.0","is-ci":"^1.0.10","is-webpack-bundle":"^1.0.0","js-yaml":"^3.13.1","leven":"^2.0.0","loud-rejection":"^1.2.0","micromatch":"^2.3.11","mkdirp":"^0.5.1","node-emoji":"^1.6.1","normalize-url":"^2.0.0","npm-logical-tree":"^1.2.1","object-path":"^0.11.2","proper-lockfile":"^2.0.0","puka":"^1.0.0","read":"^1.0.7","request":"^2.87.0","request-capture-har":"^1.2.2","rimraf":"^2.5.0","semver":"^5.1.0","ssri":"^5.3.0","strip-ansi":"^4.0.0","strip-bom":"^3.0.0","tar-fs":"^1.16.0","tar-stream":"^1.6.1","uuid":"^3.0.1","v8-compile-cache":"^2.0.0","validate-npm-package-license":"^3.0.4","yn":"^2.0.0"},"devDependencies":{"babel-core":"^6.26.0","babel-eslint":"^7.2.3","babel-loader":"^6.2.5","babel-plugin-array-includes":"^2.0.3","babel-plugin-inline-import":"^3.0.0","babel-plugin-transform-builtin-extend":"^1.1.2","babel-plugin-transform-inline-imports-commonjs":"^1.0.0","babel-plugin-transform-runtime":"^6.4.3","babel-preset-env":"^1.6.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.0.0","babylon":"^6.5.0","commitizen":"^2.9.6","cz-conventional-changelog":"^2.0.0","eslint":"^4.3.0","eslint-config-fb-strict":"^22.0.0","eslint-plugin-babel":"^5.0.0","eslint-plugin-flowtype":"^2.35.0","eslint-plugin-jasmine":"^2.6.2","eslint-plugin-jest":"^21.0.0","eslint-plugin-jsx-a11y":"^6.0.2","eslint-plugin-prefer-object-spread":"^1.2.1","eslint-plugin-prettier":"^2.1.2","eslint-plugin-react":"^7.1.0","eslint-plugin-relay":"^0.0.28","eslint-plugin-yarn-internal":"file:scripts/eslint-rules","execa":"^0.11.0","fancy-log":"^1.3.2","flow-bin":"^0.66.0","git-release-notes":"^3.0.0","gulp":"^4.0.0","gulp-babel":"^7.0.0","gulp-if":"^2.0.1","gulp-newer":"^1.0.0","gulp-plumber":"^1.0.1","gulp-sourcemaps":"^2.2.0","jest":"^22.4.4","jsinspect":"^0.12.6","minimatch":"^3.0.4","mock-stdin":"^0.3.0","prettier":"^1.5.2","string-replace-loader":"^2.1.1","temp":"^0.8.3","webpack":"^2.1.0-beta.25","yargs":"^6.3.0"},"resolutions":{"sshpk":"^1.14.2"},"engines":{"node":">=4.0.0"},"repository":"yarnpkg/yarn","bin":{"yarn":"./bin/yarn.js","yarnpkg":"./bin/yarn.js"},"scripts":{"build":"gulp build","build-bundle":"node ./scripts/build-webpack.js","build-chocolatey":"powershell ./scripts/build-chocolatey.ps1","build-deb":"./scripts/build-deb.sh","build-dist":"bash ./scripts/build-dist.sh","build-win-installer":"scripts\\build-windows-installer.bat","changelog":"git-release-notes $(git describe --tags --abbrev=0 $(git describe --tags --abbrev=0)^)..$(git describe --tags --abbrev=0) scripts/changelog.md","dupe-check":"yarn jsinspect ./src","lint":"eslint . && flow check","pkg-tests":"yarn --cwd packages/pkg-tests jest yarn.test.js","prettier":"eslint src __tests__ --fix","release-branch":"./scripts/release-branch.sh","test":"yarn lint && yarn test-only","test-only":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --verbose","test-only-debug":"node --inspect-brk --max_old_space_size=4096 node_modules/jest/bin/jest.js --runInBand --verbose","test-coverage":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --coverage --verbose","watch":"gulp watch","commit":"git-cz"},"jest":{"collectCoverageFrom":["src/**/*.js"],"testEnvironment":"node","modulePathIgnorePatterns":["__tests__/fixtures/","packages/pkg-tests/pkg-tests-fixtures","dist/"],"testPathIgnorePatterns":["__tests__/(fixtures|__mocks__)/","updates/","_(temp|mock|install|init|helpers).js$","packages/pkg-tests"]},"config":{"commitizen":{"path":"./node_modules/cz-conventional-changelog"}}}
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
/* 195 */
|
/* 195 */
|
||||||
@ -69888,12 +69876,12 @@ function getRcConfigForFolder(cwd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function loadRcFile(fileText, filePath) {
|
function loadRcFile(fileText, filePath) {
|
||||||
var _parse = (0, (_lockfile || _load_lockfile()).parse)(fileText, filePath);
|
var _parse = (0, (_lockfile || _load_lockfile()).parse)(fileText, 'yarnrc');
|
||||||
|
|
||||||
let values = _parse.object;
|
let values = _parse.object;
|
||||||
|
|
||||||
|
|
||||||
if (filePath.match(/\.yml$/) && typeof values.yarnPath === 'string') {
|
if (filePath.match(/\.yml$/)) {
|
||||||
values = { 'yarn-path': values.yarnPath };
|
values = { 'yarn-path': values.yarnPath };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74860,20 +74848,7 @@ let run = exports.run = (() => {
|
|||||||
} else {
|
} else {
|
||||||
let suggestion;
|
let suggestion;
|
||||||
|
|
||||||
for (var _iterator9 = scripts.keys(), _isArray9 = Array.isArray(_iterator9), _i9 = 0, _iterator9 = _isArray9 ? _iterator9 : _iterator9[Symbol.iterator]();;) {
|
for (const commandName in scripts) {
|
||||||
var _ref16;
|
|
||||||
|
|
||||||
if (_isArray9) {
|
|
||||||
if (_i9 >= _iterator9.length) break;
|
|
||||||
_ref16 = _iterator9[_i9++];
|
|
||||||
} else {
|
|
||||||
_i9 = _iterator9.next();
|
|
||||||
if (_i9.done) break;
|
|
||||||
_ref16 = _i9.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
const commandName = _ref16;
|
|
||||||
|
|
||||||
const steps = leven(commandName, action);
|
const steps = leven(commandName, action);
|
||||||
if (steps < 2) {
|
if (steps < 2) {
|
||||||
suggestion = commandName;
|
suggestion = commandName;
|
||||||
@ -74958,19 +74933,19 @@ let run = exports.run = (() => {
|
|||||||
|
|
||||||
const printedCommands = new Map();
|
const printedCommands = new Map();
|
||||||
|
|
||||||
for (var _iterator10 = pkgCommands, _isArray10 = Array.isArray(_iterator10), _i10 = 0, _iterator10 = _isArray10 ? _iterator10 : _iterator10[Symbol.iterator]();;) {
|
for (var _iterator9 = pkgCommands, _isArray9 = Array.isArray(_iterator9), _i9 = 0, _iterator9 = _isArray9 ? _iterator9 : _iterator9[Symbol.iterator]();;) {
|
||||||
var _ref17;
|
var _ref16;
|
||||||
|
|
||||||
if (_isArray10) {
|
if (_isArray9) {
|
||||||
if (_i10 >= _iterator10.length) break;
|
if (_i9 >= _iterator9.length) break;
|
||||||
_ref17 = _iterator10[_i10++];
|
_ref16 = _iterator9[_i9++];
|
||||||
} else {
|
} else {
|
||||||
_i10 = _iterator10.next();
|
_i9 = _iterator9.next();
|
||||||
if (_i10.done) break;
|
if (_i9.done) break;
|
||||||
_ref17 = _i10.value;
|
_ref16 = _i9.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pkgCommand = _ref17;
|
const pkgCommand = _ref16;
|
||||||
|
|
||||||
const action = scripts.get(pkgCommand);
|
const action = scripts.get(pkgCommand);
|
||||||
invariant(action, 'Action must exists');
|
invariant(action, 'Action must exists');
|
||||||
@ -76101,11 +76076,6 @@ class TarballFetcher extends (_baseFetcher || _load_baseFetcher()).default {
|
|||||||
chown: false, // don't chown. just leave as it is
|
chown: false, // don't chown. just leave as it is
|
||||||
map: header => {
|
map: header => {
|
||||||
header.mtime = now;
|
header.mtime = now;
|
||||||
if (header.linkname) {
|
|
||||||
const basePath = path.posix.dirname(path.join('/', header.name));
|
|
||||||
const jailPath = path.posix.join(basePath, header.linkname);
|
|
||||||
header.linkname = path.posix.relative('/', jailPath);
|
|
||||||
}
|
|
||||||
return header;
|
return header;
|
||||||
},
|
},
|
||||||
fs: patchedFs
|
fs: patchedFs
|
||||||
@ -78439,11 +78409,6 @@ class RequestManager {
|
|||||||
rejectNext(err);
|
rejectNext(err);
|
||||||
};
|
};
|
||||||
|
|
||||||
const rejectWithoutUrl = function rejectWithoutUrl(err) {
|
|
||||||
err.message = err.message;
|
|
||||||
rejectNext(err);
|
|
||||||
};
|
|
||||||
|
|
||||||
const queueForRetry = reason => {
|
const queueForRetry = reason => {
|
||||||
const attempts = params.retryAttempts || 0;
|
const attempts = params.retryAttempts || 0;
|
||||||
if (attempts >= this.maxRetryAttempts - 1) {
|
if (attempts >= this.maxRetryAttempts - 1) {
|
||||||
@ -78499,11 +78464,6 @@ class RequestManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res.statusCode === 401 && res.caseless && res.caseless.get('server') === 'GitHub.com') {
|
|
||||||
const message = `${res.body.message}. If using GITHUB_TOKEN in your env, check that it is valid.`;
|
|
||||||
rejectWithoutUrl(new Error(this.reporter.lang('unauthorizedResponse', res.caseless.get('server'), message)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.statusCode === 401 && res.headers['www-authenticate']) {
|
if (res.statusCode === 401 && res.headers['www-authenticate']) {
|
||||||
const authMethods = res.headers['www-authenticate'].split(/,\s*/).map(s => s.toLowerCase());
|
const authMethods = res.headers['www-authenticate'].split(/,\s*/).map(s => s.toLowerCase());
|
||||||
|
|
||||||
@ -97006,14 +96966,12 @@ function _load_asyncToGenerator() {
|
|||||||
|
|
||||||
let run = exports.run = (() => {
|
let run = exports.run = (() => {
|
||||||
var _ref = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (config, reporter, flags, args) {
|
var _ref = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (config, reporter, flags, args) {
|
||||||
const installVersion = flags[`2`] ? `berry` : flags.install;
|
if (flags.install) {
|
||||||
|
|
||||||
if (installVersion) {
|
|
||||||
const lockfilePath = path.resolve(config.cwd, 'yarn.lock');
|
const lockfilePath = path.resolve(config.cwd, 'yarn.lock');
|
||||||
if (!(yield (_fs || _load_fs()).exists(lockfilePath))) {
|
if (!(yield (_fs || _load_fs()).exists(lockfilePath))) {
|
||||||
yield (_fs || _load_fs()).writeFile(lockfilePath, '');
|
yield (_fs || _load_fs()).writeFile(lockfilePath, '');
|
||||||
}
|
}
|
||||||
yield (_child || _load_child()).spawn((_constants || _load_constants()).NODE_BIN_PATH, [process.argv[1], 'policies', 'set-version', installVersion], {
|
yield (_child || _load_child()).spawn((_constants || _load_constants()).NODE_BIN_PATH, [process.argv[1], 'policies', 'set-version', flags.install], {
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
cwd: config.cwd
|
cwd: config.cwd
|
||||||
});
|
});
|
||||||
@ -97308,7 +97266,6 @@ function setFlags(commander) {
|
|||||||
commander.option('-y, --yes', 'use default options');
|
commander.option('-y, --yes', 'use default options');
|
||||||
commander.option('-p, --private', 'use default options and private true');
|
commander.option('-p, --private', 'use default options and private true');
|
||||||
commander.option('-i, --install <value>', 'install a specific Yarn release');
|
commander.option('-i, --install <value>', 'install a specific Yarn release');
|
||||||
commander.option('-2', 'generates the project using Yarn 2');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasWrapper(commander, args) {
|
function hasWrapper(commander, args) {
|
||||||
@ -98297,7 +98254,6 @@ var _buildSubCommands = (0, (_buildSubCommands2 || _load_buildSubCommands()).def
|
|||||||
|
|
||||||
let bundleUrl;
|
let bundleUrl;
|
||||||
let bundleVersion;
|
let bundleVersion;
|
||||||
let isV2 = false;
|
|
||||||
|
|
||||||
if (range === 'nightly' || range === 'nightlies') {
|
if (range === 'nightly' || range === 'nightlies') {
|
||||||
bundleUrl = 'https://nightly.yarnpkg.com/latest.js';
|
bundleUrl = 'https://nightly.yarnpkg.com/latest.js';
|
||||||
@ -98305,18 +98261,10 @@ var _buildSubCommands = (0, (_buildSubCommands2 || _load_buildSubCommands()).def
|
|||||||
} else if (range === 'berry' || range === 'v2' || range === '2') {
|
} else if (range === 'berry' || range === 'v2' || range === '2') {
|
||||||
bundleUrl = 'https://github.com/yarnpkg/berry/raw/master/packages/berry-cli/bin/berry.js';
|
bundleUrl = 'https://github.com/yarnpkg/berry/raw/master/packages/berry-cli/bin/berry.js';
|
||||||
bundleVersion = 'berry';
|
bundleVersion = 'berry';
|
||||||
isV2 = true;
|
|
||||||
} else {
|
} else {
|
||||||
let releases = [];
|
const releases = yield fetchReleases(config, {
|
||||||
|
includePrereleases: allowRc
|
||||||
try {
|
});
|
||||||
releases = yield fetchReleases(config, {
|
|
||||||
includePrereleases: allowRc
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
reporter.error(e.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const release = releases.find(function (release) {
|
const release = releases.find(function (release) {
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
@ -98337,6 +98285,7 @@ var _buildSubCommands = (0, (_buildSubCommands2 || _load_buildSubCommands()).def
|
|||||||
reporter.log(`Downloading ${chalk.green(bundleUrl)}...`);
|
reporter.log(`Downloading ${chalk.green(bundleUrl)}...`);
|
||||||
|
|
||||||
const bundle = yield fetchBundle(config, bundleUrl);
|
const bundle = yield fetchBundle(config, bundleUrl);
|
||||||
|
const rc = (0, (_rc || _load_rc()).getRcConfigForFolder)(config.lockfileFolder);
|
||||||
|
|
||||||
const yarnPath = path.resolve(config.lockfileFolder, `.yarn/releases/yarn-${bundleVersion}.js`);
|
const yarnPath = path.resolve(config.lockfileFolder, `.yarn/releases/yarn-${bundleVersion}.js`);
|
||||||
reporter.log(`Saving it into ${chalk.magenta(yarnPath)}...`);
|
reporter.log(`Saving it into ${chalk.magenta(yarnPath)}...`);
|
||||||
@ -98344,22 +98293,10 @@ var _buildSubCommands = (0, (_buildSubCommands2 || _load_buildSubCommands()).def
|
|||||||
yield (_fs || _load_fs()).writeFile(yarnPath, bundle);
|
yield (_fs || _load_fs()).writeFile(yarnPath, bundle);
|
||||||
yield (_fs || _load_fs()).chmod(yarnPath, 0o755);
|
yield (_fs || _load_fs()).chmod(yarnPath, 0o755);
|
||||||
|
|
||||||
const targetPath = path.relative(config.lockfileFolder, yarnPath).replace(/\\/g, '/');
|
const rcPath = `${config.lockfileFolder}/.yarnrc`;
|
||||||
|
reporter.log(`Updating ${chalk.magenta(rcPath)}...`);
|
||||||
if (isV2) {
|
rc['yarn-path'] = path.relative(config.lockfileFolder, yarnPath);
|
||||||
const rcPath = `${config.lockfileFolder}/.yarnrc.yml`;
|
yield (_fs || _load_fs()).writeFilePreservingEol(rcPath, `${(0, (_lockfile || _load_lockfile()).stringify)(rc)}\n`);
|
||||||
reporter.log(`Updating ${chalk.magenta(rcPath)}...`);
|
|
||||||
|
|
||||||
yield (_fs || _load_fs()).writeFilePreservingEol(rcPath, `yarnPath: ${JSON.stringify(targetPath)}\n`);
|
|
||||||
} else {
|
|
||||||
const rcPath = `${config.lockfileFolder}/.yarnrc`;
|
|
||||||
reporter.log(`Updating ${chalk.magenta(rcPath)}...`);
|
|
||||||
|
|
||||||
const rc = (0, (_rc || _load_rc()).getRcConfigForFolder)(config.lockfileFolder);
|
|
||||||
rc['yarn-path'] = targetPath;
|
|
||||||
|
|
||||||
yield (_fs || _load_fs()).writeFilePreservingEol(rcPath, `${(0, (_lockfile || _load_lockfile()).stringify)(rc)}\n`);
|
|
||||||
}
|
|
||||||
|
|
||||||
reporter.log(`Done!`);
|
reporter.log(`Done!`);
|
||||||
})();
|
})();
|
||||||
@ -99682,11 +99619,11 @@ let run = exports.run = (() => {
|
|||||||
throw new (_errors || _load_errors()).MessageError(reporter.lang('workspaceRootNotFound', config.cwd));
|
throw new (_errors || _load_errors()).MessageError(reporter.lang('workspaceRootNotFound', config.cwd));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.length < 1) {
|
if (flags.originalArgs < 1) {
|
||||||
throw new (_errors || _load_errors()).MessageError(reporter.lang('workspaceMissingWorkspace'));
|
throw new (_errors || _load_errors()).MessageError(reporter.lang('workspaceMissingWorkspace'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.length < 2) {
|
if (flags.originalArgs < 2) {
|
||||||
throw new (_errors || _load_errors()).MessageError(reporter.lang('workspaceMissingCommand'));
|
throw new (_errors || _load_errors()).MessageError(reporter.lang('workspaceMissingCommand'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99695,7 +99632,7 @@ let run = exports.run = (() => {
|
|||||||
|
|
||||||
const workspaces = yield config.resolveWorkspaces(workspaceRootFolder, manifest);
|
const workspaces = yield config.resolveWorkspaces(workspaceRootFolder, manifest);
|
||||||
|
|
||||||
var _ref2 = args || [];
|
var _ref2 = flags.originalArgs || [];
|
||||||
|
|
||||||
const workspaceName = _ref2[0],
|
const workspaceName = _ref2[0],
|
||||||
rest = _ref2.slice(1);
|
rest = _ref2.slice(1);
|
||||||
@ -99881,23 +99818,28 @@ let runScript = exports.runScript = (() => {
|
|||||||
const workspaces = yield config.resolveWorkspaces(workspaceRootFolder, manifest);
|
const workspaces = yield config.resolveWorkspaces(workspaceRootFolder, manifest);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
var _ref6 = flags.originalArgs || [];
|
||||||
|
|
||||||
|
const _ = _ref6[0],
|
||||||
|
rest = _ref6.slice(1);
|
||||||
|
|
||||||
for (var _iterator4 = Object.keys(workspaces), _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) {
|
for (var _iterator4 = Object.keys(workspaces), _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) {
|
||||||
var _ref6;
|
var _ref7;
|
||||||
|
|
||||||
if (_isArray4) {
|
if (_isArray4) {
|
||||||
if (_i4 >= _iterator4.length) break;
|
if (_i4 >= _iterator4.length) break;
|
||||||
_ref6 = _iterator4[_i4++];
|
_ref7 = _iterator4[_i4++];
|
||||||
} else {
|
} else {
|
||||||
_i4 = _iterator4.next();
|
_i4 = _iterator4.next();
|
||||||
if (_i4.done) break;
|
if (_i4.done) break;
|
||||||
_ref6 = _i4.value;
|
_ref7 = _i4.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
const workspaceName = _ref6;
|
const workspaceName = _ref7;
|
||||||
const loc = workspaces[workspaceName].loc;
|
const loc = workspaces[workspaceName].loc;
|
||||||
|
|
||||||
reporter.log(`${os.EOL}> ${workspaceName}`);
|
reporter.log(`${os.EOL}> ${workspaceName}`);
|
||||||
yield (_child || _load_child()).spawn((_constants2 || _load_constants2()).NODE_BIN_PATH, [(_constants2 || _load_constants2()).YARN_BIN_PATH, 'run', ...args], {
|
yield (_child || _load_child()).spawn((_constants2 || _load_constants2()).NODE_BIN_PATH, [(_constants2 || _load_constants2()).YARN_BIN_PATH, ...rest], {
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
cwd: loc
|
cwd: loc
|
||||||
});
|
});
|
||||||
@ -100117,11 +100059,7 @@ let main = exports.main = (() => {
|
|||||||
commandName = 'install';
|
commandName = 'install';
|
||||||
isKnownCommand = true;
|
isKnownCommand = true;
|
||||||
}
|
}
|
||||||
if (commandName === 'set' && args[0] === 'version') {
|
|
||||||
commandName = 'policies';
|
|
||||||
args.splice(0, 1, 'set-version');
|
|
||||||
isKnownCommand = true;
|
|
||||||
}
|
|
||||||
if (!isKnownCommand) {
|
if (!isKnownCommand) {
|
||||||
// if command is not recognized, then set default to `run`
|
// if command is not recognized, then set default to `run`
|
||||||
args.unshift(commandName);
|
args.unshift(commandName);
|
||||||
@ -100132,20 +100070,15 @@ let main = exports.main = (() => {
|
|||||||
let warnAboutRunDashDash = false;
|
let warnAboutRunDashDash = false;
|
||||||
// we are using "yarn <script> -abc", "yarn run <script> -abc", or "yarn node -abc", we want -abc
|
// we are using "yarn <script> -abc", "yarn run <script> -abc", or "yarn node -abc", we want -abc
|
||||||
// to be script options, not yarn options
|
// to be script options, not yarn options
|
||||||
|
const PROXY_COMMANDS = new Set([`run`, `create`, `node`]);
|
||||||
// PROXY_COMMANDS is a map of command name to the number of preservedArgs
|
if (PROXY_COMMANDS.has(commandName)) {
|
||||||
const PROXY_COMMANDS = {
|
|
||||||
run: 1, // yarn run {command}
|
|
||||||
create: 1, // yarn create {project}
|
|
||||||
node: 0, // yarn node
|
|
||||||
workspaces: 1, // yarn workspaces {command}
|
|
||||||
workspace: 2 // yarn workspace {package} {command}
|
|
||||||
};
|
|
||||||
if (PROXY_COMMANDS.hasOwnProperty(commandName)) {
|
|
||||||
if (endArgs.length === 0) {
|
if (endArgs.length === 0) {
|
||||||
// $FlowFixMe doesn't like that PROXY_COMMANDS doesn't have keys for all commands.
|
let preservedArgs = 0;
|
||||||
let preservedArgs = PROXY_COMMANDS[commandName];
|
// the "run" and "create" command take one argument that we want to parse as usual (the
|
||||||
|
// script/package name), hence the splice(1)
|
||||||
|
if (command === (_index3 || _load_index3()).default.run || command === (_index3 || _load_index3()).default.create) {
|
||||||
|
preservedArgs += 1;
|
||||||
|
}
|
||||||
// If the --into option immediately follows the command (or the script name in the "run/create"
|
// If the --into option immediately follows the command (or the script name in the "run/create"
|
||||||
// case), we parse them as regular options so that we can cd into them
|
// case), we parse them as regular options so that we can cd into them
|
||||||
if (args[preservedArgs] === `--into`) {
|
if (args[preservedArgs] === `--into`) {
|
||||||
@ -100157,6 +100090,7 @@ let main = exports.main = (() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(_commander || _load_commander()).default.originalArgs = args;
|
||||||
args = [...preCommandArgs, ...args];
|
args = [...preCommandArgs, ...args];
|
||||||
|
|
||||||
command.setFlags((_commander || _load_commander()).default);
|
command.setFlags((_commander || _load_commander()).default);
|
||||||
@ -100598,11 +100532,6 @@ let start = (() => {
|
|||||||
const opts = { stdio: 'inherit', env: Object.assign({}, process.env, { YARN_IGNORE_PATH: 1 }) };
|
const opts = { stdio: 'inherit', env: Object.assign({}, process.env, { YARN_IGNORE_PATH: 1 }) };
|
||||||
let exitCode = 0;
|
let exitCode = 0;
|
||||||
|
|
||||||
process.on(`SIGINT`, function () {
|
|
||||||
// We don't want SIGINT to kill our process; we want it to kill the
|
|
||||||
// innermost process, whose end will cause our own to exit.
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (yarnPath.endsWith(`.js`)) {
|
if (yarnPath.endsWith(`.js`)) {
|
||||||
exitCode = yield (0, (_child || _load_child()).spawnp)(process.execPath, [yarnPath, ...argv], opts);
|
exitCode = yield (0, (_child || _load_child()).spawnp)(process.execPath, [yarnPath, ...argv], opts);
|
||||||
@ -104994,7 +104923,6 @@ const messages = {
|
|||||||
errorExtractingTarball: 'Extracting tar content of $1 failed, the file appears to be corrupt: $0',
|
errorExtractingTarball: 'Extracting tar content of $1 failed, the file appears to be corrupt: $0',
|
||||||
updateInstalling: 'Installing $0...',
|
updateInstalling: 'Installing $0...',
|
||||||
hostedGitResolveError: 'Error connecting to repository. Please, check the url.',
|
hostedGitResolveError: 'Error connecting to repository. Please, check the url.',
|
||||||
unauthorizedResponse: 'Received a 401 from $0. $1',
|
|
||||||
|
|
||||||
unknownFetcherFor: 'Unknown fetcher for $0',
|
unknownFetcherFor: 'Unknown fetcher for $0',
|
||||||
|
|
||||||
@ -106869,7 +106797,7 @@ const semver = __webpack_require__(22);
|
|||||||
const path = __webpack_require__(0);
|
const path = __webpack_require__(0);
|
||||||
const url = __webpack_require__(24);
|
const url = __webpack_require__(24);
|
||||||
|
|
||||||
const VALID_BIN_KEYS = /^(?!\.{0,2}$)[a-z0-9._-]+$/i;
|
const VALID_BIN_KEYS = /^[a-z0-9_-]+$/i;
|
||||||
|
|
||||||
const LICENSE_RENAMES = {
|
const LICENSE_RENAMES = {
|
||||||
'MIT/X11': 'MIT',
|
'MIT/X11': 'MIT',
|
||||||
@ -107732,11 +107660,7 @@ function parseRcPaths(paths, parser) {
|
|||||||
try {
|
try {
|
||||||
return parser((0, (_fs || _load_fs()).readFileSync)(path).toString(), path);
|
return parser((0, (_fs || _load_fs()).readFileSync)(path).toString(), path);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code === 'ENOENT' || error.code === 'EISDIR') {
|
return {};
|
||||||
return {};
|
|
||||||
} else {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
2
.yarnrc
2
.yarnrc
@ -2,4 +2,4 @@
|
|||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
yarn-path ".yarn/releases/yarn-1.22.4.js"
|
yarn-path ".yarn/releases/yarn-1.21.1.js"
|
||||||
|
73
BUILD.bazel
73
BUILD.bazel
@ -1,13 +1,12 @@
|
|||||||
package(default_visibility = ["//visibility:public"])
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
load("//tools:defaults.bzl", "karma_web_test")
|
||||||
|
|
||||||
exports_files([
|
exports_files([
|
||||||
"LICENSE",
|
"LICENSE",
|
||||||
|
"protractor-perf.conf.js",
|
||||||
"karma-js.conf.js",
|
"karma-js.conf.js",
|
||||||
"browser-providers.conf.js",
|
"browser-providers.conf.js",
|
||||||
"scripts/ci/track-payload-size.sh",
|
|
||||||
"scripts/ci/payload-size.sh",
|
|
||||||
"scripts/ci/payload-size.js",
|
|
||||||
"package.json",
|
|
||||||
])
|
])
|
||||||
|
|
||||||
alias(
|
alias(
|
||||||
@ -47,3 +46,69 @@ filegroup(
|
|||||||
"@npm//:node_modules/angular-mocks-1.6/angular-mocks.js",
|
"@npm//:node_modules/angular-mocks-1.6/angular-mocks.js",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# To run a karma_web_test target manually, run the "./scripts/saucelabs/run-bazel-via-tunnel.sh"
|
||||||
|
# script. Note: If you are on MacOS or Windows, you need to manually start the Saucelabs tunnel
|
||||||
|
# because the script only supports the linux Saucelabs tunnel binary. We combine all tests into
|
||||||
|
# a single "karma_web_test" target because running them as separate targets in parallel can result
|
||||||
|
# in to too many browsers being acquired at the same time. This will then result in tests being
|
||||||
|
# flaky. This target runs in CI with Saucelabs and Ivy.
|
||||||
|
karma_web_test(
|
||||||
|
name = "saucelabs_unit_tests",
|
||||||
|
# Default timeout is moderate (5min). This causes the test to be terminated while
|
||||||
|
# Saucelabs browsers keep running. Ultimately resulting in failing tests and browsers
|
||||||
|
# unnecessarily being acquired. Our specified Saucelabs idle timeout is 10min, so we use
|
||||||
|
# Bazel's long timeout (15min). This ensures that Karma can shut down properly.
|
||||||
|
timeout = "long",
|
||||||
|
tags = [
|
||||||
|
"local",
|
||||||
|
"manual",
|
||||||
|
"saucelabs",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
"//packages/core/test/acceptance:acceptance_lib",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
karma_web_test(
|
||||||
|
# This target runs in CI with View Engine as a Saucelabs and Bazel proof-of-concept. It's a
|
||||||
|
# subset of the legacy saucelabs tests.
|
||||||
|
name = "saucelabs_unit_tests_poc",
|
||||||
|
# Default timeout is moderate (5min). This causes the test to be terminated while
|
||||||
|
# Saucelabs browsers keep running. Ultimately resulting in failing tests and browsers
|
||||||
|
# unnecessarily being acquired. Our specified Saucelabs idle timeout is 10min, so we use
|
||||||
|
# Bazel's long timeout (15min). This ensures that Karma can shut down properly.
|
||||||
|
timeout = "long",
|
||||||
|
tags = [
|
||||||
|
"local",
|
||||||
|
"manual",
|
||||||
|
"saucelabs",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
# We combine all tests into a single karma_web_test target
|
||||||
|
# as running them as separate targets in parallel leads to too many
|
||||||
|
# browsers being acquired at once in SauceLabs and the tests flake out
|
||||||
|
# TODO: this is an example subset of tests below, add all remaining angular tests
|
||||||
|
"//packages/common/http/test:test_lib",
|
||||||
|
"//packages/common/http/testing/test:test_lib",
|
||||||
|
"//packages/common/test:test_lib",
|
||||||
|
"//packages/core/test:test_lib",
|
||||||
|
"//packages/forms/test:test_lib",
|
||||||
|
"//packages/http/test:test_lib",
|
||||||
|
"//packages/zone.js/test:karma_jasmine_test_ci",
|
||||||
|
# "//packages/router/test:test_lib",
|
||||||
|
# //packages/router/test:test_lib fails with:
|
||||||
|
# IE 11.0.0 (Windows 8.1.0.0) bootstrap should restore the scrolling position FAILED
|
||||||
|
# Expected undefined to equal 5000.
|
||||||
|
# at stack (eval code:2338:11)
|
||||||
|
# at buildExpectationResult (eval code:2305:5)
|
||||||
|
# at expectationResultFactory (eval code:858:11)
|
||||||
|
# at Spec.prototype.addExpectationResult (eval code:487:5)
|
||||||
|
# at addExpectationResult (eval code:802:9)
|
||||||
|
# at Anonymous function (eval code:2252:7)
|
||||||
|
# at Anonymous function (eval code:339:25)
|
||||||
|
# at step (eval code:133:17)
|
||||||
|
# at Anonymous function (eval code:114:50)
|
||||||
|
# at fulfilled (eval code:104:47)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
2893
CHANGELOG.md
2893
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@ -42,9 +42,7 @@ Please consider what kind of change it is:
|
|||||||
|
|
||||||
* For a **Major Feature**, first open an issue and outline your proposal so that it can be
|
* For a **Major Feature**, first open an issue and outline your proposal so that it can be
|
||||||
discussed. This will also allow us to better coordinate our efforts, prevent duplication of work,
|
discussed. This will also allow us to better coordinate our efforts, prevent duplication of work,
|
||||||
and help you to craft the change so that it is successfully accepted into the project. **Note**:
|
and help you to craft the change so that it is successfully accepted into the project.
|
||||||
Adding a new topic to the documentation, or significantly re-writing a topic, counts as a major
|
|
||||||
feature.
|
|
||||||
* **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
|
* **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
|
||||||
|
|
||||||
## <a name="submit"></a> Submission Guidelines
|
## <a name="submit"></a> Submission Guidelines
|
||||||
@ -208,8 +206,6 @@ The scope should be the name of the npm package affected (as perceived by the pe
|
|||||||
The following is the list of supported scopes:
|
The following is the list of supported scopes:
|
||||||
|
|
||||||
* **animations**
|
* **animations**
|
||||||
* **bazel**
|
|
||||||
* **benchpress**
|
|
||||||
* **common**
|
* **common**
|
||||||
* **compiler**
|
* **compiler**
|
||||||
* **compiler-cli**
|
* **compiler-cli**
|
||||||
@ -218,7 +214,6 @@ The following is the list of supported scopes:
|
|||||||
* **forms**
|
* **forms**
|
||||||
* **http**
|
* **http**
|
||||||
* **language-service**
|
* **language-service**
|
||||||
* **localize**
|
|
||||||
* **platform-browser**
|
* **platform-browser**
|
||||||
* **platform-browser-dynamic**
|
* **platform-browser-dynamic**
|
||||||
* **platform-server**
|
* **platform-server**
|
||||||
@ -237,10 +232,8 @@ There are currently a few exceptions to the "use package name" rule:
|
|||||||
* **changelog**: used for updating the release notes in CHANGELOG.md
|
* **changelog**: used for updating the release notes in CHANGELOG.md
|
||||||
* **docs-infra**: used for docs-app (angular.io) related changes within the /aio directory of the
|
* **docs-infra**: used for docs-app (angular.io) related changes within the /aio directory of the
|
||||||
repo
|
repo
|
||||||
* **dev-infra**: used for dev-infra related changes within the directories /scripts, /tools and /dev-infra
|
* **ivy**: used for changes to the [Ivy renderer](https://github.com/angular/angular/issues/21706).
|
||||||
* **migrations**: used for changes to the `ng update` migrations.
|
|
||||||
* **ngcc**: used for changes to the [Angular Compatibility Compiler](./packages/compiler-cli/ngcc/README.md)
|
* **ngcc**: used for changes to the [Angular Compatibility Compiler](./packages/compiler-cli/ngcc/README.md)
|
||||||
* **ve**: used for changes specific to ViewEngine (legacy compiler/renderer).
|
|
||||||
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all
|
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all
|
||||||
packages (e.g. `style: add missing semicolons`) and for docs changes that are not related to a
|
packages (e.g. `style: add missing semicolons`) and for docs changes that are not related to a
|
||||||
specific package (e.g. `docs: fix typo in tutorial`).
|
specific package (e.g. `docs: fix typo in tutorial`).
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
[](https://circleci.com/gh/angular/workflows/angular/tree/master)
|
[](https://circleci.com/gh/angular/workflows/angular/tree/master)
|
||||||
|
[](https://www.browserstack.com/automate/public-build/LzF3RzBVVGt6VWE2S0hHaC9uYllOZz09LS1BVjNTclBKV0x4eVRlcjA4QVY1M0N3PT0=--eb4ce8c8dc2c1c5b2b5352d473ee12a73ac20e06)
|
||||||
[](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
[](https://www.npmjs.com/@angular/core)
|
[](https://www.npmjs.com/@angular/core)
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ Angular is a development platform for building mobile and desktop web applicatio
|
|||||||
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our
|
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our
|
||||||
guidelines for [contributing][contributing] and then check out one of our issues in the [hotlist: community-help](https://github.com/angular/angular/labels/hotlist%3A%20community-help).
|
guidelines for [contributing][contributing] and then check out one of our issues in the [hotlist: community-help](https://github.com/angular/angular/labels/hotlist%3A%20community-help).
|
||||||
|
|
||||||
|
[browserstack]: https://www.browserstack.com/automate/public-build/LzF3RzBVVGt6VWE2S0hHaC9uYllOZz09LS1BVjNTclBKV0x4eVRlcjA4QVY1M0N3PT0=--eb4ce8c8dc2c1c5b2b5352d473ee12a73ac20e06
|
||||||
[contributing]: https://github.com/angular/angular/blob/master/CONTRIBUTING.md
|
[contributing]: https://github.com/angular/angular/blob/master/CONTRIBUTING.md
|
||||||
[quickstart]: https://angular.io/start
|
[quickstart]: https://angular.io/start
|
||||||
[changelog]: https://github.com/angular/angular/blob/master/CHANGELOG.md
|
[changelog]: https://github.com/angular/angular/blob/master/CHANGELOG.md
|
||||||
|
49
WORKSPACE
49
WORKSPACE
@ -8,33 +8,45 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
|||||||
# Fetch rules_nodejs so we can install our npm dependencies
|
# Fetch rules_nodejs so we can install our npm dependencies
|
||||||
http_archive(
|
http_archive(
|
||||||
name = "build_bazel_rules_nodejs",
|
name = "build_bazel_rules_nodejs",
|
||||||
sha256 = "84abf7ac4234a70924628baa9a73a5a5cbad944c4358cf9abdb4aab29c9a5b77",
|
sha256 = "c97bf38546c220fa250ff2cc052c1a9eac977c662c1fc23eda797b0ce8e70a43",
|
||||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.7.0/rules_nodejs-1.7.0.tar.gz"],
|
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.1.0/rules_nodejs-1.1.0.tar.gz"],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check the rules_nodejs version and download npm dependencies
|
# Check the bazel version and download npm dependencies
|
||||||
# Note: bazel (version 2 and after) will check the .bazelversion file so we don't need to
|
load("@build_bazel_rules_nodejs//:index.bzl", "check_bazel_version", "check_rules_nodejs_version", "node_repositories", "yarn_install")
|
||||||
# assert on that.
|
|
||||||
load("@build_bazel_rules_nodejs//:index.bzl", "check_rules_nodejs_version", "node_repositories", "yarn_install")
|
|
||||||
|
|
||||||
check_rules_nodejs_version(minimum_version_string = "1.7.0")
|
# Bazel version must be at least the following version because:
|
||||||
|
# - 0.26.0 managed_directories feature added which is required for nodejs rules 0.30.0
|
||||||
|
# - 0.27.0 has a fix for managed_directories after `rm -rf node_modules`
|
||||||
|
check_bazel_version(
|
||||||
|
message = """
|
||||||
|
You no longer need to install Bazel on your machine.
|
||||||
|
Angular has a dependency on the @bazel/bazel package which supplies it.
|
||||||
|
Try running `yarn bazel` instead.
|
||||||
|
(If you did run that, check that you've got a fresh `yarn install`)
|
||||||
|
|
||||||
|
""",
|
||||||
|
minimum_bazel_version = "1.1.0",
|
||||||
|
)
|
||||||
|
|
||||||
|
check_rules_nodejs_version(minimum_version_string = "1.0.0")
|
||||||
|
|
||||||
# Setup the Node.js toolchain
|
# Setup the Node.js toolchain
|
||||||
node_repositories(
|
node_repositories(
|
||||||
node_repositories = {
|
node_repositories = {
|
||||||
"12.14.1-darwin_amd64": ("node-v12.14.1-darwin-x64.tar.gz", "node-v12.14.1-darwin-x64", "0be10a28737527a1e5e3784d3ad844d742fe8b0718acd701fd48f718fd3af78f"),
|
"10.16.0-darwin_amd64": ("node-v10.16.0-darwin-x64.tar.gz", "node-v10.16.0-darwin-x64", "6c009df1b724026d84ae9a838c5b382662e30f6c5563a0995532f2bece39fa9c"),
|
||||||
"12.14.1-linux_amd64": ("node-v12.14.1-linux-x64.tar.xz", "node-v12.14.1-linux-x64", "07cfcaa0aa9d0fcb6e99725408d9e0b07be03b844701588e3ab5dbc395b98e1b"),
|
"10.16.0-linux_amd64": ("node-v10.16.0-linux-x64.tar.xz", "node-v10.16.0-linux-x64", "1827f5b99084740234de0c506f4dd2202a696ed60f76059696747c34339b9d48"),
|
||||||
"12.14.1-windows_amd64": ("node-v12.14.1-win-x64.zip", "node-v12.14.1-win-x64", "1f96ccce3ba045ecea3f458e189500adb90b8bc1a34de5d82fc10a5bf66ce7e3"),
|
"10.16.0-windows_amd64": ("node-v10.16.0-win-x64.zip", "node-v10.16.0-win-x64", "aa22cb357f0fb54ccbc06b19b60e37eefea5d7dd9940912675d3ed988bf9a059"),
|
||||||
},
|
},
|
||||||
node_version = "12.14.1",
|
node_version = "10.16.0",
|
||||||
package_json = ["//:package.json"],
|
package_json = ["//:package.json"],
|
||||||
|
# Label needs to explicitly specify the current workspace name because otherwise Bazel does
|
||||||
|
# not provide all needed data (like "workspace_root") to the repository context.
|
||||||
|
vendored_yarn = "@angular//:third_party/github.com/yarnpkg/yarn/releases/download/v1.21.1",
|
||||||
)
|
)
|
||||||
|
|
||||||
load("//integration:angular_integration_test.bzl", "npm_package_archives")
|
|
||||||
|
|
||||||
yarn_install(
|
yarn_install(
|
||||||
name = "npm",
|
name = "npm",
|
||||||
manual_build_file_contents = npm_package_archives(),
|
|
||||||
package_json = "//:package.json",
|
package_json = "//:package.json",
|
||||||
yarn_lock = "//:yarn.lock",
|
yarn_lock = "//:yarn.lock",
|
||||||
)
|
)
|
||||||
@ -64,7 +76,7 @@ load("@io_bazel_rules_webtesting//web:repositories.bzl", "web_test_repositories"
|
|||||||
|
|
||||||
web_test_repositories()
|
web_test_repositories()
|
||||||
|
|
||||||
load("//dev-infra/browsers:browser_repositories.bzl", "browser_repositories")
|
load("//tools/browsers:browser_repositories.bzl", "browser_repositories")
|
||||||
|
|
||||||
browser_repositories()
|
browser_repositories()
|
||||||
|
|
||||||
@ -91,18 +103,17 @@ rbe_autoconfig(
|
|||||||
# Need to specify a base container digest in order to ensure that we can use the checked-in
|
# Need to specify a base container digest in order to ensure that we can use the checked-in
|
||||||
# platform configurations for the "ubuntu16_04" image. Otherwise the autoconfig rule would
|
# platform configurations for the "ubuntu16_04" image. Otherwise the autoconfig rule would
|
||||||
# need to pull the image and run it in order determine the toolchain configuration. See:
|
# need to pull the image and run it in order determine the toolchain configuration. See:
|
||||||
# https://github.com/bazelbuild/bazel-toolchains/blob/3.2.0/configs/ubuntu16_04_clang/versions.bzl
|
# https://github.com/bazelbuild/bazel-toolchains/blob/1.1.2/configs/ubuntu16_04_clang/versions.bzl
|
||||||
base_container_digest = "sha256:5e750dd878df9fcf4e185c6f52b9826090f6e532b097f286913a428290622332",
|
base_container_digest = "sha256:1ab40405810effefa0b2f45824d6d608634ccddbf06366760c341ef6fbead011",
|
||||||
# Note that if you change the `digest`, you might also need to update the
|
# Note that if you change the `digest`, you might also need to update the
|
||||||
# `base_container_digest` to make sure marketplace.gcr.io/google/rbe-ubuntu16-04-webtest:<digest>
|
# `base_container_digest` to make sure marketplace.gcr.io/google/rbe-ubuntu16-04-webtest:<digest>
|
||||||
# and marketplace.gcr.io/google/rbe-ubuntu16-04:<base_container_digest> have
|
# and marketplace.gcr.io/google/rbe-ubuntu16-04:<base_container_digest> have
|
||||||
# the same Clang and JDK installed. Clang is needed because of the dependency on
|
# the same Clang and JDK installed. Clang is needed because of the dependency on
|
||||||
# @com_google_protobuf. Java is needed for the Bazel's test executor Java tool.
|
# @com_google_protobuf. Java is needed for the Bazel's test executor Java tool.
|
||||||
digest = "sha256:f743114235a43355bf8324e2ba0fa6a597236fe06f7bc99aaa9ac703631c306b",
|
digest = "sha256:0b8fa87db4b8e5366717a7164342a029d1348d2feea7ecc4b18c780bc2507059",
|
||||||
env = clang_env(),
|
env = clang_env(),
|
||||||
registry = "marketplace.gcr.io",
|
registry = "marketplace.gcr.io",
|
||||||
# We can't use the default "ubuntu16_04" RBE image provided by the autoconfig because we need
|
# We can't use the default "ubuntu16_04" RBE image provided by the autoconfig because we need
|
||||||
# a specific Linux kernel that comes with "libx11" in order to run headless browser tests.
|
# a specific Linux kernel that comes with "libx11" in order to run headless browser tests.
|
||||||
repository = "google/rbe-ubuntu16-04-webtest",
|
repository = "google/rbe-ubuntu16-04-webtest",
|
||||||
use_checked_in_confs = "Force",
|
|
||||||
)
|
)
|
||||||
|
@ -18,8 +18,8 @@ Here are the most important tasks you might need to use:
|
|||||||
|
|
||||||
* `yarn build` - create a production build of the application (after installing dependencies, boilerplate, etc).
|
* `yarn build` - create a production build of the application (after installing dependencies, boilerplate, etc).
|
||||||
* `yarn build-local` - same as `build`, but use `setup-local` instead of `setup`.
|
* `yarn build-local` - same as `build`, but use `setup-local` instead of `setup`.
|
||||||
* `yarn build-local-with-viewengine` - same as `build-local`, but in addition also turns on `ViewEngine` (pre-Ivy) mode in aio.
|
* `yarn build-local-with-viewengine` - same as `build-local`, but in addition also turns on `ViewEngine` mode in aio.
|
||||||
(Note: To turn on `ViewEngine` mode in docs examples, see `yarn boilerplate:add:viewengine` below.)
|
(Note: Docs examples run in `ViewEngine` mode by default. To turn on `ivy` mode in examples, see `yarn boilerplate:add` below.)
|
||||||
|
|
||||||
* `yarn start` - run a development web server that watches the files; then builds the doc-viewer and reloads the page, as necessary.
|
* `yarn start` - run a development web server that watches the files; then builds the doc-viewer and reloads the page, as necessary.
|
||||||
* `yarn serve-and-sync` - run both the `docs-watch` and `start` in the same console.
|
* `yarn serve-and-sync` - run both the `docs-watch` and `start` in the same console.
|
||||||
@ -34,7 +34,7 @@ Here are the most important tasks you might need to use:
|
|||||||
* `yarn docs-test` - run the unit tests for the doc generation code.
|
* `yarn docs-test` - run the unit tests for the doc generation code.
|
||||||
|
|
||||||
* `yarn boilerplate:add` - generate all the boilerplate code for the examples, so that they can be run locally.
|
* `yarn boilerplate:add` - generate all the boilerplate code for the examples, so that they can be run locally.
|
||||||
* `yarn boilerplate:add:viewengine` - same as `boilerplate:add` but also turns on `ViewEngine` (pre-Ivy) mode.
|
* `yarn boilerplate:add:ivy` - same as `boilerplate:add` but also turns on `ivy` mode.
|
||||||
|
|
||||||
* `yarn boilerplate:remove` - remove all the boilerplate code that was added via `yarn boilerplate:add`.
|
* `yarn boilerplate:remove` - remove all the boilerplate code that was added via `yarn boilerplate:add`.
|
||||||
* `yarn generate-stackblitz` - generate the stackblitz files that are used by the `live-example` tags in the docs.
|
* `yarn generate-stackblitz` - generate the stackblitz files that are used by the `live-example` tags in the docs.
|
||||||
@ -44,7 +44,7 @@ Here are the most important tasks you might need to use:
|
|||||||
- `--setup`: generate boilerplate, force webdriver update & other setup, then run tests.
|
- `--setup`: generate boilerplate, force webdriver update & other setup, then run tests.
|
||||||
- `--local`: run e2e tests with the local version of Angular contained in the "dist" folder.
|
- `--local`: run e2e tests with the local version of Angular contained in the "dist" folder.
|
||||||
_Requires `--setup` in order to take effect._
|
_Requires `--setup` in order to take effect._
|
||||||
- `--viewengine`: run e2e tests in `ViewEngine` (pre-Ivy) mode.
|
- `--ivy`: run e2e tests in `ivy` mode.
|
||||||
- `--filter=foo`: limit e2e tests to those containing the word "foo".
|
- `--filter=foo`: limit e2e tests to those containing the word "foo".
|
||||||
|
|
||||||
> **Note for Windows users**
|
> **Note for Windows users**
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Image metadata and config
|
# Image metadata and config
|
||||||
FROM debian:buster
|
FROM debian:stretch
|
||||||
|
|
||||||
LABEL name="angular.io PR preview" \
|
LABEL name="angular.io PR preview" \
|
||||||
description="This image implements the PR preview functionality for angular.io." \
|
description="This image implements the PR preview functionality for angular.io." \
|
||||||
@ -37,9 +37,9 @@ ARG TEST_AIO_NGINX_PORT_HTTPS=4433
|
|||||||
ARG AIO_SIGNIFICANT_FILES_PATTERN='^(?:aio|packages)/(?!.*[._]spec\\.[jt]s$)'
|
ARG AIO_SIGNIFICANT_FILES_PATTERN='^(?:aio|packages)/(?!.*[._]spec\\.[jt]s$)'
|
||||||
ARG TEST_AIO_SIGNIFICANT_FILES_PATTERN=$AIO_SIGNIFICANT_FILES_PATTERN
|
ARG TEST_AIO_SIGNIFICANT_FILES_PATTERN=$AIO_SIGNIFICANT_FILES_PATTERN
|
||||||
ARG AIO_TRUSTED_PR_LABEL="aio: preview"
|
ARG AIO_TRUSTED_PR_LABEL="aio: preview"
|
||||||
ARG TEST_AIO_TRUSTED_PR_LABEL=$AIO_TRUSTED_PR_LABEL
|
ARG TEST_AIO_TRUSTED_PR_LABEL="aio: preview"
|
||||||
ARG AIO_PREVIEW_SERVER_HOSTNAME=preview.localhost
|
ARG AIO_PREVIEW_SERVER_HOSTNAME=preview.localhost
|
||||||
ARG TEST_AIO_PREVIEW_SERVER_HOSTNAME=$AIO_PREVIEW_SERVER_HOSTNAME
|
ARG TEST_AIO_PREVIEW_SERVER_HOSTNAME=preview.localhost
|
||||||
ARG AIO_ARTIFACT_MAX_SIZE=26214400
|
ARG AIO_ARTIFACT_MAX_SIZE=26214400
|
||||||
ARG TEST_AIO_ARTIFACT_MAX_SIZE=200
|
ARG TEST_AIO_ARTIFACT_MAX_SIZE=200
|
||||||
ARG AIO_PREVIEW_SERVER_PORT=3000
|
ARG AIO_PREVIEW_SERVER_PORT=3000
|
||||||
@ -72,29 +72,24 @@ RUN mkdir /var/log/aio
|
|||||||
|
|
||||||
|
|
||||||
# Add extra package sources
|
# Add extra package sources
|
||||||
RUN apt-get update -y && apt-get install -y curl=7.64.0-4+deb10u1
|
RUN apt-get update -y && apt-get install -y curl
|
||||||
RUN curl --silent --show-error --location https://deb.nodesource.com/setup_12.x | bash -
|
RUN curl --silent --show-error --location https://deb.nodesource.com/setup_10.x | bash -
|
||||||
RUN curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
|
RUN curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
|
||||||
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
|
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
|
||||||
|
RUN echo "deb http://ftp.debian.org/debian stretch-backports main" | tee /etc/apt/sources.list.d/backports.list
|
||||||
|
|
||||||
|
|
||||||
# Install packages
|
# Install packages
|
||||||
# NOTE: Some packages (such as `nginx`, `nodejs`, `openssl`) make older versions unavailable on the
|
|
||||||
# repositories, so we cannot pin to specific versions for these packages :(
|
|
||||||
# See for example:
|
|
||||||
# - https://github.com/nodesource/distributions/issues/33
|
|
||||||
# - https://askubuntu.com/questions/715104/how-can-i-downgrade-openssl-via-apt-get
|
|
||||||
RUN apt-get update -y && apt-get install -y \
|
RUN apt-get update -y && apt-get install -y \
|
||||||
cron=3.0pl1-134+deb10u1 \
|
cron=3.0pl1-128+deb9u1 \
|
||||||
dnsmasq=2.80-1 \
|
dnsmasq=2.76-5+deb9u2 \
|
||||||
nano=3.2-3 \
|
nano=2.7.4-1 \
|
||||||
nginx \
|
nginx=1.10.3-1+deb9u2 \
|
||||||
nodejs \
|
nodejs=10.15.3-1nodesource1 \
|
||||||
openssl \
|
openssl=1.1.0j-1~deb9u1 \
|
||||||
rsyslog=8.1901.0-1 \
|
rsyslog=8.24.0-1 \
|
||||||
vim=2:8.1.0875-5 \
|
yarn=1.15.2-1
|
||||||
yarn=1.22.4-1
|
RUN yarn global add pm2@3.5.0
|
||||||
RUN yarn global add pm2@4.4.0
|
|
||||||
|
|
||||||
|
|
||||||
# Set up log rotation
|
# Set up log rotation
|
||||||
@ -167,7 +162,8 @@ RUN find $AIO_SCRIPTS_SH_DIR -maxdepth 1 -type f -printf "%P\n" \
|
|||||||
|
|
||||||
# Set up the Node.js scripts
|
# Set up the Node.js scripts
|
||||||
COPY scripts-js/ $AIO_SCRIPTS_JS_DIR/
|
COPY scripts-js/ $AIO_SCRIPTS_JS_DIR/
|
||||||
RUN yarn --cwd="$AIO_SCRIPTS_JS_DIR/" install --production --frozen-lockfile
|
WORKDIR $AIO_SCRIPTS_JS_DIR/
|
||||||
|
RUN yarn install --production --frozen-lockfile
|
||||||
|
|
||||||
|
|
||||||
# Set up health check
|
# Set up health check
|
||||||
|
@ -35,7 +35,6 @@ export class BuildCleaner {
|
|||||||
]);
|
]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('ERROR:', error);
|
this.logger.error('ERROR:', error);
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as express from 'express';
|
import * as express from 'express';
|
||||||
|
import {promisify} from 'util';
|
||||||
import {PreviewServerError} from './preview-error';
|
import {PreviewServerError} from './preview-error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -12,7 +13,7 @@ export async function respondWithError(res: express.Response, err: any): Promise
|
|||||||
}
|
}
|
||||||
|
|
||||||
res.status(err.status);
|
res.status(err.status);
|
||||||
return new Promise(resolve => res.end(err.message, resolve));
|
await promisify(res.end.bind(res))(err.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -93,7 +93,7 @@ class Helper {
|
|||||||
return fs.readFileSync(absFilePath, 'utf8');
|
return fs.readFileSync(absFilePath, 'utf8');
|
||||||
}
|
}
|
||||||
|
|
||||||
public runCmd(cmd: string, opts: cp.ExecOptions = {}): Promise<CmdResult> {
|
public runCmd(cmd: string, opts: cp.ExecFileOptions = {}): Promise<CmdResult> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const proc = cp.exec(cmd, opts, (err, stdout, stderr) => resolve({success: !err, err, stdout, stderr}));
|
const proc = cp.exec(cmd, opts, (err, stdout, stderr) => resolve({success: !err, err, stdout, stderr}));
|
||||||
this.createCleanUpFn(() => proc.kill());
|
this.createCleanUpFn(() => proc.kill());
|
||||||
@ -101,7 +101,7 @@ class Helper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public runForAllSupportedSchemes(suiteFactory: TestSuiteFactory): void {
|
public runForAllSupportedSchemes(suiteFactory: TestSuiteFactory): void {
|
||||||
Object.entries(this.portPerScheme).forEach(([scheme, port]) => suiteFactory(scheme, port));
|
Object.keys(this.portPerScheme).forEach(scheme => suiteFactory(scheme, this.portPerScheme[scheme]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public verifyResponse(status: number, regex: string | RegExp = /^/): VerifyCmdResultFn {
|
public verifyResponse(status: number, regex: string | RegExp = /^/): VerifyCmdResultFn {
|
||||||
|
@ -15,7 +15,7 @@ describe(`nginx`, () => {
|
|||||||
afterEach(() => h.cleanUp());
|
afterEach(() => h.cleanUp());
|
||||||
|
|
||||||
|
|
||||||
it('should redirect HTTP to HTTPS', async () => {
|
it('should redirect HTTP to HTTPS', done => {
|
||||||
const httpHost = `${AIO_NGINX_HOSTNAME}:${AIO_NGINX_PORT_HTTP}`;
|
const httpHost = `${AIO_NGINX_HOSTNAME}:${AIO_NGINX_PORT_HTTP}`;
|
||||||
const httpsHost = `${AIO_NGINX_HOSTNAME}:${AIO_NGINX_PORT_HTTPS}`;
|
const httpsHost = `${AIO_NGINX_HOSTNAME}:${AIO_NGINX_PORT_HTTPS}`;
|
||||||
const urlMap = {
|
const urlMap = {
|
||||||
@ -24,15 +24,16 @@ describe(`nginx`, () => {
|
|||||||
[`http://foo.${httpHost}/`]: `https://foo.${httpsHost}/`,
|
[`http://foo.${httpHost}/`]: `https://foo.${httpsHost}/`,
|
||||||
};
|
};
|
||||||
|
|
||||||
const verifyRedirection = async (fromUrl: string, toUrl: string) => {
|
const verifyRedirection = (httpUrl: string) => h.runCmd(`curl -i ${httpUrl}`).then(result => {
|
||||||
const result = await h.runCmd(`curl -i ${fromUrl}`);
|
|
||||||
h.verifyResponse(307)(result);
|
h.verifyResponse(307)(result);
|
||||||
|
|
||||||
const headers = result.stdout.split(/(?:\r?\n){2,}/)[0];
|
const headers = result.stdout.split(/(?:\r?\n){2,}/)[0];
|
||||||
expect(headers).toContain(`Location: ${toUrl}`);
|
expect(headers).toContain(`Location: ${urlMap[httpUrl]}`);
|
||||||
};
|
});
|
||||||
|
|
||||||
await Promise.all(Object.entries(urlMap).map(urls => verifyRedirection(...urls)));
|
Promise.
|
||||||
|
all(Object.keys(urlMap).map(verifyRedirection)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -61,15 +62,15 @@ describe(`nginx`, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return /index.html', async () => {
|
it('should return /index.html', done => {
|
||||||
const origin = `${scheme}://pr${pr}-${shortSha9}.${host}`;
|
const origin = `${scheme}://pr${pr}-${shortSha9}.${host}`;
|
||||||
const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`);
|
const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`);
|
||||||
|
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iL ${origin}/index.html`).then(h.verifyResponse(200, bodyRegex)),
|
h.runCmd(`curl -iL ${origin}/index.html`).then(h.verifyResponse(200, bodyRegex)),
|
||||||
h.runCmd(`curl -iL ${origin}/`).then(h.verifyResponse(200, bodyRegex)),
|
h.runCmd(`curl -iL ${origin}/`).then(h.verifyResponse(200, bodyRegex)),
|
||||||
h.runCmd(`curl -iL ${origin}`).then(h.verifyResponse(200, bodyRegex)),
|
h.runCmd(`curl -iL ${origin}`).then(h.verifyResponse(200, bodyRegex)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -89,11 +90,12 @@ describe(`nginx`, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return /foo/bar.js', async () => {
|
it('should return /foo/bar.js', done => {
|
||||||
const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /foo/bar\\.js$`);
|
const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /foo/bar\\.js$`);
|
||||||
|
|
||||||
await h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo/bar.js`).
|
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo/bar.js`).
|
||||||
then(h.verifyResponse(200, bodyRegex));
|
then(h.verifyResponse(200, bodyRegex)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -109,46 +111,47 @@ describe(`nginx`, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 403 for directories', async () => {
|
it('should respond with 403 for directories', done => {
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo/`).then(h.verifyResponse(403)),
|
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo/`).then(h.verifyResponse(403)),
|
||||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo`).then(h.verifyResponse(403)),
|
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo`).then(h.verifyResponse(403)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 404 for unknown paths to files', async () => {
|
it('should respond with 404 for unknown paths to files', done => {
|
||||||
await h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo/baz.css`).
|
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo/baz.css`).
|
||||||
then(h.verifyResponse(404));
|
then(h.verifyResponse(404)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should rewrite to \'index.html\' for unknown paths that don\'t look like files', async () => {
|
it('should rewrite to \'index.html\' for unknown paths that don\'t look like files', done => {
|
||||||
const origin = `${scheme}://pr${pr}-${shortSha9}.${host}`;
|
const origin = `${scheme}://pr${pr}-${shortSha9}.${host}`;
|
||||||
const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`);
|
const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`);
|
||||||
|
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iL ${origin}/foo/baz`).then(h.verifyResponse(200, bodyRegex)),
|
h.runCmd(`curl -iL ${origin}/foo/baz`).then(h.verifyResponse(200, bodyRegex)),
|
||||||
h.runCmd(`curl -iL ${origin}/foo/baz/`).then(h.verifyResponse(200, bodyRegex)),
|
h.runCmd(`curl -iL ${origin}/foo/baz/`).then(h.verifyResponse(200, bodyRegex)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 404 for unknown PRs/SHAs', async () => {
|
it('should respond with 404 for unknown PRs/SHAs', done => {
|
||||||
const otherPr = 54321;
|
const otherPr = 54321;
|
||||||
const otherShortSha = computeShortSha('8'.repeat(40));
|
const otherShortSha = computeShortSha('8'.repeat(40));
|
||||||
|
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iL ${scheme}://pr${pr}9-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://pr${pr}9-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://pr${otherPr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://pr${otherPr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}9.${host}`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}9.${host}`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-${otherShortSha}.${host}`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://pr${pr}-${otherShortSha}.${host}`).then(h.verifyResponse(404)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 404 if the subdomain format is wrong', async () => {
|
it('should respond with 404 if the subdomain format is wrong', done => {
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iL ${scheme}://xpr${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://xpr${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://prx${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://prx${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://xx${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://xx${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
||||||
@ -157,25 +160,26 @@ describe(`nginx`, () => {
|
|||||||
h.runCmd(`curl -iL ${scheme}://${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://pr${pr}${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://pr${pr}${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://pr${pr}_${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://pr${pr}_${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject PRs with leading zeros', async () => {
|
it('should reject PRs with leading zeros', done => {
|
||||||
await h.runCmd(`curl -iL ${scheme}://pr0${pr}-${shortSha9}.${host}`).
|
h.runCmd(`curl -iL ${scheme}://pr0${pr}-${shortSha9}.${host}`).
|
||||||
then(h.verifyResponse(404));
|
then(h.verifyResponse(404)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should accept SHAs with leading zeros (but not trim the zeros)', async () => {
|
it('should accept SHAs with leading zeros (but not trim the zeros)', done => {
|
||||||
const bodyRegex9 = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`);
|
const bodyRegex9 = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`);
|
||||||
const bodyRegex0 = new RegExp(`^PR: ${pr} | SHA: ${sha0} | File: /index\\.html$`);
|
const bodyRegex0 = new RegExp(`^PR: ${pr} | SHA: ${sha0} | File: /index\\.html$`);
|
||||||
|
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-0${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://pr${pr}-0${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}`).then(h.verifyResponse(200, bodyRegex9)),
|
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}`).then(h.verifyResponse(200, bodyRegex9)),
|
||||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha0}.${host}`).then(h.verifyResponse(200, bodyRegex0)),
|
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha0}.${host}`).then(h.verifyResponse(200, bodyRegex0)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -227,23 +231,23 @@ describe(`nginx`, () => {
|
|||||||
|
|
||||||
describe(`${host}/health-check`, () => {
|
describe(`${host}/health-check`, () => {
|
||||||
|
|
||||||
it('should respond with 200', async () => {
|
it('should respond with 200', done => {
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iL ${scheme}://${host}/health-check`).then(h.verifyResponse(200)),
|
h.runCmd(`curl -iL ${scheme}://${host}/health-check`).then(h.verifyResponse(200)),
|
||||||
h.runCmd(`curl -iL ${scheme}://${host}/health-check/`).then(h.verifyResponse(200)),
|
h.runCmd(`curl -iL ${scheme}://${host}/health-check/`).then(h.verifyResponse(200)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 404 if the path does not match exactly', async () => {
|
it('should respond with 404 if the path does not match exactly', done => {
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iL ${scheme}://${host}/health-check/foo`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://${host}/health-check/foo`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://${host}/health-check-foo`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://${host}/health-check-foo`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://${host}/health-checknfoo`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://${host}/health-checknfoo`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://${host}/foo/health-check`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://${host}/foo/health-check`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://${host}/foo-health-check`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://${host}/foo-health-check`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://${host}/foonhealth-check`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://${host}/foonhealth-check`).then(h.verifyResponse(404)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -287,28 +291,29 @@ describe(`nginx`, () => {
|
|||||||
|
|
||||||
describe(`${host}/circle-build`, () => {
|
describe(`${host}/circle-build`, () => {
|
||||||
|
|
||||||
it('should disallow non-POST requests', async () => {
|
it('should disallow non-POST requests', done => {
|
||||||
const url = `${scheme}://${host}/circle-build`;
|
const url = `${scheme}://${host}/circle-build`;
|
||||||
|
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iLX GET ${url}`).then(h.verifyResponse(405)),
|
h.runCmd(`curl -iLX GET ${url}`).then(h.verifyResponse(405)),
|
||||||
h.runCmd(`curl -iLX PUT ${url}`).then(h.verifyResponse(405)),
|
h.runCmd(`curl -iLX PUT ${url}`).then(h.verifyResponse(405)),
|
||||||
h.runCmd(`curl -iLX PATCH ${url}`).then(h.verifyResponse(405)),
|
h.runCmd(`curl -iLX PATCH ${url}`).then(h.verifyResponse(405)),
|
||||||
h.runCmd(`curl -iLX DELETE ${url}`).then(h.verifyResponse(405)),
|
h.runCmd(`curl -iLX DELETE ${url}`).then(h.verifyResponse(405)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should pass requests through to the preview server', async () => {
|
it('should pass requests through to the preview server', done => {
|
||||||
await h.runCmd(`curl -iLX POST ${scheme}://${host}/circle-build`).
|
h.runCmd(`curl -iLX POST ${scheme}://${host}/circle-build`).
|
||||||
then(h.verifyResponse(400, /Incorrect body content. Expected JSON/));
|
then(h.verifyResponse(400, /Incorrect body content. Expected JSON/)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 404 for unknown paths', async () => {
|
it('should respond with 404 for unknown paths', done => {
|
||||||
const cmdPrefix = `curl -iLX POST ${scheme}://${host}`;
|
const cmdPrefix = `curl -iLX POST ${scheme}://${host}`;
|
||||||
|
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`${cmdPrefix}/foo/circle-build/`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/foo/circle-build/`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/foo-circle-build/`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/foo-circle-build/`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/fooncircle-build/`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/fooncircle-build/`).then(h.verifyResponse(404)),
|
||||||
@ -317,7 +322,7 @@ describe(`nginx`, () => {
|
|||||||
h.runCmd(`${cmdPrefix}/circle-buildnfoo/`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/circle-buildnfoo/`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/circle-build/pr`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/circle-build/pr`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/circle-build/42`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/circle-build/42`).then(h.verifyResponse(404)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -327,33 +332,38 @@ describe(`nginx`, () => {
|
|||||||
const url = `${scheme}://${host}/pr-updated`;
|
const url = `${scheme}://${host}/pr-updated`;
|
||||||
|
|
||||||
|
|
||||||
it('should disallow non-POST requests', async () => {
|
it('should disallow non-POST requests', done => {
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iLX GET ${url}`).then(h.verifyResponse(405)),
|
h.runCmd(`curl -iLX GET ${url}`).then(h.verifyResponse(405)),
|
||||||
h.runCmd(`curl -iLX PUT ${url}`).then(h.verifyResponse(405)),
|
h.runCmd(`curl -iLX PUT ${url}`).then(h.verifyResponse(405)),
|
||||||
h.runCmd(`curl -iLX PATCH ${url}`).then(h.verifyResponse(405)),
|
h.runCmd(`curl -iLX PATCH ${url}`).then(h.verifyResponse(405)),
|
||||||
h.runCmd(`curl -iLX DELETE ${url}`).then(h.verifyResponse(405)),
|
h.runCmd(`curl -iLX DELETE ${url}`).then(h.verifyResponse(405)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should pass requests through to the preview server', async () => {
|
it('should pass requests through to the preview server', done => {
|
||||||
await h.runCmd(`curl -iLX POST --header "Content-Type: application/json" ${url}`).
|
const cmdPrefix = `curl -iLX POST --header "Content-Type: application/json"`;
|
||||||
then(h.verifyResponse(400, /Missing or empty 'number' field/));
|
|
||||||
|
const cmd1 = `${cmdPrefix} ${url}`;
|
||||||
|
|
||||||
|
Promise.all([
|
||||||
|
h.runCmd(cmd1).then(h.verifyResponse(400, /Missing or empty 'number' field/)),
|
||||||
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 404 for unknown paths', async () => {
|
it('should respond with 404 for unknown paths', done => {
|
||||||
const cmdPrefix = `curl -iLX POST ${scheme}://${host}`;
|
const cmdPrefix = `curl -iLX POST ${scheme}://${host}`;
|
||||||
|
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`${cmdPrefix}/foo/pr-updated`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/foo/pr-updated`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/foo-pr-updated`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/foo-pr-updated`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/foonpr-updated`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/foonpr-updated`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/pr-updated/foo`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/pr-updated/foo`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/pr-updated-foo`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/pr-updated-foo`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/pr-updatednfoo`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/pr-updatednfoo`).then(h.verifyResponse(404)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -364,7 +374,7 @@ describe(`nginx`, () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
['index.html', 'foo.js', 'foo/index.html'].forEach(relFilePath => {
|
['index.html', 'foo.js', 'foo/index.html'].forEach(relFilePath => {
|
||||||
const absFilePath = path.join(AIO_BUILDS_DIR, relFilePath);
|
const absFilePath = path.join(AIO_BUILDS_DIR, relFilePath);
|
||||||
h.writeFile(absFilePath, {content: `File: /${relFilePath}`});
|
return h.writeFile(absFilePath, {content: `File: /${relFilePath}`});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -105,8 +105,8 @@ describe('preview-server', () => {
|
|||||||
|
|
||||||
|
|
||||||
describe(`${host}/circle-build`, () => {
|
describe(`${host}/circle-build`, () => {
|
||||||
const curl = makeCurl(`${host}/circle-build`);
|
|
||||||
|
|
||||||
|
const curl = makeCurl(`${host}/circle-build`);
|
||||||
|
|
||||||
it('should disallow non-POST requests', async () => {
|
it('should disallow non-POST requests', async () => {
|
||||||
const bodyRegex = /^Unknown resource/;
|
const bodyRegex = /^Unknown resource/;
|
||||||
@ -189,7 +189,8 @@ describe('preview-server', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should respond with 201 if a new public build is created', async () => {
|
it('should respond with 201 if a new public build is created', async () => {
|
||||||
await curl(payload(BuildNums.TRUST_CHECK_ACTIVE_TRUSTED_USER)).then(h.verifyResponse(201));
|
await curl(payload(BuildNums.TRUST_CHECK_ACTIVE_TRUSTED_USER))
|
||||||
|
.then(h.verifyResponse(201));
|
||||||
expect({ prNum: PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER }).toExistAsABuild();
|
expect({ prNum: PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER }).toExistAsABuild();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -198,7 +199,7 @@ describe('preview-server', () => {
|
|||||||
expect({ prNum: PrNums.TRUST_CHECK_UNTRUSTED, isPublic: false }).toExistAsABuild();
|
expect({ prNum: PrNums.TRUST_CHECK_UNTRUSTED, isPublic: false }).toExistAsABuild();
|
||||||
});
|
});
|
||||||
|
|
||||||
[true, false].forEach(isPublic => {
|
[true].forEach(isPublic => {
|
||||||
const build = isPublic ? BuildNums.TRUST_CHECK_ACTIVE_TRUSTED_USER : BuildNums.TRUST_CHECK_UNTRUSTED;
|
const build = isPublic ? BuildNums.TRUST_CHECK_ACTIVE_TRUSTED_USER : BuildNums.TRUST_CHECK_UNTRUSTED;
|
||||||
const prNum = isPublic ? PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER : PrNums.TRUST_CHECK_UNTRUSTED;
|
const prNum = isPublic ? PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER : PrNums.TRUST_CHECK_UNTRUSTED;
|
||||||
const label = isPublic ? 'public' : 'non-public';
|
const label = isPublic ? 'public' : 'non-public';
|
||||||
@ -363,23 +364,23 @@ describe('preview-server', () => {
|
|||||||
|
|
||||||
describe(`${host}/health-check`, () => {
|
describe(`${host}/health-check`, () => {
|
||||||
|
|
||||||
it('should respond with 200', async () => {
|
it('should respond with 200', done => {
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iL ${host}/health-check`).then(h.verifyResponse(200)),
|
h.runCmd(`curl -iL ${host}/health-check`).then(h.verifyResponse(200)),
|
||||||
h.runCmd(`curl -iL ${host}/health-check/`).then(h.verifyResponse(200)),
|
h.runCmd(`curl -iL ${host}/health-check/`).then(h.verifyResponse(200)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 404 if the path does not match exactly', async () => {
|
it('should respond with 404 if the path does not match exactly', done => {
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iL ${host}/health-check/foo`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${host}/health-check/foo`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${host}/health-check-foo`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${host}/health-check-foo`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${host}/health-checknfoo`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${host}/health-checknfoo`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${host}/foo/health-check`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${host}/foo/health-check`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${host}/foo-health-check`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${host}/foo-health-check`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${host}/foonhealth-check`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${host}/foonhealth-check`).then(h.verifyResponse(404)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -425,18 +426,18 @@ describe('preview-server', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 404 for unknown paths', async () => {
|
it('should respond with 404 for unknown paths', done => {
|
||||||
const mockPayload = JSON.stringify({number: 1}); // MockExternalApiFlags.TRUST_CHECK_ACTIVE_TRUSTED_USER });
|
const mockPayload = JSON.stringify({number: 1}); // MockExternalApiFlags.TRUST_CHECK_ACTIVE_TRUSTED_USER });
|
||||||
const cmdPrefix = `curl -iLX POST --data "${mockPayload}" ${host}`;
|
const cmdPrefix = `curl -iLX POST --data "${mockPayload}" ${host}`;
|
||||||
|
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`${cmdPrefix}/foo/pr-updated`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/foo/pr-updated`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/foo-pr-updated`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/foo-pr-updated`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/foonpr-updated`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/foonpr-updated`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/pr-updated/foo`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/pr-updated/foo`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/pr-updated-foo`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/pr-updated-foo`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/pr-updatednfoo`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/pr-updatednfoo`).then(h.verifyResponse(404)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -550,10 +551,10 @@ describe('preview-server', () => {
|
|||||||
|
|
||||||
describe(`${host}/*`, () => {
|
describe(`${host}/*`, () => {
|
||||||
|
|
||||||
it('should respond with 404 for requests to unknown URLs', async () => {
|
it('should respond with 404 for requests to unknown URLs', done => {
|
||||||
const bodyRegex = /^Unknown resource/;
|
const bodyRegex = /^Unknown resource/;
|
||||||
|
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iL ${host}/index.html`).then(h.verifyResponse(404, bodyRegex)),
|
h.runCmd(`curl -iL ${host}/index.html`).then(h.verifyResponse(404, bodyRegex)),
|
||||||
h.runCmd(`curl -iL ${host}/`).then(h.verifyResponse(404, bodyRegex)),
|
h.runCmd(`curl -iL ${host}/`).then(h.verifyResponse(404, bodyRegex)),
|
||||||
h.runCmd(`curl -iL ${host}`).then(h.verifyResponse(404, bodyRegex)),
|
h.runCmd(`curl -iL ${host}`).then(h.verifyResponse(404, bodyRegex)),
|
||||||
@ -561,7 +562,7 @@ describe('preview-server', () => {
|
|||||||
h.runCmd(`curl -iLX POST ${host}`).then(h.verifyResponse(404, bodyRegex)),
|
h.runCmd(`curl -iLX POST ${host}`).then(h.verifyResponse(404, bodyRegex)),
|
||||||
h.runCmd(`curl -iLX PATCH ${host}`).then(h.verifyResponse(404, bodyRegex)),
|
h.runCmd(`curl -iLX PATCH ${host}`).then(h.verifyResponse(404, bodyRegex)),
|
||||||
h.runCmd(`curl -iLX DELETE ${host}`).then(h.verifyResponse(404, bodyRegex)),
|
h.runCmd(`curl -iLX DELETE ${host}`).then(h.verifyResponse(404, bodyRegex)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -14,41 +14,42 @@
|
|||||||
"predev": "yarn build || true",
|
"predev": "yarn build || true",
|
||||||
"dev": "run-p ~~build-watch ~~test-watch",
|
"dev": "run-p ~~build-watch ~~test-watch",
|
||||||
"lint": "tslint --project tsconfig.json",
|
"lint": "tslint --project tsconfig.json",
|
||||||
"pretest": "run-s build lint",
|
"pretest": "yarn build",
|
||||||
"test": "yarn ~~test-only",
|
"test": "yarn ~~test-only",
|
||||||
"pretest-watch": "yarn pretest",
|
"pretest-watch": "yarn pretest",
|
||||||
"test-watch": "yarn ~~test-watch",
|
"test-watch": "yarn ~~test-watch",
|
||||||
"~~build": "tsc",
|
"~~build": "tsc",
|
||||||
"~~build-watch": "yarn ~~build --watch",
|
"~~build-watch": "yarn ~~build --watch",
|
||||||
|
"pre~~test-only": "yarn lint",
|
||||||
"~~test-only": "node dist/test",
|
"~~test-only": "node dist/test",
|
||||||
"~~test-watch": "nodemon --delay 1 --exec \"yarn ~~test-only\" --watch dist"
|
"~~test-watch": "nodemon --delay 1 --exec \"yarn ~~test-only\" --watch dist"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.18.3",
|
||||||
"delete-empty": "^3.0.0",
|
"delete-empty": "^2.0.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.16.3",
|
||||||
"jasmine": "^3.5.0",
|
"jasmine": "^3.2.0",
|
||||||
"nock": "^12.0.3",
|
"nock": "^9.6.1",
|
||||||
"node-fetch": "^2.6.0",
|
"node-fetch": "^2.2.0",
|
||||||
"shelljs": "^0.8.4",
|
"shelljs": "^0.8.2",
|
||||||
"source-map-support": "^0.5.19",
|
"source-map-support": "^0.5.9",
|
||||||
"tar-stream": "^2.1.2",
|
"tar-stream": "^1.6.1",
|
||||||
"tslib": "^1.11.1"
|
"tslib": "^1.10.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/body-parser": "^1.19.0",
|
"@types/body-parser": "^1.17.0",
|
||||||
"@types/express": "^4.17.6",
|
"@types/express": "^4.16.0",
|
||||||
"@types/jasmine": "^3.5.10",
|
"@types/jasmine": "^2.8.8",
|
||||||
"@types/nock": "^11.1.0",
|
"@types/nock": "^9.3.0",
|
||||||
"@types/node": "^13.13.2",
|
"@types/node": "^10.9.2",
|
||||||
"@types/node-fetch": "^2.5.7",
|
"@types/node-fetch": "^2.1.2",
|
||||||
"@types/shelljs": "^0.8.7",
|
"@types/shelljs": "^0.8.0",
|
||||||
"@types/supertest": "^2.0.8",
|
"@types/supertest": "^2.0.5",
|
||||||
"nodemon": "^2.0.3",
|
"nodemon": "^1.18.3",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"supertest": "^4.0.2",
|
"supertest": "^3.1.0",
|
||||||
"tslint": "^6.1.1",
|
"tslint": "^5.11.0",
|
||||||
"tslint-jasmine-noSkipOrFocus": "^1.0.9",
|
"tslint-jasmine-noSkipOrFocus": "^1.0.9",
|
||||||
"typescript": "^3.8.3"
|
"typescript": "^3.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ const EXISTING_DOWNLOADS = [
|
|||||||
'20-1234567-build.zip',
|
'20-1234567-build.zip',
|
||||||
];
|
];
|
||||||
const OPEN_PRS = [10, 40];
|
const OPEN_PRS = [10, 40];
|
||||||
|
const ANY_DATE = jasmine.any(String);
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
describe('BuildCleaner', () => {
|
describe('BuildCleaner', () => {
|
||||||
@ -76,18 +77,22 @@ describe('BuildCleaner', () => {
|
|||||||
let cleanerRemoveUnnecessaryDownloadsSpy: jasmine.Spy;
|
let cleanerRemoveUnnecessaryDownloadsSpy: jasmine.Spy;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cleanerGetExistingBuildNumbersSpy = spyOn(cleaner, 'getExistingBuildNumbers').and.resolveTo(EXISTING_BUILDS);
|
cleanerGetExistingBuildNumbersSpy = spyOn(cleaner, 'getExistingBuildNumbers')
|
||||||
cleanerGetOpenPrNumbersSpy = spyOn(cleaner, 'getOpenPrNumbers').and.resolveTo(OPEN_PRS);
|
.and.callFake(() => Promise.resolve(EXISTING_BUILDS));
|
||||||
cleanerGetExistingDownloadsSpy = spyOn(cleaner, 'getExistingDownloads').and.resolveTo(EXISTING_DOWNLOADS);
|
cleanerGetOpenPrNumbersSpy = spyOn(cleaner, 'getOpenPrNumbers')
|
||||||
|
.and.callFake(() => Promise.resolve(OPEN_PRS));
|
||||||
|
cleanerGetExistingDownloadsSpy = spyOn(cleaner, 'getExistingDownloads')
|
||||||
|
.and.callFake(() => Promise.resolve(EXISTING_DOWNLOADS));
|
||||||
|
|
||||||
cleanerRemoveUnnecessaryBuildsSpy = spyOn(cleaner, 'removeUnnecessaryBuilds');
|
cleanerRemoveUnnecessaryBuildsSpy = spyOn(cleaner, 'removeUnnecessaryBuilds');
|
||||||
cleanerRemoveUnnecessaryDownloadsSpy = spyOn(cleaner, 'removeUnnecessaryDownloads');
|
cleanerRemoveUnnecessaryDownloadsSpy = spyOn(cleaner, 'removeUnnecessaryDownloads');
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return a promise', async () => {
|
it('should return a promise', async () => {
|
||||||
const promise = cleaner.cleanUp();
|
const promise = cleaner.cleanUp();
|
||||||
expect(promise).toBeInstanceOf(Promise);
|
expect(promise).toEqual(jasmine.any(Promise));
|
||||||
|
|
||||||
// Do not complete the test and release the spies synchronously, to avoid running the actual implementations.
|
// Do not complete the test and release the spies synchronously, to avoid running the actual implementations.
|
||||||
await promise;
|
await promise;
|
||||||
@ -125,32 +130,52 @@ describe('BuildCleaner', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should reject if \'getOpenPrNumbers()\' rejects', async () => {
|
it('should reject if \'getOpenPrNumbers()\' rejects', async () => {
|
||||||
cleanerGetOpenPrNumbersSpy.and.rejectWith('Test');
|
try {
|
||||||
await expectAsync(cleaner.cleanUp()).toBeRejectedWith('Test');
|
cleanerGetOpenPrNumbersSpy.and.callFake(() => Promise.reject('Test'));
|
||||||
|
await cleaner.cleanUp();
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if \'getExistingBuildNumbers()\' rejects', async () => {
|
it('should reject if \'getExistingBuildNumbers()\' rejects', async () => {
|
||||||
cleanerGetExistingBuildNumbersSpy.and.rejectWith('Test');
|
try {
|
||||||
await expectAsync(cleaner.cleanUp()).toBeRejectedWith('Test');
|
cleanerGetExistingBuildNumbersSpy.and.callFake(() => Promise.reject('Test'));
|
||||||
|
await cleaner.cleanUp();
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if \'getExistingDownloads()\' rejects', async () => {
|
it('should reject if \'getExistingDownloads()\' rejects', async () => {
|
||||||
cleanerGetExistingDownloadsSpy.and.rejectWith('Test');
|
try {
|
||||||
await expectAsync(cleaner.cleanUp()).toBeRejectedWith('Test');
|
cleanerGetExistingDownloadsSpy.and.callFake(() => Promise.reject('Test'));
|
||||||
|
await cleaner.cleanUp();
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if \'removeUnnecessaryBuilds()\' rejects', async () => {
|
it('should reject if \'removeUnnecessaryBuilds()\' rejects', async () => {
|
||||||
cleanerRemoveUnnecessaryBuildsSpy.and.rejectWith('Test');
|
try {
|
||||||
await expectAsync(cleaner.cleanUp()).toBeRejectedWith('Test');
|
cleanerRemoveUnnecessaryBuildsSpy.and.callFake(() => Promise.reject('Test'));
|
||||||
|
await cleaner.cleanUp();
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if \'removeUnnecessaryDownloads()\' rejects', async () => {
|
it('should reject if \'removeUnnecessaryDownloads()\' rejects', async () => {
|
||||||
cleanerRemoveUnnecessaryDownloadsSpy.and.rejectWith('Test');
|
try {
|
||||||
await expectAsync(cleaner.cleanUp()).toBeRejectedWith('Test');
|
cleanerRemoveUnnecessaryDownloadsSpy.and.callFake(() => Promise.reject('Test'));
|
||||||
|
await cleaner.cleanUp();
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -162,15 +187,13 @@ describe('BuildCleaner', () => {
|
|||||||
let promise: Promise<number[]>;
|
let promise: Promise<number[]>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fsReaddirSpy = spyOn(fs, 'readdir').and.callFake(
|
fsReaddirSpy = spyOn(fs, 'readdir').and.callFake((_: string, cb: typeof readdirCb) => readdirCb = cb);
|
||||||
((_: string, cb: typeof readdirCb) => readdirCb = cb) as unknown as typeof fs.readdir,
|
|
||||||
);
|
|
||||||
promise = cleaner.getExistingBuildNumbers();
|
promise = cleaner.getExistingBuildNumbers();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return a promise', () => {
|
it('should return a promise', () => {
|
||||||
expect(promise).toBeInstanceOf(Promise);
|
expect(promise).toEqual(jasmine.any(Promise));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -180,27 +203,43 @@ describe('BuildCleaner', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if an error occurs while getting the files', async () => {
|
it('should reject if an error occurs while getting the files', done => {
|
||||||
|
promise.catch(err => {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
readdirCb('Test');
|
readdirCb('Test');
|
||||||
await expectAsync(promise).toBeRejectedWith('Test');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with the returned files (as numbers)', async () => {
|
it('should resolve with the returned files (as numbers)', done => {
|
||||||
|
promise.then(result => {
|
||||||
|
expect(result).toEqual([12, 34, 56]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
readdirCb(null, ['12', '34', '56']);
|
readdirCb(null, ['12', '34', '56']);
|
||||||
await expectAsync(promise).toBeResolvedTo([12, 34, 56]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should remove `HIDDEN_DIR_PREFIX` from the filenames', async () => {
|
it('should remove `HIDDEN_DIR_PREFIX` from the filenames', done => {
|
||||||
|
promise.then(result => {
|
||||||
|
expect(result).toEqual([12, 34, 56]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
readdirCb(null, [`${HIDDEN_DIR_PREFIX}12`, '34', `${HIDDEN_DIR_PREFIX}56`]);
|
readdirCb(null, [`${HIDDEN_DIR_PREFIX}12`, '34', `${HIDDEN_DIR_PREFIX}56`]);
|
||||||
await expectAsync(promise).toBeResolvedTo([12, 34, 56]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should ignore files with non-numeric (or zero) names', async () => {
|
it('should ignore files with non-numeric (or zero) names', done => {
|
||||||
|
promise.then(result => {
|
||||||
|
expect(result).toEqual([12, 34, 56]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
readdirCb(null, ['12', 'foo', '34', 'bar', '56', '000']);
|
readdirCb(null, ['12', 'foo', '34', 'bar', '56', '000']);
|
||||||
await expectAsync(promise).toBeResolvedTo([12, 34, 56]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -220,7 +259,7 @@ describe('BuildCleaner', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should return a promise', () => {
|
it('should return a promise', () => {
|
||||||
expect(promise).toBeInstanceOf(Promise);
|
expect(promise).toEqual(jasmine.any(Promise));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -229,15 +268,31 @@ describe('BuildCleaner', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if an error occurs while fetching PRs', async () => {
|
it('should reject if an error occurs while fetching PRs', done => {
|
||||||
|
promise.catch(err => {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
prDeferred.reject('Test');
|
prDeferred.reject('Test');
|
||||||
await expectAsync(promise).toBeRejectedWith('Test');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with the numbers of the fetched PRs', async () => {
|
it('should resolve with the numbers of the fetched PRs', done => {
|
||||||
|
promise.then(prNumbers => {
|
||||||
|
expect(prNumbers).toEqual([1, 2, 3]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
prDeferred.resolve([{id: 0, number: 1}, {id: 1, number: 2}, {id: 2, number: 3}]);
|
prDeferred.resolve([{id: 0, number: 1}, {id: 1, number: 2}, {id: 2, number: 3}]);
|
||||||
await expectAsync(promise).toBeResolvedTo([1, 2, 3]);
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should log the number of open PRs', () => {
|
||||||
|
promise.then(prNumbers => {
|
||||||
|
expect(loggerLogSpy).toHaveBeenCalledWith(
|
||||||
|
ANY_DATE, 'BuildCleaner: ', `Open pull requests: ${prNumbers}`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -249,15 +304,13 @@ describe('BuildCleaner', () => {
|
|||||||
let promise: Promise<string[]>;
|
let promise: Promise<string[]>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fsReaddirSpy = spyOn(fs, 'readdir').and.callFake(
|
fsReaddirSpy = spyOn(fs, 'readdir').and.callFake((_: string, cb: typeof readdirCb) => readdirCb = cb);
|
||||||
((_: string, cb: typeof readdirCb) => readdirCb = cb) as unknown as typeof fs.readdir,
|
|
||||||
);
|
|
||||||
promise = cleaner.getExistingDownloads();
|
promise = cleaner.getExistingDownloads();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return a promise', () => {
|
it('should return a promise', () => {
|
||||||
expect(promise).toBeInstanceOf(Promise);
|
expect(promise).toEqual(jasmine.any(Promise));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -267,21 +320,33 @@ describe('BuildCleaner', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if an error occurs while getting the files', async () => {
|
it('should reject if an error occurs while getting the files', done => {
|
||||||
|
promise.catch(err => {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
readdirCb('Test');
|
readdirCb('Test');
|
||||||
await expectAsync(promise).toBeRejectedWith('Test');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with the returned file names', async () => {
|
it('should resolve with the returned file names', done => {
|
||||||
|
promise.then(result => {
|
||||||
|
expect(result).toEqual(EXISTING_DOWNLOADS);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
readdirCb(null, EXISTING_DOWNLOADS);
|
readdirCb(null, EXISTING_DOWNLOADS);
|
||||||
await expectAsync(promise).toBeResolvedTo(EXISTING_DOWNLOADS);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should ignore files that do not match the artifactPath', async () => {
|
it('should ignore files that do not match the artifactPath', done => {
|
||||||
|
promise.then(result => {
|
||||||
|
expect(result).toEqual(['10-ABCDEF-build.zip', '30-FFFFFFF-build.zip']);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
readdirCb(null, ['10-ABCDEF-build.zip', '20-AAAAAAA-otherfile.zip', '30-FFFFFFF-build.zip']);
|
readdirCb(null, ['10-ABCDEF-build.zip', '20-AAAAAAA-otherfile.zip', '30-FFFFFFF-build.zip']);
|
||||||
await expectAsync(promise).toBeResolvedTo(['10-ABCDEF-build.zip', '30-FFFFFFF-build.zip']);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -299,7 +364,7 @@ describe('BuildCleaner', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should test if the directory exists (and return if it does not)', () => {
|
it('should test if the directory exists (and return if is does not)', () => {
|
||||||
shellTestSpy.and.returnValue(false);
|
shellTestSpy.and.returnValue(false);
|
||||||
cleaner.removeDir('/foo/bar');
|
cleaner.removeDir('/foo/bar');
|
||||||
|
|
||||||
@ -316,19 +381,22 @@ describe('BuildCleaner', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should make the directory and its content writable before removing', () => {
|
it('should make the directory and its content writable before removing', () => {
|
||||||
|
shellRmSpy.and.callFake(() => expect(shellChmodSpy).toHaveBeenCalledWith('-R', 'a+w', '/foo/bar'));
|
||||||
cleaner.removeDir('/foo/bar');
|
cleaner.removeDir('/foo/bar');
|
||||||
|
|
||||||
expect(shellChmodSpy).toHaveBeenCalledBefore(shellRmSpy);
|
|
||||||
expect(shellChmodSpy).toHaveBeenCalledWith('-R', 'a+w', '/foo/bar');
|
|
||||||
expect(shellRmSpy).toHaveBeenCalled();
|
expect(shellRmSpy).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should catch errors and log them', () => {
|
it('should catch errors and log them', () => {
|
||||||
shellRmSpy.and.throwError('Test');
|
shellRmSpy.and.callFake(() => {
|
||||||
|
// tslint:disable-next-line: no-string-throw
|
||||||
|
throw 'Test';
|
||||||
|
});
|
||||||
|
|
||||||
cleaner.removeDir('/foo/bar');
|
cleaner.removeDir('/foo/bar');
|
||||||
|
|
||||||
expect(loggerErrorSpy).toHaveBeenCalledWith('ERROR: Unable to remove \'/foo/bar\' due to:', new Error('Test'));
|
expect(loggerErrorSpy).toHaveBeenCalledWith('ERROR: Unable to remove \'/foo/bar\' due to:', 'Test');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -381,7 +449,7 @@ describe('BuildCleaner', () => {
|
|||||||
expect(cleanerRemoveDirSpy).toHaveBeenCalledTimes(0);
|
expect(cleanerRemoveDirSpy).toHaveBeenCalledTimes(0);
|
||||||
cleanerRemoveDirSpy.calls.reset();
|
cleanerRemoveDirSpy.calls.reset();
|
||||||
|
|
||||||
cleaner.removeUnnecessaryBuilds([1, 2, 3, 4], []);
|
(cleaner as any).removeUnnecessaryBuilds([1, 2, 3, 4], []);
|
||||||
expect(cleanerRemoveDirSpy).toHaveBeenCalledTimes(8);
|
expect(cleanerRemoveDirSpy).toHaveBeenCalledTimes(8);
|
||||||
expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/1'));
|
expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/1'));
|
||||||
expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/2'));
|
expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/2'));
|
||||||
|
@ -45,15 +45,25 @@ describe('CircleCIApi', () => {
|
|||||||
const errorMessage = 'Invalid request';
|
const errorMessage = 'Invalid request';
|
||||||
const request = nock(BASE_URL).get(`/${buildNum}?circle-token=${TOKEN}`);
|
const request = nock(BASE_URL).get(`/${buildNum}?circle-token=${TOKEN}`);
|
||||||
|
|
||||||
request.replyWithError(errorMessage);
|
try {
|
||||||
await expectAsync(api.getBuildInfo(buildNum)).toBeRejectedWithError(
|
request.replyWithError(errorMessage);
|
||||||
|
await api.getBuildInfo(buildNum);
|
||||||
|
throw new Error('Exception Expected');
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.message).toEqual(
|
||||||
`CircleCI build info request failed ` +
|
`CircleCI build info request failed ` +
|
||||||
`(request to ${BASE_URL}/${buildNum}?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
|
`(request to ${BASE_URL}/${buildNum}?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
|
||||||
|
}
|
||||||
|
|
||||||
request.reply(404, errorMessage);
|
try {
|
||||||
await expectAsync(api.getBuildInfo(buildNum)).toBeRejectedWithError(
|
request.reply(404, errorMessage);
|
||||||
|
await api.getBuildInfo(buildNum);
|
||||||
|
throw new Error('Exception Expected');
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.message).toEqual(
|
||||||
`CircleCI build info request failed ` +
|
`CircleCI build info request failed ` +
|
||||||
`(request to ${BASE_URL}/${buildNum}?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
|
`(request to ${BASE_URL}/${buildNum}?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -68,7 +78,8 @@ describe('CircleCIApi', () => {
|
|||||||
.get(`/${buildNum}/artifacts?circle-token=${TOKEN}`)
|
.get(`/${buildNum}/artifacts?circle-token=${TOKEN}`)
|
||||||
.reply(200, [artifact0, artifact1, artifact2]);
|
.reply(200, [artifact0, artifact1, artifact2]);
|
||||||
|
|
||||||
await expectAsync(api.getBuildArtifactUrl(buildNum, 'some/path/1')).toBeResolvedTo('https://url/1');
|
const artifactUrl = await api.getBuildArtifactUrl(buildNum, 'some/path/1');
|
||||||
|
expect(artifactUrl).toEqual('https://url/1');
|
||||||
request.done();
|
request.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -79,15 +90,25 @@ describe('CircleCIApi', () => {
|
|||||||
const errorMessage = 'Invalid request';
|
const errorMessage = 'Invalid request';
|
||||||
const request = nock(BASE_URL).get(`/${buildNum}/artifacts?circle-token=${TOKEN}`);
|
const request = nock(BASE_URL).get(`/${buildNum}/artifacts?circle-token=${TOKEN}`);
|
||||||
|
|
||||||
request.replyWithError(errorMessage);
|
try {
|
||||||
await expectAsync(api.getBuildArtifactUrl(buildNum, 'some/path/1')).toBeRejectedWithError(
|
request.replyWithError(errorMessage);
|
||||||
|
await api.getBuildArtifactUrl(buildNum, 'some/path/1');
|
||||||
|
throw new Error('Exception Expected');
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.message).toEqual(
|
||||||
`CircleCI artifact URL request failed ` +
|
`CircleCI artifact URL request failed ` +
|
||||||
`(request to ${BASE_URL}/${buildNum}/artifacts?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
|
`(request to ${BASE_URL}/${buildNum}/artifacts?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
|
||||||
|
}
|
||||||
|
|
||||||
request.reply(404, errorMessage);
|
try {
|
||||||
await expectAsync(api.getBuildArtifactUrl(buildNum, 'some/path/1')).toBeRejectedWithError(
|
request.reply(404, errorMessage);
|
||||||
|
await api.getBuildArtifactUrl(buildNum, 'some/path/1');
|
||||||
|
throw new Error('Exception Expected');
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.message).toEqual(
|
||||||
`CircleCI artifact URL request failed ` +
|
`CircleCI artifact URL request failed ` +
|
||||||
`(request to ${BASE_URL}/${buildNum}/artifacts?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
|
`(request to ${BASE_URL}/${buildNum}/artifacts?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error if the response does not contain the specified artifact', async () => {
|
it('should throw an error if the response does not contain the specified artifact', async () => {
|
||||||
@ -100,9 +121,14 @@ describe('CircleCIApi', () => {
|
|||||||
.get(`/${buildNum}/artifacts?circle-token=${TOKEN}`)
|
.get(`/${buildNum}/artifacts?circle-token=${TOKEN}`)
|
||||||
.reply(200, [artifact0, artifact1, artifact2]);
|
.reply(200, [artifact0, artifact1, artifact2]);
|
||||||
|
|
||||||
await expectAsync(api.getBuildArtifactUrl(buildNum, 'some/path/3')).toBeRejectedWithError(
|
try {
|
||||||
|
await api.getBuildArtifactUrl(buildNum, 'some/path/3');
|
||||||
|
throw new Error('Exception Expected');
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.message).toEqual(
|
||||||
`CircleCI artifact URL request failed ` +
|
`CircleCI artifact URL request failed ` +
|
||||||
`(Missing artifact (some/path/3) for CircleCI build: ${buildNum})`);
|
`(Missing artifact (some/path/3) for CircleCI build: ${buildNum})`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -118,7 +118,7 @@ describe('GithubApi', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should return a promise', () => {
|
it('should return a promise', () => {
|
||||||
expect((api as any).getPaginated()).toBeInstanceOf(Promise);
|
expect((api as any).getPaginated()).toEqual(jasmine.any(Promise));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -131,30 +131,45 @@ describe('GithubApi', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if the request fails', async () => {
|
it('should reject if the request fails', done => {
|
||||||
const responsePromise = (api as any).getPaginated('/foo/bar');
|
(api as any).getPaginated('/foo/bar').catch((err: any) => {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
deferreds[0].reject('Test');
|
deferreds[0].reject('Test');
|
||||||
|
|
||||||
await expectAsync(responsePromise).toBeRejectedWith('Test');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with the returned items', async () => {
|
it('should resolve with the returned items', done => {
|
||||||
const items = [{id: 1}, {id: 2}];
|
const items = [{id: 1}, {id: 2}];
|
||||||
const responsePromise = (api as any).getPaginated('/foo/bar');
|
|
||||||
deferreds[0].resolve(items);
|
|
||||||
|
|
||||||
await expectAsync(responsePromise).toBeResolvedTo(items);
|
(api as any).getPaginated('/foo/bar').then((data: any) => {
|
||||||
|
expect(data).toEqual(items);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
deferreds[0].resolve(items);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should iteratively call \'get()\' to fetch all items', async () => {
|
it('should iteratively call \'get()\' to fetch all items', done => {
|
||||||
// Create an array or 250 objects.
|
// Create an array or 250 objects.
|
||||||
const allItems = new Array(250).fill(null).map((_, i) => ({id: i}));
|
const allItems = '.'.repeat(250).split('').map((_, i) => ({id: i}));
|
||||||
const apiGetSpy = api.get as jasmine.Spy;
|
const apiGetSpy = api.get as jasmine.Spy;
|
||||||
const paramsForPage = (page: number) => ({baz: 'qux', page, per_page: 100});
|
|
||||||
|
|
||||||
const responsePromise = (api as any).getPaginated('/foo/bar', {baz: 'qux'});
|
(api as any).getPaginated('/foo/bar', {baz: 'qux'}).then((data: any) => {
|
||||||
|
const paramsForPage = (page: number) => ({baz: 'qux', page, per_page: 100});
|
||||||
|
|
||||||
|
expect(apiGetSpy).toHaveBeenCalledTimes(3);
|
||||||
|
expect(apiGetSpy.calls.argsFor(0)).toEqual(['/foo/bar', paramsForPage(1)]);
|
||||||
|
expect(apiGetSpy.calls.argsFor(1)).toEqual(['/foo/bar', paramsForPage(2)]);
|
||||||
|
expect(apiGetSpy.calls.argsFor(2)).toEqual(['/foo/bar', paramsForPage(3)]);
|
||||||
|
|
||||||
|
expect(data).toEqual(allItems);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
deferreds[0].resolve(allItems.slice(0, 100));
|
deferreds[0].resolve(allItems.slice(0, 100));
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -163,13 +178,6 @@ describe('GithubApi', () => {
|
|||||||
deferreds[2].resolve(allItems.slice(200));
|
deferreds[2].resolve(allItems.slice(200));
|
||||||
}, 0);
|
}, 0);
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
await expectAsync(responsePromise).toBeResolvedTo(allItems);
|
|
||||||
|
|
||||||
expect(apiGetSpy).toHaveBeenCalledTimes(3);
|
|
||||||
expect(apiGetSpy.calls.argsFor(0)).toEqual(['/foo/bar', paramsForPage(1)]);
|
|
||||||
expect(apiGetSpy.calls.argsFor(1)).toEqual(['/foo/bar', paramsForPage(2)]);
|
|
||||||
expect(apiGetSpy.calls.argsFor(2)).toEqual(['/foo/bar', paramsForPage(3)]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -209,8 +217,8 @@ describe('GithubApi', () => {
|
|||||||
|
|
||||||
describe('request()', () => {
|
describe('request()', () => {
|
||||||
it('should return a promise', () => {
|
it('should return a promise', () => {
|
||||||
nock('https://api.github.com').get('/').reply(200);
|
nock('https://api.github.com').get('').reply(200);
|
||||||
expect((api as any).request()).toBeInstanceOf(Promise);
|
expect((api as any).request()).toEqual(jasmine.any(Promise));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -239,8 +247,9 @@ describe('GithubApi', () => {
|
|||||||
nock('https://api.github.com')
|
nock('https://api.github.com')
|
||||||
.intercept('/path', 'method')
|
.intercept('/path', 'method')
|
||||||
.replyWithError('Test');
|
.replyWithError('Test');
|
||||||
|
let message = 'Failed to reject error';
|
||||||
await expectAsync((api as any).request('method', '/path')).toBeRejectedWithError('Test');
|
await (api as any).request('method', '/path').catch((err: any) => message = err.message);
|
||||||
|
expect(message).toEqual('Test');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -254,69 +263,81 @@ describe('GithubApi', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if response statusCode is <200', async () => {
|
it('should reject if response statusCode is <200', done => {
|
||||||
const requestHandler = nock('https://api.github.com')
|
const requestHandler = nock('https://api.github.com')
|
||||||
.intercept('/path', 'method')
|
.intercept('/path', 'method')
|
||||||
.reply(199);
|
.reply(199);
|
||||||
const responsePromise = (api as any).request('method', '/path');
|
|
||||||
|
|
||||||
await expectAsync(responsePromise).toBeRejectedWith(jasmine.stringMatching('failed'));
|
|
||||||
await expectAsync(responsePromise).toBeRejectedWith(jasmine.stringMatching('status: 199'));
|
|
||||||
|
|
||||||
|
(api as any).request('method', '/path')
|
||||||
|
.catch((err: string) => {
|
||||||
|
expect(err).toContain('failed');
|
||||||
|
expect(err).toContain('status: 199');
|
||||||
|
done();
|
||||||
|
});
|
||||||
requestHandler.done();
|
requestHandler.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if response statusCode is >=400', async () => {
|
it('should reject if response statusCode is >=400', done => {
|
||||||
const requestHandler = nock('https://api.github.com')
|
const requestHandler = nock('https://api.github.com')
|
||||||
.intercept('/path', 'method')
|
.intercept('/path', 'method')
|
||||||
.reply(400);
|
.reply(400);
|
||||||
const responsePromise = (api as any).request('method', '/path');
|
|
||||||
|
|
||||||
await expectAsync(responsePromise).toBeRejectedWith(jasmine.stringMatching('failed'));
|
|
||||||
await expectAsync(responsePromise).toBeRejectedWith(jasmine.stringMatching('status: 400'));
|
|
||||||
|
|
||||||
|
(api as any).request('method', '/path')
|
||||||
|
.catch((err: string) => {
|
||||||
|
expect(err).toContain('failed');
|
||||||
|
expect(err).toContain('status: 400');
|
||||||
|
done();
|
||||||
|
});
|
||||||
requestHandler.done();
|
requestHandler.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should include the response text in the rejection message', async () => {
|
it('should include the response text in the rejection message', done => {
|
||||||
const requestHandler = nock('https://api.github.com')
|
const requestHandler = nock('https://api.github.com')
|
||||||
.intercept('/path', 'method')
|
.intercept('/path', 'method')
|
||||||
.reply(500, 'Test');
|
.reply(500, 'Test');
|
||||||
const responsePromise = (api as any).request('method', '/path');
|
|
||||||
|
|
||||||
await expectAsync(responsePromise).toBeRejectedWith(jasmine.stringMatching('Test'));
|
|
||||||
|
|
||||||
|
(api as any).request('method', '/path')
|
||||||
|
.catch((err: string) => {
|
||||||
|
expect(err).toContain('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
requestHandler.done();
|
requestHandler.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve if returned statusCode is >=200 and <400', async () => {
|
it('should resolve if returned statusCode is >=200 and <400', done => {
|
||||||
const requestHandler = nock('https://api.github.com')
|
const requestHandler = nock('https://api.github.com')
|
||||||
.intercept('/path', 'method')
|
.intercept('/path', 'method')
|
||||||
.reply(200);
|
.reply(200);
|
||||||
|
|
||||||
await expectAsync((api as any).request('method', '/path')).toBeResolved();
|
(api as any).request('method', '/path').then(done);
|
||||||
requestHandler.done();
|
requestHandler.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should parse the response body into an object using \'JSON.parse\'', async () => {
|
it('should parse the response body into an object using \'JSON.parse\'', done => {
|
||||||
const requestHandler = nock('https://api.github.com')
|
const requestHandler = nock('https://api.github.com')
|
||||||
.intercept('/path', 'method')
|
.intercept('/path', 'method')
|
||||||
.reply(300, '{"foo": "bar"}');
|
.reply(300, '{"foo": "bar"}');
|
||||||
|
|
||||||
await expectAsync((api as any).request('method', '/path')).toBeResolvedTo({foo: 'bar'});
|
(api as any).request('method', '/path').then((data: any) => {
|
||||||
|
expect(data).toEqual({foo: 'bar'});
|
||||||
|
done();
|
||||||
|
});
|
||||||
requestHandler.done();
|
requestHandler.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reject if the response text is malformed JSON', async () => {
|
it('should reject if the response text is malformed JSON', done => {
|
||||||
const requestHandler = nock('https://api.github.com')
|
const requestHandler = nock('https://api.github.com')
|
||||||
.intercept('/path', 'method')
|
.intercept('/path', 'method')
|
||||||
.reply(300, '}');
|
.reply(300, '}');
|
||||||
|
|
||||||
await expectAsync((api as any).request('method', '/path')).toBeRejectedWithError(SyntaxError);
|
(api as any).request('method', '/path').catch((err: any) => {
|
||||||
|
expect(err).toEqual(jasmine.any(SyntaxError));
|
||||||
|
done();
|
||||||
|
});
|
||||||
requestHandler.done();
|
requestHandler.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Imports
|
// Imports
|
||||||
import {GithubApi} from '../../lib/common/github-api';
|
import {GithubApi} from '../../lib/common/github-api';
|
||||||
import {GithubPullRequests, PullRequest} from '../../lib/common/github-pull-requests';
|
import {GithubPullRequests} from '../../lib/common/github-pull-requests';
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
describe('GithubPullRequests', () => {
|
describe('GithubPullRequests', () => {
|
||||||
@ -47,21 +47,27 @@ describe('GithubPullRequests', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should make a POST request to Github with the correct pathname, params and data', () => {
|
it('should make a POST request to Github with the correct pathname, params and data', () => {
|
||||||
githubApi.post.and.resolveTo();
|
githubApi.post.and.callFake(() => Promise.resolve());
|
||||||
prs.addComment(42, 'body');
|
prs.addComment(42, 'body');
|
||||||
expect(githubApi.post).toHaveBeenCalledWith('/repos/foo/bar/issues/42/comments', null, {body: 'body'});
|
expect(githubApi.post).toHaveBeenCalledWith('/repos/foo/bar/issues/42/comments', null, {body: 'body'});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if the request fails', async () => {
|
it('should reject if the request fails', done => {
|
||||||
githubApi.post.and.rejectWith('Test');
|
githubApi.post.and.callFake(() => Promise.reject('Test'));
|
||||||
await expectAsync(prs.addComment(42, 'body')).toBeRejectedWith('Test');
|
prs.addComment(42, 'body').catch(err => {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with the data from the Github POST', async () => {
|
it('should resolve with the data from the Github POST', done => {
|
||||||
githubApi.post.and.resolveTo('Test');
|
githubApi.post.and.callFake(() => Promise.resolve('Test'));
|
||||||
await expectAsync(prs.addComment(42, 'body')).toBeResolvedTo('Test');
|
prs.addComment(42, 'body').then(data => {
|
||||||
|
expect(data).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -81,11 +87,13 @@ describe('GithubPullRequests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with the data returned from GitHub', async () => {
|
it('should resolve with the data returned from GitHub', done => {
|
||||||
const expected: any = {number: 42};
|
const expected: any = {number: 42};
|
||||||
githubApi.get.and.resolveTo(expected);
|
githubApi.get.and.callFake(() => Promise.resolve(expected));
|
||||||
|
prs.fetch(42).then(data => {
|
||||||
await expectAsync(prs.fetch(42)).toBeResolvedTo(expected);
|
expect(data).toEqual(expected);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -117,14 +125,9 @@ describe('GithubPullRequests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should forward the value returned by \'getPaginated()\'', async () => {
|
it('should forward the value returned by \'getPaginated()\'', () => {
|
||||||
const mockPrs: PullRequest[] = [
|
githubApi.getPaginated.and.returnValue('Test');
|
||||||
{number: 1, user: {login: 'foo'}, labels: []},
|
expect(prs.fetchAll() as any).toBe('Test');
|
||||||
{number: 2, user: {login: 'bar'}, labels: []},
|
|
||||||
];
|
|
||||||
|
|
||||||
githubApi.getPaginated.and.resolveTo(mockPrs);
|
|
||||||
expect(await prs.fetchAll()).toBe(mockPrs);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -144,11 +147,13 @@ describe('GithubPullRequests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with the data returned from GitHub', async () => {
|
it('should resolve with the data returned from GitHub', done => {
|
||||||
const expected: any = [{sha: 'ABCDE', filename: 'a/b/c'}, {sha: '12345', filename: 'x/y/z'}];
|
const expected: any = [{sha: 'ABCDE', filename: 'a/b/c'}, {sha: '12345', filename: 'x/y/z'}];
|
||||||
githubApi.getPaginated.and.resolveTo(expected);
|
githubApi.getPaginated.and.callFake(() => Promise.resolve(expected));
|
||||||
|
prs.fetchFiles(42).then(data => {
|
||||||
await expectAsync(prs.fetchFiles(42)).toBeResolvedTo(expected);
|
expect(data).toEqual(expected);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {GithubApi} from '../../lib/common/github-api';
|
import {GithubApi} from '../../lib/common/github-api';
|
||||||
import {GithubTeams, Team} from '../../lib/common/github-teams';
|
import {GithubTeams} from '../../lib/common/github-teams';
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
describe('GithubTeams', () => {
|
describe('GithubTeams', () => {
|
||||||
@ -16,7 +16,6 @@ describe('GithubTeams', () => {
|
|||||||
expect(() => new GithubTeams(githubApi, '')).
|
expect(() => new GithubTeams(githubApi, '')).
|
||||||
toThrowError('Missing or empty required parameter \'githubOrg\'!');
|
toThrowError('Missing or empty required parameter \'githubOrg\'!');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -34,14 +33,9 @@ describe('GithubTeams', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should forward the value returned by \'getPaginated()\'', async () => {
|
it('should forward the value returned by \'getPaginated()\'', () => {
|
||||||
const mockTeams: Team[] = [
|
githubApi.getPaginated.and.returnValue('Test');
|
||||||
{id: 1, slug: 'foo'},
|
expect(teams.fetchAll() as any).toBe('Test');
|
||||||
{id: 2, slug: 'bar'},
|
|
||||||
];
|
|
||||||
|
|
||||||
githubApi.getPaginated.and.resolveTo(mockTeams);
|
|
||||||
expect(await teams.fetchAll()).toBe(mockTeams);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -56,77 +50,100 @@ describe('GithubTeams', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should return a promise', () => {
|
it('should return a promise', () => {
|
||||||
githubApi.get.and.resolveTo();
|
githubApi.get.and.callFake(() => Promise.resolve());
|
||||||
const promise = teams.isMemberById('user', [1]);
|
const promise = teams.isMemberById('user', [1]);
|
||||||
expect(promise).toBeInstanceOf(Promise);
|
expect(promise).toEqual(jasmine.any(Promise));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with false if called with an empty array', async () => {
|
it('should resolve with false if called with an empty array', done => {
|
||||||
await expectAsync(teams.isMemberById('user', [])).toBeResolvedTo(false);
|
teams.isMemberById('user', []).then(isMember => {
|
||||||
expect(githubApi.get).not.toHaveBeenCalled();
|
expect(isMember).toBe(false);
|
||||||
|
expect(githubApi.get).not.toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should call \'get()\' with the correct pathname', async () => {
|
it('should call \'get()\' with the correct pathname', done => {
|
||||||
githubApi.get.and.resolveTo();
|
githubApi.get.and.callFake(() => Promise.resolve());
|
||||||
await teams.isMemberById('user', [1]);
|
teams.isMemberById('user', [1]).then(() => {
|
||||||
|
expect(githubApi.get).toHaveBeenCalledWith('/teams/1/memberships/user');
|
||||||
expect(githubApi.get).toHaveBeenCalledWith('/teams/1/memberships/user');
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with false if \'get()\' rejects', async () => {
|
it('should resolve with false if \'get()\' rejects', done => {
|
||||||
githubApi.get.and.rejectWith(null);
|
githubApi.get.and.callFake(() => Promise.reject(null));
|
||||||
|
teams.isMemberById('user', [1]).then(isMember => {
|
||||||
await expectAsync(teams.isMemberById('user', [1])).toBeResolvedTo(false);
|
expect(isMember).toBe(false);
|
||||||
expect(githubApi.get).toHaveBeenCalled();
|
expect(githubApi.get).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with false if the membership is not active', async () => {
|
it('should resolve with false if the membership is not active', done => {
|
||||||
githubApi.get.and.resolveTo({state: 'pending'});
|
githubApi.get.and.callFake(() => Promise.resolve({state: 'pending'}));
|
||||||
|
teams.isMemberById('user', [1]).then(isMember => {
|
||||||
await expectAsync(teams.isMemberById('user', [1])).toBeResolvedTo(false);
|
expect(isMember).toBe(false);
|
||||||
expect(githubApi.get).toHaveBeenCalled();
|
expect(githubApi.get).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with true if the membership is active', async () => {
|
it('should resolve with true if the membership is active', done => {
|
||||||
githubApi.get.and.resolveTo({state: 'active'});
|
githubApi.get.and.callFake(() => Promise.resolve({state: 'active'}));
|
||||||
await expectAsync(teams.isMemberById('user', [1])).toBeResolvedTo(true);
|
teams.isMemberById('user', [1]).then(isMember => {
|
||||||
|
expect(isMember).toBe(true);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should sequentially call \'get()\' until an active membership is found', async () => {
|
it('should sequentially call \'get()\' until an active membership is found', done => {
|
||||||
githubApi.get.
|
const trainedResponses: {[pathname: string]: Promise<{state: string}>} = {
|
||||||
withArgs('/teams/1/memberships/user').and.resolveTo({state: 'pending'}).
|
'/teams/1/memberships/user': Promise.resolve({state: 'pending'}),
|
||||||
withArgs('/teams/2/memberships/user').and.rejectWith(null).
|
'/teams/2/memberships/user': Promise.reject(null),
|
||||||
withArgs('/teams/3/memberships/user').and.resolveTo({state: 'active'});
|
'/teams/3/memberships/user': Promise.resolve({state: 'active'}),
|
||||||
|
};
|
||||||
|
githubApi.get.and.callFake((pathname: string) => trainedResponses[pathname]);
|
||||||
|
|
||||||
await expectAsync(teams.isMemberById('user', [1, 2, 3, 4])).toBeResolvedTo(true);
|
teams.isMemberById('user', [1, 2, 3, 4]).then(isMember => {
|
||||||
|
expect(isMember).toBe(true);
|
||||||
|
|
||||||
expect(githubApi.get).toHaveBeenCalledTimes(3);
|
expect(githubApi.get).toHaveBeenCalledTimes(3);
|
||||||
expect(githubApi.get.calls.argsFor(0)[0]).toBe('/teams/1/memberships/user');
|
expect(githubApi.get.calls.argsFor(0)[0]).toBe('/teams/1/memberships/user');
|
||||||
expect(githubApi.get.calls.argsFor(1)[0]).toBe('/teams/2/memberships/user');
|
expect(githubApi.get.calls.argsFor(1)[0]).toBe('/teams/2/memberships/user');
|
||||||
expect(githubApi.get.calls.argsFor(2)[0]).toBe('/teams/3/memberships/user');
|
expect(githubApi.get.calls.argsFor(2)[0]).toBe('/teams/3/memberships/user');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with false if no active membership is found', async () => {
|
it('should resolve with false if no active membership is found', done => {
|
||||||
githubApi.get.
|
const trainedResponses: {[pathname: string]: Promise<{state: string}>} = {
|
||||||
withArgs('/teams/1/memberships/user').and.resolveTo({state: 'pending'}).
|
'/teams/1/memberships/user': Promise.resolve({state: 'pending'}),
|
||||||
withArgs('/teams/2/memberships/user').and.rejectWith(null).
|
'/teams/2/memberships/user': Promise.reject(null),
|
||||||
withArgs('/teams/3/memberships/user').and.resolveTo({state: 'not active'}).
|
'/teams/3/memberships/user': Promise.resolve({state: 'not active'}),
|
||||||
withArgs('/teams/4/memberships/user').and.rejectWith(null);
|
'/teams/4/memberships/user': Promise.reject(null),
|
||||||
|
};
|
||||||
|
githubApi.get.and.callFake((pathname: string) => trainedResponses[pathname]);
|
||||||
|
|
||||||
await expectAsync(teams.isMemberById('user', [1, 2, 3, 4])).toBeResolvedTo(false);
|
teams.isMemberById('user', [1, 2, 3, 4]).then(isMember => {
|
||||||
|
expect(isMember).toBe(false);
|
||||||
|
|
||||||
expect(githubApi.get).toHaveBeenCalledTimes(4);
|
expect(githubApi.get).toHaveBeenCalledTimes(4);
|
||||||
expect(githubApi.get.calls.argsFor(0)[0]).toBe('/teams/1/memberships/user');
|
expect(githubApi.get.calls.argsFor(0)[0]).toBe('/teams/1/memberships/user');
|
||||||
expect(githubApi.get.calls.argsFor(1)[0]).toBe('/teams/2/memberships/user');
|
expect(githubApi.get.calls.argsFor(1)[0]).toBe('/teams/2/memberships/user');
|
||||||
expect(githubApi.get.calls.argsFor(2)[0]).toBe('/teams/3/memberships/user');
|
expect(githubApi.get.calls.argsFor(2)[0]).toBe('/teams/3/memberships/user');
|
||||||
expect(githubApi.get.calls.argsFor(3)[0]).toBe('/teams/4/memberships/user');
|
expect(githubApi.get.calls.argsFor(3)[0]).toBe('/teams/4/memberships/user');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -140,13 +157,14 @@ describe('GithubTeams', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
teams = new GithubTeams(githubApi, 'foo');
|
teams = new GithubTeams(githubApi, 'foo');
|
||||||
|
|
||||||
teamsFetchAllSpy = spyOn(teams, 'fetchAll').and.resolveTo([{id: 1, slug: 'team1'}, {id: 2, slug: 'team2'}]);
|
const mockResponse = Promise.resolve([{id: 1, slug: 'team1'}, {id: 2, slug: 'team2'}]);
|
||||||
|
teamsFetchAllSpy = spyOn(teams, 'fetchAll').and.returnValue(mockResponse);
|
||||||
teamsIsMemberByIdSpy = spyOn(teams, 'isMemberById');
|
teamsIsMemberByIdSpy = spyOn(teams, 'isMemberById');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return a promise', () => {
|
it('should return a promise', () => {
|
||||||
expect(teams.isMemberBySlug('user', ['team-slug'])).toBeInstanceOf(Promise);
|
expect(teams.isMemberBySlug('user', ['team-slug'])).toEqual(jasmine.any(Promise));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -156,46 +174,55 @@ describe('GithubTeams', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with false if \'fetchAll()\' rejects', async () => {
|
it('should resolve with false if \'fetchAll()\' rejects', done => {
|
||||||
teamsFetchAllSpy.and.rejectWith(null);
|
teamsFetchAllSpy.and.callFake(() => Promise.reject(null));
|
||||||
await expectAsync(teams.isMemberBySlug('user', ['team-slug'])).toBeResolvedTo(false);
|
teams.isMemberBySlug('user', ['team-slug']).then(isMember => {
|
||||||
|
expect(isMember).toBe(false);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should call \'isMemberById()\' with the correct params if no team is found', async () => {
|
it('should call \'isMemberById()\' with the correct params if no team is found', done => {
|
||||||
await teams.isMemberBySlug('user', ['no-match']);
|
teams.isMemberBySlug('user', ['no-match']).then(() => {
|
||||||
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('user', []);
|
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('user', []);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should call \'isMemberById()\' with the correct params if teams are found', async () => {
|
it('should call \'isMemberById()\' with the correct params if teams are found', done => {
|
||||||
await teams.isMemberBySlug('userA', ['team1']);
|
const spy = teamsIsMemberByIdSpy;
|
||||||
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('userA', [1]);
|
|
||||||
|
|
||||||
await teams.isMemberBySlug('userB', ['team2']);
|
Promise.all([
|
||||||
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('userB', [2]);
|
teams.isMemberBySlug('user', ['team1']).then(() => expect(spy).toHaveBeenCalledWith('user', [1])),
|
||||||
|
teams.isMemberBySlug('user', ['team2']).then(() => expect(spy).toHaveBeenCalledWith('user', [2])),
|
||||||
await teams.isMemberBySlug('userC', ['team1', 'team2']);
|
teams.isMemberBySlug('user', ['team1', 'team2']).then(() => expect(spy).toHaveBeenCalledWith('user', [1, 2])),
|
||||||
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('userC', [1, 2]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with false if \'isMemberById()\' rejects', async () => {
|
it('should resolve with false if \'isMemberById()\' rejects', done => {
|
||||||
teamsIsMemberByIdSpy.and.rejectWith(null);
|
teamsIsMemberByIdSpy.and.callFake(() => Promise.reject(null));
|
||||||
|
teams.isMemberBySlug('user', ['team1']).then(isMember => {
|
||||||
await expectAsync(teams.isMemberBySlug('user', ['team1'])).toBeResolvedTo(false);
|
expect(isMember).toBe(false);
|
||||||
expect(teamsIsMemberByIdSpy).toHaveBeenCalled();
|
expect(teamsIsMemberByIdSpy).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with the value \'isMemberById()\' resolves with', async () => {
|
it('should resolve with the value \'isMemberById()\' resolves with', async () => {
|
||||||
teamsIsMemberByIdSpy.and.resolveTo(true);
|
|
||||||
await expectAsync(teams.isMemberBySlug('userA', ['team1'])).toBeResolvedTo(true);
|
|
||||||
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('userA', [1]);
|
|
||||||
|
|
||||||
teamsIsMemberByIdSpy.and.resolveTo(false);
|
teamsIsMemberByIdSpy.and.callFake(() => Promise.resolve(true));
|
||||||
await expectAsync(teams.isMemberBySlug('userB', ['team1'])).toBeResolvedTo(false);
|
const isMember1 = await teams.isMemberBySlug('user', ['team1']);
|
||||||
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('userB', [1]);
|
expect(isMember1).toBe(true);
|
||||||
|
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('user', [1]);
|
||||||
|
|
||||||
|
teamsIsMemberByIdSpy.and.callFake(() => Promise.resolve(false));
|
||||||
|
const isMember2 = await teams.isMemberBySlug('user', ['team1']);
|
||||||
|
expect(isMember2).toBe(false);
|
||||||
|
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('user', [1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -9,8 +9,7 @@ import {Logger} from '../../lib/common/utils';
|
|||||||
import {BuildCreator} from '../../lib/preview-server/build-creator';
|
import {BuildCreator} from '../../lib/preview-server/build-creator';
|
||||||
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from '../../lib/preview-server/build-events';
|
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from '../../lib/preview-server/build-events';
|
||||||
import {PreviewServerError} from '../../lib/preview-server/preview-error';
|
import {PreviewServerError} from '../../lib/preview-server/preview-error';
|
||||||
import {customAsyncMatchers} from './jasmine-custom-async-matchers';
|
import {expectToBePreviewServerError} from './helpers';
|
||||||
|
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
describe('BuildCreator', () => {
|
describe('BuildCreator', () => {
|
||||||
@ -25,7 +24,6 @@ describe('BuildCreator', () => {
|
|||||||
const publicShaDir = path.join(publicPrDir, shortSha);
|
const publicShaDir = path.join(publicPrDir, shortSha);
|
||||||
let bc: BuildCreator;
|
let bc: BuildCreator;
|
||||||
|
|
||||||
beforeEach(() => jasmine.addAsyncMatchers(customAsyncMatchers));
|
|
||||||
beforeEach(() => bc = new BuildCreator(buildsDir));
|
beforeEach(() => bc = new BuildCreator(buildsDir));
|
||||||
|
|
||||||
|
|
||||||
@ -37,8 +35,8 @@ describe('BuildCreator', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should extend EventEmitter', () => {
|
it('should extend EventEmitter', () => {
|
||||||
expect(bc).toBeInstanceOf(BuildCreator);
|
expect(bc).toEqual(jasmine.any(BuildCreator));
|
||||||
expect(bc).toBeInstanceOf(EventEmitter);
|
expect(bc).toEqual(jasmine.any(EventEmitter));
|
||||||
|
|
||||||
expect(Object.getPrototypeOf(bc)).toBe(BuildCreator.prototype);
|
expect(Object.getPrototypeOf(bc)).toBe(BuildCreator.prototype);
|
||||||
});
|
});
|
||||||
@ -69,43 +67,47 @@ describe('BuildCreator', () => {
|
|||||||
const shaDir = isPublic ? publicShaDir : hiddenShaDir;
|
const shaDir = isPublic ? publicShaDir : hiddenShaDir;
|
||||||
|
|
||||||
|
|
||||||
it('should return a promise', async () => {
|
it('should return a promise', done => {
|
||||||
const promise = bc.create(pr, sha, archive, isPublic);
|
const promise = bc.create(pr, sha, archive, isPublic);
|
||||||
expect(promise).toBeInstanceOf(Promise);
|
promise.then(done); // Do not complete the test (and release the spies) synchronously
|
||||||
|
// to avoid running the actual `extractArchive()`.
|
||||||
|
|
||||||
// Do not complete the test (and release the spies) synchronously to avoid running the actual
|
expect(promise).toEqual(jasmine.any(Promise));
|
||||||
// `extractArchive()`.
|
|
||||||
await promise;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should update the PR\'s visibility first if necessary', async () => {
|
it('should update the PR\'s visibility first if necessary', done => {
|
||||||
await bc.create(pr, sha, archive, isPublic);
|
bcUpdatePrVisibilitySpy.and.callFake(() => expect(shellMkdirSpy).not.toHaveBeenCalled());
|
||||||
|
|
||||||
expect(bcUpdatePrVisibilitySpy).toHaveBeenCalledBefore(shellMkdirSpy);
|
bc.create(pr, sha, archive, isPublic).
|
||||||
expect(bcUpdatePrVisibilitySpy).toHaveBeenCalledWith(pr, isPublic);
|
then(() => {
|
||||||
expect(shellMkdirSpy).toHaveBeenCalled();
|
expect(bcUpdatePrVisibilitySpy).toHaveBeenCalledWith(pr, isPublic);
|
||||||
|
expect(shellMkdirSpy).toHaveBeenCalled();
|
||||||
|
}).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should create the build directory (and any parent directories)', async () => {
|
it('should create the build directory (and any parent directories)', done => {
|
||||||
await bc.create(pr, sha, archive, isPublic);
|
bc.create(pr, sha, archive, isPublic).
|
||||||
expect(shellMkdirSpy).toHaveBeenCalledWith('-p', shaDir);
|
then(() => expect(shellMkdirSpy).toHaveBeenCalledWith('-p', shaDir)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should extract the archive contents into the build directory', async () => {
|
it('should extract the archive contents into the build directory', done => {
|
||||||
await bc.create(pr, sha, archive, isPublic);
|
bc.create(pr, sha, archive, isPublic).
|
||||||
expect(bcExtractArchiveSpy).toHaveBeenCalledWith(archive, shaDir);
|
then(() => expect(bcExtractArchiveSpy).toHaveBeenCalledWith(archive, shaDir)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should emit a CreatedBuildEvent on success', async () => {
|
it('should emit a CreatedBuildEvent on success', done => {
|
||||||
let emitted = false;
|
let emitted = false;
|
||||||
|
|
||||||
bcEmitSpy.and.callFake((type: string, evt: CreatedBuildEvent) => {
|
bcEmitSpy.and.callFake((type: string, evt: CreatedBuildEvent) => {
|
||||||
expect(type).toBe(CreatedBuildEvent.type);
|
expect(type).toBe(CreatedBuildEvent.type);
|
||||||
expect(evt).toBeInstanceOf(CreatedBuildEvent);
|
expect(evt).toEqual(jasmine.any(CreatedBuildEvent));
|
||||||
expect(evt.pr).toBe(+pr);
|
expect(evt.pr).toBe(+pr);
|
||||||
expect(evt.sha).toBe(shortSha);
|
expect(evt.sha).toBe(shortSha);
|
||||||
expect(evt.isPublic).toBe(isPublic);
|
expect(evt.isPublic).toBe(isPublic);
|
||||||
@ -113,108 +115,130 @@ describe('BuildCreator', () => {
|
|||||||
emitted = true;
|
emitted = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
await bc.create(pr, sha, archive, isPublic);
|
bc.create(pr, sha, archive, isPublic).
|
||||||
expect(emitted).toBe(true);
|
then(() => expect(emitted).toBe(true)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('on error', () => {
|
describe('on error', () => {
|
||||||
|
let existsValues: {[dir: string]: boolean};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
bcExistsSpy.and.returnValue(false);
|
existsValues = {
|
||||||
|
[prDir]: false,
|
||||||
|
[shaDir]: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
bcExistsSpy.and.callFake((dir: string) => existsValues[dir]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should abort and skip further operations if changing the PR\'s visibility fails', async () => {
|
it('should abort and skip further operations if changing the PR\'s visibility fails', done => {
|
||||||
const mockError = new PreviewServerError(543, 'Test');
|
const mockError = new PreviewServerError(543, 'Test');
|
||||||
bcUpdatePrVisibilitySpy.and.rejectWith(mockError);
|
bcUpdatePrVisibilitySpy.and.callFake(() => Promise.reject(mockError));
|
||||||
|
|
||||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejectedWith(mockError);
|
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||||
|
expect(err).toBe(mockError);
|
||||||
|
|
||||||
expect(bcExistsSpy).not.toHaveBeenCalled();
|
expect(bcExistsSpy).not.toHaveBeenCalled();
|
||||||
expect(shellMkdirSpy).not.toHaveBeenCalled();
|
expect(shellMkdirSpy).not.toHaveBeenCalled();
|
||||||
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should abort and skip further operations if the build does already exist', async () => {
|
it('should abort and skip further operations if the build does already exist', done => {
|
||||||
bcExistsSpy.withArgs(shaDir).and.returnValue(true);
|
existsValues[shaDir] = true;
|
||||||
|
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejectedWithPreviewServerError(
|
const publicOrNot = isPublic ? 'public' : 'non-public';
|
||||||
409, `Request to overwrite existing ${isPublic ? '' : 'non-'}public directory: ${shaDir}`);
|
expectToBePreviewServerError(err, 409, `Request to overwrite existing ${publicOrNot} directory: ${shaDir}`);
|
||||||
|
expect(shellMkdirSpy).not.toHaveBeenCalled();
|
||||||
expect(shellMkdirSpy).not.toHaveBeenCalled();
|
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
||||||
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should detect existing build directory after visibility change', async () => {
|
it('should detect existing build directory after visibility change', done => {
|
||||||
bcUpdatePrVisibilitySpy.and.callFake(() => bcExistsSpy.and.returnValue(true));
|
bcUpdatePrVisibilitySpy.and.callFake(() => existsValues[prDir] = existsValues[shaDir] = true);
|
||||||
|
|
||||||
expect(bcExistsSpy(prDir)).toBe(false);
|
expect(bcExistsSpy(prDir)).toBe(false);
|
||||||
expect(bcExistsSpy(shaDir)).toBe(false);
|
expect(bcExistsSpy(shaDir)).toBe(false);
|
||||||
|
|
||||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejectedWithPreviewServerError(
|
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||||
409, `Request to overwrite existing ${isPublic ? '' : 'non-'}public directory: ${shaDir}`);
|
const publicOrNot = isPublic ? 'public' : 'non-public';
|
||||||
|
expectToBePreviewServerError(err, 409, `Request to overwrite existing ${publicOrNot} directory: ${shaDir}`);
|
||||||
expect(shellMkdirSpy).not.toHaveBeenCalled();
|
expect(shellMkdirSpy).not.toHaveBeenCalled();
|
||||||
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should abort and skip further operations if it fails to create the directories', async () => {
|
it('should abort and skip further operations if it fails to create the directories', done => {
|
||||||
shellMkdirSpy.and.throwError('');
|
shellMkdirSpy.and.throwError('');
|
||||||
|
bc.create(pr, sha, archive, isPublic).catch(() => {
|
||||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejected();
|
expect(shellMkdirSpy).toHaveBeenCalled();
|
||||||
|
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
||||||
expect(shellMkdirSpy).toHaveBeenCalled();
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
done();
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should abort and skip further operations if it fails to extract the archive', async () => {
|
it('should abort and skip further operations if it fails to extract the archive', done => {
|
||||||
|
bcExtractArchiveSpy.and.throwError('');
|
||||||
|
bc.create(pr, sha, archive, isPublic).catch(() => {
|
||||||
|
expect(shellMkdirSpy).toHaveBeenCalled();
|
||||||
|
expect(bcExtractArchiveSpy).toHaveBeenCalled();
|
||||||
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should delete the PR directory (for new PR)', done => {
|
||||||
|
bcExtractArchiveSpy.and.throwError('');
|
||||||
|
bc.create(pr, sha, archive, isPublic).catch(() => {
|
||||||
|
expect(shellRmSpy).toHaveBeenCalledWith('-rf', prDir);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should delete the SHA directory (for existing PR)', done => {
|
||||||
|
existsValues[prDir] = true;
|
||||||
bcExtractArchiveSpy.and.throwError('');
|
bcExtractArchiveSpy.and.throwError('');
|
||||||
|
|
||||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejected();
|
bc.create(pr, sha, archive, isPublic).catch(() => {
|
||||||
|
expect(shellRmSpy).toHaveBeenCalledWith('-rf', shaDir);
|
||||||
expect(shellMkdirSpy).toHaveBeenCalled();
|
done();
|
||||||
expect(bcExtractArchiveSpy).toHaveBeenCalled();
|
});
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should delete the PR directory (for new PR)', async () => {
|
it('should reject with an PreviewServerError', done => {
|
||||||
bcExtractArchiveSpy.and.throwError('');
|
|
||||||
|
|
||||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejected();
|
|
||||||
expect(shellRmSpy).toHaveBeenCalledWith('-rf', prDir);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should delete the SHA directory (for existing PR)', async () => {
|
|
||||||
bcExistsSpy.withArgs(prDir).and.returnValue(true);
|
|
||||||
bcExtractArchiveSpy.and.throwError('');
|
|
||||||
|
|
||||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejected();
|
|
||||||
expect(shellRmSpy).toHaveBeenCalledWith('-rf', shaDir);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should reject with an PreviewServerError', async () => {
|
|
||||||
// tslint:disable-next-line: no-string-throw
|
// tslint:disable-next-line: no-string-throw
|
||||||
shellMkdirSpy.and.callFake(() => { throw 'Test'; });
|
shellMkdirSpy.and.callFake(() => { throw 'Test'; });
|
||||||
|
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejectedWithPreviewServerError(
|
expectToBePreviewServerError(err, 500, `Error while creating preview at: ${shaDir}\nTest`);
|
||||||
500, `Error while creating preview at: ${shaDir}\nTest`);
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should pass PreviewServerError instances unmodified', async () => {
|
it('should pass PreviewServerError instances unmodified', done => {
|
||||||
shellMkdirSpy.and.callFake(() => { throw new PreviewServerError(543, 'Test'); });
|
shellMkdirSpy.and.callFake(() => { throw new PreviewServerError(543, 'Test'); });
|
||||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejectedWithPreviewServerError(543, 'Test');
|
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||||
|
expectToBePreviewServerError(err, 543, 'Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -241,12 +265,12 @@ describe('BuildCreator', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return a promise', async () => {
|
it('should return a promise', done => {
|
||||||
const promise = bc.updatePrVisibility(pr, true);
|
const promise = bc.updatePrVisibility(pr, true);
|
||||||
expect(promise).toBeInstanceOf(Promise);
|
promise.then(done); // Do not complete the test (and release the spies) synchronously
|
||||||
|
// to avoid running the actual `extractArchive()`.
|
||||||
|
|
||||||
// Do not complete the test (and release the spies) synchronously to avoid running the actual `extractArchive()`.
|
expect(promise).toEqual(jasmine.any(Promise));
|
||||||
await promise;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -255,53 +279,58 @@ describe('BuildCreator', () => {
|
|||||||
const newPrDir = makePublic ? publicPrDir : hiddenPrDir;
|
const newPrDir = makePublic ? publicPrDir : hiddenPrDir;
|
||||||
|
|
||||||
|
|
||||||
it('should rename the directory', async () => {
|
it('should rename the directory', done => {
|
||||||
await bc.updatePrVisibility(pr, makePublic);
|
bc.updatePrVisibility(pr, makePublic).
|
||||||
expect(shellMvSpy).toHaveBeenCalledWith(oldPrDir, newPrDir);
|
then(() => expect(shellMvSpy).toHaveBeenCalledWith(oldPrDir, newPrDir)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('when the visibility is updated', () => {
|
describe('when the visibility is updated', () => {
|
||||||
|
|
||||||
it('should resolve to true', async () => {
|
it('should resolve to true', done => {
|
||||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeResolvedTo(true);
|
bc.updatePrVisibility(pr, makePublic).
|
||||||
|
then(result => expect(result).toBe(true)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should rename the directory', async () => {
|
it('should rename the directory', done => {
|
||||||
await bc.updatePrVisibility(pr, makePublic);
|
bc.updatePrVisibility(pr, makePublic).
|
||||||
expect(shellMvSpy).toHaveBeenCalledWith(oldPrDir, newPrDir);
|
then(() => expect(shellMvSpy).toHaveBeenCalledWith(oldPrDir, newPrDir)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should emit a ChangedPrVisibilityEvent on success', async () => {
|
it('should emit a ChangedPrVisibilityEvent on success', done => {
|
||||||
let emitted = false;
|
let emitted = false;
|
||||||
|
|
||||||
bcEmitSpy.and.callFake((type: string, evt: ChangedPrVisibilityEvent) => {
|
bcEmitSpy.and.callFake((type: string, evt: ChangedPrVisibilityEvent) => {
|
||||||
expect(type).toBe(ChangedPrVisibilityEvent.type);
|
expect(type).toBe(ChangedPrVisibilityEvent.type);
|
||||||
expect(evt).toBeInstanceOf(ChangedPrVisibilityEvent);
|
expect(evt).toEqual(jasmine.any(ChangedPrVisibilityEvent));
|
||||||
expect(evt.pr).toBe(+pr);
|
expect(evt.pr).toBe(+pr);
|
||||||
expect(evt.shas).toBeInstanceOf(Array);
|
expect(evt.shas).toEqual(jasmine.any(Array));
|
||||||
expect(evt.isPublic).toBe(makePublic);
|
expect(evt.isPublic).toBe(makePublic);
|
||||||
|
|
||||||
emitted = true;
|
emitted = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
await bc.updatePrVisibility(pr, makePublic);
|
bc.updatePrVisibility(pr, makePublic).
|
||||||
expect(emitted).toBe(true);
|
then(() => expect(emitted).toBe(true)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should include all shas in the emitted event', async () => {
|
it('should include all shas in the emitted event', done => {
|
||||||
const shas = ['foo', 'bar', 'baz'];
|
const shas = ['foo', 'bar', 'baz'];
|
||||||
let emitted = false;
|
let emitted = false;
|
||||||
|
|
||||||
bcListShasByDate.and.resolveTo(shas);
|
bcListShasByDate.and.callFake(() => Promise.resolve(shas));
|
||||||
bcEmitSpy.and.callFake((type: string, evt: ChangedPrVisibilityEvent) => {
|
bcEmitSpy.and.callFake((type: string, evt: ChangedPrVisibilityEvent) => {
|
||||||
expect(bcListShasByDate).toHaveBeenCalledWith(newPrDir);
|
expect(bcListShasByDate).toHaveBeenCalledWith(newPrDir);
|
||||||
|
|
||||||
expect(type).toBe(ChangedPrVisibilityEvent.type);
|
expect(type).toBe(ChangedPrVisibilityEvent.type);
|
||||||
expect(evt).toBeInstanceOf(ChangedPrVisibilityEvent);
|
expect(evt).toEqual(jasmine.any(ChangedPrVisibilityEvent));
|
||||||
expect(evt.pr).toBe(+pr);
|
expect(evt.pr).toBe(+pr);
|
||||||
expect(evt.shas).toBe(shas);
|
expect(evt.shas).toBe(shas);
|
||||||
expect(evt.isPublic).toBe(makePublic);
|
expect(evt.isPublic).toBe(makePublic);
|
||||||
@ -309,82 +338,94 @@ describe('BuildCreator', () => {
|
|||||||
emitted = true;
|
emitted = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
await bc.updatePrVisibility(pr, makePublic);
|
bc.updatePrVisibility(pr, makePublic).
|
||||||
expect(emitted).toBe(true);
|
then(() => expect(emitted).toBe(true)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should do nothing if the visibility is already up-to-date', async () => {
|
it('should do nothing if the visibility is already up-to-date', done => {
|
||||||
bcExistsSpy.and.callFake((dir: string) => dir === newPrDir);
|
bcExistsSpy.and.callFake((dir: string) => dir === newPrDir);
|
||||||
|
bc.updatePrVisibility(pr, makePublic).
|
||||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeResolvedTo(false);
|
then(result => {
|
||||||
|
expect(result).toBe(false);
|
||||||
expect(shellMvSpy).not.toHaveBeenCalled();
|
expect(shellMvSpy).not.toHaveBeenCalled();
|
||||||
expect(bcListShasByDate).not.toHaveBeenCalled();
|
expect(bcListShasByDate).not.toHaveBeenCalled();
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
|
}).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should do nothing if the PR directory does not exist', async () => {
|
it('should do nothing if the PR directory does not exist', done => {
|
||||||
bcExistsSpy.and.returnValue(false);
|
bcExistsSpy.and.returnValue(false);
|
||||||
|
bc.updatePrVisibility(pr, makePublic).
|
||||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeResolvedTo(false);
|
then(result => {
|
||||||
|
expect(result).toBe(false);
|
||||||
expect(shellMvSpy).not.toHaveBeenCalled();
|
expect(shellMvSpy).not.toHaveBeenCalled();
|
||||||
expect(bcListShasByDate).not.toHaveBeenCalled();
|
expect(bcListShasByDate).not.toHaveBeenCalled();
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
|
}).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('on error', () => {
|
describe('on error', () => {
|
||||||
|
|
||||||
it('should abort and skip further operations if both directories exist', async () => {
|
it('should abort and skip further operations if both directories exist', done => {
|
||||||
bcExistsSpy.and.returnValue(true);
|
bcExistsSpy.and.returnValue(true);
|
||||||
|
bc.updatePrVisibility(pr, makePublic).catch(err => {
|
||||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeRejectedWithPreviewServerError(
|
expectToBePreviewServerError(err, 409,
|
||||||
409, `Request to move '${oldPrDir}' to existing directory '${newPrDir}'.`);
|
`Request to move '${oldPrDir}' to existing directory '${newPrDir}'.`);
|
||||||
|
expect(shellMvSpy).not.toHaveBeenCalled();
|
||||||
expect(shellMvSpy).not.toHaveBeenCalled();
|
expect(bcListShasByDate).not.toHaveBeenCalled();
|
||||||
expect(bcListShasByDate).not.toHaveBeenCalled();
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should abort and skip further operations if it fails to rename the directory', async () => {
|
it('should abort and skip further operations if it fails to rename the directory', done => {
|
||||||
shellMvSpy.and.throwError('');
|
shellMvSpy.and.throwError('');
|
||||||
|
bc.updatePrVisibility(pr, makePublic).catch(() => {
|
||||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeRejected();
|
expect(shellMvSpy).toHaveBeenCalled();
|
||||||
|
expect(bcListShasByDate).not.toHaveBeenCalled();
|
||||||
expect(shellMvSpy).toHaveBeenCalled();
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
expect(bcListShasByDate).not.toHaveBeenCalled();
|
done();
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should abort and skip further operations if it fails to list the SHAs', async () => {
|
it('should abort and skip further operations if it fails to list the SHAs', done => {
|
||||||
bcListShasByDate.and.throwError('');
|
bcListShasByDate.and.throwError('');
|
||||||
|
bc.updatePrVisibility(pr, makePublic).catch(() => {
|
||||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeRejected();
|
expect(shellMvSpy).toHaveBeenCalled();
|
||||||
|
expect(bcListShasByDate).toHaveBeenCalled();
|
||||||
expect(shellMvSpy).toHaveBeenCalled();
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
expect(bcListShasByDate).toHaveBeenCalled();
|
done();
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject with an PreviewServerError', async () => {
|
it('should reject with an PreviewServerError', done => {
|
||||||
// tslint:disable-next-line: no-string-throw
|
// tslint:disable-next-line: no-string-throw
|
||||||
shellMvSpy.and.callFake(() => { throw 'Test'; });
|
shellMvSpy.and.callFake(() => { throw 'Test'; });
|
||||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeRejectedWithPreviewServerError(
|
bc.updatePrVisibility(pr, makePublic).catch(err => {
|
||||||
500, `Error while making PR ${pr} ${makePublic ? 'public' : 'hidden'}.\nTest`);
|
expectToBePreviewServerError(err, 500,
|
||||||
|
`Error while making PR ${pr} ${makePublic ? 'public' : 'hidden'}.\nTest`);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should pass PreviewServerError instances unmodified', async () => {
|
it('should pass PreviewServerError instances unmodified', done => {
|
||||||
shellMvSpy.and.callFake(() => { throw new PreviewServerError(543, 'Test'); });
|
shellMvSpy.and.callFake(() => { throw new PreviewServerError(543, 'Test'); });
|
||||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeRejectedWithPreviewServerError(543, 'Test');
|
bc.updatePrVisibility(pr, makePublic).catch(err => {
|
||||||
|
expectToBePreviewServerError(err, 543, 'Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -402,14 +443,12 @@ describe('BuildCreator', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fsAccessCbs = [];
|
fsAccessCbs = [];
|
||||||
fsAccessSpy = spyOn(fs, 'access').and.callFake(
|
fsAccessSpy = spyOn(fs, 'access').and.callFake((_: string, cb: (v?: any) => void) => fsAccessCbs.push(cb));
|
||||||
((_: string, cb: (v?: any) => void) => fsAccessCbs.push(cb)) as unknown as typeof fs.access,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return a promise', () => {
|
it('should return a promise', () => {
|
||||||
expect((bc as any).exists('foo')).toBeInstanceOf(Promise);
|
expect((bc as any).exists('foo')).toEqual(jasmine.any(Promise));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -419,29 +458,25 @@ describe('BuildCreator', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with \'true\' if \'fs.access()\' succeeds', async () => {
|
it('should resolve with \'true\' if \'fs.access()\' succeeds', done => {
|
||||||
const existsPromises = [
|
Promise.
|
||||||
(bc as any).exists('foo'),
|
all([(bc as any).exists('foo'), (bc as any).exists('bar')]).
|
||||||
(bc as any).exists('bar'),
|
then(results => expect(results).toEqual([true, true])).
|
||||||
];
|
then(done);
|
||||||
|
|
||||||
fsAccessCbs[0]();
|
fsAccessCbs[0]();
|
||||||
fsAccessCbs[1](null);
|
fsAccessCbs[1](null);
|
||||||
|
|
||||||
await expectAsync(Promise.all(existsPromises)).toBeResolvedTo([true, true]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with \'false\' if \'fs.access()\' errors', async () => {
|
it('should resolve with \'false\' if \'fs.access()\' errors', done => {
|
||||||
const existsPromises = [
|
Promise.
|
||||||
(bc as any).exists('foo'),
|
all([(bc as any).exists('foo'), (bc as any).exists('bar')]).
|
||||||
(bc as any).exists('bar'),
|
then(results => expect(results).toEqual([false, false])).
|
||||||
];
|
then(done);
|
||||||
|
|
||||||
fsAccessCbs[0]('Error');
|
fsAccessCbs[0]('Error');
|
||||||
fsAccessCbs[1](new Error());
|
fsAccessCbs[1](new Error());
|
||||||
|
|
||||||
await expectAsync(Promise.all(existsPromises)).toBeResolvedTo([false, false]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -460,15 +495,12 @@ describe('BuildCreator', () => {
|
|||||||
consoleWarnSpy = spyOn(Logger.prototype, 'warn');
|
consoleWarnSpy = spyOn(Logger.prototype, 'warn');
|
||||||
shellChmodSpy = spyOn(shell, 'chmod');
|
shellChmodSpy = spyOn(shell, 'chmod');
|
||||||
shellRmSpy = spyOn(shell, 'rm');
|
shellRmSpy = spyOn(shell, 'rm');
|
||||||
cpExecSpy = spyOn(cp, 'exec').and.callFake(
|
cpExecSpy = spyOn(cp, 'exec').and.callFake((_: string, cb: (...args: any[]) => void) => cpExecCbs.push(cb));
|
||||||
((_: string, cb: (...args: any[]) => void) =>
|
|
||||||
cpExecCbs.push(cb)) as unknown as typeof cp.exec,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return a promise', () => {
|
it('should return a promise', () => {
|
||||||
expect((bc as any).extractArchive('foo', 'bar')).toBeInstanceOf(Promise);
|
expect((bc as any).extractArchive('foo', 'bar')).toEqual(jasmine.any(Promise));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -480,68 +512,78 @@ describe('BuildCreator', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should log (as a warning) any stderr output if extracting succeeded', async () => {
|
it('should log (as a warning) any stderr output if extracting succeeded', done => {
|
||||||
const extractPromise = (bc as any).extractArchive('foo', 'bar');
|
(bc as any).extractArchive('foo', 'bar').
|
||||||
|
then(() => expect(consoleWarnSpy).toHaveBeenCalledWith('This is stderr')).
|
||||||
|
then(done);
|
||||||
|
|
||||||
cpExecCbs[0](null, 'This is stdout', 'This is stderr');
|
cpExecCbs[0](null, 'This is stdout', 'This is stderr');
|
||||||
|
|
||||||
await expectAsync(extractPromise).toBeResolved();
|
|
||||||
expect(consoleWarnSpy).toHaveBeenCalledWith('This is stderr');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should make the build directory non-writable', async () => {
|
it('should make the build directory non-writable', done => {
|
||||||
const extractPromise = (bc as any).extractArchive('foo', 'bar');
|
(bc as any).extractArchive('foo', 'bar').
|
||||||
cpExecCbs[0]();
|
then(() => expect(shellChmodSpy).toHaveBeenCalledWith('-R', 'a-w', 'bar')).
|
||||||
|
then(done);
|
||||||
|
|
||||||
await expectAsync(extractPromise).toBeResolved();
|
cpExecCbs[0]();
|
||||||
expect(shellChmodSpy).toHaveBeenCalledWith('-R', 'a-w', 'bar');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should delete the build artifact file on success', async () => {
|
it('should delete the build artifact file on success', done => {
|
||||||
const extractPromise = (bc as any).extractArchive('input/file', 'output/dir');
|
(bc as any).extractArchive('input/file', 'output/dir').
|
||||||
cpExecCbs[0]();
|
then(() => expect(shellRmSpy).toHaveBeenCalledWith('-f', 'input/file')).
|
||||||
|
then(done);
|
||||||
|
|
||||||
await expectAsync(extractPromise).toBeResolved();
|
cpExecCbs[0]();
|
||||||
expect(shellRmSpy).toHaveBeenCalledWith('-f', 'input/file');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('on error', () => {
|
describe('on error', () => {
|
||||||
|
|
||||||
it('should abort and skip further operations if it fails to extract the archive', async () => {
|
it('should abort and skip further operations if it fails to extract the archive', done => {
|
||||||
const extractPromise = (bc as any).extractArchive('foo', 'bar');
|
(bc as any).extractArchive('foo', 'bar').catch((err: any) => {
|
||||||
|
expect(shellChmodSpy).not.toHaveBeenCalled();
|
||||||
|
expect(shellRmSpy).not.toHaveBeenCalled();
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
cpExecCbs[0]('Test');
|
cpExecCbs[0]('Test');
|
||||||
|
|
||||||
await expectAsync(extractPromise).toBeRejectedWith('Test');
|
|
||||||
expect(shellChmodSpy).not.toHaveBeenCalled();
|
|
||||||
expect(shellRmSpy).not.toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should abort and skip further operations if it fails to make non-writable', async () => {
|
it('should abort and skip further operations if it fails to make non-writable', done => {
|
||||||
// tslint:disable-next-line: no-string-throw
|
(bc as any).extractArchive('foo', 'bar').catch((err: any) => {
|
||||||
shellChmodSpy.and.callFake(() => { throw 'Test'; });
|
expect(shellChmodSpy).toHaveBeenCalled();
|
||||||
|
expect(shellRmSpy).not.toHaveBeenCalled();
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
shellChmodSpy.and.callFake(() => {
|
||||||
|
// tslint:disable-next-line: no-string-throw
|
||||||
|
throw 'Test';
|
||||||
|
});
|
||||||
|
|
||||||
const extractPromise = (bc as any).extractArchive('foo', 'bar');
|
|
||||||
cpExecCbs[0]();
|
cpExecCbs[0]();
|
||||||
|
|
||||||
await expectAsync(extractPromise).toBeRejectedWith('Test');
|
|
||||||
expect(shellChmodSpy).toHaveBeenCalled();
|
|
||||||
expect(shellRmSpy).not.toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should abort and reject if it fails to remove the build artifact file', async () => {
|
it('should abort and reject if it fails to remove the build artifact file', done => {
|
||||||
// tslint:disable-next-line: no-string-throw
|
(bc as any).extractArchive('foo', 'bar').catch((err: any) => {
|
||||||
shellRmSpy.and.callFake(() => { throw 'Test'; });
|
expect(shellChmodSpy).toHaveBeenCalled();
|
||||||
|
expect(shellRmSpy).toHaveBeenCalled();
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
shellRmSpy.and.callFake(() => {
|
||||||
|
// tslint:disable-next-line: no-string-throw
|
||||||
|
throw 'Test';
|
||||||
|
});
|
||||||
|
|
||||||
const extractPromise = (bc as any).extractArchive('foo', 'bar');
|
|
||||||
cpExecCbs[0]();
|
cpExecCbs[0]();
|
||||||
|
|
||||||
await expectAsync(extractPromise).toBeRejectedWith('Test');
|
|
||||||
expect(shellChmodSpy).toHaveBeenCalled();
|
|
||||||
expect(shellRmSpy).toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -558,54 +600,62 @@ describe('BuildCreator', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
shellLsSpy = spyOn(shell, 'ls').and.returnValue([] as unknown as shell.ShellArray);
|
shellLsSpy = spyOn(shell, 'ls').and.returnValue([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return a promise', async () => {
|
it('should return a promise', done => {
|
||||||
const promise = (bc as any).listShasByDate('input/dir');
|
const promise = (bc as any).listShasByDate('input/dir');
|
||||||
expect(promise).toBeInstanceOf(Promise);
|
promise.then(done); // Do not complete the test (and release the spies) synchronously
|
||||||
|
// to avoid running the actual `ls()`.
|
||||||
|
|
||||||
// Do not complete the test (and release the spies) synchronously to avoid running the actual `ls()`.
|
expect(promise).toEqual(jasmine.any(Promise));
|
||||||
await promise;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should `ls()` files with their metadata', async () => {
|
it('should `ls()` files with their metadata', done => {
|
||||||
await (bc as any).listShasByDate('input/dir');
|
(bc as any).listShasByDate('input/dir').
|
||||||
expect(shellLsSpy).toHaveBeenCalledWith('-l', 'input/dir');
|
then(() => expect(shellLsSpy).toHaveBeenCalledWith('-l', 'input/dir')).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if listing files fails', async () => {
|
it('should reject if listing files fails', done => {
|
||||||
shellLsSpy.and.rejectWith('Test');
|
shellLsSpy.and.callFake(() => Promise.reject('Test'));
|
||||||
await expectAsync((bc as any).listShasByDate('input/dir')).toBeRejectedWith('Test');
|
(bc as any).listShasByDate('input/dir').catch((err: string) => {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return the filenames', async () => {
|
it('should return the filenames', done => {
|
||||||
shellLsSpy.and.resolveTo([
|
shellLsSpy.and.callFake(() => Promise.resolve([
|
||||||
lsResult('foo', 100),
|
lsResult('foo', 100),
|
||||||
lsResult('bar', 200),
|
lsResult('bar', 200),
|
||||||
lsResult('baz', 300),
|
lsResult('baz', 300),
|
||||||
]);
|
]));
|
||||||
|
|
||||||
await expectAsync((bc as any).listShasByDate('input/dir')).toBeResolvedTo(['foo', 'bar', 'baz']);
|
(bc as any).listShasByDate('input/dir').
|
||||||
|
then((shas: string[]) => expect(shas).toEqual(['foo', 'bar', 'baz'])).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should sort by date', async () => {
|
it('should sort by date', done => {
|
||||||
shellLsSpy.and.resolveTo([
|
shellLsSpy.and.callFake(() => Promise.resolve([
|
||||||
lsResult('foo', 300),
|
lsResult('foo', 300),
|
||||||
lsResult('bar', 100),
|
lsResult('bar', 100),
|
||||||
lsResult('baz', 200),
|
lsResult('baz', 200),
|
||||||
]);
|
]));
|
||||||
|
|
||||||
await expectAsync((bc as any).listShasByDate('input/dir')).toBeResolvedTo(['bar', 'baz', 'foo']);
|
(bc as any).listShasByDate('input/dir').
|
||||||
|
then((shas: string[]) => expect(shas).toEqual(['bar', 'baz', 'foo'])).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should not break with ShellJS\' custom `sort()` method', async () => {
|
it('should not break with ShellJS\' custom `sort()` method', done => {
|
||||||
const mockArray = [
|
const mockArray = [
|
||||||
lsResult('foo', 300),
|
lsResult('foo', 300),
|
||||||
lsResult('bar', 100),
|
lsResult('bar', 100),
|
||||||
@ -613,21 +663,26 @@ describe('BuildCreator', () => {
|
|||||||
];
|
];
|
||||||
mockArray.sort = jasmine.createSpy('sort');
|
mockArray.sort = jasmine.createSpy('sort');
|
||||||
|
|
||||||
shellLsSpy.and.resolveTo(mockArray);
|
shellLsSpy.and.callFake(() => Promise.resolve(mockArray));
|
||||||
|
(bc as any).listShasByDate('input/dir').
|
||||||
await expectAsync((bc as any).listShasByDate('input/dir')).toBeResolvedTo(['bar', 'baz', 'foo']);
|
then((shas: string[]) => {
|
||||||
expect(mockArray.sort).not.toHaveBeenCalled();
|
expect(shas).toEqual(['bar', 'baz', 'foo']);
|
||||||
|
expect(mockArray.sort).not.toHaveBeenCalled();
|
||||||
|
}).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should only include directories', async () => {
|
it('should only include directories', done => {
|
||||||
shellLsSpy.and.resolveTo([
|
shellLsSpy.and.callFake(() => Promise.resolve([
|
||||||
lsResult('foo', 100),
|
lsResult('foo', 100),
|
||||||
lsResult('bar', 200, false),
|
lsResult('bar', 200, false),
|
||||||
lsResult('baz', 300),
|
lsResult('baz', 300),
|
||||||
]);
|
]));
|
||||||
|
|
||||||
await expectAsync((bc as any).listShasByDate('input/dir')).toBeResolvedTo(['foo', 'baz']);
|
(bc as any).listShasByDate('input/dir').
|
||||||
|
then((shas: string[]) => expect(shas).toEqual(['foo', 'baz'])).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -32,18 +32,18 @@ describe('BuildRetriever', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
api = new CircleCiApi('ORG', 'REPO', 'TOKEN');
|
api = new CircleCiApi('ORG', 'REPO', 'TOKEN');
|
||||||
spyOn(api, 'getBuildInfo').and.resolveTo(BUILD_INFO);
|
spyOn(api, 'getBuildInfo').and.callFake(() => Promise.resolve(BUILD_INFO));
|
||||||
getBuildArtifactUrlSpy = spyOn(api, 'getBuildArtifactUrl').and.resolveTo(BASE_URL + ARTIFACT_PATH);
|
getBuildArtifactUrlSpy = spyOn(api, 'getBuildArtifactUrl')
|
||||||
|
.and.callFake(() => Promise.resolve(BASE_URL + ARTIFACT_PATH));
|
||||||
|
|
||||||
WRITEFILE_RESULT = undefined;
|
WRITEFILE_RESULT = undefined;
|
||||||
writeFileSpy = spyOn(fs, 'writeFile').and.callFake(
|
writeFileSpy = spyOn(fs, 'writeFile').and.callFake(
|
||||||
((_path: string, _buffer: Buffer, callback: fs.NoParamCallback) =>
|
(_path: string, _buffer: Buffer, callback: (err?: any) => {}) => callback(WRITEFILE_RESULT),
|
||||||
callback(WRITEFILE_RESULT)) as typeof fs.writeFile,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
EXISTS_RESULT = false;
|
EXISTS_RESULT = false;
|
||||||
existsSpy = spyOn(fs, 'exists').and.callFake(
|
existsSpy = spyOn(fs, 'exists').and.callFake(
|
||||||
((_path, callback) => callback(EXISTS_RESULT)) as typeof fs.exists,
|
(_path: string, callback: (exists: boolean) => {}) => callback(EXISTS_RESULT),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -56,7 +56,6 @@ describe('BuildRetriever', () => {
|
|||||||
expect(() => new BuildRetriever(api, -1, DOWNLOAD_DIR))
|
expect(() => new BuildRetriever(api, -1, DOWNLOAD_DIR))
|
||||||
.toThrowError(`Invalid parameter "downloadSizeLimit" should be a number greater than 0.`);
|
.toThrowError(`Invalid parameter "downloadSizeLimit" should be a number greater than 0.`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail if the "downloadDir" is missing', () => {
|
it('should fail if the "downloadDir" is missing', () => {
|
||||||
expect(() => new BuildRetriever(api, MAX_DOWNLOAD_SIZE, ''))
|
expect(() => new BuildRetriever(api, MAX_DOWNLOAD_SIZE, ''))
|
||||||
.toThrowError(`Missing or empty required parameter 'downloadDir'!`);
|
.toThrowError(`Missing or empty required parameter 'downloadDir'!`);
|
||||||
@ -73,10 +72,14 @@ describe('BuildRetriever', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should error if it is not possible to extract the PR number from the branch', async () => {
|
it('should error if it is not possible to extract the PR number from the branch', async () => {
|
||||||
BUILD_INFO.branch = 'master';
|
|
||||||
const retriever = new BuildRetriever(api, MAX_DOWNLOAD_SIZE, DOWNLOAD_DIR);
|
const retriever = new BuildRetriever(api, MAX_DOWNLOAD_SIZE, DOWNLOAD_DIR);
|
||||||
|
try {
|
||||||
await expectAsync(retriever.getGithubInfo(12345)).toBeRejectedWithError('No PR found in branch field: master');
|
BUILD_INFO.branch = 'master';
|
||||||
|
await retriever.getGithubInfo(12345);
|
||||||
|
throw new Error('Exception Expected');
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.message).toEqual('No PR found in branch field: master');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -107,10 +110,12 @@ describe('BuildRetriever', () => {
|
|||||||
it('should fail if the artifact is too large', async () => {
|
it('should fail if the artifact is too large', async () => {
|
||||||
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(200, ARTIFACT_CONTENTS);
|
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(200, ARTIFACT_CONTENTS);
|
||||||
retriever = new BuildRetriever(api, 10, DOWNLOAD_DIR);
|
retriever = new BuildRetriever(api, 10, DOWNLOAD_DIR);
|
||||||
|
try {
|
||||||
await expectAsync(retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH)).
|
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
||||||
toBeRejectedWith(jasmine.objectContaining({status: 413}));
|
throw new Error('Exception Expected');
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.status).toEqual(413);
|
||||||
|
}
|
||||||
artifactRequest.done();
|
artifactRequest.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -138,40 +143,50 @@ describe('BuildRetriever', () => {
|
|||||||
artifactRequest.done();
|
artifactRequest.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail if the CircleCI API fails', async () => {
|
it('should fail if the CircleCI API fails', async () => {
|
||||||
getBuildArtifactUrlSpy.and.rejectWith('getBuildArtifactUrl failed');
|
try {
|
||||||
await expectAsync(retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH)).
|
getBuildArtifactUrlSpy.and.callFake(() => Promise.reject('getBuildArtifactUrl failed'));
|
||||||
toBeRejectedWithError('CircleCI artifact download failed (getBuildArtifactUrl failed)');
|
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
||||||
|
throw new Error('Exception Expected');
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.message).toEqual('CircleCI artifact download failed (getBuildArtifactUrl failed)');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail if the URL fetch errors', async () => {
|
it('should fail if the URL fetch errors', async () => {
|
||||||
// create a new handler that errors
|
// create a new handler that errors
|
||||||
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).replyWithError('Artifact Request Failed');
|
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).replyWithError('Artifact Request Failed');
|
||||||
|
try {
|
||||||
await expectAsync(retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH)).toBeRejectedWithError(
|
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
||||||
'CircleCI artifact download failed ' +
|
throw new Error('Exception Expected');
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.message).toEqual('CircleCI artifact download failed ' +
|
||||||
'(request to http://test.com/some/path/build.zip failed, reason: Artifact Request Failed)');
|
'(request to http://test.com/some/path/build.zip failed, reason: Artifact Request Failed)');
|
||||||
|
}
|
||||||
artifactRequest.done();
|
artifactRequest.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail if the URL fetch 404s', async () => {
|
it('should fail if the URL fetch 404s', async () => {
|
||||||
// create a new handler that errors
|
// create a new handler that errors
|
||||||
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(404, 'No such artifact');
|
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(404, 'No such artifact');
|
||||||
|
try {
|
||||||
await expectAsync(retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH)).
|
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
||||||
toBeRejectedWithError('CircleCI artifact download failed (Error 404 - Not Found)');
|
throw new Error('Exception Expected');
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.message).toEqual('CircleCI artifact download failed (Error 404 - Not Found)');
|
||||||
|
}
|
||||||
artifactRequest.done();
|
artifactRequest.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail if file write fails', async () => {
|
it('should fail if file write fails', async () => {
|
||||||
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(200, ARTIFACT_CONTENTS);
|
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(200, ARTIFACT_CONTENTS);
|
||||||
WRITEFILE_RESULT = 'Test Error';
|
try {
|
||||||
|
WRITEFILE_RESULT = 'Test Error';
|
||||||
await expectAsync(retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH)).
|
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
||||||
toBeRejectedWithError('CircleCI artifact download failed (Test Error)');
|
throw new Error('Exception Expected');
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.message).toEqual('CircleCI artifact download failed (Test Error)');
|
||||||
|
}
|
||||||
artifactRequest.done();
|
artifactRequest.done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -51,10 +51,7 @@ describe('BuildVerifier', () => {
|
|||||||
describe('getSignificantFilesChanged', () => {
|
describe('getSignificantFilesChanged', () => {
|
||||||
it('should return false if none of the fetched files match the given pattern', async () => {
|
it('should return false if none of the fetched files match the given pattern', async () => {
|
||||||
const fetchFilesSpy = spyOn(prs, 'fetchFiles');
|
const fetchFilesSpy = spyOn(prs, 'fetchFiles');
|
||||||
fetchFilesSpy.and.resolveTo([
|
fetchFilesSpy.and.callFake(() => Promise.resolve([{filename: 'a/b/c'}, {filename: 'd/e/f'}]));
|
||||||
{filename: 'a/b/c', sha: 'a1'},
|
|
||||||
{filename: 'd/e/f', sha: 'b2'},
|
|
||||||
]);
|
|
||||||
expect(await bv.getSignificantFilesChanged(777, /^x/)).toEqual(false);
|
expect(await bv.getSignificantFilesChanged(777, /^x/)).toEqual(false);
|
||||||
expect(fetchFilesSpy).toHaveBeenCalledWith(777);
|
expect(fetchFilesSpy).toHaveBeenCalledWith(777);
|
||||||
|
|
||||||
@ -81,30 +78,37 @@ describe('BuildVerifier', () => {
|
|||||||
user: {login: 'username'},
|
user: {login: 'username'},
|
||||||
};
|
};
|
||||||
|
|
||||||
prsFetchSpy = spyOn(GithubPullRequests.prototype, 'fetch').and.resolveTo(mockPrInfo);
|
prsFetchSpy = spyOn(GithubPullRequests.prototype, 'fetch').
|
||||||
teamsIsMemberBySlugSpy = spyOn(GithubTeams.prototype, 'isMemberBySlug').and.resolveTo(true);
|
and.callFake(() => Promise.resolve(mockPrInfo));
|
||||||
|
|
||||||
|
teamsIsMemberBySlugSpy = spyOn(GithubTeams.prototype, 'isMemberBySlug').
|
||||||
|
and.callFake(() => Promise.resolve(true));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return a promise', async () => {
|
it('should return a promise', done => {
|
||||||
const promise = bv.getPrIsTrusted(pr);
|
const promise = bv.getPrIsTrusted(pr);
|
||||||
expect(promise).toBeInstanceOf(Promise);
|
promise.then(done); // Do not complete the test (and release the spies) synchronously
|
||||||
|
// to avoid running the actual `GithubTeams#isMemberBySlug()`.
|
||||||
|
|
||||||
// Do not complete the test (and release the spies) synchronously to avoid running the actual
|
expect(promise).toEqual(jasmine.any(Promise));
|
||||||
// `GithubTeams#isMemberBySlug()`.
|
|
||||||
await promise;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should fetch the corresponding PR', async () => {
|
it('should fetch the corresponding PR', done => {
|
||||||
await bv.getPrIsTrusted(pr);
|
bv.getPrIsTrusted(pr).then(() => {
|
||||||
expect(prsFetchSpy).toHaveBeenCalledWith(pr);
|
expect(prsFetchSpy).toHaveBeenCalledWith(pr);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should fail if fetching the PR errors', async () => {
|
it('should fail if fetching the PR errors', done => {
|
||||||
prsFetchSpy.and.rejectWith('Test');
|
prsFetchSpy.and.callFake(() => Promise.reject('Test'));
|
||||||
await expectAsync(bv.getPrIsTrusted(pr)).toBeRejectedWith('Test');
|
bv.getPrIsTrusted(pr).catch(err => {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -113,14 +117,19 @@ describe('BuildVerifier', () => {
|
|||||||
beforeEach(() => mockPrInfo.labels.push({name: 'trusted: pr-label'}));
|
beforeEach(() => mockPrInfo.labels.push({name: 'trusted: pr-label'}));
|
||||||
|
|
||||||
|
|
||||||
it('should resolve to true', async () => {
|
it('should resolve to true', done => {
|
||||||
await expectAsync(bv.getPrIsTrusted(pr)).toBeResolvedTo(true);
|
bv.getPrIsTrusted(pr).then(isTrusted => {
|
||||||
|
expect(isTrusted).toBe(true);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should not try to verify the author\'s membership status', async () => {
|
it('should not try to verify the author\'s membership status', done => {
|
||||||
await expectAsync(bv.getPrIsTrusted(pr));
|
bv.getPrIsTrusted(pr).then(() => {
|
||||||
expect(teamsIsMemberBySlugSpy).not.toHaveBeenCalled();
|
expect(teamsIsMemberBySlugSpy).not.toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -128,27 +137,40 @@ describe('BuildVerifier', () => {
|
|||||||
|
|
||||||
describe('when the PR does not have the "trusted PR" label', () => {
|
describe('when the PR does not have the "trusted PR" label', () => {
|
||||||
|
|
||||||
it('should verify the PR author\'s membership in the specified teams', async () => {
|
it('should verify the PR author\'s membership in the specified teams', done => {
|
||||||
await bv.getPrIsTrusted(pr);
|
bv.getPrIsTrusted(pr).then(() => {
|
||||||
expect(teamsIsMemberBySlugSpy).toHaveBeenCalledWith('username', ['team1', 'team2']);
|
expect(teamsIsMemberBySlugSpy).toHaveBeenCalledWith('username', ['team1', 'team2']);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should fail if verifying membership errors', async () => {
|
it('should fail if verifying membership errors', done => {
|
||||||
teamsIsMemberBySlugSpy.and.rejectWith('Test');
|
teamsIsMemberBySlugSpy.and.callFake(() => Promise.reject('Test'));
|
||||||
await expectAsync(bv.getPrIsTrusted(pr)).toBeRejectedWith('Test');
|
bv.getPrIsTrusted(pr).catch(err => {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve to true if the PR\'s author is a member', async () => {
|
it('should resolve to true if the PR\'s author is a member', done => {
|
||||||
teamsIsMemberBySlugSpy.and.resolveTo(true);
|
teamsIsMemberBySlugSpy.and.callFake(() => Promise.resolve(true));
|
||||||
await expectAsync(bv.getPrIsTrusted(pr)).toBeResolvedTo(true);
|
|
||||||
|
bv.getPrIsTrusted(pr).then(isTrusted => {
|
||||||
|
expect(isTrusted).toBe(true);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve to false if the PR\'s author is not a member', async () => {
|
it('should resolve to false if the PR\'s author is not a member', done => {
|
||||||
teamsIsMemberBySlugSpy.and.resolveTo(false);
|
teamsIsMemberBySlugSpy.and.callFake(() => Promise.resolve(false));
|
||||||
await expectAsync(bv.getPrIsTrusted(pr)).toBeResolvedTo(false);
|
|
||||||
|
bv.getPrIsTrusted(pr).then(isTrusted => {
|
||||||
|
expect(isTrusted).toBe(false);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
import {PreviewServerError} from '../../lib/preview-server/preview-error';
|
||||||
|
|
||||||
|
export const expectToBePreviewServerError = (actual: PreviewServerError, status?: number, message?: string) => {
|
||||||
|
expect(actual).toEqual(jasmine.any(PreviewServerError));
|
||||||
|
if (status != null) {
|
||||||
|
expect(actual.status).toBe(status);
|
||||||
|
}
|
||||||
|
if (message != null) {
|
||||||
|
expect(actual.message).toBe(message);
|
||||||
|
}
|
||||||
|
};
|
@ -1,5 +0,0 @@
|
|||||||
declare module jasmine {
|
|
||||||
interface AsyncMatchers {
|
|
||||||
toBeRejectedWithPreviewServerError(status: number, message?: string | RegExp): Promise<void>;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
import {PreviewServerError} from '../../lib/preview-server/preview-error';
|
|
||||||
|
|
||||||
|
|
||||||
// Matchers
|
|
||||||
const toBeRejectedWithPreviewServerError: jasmine.CustomAsyncMatcherFactory = () => {
|
|
||||||
return {
|
|
||||||
async compare(actualPromise: Promise<never>, expectedStatus: number, expectedMessage?: string | RegExp) {
|
|
||||||
if (!(actualPromise instanceof Promise)) {
|
|
||||||
throw new Error(`Expected '${toBeRejectedWithPreviewServerError.name}()' to be called on a promise.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await actualPromise;
|
|
||||||
|
|
||||||
return {
|
|
||||||
pass: false,
|
|
||||||
message: `Expected a promise to be rejected with a '${PreviewServerError.name}', but it was resolved.`,
|
|
||||||
};
|
|
||||||
} catch (actualError) {
|
|
||||||
const actualPrintValue = stringify(actualError);
|
|
||||||
const expectedPrintValue =
|
|
||||||
stringify(new PreviewServerError(expectedStatus, expectedMessage && `${expectedMessage}`));
|
|
||||||
|
|
||||||
const pass = errorMatches(actualError, expectedStatus, expectedMessage);
|
|
||||||
const message =
|
|
||||||
`Expected a promise ${pass ? 'not ' : ''}to be rejected with ${expectedPrintValue}, but is was` +
|
|
||||||
`${pass ? '' : ` rejected with ${actualPrintValue}`}.`;
|
|
||||||
|
|
||||||
return {pass, message};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Helpers
|
|
||||||
function errorMatches(actualErr: unknown, expectedStatus: number, expectedMsg?: string | RegExp): boolean {
|
|
||||||
if (!(actualErr instanceof PreviewServerError)) return false;
|
|
||||||
if (actualErr.status !== expectedStatus) return false;
|
|
||||||
return messageMatches(actualErr.message, expectedMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
function messageMatches(actualMsg: string, expectedMsg?: string | RegExp): boolean {
|
|
||||||
if (typeof expectedMsg === 'undefined') return true;
|
|
||||||
if (typeof expectedMsg === 'string') return actualMsg === expectedMsg;
|
|
||||||
return expectedMsg.test(actualMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
function stringify(value: unknown): string {
|
|
||||||
if (value instanceof PreviewServerError) {
|
|
||||||
return `${PreviewServerError.name}(${value.status}${value.message ? `, ${value.message}` : ''})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return jasmine.pp(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Exports
|
|
||||||
export const customAsyncMatchers: jasmine.CustomAsyncMatcherFactories = {
|
|
||||||
toBeRejectedWithPreviewServerError,
|
|
||||||
};
|
|
@ -9,8 +9,8 @@ describe('PreviewServerError', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should extend Error', () => {
|
it('should extend Error', () => {
|
||||||
expect(err).toBeInstanceOf(PreviewServerError);
|
expect(err).toEqual(jasmine.any(PreviewServerError));
|
||||||
expect(err).toBeInstanceOf(Error);
|
expect(err).toEqual(jasmine.any(Error));
|
||||||
|
|
||||||
expect(Object.getPrototypeOf(err)).toBe(PreviewServerError.prototype);
|
expect(Object.getPrototypeOf(err)).toBe(PreviewServerError.prototype);
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Imports
|
// Imports
|
||||||
|
import * as express from 'express';
|
||||||
import * as http from 'http';
|
import * as http from 'http';
|
||||||
import * as supertest from 'supertest';
|
import * as supertest from 'supertest';
|
||||||
import {CircleCiApi} from '../../lib/common/circle-ci-api';
|
import {CircleCiApi} from '../../lib/common/circle-ci-api';
|
||||||
@ -133,7 +134,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
const buildCreator = jasmine.any(BuildCreator);
|
const buildCreator = jasmine.any(BuildCreator);
|
||||||
expect(usfCreateMiddlewareSpy).toHaveBeenCalledWith(buildRetriever, buildVerifier, buildCreator, defaultConfig);
|
expect(usfCreateMiddlewareSpy).toHaveBeenCalledWith(buildRetriever, buildVerifier, buildCreator, defaultConfig);
|
||||||
|
|
||||||
const middleware = usfCreateMiddlewareSpy.calls.mostRecent().returnValue;
|
const middleware: express.Express = usfCreateMiddlewareSpy.calls.mostRecent().returnValue;
|
||||||
expect(httpCreateServerSpy).toHaveBeenCalledWith(middleware);
|
expect(httpCreateServerSpy).toHaveBeenCalledWith(middleware);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -229,7 +230,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
expect(prsAddCommentSpy).toHaveBeenCalledTimes(2);
|
expect(prsAddCommentSpy).toHaveBeenCalledTimes(2);
|
||||||
expect(prs).toBe(allCalls[1].object);
|
expect(prs).toBe(allCalls[1].object);
|
||||||
expect(prs).toBeInstanceOf(GithubPullRequests);
|
expect(prs).toEqual(jasmine.any(GithubPullRequests));
|
||||||
expect(prs.repoSlug).toBe('organisation/repo');
|
expect(prs.repoSlug).toBe('organisation/repo');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -301,8 +302,9 @@ describe('PreviewServerFactory', () => {
|
|||||||
let bvGetSignificantFilesChangedSpy: jasmine.Spy;
|
let bvGetSignificantFilesChangedSpy: jasmine.Spy;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
bvGetPrIsTrustedSpy = spyOn(buildVerifier, 'getPrIsTrusted').and.resolveTo(true);
|
bvGetPrIsTrustedSpy = spyOn(buildVerifier, 'getPrIsTrusted').and.returnValue(Promise.resolve(true));
|
||||||
bvGetSignificantFilesChangedSpy = spyOn(buildVerifier, 'getSignificantFilesChanged').and.resolveTo(true);
|
bvGetSignificantFilesChangedSpy = spyOn(buildVerifier, 'getSignificantFilesChanged').
|
||||||
|
and.returnValue(Promise.resolve(true));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -329,7 +331,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should respond appropriately if the PR did not touch any significant files', async () => {
|
it('should respond appropriately if the PR did not touch any significant files', async () => {
|
||||||
bvGetSignificantFilesChangedSpy.and.resolveTo(false);
|
bvGetSignificantFilesChangedSpy.and.returnValue(Promise.resolve(false));
|
||||||
|
|
||||||
const expectedResponse = {canHavePublicPreview: false, reason: 'No significant files touched.'};
|
const expectedResponse = {canHavePublicPreview: false, reason: 'No significant files touched.'};
|
||||||
const expectedLog = `PR:${pr} - Cannot have a public preview, because it did not touch any significant files.`;
|
const expectedLog = `PR:${pr} - Cannot have a public preview, because it did not touch any significant files.`;
|
||||||
@ -343,7 +345,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should respond appropriately if the PR is not automatically verifiable as "trusted"', async () => {
|
it('should respond appropriately if the PR is not automatically verifiable as "trusted"', async () => {
|
||||||
bvGetPrIsTrustedSpy.and.resolveTo(false);
|
bvGetPrIsTrustedSpy.and.returnValue(Promise.resolve(false));
|
||||||
|
|
||||||
const expectedResponse = {canHavePublicPreview: false, reason: 'Not automatically verifiable as "trusted".'};
|
const expectedResponse = {canHavePublicPreview: false, reason: 'Not automatically verifiable as "trusted".'};
|
||||||
const expectedLog =
|
const expectedLog =
|
||||||
@ -370,7 +372,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should respond with error if `getSignificantFilesChanged()` fails', async () => {
|
it('should respond with error if `getSignificantFilesChanged()` fails', async () => {
|
||||||
bvGetSignificantFilesChangedSpy.and.rejectWith('getSignificantFilesChanged error');
|
bvGetSignificantFilesChangedSpy.and.callFake(() => Promise.reject('getSignificantFilesChanged error'));
|
||||||
|
|
||||||
await agent.get(url).expect(500, 'getSignificantFilesChanged error');
|
await agent.get(url).expect(500, 'getSignificantFilesChanged error');
|
||||||
expect(loggerErrorSpy).toHaveBeenCalledWith('Previewability check error', 'getSignificantFilesChanged error');
|
expect(loggerErrorSpy).toHaveBeenCalledWith('Previewability check error', 'getSignificantFilesChanged error');
|
||||||
@ -378,10 +380,11 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should respond with error if `getPrIsTrusted()` fails', async () => {
|
it('should respond with error if `getPrIsTrusted()` fails', async () => {
|
||||||
bvGetPrIsTrustedSpy.and.throwError('getPrIsTrusted error');
|
const error = new Error('getPrIsTrusted error');
|
||||||
|
bvGetPrIsTrustedSpy.and.callFake(() => { throw error; });
|
||||||
|
|
||||||
await agent.get(url).expect(500, 'getPrIsTrusted error');
|
await agent.get(url).expect(500, 'getPrIsTrusted error');
|
||||||
expect(loggerErrorSpy).toHaveBeenCalledWith('Previewability check error', new Error('getPrIsTrusted error'));
|
expect(loggerErrorSpy).toHaveBeenCalledWith('Previewability check error', error);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -494,7 +497,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
// Note it is important to put the `reject` into `and.callFake`;
|
// Note it is important to put the `reject` into `and.callFake`;
|
||||||
// If you just `and.returnValue` the rejected promise
|
// If you just `and.returnValue` the rejected promise
|
||||||
// then you get an "unhandled rejection" message in the console.
|
// then you get an "unhandled rejection" message in the console.
|
||||||
getGithubInfoSpy.and.rejectWith('Test Error');
|
getGithubInfoSpy.and.callFake(() => Promise.reject('Test Error'));
|
||||||
await agent.post(URL).send(BASIC_PAYLOAD).expect(500, 'Test Error');
|
await agent.post(URL).send(BASIC_PAYLOAD).expect(500, 'Test Error');
|
||||||
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
||||||
expect(downloadBuildArtifactSpy).not.toHaveBeenCalled();
|
expect(downloadBuildArtifactSpy).not.toHaveBeenCalled();
|
||||||
@ -515,7 +518,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should fail if the artifact fetch request fails', async () => {
|
it('should fail if the artifact fetch request fails', async () => {
|
||||||
downloadBuildArtifactSpy.and.rejectWith('Test Error');
|
downloadBuildArtifactSpy.and.callFake(() => Promise.reject('Test Error'));
|
||||||
await agent.post(URL).send(BASIC_PAYLOAD).expect(500, 'Test Error');
|
await agent.post(URL).send(BASIC_PAYLOAD).expect(500, 'Test Error');
|
||||||
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
||||||
expect(downloadBuildArtifactSpy).toHaveBeenCalled();
|
expect(downloadBuildArtifactSpy).toHaveBeenCalled();
|
||||||
@ -524,7 +527,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should fail if verifying the PR fails', async () => {
|
it('should fail if verifying the PR fails', async () => {
|
||||||
getPrIsTrustedSpy.and.rejectWith('Test Error');
|
getPrIsTrustedSpy.and.callFake(() => Promise.reject('Test Error'));
|
||||||
await agent.post(URL).send(BASIC_PAYLOAD).expect(500, 'Test Error');
|
await agent.post(URL).send(BASIC_PAYLOAD).expect(500, 'Test Error');
|
||||||
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
||||||
expect(downloadBuildArtifactSpy).toHaveBeenCalled();
|
expect(downloadBuildArtifactSpy).toHaveBeenCalled();
|
||||||
@ -533,7 +536,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should fail if creating the preview build fails', async () => {
|
it('should fail if creating the preview build fails', async () => {
|
||||||
createBuildSpy.and.rejectWith('Test Error');
|
createBuildSpy.and.callFake(() => Promise.reject('Test Error'));
|
||||||
await agent.post(URL).send(BASIC_PAYLOAD).expect(500, 'Test Error');
|
await agent.post(URL).send(BASIC_PAYLOAD).expect(500, 'Test Error');
|
||||||
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
||||||
expect(downloadBuildArtifactSpy).toHaveBeenCalled();
|
expect(downloadBuildArtifactSpy).toHaveBeenCalled();
|
||||||
@ -602,7 +605,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should propagate errors from BuildVerifier', async () => {
|
it('should propagate errors from BuildVerifier', async () => {
|
||||||
bvGetPrIsTrustedSpy.and.rejectWith('Test');
|
bvGetPrIsTrustedSpy.and.callFake(() => Promise.reject('Test'));
|
||||||
|
|
||||||
await createRequest(+pr).expect(500, 'Test');
|
await createRequest(+pr).expect(500, 'Test');
|
||||||
|
|
||||||
@ -612,9 +615,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should call \'BuildCreator#updatePrVisibility()\' with the correct arguments', async () => {
|
it('should call \'BuildCreator#updatePrVisibility()\' with the correct arguments', async () => {
|
||||||
bvGetPrIsTrustedSpy.
|
bvGetPrIsTrustedSpy.and.callFake((pr2: number) => Promise.resolve(pr2 === 42));
|
||||||
withArgs(24).and.resolveTo(false).
|
|
||||||
withArgs(42).and.resolveTo(true);
|
|
||||||
|
|
||||||
await createRequest(24);
|
await createRequest(24);
|
||||||
expect(bcUpdatePrVisibilitySpy).toHaveBeenCalledWith(24, false);
|
expect(bcUpdatePrVisibilitySpy).toHaveBeenCalledWith(24, false);
|
||||||
@ -625,7 +626,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should propagate errors from BuildCreator', async () => {
|
it('should propagate errors from BuildCreator', async () => {
|
||||||
bcUpdatePrVisibilitySpy.and.rejectWith('Test');
|
bcUpdatePrVisibilitySpy.and.callFake(() => Promise.reject('Test'));
|
||||||
await createRequest(+pr).expect(500, 'Test');
|
await createRequest(+pr).expect(500, 'Test');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -633,9 +634,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
describe('on success', () => {
|
describe('on success', () => {
|
||||||
|
|
||||||
it('should respond with 200 (action: undefined)', async () => {
|
it('should respond with 200 (action: undefined)', async () => {
|
||||||
bvGetPrIsTrustedSpy.
|
bvGetPrIsTrustedSpy.and.returnValues(Promise.resolve(true), Promise.resolve(false));
|
||||||
withArgs(2).and.resolveTo(false).
|
|
||||||
withArgs(4).and.resolveTo(true);
|
|
||||||
|
|
||||||
const reqs = [4, 2].map(num => createRequest(num).expect(200, http.STATUS_CODES[200]));
|
const reqs = [4, 2].map(num => createRequest(num).expect(200, http.STATUS_CODES[200]));
|
||||||
await Promise.all(reqs);
|
await Promise.all(reqs);
|
||||||
@ -643,9 +642,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should respond with 200 (action: labeled)', async () => {
|
it('should respond with 200 (action: labeled)', async () => {
|
||||||
bvGetPrIsTrustedSpy.
|
bvGetPrIsTrustedSpy.and.returnValues(Promise.resolve(true), Promise.resolve(false));
|
||||||
withArgs(2).and.resolveTo(false).
|
|
||||||
withArgs(4).and.resolveTo(true);
|
|
||||||
|
|
||||||
const reqs = [4, 2].map(num => createRequest(num, 'labeled').expect(200, http.STATUS_CODES[200]));
|
const reqs = [4, 2].map(num => createRequest(num, 'labeled').expect(200, http.STATUS_CODES[200]));
|
||||||
await Promise.all(reqs);
|
await Promise.all(reqs);
|
||||||
@ -653,9 +650,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should respond with 200 (action: unlabeled)', async () => {
|
it('should respond with 200 (action: unlabeled)', async () => {
|
||||||
bvGetPrIsTrustedSpy.
|
bvGetPrIsTrustedSpy.and.returnValues(Promise.resolve(true), Promise.resolve(false));
|
||||||
withArgs(2).and.resolveTo(false).
|
|
||||||
withArgs(4).and.resolveTo(true);
|
|
||||||
|
|
||||||
const reqs = [4, 2].map(num => createRequest(num, 'unlabeled').expect(200, http.STATUS_CODES[200]));
|
const reqs = [4, 2].map(num => createRequest(num, 'unlabeled').expect(200, http.STATUS_CODES[200]));
|
||||||
await Promise.all(reqs);
|
await Promise.all(reqs);
|
||||||
|
@ -39,7 +39,7 @@ describe('preview-server/utils', () => {
|
|||||||
throwRequestError(505, 'ERROR MESSAGE', request);
|
throwRequestError(505, 'ERROR MESSAGE', request);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
caught = true;
|
caught = true;
|
||||||
expect(error).toBeInstanceOf(PreviewServerError);
|
expect(error).toEqual(jasmine.any(PreviewServerError));
|
||||||
expect(error.status).toEqual(505);
|
expect(error.status).toEqual(505);
|
||||||
expect(error.message).toEqual(`ERROR MESSAGE in request: POST some.domain.com/path "The request body"`);
|
expect(error.message).toEqual(`ERROR MESSAGE in request: POST some.domain.com/path "The request body"`);
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -8,32 +8,10 @@ exitCode=0
|
|||||||
|
|
||||||
|
|
||||||
# Helpers
|
# Helpers
|
||||||
function checkCert {
|
|
||||||
local certPath=$1
|
|
||||||
|
|
||||||
if [[ ! -f "$certPath" ]]; then
|
|
||||||
echo "Certificate '$certPath' does not exist. Skipping expiration check..."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
openssl x509 -checkend 0 -in "$certPath" -noout > /dev/null
|
|
||||||
reportStatus "Certificate '$certPath'"
|
|
||||||
|
|
||||||
if [[ $? -ne 0 ]]; then
|
|
||||||
echo " [WARN]"
|
|
||||||
echo " If you did not provide the certificate explicitly, try running the"
|
|
||||||
echo " 'docker build' command again with the '--no-cache' option to generate"
|
|
||||||
echo " a new self-signed certificate."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function reportStatus {
|
function reportStatus {
|
||||||
local lastExitCode=$?
|
local lastExitCode=$?
|
||||||
|
|
||||||
echo "$1: $([[ $lastExitCode -eq 0 ]] && echo OK || echo NOT OK)"
|
echo "$1: $([[ $lastExitCode -eq 0 ]] && echo OK || echo NOT OK)"
|
||||||
[[ $lastExitCode -eq 0 ]] || exitCode=1
|
[[ $lastExitCode -eq 0 ]] || exitCode=1
|
||||||
|
|
||||||
return $lastExitCode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -50,16 +28,6 @@ for s in ${services[@]}; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
|
|
||||||
# Check SSL/TLS certificates expiration
|
|
||||||
certs=(
|
|
||||||
"$AIO_LOCALCERTS_DIR/$AIO_DOMAIN_NAME.crt"
|
|
||||||
"$TEST_AIO_LOCALCERTS_DIR/$TEST_AIO_DOMAIN_NAME.crt"
|
|
||||||
)
|
|
||||||
for c in ${certs[@]}; do
|
|
||||||
checkCert $c
|
|
||||||
done
|
|
||||||
|
|
||||||
|
|
||||||
# Check servers
|
# Check servers
|
||||||
origins=(
|
origins=(
|
||||||
http://$AIO_PREVIEW_SERVER_HOSTNAME:$AIO_PREVIEW_SERVER_PORT
|
http://$AIO_PREVIEW_SERVER_HOSTNAME:$AIO_PREVIEW_SERVER_PORT
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
## Create `aio-builds` persistent disk (if not already exists)
|
## Create `aio-builds` persistent disk (if not already exists)
|
||||||
- Follow instructions [here](https://cloud.google.com/compute/docs/disks/add-persistent-disk#create_disk).
|
- Follow instructions [here](https://cloud.google.com/compute/docs/disks/add-persistent-disk#create_disk).
|
||||||
- `sudo mkfs.ext4 -m 0 -E lazy_itable_init=0,lazy_journal_init=0,discard /dev/disk/by-id/google-aio-builds`
|
- `sudo mkfs.ext4 -F -E lazy_itable_init=0,lazy_journal_init=0,discard /dev/disk/by-id/google-aio-builds`
|
||||||
|
|
||||||
|
|
||||||
## Mount disk
|
## Mount disk
|
||||||
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
## Mount disk on boot
|
## Mount disk on boot
|
||||||
- Run:
|
- Run:
|
||||||
```sh
|
```
|
||||||
echo UUID=`sudo blkid -s UUID -o value /dev/disk/by-id/google-aio-builds` \
|
echo UUID=`sudo blkid -s UUID -o value /dev/disk/by-id/google-aio-builds` \
|
||||||
/mnt/disks/aio-builds ext4 defaults,discard,nofail 0 2 | sudo tee -a /etc/fstab
|
/mnt/disks/aio-builds ext4 discard,defaults,nofail 0 2 | sudo tee -a /etc/fstab
|
||||||
```
|
```
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
# VM setup - Create docker image
|
# VM setup - Create docker image
|
||||||
|
|
||||||
|
|
||||||
## Install git, Node.js and yarn
|
## Install node and yarn
|
||||||
- `sudo apt-get update`
|
- Install [nvm](https://github.com/creationix/nvm#installation).
|
||||||
- `sudo apt-get install -y git`
|
- Install node.js: `nvm install 8`
|
||||||
- Install the latest stable version of [Node.js](https://nodejs.org/en/download).
|
- Install yarn: `npm -g install yarn`
|
||||||
- Install the latest stable version of [yarn](https://classic.yarnpkg.com/en/docs/install).
|
|
||||||
|
|
||||||
|
|
||||||
## Checkout repository
|
## Checkout repository
|
||||||
@ -17,11 +16,7 @@
|
|||||||
- You can overwrite the default environment variables inside the image, by passing new values using
|
- You can overwrite the default environment variables inside the image, by passing new values using
|
||||||
`--build-arg`.
|
`--build-arg`.
|
||||||
|
|
||||||
**Note 1:** The script has to execute docker commands with `sudo`.
|
**Note:** The script has to execute docker commands with `sudo`.
|
||||||
|
|
||||||
**Note 2:**
|
|
||||||
The script has to execute `yarn` commands, so make sure `yarn` is on the `PATH` when invoking the
|
|
||||||
script.
|
|
||||||
|
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
@ -31,7 +26,7 @@ The following commands would create a docker image from GitHub repo `foo/bar` to
|
|||||||
|
|
||||||
- `git clone https://github.com/foo/bar.git foobar`
|
- `git clone https://github.com/foo/bar.git foobar`
|
||||||
- Run:
|
- Run:
|
||||||
```sh
|
```
|
||||||
./foobar/aio-builds-setup/scripts/create-image.sh foobar-builds \
|
./foobar/aio-builds-setup/scripts/create-image.sh foobar-builds \
|
||||||
--build-arg AIO_REPO_SLUG=foo/bar \
|
--build-arg AIO_REPO_SLUG=foo/bar \
|
||||||
--build-arg AIO_DOMAIN_NAME=foobar-builds.io \
|
--build-arg AIO_DOMAIN_NAME=foobar-builds.io \
|
||||||
|
@ -3,17 +3,24 @@
|
|||||||
|
|
||||||
## Install docker
|
## Install docker
|
||||||
|
|
||||||
Official installation instructions: https://docs.docker.com/engine/install
|
_Debian (jessie):_
|
||||||
Example:
|
- `sudo apt-get update`
|
||||||
|
- `sudo apt-get install -y apt-transport-https ca-certificates curl git software-properties-common`
|
||||||
|
- `curl -fsSL https://apt.dockerproject.org/gpg | sudo apt-key add -`
|
||||||
|
- `apt-key fingerprint 58118E89F3A912897C070ADBF76221572C52609D`
|
||||||
|
- `sudo add-apt-repository "deb https://apt.dockerproject.org/repo/ debian-$(lsb_release -cs) main"`
|
||||||
|
- `sudo apt-get update`
|
||||||
|
- `sudo apt-get -y install docker-engine`
|
||||||
|
|
||||||
_Debian (buster):_
|
_Ubuntu (16.04):_
|
||||||
- `sudo apt-get update`
|
- `sudo apt-get update`
|
||||||
- `sudo apt-get install -y apt-transport-https ca-certificates curl gnupg-agent software-properties-common`
|
- `sudo apt-get install -y curl git linux-image-extra-$(uname -r) linux-image-extra-virtual`
|
||||||
- `curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -`
|
- `sudo apt-get install -y apt-transport-https ca-certificates`
|
||||||
- `sudo apt-key fingerprint 0EBFCD88`
|
- `curl -fsSL https://yum.dockerproject.org/gpg | sudo apt-key add -`
|
||||||
- `sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"`
|
- `apt-key fingerprint 58118E89F3A912897C070ADBF76221572C52609D`
|
||||||
|
- `sudo add-apt-repository "deb https://apt.dockerproject.org/repo/ ubuntu-$(lsb_release -cs) main"`
|
||||||
- `sudo apt-get update`
|
- `sudo apt-get update`
|
||||||
- `sudo apt-get -y install docker-ce docker-ce-cli containerd.io`
|
- `sudo apt-get -y install docker-engine`
|
||||||
|
|
||||||
|
|
||||||
## Start the docker
|
## Start the docker
|
||||||
|
@ -8,16 +8,16 @@ VM host to update the preview server based on changes in the source code.
|
|||||||
|
|
||||||
The script will pull the latest changes from the origin's master branch and examine if there have
|
The script will pull the latest changes from the origin's master branch and examine if there have
|
||||||
been any changes in files inside the preview server source code directory (see below). If there are,
|
been any changes in files inside the preview server source code directory (see below). If there are,
|
||||||
it will create a new image and verify that it works as expected. Finally, it will stop and remove
|
it will create a new image and verify that is works as expected. Finally, it will stop and remove
|
||||||
the old docker container and image, create a new container based on the new image and start it.
|
the old docker container and image, create a new container based on the new image and start it.
|
||||||
|
|
||||||
The script assumes that the preview server source code is in the repository's
|
The script assumes that the preview server source code is in the repository's
|
||||||
`aio/aio-builds-setup/` directory and expects the following inputs:
|
`aio/aio-builds-setup/` directory and expects the following inputs:
|
||||||
|
|
||||||
- **$1**: `HOST_REPO_DIR`
|
- **$1**: `HOST_REPO_DIR`
|
||||||
- **$2**: `HOST_SECRETS_DIR`
|
- **$2**: `HOST_LOCALCERTS_DIR`
|
||||||
- **$3**: `HOST_BUILDS_DIR`
|
- **$3**: `HOST_SECRETS_DIR`
|
||||||
- **$4**: `HOST_LOCALCERTS_DIR`
|
- **$4**: `HOST_BUILDS_DIR`
|
||||||
- **$5**: `HOST_LOGS_DIR`
|
- **$5**: `HOST_LOGS_DIR`
|
||||||
|
|
||||||
See [here](vm-setup--create-host-dirs-and-files.md) for more info on what each input directory is
|
See [here](vm-setup--create-host-dirs-and-files.md) for more info on what each input directory is
|
||||||
@ -25,38 +25,28 @@ used for.
|
|||||||
|
|
||||||
**Note 1:** The script has to execute docker commands with `sudo`.
|
**Note 1:** The script has to execute docker commands with `sudo`.
|
||||||
|
|
||||||
**Note 2:**
|
**Note 2:** Make sure the user that executes the script has access to update the repository
|
||||||
The script has to execute `yarn` commands, so make sure `yarn` is on the `PATH` when invoking the
|
|
||||||
script.
|
|
||||||
|
|
||||||
**Note 3:** Make sure the user that executes the script has access to update the repository.
|
|
||||||
|
|
||||||
|
|
||||||
## Run the script manually
|
## Run the script manually
|
||||||
You may choose to manually run the script, when necessary. Example:
|
You may choose to manually run the script, when necessary. Example:
|
||||||
|
|
||||||
```sh
|
```
|
||||||
update-preview-server.sh \
|
update-preview-server.sh \
|
||||||
/path/to/repo \
|
/path/to/repo \
|
||||||
|
/path/to/localcerts \
|
||||||
/path/to/secrets \
|
/path/to/secrets \
|
||||||
/path/to/builds \
|
/path/to/builds \
|
||||||
/path/to/localcerts \
|
|
||||||
/path/to/logs
|
/path/to/logs
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Run the script automatically
|
## Run the script automatically
|
||||||
You may choose to automatically trigger the script, e.g. using a cronjob. For example, the following
|
You may choose to automatically trigger the script, e.g. using a cronjob. For example, the following
|
||||||
cronjob entry would run the script every 30 minutes, update the preview server (if necessary) and
|
cronjob entry would run the script every hour and update the preview server (assuming the user has
|
||||||
log its output to `update-preview-server.log` (assuming the user has the necessary permissions):
|
the necessary permissions):
|
||||||
|
|
||||||
```
|
```
|
||||||
# Periodically check for changes and update the preview server (if necessary)
|
# Periodically check for changes and update the preview server (if necessary)
|
||||||
*/30 * * * * /path/to/update-preview-server.sh /path/to/repo /path/to/secrets /path/to/builds /path/to/localcerts /path/to/logs >> /path/to/update-preview-server.log 2>&1
|
*/30 * * * * /path/to/update-preview-server.sh /path/to/repo /path/to/localcerts /path/to/secrets /path/to/builds /path/to/logs
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note:**
|
|
||||||
Keep in mind that cron jobs run in non-interactive, non-login shells. This means that the execution
|
|
||||||
context might be different compared to when running the same commands from an interactive, login
|
|
||||||
shell. For example, `.bashrc` files are normally _not_ sourced automatically in cron jobs. See
|
|
||||||
[here](http://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html) for more info.
|
|
||||||
|
@ -7,14 +7,13 @@ echo -e "\n\n[`date`] - Updating the preview server..."
|
|||||||
|
|
||||||
# Input
|
# Input
|
||||||
readonly HOST_REPO_DIR=$1
|
readonly HOST_REPO_DIR=$1
|
||||||
readonly HOST_SECRETS_DIR=$2
|
readonly HOST_LOCALCERTS_DIR=$2
|
||||||
readonly HOST_BUILDS_DIR=$3
|
readonly HOST_SECRETS_DIR=$3
|
||||||
readonly HOST_LOCALCERTS_DIR=$4
|
readonly HOST_BUILDS_DIR=$4
|
||||||
readonly HOST_LOGS_DIR=$5
|
readonly HOST_LOGS_DIR=$5
|
||||||
|
|
||||||
# Constants
|
# Constants
|
||||||
readonly PROVISIONAL_TAG=provisional
|
readonly PROVISIONAL_IMAGE_NAME=aio-builds:provisional
|
||||||
readonly PROVISIONAL_IMAGE_NAME=aio-builds:$PROVISIONAL_TAG
|
|
||||||
readonly LATEST_IMAGE_NAME=aio-builds:latest
|
readonly LATEST_IMAGE_NAME=aio-builds:latest
|
||||||
readonly CONTAINER_NAME=aio
|
readonly CONTAINER_NAME=aio
|
||||||
|
|
||||||
@ -31,7 +30,7 @@ readonly CONTAINER_NAME=aio
|
|||||||
# Do not update the server unless files inside `aio-builds-setup/` have changed
|
# Do not update the server unless files inside `aio-builds-setup/` have changed
|
||||||
# or the last attempt failed (identified by the provisional image still being around).
|
# or the last attempt failed (identified by the provisional image still being around).
|
||||||
readonly relevantChangedFilesCount=$(git diff --name-only $lastDeployedCommit...HEAD | grep -P "^aio/aio-builds-setup/" | wc -l)
|
readonly relevantChangedFilesCount=$(git diff --name-only $lastDeployedCommit...HEAD | grep -P "^aio/aio-builds-setup/" | wc -l)
|
||||||
readonly lastAttemptFailed=$(sudo docker image ls | grep "$PROVISIONAL_TAG" >> /dev/fd/3 && echo "true" || echo "false")
|
readonly lastAttemptFailed=$(sudo docker rmi "$PROVISIONAL_IMAGE_NAME" >> /dev/fd/3 && echo "true" || echo "false")
|
||||||
if [[ $relevantChangedFilesCount -eq 0 ]] && [[ "$lastAttemptFailed" != "true" ]]; then
|
if [[ $relevantChangedFilesCount -eq 0 ]] && [[ "$lastAttemptFailed" != "true" ]]; then
|
||||||
echo "Skipping update because no relevant files have been touched."
|
echo "Skipping update because no relevant files have been touched."
|
||||||
exit 0
|
exit 0
|
||||||
@ -61,9 +60,9 @@ readonly CONTAINER_NAME=aio
|
|||||||
--publish 80:80 \
|
--publish 80:80 \
|
||||||
--publish 443:443 \
|
--publish 443:443 \
|
||||||
--restart unless-stopped \
|
--restart unless-stopped \
|
||||||
|
--volume $HOST_LOCALCERTS_DIR:/etc/ssl/localcerts:ro \
|
||||||
--volume $HOST_SECRETS_DIR:/aio-secrets:ro \
|
--volume $HOST_SECRETS_DIR:/aio-secrets:ro \
|
||||||
--volume $HOST_BUILDS_DIR:/var/www/aio-builds \
|
--volume $HOST_BUILDS_DIR:/var/www/aio-builds \
|
||||||
--volume $HOST_LOCALCERTS_DIR:/etc/ssl/localcerts:ro \
|
|
||||||
--volume $HOST_LOGS_DIR:/var/log/aio \
|
--volume $HOST_LOGS_DIR:/var/log/aio \
|
||||||
"$LATEST_IMAGE_NAME"
|
"$LATEST_IMAGE_NAME"
|
||||||
|
|
||||||
|
@ -5,8 +5,7 @@
|
|||||||
"packageManager": "yarn",
|
"packageManager": "yarn",
|
||||||
"warnings": {
|
"warnings": {
|
||||||
"typescriptMismatch": false
|
"typescriptMismatch": false
|
||||||
},
|
}
|
||||||
"analytics": false
|
|
||||||
},
|
},
|
||||||
"newProjectRoot": "projects",
|
"newProjectRoot": "projects",
|
||||||
"projects": {
|
"projects": {
|
||||||
|
@ -109,3 +109,9 @@ Options that specify files can be given as absolute paths, or as paths relative
|
|||||||
The [ng generate](cli/generate) and [ng add](cli/add) commands take as an argument the artifact or library to be generated or added to the current project.
|
The [ng generate](cli/generate) and [ng add](cli/add) commands take as an argument the artifact or library to be generated or added to the current project.
|
||||||
In addition to any general options, each artifact or library defines its own options in a *schematic*.
|
In addition to any general options, each artifact or library defines its own options in a *schematic*.
|
||||||
Schematic options are supplied to the command in the same format as immediate command options.
|
Schematic options are supplied to the command in the same format as immediate command options.
|
||||||
|
|
||||||
|
|
||||||
|
### Building with Bazel
|
||||||
|
|
||||||
|
Optionally, you can configure the Angular CLI to use [Bazel](https://docs.bazel.build) as the build tool. For more information, see [Building with Bazel](guide/bazel).
|
||||||
|
|
||||||
|
3
aio/content/examples/.gitignore
vendored
3
aio/content/examples/.gitignore
vendored
@ -82,6 +82,9 @@ upgrade-phonecat-2-hybrid/aot/**/*
|
|||||||
# styleguide
|
# styleguide
|
||||||
!styleguide/src/systemjs.custom.js
|
!styleguide/src/systemjs.custom.js
|
||||||
|
|
||||||
|
# universal
|
||||||
|
!universal/webpack.server.config.js
|
||||||
|
|
||||||
# stackblitz
|
# stackblitz
|
||||||
*stackblitz.no-link.html
|
*stackblitz.no-link.html
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Accessibility Example</title>
|
<title>Accessibility Example</title>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
</head>
|
</head>
|
||||||
|
@ -89,14 +89,14 @@ describe('Animation Tests', () => {
|
|||||||
sleepFor(2000);
|
sleepFor(2000);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be inactive with a blue background', async () => {
|
it('should be inactive with an orange background', async () => {
|
||||||
const toggleButton = statusSlider.getToggleButton();
|
const toggleButton = statusSlider.getToggleButton();
|
||||||
const container = statusSlider.getComponentContainer();
|
const container = statusSlider.getComponentContainer();
|
||||||
let text = await container.getText();
|
let text = await container.getText();
|
||||||
|
|
||||||
if (text === 'Active') {
|
if (text === 'Active') {
|
||||||
await toggleButton.click();
|
await toggleButton.click();
|
||||||
await browser.wait(async () => await container.getCssValue('backgroundColor') === inactiveColor, 3000);
|
await browser.wait(async () => await container.getCssValue('backgroundColor') === inactiveColor, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
text = await container.getText();
|
text = await container.getText();
|
||||||
@ -106,14 +106,14 @@ describe('Animation Tests', () => {
|
|||||||
expect(bgColor).toBe(inactiveColor);
|
expect(bgColor).toBe(inactiveColor);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be active with an orange background', async () => {
|
it('should be active with a blue background', async () => {
|
||||||
const toggleButton = statusSlider.getToggleButton();
|
const toggleButton = statusSlider.getToggleButton();
|
||||||
const container = statusSlider.getComponentContainer();
|
const container = statusSlider.getComponentContainer();
|
||||||
let text = await container.getText();
|
let text = await container.getText();
|
||||||
|
|
||||||
if (text === 'Inactive') {
|
if (text === 'Inactive') {
|
||||||
await toggleButton.click();
|
await toggleButton.click();
|
||||||
await browser.wait(async () => await container.getCssValue('backgroundColor') === activeColor, 3000);
|
await browser.wait(async () => await container.getCssValue('backgroundColor') === activeColor, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
text = await container.getText();
|
text = await container.getText();
|
||||||
|
@ -12,8 +12,6 @@ Toggle All Animations <input type="checkbox" [checked]="!animationsDisabled" (cl
|
|||||||
<a id="auto" routerLink="/auto" routerLinkActive="active">Auto Calculation</a>
|
<a id="auto" routerLink="/auto" routerLinkActive="active">Auto Calculation</a>
|
||||||
<a id="heroes" routerLink="/heroes" routerLinkActive="active">Filter/Stagger</a>
|
<a id="heroes" routerLink="/heroes" routerLinkActive="active">Filter/Stagger</a>
|
||||||
<a id="hero-groups" routerLink="/hero-groups" routerLinkActive="active">Hero Groups</a>
|
<a id="hero-groups" routerLink="/hero-groups" routerLinkActive="active">Hero Groups</a>
|
||||||
<a id="insert-remove" routerLink="/insert-remove" routerLinkActive="active">Insert/Remove</a>
|
|
||||||
|
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- #docregion route-animations-outlet -->
|
<!-- #docregion route-animations-outlet -->
|
||||||
|
@ -35,7 +35,6 @@ import { InsertRemoveComponent } from './insert-remove.component';
|
|||||||
{ path: 'hero-groups', component: HeroListGroupPageComponent },
|
{ path: 'hero-groups', component: HeroListGroupPageComponent },
|
||||||
{ path: 'enter-leave', component: HeroListEnterLeavePageComponent },
|
{ path: 'enter-leave', component: HeroListEnterLeavePageComponent },
|
||||||
{ path: 'auto', component: HeroListAutoCalcPageComponent },
|
{ path: 'auto', component: HeroListAutoCalcPageComponent },
|
||||||
{ path: 'insert-remove', component: InsertRemoveComponent},
|
|
||||||
{ path: 'home', component: HomeComponent, data: {animation: 'HomePage'} },
|
{ path: 'home', component: HomeComponent, data: {animation: 'HomePage'} },
|
||||||
{ path: 'about', component: AboutComponent, data: {animation: 'AboutPage'} },
|
{ path: 'about', component: AboutComponent, data: {animation: 'AboutPage'} },
|
||||||
|
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
<!-- #docplaster -->
|
<!-- #docplaster -->
|
||||||
|
|
||||||
<h2>Insert/Remove</h2>
|
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<button (click)="toggle()">Toggle Insert/Remove</button>
|
<button (click)="toggle()">Toggle Insert/Remove</button>
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -9,10 +9,10 @@ import { trigger, transition, animate, style } from '@angular/animations';
|
|||||||
trigger('myInsertRemoveTrigger', [
|
trigger('myInsertRemoveTrigger', [
|
||||||
transition(':enter', [
|
transition(':enter', [
|
||||||
style({ opacity: 0 }),
|
style({ opacity: 0 }),
|
||||||
animate('100ms', style({ opacity: 1 })),
|
animate('5s', style({ opacity: 1 })),
|
||||||
]),
|
]),
|
||||||
transition(':leave', [
|
transition(':leave', [
|
||||||
animate('100ms', style({ opacity: 0 }))
|
animate('5s', style({ opacity: 0 }))
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
// #enddocregion enter-leave-trigger
|
// #enddocregion enter-leave-trigger
|
||||||
|
@ -25,12 +25,23 @@ describe('Attribute binding example', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should display a blue div with a red border', function () {
|
it('should display a blue div with a red border', function () {
|
||||||
expect(element.all(by.css('div')).get(1).getCssValue('border')).toEqual('2px solid rgb(212, 30, 46)');
|
expect(element.all(by.css('div')).get(4).getCssValue('border')).toEqual('2px solid rgb(212, 30, 46)');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display a div with many classes', function () {
|
it('should display a div with replaced classes', function () {
|
||||||
expect(element.all(by.css('div')).get(1).getAttribute('class')).toContain('special');
|
expect(element.all(by.css('div')).get(5).getAttribute('class')).toEqual('new-class');
|
||||||
expect(element.all(by.css('div')).get(1).getAttribute('class')).toContain('clearance');
|
});
|
||||||
|
|
||||||
|
it('should display four buttons', function() {
|
||||||
|
let redButton = element.all(by.css('button')).get(1);
|
||||||
|
let saveButton = element.all(by.css('button')).get(2);
|
||||||
|
let bigButton = element.all(by.css('button')).get(3);
|
||||||
|
let smallButton = element.all(by.css('button')).get(4);
|
||||||
|
|
||||||
|
expect(redButton.getCssValue('color')).toEqual('rgba(255, 0, 0, 1)');
|
||||||
|
expect(saveButton.getCssValue('background-color')).toEqual('rgba(0, 255, 255, 1)');
|
||||||
|
expect(bigButton.getText()).toBe('Big');
|
||||||
|
expect(smallButton.getText()).toBe('Small');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -27,41 +27,39 @@
|
|||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<h2>Styling precedence</h2>
|
<h2>Class binding</h2>
|
||||||
|
|
||||||
<!-- #docregion basic-specificity -->
|
<!-- #docregion is-special -->
|
||||||
<h3>Basic specificity</h3>
|
<h3>toggle the "special" class on/off with a property:</h3>
|
||||||
|
<div [class.special]="isSpecial">The class binding is special.</div>
|
||||||
|
|
||||||
<!-- The `class.special` binding will override any value for the `special` class in `classExpr`. -->
|
<h3>binding to class.special overrides the class attribute:</h3>
|
||||||
<div [class.special]="isSpecial" [class]="classExpr">Some text.</div>
|
<div class="special" [class.special]="!isSpecial">This one is not so special.</div>
|
||||||
|
|
||||||
<!-- The `style.color` binding will override any value for the `color` property in `styleExpr`. -->
|
<h3>Using the bind- syntax:</h3>
|
||||||
<div [style.color]="color" [style]="styleExpr">Some text.</div>
|
<div bind-class.special="isSpecial">This class binding is special too.</div>
|
||||||
<!-- #enddocregion basic-specificity -->
|
<!-- #enddocregion is-special -->
|
||||||
|
|
||||||
<!-- #docregion source-specificity -->
|
<!-- #docregion add-class -->
|
||||||
<h3>Source specificity</h3>
|
<h3>Add a class:</h3>
|
||||||
|
<div class="item clearance special" [class.item-clearance]="itemClearance">Add another class</div>
|
||||||
|
<!-- #enddocregion add-class -->
|
||||||
|
|
||||||
<!-- The `class.special` template binding will override any host binding to the `special` class set by `dirWithClassBinding` or `comp-with-host-binding`.-->
|
<!-- #docregion class-override -->
|
||||||
<comp-with-host-binding [class.special]="isSpecial" dirWithClassBinding>Some text.</comp-with-host-binding>
|
<h3>Overwrite all existing classes with a new class:</h3>
|
||||||
|
<div class="item clearance special" [attr.class]="resetClasses">Reset all classes at once</div>
|
||||||
|
<!-- #enddocregion class-override -->
|
||||||
|
|
||||||
<!-- The `style.color` template binding will override any host binding to the `color` property set by `dirWithStyleBinding` or `comp-with-host-binding`. -->
|
<hr />
|
||||||
<comp-with-host-binding [style.color]="color" dirWithStyleBinding>Some text.</comp-with-host-binding>
|
|
||||||
<!-- #enddocregion source-specificity -->
|
|
||||||
|
|
||||||
<!-- #docregion dynamic-priority -->
|
<h2>Style binding</h2>
|
||||||
<h3>Dynamic vs static</h3>
|
|
||||||
|
|
||||||
<!-- If `classExpr` has a value for the `special` class, this value will override the `class="special"` below -->
|
|
||||||
<div class="special" [class]="classExpr">Some text.</div>
|
|
||||||
|
|
||||||
<!-- If `styleExpr` has a value for the `color` property, this value will override the `style="color: blue"` below -->
|
|
||||||
<div style="color: blue" [style]="styleExpr">Some text.</div>
|
|
||||||
|
|
||||||
<!-- #enddocregion dynamic-priority -->
|
|
||||||
|
|
||||||
<!-- #docregion style-delegation -->
|
|
||||||
<comp-with-host-binding dirWithHostBinding></comp-with-host-binding>
|
|
||||||
<!-- #enddocregion style-delegation -->
|
|
||||||
|
|
||||||
|
<!-- #docregion style-binding-->
|
||||||
|
<button [style.color]="isSpecial ? 'red': 'green'">Red</button>
|
||||||
|
<button [style.background-color]="canSave ? 'cyan': 'grey'" >Save</button>
|
||||||
|
<!-- #enddocregion style-binding -->
|
||||||
|
|
||||||
|
<!-- #docregion style-binding-condition-->
|
||||||
|
<button [style.font-size.em]="isSpecial ? 3 : 1" >Big</button>
|
||||||
|
<button [style.font-size.%]="!isSpecial ? 150 : 50" >Small</button>
|
||||||
|
<!-- #enddocregion style-binding-condition-->
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
import { TestBed, async } from '@angular/core/testing';
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
describe('AppComponent', () => {
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [
|
||||||
|
AppComponent
|
||||||
|
],
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
it('should create the app', async(() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
const app = fixture.debugElement.componentInstance;
|
||||||
|
expect(app).toBeTruthy();
|
||||||
|
}));
|
||||||
|
it(`should have as title 'app'`, async(() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
const app = fixture.debugElement.componentInstance;
|
||||||
|
expect(app.title).toEqual('app');
|
||||||
|
}));
|
||||||
|
it('should render title in a h1 tag', async(() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.debugElement.nativeElement;
|
||||||
|
expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
|
||||||
|
}));
|
||||||
|
});
|
@ -8,8 +8,8 @@ import { Component } from '@angular/core';
|
|||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
actionName = 'Go for it';
|
actionName = 'Go for it';
|
||||||
isSpecial = true;
|
isSpecial = true;
|
||||||
|
itemClearance = true;
|
||||||
|
resetClasses = 'new-class';
|
||||||
canSave = true;
|
canSave = true;
|
||||||
classExpr = 'special clearance';
|
|
||||||
styleExpr = 'color: red';
|
|
||||||
color = 'blue';
|
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,11 @@ import { NgModule } from '@angular/core';
|
|||||||
|
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { CompWithHostBindingComponent } from './comp-with-host-binding.component';
|
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent
|
||||||
CompWithHostBindingComponent
|
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule
|
BrowserModule
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'comp-with-host-binding',
|
|
||||||
template: 'I am a component!',
|
|
||||||
host: {
|
|
||||||
'[class.special]': 'isSpecial',
|
|
||||||
'[style.color]': 'color',
|
|
||||||
'[style.width]': 'width'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
export class CompWithHostBindingComponent {
|
|
||||||
isSpecial = false;
|
|
||||||
color = 'green';
|
|
||||||
width = '200px';
|
|
||||||
}
|
|
@ -4,6 +4,7 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>AttributeBinding</title>
|
<title>AttributeBinding</title>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
</head>
|
</head>
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
import { TestBed, async } from '@angular/core/testing';
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
describe('AppComponent', () => {
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [
|
||||||
|
AppComponent
|
||||||
|
],
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
it('should create the app', async(() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
const app = fixture.debugElement.componentInstance;
|
||||||
|
expect(app).toBeTruthy();
|
||||||
|
}));
|
||||||
|
it(`should have as title 'app'`, async(() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
const app = fixture.debugElement.componentInstance;
|
||||||
|
expect(app.title).toEqual('app');
|
||||||
|
}));
|
||||||
|
it('should render title in a h1 tag', async(() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.debugElement.nativeElement;
|
||||||
|
expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
|
||||||
|
}));
|
||||||
|
});
|
@ -0,0 +1,32 @@
|
|||||||
|
import { TestBed, async } from '@angular/core/testing';
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
|
describe('AppComponent', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [
|
||||||
|
AppComponent
|
||||||
|
],
|
||||||
|
});
|
||||||
|
TestBed.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create the app', async(() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
const app = fixture.debugElement.componentInstance;
|
||||||
|
expect(app).toBeTruthy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it(`should have as title 'app works!'`, async(() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
const app = fixture.debugElement.componentInstance;
|
||||||
|
expect(app.title).toEqual('app works!');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should render title in a h1 tag', async(() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.debugElement.nativeElement;
|
||||||
|
expect(compiled.querySelector('h1').textContent).toContain('app works!');
|
||||||
|
}));
|
||||||
|
});
|
@ -0,0 +1,8 @@
|
|||||||
|
import { ItemDirective } from './item.directive';
|
||||||
|
|
||||||
|
describe('ItemDirective', () => {
|
||||||
|
it('should create an instance', () => {
|
||||||
|
const directive = new ItemDirective();
|
||||||
|
expect(directive).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -4,6 +4,7 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>NgmoduleDemo</title>
|
<title>NgmoduleDemo</title>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
</head>
|
</head>
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ItemDetailComponent } from './item-detail.component';
|
||||||
|
|
||||||
|
describe('ItemDetailComponent', () => {
|
||||||
|
let component: ItemDetailComponent;
|
||||||
|
let fixture: ComponentFixture<ItemDetailComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ ItemDetailComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ItemDetailComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -4,6 +4,7 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>BuiltInDirectives</title>
|
<title>BuiltInDirectives</title>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
</head>
|
</head>
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
import { TestBed, async } from '@angular/core/testing';
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
describe('AppComponent', () => {
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [
|
||||||
|
AppComponent
|
||||||
|
],
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
it('should create the app', async(() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
const app = fixture.debugElement.componentInstance;
|
||||||
|
expect(app).toBeTruthy();
|
||||||
|
}));
|
||||||
|
});
|
@ -4,6 +4,7 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Built-in Template Functions Example</title>
|
<title>Built-in Template Functions Example</title>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
</head>
|
</head>
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
{
|
{
|
||||||
"tests": [
|
"e2e": [
|
||||||
{
|
{
|
||||||
"cmd": "yarn",
|
"cmd": "yarn",
|
||||||
"args": [
|
"args": [
|
||||||
"e2e",
|
"e2e",
|
||||||
"--protractor-config=e2e/protractor-puppeteer.conf.js",
|
|
||||||
"--no-webdriver-update",
|
"--no-webdriver-update",
|
||||||
"--port={PORT}"
|
"--port={PORT}"
|
||||||
]
|
]
|
||||||
|
@ -23,7 +23,7 @@ export class HeroContactComponent {
|
|||||||
|
|
||||||
@Host() // limit search for logger; hides the application-wide logger
|
@Host() // limit search for logger; hides the application-wide logger
|
||||||
@Optional() // ok if the logger doesn't exist
|
@Optional() // ok if the logger doesn't exist
|
||||||
private loggerService?: LoggerService
|
private loggerService: LoggerService
|
||||||
// #enddocregion ctor-params
|
// #enddocregion ctor-params
|
||||||
) {
|
) {
|
||||||
if (loggerService) {
|
if (loggerService) {
|
||||||
|
@ -52,7 +52,7 @@ const templateC = `
|
|||||||
export class CarolComponent {
|
export class CarolComponent {
|
||||||
name = 'Carol';
|
name = 'Carol';
|
||||||
// #docregion carol-ctor
|
// #docregion carol-ctor
|
||||||
constructor( @Optional() public parent?: Parent ) { }
|
constructor( @Optional() public parent: Parent ) { }
|
||||||
// #enddocregion carol-ctor
|
// #enddocregion carol-ctor
|
||||||
}
|
}
|
||||||
// #enddocregion carol-class
|
// #enddocregion carol-class
|
||||||
@ -64,7 +64,7 @@ export class CarolComponent {
|
|||||||
})
|
})
|
||||||
export class ChrisComponent {
|
export class ChrisComponent {
|
||||||
name = 'Chris';
|
name = 'Chris';
|
||||||
constructor( @Optional() public parent?: Parent ) { }
|
constructor( @Optional() public parent: Parent ) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
////// Craig ///////////
|
////// Craig ///////////
|
||||||
@ -81,7 +81,7 @@ export class ChrisComponent {
|
|||||||
</div>`
|
</div>`
|
||||||
})
|
})
|
||||||
export class CraigComponent {
|
export class CraigComponent {
|
||||||
constructor( @Optional() public alex?: Base ) { }
|
constructor( @Optional() public alex: Base ) { }
|
||||||
}
|
}
|
||||||
// #enddocregion craig
|
// #enddocregion craig
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ const templateB = `
|
|||||||
export class BarryComponent implements Parent {
|
export class BarryComponent implements Parent {
|
||||||
name = 'Barry';
|
name = 'Barry';
|
||||||
// #docregion barry-ctor
|
// #docregion barry-ctor
|
||||||
constructor( @SkipSelf() @Optional() public parent?: Parent ) { }
|
constructor( @SkipSelf() @Optional() public parent: Parent ) { }
|
||||||
// #enddocregion barry-ctor
|
// #enddocregion barry-ctor
|
||||||
}
|
}
|
||||||
// #enddocregion barry
|
// #enddocregion barry
|
||||||
@ -117,7 +117,7 @@ export class BarryComponent implements Parent {
|
|||||||
})
|
})
|
||||||
export class BobComponent implements Parent {
|
export class BobComponent implements Parent {
|
||||||
name = 'Bob';
|
name = 'Bob';
|
||||||
constructor( @SkipSelf() @Optional() public parent?: Parent ) { }
|
constructor( @SkipSelf() @Optional() public parent: Parent ) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -129,7 +129,7 @@ export class BobComponent implements Parent {
|
|||||||
})
|
})
|
||||||
export class BethComponent implements Parent {
|
export class BethComponent implements Parent {
|
||||||
name = 'Beth';
|
name = 'Beth';
|
||||||
constructor( @SkipSelf() @Optional() public parent?: Parent ) { }
|
constructor( @SkipSelf() @Optional() public parent: Parent ) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
///////// A - Grandparent //////
|
///////// A - Grandparent //////
|
||||||
@ -200,7 +200,7 @@ export class AliceComponent implements Parent
|
|||||||
</div>`
|
</div>`
|
||||||
})
|
})
|
||||||
export class CathyComponent {
|
export class CathyComponent {
|
||||||
constructor( @Optional() public alex?: AlexComponent ) { }
|
constructor( @Optional() public alex: AlexComponent ) { }
|
||||||
}
|
}
|
||||||
// #enddocregion cathy
|
// #enddocregion cathy
|
||||||
|
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"e2e": [
|
||||||
|
{
|
||||||
|
"cmd": "yarn",
|
||||||
|
"args": [
|
||||||
|
"e2e",
|
||||||
|
"--no-webdriver-update",
|
||||||
|
"--port={PORT}"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -158,11 +158,11 @@ export class Provider6bComponent {
|
|||||||
|
|
||||||
// #docregion silent-logger
|
// #docregion silent-logger
|
||||||
// An object in the shape of the logger service
|
// An object in the shape of the logger service
|
||||||
function silentLoggerFn() {}
|
export function SilentLoggerFn() {}
|
||||||
|
|
||||||
export const SilentLogger = {
|
const silentLogger = {
|
||||||
logs: ['Silent logger says "Shhhhh!". Provided via "useValue"'],
|
logs: ['Silent logger says "Shhhhh!". Provided via "useValue"'],
|
||||||
log: silentLoggerFn
|
log: SilentLoggerFn
|
||||||
};
|
};
|
||||||
// #enddocregion silent-logger
|
// #enddocregion silent-logger
|
||||||
|
|
||||||
@ -171,7 +171,7 @@ export const SilentLogger = {
|
|||||||
template: template,
|
template: template,
|
||||||
providers:
|
providers:
|
||||||
// #docregion providers-7
|
// #docregion providers-7
|
||||||
[{ provide: Logger, useValue: SilentLogger }]
|
[{ provide: Logger, useValue: silentLogger }]
|
||||||
// #enddocregion providers-7
|
// #enddocregion providers-7
|
||||||
})
|
})
|
||||||
export class Provider7Component {
|
export class Provider7Component {
|
||||||
@ -247,7 +247,7 @@ let some_message = 'Hello from the injected logger';
|
|||||||
export class Provider10Component implements OnInit {
|
export class Provider10Component implements OnInit {
|
||||||
log: string;
|
log: string;
|
||||||
// #docregion provider-10-ctor
|
// #docregion provider-10-ctor
|
||||||
constructor(@Optional() private logger?: Logger) {
|
constructor(@Optional() private logger: Logger) {
|
||||||
if (this.logger) {
|
if (this.logger) {
|
||||||
this.logger.log(some_message);
|
this.logger.log(some_message);
|
||||||
}
|
}
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"projectType": "elements"
|
|
||||||
}
|
|
||||||
|
@ -39,7 +39,7 @@ import { animate, state, style, transition, trigger } from '@angular/animations'
|
|||||||
`]
|
`]
|
||||||
})
|
})
|
||||||
export class PopupComponent {
|
export class PopupComponent {
|
||||||
state: 'opened' | 'closed' = 'closed';
|
private state: 'opened' | 'closed' = 'closed';
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
set message(message: string) {
|
set message(message: string) {
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
import { TestBed, async } from '@angular/core/testing';
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
describe('AppComponent', () => {
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [
|
||||||
|
AppComponent
|
||||||
|
],
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
it('should create the app', async(() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
const app = fixture.debugElement.componentInstance;
|
||||||
|
expect(app).toBeTruthy();
|
||||||
|
}));
|
||||||
|
it(`should have as title 'Featured product:'`, async(() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
const app = fixture.debugElement.componentInstance;
|
||||||
|
expect(app.title).toEqual('Featured product:');
|
||||||
|
}));
|
||||||
|
it('should render title in a p tag', async(() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.debugElement.nativeElement;
|
||||||
|
expect(compiled.querySelector('p').textContent).toContain('Featured product:');
|
||||||
|
}));
|
||||||
|
});
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ItemDetailComponent } from './item-detail.component';
|
||||||
|
|
||||||
|
describe('ItemDetailComponent', () => {
|
||||||
|
let component: ItemDetailComponent;
|
||||||
|
let fixture: ComponentFixture<ItemDetailComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ ItemDetailComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ItemDetailComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -4,6 +4,7 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>EventBinding</title>
|
<title>EventBinding</title>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
</head>
|
</head>
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"build": "build:cli",
|
||||||
|
"run": "serve:cli"
|
||||||
|
}
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
import { TestBed, async } from '@angular/core/testing';
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
|
describe('AppComponent', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [
|
||||||
|
AppComponent
|
||||||
|
],
|
||||||
|
});
|
||||||
|
TestBed.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create the app', async(() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
const app = fixture.debugElement.componentInstance;
|
||||||
|
expect(app).toBeTruthy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it(`should have as title 'app works!'`, async(() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
const app = fixture.debugElement.componentInstance;
|
||||||
|
expect(app.title).toEqual('app works!');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should render title in a h1 tag', async(() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.debugElement.nativeElement;
|
||||||
|
expect(compiled.querySelector('h1').textContent).toContain('app works!');
|
||||||
|
}));
|
||||||
|
});
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { CustomerDashboardComponent } from './customer-dashboard.component';
|
||||||
|
|
||||||
|
describe('CustomerDashboardComponent', () => {
|
||||||
|
let component: CustomerDashboardComponent;
|
||||||
|
let fixture: ComponentFixture<CustomerDashboardComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ CustomerDashboardComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CustomerDashboardComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -4,6 +4,7 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Feature Modules</title>
|
<title>Feature Modules</title>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
</head>
|
</head>
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user