Compare commits
2990 Commits
zone.js-0.
...
10.0.0-nex
Author | SHA1 | Date | |
---|---|---|---|
1754beb59c | |||
844208f463 | |||
1689f9378b | |||
011fdfa94f | |||
6466fb20c2 | |||
3f9b3d7666 | |||
8524688a87 | |||
7f58c579f3 | |||
3d7c85b2aa | |||
55c2433171 | |||
03afa36334 | |||
05f1df3224 | |||
901db0e730 | |||
564ca9e8ec | |||
20d377c11b | |||
2c5e873da0 | |||
42a9e5ad7d | |||
cda2530df5 | |||
aaa89bb715 | |||
ea971f7098 | |||
ddaa124162 | |||
7a30153aa1 | |||
7acd33007d | |||
8a56c99f87 | |||
77c0ef38be | |||
5f64e52ed4 | |||
b2a3325258 | |||
95f429c7e2 | |||
f16ca1ce46 | |||
508c555ce2 | |||
f97d8a9cbd | |||
24ec5235a0 | |||
bbd556ee9f | |||
466df41c8f | |||
4e1b5e43fa | |||
b0e362f75b | |||
91092f668e | |||
d268d2ad85 | |||
2486db7c2b | |||
0672a0e547 | |||
9846b19986 | |||
8b79075497 | |||
2956f21d82 | |||
c8ee390d23 | |||
d7440c452a | |||
a2b8dc1cfb | |||
7ebf35c5f8 | |||
dbcaf22805 | |||
0066a1ae70 | |||
491da99abe | |||
13ba84731f | |||
b58bd2bb91 | |||
a32cbed89c | |||
c42f35eb2f | |||
2418c6aca1 | |||
90205079d7 | |||
57a868627f | |||
2cacab92e6 | |||
0520ecb7d0 | |||
047642556c | |||
9edea0bb75 | |||
ce39755937 | |||
7d3f5043e1 | |||
14c0ec97d8 | |||
45f4a47286 | |||
141fcb95a4 | |||
dbf1f6b233 | |||
5d12c19ce9 | |||
352b9c78e0 | |||
019835c721 | |||
255c2b2ff0 | |||
0731fc47e4 | |||
7ffdbc05d2 | |||
4f3ac1d983 | |||
d7c70caa4c | |||
2cddeabce0 | |||
7addc9f1a9 | |||
cb6ddfc215 | |||
681741cd13 | |||
8fe532e593 | |||
198fc6f108 | |||
8d8e419664 | |||
1e208e8c87 | |||
286fbf42c6 | |||
40025f55ad | |||
7eb1a58b26 | |||
28f3c1c96a | |||
0c8adbc4ec | |||
ed1b4a8f19 | |||
bd30c37040 | |||
eb34aa551a | |||
1142c378fd | |||
82a3bd5e8b | |||
5f3fba43fa | |||
eca2246fbc | |||
df71c7c617 | |||
c184de175d | |||
8b65946eb8 | |||
de0c71c467 | |||
39e066add1 | |||
178e750f16 | |||
14be55c9fa | |||
fdd61152c5 | |||
7cc332efe6 | |||
1eba1aaf15 | |||
4e628a887a | |||
b6bc0aeb61 | |||
77d8afb7da | |||
42d1091d6a | |||
d0280a0335 | |||
97d6d909e7 | |||
f7815cf96d | |||
9e43e4900e | |||
8ce38cac0d | |||
a10c126692 | |||
b682bd1916 | |||
26eacd4fcb | |||
fc4741f638 | |||
3f3e9b7555 | |||
53a8459d5f | |||
0ec0ff3bce | |||
f930e75a80 | |||
854bd7d0c8 | |||
e17fe90aaa | |||
0577bf0e3e | |||
4c92cf43cf | |||
c6ecdc9a81 | |||
20cc3ab37e | |||
420b9be1c1 | |||
3c6c00d1d4 | |||
6fa36e387f | |||
9bde1be5a4 | |||
040253c426 | |||
c98a4d6ddd | |||
d5293d2aa3 | |||
d578ab8f3c | |||
9dbb30f884 | |||
fafa50d97f | |||
e010f2ca54 | |||
c9e0db55f7 | |||
ecffc3557f | |||
b861e9c0ac | |||
4213e8d5f0 | |||
bab90a7709 | |||
da79e0433f | |||
1b8752e595 | |||
b3cf5b246f | |||
0a695e181e | |||
a5d1d1161b | |||
a25c3c5c3b | |||
d1a8afbbd0 | |||
f4fcb0c54c | |||
e92b81351b | |||
53805f07ba | |||
e73daa3736 | |||
eef01160f4 | |||
333abf16e4 | |||
388dc93cee | |||
fbd281c26e | |||
420c179b39 | |||
35d61c11fd | |||
e30e1325f3 | |||
fc6c3ae97d | |||
63a28d6805 | |||
b95a336f12 | |||
b4df19c029 | |||
dbd0f8e699 | |||
b7fb92a048 | |||
2ff4b357d7 | |||
1c26f40cd4 | |||
2de34770c4 | |||
0fc6ed3c4a | |||
63455b839a | |||
70b25a3d4f | |||
2200c8a87b | |||
306f46ce92 | |||
1b2d6ea11b | |||
03198de73a | |||
bb0e57eccb | |||
a468f11c7c | |||
9d2241b901 | |||
88a235de3a | |||
7bd9e09c78 | |||
42c58d9fe8 | |||
b3713a112f | |||
e037840b88 | |||
ec6b9cc17d | |||
db4c59dad9 | |||
58ea040570 | |||
6046e86506 | |||
297dab845d | |||
2cb5f59acc | |||
0f3831b105 | |||
9d9d46f52b | |||
d9c4840a9c | |||
12fcc7cafe | |||
95a407d9dd | |||
e9300c979e | |||
df274b478e | |||
f8941a5b6b | |||
38f805cd06 | |||
98931bf9b5 | |||
9b85b533ff | |||
d805526659 | |||
ee435761fd | |||
35c6ed2675 | |||
72f534f7f8 | |||
3cb9b43851 | |||
bf0c520f2e | |||
ee35e223a7 | |||
dc9f4b994e | |||
49be32c931 | |||
1786586747 | |||
45c09416ed | |||
4779c4b94a | |||
793cb328de | |||
966598cda7 | |||
772ccf0d9f | |||
ff6e93163f | |||
dff5129661 | |||
e367593a26 | |||
16039d837e | |||
4665c35453 | |||
4c63241b34 | |||
9aa778e843 | |||
62ba0acfb5 | |||
52d2e46700 | |||
c89421bfa8 | |||
cebd4fada7 | |||
3d82aa781d | |||
89c589085d | |||
a6a7e1bb99 | |||
70dd27ffd8 | |||
7bc5bcde34 | |||
aa0dd0d5ff | |||
e0aa39929b | |||
4172707346 | |||
7c8c41353b | |||
1d9375d115 | |||
f27deea003 | |||
00e6cb1d62 | |||
cae2a893f2 | |||
b34fb04cbf | |||
2365bb89d7 | |||
7b5a0ba8c3 | |||
ad8c4cdd75 | |||
f5ff2068a4 | |||
90a2796a7e | |||
76190c71fc | |||
dd049caf0a | |||
bcd31cb857 | |||
e6161ca307 | |||
d6f6cd0cb1 | |||
b7f2a033df | |||
e37f2663c2 | |||
5e579c4dc9 | |||
f0ec4ae941 | |||
b80b43ddae | |||
f0c570bd41 | |||
e5d2853070 | |||
152293d3cf | |||
ceb61d10c1 | |||
dd806b0d99 | |||
113c75dd27 | |||
b7acf07a70 | |||
519f2baff0 | |||
3962908d36 | |||
afd2cbc3c4 | |||
4275b3412f | |||
1b3ee17888 | |||
8306da5ce1 | |||
75dff8a93a | |||
fc1b557d98 | |||
47f98673c7 | |||
781f5611d1 | |||
deea6da0e0 | |||
43622d32e4 | |||
5db8d93fe5 | |||
535c249f3a | |||
942b986ef3 | |||
acf6075ca9 | |||
28995dba19 | |||
f186c32245 | |||
d7c77cb175 | |||
51b09244ff | |||
458dc10ddc | |||
4341743b4a | |||
b5e92b9a5d | |||
28ff02b116 | |||
f4ef8a92f8 | |||
d7e9d8746a | |||
351759906b | |||
0c2ed4c3e5 | |||
37bfb14956 | |||
81d23b33ef | |||
bb150c2704 | |||
46ff673192 | |||
7842f73e8b | |||
e5f9e3d018 | |||
aebd5f9af6 | |||
4fb034aeec | |||
4da901f8c0 | |||
39b043646b | |||
4480ba3e29 | |||
5e79799b89 | |||
1fa1dd5aaa | |||
8be0972836 | |||
78840e59a7 | |||
c332d4d916 | |||
bb944eecd6 | |||
443f5eee85 | |||
7e5e60b757 | |||
33df4b74da | |||
3c14e9612f | |||
cabf997933 | |||
2ed7146393 | |||
bfda7ca5bd | |||
305b059809 | |||
297b7da166 | |||
6aac91743b | |||
f06d5f02b3 | |||
4a9f0bebc3 | |||
88b0985bad | |||
1e3b8a1354 | |||
5a09753e07 | |||
02bfa9e0e0 | |||
306ff3cd54 | |||
ec5c108b60 | |||
4d9da9b0a1 | |||
3ce7c87cbd | |||
061107c5f6 | |||
9f039d40c1 | |||
b1f1d3ffd2 | |||
03ff380e13 | |||
c6e5fc4fbe | |||
074266b896 | |||
c4dc642f99 | |||
e041ac6f0d | |||
66effde9f3 | |||
663b768780 | |||
0147de9044 | |||
b010849102 | |||
698b0288be | |||
5e80e7e216 | |||
d50cb30443 | |||
29122c518b | |||
58e175318c | |||
f88e63519a | |||
0cc53fb398 | |||
713bce19f4 | |||
d972821889 | |||
2d16b4711e | |||
9181b73e5a | |||
5737e65700 | |||
ca677481a2 | |||
6ab43d7335 | |||
fee316161d | |||
2e4244d521 | |||
3bedfdac9d | |||
ec0ce6005a | |||
a185efbd60 | |||
4aa4e6fd03 | |||
078b0be4dc | |||
13ae2d7271 | |||
99b93e55cb | |||
80e6c07d89 | |||
34aa5570ed | |||
e526f74dfd | |||
9e78f55c32 | |||
f1731d807e | |||
92c4f3d508 | |||
e92fce1c27 | |||
83a9159063 | |||
0a69a2832b | |||
717df13207 | |||
b40b5576df | |||
03f2f1ae47 | |||
a8978ebf8e | |||
7d7b59efa5 | |||
4cf89d4bd7 | |||
d47b318fee | |||
e485236502 | |||
41667de778 | |||
ef4736d052 | |||
db4a448439 | |||
7549c65502 | |||
1beccc1a6c | |||
0075017b43 | |||
4374931b0e | |||
64022f51d4 | |||
f9f6e2e1b3 | |||
aecf9de738 | |||
81195a238b | |||
96a3de6364 | |||
00027130ea | |||
4a18428de6 | |||
7f28845f58 | |||
1dd0b6cc18 | |||
e0415dbf16 | |||
95fc3d4c5c | |||
e145fa13b1 | |||
5fa7b8ba56 | |||
2c7d366c82 | |||
2dd6f25647 | |||
d2623f13d9 | |||
7d0af179e3 | |||
416c786774 | |||
93302b7fb8 | |||
eb8c6c7eff | |||
8660806ddc | |||
4c5e085c93 | |||
d707124fd9 | |||
aece3669e5 | |||
09c84169d5 | |||
d43c30688a | |||
ee70a18a75 | |||
76a8cd57ae | |||
4c7f89ff6d | |||
f9fb8338f5 | |||
6b3aa60446 | |||
2463548fa7 | |||
1140bbc25c | |||
1b4df6484e | |||
f40d51733a | |||
e893c5a330 | |||
8be8466a00 | |||
c8bef1259c | |||
568e9df1d6 | |||
a555fdba32 | |||
36535e9abd | |||
6402a9ae2a | |||
ca25c957bf | |||
93f07aee6c | |||
0af6e9fcbb | |||
008e12edda | |||
1927d0c7db | |||
9a0a90feb3 | |||
76d86d5a07 | |||
de7a9a3b93 | |||
04f61c0c3e | |||
c810ac7153 | |||
c24ad560fa | |||
3d2db5c5f0 | |||
32eafef6a7 | |||
2366480250 | |||
8e55a11283 | |||
e9de28111d | |||
75afd80ae8 | |||
a5eb0e56b6 | |||
4d458db1b5 | |||
24a92472bf | |||
43006bcc45 | |||
326240eb91 | |||
b59bc0e38c | |||
5516802142 | |||
cc4b813e75 | |||
64631063ae | |||
719224bffd | |||
fe2b6923ba | |||
38ad1d97ab | |||
372b9101e2 | |||
7e62aa0c6e | |||
36e927a8c6 | |||
9d8bb634f9 | |||
c5c57f6737 | |||
51a89c32c4 | |||
e1ac2efe4a | |||
702e17cfe2 | |||
4d1d0fa03b | |||
ec8bae1b27 | |||
08348fc2e8 | |||
63fbc71439 | |||
b44f7b5e16 | |||
c5df9ce474 | |||
2510e7dad6 | |||
80c68583d1 | |||
5b6ced5599 | |||
a383116b43 | |||
c6dd900f60 | |||
5ac308060d | |||
bc089abd32 | |||
c94a33c525 | |||
5cee709266 | |||
2d0847c307 | |||
995cd15a69 | |||
16497438d6 | |||
b54db86f43 | |||
44acf6734b | |||
b07b6edc2a | |||
83e4a76afa | |||
bfa7b1a494 | |||
8e0dec538e | |||
1c385a1c22 | |||
f9bc84cd99 | |||
2d7c95fb70 | |||
29e8a64cf0 | |||
00efacf561 | |||
d96995b4e3 | |||
fc3e5cb6d3 | |||
d37dad82f1 | |||
22710fc353 | |||
4f66250618 | |||
d783519835 | |||
b8e9a30d3b | |||
32ce8b1326 | |||
ff4eb0cb63 | |||
9ba46d9f88 | |||
b14ac96750 | |||
55dac05cf2 | |||
ae28d7c0b2 | |||
380de1e7b4 | |||
4f9717331d | |||
36fc28642a | |||
a323b9b1a3 | |||
58f4254fba | |||
4419907b08 | |||
9d415f9c3f | |||
fced8ee40e | |||
1cb7b88505 | |||
0e76b32aa5 | |||
e81ad3a1bc | |||
c3a85ceabc | |||
85e0e366df | |||
0ce8ad3493 | |||
528e25a81a | |||
d25012e864 | |||
c21c46a8e8 | |||
9cc8bd5f7d | |||
ae3eaf8b16 | |||
d714b95fb9 | |||
912692137a | |||
6fc85073d2 | |||
989dea7083 | |||
8968b206b4 | |||
df890d7629 | |||
47bfec4e46 | |||
b4972fa165 | |||
fb92f5de1a | |||
e342ffd855 | |||
c0143cb2ab | |||
c9f554cda7 | |||
ff665b9e6a | |||
1790b63a5d | |||
39d4016fe9 | |||
712f2642d5 | |||
d50881a86e | |||
c1a0f0617c | |||
13f7631c25 | |||
d2c60cc216 | |||
1aa5783a45 | |||
67b9adfd1a | |||
5007b97db6 | |||
5e45061a94 | |||
2132ee38e4 | |||
f6adc0c3f9 | |||
9afd360eba | |||
dbbc8ef8a6 | |||
9d22e2a445 | |||
c9fd8e39f3 | |||
e108646d43 | |||
2cb862447c | |||
fc325c49ec | |||
a8e936791b | |||
5fccb46f6f | |||
5a7ac8c3b9 | |||
21b8d6956a | |||
cc238e6377 | |||
df7504ade1 | |||
fbed0a1471 | |||
4cde83188a | |||
3e08347d8a | |||
9e70bcb34f | |||
1bc3893c65 | |||
a1e00f82f4 | |||
a1cae28283 | |||
02281281a8 | |||
22be967104 | |||
f5a5c36d3c | |||
b7138c1ec5 | |||
7aab399c84 | |||
9ea53803f7 | |||
34a17f3699 | |||
f862536ec4 | |||
afc9839f43 | |||
e3ecdc6a63 | |||
4af2a068c5 | |||
52d66ea5a9 | |||
61c37954c6 | |||
acc483e2eb | |||
168a393589 | |||
e552591768 | |||
46fe8fb8b4 | |||
31bec8ce61 | |||
e179c5827f | |||
79659ee5aa | |||
3fa895298d | |||
54634628ac | |||
8456c5ec60 | |||
0f8e710c7c | |||
05d0586223 | |||
6b4c5289b3 | |||
db74f2074b | |||
772bb5e742 | |||
47025e07ce | |||
e8900824dd | |||
a40be00e17 | |||
348ff0c8ea | |||
c852ec9283 | |||
8c2d8428d5 | |||
4a9514ec4e | |||
63815b54f3 | |||
372a56a0c9 | |||
2e493edf80 | |||
43635a6c54 | |||
37a48391f2 | |||
0d806c84e2 | |||
2b6028b643 | |||
99ea5d7044 | |||
8ea61a19cd | |||
ec9f4d5bc6 | |||
560542c2a8 | |||
a0ce8bc236 | |||
74e47c503a | |||
5af0e7523c | |||
18b1bd4415 | |||
06779cfe24 | |||
32f099aa36 | |||
96e3449ed7 | |||
20f01163af | |||
d8f9ec5291 | |||
7bd5368d8b | |||
cffbaba914 | |||
146ef48a6e | |||
9a1240360b | |||
b99b80c18b | |||
77da6887f7 | |||
5f7d06668e | |||
5e3a898f10 | |||
49478f5e19 | |||
10f1e94f18 | |||
1f89c6130e | |||
aad02e8337 | |||
4fba8a6aea | |||
42375c4b3a | |||
13495c64f7 | |||
f5c7e883a9 | |||
15f8afa4bf | |||
19cfaf7f4c | |||
6f95bc915d | |||
e74738db8c | |||
3aa5ae98ab | |||
5cb082ed53 | |||
95c729f5d1 | |||
c98c6e80c8 | |||
e4b1e6c622 | |||
65a6848ed7 | |||
0bf6e58db2 | |||
191e4d15b5 | |||
9368053be5 | |||
983f48136a | |||
edf881dbf1 | |||
287bfefade | |||
64d6f13b87 | |||
fc71032dc4 | |||
81cb54fc15 | |||
3d46a45fa8 | |||
406419bc0f | |||
a73e125c04 | |||
71309d223d | |||
15482e7367 | |||
9ff9a072e6 | |||
fc4c3c3eb5 | |||
1882451ec0 | |||
2b8edbbb1d | |||
446b51e164 | |||
11f7e275e6 | |||
0e2a577b42 | |||
08071e5634 | |||
350ac11554 | |||
cd32085a75 | |||
d88b237f1e | |||
99ce015f42 | |||
55b3f97be0 | |||
958165888c | |||
68bebd67f7 | |||
82a6fc5ef9 | |||
86a45ff561 | |||
84c2458ea7 | |||
c06f9a1ce3 | |||
d912d4f34c | |||
59607dc495 | |||
bef14cf424 | |||
59078c444c | |||
4e1d780554 | |||
44f9fa6ce5 | |||
a1b90619ba | |||
b42e2e0dff | |||
c55f900081 | |||
4acd658635 | |||
94fa140888 | |||
bdaab4184d | |||
98a9daf4f4 | |||
716d50aa21 | |||
876aa5a78a | |||
e0a35e13d5 | |||
8ef29b65ff | |||
a1d0396c55 | |||
de67978a7d | |||
b57be3bb41 | |||
dcf28bf320 | |||
1b8a66893e | |||
25ab4647c5 | |||
3ce5be7715 | |||
603ab40e85 | |||
c1b5daf5a8 | |||
c296bfcaf9 | |||
7d832ae100 | |||
4c7d833233 | |||
3f88de9407 | |||
2e728f7fff | |||
d543b13e5c | |||
00f3c58bb9 | |||
ed44073a58 | |||
2c41bb8490 | |||
1f8a243b67 | |||
17cf04ebea | |||
c7d0567d37 | |||
5615928df9 | |||
5349e46b46 | |||
93fc392549 | |||
89b5b97368 | |||
3b0b90527e | |||
f13ee3e692 | |||
0653db14e7 | |||
bf42807aa6 | |||
c195d22f68 | |||
9cf85d2177 | |||
ba3612774f | |||
dc6a7918e3 | |||
4052dd8188 | |||
47a1811e0b | |||
8eb4a9d395 | |||
ef2721b903 | |||
4ba4d9076d | |||
5652fb1018 | |||
44e47da4cf | |||
a153b61098 | |||
a4e956a7cf | |||
40da51f641 | |||
35c9f0dc2f | |||
d7efc45c04 | |||
dc40a93317 | |||
0a5a841994 | |||
746e313a76 | |||
5c2a90814b | |||
d4fa9744e4 | |||
5bc39f8c8d | |||
ef75875ca3 | |||
40035380e7 | |||
8f38eb7acb | |||
40039d8068 | |||
208ef7bd62 | |||
de4e17b76e | |||
73ee57a6e3 | |||
5e38056459 | |||
a55f7aeff0 | |||
3e3a1ef30d | |||
03d88c7965 | |||
b7519e5cfa | |||
7b13977c3a | |||
8fed1fe792 | |||
bc7b589611 | |||
5d8f7da3aa | |||
73cf7d5cb4 | |||
72c4fda613 | |||
01ab168774 | |||
aec463c606 | |||
30f29ee8e7 | |||
20b0c80b0b | |||
0b97d07ad6 | |||
7ca2a2a863 | |||
d3c0d92d28 | |||
916dabe5f4 | |||
91934ebb5c | |||
0bc35a71e2 | |||
ec789b0198 | |||
59c0689ade | |||
8f5b7f367c | |||
184a8493c8 | |||
1f3dd000ad | |||
f9f44bf743 | |||
500e49f0ac | |||
e3a5e24ef3 | |||
173a1ac8e4 | |||
2d89b5d13d | |||
3af103aa61 | |||
975a11b37f | |||
9c01ca42d3 | |||
4acb676f2e | |||
df816c9c80 | |||
2a8dd4758c | |||
d41988437e | |||
a7ed74f573 | |||
8fbb966ca7 | |||
6f68eae9dd | |||
bde4cd7982 | |||
835618c678 | |||
1e20b2ca36 | |||
294e56d529 | |||
8e354dae00 | |||
0a1a989fa9 | |||
c414f45ddf | |||
96cdf035d8 | |||
24f32e373d | |||
f9d483e76e | |||
af76651ccc | |||
f1ffa7a367 | |||
3887be464b | |||
f89d9f384e | |||
3400af6888 | |||
c839c05620 | |||
737ec52e9d | |||
3893799183 | |||
71b5970450 | |||
53f059ee8f | |||
a7d5c55926 | |||
ea991f7edf | |||
74c4fe10e9 | |||
40ae89e3bf | |||
983e487a8f | |||
5e55587633 | |||
561d402c65 | |||
6375454fe6 | |||
92b82605b9 | |||
899aafbb16 | |||
dde68ff954 | |||
e084835fb1 | |||
e17bde99f8 | |||
4253662231 | |||
a61fe4177f | |||
049f118c1b | |||
54fd33fb80 | |||
bd6a39c364 | |||
14744f27c5 | |||
b6e8847967 | |||
d7d359e3ee | |||
8cb1f65e7d | |||
22786c8e88 | |||
9228d7f15d | |||
39ef57971c | |||
17b862cf82 | |||
2562a3b1b0 | |||
66c06eb1ad | |||
d2a112fa72 | |||
3cc812711b | |||
fde89156fa | |||
2baf90209b | |||
646655d09a | |||
df75451a0c | |||
c91304f191 | |||
2aa940f55c | |||
34b84f61e0 | |||
dc4ae4b4cf | |||
1b72fc10eb | |||
4c693ea817 | |||
1997b86a00 | |||
a756161dc2 | |||
8e12707f88 | |||
675b5aca0e | |||
90692f0f2e | |||
d0d36a5ebf | |||
eef07539a6 | |||
7e8ce24116 | |||
f95b8ce07e | |||
3f4e02b8c7 | |||
5fbfe6996a | |||
183a8629a4 | |||
2f18e8565e | |||
a788d585f8 | |||
c013dd4ca6 | |||
7b56daffe6 | |||
19396769e2 | |||
bdfe71ae93 | |||
ab8199f7c9 | |||
d7c4f40171 | |||
99fdba0165 | |||
939e2f76f6 | |||
d05629be3b | |||
8c75f2155d | |||
be5e75c804 | |||
2ca7984d55 | |||
5576031b44 | |||
b9b512f729 | |||
27cd01f606 | |||
2780dcc487 | |||
d66a8c8189 | |||
9ca1faf865 | |||
b2222e7e02 | |||
e09abc5b5a | |||
a7ee2bcaba | |||
417ed3aa85 | |||
e34c7da830 | |||
e2b8b7c0be | |||
5a219e2588 | |||
f015d99117 | |||
dc63ceb5a3 | |||
d6bc63fa38 | |||
e8ea3be843 | |||
a6aa35e598 | |||
6091039223 | |||
f222bc1e30 | |||
1530c28e05 | |||
a787b9d2dd | |||
3bf3e5d760 | |||
4aeb6c4e78 | |||
eef047bc5f | |||
b9b38f0ad2 | |||
afdd405995 | |||
414dd95a0b | |||
acfd0edd38 | |||
7c9735a995 | |||
332937ef24 | |||
81241af7ac | |||
a92d97cda7 | |||
e1160f19be | |||
5f57376899 | |||
dcb8d6740e | |||
9a55020286 | |||
76b77ec3c2 | |||
2d5bb26b02 | |||
13be065ed9 | |||
045a989a03 | |||
ae0253f34a | |||
d5d9971c28 | |||
60471c092f | |||
5efe9be051 | |||
5ca7c2d40f | |||
26b17aee14 | |||
5de5b52beb | |||
daac33cdc8 | |||
f7e1c21596 | |||
def64efdfc | |||
843b3a2197 | |||
54c3a5da3f | |||
1c85c24ed3 | |||
b5fa4b58ca | |||
0cbdd54fd0 | |||
79742a397f | |||
5b80e944be | |||
53193c9779 | |||
16eb691e7f | |||
d3650ce3d8 | |||
bee6d73755 | |||
363e1ab775 | |||
dc5ac88a19 | |||
dcff6c9940 | |||
129010e4c2 | |||
6fb538ed1d | |||
c4ad12c1c3 | |||
10ad984334 | |||
27d16a7881 | |||
3c69442dbd | |||
14aa6d090e | |||
49dd7b6a0a | |||
532ccb647c | |||
ba0cdd3010 | |||
86db592b5c | |||
053dabb7ed | |||
22acdc2728 | |||
548b0e5fc2 | |||
722251da40 | |||
c286fbb9dc | |||
7526ef77b7 | |||
4e9d62ff81 | |||
a08f6d9795 | |||
4fc2678438 | |||
c73d7181ae | |||
9f99c9955f | |||
c35671c0a4 | |||
1f6da8c7d5 | |||
491654d882 | |||
12b49167b8 | |||
960e5e25e5 | |||
aed5c7caad | |||
d528dedd50 | |||
770980de69 | |||
7fb66eca25 | |||
991e665fba | |||
e210715958 | |||
6ddf5508db | |||
5cada5cce1 | |||
f7d4cc9cab | |||
8499bbdd17 | |||
51f47535e8 | |||
c2c4aea97d | |||
e6cb60ee22 | |||
7369dd6f24 | |||
0e3eeb4e71 | |||
128d4ac8a9 | |||
523c785e8f | |||
3c30474417 | |||
9601b5d18a | |||
ab931cf872 | |||
a8609ba0ad | |||
c8584e5d36 | |||
01308e4c7c | |||
65dbd50594 | |||
3d4067a464 | |||
a3c7ab99b7 | |||
4d36b2f6e9 | |||
f0b8f0cc7c | |||
2bfddcf29f | |||
2e52fcf1eb | |||
16e15f50d2 | |||
3cf55c195b | |||
cc43bfa725 | |||
171a79d04f | |||
6d11a81994 | |||
58b29f1503 | |||
60018d265f | |||
da604310c6 | |||
513c69169e | |||
c77cf717aa | |||
115b7e42c6 | |||
a622a281d3 | |||
e672b1f2ac | |||
7431206247 | |||
fcfce99e9e | |||
6b710f7ae1 | |||
c070037357 | |||
a4cacb55eb | |||
69b52903f0 | |||
68f51e0adc | |||
374a8ec263 | |||
a25cb2d1e0 | |||
9f5b490800 | |||
cd9ae66b35 | |||
471375adbe | |||
d72cfc9d57 | |||
37f39d6db5 | |||
c1cf46c5c3 | |||
ee8b8f52aa | |||
cf07d428a1 | |||
17650523a8 | |||
95383ee6a9 | |||
7ca74375cb | |||
a3de110b52 | |||
d5205a01cb | |||
a892054448 | |||
b8ffcf973c | |||
519e9e1ae8 | |||
4a5467bac6 | |||
b608fa55f0 | |||
b64ead5cb8 | |||
3822455928 | |||
8fd2a97c44 | |||
d43187f7ef | |||
fb852f7252 | |||
53c4c8de8f | |||
9c55fc0f13 | |||
4d88b4bc26 | |||
669df70da5 | |||
c8eb164f01 | |||
0f02e66170 | |||
9e351b8fe1 | |||
bd61cd0267 | |||
98a6fb9192 | |||
dff4e1e19c | |||
a5a598104f | |||
1cee9b32c3 | |||
d15d6b7a63 | |||
296dc0622f | |||
7f44fa65a7 | |||
e7c74cbd69 | |||
24864ee71e | |||
90b303faa3 | |||
304584c291 | |||
7069a83727 | |||
6e5cfd2cd2 | |||
36ff0dba1c | |||
c485c8a8c0 | |||
549ab2ecc3 | |||
09869af90f | |||
e7dff9eb0c | |||
0a1af2ff36 | |||
53b7bbf3ff | |||
b3bd6ca925 | |||
8fbb48cc2e | |||
724ce8c22b | |||
2f36f17fc0 | |||
c349cb2d16 | |||
3d4b1431ec | |||
4c3a543f7f | |||
7360e1ea2c | |||
241f0acfb4 | |||
a58034b316 | |||
be8d06c6ea | |||
3414ef276e | |||
8926f764a5 | |||
9bd9590767 | |||
4c7087ccdb | |||
5aabe93abe | |||
b4a711ea9f | |||
69de7680f5 | |||
4005815114 | |||
b7ff38b1ef | |||
54af220107 | |||
da7e362bce | |||
2227d471a4 | |||
2961bf06c6 | |||
94504ff5c8 | |||
76698d38f7 | |||
ef95da6d3b | |||
ed24100976 | |||
24b2f1da2b | |||
31e9dda2c8 | |||
0223aa6121 | |||
b50ed5c22c | |||
7659f2e24b | |||
ba82532812 | |||
5b42084912 | |||
ba2bf82540 | |||
fa8d324929 | |||
5aa0507f6a | |||
5b2fa3cfd3 | |||
fa4ea34401 | |||
3049878aa9 | |||
055533b6a7 | |||
4ba478267e | |||
eb4b7d2d8b | |||
970b22f98e | |||
fd3cfbb678 | |||
0c8d085666 | |||
cb11380515 | |||
5b9c96b9b8 | |||
af015982f5 | |||
cfe5dccdd2 | |||
22c957a93d | |||
6b468f9b2e | |||
93ffc67bfb | |||
43db4ffcd6 | |||
68025ce09a | |||
1ea04ffc05 | |||
48f8ca5483 | |||
32b72f39f0 | |||
d15cf60c49 | |||
03465b621b | |||
a107e9edc6 | |||
3a6cb6a5d2 | |||
ecbc25044c | |||
92c411f86d | |||
1be5b69174 | |||
f49b45832c | |||
77c53559d0 | |||
00f13cc074 | |||
e1fc44f5d6 | |||
13ce3756c2 | |||
32489c7426 | |||
61ad50d40a | |||
e7cf37d99f | |||
2954a14c3d | |||
cc956744d7 | |||
ef665889a1 | |||
f723a27a71 | |||
84d24c08e1 | |||
0d8309509b | |||
b5e9264579 | |||
11a4370e15 | |||
a3b2d6735a | |||
5b77e67dff | |||
39ec188003 | |||
def4127bf1 | |||
7342d4b6f0 | |||
20419d3d49 | |||
697f6a55a5 | |||
15ae924035 | |||
6c8e322fa1 | |||
17e98e1678 | |||
47f233a4a7 | |||
b04c3cce09 | |||
2f7857f0fb | |||
fae5c521d8 | |||
4dbc871169 | |||
2694a3b734 | |||
e0ad9ecda0 | |||
0b1e34de40 | |||
abd4628587 | |||
27b9eb5e38 | |||
f2df1c79d2 | |||
d56cf6a92d | |||
63c9c2d2be | |||
f2a545479d | |||
7dc74decb7 | |||
39ab72ebdc | |||
3102dc8344 | |||
436e7388c3 | |||
6174dffc2a | |||
6bb6588ce0 | |||
e0601df59d | |||
0b377bd445 | |||
a28c02bf89 | |||
1e3f41be8f | |||
81f6da173b | |||
e0827f2ad5 | |||
cc0f3c1422 | |||
aee67f08d9 | |||
a4bbc35005 | |||
99d73906a8 | |||
96a61d21db | |||
45ae62787a | |||
dcff76e8b9 | |||
ba662fa7a9 | |||
93c2df23bf | |||
3d5bcd5883 | |||
85b5c365fc | |||
da51d884a1 | |||
b7385a77ad | |||
bf8ba89394 | |||
9c211f9625 | |||
c9f24e6a58 | |||
649d01e52a | |||
5d203c318f | |||
dd03dca321 | |||
31abafe0a1 | |||
c7c38ac16e | |||
dcdaa921d8 | |||
f5a4de7428 | |||
2019b2c911 | |||
62cb1b56dc | |||
1f79e624d1 | |||
d7ea389c84 | |||
cb142b6df9 | |||
c3c72f689a | |||
9ceee07d83 | |||
d863526ca9 | |||
84c659e246 | |||
23cbfa791c | |||
ec147bd839 | |||
fa63c2160c | |||
b188b8c3ba | |||
3fada8d100 | |||
475d07c72b | |||
6cdfa73dd0 | |||
c2766f4e82 | |||
4f6924c622 | |||
55047eaa10 | |||
280aae6fc5 | |||
277681096d | |||
1a453872c8 | |||
6f916c1240 | |||
c8c6fd7153 | |||
7005645592 | |||
5260021447 | |||
7325053dfa | |||
15b4173a76 | |||
7d401853b5 | |||
58f10026c4 | |||
39b479dae0 | |||
2f31ceca47 | |||
ed2f5e0efa | |||
ca1bc7e80b | |||
cfbb1a1e77 | |||
eb6e1af46d | |||
8de3c20e50 | |||
ebcd59ae4f | |||
55037d1107 | |||
5f1f42ba54 | |||
3dee2244da | |||
afc88074cd | |||
0b9c4245f9 | |||
4b534358bb | |||
a44479d0a0 | |||
bfe9bc912a | |||
6d534f10e6 | |||
6d28a209e4 | |||
f6dee72a88 | |||
e88d652f2a | |||
aa0244765a | |||
538d0446b5 | |||
7e9ac7650b | |||
7eddbe5d7f | |||
ae007513f5 | |||
6e5774b9a0 | |||
56ef500716 | |||
e2fe62d4cf | |||
5e60215470 | |||
01d1da67ee | |||
eabe961197 | |||
b788c36909 | |||
66fbc2a1b8 | |||
24679d8676 | |||
88c7dfefe6 | |||
2f810b2c1f | |||
6d385e8cb6 | |||
475ffd67f5 | |||
efdf347f9c | |||
9e0583045c | |||
7a63e4f7d5 | |||
753b5bc305 | |||
79a0d007b4 | |||
67044ef55d | |||
c3e8439954 | |||
4754e495dc | |||
3fda893f00 | |||
da4782e67f | |||
0e52d9297f | |||
d0400a1188 | |||
8042433cb0 | |||
1ba6fd3f42 | |||
7203169c3d | |||
053b23d902 | |||
9bb349e1c8 | |||
96643e7ce2 | |||
cb6ffa1211 | |||
4d95c3d39a | |||
c3271ac22a | |||
b1eb84c57e | |||
794dcb58d3 | |||
b3441eea4c | |||
25eaff447a | |||
9c5510b28e | |||
6315acae94 | |||
c3f14bb7a5 | |||
181d766941 | |||
97c3e9d59f | |||
443e423b58 | |||
f87b84593b | |||
f981dd9175 | |||
e48e36bde3 | |||
99cc7cdc15 | |||
0083443050 | |||
8815ace418 | |||
9f65b787d0 | |||
660546d0ec | |||
58cdc22791 | |||
570574df5b | |||
efbb147f65 | |||
02baaa4813 | |||
8942b7106d | |||
bcf55b0b4c | |||
d909fb0b1f | |||
6fda7f3da4 | |||
07ea6cf582 | |||
c38195f59e | |||
6606ce69f6 | |||
17d5e2bc99 | |||
d5fd742763 | |||
6654f82522 | |||
02e4921ac9 | |||
39bf104a03 | |||
b3398eee61 | |||
18d89c9c89 | |||
a00f82797b | |||
4d7a9db44c | |||
852746e032 | |||
1b6ad15bc5 | |||
c288975898 | |||
8f389361ec | |||
7cdc118c8e | |||
d66cf31ffc | |||
c2d03e5e21 | |||
ccb7bd0630 | |||
ff72751f13 | |||
10e29355db | |||
4f42de9704 | |||
69950e3888 | |||
e2b184515b | |||
3053e022d3 | |||
b9b8920227 | |||
b4c5bdb093 | |||
0254cba4b3 | |||
effb92dfae | |||
5b864ede13 | |||
ee46b9b44f | |||
c079f38cbb | |||
ba2fd31e62 | |||
eab7f9f101 | |||
e14e43bda2 | |||
e116816131 | |||
2e82357611 | |||
1de49ba369 | |||
f27187c063 | |||
19944c2424 | |||
8410278b6d | |||
9b9116c79d | |||
2dffe65cfd | |||
260a061f9f | |||
cf37c003ff | |||
dcc8ff4ce7 | |||
6057c7a373 | |||
498a2ffba3 | |||
047488c5d8 | |||
3959511b80 | |||
024e3b30ac | |||
8c6468a025 | |||
23595272fe | |||
9264f43511 | |||
918d8c9909 | |||
31be29a9f3 | |||
f22a6eb00e | |||
e9fb5fdb89 | |||
47666f548c | |||
0b837e2f0d | |||
dfecca29da | |||
1eae7c81e9 | |||
a04f7c0d5f | |||
9d1175e2b2 | |||
3e201181bb | |||
7938ff34b1 | |||
b637b9322e | |||
6bfe214346 | |||
9ba8059e88 | |||
763f8d470a | |||
9cabd6638e | |||
cd8a837956 | |||
5cecd97493 | |||
5df8a3ba95 | |||
28b4f4abce | |||
5eaab85fc0 | |||
357a0733c7 | |||
835ed0f35f | |||
1144ce97f9 | |||
23cf11a788 | |||
d0a04bf309 | |||
ddc229b69b | |||
12444a8afc | |||
a186dbc1d4 | |||
04ab03664d | |||
6d3a25d897 | |||
3bbd12d560 | |||
c049cf2206 | |||
3ceb2b85da | |||
fac997c53b | |||
f79110c637 | |||
c2a904da09 | |||
ded78e5688 | |||
1efa0ca4d0 | |||
758f7a7d8e | |||
8eb0596463 | |||
c45a70c3b8 | |||
0100a39e21 | |||
17f7f06ca5 | |||
ea57587b7c | |||
a781800276 | |||
b405942b0c | |||
0fba79cda2 | |||
a9e3cbd534 | |||
af95dddd7e | |||
ff0a91422a | |||
014a7137f4 | |||
4945274080 | |||
8c2cbdd385 | |||
2f5ddd9c96 | |||
6e5f076330 | |||
3255d2b197 | |||
6e7ca05efc | |||
6ba5fdc208 | |||
74edde0a94 | |||
50cdc0ac1b | |||
252e3e9487 | |||
c13a4b8c03 | |||
05c1398b4d | |||
1583293694 | |||
ad987021ce | |||
9a800e8fa2 | |||
0bf9263297 | |||
ef179e6a00 | |||
caaeb21e59 | |||
c77656e2dd | |||
5f2897be0b | |||
6ce940315b | |||
656607b640 | |||
b0534177e0 | |||
7df2b4aeff | |||
bfeaa7d564 | |||
dd3b27e971 | |||
db4f508737 | |||
08ea38f197 | |||
8572911e94 | |||
f503832abe | |||
b72c7a89a9 | |||
a8fced8846 | |||
0984fbc748 | |||
22ad701134 | |||
c9df9afddd | |||
c8b5b81516 | |||
c8447d21bd | |||
4ef6f1ab2e | |||
bb52fb798c | |||
2ecf9f3f2a | |||
8683c0335b | |||
419b153b17 | |||
f95e2c7e7e | |||
659356a97b | |||
ead169a402 | |||
95429d55ff | |||
0f0fd25038 | |||
9fa2c398e7 | |||
13c2fad240 | |||
7823c23932 | |||
2f3d41f081 | |||
d3069dbec6 | |||
f98aeca146 | |||
a1d0f1e5d2 | |||
a12b5f930a | |||
b342a69bbc | |||
718d7fe5fe | |||
f72de51a6f | |||
89ef77f750 | |||
e7cf1e0580 | |||
7a6e326ec6 | |||
613dc1122a | |||
3805172f9c | |||
f05e1719cc | |||
a91ca99b1f | |||
7a86a32040 | |||
148a060daa | |||
2ebaa51514 | |||
0d95c08cda | |||
d6f278867f | |||
0341f3239a | |||
421dba0184 | |||
de62d8ebdb | |||
02134172dd | |||
023c9bebe5 | |||
ecb29de82f | |||
1084d4ab49 | |||
37bb90140c | |||
542f236dc4 | |||
a188312929 | |||
61e8ed6623 | |||
c0869ecfb3 | |||
28ef1af376 | |||
e71d151571 | |||
f9e959e098 | |||
634887c0e7 | |||
5e94f9a100 | |||
c4ce24647b | |||
bc7cde0f01 | |||
cca2616637 | |||
9b6a1b85b1 | |||
c50faa97ca | |||
60b13d9948 | |||
e315215983 | |||
a295255e58 | |||
fcbc38cb22 | |||
5a52990b91 | |||
2cf25facd9 | |||
bd820fdf84 | |||
627814cc4a | |||
8f102fe674 | |||
15d3e741e9 | |||
3c2438425b | |||
86d5472d91 | |||
671deadde1 | |||
f84f362de3 | |||
e6909bda89 | |||
539d8f09e0 | |||
6b1a47183d | |||
41485599f3 | |||
f6a6f26e0f | |||
f16f6a290b | |||
cebe49d3c9 | |||
e524322c43 | |||
8e96b450e2 | |||
599a55e691 | |||
0971d305e1 | |||
80d326bd49 | |||
e12933a3aa | |||
67eac733d2 | |||
56f4e56094 | |||
dd944ef73f | |||
c60d7563a8 | |||
d4b83681f1 | |||
0cd8431698 | |||
755d2d572f | |||
02958c07f6 | |||
a6b6d74c00 | |||
e6dbcd0f46 | |||
d529b55651 | |||
180a41af68 | |||
1421eff382 | |||
d8792b3c36 | |||
b8eb2f2f23 | |||
5a7f33496b | |||
e7cc1d606a | |||
5b04abd5c0 | |||
040673464c | |||
7eccbcd30d | |||
99320e1ffc | |||
39722df41e | |||
c16a79df5c | |||
72d366fe21 | |||
1425e63029 | |||
72abde603d | |||
bbce2ad7d4 | |||
2cc954d5a5 | |||
3408468121 | |||
b15f86e1ce | |||
d251f22ebd | |||
df6d6e0b1d | |||
ea37e82e69 | |||
168abc6d6f | |||
6108962291 | |||
85b551a388 | |||
c5006e025f | |||
5a227d8d7c | |||
de7713d6ea | |||
2fb9b7ff1b | |||
658087be7e | |||
8c33f91529 | |||
0366743fc3 | |||
55b7fe9998 | |||
ee7857300b | |||
d5aedbe892 | |||
92c547830a | |||
c291c8e2cb | |||
005c4a1cc5 | |||
c4335e2817 | |||
3f68377c3d | |||
4413660d47 | |||
4ece0eb27c | |||
7f0d7f4d12 | |||
49804fe017 | |||
6d7d2e439c | |||
807c89441a | |||
4022376b3f | |||
5e3f6d203d | |||
f9def8cd30 | |||
3414760bdd | |||
48a1acab52 | |||
78c65c330f | |||
5ff98fe701 | |||
af2d22c43d | |||
35a57cb213 | |||
fadb2d9004 | |||
f0f426b2d0 | |||
5de7960f01 | |||
d655093123 | |||
953365d090 | |||
7cd16b9e2c | |||
d25de63ac8 | |||
1a0ee18d62 | |||
1ab2ebc719 | |||
ed55355363 | |||
9ba5344f3e | |||
25dcc7631f | |||
ec495c807f | |||
9b5299131b | |||
9bc8864d03 | |||
56f33bc266 | |||
31e2bdffa8 | |||
d80eeb6959 | |||
8969f35bb5 | |||
478f8821a0 | |||
04b12fc0d6 | |||
f2800b90b2 | |||
44225e4010 | |||
51a56bc4c6 | |||
1217c6b7cc | |||
8c76e78ec4 | |||
5129bccebf | |||
fe91700dc6 | |||
e24453b9f8 | |||
4cf197998a | |||
a4c3ceeddb | |||
17857a311f | |||
588823b437 | |||
de043a4bcb | |||
955423c79b | |||
804933284b | |||
d752e26eb2 | |||
ff1be4cd28 | |||
485ce640fd | |||
dca6ad87fe | |||
356e6a88f4 | |||
621b659aa9 | |||
82be6215af | |||
3c335c3590 | |||
a37bf9a516 | |||
9f211ba0f5 | |||
bb79018f92 | |||
310ce6dcc2 | |||
5024e217d7 | |||
2dd572b15c | |||
828e5c1f79 | |||
c8ea99f9da | |||
7d579b913c | |||
3d69693692 | |||
fd5f101bab | |||
2303dc4f28 | |||
9d7b939554 | |||
e128d87030 | |||
acd8734937 | |||
4d03ec0e85 | |||
bf1bcd1e08 | |||
346e1c14d9 | |||
df71aa425f | |||
715d02aa14 | |||
e53375b7b6 | |||
891708cfc9 | |||
fc2f6b8456 | |||
11a35e5d14 | |||
4ae754a222 | |||
b07b6f1d40 | |||
70311ebca1 | |||
fd83d9479a | |||
87994d2c03 | |||
55748dbc55 | |||
6a5475f65b | |||
5af3bd4728 | |||
e13aa65f49 | |||
7b4853bae4 | |||
51cee50ee3 | |||
27562e92db | |||
d78d29ff8c | |||
08a4f10ee7 | |||
b54ed980ed | |||
eb6975acaf | |||
97fbdab3b8 | |||
4be8929844 | |||
cf9aa4fd14 | |||
9e45203679 | |||
b7fd86ec50 | |||
a959fae66e | |||
bb290cefae | |||
850aee2448 | |||
51720745dd | |||
6bf2531b19 | |||
e698d355c1 | |||
da0c372fdf | |||
5aec1798eb | |||
8a052dc858 | |||
f69c6e204a | |||
8555d51bc7 | |||
ad4300f52b | |||
3557849371 | |||
be58159550 | |||
5c23ea71c4 | |||
288009d6fb | |||
29b8666b10 | |||
c68200cf42 | |||
90c3d98dd9 | |||
c44415494a | |||
96499c898f | |||
ffa3936b4c | |||
a6247aafa1 | |||
62f7d0fe5c | |||
74e6d379d8 | |||
49c9f782ab | |||
a681c8553a | |||
f4caf263d4 | |||
15fefdbb8d | |||
71b8e271b3 | |||
341d5847d6 | |||
2e7a3e9ae6 | |||
5fb7d3d3b0 | |||
6bc28b925c | |||
91a2506c82 | |||
cec60b792c | |||
7215889b3c | |||
32a4a549fd | |||
985cadb73d | |||
023c4ffdde | |||
06bca0cc66 | |||
526537fefe | |||
5beae0633b | |||
e666d283dd | |||
19a6c158d2 | |||
e2cba6ed7d | |||
a48573efe8 | |||
6fadb7a3a4 | |||
53fc2ed8bf | |||
d27f0b5c22 | |||
e7dd7525cb | |||
a67cb92e6b | |||
592fd37b3e | |||
7eb3e3bce6 | |||
c89eed56b6 | |||
b90aac0d43 | |||
9d21065e25 | |||
7f2885ec7d | |||
e6045ee0b7 | |||
f2828a08bd | |||
cbb1d9f2bb | |||
1d3aae6f92 | |||
ab0bcee144 | |||
c5a75fd807 | |||
c365eded5b | |||
7c64b1889f | |||
8f034896a3 | |||
7bf3e70553 | |||
695bb6ca87 | |||
ffe57a004c | |||
336c2d5461 | |||
e51ec671a5 | |||
96c9ccc81a | |||
eca85d5880 | |||
fc78c64e8d | |||
e25b256aa4 | |||
9935aa43ad | |||
784fd26473 | |||
f4cdd35b3c | |||
fe4447d568 | |||
72b9276ec9 | |||
b30bb8dd91 | |||
033aba9351 | |||
704775168d | |||
c79d50f38f | |||
624425a288 | |||
fcdada53f1 | |||
9a68f23dd2 | |||
78912093f8 | |||
ac08a499a3 | |||
7df7e340ce | |||
0fecea1427 | |||
f1b32840d0 | |||
18a8abb2bf | |||
1d563a7acd | |||
1e1e242570 | |||
d21471e24e | |||
9c6ee5fcd0 | |||
7f24975a60 | |||
95715fc71e | |||
6888cb3d7a | |||
2be0a1d35b | |||
cf10b336e7 | |||
9485e16e3b | |||
84a0105625 | |||
f922903b62 | |||
9a840e5021 | |||
1389d173fb | |||
7a29b24720 | |||
15f8638b1c | |||
31c5e67ba6 | |||
6899ee5ddd | |||
b62b11bd6b | |||
641c671bac | |||
2f0b8bc541 | |||
db2c606d41 | |||
e31f62045d | |||
ccee818034 | |||
2ce9cdab9d | |||
74b7d1ace1 | |||
f1b0547f0a | |||
e1ee90c218 | |||
a972e4cd4d | |||
fcde671ae2 | |||
8b91ea5532 | |||
942e2ebe44 | |||
472641cea0 | |||
e511bfcab5 | |||
c303371b26 | |||
f7475870a6 | |||
d8be830fce | |||
b3c3000004 | |||
52d1500155 | |||
c6ebcd1eb9 | |||
eece138fa7 | |||
ca1ff3ea11 | |||
a8f9ff52f3 | |||
a1e9956290 | |||
1527ac6c47 | |||
6cb9988d63 | |||
b197e90206 | |||
976bdf1087 | |||
5c6e36eb95 | |||
0260b8c795 | |||
39712bcdb2 | |||
9d99c7244f | |||
05d7c575e4 | |||
056236cafd | |||
1d429b2165 | |||
a40103d849 | |||
b97577bd94 | |||
4385cbaa65 | |||
98766b1d07 | |||
afbaaf93c5 | |||
1db86500de | |||
570936e39f | |||
43ffe7257a | |||
c4fbd0bef7 | |||
f63e5d9319 | |||
c57759f191 | |||
a33162bb66 | |||
c77faf566e | |||
e51acd5bcc | |||
71b400eb7b | |||
0ca2a390d5 | |||
e8d43d8fde | |||
81828ae7f4 | |||
1aa3053b12 | |||
7c5c2139ab | |||
8912b11f56 | |||
8d72a37f3e | |||
becc820159 | |||
68a1edd4cc | |||
42d394973d | |||
bca437617f | |||
1ebe172c2e | |||
c25503b142 | |||
10583f951d | |||
39550746f8 | |||
e3895b8a95 | |||
c0f69f3245 | |||
55b1b7745b | |||
a20979d5f6 | |||
2ed04ffcdc | |||
cf598f71fd | |||
1c22e464b2 | |||
fe12d0dc78 | |||
7b87392f47 | |||
e2d7b25e0d | |||
d749dd3ea1 | |||
2a4061a3fd | |||
41560b47c4 | |||
e89c2dd8d0 | |||
3297a76195 | |||
5453c4cd96 | |||
8e6bed988e | |||
8c6fb17d29 | |||
3de72e4124 | |||
d751ca7596 | |||
974005b064 | |||
1bd8fdb766 | |||
29007e406d | |||
c0ad47a3fb | |||
4b62ba9017 | |||
0addaab270 | |||
bf10e98891 | |||
44623a1161 | |||
c5894e08bc | |||
7e8eec57f0 | |||
5b21b71c9a | |||
cd6ab209b6 | |||
a3c44124ab | |||
502dd89290 | |||
85298e345d | |||
93a23b9ae0 | |||
90f33dd11d | |||
8d0de89ece | |||
d9a38928f5 | |||
25aaff2ee1 | |||
5bc1b6f9ac | |||
4abf15c50c | |||
9c13d6e8e6 | |||
91147ade2e | |||
e3189f97ff | |||
4ff43e1324 | |||
66725b7b37 | |||
5437e2da29 | |||
c83f5013bf | |||
ac9d044cad | |||
9e83814e61 | |||
f185fabc11 | |||
4857c53a49 | |||
4e7ffbfbac | |||
02308c4f71 | |||
a8f4f14bdc | |||
f02213a661 | |||
09536423e8 | |||
fc8eecad3f | |||
feb5f27d09 | |||
31116f0ced | |||
9ebac71521 | |||
ce30888a26 | |||
4d4b527474 | |||
602eeee7d6 | |||
38758d856a | |||
9db59d010d | |||
72eba7745f | |||
083d48e072 | |||
df1bef31a4 | |||
4452d6d848 | |||
41caafcaf2 | |||
2c208f98a9 | |||
3ff712a0f5 | |||
1d141a8ab1 | |||
d5ae854b5b | |||
d863839ecc | |||
66ab701606 | |||
64ac106248 | |||
4099822ba2 | |||
7355906a95 | |||
2b1ba1835e | |||
12703d5601 | |||
e4ebffab8d | |||
c4695bb387 | |||
7caed51c24 | |||
2eed7e33ef | |||
a38508122a | |||
c35587ba56 | |||
31dccab2da | |||
300d7ca6da | |||
563a507315 | |||
a5167bd53c | |||
87743f1aa1 | |||
502fb7e307 | |||
f197191a5f | |||
7e135f6d3a | |||
fba72f0d4d | |||
5666d11633 | |||
540d104b17 | |||
7193e151d7 | |||
1de757993d | |||
5ed6abe3df | |||
649093c0ec | |||
bf913cc39b | |||
ba0c178dbb | |||
c3e93564d0 | |||
d1246a1d10 | |||
f4160b5d5d | |||
936700ad9f | |||
a3034ef92e | |||
bd40c89688 | |||
da4eb91283 | |||
afa50838a7 | |||
4e38cabc49 | |||
27745c5d8c | |||
2f6c97e93c | |||
8b5ca670ad | |||
89704cc5f3 | |||
2c623fde16 | |||
c2f13a1e3a | |||
e483acaa17 | |||
e16f75db56 | |||
c61f413477 | |||
eb0d8c09e3 | |||
d8d8b8915c | |||
4ee354da99 | |||
41979d6a27 | |||
62b2840822 | |||
e23bc4991d | |||
7a280b1a21 | |||
26f44c0d6b | |||
5607ad8c62 | |||
335854f6bc | |||
4d23b60d09 | |||
eeecbf28e4 | |||
4b81bb5c97 | |||
14c4b1b205 | |||
3505692f75 | |||
8d15bfa6ee | |||
e4e8dbdee0 | |||
f085fd5666 | |||
49bfc7421a | |||
01455d70e3 | |||
aa36b55bd9 | |||
348615be62 | |||
e2310732d7 | |||
a78b70178e | |||
6323a35468 | |||
b381497126 | |||
818c514968 | |||
6b267482d7 | |||
2e5e1dd5f5 | |||
3858b26211 | |||
31b9492951 | |||
0de2dbfec1 | |||
dcdb433b7d | |||
ee4fc12e42 | |||
3f195fefa9 | |||
a17cc9beee | |||
0d9be22023 | |||
113411c9b0 | |||
d8ce2129d5 | |||
4aa51b751b | |||
09a2bb839f | |||
9ea3430a5b | |||
4821330a17 | |||
d883007cc6 | |||
4c4217c5b7 | |||
93a0b1ba13 | |||
73530a9e25 | |||
b00189bb9a | |||
a42057d0f8 | |||
9a5e08f2a7 | |||
63f0ded5cf | |||
25ed82db23 | |||
f76b370d70 | |||
fb84ea74fe | |||
f17072c7af | |||
fde8363e0d | |||
ed4244e932 | |||
efbbae5a48 | |||
22e483858e | |||
122f755b21 | |||
3d11355fec | |||
2805af900f | |||
2f3812a1ef | |||
f1269d98dc | |||
a1d7b6bb86 | |||
d40ee6a259 | |||
c79d6ec502 | |||
0e9140c738 | |||
2f541a979e | |||
e2211ed211 | |||
77240e1b60 | |||
ba29e4d953 | |||
355e54a410 | |||
1b607529a6 | |||
2ecc4c7886 | |||
3f257e96c6 | |||
8bc5fb2ab6 | |||
b04de1363d | |||
bd89626e2e | |||
66519de2dd | |||
f013515307 | |||
43ac02e566 | |||
ed4d96f858 | |||
383457f898 | |||
49eec5d872 | |||
c0b90c2010 | |||
398ff1e7e7 | |||
b3d6d500be | |||
1b8b04cf26 | |||
5632424d04 | |||
f1ffd57105 | |||
7f7dc7c294 | |||
5d86e4a9b1 | |||
aaa08f7be3 | |||
58b3a51e64 | |||
03103d2d59 | |||
447e251736 | |||
72d7eb7a93 | |||
df92abc60a | |||
65a0d2b53d | |||
e030375d9a | |||
c4733c15c0 | |||
a86a179f45 | |||
ec482dadb1 | |||
427a1ccd9a | |||
403bb6bee3 | |||
418e9cf3c4 | |||
fa0ab38546 | |||
3b643b8f3c | |||
37a87418b6 | |||
2ea52b0cb9 | |||
931739ac73 | |||
717bace7ba | |||
9de4b1c441 | |||
ff36a8daf6 | |||
8e2e1f8340 | |||
3ae72dccfb | |||
3f273f8d63 | |||
fbf6ec8813 | |||
56731f624a | |||
e5081bcf25 | |||
c0ebecf54d | |||
160547dad6 | |||
6471a2668e | |||
609d2557bc | |||
abd2a58c67 | |||
d7dc6cbc04 | |||
8017229292 | |||
755a80c7ec | |||
1b4eaea6d4 | |||
d4db746898 | |||
29bc3a775f | |||
8d44b5edac | |||
f289411fa9 | |||
32b042014d | |||
940fbbb014 | |||
1799f621b7 | |||
cb4f803238 | |||
0e08ad628a | |||
e0059c7d51 | |||
e122d27176 | |||
bab740d793 | |||
60e4490808 | |||
251a512222 | |||
692535a935 | |||
7c64b8d3fd | |||
c44a1a78a9 | |||
f6667f8281 | |||
6958d11d95 | |||
bfd07b3c94 | |||
01d3599f32 | |||
a0d16dcfea | |||
f0366843ea | |||
86e1e6c082 | |||
117ca7cf39 | |||
7fb55f29b6 | |||
eaa1984d41 | |||
d2222541e8 | |||
9e7668f16b | |||
5dfbcd5631 | |||
de445709d4 | |||
50710838bf | |||
4da2dda647 | |||
afcff73be3 | |||
2196114501 | |||
fd4fed14d8 | |||
422eb14dc0 | |||
7b64680670 | |||
d5b59009d4 | |||
f45c43188f | |||
9d54679e66 | |||
7e64bbe5a8 | |||
1a8bd22fa3 | |||
78214e72ea | |||
bb53b6549c | |||
43487f6761 | |||
6f203c9575 | |||
08cb2fa80f | |||
6c3c030f0d | |||
b60541c92a | |||
d1323b5c8f | |||
06093559a2 | |||
4800fa1c08 | |||
cc7c2a81df | |||
ab09895810 | |||
a9fd36f2f8 | |||
16fe90d6ac | |||
0e260d2c3b | |||
3dc6490e72 | |||
082aed6e46 | |||
d192a7b47a | |||
9e9491272f | |||
e4504a40bd | |||
a914859067 | |||
724707c6e4 | |||
e9ee6859e3 | |||
a67df6520a | |||
e71a93afb3 | |||
25bc56ed64 | |||
31b5db6fa8 | |||
11bf7679a1 | |||
43241a560a | |||
becd62d4a1 | |||
86104b82b8 | |||
cda9248b33 | |||
3e14c2d02c | |||
aef7dca234 | |||
21c1e14385 | |||
4c63e6ba04 | |||
4659cc26ea | |||
67c914819a | |||
51b9ce44ea | |||
d3f3d9b4cb | |||
4198ea7ca6 | |||
8c57a23b90 | |||
ad72c90447 | |||
11e04b1892 | |||
ec6a9f2a02 | |||
30d25f67af | |||
ca94d2b7f0 | |||
0bdb71c122 | |||
1cda80eb3a | |||
a86893c10f | |||
ce7f934c66 | |||
6bc016f3fa | |||
b6dfc8b08c | |||
8b0cb2f0c5 | |||
ac745c8356 | |||
a9228aabac | |||
8321bd8a74 | |||
72494c4411 | |||
10dcbcf45d | |||
5265b74138 | |||
64aae3a9df | |||
4c0726db9c | |||
fc93dafab1 | |||
d62eff7316 | |||
f433d6604b | |||
e409ed0eab | |||
0de2a5e408 | |||
c3aaa5211e | |||
dcbc3b197d | |||
0004896ff9 | |||
5e694e519b | |||
cd7b199219 | |||
f05999730a | |||
ece0b2d7ce | |||
50bf17aca0 | |||
39587ad127 | |||
5557dec120 | |||
52483bf680 | |||
1353afc2b1 | |||
b04488d692 | |||
68f06c8dd6 | |||
e2d5bc2514 | |||
868b3f9463 | |||
83425fa119 | |||
1845faa66b | |||
d617373a76 | |||
127cec0214 | |||
4b30108734 | |||
25af147a8c | |||
1a34fbce25 | |||
4acf0a09ac | |||
84ba1f012e | |||
1a67d70bf8 | |||
d8249d1230 | |||
c88305d2eb | |||
64fd0d6db9 | |||
d4d07233dc | |||
dcca80bb1e | |||
b800b88224 | |||
0b1daa9ebd | |||
15e3b5f531 | |||
0c69ec20c2 | |||
0dcff40ec2 | |||
b934898e45 | |||
9c153cfb3e | |||
c8fd94ea44 | |||
2ddc851090 | |||
f54adf10b5 | |||
b0834fe962 | |||
6ab5f3648a | |||
1ae77da609 | |||
100ba0bd06 | |||
df78d7c0d9 | |||
90007e97ca | |||
916762440c | |||
35a95a8a7e | |||
728cd8446f | |||
f640a4a494 | |||
5ede5b7807 | |||
af1f27e756 | |||
b2decf0266 | |||
8d111da7f6 | |||
22d4efbed1 | |||
3001716a2f | |||
ebd557c1e1 | |||
d349cd91b1 | |||
a2d2a5d572 | |||
b2666a2857 | |||
9abc1f9156 | |||
4283e1f784 | |||
4541b9b565 | |||
dcd28b591d | |||
4e1f37fc17 | |||
be54c580bf | |||
676a95a21c | |||
a3ef3e11c4 | |||
57d099ceea | |||
6028159dfb | |||
748edb81ec | |||
305f3686c3 | |||
009cab8dce | |||
90855f331f | |||
2cdb3a079d | |||
d5b87d32b0 | |||
b2b917d2d8 | |||
9f0c549bc8 | |||
affae99b22 | |||
ed711418f1 | |||
c653a16b57 | |||
64823f561c | |||
e1d07b2fd2 | |||
cdfbda3d3b | |||
78ada980a6 | |||
3830f6fc14 | |||
f783244ad1 | |||
2265cb5938 | |||
c507dda21a | |||
40d87dd801 | |||
0ce8621196 | |||
2089727db9 | |||
c1bb88603e | |||
fee28e20bb | |||
3efb060127 | |||
c61e4d7841 | |||
393398e6f5 | |||
e007918e35 | |||
3c2770bfc7 | |||
90fb5d9f7a | |||
d18289fa9c | |||
bad3434337 | |||
0119f46daf | |||
f8eca840ee | |||
7f6429d802 | |||
94b9b7e154 | |||
3a53e2c960 | |||
01677b21b6 | |||
60047037a3 | |||
e6881b5b42 | |||
8593d0d52e | |||
53d13c3fc6 | |||
442f323a32 | |||
480b775ba7 | |||
32b16de8ea | |||
ea9245446f | |||
ad753d3fa7 | |||
7c9219f029 | |||
eb72cecc42 | |||
bc886a64a7 | |||
75fd407bbd | |||
adb562bca6 | |||
900d0055e0 | |||
7806596fba | |||
deaac32bbd | |||
65297cde19 | |||
fca3e79415 | |||
bcbf3e4123 | |||
fc28b266cd | |||
447fa2fccd | |||
0f21ae9a74 | |||
9364a14d36 | |||
efbce7501b | |||
5332b04f35 | |||
6f5f481fda | |||
90dda5873a | |||
7503e3540d | |||
32e315755b | |||
9188751adc | |||
d24ade91b8 | |||
9b15588188 | |||
97d5700456 | |||
601f87c2ec | |||
052cae6427 | |||
e7729b6863 | |||
0d186dda35 | |||
3de59d48b5 | |||
3c7ac5f835 | |||
41ed694d01 | |||
954b8e53a2 | |||
a3b6b109d8 | |||
4e35e348af | |||
2729747225 | |||
be83ae752e | |||
70429bf350 | |||
add5c3992c | |||
04bfe87764 | |||
879ad69602 | |||
39e8ceb2cd | |||
448749cafa | |||
25219baeb4 | |||
948b01ce55 | |||
0f3a48e4d4 | |||
653eb0fef9 | |||
fc3260d87e | |||
296954041e | |||
1115961892 | |||
6db5c4a6f6 | |||
5d5c94d83a | |||
090732fc2d | |||
206fb82330 | |||
63256b511a | |||
01e4d44e8c | |||
66658c447f | |||
668f57abaa | |||
72f3747d7b | |||
088435c5eb | |||
32b6548be9 | |||
98feee7e0e | |||
28358b6395 | |||
9e7aa60ae7 | |||
ffc34b3676 | |||
b3549e6680 | |||
475e36abb5 | |||
53b32f17b3 | |||
45c893d0e1 | |||
c32b2ae0a8 | |||
747f0cff9e | |||
002a97d852 | |||
f438ae8a3a | |||
d5071651df | |||
8985233804 | |||
877f9ad7b9 | |||
a23ee2b0a1 | |||
e41cbfb585 | |||
84feda1db7 | |||
ecd7554051 | |||
5ae48d52e6 | |||
966c2a326a | |||
278d634723 | |||
f6914159e4 | |||
2e730287fb | |||
a7d090d22e | |||
85990c3213 | |||
fbad4ff65c | |||
c07b6fece5 | |||
e9867e8ae1 | |||
0ea4875b10 | |||
c1346462db | |||
c8be987b40 | |||
a54adcaff0 | |||
fe54af9ba6 | |||
8da26d9de9 | |||
4dddee7424 | |||
39bc6f7bea | |||
948714c17c | |||
e668d7971d | |||
3a96631387 | |||
c093390c22 | |||
32b6c2285e | |||
86fd5719b5 | |||
32f4544f34 | |||
f8f7c1540a | |||
52552b0520 | |||
5651fa3a95 | |||
353368cccd | |||
7a00c676ec | |||
04ddcfecd8 | |||
f3859ff2b9 | |||
0450f39625 | |||
5d12cb9fdf | |||
ec0aa88f4e | |||
f88f717094 | |||
0618bed83e | |||
708ae4c8ef | |||
5c2e890d76 | |||
5ace90f04d | |||
9d8dc793da | |||
2846505dbd | |||
ab29874f09 | |||
9b0861c649 | |||
df1c456347 | |||
05e1b3b312 | |||
3ace25f4a1 | |||
4726ac2481 | |||
252966bcca | |||
f1b1de9a3d | |||
4c168ed9ba | |||
cd4021ba41 | |||
a0d04c628c | |||
b741a1c3e7 | |||
357aa4a097 | |||
870d189433 | |||
c7abb7d196 | |||
fd62ed66e3 | |||
e5a3de575f | |||
894c4b5390 | |||
d9054803e3 | |||
4ff9c942c0 | |||
67d08b15e0 | |||
d2bf8fac5b | |||
a2e890e4f7 | |||
4f41473048 | |||
e6ed4a21e4 | |||
4c061271db | |||
52a6da043d | |||
1748aeb9c8 | |||
f6d66671b6 | |||
3a90a7cb43 | |||
8415460b12 | |||
217db9b216 | |||
be13e8b1e4 | |||
bfb3995869 | |||
5328bb223a | |||
0477bfc8ed | |||
e1065eec5b | |||
082b0d3ef8 | |||
c442c0334f | |||
c7ea3260bc | |||
bbb2798d41 | |||
2bf5606bbe | |||
e82f56b96f | |||
3c7da767d8 | |||
c4e039a43a | |||
5a830c49cf | |||
bd679581e2 | |||
ad178c55fd | |||
adeee0fa7f | |||
88c28ce208 | |||
e013aee636 | |||
373e1337de | |||
2279cb8dc0 | |||
222af38462 | |||
73cb581728 | |||
527ce3b142 | |||
f06505aafd | |||
51292e27c9 | |||
6674746e86 | |||
024765b86a | |||
eab959aa18 | |||
1771d6ff25 | |||
7a569a7c52 | |||
5cf597249c | |||
0a0489d3d0 | |||
fcdd06896e | |||
ea378a993a | |||
5f095a501e | |||
a85eccd6ff | |||
5356826a38 | |||
97dae900fb | |||
f0a969579d | |||
b6f439d91b | |||
d30cd3309b | |||
a32df388f8 | |||
8a911773b8 | |||
f5982fd746 | |||
4f033235b1 | |||
53dbff66d7 | |||
bb9e61202c | |||
c1ac21d5f7 | |||
15aeab1620 | |||
55b55e7c97 | |||
ff228b14d1 | |||
43bbc409a2 | |||
c84c27f7f4 | |||
a813ae07e2 | |||
6ccb4e0bab | |||
65f5c0476f | |||
d0dd69f177 | |||
79b673a17f | |||
3b37469735 | |||
8a7129e21a | |||
7059f7af11 | |||
f937f8f604 | |||
9448828b0d | |||
21245887e6 | |||
72807101ed | |||
25d90dabbe | |||
544eb89198 | |||
e38351fce7 | |||
f00bb85b58 | |||
5b2408f0a6 | |||
bbd4a33f6c | |||
6052b12fb3 | |||
ded57245e1 | |||
8a6e54a06d | |||
a1beba4b6e | |||
664e0015d4 | |||
2230dfaea7 | |||
ea6a2e9f25 | |||
9166baf709 | |||
a64eded521 | |||
a391aebbcf | |||
c3a1ef219e | |||
2895edc9c6 | |||
66df745721 | |||
18798a1cb2 | |||
8296f6b681 | |||
c714330856 | |||
e36e6c85ef | |||
f4e4bb2085 | |||
2844dd2972 | |||
0cf94e3ed5 | |||
9270d3f279 | |||
3127ba3c35 | |||
5c213e5474 | |||
3d9dd6df0e | |||
38359b166e | |||
bd1de32b33 | |||
bf15d3eea8 | |||
a65d3fa1de | |||
b5eda603a2 | |||
df5924abd0 | |||
33038f6ebe | |||
1716b91334 | |||
8ae7f14252 | |||
bc061b78be | |||
4c3674f660 | |||
497d6b1323 | |||
62d92f8091 | |||
098feec4a0 | |||
a9ff48e67f | |||
da42a7648a | |||
5ab7cb4188 | |||
fed6b25be0 | |||
7bbc3523ba | |||
f00d03356f | |||
36d613dd67 | |||
e505fa61f3 | |||
cc06153e85 | |||
d4003452c7 | |||
007282d2bb | |||
094538c0ce | |||
bda2b4ebb6 | |||
20dc5e83ee | |||
24b8b3427c | |||
a731119f9f | |||
0187b3288b | |||
4b037abda4 | |||
c849a30db4 | |||
5d8eb74634 | |||
7cc4225eb9 | |||
f6e88cd659 | |||
bdbf0c94b1 | |||
01e0f58dd6 | |||
f0bdf2a1ae | |||
641c5c1c1e | |||
a383a5a165 | |||
cfa09b84dd | |||
e213080c0b | |||
04d4fea3e8 | |||
89434e09c2 | |||
1a7c79746d | |||
c207ad80fd | |||
970b58b13f | |||
1ed3531049 | |||
a20fcbbe06 | |||
d0b3688a23 | |||
e8f9ba4b6c | |||
f5bec3ff50 | |||
9a55eaf10d | |||
8cdfcc5489 | |||
fe5caca884 | |||
a7f61e63fa | |||
c024d89448 | |||
fa79f51645 | |||
b21397bde9 | |||
b34bdf5c42 | |||
260217a800 | |||
c8b065524e | |||
60f963986f | |||
c507e4907d | |||
afc6ab5442 | |||
c56c2416a9 | |||
d498314850 | |||
8dc3f3647c | |||
67d80f9ae8 | |||
43619fc847 | |||
ad13520b24 | |||
350ea47def | |||
8c388e3730 | |||
9bbe67f286 | |||
ab30b2ad01 | |||
ba5e07efc7 | |||
df8e6750a1 | |||
1537791f06 | |||
77c382ccba | |||
8a47b48912 | |||
f7471eea3c | |||
4161d19374 | |||
375897801b | |||
03cbce8c66 | |||
288e5d894d | |||
b48dd52494 | |||
7fb3cc07de | |||
3af99a7b4a | |||
b4d3468088 | |||
266c267b62 | |||
d5101dff3b | |||
63dff9c888 | |||
7742a99cee | |||
5635505f2e | |||
cc288aec3d | |||
2d96576a10 | |||
a9864471a4 | |||
47a4edb817 | |||
3aba7ebe6a | |||
2b28b91fd9 | |||
18ce58c2bc | |||
852afb312a | |||
d4703d9316 | |||
1bb9ce5d8c | |||
97fc45f32a | |||
4b1251106e | |||
a1e91b00d2 | |||
fac066ea9f | |||
85864ed9f7 | |||
581b837e88 | |||
60a056d5dc | |||
543631f2b3 | |||
d0f3539e6e | |||
e5636a322c | |||
b094936d72 | |||
f3e4cb491e | |||
cb5701f8d9 | |||
c0680602f9 | |||
30dabdf8fc | |||
ede5786d1e | |||
46caf88b2c | |||
98420c27de | |||
a8131365c5 | |||
3e52e32dce | |||
0a09fb1432 | |||
2f6062f632 | |||
066b281979 | |||
125ef0a0ec | |||
65aaffcfe4 | |||
ac11a0be15 | |||
e3f42818e9 | |||
ef2047555a | |||
d953c1cee3 | |||
7c7fcd7ab8 | |||
c885178d5f | |||
14feb56139 | |||
f209aacbfa | |||
55eaa5fb6d | |||
70cf8ed05d | |||
c7c7f9fbe8 | |||
1cb62346e4 | |||
f57c17de2c | |||
0874bf42b6 | |||
3a4839c97e | |||
82abbecddf | |||
e63a7b0532 | |||
e3422e0aed | |||
860b5d0efa | |||
9bc9685911 | |||
cf4b944865 | |||
28c8b03797 | |||
24127a2492 | |||
bceeeba405 | |||
7bcd42e7be | |||
4908a5cffc | |||
c3f9893d81 | |||
25f31f2a14 | |||
e563d77128 | |||
e79ba194b4 | |||
ae9960be53 | |||
c7f4d500db | |||
e188c9703c | |||
b9fbd9bb03 | |||
a58a5fd7bb | |||
9bb5f05cb9 | |||
3693d7ad23 | |||
c624b14e8e | |||
3dd614db61 | |||
d443a28743 | |||
21bb74d16f | |||
1d1ab37546 | |||
a00824c986 | |||
d5aca7ef51 | |||
47cc92f004 | |||
e21e01ca22 | |||
6d11154652 | |||
6b245a39ee | |||
4f7c971ee7 | |||
0677cf0cbe | |||
904a2018e0 | |||
b6fa9299e5 | |||
21edc6a82e | |||
a367b90a82 | |||
a84b5ef81b | |||
5c94833b8f | |||
b85ac03136 | |||
7fed0faa44 | |||
02a567e46f | |||
0b009f06a9 | |||
e8c09e3b57 | |||
53bfa7c6d6 | |||
53f33c1cec | |||
10629600c5 | |||
4d549f69f8 | |||
de8ebbdfd0 | |||
0287b234ea | |||
bfc26bcd8c | |||
daac386f4d | |||
0db1b5d8f1 | |||
8e1a725462 | |||
424ab48672 | |||
62f4140634 | |||
64770571b2 | |||
c957dfc167 | |||
10ea3a9f4d | |||
ec4381dd40 | |||
2b64031ddc | |||
388578fec9 | |||
dd6070a49b | |||
3dbc4ab572 | |||
cfed0c0cf1 | |||
bb3c684b98 | |||
f8b995dbf9 | |||
639b732024 | |||
5da5ca5c23 | |||
0b1bf14cd8 | |||
d1cc7a0b26 | |||
431ddb9a45 | |||
7c2cd97e60 | |||
c2868de25a | |||
994264c0ba | |||
4bbf16e654 | |||
ae142a6827 | |||
e85ec23037 | |||
172bb76964 | |||
adb4f7c7cb | |||
5f76de1d71 | |||
71ada483bf | |||
abb44f7db0 | |||
43163523f6 | |||
ee486233e9 | |||
4c3b791ff3 | |||
964d72610f | |||
02bab8cf90 | |||
69ce1c2d41 | |||
6a0b1d58ba | |||
373d9660d0 | |||
1c6516199e | |||
3cf2005a93 | |||
2e4d17f3a9 | |||
40b28742a9 | |||
253a1125bf | |||
f41c41fd50 | |||
33fab26930 | |||
be665d8de1 | |||
c422c7210f | |||
f6a1de6b31 | |||
5a562d8a0a | |||
d6bbc4d76d | |||
4055150910 | |||
4d436800de | |||
0660903784 | |||
984d23f687 | |||
8af85d812d | |||
843881120a | |||
9e6c677135 | |||
9808d91c62 | |||
7a75f7805c | |||
cda205deb4 | |||
466f5c67d6 | |||
b9dfe66028 | |||
4d96cf5197 | |||
024c31da25 | |||
5064dc75ac | |||
bef27f2a28 | |||
628b0c1154 | |||
e4d5102b17 | |||
b25f925311 | |||
7d4c9e4b67 | |||
a5f39aeda6 | |||
a91ab15525 | |||
a95f860a96 | |||
6215636bc6 | |||
3cd8a2b907 | |||
ebcf79d7f3 | |||
a91a55a6d2 | |||
48487cb70e | |||
c68371ed14 | |||
9406104c0a | |||
c0194e0115 | |||
914900a561 | |||
4ea3e7e000 | |||
6eb9c2fab0 | |||
7b9414565e | |||
37de490e23 | |||
753080133b | |||
9a37e827e2 | |||
3df54be9e4 | |||
c0d5684078 | |||
2b289250d8 | |||
ddd02044ea | |||
c198a27a3c | |||
4f37487b1c | |||
0ddf0c4895 | |||
fd6ed1713d | |||
eb5412d76f | |||
7533338362 | |||
9896d438c0 | |||
684579b338 | |||
695f322dc1 | |||
f90c7a9df0 | |||
f2466cf4ee | |||
bed680cff8 | |||
a647298412 | |||
2abbe98e33 | |||
7613f13e54 | |||
4b8cdd4b57 | |||
17e289c39f | |||
2913340af7 | |||
f8c27d42ed | |||
17bb633031 | |||
9106271f2c | |||
48a3741d5a | |||
9d1f43f3ba | |||
6f98107d5e | |||
a8e2ee1343 | |||
e906a4f0d8 | |||
b5b33d12d6 | |||
22d3cabc10 | |||
a06043b703 | |||
4689ea2727 | |||
939529ce5d | |||
46304a4f83 | |||
f7eebd0227 | |||
8af2cc1efe | |||
e5a89e047c | |||
29d3b68554 | |||
93d27eefd5 | |||
ed70f73794 | |||
ef12e10e59 | |||
2954d1b5ca | |||
3077c9a1f8 | |||
9537b2ff84 | |||
961d663fbe | |||
57e15fc08b | |||
b70746a113 | |||
0709ed4c2b | |||
fa699f65d7 | |||
18bc4eda9f | |||
f542649b2b | |||
a574e462c9 | |||
65cafa0eec | |||
18aa173d39 | |||
bc8eb8508b | |||
7db269ba6a | |||
8e5567d964 | |||
541ce98432 | |||
e7e3f5d952 | |||
382d3ed1d2 | |||
a07de82f79 | |||
2e84f4e0cd | |||
a5b12db7d6 | |||
8b94d6a402 | |||
96cbcd6da4 |
92
.bazelignore
92
.bazelignore
@ -1,9 +1,97 @@
|
||||
# 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
|
||||
node_modules
|
||||
dist
|
||||
aio/content
|
||||
aio/node_modules
|
||||
aio/tools/examples/shared/node_modules
|
||||
integration/bazel
|
||||
integration/bazel-schematics/demo
|
||||
packages/bazel/node_modules
|
||||
integration/bazel/bazel-bazel
|
||||
integration/bazel/bazel-bin
|
||||
integration/bazel/bazel-out
|
||||
integration/bazel/bazel-testlogs
|
||||
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/service-worker-schema/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
|
||||
|
119
.bazelrc
119
.bazelrc
@ -18,11 +18,8 @@ test:debug --test_arg=--node_options=--inspect-brk --test_output=streamed --test
|
||||
# See https://github.com/bazelbuild/bazel/issues/4603
|
||||
build --symlink_prefix=dist/
|
||||
|
||||
# Disable watchfs as it causes tests to be flaky on Windows
|
||||
# https://github.com/angular/angular/issues/29541
|
||||
build --nowatchfs
|
||||
|
||||
# Turn off legacy external runfiles
|
||||
build --nolegacy_external_runfiles
|
||||
run --nolegacy_external_runfiles
|
||||
test --nolegacy_external_runfiles
|
||||
|
||||
@ -36,21 +33,10 @@ build --incompatible_strict_action_env
|
||||
run --incompatible_strict_action_env
|
||||
test --incompatible_strict_action_env
|
||||
|
||||
###############################
|
||||
# Saucelabs support #
|
||||
# Turn on these settings with #
|
||||
# --config=saucelabs #
|
||||
###############################
|
||||
|
||||
# Expose SauceLabs environment to actions
|
||||
# These environment variables are needed by
|
||||
# web_test_karma to run on Saucelabs
|
||||
test:saucelabs --action_env=SAUCE_USERNAME
|
||||
test:saucelabs --action_env=SAUCE_ACCESS_KEY
|
||||
test:saucelabs --action_env=SAUCE_READY_FILE
|
||||
test:saucelabs --action_env=SAUCE_PID_FILE
|
||||
test:saucelabs --action_env=SAUCE_TUNNEL_IDENTIFIER
|
||||
test:saucelabs --define=KARMA_WEB_TEST_MODE=SL_REQUIRED
|
||||
# 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 #
|
||||
@ -61,7 +47,8 @@ test:saucelabs --define=KARMA_WEB_TEST_MODE=SL_REQUIRED
|
||||
# Releases should always be stamped with version control info
|
||||
# This command assumes node on the path and is a workaround for
|
||||
# https://github.com/bazelbuild/bazel/issues/4802
|
||||
build:release --workspace_status_command="node ./tools/bazel_stamp_vars.js"
|
||||
build:release --workspace_status_command="yarn -s ng-dev release build-env-stamp"
|
||||
build:release --stamp
|
||||
|
||||
###############################
|
||||
# Output #
|
||||
@ -78,27 +65,49 @@ test --test_output=errors
|
||||
# Settings for CircleCI #
|
||||
################################
|
||||
|
||||
# Bazel flags for CircleCI are in /.circleci/bazel.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 #
|
||||
################################
|
||||
# to determine if the compiler used should be Ivy or ViewEngine one can use `--define=compile=aot` on
|
||||
# any bazel target. This is a temporary flag until codebase is permanently switched to Ivy.
|
||||
build --define=compile=legacy
|
||||
# To determine if the compiler used should be Ivy instead of ViewEngine, one can use `--config=ivy`
|
||||
# on any bazel target. This is a temporary flag until codebase is permanently switched to Ivy.
|
||||
build --define=angular_ivy_enabled=False
|
||||
|
||||
###############################
|
||||
# Remote Build Execution support
|
||||
# Turn on these settings with
|
||||
# --config=remote
|
||||
###############################
|
||||
build:view-engine --define=angular_ivy_enabled=False
|
||||
build:ivy --define=angular_ivy_enabled=True
|
||||
|
||||
# Load default settings for Remote Build Execution.
|
||||
import %workspace%/third_party/github.com/bazelbuild/bazel-toolchains/bazelrc/.bazelrc.notoolchain
|
||||
##################################
|
||||
# Remote Build Execution support #
|
||||
# Turn on these settings with #
|
||||
# --config=remote #
|
||||
##################################
|
||||
|
||||
# The following --define=EXECUTOR=remote will be able to be removed
|
||||
# once https://github.com/bazelbuild/bazel/issues/7254 is fixed
|
||||
build:remote --define=EXECUTOR=remote
|
||||
|
||||
# Set a higher timeout value, just in case.
|
||||
build:remote --remote_timeout=600
|
||||
|
||||
# Increase the default number of jobs by 50% because our build has lots of
|
||||
# parallelism
|
||||
build:remote --jobs=150
|
||||
build:remote --google_default_credentials
|
||||
|
||||
# Force remote exeuctions to consider the entire run as linux
|
||||
build:remote --cpu=k8
|
||||
build:remote --host_cpu=k8
|
||||
|
||||
# Toolchain and platform related flags
|
||||
build:remote --host_javabase=@rbe_ubuntu1604_angular//java:jdk
|
||||
@ -106,27 +115,26 @@ build:remote --javabase=@rbe_ubuntu1604_angular//java:jdk
|
||||
build:remote --host_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 --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1
|
||||
build:remote --extra_toolchains=@rbe_ubuntu1604_angular//config:cc-toolchain
|
||||
build:remote --extra_execution_platforms=//tools:rbe_ubuntu1604-angular
|
||||
build:remote --host_platform=//tools:rbe_ubuntu1604-angular
|
||||
build:remote --platforms=//tools:rbe_ubuntu1604-angular
|
||||
|
||||
# Remote instance.
|
||||
# Remote instance and caching
|
||||
build:remote --remote_instance_name=projects/internal-200822/instances/default_instance
|
||||
build:remote --project_id=internal-200822
|
||||
|
||||
# Remote caching
|
||||
build:remote --remote_cache=remotebuildexecution.googleapis.com
|
||||
# By default, do not accept remote cache, to be set to true for CI based on environment
|
||||
build:remote --remote_accept_cached=false
|
||||
# By default, do not upload local results to cache, to be set to true for CI based on environment
|
||||
build:remote --remote_upload_local_results=false
|
||||
build:remote --remote_executor=remotebuildexecution.googleapis.com
|
||||
|
||||
# Build Event Service Configuration
|
||||
build:remote --bes_backend=buildeventservice.googleapis.com
|
||||
build:remote --bes_timeout=30s
|
||||
build:remote --bes_results_url="https://source.cloud.google.com/results/invocations/"
|
||||
##################################
|
||||
# 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
|
||||
@ -137,31 +145,6 @@ build:remote --bes_results_url="https://source.cloud.google.com/results/invocati
|
||||
# This allows us to avoid installing a second copy of node_modules
|
||||
common --experimental_allow_incremental_repository_updates
|
||||
|
||||
# This option is changed to true in Bazel 0.27 and exposes a possible
|
||||
# regression in Bazel 0.27.0.
|
||||
# Error observed is in npm_package target `//packages/common/locales:package`:
|
||||
# ```
|
||||
# ERROR: /home/circleci/ng/packages/common/locales/BUILD.bazel:13:1: Assembling
|
||||
# npm package packages/common/locales/package failed: No usable spawn strategy found
|
||||
# for spawn with mnemonic SkylarkAction. Your --spawn_strategyor --strategy flags
|
||||
# are probably too strict. Visit https://github.com/bazelbuild/bazel/issues/7480 for
|
||||
# migration advises
|
||||
# ```
|
||||
# Suspect is https://github.com/bazelbuild/rules_nodejs/blob/master/internal/npm_package/npm_package.bzl#L75-L82:
|
||||
# ```
|
||||
# execution_requirements = {
|
||||
# # Never schedule this action remotely because it's not computationally expensive.
|
||||
# # It just copies files into a directory; it's not worth copying inputs and outputs to a remote worker.
|
||||
# # Also don't run it in a sandbox, because it resolves an absolute path to the bazel-out directory
|
||||
# # allowing the .pack and .publish runnables to work with no symlink_prefix
|
||||
# # See https://github.com/bazelbuild/rules_nodejs/issues/187
|
||||
# "local": "1",
|
||||
# },
|
||||
# ```
|
||||
build --incompatible_list_based_execution_strategy_selection=false
|
||||
test --incompatible_list_based_execution_strategy_selection=false
|
||||
run --incompatible_list_based_execution_strategy_selection=false
|
||||
|
||||
####################################################
|
||||
# User bazel configuration
|
||||
# NOTE: This needs to be the *last* entry in the config.
|
||||
|
3
.bazelversion
Normal file
3
.bazelversion
Normal file
@ -0,0 +1,3 @@
|
||||
2.1.1
|
||||
# [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
|
15
.circleci/bazel.common.rc
Normal file
15
.circleci/bazel.common.rc
Normal file
@ -0,0 +1,15 @@
|
||||
# Settings in this file should be OS agnostic. Use the bazel.<OS>.rc files for OS specific settings.
|
||||
|
||||
# Don't be spammy in the logs
|
||||
build --noshow_progress
|
||||
|
||||
# Print all the options that apply to the build.
|
||||
# This helps us diagnose which options override others
|
||||
# (e.g. /etc/bazel.bazelrc vs. tools/bazel.rc)
|
||||
build --announce_rc
|
||||
|
||||
# Retry in the event of flakes, eg. https://circleci.com/gh/angular/angular/31309
|
||||
test --flaky_test_attempts=2
|
||||
|
||||
# More details on failures
|
||||
build --verbose_failures=true
|
22
.circleci/bazel.linux.rc
Normal file
22
.circleci/bazel.linux.rc
Normal file
@ -0,0 +1,22 @@
|
||||
# These options are enabled when running on CI
|
||||
# We do this by copying this file to /etc/bazel.bazelrc at the start of the build.
|
||||
# See documentation in /docs/BAZEL.md
|
||||
|
||||
# Import config items common to both Linux and Windows setups.
|
||||
# https://docs.bazel.build/versions/master/guide.html#bazelrc-syntax-and-semantics
|
||||
try-import %workspace%/.circleci/bazel.common.rc
|
||||
|
||||
# Save downloaded repositories in a location that can be cached by CircleCI. This helps us
|
||||
# speeding up the analysis time significantly with Bazel managed node dependencies on the CI.
|
||||
build --repository_cache=/home/circleci/bazel_repository_cache
|
||||
|
||||
# Workaround https://github.com/bazelbuild/bazel/issues/3645
|
||||
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
|
||||
# Limit Bazel to consuming resources that fit in CircleCI "xlarge" class
|
||||
# https://circleci.com/docs/2.0/configuration-reference/#resource_class
|
||||
build --local_cpu_resources=8
|
||||
build --local_ram_resources=14336
|
||||
|
||||
# All build executed remotely should be done using our RBE configuration.
|
||||
build:remote --google_default_credentials
|
||||
build --config=remote
|
@ -1,41 +0,0 @@
|
||||
# These options are enabled when running on CI
|
||||
# We do this by copying this file to /etc/bazel.bazelrc at the start of the build.
|
||||
# See documentation in /docs/BAZEL.md
|
||||
|
||||
# Save downloaded repositories in a location that can be cached by CircleCI. This helps us
|
||||
# speeding up the analysis time significantly with Bazel managed node dependencies on the CI.
|
||||
build --repository_cache=/home/circleci/bazel_repository_cache
|
||||
|
||||
# Don't be spammy in the logs
|
||||
# TODO(gmagolan): Hide progress again once build performance improves
|
||||
# Presently, CircleCI can timeout during bazel test ... with the following
|
||||
# error: Too long with no output (exceeded 10m0s)
|
||||
# build --noshow_progress
|
||||
|
||||
# Print all the options that apply to the build.
|
||||
# This helps us diagnose which options override others
|
||||
# (e.g. /etc/bazel.bazelrc vs. tools/bazel.rc)
|
||||
build --announce_rc
|
||||
|
||||
# Workaround https://github.com/bazelbuild/bazel/issues/3645
|
||||
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
|
||||
# Limit Bazel to consuming resources that fit in CircleCI "xlarge" class
|
||||
# https://circleci.com/docs/2.0/configuration-reference/#resource_class
|
||||
build --local_resources=14336,8.0,1.0
|
||||
|
||||
# Retry in the event of flakes, eg. https://circleci.com/gh/angular/angular/31309
|
||||
test --flaky_test_attempts=2
|
||||
|
||||
# More details on failures
|
||||
build --verbose_failures=true
|
||||
|
||||
# We have seen some flakiness in using TS workers on CircleCI
|
||||
# https://angular-team.slack.com/archives/C07DT5M6V/p1562693245183400
|
||||
# > failures like `ERROR: /home/circleci/ng/packages/core/test/BUILD.bazel:5:1:
|
||||
# > Compiling TypeScript (devmode) //packages/core/test:test_lib failed: Worker process did not return a WorkResponse:`
|
||||
# > I saw that issue a couple times today.
|
||||
# > Example job: https://circleci.com/gh/angular/angular/385517
|
||||
# We expect that TypeScript compilations will parallelize wider than the number of local cores anyway
|
||||
# so we should saturate remote workers with TS compilations
|
||||
build --strategy=TypeScriptCompile=standalone
|
||||
build --strategy=AngularTemplateCompile=standalone
|
21
.circleci/bazel.windows.rc
Normal file
21
.circleci/bazel.windows.rc
Normal file
@ -0,0 +1,21 @@
|
||||
# These options are enabled when running on CI
|
||||
# We do this by copying this file to $env:ProgramData\bazel.bazelrc at the start of the build.
|
||||
# See documentation in /docs/BAZEL.md
|
||||
|
||||
# Import config items common to both Linux and Windows setups.
|
||||
# https://docs.bazel.build/versions/master/guide.html#bazelrc-syntax-and-semantics
|
||||
try-import %workspace%/.circleci/bazel.common.rc
|
||||
|
||||
# Save downloaded repositories in a location that can be cached by CircleCI. This helps us
|
||||
# speeding up the analysis time significantly with Bazel managed node dependencies on the CI.
|
||||
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
|
||||
build --remote_http_cache=https://storage.googleapis.com/angular-team-cache
|
||||
build --remote_accept_cached=true
|
||||
build --remote_upload_local_results=true
|
||||
build --google_default_credentials
|
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,7 @@
|
||||
# Variables
|
||||
readonly projectDir=$(realpath "$(dirname ${BASH_SOURCE[0]})/..")
|
||||
readonly envHelpersPath="$projectDir/.circleci/env-helpers.inc.sh";
|
||||
readonly getCommitRangePath="$projectDir/.circleci/get-commit-range.js";
|
||||
readonly bashEnvCachePath="$projectDir/.circleci/bash_env_cache";
|
||||
|
||||
# Load helpers and make them available everywhere (through `$BASH_ENV`).
|
||||
source $envHelpersPath;
|
||||
@ -15,23 +15,23 @@ echo "source $envHelpersPath;" >> $BASH_ENV;
|
||||
####################################################################################################
|
||||
# See https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables for more info.
|
||||
####################################################################################################
|
||||
setPublicVar CI "$CI"
|
||||
setPublicVar PROJECT_ROOT "$projectDir";
|
||||
setPublicVar CI_AIO_MIN_PWA_SCORE "95";
|
||||
# This is the branch being built; e.g. `pull/12345` for PR builds.
|
||||
setPublicVar CI_BRANCH "$CIRCLE_BRANCH";
|
||||
# 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_BUILD_URL "$CIRCLE_BUILD_URL";
|
||||
setPublicVar CI_COMMIT "$CIRCLE_SHA1";
|
||||
# `CI_COMMIT_RANGE` will only be available when `CIRCLE_COMPARE_URL` is also available (or can be
|
||||
# retrieved via `get-compare-url.js`), i.e. on push builds (a.k.a. non-PR, non-scheduled builds and
|
||||
# rerun workflows of such builds). That is fine, since we only need it in push builds.
|
||||
setPublicVar CI_COMMIT_RANGE "`[[ ${CIRCLE_PR_NUMBER:-false} != false ]] && echo "" || node $getCommitRangePath "$CIRCLE_BUILD_NUM" "$CIRCLE_COMPARE_URL"`";
|
||||
# `CI_COMMIT_RANGE` is only used on push builds (a.k.a. non-PR, non-scheduled builds and rerun
|
||||
# workflows of such builds).
|
||||
setPublicVar CI_GIT_BASE_REVISION "${CIRCLE_GIT_BASE_REVISION}";
|
||||
setPublicVar CI_GIT_REVISION "${CIRCLE_GIT_REVISION}";
|
||||
setPublicVar CI_COMMIT_RANGE "$CIRCLE_GIT_BASE_REVISION..$CIRCLE_GIT_REVISION";
|
||||
setPublicVar CI_PULL_REQUEST "${CIRCLE_PR_NUMBER:-false}";
|
||||
setPublicVar CI_REPO_NAME "$CIRCLE_PROJECT_REPONAME";
|
||||
setPublicVar CI_REPO_OWNER "$CIRCLE_PROJECT_USERNAME";
|
||||
setPublicVar CI_PR_REPONAME "$CIRCLE_PR_REPONAME";
|
||||
setPublicVar CI_PR_USERNAME "$CIRCLE_PR_USERNAME";
|
||||
|
||||
|
||||
####################################################################################################
|
||||
@ -51,35 +51,63 @@ setSecretVar CI_SECRET_PAYLOAD_FIREBASE_TOKEN "$ANGULAR_PAYLOAD_TOKEN";
|
||||
####################################################################################################
|
||||
# Define SauceLabs environment variables for CircleCI.
|
||||
####################################################################################################
|
||||
# In order to have a meaningful SauceLabs badge on the repo page,
|
||||
# the angular2-ci account is used only when pushing commits to master;
|
||||
# in all other cases, the regular angular-ci account is used.
|
||||
if [ "${CI_PULL_REQUEST}" = "false" ] && [ "${CI_REPO_OWNER}" = "angular" ] && [ "${CI_BRANCH}" = "master" ]; then
|
||||
setPublicVar SAUCE_USERNAME "angular2-ci";
|
||||
setSecretVar SAUCE_ACCESS_KEY "693ebc16208a-0b5b-1614-8d66-a2662f4e";
|
||||
else
|
||||
setPublicVar SAUCE_USERNAME "angular-ci";
|
||||
setSecretVar SAUCE_ACCESS_KEY "9b988f434ff8-fbca-8aa4-4ae3-35442987";
|
||||
fi
|
||||
setPublicVar SAUCE_USERNAME "angular-framework";
|
||||
setSecretVar SAUCE_ACCESS_KEY "0c731274ed5f-cbc9-16f4-021a-9835e39f";
|
||||
# TODO(josephperrott): Remove environment variables once all saucelabs tests are via bazel method.
|
||||
setPublicVar SAUCE_LOG_FILE /tmp/angular/sauce-connect.log
|
||||
setPublicVar SAUCE_READY_FILE /tmp/angular/sauce-connect-ready-file.lock
|
||||
setPublicVar SAUCE_PID_FILE /tmp/angular/sauce-connect-pid-file.lock
|
||||
setPublicVar SAUCE_TUNNEL_IDENTIFIER "angular-${CIRCLE_BUILD_NUM}-${CIRCLE_NODE_INDEX}"
|
||||
setPublicVar SAUCE_TUNNEL_IDENTIFIER "angular-framework-${CIRCLE_BUILD_NUM}-${CIRCLE_NODE_INDEX}"
|
||||
# Amount of seconds we wait for sauceconnect to establish a tunnel instance. In order to not
|
||||
# acquire CircleCI instances for too long if sauceconnect failed, we need a connect timeout.
|
||||
setPublicVar SAUCE_READY_FILE_TIMEOUT 120
|
||||
|
||||
|
||||
####################################################################################################
|
||||
# Define environment variables for the Angular Material unit tests job.
|
||||
# Define environment variables for the `angular/components` repo unit tests job.
|
||||
####################################################################################################
|
||||
# We specifically use a directory within "/tmp" here because we want the cloned repo to be
|
||||
# completely isolated from angular/angular in order to avoid any bad interactions between
|
||||
# their separate build setups.
|
||||
setPublicVar MATERIAL_REPO_TMP_DIR "/tmp/material2"
|
||||
setPublicVar MATERIAL_REPO_URL "https://github.com/angular/material2.git"
|
||||
setPublicVar MATERIAL_REPO_BRANCH "master"
|
||||
# **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI "config.yml".
|
||||
setPublicVar MATERIAL_REPO_COMMIT "701302dc482d7e4b77990b24e3b5ab330bbf1aa5"
|
||||
# their separate build setups. **NOTE**: When updating the temporary directory, also update
|
||||
# the `save_cache` path configuration in `config.yml`
|
||||
setPublicVar COMPONENTS_REPO_TMP_DIR "/tmp/angular-components-repo"
|
||||
setPublicVar COMPONENTS_REPO_URL "https://github.com/angular/components.git"
|
||||
setPublicVar COMPONENTS_REPO_BRANCH "master"
|
||||
# **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI `config.yml`.
|
||||
setPublicVar COMPONENTS_REPO_COMMIT "448523bffffecd2b53a3d2854c3051b6b7a3934f"
|
||||
|
||||
# Source `$BASH_ENV` to make the variables available immediately.
|
||||
|
||||
####################################################################################################
|
||||
# Decrypt GCP Credentials and store them as the Google default credentials.
|
||||
####################################################################################################
|
||||
mkdir -p "$HOME/.config/gcloud";
|
||||
openssl aes-256-cbc -d -in "${projectDir}/.circleci/gcp_token" \
|
||||
-md md5 -k "$CIRCLE_PROJECT_REPONAME" -out "$HOME/.config/gcloud/application_default_credentials.json"
|
||||
####################################################################################################
|
||||
# Set bazel configuration for CircleCI runs.
|
||||
####################################################################################################
|
||||
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. ##
|
||||
## ***NOTE: This must remain the the last action in this script*** ##
|
||||
####################################################################################################
|
||||
####################################################################################################
|
||||
source $BASH_ENV;
|
||||
|
@ -1,159 +0,0 @@
|
||||
#!/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.
|
||||
*
|
||||
* **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 workflow, 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();
|
||||
});
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u -e -o pipefail
|
||||
|
||||
# The path of the .bazelrc.user file to update should be passed as first parameter to this script.
|
||||
# This allows to setup RBE for both the Angular repo and the Material repo.
|
||||
bazelrc_user="$1"
|
||||
|
||||
echo "Writing RBE configuration to ${bazelrc_user}"
|
||||
|
||||
touch ${bazelrc_user}
|
||||
echo -e 'build --config=remote\n' >> ${bazelrc_user}
|
||||
echo -e 'build:remote --remote_accept_cached=true\n' >> ${bazelrc_user}
|
||||
echo "Reading from remote cache for bazel remote jobs."
|
||||
if [[ "$CI_PULL_REQUEST" == "false" ]]; then
|
||||
echo -e 'build:remote --remote_upload_local_results=true\n' >> ${bazelrc_user}
|
||||
echo "Uploading local build results to remote cache."
|
||||
else
|
||||
echo -e 'build:remote --remote_upload_local_results=false\n' >> ${bazelrc_user}
|
||||
echo "Not uploading local build results to remote cache."
|
||||
fi
|
56
.circleci/windows-env.ps1
Normal file
56
.circleci/windows-env.ps1
Normal file
@ -0,0 +1,56 @@
|
||||
# Install Bazel pre-reqs on Windows
|
||||
# https://docs.bazel.build/versions/master/install-windows.html
|
||||
# https://docs.bazel.build/versions/master/windows.html
|
||||
# Install MSYS2 and packages
|
||||
choco install msys2 --version 20180531.0.0 --no-progress --package-parameters "/NoUpdate"
|
||||
C:\tools\msys64\usr\bin\bash.exe -l -c "pacman --needed --noconfirm -S zip unzip patch diffutils git"
|
||||
|
||||
# Add PATH modifications to the Powershell profile. This is the win equivalent of .bash_profile.
|
||||
# https://docs.microsoft.com/en-us/previous-versions//bb613488(v=vs.85)
|
||||
new-item -path $profile -itemtype file -force
|
||||
# Paths for nodejs, npm, yarn, and msys2. Use single quotes to prevent interpolation.
|
||||
# Add before the original path to use msys2 instead of the installed gitbash.
|
||||
Add-Content $profile '$Env:path = "${Env:ProgramFiles}\nodejs\;C:\Users\circleci\AppData\Roaming\npm\;${Env:ProgramFiles(x86)}\Yarn\bin\;C:\Users\circleci\AppData\Local\Yarn\bin\;C:\tools\msys64\usr\bin\;" + $Env:path'
|
||||
# Environment variables for Bazel
|
||||
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.
|
||||
$bazeliskVersion = & ${Env:ProgramFiles}\nodejs\node.exe -e "console.log(require('./package.json').devDependencies['@bazel/bazelisk'])"
|
||||
# This is a tricky situation: we want $bazeliskVersion to be evaluated but not $Env:BAZELISK_VERSION.
|
||||
# Formatting works https://stackoverflow.com/questions/32127583/expand-variable-inside-single-quotes
|
||||
$bazeliskVersionGlobalVar = '$Env:BAZELISK_VERSION = "{0}"' -f $bazeliskVersion
|
||||
Add-Content $profile $bazeliskVersionGlobalVar
|
||||
|
||||
# Remove the CircleCI checkout SSH override, because it breaks cloning repositories through Bazel.
|
||||
# See https://circleci.com/gh/angular/angular/401454 for an example.
|
||||
# TODO: is this really needed? Maybe there's a better way. It doesn't happen on Linux or on Codefresh.
|
||||
git config --global --unset url.ssh://git@github.com.insteadOf
|
||||
|
||||
|
||||
####################################################################################################
|
||||
# Decrypt GCP Credentials and store them as the Google default credentials.
|
||||
####################################################################################################
|
||||
mkdir ${env:APPDATA}\gcloud
|
||||
openssl aes-256-cbc -d -in .circleci\gcp_token -md md5 -out "$env:APPDATA\gcloud\application_default_credentials.json" -k "$env:CIRCLE_PROJECT_REPONAME"
|
||||
|
||||
####################################################################################################
|
||||
# Set bazel configuration for CircleCI runs.
|
||||
####################################################################################################
|
||||
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.
|
||||
# choco install yarn --version 1.16.0 --no-progress
|
||||
# choco install vcredist2015 --version 14.0.24215.20170201
|
||||
|
||||
# We don't need VS Build Tools for the tested bazel targets.
|
||||
# If it's needed again, uncomment these lines.
|
||||
# VS Build Tools are needed for Bazel C++ targets (like com_google_protobuf)
|
||||
# choco install visualstudio2019buildtools --version 16.1.2.0 --no-progress --package-parameters "--add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.Component.VC.Runtime.UCRTSDK --add Microsoft.VisualStudio.Component.Windows10SDK.17763"
|
||||
# Add-Content $profile '$Env:BAZEL_VC = "${Env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\BuildTools\VC\"'
|
||||
# Python is needed for Bazel Python targets
|
||||
# choco install python --version 3.5.1 --no-progress
|
@ -1,126 +0,0 @@
|
||||
# escape=`
|
||||
|
||||
ARG core=mcr.microsoft.com/windows/servercore:1809
|
||||
ARG target=mcr.microsoft.com/powershell:windowsservercore-1809
|
||||
|
||||
FROM $core as download
|
||||
|
||||
ARG node_version=10.13.0
|
||||
ARG yarn_version=1.13.0
|
||||
|
||||
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
|
||||
|
||||
ENV GPG_VERSION 2.3.4
|
||||
|
||||
RUN Invoke-WebRequest $('https://files.gpg4win.org/gpg4win-vanilla-{0}.exe' -f $env:GPG_VERSION) -OutFile 'gpg4win.exe' -UseBasicParsing ; `
|
||||
Start-Process .\gpg4win.exe -ArgumentList '/S' -NoNewWindow -Wait
|
||||
|
||||
RUN @( `
|
||||
'94AE36675C464D64BAFA68DD7434390BDBE9B9C5', `
|
||||
'FD3A5288F042B6850C66B31F09FE44734EB7990E', `
|
||||
'71DCFD284A79C3B38668286BC97EC7A07EDE3FC1', `
|
||||
'DD8F2338BAE7501E3DD5AC78C273792F7D83545D', `
|
||||
'C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8', `
|
||||
'B9AE9905FFD7803F25714661B63B535A4C206CA9', `
|
||||
'77984A986EBC2AA786BC0F66B01FBB92821C587A', `
|
||||
'8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600', `
|
||||
'4ED778F539E3634C779C87C6D7062848A1AB005C', `
|
||||
'A48C2BEE680E841632CD4E44F07496B3EB3C1762', `
|
||||
'B9E2F5981AA6E0CD28160D9FF13993A75599653C' `
|
||||
) | foreach { `
|
||||
gpg --keyserver ha.pool.sks-keyservers.net --recv-keys $_ ; `
|
||||
}
|
||||
|
||||
ENV NODE_VERSION=$node_version
|
||||
|
||||
RUN Invoke-WebRequest $('https://nodejs.org/dist/v{0}/SHASUMS256.txt.asc' -f $env:NODE_VERSION) -OutFile 'SHASUMS256.txt.asc' -UseBasicParsing ; `
|
||||
gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc
|
||||
|
||||
RUN Invoke-WebRequest $('https://nodejs.org/dist/v{0}/node-v{0}-win-x64.zip' -f $env:NODE_VERSION) -OutFile 'node.zip' -UseBasicParsing ; `
|
||||
$sum = $(cat SHASUMS256.txt.asc | sls $(' node-v{0}-win-x64.zip' -f $env:NODE_VERSION)) -Split ' ' ; `
|
||||
if ((Get-FileHash node.zip -Algorithm sha256).Hash -ne $sum[0]) { Write-Error 'SHA256 mismatch' } ; `
|
||||
Expand-Archive node.zip -DestinationPath C:\ ; `
|
||||
Rename-Item -Path $('C:\node-v{0}-win-x64' -f $env:NODE_VERSION) -NewName 'C:\nodejs'
|
||||
|
||||
ENV YARN_VERSION=$yarn_version
|
||||
|
||||
RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 ; `
|
||||
Invoke-WebRequest $('https://yarnpkg.com/downloads/{0}/yarn-{0}.msi' -f $env:YARN_VERSION) -OutFile yarn.msi -UseBasicParsing ; `
|
||||
$sig = Get-AuthenticodeSignature yarn.msi ; `
|
||||
if ($sig.Status -ne 'Valid') { Write-Error 'Authenticode signature is not valid' } ; `
|
||||
Write-Output $sig.SignerCertificate.Thumbprint ; `
|
||||
if (@( `
|
||||
'7E253367F8A102A91D04829E37F3410F14B68A5F', `
|
||||
'AF764E1EA56C762617BDC757C8B0F3780A0CF5F9' `
|
||||
) -notcontains $sig.SignerCertificate.Thumbprint) { Write-Error 'Unknown signer certificate' } ; `
|
||||
Start-Process msiexec.exe -ArgumentList '/i', 'yarn.msi', '/quiet', '/norestart' -NoNewWindow -Wait
|
||||
|
||||
ENV GIT_VERSION 2.20.1
|
||||
ENV GIT_DOWNLOAD_URL https://github.com/git-for-windows/git/releases/download/v${GIT_VERSION}.windows.1/MinGit-${GIT_VERSION}-busybox-64-bit.zip
|
||||
ENV GIT_SHA256 9817ab455d9cbd0b09d8664b4afbe4bbf78d18b556b3541d09238501a749486c
|
||||
|
||||
RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 ; `
|
||||
Invoke-WebRequest -UseBasicParsing $env:GIT_DOWNLOAD_URL -OutFile git.zip; `
|
||||
if ((Get-FileHash git.zip -Algorithm sha256).Hash -ne $env:GIT_SHA256) {exit 1} ; `
|
||||
Expand-Archive git.zip -DestinationPath C:\git; `
|
||||
Remove-Item git.zip
|
||||
|
||||
FROM $target as baseimage
|
||||
|
||||
ENV NPM_CONFIG_LOGLEVEL info
|
||||
|
||||
COPY --from=download /nodejs /nodejs
|
||||
COPY --from=download [ "/Program Files (x86)/yarn", "/yarn" ]
|
||||
COPY --from=download /git /git
|
||||
|
||||
ARG SETX=/M
|
||||
RUN setx %SETX% PATH "%PATH%;C:\nodejs;C:\yarn\bin;C:\git\cmd;C:\git\mingw64\bin;C:\git\usr\bin"
|
||||
|
||||
CMD [ "node.exe" ]
|
||||
|
||||
FROM baseimage
|
||||
|
||||
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
|
||||
|
||||
# Install Bazel prereqs on Windows (https://docs.bazel.build/versions/master/install-windows.html)
|
||||
|
||||
# Install MSYS2
|
||||
RUN Invoke-WebRequest -UseBasicParsing 'https://www.7-zip.org/a/7z1805-x64.exe' -OutFile 7z.exe; `
|
||||
Start-Process -FilePath 'C:\\7z.exe' -ArgumentList '/S', '/D=C:\\7zip0' -NoNewWindow -Wait; `
|
||||
Invoke-WebRequest -UseBasicParsing 'http://repo.msys2.org/distrib/x86_64/msys2-base-x86_64-20180531.tar.xz' -OutFile msys2.tar.xz; `
|
||||
Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'e', 'msys2.tar.xz' -Wait; `
|
||||
Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'x', 'msys2.tar', '-oC:\\' -Wait; `
|
||||
Remove-Item msys2.tar.xz; `
|
||||
Remove-Item msys2.tar; `
|
||||
Remove-Item 7z.exe; `
|
||||
Remove-Item -Recurse 7zip; `
|
||||
[Environment]::SetEnvironmentVariable('Path', $env:Path + ';C:\msys64\usr\bin', [System.EnvironmentVariableTarget]::Machine); `
|
||||
[Environment]::SetEnvironmentVariable('BAZEL_SH', 'C:\msys64\usr\bin\bash.exe', [System.EnvironmentVariableTarget]::Machine)
|
||||
|
||||
# Install MSYS2 packages
|
||||
RUN C:\msys64\usr\bin\bash.exe -l -c \"pacman --needed --noconfirm -S zip unzip patch diffutils git\"
|
||||
|
||||
# Install VS Build Tools (required to build C++ targets)
|
||||
RUN Invoke-WebRequest -UseBasicParsing https://download.visualstudio.microsoft.com/download/pr/df649173-11e9-4af2-8eb7-0eb02ba8958a/cadb5bdac41e55bb8f6a6b7c45273370/vs_buildtools.exe -OutFile vs_BuildTools.exe; `
|
||||
# Installer won't detect DOTNET_SKIP_FIRST_TIME_EXPERIENCE if ENV is used, must use setx /M
|
||||
setx /M DOTNET_SKIP_FIRST_TIME_EXPERIENCE 1; `
|
||||
Start-Process vs_BuildTools.exe `
|
||||
-ArgumentList `
|
||||
'--add', 'Microsoft.VisualStudio.Workload.VCTools', `
|
||||
'--add', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', `
|
||||
'--add', 'Microsoft.Component.VC.Runtime.UCRTSDK', `
|
||||
'--add', 'Microsoft.VisualStudio.Component.Windows10SDK.17763', `
|
||||
'--quiet', '--norestart', '--nocache' `
|
||||
-NoNewWindow -Wait; `
|
||||
Remove-Item -Force vs_buildtools.exe; `
|
||||
Remove-Item -Force -Recurse \"${Env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\"; `
|
||||
Remove-Item -Force -Recurse ${Env:TEMP}\*; `
|
||||
Remove-Item -Force -Recurse \"${Env:ProgramData}\Package Cache\"; `
|
||||
[Environment]::SetEnvironmentVariable('BAZEL_VC', \"${Env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\BuildTools\VC\", [System.EnvironmentVariableTarget]::Machine)
|
||||
|
||||
# Install Python (required to build Python targets)
|
||||
RUN Invoke-WebRequest -UseBasicParsing https://www.python.org/ftp/python/3.5.1/python-3.5.1.exe -OutFile python-3.5.1.exe; `
|
||||
Start-Process python-3.5.1.exe -ArgumentList '/quiet InstallAllUsers=1 PrependPath=1' -Wait; `
|
||||
Remove-Item -Force python-3.5.1.exe
|
||||
|
||||
CMD ["cmd.exe"]
|
@ -1,33 +0,0 @@
|
||||
# CodeFresh configuration
|
||||
|
||||
[](https://g.codefresh.io/public/accounts/angular/pipelines/angular/angular/angular)
|
||||
|
||||
This folder contains configuration for the [CodeFresh](<https://codefresh.io/>) based CI checks for this repository.
|
||||
|
||||
## The build pipeline
|
||||
|
||||
CodeFresh uses a several pipeline for each repository. The `codefresh.yml` file defines pipeline [build steps](https://codefresh.io/docs/docs/configure-ci-cd-pipeline/introduction-to-codefresh-pipelines/) for this repository.
|
||||
|
||||
Run results can be seen in the GitHub checks interface and in the [public pipeline](https://g.codefresh.io/public/accounts/angular/pipelines/angular/angular/angular)
|
||||
|
||||
Although most configuration is done via `pipeline.yml`, some options are only available in the online [pipeline settings](https://g.codefresh.io/pipelines/angular/services?repoOwner=angular&repoName=angular&project=angular%2Fangular&context=github&serviceName=angular%2Fangular), which needs a login to access.
|
||||
|
||||
|
||||
## Caretaker
|
||||
|
||||
CodeFresh status can be found at <http://status.codefresh.io/>.
|
||||
|
||||
Issues related to the CodeFresh setup should be escalated to the Tools Team via the current caretaker, followed by Alex Eagle and Filipe Silva.
|
||||
|
||||
## Rollout strategy
|
||||
|
||||
Currently it is only used for tests on Windows platforms, on the master branch, and without pushing user-facing reports. It's only possible to see current builds in the [public pipeline dashboard](https://g.codefresh.io/public/accounts/angular/pipelines/angular/angular/angular).
|
||||
|
||||
After a week or two of running like this, we should reassess how stable and reliable it is.
|
||||
|
||||
Next steps include:
|
||||
- building PRs
|
||||
- showing build status publicly
|
||||
- blocking PRs that break the build
|
||||
- expanding the test suite
|
||||
|
@ -1,38 +0,0 @@
|
||||
# These options are enabled when running on CI
|
||||
# We do this by copying this file to /etc/bazel.bazelrc at the start of the build.
|
||||
# See documentation in /docs/BAZEL.md
|
||||
|
||||
# Save built files and downloaded repositories in a location that can be cached by CodeFresh and
|
||||
# shared between builds. This helps speed up the analysis time significantly with Bazel managed node
|
||||
# dependencies on the CI.
|
||||
# https://codefresh.io/docs/docs/configure-ci-cd-pipeline/introduction-to-codefresh-pipelines/#caching-the-artifacts-of-your-build-system
|
||||
build --repository_cache=C:/codefresh/volume/bazel_repository_cache
|
||||
# Setting the output_base to a Docker volume is currently broken because of a Docker bug on Windows:
|
||||
# https://github.com/moby/moby/issues/37024
|
||||
# This affects Bazel because bazel_output_base\external\bazel_tools is an absolute path junction.
|
||||
# When its fixed we can uncomment this line, and use a different output_base for Ivy tests (they
|
||||
# use a separate compiler and destructively replace the cache).
|
||||
# startup --output_base=C:/codefresh/volume/bazel_output_base
|
||||
|
||||
# Don't be spammy in the logs
|
||||
# TODO(gmagolan): Hide progress again once build performance improves
|
||||
# Presently, CircleCI can timeout during bazel test ... with the following
|
||||
# error: Too long with no output (exceeded 10m0s)
|
||||
build --noshow_progress
|
||||
|
||||
# Print all the options that apply to the build.
|
||||
# This helps us diagnose which options override others
|
||||
# (e.g. /etc/bazel.bazelrc vs. tools/bazel.rc)
|
||||
build --announce_rc
|
||||
|
||||
# Workaround https://github.com/bazelbuild/bazel/issues/3645
|
||||
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
|
||||
# Limit Bazel to consuming resources that fit in CodeFresh VMs
|
||||
# TODO(filipesilva): determine the correct memory limit
|
||||
build --local_resources=10240,8.0,1.0
|
||||
|
||||
# Retry in the event of flakes, eg. https://circleci.com/gh/angular/angular/31309
|
||||
test --flaky_test_attempts=2
|
||||
|
||||
# More details on failures
|
||||
build --verbose_failures=true
|
@ -1,28 +0,0 @@
|
||||
version: '1.0'
|
||||
|
||||
steps:
|
||||
BuildImage:
|
||||
title: Build Docker image
|
||||
type: build
|
||||
image_name: node-bazel-windows
|
||||
working_directory: ./.codefresh
|
||||
no_cf_cache: true
|
||||
build_arguments:
|
||||
- node_version=10.13.0
|
||||
- yarn_version=1.13.0
|
||||
dockerfile: ./Dockerfile.win-1809
|
||||
|
||||
RunTests:
|
||||
title: Run Bazel tests
|
||||
image: ${{BuildImage}}
|
||||
commands:
|
||||
# Install dependencies
|
||||
- yarn install --frozen-lockfile --non-interactive --network-timeout 100000 --no-progress
|
||||
# Add Bazel CI config
|
||||
- copy .codefresh\bazel.rc %ProgramData%\bazel.bazelrc
|
||||
# Run tests
|
||||
# At the moment 'browser:chromium-local' are broken in CI while locally they work
|
||||
# VE
|
||||
- yarn bazel test --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only,-browser:chromium-local //...
|
||||
# Ivy
|
||||
- yarn bazel test --define=compile=aot --build_tag_filters=-no-ivy-aot,-fixme-ivy-aot --test_tag_filters=-no-ivy-aot,-fixme-ivy-aot,-browser:chromium-local //...
|
31
.devcontainer/README.md
Normal file
31
.devcontainer/README.md
Normal file
@ -0,0 +1,31 @@
|
||||
# VSCode Remote Development - Developing inside a Containers
|
||||
|
||||
This folder contains configuration files that can be used to opt into working on this repository in a [Docker container](https://www.docker.com/resources/what-container) via [VSCode](https://code.visualstudio.com/)'s Remote Development feature (see below).
|
||||
|
||||
Info on remote development and developing inside a container with VSCode:
|
||||
- [VSCode: Remote Development](https://code.visualstudio.com/docs/remote/remote-overview)
|
||||
- [VSCode: Developing inside a Container](https://code.visualstudio.com/docs/remote/containers)
|
||||
- [VSCode: Remote Development FAQ](https://code.visualstudio.com/docs/remote/faq)
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
_Prerequisite: [Install Docker](https://docs.docker.com/install) on your local environment._
|
||||
|
||||
To get started, read and follow the instuctions in [Developing inside a Container](https://code.visualstudio.com/docs/remote/containers). The [.devcontainer/](.) directory contains pre-configured `devcontainer.json` and `Dockerfile` files, which you can use to set up remote development with a docker container.
|
||||
|
||||
In a nutshell, you need to:
|
||||
- Install the [Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension.
|
||||
- Copy [recommended-Dockerfile](./recommended-Dockerfile) to `Dockerfile` (and optionally tweak to suit your needs).
|
||||
- Copy [recommended-devcontainer.json](./recommended-devcontainer.json) to `devcontainer.json` (and optionally tweak to suit your needs).
|
||||
- Open VSCode and bring up the [Command Palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette).
|
||||
- Type `Remote-Containers: Open Folder in Container` and choose your local clone of [angular/angular](https://github.com/angular/angular).
|
||||
|
||||
The `.devcontainer/devcontainer.json` and `.devcontainer/Dockerfile` files are ignored by git, so you can have your own local versions. We may occasionally update the template files ([recommended-devcontainer.json](./recommended-devcontainer.json), [recommended-Dockerfile](./recommended-Dockerfile)), in which case you will need to manually update your local copies (if desired).
|
||||
|
||||
|
||||
## Updating `recommended-devcontainer.json` and `recommended-Dockerfile`
|
||||
|
||||
You can update and commit the recommended config files (which people use as basis for their local configs), if you find that something is broken, out-of-date or can be improved.
|
||||
|
||||
Please, keep in mind that any changes you make will potentially be used by many people on different environments. Try to keep these config files cross-platform compatible and free of personal preferences.
|
@ -1,5 +1,6 @@
|
||||
# Image metadata and config.
|
||||
FROM circleci/node:10-browsers
|
||||
FROM circleci/node:10-browsers # Ideally, the image version should be what we use on CI.
|
||||
# See `executors > browsers-executor` in `.circleci/config.yml`.
|
||||
|
||||
LABEL name="Angular dev environment" \
|
||||
description="This image can be used to create a dev environment for building Angular." \
|
||||
@ -15,7 +16,8 @@ USER root
|
||||
|
||||
# Configure `Node.js`/`npm` and install utilities.
|
||||
RUN npm config --global set user root
|
||||
RUN npm install --global yarn@1.13.0 # This needs to be in sync with what we use on CI.
|
||||
RUN npm install --global yarn@latest # Ideally, the version should be what we use on CI.
|
||||
# See `commands > overwrite_yarn` in `.circleci/config.yml`.
|
||||
|
||||
|
||||
# Go! (And keep going.)
|
||||
|
939
.github/CODEOWNERS
vendored
939
.github/CODEOWNERS
vendored
@ -1,939 +0,0 @@
|
||||
# ==================================================================================
|
||||
# ==================================================================================
|
||||
# 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
|
||||
# benlesh - Ben Lesh
|
||||
# brandonroberts - Brandon Roberts
|
||||
# devversion - Paul Gschwendtner
|
||||
# filipesilva - Filipe Silva
|
||||
# gkalpak - George Kalpakas
|
||||
# hansl - Hans Larsen
|
||||
# IgorMinar - Igor Minar
|
||||
# jasonaden - Jason Aden
|
||||
# jenniferfell - Jennifer Fell
|
||||
# JiaLiPassion - Jia Li
|
||||
# josephperrott - Joey Perrott
|
||||
# kara - Kara Erickson
|
||||
# kyliau - Keen Yee Liau
|
||||
# matsko - Matias Niemelä
|
||||
# mgechev - Minko Gechev
|
||||
# mhevery - Misko Hevery
|
||||
# ocombe - Olivier Combe
|
||||
# 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
|
||||
# - kara
|
||||
# - mhevery
|
||||
# - alexeagle
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @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)
|
||||
#
|
||||
# - brandonroberts
|
||||
# - gkalpak
|
||||
# - jenniferfell
|
||||
# - petebacondarwin
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/fw-animations
|
||||
# ===========================================================
|
||||
#
|
||||
# - matsko
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/tools-bazel
|
||||
# ===========================================================
|
||||
#
|
||||
# - alexeagle
|
||||
# - kyliau
|
||||
# - IgorMinar
|
||||
# - mgechev
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/tools-cli
|
||||
# ===========================================================
|
||||
#
|
||||
# - alexeagle
|
||||
# - filipesilva
|
||||
# - hansl
|
||||
# - mgechev
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/fw-compiler
|
||||
# ===========================================================
|
||||
#
|
||||
# - alxhub
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @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
|
||||
# ===========================================================
|
||||
#
|
||||
# - kara
|
||||
# - jasonaden
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/tools-language-service
|
||||
# ===========================================================
|
||||
#
|
||||
# - kyliau
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/fw-server
|
||||
# ===========================================================
|
||||
#
|
||||
# - alxhub
|
||||
# - vikerman
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/fw-router
|
||||
# ===========================================================
|
||||
#
|
||||
# - jasonaden
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/fw-service-worker
|
||||
# ===========================================================
|
||||
#
|
||||
# - alxhub
|
||||
# - gkalpak
|
||||
# - IgorMinar
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/fw-upgrade
|
||||
# ===========================================================
|
||||
#
|
||||
# - gkalpak
|
||||
# - petebacondarwin
|
||||
# - jasonaden
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/fw-testing
|
||||
# ===========================================================
|
||||
#
|
||||
# - vikerman
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/fw-i18n
|
||||
# ===========================================================
|
||||
#
|
||||
# - AndrewKushnir
|
||||
# - mhevery
|
||||
# - ocombe
|
||||
# - vikerman
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/fw-security
|
||||
# ===========================================================
|
||||
#
|
||||
# - IgorMinar
|
||||
# - mhevery
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/fw-zones
|
||||
# ===========================================================
|
||||
#
|
||||
# - JiaLiPassion
|
||||
# - mhevery
|
||||
# - vikerman
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/tools-benchpress
|
||||
# ===========================================================
|
||||
#
|
||||
# - alxhub
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/fw-integration
|
||||
# ===========================================================
|
||||
#
|
||||
# - alexeagle
|
||||
# - IgorMinar
|
||||
# - mhevery
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/docs-infra
|
||||
# ===========================================================
|
||||
#
|
||||
# - brandonroberts
|
||||
# - gkalpak
|
||||
# - IgorMinar
|
||||
# - petebacondarwin
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/fw-docs-intro
|
||||
# ===========================================================
|
||||
#
|
||||
# - jenniferfell
|
||||
# - brandonroberts
|
||||
# - IgorMinar
|
||||
# - stephenfluin
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/fw-docs-observables
|
||||
# ===========================================================
|
||||
#
|
||||
# - benlesh
|
||||
# - jasonaden
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/fw-docs-packaging
|
||||
# ===========================================================
|
||||
#
|
||||
# - alexeagle
|
||||
# - IgorMinar
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/tools-docs-libraries
|
||||
# ===========================================================
|
||||
#
|
||||
# - alan-agius4
|
||||
# - alexeagle
|
||||
# - hansl
|
||||
# - IgorMinar
|
||||
# - mgechev
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/tools-docs-schematics
|
||||
# ===========================================================
|
||||
#
|
||||
# - alan-agius4
|
||||
# - alexeagle
|
||||
# - hansl
|
||||
# - IgorMinar
|
||||
# - mgechev
|
||||
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/fw-docs-marketing
|
||||
# ===========================================================
|
||||
#
|
||||
# - IgorMinar
|
||||
# - stephenfluin
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/fw-public-api
|
||||
# ===========================================================
|
||||
#
|
||||
# - IgorMinar
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/fw-dev-infra
|
||||
# ===========================================================
|
||||
#
|
||||
# - alexeagle
|
||||
# - devversion
|
||||
# - filipesilva
|
||||
# - gkalpak
|
||||
# - IgorMinar
|
||||
# - josephperrott
|
||||
|
||||
|
||||
|
||||
|
||||
######################################################################################################
|
||||
#
|
||||
# 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
|
||||
|
||||
|
||||
|
||||
# ================================================
|
||||
# @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
|
||||
|
||||
|
||||
|
||||
# ================================================
|
||||
# @angular/compiler
|
||||
# @angular/compiler-cli
|
||||
# ================================================
|
||||
|
||||
/packages/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/aot-compiler.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/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/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/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
|
||||
|
||||
/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/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/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/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
|
||||
/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
|
||||
/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
|
||||
/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
|
||||
/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/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
|
||||
/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/change-log.md @angular/docs-infra @angular/framework-global-approvers
|
||||
/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/quickstart.md @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/cli-quickstart/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/images/guide/cli-quickstart/** @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/getting-started/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/images/guide/getting-started/** @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/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/guide/setup.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/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
|
||||
|
||||
|
||||
|
||||
# ================================================
|
||||
# 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
|
||||
|
||||
|
||||
|
||||
# ================================================
|
||||
# Build & CI Owners
|
||||
# ================================================
|
||||
|
||||
/* @angular/fw-dev-infra
|
||||
/.buildkite/** @angular/fw-dev-infra
|
||||
/.circleci/** @angular/fw-dev-infra
|
||||
/.codefresh/** @angular/fw-dev-infra
|
||||
/.devcontainer/** @angular/fw-dev-infra
|
||||
/.github/** @angular/fw-dev-infra
|
||||
/.vscode/** @angular/fw-dev-infra
|
||||
/docs/BAZEL.md @angular/fw-dev-infra
|
||||
/packages/* @angular/fw-dev-infra
|
||||
/scripts/** @angular/fw-dev-infra
|
||||
/third_party/** @angular/fw-dev-infra
|
||||
/tools/build/** @angular/fw-dev-infra
|
||||
/tools/cjs-jasmine/** @angular/fw-dev-infra
|
||||
/tools/gulp-tasks/** @angular/fw-dev-infra
|
||||
/tools/ngcontainer/** @angular/fw-dev-infra
|
||||
/tools/npm/** @angular/fw-dev-infra
|
||||
/tools/npm_workspace/** @angular/fw-dev-infra
|
||||
/tools/public_api_guard/** @angular/fw-dev-infra
|
||||
/tools/rxjs/** @angular/fw-dev-infra
|
||||
/tools/source-map-test/** @angular/fw-dev-infra
|
||||
/tools/symbol-extractor/** @angular/fw-dev-infra
|
||||
/tools/testing/** @angular/fw-dev-infra
|
||||
/tools/ts-api-guardian/** @angular/fw-dev-infra
|
||||
/tools/tslint/** @angular/fw-dev-infra
|
||||
/tools/validate-commit-message/** @angular/fw-dev-infra
|
||||
/tools/yarn/** @angular/fw-dev-infra
|
||||
/tools/*
|
||||
*.bzl @angular/fw-dev-infra
|
||||
|
||||
|
||||
|
||||
# ================================================
|
||||
# Material CI
|
||||
# ================================================
|
||||
|
||||
/tools/material-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
|
||||
|
||||
|
||||
|
||||
# ================================================
|
||||
# CODEOWNERS Owners owners ...
|
||||
# ================================================
|
||||
|
||||
/.github/CODEOWNERS @IgorMinar @angular/framework-global-approvers
|
13
.github/ISSUE_TEMPLATE/7-angular-components.md
vendored
Normal file
13
.github/ISSUE_TEMPLATE/7-angular-components.md
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
name: "\U0001F48EAngular Components"
|
||||
about: Issues and feature requests for Angular Components
|
||||
|
||||
---
|
||||
|
||||
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
|
||||
|
||||
Please file any Angular Components issues at: https://github.com/angular/components/issues/new
|
||||
|
||||
For the time being, we keep Angular Components issues in a separate repository.
|
||||
|
||||
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
|
13
.github/ISSUE_TEMPLATE/7-angular-material.md
vendored
13
.github/ISSUE_TEMPLATE/7-angular-material.md
vendored
@ -1,13 +0,0 @@
|
||||
---
|
||||
name: "\U0001F48EAngular Material"
|
||||
about: Issues and feature requests for Angular Material
|
||||
|
||||
---
|
||||
|
||||
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
|
||||
|
||||
Please file any Angular Material issues at: https://github.com/angular/material2/issues/new
|
||||
|
||||
For the time being, we keep Angular Material issues in a separate repository.
|
||||
|
||||
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
|
9
.github/angular-robot.yml
vendored
9
.github/angular-robot.yml
vendored
@ -30,7 +30,7 @@ merge:
|
||||
# text to show when the status is success
|
||||
successDesc: "Does not affect google3"
|
||||
# link to use for the details
|
||||
url: "http://go/angular-g3sync"
|
||||
url: "http://go/angular/g3sync"
|
||||
# list of patterns to check for the files changed by the PR
|
||||
# this list must be manually kept in sync with google3/third_party/javascript/angular2/copy.bara.sky
|
||||
include:
|
||||
@ -52,6 +52,7 @@ merge:
|
||||
- "packages/elements/schematics/**"
|
||||
- "packages/examples/**"
|
||||
- "packages/language-service/**"
|
||||
- "packages/localize/**"
|
||||
- "packages/private/**"
|
||||
- "packages/service-worker/**"
|
||||
- "**/.gitignore"
|
||||
@ -61,10 +62,15 @@ merge:
|
||||
- "**/third_party/**"
|
||||
- "**/tsconfig-build.json"
|
||||
- "**/tsconfig.json"
|
||||
- "**/rollup.config.js"
|
||||
- "**/BUILD.bazel"
|
||||
- "**/*.md"
|
||||
- "packages/**/integrationtest/**"
|
||||
- "packages/**/test/**"
|
||||
- "packages/zone.js/*"
|
||||
- "packages/zone.js/doc/**"
|
||||
- "packages/zone.js/example/**"
|
||||
- "packages/zone.js/scripts/**"
|
||||
|
||||
# comment that will be added to a PR when there is a conflict, leave empty or set to false to disable
|
||||
mergeConflictComment: "Hi @{{PRAuthor}}! This PR has merge conflicts due to recent upstream merges.
|
||||
@ -109,6 +115,7 @@ merge:
|
||||
- "ci/angular: size"
|
||||
- "cla/google"
|
||||
- "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
|
||||
|
15
.github/workflows/lock-closed.yml
vendored
Normal file
15
.github/workflows/lock-closed.yml
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
name: Lock closed inactive issues
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run at 16:00 every day
|
||||
- cron: '0 16 * * *'
|
||||
|
||||
jobs:
|
||||
lock_closed:
|
||||
if: github.repository == 'angular/angular'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: angular/dev-infra/github-actions/lock-closed@66462f6
|
||||
with:
|
||||
lock-bot-key: ${{ secrets.LOCK_BOT_PRIVATE_KEY }}
|
9
.gitignore
vendored
9
.gitignore
vendored
@ -3,23 +3,26 @@
|
||||
/dist/
|
||||
/bazel-out
|
||||
/integration/bazel/bazel-*
|
||||
e2e_test.*
|
||||
*.log
|
||||
node_modules
|
||||
tools/gulp-tasks/cldr/cldr-data/
|
||||
|
||||
# Include when developing application packages.
|
||||
pubspec.lock
|
||||
.c9
|
||||
.idea/
|
||||
.devcontainer/*
|
||||
!.devcontainer/README.md
|
||||
!.devcontainer/recommended-devcontainer.json
|
||||
!.devcontainer/recommended-Dockerfile
|
||||
.settings/
|
||||
.vscode/launch.json
|
||||
.vscode/settings.json
|
||||
.vscode/tasks.json
|
||||
*.swo
|
||||
modules/.settings
|
||||
modules/.vscode
|
||||
.vimrc
|
||||
.nvimrc
|
||||
|
||||
# Don't check in secret files
|
||||
*secret.js
|
||||
@ -37,3 +40,5 @@ yarn-error.log
|
||||
# User specific bazel settings
|
||||
.bazelrc.user
|
||||
|
||||
.notes.md
|
||||
baseline.json
|
||||
|
86
.ng-dev-config.ts
Normal file
86
.ng-dev-config.ts
Normal file
@ -0,0 +1,86 @@
|
||||
// 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',
|
||||
'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': [
|
||||
'dev-infra/**/*.{js,ts}',
|
||||
'packages/**/*.{js,ts}',
|
||||
'!packages/zone.js',
|
||||
'!packages/common/locales/**/*.{js,ts}',
|
||||
'!packages/common/src/i18n/available_locales.ts',
|
||||
'!packages/common/src/i18n/currencies.ts',
|
||||
'!packages/common/src/i18n/locale_en.ts',
|
||||
'modules/benchmarks/**/*.{js,ts}',
|
||||
'modules/playground/**/*.{js,ts}',
|
||||
'tools/**/*.{js,ts}',
|
||||
'!tools/gulp-tasks/cldr/extract.js',
|
||||
'!tools/public_api_guard/**/*.d.ts',
|
||||
'!tools/ts-api-guardian/test/fixtures/**',
|
||||
'*.{js,ts}',
|
||||
'!**/node_modules/**',
|
||||
'!**/dist/**',
|
||||
'!**/built/**',
|
||||
'!shims_for_IE.js',
|
||||
]
|
||||
},
|
||||
'buildifier': true
|
||||
};
|
||||
|
||||
// Github metadata information for `ng-dev` commands.
|
||||
const github = {
|
||||
owner: 'angular',
|
||||
name: 'angular',
|
||||
};
|
||||
|
||||
// Export function to build ng-dev configuration object.
|
||||
module.exports = {
|
||||
commitMessage,
|
||||
format,
|
||||
github,
|
||||
};
|
1100
.pullapprove.yml
Normal file
1100
.pullapprove.yml
Normal file
File diff suppressed because it is too large
Load Diff
18
.vscode/README.md
vendored
18
.vscode/README.md
vendored
@ -1,23 +1,25 @@
|
||||
# VSCode Configuration
|
||||
|
||||
This folder contains opt-in [Workspace Settings](https://code.visualstudio.com/docs/getstarted/settings) and [Extension Recommendations](https://code.visualstudio.com/docs/editor/extension-gallery#_workspace-recommended-extensions) that the Angular team recommends using when working on this repository.
|
||||
This folder contains opt-in [Workspace Settings](https://code.visualstudio.com/docs/getstarted/settings), [Tasks](https://code.visualstudio.com/docs/editor/tasks), [Launch Configurations](https://code.visualstudio.com/Docs/editor/debugging#_launch-configurations) and [Extension Recommendations](https://code.visualstudio.com/docs/editor/extension-gallery#_workspace-recommended-extensions) that the Angular team recommends using when working on this repository.
|
||||
|
||||
## Usage
|
||||
|
||||
To use the recommended settings follow the steps below:
|
||||
To use the recommended configurations follow the steps below:
|
||||
|
||||
- install <https://marketplace.visualstudio.com/items?itemName=xaver.clang-format>
|
||||
- copy `.vscode/recommended-settings.json` to `.vscode/settings.json`
|
||||
- install the recommneded extensions in `.vscode/extensions.json`
|
||||
- copy (or link) `.vscode/recommended-settings.json` to `.vscode/settings.json`
|
||||
- copy (or link) `.vscode/recommended-launch.json` to `.vscode/launch.json`
|
||||
- copy (or link) `.vscode/recommended-tasks.json` to `.vscode/tasks.json`
|
||||
- restart the editor
|
||||
|
||||
If you already have your custom workspace settings you should instead manually merge the file content.
|
||||
If you already have your custom workspace settings you should instead manually merge the file contents.
|
||||
|
||||
This isn't an automatic process so you will need to repeat it when settings are updated.
|
||||
|
||||
To see the recommended extensions select "Extensions: Show Recommended Extensions" in the [Command Palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette).
|
||||
|
||||
## Editing `.vscode/recommended-settings.json`
|
||||
## Editing `.vscode/recommended-*.json` files
|
||||
|
||||
If you wish to add extra configuration items please keep in mind any settings you add here will be used by many users.
|
||||
If you wish to add extra configuration items please keep in mind any modifications you make here will be used by many users.
|
||||
|
||||
Try to keep these settings to things that help facilitate the development process and avoid altering the user workflow whenever possible.
|
||||
Try to keep these settings/configuations to things that help facilitate the development process and avoid altering the user workflow whenever possible.
|
||||
|
85
.vscode/recommended-launch.json
vendored
Normal file
85
.vscode/recommended-launch.json
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Attach to bazel test ... --config=debug",
|
||||
"type": "node",
|
||||
"request": "attach",
|
||||
"port": 9229,
|
||||
"address": "localhost",
|
||||
"restart": false,
|
||||
"sourceMaps": true,
|
||||
"localRoot": "${workspaceRoot}",
|
||||
"remoteRoot": "${workspaceRoot}",
|
||||
"stopOnEntry": false,
|
||||
"timeout": 600000,
|
||||
},
|
||||
{
|
||||
"name": "Attach to bazel test ... --config=debug (no source maps)",
|
||||
"type": "node",
|
||||
"request": "attach",
|
||||
"port": 9229,
|
||||
"address": "localhost",
|
||||
"restart": false,
|
||||
"sourceMaps": false,
|
||||
"localRoot": "${workspaceRoot}",
|
||||
"remoteRoot": "${workspaceRoot}",
|
||||
"stopOnEntry": false,
|
||||
"timeout": 600000,
|
||||
},
|
||||
{
|
||||
"name": "IVY:packages/core/test/acceptance",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
||||
"args": [
|
||||
"test",
|
||||
"--config=ivy",
|
||||
"packages/core/test/acceptance",
|
||||
"--config=debug"
|
||||
],
|
||||
"port": 9229,
|
||||
"address": "localhost",
|
||||
"restart": true,
|
||||
"sourceMaps": true,
|
||||
"timeout": 600000,
|
||||
},
|
||||
{
|
||||
"name": "IVY:packages/core/test/render3",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
||||
"args": [
|
||||
"test",
|
||||
"--config=ivy",
|
||||
"packages/core/test/render3",
|
||||
"--config=debug"
|
||||
],
|
||||
"port": 9229,
|
||||
"address": "localhost",
|
||||
"restart": true,
|
||||
"sourceMaps": true,
|
||||
"timeout": 600000,
|
||||
},
|
||||
{
|
||||
"name": "IVY:packages/core/test",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
||||
"args": [
|
||||
"test",
|
||||
"--config=ivy",
|
||||
"packages/core/test",
|
||||
"--config=debug"
|
||||
],
|
||||
"port": 9229,
|
||||
"address": "localhost",
|
||||
"restart": true,
|
||||
"sourceMaps": true,
|
||||
"timeout": 600000,
|
||||
},
|
||||
]
|
||||
}
|
113
.vscode/recommended-tasks.json
vendored
Normal file
113
.vscode/recommended-tasks.json
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "IVY:packages/core/test/...",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
||||
"args": [
|
||||
"test",
|
||||
"--config=ivy",
|
||||
"packages/core/test",
|
||||
"packages/core/test/acceptance",
|
||||
"packages/core/test/render3",
|
||||
],
|
||||
"group": "test",
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated",
|
||||
},
|
||||
},
|
||||
{
|
||||
"label": "VE:packages/core/test/...",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
||||
"args": [
|
||||
"test",
|
||||
"packages/core/test",
|
||||
"packages/core/test/acceptance",
|
||||
"packages/core/test/render3",
|
||||
],
|
||||
"group": "test",
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated",
|
||||
},
|
||||
},
|
||||
{
|
||||
"label": "IVY:packages/core/test/acceptance",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
||||
"args": [
|
||||
"test",
|
||||
"--config=ivy",
|
||||
"packages/core/test/acceptance",
|
||||
],
|
||||
"group": "test",
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated",
|
||||
},
|
||||
},
|
||||
{
|
||||
"label": "VE:packages/core/test/acceptance",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
||||
"args": [
|
||||
"test",
|
||||
"packages/core/test/acceptance",
|
||||
],
|
||||
"group": "test",
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated",
|
||||
},
|
||||
},
|
||||
{
|
||||
"label": "IVY:packages/core/test",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
||||
"args": [
|
||||
"test",
|
||||
"--config=ivy",
|
||||
"packages/core/test",
|
||||
],
|
||||
"group": "test",
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated",
|
||||
},
|
||||
},
|
||||
{
|
||||
"label": "VE:packages/core/test",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
||||
"args": [
|
||||
"test",
|
||||
"packages/core/test",
|
||||
],
|
||||
"group": "test",
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated",
|
||||
},
|
||||
},
|
||||
{
|
||||
"label": "IVY:packages/core/test/render3",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
||||
"args": [
|
||||
"test",
|
||||
"--config=ivy",
|
||||
"packages/core/test/render3",
|
||||
],
|
||||
"group": "test",
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
13
.yarn/README.md
Normal file
13
.yarn/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
# Yarn Vendoring
|
||||
We utilize Yarn's `yarn-path` configuration in a shared `.yarnrc` file to enforce
|
||||
everyone using the same version of Yarn. Yarn checks the `.yarnrc` file to
|
||||
determine if yarn should delegate the command to a vendored version at the
|
||||
provided path.
|
||||
|
||||
## How to update
|
||||
To update to the latest version of Yarn as our vendored version:
|
||||
- Run this command
|
||||
```sh
|
||||
yarn policies set-version latest
|
||||
```
|
||||
- Remove the previous version
|
22825
third_party/github.com/yarnpkg/yarn/releases/download/v1.13.0/bin/yarn.js → .yarn/releases/yarn-1.22.4.js
vendored
Normal file → Executable file
22825
third_party/github.com/yarnpkg/yarn/releases/download/v1.13.0/bin/yarn.js → .yarn/releases/yarn-1.22.4.js
vendored
Normal file → Executable file
File diff suppressed because one or more lines are too long
5
.yarnrc
Normal file
5
.yarnrc
Normal file
@ -0,0 +1,5 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
yarn-path ".yarn/releases/yarn-1.22.4.js"
|
57
BUILD.bazel
57
BUILD.bazel
@ -1,12 +1,14 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("//tools:defaults.bzl", "karma_web_test")
|
||||
|
||||
exports_files([
|
||||
"LICENSE",
|
||||
"protractor-perf.conf.js",
|
||||
"karma-js.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(
|
||||
@ -19,9 +21,9 @@ filegroup(
|
||||
# do not sort
|
||||
srcs = [
|
||||
"@npm//:node_modules/core-js/client/core.js",
|
||||
"@npm//:node_modules/zone.js/dist/zone.js",
|
||||
"@npm//:node_modules/zone.js/dist/zone-testing.js",
|
||||
"@npm//:node_modules/zone.js/dist/task-tracking.js",
|
||||
"//packages/zone.js/dist:zone.js",
|
||||
"//packages/zone.js/dist:zone-testing.js",
|
||||
"//packages/zone.js/dist:task-tracking.js",
|
||||
"//:test-events.js",
|
||||
"//:shims_for_IE.js",
|
||||
# Including systemjs because it defines `__eval`, which produces correct stack traces.
|
||||
@ -46,48 +48,3 @@ filegroup(
|
||||
"@npm//:node_modules/angular-mocks-1.6/angular-mocks.js",
|
||||
],
|
||||
)
|
||||
|
||||
# To run a karma_web_test target locally on SauceLabs:
|
||||
# 1) have SAUCE_USERNAME, SAUCE_ACCESS_KEY (and optionally a SAUCE_TUNNEL_IDENTIFIER) set in your environment
|
||||
# 2) open a sauce connection with `./scripts/saucelabs/start-tunnel.sh`
|
||||
# NOTE: start-tunnel.sh uses `node_modules/sauce-connect` which is current linux specific:
|
||||
# "sauce-connect": "https://saucelabs.com/downloads/sc-4.5.3-linux.tar.gz".
|
||||
# On OSX or Windows you'll need to use the appropriate sauce-connect binary.
|
||||
# 3) run target with `yarn bazel test --config=saucelabs <target>`
|
||||
# NOTE: --config=saucelabs is required as it makes the SAUCE_XXX environment variables available to
|
||||
# the action. See /.bazelrc.
|
||||
karma_web_test(
|
||||
name = "test_web_all",
|
||||
tags = [
|
||||
"local",
|
||||
"manual",
|
||||
"saucelabs",
|
||||
],
|
||||
deps = [
|
||||
# We combine all tests into a single karma_web_test target
|
||||
# as running them as seperate 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)
|
||||
],
|
||||
)
|
||||
|
2340
CHANGELOG.md
2340
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@ -55,9 +55,9 @@ We want to fix all the issues as soon as possible, but before fixing a bug we ne
|
||||
|
||||
A minimal reproduction allows us to quickly confirm a bug (or point out a coding problem) as well as confirm that we are fixing the right problem.
|
||||
|
||||
We will be insisting on a minimal reproduction scenario in order to save maintainers time and ultimately be able to fix more bugs. Interestingly, from our experience users often find coding problems themselves while preparing a minimal reproduction. We understand that sometimes it might be hard to extract essential bits of code from a larger code-base but we really need to isolate the problem before we can fix it.
|
||||
We will be insisting on a minimal reproduction scenario in order to save maintainers time and ultimately be able to fix more bugs. Interestingly, from our experience, users often find coding problems themselves while preparing a minimal reproduction. We understand that sometimes it might be hard to extract essential bits of code from a larger codebase but we really need to isolate the problem before we can fix it.
|
||||
|
||||
Unfortunately, we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you we are going to close an issue that doesn't have enough info to be reproduced.
|
||||
Unfortunately, we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you, we are going to close an issue that doesn't have enough info to be reproduced.
|
||||
|
||||
You can file new issues by selecting from our [new issue templates](https://github.com/angular/angular/issues/new/choose) and filling out the issue template.
|
||||
|
||||
@ -168,7 +168,7 @@ format that includes a **type**, a **scope** and a **subject**:
|
||||
|
||||
The **header** is mandatory and the **scope** of the header is optional.
|
||||
|
||||
Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
|
||||
Any line of the commit message cannot be longer than 100 characters! This allows the message to be easier
|
||||
to read on GitHub as well as in various git tools.
|
||||
|
||||
The footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages/) if any.
|
||||
@ -201,11 +201,13 @@ Must be one of the following:
|
||||
* **test**: Adding missing tests or correcting existing tests
|
||||
|
||||
### Scope
|
||||
The scope should be the name of the npm package affected (as perceived by the person reading the changelog generated from commit messages.
|
||||
The scope should be the name of the npm package affected (as perceived by the person reading the changelog generated from commit messages).
|
||||
|
||||
The following is the list of supported scopes:
|
||||
|
||||
* **animations**
|
||||
* **bazel**
|
||||
* **benchpress**
|
||||
* **common**
|
||||
* **compiler**
|
||||
* **compiler-cli**
|
||||
@ -214,6 +216,7 @@ The following is the list of supported scopes:
|
||||
* **forms**
|
||||
* **http**
|
||||
* **language-service**
|
||||
* **localize**
|
||||
* **platform-browser**
|
||||
* **platform-browser-dynamic**
|
||||
* **platform-server**
|
||||
@ -232,7 +235,9 @@ There are currently a few exceptions to the "use package name" rule:
|
||||
* **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
|
||||
repo
|
||||
* **ivy**: used for changes to the [Ivy renderer](https://github.com/angular/angular/issues/21706).
|
||||
* **dev-infra**: used for dev-infra related changes within the directories /scripts, /tools and /dev-infra
|
||||
* **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
|
||||
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`).
|
||||
@ -261,8 +266,8 @@ A detailed explanation can be found in this [document][commit-message-format].
|
||||
Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code
|
||||
changes to be accepted, the CLA must be signed. It's a quick process, we promise!
|
||||
|
||||
* For individuals we have a [simple click-through form][individual-cla].
|
||||
* For corporations we'll need you to
|
||||
* For individuals, we have a [simple click-through form][individual-cla].
|
||||
* For corporations, we'll need you to
|
||||
[print, sign and one of scan+email, fax or mail the form][corporate-cla].
|
||||
|
||||
<hr>
|
||||
@ -278,7 +283,6 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
[angular-group]: https://groups.google.com/forum/#!forum/angular
|
||||
[coc]: https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md
|
||||
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2019 Google LLC. http://angular.io/license
|
||||
Copyright (c) 2010-2020 Google LLC. http://angular.io/license
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,12 +1,11 @@
|
||||
[](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://www.npmjs.com/@angular/core)
|
||||
|
||||
|
||||
# Angular
|
||||
|
||||
Angular is a development platform for building mobile and desktop web applications using Typescript/JavaScript and other languages.
|
||||
Angular is a development platform for building mobile and desktop web applications using TypeScript/JavaScript and other languages.
|
||||
|
||||
## Quickstart
|
||||
|
||||
@ -21,7 +20,6 @@ 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
|
||||
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
|
||||
[quickstart]: https://angular.io/start
|
||||
[changelog]: https://github.com/angular/angular/blob/master/CHANGELOG.md
|
||||
|
85
WORKSPACE
85
WORKSPACE
@ -5,79 +5,36 @@ workspace(
|
||||
|
||||
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
|
||||
# Uncomment for local bazel rules development
|
||||
#local_repository(
|
||||
# name = "build_bazel_rules_nodejs",
|
||||
# path = "../rules_nodejs",
|
||||
#)
|
||||
#local_repository(
|
||||
# name = "npm_bazel_typescript",
|
||||
# path = "../rules_typescript",
|
||||
#)
|
||||
|
||||
# Fetch rules_nodejs so we can install our npm dependencies
|
||||
http_archive(
|
||||
name = "build_bazel_rules_nodejs",
|
||||
patch_args = ["-p1"],
|
||||
# Patch https://github.com/bazelbuild/rules_nodejs/pull/903
|
||||
patches = ["//tools:rollup_bundle_commonjs_ignoreGlobal.patch"],
|
||||
sha256 = "7c4a690268be97c96f04d505224ec4cb1ae53c2c2b68be495c9bd2634296a5cd",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.34.0/rules_nodejs-0.34.0.tar.gz"],
|
||||
sha256 = "f9e7b9f42ae202cc2d2ce6d698ccb49a9f7f7ea572a78fd451696d03ef2ee116",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.6.0/rules_nodejs-1.6.0.tar.gz"],
|
||||
)
|
||||
|
||||
# Check the bazel version and download npm dependencies
|
||||
load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "check_rules_nodejs_version", "node_repositories", "yarn_install")
|
||||
# Check the rules_nodejs version and download npm dependencies
|
||||
# Note: bazel (version 2 and after) will check the .bazelversion file so we don't need to
|
||||
# assert on that.
|
||||
load("@build_bazel_rules_nodejs//:index.bzl", "check_rules_nodejs_version", "node_repositories", "yarn_install")
|
||||
|
||||
# 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 = "0.27.0",
|
||||
)
|
||||
|
||||
# The NodeJS rules version must be at least the following version because:
|
||||
# - 0.15.2 Re-introduced the prod_only attribute on yarn_install
|
||||
# - 0.15.3 Includes a fix for the `jasmine_node_test` rule ignoring target tags
|
||||
# - 0.16.8 Supports npm installed bazel workspaces
|
||||
# - 0.26.0 Fix for data files in yarn_install and npm_install
|
||||
# - 0.27.12 Adds NodeModuleSources provider for transtive npm deps support
|
||||
# - 0.30.0 yarn_install now uses symlinked node_modules with new managed directories Bazel 0.26.0 feature
|
||||
# - 0.31.1 entry_point attribute of nodejs_binary & rollup_bundle is now a label
|
||||
# - 0.32.0 yarn_install and npm_install no longer puts build files under symlinked node_modules
|
||||
# - 0.32.1 remove override of @bazel/tsetse & exclude typescript lib declarations in node_module_library transitive_declarations
|
||||
# - 0.32.2 resolves bug in @bazel/hide-bazel-files postinstall step
|
||||
# - 0.34.0 introduces protractor rule
|
||||
check_rules_nodejs_version(minimum_version_string = "0.34.0")
|
||||
check_rules_nodejs_version(minimum_version_string = "1.6.0")
|
||||
|
||||
# Setup the Node.js toolchain
|
||||
node_repositories(
|
||||
node_repositories = {
|
||||
"10.16.0-darwin_amd64": ("node-v10.16.0-darwin-x64.tar.gz", "node-v10.16.0-darwin-x64", "6c009df1b724026d84ae9a838c5b382662e30f6c5563a0995532f2bece39fa9c"),
|
||||
"10.16.0-linux_amd64": ("node-v10.16.0-linux-x64.tar.xz", "node-v10.16.0-linux-x64", "1827f5b99084740234de0c506f4dd2202a696ed60f76059696747c34339b9d48"),
|
||||
"10.16.0-windows_amd64": ("node-v10.16.0-win-x64.zip", "node-v10.16.0-win-x64", "aa22cb357f0fb54ccbc06b19b60e37eefea5d7dd9940912675d3ed988bf9a059"),
|
||||
"12.14.1-darwin_amd64": ("node-v12.14.1-darwin-x64.tar.gz", "node-v12.14.1-darwin-x64", "0be10a28737527a1e5e3784d3ad844d742fe8b0718acd701fd48f718fd3af78f"),
|
||||
"12.14.1-linux_amd64": ("node-v12.14.1-linux-x64.tar.xz", "node-v12.14.1-linux-x64", "07cfcaa0aa9d0fcb6e99725408d9e0b07be03b844701588e3ab5dbc395b98e1b"),
|
||||
"12.14.1-windows_amd64": ("node-v12.14.1-win-x64.zip", "node-v12.14.1-win-x64", "1f96ccce3ba045ecea3f458e189500adb90b8bc1a34de5d82fc10a5bf66ce7e3"),
|
||||
},
|
||||
node_version = "10.16.0",
|
||||
node_version = "12.14.1",
|
||||
package_json = ["//:package.json"],
|
||||
# yarn 1.13.0 under Bazel has a regression on Windows that causes build errors on rebuilds:
|
||||
# ```
|
||||
# ERROR: Source forest creation failed: C:/.../fyuc5c3n/execroot/angular/external (Directory not empty)
|
||||
# ```
|
||||
# See https://github.com/angular/angular/pull/29431 for more information.
|
||||
# It possible that versions of yarn past 1.13.0 do not have this issue, however, before
|
||||
# advancing this version we need to test manually on Windows that the above error does not
|
||||
# happen as the issue is not caught by CI.
|
||||
yarn_version = "1.12.1",
|
||||
)
|
||||
|
||||
load("//integration:angular_integration_test.bzl", "npm_package_archives")
|
||||
|
||||
yarn_install(
|
||||
name = "npm",
|
||||
manual_build_file_contents = npm_package_archives(),
|
||||
package_json = "//:package.json",
|
||||
yarn_lock = "//:yarn.lock",
|
||||
)
|
||||
@ -98,18 +55,16 @@ load("@npm_bazel_protractor//:package.bzl", "npm_bazel_protractor_dependencies")
|
||||
npm_bazel_protractor_dependencies()
|
||||
|
||||
# Load karma dependencies
|
||||
load("@npm_bazel_karma//:package.bzl", "rules_karma_dependencies")
|
||||
load("@npm_bazel_karma//:package.bzl", "npm_bazel_karma_dependencies")
|
||||
|
||||
rules_karma_dependencies()
|
||||
npm_bazel_karma_dependencies()
|
||||
|
||||
# Setup the rules_webtesting toolchain
|
||||
load("@io_bazel_rules_webtesting//web:repositories.bzl", "web_test_repositories")
|
||||
|
||||
web_test_repositories()
|
||||
|
||||
# Temporary work-around for https://github.com/angular/angular/issues/28681
|
||||
# TODO(gregmagolan): go back to @io_bazel_rules_webtesting browser_repositories
|
||||
load("//:browser_repositories.bzl", "browser_repositories")
|
||||
load("//tools/browsers:browser_repositories.bzl", "browser_repositories")
|
||||
|
||||
browser_repositories()
|
||||
|
||||
@ -136,14 +91,14 @@ rbe_autoconfig(
|
||||
# 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
|
||||
# need to pull the image and run it in order determine the toolchain configuration. See:
|
||||
# https://github.com/bazelbuild/bazel-toolchains/blob/0.27.0/configs/ubuntu16_04_clang/versions.bzl
|
||||
base_container_digest = "sha256:94d7d8552902d228c32c8c148cc13f0effc2b4837757a6e95b73fdc5c5e4b07b",
|
||||
# https://github.com/bazelbuild/bazel-toolchains/blob/1.1.2/configs/ubuntu16_04_clang/versions.bzl
|
||||
base_container_digest = "sha256:1ab40405810effefa0b2f45824d6d608634ccddbf06366760c341ef6fbead011",
|
||||
# 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>
|
||||
# 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
|
||||
# @com_google_protobuf. Java is needed for the Bazel's test executor Java tool.
|
||||
digest = "sha256:76e2e4a894f9ffbea0a0cb2fbde741b5d223d40f265dbb9bca78655430173990",
|
||||
digest = "sha256:0b8fa87db4b8e5366717a7164342a029d1348d2feea7ecc4b18c780bc2507059",
|
||||
env = clang_env(),
|
||||
registry = "marketplace.gcr.io",
|
||||
# We can't use the default "ubuntu16_04" RBE image provided by the autoconfig because we need
|
||||
|
@ -14,10 +14,12 @@ Here are the most important tasks you might need to use:
|
||||
|
||||
* `yarn` - install all the dependencies.
|
||||
* `yarn setup` - install all the dependencies, boilerplate, stackblitz, zips and run dgeni on the docs.
|
||||
* `yarn setup-local` - same as `setup`, but use the locally built Angular packages for aio and docs examples boilerplate.
|
||||
* `yarn setup-local` - same as `setup`, but build the Angular packages from the source code and use these locally built versions (instead of the ones fetched from npm) for aio and docs examples boilerplate.
|
||||
|
||||
* `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-with-viewengine` - same as `build-local`, but in addition also turns on `ViewEngine` (pre-Ivy) mode in aio.
|
||||
(Note: To turn on `ViewEngine` mode in docs examples, see `yarn boilerplate:add:viewengine` below.)
|
||||
|
||||
* `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.
|
||||
@ -31,16 +33,29 @@ Here are the most important tasks you might need to use:
|
||||
* `yarn docs-lint` - check that the doc gen code follows our style rules.
|
||||
* `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. Add the option `--local` to use your local version of Angular contained in the "dist" folder.
|
||||
* `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: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-zips` - generate the zip files from the examples. Zip available via the `live-example` tags in the docs.
|
||||
|
||||
* `yarn example-e2e` - run all e2e tests for examples
|
||||
- `yarn example-e2e --setup` - force webdriver update & other setup, then run tests
|
||||
- `yarn example-e2e --filter=foo` - limit e2e tests to those containing the word "foo"
|
||||
- `yarn example-e2e --setup --local` - run e2e tests with the local version of Angular contained in the "dist" folder
|
||||
* `yarn example-e2e` - run all e2e tests for examples. Available options:
|
||||
- `--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.
|
||||
_Requires `--setup` in order to take effect._
|
||||
- `--viewengine`: run e2e tests in `ViewEngine` (pre-Ivy) mode.
|
||||
- `--filter=foo`: limit e2e tests to those containing the word "foo".
|
||||
|
||||
> **Note for Windows users**
|
||||
>
|
||||
> Setting up the examples involves creating some [symbolic links](https://en.wikipedia.org/wiki/Symbolic_link) (see [here](./tools/examples/README.md#symlinked-node_modules) for details). On Windows, this requires to either have [Developer Mode enabled](https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10) (supported on Windows 10 or newer) or run the setup commands as administrator.
|
||||
>
|
||||
> The affected commands are:
|
||||
> - `yarn setup` / `yarn setup-*`
|
||||
> - `yarn build` / `yarn build-*`
|
||||
> - `yarn boilerplate:add`
|
||||
> - `yarn example-e2e --setup`
|
||||
|
||||
## Using ServiceWorker locally
|
||||
|
||||
@ -89,7 +104,7 @@ You also want to see those changes displayed properly in the doc viewer
|
||||
with a quick, edit/view cycle time.
|
||||
|
||||
For this purpose, use the `yarn docs-watch` task, which watches for changes to source files and only
|
||||
re-processes the the files necessary to generate the docs that are related to the file that has changed.
|
||||
re-processes the files necessary to generate the docs that are related to the file that has changed.
|
||||
Since this task takes shortcuts, it is much faster (often less than 1 second) but it won't produce full
|
||||
fidelity content. For example, links to other docs and code examples may not render correctly. This is
|
||||
most particularly noticed in links to other docs and in the embedded examples, which may not always render
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Image metadata and config
|
||||
FROM debian:stretch
|
||||
FROM debian:buster
|
||||
|
||||
LABEL name="angular.io PR preview" \
|
||||
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 TEST_AIO_SIGNIFICANT_FILES_PATTERN=$AIO_SIGNIFICANT_FILES_PATTERN
|
||||
ARG AIO_TRUSTED_PR_LABEL="aio: preview"
|
||||
ARG TEST_AIO_TRUSTED_PR_LABEL="aio: preview"
|
||||
ARG TEST_AIO_TRUSTED_PR_LABEL=$AIO_TRUSTED_PR_LABEL
|
||||
ARG AIO_PREVIEW_SERVER_HOSTNAME=preview.localhost
|
||||
ARG TEST_AIO_PREVIEW_SERVER_HOSTNAME=preview.localhost
|
||||
ARG TEST_AIO_PREVIEW_SERVER_HOSTNAME=$AIO_PREVIEW_SERVER_HOSTNAME
|
||||
ARG AIO_ARTIFACT_MAX_SIZE=26214400
|
||||
ARG TEST_AIO_ARTIFACT_MAX_SIZE=200
|
||||
ARG AIO_PREVIEW_SERVER_PORT=3000
|
||||
@ -72,24 +72,29 @@ RUN mkdir /var/log/aio
|
||||
|
||||
|
||||
# Add extra package sources
|
||||
RUN apt-get update -y && apt-get install -y curl
|
||||
RUN curl --silent --show-error --location https://deb.nodesource.com/setup_10.x | bash -
|
||||
RUN apt-get update -y && apt-get install -y curl=7.64.0-4+deb10u1
|
||||
RUN curl --silent --show-error --location https://deb.nodesource.com/setup_12.x | bash -
|
||||
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 http://ftp.debian.org/debian stretch-backports main" | tee /etc/apt/sources.list.d/backports.list
|
||||
|
||||
|
||||
# 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 \
|
||||
cron=3.0pl1-128+deb9u1 \
|
||||
dnsmasq=2.76-5+deb9u2 \
|
||||
nano=2.7.4-1 \
|
||||
nginx=1.10.3-1+deb9u2 \
|
||||
nodejs=10.15.3-1nodesource1 \
|
||||
openssl=1.1.0j-1~deb9u1 \
|
||||
rsyslog=8.24.0-1 \
|
||||
yarn=1.15.2-1
|
||||
RUN yarn global add pm2@3.5.0
|
||||
cron=3.0pl1-134+deb10u1 \
|
||||
dnsmasq=2.80-1 \
|
||||
nano=3.2-3 \
|
||||
nginx \
|
||||
nodejs \
|
||||
openssl \
|
||||
rsyslog=8.1901.0-1 \
|
||||
vim=2:8.1.0875-5 \
|
||||
yarn=1.22.4-1
|
||||
RUN yarn global add pm2@4.4.0
|
||||
|
||||
|
||||
# Set up log rotation
|
||||
@ -162,8 +167,7 @@ RUN find $AIO_SCRIPTS_SH_DIR -maxdepth 1 -type f -printf "%P\n" \
|
||||
|
||||
# Set up the Node.js scripts
|
||||
COPY scripts-js/ $AIO_SCRIPTS_JS_DIR/
|
||||
WORKDIR $AIO_SCRIPTS_JS_DIR/
|
||||
RUN yarn install --production --frozen-lockfile
|
||||
RUN yarn --cwd="$AIO_SCRIPTS_JS_DIR/" install --production --frozen-lockfile
|
||||
|
||||
|
||||
# Set up health check
|
||||
|
@ -35,6 +35,7 @@ export class BuildCleaner {
|
||||
]);
|
||||
} catch (error) {
|
||||
this.logger.error('ERROR:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
import * as express from 'express';
|
||||
import {promisify} from 'util';
|
||||
import {PreviewServerError} from './preview-error';
|
||||
|
||||
/**
|
||||
@ -13,7 +12,7 @@ export async function respondWithError(res: express.Response, err: any): Promise
|
||||
}
|
||||
|
||||
res.status(err.status);
|
||||
await promisify(res.end.bind(res))(err.message);
|
||||
return new Promise(resolve => res.end(err.message, resolve));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -93,7 +93,7 @@ class Helper {
|
||||
return fs.readFileSync(absFilePath, 'utf8');
|
||||
}
|
||||
|
||||
public runCmd(cmd: string, opts: cp.ExecFileOptions = {}): Promise<CmdResult> {
|
||||
public runCmd(cmd: string, opts: cp.ExecOptions = {}): Promise<CmdResult> {
|
||||
return new Promise(resolve => {
|
||||
const proc = cp.exec(cmd, opts, (err, stdout, stderr) => resolve({success: !err, err, stdout, stderr}));
|
||||
this.createCleanUpFn(() => proc.kill());
|
||||
@ -101,7 +101,7 @@ class Helper {
|
||||
}
|
||||
|
||||
public runForAllSupportedSchemes(suiteFactory: TestSuiteFactory): void {
|
||||
Object.keys(this.portPerScheme).forEach(scheme => suiteFactory(scheme, this.portPerScheme[scheme]));
|
||||
Object.entries(this.portPerScheme).forEach(([scheme, port]) => suiteFactory(scheme, port));
|
||||
}
|
||||
|
||||
public verifyResponse(status: number, regex: string | RegExp = /^/): VerifyCmdResultFn {
|
||||
|
@ -15,7 +15,7 @@ describe(`nginx`, () => {
|
||||
afterEach(() => h.cleanUp());
|
||||
|
||||
|
||||
it('should redirect HTTP to HTTPS', done => {
|
||||
it('should redirect HTTP to HTTPS', async () => {
|
||||
const httpHost = `${AIO_NGINX_HOSTNAME}:${AIO_NGINX_PORT_HTTP}`;
|
||||
const httpsHost = `${AIO_NGINX_HOSTNAME}:${AIO_NGINX_PORT_HTTPS}`;
|
||||
const urlMap = {
|
||||
@ -24,16 +24,15 @@ describe(`nginx`, () => {
|
||||
[`http://foo.${httpHost}/`]: `https://foo.${httpsHost}/`,
|
||||
};
|
||||
|
||||
const verifyRedirection = (httpUrl: string) => h.runCmd(`curl -i ${httpUrl}`).then(result => {
|
||||
const verifyRedirection = async (fromUrl: string, toUrl: string) => {
|
||||
const result = await h.runCmd(`curl -i ${fromUrl}`);
|
||||
h.verifyResponse(307)(result);
|
||||
|
||||
const headers = result.stdout.split(/(?:\r?\n){2,}/)[0];
|
||||
expect(headers).toContain(`Location: ${urlMap[httpUrl]}`);
|
||||
});
|
||||
expect(headers).toContain(`Location: ${toUrl}`);
|
||||
};
|
||||
|
||||
Promise.
|
||||
all(Object.keys(urlMap).map(verifyRedirection)).
|
||||
then(done);
|
||||
await Promise.all(Object.entries(urlMap).map(urls => verifyRedirection(...urls)));
|
||||
});
|
||||
|
||||
|
||||
@ -62,15 +61,15 @@ describe(`nginx`, () => {
|
||||
});
|
||||
|
||||
|
||||
it('should return /index.html', done => {
|
||||
it('should return /index.html', async () => {
|
||||
const origin = `${scheme}://pr${pr}-${shortSha9}.${host}`;
|
||||
const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`);
|
||||
|
||||
Promise.all([
|
||||
await Promise.all([
|
||||
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)),
|
||||
]).then(done);
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@ -90,12 +89,11 @@ describe(`nginx`, () => {
|
||||
});
|
||||
|
||||
|
||||
it('should return /foo/bar.js', done => {
|
||||
it('should return /foo/bar.js', async () => {
|
||||
const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /foo/bar\\.js$`);
|
||||
|
||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo/bar.js`).
|
||||
then(h.verifyResponse(200, bodyRegex)).
|
||||
then(done);
|
||||
await h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo/bar.js`).
|
||||
then(h.verifyResponse(200, bodyRegex));
|
||||
});
|
||||
|
||||
|
||||
@ -111,47 +109,46 @@ describe(`nginx`, () => {
|
||||
});
|
||||
|
||||
|
||||
it('should respond with 403 for directories', done => {
|
||||
Promise.all([
|
||||
it('should respond with 403 for directories', async () => {
|
||||
await 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)),
|
||||
]).then(done);
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
it('should respond with 404 for unknown paths to files', done => {
|
||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo/baz.css`).
|
||||
then(h.verifyResponse(404)).
|
||||
then(done);
|
||||
it('should respond with 404 for unknown paths to files', async () => {
|
||||
await h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo/baz.css`).
|
||||
then(h.verifyResponse(404));
|
||||
});
|
||||
|
||||
|
||||
it('should rewrite to \'index.html\' for unknown paths that don\'t look like files', done => {
|
||||
it('should rewrite to \'index.html\' for unknown paths that don\'t look like files', async () => {
|
||||
const origin = `${scheme}://pr${pr}-${shortSha9}.${host}`;
|
||||
const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`);
|
||||
|
||||
Promise.all([
|
||||
await 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)),
|
||||
]).then(done);
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
it('should respond with 404 for unknown PRs/SHAs', done => {
|
||||
it('should respond with 404 for unknown PRs/SHAs', async () => {
|
||||
const otherPr = 54321;
|
||||
const otherShortSha = computeShortSha('8'.repeat(40));
|
||||
|
||||
Promise.all([
|
||||
await Promise.all([
|
||||
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${pr}-${shortSha9}9.${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', done => {
|
||||
Promise.all([
|
||||
it('should respond with 404 if the subdomain format is wrong', async () => {
|
||||
await Promise.all([
|
||||
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}://xx${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
||||
@ -160,26 +157,25 @@ describe(`nginx`, () => {
|
||||
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)),
|
||||
]).then(done);
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
it('should reject PRs with leading zeros', done => {
|
||||
h.runCmd(`curl -iL ${scheme}://pr0${pr}-${shortSha9}.${host}`).
|
||||
then(h.verifyResponse(404)).
|
||||
then(done);
|
||||
it('should reject PRs with leading zeros', async () => {
|
||||
await h.runCmd(`curl -iL ${scheme}://pr0${pr}-${shortSha9}.${host}`).
|
||||
then(h.verifyResponse(404));
|
||||
});
|
||||
|
||||
|
||||
it('should accept SHAs with leading zeros (but not trim the zeros)', done => {
|
||||
it('should accept SHAs with leading zeros (but not trim the zeros)', async () => {
|
||||
const bodyRegex9 = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`);
|
||||
const bodyRegex0 = new RegExp(`^PR: ${pr} | SHA: ${sha0} | File: /index\\.html$`);
|
||||
|
||||
Promise.all([
|
||||
await Promise.all([
|
||||
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}-${shortSha0}.${host}`).then(h.verifyResponse(200, bodyRegex0)),
|
||||
]).then(done);
|
||||
]);
|
||||
});
|
||||
|
||||
});
|
||||
@ -231,23 +227,23 @@ describe(`nginx`, () => {
|
||||
|
||||
describe(`${host}/health-check`, () => {
|
||||
|
||||
it('should respond with 200', done => {
|
||||
Promise.all([
|
||||
it('should respond with 200', async () => {
|
||||
await 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)),
|
||||
]).then(done);
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
it('should respond with 404 if the path does not match exactly', done => {
|
||||
Promise.all([
|
||||
it('should respond with 404 if the path does not match exactly', async () => {
|
||||
await 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-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}/foonhealth-check`).then(h.verifyResponse(404)),
|
||||
]).then(done);
|
||||
]);
|
||||
});
|
||||
|
||||
});
|
||||
@ -291,29 +287,28 @@ describe(`nginx`, () => {
|
||||
|
||||
describe(`${host}/circle-build`, () => {
|
||||
|
||||
it('should disallow non-POST requests', done => {
|
||||
it('should disallow non-POST requests', async () => {
|
||||
const url = `${scheme}://${host}/circle-build`;
|
||||
|
||||
Promise.all([
|
||||
await Promise.all([
|
||||
h.runCmd(`curl -iLX GET ${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 DELETE ${url}`).then(h.verifyResponse(405)),
|
||||
]).then(done);
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
it('should pass requests through to the preview server', done => {
|
||||
h.runCmd(`curl -iLX POST ${scheme}://${host}/circle-build`).
|
||||
then(h.verifyResponse(400, /Incorrect body content. Expected JSON/)).
|
||||
then(done);
|
||||
it('should pass requests through to the preview server', async () => {
|
||||
await h.runCmd(`curl -iLX POST ${scheme}://${host}/circle-build`).
|
||||
then(h.verifyResponse(400, /Incorrect body content. Expected JSON/));
|
||||
});
|
||||
|
||||
|
||||
it('should respond with 404 for unknown paths', done => {
|
||||
it('should respond with 404 for unknown paths', async () => {
|
||||
const cmdPrefix = `curl -iLX POST ${scheme}://${host}`;
|
||||
|
||||
Promise.all([
|
||||
await 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}/fooncircle-build/`).then(h.verifyResponse(404)),
|
||||
@ -322,7 +317,7 @@ describe(`nginx`, () => {
|
||||
h.runCmd(`${cmdPrefix}/circle-buildnfoo/`).then(h.verifyResponse(404)),
|
||||
h.runCmd(`${cmdPrefix}/circle-build/pr`).then(h.verifyResponse(404)),
|
||||
h.runCmd(`${cmdPrefix}/circle-build/42`).then(h.verifyResponse(404)),
|
||||
]).then(done);
|
||||
]);
|
||||
});
|
||||
|
||||
});
|
||||
@ -332,38 +327,33 @@ describe(`nginx`, () => {
|
||||
const url = `${scheme}://${host}/pr-updated`;
|
||||
|
||||
|
||||
it('should disallow non-POST requests', done => {
|
||||
Promise.all([
|
||||
it('should disallow non-POST requests', async () => {
|
||||
await Promise.all([
|
||||
h.runCmd(`curl -iLX GET ${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 DELETE ${url}`).then(h.verifyResponse(405)),
|
||||
]).then(done);
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
it('should pass requests through to the preview server', done => {
|
||||
const cmdPrefix = `curl -iLX POST --header "Content-Type: application/json"`;
|
||||
|
||||
const cmd1 = `${cmdPrefix} ${url}`;
|
||||
|
||||
Promise.all([
|
||||
h.runCmd(cmd1).then(h.verifyResponse(400, /Missing or empty 'number' field/)),
|
||||
]).then(done);
|
||||
it('should pass requests through to the preview server', async () => {
|
||||
await h.runCmd(`curl -iLX POST --header "Content-Type: application/json" ${url}`).
|
||||
then(h.verifyResponse(400, /Missing or empty 'number' field/));
|
||||
});
|
||||
|
||||
|
||||
it('should respond with 404 for unknown paths', done => {
|
||||
it('should respond with 404 for unknown paths', async () => {
|
||||
const cmdPrefix = `curl -iLX POST ${scheme}://${host}`;
|
||||
|
||||
Promise.all([
|
||||
await 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}/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-updatednfoo`).then(h.verifyResponse(404)),
|
||||
]).then(done);
|
||||
]);
|
||||
});
|
||||
|
||||
});
|
||||
@ -374,7 +364,7 @@ describe(`nginx`, () => {
|
||||
beforeEach(() => {
|
||||
['index.html', 'foo.js', 'foo/index.html'].forEach(relFilePath => {
|
||||
const absFilePath = path.join(AIO_BUILDS_DIR, relFilePath);
|
||||
return h.writeFile(absFilePath, {content: `File: /${relFilePath}`});
|
||||
h.writeFile(absFilePath, {content: `File: /${relFilePath}`});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -105,9 +105,9 @@ describe('preview-server', () => {
|
||||
|
||||
|
||||
describe(`${host}/circle-build`, () => {
|
||||
|
||||
const curl = makeCurl(`${host}/circle-build`);
|
||||
|
||||
|
||||
it('should disallow non-POST requests', async () => {
|
||||
const bodyRegex = /^Unknown resource/;
|
||||
|
||||
@ -189,8 +189,7 @@ describe('preview-server', () => {
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
@ -199,7 +198,7 @@ describe('preview-server', () => {
|
||||
expect({ prNum: PrNums.TRUST_CHECK_UNTRUSTED, isPublic: false }).toExistAsABuild();
|
||||
});
|
||||
|
||||
[true].forEach(isPublic => {
|
||||
[true, false].forEach(isPublic => {
|
||||
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 label = isPublic ? 'public' : 'non-public';
|
||||
@ -364,23 +363,23 @@ describe('preview-server', () => {
|
||||
|
||||
describe(`${host}/health-check`, () => {
|
||||
|
||||
it('should respond with 200', done => {
|
||||
Promise.all([
|
||||
it('should respond with 200', async () => {
|
||||
await Promise.all([
|
||||
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', done => {
|
||||
Promise.all([
|
||||
it('should respond with 404 if the path does not match exactly', async () => {
|
||||
await 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-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}/foonhealth-check`).then(h.verifyResponse(404)),
|
||||
]).then(done);
|
||||
]);
|
||||
});
|
||||
|
||||
});
|
||||
@ -426,18 +425,18 @@ describe('preview-server', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should respond with 404 for unknown paths', done => {
|
||||
it('should respond with 404 for unknown paths', async () => {
|
||||
const mockPayload = JSON.stringify({number: 1}); // MockExternalApiFlags.TRUST_CHECK_ACTIVE_TRUSTED_USER });
|
||||
const cmdPrefix = `curl -iLX POST --data "${mockPayload}" ${host}`;
|
||||
|
||||
Promise.all([
|
||||
await 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}/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-updatednfoo`).then(h.verifyResponse(404)),
|
||||
]).then(done);
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@ -551,10 +550,10 @@ describe('preview-server', () => {
|
||||
|
||||
describe(`${host}/*`, () => {
|
||||
|
||||
it('should respond with 404 for requests to unknown URLs', done => {
|
||||
it('should respond with 404 for requests to unknown URLs', async () => {
|
||||
const bodyRegex = /^Unknown resource/;
|
||||
|
||||
Promise.all([
|
||||
await Promise.all([
|
||||
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)),
|
||||
@ -562,7 +561,7 @@ describe('preview-server', () => {
|
||||
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 DELETE ${host}`).then(h.verifyResponse(404, bodyRegex)),
|
||||
]).then(done);
|
||||
]);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -14,42 +14,41 @@
|
||||
"predev": "yarn build || true",
|
||||
"dev": "run-p ~~build-watch ~~test-watch",
|
||||
"lint": "tslint --project tsconfig.json",
|
||||
"pretest": "yarn build",
|
||||
"pretest": "run-s build lint",
|
||||
"test": "yarn ~~test-only",
|
||||
"pretest-watch": "yarn pretest",
|
||||
"test-watch": "yarn ~~test-watch",
|
||||
"~~build": "tsc",
|
||||
"~~build-watch": "yarn ~~build --watch",
|
||||
"pre~~test-only": "yarn lint",
|
||||
"~~test-only": "node dist/test",
|
||||
"~~test-watch": "nodemon --delay 1 --exec \"yarn ~~test-only\" --watch dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"body-parser": "^1.18.3",
|
||||
"delete-empty": "^2.0.0",
|
||||
"express": "^4.16.3",
|
||||
"jasmine": "^3.2.0",
|
||||
"nock": "^9.6.1",
|
||||
"node-fetch": "^2.2.0",
|
||||
"shelljs": "^0.8.2",
|
||||
"source-map-support": "^0.5.9",
|
||||
"tar-stream": "^1.6.1",
|
||||
"tslib": "^1.9.3"
|
||||
"body-parser": "^1.19.0",
|
||||
"delete-empty": "^3.0.0",
|
||||
"express": "^4.17.1",
|
||||
"jasmine": "^3.5.0",
|
||||
"nock": "^12.0.3",
|
||||
"node-fetch": "^2.6.0",
|
||||
"shelljs": "^0.8.4",
|
||||
"source-map-support": "^0.5.19",
|
||||
"tar-stream": "^2.1.2",
|
||||
"tslib": "^1.11.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/body-parser": "^1.17.0",
|
||||
"@types/express": "^4.16.0",
|
||||
"@types/jasmine": "^2.8.8",
|
||||
"@types/nock": "^9.3.0",
|
||||
"@types/node": "^10.9.2",
|
||||
"@types/node-fetch": "^2.1.2",
|
||||
"@types/shelljs": "^0.8.0",
|
||||
"@types/supertest": "^2.0.5",
|
||||
"nodemon": "^1.18.3",
|
||||
"@types/body-parser": "^1.19.0",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/jasmine": "^3.5.10",
|
||||
"@types/nock": "^11.1.0",
|
||||
"@types/node": "^13.13.2",
|
||||
"@types/node-fetch": "^2.5.7",
|
||||
"@types/shelljs": "^0.8.7",
|
||||
"@types/supertest": "^2.0.8",
|
||||
"nodemon": "^2.0.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"supertest": "^3.1.0",
|
||||
"tslint": "^5.11.0",
|
||||
"supertest": "^4.0.2",
|
||||
"tslint": "^6.1.1",
|
||||
"tslint-jasmine-noSkipOrFocus": "^1.0.9",
|
||||
"typescript": "^3.0.1"
|
||||
"typescript": "^3.8.3"
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ const EXISTING_DOWNLOADS = [
|
||||
'20-1234567-build.zip',
|
||||
];
|
||||
const OPEN_PRS = [10, 40];
|
||||
const ANY_DATE = jasmine.any(String);
|
||||
|
||||
// Tests
|
||||
describe('BuildCleaner', () => {
|
||||
@ -77,22 +76,18 @@ describe('BuildCleaner', () => {
|
||||
let cleanerRemoveUnnecessaryDownloadsSpy: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
cleanerGetExistingBuildNumbersSpy = spyOn(cleaner, 'getExistingBuildNumbers')
|
||||
.and.callFake(() => Promise.resolve(EXISTING_BUILDS));
|
||||
cleanerGetOpenPrNumbersSpy = spyOn(cleaner, 'getOpenPrNumbers')
|
||||
.and.callFake(() => Promise.resolve(OPEN_PRS));
|
||||
cleanerGetExistingDownloadsSpy = spyOn(cleaner, 'getExistingDownloads')
|
||||
.and.callFake(() => Promise.resolve(EXISTING_DOWNLOADS));
|
||||
cleanerGetExistingBuildNumbersSpy = spyOn(cleaner, 'getExistingBuildNumbers').and.resolveTo(EXISTING_BUILDS);
|
||||
cleanerGetOpenPrNumbersSpy = spyOn(cleaner, 'getOpenPrNumbers').and.resolveTo(OPEN_PRS);
|
||||
cleanerGetExistingDownloadsSpy = spyOn(cleaner, 'getExistingDownloads').and.resolveTo(EXISTING_DOWNLOADS);
|
||||
|
||||
cleanerRemoveUnnecessaryBuildsSpy = spyOn(cleaner, 'removeUnnecessaryBuilds');
|
||||
cleanerRemoveUnnecessaryDownloadsSpy = spyOn(cleaner, 'removeUnnecessaryDownloads');
|
||||
|
||||
});
|
||||
|
||||
|
||||
it('should return a promise', async () => {
|
||||
const promise = cleaner.cleanUp();
|
||||
expect(promise).toEqual(jasmine.any(Promise));
|
||||
expect(promise).toBeInstanceOf(Promise);
|
||||
|
||||
// Do not complete the test and release the spies synchronously, to avoid running the actual implementations.
|
||||
await promise;
|
||||
@ -130,52 +125,32 @@ describe('BuildCleaner', () => {
|
||||
|
||||
|
||||
it('should reject if \'getOpenPrNumbers()\' rejects', async () => {
|
||||
try {
|
||||
cleanerGetOpenPrNumbersSpy.and.callFake(() => Promise.reject('Test'));
|
||||
await cleaner.cleanUp();
|
||||
} catch (err) {
|
||||
expect(err).toBe('Test');
|
||||
}
|
||||
cleanerGetOpenPrNumbersSpy.and.rejectWith('Test');
|
||||
await expectAsync(cleaner.cleanUp()).toBeRejectedWith('Test');
|
||||
});
|
||||
|
||||
|
||||
it('should reject if \'getExistingBuildNumbers()\' rejects', async () => {
|
||||
try {
|
||||
cleanerGetExistingBuildNumbersSpy.and.callFake(() => Promise.reject('Test'));
|
||||
await cleaner.cleanUp();
|
||||
} catch (err) {
|
||||
expect(err).toBe('Test');
|
||||
}
|
||||
cleanerGetExistingBuildNumbersSpy.and.rejectWith('Test');
|
||||
await expectAsync(cleaner.cleanUp()).toBeRejectedWith('Test');
|
||||
});
|
||||
|
||||
|
||||
it('should reject if \'getExistingDownloads()\' rejects', async () => {
|
||||
try {
|
||||
cleanerGetExistingDownloadsSpy.and.callFake(() => Promise.reject('Test'));
|
||||
await cleaner.cleanUp();
|
||||
} catch (err) {
|
||||
expect(err).toBe('Test');
|
||||
}
|
||||
cleanerGetExistingDownloadsSpy.and.rejectWith('Test');
|
||||
await expectAsync(cleaner.cleanUp()).toBeRejectedWith('Test');
|
||||
});
|
||||
|
||||
|
||||
it('should reject if \'removeUnnecessaryBuilds()\' rejects', async () => {
|
||||
try {
|
||||
cleanerRemoveUnnecessaryBuildsSpy.and.callFake(() => Promise.reject('Test'));
|
||||
await cleaner.cleanUp();
|
||||
} catch (err) {
|
||||
expect(err).toBe('Test');
|
||||
}
|
||||
cleanerRemoveUnnecessaryBuildsSpy.and.rejectWith('Test');
|
||||
await expectAsync(cleaner.cleanUp()).toBeRejectedWith('Test');
|
||||
});
|
||||
|
||||
|
||||
it('should reject if \'removeUnnecessaryDownloads()\' rejects', async () => {
|
||||
try {
|
||||
cleanerRemoveUnnecessaryDownloadsSpy.and.callFake(() => Promise.reject('Test'));
|
||||
await cleaner.cleanUp();
|
||||
} catch (err) {
|
||||
expect(err).toBe('Test');
|
||||
}
|
||||
cleanerRemoveUnnecessaryDownloadsSpy.and.rejectWith('Test');
|
||||
await expectAsync(cleaner.cleanUp()).toBeRejectedWith('Test');
|
||||
});
|
||||
|
||||
});
|
||||
@ -187,13 +162,15 @@ describe('BuildCleaner', () => {
|
||||
let promise: Promise<number[]>;
|
||||
|
||||
beforeEach(() => {
|
||||
fsReaddirSpy = spyOn(fs, 'readdir').and.callFake((_: string, cb: typeof readdirCb) => readdirCb = cb);
|
||||
fsReaddirSpy = spyOn(fs, 'readdir').and.callFake(
|
||||
((_: string, cb: typeof readdirCb) => readdirCb = cb) as unknown as typeof fs.readdir,
|
||||
);
|
||||
promise = cleaner.getExistingBuildNumbers();
|
||||
});
|
||||
|
||||
|
||||
it('should return a promise', () => {
|
||||
expect(promise).toEqual(jasmine.any(Promise));
|
||||
expect(promise).toBeInstanceOf(Promise);
|
||||
});
|
||||
|
||||
|
||||
@ -203,43 +180,27 @@ describe('BuildCleaner', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should reject if an error occurs while getting the files', done => {
|
||||
promise.catch(err => {
|
||||
expect(err).toBe('Test');
|
||||
done();
|
||||
});
|
||||
|
||||
it('should reject if an error occurs while getting the files', async () => {
|
||||
readdirCb('Test');
|
||||
await expectAsync(promise).toBeRejectedWith('Test');
|
||||
});
|
||||
|
||||
|
||||
it('should resolve with the returned files (as numbers)', done => {
|
||||
promise.then(result => {
|
||||
expect(result).toEqual([12, 34, 56]);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should resolve with the returned files (as numbers)', async () => {
|
||||
readdirCb(null, ['12', '34', '56']);
|
||||
await expectAsync(promise).toBeResolvedTo([12, 34, 56]);
|
||||
});
|
||||
|
||||
|
||||
it('should remove `HIDDEN_DIR_PREFIX` from the filenames', done => {
|
||||
promise.then(result => {
|
||||
expect(result).toEqual([12, 34, 56]);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should remove `HIDDEN_DIR_PREFIX` from the filenames', async () => {
|
||||
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', done => {
|
||||
promise.then(result => {
|
||||
expect(result).toEqual([12, 34, 56]);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should ignore files with non-numeric (or zero) names', async () => {
|
||||
readdirCb(null, ['12', 'foo', '34', 'bar', '56', '000']);
|
||||
await expectAsync(promise).toBeResolvedTo([12, 34, 56]);
|
||||
});
|
||||
|
||||
});
|
||||
@ -259,7 +220,7 @@ describe('BuildCleaner', () => {
|
||||
|
||||
|
||||
it('should return a promise', () => {
|
||||
expect(promise).toEqual(jasmine.any(Promise));
|
||||
expect(promise).toBeInstanceOf(Promise);
|
||||
});
|
||||
|
||||
|
||||
@ -268,31 +229,15 @@ describe('BuildCleaner', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should reject if an error occurs while fetching PRs', done => {
|
||||
promise.catch(err => {
|
||||
expect(err).toBe('Test');
|
||||
done();
|
||||
});
|
||||
|
||||
it('should reject if an error occurs while fetching PRs', async () => {
|
||||
prDeferred.reject('Test');
|
||||
await expectAsync(promise).toBeRejectedWith('Test');
|
||||
});
|
||||
|
||||
|
||||
it('should resolve with the numbers of the fetched PRs', done => {
|
||||
promise.then(prNumbers => {
|
||||
expect(prNumbers).toEqual([1, 2, 3]);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should resolve with the numbers of the fetched PRs', async () => {
|
||||
prDeferred.resolve([{id: 0, number: 1}, {id: 1, number: 2}, {id: 2, number: 3}]);
|
||||
});
|
||||
|
||||
|
||||
it('should log the number of open PRs', () => {
|
||||
promise.then(prNumbers => {
|
||||
expect(loggerLogSpy).toHaveBeenCalledWith(
|
||||
ANY_DATE, 'BuildCleaner: ', `Open pull requests: ${prNumbers}`);
|
||||
});
|
||||
await expectAsync(promise).toBeResolvedTo([1, 2, 3]);
|
||||
});
|
||||
|
||||
});
|
||||
@ -304,13 +249,15 @@ describe('BuildCleaner', () => {
|
||||
let promise: Promise<string[]>;
|
||||
|
||||
beforeEach(() => {
|
||||
fsReaddirSpy = spyOn(fs, 'readdir').and.callFake((_: string, cb: typeof readdirCb) => readdirCb = cb);
|
||||
fsReaddirSpy = spyOn(fs, 'readdir').and.callFake(
|
||||
((_: string, cb: typeof readdirCb) => readdirCb = cb) as unknown as typeof fs.readdir,
|
||||
);
|
||||
promise = cleaner.getExistingDownloads();
|
||||
});
|
||||
|
||||
|
||||
it('should return a promise', () => {
|
||||
expect(promise).toEqual(jasmine.any(Promise));
|
||||
expect(promise).toBeInstanceOf(Promise);
|
||||
});
|
||||
|
||||
|
||||
@ -320,33 +267,21 @@ describe('BuildCleaner', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should reject if an error occurs while getting the files', done => {
|
||||
promise.catch(err => {
|
||||
expect(err).toBe('Test');
|
||||
done();
|
||||
});
|
||||
|
||||
it('should reject if an error occurs while getting the files', async () => {
|
||||
readdirCb('Test');
|
||||
await expectAsync(promise).toBeRejectedWith('Test');
|
||||
});
|
||||
|
||||
|
||||
it('should resolve with the returned file names', done => {
|
||||
promise.then(result => {
|
||||
expect(result).toEqual(EXISTING_DOWNLOADS);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should resolve with the returned file names', async () => {
|
||||
readdirCb(null, EXISTING_DOWNLOADS);
|
||||
await expectAsync(promise).toBeResolvedTo(EXISTING_DOWNLOADS);
|
||||
});
|
||||
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
it('should ignore files that do not match the artifactPath', async () => {
|
||||
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']);
|
||||
});
|
||||
|
||||
});
|
||||
@ -364,7 +299,7 @@ describe('BuildCleaner', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should test if the directory exists (and return if is does not)', () => {
|
||||
it('should test if the directory exists (and return if it does not)', () => {
|
||||
shellTestSpy.and.returnValue(false);
|
||||
cleaner.removeDir('/foo/bar');
|
||||
|
||||
@ -381,22 +316,19 @@ describe('BuildCleaner', () => {
|
||||
|
||||
|
||||
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');
|
||||
|
||||
expect(shellChmodSpy).toHaveBeenCalledBefore(shellRmSpy);
|
||||
expect(shellChmodSpy).toHaveBeenCalledWith('-R', 'a+w', '/foo/bar');
|
||||
expect(shellRmSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
it('should catch errors and log them', () => {
|
||||
shellRmSpy.and.callFake(() => {
|
||||
// tslint:disable-next-line: no-string-throw
|
||||
throw 'Test';
|
||||
});
|
||||
|
||||
shellRmSpy.and.throwError('Test');
|
||||
cleaner.removeDir('/foo/bar');
|
||||
|
||||
expect(loggerErrorSpy).toHaveBeenCalledWith('ERROR: Unable to remove \'/foo/bar\' due to:', 'Test');
|
||||
expect(loggerErrorSpy).toHaveBeenCalledWith('ERROR: Unable to remove \'/foo/bar\' due to:', new Error('Test'));
|
||||
});
|
||||
|
||||
});
|
||||
@ -449,7 +381,7 @@ describe('BuildCleaner', () => {
|
||||
expect(cleanerRemoveDirSpy).toHaveBeenCalledTimes(0);
|
||||
cleanerRemoveDirSpy.calls.reset();
|
||||
|
||||
(cleaner as any).removeUnnecessaryBuilds([1, 2, 3, 4], []);
|
||||
cleaner.removeUnnecessaryBuilds([1, 2, 3, 4], []);
|
||||
expect(cleanerRemoveDirSpy).toHaveBeenCalledTimes(8);
|
||||
expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/1'));
|
||||
expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/2'));
|
||||
|
@ -45,25 +45,15 @@ describe('CircleCIApi', () => {
|
||||
const errorMessage = 'Invalid request';
|
||||
const request = nock(BASE_URL).get(`/${buildNum}?circle-token=${TOKEN}`);
|
||||
|
||||
try {
|
||||
request.replyWithError(errorMessage);
|
||||
await api.getBuildInfo(buildNum);
|
||||
throw new Error('Exception Expected');
|
||||
} catch (err) {
|
||||
expect(err.message).toEqual(
|
||||
await expectAsync(api.getBuildInfo(buildNum)).toBeRejectedWithError(
|
||||
`CircleCI build info request failed ` +
|
||||
`(request to ${BASE_URL}/${buildNum}?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
|
||||
}
|
||||
|
||||
try {
|
||||
request.reply(404, errorMessage);
|
||||
await api.getBuildInfo(buildNum);
|
||||
throw new Error('Exception Expected');
|
||||
} catch (err) {
|
||||
expect(err.message).toEqual(
|
||||
await expectAsync(api.getBuildInfo(buildNum)).toBeRejectedWithError(
|
||||
`CircleCI build info request failed ` +
|
||||
`(request to ${BASE_URL}/${buildNum}?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -78,8 +68,7 @@ describe('CircleCIApi', () => {
|
||||
.get(`/${buildNum}/artifacts?circle-token=${TOKEN}`)
|
||||
.reply(200, [artifact0, artifact1, artifact2]);
|
||||
|
||||
const artifactUrl = await api.getBuildArtifactUrl(buildNum, 'some/path/1');
|
||||
expect(artifactUrl).toEqual('https://url/1');
|
||||
await expectAsync(api.getBuildArtifactUrl(buildNum, 'some/path/1')).toBeResolvedTo('https://url/1');
|
||||
request.done();
|
||||
});
|
||||
|
||||
@ -90,25 +79,15 @@ describe('CircleCIApi', () => {
|
||||
const errorMessage = 'Invalid request';
|
||||
const request = nock(BASE_URL).get(`/${buildNum}/artifacts?circle-token=${TOKEN}`);
|
||||
|
||||
try {
|
||||
request.replyWithError(errorMessage);
|
||||
await api.getBuildArtifactUrl(buildNum, 'some/path/1');
|
||||
throw new Error('Exception Expected');
|
||||
} catch (err) {
|
||||
expect(err.message).toEqual(
|
||||
await expectAsync(api.getBuildArtifactUrl(buildNum, 'some/path/1')).toBeRejectedWithError(
|
||||
`CircleCI artifact URL request failed ` +
|
||||
`(request to ${BASE_URL}/${buildNum}/artifacts?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
|
||||
}
|
||||
|
||||
try {
|
||||
request.reply(404, errorMessage);
|
||||
await api.getBuildArtifactUrl(buildNum, 'some/path/1');
|
||||
throw new Error('Exception Expected');
|
||||
} catch (err) {
|
||||
expect(err.message).toEqual(
|
||||
await expectAsync(api.getBuildArtifactUrl(buildNum, 'some/path/1')).toBeRejectedWithError(
|
||||
`CircleCI artifact URL request failed ` +
|
||||
`(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 () => {
|
||||
@ -121,14 +100,9 @@ describe('CircleCIApi', () => {
|
||||
.get(`/${buildNum}/artifacts?circle-token=${TOKEN}`)
|
||||
.reply(200, [artifact0, artifact1, artifact2]);
|
||||
|
||||
try {
|
||||
await api.getBuildArtifactUrl(buildNum, 'some/path/3');
|
||||
throw new Error('Exception Expected');
|
||||
} catch (err) {
|
||||
expect(err.message).toEqual(
|
||||
await expectAsync(api.getBuildArtifactUrl(buildNum, 'some/path/3')).toBeRejectedWithError(
|
||||
`CircleCI artifact URL request failed ` +
|
||||
`(Missing artifact (some/path/3) for CircleCI build: ${buildNum})`);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -118,7 +118,7 @@ describe('GithubApi', () => {
|
||||
|
||||
|
||||
it('should return a promise', () => {
|
||||
expect((api as any).getPaginated()).toEqual(jasmine.any(Promise));
|
||||
expect((api as any).getPaginated()).toBeInstanceOf(Promise);
|
||||
});
|
||||
|
||||
|
||||
@ -131,45 +131,30 @@ describe('GithubApi', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should reject if the request fails', done => {
|
||||
(api as any).getPaginated('/foo/bar').catch((err: any) => {
|
||||
expect(err).toBe('Test');
|
||||
done();
|
||||
});
|
||||
|
||||
it('should reject if the request fails', async () => {
|
||||
const responsePromise = (api as any).getPaginated('/foo/bar');
|
||||
deferreds[0].reject('Test');
|
||||
|
||||
await expectAsync(responsePromise).toBeRejectedWith('Test');
|
||||
});
|
||||
|
||||
|
||||
it('should resolve with the returned items', done => {
|
||||
it('should resolve with the returned items', async () => {
|
||||
const items = [{id: 1}, {id: 2}];
|
||||
|
||||
(api as any).getPaginated('/foo/bar').then((data: any) => {
|
||||
expect(data).toEqual(items);
|
||||
done();
|
||||
});
|
||||
|
||||
const responsePromise = (api as any).getPaginated('/foo/bar');
|
||||
deferreds[0].resolve(items);
|
||||
|
||||
await expectAsync(responsePromise).toBeResolvedTo(items);
|
||||
});
|
||||
|
||||
|
||||
it('should iteratively call \'get()\' to fetch all items', done => {
|
||||
it('should iteratively call \'get()\' to fetch all items', async () => {
|
||||
// Create an array or 250 objects.
|
||||
const allItems = '.'.repeat(250).split('').map((_, i) => ({id: i}));
|
||||
const allItems = new Array(250).fill(null).map((_, i) => ({id: i}));
|
||||
const apiGetSpy = api.get as jasmine.Spy;
|
||||
|
||||
(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();
|
||||
});
|
||||
const responsePromise = (api as any).getPaginated('/foo/bar', {baz: 'qux'});
|
||||
|
||||
deferreds[0].resolve(allItems.slice(0, 100));
|
||||
setTimeout(() => {
|
||||
@ -178,6 +163,13 @@ describe('GithubApi', () => {
|
||||
deferreds[2].resolve(allItems.slice(200));
|
||||
}, 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)]);
|
||||
});
|
||||
|
||||
});
|
||||
@ -217,8 +209,8 @@ describe('GithubApi', () => {
|
||||
|
||||
describe('request()', () => {
|
||||
it('should return a promise', () => {
|
||||
nock('https://api.github.com').get('').reply(200);
|
||||
expect((api as any).request()).toEqual(jasmine.any(Promise));
|
||||
nock('https://api.github.com').get('/').reply(200);
|
||||
expect((api as any).request()).toBeInstanceOf(Promise);
|
||||
});
|
||||
|
||||
|
||||
@ -247,9 +239,8 @@ describe('GithubApi', () => {
|
||||
nock('https://api.github.com')
|
||||
.intercept('/path', 'method')
|
||||
.replyWithError('Test');
|
||||
let message = 'Failed to reject error';
|
||||
await (api as any).request('method', '/path').catch((err: any) => message = err.message);
|
||||
expect(message).toEqual('Test');
|
||||
|
||||
await expectAsync((api as any).request('method', '/path')).toBeRejectedWithError('Test');
|
||||
});
|
||||
|
||||
|
||||
@ -263,81 +254,69 @@ describe('GithubApi', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should reject if response statusCode is <200', done => {
|
||||
it('should reject if response statusCode is <200', async () => {
|
||||
const requestHandler = nock('https://api.github.com')
|
||||
.intercept('/path', 'method')
|
||||
.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();
|
||||
});
|
||||
|
||||
|
||||
it('should reject if response statusCode is >=400', done => {
|
||||
it('should reject if response statusCode is >=400', async () => {
|
||||
const requestHandler = nock('https://api.github.com')
|
||||
.intercept('/path', 'method')
|
||||
.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();
|
||||
});
|
||||
|
||||
|
||||
it('should include the response text in the rejection message', done => {
|
||||
it('should include the response text in the rejection message', async () => {
|
||||
const requestHandler = nock('https://api.github.com')
|
||||
.intercept('/path', 'method')
|
||||
.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();
|
||||
});
|
||||
|
||||
|
||||
it('should resolve if returned statusCode is >=200 and <400', done => {
|
||||
it('should resolve if returned statusCode is >=200 and <400', async () => {
|
||||
const requestHandler = nock('https://api.github.com')
|
||||
.intercept('/path', 'method')
|
||||
.reply(200);
|
||||
|
||||
(api as any).request('method', '/path').then(done);
|
||||
await expectAsync((api as any).request('method', '/path')).toBeResolved();
|
||||
requestHandler.done();
|
||||
});
|
||||
|
||||
|
||||
it('should parse the response body into an object using \'JSON.parse\'', done => {
|
||||
it('should parse the response body into an object using \'JSON.parse\'', async () => {
|
||||
const requestHandler = nock('https://api.github.com')
|
||||
.intercept('/path', 'method')
|
||||
.reply(300, '{"foo": "bar"}');
|
||||
|
||||
(api as any).request('method', '/path').then((data: any) => {
|
||||
expect(data).toEqual({foo: 'bar'});
|
||||
done();
|
||||
});
|
||||
await expectAsync((api as any).request('method', '/path')).toBeResolvedTo({foo: 'bar'});
|
||||
requestHandler.done();
|
||||
});
|
||||
|
||||
it('should reject if the response text is malformed JSON', done => {
|
||||
it('should reject if the response text is malformed JSON', async () => {
|
||||
const requestHandler = nock('https://api.github.com')
|
||||
.intercept('/path', 'method')
|
||||
.reply(300, '}');
|
||||
|
||||
(api as any).request('method', '/path').catch((err: any) => {
|
||||
expect(err).toEqual(jasmine.any(SyntaxError));
|
||||
done();
|
||||
});
|
||||
await expectAsync((api as any).request('method', '/path')).toBeRejectedWithError(SyntaxError);
|
||||
requestHandler.done();
|
||||
});
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Imports
|
||||
import {GithubApi} from '../../lib/common/github-api';
|
||||
import {GithubPullRequests} from '../../lib/common/github-pull-requests';
|
||||
import {GithubPullRequests, PullRequest} from '../../lib/common/github-pull-requests';
|
||||
|
||||
// Tests
|
||||
describe('GithubPullRequests', () => {
|
||||
@ -47,27 +47,21 @@ describe('GithubPullRequests', () => {
|
||||
|
||||
|
||||
it('should make a POST request to Github with the correct pathname, params and data', () => {
|
||||
githubApi.post.and.callFake(() => Promise.resolve());
|
||||
githubApi.post.and.resolveTo();
|
||||
prs.addComment(42, 'body');
|
||||
expect(githubApi.post).toHaveBeenCalledWith('/repos/foo/bar/issues/42/comments', null, {body: 'body'});
|
||||
});
|
||||
|
||||
|
||||
it('should reject if the request fails', done => {
|
||||
githubApi.post.and.callFake(() => Promise.reject('Test'));
|
||||
prs.addComment(42, 'body').catch(err => {
|
||||
expect(err).toBe('Test');
|
||||
done();
|
||||
});
|
||||
it('should reject if the request fails', async () => {
|
||||
githubApi.post.and.rejectWith('Test');
|
||||
await expectAsync(prs.addComment(42, 'body')).toBeRejectedWith('Test');
|
||||
});
|
||||
|
||||
|
||||
it('should resolve with the data from the Github POST', done => {
|
||||
githubApi.post.and.callFake(() => Promise.resolve('Test'));
|
||||
prs.addComment(42, 'body').then(data => {
|
||||
expect(data).toBe('Test');
|
||||
done();
|
||||
});
|
||||
it('should resolve with the data from the Github POST', async () => {
|
||||
githubApi.post.and.resolveTo('Test');
|
||||
await expectAsync(prs.addComment(42, 'body')).toBeResolvedTo('Test');
|
||||
});
|
||||
|
||||
});
|
||||
@ -87,13 +81,11 @@ describe('GithubPullRequests', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should resolve with the data returned from GitHub', done => {
|
||||
it('should resolve with the data returned from GitHub', async () => {
|
||||
const expected: any = {number: 42};
|
||||
githubApi.get.and.callFake(() => Promise.resolve(expected));
|
||||
prs.fetch(42).then(data => {
|
||||
expect(data).toEqual(expected);
|
||||
done();
|
||||
});
|
||||
githubApi.get.and.resolveTo(expected);
|
||||
|
||||
await expectAsync(prs.fetch(42)).toBeResolvedTo(expected);
|
||||
});
|
||||
|
||||
});
|
||||
@ -125,9 +117,14 @@ describe('GithubPullRequests', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should forward the value returned by \'getPaginated()\'', () => {
|
||||
githubApi.getPaginated.and.returnValue('Test');
|
||||
expect(prs.fetchAll() as any).toBe('Test');
|
||||
it('should forward the value returned by \'getPaginated()\'', async () => {
|
||||
const mockPrs: PullRequest[] = [
|
||||
{number: 1, user: {login: 'foo'}, labels: []},
|
||||
{number: 2, user: {login: 'bar'}, labels: []},
|
||||
];
|
||||
|
||||
githubApi.getPaginated.and.resolveTo(mockPrs);
|
||||
expect(await prs.fetchAll()).toBe(mockPrs);
|
||||
});
|
||||
|
||||
});
|
||||
@ -147,13 +144,11 @@ describe('GithubPullRequests', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should resolve with the data returned from GitHub', done => {
|
||||
it('should resolve with the data returned from GitHub', async () => {
|
||||
const expected: any = [{sha: 'ABCDE', filename: 'a/b/c'}, {sha: '12345', filename: 'x/y/z'}];
|
||||
githubApi.getPaginated.and.callFake(() => Promise.resolve(expected));
|
||||
prs.fetchFiles(42).then(data => {
|
||||
expect(data).toEqual(expected);
|
||||
done();
|
||||
});
|
||||
githubApi.getPaginated.and.resolveTo(expected);
|
||||
|
||||
await expectAsync(prs.fetchFiles(42)).toBeResolvedTo(expected);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {GithubApi} from '../../lib/common/github-api';
|
||||
import {GithubTeams} from '../../lib/common/github-teams';
|
||||
import {GithubTeams, Team} from '../../lib/common/github-teams';
|
||||
|
||||
// Tests
|
||||
describe('GithubTeams', () => {
|
||||
@ -16,6 +16,7 @@ describe('GithubTeams', () => {
|
||||
expect(() => new GithubTeams(githubApi, '')).
|
||||
toThrowError('Missing or empty required parameter \'githubOrg\'!');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
@ -33,9 +34,14 @@ describe('GithubTeams', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should forward the value returned by \'getPaginated()\'', () => {
|
||||
githubApi.getPaginated.and.returnValue('Test');
|
||||
expect(teams.fetchAll() as any).toBe('Test');
|
||||
it('should forward the value returned by \'getPaginated()\'', async () => {
|
||||
const mockTeams: Team[] = [
|
||||
{id: 1, slug: 'foo'},
|
||||
{id: 2, slug: 'bar'},
|
||||
];
|
||||
|
||||
githubApi.getPaginated.and.resolveTo(mockTeams);
|
||||
expect(await teams.fetchAll()).toBe(mockTeams);
|
||||
});
|
||||
|
||||
});
|
||||
@ -50,100 +56,77 @@ describe('GithubTeams', () => {
|
||||
|
||||
|
||||
it('should return a promise', () => {
|
||||
githubApi.get.and.callFake(() => Promise.resolve());
|
||||
githubApi.get.and.resolveTo();
|
||||
const promise = teams.isMemberById('user', [1]);
|
||||
expect(promise).toEqual(jasmine.any(Promise));
|
||||
expect(promise).toBeInstanceOf(Promise);
|
||||
});
|
||||
|
||||
|
||||
it('should resolve with false if called with an empty array', done => {
|
||||
teams.isMemberById('user', []).then(isMember => {
|
||||
expect(isMember).toBe(false);
|
||||
it('should resolve with false if called with an empty array', async () => {
|
||||
await expectAsync(teams.isMemberById('user', [])).toBeResolvedTo(false);
|
||||
expect(githubApi.get).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should call \'get()\' with the correct pathname', done => {
|
||||
githubApi.get.and.callFake(() => Promise.resolve());
|
||||
teams.isMemberById('user', [1]).then(() => {
|
||||
it('should call \'get()\' with the correct pathname', async () => {
|
||||
githubApi.get.and.resolveTo();
|
||||
await teams.isMemberById('user', [1]);
|
||||
|
||||
expect(githubApi.get).toHaveBeenCalledWith('/teams/1/memberships/user');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should resolve with false if \'get()\' rejects', done => {
|
||||
githubApi.get.and.callFake(() => Promise.reject(null));
|
||||
teams.isMemberById('user', [1]).then(isMember => {
|
||||
expect(isMember).toBe(false);
|
||||
it('should resolve with false if \'get()\' rejects', async () => {
|
||||
githubApi.get.and.rejectWith(null);
|
||||
|
||||
await expectAsync(teams.isMemberById('user', [1])).toBeResolvedTo(false);
|
||||
expect(githubApi.get).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should resolve with false if the membership is not active', done => {
|
||||
githubApi.get.and.callFake(() => Promise.resolve({state: 'pending'}));
|
||||
teams.isMemberById('user', [1]).then(isMember => {
|
||||
expect(isMember).toBe(false);
|
||||
it('should resolve with false if the membership is not active', async () => {
|
||||
githubApi.get.and.resolveTo({state: 'pending'});
|
||||
|
||||
await expectAsync(teams.isMemberById('user', [1])).toBeResolvedTo(false);
|
||||
expect(githubApi.get).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should resolve with true if the membership is active', done => {
|
||||
githubApi.get.and.callFake(() => Promise.resolve({state: 'active'}));
|
||||
teams.isMemberById('user', [1]).then(isMember => {
|
||||
expect(isMember).toBe(true);
|
||||
done();
|
||||
});
|
||||
it('should resolve with true if the membership is active', async () => {
|
||||
githubApi.get.and.resolveTo({state: 'active'});
|
||||
await expectAsync(teams.isMemberById('user', [1])).toBeResolvedTo(true);
|
||||
});
|
||||
|
||||
|
||||
it('should sequentially call \'get()\' until an active membership is found', done => {
|
||||
const trainedResponses: {[pathname: string]: Promise<{state: string}>} = {
|
||||
'/teams/1/memberships/user': Promise.resolve({state: 'pending'}),
|
||||
'/teams/2/memberships/user': Promise.reject(null),
|
||||
'/teams/3/memberships/user': Promise.resolve({state: 'active'}),
|
||||
};
|
||||
githubApi.get.and.callFake((pathname: string) => trainedResponses[pathname]);
|
||||
it('should sequentially call \'get()\' until an active membership is found', async () => {
|
||||
githubApi.get.
|
||||
withArgs('/teams/1/memberships/user').and.resolveTo({state: 'pending'}).
|
||||
withArgs('/teams/2/memberships/user').and.rejectWith(null).
|
||||
withArgs('/teams/3/memberships/user').and.resolveTo({state: 'active'});
|
||||
|
||||
teams.isMemberById('user', [1, 2, 3, 4]).then(isMember => {
|
||||
expect(isMember).toBe(true);
|
||||
await expectAsync(teams.isMemberById('user', [1, 2, 3, 4])).toBeResolvedTo(true);
|
||||
|
||||
expect(githubApi.get).toHaveBeenCalledTimes(3);
|
||||
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(2)[0]).toBe('/teams/3/memberships/user');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should resolve with false if no active membership is found', done => {
|
||||
const trainedResponses: {[pathname: string]: Promise<{state: string}>} = {
|
||||
'/teams/1/memberships/user': Promise.resolve({state: 'pending'}),
|
||||
'/teams/2/memberships/user': Promise.reject(null),
|
||||
'/teams/3/memberships/user': Promise.resolve({state: 'not active'}),
|
||||
'/teams/4/memberships/user': Promise.reject(null),
|
||||
};
|
||||
githubApi.get.and.callFake((pathname: string) => trainedResponses[pathname]);
|
||||
it('should resolve with false if no active membership is found', async () => {
|
||||
githubApi.get.
|
||||
withArgs('/teams/1/memberships/user').and.resolveTo({state: 'pending'}).
|
||||
withArgs('/teams/2/memberships/user').and.rejectWith(null).
|
||||
withArgs('/teams/3/memberships/user').and.resolveTo({state: 'not active'}).
|
||||
withArgs('/teams/4/memberships/user').and.rejectWith(null);
|
||||
|
||||
teams.isMemberById('user', [1, 2, 3, 4]).then(isMember => {
|
||||
expect(isMember).toBe(false);
|
||||
await expectAsync(teams.isMemberById('user', [1, 2, 3, 4])).toBeResolvedTo(false);
|
||||
|
||||
expect(githubApi.get).toHaveBeenCalledTimes(4);
|
||||
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(2)[0]).toBe('/teams/3/memberships/user');
|
||||
expect(githubApi.get.calls.argsFor(3)[0]).toBe('/teams/4/memberships/user');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@ -157,14 +140,13 @@ describe('GithubTeams', () => {
|
||||
beforeEach(() => {
|
||||
teams = new GithubTeams(githubApi, 'foo');
|
||||
|
||||
const mockResponse = Promise.resolve([{id: 1, slug: 'team1'}, {id: 2, slug: 'team2'}]);
|
||||
teamsFetchAllSpy = spyOn(teams, 'fetchAll').and.returnValue(mockResponse);
|
||||
teamsFetchAllSpy = spyOn(teams, 'fetchAll').and.resolveTo([{id: 1, slug: 'team1'}, {id: 2, slug: 'team2'}]);
|
||||
teamsIsMemberByIdSpy = spyOn(teams, 'isMemberById');
|
||||
});
|
||||
|
||||
|
||||
it('should return a promise', () => {
|
||||
expect(teams.isMemberBySlug('user', ['team-slug'])).toEqual(jasmine.any(Promise));
|
||||
expect(teams.isMemberBySlug('user', ['team-slug'])).toBeInstanceOf(Promise);
|
||||
});
|
||||
|
||||
|
||||
@ -174,55 +156,46 @@ describe('GithubTeams', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should resolve with false if \'fetchAll()\' rejects', done => {
|
||||
teamsFetchAllSpy.and.callFake(() => Promise.reject(null));
|
||||
teams.isMemberBySlug('user', ['team-slug']).then(isMember => {
|
||||
expect(isMember).toBe(false);
|
||||
done();
|
||||
});
|
||||
it('should resolve with false if \'fetchAll()\' rejects', async () => {
|
||||
teamsFetchAllSpy.and.rejectWith(null);
|
||||
await expectAsync(teams.isMemberBySlug('user', ['team-slug'])).toBeResolvedTo(false);
|
||||
});
|
||||
|
||||
|
||||
it('should call \'isMemberById()\' with the correct params if no team is found', done => {
|
||||
teams.isMemberBySlug('user', ['no-match']).then(() => {
|
||||
it('should call \'isMemberById()\' with the correct params if no team is found', async () => {
|
||||
await teams.isMemberBySlug('user', ['no-match']);
|
||||
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('user', []);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should call \'isMemberById()\' with the correct params if teams are found', done => {
|
||||
const spy = teamsIsMemberByIdSpy;
|
||||
it('should call \'isMemberById()\' with the correct params if teams are found', async () => {
|
||||
await teams.isMemberBySlug('userA', ['team1']);
|
||||
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('userA', [1]);
|
||||
|
||||
Promise.all([
|
||||
teams.isMemberBySlug('user', ['team1']).then(() => expect(spy).toHaveBeenCalledWith('user', [1])),
|
||||
teams.isMemberBySlug('user', ['team2']).then(() => expect(spy).toHaveBeenCalledWith('user', [2])),
|
||||
teams.isMemberBySlug('user', ['team1', 'team2']).then(() => expect(spy).toHaveBeenCalledWith('user', [1, 2])),
|
||||
]).then(done);
|
||||
await teams.isMemberBySlug('userB', ['team2']);
|
||||
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('userB', [2]);
|
||||
|
||||
await teams.isMemberBySlug('userC', ['team1', 'team2']);
|
||||
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('userC', [1, 2]);
|
||||
});
|
||||
|
||||
|
||||
it('should resolve with false if \'isMemberById()\' rejects', done => {
|
||||
teamsIsMemberByIdSpy.and.callFake(() => Promise.reject(null));
|
||||
teams.isMemberBySlug('user', ['team1']).then(isMember => {
|
||||
expect(isMember).toBe(false);
|
||||
it('should resolve with false if \'isMemberById()\' rejects', async () => {
|
||||
teamsIsMemberByIdSpy.and.rejectWith(null);
|
||||
|
||||
await expectAsync(teams.isMemberBySlug('user', ['team1'])).toBeResolvedTo(false);
|
||||
expect(teamsIsMemberByIdSpy).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
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.callFake(() => Promise.resolve(true));
|
||||
const isMember1 = await teams.isMemberBySlug('user', ['team1']);
|
||||
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]);
|
||||
teamsIsMemberByIdSpy.and.resolveTo(false);
|
||||
await expectAsync(teams.isMemberBySlug('userB', ['team1'])).toBeResolvedTo(false);
|
||||
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('userB', [1]);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -9,7 +9,8 @@ import {Logger} from '../../lib/common/utils';
|
||||
import {BuildCreator} from '../../lib/preview-server/build-creator';
|
||||
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from '../../lib/preview-server/build-events';
|
||||
import {PreviewServerError} from '../../lib/preview-server/preview-error';
|
||||
import {expectToBePreviewServerError} from './helpers';
|
||||
import {customAsyncMatchers} from './jasmine-custom-async-matchers';
|
||||
|
||||
|
||||
// Tests
|
||||
describe('BuildCreator', () => {
|
||||
@ -24,6 +25,7 @@ describe('BuildCreator', () => {
|
||||
const publicShaDir = path.join(publicPrDir, shortSha);
|
||||
let bc: BuildCreator;
|
||||
|
||||
beforeEach(() => jasmine.addAsyncMatchers(customAsyncMatchers));
|
||||
beforeEach(() => bc = new BuildCreator(buildsDir));
|
||||
|
||||
|
||||
@ -35,8 +37,8 @@ describe('BuildCreator', () => {
|
||||
|
||||
|
||||
it('should extend EventEmitter', () => {
|
||||
expect(bc).toEqual(jasmine.any(BuildCreator));
|
||||
expect(bc).toEqual(jasmine.any(EventEmitter));
|
||||
expect(bc).toBeInstanceOf(BuildCreator);
|
||||
expect(bc).toBeInstanceOf(EventEmitter);
|
||||
|
||||
expect(Object.getPrototypeOf(bc)).toBe(BuildCreator.prototype);
|
||||
});
|
||||
@ -67,47 +69,43 @@ describe('BuildCreator', () => {
|
||||
const shaDir = isPublic ? publicShaDir : hiddenShaDir;
|
||||
|
||||
|
||||
it('should return a promise', done => {
|
||||
it('should return a promise', async () => {
|
||||
const promise = bc.create(pr, sha, archive, isPublic);
|
||||
promise.then(done); // Do not complete the test (and release the spies) synchronously
|
||||
// to avoid running the actual `extractArchive()`.
|
||||
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
|
||||
// `extractArchive()`.
|
||||
await promise;
|
||||
});
|
||||
|
||||
|
||||
it('should update the PR\'s visibility first if necessary', done => {
|
||||
bcUpdatePrVisibilitySpy.and.callFake(() => expect(shellMkdirSpy).not.toHaveBeenCalled());
|
||||
it('should update the PR\'s visibility first if necessary', async () => {
|
||||
await bc.create(pr, sha, archive, isPublic);
|
||||
|
||||
bc.create(pr, sha, archive, isPublic).
|
||||
then(() => {
|
||||
expect(bcUpdatePrVisibilitySpy).toHaveBeenCalledBefore(shellMkdirSpy);
|
||||
expect(bcUpdatePrVisibilitySpy).toHaveBeenCalledWith(pr, isPublic);
|
||||
expect(shellMkdirSpy).toHaveBeenCalled();
|
||||
}).
|
||||
then(done);
|
||||
});
|
||||
|
||||
|
||||
it('should create the build directory (and any parent directories)', done => {
|
||||
bc.create(pr, sha, archive, isPublic).
|
||||
then(() => expect(shellMkdirSpy).toHaveBeenCalledWith('-p', shaDir)).
|
||||
then(done);
|
||||
it('should create the build directory (and any parent directories)', async () => {
|
||||
await bc.create(pr, sha, archive, isPublic);
|
||||
expect(shellMkdirSpy).toHaveBeenCalledWith('-p', shaDir);
|
||||
});
|
||||
|
||||
|
||||
it('should extract the archive contents into the build directory', done => {
|
||||
bc.create(pr, sha, archive, isPublic).
|
||||
then(() => expect(bcExtractArchiveSpy).toHaveBeenCalledWith(archive, shaDir)).
|
||||
then(done);
|
||||
it('should extract the archive contents into the build directory', async () => {
|
||||
await bc.create(pr, sha, archive, isPublic);
|
||||
expect(bcExtractArchiveSpy).toHaveBeenCalledWith(archive, shaDir);
|
||||
});
|
||||
|
||||
|
||||
it('should emit a CreatedBuildEvent on success', done => {
|
||||
it('should emit a CreatedBuildEvent on success', async () => {
|
||||
let emitted = false;
|
||||
|
||||
bcEmitSpy.and.callFake((type: string, evt: CreatedBuildEvent) => {
|
||||
expect(type).toBe(CreatedBuildEvent.type);
|
||||
expect(evt).toEqual(jasmine.any(CreatedBuildEvent));
|
||||
expect(evt).toBeInstanceOf(CreatedBuildEvent);
|
||||
expect(evt.pr).toBe(+pr);
|
||||
expect(evt.sha).toBe(shortSha);
|
||||
expect(evt.isPublic).toBe(isPublic);
|
||||
@ -115,130 +113,108 @@ describe('BuildCreator', () => {
|
||||
emitted = true;
|
||||
});
|
||||
|
||||
bc.create(pr, sha, archive, isPublic).
|
||||
then(() => expect(emitted).toBe(true)).
|
||||
then(done);
|
||||
await bc.create(pr, sha, archive, isPublic);
|
||||
expect(emitted).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
describe('on error', () => {
|
||||
let existsValues: {[dir: string]: boolean};
|
||||
|
||||
beforeEach(() => {
|
||||
existsValues = {
|
||||
[prDir]: false,
|
||||
[shaDir]: false,
|
||||
};
|
||||
|
||||
bcExistsSpy.and.callFake((dir: string) => existsValues[dir]);
|
||||
bcExistsSpy.and.returnValue(false);
|
||||
});
|
||||
|
||||
|
||||
it('should abort and skip further operations if changing the PR\'s visibility fails', done => {
|
||||
it('should abort and skip further operations if changing the PR\'s visibility fails', async () => {
|
||||
const mockError = new PreviewServerError(543, 'Test');
|
||||
bcUpdatePrVisibilitySpy.and.callFake(() => Promise.reject(mockError));
|
||||
bcUpdatePrVisibilitySpy.and.rejectWith(mockError);
|
||||
|
||||
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||
expect(err).toBe(mockError);
|
||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejectedWith(mockError);
|
||||
|
||||
expect(bcExistsSpy).not.toHaveBeenCalled();
|
||||
expect(shellMkdirSpy).not.toHaveBeenCalled();
|
||||
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should abort and skip further operations if the build does already exist', done => {
|
||||
existsValues[shaDir] = true;
|
||||
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||
const publicOrNot = isPublic ? 'public' : 'non-public';
|
||||
expectToBePreviewServerError(err, 409, `Request to overwrite existing ${publicOrNot} directory: ${shaDir}`);
|
||||
it('should abort and skip further operations if the build does already exist', async () => {
|
||||
bcExistsSpy.withArgs(shaDir).and.returnValue(true);
|
||||
|
||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejectedWithPreviewServerError(
|
||||
409, `Request to overwrite existing ${isPublic ? '' : 'non-'}public directory: ${shaDir}`);
|
||||
|
||||
expect(shellMkdirSpy).not.toHaveBeenCalled();
|
||||
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should detect existing build directory after visibility change', done => {
|
||||
bcUpdatePrVisibilitySpy.and.callFake(() => existsValues[prDir] = existsValues[shaDir] = true);
|
||||
it('should detect existing build directory after visibility change', async () => {
|
||||
bcUpdatePrVisibilitySpy.and.callFake(() => bcExistsSpy.and.returnValue(true));
|
||||
|
||||
expect(bcExistsSpy(prDir)).toBe(false);
|
||||
expect(bcExistsSpy(shaDir)).toBe(false);
|
||||
|
||||
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||
const publicOrNot = isPublic ? 'public' : 'non-public';
|
||||
expectToBePreviewServerError(err, 409, `Request to overwrite existing ${publicOrNot} directory: ${shaDir}`);
|
||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejectedWithPreviewServerError(
|
||||
409, `Request to overwrite existing ${isPublic ? '' : 'non-'}public directory: ${shaDir}`);
|
||||
|
||||
expect(shellMkdirSpy).not.toHaveBeenCalled();
|
||||
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should abort and skip further operations if it fails to create the directories', done => {
|
||||
it('should abort and skip further operations if it fails to create the directories', async () => {
|
||||
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(bcEmitSpy).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should abort and skip further operations if it fails to extract the archive', done => {
|
||||
it('should abort and skip further operations if it fails to extract the archive', async () => {
|
||||
bcExtractArchiveSpy.and.throwError('');
|
||||
bc.create(pr, sha, archive, isPublic).catch(() => {
|
||||
|
||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejected();
|
||||
|
||||
expect(shellMkdirSpy).toHaveBeenCalled();
|
||||
expect(bcExtractArchiveSpy).toHaveBeenCalled();
|
||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should delete the PR directory (for new PR)', done => {
|
||||
it('should delete the PR directory (for new PR)', async () => {
|
||||
bcExtractArchiveSpy.and.throwError('');
|
||||
bc.create(pr, sha, archive, isPublic).catch(() => {
|
||||
|
||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejected();
|
||||
expect(shellRmSpy).toHaveBeenCalledWith('-rf', prDir);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should delete the SHA directory (for existing PR)', done => {
|
||||
existsValues[prDir] = true;
|
||||
it('should delete the SHA directory (for existing PR)', async () => {
|
||||
bcExistsSpy.withArgs(prDir).and.returnValue(true);
|
||||
bcExtractArchiveSpy.and.throwError('');
|
||||
|
||||
bc.create(pr, sha, archive, isPublic).catch(() => {
|
||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejected();
|
||||
expect(shellRmSpy).toHaveBeenCalledWith('-rf', shaDir);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should reject with an PreviewServerError', done => {
|
||||
it('should reject with an PreviewServerError', async () => {
|
||||
// tslint:disable-next-line: no-string-throw
|
||||
shellMkdirSpy.and.callFake(() => { throw 'Test'; });
|
||||
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||
expectToBePreviewServerError(err, 500, `Error while creating preview at: ${shaDir}\nTest`);
|
||||
done();
|
||||
});
|
||||
|
||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejectedWithPreviewServerError(
|
||||
500, `Error while creating preview at: ${shaDir}\nTest`);
|
||||
});
|
||||
|
||||
|
||||
it('should pass PreviewServerError instances unmodified', done => {
|
||||
it('should pass PreviewServerError instances unmodified', async () => {
|
||||
shellMkdirSpy.and.callFake(() => { throw new PreviewServerError(543, 'Test'); });
|
||||
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||
expectToBePreviewServerError(err, 543, 'Test');
|
||||
done();
|
||||
});
|
||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejectedWithPreviewServerError(543, 'Test');
|
||||
});
|
||||
|
||||
});
|
||||
@ -265,12 +241,12 @@ describe('BuildCreator', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should return a promise', done => {
|
||||
it('should return a promise', async () => {
|
||||
const promise = bc.updatePrVisibility(pr, true);
|
||||
promise.then(done); // Do not complete the test (and release the spies) synchronously
|
||||
// to avoid running the actual `extractArchive()`.
|
||||
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 `extractArchive()`.
|
||||
await promise;
|
||||
});
|
||||
|
||||
|
||||
@ -279,58 +255,53 @@ describe('BuildCreator', () => {
|
||||
const newPrDir = makePublic ? publicPrDir : hiddenPrDir;
|
||||
|
||||
|
||||
it('should rename the directory', done => {
|
||||
bc.updatePrVisibility(pr, makePublic).
|
||||
then(() => expect(shellMvSpy).toHaveBeenCalledWith(oldPrDir, newPrDir)).
|
||||
then(done);
|
||||
it('should rename the directory', async () => {
|
||||
await bc.updatePrVisibility(pr, makePublic);
|
||||
expect(shellMvSpy).toHaveBeenCalledWith(oldPrDir, newPrDir);
|
||||
});
|
||||
|
||||
|
||||
describe('when the visibility is updated', () => {
|
||||
|
||||
it('should resolve to true', done => {
|
||||
bc.updatePrVisibility(pr, makePublic).
|
||||
then(result => expect(result).toBe(true)).
|
||||
then(done);
|
||||
it('should resolve to true', async () => {
|
||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeResolvedTo(true);
|
||||
});
|
||||
|
||||
|
||||
it('should rename the directory', done => {
|
||||
bc.updatePrVisibility(pr, makePublic).
|
||||
then(() => expect(shellMvSpy).toHaveBeenCalledWith(oldPrDir, newPrDir)).
|
||||
then(done);
|
||||
it('should rename the directory', async () => {
|
||||
await bc.updatePrVisibility(pr, makePublic);
|
||||
expect(shellMvSpy).toHaveBeenCalledWith(oldPrDir, newPrDir);
|
||||
});
|
||||
|
||||
|
||||
it('should emit a ChangedPrVisibilityEvent on success', done => {
|
||||
it('should emit a ChangedPrVisibilityEvent on success', async () => {
|
||||
let emitted = false;
|
||||
|
||||
bcEmitSpy.and.callFake((type: string, evt: ChangedPrVisibilityEvent) => {
|
||||
expect(type).toBe(ChangedPrVisibilityEvent.type);
|
||||
expect(evt).toEqual(jasmine.any(ChangedPrVisibilityEvent));
|
||||
expect(evt).toBeInstanceOf(ChangedPrVisibilityEvent);
|
||||
expect(evt.pr).toBe(+pr);
|
||||
expect(evt.shas).toEqual(jasmine.any(Array));
|
||||
expect(evt.shas).toBeInstanceOf(Array);
|
||||
expect(evt.isPublic).toBe(makePublic);
|
||||
|
||||
emitted = true;
|
||||
});
|
||||
|
||||
bc.updatePrVisibility(pr, makePublic).
|
||||
then(() => expect(emitted).toBe(true)).
|
||||
then(done);
|
||||
await bc.updatePrVisibility(pr, makePublic);
|
||||
expect(emitted).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
it('should include all shas in the emitted event', done => {
|
||||
it('should include all shas in the emitted event', async () => {
|
||||
const shas = ['foo', 'bar', 'baz'];
|
||||
let emitted = false;
|
||||
|
||||
bcListShasByDate.and.callFake(() => Promise.resolve(shas));
|
||||
bcListShasByDate.and.resolveTo(shas);
|
||||
bcEmitSpy.and.callFake((type: string, evt: ChangedPrVisibilityEvent) => {
|
||||
expect(bcListShasByDate).toHaveBeenCalledWith(newPrDir);
|
||||
|
||||
expect(type).toBe(ChangedPrVisibilityEvent.type);
|
||||
expect(evt).toEqual(jasmine.any(ChangedPrVisibilityEvent));
|
||||
expect(evt).toBeInstanceOf(ChangedPrVisibilityEvent);
|
||||
expect(evt.pr).toBe(+pr);
|
||||
expect(evt.shas).toBe(shas);
|
||||
expect(evt.isPublic).toBe(makePublic);
|
||||
@ -338,94 +309,82 @@ describe('BuildCreator', () => {
|
||||
emitted = true;
|
||||
});
|
||||
|
||||
bc.updatePrVisibility(pr, makePublic).
|
||||
then(() => expect(emitted).toBe(true)).
|
||||
then(done);
|
||||
await bc.updatePrVisibility(pr, makePublic);
|
||||
expect(emitted).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
it('should do nothing if the visibility is already up-to-date', done => {
|
||||
it('should do nothing if the visibility is already up-to-date', async () => {
|
||||
bcExistsSpy.and.callFake((dir: string) => dir === newPrDir);
|
||||
bc.updatePrVisibility(pr, makePublic).
|
||||
then(result => {
|
||||
expect(result).toBe(false);
|
||||
|
||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeResolvedTo(false);
|
||||
|
||||
expect(shellMvSpy).not.toHaveBeenCalled();
|
||||
expect(bcListShasByDate).not.toHaveBeenCalled();
|
||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||
}).
|
||||
then(done);
|
||||
});
|
||||
|
||||
|
||||
it('should do nothing if the PR directory does not exist', done => {
|
||||
it('should do nothing if the PR directory does not exist', async () => {
|
||||
bcExistsSpy.and.returnValue(false);
|
||||
bc.updatePrVisibility(pr, makePublic).
|
||||
then(result => {
|
||||
expect(result).toBe(false);
|
||||
|
||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeResolvedTo(false);
|
||||
|
||||
expect(shellMvSpy).not.toHaveBeenCalled();
|
||||
expect(bcListShasByDate).not.toHaveBeenCalled();
|
||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||
}).
|
||||
then(done);
|
||||
});
|
||||
|
||||
|
||||
describe('on error', () => {
|
||||
|
||||
it('should abort and skip further operations if both directories exist', done => {
|
||||
it('should abort and skip further operations if both directories exist', async () => {
|
||||
bcExistsSpy.and.returnValue(true);
|
||||
bc.updatePrVisibility(pr, makePublic).catch(err => {
|
||||
expectToBePreviewServerError(err, 409,
|
||||
`Request to move '${oldPrDir}' to existing directory '${newPrDir}'.`);
|
||||
|
||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeRejectedWithPreviewServerError(
|
||||
409, `Request to move '${oldPrDir}' to existing directory '${newPrDir}'.`);
|
||||
|
||||
expect(shellMvSpy).not.toHaveBeenCalled();
|
||||
expect(bcListShasByDate).not.toHaveBeenCalled();
|
||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should abort and skip further operations if it fails to rename the directory', done => {
|
||||
it('should abort and skip further operations if it fails to rename the directory', async () => {
|
||||
shellMvSpy.and.throwError('');
|
||||
bc.updatePrVisibility(pr, makePublic).catch(() => {
|
||||
|
||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeRejected();
|
||||
|
||||
expect(shellMvSpy).toHaveBeenCalled();
|
||||
expect(bcListShasByDate).not.toHaveBeenCalled();
|
||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should abort and skip further operations if it fails to list the SHAs', done => {
|
||||
it('should abort and skip further operations if it fails to list the SHAs', async () => {
|
||||
bcListShasByDate.and.throwError('');
|
||||
bc.updatePrVisibility(pr, makePublic).catch(() => {
|
||||
|
||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeRejected();
|
||||
|
||||
expect(shellMvSpy).toHaveBeenCalled();
|
||||
expect(bcListShasByDate).toHaveBeenCalled();
|
||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should reject with an PreviewServerError', done => {
|
||||
it('should reject with an PreviewServerError', async () => {
|
||||
// tslint:disable-next-line: no-string-throw
|
||||
shellMvSpy.and.callFake(() => { throw 'Test'; });
|
||||
bc.updatePrVisibility(pr, makePublic).catch(err => {
|
||||
expectToBePreviewServerError(err, 500,
|
||||
`Error while making PR ${pr} ${makePublic ? 'public' : 'hidden'}.\nTest`);
|
||||
done();
|
||||
});
|
||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeRejectedWithPreviewServerError(
|
||||
500, `Error while making PR ${pr} ${makePublic ? 'public' : 'hidden'}.\nTest`);
|
||||
});
|
||||
|
||||
|
||||
it('should pass PreviewServerError instances unmodified', done => {
|
||||
it('should pass PreviewServerError instances unmodified', async () => {
|
||||
shellMvSpy.and.callFake(() => { throw new PreviewServerError(543, 'Test'); });
|
||||
bc.updatePrVisibility(pr, makePublic).catch(err => {
|
||||
expectToBePreviewServerError(err, 543, 'Test');
|
||||
done();
|
||||
});
|
||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeRejectedWithPreviewServerError(543, 'Test');
|
||||
});
|
||||
|
||||
});
|
||||
@ -443,12 +402,14 @@ describe('BuildCreator', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
fsAccessCbs = [];
|
||||
fsAccessSpy = spyOn(fs, 'access').and.callFake((_: string, cb: (v?: any) => void) => fsAccessCbs.push(cb));
|
||||
fsAccessSpy = spyOn(fs, 'access').and.callFake(
|
||||
((_: string, cb: (v?: any) => void) => fsAccessCbs.push(cb)) as unknown as typeof fs.access,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
it('should return a promise', () => {
|
||||
expect((bc as any).exists('foo')).toEqual(jasmine.any(Promise));
|
||||
expect((bc as any).exists('foo')).toBeInstanceOf(Promise);
|
||||
});
|
||||
|
||||
|
||||
@ -458,25 +419,29 @@ describe('BuildCreator', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should resolve with \'true\' if \'fs.access()\' succeeds', done => {
|
||||
Promise.
|
||||
all([(bc as any).exists('foo'), (bc as any).exists('bar')]).
|
||||
then(results => expect(results).toEqual([true, true])).
|
||||
then(done);
|
||||
it('should resolve with \'true\' if \'fs.access()\' succeeds', async () => {
|
||||
const existsPromises = [
|
||||
(bc as any).exists('foo'),
|
||||
(bc as any).exists('bar'),
|
||||
];
|
||||
|
||||
fsAccessCbs[0]();
|
||||
fsAccessCbs[1](null);
|
||||
|
||||
await expectAsync(Promise.all(existsPromises)).toBeResolvedTo([true, true]);
|
||||
});
|
||||
|
||||
|
||||
it('should resolve with \'false\' if \'fs.access()\' errors', done => {
|
||||
Promise.
|
||||
all([(bc as any).exists('foo'), (bc as any).exists('bar')]).
|
||||
then(results => expect(results).toEqual([false, false])).
|
||||
then(done);
|
||||
it('should resolve with \'false\' if \'fs.access()\' errors', async () => {
|
||||
const existsPromises = [
|
||||
(bc as any).exists('foo'),
|
||||
(bc as any).exists('bar'),
|
||||
];
|
||||
|
||||
fsAccessCbs[0]('Error');
|
||||
fsAccessCbs[1](new Error());
|
||||
|
||||
await expectAsync(Promise.all(existsPromises)).toBeResolvedTo([false, false]);
|
||||
});
|
||||
|
||||
});
|
||||
@ -495,12 +460,15 @@ describe('BuildCreator', () => {
|
||||
consoleWarnSpy = spyOn(Logger.prototype, 'warn');
|
||||
shellChmodSpy = spyOn(shell, 'chmod');
|
||||
shellRmSpy = spyOn(shell, 'rm');
|
||||
cpExecSpy = spyOn(cp, 'exec').and.callFake((_: string, cb: (...args: any[]) => void) => cpExecCbs.push(cb));
|
||||
cpExecSpy = spyOn(cp, 'exec').and.callFake(
|
||||
((_: string, cb: (...args: any[]) => void) =>
|
||||
cpExecCbs.push(cb)) as unknown as typeof cp.exec,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
it('should return a promise', () => {
|
||||
expect((bc as any).extractArchive('foo', 'bar')).toEqual(jasmine.any(Promise));
|
||||
expect((bc as any).extractArchive('foo', 'bar')).toBeInstanceOf(Promise);
|
||||
});
|
||||
|
||||
|
||||
@ -512,78 +480,68 @@ describe('BuildCreator', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should log (as a warning) any stderr output if extracting succeeded', done => {
|
||||
(bc as any).extractArchive('foo', 'bar').
|
||||
then(() => expect(consoleWarnSpy).toHaveBeenCalledWith('This is stderr')).
|
||||
then(done);
|
||||
|
||||
it('should log (as a warning) any stderr output if extracting succeeded', async () => {
|
||||
const extractPromise = (bc as any).extractArchive('foo', 'bar');
|
||||
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', done => {
|
||||
(bc as any).extractArchive('foo', 'bar').
|
||||
then(() => expect(shellChmodSpy).toHaveBeenCalledWith('-R', 'a-w', 'bar')).
|
||||
then(done);
|
||||
|
||||
it('should make the build directory non-writable', async () => {
|
||||
const extractPromise = (bc as any).extractArchive('foo', 'bar');
|
||||
cpExecCbs[0]();
|
||||
|
||||
await expectAsync(extractPromise).toBeResolved();
|
||||
expect(shellChmodSpy).toHaveBeenCalledWith('-R', 'a-w', 'bar');
|
||||
});
|
||||
|
||||
|
||||
it('should delete the build artifact file on success', done => {
|
||||
(bc as any).extractArchive('input/file', 'output/dir').
|
||||
then(() => expect(shellRmSpy).toHaveBeenCalledWith('-f', 'input/file')).
|
||||
then(done);
|
||||
|
||||
it('should delete the build artifact file on success', async () => {
|
||||
const extractPromise = (bc as any).extractArchive('input/file', 'output/dir');
|
||||
cpExecCbs[0]();
|
||||
|
||||
await expectAsync(extractPromise).toBeResolved();
|
||||
expect(shellRmSpy).toHaveBeenCalledWith('-f', 'input/file');
|
||||
});
|
||||
|
||||
|
||||
describe('on error', () => {
|
||||
|
||||
it('should abort and skip further operations if it fails to extract the archive', done => {
|
||||
(bc as any).extractArchive('foo', 'bar').catch((err: any) => {
|
||||
it('should abort and skip further operations if it fails to extract the archive', async () => {
|
||||
const extractPromise = (bc as any).extractArchive('foo', 'bar');
|
||||
cpExecCbs[0]('Test');
|
||||
|
||||
await expectAsync(extractPromise).toBeRejectedWith('Test');
|
||||
expect(shellChmodSpy).not.toHaveBeenCalled();
|
||||
expect(shellRmSpy).not.toHaveBeenCalled();
|
||||
expect(err).toBe('Test');
|
||||
done();
|
||||
});
|
||||
|
||||
cpExecCbs[0]('Test');
|
||||
});
|
||||
|
||||
|
||||
it('should abort and skip further operations if it fails to make non-writable', done => {
|
||||
(bc as any).extractArchive('foo', 'bar').catch((err: any) => {
|
||||
it('should abort and skip further operations if it fails to make non-writable', async () => {
|
||||
// tslint:disable-next-line: no-string-throw
|
||||
shellChmodSpy.and.callFake(() => { throw 'Test'; });
|
||||
|
||||
const extractPromise = (bc as any).extractArchive('foo', 'bar');
|
||||
cpExecCbs[0]();
|
||||
|
||||
await expectAsync(extractPromise).toBeRejectedWith('Test');
|
||||
expect(shellChmodSpy).toHaveBeenCalled();
|
||||
expect(shellRmSpy).not.toHaveBeenCalled();
|
||||
expect(err).toBe('Test');
|
||||
done();
|
||||
});
|
||||
|
||||
shellChmodSpy.and.callFake(() => {
|
||||
|
||||
it('should abort and reject if it fails to remove the build artifact file', async () => {
|
||||
// tslint:disable-next-line: no-string-throw
|
||||
throw 'Test';
|
||||
});
|
||||
shellRmSpy.and.callFake(() => { throw 'Test'; });
|
||||
|
||||
const extractPromise = (bc as any).extractArchive('foo', 'bar');
|
||||
cpExecCbs[0]();
|
||||
});
|
||||
|
||||
|
||||
it('should abort and reject if it fails to remove the build artifact file', done => {
|
||||
(bc as any).extractArchive('foo', 'bar').catch((err: any) => {
|
||||
await expectAsync(extractPromise).toBeRejectedWith('Test');
|
||||
expect(shellChmodSpy).toHaveBeenCalled();
|
||||
expect(shellRmSpy).toHaveBeenCalled();
|
||||
expect(err).toBe('Test');
|
||||
done();
|
||||
});
|
||||
|
||||
shellRmSpy.and.callFake(() => {
|
||||
// tslint:disable-next-line: no-string-throw
|
||||
throw 'Test';
|
||||
});
|
||||
|
||||
cpExecCbs[0]();
|
||||
});
|
||||
|
||||
});
|
||||
@ -600,62 +558,54 @@ describe('BuildCreator', () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
shellLsSpy = spyOn(shell, 'ls').and.returnValue([]);
|
||||
shellLsSpy = spyOn(shell, 'ls').and.returnValue([] as unknown as shell.ShellArray);
|
||||
});
|
||||
|
||||
|
||||
it('should return a promise', done => {
|
||||
it('should return a promise', async () => {
|
||||
const promise = (bc as any).listShasByDate('input/dir');
|
||||
promise.then(done); // Do not complete the test (and release the spies) synchronously
|
||||
// to avoid running the actual `ls()`.
|
||||
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 `ls()`.
|
||||
await promise;
|
||||
});
|
||||
|
||||
|
||||
it('should `ls()` files with their metadata', done => {
|
||||
(bc as any).listShasByDate('input/dir').
|
||||
then(() => expect(shellLsSpy).toHaveBeenCalledWith('-l', 'input/dir')).
|
||||
then(done);
|
||||
it('should `ls()` files with their metadata', async () => {
|
||||
await (bc as any).listShasByDate('input/dir');
|
||||
expect(shellLsSpy).toHaveBeenCalledWith('-l', 'input/dir');
|
||||
});
|
||||
|
||||
|
||||
it('should reject if listing files fails', done => {
|
||||
shellLsSpy.and.callFake(() => Promise.reject('Test'));
|
||||
(bc as any).listShasByDate('input/dir').catch((err: string) => {
|
||||
expect(err).toBe('Test');
|
||||
done();
|
||||
});
|
||||
it('should reject if listing files fails', async () => {
|
||||
shellLsSpy.and.rejectWith('Test');
|
||||
await expectAsync((bc as any).listShasByDate('input/dir')).toBeRejectedWith('Test');
|
||||
});
|
||||
|
||||
|
||||
it('should return the filenames', done => {
|
||||
shellLsSpy.and.callFake(() => Promise.resolve([
|
||||
it('should return the filenames', async () => {
|
||||
shellLsSpy.and.resolveTo([
|
||||
lsResult('foo', 100),
|
||||
lsResult('bar', 200),
|
||||
lsResult('baz', 300),
|
||||
]));
|
||||
]);
|
||||
|
||||
(bc as any).listShasByDate('input/dir').
|
||||
then((shas: string[]) => expect(shas).toEqual(['foo', 'bar', 'baz'])).
|
||||
then(done);
|
||||
await expectAsync((bc as any).listShasByDate('input/dir')).toBeResolvedTo(['foo', 'bar', 'baz']);
|
||||
});
|
||||
|
||||
|
||||
it('should sort by date', done => {
|
||||
shellLsSpy.and.callFake(() => Promise.resolve([
|
||||
it('should sort by date', async () => {
|
||||
shellLsSpy.and.resolveTo([
|
||||
lsResult('foo', 300),
|
||||
lsResult('bar', 100),
|
||||
lsResult('baz', 200),
|
||||
]));
|
||||
]);
|
||||
|
||||
(bc as any).listShasByDate('input/dir').
|
||||
then((shas: string[]) => expect(shas).toEqual(['bar', 'baz', 'foo'])).
|
||||
then(done);
|
||||
await expectAsync((bc as any).listShasByDate('input/dir')).toBeResolvedTo(['bar', 'baz', 'foo']);
|
||||
});
|
||||
|
||||
|
||||
it('should not break with ShellJS\' custom `sort()` method', done => {
|
||||
it('should not break with ShellJS\' custom `sort()` method', async () => {
|
||||
const mockArray = [
|
||||
lsResult('foo', 300),
|
||||
lsResult('bar', 100),
|
||||
@ -663,26 +613,21 @@ describe('BuildCreator', () => {
|
||||
];
|
||||
mockArray.sort = jasmine.createSpy('sort');
|
||||
|
||||
shellLsSpy.and.callFake(() => Promise.resolve(mockArray));
|
||||
(bc as any).listShasByDate('input/dir').
|
||||
then((shas: string[]) => {
|
||||
expect(shas).toEqual(['bar', 'baz', 'foo']);
|
||||
shellLsSpy.and.resolveTo(mockArray);
|
||||
|
||||
await expectAsync((bc as any).listShasByDate('input/dir')).toBeResolvedTo(['bar', 'baz', 'foo']);
|
||||
expect(mockArray.sort).not.toHaveBeenCalled();
|
||||
}).
|
||||
then(done);
|
||||
});
|
||||
|
||||
|
||||
it('should only include directories', done => {
|
||||
shellLsSpy.and.callFake(() => Promise.resolve([
|
||||
it('should only include directories', async () => {
|
||||
shellLsSpy.and.resolveTo([
|
||||
lsResult('foo', 100),
|
||||
lsResult('bar', 200, false),
|
||||
lsResult('baz', 300),
|
||||
]));
|
||||
]);
|
||||
|
||||
(bc as any).listShasByDate('input/dir').
|
||||
then((shas: string[]) => expect(shas).toEqual(['foo', 'baz'])).
|
||||
then(done);
|
||||
await expectAsync((bc as any).listShasByDate('input/dir')).toBeResolvedTo(['foo', 'baz']);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -32,18 +32,18 @@ describe('BuildRetriever', () => {
|
||||
};
|
||||
|
||||
api = new CircleCiApi('ORG', 'REPO', 'TOKEN');
|
||||
spyOn(api, 'getBuildInfo').and.callFake(() => Promise.resolve(BUILD_INFO));
|
||||
getBuildArtifactUrlSpy = spyOn(api, 'getBuildArtifactUrl')
|
||||
.and.callFake(() => Promise.resolve(BASE_URL + ARTIFACT_PATH));
|
||||
spyOn(api, 'getBuildInfo').and.resolveTo(BUILD_INFO);
|
||||
getBuildArtifactUrlSpy = spyOn(api, 'getBuildArtifactUrl').and.resolveTo(BASE_URL + ARTIFACT_PATH);
|
||||
|
||||
WRITEFILE_RESULT = undefined;
|
||||
writeFileSpy = spyOn(fs, 'writeFile').and.callFake(
|
||||
(_path: string, _buffer: Buffer, callback: (err?: any) => {}) => callback(WRITEFILE_RESULT),
|
||||
((_path: string, _buffer: Buffer, callback: fs.NoParamCallback) =>
|
||||
callback(WRITEFILE_RESULT)) as typeof fs.writeFile,
|
||||
);
|
||||
|
||||
EXISTS_RESULT = false;
|
||||
existsSpy = spyOn(fs, 'exists').and.callFake(
|
||||
(_path: string, callback: (exists: boolean) => {}) => callback(EXISTS_RESULT),
|
||||
((_path, callback) => callback(EXISTS_RESULT)) as typeof fs.exists,
|
||||
);
|
||||
});
|
||||
|
||||
@ -56,6 +56,7 @@ describe('BuildRetriever', () => {
|
||||
expect(() => new BuildRetriever(api, -1, DOWNLOAD_DIR))
|
||||
.toThrowError(`Invalid parameter "downloadSizeLimit" should be a number greater than 0.`);
|
||||
});
|
||||
|
||||
it('should fail if the "downloadDir" is missing', () => {
|
||||
expect(() => new BuildRetriever(api, MAX_DOWNLOAD_SIZE, ''))
|
||||
.toThrowError(`Missing or empty required parameter 'downloadDir'!`);
|
||||
@ -72,14 +73,10 @@ describe('BuildRetriever', () => {
|
||||
});
|
||||
|
||||
it('should error if it is not possible to extract the PR number from the branch', async () => {
|
||||
const retriever = new BuildRetriever(api, MAX_DOWNLOAD_SIZE, DOWNLOAD_DIR);
|
||||
try {
|
||||
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');
|
||||
}
|
||||
const retriever = new BuildRetriever(api, MAX_DOWNLOAD_SIZE, DOWNLOAD_DIR);
|
||||
|
||||
await expectAsync(retriever.getGithubInfo(12345)).toBeRejectedWithError('No PR found in branch field: master');
|
||||
});
|
||||
});
|
||||
|
||||
@ -110,12 +107,10 @@ describe('BuildRetriever', () => {
|
||||
it('should fail if the artifact is too large', async () => {
|
||||
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(200, ARTIFACT_CONTENTS);
|
||||
retriever = new BuildRetriever(api, 10, DOWNLOAD_DIR);
|
||||
try {
|
||||
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
||||
throw new Error('Exception Expected');
|
||||
} catch (error) {
|
||||
expect(error.status).toEqual(413);
|
||||
}
|
||||
|
||||
await expectAsync(retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH)).
|
||||
toBeRejectedWith(jasmine.objectContaining({status: 413}));
|
||||
|
||||
artifactRequest.done();
|
||||
});
|
||||
|
||||
@ -144,49 +139,39 @@ describe('BuildRetriever', () => {
|
||||
});
|
||||
|
||||
it('should fail if the CircleCI API fails', async () => {
|
||||
try {
|
||||
getBuildArtifactUrlSpy.and.callFake(() => Promise.reject('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)');
|
||||
}
|
||||
getBuildArtifactUrlSpy.and.rejectWith('getBuildArtifactUrl failed');
|
||||
await expectAsync(retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH)).
|
||||
toBeRejectedWithError('CircleCI artifact download failed (getBuildArtifactUrl failed)');
|
||||
});
|
||||
|
||||
it('should fail if the URL fetch errors', async () => {
|
||||
// create a new handler that errors
|
||||
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).replyWithError('Artifact Request Failed');
|
||||
try {
|
||||
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
||||
throw new Error('Exception Expected');
|
||||
} catch (error) {
|
||||
expect(error.message).toEqual('CircleCI artifact download failed ' +
|
||||
|
||||
await expectAsync(retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH)).toBeRejectedWithError(
|
||||
'CircleCI artifact download failed ' +
|
||||
'(request to http://test.com/some/path/build.zip failed, reason: Artifact Request Failed)');
|
||||
}
|
||||
|
||||
artifactRequest.done();
|
||||
});
|
||||
|
||||
it('should fail if the URL fetch 404s', async () => {
|
||||
// create a new handler that errors
|
||||
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(404, 'No such artifact');
|
||||
try {
|
||||
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
||||
throw new Error('Exception Expected');
|
||||
} catch (error) {
|
||||
expect(error.message).toEqual('CircleCI artifact download failed (Error 404 - Not Found)');
|
||||
}
|
||||
|
||||
await expectAsync(retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH)).
|
||||
toBeRejectedWithError('CircleCI artifact download failed (Error 404 - Not Found)');
|
||||
|
||||
artifactRequest.done();
|
||||
});
|
||||
|
||||
it('should fail if file write fails', async () => {
|
||||
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(200, ARTIFACT_CONTENTS);
|
||||
try {
|
||||
WRITEFILE_RESULT = 'Test Error';
|
||||
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
||||
throw new Error('Exception Expected');
|
||||
} catch (error) {
|
||||
expect(error.message).toEqual('CircleCI artifact download failed (Test Error)');
|
||||
}
|
||||
|
||||
await expectAsync(retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH)).
|
||||
toBeRejectedWithError('CircleCI artifact download failed (Test Error)');
|
||||
|
||||
artifactRequest.done();
|
||||
});
|
||||
});
|
||||
|
@ -51,7 +51,10 @@ describe('BuildVerifier', () => {
|
||||
describe('getSignificantFilesChanged', () => {
|
||||
it('should return false if none of the fetched files match the given pattern', async () => {
|
||||
const fetchFilesSpy = spyOn(prs, 'fetchFiles');
|
||||
fetchFilesSpy.and.callFake(() => Promise.resolve([{filename: 'a/b/c'}, {filename: 'd/e/f'}]));
|
||||
fetchFilesSpy.and.resolveTo([
|
||||
{filename: 'a/b/c', sha: 'a1'},
|
||||
{filename: 'd/e/f', sha: 'b2'},
|
||||
]);
|
||||
expect(await bv.getSignificantFilesChanged(777, /^x/)).toEqual(false);
|
||||
expect(fetchFilesSpy).toHaveBeenCalledWith(777);
|
||||
|
||||
@ -78,37 +81,30 @@ describe('BuildVerifier', () => {
|
||||
user: {login: 'username'},
|
||||
};
|
||||
|
||||
prsFetchSpy = spyOn(GithubPullRequests.prototype, 'fetch').
|
||||
and.callFake(() => Promise.resolve(mockPrInfo));
|
||||
|
||||
teamsIsMemberBySlugSpy = spyOn(GithubTeams.prototype, 'isMemberBySlug').
|
||||
and.callFake(() => Promise.resolve(true));
|
||||
prsFetchSpy = spyOn(GithubPullRequests.prototype, 'fetch').and.resolveTo(mockPrInfo);
|
||||
teamsIsMemberBySlugSpy = spyOn(GithubTeams.prototype, 'isMemberBySlug').and.resolveTo(true);
|
||||
});
|
||||
|
||||
|
||||
it('should return a promise', done => {
|
||||
it('should return a promise', async () => {
|
||||
const promise = bv.getPrIsTrusted(pr);
|
||||
promise.then(done); // Do not complete the test (and release the spies) synchronously
|
||||
// to avoid running the actual `GithubTeams#isMemberBySlug()`.
|
||||
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
|
||||
// `GithubTeams#isMemberBySlug()`.
|
||||
await promise;
|
||||
});
|
||||
|
||||
|
||||
it('should fetch the corresponding PR', done => {
|
||||
bv.getPrIsTrusted(pr).then(() => {
|
||||
it('should fetch the corresponding PR', async () => {
|
||||
await bv.getPrIsTrusted(pr);
|
||||
expect(prsFetchSpy).toHaveBeenCalledWith(pr);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should fail if fetching the PR errors', done => {
|
||||
prsFetchSpy.and.callFake(() => Promise.reject('Test'));
|
||||
bv.getPrIsTrusted(pr).catch(err => {
|
||||
expect(err).toBe('Test');
|
||||
done();
|
||||
});
|
||||
it('should fail if fetching the PR errors', async () => {
|
||||
prsFetchSpy.and.rejectWith('Test');
|
||||
await expectAsync(bv.getPrIsTrusted(pr)).toBeRejectedWith('Test');
|
||||
});
|
||||
|
||||
|
||||
@ -117,19 +113,14 @@ describe('BuildVerifier', () => {
|
||||
beforeEach(() => mockPrInfo.labels.push({name: 'trusted: pr-label'}));
|
||||
|
||||
|
||||
it('should resolve to true', done => {
|
||||
bv.getPrIsTrusted(pr).then(isTrusted => {
|
||||
expect(isTrusted).toBe(true);
|
||||
done();
|
||||
});
|
||||
it('should resolve to true', async () => {
|
||||
await expectAsync(bv.getPrIsTrusted(pr)).toBeResolvedTo(true);
|
||||
});
|
||||
|
||||
|
||||
it('should not try to verify the author\'s membership status', done => {
|
||||
bv.getPrIsTrusted(pr).then(() => {
|
||||
it('should not try to verify the author\'s membership status', async () => {
|
||||
await expectAsync(bv.getPrIsTrusted(pr));
|
||||
expect(teamsIsMemberBySlugSpy).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@ -137,40 +128,27 @@ describe('BuildVerifier', () => {
|
||||
|
||||
describe('when the PR does not have the "trusted PR" label', () => {
|
||||
|
||||
it('should verify the PR author\'s membership in the specified teams', done => {
|
||||
bv.getPrIsTrusted(pr).then(() => {
|
||||
it('should verify the PR author\'s membership in the specified teams', async () => {
|
||||
await bv.getPrIsTrusted(pr);
|
||||
expect(teamsIsMemberBySlugSpy).toHaveBeenCalledWith('username', ['team1', 'team2']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should fail if verifying membership errors', done => {
|
||||
teamsIsMemberBySlugSpy.and.callFake(() => Promise.reject('Test'));
|
||||
bv.getPrIsTrusted(pr).catch(err => {
|
||||
expect(err).toBe('Test');
|
||||
done();
|
||||
});
|
||||
it('should fail if verifying membership errors', async () => {
|
||||
teamsIsMemberBySlugSpy.and.rejectWith('Test');
|
||||
await expectAsync(bv.getPrIsTrusted(pr)).toBeRejectedWith('Test');
|
||||
});
|
||||
|
||||
|
||||
it('should resolve to true if the PR\'s author is a member', done => {
|
||||
teamsIsMemberBySlugSpy.and.callFake(() => Promise.resolve(true));
|
||||
|
||||
bv.getPrIsTrusted(pr).then(isTrusted => {
|
||||
expect(isTrusted).toBe(true);
|
||||
done();
|
||||
});
|
||||
it('should resolve to true if the PR\'s author is a member', async () => {
|
||||
teamsIsMemberBySlugSpy.and.resolveTo(true);
|
||||
await expectAsync(bv.getPrIsTrusted(pr)).toBeResolvedTo(true);
|
||||
});
|
||||
|
||||
|
||||
it('should resolve to false if the PR\'s author is not a member', done => {
|
||||
teamsIsMemberBySlugSpy.and.callFake(() => Promise.resolve(false));
|
||||
|
||||
bv.getPrIsTrusted(pr).then(isTrusted => {
|
||||
expect(isTrusted).toBe(false);
|
||||
done();
|
||||
});
|
||||
it('should resolve to false if the PR\'s author is not a member', async () => {
|
||||
teamsIsMemberBySlugSpy.and.resolveTo(false);
|
||||
await expectAsync(bv.getPrIsTrusted(pr)).toBeResolvedTo(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -1,11 +0,0 @@
|
||||
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);
|
||||
}
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
declare module jasmine {
|
||||
interface AsyncMatchers {
|
||||
toBeRejectedWithPreviewServerError(status: number, message?: string | RegExp): Promise<void>;
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
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', () => {
|
||||
expect(err).toEqual(jasmine.any(PreviewServerError));
|
||||
expect(err).toEqual(jasmine.any(Error));
|
||||
expect(err).toBeInstanceOf(PreviewServerError);
|
||||
expect(err).toBeInstanceOf(Error);
|
||||
|
||||
expect(Object.getPrototypeOf(err)).toBe(PreviewServerError.prototype);
|
||||
});
|
||||
|
@ -1,5 +1,4 @@
|
||||
// Imports
|
||||
import * as express from 'express';
|
||||
import * as http from 'http';
|
||||
import * as supertest from 'supertest';
|
||||
import {CircleCiApi} from '../../lib/common/circle-ci-api';
|
||||
@ -134,7 +133,7 @@ describe('PreviewServerFactory', () => {
|
||||
const buildCreator = jasmine.any(BuildCreator);
|
||||
expect(usfCreateMiddlewareSpy).toHaveBeenCalledWith(buildRetriever, buildVerifier, buildCreator, defaultConfig);
|
||||
|
||||
const middleware: express.Express = usfCreateMiddlewareSpy.calls.mostRecent().returnValue;
|
||||
const middleware = usfCreateMiddlewareSpy.calls.mostRecent().returnValue;
|
||||
expect(httpCreateServerSpy).toHaveBeenCalledWith(middleware);
|
||||
});
|
||||
|
||||
@ -230,7 +229,7 @@ describe('PreviewServerFactory', () => {
|
||||
|
||||
expect(prsAddCommentSpy).toHaveBeenCalledTimes(2);
|
||||
expect(prs).toBe(allCalls[1].object);
|
||||
expect(prs).toEqual(jasmine.any(GithubPullRequests));
|
||||
expect(prs).toBeInstanceOf(GithubPullRequests);
|
||||
expect(prs.repoSlug).toBe('organisation/repo');
|
||||
});
|
||||
|
||||
@ -302,9 +301,8 @@ describe('PreviewServerFactory', () => {
|
||||
let bvGetSignificantFilesChangedSpy: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
bvGetPrIsTrustedSpy = spyOn(buildVerifier, 'getPrIsTrusted').and.returnValue(Promise.resolve(true));
|
||||
bvGetSignificantFilesChangedSpy = spyOn(buildVerifier, 'getSignificantFilesChanged').
|
||||
and.returnValue(Promise.resolve(true));
|
||||
bvGetPrIsTrustedSpy = spyOn(buildVerifier, 'getPrIsTrusted').and.resolveTo(true);
|
||||
bvGetSignificantFilesChangedSpy = spyOn(buildVerifier, 'getSignificantFilesChanged').and.resolveTo(true);
|
||||
});
|
||||
|
||||
|
||||
@ -331,7 +329,7 @@ describe('PreviewServerFactory', () => {
|
||||
|
||||
|
||||
it('should respond appropriately if the PR did not touch any significant files', async () => {
|
||||
bvGetSignificantFilesChangedSpy.and.returnValue(Promise.resolve(false));
|
||||
bvGetSignificantFilesChangedSpy.and.resolveTo(false);
|
||||
|
||||
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.`;
|
||||
@ -345,7 +343,7 @@ describe('PreviewServerFactory', () => {
|
||||
|
||||
|
||||
it('should respond appropriately if the PR is not automatically verifiable as "trusted"', async () => {
|
||||
bvGetPrIsTrustedSpy.and.returnValue(Promise.resolve(false));
|
||||
bvGetPrIsTrustedSpy.and.resolveTo(false);
|
||||
|
||||
const expectedResponse = {canHavePublicPreview: false, reason: 'Not automatically verifiable as "trusted".'};
|
||||
const expectedLog =
|
||||
@ -372,7 +370,7 @@ describe('PreviewServerFactory', () => {
|
||||
|
||||
|
||||
it('should respond with error if `getSignificantFilesChanged()` fails', async () => {
|
||||
bvGetSignificantFilesChangedSpy.and.callFake(() => Promise.reject('getSignificantFilesChanged error'));
|
||||
bvGetSignificantFilesChangedSpy.and.rejectWith('getSignificantFilesChanged error');
|
||||
|
||||
await agent.get(url).expect(500, 'getSignificantFilesChanged error');
|
||||
expect(loggerErrorSpy).toHaveBeenCalledWith('Previewability check error', 'getSignificantFilesChanged error');
|
||||
@ -380,11 +378,10 @@ describe('PreviewServerFactory', () => {
|
||||
|
||||
|
||||
it('should respond with error if `getPrIsTrusted()` fails', async () => {
|
||||
const error = new Error('getPrIsTrusted error');
|
||||
bvGetPrIsTrustedSpy.and.callFake(() => { throw error; });
|
||||
bvGetPrIsTrustedSpy.and.throwError('getPrIsTrusted error');
|
||||
|
||||
await agent.get(url).expect(500, 'getPrIsTrusted error');
|
||||
expect(loggerErrorSpy).toHaveBeenCalledWith('Previewability check error', error);
|
||||
expect(loggerErrorSpy).toHaveBeenCalledWith('Previewability check error', new Error('getPrIsTrusted error'));
|
||||
});
|
||||
|
||||
});
|
||||
@ -497,7 +494,7 @@ describe('PreviewServerFactory', () => {
|
||||
// Note it is important to put the `reject` into `and.callFake`;
|
||||
// If you just `and.returnValue` the rejected promise
|
||||
// then you get an "unhandled rejection" message in the console.
|
||||
getGithubInfoSpy.and.callFake(() => Promise.reject('Test Error'));
|
||||
getGithubInfoSpy.and.rejectWith('Test Error');
|
||||
await agent.post(URL).send(BASIC_PAYLOAD).expect(500, 'Test Error');
|
||||
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
||||
expect(downloadBuildArtifactSpy).not.toHaveBeenCalled();
|
||||
@ -518,7 +515,7 @@ describe('PreviewServerFactory', () => {
|
||||
});
|
||||
|
||||
it('should fail if the artifact fetch request fails', async () => {
|
||||
downloadBuildArtifactSpy.and.callFake(() => Promise.reject('Test Error'));
|
||||
downloadBuildArtifactSpy.and.rejectWith('Test Error');
|
||||
await agent.post(URL).send(BASIC_PAYLOAD).expect(500, 'Test Error');
|
||||
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
||||
expect(downloadBuildArtifactSpy).toHaveBeenCalled();
|
||||
@ -527,7 +524,7 @@ describe('PreviewServerFactory', () => {
|
||||
});
|
||||
|
||||
it('should fail if verifying the PR fails', async () => {
|
||||
getPrIsTrustedSpy.and.callFake(() => Promise.reject('Test Error'));
|
||||
getPrIsTrustedSpy.and.rejectWith('Test Error');
|
||||
await agent.post(URL).send(BASIC_PAYLOAD).expect(500, 'Test Error');
|
||||
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
||||
expect(downloadBuildArtifactSpy).toHaveBeenCalled();
|
||||
@ -536,7 +533,7 @@ describe('PreviewServerFactory', () => {
|
||||
});
|
||||
|
||||
it('should fail if creating the preview build fails', async () => {
|
||||
createBuildSpy.and.callFake(() => Promise.reject('Test Error'));
|
||||
createBuildSpy.and.rejectWith('Test Error');
|
||||
await agent.post(URL).send(BASIC_PAYLOAD).expect(500, 'Test Error');
|
||||
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
||||
expect(downloadBuildArtifactSpy).toHaveBeenCalled();
|
||||
@ -605,7 +602,7 @@ describe('PreviewServerFactory', () => {
|
||||
|
||||
|
||||
it('should propagate errors from BuildVerifier', async () => {
|
||||
bvGetPrIsTrustedSpy.and.callFake(() => Promise.reject('Test'));
|
||||
bvGetPrIsTrustedSpy.and.rejectWith('Test');
|
||||
|
||||
await createRequest(+pr).expect(500, 'Test');
|
||||
|
||||
@ -615,7 +612,9 @@ describe('PreviewServerFactory', () => {
|
||||
|
||||
|
||||
it('should call \'BuildCreator#updatePrVisibility()\' with the correct arguments', async () => {
|
||||
bvGetPrIsTrustedSpy.and.callFake((pr2: number) => Promise.resolve(pr2 === 42));
|
||||
bvGetPrIsTrustedSpy.
|
||||
withArgs(24).and.resolveTo(false).
|
||||
withArgs(42).and.resolveTo(true);
|
||||
|
||||
await createRequest(24);
|
||||
expect(bcUpdatePrVisibilitySpy).toHaveBeenCalledWith(24, false);
|
||||
@ -626,7 +625,7 @@ describe('PreviewServerFactory', () => {
|
||||
|
||||
|
||||
it('should propagate errors from BuildCreator', async () => {
|
||||
bcUpdatePrVisibilitySpy.and.callFake(() => Promise.reject('Test'));
|
||||
bcUpdatePrVisibilitySpy.and.rejectWith('Test');
|
||||
await createRequest(+pr).expect(500, 'Test');
|
||||
});
|
||||
|
||||
@ -634,7 +633,9 @@ describe('PreviewServerFactory', () => {
|
||||
describe('on success', () => {
|
||||
|
||||
it('should respond with 200 (action: undefined)', async () => {
|
||||
bvGetPrIsTrustedSpy.and.returnValues(Promise.resolve(true), Promise.resolve(false));
|
||||
bvGetPrIsTrustedSpy.
|
||||
withArgs(2).and.resolveTo(false).
|
||||
withArgs(4).and.resolveTo(true);
|
||||
|
||||
const reqs = [4, 2].map(num => createRequest(num).expect(200, http.STATUS_CODES[200]));
|
||||
await Promise.all(reqs);
|
||||
@ -642,7 +643,9 @@ describe('PreviewServerFactory', () => {
|
||||
|
||||
|
||||
it('should respond with 200 (action: labeled)', async () => {
|
||||
bvGetPrIsTrustedSpy.and.returnValues(Promise.resolve(true), Promise.resolve(false));
|
||||
bvGetPrIsTrustedSpy.
|
||||
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]));
|
||||
await Promise.all(reqs);
|
||||
@ -650,7 +653,9 @@ describe('PreviewServerFactory', () => {
|
||||
|
||||
|
||||
it('should respond with 200 (action: unlabeled)', async () => {
|
||||
bvGetPrIsTrustedSpy.and.returnValues(Promise.resolve(true), Promise.resolve(false));
|
||||
bvGetPrIsTrustedSpy.
|
||||
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]));
|
||||
await Promise.all(reqs);
|
||||
|
@ -39,7 +39,7 @@ describe('preview-server/utils', () => {
|
||||
throwRequestError(505, 'ERROR MESSAGE', request);
|
||||
} catch (error) {
|
||||
caught = true;
|
||||
expect(error).toEqual(jasmine.any(PreviewServerError));
|
||||
expect(error).toBeInstanceOf(PreviewServerError);
|
||||
expect(error.status).toEqual(505);
|
||||
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,10 +8,32 @@ exitCode=0
|
||||
|
||||
|
||||
# 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 {
|
||||
local lastExitCode=$?
|
||||
|
||||
echo "$1: $([[ $lastExitCode -eq 0 ]] && echo OK || echo NOT OK)"
|
||||
[[ $lastExitCode -eq 0 ]] || exitCode=1
|
||||
|
||||
return $lastExitCode
|
||||
}
|
||||
|
||||
|
||||
@ -28,6 +50,16 @@ for s in ${services[@]}; do
|
||||
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
|
||||
origins=(
|
||||
http://$AIO_PREVIEW_SERVER_HOSTNAME:$AIO_PREVIEW_SERVER_PORT
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
## Create `aio-builds` persistent disk (if not already exists)
|
||||
- Follow instructions [here](https://cloud.google.com/compute/docs/disks/add-persistent-disk#create_disk).
|
||||
- `sudo mkfs.ext4 -F -E lazy_itable_init=0,lazy_journal_init=0,discard /dev/disk/by-id/google-aio-builds`
|
||||
- `sudo mkfs.ext4 -m 0 -E lazy_itable_init=0,lazy_journal_init=0,discard /dev/disk/by-id/google-aio-builds`
|
||||
|
||||
|
||||
## Mount disk
|
||||
@ -14,7 +14,7 @@
|
||||
|
||||
## Mount disk on boot
|
||||
- Run:
|
||||
```
|
||||
```sh
|
||||
echo UUID=`sudo blkid -s UUID -o value /dev/disk/by-id/google-aio-builds` \
|
||||
/mnt/disks/aio-builds ext4 discard,defaults,nofail 0 2 | sudo tee -a /etc/fstab
|
||||
/mnt/disks/aio-builds ext4 defaults,discard,nofail 0 2 | sudo tee -a /etc/fstab
|
||||
```
|
||||
|
@ -1,10 +1,12 @@
|
||||
# VM setup - Create docker image
|
||||
|
||||
|
||||
## Install node and yarn
|
||||
- Install [nvm](https://github.com/creationix/nvm#installation).
|
||||
- Install node.js: `nvm install 8`
|
||||
- Install yarn: `npm -g install yarn`
|
||||
## Install git, Node.js and yarn
|
||||
- `sudo apt-get update`
|
||||
- `sudo apt-get install -y git`
|
||||
- Install [nvm](https://github.com/nvm-sh/nvm#installing-and-updating).
|
||||
- Install Node.js: `nvm install 12`
|
||||
- Install yarn: `npm install --global yarn`
|
||||
|
||||
|
||||
## Checkout repository
|
||||
@ -26,7 +28,7 @@ The following commands would create a docker image from GitHub repo `foo/bar` to
|
||||
|
||||
- `git clone https://github.com/foo/bar.git foobar`
|
||||
- Run:
|
||||
```
|
||||
```sh
|
||||
./foobar/aio-builds-setup/scripts/create-image.sh foobar-builds \
|
||||
--build-arg AIO_REPO_SLUG=foo/bar \
|
||||
--build-arg AIO_DOMAIN_NAME=foobar-builds.io \
|
||||
|
@ -3,24 +3,17 @@
|
||||
|
||||
## Install docker
|
||||
|
||||
_Debian (jessie):_
|
||||
- `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`
|
||||
Official installation instructions: https://docs.docker.com/engine/install
|
||||
Example:
|
||||
|
||||
_Ubuntu (16.04):_
|
||||
_Debian (buster):_
|
||||
- `sudo apt-get update`
|
||||
- `sudo apt-get install -y curl git linux-image-extra-$(uname -r) linux-image-extra-virtual`
|
||||
- `sudo apt-get install -y apt-transport-https ca-certificates`
|
||||
- `curl -fsSL https://yum.dockerproject.org/gpg | sudo apt-key add -`
|
||||
- `apt-key fingerprint 58118E89F3A912897C070ADBF76221572C52609D`
|
||||
- `sudo add-apt-repository "deb https://apt.dockerproject.org/repo/ ubuntu-$(lsb_release -cs) main"`
|
||||
- `sudo apt-get install -y apt-transport-https ca-certificates curl gnupg-agent software-properties-common`
|
||||
- `curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -`
|
||||
- `sudo apt-key fingerprint 0EBFCD88`
|
||||
- `sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"`
|
||||
- `sudo apt-get update`
|
||||
- `sudo apt-get -y install docker-engine`
|
||||
- `sudo apt-get -y install docker-ce docker-ce-cli containerd.io`
|
||||
|
||||
|
||||
## Start the docker
|
||||
|
@ -15,9 +15,9 @@ The script assumes that the preview server source code is in the repository's
|
||||
`aio/aio-builds-setup/` directory and expects the following inputs:
|
||||
|
||||
- **$1**: `HOST_REPO_DIR`
|
||||
- **$2**: `HOST_LOCALCERTS_DIR`
|
||||
- **$3**: `HOST_SECRETS_DIR`
|
||||
- **$4**: `HOST_BUILDS_DIR`
|
||||
- **$2**: `HOST_SECRETS_DIR`
|
||||
- **$3**: `HOST_BUILDS_DIR`
|
||||
- **$4**: `HOST_LOCALCERTS_DIR`
|
||||
- **$5**: `HOST_LOGS_DIR`
|
||||
|
||||
See [here](vm-setup--create-host-dirs-and-files.md) for more info on what each input directory is
|
||||
@ -31,22 +31,22 @@ used for.
|
||||
## Run the script manually
|
||||
You may choose to manually run the script, when necessary. Example:
|
||||
|
||||
```
|
||||
```sh
|
||||
update-preview-server.sh \
|
||||
/path/to/repo \
|
||||
/path/to/localcerts \
|
||||
/path/to/secrets \
|
||||
/path/to/builds \
|
||||
/path/to/localcerts \
|
||||
/path/to/logs
|
||||
```
|
||||
|
||||
|
||||
## Run the script automatically
|
||||
You may choose to automatically trigger the script, e.g. using a cronjob. For example, the following
|
||||
cronjob entry would run the script every hour and update the preview server (assuming the user has
|
||||
the necessary permissions):
|
||||
cronjob entry would run the script every 30 minutes, update the preview server (if necessary) and
|
||||
log its output to `update-preview-server.log` (assuming the user has the necessary permissions):
|
||||
|
||||
```
|
||||
# Periodically check for changes and update the preview server (if necessary)
|
||||
*/30 * * * * /path/to/update-preview-server.sh /path/to/repo /path/to/localcerts /path/to/secrets /path/to/builds /path/to/logs
|
||||
*/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
|
||||
```
|
||||
|
@ -7,9 +7,9 @@ echo -e "\n\n[`date`] - Updating the preview server..."
|
||||
|
||||
# Input
|
||||
readonly HOST_REPO_DIR=$1
|
||||
readonly HOST_LOCALCERTS_DIR=$2
|
||||
readonly HOST_SECRETS_DIR=$3
|
||||
readonly HOST_BUILDS_DIR=$4
|
||||
readonly HOST_SECRETS_DIR=$2
|
||||
readonly HOST_BUILDS_DIR=$3
|
||||
readonly HOST_LOCALCERTS_DIR=$4
|
||||
readonly HOST_LOGS_DIR=$5
|
||||
|
||||
# Constants
|
||||
@ -60,9 +60,9 @@ readonly CONTAINER_NAME=aio
|
||||
--publish 80:80 \
|
||||
--publish 443:443 \
|
||||
--restart unless-stopped \
|
||||
--volume $HOST_LOCALCERTS_DIR:/etc/ssl/localcerts:ro \
|
||||
--volume $HOST_SECRETS_DIR:/aio-secrets:ro \
|
||||
--volume $HOST_BUILDS_DIR:/var/www/aio-builds \
|
||||
--volume $HOST_LOCALCERTS_DIR:/etc/ssl/localcerts:ro \
|
||||
--volume $HOST_LOGS_DIR:/var/log/aio \
|
||||
"$LATEST_IMAGE_NAME"
|
||||
|
||||
|
@ -5,7 +5,8 @@
|
||||
"packageManager": "yarn",
|
||||
"warnings": {
|
||||
"typescriptMismatch": false
|
||||
}
|
||||
},
|
||||
"analytics": false
|
||||
},
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
@ -59,7 +60,13 @@
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
"scripts": [],
|
||||
"budgets": [
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "6kb"
|
||||
}
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
"fast": {
|
||||
@ -91,6 +98,9 @@
|
||||
}
|
||||
],
|
||||
"serviceWorker": true
|
||||
},
|
||||
"ci": {
|
||||
"progress": false
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -113,7 +123,7 @@
|
||||
"browserTarget": "site:build:archive"
|
||||
},
|
||||
"ci": {
|
||||
"progress": false
|
||||
"browserTarget": "site:build:ci"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
3
aio/content/examples/.gitignore
vendored
3
aio/content/examples/.gitignore
vendored
@ -82,9 +82,6 @@ upgrade-phonecat-2-hybrid/aot/**/*
|
||||
# styleguide
|
||||
!styleguide/src/systemjs.custom.js
|
||||
|
||||
# universal
|
||||
!universal/webpack.server.config.js
|
||||
|
||||
# stackblitz
|
||||
*stackblitz.no-link.html
|
||||
|
||||
|
21
aio/content/examples/accessibility/e2e/src/app.e2e-spec.ts
Normal file
21
aio/content/examples/accessibility/e2e/src/app.e2e-spec.ts
Normal file
@ -0,0 +1,21 @@
|
||||
'use strict'; // necessary for es6 output in node
|
||||
|
||||
import { browser, element, by } from 'protractor';
|
||||
|
||||
describe('Accessibility example e2e tests', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
browser.get('');
|
||||
});
|
||||
|
||||
it('should display Accessibility Example', function () {
|
||||
expect(element(by.css('h1')).getText()).toEqual('Accessibility Example');
|
||||
});
|
||||
|
||||
it('should take a number and change progressbar width', function () {
|
||||
element(by.css('input')).sendKeys('16');
|
||||
expect(element(by.css('input')).getAttribute('value')).toEqual('016');
|
||||
expect(element(by.css('app-example-progressbar div')).getCssValue('width')).toBe('48px');
|
||||
});
|
||||
|
||||
});
|
13
aio/content/examples/accessibility/src/app/app.component.html
Executable file
13
aio/content/examples/accessibility/src/app/app.component.html
Executable file
@ -0,0 +1,13 @@
|
||||
<h1>Accessibility Example</h1>
|
||||
<!-- #docregion template -->
|
||||
<label>
|
||||
Enter an example progress value
|
||||
<input type="number" min="0" max="100"
|
||||
[value]="progress" (input)="progress = $event.target.value">
|
||||
</label>
|
||||
|
||||
<!-- The user of the progressbar sets an aria-label to communicate what the progress means. -->
|
||||
<app-example-progressbar [value]="progress" aria-label="Example of a progress bar">
|
||||
</app-example-progressbar>
|
||||
<!-- #enddocregion template -->
|
||||
|
10
aio/content/examples/accessibility/src/app/app.component.ts
Executable file
10
aio/content/examples/accessibility/src/app/app.component.ts
Executable file
@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: [ './app.component.css' ]
|
||||
})
|
||||
export class AppComponent {
|
||||
progress = 0;
|
||||
}
|
12
aio/content/examples/accessibility/src/app/app.module.ts
Executable file
12
aio/content/examples/accessibility/src/app/app.module.ts
Executable file
@ -0,0 +1,12 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { ExampleProgressbarComponent } from './progress-bar.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [ BrowserModule ],
|
||||
declarations: [ AppComponent, ExampleProgressbarComponent ],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
export class AppModule { }
|
12
aio/content/examples/accessibility/src/app/progress-bar.component.css
Executable file
12
aio/content/examples/accessibility/src/app/progress-bar.component.css
Executable file
@ -0,0 +1,12 @@
|
||||
:host {
|
||||
display: block;
|
||||
width: 300px;
|
||||
height: 25px;
|
||||
border: 1px solid black;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.bar {
|
||||
background: blue;
|
||||
height: 100%;
|
||||
}
|
28
aio/content/examples/accessibility/src/app/progress-bar.component.ts
Executable file
28
aio/content/examples/accessibility/src/app/progress-bar.component.ts
Executable file
@ -0,0 +1,28 @@
|
||||
// #docregion progressbar-component
|
||||
import { Component, Input } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Example progressbar component.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'app-example-progressbar',
|
||||
template: `<div class="bar" [style.width.%]="value"></div>`,
|
||||
styleUrls: ['./progress-bar.component.css'],
|
||||
host: {
|
||||
// Sets the role for this component to "progressbar"
|
||||
role: 'progressbar',
|
||||
|
||||
// Sets the minimum and maximum values for the progressbar role.
|
||||
'aria-valuemin': '0',
|
||||
'aria-valuemax': '100',
|
||||
|
||||
// Binding that updates the current value of the progressbar.
|
||||
'[attr.aria-valuenow]': 'value',
|
||||
}
|
||||
})
|
||||
export class ExampleProgressbarComponent {
|
||||
/** Current value of the progressbar. */
|
||||
@Input() value = 0;
|
||||
}
|
||||
|
||||
// #enddocregion progressbar-component
|
13
aio/content/examples/accessibility/src/index.html
Normal file
13
aio/content/examples/accessibility/src/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Accessibility Example</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<app-root>Loading...</app-root>
|
||||
</body>
|
||||
</html>
|
11
aio/content/examples/accessibility/src/main.ts
Normal file
11
aio/content/examples/accessibility/src/main.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { environment } from './environments/environment';
|
||||
|
||||
if (environment.production) {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
9
aio/content/examples/accessibility/stackblitz.json
Normal file
9
aio/content/examples/accessibility/stackblitz.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"description": "Accessibility",
|
||||
"files": [
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js",
|
||||
"!**/*.[1,2].*"
|
||||
],
|
||||
"tags": ["Accessibility"]
|
||||
}
|
@ -89,14 +89,14 @@ describe('Animation Tests', () => {
|
||||
sleepFor(2000);
|
||||
});
|
||||
|
||||
it('should be inactive with an orange background', async () => {
|
||||
it('should be inactive with a blue background', async () => {
|
||||
const toggleButton = statusSlider.getToggleButton();
|
||||
const container = statusSlider.getComponentContainer();
|
||||
let text = await container.getText();
|
||||
|
||||
if (text === 'Active') {
|
||||
await toggleButton.click();
|
||||
await browser.wait(async () => await container.getCssValue('backgroundColor') === inactiveColor, 2000);
|
||||
await browser.wait(async () => await container.getCssValue('backgroundColor') === inactiveColor, 3000);
|
||||
}
|
||||
|
||||
text = await container.getText();
|
||||
@ -106,14 +106,14 @@ describe('Animation Tests', () => {
|
||||
expect(bgColor).toBe(inactiveColor);
|
||||
});
|
||||
|
||||
it('should be active with a blue background', async () => {
|
||||
it('should be active with an orange background', async () => {
|
||||
const toggleButton = statusSlider.getToggleButton();
|
||||
const container = statusSlider.getComponentContainer();
|
||||
let text = await container.getText();
|
||||
|
||||
if (text === 'Inactive') {
|
||||
await toggleButton.click();
|
||||
await browser.wait(async () => await container.getCssValue('backgroundColor') === activeColor, 2000);
|
||||
await browser.wait(async () => await container.getCssValue('backgroundColor') === activeColor, 3000);
|
||||
}
|
||||
|
||||
text = await container.getText();
|
||||
|
@ -12,6 +12,8 @@ Toggle All Animations <input type="checkbox" [checked]="!animationsDisabled" (cl
|
||||
<a id="auto" routerLink="/auto" routerLinkActive="active">Auto Calculation</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="insert-remove" routerLink="/insert-remove" routerLinkActive="active">Insert/Remove</a>
|
||||
|
||||
</nav>
|
||||
|
||||
<!-- #docregion route-animations-outlet -->
|
||||
|
@ -35,6 +35,7 @@ import { InsertRemoveComponent } from './insert-remove.component';
|
||||
{ path: 'hero-groups', component: HeroListGroupPageComponent },
|
||||
{ path: 'enter-leave', component: HeroListEnterLeavePageComponent },
|
||||
{ path: 'auto', component: HeroListAutoCalcPageComponent },
|
||||
{ path: 'insert-remove', component: InsertRemoveComponent},
|
||||
{ path: 'home', component: HomeComponent, data: {animation: 'HomePage'} },
|
||||
{ path: 'about', component: AboutComponent, data: {animation: 'AboutPage'} },
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
export class Hero {
|
||||
export interface Hero {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
<!-- #docplaster -->
|
||||
|
||||
<h2>Insert/Remove</h2>
|
||||
|
||||
<nav>
|
||||
<button (click)="toggle()">Toggle Insert/Remove</button>
|
||||
</nav>
|
||||
|
@ -9,10 +9,10 @@ import { trigger, transition, animate, style } from '@angular/animations';
|
||||
trigger('myInsertRemoveTrigger', [
|
||||
transition(':enter', [
|
||||
style({ opacity: 0 }),
|
||||
animate('5s', style({ opacity: 1 })),
|
||||
animate('100ms', style({ opacity: 1 })),
|
||||
]),
|
||||
transition(':leave', [
|
||||
animate('5s', style({ opacity: 0 }))
|
||||
animate('100ms', style({ opacity: 0 }))
|
||||
])
|
||||
]),
|
||||
// #enddocregion enter-leave-trigger
|
||||
|
@ -36,9 +36,6 @@ import { transAnimation } from './animations';
|
||||
})
|
||||
])
|
||||
])
|
||||
// #docregion runtime
|
||||
],
|
||||
// #enddocregion runtime
|
||||
// #enddocregion reusable
|
||||
templateUrl: 'open-close.component.html',
|
||||
styleUrls: ['open-close.component.css']
|
||||
|
@ -5,5 +5,6 @@
|
||||
"!**/*.js",
|
||||
"!**/*.[1,2,3].*"
|
||||
],
|
||||
"file": "src/app/app.component.ts",
|
||||
"tags": ["animations"]
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import { protractor, browser, element, by, ElementFinder } from 'protractor';
|
||||
|
||||
const nameSuffix = 'X';
|
||||
|
||||
class Hero {
|
||||
interface Hero {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
@ -5,5 +5,5 @@
|
||||
"!**/*.js",
|
||||
"!**/*.[1].*"
|
||||
],
|
||||
"file": "src/app/app.module.ts"
|
||||
"file": "src/app/hero-list.component.html"
|
||||
}
|
||||
|
@ -25,23 +25,12 @@ describe('Attribute binding example', function () {
|
||||
});
|
||||
|
||||
it('should display a blue div with a red border', function () {
|
||||
expect(element.all(by.css('div')).get(4).getCssValue('border')).toEqual('2px solid rgb(212, 30, 46)');
|
||||
expect(element.all(by.css('div')).get(1).getCssValue('border')).toEqual('2px solid rgb(212, 30, 46)');
|
||||
});
|
||||
|
||||
it('should display a div with replaced classes', function () {
|
||||
expect(element.all(by.css('div')).get(5).getAttribute('class')).toEqual('new-class');
|
||||
});
|
||||
|
||||
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');
|
||||
it('should display a div with many classes', function () {
|
||||
expect(element.all(by.css('div')).get(1).getAttribute('class')).toContain('special');
|
||||
expect(element.all(by.css('div')).get(1).getAttribute('class')).toContain('clearance');
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -27,39 +27,41 @@
|
||||
|
||||
<hr />
|
||||
|
||||
<h2>Class binding</h2>
|
||||
<h2>Styling precedence</h2>
|
||||
|
||||
<!-- #docregion is-special -->
|
||||
<h3>toggle the "special" class on/off with a property:</h3>
|
||||
<div [class.special]="isSpecial">The class binding is special.</div>
|
||||
<!-- #docregion basic-specificity -->
|
||||
<h3>Basic specificity</h3>
|
||||
|
||||
<h3>binding to class.special overrides the class attribute:</h3>
|
||||
<div class="special" [class.special]="!isSpecial">This one is not so special.</div>
|
||||
<!-- The `class.special` binding will override any value for the `special` class in `classExpr`. -->
|
||||
<div [class.special]="isSpecial" [class]="classExpr">Some text.</div>
|
||||
|
||||
<h3>Using the bind- syntax:</h3>
|
||||
<div bind-class.special="isSpecial">This class binding is special too.</div>
|
||||
<!-- #enddocregion is-special -->
|
||||
<!-- The `style.color` binding will override any value for the `color` property in `styleExpr`. -->
|
||||
<div [style.color]="color" [style]="styleExpr">Some text.</div>
|
||||
<!-- #enddocregion basic-specificity -->
|
||||
|
||||
<!-- #docregion add-class -->
|
||||
<h3>Add a class:</h3>
|
||||
<div class="item clearance special" [class.item-clearance]="itemClearance">Add another class</div>
|
||||
<!-- #enddocregion add-class -->
|
||||
<!-- #docregion source-specificity -->
|
||||
<h3>Source specificity</h3>
|
||||
|
||||
<!-- #docregion class-override -->
|
||||
<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 `class.special` template binding will override any host binding to the `special` class set by `dirWithClassBinding` or `comp-with-host-binding`.-->
|
||||
<comp-with-host-binding [class.special]="isSpecial" dirWithClassBinding>Some text.</comp-with-host-binding>
|
||||
|
||||
<hr />
|
||||
<!-- The `style.color` template binding will override any host binding to the `color` property set by `dirWithStyleBinding` or `comp-with-host-binding`. -->
|
||||
<comp-with-host-binding [style.color]="color" dirWithStyleBinding>Some text.</comp-with-host-binding>
|
||||
<!-- #enddocregion source-specificity -->
|
||||
|
||||
<h2>Style binding</h2>
|
||||
<!-- #docregion dynamic-priority -->
|
||||
<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-->
|
||||
|
@ -1,27 +0,0 @@
|
||||
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 {
|
||||
actionName = 'Go for it';
|
||||
isSpecial = true;
|
||||
itemClearance = true;
|
||||
resetClasses = 'new-class';
|
||||
canSave = true;
|
||||
|
||||
classExpr = 'special clearance';
|
||||
styleExpr = 'color: red';
|
||||
color = 'blue';
|
||||
}
|
||||
|
@ -3,11 +3,13 @@ import { NgModule } from '@angular/core';
|
||||
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { CompWithHostBindingComponent } from './comp-with-host-binding.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent
|
||||
AppComponent,
|
||||
CompWithHostBindingComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule
|
||||
|
@ -0,0 +1,16 @@
|
||||
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,7 +4,6 @@
|
||||
<meta charset="utf-8">
|
||||
<title>AttributeBinding</title>
|
||||
<base href="/">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
</head>
|
||||
|
@ -5,5 +5,6 @@
|
||||
"!**/*.js",
|
||||
"!**/*.[1,2,3].*"
|
||||
],
|
||||
"file": "src/app/highlight.directive.ts",
|
||||
"tags": ["attribute", "directive"]
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user