##// END OF EJS Templates
branching: merge stable into default
Raphaël Gomès -
r50367:f59e49f6 merge default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,233 +1,235 b''
1 1 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0 iD8DBQBEYmO2ywK+sNU5EO8RAnaYAKCO7x15xUn5mnhqWNXqk/ehlhRt2QCfRDfY0LrUq2q4oK/KypuJYPHgq1A=
2 2 2be3001847cb18a23c403439d9e7d0ace30804e9 0 iD8DBQBExUbjywK+sNU5EO8RAhzxAKCtyHAQUzcTSZTqlfJ0by6vhREwWQCghaQFHfkfN0l9/40EowNhuMOKnJk=
3 3 36a957364b1b89c150f2d0e60a99befe0ee08bd3 0 iD8DBQBFfL2QywK+sNU5EO8RAjYFAKCoGlaWRTeMsjdmxAjUYx6diZxOBwCfY6IpBYsKvPTwB3oktnPt5Rmrlys=
4 4 27230c29bfec36d5540fbe1c976810aefecfd1d2 0 iD8DBQBFheweywK+sNU5EO8RAt7VAKCrqJQWT2/uo2RWf0ZI4bLp6v82jACgjrMdsaTbxRsypcmEsdPhlG6/8F4=
5 5 fb4b6d5fe100b0886f8bc3d6731ec0e5ed5c4694 0 iD8DBQBGgHicywK+sNU5EO8RAgNxAJ0VG8ixAaeudx4sZbhngI1syu49HQCeNUJQfWBgA8bkJ2pvsFpNxwYaX3I=
6 6 23889160905a1b09fffe1c07378e9fc1827606eb 0 iD8DBQBHGTzoywK+sNU5EO8RAr/UAJ0Y8s4jQtzgS+G9vM8z6CWBThZ8fwCcCT5XDj2XwxKkz/0s6UELwjsO3LU=
7 7 bae2e9c838e90a393bae3973a7850280413e091a 0 iD8DBQBH6DO5ywK+sNU5EO8RAsfrAJ0e4r9c9GF/MJsM7Xjd3NesLRC3+ACffj6+6HXdZf8cswAoFPO+DY00oD0=
8 8 d5cbbe2c49cee22a9fbeb9ea41daa0ac4e26b846 0 iD8DBQBINdwsywK+sNU5EO8RAjIUAKCPmlFJSpsPAAUKF+iNHAwVnwmzeQCdEXrL27CWclXuUKdbQC8De7LICtE=
9 9 d2375bbee6d47e62ba8e415c86e83a465dc4dce9 0 iD8DBQBIo1wpywK+sNU5EO8RAmRNAJ94x3OFt6blbqu/yBoypm/AJ44fuACfUaldXcV5z9tht97hSp22DVTEPGc=
10 10 2a67430f92f15ea5159c26b09ec4839a0c549a26 0 iEYEABECAAYFAkk1hykACgkQywK+sNU5EO85QACeNJNUanjc2tl4wUoPHNuv+lSj0ZMAoIm93wSTc/feyYnO2YCaQ1iyd9Nu
11 11 3773e510d433969e277b1863c317b674cbee2065 0 iEYEABECAAYFAklNbbAACgkQywK+sNU5EO8o+gCfeb2/lfIJZMvyDA1m+G1CsBAxfFsAoIa6iAMG8SBY7hW1Q85Yf/LXEvaE
12 12 11a4eb81fb4f4742451591489e2797dc47903277 0 iEYEABECAAYFAklcAnsACgkQywK+sNU5EO+uXwCbBVHNNsLy1g7BlAyQJwadYVyHOXoAoKvtAVO71+bv7EbVoukwTzT+P4Sx
13 13 11efa41037e280d08cfb07c09ad485df30fb0ea8 0 iEYEABECAAYFAkmvJRQACgkQywK+sNU5EO9XZwCeLMgDgPSMWMm6vgjL4lDs2pEc5+0AnRxfiFbpbBfuEFTqKz9nbzeyoBlx
14 14 02981000012e3adf40c4849bd7b3d5618f9ce82d 0 iEYEABECAAYFAknEH3wACgkQywK+sNU5EO+uXwCeI+LbLMmhjU1lKSfU3UWJHjjUC7oAoIZLvYDGOL/tNZFUuatc3RnZ2eje
15 15 196d40e7c885fa6e95f89134809b3ec7bdbca34b 0 iEYEABECAAYFAkpL2X4ACgkQywK+sNU5EO9FOwCfXJycjyKJXsvQqKkHrglwOQhEKS4An36GfKzptfN8b1qNc3+ya/5c2WOM
16 16 3ef6c14a1e8e83a31226f5881b7fe6095bbfa6f6 0 iEYEABECAAYFAkpopLIACgkQywK+sNU5EO8QSgCfZ0ztsd071rOa2lhmp9Fyue/WoI0AoLTei80/xrhRlB8L/rZEf2KBl8dA
17 17 31ec469f9b556f11819937cf68ee53f2be927ebf 0 iEYEABECAAYFAksBuxAACgkQywK+sNU5EO+mBwCfagB+A0txzWZ6dRpug3LEoK7Z1QsAoKpbk8vsLjv6/oRDicSk/qBu33+m
18 18 439d7ea6fe3aa4ab9ec274a68846779153789de9 0 iEYEABECAAYFAksVw0kACgkQywK+sNU5EO/oZwCfdfBEkgp38xq6wN2F4nj+SzofrJIAnjmxt04vaJSeOOeHylHvk6lzuQsw
19 19 296a0b14a68621f6990c54fdba0083f6f20935bf 0 iEYEABECAAYFAks+jCoACgkQywK+sNU5EO9J8wCeMUGF9E/gS2UBsqIz56WS4HMPRPUAoI5J95mwEIK8Clrl7qFRidNI6APq
20 20 4aa619c4c2c09907034d9824ebb1dd0e878206eb 0 iEYEABECAAYFAktm9IsACgkQywK+sNU5EO9XGgCgk4HclRQhexEtooPE5GcUCdB6M8EAn2ptOhMVbIoO+JncA+tNACPFXh0O
21 21 ff2704a8ded37fbebd8b6eb5ec733731d725da8a 0 iEYEABECAAYFAkuRoSQACgkQywK+sNU5EO//3QCeJDc5r2uFyFCtAlpSA27DEE5rrxAAn2FSwTy9fhrB3QAdDQlwkEZcQzDh
22 22 2b01dab594167bc0dd33331dbaa6dca3dca1b3aa 0 iEYEABECAAYFAku1IwIACgkQywK+sNU5EO9MjgCdHLVwkTZlNHxhcznZKBL1rjN+J7cAoLLWi9LTL6f/TgBaPSKOy1ublbaW
23 23 39f725929f0c48c5fb3b90c071fc3066012456ca 0 iEYEABECAAYFAkvclvsACgkQywK+sNU5EO9FSwCeL9i5x8ALW/LE5+lCX6MFEAe4MhwAn1ev5o6SX6GrNdDfKweiemfO2VBk
24 24 fdcf80f26604f233dc4d8f0a5ef9d7470e317e8a 0 iEYEABECAAYFAkvsKTkACgkQywK+sNU5EO9qEACgiSiRGvTG2vXGJ65tUSOIYihTuFAAnRzRIqEVSw8M8/RGeUXRps0IzaCO
25 25 24fe2629c6fd0c74c90bd066e77387c2b02e8437 0 iEYEABECAAYFAkwFLRsACgkQywK+sNU5EO+pJACgp13tPI+pbwKZV+LeMjcQ4H6tCZYAoJebzhd6a8yYx6qiwpJxA9BXZNXy
26 26 f786fc4b8764cd2a5526d259cf2f94d8a66924d9 0 iEYEABECAAYFAkwsyxcACgkQywK+sNU5EO+crACfUpNAF57PmClkSri9nJcBjb2goN4AniPCNaKvnki7TnUsi1u2oxltpKKL
27 27 bf1774d95bde614af3956d92b20e2a0c68c5fec7 0 iEYEABECAAYFAkxVwccACgkQywK+sNU5EO+oFQCeJzwZ+we1fIIyBGCddHceOUAN++cAnjvT6A8ZWW0zV21NXIFF1qQmjxJd
28 28 c00f03a4982e467fb6b6bd45908767db6df4771d 0 iEYEABECAAYFAkxXDqsACgkQywK+sNU5EO/GJACfT9Rz4hZOxPQEs91JwtmfjevO84gAmwSmtfo5mmWSm8gtTUebCcdTv0Kf
29 29 ff5cec76b1c5b6be9c3bb923aae8c3c6d079d6b9 0 iD8DBQBMdo+qywK+sNU5EO8RAqQpAJ975BL2CCAiWMz9SXthNQ9xG181IwCgp4O+KViHPkufZVFn2aTKMNvcr1A=
30 30 93d8bff78c96fe7e33237b257558ee97290048a4 0 iD8DBQBMpfvdywK+sNU5EO8RAsxVAJ0UaL1XB51C76JUBhafc9GBefuMxwCdEWkTOzwvE0SarJBe9i008jhbqW4=
31 31 333421b9e0f96c7bc788e5667c146a58a9440a55 0 iD8DBQBMz0HOywK+sNU5EO8RAlsEAJ0USh6yOG7OrWkADGunVt9QimBQnwCbBqeMnKgSbwEw8jZwE3Iz1mdrYlo=
32 32 4438875ec01bd0fc32be92b0872eb6daeed4d44f 0 iD8DBQBM4WYUywK+sNU5EO8RAhCVAJ0dJswachwFAHALmk1x0RJehxzqPQCbBNskP9n/X689jB+btNTZTyKU/fw=
33 33 6aff4f144ad356311318b0011df0bb21f2c97429 0 iD8DBQBM9uxXywK+sNU5EO8RAv+4AKCDj4qKP16GdPaq1tP6BUwpM/M1OACfRyzLPp/qiiN8xJTWoWYSe/XjJug=
34 34 e3bf16703e2601de99e563cdb3a5d50b64e6d320 0 iD8DBQBNH8WqywK+sNU5EO8RAiQTAJ9sBO+TeiGro4si77VVaQaA6jcRUgCfSA28dBbjj0oFoQwvPoZjANiZBH8=
35 35 a6c855c32ea081da3c3b8ff628f1847ff271482f 0 iD8DBQBNSJJ+ywK+sNU5EO8RAoJaAKCweDEF70fu+r1Zn7pYDXdlk5RuSgCeO9gK/eit8Lin/1n3pO7aYguFLok=
36 36 2b2155623ee2559caf288fd333f30475966c4525 0 iD8DBQBNSJeBywK+sNU5EO8RAm1KAJ4hW9Cm9nHaaGJguchBaPLlAr+O3wCgqgmMok8bdAS06N6PL60PSTM//Gg=
37 37 2616325766e3504c8ae7c84bd15ee610901fe91d 0 iD8DBQBNbWy9ywK+sNU5EO8RAlWCAJ4mW8HbzjJj9GpK98muX7k+7EvEHwCfaTLbC/DH3QEsZBhEP+M8tzL6RU4=
38 38 aa1f3be38ab127280761889d2dca906ca465b5f4 0 iD8DBQBNeQq7ywK+sNU5EO8RAlEOAJ4tlEDdetE9lKfjGgjbkcR8PrC3egCfXCfF3qNVvU/2YYjpgvRwevjvDy0=
39 39 b032bec2c0a651ca0ddecb65714bfe6770f67d70 0 iD8DBQBNlg5kywK+sNU5EO8RAnGEAJ9gmEx6MfaR4XcG2m/93vwtfyzs3gCgltzx8/YdHPwqDwRX/WbpYgi33is=
40 40 3cb1e95676ad089596bd81d0937cad37d6e3b7fb 0 iD8DBQBNvTy4ywK+sNU5EO8RAmp8AJ9QnxK4jTJ7G722MyeBxf0UXEdGwACgtlM7BKtNQfbEH/fOW5y+45W88VI=
41 41 733af5d9f6b22387913e1d11350fb8cb7c1487dd 0 iD8DBQBN5q/8ywK+sNU5EO8RArRGAKCNGT94GKIYtSuwZ57z1sQbcw6uLACfffpbMV4NAPMl8womAwg+7ZPKnIU=
42 42 de9eb6b1da4fc522b1cab16d86ca166204c24f25 0 iD8DBQBODhfhywK+sNU5EO8RAr2+AJ4ugbAj8ae8/K0bYZzx3sascIAg1QCeK3b+zbbVVqd3b7CDpwFnaX8kTd4=
43 43 4a43e23b8c55b4566b8200bf69fe2158485a2634 0 iD8DBQBONzIMywK+sNU5EO8RAj5SAJ0aPS3+JHnyI6bHB2Fl0LImbDmagwCdGbDLp1S7TFobxXudOH49bX45Iik=
44 44 d629f1e89021103f1753addcef6b310e4435b184 0 iD8DBQBOWAsBywK+sNU5EO8RAht4AJwJl9oNFopuGkj5m8aKuf7bqPkoAQCeNrEm7UhFsZKYT5iUOjnMV7s2LaM=
45 45 351a9292e430e35766c552066ed3e87c557b803b 0 iD8DBQBOh3zUywK+sNU5EO8RApFMAKCD3Y/u3avDFndznwqfG5UeTHMlvACfUivPIVQZyDZnhZMq0UhC6zhCEQg=
46 46 384082750f2c51dc917d85a7145748330fa6ef4d 0 iD8DBQBOmd+OywK+sNU5EO8RAgDgAJ9V/X+G7VLwhTpHrZNiOHabzSyzYQCdE2kKfIevJUYB9QLAWCWP6DPwrwI=
47 47 41453d55b481ddfcc1dacb445179649e24ca861d 0 iD8DBQBOsFhpywK+sNU5EO8RAqM6AKCyfxUae3/zLuiLdQz+JR78690eMACfQ6JTBQib4AbE+rUDdkeFYg9K/+4=
48 48 195dbd1cef0c2f9f8bcf4ea303238105f716bda3 0 iD8DBQBO1/fWywK+sNU5EO8RAmoPAKCR5lpv1D6JLURHD8KVLSV4GRVEBgCgnd0Sy78ligNfqAMafmACRDvj7vo=
49 49 6344043924497cd06d781d9014c66802285072e4 0 iD8DBQBPALgmywK+sNU5EO8RAlfhAJ9nYOdWnhfVDHYtDTJAyJtXBAQS9wCgnefoSQt7QABkbGxM+Q85UYEBuD0=
50 50 db33555eafeaf9df1e18950e29439eaa706d399b 0 iD8DBQBPGdzxywK+sNU5EO8RAppkAJ9jOXhUVE/97CPgiMA0pMGiIYnesQCfengAszcBiSiKGugiI8Okc9ghU+Y=
51 51 2aa5b51f310fb3befd26bed99c02267f5c12c734 0 iD8DBQBPKZ9bywK+sNU5EO8RAt1TAJ45r1eJ0YqSkInzrrayg4TVCh0SnQCgm0GA/Ua74jnnDwVQ60lAwROuz1Q=
52 52 53e2cd303ecf8ca7c7eeebd785c34e5ed6b0f4a4 0 iD8DBQBPT/fvywK+sNU5EO8RAnfYAKCn7d0vwqIb100YfWm1F7nFD5B+FACeM02YHpQLSNsztrBCObtqcnfod7Q=
53 53 b9bd95e61b49c221c4cca24e6da7c946fc02f992 0 iD8DBQBPeLsIywK+sNU5EO8RAvpNAKCtKe2gitz8dYn52IRF0hFOPCR7AQCfRJL/RWCFweu2T1vH/mUOCf8SXXc=
54 54 d9e2f09d5488c395ae9ddbb320ceacd24757e055 0 iD8DBQBPju/dywK+sNU5EO8RArBYAJ9xtifdbk+hCOJO8OZa4JfHX8OYZQCeKPMBaBWiT8N/WHoOm1XU0q+iono=
55 55 00182b3d087909e3c3ae44761efecdde8f319ef3 0 iD8DBQBPoFhIywK+sNU5EO8RAhzhAKCBj1n2jxPTkZNJJ5pSp3soa+XHIgCgsZZpAQxOpXwCp0eCdNGe0+pmxmg=
56 56 5983de86462c5a9f42a3ad0f5e90ce5b1d221d25 0 iD8DBQBPovNWywK+sNU5EO8RAhgiAJ980T91FdPTRMmVONDhpkMsZwVIMACgg3bKvoWSeuCW28llUhAJtUjrMv0=
57 57 85a358df5bbbe404ca25730c9c459b34263441dc 0 iD8DBQBPyZsWywK+sNU5EO8RAnpLAJ48qrGDJRT+pteS0mSQ11haqHstPwCdG4ccGbk+0JHb7aNy8/NRGAOqn9w=
58 58 b013baa3898e117959984fc64c29d8c784d2f28b 0 iD8DBQBP8QOPywK+sNU5EO8RAqimAKCFRSx0lvG6y8vne2IhNG062Hn0dACeMLI5/zhpWpHBIVeAAquYfx2XFeA=
59 59 7f5094bb3f423fc799e471aac2aee81a7ce57a0b 0 iD8DBQBQGiL8ywK+sNU5EO8RAq5oAJ4rMMCPx6O+OuzNXVOexogedWz/QgCeIiIxLd76I4pXO48tdXhr0hQcBuM=
60 60 072209ae4ddb654eb2d5fd35bff358c738414432 0 iD8DBQBQQkq0ywK+sNU5EO8RArDTAJ9nk5CySnNAjAXYvqvx4uWCw9ThZwCgqmFRehH/l+oTwj3f8nw8u8qTCdc=
61 61 b3f0f9a39c4e1d0250048cd803ab03542d6f140a 0 iD8DBQBQamltywK+sNU5EO8RAlsqAJ4qF/m6aFu4mJCOKTiAP5RvZFK02ACfawYShUZO6OXEFfveU0aAxDR0M1k=
62 62 d118a4f4fd16d9b558ec3f3e87bfee772861d2b7 0 iD8DBQBQgPV5ywK+sNU5EO8RArylAJ0abcx5NlDjyv3ZDWpAfRIHyRsJtQCgn4TMuEayqgxzrvadQZHdTEU2g38=
63 63 195ad823b5d58c68903a6153a25e3fb4ed25239d 0 iD8DBQBQkuT9ywK+sNU5EO8RAhB4AKCeerItoK2Jipm2cVf4euGofAa/WACeJj3TVd4pFILpb+ogj7ebweFLJi0=
64 64 0c10cf8191469e7c3c8844922e17e71a176cb7cb 0 iD8DBQBQvQWoywK+sNU5EO8RAnq3AJoCn98u4geFx5YaQaeh99gFhCd7bQCgjoBwBSUyOvGd0yBy60E3Vv3VZhM=
65 65 a4765077b65e6ae29ba42bab7834717b5072d5ba 0 iD8DBQBQ486sywK+sNU5EO8RAhmJAJ90aLfLKZhmcZN7kqphigQJxiFOQACeJ5IUZxjGKH4xzi3MrgIcx9n+dB0=
66 66 f5fbe15ca7449f2c9a3cf817c86d0ae68b307214 0 iD8DBQBQ+yuYywK+sNU5EO8RAm9JAJoD/UciWvpGeKBcpGtZJBFJVcL/HACghDXSgQ+xQDjB+6uGrdgAQsRR1Lg=
67 67 a6088c05e43a8aee0472ca3a4f6f8d7dd914ebbf 0 iD8DBQBRDDROywK+sNU5EO8RAh75AJ9uJCGoCWnP0Lv/+XuYs4hvUl+sAgCcD36QgAnuw8IQXrvv684BAXAnHcA=
68 68 7511d4df752e61fe7ae4f3682e0a0008573b0402 0 iD8DBQBRFYaoywK+sNU5EO8RAuErAJoDyhXn+lptU3+AevVdwAIeNFyR2gCdHzPHyWd+JDeWCUR+pSOBi8O2ppM=
69 69 5b7175377babacce80a6c1e12366d8032a6d4340 0 iD8DBQBRMCYgywK+sNU5EO8RAq1/AKCWKlt9ysibyQgYwoxxIOZv5J8rpwCcDSHQaaf1fFZUTnQsOePwcM2Y/Sg=
70 70 50c922c1b5145dab8baefefb0437d363b6a6c21c 0 iD8DBQBRWnUnywK+sNU5EO8RAuQRAJwM42cJqJPeqJ0jVNdMqKMDqr4dSACeP0cRVGz1gitMuV0x8f3mrZrqc7I=
71 71 8a7bd2dccd44ed571afe7424cd7f95594f27c092 0 iD8DBQBRXfBvywK+sNU5EO8RAn+LAKCsMmflbuXjYRxlzFwId5ptm8TZcwCdGkyLbZcASBOkzQUm/WW1qfknJHU=
72 72 292cd385856d98bacb2c3086f8897bc660c2beea 0 iD8DBQBRcM0BywK+sNU5EO8RAjp4AKCJBykQbvXhKuvLSMxKx3a2TBiXcACfbr/kLg5GlZTF/XDPmY+PyHgI/GM=
73 73 23f785b38af38d2fca6b8f3db56b8007a84cd73a 0 iD8DBQBRgZwNywK+sNU5EO8RAmO4AJ4u2ILGuimRP6MJgE2t65LZ5dAdkACgiENEstIdrlFC80p+sWKD81kKIYI=
74 74 ddc7a6be20212d18f3e27d9d7e6f079a66d96f21 0 iD8DBQBRkswvywK+sNU5EO8RAiYYAJsHTHyHbJeAgmGvBTmDrfcKu4doUgCeLm7eGBjx7yAPUvEtxef8rAkQmXI=
75 75 cceaf7af4c9e9e6fa2dbfdcfe9856c5da69c4ffd 0 iD8DBQBRqnFLywK+sNU5EO8RAsWNAJ9RR6t+y1DLFc2HeH0eN9VfZAKF9gCeJ8ezvhtKq/LMs0/nvcgKQc/d5jk=
76 76 009794acc6e37a650f0fae37872e733382ac1c0c 0 iD8DBQBR0guxywK+sNU5EO8RArNkAKCq9pMihVzP8Os5kCmgbWpe5C37wgCgqzuPZTHvAsXF5wTyaSTMVa9Ccq4=
77 77 f0d7721d7322dcfb5af33599c2543f27335334bb 0 iD8DBQBR8taaywK+sNU5EO8RAqeEAJ4idDhhDuEsgsUjeQgWNj498matHACfT67gSF5w0ylsrBx1Hb52HkGXDm0=
78 78 f37b5a17e6a0ee17afde2cdde5393dd74715fb58 0 iD8DBQBR+ymFywK+sNU5EO8RAuSdAJkBMcd9DAZ3rWE9WGKPm2YZ8LBoXACfXn/wbEsVy7ZgJoUwiWmHSnQaWCI=
79 79 335a558f81dc73afeab4d7be63617392b130117f 0 iQIVAwUAUiZrIyBXgaxoKi1yAQK2iw//cquNqqSkc8Re5/TZT9I6NH+lh6DbOKjJP0Xl1Wqq0K+KSIUgZG4G32ovaEb2l5X0uY+3unRPiZ0ebl0YSw4Fb2ZiPIADXLBTOYRrY2Wwd3tpJeGI6wEgZt3SfcITV/g7NJrCjT3FlYoSOIayrExM80InSdcEM0Q3Rx6HKzY2acyxzgZeAtAW5ohFvHilSvY6p5Gcm4+QptMxvw45GPdreUmjeXZxNXNXZ8P+MjMz/QJbai/N7PjmK8lqnhkBsT48Ng/KhhmOkGntNJ2/ImBWLFGcWngSvJ7sfWwnyhndvGhe0Hq1NcCf7I8TjNDxU5TR+m+uW7xjXdLoDbUjBdX4sKXnh8ZjbYiODKBOrrDq25cf8nA/tnpKyE/qsVy60kOk6loY4XKiYmn1V49Ta0emmDx0hqo3HgxHHsHX0NDnGdWGol7cPRET0RzVobKq1A0jnrhPooWidvLh9bPzLonrWDo+ib+DuySoRkuYUK4pgZJ2mbg6daFOBEZygkSyRB8bo1UQUP7EgQDrWe4khb/5GHEfDkrQz3qu/sXvc0Ir1mOUWBFPHC2DjjCn/oMJuUkG1SwM8l2Bfv7h67ssES6YQ2+RjOix4yid7EXS/Ogl45PzCIPSI5+BbNs10JhE0w5uErBHlF53EDTe/TSLc+GU6DB6PP6dH912Njdr3jpNSUQ=
80 80 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
@@ -1,246 +1,248 b''
1 1 d40cc5aacc31ed673d9b5b24f98bee78c283062c 0.4f
2 2 1c590d34bf61e2ea12c71738e5a746cd74586157 0.4e
3 3 7eca4cfa8aad5fce9a04f7d8acadcd0452e2f34e 0.4d
4 4 b4d0c3786ad3e47beacf8412157326a32b6d25a4 0.4c
5 5 f40273b0ad7b3a6d3012fd37736d0611f41ecf54 0.5
6 6 0a28dfe59f8fab54a5118c5be4f40da34a53cdb7 0.5b
7 7 12e0fdbc57a0be78f0e817fd1d170a3615cd35da 0.6
8 8 4ccf3de52989b14c3d84e1097f59e39a992e00bd 0.6b
9 9 eac9c8efcd9bd8244e72fb6821f769f450457a32 0.6c
10 10 979c049974485125e1f9357f6bbe9c1b548a64c3 0.7
11 11 3a56574f329a368d645853e0f9e09472aee62349 0.8
12 12 6a03cff2b0f5d30281e6addefe96b993582f2eac 0.8.1
13 13 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0.9
14 14 2be3001847cb18a23c403439d9e7d0ace30804e9 0.9.1
15 15 36a957364b1b89c150f2d0e60a99befe0ee08bd3 0.9.2
16 16 27230c29bfec36d5540fbe1c976810aefecfd1d2 0.9.3
17 17 fb4b6d5fe100b0886f8bc3d6731ec0e5ed5c4694 0.9.4
18 18 23889160905a1b09fffe1c07378e9fc1827606eb 0.9.5
19 19 bae2e9c838e90a393bae3973a7850280413e091a 1.0
20 20 d5cbbe2c49cee22a9fbeb9ea41daa0ac4e26b846 1.0.1
21 21 d2375bbee6d47e62ba8e415c86e83a465dc4dce9 1.0.2
22 22 2a67430f92f15ea5159c26b09ec4839a0c549a26 1.1
23 23 3773e510d433969e277b1863c317b674cbee2065 1.1.1
24 24 11a4eb81fb4f4742451591489e2797dc47903277 1.1.2
25 25 11efa41037e280d08cfb07c09ad485df30fb0ea8 1.2
26 26 02981000012e3adf40c4849bd7b3d5618f9ce82d 1.2.1
27 27 196d40e7c885fa6e95f89134809b3ec7bdbca34b 1.3
28 28 3ef6c14a1e8e83a31226f5881b7fe6095bbfa6f6 1.3.1
29 29 31ec469f9b556f11819937cf68ee53f2be927ebf 1.4
30 30 439d7ea6fe3aa4ab9ec274a68846779153789de9 1.4.1
31 31 296a0b14a68621f6990c54fdba0083f6f20935bf 1.4.2
32 32 4aa619c4c2c09907034d9824ebb1dd0e878206eb 1.4.3
33 33 ff2704a8ded37fbebd8b6eb5ec733731d725da8a 1.5
34 34 2b01dab594167bc0dd33331dbaa6dca3dca1b3aa 1.5.1
35 35 39f725929f0c48c5fb3b90c071fc3066012456ca 1.5.2
36 36 fdcf80f26604f233dc4d8f0a5ef9d7470e317e8a 1.5.3
37 37 24fe2629c6fd0c74c90bd066e77387c2b02e8437 1.5.4
38 38 f786fc4b8764cd2a5526d259cf2f94d8a66924d9 1.6
39 39 bf1774d95bde614af3956d92b20e2a0c68c5fec7 1.6.1
40 40 c00f03a4982e467fb6b6bd45908767db6df4771d 1.6.2
41 41 ff5cec76b1c5b6be9c3bb923aae8c3c6d079d6b9 1.6.3
42 42 93d8bff78c96fe7e33237b257558ee97290048a4 1.6.4
43 43 333421b9e0f96c7bc788e5667c146a58a9440a55 1.7
44 44 4438875ec01bd0fc32be92b0872eb6daeed4d44f 1.7.1
45 45 6aff4f144ad356311318b0011df0bb21f2c97429 1.7.2
46 46 e3bf16703e2601de99e563cdb3a5d50b64e6d320 1.7.3
47 47 a6c855c32ea081da3c3b8ff628f1847ff271482f 1.7.4
48 48 2b2155623ee2559caf288fd333f30475966c4525 1.7.5
49 49 2616325766e3504c8ae7c84bd15ee610901fe91d 1.8
50 50 aa1f3be38ab127280761889d2dca906ca465b5f4 1.8.1
51 51 b032bec2c0a651ca0ddecb65714bfe6770f67d70 1.8.2
52 52 3cb1e95676ad089596bd81d0937cad37d6e3b7fb 1.8.3
53 53 733af5d9f6b22387913e1d11350fb8cb7c1487dd 1.8.4
54 54 de9eb6b1da4fc522b1cab16d86ca166204c24f25 1.9
55 55 4a43e23b8c55b4566b8200bf69fe2158485a2634 1.9.1
56 56 d629f1e89021103f1753addcef6b310e4435b184 1.9.2
57 57 351a9292e430e35766c552066ed3e87c557b803b 1.9.3
58 58 384082750f2c51dc917d85a7145748330fa6ef4d 2.0-rc
59 59 41453d55b481ddfcc1dacb445179649e24ca861d 2.0
60 60 195dbd1cef0c2f9f8bcf4ea303238105f716bda3 2.0.1
61 61 6344043924497cd06d781d9014c66802285072e4 2.0.2
62 62 db33555eafeaf9df1e18950e29439eaa706d399b 2.1-rc
63 63 2aa5b51f310fb3befd26bed99c02267f5c12c734 2.1
64 64 53e2cd303ecf8ca7c7eeebd785c34e5ed6b0f4a4 2.1.1
65 65 b9bd95e61b49c221c4cca24e6da7c946fc02f992 2.1.2
66 66 d9e2f09d5488c395ae9ddbb320ceacd24757e055 2.2-rc
67 67 00182b3d087909e3c3ae44761efecdde8f319ef3 2.2
68 68 5983de86462c5a9f42a3ad0f5e90ce5b1d221d25 2.2.1
69 69 85a358df5bbbe404ca25730c9c459b34263441dc 2.2.2
70 70 b013baa3898e117959984fc64c29d8c784d2f28b 2.2.3
71 71 a06e2681dd1786e2354d84a5fa9c1c88dd4fa3e0 2.3-rc
72 72 7f5094bb3f423fc799e471aac2aee81a7ce57a0b 2.3
73 73 072209ae4ddb654eb2d5fd35bff358c738414432 2.3.1
74 74 b3f0f9a39c4e1d0250048cd803ab03542d6f140a 2.3.2
75 75 d118a4f4fd16d9b558ec3f3e87bfee772861d2b7 2.4-rc
76 76 195ad823b5d58c68903a6153a25e3fb4ed25239d 2.4
77 77 0c10cf8191469e7c3c8844922e17e71a176cb7cb 2.4.1
78 78 a4765077b65e6ae29ba42bab7834717b5072d5ba 2.4.2
79 79 f5fbe15ca7449f2c9a3cf817c86d0ae68b307214 2.5-rc
80 80 a6088c05e43a8aee0472ca3a4f6f8d7dd914ebbf 2.5
81 81 7511d4df752e61fe7ae4f3682e0a0008573b0402 2.5.1
82 82 5b7175377babacce80a6c1e12366d8032a6d4340 2.5.2
83 83 50c922c1b5145dab8baefefb0437d363b6a6c21c 2.5.3
84 84 8a7bd2dccd44ed571afe7424cd7f95594f27c092 2.5.4
85 85 292cd385856d98bacb2c3086f8897bc660c2beea 2.6-rc
86 86 23f785b38af38d2fca6b8f3db56b8007a84cd73a 2.6
87 87 ddc7a6be20212d18f3e27d9d7e6f079a66d96f21 2.6.1
88 88 cceaf7af4c9e9e6fa2dbfdcfe9856c5da69c4ffd 2.6.2
89 89 009794acc6e37a650f0fae37872e733382ac1c0c 2.6.3
90 90 f0d7721d7322dcfb5af33599c2543f27335334bb 2.7-rc
91 91 f37b5a17e6a0ee17afde2cdde5393dd74715fb58 2.7
92 92 335a558f81dc73afeab4d7be63617392b130117f 2.7.1
93 93 e7fa36d2ad3a7944a52dca126458d6f482db3524 2.7.2
94 94 1596f2d8f2421314b1ddead8f7d0c91009358994 2.8-rc
95 95 d825e4025e39d1c39db943cdc89818abd0a87c27 2.8
96 96 209e04a06467e2969c0cc6501335be0406d46ef0 2.8.1
97 97 ca387377df7a3a67dbb90b6336b781cdadc3ef41 2.8.2
98 98 8862469e16f9236208581b20de5f96bd13cc039d 2.9-rc
99 99 3cec5134e9c4bceab6a00c60f52a4f80677a78f2 2.9
100 100 b96cb15ec9e04d8ac5ee08b34fcbbe4200588965 2.9.1
101 101 3f83fc5cfe715d292069ee8417c83804f6c6c1e4 2.9.2
102 102 564f55b251224f16508dd1311452db7780dafe2b 3.0-rc
103 103 2195ac506c6ababe86985b932f4948837c0891b5 3.0
104 104 269c80ee5b3cb3684fa8edc61501b3506d02eb10 3.0.1
105 105 2d8cd3d0e83c7336c0cb45a9f88638363f993848 3.0.2
106 106 6c36dc6cd61a0e1b563f1d51e55bdf4dacf12162 3.1-rc
107 107 3178e49892020336491cdc6945885c4de26ffa8b 3.1
108 108 5dc91146f35369949ea56b40172308158b59063a 3.1.1
109 109 f768c888aaa68d12dd7f509dcc7f01c9584357d0 3.1.2
110 110 7f8d16af8cae246fa5a48e723d48d58b015aed94 3.2-rc
111 111 ced632394371a36953ce4d394f86278ae51a2aae 3.2
112 112 643c58303fb0ec020907af28b9e486be299ba043 3.2.1
113 113 902554884335e5ca3661d63be9978eb4aec3f68a 3.2.2
114 114 6dad422ecc5adb63d9fa649eeb8e05a5f9bc4900 3.2.3
115 115 1265a3a71d75396f5d4cf6935ae7d9ba5407a547 3.2.4
116 116 db8e3f7948b1fdeb9ad12d448fc3525759908b9f 3.3-rc
117 117 fbdd5195528fae4f41feebc1838215c110b25d6a 3.3
118 118 5b4ed033390bf6e2879c8f5c28c84e1ee3b87231 3.3.1
119 119 07a92bbd02e5e3a625e0820389b47786b02b2cea 3.3.2
120 120 2e2e9a0750f91a6fe0ad88e4de34f8efefdcab08 3.3.3
121 121 e89f909edffad558b56f4affa8239e4832f88de0 3.4-rc
122 122 8cc6036bca532e06681c5a8fa37efaa812de67b5 3.4
123 123 ed18f4acf435a2824c6f49fba40f42b9df5da7ad 3.4.1
124 124 540cd0ddac49c1125b2e013aa2ff18ecbd4dd954 3.4.2
125 125 96a38d44ba093bd1d1ecfd34119e94056030278b 3.5-rc
126 126 21aa1c313b05b1a85f8ffa1120d51579ddf6bf24 3.5
127 127 1a45e49a6bed023deb229102a8903234d18054d3 3.5.1
128 128 9a466b9f9792e3ad7ae3fc6c43c3ff2e136b718d 3.5.2
129 129 b66e3ca0b90c3095ea28dfd39aa24247bebf5c20 3.6-rc
130 130 47dd34f2e7272be9e3b2a5a83cd0d20be44293f4 3.6
131 131 1aa5083cbebbe7575c88f3402ab377539b484897 3.6.1
132 132 2d437a0f3355834a9485bbbeb30a52a052c98f19 3.6.2
133 133 ea389970c08449440587712117f178d33bab3f1e 3.6.3
134 134 158bdc8965720ca4061f8f8d806563cfc7cdb62e 3.7-rc
135 135 2408645de650d8a29a6ce9e7dce601d8dd0d1474 3.7
136 136 b698abf971e7377d9b7ec7fc8c52df45255b0329 3.7.1
137 137 d493d64757eb45ada99fcb3693e479a51b7782da 3.7.2
138 138 ae279d4a19e9683214cbd1fe8298cf0b50571432 3.7.3
139 139 740156eedf2c450aee58b1a90b0e826f47c5da64 3.8-rc
140 140 f85de28eae32e7d3064b1a1321309071bbaaa069 3.8
141 141 a56296f55a5e1038ea5016dace2076b693c28a56 3.8.1
142 142 aaabed77791a75968a12b8c43ad263631a23ee81 3.8.2
143 143 a9764ab80e11bcf6a37255db7dd079011f767c6c 3.8.3
144 144 26a5d605b8683a292bb89aea11f37a81b06ac016 3.8.4
145 145 519bb4f9d3a47a6e83c2b414d58811ed38f503c2 3.9-rc
146 146 299546f84e68dbb9bd026f0f3a974ce4bdb93686 3.9
147 147 ccd436f7db6d5d7b9af89715179b911d031d44f1 3.9.1
148 148 149433e68974eb5c63ccb03f794d8b57339a80c4 3.9.2
149 149 438173c415874f6ac653efc1099dec9c9150e90f 4.0-rc
150 150 eab27446995210c334c3d06f1a659e3b9b5da769 4.0
151 151 b3b1ae98f6a0e14c1e1ba806a6c18e193b6dae5c 4.0.1
152 152 e69874dc1f4e142746ff3df91e678a09c6fc208c 4.0.2
153 153 a1dd2c0c479e0550040542e392e87bc91262517e 4.1-rc
154 154 e1526da1e6d84e03146151c9b6e6950fe9a83d7d 4.1
155 155 25703b624d27e3917d978af56d6ad59331e0464a 4.1.1
156 156 ed5b25874d998ababb181a939dd37a16ea644435 4.1.2
157 157 77eaf9539499a1b8be259ffe7ada787d07857f80 4.1.3
158 158 616e788321cc4ae9975b7f0c54c849f36d82182b 4.2-rc
159 159 bb96d4a497432722623ae60d9bc734a1e360179e 4.2
160 160 c850f0ed54c1d42f9aa079ad528f8127e5775217 4.2.1
161 161 26c49ed51a698ec016d2b4c6b44ca3c3f73cc788 4.2.2
162 162 857876ebaed4e315f63157bd157d6ce553c7ab73 4.3-rc
163 163 5544af8622863796a0027566f6b646e10d522c4c 4.3
164 164 943c91326b23954e6e1c6960d0239511f9530258 4.2.3
165 165 3fee7f7d2da04226914c2258cc2884dc27384fd7 4.3.1
166 166 920977f72c7b70acfdaf56ab35360584d7845827 4.3.2
167 167 2f427b57bf9019c6dc3750baa539dc22c1be50f6 4.3.3
168 168 1e2454b60e5936f5e77498cab2648db469504487 4.4-rc
169 169 0ccb43d4cf01d013ae05917ec4f305509f851b2d 4.4
170 170 cabc840ffdee8a72f3689fb77dd74d04fdc2bc04 4.4.1
171 171 a92b9f8e11ba330614cdfd6af0e03b15c1ff3797 4.4.2
172 172 27b6df1b5adbdf647cf5c6675b40575e1b197c60 4.5-rc
173 173 d334afc585e29577f271c5eda03378736a16ca6b 4.5
174 174 369aadf7a3264b03c8b09efce715bc41e6ab4a9b 4.5.1
175 175 8bba684efde7f45add05f737952093bb2aa07155 4.5.2
176 176 7de7bd407251af2bc98e5b809c8598ee95830daf 4.5.3
177 177 ed5448edcbfa747b9154099e18630e49024fd47b 4.6rc0
178 178 1ec874717d8a93b19e0d50628443e0ee5efab3a9 4.6rc1
179 179 6614cac550aea66d19c601e45efd1b7bd08d7c40 4.6
180 180 9c5ced5276d6e7d54f7c3dadf5247b7ee98ec79c 4.6.1
181 181 0b63a6743010dfdbf8a8154186e119949bdaa1cc 4.6.2
182 182 e90130af47ce8dd53a3109aed9d15876b3e7dee8 4.7rc0
183 183 33ac6a72308a215e6086fbced347ec10aa963b0a 4.7
184 184 ede3bf31fe63677fdf5bd8db687977d4e3d792ed 4.7.1
185 185 5405cb1a79010ac50c58cd84e6f50c4556bf2a4c 4.7.2
186 186 956ec6f1320df26f3133ec40f3de866ea0695fd7 4.8rc0
187 187 a91a2837150bdcb27ae76b3646e6c93cd6a15904 4.8
188 188 1c8c54cf97256f4468da2eb4dbee24f7f3888e71 4.8.1
189 189 197f092b2cd9691e2a55d198f717b231af9be6f9 4.8.2
190 190 593718ff5844cad7a27ee3eb5adad89ac8550949 4.9rc0
191 191 83377b4b4ae0e9a6b8e579f7b0a693b8cf5c3b10 4.9
192 192 4ea21df312ec7159c5b3633096b6ecf68750b0dd 4.9.1
193 193 4a8d9ed864754837a185a642170cde24392f9abf 5.0rc0
194 194 07e479ef7c9639be0029f00e6a722b96dcc05fee 5.0
195 195 c3484ddbdb9621256d597ed86b90d229c59c2af9 5.0.1
196 196 97ada9b8d51bef24c5cb4cdca4243f0db694ab6e 5.0.2
197 197 e386b5f4f8360dbb43a576dd9b1368e386fefa5b 5.1rc0
198 198 e91930d712e8507d1bc1b2dffd96c83edc4cbed3 5.1
199 199 a4e32fd539ab41489a51b2aa88bda9a73b839562 5.1.1
200 200 181e52f2b62f4768aa0d988936c929dc7c4a41a0 5.1.2
201 201 59338f9561099de77c684c00f76507f11e46ebe8 5.2rc0
202 202 ca3dca416f8d5863ca6f5a4a6a6bb835dcd5feeb 5.2
203 203 a50fecefa691c9b72a99e49aa6fe9dd13943c2bf 5.2.1
204 204 b4c82b70418022e67cc0e69b1aa3c3aa43aa1d29 5.2.2
205 205 84a0102c05c7852c8215ef6cf21d809927586b69 5.3rc0
206 206 e4344e463c0c888a2f437b78b5982ecdf3f6650a 5.3rc1
207 207 7f5410dfc8a64bb587d19637deb95d378fd1eb5c 5.3
208 208 6d121acbb82e65fe4dd3c2318a1b61981b958492 5.3.1
209 209 8fca7e8449a847e3cf1054f2c07b51237699fad3 5.3.2
210 210 26ce8e7515036d3431a03aaeb7bc72dd96cb1112 5.4rc0
211 211 cf3e07d7648a4371ce584d15dd692e7a6845792f 5.4
212 212 065704cbdbdbb05dcd6bb814eb9bbdd982211b28 5.4.1
213 213 0ea9c86fac8974cd74dc12ea681c8986eb6da6c4 5.4.2
214 214 28163c5de797e5416f9b588940f4608269b4d50a 5.5rc0
215 215 7fc3c5fbc65f6fe85d70ea63923b8767dda4f2e0 5.5
216 216 f62bb5d07848ca598aa860a517394130b61bf2ee 5.5.1
217 217 07731064ac41dacdf0ec869ebd05c2e848c14fbf 5.5.2
218 218 0e06a7ab9e0d5c65af4e511aee1e0342998799df 5.6rc0
219 219 18c17d63fdabd009e70bf994e5efb7db422f4f7f 5.6
220 220 1d5189a57405ceca5aa244052c9f948977f4699b 5.6.1
221 221 9da65e3cf3706ff41e08b311381c588440c27baf 5.7rc0
222 222 0e2e7300f4302b02412b0b734717697049494c4c 5.7
223 223 d5d9177c0045d206db575bae6daa98e2cb2fe5bc 5.7.1
224 224 f67b8946bb1b6cfa8328dbf8d6a9128b69ccdcb4 5.8rc0
225 225 8d2b62d716b095507effaa8d56f87cd27ba659ab 5.8rc1
226 226 067f2c53fb24506c9e9fb4639871b13b19a85f8a 5.8
227 227 411dc27fd9fd076d6a031a08fcaace659afe2fe3 5.8.1
228 228 d7515d29761d5ada7d9c765f517db67db75dea9a 5.9rc0
229 229 2813d406b03607cdb8c06cb04c44efcc9a79d9a2 5.9rc1
230 230 53221078e0de65d1a821ce5311dec45a7a978301 5.9
231 231 86a60679cf619e14cee9442f865fcf31b142cb9f 5.9.1
232 232 750920b18aaaddd654756be40dec59d90f2643be 5.9.2
233 233 6ee0244fc1cf889ae543d2ce0ec45201ae0be6e1 5.9.3
234 234 a44bb185f6bdbecc754996d8386722e2f0123b0a 6.0rc0
235 235 5d08b289e2e526259d7d5ea32b70fe76d5b327d7 6.0
236 236 799fdf4cca80cb9ae40537a90995e6bd163ebc0b 6.0.1
237 237 75676122c2bf7594ac732b7388db4c74c648b365 6.0.2
238 238 dcec16e799ddb6d33fcd11b04af530250a417a58 6.0.3
239 239 c00d3ce4e94bb0ee8d809e25e1dcb2a5fab84e2c 6.1rc0
240 240 d4486810a1795fba9521449b8885ced034f3a6dd 6.1
241 241 5bd6bcd31dd1ebb63b8914b00064f96297267af7 6.1.1
242 242 0ddd5e1f5f67438af85d12e4ce6c39021dde9916 6.1.2
243 243 6b10151b962108f65bfa12b3918b1021ca334f73 6.1.3
244 244 0cc5f74ff7f0f4ac2427096bddbe102dbc2453ae 6.1.4
245 245 288de6f5d724bba7bf1669e2838f196962bb7528 6.2rc0
246 246 094a5fa3cf52f936e0de3f1e507c818bee5ece6b 6.2
247 f69bffd00abe3a1b94d1032eb2c92e611d16a192 6.2.1
248 b5c8524827d20fe2e0ca8fb1234a0fe35a1a36c7 6.2.2
@@ -1,1325 +1,1330 b''
1 1 # aws.py - Automation code for Amazon Web Services
2 2 #
3 3 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 # no-check-code because Python 3 native.
9 9
10 10 import contextlib
11 11 import copy
12 12 import hashlib
13 13 import json
14 14 import os
15 15 import pathlib
16 16 import subprocess
17 17 import time
18 18
19 19 import boto3
20 20 import botocore.exceptions
21 21
22 22 from .linux import BOOTSTRAP_DEBIAN
23 23 from .ssh import (
24 24 exec_command as ssh_exec_command,
25 25 wait_for_ssh,
26 26 )
27 27 from .winrm import (
28 28 run_powershell,
29 29 wait_for_winrm,
30 30 )
31 31
32 32
33 33 SOURCE_ROOT = pathlib.Path(
34 34 os.path.abspath(__file__)
35 35 ).parent.parent.parent.parent
36 36
37 37 INSTALL_WINDOWS_DEPENDENCIES = (
38 38 SOURCE_ROOT / 'contrib' / 'install-windows-dependencies.ps1'
39 39 )
40 40
41 41
42 42 INSTANCE_TYPES_WITH_STORAGE = {
43 43 'c5d',
44 44 'd2',
45 45 'h1',
46 46 'i3',
47 47 'm5ad',
48 48 'm5d',
49 49 'r5d',
50 50 'r5ad',
51 51 'x1',
52 52 'z1d',
53 53 }
54 54
55 55
56 56 AMAZON_ACCOUNT_ID = '801119661308'
57 57 DEBIAN_ACCOUNT_ID = '379101102735'
58 58 DEBIAN_ACCOUNT_ID_2 = '136693071363'
59 59 UBUNTU_ACCOUNT_ID = '099720109477'
60 60
61 61
62 WINDOWS_BASE_IMAGE_NAME = 'Windows_Server-2019-English-Full-Base-*'
62 WINDOWS_BASE_IMAGE_NAME = 'Windows_Server-2022-English-Full-Base-*'
63 63
64 64
65 65 KEY_PAIRS = {
66 66 'automation',
67 67 }
68 68
69 69
70 70 SECURITY_GROUPS = {
71 71 'linux-dev-1': {
72 72 'description': 'Mercurial Linux instances that perform build/test automation',
73 73 'ingress': [
74 74 {
75 75 'FromPort': 22,
76 76 'ToPort': 22,
77 77 'IpProtocol': 'tcp',
78 78 'IpRanges': [
79 79 {
80 80 'CidrIp': '0.0.0.0/0',
81 81 'Description': 'SSH from entire Internet',
82 82 },
83 83 ],
84 84 },
85 85 ],
86 86 },
87 87 'windows-dev-1': {
88 88 'description': 'Mercurial Windows instances that perform build automation',
89 89 'ingress': [
90 90 {
91 91 'FromPort': 22,
92 92 'ToPort': 22,
93 93 'IpProtocol': 'tcp',
94 94 'IpRanges': [
95 95 {
96 96 'CidrIp': '0.0.0.0/0',
97 97 'Description': 'SSH from entire Internet',
98 98 },
99 99 ],
100 100 },
101 101 {
102 102 'FromPort': 3389,
103 103 'ToPort': 3389,
104 104 'IpProtocol': 'tcp',
105 105 'IpRanges': [
106 106 {
107 107 'CidrIp': '0.0.0.0/0',
108 108 'Description': 'RDP from entire Internet',
109 109 },
110 110 ],
111 111 },
112 112 {
113 113 'FromPort': 5985,
114 114 'ToPort': 5986,
115 115 'IpProtocol': 'tcp',
116 116 'IpRanges': [
117 117 {
118 118 'CidrIp': '0.0.0.0/0',
119 119 'Description': 'PowerShell Remoting (Windows Remote Management)',
120 120 },
121 121 ],
122 122 },
123 123 ],
124 124 },
125 125 }
126 126
127 127
128 128 IAM_ROLES = {
129 129 'ephemeral-ec2-role-1': {
130 130 'description': 'Mercurial temporary EC2 instances',
131 131 'policy_arns': [
132 132 'arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM',
133 133 ],
134 134 },
135 135 }
136 136
137 137
138 138 ASSUME_ROLE_POLICY_DOCUMENT = '''
139 139 {
140 140 "Version": "2012-10-17",
141 141 "Statement": [
142 142 {
143 143 "Effect": "Allow",
144 144 "Principal": {
145 145 "Service": "ec2.amazonaws.com"
146 146 },
147 147 "Action": "sts:AssumeRole"
148 148 }
149 149 ]
150 150 }
151 151 '''.strip()
152 152
153 153
154 154 IAM_INSTANCE_PROFILES = {
155 155 'ephemeral-ec2-1': {
156 156 'roles': [
157 157 'ephemeral-ec2-role-1',
158 158 ],
159 159 }
160 160 }
161 161
162 162
163 163 # User Data for Windows EC2 instance. Mainly used to set the password
164 164 # and configure WinRM.
165 165 # Inspired by the User Data script used by Packer
166 166 # (from https://www.packer.io/intro/getting-started/build-image.html).
167 167 WINDOWS_USER_DATA = r'''
168 168 <powershell>
169 169
170 170 # TODO enable this once we figure out what is failing.
171 171 #$ErrorActionPreference = "stop"
172 172
173 173 # Set administrator password
174 174 net user Administrator "%s"
175 175 wmic useraccount where "name='Administrator'" set PasswordExpires=FALSE
176 176
177 # And set it via EC2Launch so it persists across reboots.
178 $config = & $env:ProgramFiles\Amazon\EC2Launch\EC2Launch.exe get-agent-config --format json | ConvertFrom-Json
179 $config | ConvertTo-Json -Depth 6 | Out-File -encoding UTF8 $env:ProgramData/Amazon/EC2Launch/config/agent-config.yml
180 $setAdminAccount = @"
181 {
182 "task": "setAdminAccount",
183 "inputs": {
184 "password": {
185 "type": "static",
186 "data": "%s"
187 }
188 }
189 }
190 "@
191 $config.config | %%{if($_.stage -eq 'preReady'){$_.tasks += (ConvertFrom-Json -InputObject $setAdminAccount)}}
192 $config | ConvertTo-Json -Depth 6 | Out-File -encoding UTF8 $env:ProgramData/Amazon/EC2Launch/config/agent-config.yml
193
177 194 # First, make sure WinRM can't be connected to
178 195 netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new enable=yes action=block
179 196
180 197 # Delete any existing WinRM listeners
181 198 winrm delete winrm/config/listener?Address=*+Transport=HTTP 2>$Null
182 199 winrm delete winrm/config/listener?Address=*+Transport=HTTPS 2>$Null
183 200
184 201 # Create a new WinRM listener and configure
185 202 winrm create winrm/config/listener?Address=*+Transport=HTTP
186 203 winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="0"}'
187 204 winrm set winrm/config '@{MaxTimeoutms="7200000"}'
188 205 winrm set winrm/config/service '@{AllowUnencrypted="true"}'
189 206 winrm set winrm/config/service '@{MaxConcurrentOperationsPerUser="12000"}'
190 207 winrm set winrm/config/service/auth '@{Basic="true"}'
191 208 winrm set winrm/config/client/auth '@{Basic="true"}'
192 209
193 210 # Configure UAC to allow privilege elevation in remote shells
194 211 $Key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System'
195 212 $Setting = 'LocalAccountTokenFilterPolicy'
196 213 Set-ItemProperty -Path $Key -Name $Setting -Value 1 -Force
197 214
198 215 # Avoid long usernames in the temp directory path because the '~' causes extra quoting in ssh output
199 216 [System.Environment]::SetEnvironmentVariable('TMP', 'C:\Temp', [System.EnvironmentVariableTarget]::User)
200 217 [System.Environment]::SetEnvironmentVariable('TEMP', 'C:\Temp', [System.EnvironmentVariableTarget]::User)
201 218
202 219 # Configure and restart the WinRM Service; Enable the required firewall exception
203 220 Stop-Service -Name WinRM
204 221 Set-Service -Name WinRM -StartupType Automatic
205 222 netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new action=allow localip=any remoteip=any
206 223 Start-Service -Name WinRM
207 224
208 225 # Disable firewall on private network interfaces so prompts don't appear.
209 226 Set-NetFirewallProfile -Name private -Enabled false
210 227 </powershell>
211 228 '''.lstrip()
212 229
213 230
214 231 WINDOWS_BOOTSTRAP_POWERSHELL = '''
215 232 Write-Output "installing PowerShell dependencies"
216 233 Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
217 234 Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
218 235 Install-Module -Name OpenSSHUtils -RequiredVersion 0.0.2.0
219 236
220 237 Write-Output "installing OpenSSL server"
221 238 Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
222 239 # Various tools will attempt to use older versions of .NET. So we enable
223 240 # the feature that provides them so it doesn't have to be auto-enabled
224 241 # later.
225 242 Write-Output "enabling .NET Framework feature"
226 243 Install-WindowsFeature -Name Net-Framework-Core
227 244 '''
228 245
229 246
230 247 class AWSConnection:
231 248 """Manages the state of a connection with AWS."""
232 249
233 250 def __init__(self, automation, region: str, ensure_ec2_state: bool = True):
234 251 self.automation = automation
235 252 self.local_state_path = automation.state_path
236 253
237 254 self.prefix = 'hg-'
238 255
239 256 self.session = boto3.session.Session(region_name=region)
240 257 self.ec2client = self.session.client('ec2')
241 258 self.ec2resource = self.session.resource('ec2')
242 259 self.iamclient = self.session.client('iam')
243 260 self.iamresource = self.session.resource('iam')
244 261 self.security_groups = {}
245 262
246 263 if ensure_ec2_state:
247 264 ensure_key_pairs(automation.state_path, self.ec2resource)
248 265 self.security_groups = ensure_security_groups(self.ec2resource)
249 266 ensure_iam_state(self.iamclient, self.iamresource)
250 267
251 268 def key_pair_path_private(self, name):
252 269 """Path to a key pair private key file."""
253 270 return self.local_state_path / 'keys' / ('keypair-%s' % name)
254 271
255 272 def key_pair_path_public(self, name):
256 273 return self.local_state_path / 'keys' / ('keypair-%s.pub' % name)
257 274
258 275
259 276 def rsa_key_fingerprint(p: pathlib.Path):
260 277 """Compute the fingerprint of an RSA private key."""
261 278
262 279 # TODO use rsa package.
263 280 res = subprocess.run(
264 281 [
265 282 'openssl',
266 283 'pkcs8',
267 284 '-in',
268 285 str(p),
269 286 '-nocrypt',
270 287 '-topk8',
271 288 '-outform',
272 289 'DER',
273 290 ],
274 291 capture_output=True,
275 292 check=True,
276 293 )
277 294
278 295 sha1 = hashlib.sha1(res.stdout).hexdigest()
279 296 return ':'.join(a + b for a, b in zip(sha1[::2], sha1[1::2]))
280 297
281 298
282 299 def ensure_key_pairs(state_path: pathlib.Path, ec2resource, prefix='hg-'):
283 300 remote_existing = {}
284 301
285 302 for kpi in ec2resource.key_pairs.all():
286 303 if kpi.name.startswith(prefix):
287 304 remote_existing[kpi.name[len(prefix) :]] = kpi.key_fingerprint
288 305
289 306 # Validate that we have these keys locally.
290 307 key_path = state_path / 'keys'
291 308 key_path.mkdir(exist_ok=True, mode=0o700)
292 309
293 310 def remove_remote(name):
294 311 print('deleting key pair %s' % name)
295 312 key = ec2resource.KeyPair(name)
296 313 key.delete()
297 314
298 315 def remove_local(name):
299 316 pub_full = key_path / ('keypair-%s.pub' % name)
300 317 priv_full = key_path / ('keypair-%s' % name)
301 318
302 319 print('removing %s' % pub_full)
303 320 pub_full.unlink()
304 321 print('removing %s' % priv_full)
305 322 priv_full.unlink()
306 323
307 324 local_existing = {}
308 325
309 326 for f in sorted(os.listdir(key_path)):
310 327 if not f.startswith('keypair-') or not f.endswith('.pub'):
311 328 continue
312 329
313 330 name = f[len('keypair-') : -len('.pub')]
314 331
315 332 pub_full = key_path / f
316 333 priv_full = key_path / ('keypair-%s' % name)
317 334
318 335 with open(pub_full, 'r', encoding='ascii') as fh:
319 336 data = fh.read()
320 337
321 338 if not data.startswith('ssh-rsa '):
322 339 print(
323 340 'unexpected format for key pair file: %s; removing' % pub_full
324 341 )
325 342 pub_full.unlink()
326 343 priv_full.unlink()
327 344 continue
328 345
329 346 local_existing[name] = rsa_key_fingerprint(priv_full)
330 347
331 348 for name in sorted(set(remote_existing) | set(local_existing)):
332 349 if name not in local_existing:
333 350 actual = '%s%s' % (prefix, name)
334 351 print('remote key %s does not exist locally' % name)
335 352 remove_remote(actual)
336 353 del remote_existing[name]
337 354
338 355 elif name not in remote_existing:
339 356 print('local key %s does not exist remotely' % name)
340 357 remove_local(name)
341 358 del local_existing[name]
342 359
343 360 elif remote_existing[name] != local_existing[name]:
344 361 print(
345 362 'key fingerprint mismatch for %s; '
346 363 'removing from local and remote' % name
347 364 )
348 365 remove_local(name)
349 366 remove_remote('%s%s' % (prefix, name))
350 367 del local_existing[name]
351 368 del remote_existing[name]
352 369
353 370 missing = KEY_PAIRS - set(remote_existing)
354 371
355 372 for name in sorted(missing):
356 373 actual = '%s%s' % (prefix, name)
357 374 print('creating key pair %s' % actual)
358 375
359 376 priv_full = key_path / ('keypair-%s' % name)
360 377 pub_full = key_path / ('keypair-%s.pub' % name)
361 378
362 379 kp = ec2resource.create_key_pair(KeyName=actual)
363 380
364 381 with priv_full.open('w', encoding='ascii') as fh:
365 382 fh.write(kp.key_material)
366 383 fh.write('\n')
367 384
368 385 priv_full.chmod(0o0600)
369 386
370 387 # SSH public key can be extracted via `ssh-keygen`.
371 388 with pub_full.open('w', encoding='ascii') as fh:
372 389 subprocess.run(
373 390 ['ssh-keygen', '-y', '-f', str(priv_full)],
374 391 stdout=fh,
375 392 check=True,
376 393 )
377 394
378 395 pub_full.chmod(0o0600)
379 396
380 397
381 398 def delete_instance_profile(profile):
382 399 for role in profile.roles:
383 400 print(
384 401 'removing role %s from instance profile %s'
385 402 % (role.name, profile.name)
386 403 )
387 404 profile.remove_role(RoleName=role.name)
388 405
389 406 print('deleting instance profile %s' % profile.name)
390 407 profile.delete()
391 408
392 409
393 410 def ensure_iam_state(iamclient, iamresource, prefix='hg-'):
394 411 """Ensure IAM state is in sync with our canonical definition."""
395 412
396 413 remote_profiles = {}
397 414
398 415 for profile in iamresource.instance_profiles.all():
399 416 if profile.name.startswith(prefix):
400 417 remote_profiles[profile.name[len(prefix) :]] = profile
401 418
402 419 for name in sorted(set(remote_profiles) - set(IAM_INSTANCE_PROFILES)):
403 420 delete_instance_profile(remote_profiles[name])
404 421 del remote_profiles[name]
405 422
406 423 remote_roles = {}
407 424
408 425 for role in iamresource.roles.all():
409 426 if role.name.startswith(prefix):
410 427 remote_roles[role.name[len(prefix) :]] = role
411 428
412 429 for name in sorted(set(remote_roles) - set(IAM_ROLES)):
413 430 role = remote_roles[name]
414 431
415 432 print('removing role %s' % role.name)
416 433 role.delete()
417 434 del remote_roles[name]
418 435
419 436 # We've purged remote state that doesn't belong. Create missing
420 437 # instance profiles and roles.
421 438 for name in sorted(set(IAM_INSTANCE_PROFILES) - set(remote_profiles)):
422 439 actual = '%s%s' % (prefix, name)
423 440 print('creating IAM instance profile %s' % actual)
424 441
425 442 profile = iamresource.create_instance_profile(
426 443 InstanceProfileName=actual
427 444 )
428 445 remote_profiles[name] = profile
429 446
430 447 waiter = iamclient.get_waiter('instance_profile_exists')
431 448 waiter.wait(InstanceProfileName=actual)
432 449 print('IAM instance profile %s is available' % actual)
433 450
434 451 for name in sorted(set(IAM_ROLES) - set(remote_roles)):
435 452 entry = IAM_ROLES[name]
436 453
437 454 actual = '%s%s' % (prefix, name)
438 455 print('creating IAM role %s' % actual)
439 456
440 457 role = iamresource.create_role(
441 458 RoleName=actual,
442 459 Description=entry['description'],
443 460 AssumeRolePolicyDocument=ASSUME_ROLE_POLICY_DOCUMENT,
444 461 )
445 462
446 463 waiter = iamclient.get_waiter('role_exists')
447 464 waiter.wait(RoleName=actual)
448 465 print('IAM role %s is available' % actual)
449 466
450 467 remote_roles[name] = role
451 468
452 469 for arn in entry['policy_arns']:
453 470 print('attaching policy %s to %s' % (arn, role.name))
454 471 role.attach_policy(PolicyArn=arn)
455 472
456 473 # Now reconcile state of profiles.
457 474 for name, meta in sorted(IAM_INSTANCE_PROFILES.items()):
458 475 profile = remote_profiles[name]
459 476 wanted = {'%s%s' % (prefix, role) for role in meta['roles']}
460 477 have = {role.name for role in profile.roles}
461 478
462 479 for role in sorted(have - wanted):
463 480 print('removing role %s from %s' % (role, profile.name))
464 481 profile.remove_role(RoleName=role)
465 482
466 483 for role in sorted(wanted - have):
467 484 print('adding role %s to %s' % (role, profile.name))
468 485 profile.add_role(RoleName=role)
469 486
470 487
471 488 def find_image(ec2resource, owner_id, name, reverse_sort_field=None):
472 489 """Find an AMI by its owner ID and name."""
473 490
474 491 images = ec2resource.images.filter(
475 492 Filters=[
476 493 {
477 494 'Name': 'owner-id',
478 495 'Values': [owner_id],
479 496 },
480 497 {
481 498 'Name': 'state',
482 499 'Values': ['available'],
483 500 },
484 501 {
485 502 'Name': 'image-type',
486 503 'Values': ['machine'],
487 504 },
488 505 {
489 506 'Name': 'name',
490 507 'Values': [name],
491 508 },
492 509 ]
493 510 )
494 511
495 512 if reverse_sort_field:
496 513 images = sorted(
497 514 images,
498 515 key=lambda image: getattr(image, reverse_sort_field),
499 516 reverse=True,
500 517 )
501 518
502 519 for image in images:
503 520 return image
504 521
505 522 raise Exception('unable to find image for %s' % name)
506 523
507 524
508 525 def ensure_security_groups(ec2resource, prefix='hg-'):
509 526 """Ensure all necessary Mercurial security groups are present.
510 527
511 528 All security groups are prefixed with ``hg-`` by default. Any security
512 529 groups having this prefix but aren't in our list are deleted.
513 530 """
514 531 existing = {}
515 532
516 533 for group in ec2resource.security_groups.all():
517 534 if group.group_name.startswith(prefix):
518 535 existing[group.group_name[len(prefix) :]] = group
519 536
520 537 purge = set(existing) - set(SECURITY_GROUPS)
521 538
522 539 for name in sorted(purge):
523 540 group = existing[name]
524 541 print('removing legacy security group: %s' % group.group_name)
525 542 group.delete()
526 543
527 544 security_groups = {}
528 545
529 546 for name, group in sorted(SECURITY_GROUPS.items()):
530 547 if name in existing:
531 548 security_groups[name] = existing[name]
532 549 continue
533 550
534 551 actual = '%s%s' % (prefix, name)
535 552 print('adding security group %s' % actual)
536 553
537 554 group_res = ec2resource.create_security_group(
538 555 Description=group['description'],
539 556 GroupName=actual,
540 557 )
541 558
542 559 group_res.authorize_ingress(
543 560 IpPermissions=group['ingress'],
544 561 )
545 562
546 563 security_groups[name] = group_res
547 564
548 565 return security_groups
549 566
550 567
551 568 def terminate_ec2_instances(ec2resource, prefix='hg-'):
552 569 """Terminate all EC2 instances managed by us."""
553 570 waiting = []
554 571
555 572 for instance in ec2resource.instances.all():
556 573 if instance.state['Name'] == 'terminated':
557 574 continue
558 575
559 576 for tag in instance.tags or []:
560 577 if tag['Key'] == 'Name' and tag['Value'].startswith(prefix):
561 578 print('terminating %s' % instance.id)
562 579 instance.terminate()
563 580 waiting.append(instance)
564 581
565 582 for instance in waiting:
566 583 instance.wait_until_terminated()
567 584
568 585
569 586 def remove_resources(c, prefix='hg-'):
570 587 """Purge all of our resources in this EC2 region."""
571 588 ec2resource = c.ec2resource
572 589 iamresource = c.iamresource
573 590
574 591 terminate_ec2_instances(ec2resource, prefix=prefix)
575 592
576 593 for image in ec2resource.images.filter(Owners=['self']):
577 594 if image.name.startswith(prefix):
578 595 remove_ami(ec2resource, image)
579 596
580 597 for group in ec2resource.security_groups.all():
581 598 if group.group_name.startswith(prefix):
582 599 print('removing security group %s' % group.group_name)
583 600 group.delete()
584 601
585 602 for profile in iamresource.instance_profiles.all():
586 603 if profile.name.startswith(prefix):
587 604 delete_instance_profile(profile)
588 605
589 606 for role in iamresource.roles.all():
590 607 if role.name.startswith(prefix):
591 608 for p in role.attached_policies.all():
592 609 print('detaching policy %s from %s' % (p.arn, role.name))
593 610 role.detach_policy(PolicyArn=p.arn)
594 611
595 612 print('removing role %s' % role.name)
596 613 role.delete()
597 614
598 615
599 616 def wait_for_ip_addresses(instances):
600 617 """Wait for the public IP addresses of an iterable of instances."""
601 618 for instance in instances:
602 619 while True:
603 620 if not instance.public_ip_address:
604 621 time.sleep(2)
605 622 instance.reload()
606 623 continue
607 624
608 625 print(
609 626 'public IP address for %s: %s'
610 627 % (instance.id, instance.public_ip_address)
611 628 )
612 629 break
613 630
614 631
615 632 def remove_ami(ec2resource, image):
616 633 """Remove an AMI and its underlying snapshots."""
617 634 snapshots = []
618 635
619 636 for device in image.block_device_mappings:
620 637 if 'Ebs' in device:
621 638 snapshots.append(ec2resource.Snapshot(device['Ebs']['SnapshotId']))
622 639
623 640 print('deregistering %s' % image.id)
624 641 image.deregister()
625 642
626 643 for snapshot in snapshots:
627 644 print('deleting snapshot %s' % snapshot.id)
628 645 snapshot.delete()
629 646
630 647
631 648 def wait_for_ssm(ssmclient, instances):
632 649 """Wait for SSM to come online for an iterable of instance IDs."""
633 650 while True:
634 651 res = ssmclient.describe_instance_information(
635 652 Filters=[
636 653 {
637 654 'Key': 'InstanceIds',
638 655 'Values': [i.id for i in instances],
639 656 },
640 657 ],
641 658 )
642 659
643 660 available = len(res['InstanceInformationList'])
644 661 wanted = len(instances)
645 662
646 663 print('%d/%d instances available in SSM' % (available, wanted))
647 664
648 665 if available == wanted:
649 666 return
650 667
651 668 time.sleep(2)
652 669
653 670
654 671 def run_ssm_command(ssmclient, instances, document_name, parameters):
655 672 """Run a PowerShell script on an EC2 instance."""
656 673
657 674 res = ssmclient.send_command(
658 675 InstanceIds=[i.id for i in instances],
659 676 DocumentName=document_name,
660 677 Parameters=parameters,
661 678 CloudWatchOutputConfig={
662 679 'CloudWatchOutputEnabled': True,
663 680 },
664 681 )
665 682
666 683 command_id = res['Command']['CommandId']
667 684
668 685 for instance in instances:
669 686 while True:
670 687 try:
671 688 res = ssmclient.get_command_invocation(
672 689 CommandId=command_id,
673 690 InstanceId=instance.id,
674 691 )
675 692 except botocore.exceptions.ClientError as e:
676 693 if e.response['Error']['Code'] == 'InvocationDoesNotExist':
677 694 print('could not find SSM command invocation; waiting')
678 695 time.sleep(1)
679 696 continue
680 697 else:
681 698 raise
682 699
683 700 if res['Status'] == 'Success':
684 701 break
685 702 elif res['Status'] in ('Pending', 'InProgress', 'Delayed'):
686 703 time.sleep(2)
687 704 else:
688 705 raise Exception(
689 706 'command failed on %s: %s' % (instance.id, res['Status'])
690 707 )
691 708
692 709
693 710 @contextlib.contextmanager
694 711 def temporary_ec2_instances(ec2resource, config):
695 712 """Create temporary EC2 instances.
696 713
697 714 This is a proxy to ``ec2client.run_instances(**config)`` that takes care of
698 715 managing the lifecycle of the instances.
699 716
700 717 When the context manager exits, the instances are terminated.
701 718
702 719 The context manager evaluates to the list of data structures
703 720 describing each created instance. The instances may not be available
704 721 for work immediately: it is up to the caller to wait for the instance
705 722 to start responding.
706 723 """
707 724
708 725 ids = None
709 726
710 727 try:
711 728 res = ec2resource.create_instances(**config)
712 729
713 730 ids = [i.id for i in res]
714 731 print('started instances: %s' % ' '.join(ids))
715 732
716 733 yield res
717 734 finally:
718 735 if ids:
719 736 print('terminating instances: %s' % ' '.join(ids))
720 737 for instance in res:
721 738 instance.terminate()
722 739 print('terminated %d instances' % len(ids))
723 740
724 741
725 742 @contextlib.contextmanager
726 743 def create_temp_windows_ec2_instances(
727 744 c: AWSConnection, config, bootstrap: bool = False
728 745 ):
729 746 """Create temporary Windows EC2 instances.
730 747
731 748 This is a higher-level wrapper around ``create_temp_ec2_instances()`` that
732 749 configures the Windows instance for Windows Remote Management. The emitted
733 750 instances will have a ``winrm_client`` attribute containing a
734 751 ``pypsrp.client.Client`` instance bound to the instance.
735 752 """
736 753 if 'IamInstanceProfile' in config:
737 754 raise ValueError('IamInstanceProfile cannot be provided in config')
738 755 if 'UserData' in config:
739 756 raise ValueError('UserData cannot be provided in config')
740 757
741 758 password = c.automation.default_password()
742 759
743 760 config = copy.deepcopy(config)
744 761 config['IamInstanceProfile'] = {
745 762 'Name': 'hg-ephemeral-ec2-1',
746 763 }
747 764 config.setdefault('TagSpecifications', []).append(
748 765 {
749 766 'ResourceType': 'instance',
750 767 'Tags': [{'Key': 'Name', 'Value': 'hg-temp-windows'}],
751 768 }
752 769 )
753 770
754 771 if bootstrap:
755 config['UserData'] = WINDOWS_USER_DATA % password
772 config['UserData'] = WINDOWS_USER_DATA % (password, password)
756 773
757 774 with temporary_ec2_instances(c.ec2resource, config) as instances:
758 775 wait_for_ip_addresses(instances)
759 776
760 777 print('waiting for Windows Remote Management service...')
761 778
762 779 for instance in instances:
763 780 client = wait_for_winrm(
764 781 instance.public_ip_address, 'Administrator', password
765 782 )
766 783 print('established WinRM connection to %s' % instance.id)
767 784 instance.winrm_client = client
768 785
769 786 yield instances
770 787
771 788
772 789 def resolve_fingerprint(fingerprint):
773 790 fingerprint = json.dumps(fingerprint, sort_keys=True)
774 791 return hashlib.sha256(fingerprint.encode('utf-8')).hexdigest()
775 792
776 793
777 794 def find_and_reconcile_image(ec2resource, name, fingerprint):
778 795 """Attempt to find an existing EC2 AMI with a name and fingerprint.
779 796
780 797 If an image with the specified fingerprint is found, it is returned.
781 798 Otherwise None is returned.
782 799
783 800 Existing images for the specified name that don't have the specified
784 801 fingerprint or are missing required metadata or deleted.
785 802 """
786 803 # Find existing AMIs with this name and delete the ones that are invalid.
787 804 # Store a reference to a good image so it can be returned one the
788 805 # image state is reconciled.
789 806 images = ec2resource.images.filter(
790 807 Filters=[{'Name': 'name', 'Values': [name]}]
791 808 )
792 809
793 810 existing_image = None
794 811
795 812 for image in images:
796 813 if image.tags is None:
797 814 print(
798 815 'image %s for %s lacks required tags; removing'
799 816 % (image.id, image.name)
800 817 )
801 818 remove_ami(ec2resource, image)
802 819 else:
803 820 tags = {t['Key']: t['Value'] for t in image.tags}
804 821
805 822 if tags.get('HGIMAGEFINGERPRINT') == fingerprint:
806 823 existing_image = image
807 824 else:
808 825 print(
809 826 'image %s for %s has wrong fingerprint; removing'
810 827 % (image.id, image.name)
811 828 )
812 829 remove_ami(ec2resource, image)
813 830
814 831 return existing_image
815 832
816 833
817 834 def create_ami_from_instance(
818 835 ec2client, instance, name, description, fingerprint
819 836 ):
820 837 """Create an AMI from a running instance.
821 838
822 839 Returns the ``ec2resource.Image`` representing the created AMI.
823 840 """
824 841 instance.stop()
825 842
826 843 ec2client.get_waiter('instance_stopped').wait(
827 844 InstanceIds=[instance.id],
828 845 WaiterConfig={
829 846 'Delay': 5,
830 847 },
831 848 )
832 849 print('%s is stopped' % instance.id)
833 850
834 851 image = instance.create_image(
835 852 Name=name,
836 853 Description=description,
837 854 )
838 855
839 856 image.create_tags(
840 857 Tags=[
841 858 {
842 859 'Key': 'HGIMAGEFINGERPRINT',
843 860 'Value': fingerprint,
844 861 },
845 862 ]
846 863 )
847 864
848 865 print('waiting for image %s' % image.id)
849 866
850 867 ec2client.get_waiter('image_available').wait(
851 868 ImageIds=[image.id],
852 869 )
853 870
854 871 print('image %s available as %s' % (image.id, image.name))
855 872
856 873 return image
857 874
858 875
859 876 def ensure_linux_dev_ami(c: AWSConnection, distro='debian10', prefix='hg-'):
860 877 """Ensures a Linux development AMI is available and up-to-date.
861 878
862 879 Returns an ``ec2.Image`` of either an existing AMI or a newly-built one.
863 880 """
864 881 ec2client = c.ec2client
865 882 ec2resource = c.ec2resource
866 883
867 884 name = '%s%s-%s' % (prefix, 'linux-dev', distro)
868 885
869 886 if distro == 'debian9':
870 887 image = find_image(
871 888 ec2resource,
872 889 DEBIAN_ACCOUNT_ID,
873 890 'debian-stretch-hvm-x86_64-gp2-2019-09-08-17994',
874 891 )
875 892 ssh_username = 'admin'
876 893 elif distro == 'debian10':
877 894 image = find_image(
878 895 ec2resource,
879 896 DEBIAN_ACCOUNT_ID_2,
880 897 'debian-10-amd64-20190909-10',
881 898 )
882 899 ssh_username = 'admin'
883 900 elif distro == 'ubuntu18.04':
884 901 image = find_image(
885 902 ec2resource,
886 903 UBUNTU_ACCOUNT_ID,
887 904 'ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20190918',
888 905 )
889 906 ssh_username = 'ubuntu'
890 907 elif distro == 'ubuntu19.04':
891 908 image = find_image(
892 909 ec2resource,
893 910 UBUNTU_ACCOUNT_ID,
894 911 'ubuntu/images/hvm-ssd/ubuntu-disco-19.04-amd64-server-20190918',
895 912 )
896 913 ssh_username = 'ubuntu'
897 914 else:
898 915 raise ValueError('unsupported Linux distro: %s' % distro)
899 916
900 917 config = {
901 918 'BlockDeviceMappings': [
902 919 {
903 920 'DeviceName': image.block_device_mappings[0]['DeviceName'],
904 921 'Ebs': {
905 922 'DeleteOnTermination': True,
906 923 'VolumeSize': 10,
907 924 'VolumeType': 'gp3',
908 925 },
909 926 },
910 927 ],
911 928 'EbsOptimized': True,
912 929 'ImageId': image.id,
913 930 'InstanceInitiatedShutdownBehavior': 'stop',
914 931 # 8 VCPUs for compiling Python.
915 932 'InstanceType': 't3.2xlarge',
916 933 'KeyName': '%sautomation' % prefix,
917 934 'MaxCount': 1,
918 935 'MinCount': 1,
919 936 'SecurityGroupIds': [c.security_groups['linux-dev-1'].id],
920 937 }
921 938
922 939 requirements3_path = (
923 940 pathlib.Path(__file__).parent.parent / 'linux-requirements-py3.txt'
924 941 )
925 942 requirements35_path = (
926 943 pathlib.Path(__file__).parent.parent / 'linux-requirements-py3.5.txt'
927 944 )
928 945 with requirements3_path.open('r', encoding='utf-8') as fh:
929 946 requirements3 = fh.read()
930 947 with requirements35_path.open('r', encoding='utf-8') as fh:
931 948 requirements35 = fh.read()
932 949
933 950 # Compute a deterministic fingerprint to determine whether image needs to
934 951 # be regenerated.
935 952 fingerprint = resolve_fingerprint(
936 953 {
937 954 'instance_config': config,
938 955 'bootstrap_script': BOOTSTRAP_DEBIAN,
939 956 'requirements_py3': requirements3,
940 957 'requirements_py35': requirements35,
941 958 }
942 959 )
943 960
944 961 existing_image = find_and_reconcile_image(ec2resource, name, fingerprint)
945 962
946 963 if existing_image:
947 964 return existing_image
948 965
949 966 print('no suitable %s image found; creating one...' % name)
950 967
951 968 with temporary_ec2_instances(ec2resource, config) as instances:
952 969 wait_for_ip_addresses(instances)
953 970
954 971 instance = instances[0]
955 972
956 973 client = wait_for_ssh(
957 974 instance.public_ip_address,
958 975 22,
959 976 username=ssh_username,
960 977 key_filename=str(c.key_pair_path_private('automation')),
961 978 )
962 979
963 980 home = '/home/%s' % ssh_username
964 981
965 982 with client:
966 983 print('connecting to SSH server')
967 984 sftp = client.open_sftp()
968 985
969 986 print('uploading bootstrap files')
970 987 with sftp.open('%s/bootstrap' % home, 'wb') as fh:
971 988 fh.write(BOOTSTRAP_DEBIAN)
972 989 fh.chmod(0o0700)
973 990
974 991 with sftp.open('%s/requirements-py3.txt' % home, 'wb') as fh:
975 992 fh.write(requirements3)
976 993 fh.chmod(0o0700)
977 994
978 995 with sftp.open('%s/requirements-py3.5.txt' % home, 'wb') as fh:
979 996 fh.write(requirements35)
980 997 fh.chmod(0o0700)
981 998
982 999 print('executing bootstrap')
983 1000 chan, stdin, stdout = ssh_exec_command(
984 1001 client, '%s/bootstrap' % home
985 1002 )
986 1003 stdin.close()
987 1004
988 1005 for line in stdout:
989 1006 print(line, end='')
990 1007
991 1008 res = chan.recv_exit_status()
992 1009 if res:
993 1010 raise Exception('non-0 exit from bootstrap: %d' % res)
994 1011
995 1012 print(
996 1013 'bootstrap completed; stopping %s to create %s'
997 1014 % (instance.id, name)
998 1015 )
999 1016
1000 1017 return create_ami_from_instance(
1001 1018 ec2client,
1002 1019 instance,
1003 1020 name,
1004 1021 'Mercurial Linux development environment',
1005 1022 fingerprint,
1006 1023 )
1007 1024
1008 1025
1009 1026 @contextlib.contextmanager
1010 1027 def temporary_linux_dev_instances(
1011 1028 c: AWSConnection,
1012 1029 image,
1013 1030 instance_type,
1014 1031 prefix='hg-',
1015 1032 ensure_extra_volume=False,
1016 1033 ):
1017 1034 """Create temporary Linux development EC2 instances.
1018 1035
1019 1036 Context manager resolves to a list of ``ec2.Instance`` that were created
1020 1037 and are running.
1021 1038
1022 1039 ``ensure_extra_volume`` can be set to ``True`` to require that instances
1023 1040 have a 2nd storage volume available other than the primary AMI volume.
1024 1041 For instance types with instance storage, this does nothing special.
1025 1042 But for instance types without instance storage, an additional EBS volume
1026 1043 will be added to the instance.
1027 1044
1028 1045 Instances have an ``ssh_client`` attribute containing a paramiko SSHClient
1029 1046 instance bound to the instance.
1030 1047
1031 1048 Instances have an ``ssh_private_key_path`` attributing containing the
1032 1049 str path to the SSH private key to connect to the instance.
1033 1050 """
1034 1051
1035 1052 block_device_mappings = [
1036 1053 {
1037 1054 'DeviceName': image.block_device_mappings[0]['DeviceName'],
1038 1055 'Ebs': {
1039 1056 'DeleteOnTermination': True,
1040 1057 'VolumeSize': 12,
1041 1058 'VolumeType': 'gp3',
1042 1059 },
1043 1060 }
1044 1061 ]
1045 1062
1046 1063 # This is not an exhaustive list of instance types having instance storage.
1047 1064 # But
1048 1065 if ensure_extra_volume and not instance_type.startswith(
1049 1066 tuple(INSTANCE_TYPES_WITH_STORAGE)
1050 1067 ):
1051 1068 main_device = block_device_mappings[0]['DeviceName']
1052 1069
1053 1070 if main_device == 'xvda':
1054 1071 second_device = 'xvdb'
1055 1072 elif main_device == '/dev/sda1':
1056 1073 second_device = '/dev/sdb'
1057 1074 else:
1058 1075 raise ValueError(
1059 1076 'unhandled primary EBS device name: %s' % main_device
1060 1077 )
1061 1078
1062 1079 block_device_mappings.append(
1063 1080 {
1064 1081 'DeviceName': second_device,
1065 1082 'Ebs': {
1066 1083 'DeleteOnTermination': True,
1067 1084 'VolumeSize': 8,
1068 1085 'VolumeType': 'gp3',
1069 1086 },
1070 1087 }
1071 1088 )
1072 1089
1073 1090 config = {
1074 1091 'BlockDeviceMappings': block_device_mappings,
1075 1092 'EbsOptimized': True,
1076 1093 'ImageId': image.id,
1077 1094 'InstanceInitiatedShutdownBehavior': 'terminate',
1078 1095 'InstanceType': instance_type,
1079 1096 'KeyName': '%sautomation' % prefix,
1080 1097 'MaxCount': 1,
1081 1098 'MinCount': 1,
1082 1099 'SecurityGroupIds': [c.security_groups['linux-dev-1'].id],
1083 1100 }
1084 1101
1085 1102 with temporary_ec2_instances(c.ec2resource, config) as instances:
1086 1103 wait_for_ip_addresses(instances)
1087 1104
1088 1105 ssh_private_key_path = str(c.key_pair_path_private('automation'))
1089 1106
1090 1107 for instance in instances:
1091 1108 client = wait_for_ssh(
1092 1109 instance.public_ip_address,
1093 1110 22,
1094 1111 username='hg',
1095 1112 key_filename=ssh_private_key_path,
1096 1113 )
1097 1114
1098 1115 instance.ssh_client = client
1099 1116 instance.ssh_private_key_path = ssh_private_key_path
1100 1117
1101 1118 try:
1102 1119 yield instances
1103 1120 finally:
1104 1121 for instance in instances:
1105 1122 instance.ssh_client.close()
1106 1123
1107 1124
1108 1125 def ensure_windows_dev_ami(
1109 1126 c: AWSConnection,
1110 1127 prefix='hg-',
1111 1128 base_image_name=WINDOWS_BASE_IMAGE_NAME,
1112 1129 ):
1113 1130 """Ensure Windows Development AMI is available and up-to-date.
1114 1131
1115 1132 If necessary, a modern AMI will be built by starting a temporary EC2
1116 1133 instance and bootstrapping it.
1117 1134
1118 1135 Obsolete AMIs will be deleted so there is only a single AMI having the
1119 1136 desired name.
1120 1137
1121 1138 Returns an ``ec2.Image`` of either an existing AMI or a newly-built
1122 1139 one.
1123 1140 """
1124 1141 ec2client = c.ec2client
1125 1142 ec2resource = c.ec2resource
1126 1143 ssmclient = c.session.client('ssm')
1127 1144
1128 1145 name = '%s%s' % (prefix, 'windows-dev')
1129 1146
1130 1147 image = find_image(
1131 1148 ec2resource,
1132 1149 AMAZON_ACCOUNT_ID,
1133 1150 base_image_name,
1134 1151 reverse_sort_field="name",
1135 1152 )
1136 1153
1137 1154 config = {
1138 1155 'BlockDeviceMappings': [
1139 1156 {
1140 1157 'DeviceName': '/dev/sda1',
1141 1158 'Ebs': {
1142 1159 'DeleteOnTermination': True,
1143 1160 'VolumeSize': 32,
1144 1161 'VolumeType': 'gp3',
1145 1162 },
1146 1163 }
1147 1164 ],
1148 1165 'ImageId': image.id,
1149 1166 'InstanceInitiatedShutdownBehavior': 'stop',
1150 1167 'InstanceType': 'm6i.large',
1151 1168 'KeyName': '%sautomation' % prefix,
1152 1169 'MaxCount': 1,
1153 1170 'MinCount': 1,
1154 1171 'SecurityGroupIds': [c.security_groups['windows-dev-1'].id],
1155 1172 }
1156 1173
1157 1174 commands = [
1158 1175 # Need to start the service so sshd_config is generated.
1159 1176 'Start-Service sshd',
1160 1177 'Write-Output "modifying sshd_config"',
1161 1178 r'$content = Get-Content C:\ProgramData\ssh\sshd_config',
1162 1179 '$content = $content -replace "Match Group administrators","" -replace "AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys",""',
1163 1180 r'$content | Set-Content C:\ProgramData\ssh\sshd_config',
1164 1181 'Import-Module OpenSSHUtils',
1165 1182 r'Repair-SshdConfigPermission C:\ProgramData\ssh\sshd_config -Confirm:$false',
1166 1183 'Restart-Service sshd',
1167 1184 'Write-Output "installing OpenSSL client"',
1168 1185 'Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0',
1169 1186 'Set-Service -Name sshd -StartupType "Automatic"',
1170 1187 'Write-Output "OpenSSH server running"',
1171 1188 ]
1172 1189
1173 1190 with INSTALL_WINDOWS_DEPENDENCIES.open('r', encoding='utf-8') as fh:
1174 1191 commands.extend(l.rstrip() for l in fh)
1175 1192
1176 # Schedule run of EC2Launch on next boot. This ensures that UserData
1177 # is executed.
1178 # We disable setComputerName because it forces a reboot.
1179 # We set an explicit admin password because this causes UserData to run
1180 # as Administrator instead of System.
1181 commands.extend(
1182 [
1183 r'''Set-Content -Path C:\ProgramData\Amazon\EC2-Windows\Launch\Config\LaunchConfig.json '''
1184 r'''-Value '{"setComputerName": false, "setWallpaper": true, "addDnsSuffixList": true, '''
1185 r'''"extendBootVolumeSize": true, "handleUserData": true, '''
1186 r'''"adminPasswordType": "Specify", "adminPassword": "%s"}' '''
1187 % c.automation.default_password(),
1188 r'C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeInstance.ps1 '
1189 r'–Schedule',
1190 ]
1191 )
1192
1193 1193 # Disable Windows Defender when bootstrapping because it just slows
1194 1194 # things down.
1195 1195 commands.insert(0, 'Set-MpPreference -DisableRealtimeMonitoring $true')
1196 1196 commands.append('Set-MpPreference -DisableRealtimeMonitoring $false')
1197 1197
1198 # Trigger shutdown to prepare for imaging.
1199 commands.append(
1200 'Stop-Computer -ComputerName localhost',
1201 )
1202
1198 1203 # Compute a deterministic fingerprint to determine whether image needs
1199 1204 # to be regenerated.
1200 1205 fingerprint = resolve_fingerprint(
1201 1206 {
1202 1207 'instance_config': config,
1203 1208 'user_data': WINDOWS_USER_DATA,
1204 1209 'initial_bootstrap': WINDOWS_BOOTSTRAP_POWERSHELL,
1205 1210 'bootstrap_commands': commands,
1206 1211 'base_image_name': base_image_name,
1207 1212 }
1208 1213 )
1209 1214
1210 1215 existing_image = find_and_reconcile_image(ec2resource, name, fingerprint)
1211 1216
1212 1217 if existing_image:
1213 1218 return existing_image
1214 1219
1215 1220 print('no suitable Windows development image found; creating one...')
1216 1221
1217 1222 with create_temp_windows_ec2_instances(
1218 1223 c, config, bootstrap=True
1219 1224 ) as instances:
1220 1225 assert len(instances) == 1
1221 1226 instance = instances[0]
1222 1227
1223 1228 wait_for_ssm(ssmclient, [instance])
1224 1229
1225 1230 # On first boot, install various Windows updates.
1226 1231 # We would ideally use PowerShell Remoting for this. However, there are
1227 1232 # trust issues that make it difficult to invoke Windows Update
1228 1233 # remotely. So we use SSM, which has a mechanism for running Windows
1229 1234 # Update.
1230 1235 print('installing Windows features...')
1231 1236 run_ssm_command(
1232 1237 ssmclient,
1233 1238 [instance],
1234 1239 'AWS-RunPowerShellScript',
1235 1240 {
1236 1241 'commands': WINDOWS_BOOTSTRAP_POWERSHELL.split('\n'),
1237 1242 },
1238 1243 )
1239 1244
1240 1245 # Reboot so all updates are fully applied.
1241 1246 #
1242 1247 # We don't use instance.reboot() here because it is asynchronous and
1243 1248 # we don't know when exactly the instance has rebooted. It could take
1244 1249 # a while to stop and we may start trying to interact with the instance
1245 1250 # before it has rebooted.
1246 1251 print('rebooting instance %s' % instance.id)
1247 1252 instance.stop()
1248 1253 ec2client.get_waiter('instance_stopped').wait(
1249 1254 InstanceIds=[instance.id],
1250 1255 WaiterConfig={
1251 1256 'Delay': 5,
1252 1257 },
1253 1258 )
1254 1259
1255 1260 instance.start()
1256 1261 wait_for_ip_addresses([instance])
1257 1262
1258 1263 # There is a race condition here between the User Data PS script running
1259 1264 # and us connecting to WinRM. This can manifest as
1260 1265 # "AuthorizationManager check failed" failures during run_powershell().
1261 1266 # TODO figure out a workaround.
1262 1267
1263 1268 print('waiting for Windows Remote Management to come back...')
1264 1269 client = wait_for_winrm(
1265 1270 instance.public_ip_address,
1266 1271 'Administrator',
1267 1272 c.automation.default_password(),
1268 1273 )
1269 1274 print('established WinRM connection to %s' % instance.id)
1270 1275 instance.winrm_client = client
1271 1276
1272 1277 print('bootstrapping instance...')
1273 1278 run_powershell(instance.winrm_client, '\n'.join(commands))
1274 1279
1275 1280 print('bootstrap completed; stopping %s to create image' % instance.id)
1276 1281 return create_ami_from_instance(
1277 1282 ec2client,
1278 1283 instance,
1279 1284 name,
1280 1285 'Mercurial Windows development environment',
1281 1286 fingerprint,
1282 1287 )
1283 1288
1284 1289
1285 1290 @contextlib.contextmanager
1286 1291 def temporary_windows_dev_instances(
1287 1292 c: AWSConnection,
1288 1293 image,
1289 1294 instance_type,
1290 1295 prefix='hg-',
1291 1296 disable_antivirus=False,
1292 1297 ):
1293 1298 """Create a temporary Windows development EC2 instance.
1294 1299
1295 1300 Context manager resolves to the list of ``EC2.Instance`` that were created.
1296 1301 """
1297 1302 config = {
1298 1303 'BlockDeviceMappings': [
1299 1304 {
1300 1305 'DeviceName': '/dev/sda1',
1301 1306 'Ebs': {
1302 1307 'DeleteOnTermination': True,
1303 1308 'VolumeSize': 32,
1304 1309 'VolumeType': 'gp3',
1305 1310 },
1306 1311 }
1307 1312 ],
1308 1313 'ImageId': image.id,
1309 1314 'InstanceInitiatedShutdownBehavior': 'stop',
1310 1315 'InstanceType': instance_type,
1311 1316 'KeyName': '%sautomation' % prefix,
1312 1317 'MaxCount': 1,
1313 1318 'MinCount': 1,
1314 1319 'SecurityGroupIds': [c.security_groups['windows-dev-1'].id],
1315 1320 }
1316 1321
1317 1322 with create_temp_windows_ec2_instances(c, config) as instances:
1318 1323 if disable_antivirus:
1319 1324 for instance in instances:
1320 1325 run_powershell(
1321 1326 instance.winrm_client,
1322 1327 'Set-MpPreference -DisableRealtimeMonitoring $true',
1323 1328 )
1324 1329
1325 1330 yield instances
@@ -1,539 +1,542 b''
1 1 # windows.py - Automation specific to Windows
2 2 #
3 3 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 # no-check-code because Python 3 native.
9 9
10 10 import datetime
11 11 import os
12 12 import paramiko
13 13 import pathlib
14 14 import re
15 15 import subprocess
16 16 import tempfile
17 17
18 18 from .pypi import upload as pypi_upload
19 19 from .winrm import run_powershell
20 20
21 21
22 22 HG_PURGE = r'''
23 23 $Env:PATH = "C:\hgdev\venv-bootstrap\Scripts;$Env:PATH"
24 24 Set-Location C:\hgdev\src
25 25 hg.exe --config extensions.purge= purge --all
26 26 if ($LASTEXITCODE -ne 0) {
27 27 throw "process exited non-0: $LASTEXITCODE"
28 28 }
29 29 Write-Output "purged Mercurial repo"
30 30 '''
31 31
32 32 HG_UPDATE_CLEAN = r'''
33 33 $Env:PATH = "C:\hgdev\venv-bootstrap\Scripts;$Env:PATH"
34 34 Set-Location C:\hgdev\src
35 35 hg.exe --config extensions.purge= purge --all
36 36 if ($LASTEXITCODE -ne 0) {{
37 37 throw "process exited non-0: $LASTEXITCODE"
38 38 }}
39 39 hg.exe update -C {revision}
40 40 if ($LASTEXITCODE -ne 0) {{
41 41 throw "process exited non-0: $LASTEXITCODE"
42 42 }}
43 43 hg.exe log -r .
44 44 Write-Output "updated Mercurial working directory to {revision}"
45 45 '''.lstrip()
46 46
47 47 BUILD_INNO_PYTHON3 = r'''
48 $Env:PATH = "C:\hgdev\venv-bootstrap\Scripts;$Env:PATH"
48 49 $Env:RUSTUP_HOME = "C:\hgdev\rustup"
49 50 $Env:CARGO_HOME = "C:\hgdev\cargo"
50 51 Set-Location C:\hgdev\src
51 52 C:\hgdev\python37-x64\python.exe contrib\packaging\packaging.py inno --pyoxidizer-target {pyoxidizer_target} --version {version}
52 53 if ($LASTEXITCODE -ne 0) {{
53 54 throw "process exited non-0: $LASTEXITCODE"
54 55 }}
55 56 '''
56 57
57 58
58 59 BUILD_WHEEL = r'''
60 $Env:PATH = "C:\hgdev\venv-bootstrap\Scripts;$Env:PATH"
59 61 Set-Location C:\hgdev\src
60 62 C:\hgdev\python{python_version}-{arch}\python.exe -m pip wheel --wheel-dir dist .
61 63 if ($LASTEXITCODE -ne 0) {{
62 64 throw "process exited non-0: $LASTEXITCODE"
63 65 }}
64 66 '''
65 67
66 68 BUILD_WIX_PYTHON3 = r'''
69 $Env:PATH = "C:\hgdev\venv-bootstrap\Scripts;$Env:PATH"
67 70 $Env:RUSTUP_HOME = "C:\hgdev\rustup"
68 71 $Env:CARGO_HOME = "C:\hgdev\cargo"
69 72 Set-Location C:\hgdev\src
70 73 C:\hgdev\python37-x64\python.exe contrib\packaging\packaging.py wix --pyoxidizer-target {pyoxidizer_target} --version {version}
71 74 if ($LASTEXITCODE -ne 0) {{
72 75 throw "process exited non-0: $LASTEXITCODE"
73 76 }}
74 77 '''
75 78
76 79
77 80 RUN_TESTS = r'''
78 81 C:\hgdev\MinGW\msys\1.0\bin\sh.exe --login -c "cd /c/hgdev/src/tests && /c/hgdev/{python_path}/python.exe run-tests.py {test_flags}"
79 82 if ($LASTEXITCODE -ne 0) {{
80 83 throw "process exited non-0: $LASTEXITCODE"
81 84 }}
82 85 '''
83 86
84 87
85 88 WHEEL_FILENAME_PYTHON37_X86 = 'mercurial-{version}-cp37-cp37m-win32.whl'
86 89 WHEEL_FILENAME_PYTHON37_X64 = 'mercurial-{version}-cp37-cp37m-win_amd64.whl'
87 90 WHEEL_FILENAME_PYTHON38_X86 = 'mercurial-{version}-cp38-cp38-win32.whl'
88 91 WHEEL_FILENAME_PYTHON38_X64 = 'mercurial-{version}-cp38-cp38-win_amd64.whl'
89 92 WHEEL_FILENAME_PYTHON39_X86 = 'mercurial-{version}-cp39-cp39-win32.whl'
90 93 WHEEL_FILENAME_PYTHON39_X64 = 'mercurial-{version}-cp39-cp39-win_amd64.whl'
91 94 WHEEL_FILENAME_PYTHON310_X86 = 'mercurial-{version}-cp310-cp310-win32.whl'
92 95 WHEEL_FILENAME_PYTHON310_X64 = 'mercurial-{version}-cp310-cp310-win_amd64.whl'
93 96
94 97 EXE_FILENAME_PYTHON3_X86 = 'Mercurial-{version}-x86.exe'
95 98 EXE_FILENAME_PYTHON3_X64 = 'Mercurial-{version}-x64.exe'
96 99
97 100 MSI_FILENAME_PYTHON3_X86 = 'mercurial-{version}-x86.msi'
98 101 MSI_FILENAME_PYTHON3_X64 = 'mercurial-{version}-x64.msi'
99 102
100 103 MERCURIAL_SCM_BASE_URL = 'https://mercurial-scm.org/release/windows'
101 104
102 105 X86_USER_AGENT_PATTERN = '.*Windows.*'
103 106 X64_USER_AGENT_PATTERN = '.*Windows.*(WOW|x)64.*'
104 107
105 108 # TODO remove Python version once Python 2 is dropped.
106 109 EXE_PYTHON3_X86_DESCRIPTION = (
107 110 'Mercurial {version} Inno Setup installer - x86 Windows (Python 3) '
108 111 '- does not require admin rights'
109 112 )
110 113 EXE_PYTHON3_X64_DESCRIPTION = (
111 114 'Mercurial {version} Inno Setup installer - x64 Windows (Python 3) '
112 115 '- does not require admin rights'
113 116 )
114 117 MSI_PYTHON3_X86_DESCRIPTION = (
115 118 'Mercurial {version} MSI installer - x86 Windows (Python 3) '
116 119 '- requires admin rights'
117 120 )
118 121 MSI_PYTHON3_X64_DESCRIPTION = (
119 122 'Mercurial {version} MSI installer - x64 Windows (Python 3) '
120 123 '- requires admin rights'
121 124 )
122 125
123 126
124 127 def fix_authorized_keys_permissions(winrm_client, path):
125 128 commands = [
126 129 '$ErrorActionPreference = "Stop"',
127 130 'Repair-AuthorizedKeyPermission -FilePath %s -Confirm:$false' % path,
128 131 r'icacls %s /remove:g "NT Service\sshd"' % path,
129 132 ]
130 133
131 134 run_powershell(winrm_client, '\n'.join(commands))
132 135
133 136
134 137 def synchronize_hg(hg_repo: pathlib.Path, revision: str, ec2_instance):
135 138 """Synchronize local Mercurial repo to remote EC2 instance."""
136 139
137 140 winrm_client = ec2_instance.winrm_client
138 141
139 142 with tempfile.TemporaryDirectory() as temp_dir:
140 143 temp_dir = pathlib.Path(temp_dir)
141 144
142 145 ssh_dir = temp_dir / '.ssh'
143 146 ssh_dir.mkdir()
144 147 ssh_dir.chmod(0o0700)
145 148
146 149 # Generate SSH key to use for communication.
147 150 subprocess.run(
148 151 [
149 152 'ssh-keygen',
150 153 '-t',
151 154 'rsa',
152 155 '-b',
153 156 '4096',
154 157 '-N',
155 158 '',
156 159 '-f',
157 160 str(ssh_dir / 'id_rsa'),
158 161 ],
159 162 check=True,
160 163 capture_output=True,
161 164 )
162 165
163 166 # Add it to ~/.ssh/authorized_keys on remote.
164 167 # This assumes the file doesn't already exist.
165 168 authorized_keys = r'c:\Users\Administrator\.ssh\authorized_keys'
166 169 winrm_client.execute_cmd(r'mkdir c:\Users\Administrator\.ssh')
167 170 winrm_client.copy(str(ssh_dir / 'id_rsa.pub'), authorized_keys)
168 171 fix_authorized_keys_permissions(winrm_client, authorized_keys)
169 172
170 173 public_ip = ec2_instance.public_ip_address
171 174
172 175 ssh_config = temp_dir / '.ssh' / 'config'
173 176
174 177 with open(ssh_config, 'w', encoding='utf-8') as fh:
175 178 fh.write('Host %s\n' % public_ip)
176 179 fh.write(' User Administrator\n')
177 180 fh.write(' StrictHostKeyChecking no\n')
178 181 fh.write(' UserKnownHostsFile %s\n' % (ssh_dir / 'known_hosts'))
179 182 fh.write(' IdentityFile %s\n' % (ssh_dir / 'id_rsa'))
180 183
181 184 if not (hg_repo / '.hg').is_dir():
182 185 raise Exception(
183 186 '%s is not a Mercurial repository; '
184 187 'synchronization not yet supported' % hg_repo
185 188 )
186 189
187 190 env = dict(os.environ)
188 191 env['HGPLAIN'] = '1'
189 192 env['HGENCODING'] = 'utf-8'
190 193
191 194 hg_bin = hg_repo / 'hg'
192 195
193 196 res = subprocess.run(
194 197 ['python3', str(hg_bin), 'log', '-r', revision, '-T', '{node}'],
195 198 cwd=str(hg_repo),
196 199 env=env,
197 200 check=True,
198 201 capture_output=True,
199 202 )
200 203
201 204 full_revision = res.stdout.decode('ascii')
202 205
203 206 args = [
204 207 'python3',
205 208 hg_bin,
206 209 '--config',
207 210 'ui.ssh=ssh -F %s' % ssh_config,
208 211 '--config',
209 212 'ui.remotecmd=c:/hgdev/venv-bootstrap/Scripts/hg.exe',
210 213 # Also ensure .hgtags changes are present so auto version
211 214 # calculation works.
212 215 'push',
213 216 '-f',
214 217 '-r',
215 218 full_revision,
216 219 '-r',
217 220 'file(.hgtags)',
218 221 'ssh://%s/c:/hgdev/src' % public_ip,
219 222 ]
220 223
221 224 res = subprocess.run(args, cwd=str(hg_repo), env=env)
222 225
223 226 # Allow 1 (no-op) to not trigger error.
224 227 if res.returncode not in (0, 1):
225 228 res.check_returncode()
226 229
227 230 run_powershell(
228 231 winrm_client, HG_UPDATE_CLEAN.format(revision=full_revision)
229 232 )
230 233
231 234 # TODO detect dirty local working directory and synchronize accordingly.
232 235
233 236
234 237 def purge_hg(winrm_client):
235 238 """Purge the Mercurial source repository on an EC2 instance."""
236 239 run_powershell(winrm_client, HG_PURGE)
237 240
238 241
239 242 def find_latest_dist(winrm_client, pattern):
240 243 """Find path to newest file in dist/ directory matching a pattern."""
241 244
242 245 res = winrm_client.execute_ps(
243 246 r'$v = Get-ChildItem -Path C:\hgdev\src\dist -Filter "%s" '
244 247 '| Sort-Object LastWriteTime -Descending '
245 248 '| Select-Object -First 1\n'
246 249 '$v.name' % pattern
247 250 )
248 251 return res[0]
249 252
250 253
251 254 def copy_latest_dist(winrm_client, pattern, dest_path):
252 255 """Copy latest file matching pattern in dist/ directory.
253 256
254 257 Given a WinRM client and a file pattern, find the latest file on the remote
255 258 matching that pattern and copy it to the ``dest_path`` directory on the
256 259 local machine.
257 260 """
258 261 latest = find_latest_dist(winrm_client, pattern)
259 262 source = r'C:\hgdev\src\dist\%s' % latest
260 263 dest = dest_path / latest
261 264 print('copying %s to %s' % (source, dest))
262 265 winrm_client.fetch(source, str(dest))
263 266
264 267
265 268 def build_inno_installer(
266 269 winrm_client,
267 270 arch: str,
268 271 dest_path: pathlib.Path,
269 272 version=None,
270 273 ):
271 274 """Build the Inno Setup installer on a remote machine.
272 275
273 276 Using a WinRM client, remote commands are executed to build
274 277 a Mercurial Inno Setup installer.
275 278 """
276 279 print('building Inno Setup installer for %s' % arch)
277 280
278 281 # TODO fix this limitation in packaging code
279 282 if not version:
280 283 raise Exception("version string is required when building for Python 3")
281 284
282 285 if arch == "x86":
283 286 target_triple = "i686-pc-windows-msvc"
284 287 elif arch == "x64":
285 288 target_triple = "x86_64-pc-windows-msvc"
286 289 else:
287 290 raise Exception("unhandled arch: %s" % arch)
288 291
289 292 ps = BUILD_INNO_PYTHON3.format(
290 293 pyoxidizer_target=target_triple,
291 294 version=version,
292 295 )
293 296
294 297 run_powershell(winrm_client, ps)
295 298 copy_latest_dist(winrm_client, '*.exe', dest_path)
296 299
297 300
298 301 def build_wheel(
299 302 winrm_client, python_version: str, arch: str, dest_path: pathlib.Path
300 303 ):
301 304 """Build Python wheels on a remote machine.
302 305
303 306 Using a WinRM client, remote commands are executed to build a Python wheel
304 307 for Mercurial.
305 308 """
306 309 print('Building Windows wheel for Python %s %s' % (python_version, arch))
307 310
308 311 ps = BUILD_WHEEL.format(
309 312 python_version=python_version.replace(".", ""), arch=arch
310 313 )
311 314
312 315 run_powershell(winrm_client, ps)
313 316 copy_latest_dist(winrm_client, '*.whl', dest_path)
314 317
315 318
316 319 def build_wix_installer(
317 320 winrm_client,
318 321 arch: str,
319 322 dest_path: pathlib.Path,
320 323 version=None,
321 324 ):
322 325 """Build the WiX installer on a remote machine.
323 326
324 327 Using a WinRM client, remote commands are executed to build a WiX installer.
325 328 """
326 329 print('Building WiX installer for %s' % arch)
327 330
328 331 # TODO fix this limitation in packaging code
329 332 if not version:
330 333 raise Exception("version string is required when building for Python 3")
331 334
332 335 if arch == "x86":
333 336 target_triple = "i686-pc-windows-msvc"
334 337 elif arch == "x64":
335 338 target_triple = "x86_64-pc-windows-msvc"
336 339 else:
337 340 raise Exception("unhandled arch: %s" % arch)
338 341
339 342 ps = BUILD_WIX_PYTHON3.format(
340 343 pyoxidizer_target=target_triple,
341 344 version=version,
342 345 )
343 346
344 347 run_powershell(winrm_client, ps)
345 348 copy_latest_dist(winrm_client, '*.msi', dest_path)
346 349
347 350
348 351 def run_tests(winrm_client, python_version, arch, test_flags=''):
349 352 """Run tests on a remote Windows machine.
350 353
351 354 ``python_version`` is a ``X.Y`` string like ``2.7`` or ``3.7``.
352 355 ``arch`` is ``x86`` or ``x64``.
353 356 ``test_flags`` is a str representing extra arguments to pass to
354 357 ``run-tests.py``.
355 358 """
356 359 if not re.match(r'\d\.\d', python_version):
357 360 raise ValueError(
358 361 r'python_version must be \d.\d; got %s' % python_version
359 362 )
360 363
361 364 if arch not in ('x86', 'x64'):
362 365 raise ValueError('arch must be x86 or x64; got %s' % arch)
363 366
364 367 python_path = 'python%s-%s' % (python_version.replace('.', ''), arch)
365 368
366 369 ps = RUN_TESTS.format(
367 370 python_path=python_path,
368 371 test_flags=test_flags or '',
369 372 )
370 373
371 374 run_powershell(winrm_client, ps)
372 375
373 376
374 377 def resolve_wheel_artifacts(dist_path: pathlib.Path, version: str):
375 378 return (
376 379 dist_path / WHEEL_FILENAME_PYTHON37_X86.format(version=version),
377 380 dist_path / WHEEL_FILENAME_PYTHON37_X64.format(version=version),
378 381 dist_path / WHEEL_FILENAME_PYTHON38_X86.format(version=version),
379 382 dist_path / WHEEL_FILENAME_PYTHON38_X64.format(version=version),
380 383 dist_path / WHEEL_FILENAME_PYTHON39_X86.format(version=version),
381 384 dist_path / WHEEL_FILENAME_PYTHON39_X64.format(version=version),
382 385 dist_path / WHEEL_FILENAME_PYTHON310_X86.format(version=version),
383 386 dist_path / WHEEL_FILENAME_PYTHON310_X64.format(version=version),
384 387 )
385 388
386 389
387 390 def resolve_all_artifacts(dist_path: pathlib.Path, version: str):
388 391 return (
389 392 dist_path / WHEEL_FILENAME_PYTHON37_X86.format(version=version),
390 393 dist_path / WHEEL_FILENAME_PYTHON37_X64.format(version=version),
391 394 dist_path / WHEEL_FILENAME_PYTHON38_X86.format(version=version),
392 395 dist_path / WHEEL_FILENAME_PYTHON38_X64.format(version=version),
393 396 dist_path / WHEEL_FILENAME_PYTHON39_X86.format(version=version),
394 397 dist_path / WHEEL_FILENAME_PYTHON39_X64.format(version=version),
395 398 dist_path / WHEEL_FILENAME_PYTHON310_X86.format(version=version),
396 399 dist_path / WHEEL_FILENAME_PYTHON310_X64.format(version=version),
397 400 dist_path / EXE_FILENAME_PYTHON3_X86.format(version=version),
398 401 dist_path / EXE_FILENAME_PYTHON3_X64.format(version=version),
399 402 dist_path / MSI_FILENAME_PYTHON3_X86.format(version=version),
400 403 dist_path / MSI_FILENAME_PYTHON3_X64.format(version=version),
401 404 )
402 405
403 406
404 407 def generate_latest_dat(version: str):
405 408 python3_x86_exe_filename = EXE_FILENAME_PYTHON3_X86.format(version=version)
406 409 python3_x64_exe_filename = EXE_FILENAME_PYTHON3_X64.format(version=version)
407 410 python3_x86_msi_filename = MSI_FILENAME_PYTHON3_X86.format(version=version)
408 411 python3_x64_msi_filename = MSI_FILENAME_PYTHON3_X64.format(version=version)
409 412
410 413 entries = (
411 414 (
412 415 '10',
413 416 version,
414 417 X86_USER_AGENT_PATTERN,
415 418 '%s/%s' % (MERCURIAL_SCM_BASE_URL, python3_x86_exe_filename),
416 419 EXE_PYTHON3_X86_DESCRIPTION.format(version=version),
417 420 ),
418 421 (
419 422 '10',
420 423 version,
421 424 X64_USER_AGENT_PATTERN,
422 425 '%s/%s' % (MERCURIAL_SCM_BASE_URL, python3_x64_exe_filename),
423 426 EXE_PYTHON3_X64_DESCRIPTION.format(version=version),
424 427 ),
425 428 (
426 429 '10',
427 430 version,
428 431 X86_USER_AGENT_PATTERN,
429 432 '%s/%s' % (MERCURIAL_SCM_BASE_URL, python3_x86_msi_filename),
430 433 MSI_PYTHON3_X86_DESCRIPTION.format(version=version),
431 434 ),
432 435 (
433 436 '10',
434 437 version,
435 438 X64_USER_AGENT_PATTERN,
436 439 '%s/%s' % (MERCURIAL_SCM_BASE_URL, python3_x64_msi_filename),
437 440 MSI_PYTHON3_X64_DESCRIPTION.format(version=version),
438 441 ),
439 442 )
440 443
441 444 lines = ['\t'.join(e) for e in entries]
442 445
443 446 return '\n'.join(lines) + '\n'
444 447
445 448
446 449 def publish_artifacts_pypi(dist_path: pathlib.Path, version: str):
447 450 """Publish Windows release artifacts to PyPI."""
448 451
449 452 wheel_paths = resolve_wheel_artifacts(dist_path, version)
450 453
451 454 for p in wheel_paths:
452 455 if not p.exists():
453 456 raise Exception('%s not found' % p)
454 457
455 458 print('uploading wheels to PyPI (you may be prompted for credentials)')
456 459 pypi_upload(wheel_paths)
457 460
458 461
459 462 def publish_artifacts_mercurial_scm_org(
460 463 dist_path: pathlib.Path, version: str, ssh_username=None
461 464 ):
462 465 """Publish Windows release artifacts to mercurial-scm.org."""
463 466 all_paths = resolve_all_artifacts(dist_path, version)
464 467
465 468 for p in all_paths:
466 469 if not p.exists():
467 470 raise Exception('%s not found' % p)
468 471
469 472 client = paramiko.SSHClient()
470 473 client.load_system_host_keys()
471 474 # We assume the system SSH configuration knows how to connect.
472 475 print('connecting to mercurial-scm.org via ssh...')
473 476 try:
474 477 client.connect('mercurial-scm.org', username=ssh_username)
475 478 except paramiko.AuthenticationException:
476 479 print('error authenticating; is an SSH key available in an SSH agent?')
477 480 raise
478 481
479 482 print('SSH connection established')
480 483
481 484 print('opening SFTP client...')
482 485 sftp = client.open_sftp()
483 486 print('SFTP client obtained')
484 487
485 488 for p in all_paths:
486 489 dest_path = '/var/www/release/windows/%s' % p.name
487 490 print('uploading %s to %s' % (p, dest_path))
488 491
489 492 with p.open('rb') as fh:
490 493 data = fh.read()
491 494
492 495 with sftp.open(dest_path, 'wb') as fh:
493 496 fh.write(data)
494 497 fh.chmod(0o0664)
495 498
496 499 latest_dat_path = '/var/www/release/windows/latest.dat'
497 500
498 501 now = datetime.datetime.utcnow()
499 502 backup_path = dist_path / (
500 503 'latest-windows-%s.dat' % now.strftime('%Y%m%dT%H%M%S')
501 504 )
502 505 print('backing up %s to %s' % (latest_dat_path, backup_path))
503 506
504 507 with sftp.open(latest_dat_path, 'rb') as fh:
505 508 latest_dat_old = fh.read()
506 509
507 510 with backup_path.open('wb') as fh:
508 511 fh.write(latest_dat_old)
509 512
510 513 print('writing %s with content:' % latest_dat_path)
511 514 latest_dat_content = generate_latest_dat(version)
512 515 print(latest_dat_content)
513 516
514 517 with sftp.open(latest_dat_path, 'wb') as fh:
515 518 fh.write(latest_dat_content.encode('ascii'))
516 519
517 520
518 521 def publish_artifacts(
519 522 dist_path: pathlib.Path,
520 523 version: str,
521 524 pypi=True,
522 525 mercurial_scm_org=True,
523 526 ssh_username=None,
524 527 ):
525 528 """Publish Windows release artifacts.
526 529
527 530 Files are found in `dist_path`. We will look for files with version string
528 531 `version`.
529 532
530 533 `pypi` controls whether we upload to PyPI.
531 534 `mercurial_scm_org` controls whether we upload to mercurial-scm.org.
532 535 """
533 536 if pypi:
534 537 publish_artifacts_pypi(dist_path, version)
535 538
536 539 if mercurial_scm_org:
537 540 publish_artifacts_mercurial_scm_org(
538 541 dist_path, version, ssh_username=ssh_username
539 542 )
@@ -1,193 +1,267 b''
1 1 #
2 # This file is autogenerated by pip-compile
2 # This file is autogenerated by pip-compile with python 3.10
3 3 # To update, run:
4 4 #
5 5 # pip-compile --generate-hashes --output-file=contrib/automation/requirements.txt contrib/automation/requirements.txt.in
6 6 #
7 asn1crypto==1.0.1 \
8 --hash=sha256:0b199f211ae690df3db4fd6c1c4ff976497fb1da689193e368eedbadc53d9292 \
9 --hash=sha256:bca90060bd995c3f62c4433168eab407e44bdbdb567b3f3a396a676c1a4c4a3f \
10 # via cryptography
11 bcrypt==3.1.7 \
12 --hash=sha256:0258f143f3de96b7c14f762c770f5fc56ccd72f8a1857a451c1cd9a655d9ac89 \
13 --hash=sha256:0b0069c752ec14172c5f78208f1863d7ad6755a6fae6fe76ec2c80d13be41e42 \
14 --hash=sha256:19a4b72a6ae5bb467fea018b825f0a7d917789bcfe893e53f15c92805d187294 \
15 --hash=sha256:5432dd7b34107ae8ed6c10a71b4397f1c853bd39a4d6ffa7e35f40584cffd161 \
16 --hash=sha256:69361315039878c0680be456640f8705d76cb4a3a3fe1e057e0f261b74be4b31 \
17 --hash=sha256:6fe49a60b25b584e2f4ef175b29d3a83ba63b3a4df1b4c0605b826668d1b6be5 \
18 --hash=sha256:74a015102e877d0ccd02cdeaa18b32aa7273746914a6c5d0456dd442cb65b99c \
19 --hash=sha256:763669a367869786bb4c8fcf731f4175775a5b43f070f50f46f0b59da45375d0 \
20 --hash=sha256:8b10acde4e1919d6015e1df86d4c217d3b5b01bb7744c36113ea43d529e1c3de \
21 --hash=sha256:9fe92406c857409b70a38729dbdf6578caf9228de0aef5bc44f859ffe971a39e \
22 --hash=sha256:a190f2a5dbbdbff4b74e3103cef44344bc30e61255beb27310e2aec407766052 \
23 --hash=sha256:a595c12c618119255c90deb4b046e1ca3bcfad64667c43d1166f2b04bc72db09 \
24 --hash=sha256:c9457fa5c121e94a58d6505cadca8bed1c64444b83b3204928a866ca2e599105 \
25 --hash=sha256:cb93f6b2ab0f6853550b74e051d297c27a638719753eb9ff66d1e4072be67133 \
26 --hash=sha256:d7bdc26475679dd073ba0ed2766445bb5b20ca4793ca0db32b399dccc6bc84b7 \
27 --hash=sha256:ff032765bb8716d9387fd5376d987a937254b0619eff0972779515b5c98820bc \
7 bcrypt==3.2.2 \
8 --hash=sha256:2b02d6bfc6336d1094276f3f588aa1225a598e27f8e3388f4db9948cb707b521 \
9 --hash=sha256:433c410c2177057705da2a9f2cd01dd157493b2a7ac14c8593a16b3dab6b6bfb \
10 --hash=sha256:4e029cef560967fb0cf4a802bcf4d562d3d6b4b1bf81de5ec1abbe0f1adb027e \
11 --hash=sha256:61bae49580dce88095d669226d5076d0b9d927754cedbdf76c6c9f5099ad6f26 \
12 --hash=sha256:6d2cb9d969bfca5bc08e45864137276e4c3d3d7de2b162171def3d188bf9d34a \
13 --hash=sha256:7180d98a96f00b1050e93f5b0f556e658605dd9f524d0b0e68ae7944673f525e \
14 --hash=sha256:7d9ba2e41e330d2af4af6b1b6ec9e6128e91343d0b4afb9282e54e5508f31baa \
15 --hash=sha256:7ff2069240c6bbe49109fe84ca80508773a904f5a8cb960e02a977f7f519b129 \
16 --hash=sha256:88273d806ab3a50d06bc6a2fc7c87d737dd669b76ad955f449c43095389bc8fb \
17 --hash=sha256:a2c46100e315c3a5b90fdc53e429c006c5f962529bc27e1dfd656292c20ccc40 \
18 --hash=sha256:cd43303d6b8a165c29ec6756afd169faba9396a9472cdff753fe9f19b96ce2fa
28 19 # via paramiko
29 bleach==3.1.0 \
30 --hash=sha256:213336e49e102af26d9cde77dd2d0397afabc5a6bf2fed985dc35b5d1e285a16 \
31 --hash=sha256:3fdf7f77adcf649c9911387df51254b813185e32b2c6619f690b593a617e19fa \
20 bleach==5.0.0 \
21 --hash=sha256:08a1fe86d253b5c88c92cc3d810fd8048a16d15762e1e5b74d502256e5926aa1 \
22 --hash=sha256:c6d6cc054bdc9c83b48b8083e236e5f00f238428666d2ce2e083eaa5fd568565
32 23 # via readme-renderer
33 boto3==1.9.243 \
34 --hash=sha256:404acbecef8f4912f18312fcfaffe7eba7f10b3b7adf7853bdba59cdf2275ebb \
35 --hash=sha256:c6e5a7e4548ce7586c354ff633f2a66ba3c471d15a8ae6a30f873122ab04e1cf
36 botocore==1.12.243 \
37 --hash=sha256:397585a7881230274afb8d1877ef69a661b0a311745cd324f14a052fb2a2863a \
38 --hash=sha256:4496f8da89cb496462a831897ad248e13e431d9fa7e41e06d426fd6658ab6e59 \
39 # via boto3, s3transfer
40 certifi==2021.5.30 \
41 --hash=sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee \
42 --hash=sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8 \
24 boto3==1.22.7 \
25 --hash=sha256:4dc0df36c3465ff0d586017da68b0152123695f38f30ad98fed7185e59298d2c \
26 --hash=sha256:de4fa49ca1cbc93313144e93e6d0997cbb61c8cca91f3418b4e3646dc215f441
27 # via -r contrib/automation/requirements.txt.in
28 botocore==1.25.7 \
29 --hash=sha256:190361776d96323ff401b976175f76172acf7ebbe3efbb19c4c6f9800a9ad6b6 \
30 --hash=sha256:83da857c12fff5cf4c1a64afaa72a28ff5bef929a2834ec4ed5b0b674f88fd0e
31 # via
32 # boto3
33 # s3transfer
34 certifi==2021.10.8 \
35 --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 \
36 --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569
43 37 # via requests
44 cffi==1.12.3 \
45 --hash=sha256:041c81822e9f84b1d9c401182e174996f0bae9991f33725d059b771744290774 \
46 --hash=sha256:046ef9a22f5d3eed06334d01b1e836977eeef500d9b78e9ef693f9380ad0b83d \
47 --hash=sha256:066bc4c7895c91812eff46f4b1c285220947d4aa46fa0a2651ff85f2afae9c90 \
48 --hash=sha256:066c7ff148ae33040c01058662d6752fd73fbc8e64787229ea8498c7d7f4041b \
49 --hash=sha256:2444d0c61f03dcd26dbf7600cf64354376ee579acad77aef459e34efcb438c63 \
50 --hash=sha256:300832850b8f7967e278870c5d51e3819b9aad8f0a2c8dbe39ab11f119237f45 \
51 --hash=sha256:34c77afe85b6b9e967bd8154e3855e847b70ca42043db6ad17f26899a3df1b25 \
52 --hash=sha256:46de5fa00f7ac09f020729148ff632819649b3e05a007d286242c4882f7b1dc3 \
53 --hash=sha256:4aa8ee7ba27c472d429b980c51e714a24f47ca296d53f4d7868075b175866f4b \
54 --hash=sha256:4d0004eb4351e35ed950c14c11e734182591465a33e960a4ab5e8d4f04d72647 \
55 --hash=sha256:4e3d3f31a1e202b0f5a35ba3bc4eb41e2fc2b11c1eff38b362de710bcffb5016 \
56 --hash=sha256:50bec6d35e6b1aaeb17f7c4e2b9374ebf95a8975d57863546fa83e8d31bdb8c4 \
57 --hash=sha256:55cad9a6df1e2a1d62063f79d0881a414a906a6962bc160ac968cc03ed3efcfb \
58 --hash=sha256:5662ad4e4e84f1eaa8efce5da695c5d2e229c563f9d5ce5b0113f71321bcf753 \
59 --hash=sha256:59b4dc008f98fc6ee2bb4fd7fc786a8d70000d058c2bbe2698275bc53a8d3fa7 \
60 --hash=sha256:73e1ffefe05e4ccd7bcea61af76f36077b914f92b76f95ccf00b0c1b9186f3f9 \
61 --hash=sha256:a1f0fd46eba2d71ce1589f7e50a9e2ffaeb739fb2c11e8192aa2b45d5f6cc41f \
62 --hash=sha256:a2e85dc204556657661051ff4bab75a84e968669765c8a2cd425918699c3d0e8 \
63 --hash=sha256:a5457d47dfff24882a21492e5815f891c0ca35fefae8aa742c6c263dac16ef1f \
64 --hash=sha256:a8dccd61d52a8dae4a825cdbb7735da530179fea472903eb871a5513b5abbfdc \
65 --hash=sha256:ae61af521ed676cf16ae94f30fe202781a38d7178b6b4ab622e4eec8cefaff42 \
66 --hash=sha256:b012a5edb48288f77a63dba0840c92d0504aa215612da4541b7b42d849bc83a3 \
67 --hash=sha256:d2c5cfa536227f57f97c92ac30c8109688ace8fa4ac086d19d0af47d134e2909 \
68 --hash=sha256:d42b5796e20aacc9d15e66befb7a345454eef794fdb0737d1af593447c6c8f45 \
69 --hash=sha256:dee54f5d30d775f525894d67b1495625dd9322945e7fee00731952e0368ff42d \
70 --hash=sha256:e070535507bd6aa07124258171be2ee8dfc19119c28ca94c9dfb7efd23564512 \
71 --hash=sha256:e1ff2748c84d97b065cc95429814cdba39bcbd77c9c85c89344b317dc0d9cbff \
72 --hash=sha256:ed851c75d1e0e043cbf5ca9a8e1b13c4c90f3fbd863dacb01c0808e2b5204201 \
73 # via bcrypt, cryptography, pynacl
74 chardet==3.0.4 \
75 --hash=sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae \
76 --hash=sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691 \
38 cffi==1.15.0 \
39 --hash=sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3 \
40 --hash=sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2 \
41 --hash=sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636 \
42 --hash=sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20 \
43 --hash=sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728 \
44 --hash=sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27 \
45 --hash=sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66 \
46 --hash=sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443 \
47 --hash=sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0 \
48 --hash=sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7 \
49 --hash=sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39 \
50 --hash=sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605 \
51 --hash=sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a \
52 --hash=sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37 \
53 --hash=sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029 \
54 --hash=sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139 \
55 --hash=sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc \
56 --hash=sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df \
57 --hash=sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14 \
58 --hash=sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880 \
59 --hash=sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2 \
60 --hash=sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a \
61 --hash=sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e \
62 --hash=sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474 \
63 --hash=sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024 \
64 --hash=sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8 \
65 --hash=sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0 \
66 --hash=sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e \
67 --hash=sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a \
68 --hash=sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e \
69 --hash=sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032 \
70 --hash=sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6 \
71 --hash=sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e \
72 --hash=sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b \
73 --hash=sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e \
74 --hash=sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954 \
75 --hash=sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962 \
76 --hash=sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c \
77 --hash=sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4 \
78 --hash=sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55 \
79 --hash=sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962 \
80 --hash=sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023 \
81 --hash=sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c \
82 --hash=sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6 \
83 --hash=sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8 \
84 --hash=sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382 \
85 --hash=sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7 \
86 --hash=sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc \
87 --hash=sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997 \
88 --hash=sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796
89 # via
90 # bcrypt
91 # cryptography
92 # pynacl
93 charset-normalizer==2.0.12 \
94 --hash=sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597 \
95 --hash=sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df
77 96 # via requests
78 cryptography==2.7 \
79 --hash=sha256:24b61e5fcb506424d3ec4e18bca995833839bf13c59fc43e530e488f28d46b8c \
80 --hash=sha256:25dd1581a183e9e7a806fe0543f485103232f940fcfc301db65e630512cce643 \
81 --hash=sha256:3452bba7c21c69f2df772762be0066c7ed5dc65df494a1d53a58b683a83e1216 \
82 --hash=sha256:41a0be220dd1ed9e998f5891948306eb8c812b512dc398e5a01846d855050799 \
83 --hash=sha256:5751d8a11b956fbfa314f6553d186b94aa70fdb03d8a4d4f1c82dcacf0cbe28a \
84 --hash=sha256:5f61c7d749048fa6e3322258b4263463bfccefecb0dd731b6561cb617a1d9bb9 \
85 --hash=sha256:72e24c521fa2106f19623a3851e9f89ddfdeb9ac63871c7643790f872a305dfc \
86 --hash=sha256:7b97ae6ef5cba2e3bb14256625423413d5ce8d1abb91d4f29b6d1a081da765f8 \
87 --hash=sha256:961e886d8a3590fd2c723cf07be14e2a91cf53c25f02435c04d39e90780e3b53 \
88 --hash=sha256:96d8473848e984184b6728e2c9d391482008646276c3ff084a1bd89e15ff53a1 \
89 --hash=sha256:ae536da50c7ad1e002c3eee101871d93abdc90d9c5f651818450a0d3af718609 \
90 --hash=sha256:b0db0cecf396033abb4a93c95d1602f268b3a68bb0a9cc06a7cff587bb9a7292 \
91 --hash=sha256:cfee9164954c186b191b91d4193989ca994703b2fff406f71cf454a2d3c7327e \
92 --hash=sha256:e6347742ac8f35ded4a46ff835c60e68c22a536a8ae5c4422966d06946b6d4c6 \
93 --hash=sha256:f27d93f0139a3c056172ebb5d4f9056e770fdf0206c2f422ff2ebbad142e09ed \
94 --hash=sha256:f57b76e46a58b63d1c6375017f4564a28f19a5ca912691fd2e4261b3414b618d \
95 # via paramiko, pypsrp
96 docutils==0.15.2 \
97 --hash=sha256:6c4f696463b79f1fb8ba0c594b63840ebd41f059e92b31957c46b74a4599b6d0 \
98 --hash=sha256:9e4d7ecfc600058e07ba661411a2b7de2fd0fafa17d1a7f7361cd47b1175c827 \
99 --hash=sha256:a2aeea129088da402665e92e0b25b04b073c04b2dce4ab65caaa38b7ce2e1a99 \
100 # via botocore, readme-renderer
101 idna==2.8 \
102 --hash=sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407 \
103 --hash=sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c \
97 commonmark==0.9.1 \
98 --hash=sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60 \
99 --hash=sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9
100 # via rich
101 cryptography==37.0.2 \
102 --hash=sha256:093cb351031656d3ee2f4fa1be579a8c69c754cf874206be1d4cf3b542042804 \
103 --hash=sha256:0cc20f655157d4cfc7bada909dc5cc228211b075ba8407c46467f63597c78178 \
104 --hash=sha256:1b9362d34363f2c71b7853f6251219298124aa4cc2075ae2932e64c91a3e2717 \
105 --hash=sha256:1f3bfbd611db5cb58ca82f3deb35e83af34bb8cf06043fa61500157d50a70982 \
106 --hash=sha256:2bd1096476aaac820426239ab534b636c77d71af66c547b9ddcd76eb9c79e004 \
107 --hash=sha256:31fe38d14d2e5f787e0aecef831457da6cec68e0bb09a35835b0b44ae8b988fe \
108 --hash=sha256:3b8398b3d0efc420e777c40c16764d6870bcef2eb383df9c6dbb9ffe12c64452 \
109 --hash=sha256:3c81599befb4d4f3d7648ed3217e00d21a9341a9a688ecdd615ff72ffbed7336 \
110 --hash=sha256:419c57d7b63f5ec38b1199a9521d77d7d1754eb97827bbb773162073ccd8c8d4 \
111 --hash=sha256:46f4c544f6557a2fefa7ac8ac7d1b17bf9b647bd20b16decc8fbcab7117fbc15 \
112 --hash=sha256:471e0d70201c069f74c837983189949aa0d24bb2d751b57e26e3761f2f782b8d \
113 --hash=sha256:59b281eab51e1b6b6afa525af2bd93c16d49358404f814fe2c2410058623928c \
114 --hash=sha256:731c8abd27693323b348518ed0e0705713a36d79fdbd969ad968fbef0979a7e0 \
115 --hash=sha256:95e590dd70642eb2079d280420a888190aa040ad20f19ec8c6e097e38aa29e06 \
116 --hash=sha256:a68254dd88021f24a68b613d8c51d5c5e74d735878b9e32cc0adf19d1f10aaf9 \
117 --hash=sha256:a7d5137e556cc0ea418dca6186deabe9129cee318618eb1ffecbd35bee55ddc1 \
118 --hash=sha256:aeaba7b5e756ea52c8861c133c596afe93dd716cbcacae23b80bc238202dc023 \
119 --hash=sha256:dc26bb134452081859aa21d4990474ddb7e863aa39e60d1592800a8865a702de \
120 --hash=sha256:e53258e69874a306fcecb88b7534d61820db8a98655662a3dd2ec7f1afd9132f \
121 --hash=sha256:ef15c2df7656763b4ff20a9bc4381d8352e6640cfeb95c2972c38ef508e75181 \
122 --hash=sha256:f224ad253cc9cea7568f49077007d2263efa57396a2f2f78114066fd54b5c68e \
123 --hash=sha256:f8ec91983e638a9bcd75b39f1396e5c0dc2330cbd9ce4accefe68717e6779e0a
124 # via
125 # paramiko
126 # pypsrp
127 # pyspnego
128 # secretstorage
129 docutils==0.18.1 \
130 --hash=sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c \
131 --hash=sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06
132 # via readme-renderer
133 idna==3.3 \
134 --hash=sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff \
135 --hash=sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d
104 136 # via requests
105 jmespath==0.9.4 \
106 --hash=sha256:3720a4b1bd659dd2eecad0666459b9788813e032b83e7ba58578e48254e0a0e6 \
107 --hash=sha256:bde2aef6f44302dfb30320115b17d030798de8c4110e28d5cf6cf91a7a31074c \
108 # via boto3, botocore
109 ntlm-auth==1.4.0 \
110 --hash=sha256:11f7a3cec38155b7cecdd9bbc8c37cd738d8012f0523b3f98d8caefe394feb97 \
111 --hash=sha256:350f2389c8ee5517f47db55a36ac2f8efc9742a60a678d6e2caa92385bdcaa9a \
137 importlib-metadata==4.11.3 \
138 --hash=sha256:1208431ca90a8cca1a6b8af391bb53c1a2db74e5d1cef6ddced95d4b2062edc6 \
139 --hash=sha256:ea4c597ebf37142f827b8f39299579e31685c31d3a438b59f469406afd0f2539
140 # via
141 # keyring
142 # twine
143 jeepney==0.8.0 \
144 --hash=sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806 \
145 --hash=sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755
146 # via
147 # keyring
148 # secretstorage
149 jmespath==1.0.0 \
150 --hash=sha256:a490e280edd1f57d6de88636992d05b71e97d69a26a19f058ecf7d304474bf5e \
151 --hash=sha256:e8dcd576ed616f14ec02eed0005c85973b5890083313860136657e24784e4c04
152 # via
153 # boto3
154 # botocore
155 keyring==23.5.0 \
156 --hash=sha256:9012508e141a80bd1c0b6778d5c610dd9f8c464d75ac6774248500503f972fb9 \
157 --hash=sha256:b0d28928ac3ec8e42ef4cc227822647a19f1d544f21f96457965dc01cf555261
158 # via twine
159 paramiko==2.10.4 \
160 --hash=sha256:3c9ed6084f4b671ab66dc3c729092d32d96c3258f1426071301cb33654b09027 \
161 --hash=sha256:3d2e650b6812ce6d160abff701d6ef4434ec97934b13e95cf1ad3da70ffb5c58
162 # via -r contrib/automation/requirements.txt.in
163 pkginfo==1.8.2 \
164 --hash=sha256:542e0d0b6750e2e21c20179803e40ab50598d8066d51097a0e382cba9eb02bff \
165 --hash=sha256:c24c487c6a7f72c66e816ab1796b96ac6c3d14d49338293d2141664330b55ffc
166 # via twine
167 pycparser==2.21 \
168 --hash=sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9 \
169 --hash=sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206
170 # via cffi
171 pygments==2.12.0 \
172 --hash=sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb \
173 --hash=sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519
174 # via
175 # readme-renderer
176 # rich
177 pynacl==1.5.0 \
178 --hash=sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858 \
179 --hash=sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d \
180 --hash=sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93 \
181 --hash=sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1 \
182 --hash=sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92 \
183 --hash=sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff \
184 --hash=sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba \
185 --hash=sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394 \
186 --hash=sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b \
187 --hash=sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543
188 # via paramiko
189 pypsrp==0.8.1 \
190 --hash=sha256:0101345ceb415896fed9b056e7b77d65312089ddc73c4286247ccf1859d4bc4d \
191 --hash=sha256:f5500acd11dfe742d51b7fbb61321ba721038a300d67763dc52babe709db65e7
192 # via -r contrib/automation/requirements.txt.in
193 pyspnego==0.5.2 \
194 --hash=sha256:1fed228edc4b1730844da8237b90489be28c55681cf3934cd04fceb2253e55bf \
195 --hash=sha256:25fbc90fc6bd16881480316739bab820cc91364765e46340da17f861f89691f1 \
196 --hash=sha256:274b3a2d37e45ad4567bc5754be04b5fefad3f7cdea7d205f739d8a26b5a9189 \
197 --hash=sha256:36db7ec38023a23a545114dfd23825639571f135c72fb4b13a1ed559a0a4d93c \
198 --hash=sha256:3b1ff3c1d5588b66f8e4ebafa3079a7bf0bdcc6fb144b944c5a101e688a5a280 \
199 --hash=sha256:4b9bda51bd964f40322aa1b33dcfc5d68f23b0680b4b5158175f2e9a04119aa9 \
200 --hash=sha256:5d6d91e35ee63a5de30eb70716bf25274bf16c2c472b046dd21fad60fe63b0b6 \
201 --hash=sha256:7562bc640bf402bb2849f325b0bb41260bd2c0cb06e38b9a8c6f7021b452c873 \
202 --hash=sha256:9c5bdb9f0207e2ce9e5410ee2205bf016755712018534c711ae6c1daff7fa7db \
203 --hash=sha256:a5c135d20819db3c48f65054d648317f369a61b7b22dc17b9e5ec9c0169541a0 \
204 --hash=sha256:bd95633e7dce69e267579bdbe992fc081a14310236b4e84c3d179b1cf6439ca5 \
205 --hash=sha256:eb41b970dbda0dfe07b1da6fc83fe9f534a66d8dea38c06c0155377697407d9a
112 206 # via pypsrp
113 paramiko==2.6.0 \
114 --hash=sha256:99f0179bdc176281d21961a003ffdb2ec369daac1a1007241f53374e376576cf \
115 --hash=sha256:f4b2edfa0d226b70bd4ca31ea7e389325990283da23465d572ed1f70a7583041
116 pkginfo==1.5.0.1 \
117 --hash=sha256:7424f2c8511c186cd5424bbf31045b77435b37a8d604990b79d4e70d741148bb \
118 --hash=sha256:a6d9e40ca61ad3ebd0b72fbadd4fba16e4c0e4df0428c041e01e06eb6ee71f32 \
207 python-dateutil==2.8.2 \
208 --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \
209 --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9
210 # via botocore
211 readme-renderer==35.0 \
212 --hash=sha256:73b84905d091c31f36e50b4ae05ae2acead661f6a09a9abb4df7d2ddcdb6a698 \
213 --hash=sha256:a727999acfc222fc21d82a12ed48c957c4989785e5865807c65a487d21677497
119 214 # via twine
120 pycparser==2.19 \
121 --hash=sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3 \
122 # via cffi
123 pygments==2.4.2 \
124 --hash=sha256:71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127 \
125 --hash=sha256:881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297 \
126 # via readme-renderer
127 pynacl==1.3.0 \
128 --hash=sha256:05c26f93964373fc0abe332676cb6735f0ecad27711035b9472751faa8521255 \
129 --hash=sha256:0c6100edd16fefd1557da078c7a31e7b7d7a52ce39fdca2bec29d4f7b6e7600c \
130 --hash=sha256:0d0a8171a68edf51add1e73d2159c4bc19fc0718e79dec51166e940856c2f28e \
131 --hash=sha256:1c780712b206317a746ace34c209b8c29dbfd841dfbc02aa27f2084dd3db77ae \
132 --hash=sha256:2424c8b9f41aa65bbdbd7a64e73a7450ebb4aa9ddedc6a081e7afcc4c97f7621 \
133 --hash=sha256:2d23c04e8d709444220557ae48ed01f3f1086439f12dbf11976e849a4926db56 \
134 --hash=sha256:30f36a9c70450c7878053fa1344aca0145fd47d845270b43a7ee9192a051bf39 \
135 --hash=sha256:37aa336a317209f1bb099ad177fef0da45be36a2aa664507c5d72015f956c310 \
136 --hash=sha256:4943decfc5b905748f0756fdd99d4f9498d7064815c4cf3643820c9028b711d1 \
137 --hash=sha256:57ef38a65056e7800859e5ba9e6091053cd06e1038983016effaffe0efcd594a \
138 --hash=sha256:5bd61e9b44c543016ce1f6aef48606280e45f892a928ca7068fba30021e9b786 \
139 --hash=sha256:6482d3017a0c0327a49dddc8bd1074cc730d45db2ccb09c3bac1f8f32d1eb61b \
140 --hash=sha256:7d3ce02c0784b7cbcc771a2da6ea51f87e8716004512493a2b69016326301c3b \
141 --hash=sha256:a14e499c0f5955dcc3991f785f3f8e2130ed504fa3a7f44009ff458ad6bdd17f \
142 --hash=sha256:a39f54ccbcd2757d1d63b0ec00a00980c0b382c62865b61a505163943624ab20 \
143 --hash=sha256:aabb0c5232910a20eec8563503c153a8e78bbf5459490c49ab31f6adf3f3a415 \
144 --hash=sha256:bd4ecb473a96ad0f90c20acba4f0bf0df91a4e03a1f4dd6a4bdc9ca75aa3a715 \
145 --hash=sha256:e2da3c13307eac601f3de04887624939aca8ee3c9488a0bb0eca4fb9401fc6b1 \
146 --hash=sha256:f67814c38162f4deb31f68d590771a29d5ae3b1bd64b75cf232308e5c74777e0 \
147 # via paramiko
148 pypsrp==0.4.0 \
149 --hash=sha256:64b5bdd725a9744c821483b05ecd266f6417f4c6e90ee961a08838480f7d025e \
150 --hash=sha256:f42919247fb80f7dc24c552560d7c24e754d15326030c9e3b7b94f51cfa4dc69
151 python-dateutil==2.8.0 \
152 --hash=sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb \
153 --hash=sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e \
154 # via botocore
155 readme-renderer==24.0 \
156 --hash=sha256:bb16f55b259f27f75f640acf5e00cf897845a8b3e4731b5c1a436e4b8529202f \
157 --hash=sha256:c8532b79afc0375a85f10433eca157d6b50f7d6990f337fa498c96cd4bfc203d \
158 # via twine
215 requests==2.27.1 \
216 --hash=sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61 \
217 --hash=sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d
218 # via
219 # pypsrp
220 # requests-toolbelt
221 # twine
159 222 requests-toolbelt==0.9.1 \
160 223 --hash=sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f \
161 --hash=sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0 \
224 --hash=sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0
225 # via twine
226 rfc3986==2.0.0 \
227 --hash=sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd \
228 --hash=sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c
162 229 # via twine
163 requests==2.22.0 \
164 --hash=sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4 \
165 --hash=sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31 \
166 # via pypsrp, requests-toolbelt, twine
167 s3transfer==0.2.1 \
168 --hash=sha256:6efc926738a3cd576c2a79725fed9afde92378aa5c6a957e3af010cb019fac9d \
169 --hash=sha256:b780f2411b824cb541dbcd2c713d0cb61c7d1bcadae204cdddda2b35cef493ba \
230 rich==12.3.0 \
231 --hash=sha256:0eb63013630c6ee1237e0e395d51cb23513de6b5531235e33889e8842bdf3a6f \
232 --hash=sha256:7e8700cda776337036a712ff0495b04052fb5f957c7dfb8df997f88350044b64
233 # via twine
234 s3transfer==0.5.2 \
235 --hash=sha256:7a6f4c4d1fdb9a2b640244008e142cbc2cd3ae34b386584ef044dd0f27101971 \
236 --hash=sha256:95c58c194ce657a5f4fb0b9e60a84968c808888aed628cd98ab8771fe1db98ed
170 237 # via boto3
171 six==1.12.0 \
172 --hash=sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c \
173 --hash=sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73 \
174 # via bcrypt, bleach, cryptography, pynacl, pypsrp, python-dateutil, readme-renderer
175 tqdm==4.36.1 \
176 --hash=sha256:abc25d0ce2397d070ef07d8c7e706aede7920da163c64997585d42d3537ece3d \
177 --hash=sha256:dd3fcca8488bb1d416aa7469d2f277902f26260c45aa86b667b074cd44b3b115 \
178 # via twine
179 twine==2.0.0 \
180 --hash=sha256:5319dd3e02ac73fcddcd94f035b9631589ab5d23e1f4699d57365199d85261e1 \
181 --hash=sha256:9fe7091715c7576df166df8ef6654e61bada39571783f2fd415bdcba867c6993
182 urllib3==1.25.6 \
183 --hash=sha256:3de946ffbed6e6746608990594d08faac602528ac7015ac28d33cee6a45b7398 \
184 --hash=sha256:9a107b99a5393caf59c7aa3c1249c16e6879447533d0887f4336dde834c7be86 \
185 # via botocore, requests
238 secretstorage==3.3.2 \
239 --hash=sha256:0a8eb9645b320881c222e827c26f4cfcf55363e8b374a021981ef886657a912f \
240 --hash=sha256:755dc845b6ad76dcbcbc07ea3da75ae54bb1ea529eb72d15f83d26499a5df319
241 # via keyring
242 six==1.16.0 \
243 --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
244 --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
245 # via
246 # bleach
247 # paramiko
248 # python-dateutil
249 twine==4.0.0 \
250 --hash=sha256:6f7496cf14a3a8903474552d5271c79c71916519edb42554f23f42a8563498a9 \
251 --hash=sha256:817aa0c0bdc02a5ebe32051e168e23c71a0608334e624c793011f120dbbc05b7
252 # via -r contrib/automation/requirements.txt.in
253 urllib3==1.26.9 \
254 --hash=sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14 \
255 --hash=sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e
256 # via
257 # botocore
258 # requests
259 # twine
186 260 webencodings==0.5.1 \
187 261 --hash=sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78 \
188 --hash=sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923 \
262 --hash=sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923
189 263 # via bleach
190
191 # WARNING: The following packages were not pinned, but pip requires them to be
192 # pinned when the requirements file includes hashes. Consider using the --allow-unsafe flag.
193 # setuptools==41.2.0 # via twine
264 zipp==3.8.0 \
265 --hash=sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad \
266 --hash=sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099
267 # via importlib-metadata
@@ -1,204 +1,204 b''
1 1 # install-dependencies.ps1 - Install Windows dependencies for building Mercurial
2 2 #
3 3 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 # This script can be used to bootstrap a Mercurial build environment on
9 9 # Windows.
10 10 #
11 11 # The script makes a lot of assumptions about how things should work.
12 12 # For example, the install location of Python is hardcoded to c:\hgdev\*.
13 13 #
14 14 # The script should be executed from a PowerShell with elevated privileges
15 15 # if you don't want to see a UAC prompt for various installers.
16 16 #
17 17 # The script is tested on Windows 10 and Windows Server 2019 (in EC2).
18 18
19 19 $VS_BUILD_TOOLS_URL = "https://download.visualstudio.microsoft.com/download/pr/a1603c02-8a66-4b83-b821-811e3610a7c4/aa2db8bb39e0cbd23e9940d8951e0bc3/vs_buildtools.exe"
20 20 $VS_BUILD_TOOLS_SHA256 = "911E292B8E6E5F46CBC17003BDCD2D27A70E616E8D5E6E69D5D489A605CAA139"
21 21
22 22 $PYTHON37_x86_URL = "https://www.python.org/ftp/python/3.7.9/python-3.7.9.exe"
23 23 $PYTHON37_x86_SHA256 = "769bb7c74ad1df6d7d74071cc16a984ff6182e4016e11b8949b93db487977220"
24 24 $PYTHON37_X64_URL = "https://www.python.org/ftp/python/3.7.9/python-3.7.9-amd64.exe"
25 25 $PYTHON37_x64_SHA256 = "e69ed52afb5a722e5c56f6c21d594e85c17cb29f12f18bb69751cf1714e0f987"
26 26
27 27 $PYTHON38_x86_URL = "https://www.python.org/ftp/python/3.8.10/python-3.8.10.exe"
28 28 $PYTHON38_x86_SHA256 = "ad07633a1f0cd795f3bf9da33729f662281df196b4567fa795829f3bb38a30ac"
29 29 $PYTHON38_x64_URL = "https://www.python.org/ftp/python/3.8.10/python-3.8.10-amd64.exe"
30 30 $PYTHON38_x64_SHA256 = "7628244cb53408b50639d2c1287c659f4e29d3dfdb9084b11aed5870c0c6a48a"
31 31
32 32 $PYTHON39_x86_URL = "https://www.python.org/ftp/python/3.9.12/python-3.9.12.exe"
33 33 $PYTHON39_x86_SHA256 = "3d883326f30ac231c06b33f2a8ea700a185c20bf98d01da118079e9134d5fd20"
34 34 $PYTHON39_X64_URL = "https://www.python.org/ftp/python/3.9.12/python-3.9.12-amd64.exe"
35 35 $PYTHON39_x64_SHA256 = "2ba57ab2281094f78fc0227a27f4d47c90d94094e7cca35ce78419e616b3cb63"
36 36
37 37 $PYTHON310_x86_URL = "https://www.python.org/ftp/python/3.10.4/python-3.10.4.exe"
38 38 $PYTHON310_x86_SHA256 = "97c37c53c7a826f5b00e185754ab2a324a919f7afc469b20764b71715c80041d"
39 39 $PYTHON310_X64_URL = "https://www.python.org/ftp/python/3.10.4/python-3.10.4-amd64.exe"
40 40 $PYTHON310_x64_SHA256 = "a81fc4180f34e5733c3f15526c668ff55de096366f9006d8a44c0336704e50f1"
41 41
42 42 # PIP 22.0.4.
43 43 $PIP_URL = "https://github.com/pypa/get-pip/raw/38e54e5de07c66e875c11a1ebbdb938854625dd8/public/get-pip.py"
44 44 $PIP_SHA256 = "e235c437e5c7d7524fbce3880ca39b917a73dc565e0c813465b7a7a329bb279a"
45 45
46 46 $INNO_SETUP_URL = "http://files.jrsoftware.org/is/5/innosetup-5.6.1-unicode.exe"
47 47 $INNO_SETUP_SHA256 = "27D49E9BC769E9D1B214C153011978DB90DC01C2ACD1DDCD9ED7B3FE3B96B538"
48 48
49 49 $MINGW_BIN_URL = "https://osdn.net/frs/redir.php?m=constant&f=mingw%2F68260%2Fmingw-get-0.6.3-mingw32-pre-20170905-1-bin.zip"
50 50 $MINGW_BIN_SHA256 = "2AB8EFD7C7D1FC8EAF8B2FA4DA4EEF8F3E47768284C021599BC7435839A046DF"
51 51
52 $MERCURIAL_WHEEL_FILENAME = "mercurial-5.8.1-cp39-cp39-win_amd64.whl"
53 $MERCURIAL_WHEEL_URL = "https://files.pythonhosted.org/packages/5c/b5/a5fa664761eef29b6c90eb24cb09ab8fe2c9b4b86af41d42c17476aff29b/$MERCURIAL_WHEEL_FILENAME"
54 $MERCURIAL_WHEEL_SHA256 = "cbf3efa68fd7ebf94691bd00d2c86bbd47ca73620c8faa4f18b6c394bf5f82b0"
52 $MERCURIAL_WHEEL_FILENAME = "mercurial-6.1.4-cp39-cp39-win_amd64.whl"
53 $MERCURIAL_WHEEL_URL = "https://files.pythonhosted.org/packages/82/86/fbcc4b552f6c1bdfdbbc5a68b0896a55ac3c6c0e8baf51394816bdc320bd/$MERCURIAL_WHEEL_FILENAME"
54 $MERCURIAL_WHEEL_SHA256 = "ab578daec7c21786c668b0da2e71282a290d18010255719f78d0e55145020d46"
55 55
56 56 $RUSTUP_INIT_URL = "https://static.rust-lang.org/rustup/archive/1.21.1/x86_64-pc-windows-gnu/rustup-init.exe"
57 57 $RUSTUP_INIT_SHA256 = "d17df34ba974b9b19cf5c75883a95475aa22ddc364591d75d174090d55711c72"
58 58
59 59 $PYOXIDIZER_URL = "https://github.com/indygreg/PyOxidizer/releases/download/pyoxidizer%2F0.17/PyOxidizer-0.17.0-x64.msi"
60 60 $PYOXIDIZER_SHA256 = "85c3bc21a18eb5e2db4dad87cca29accf725c7d59dd364a853ab5099c272024b"
61 61
62 62 # Writing progress slows down downloads substantially. So disable it.
63 63 $progressPreference = 'silentlyContinue'
64 64
65 65 function Secure-Download($url, $path, $sha256) {
66 66 if (Test-Path -Path $path) {
67 67 Get-FileHash -Path $path -Algorithm SHA256 -OutVariable hash
68 68
69 69 if ($hash.Hash -eq $sha256) {
70 70 Write-Output "SHA256 of $path verified as $sha256"
71 71 return
72 72 }
73 73
74 74 Write-Output "hash mismatch on $path; downloading again"
75 75 }
76 76
77 77 Write-Output "downloading $url to $path"
78 78 Invoke-WebRequest -Uri $url -OutFile $path
79 79 Get-FileHash -Path $path -Algorithm SHA256 -OutVariable hash
80 80
81 81 if ($hash.Hash -ne $sha256) {
82 82 Remove-Item -Path $path
83 83 throw "hash mismatch when downloading $url; got $($hash.Hash), expected $sha256"
84 84 }
85 85 }
86 86
87 87 function Invoke-Process($path, $arguments) {
88 88 echo "$path $arguments"
89 89
90 90 $p = Start-Process -FilePath $path -ArgumentList $arguments -Wait -PassThru -WindowStyle Hidden
91 91
92 92 if ($p.ExitCode -ne 0) {
93 93 # If the MSI is already installed, ignore the error
94 94 if ($p.ExitCode -eq 1638) {
95 95 Write-Output "program already installed; continuing..."
96 96 }
97 97 else {
98 98 throw "process exited non-0: $($p.ExitCode)"
99 99 }
100 100 }
101 101 }
102 102
103 103 function Install-Python3($name, $installer, $dest, $pip) {
104 104 Write-Output "installing $name"
105 105
106 106 # We hit this when running the script as part of Simple Systems Manager in
107 107 # EC2. The Python 3 installer doesn't seem to like per-user installs
108 108 # when running as the SYSTEM user. So enable global installs if executed in
109 109 # this mode.
110 110 if ($env:USERPROFILE -eq "C:\Windows\system32\config\systemprofile") {
111 111 Write-Output "running with SYSTEM account; installing for all users"
112 112 $allusers = "1"
113 113 }
114 114 else {
115 115 $allusers = "0"
116 116 }
117 117
118 118 Invoke-Process $installer "/quiet TargetDir=${dest} InstallAllUsers=${allusers} AssociateFiles=0 CompileAll=0 PrependPath=0 Include_doc=0 Include_launcher=0 InstallLauncherAllUsers=0 Include_pip=0 Include_test=0"
119 119 Invoke-Process ${dest}\python.exe $pip
120 120 }
121 121
122 122 function Install-Rust($prefix) {
123 123 Write-Output "installing Rust"
124 124 $Env:RUSTUP_HOME = "${prefix}\rustup"
125 125 $Env:CARGO_HOME = "${prefix}\cargo"
126 126
127 127 Invoke-Process "${prefix}\assets\rustup-init.exe" "-y --default-host x86_64-pc-windows-msvc"
128 128 Invoke-Process "${prefix}\cargo\bin\rustup.exe" "target add i686-pc-windows-msvc"
129 129 Invoke-Process "${prefix}\cargo\bin\rustup.exe" "install 1.52.0"
130 130 Invoke-Process "${prefix}\cargo\bin\rustup.exe" "component add clippy"
131 131 }
132 132
133 133 function Install-Dependencies($prefix) {
134 134 if (!(Test-Path -Path $prefix\assets)) {
135 135 New-Item -Path $prefix\assets -ItemType Directory
136 136 }
137 137
138 138 $pip = "${prefix}\assets\get-pip.py"
139 139
140 140 Secure-Download $PYTHON37_x86_URL ${prefix}\assets\python37-x86.exe $PYTHON37_x86_SHA256
141 141 Secure-Download $PYTHON37_x64_URL ${prefix}\assets\python37-x64.exe $PYTHON37_x64_SHA256
142 142 Secure-Download $PYTHON38_x86_URL ${prefix}\assets\python38-x86.exe $PYTHON38_x86_SHA256
143 143 Secure-Download $PYTHON38_x64_URL ${prefix}\assets\python38-x64.exe $PYTHON38_x64_SHA256
144 144 Secure-Download $PYTHON39_x86_URL ${prefix}\assets\python39-x86.exe $PYTHON39_x86_SHA256
145 145 Secure-Download $PYTHON39_x64_URL ${prefix}\assets\python39-x64.exe $PYTHON39_x64_SHA256
146 146 Secure-Download $PYTHON310_x86_URL ${prefix}\assets\python310-x86.exe $PYTHON310_x86_SHA256
147 147 Secure-Download $PYTHON310_x64_URL ${prefix}\assets\python310-x64.exe $PYTHON310_x64_SHA256
148 148 Secure-Download $PIP_URL ${pip} $PIP_SHA256
149 149 Secure-Download $VS_BUILD_TOOLS_URL ${prefix}\assets\vs_buildtools.exe $VS_BUILD_TOOLS_SHA256
150 150 Secure-Download $INNO_SETUP_URL ${prefix}\assets\InnoSetup.exe $INNO_SETUP_SHA256
151 151 Secure-Download $MINGW_BIN_URL ${prefix}\assets\mingw-get-bin.zip $MINGW_BIN_SHA256
152 152 Secure-Download $MERCURIAL_WHEEL_URL ${prefix}\assets\${MERCURIAL_WHEEL_FILENAME} $MERCURIAL_WHEEL_SHA256
153 153 Secure-Download $RUSTUP_INIT_URL ${prefix}\assets\rustup-init.exe $RUSTUP_INIT_SHA256
154 154 Secure-Download $PYOXIDIZER_URL ${prefix}\assets\PyOxidizer.msi $PYOXIDIZER_SHA256
155 155
156 156 Install-Python3 "Python 3.7 32-bit" ${prefix}\assets\python37-x86.exe ${prefix}\python37-x86 ${pip}
157 157 Install-Python3 "Python 3.7 64-bit" ${prefix}\assets\python37-x64.exe ${prefix}\python37-x64 ${pip}
158 158 Install-Python3 "Python 3.8 32-bit" ${prefix}\assets\python38-x86.exe ${prefix}\python38-x86 ${pip}
159 159 Install-Python3 "Python 3.8 64-bit" ${prefix}\assets\python38-x64.exe ${prefix}\python38-x64 ${pip}
160 160 Install-Python3 "Python 3.9 32-bit" ${prefix}\assets\python39-x86.exe ${prefix}\python39-x86 ${pip}
161 161 Install-Python3 "Python 3.9 64-bit" ${prefix}\assets\python39-x64.exe ${prefix}\python39-x64 ${pip}
162 162 Install-Python3 "Python 3.10 32-bit" ${prefix}\assets\python310-x86.exe ${prefix}\python310-x86 ${pip}
163 163 Install-Python3 "Python 3.10 64-bit" ${prefix}\assets\python310-x64.exe ${prefix}\python310-x64 ${pip}
164 164
165 165 Write-Output "installing Visual Studio 2017 Build Tools and SDKs"
166 166 Invoke-Process ${prefix}\assets\vs_buildtools.exe "--quiet --wait --norestart --nocache --channelUri https://aka.ms/vs/15/release/channel --add Microsoft.VisualStudio.Workload.MSBuildTools --add Microsoft.VisualStudio.Component.Windows10SDK.17763 --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.Windows10SDK --add Microsoft.VisualStudio.Component.VC.140"
167 167
168 168 Write-Output "installing PyOxidizer"
169 169 Invoke-Process msiexec.exe "/i ${prefix}\assets\PyOxidizer.msi /l* ${prefix}\assets\PyOxidizer.log /quiet"
170 170
171 171 Install-Rust ${prefix}
172 172
173 173 Write-Output "installing Inno Setup"
174 174 Invoke-Process ${prefix}\assets\InnoSetup.exe "/SP- /VERYSILENT /SUPPRESSMSGBOXES"
175 175
176 176 Write-Output "extracting MinGW base archive"
177 177 Expand-Archive -Path ${prefix}\assets\mingw-get-bin.zip -DestinationPath "${prefix}\MinGW" -Force
178 178
179 179 Write-Output "updating MinGW package catalogs"
180 180 Invoke-Process ${prefix}\MinGW\bin\mingw-get.exe "update"
181 181
182 182 Write-Output "installing MinGW packages"
183 183 Invoke-Process ${prefix}\MinGW\bin\mingw-get.exe "install msys-base msys-coreutils msys-diffutils msys-unzip"
184 184
185 185 # Construct a virtualenv useful for bootstrapping. It conveniently contains a
186 186 # Mercurial install.
187 187 Write-Output "creating bootstrap virtualenv with Mercurial"
188 188 Invoke-Process "$prefix\python39-x64\python.exe" "-m venv ${prefix}\venv-bootstrap"
189 189 Invoke-Process "${prefix}\venv-bootstrap\Scripts\pip.exe" "install ${prefix}\assets\${MERCURIAL_WHEEL_FILENAME}"
190 190 }
191 191
192 192 function Clone-Mercurial-Repo($prefix, $repo_url, $dest) {
193 193 Write-Output "cloning $repo_url to $dest"
194 194 # TODO Figure out why CA verification isn't working in EC2 and remove
195 195 # --insecure.
196 196 Invoke-Process "${prefix}\venv-bootstrap\Scripts\python.exe" "${prefix}\venv-bootstrap\Scripts\hg clone --insecure $repo_url $dest"
197 197
198 198 # Mark repo as non-publishing by default for convenience.
199 199 Add-Content -Path "$dest\.hg\hgrc" -Value "`n[phases]`npublish = false"
200 200 }
201 201
202 202 $prefix = "c:\hgdev"
203 203 Install-Dependencies $prefix
204 204 Clone-Mercurial-Repo $prefix "https://www.mercurial-scm.org/repo/hg" $prefix\src
@@ -1,328 +1,336 b''
1 1 #
2 2 # This file is autogenerated by pip-compile with python 3.7
3 3 # To update, run:
4 4 #
5 5 # pip-compile --generate-hashes --output-file=contrib/packaging/requirements-windows-py3.txt contrib/packaging/requirements-windows.txt.in
6 6 #
7 7 atomicwrites==1.4.0 \
8 8 --hash=sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197 \
9 9 --hash=sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a
10 10 # via pytest
11 11 attrs==21.2.0 \
12 12 --hash=sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1 \
13 13 --hash=sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb
14 14 # via pytest
15 15 cached-property==1.5.2 \
16 16 --hash=sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130 \
17 17 --hash=sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0
18 18 # via pygit2
19 19 certifi==2021.5.30 \
20 20 --hash=sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee \
21 21 --hash=sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8
22 22 # via dulwich
23 23 cffi==1.15.0 \
24 24 --hash=sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3 \
25 25 --hash=sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2 \
26 26 --hash=sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636 \
27 27 --hash=sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20 \
28 28 --hash=sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728 \
29 29 --hash=sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27 \
30 30 --hash=sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66 \
31 31 --hash=sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443 \
32 32 --hash=sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0 \
33 33 --hash=sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7 \
34 34 --hash=sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39 \
35 35 --hash=sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605 \
36 36 --hash=sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a \
37 37 --hash=sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37 \
38 38 --hash=sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029 \
39 39 --hash=sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139 \
40 40 --hash=sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc \
41 41 --hash=sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df \
42 42 --hash=sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14 \
43 43 --hash=sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880 \
44 44 --hash=sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2 \
45 45 --hash=sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a \
46 46 --hash=sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e \
47 47 --hash=sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474 \
48 48 --hash=sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024 \
49 49 --hash=sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8 \
50 50 --hash=sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0 \
51 51 --hash=sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e \
52 52 --hash=sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a \
53 53 --hash=sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e \
54 54 --hash=sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032 \
55 55 --hash=sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6 \
56 56 --hash=sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e \
57 57 --hash=sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b \
58 58 --hash=sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e \
59 59 --hash=sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954 \
60 60 --hash=sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962 \
61 61 --hash=sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c \
62 62 --hash=sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4 \
63 63 --hash=sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55 \
64 64 --hash=sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962 \
65 65 --hash=sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023 \
66 66 --hash=sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c \
67 67 --hash=sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6 \
68 68 --hash=sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8 \
69 69 --hash=sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382 \
70 70 --hash=sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7 \
71 71 --hash=sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc \
72 72 --hash=sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997 \
73 73 --hash=sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796
74 74 # via pygit2
75 75 colorama==0.4.4 \
76 76 --hash=sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b \
77 77 --hash=sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2
78 78 # via pytest
79 79 docutils==0.16 \
80 80 --hash=sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af \
81 81 --hash=sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc
82 82 # via -r contrib/packaging/requirements-windows.txt.in
83 dulwich==0.20.6 ; python_version >= "3" \
84 --hash=sha256:1ccd55e38fa9f169290f93e027ab4508202f5bdd6ef534facac4edd3f6903f0d \
85 --hash=sha256:2452a0379cc7bbbd7ab893ec104d18039f1ea98b0d6be6bca5646e5cf29e0ae9 \
86 --hash=sha256:2f4aebc54ed2d37dcee737024421452375570a422eb682232e676aa7ebc9cb4b \
87 --hash=sha256:304f52b10c49c3a6ddfbd73e2e93d8e979350225cfba9688e51110e74fa2f718 \
88 --hash=sha256:49e747c72d9099e873bf6196260346d5996c3f28af788294d47a8accdc524de7 \
89 --hash=sha256:4fee359928c59b53af153a582a7ed7595259a5a825df400301a29e17fd78dfd3 \
90 --hash=sha256:50ef300a9fa4efd9f85009c2bd8b515266ec1529400f8834f85c04fa9f09b2c0 \
91 --hash=sha256:5348310f21b2a23847342ce464461499b6652483fa42de03714d0f6421a99698 \
92 --hash=sha256:7e7b5dea5178b6493fdb83adccbe81de9ddff55f79880185ed594c0e3a97209b \
93 --hash=sha256:8f7a7f973be2beedfb10dd8d3eb6bdf9ec466c72ad555704897cbd6357fe5021 \
94 --hash=sha256:bea6e6caffc6c73bfd1647714c5715ab96ac49deb8beb8b67511529afa25685a \
95 --hash=sha256:e5871b86a079e9e290f52ab14559cea1b694a0b8ed2b9ebb898f6ced7f14a406 \
96 --hash=sha256:e593f514b8ac740b4ceeb047745b4719bfc9f334904245c6edcb3a9d002f577b
83 dulwich==0.20.45 \
84 --hash=sha256:042bc206764968b17338e32c52bb6a116154eb87a63651971946917dfa37a359 \
85 --hash=sha256:22d433ba9c776f2b0e19b1186e01e25ca286175e20f4ac422141db94eeaac08b \
86 --hash=sha256:2d7cf5171034d9d61b928bd5f9c509000e895d1ba29bd6ea850b9e4f93fca0f7 \
87 --hash=sha256:3136bcaf7508522a2aa63f856743f06129261bc5a03331aa6a0654fa6d04a4ae \
88 --hash=sha256:35015e43207752cf7924860e85a3c2290c652c0c3ee81e7c95c52d34638f605d \
89 --hash=sha256:3f2c137a0003e80e384d116e65b453f8a704c2d393c30a47b447764e7f9c05a1 \
90 --hash=sha256:49852f12c1e1d50039f927e9fdee1bd00a9b428c31b078ba5ba9fc1cf88e9d3e \
91 --hash=sha256:4abb1b0e1e50192ce7204c4e14f24c989c5920c56de908365f4e66c6e3458945 \
92 --hash=sha256:4e405ac9426288ca782c45e066f816d878b4a529acf4d4b0b2a5bb45a804dfec \
93 --hash=sha256:5e41044ac51a4b3454d67e5f691808540470deeb6a852d7c5c6ca44c48b4cdc3 \
94 --hash=sha256:65334bd7a1d91054516a49f86343e9c2549740bbddebcbb4763c8aacf2aac48c \
95 --hash=sha256:6e02babb44bdad17b6c9c50b4f9df42f6e511e3a51555ac07dd85ec904efe0b1 \
96 --hash=sha256:70710dd9ca2a442190c7e506892db074c318ac762e221f7529b8ce34802041b7 \
97 --hash=sha256:9b689b05bc7baa5cb20ebff54291085b598a9bdf7caeab23daf93b46421d96ff \
98 --hash=sha256:b3f64870f2f206dda3308cb73563f5f59fdc084179271651a0488d12ab4185b9 \
99 --hash=sha256:bb75268cec2f3ae6f6b7addbc0db50db2e9e42b2ad8364e74b9f5b17ab0053b5 \
100 --hash=sha256:c8c0fc7d2e3b0ad6a4faadf96f0626fa50935ababfd774b9b94edaa28f0668ec \
101 --hash=sha256:d89f53a739ac3394b5ef2f178480569b7d36d4fe7b4bb49678582914530ce35b \
102 --hash=sha256:d8b6aae7af8edbfac8038e1777ae820efac33c7c22a8025d3254bbd53ec725b5 \
103 --hash=sha256:eb4189d72a0e2f3070e2abdbd10a05c0e62355cd5496761d6e68f1e865ac6fad \
104 --hash=sha256:efe46167eb02ba85d9c2e993635e7543e1e04bb3261112e9d54daff2385ae5df
97 105 # via -r contrib/packaging/requirements-windows.txt.in
98 106 fuzzywuzzy==0.18.0 \
99 107 --hash=sha256:45016e92264780e58972dca1b3d939ac864b78437422beecebb3095f8efd00e8
100 108 # via -r contrib/packaging/requirements-windows.txt.in
101 109 idna==3.2 \
102 110 --hash=sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a \
103 111 --hash=sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3
104 112 # via yarl
105 113 importlib-metadata==3.1.0 \
106 114 --hash=sha256:590690d61efdd716ff82c39ca9a9d4209252adfe288a4b5721181050acbd4175 \
107 115 --hash=sha256:d9b8a46a0885337627a6430db287176970fff18ad421becec1d64cfc763c2099
108 116 # via
109 117 # keyring
110 118 # pluggy
111 119 # pytest
112 120 iniconfig==1.1.1 \
113 121 --hash=sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3 \
114 122 --hash=sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32
115 123 # via pytest
116 keyring==21.4.0 \
117 --hash=sha256:4e34ea2fdec90c1c43d6610b5a5fafa1b9097db1802948e90caf5763974b8f8d \
118 --hash=sha256:9aeadd006a852b78f4b4ef7c7556c2774d2432bbef8ee538a3e9089ac8b11466
124 keyring==22.3.0 \
125 --hash=sha256:16927a444b2c73f983520a48dec79ddab49fe76429ea05b8d528d778c8339522 \
126 --hash=sha256:2bc8363ebdd63886126a012057a85c8cb6e143877afa02619ac7dbc9f38a207b
119 127 # via -r contrib/packaging/requirements-windows.txt.in
120 128 multidict==5.1.0 \
121 129 --hash=sha256:018132dbd8688c7a69ad89c4a3f39ea2f9f33302ebe567a879da8f4ca73f0d0a \
122 130 --hash=sha256:051012ccee979b2b06be928a6150d237aec75dd6bf2d1eeeb190baf2b05abc93 \
123 131 --hash=sha256:05c20b68e512166fddba59a918773ba002fdd77800cad9f55b59790030bab632 \
124 132 --hash=sha256:07b42215124aedecc6083f1ce6b7e5ec5b50047afa701f3442054373a6deb656 \
125 133 --hash=sha256:0e3c84e6c67eba89c2dbcee08504ba8644ab4284863452450520dad8f1e89b79 \
126 134 --hash=sha256:0e929169f9c090dae0646a011c8b058e5e5fb391466016b39d21745b48817fd7 \
127 135 --hash=sha256:1ab820665e67373de5802acae069a6a05567ae234ddb129f31d290fc3d1aa56d \
128 136 --hash=sha256:25b4e5f22d3a37ddf3effc0710ba692cfc792c2b9edfb9c05aefe823256e84d5 \
129 137 --hash=sha256:2e68965192c4ea61fff1b81c14ff712fc7dc15d2bd120602e4a3494ea6584224 \
130 138 --hash=sha256:2f1a132f1c88724674271d636e6b7351477c27722f2ed789f719f9e3545a3d26 \
131 139 --hash=sha256:37e5438e1c78931df5d3c0c78ae049092877e5e9c02dd1ff5abb9cf27a5914ea \
132 140 --hash=sha256:3a041b76d13706b7fff23b9fc83117c7b8fe8d5fe9e6be45eee72b9baa75f348 \
133 141 --hash=sha256:3a4f32116f8f72ecf2a29dabfb27b23ab7cdc0ba807e8459e59a93a9be9506f6 \
134 142 --hash=sha256:46c73e09ad374a6d876c599f2328161bcd95e280f84d2060cf57991dec5cfe76 \
135 143 --hash=sha256:46dd362c2f045095c920162e9307de5ffd0a1bfbba0a6e990b344366f55a30c1 \
136 144 --hash=sha256:4b186eb7d6ae7c06eb4392411189469e6a820da81447f46c0072a41c748ab73f \
137 145 --hash=sha256:54fd1e83a184e19c598d5e70ba508196fd0bbdd676ce159feb412a4a6664f952 \
138 146 --hash=sha256:585fd452dd7782130d112f7ddf3473ffdd521414674c33876187e101b588738a \
139 147 --hash=sha256:5cf3443199b83ed9e955f511b5b241fd3ae004e3cb81c58ec10f4fe47c7dce37 \
140 148 --hash=sha256:6a4d5ce640e37b0efcc8441caeea8f43a06addace2335bd11151bc02d2ee31f9 \
141 149 --hash=sha256:7df80d07818b385f3129180369079bd6934cf70469f99daaebfac89dca288359 \
142 150 --hash=sha256:806068d4f86cb06af37cd65821554f98240a19ce646d3cd24e1c33587f313eb8 \
143 151 --hash=sha256:830f57206cc96ed0ccf68304141fec9481a096c4d2e2831f311bde1c404401da \
144 152 --hash=sha256:929006d3c2d923788ba153ad0de8ed2e5ed39fdbe8e7be21e2f22ed06c6783d3 \
145 153 --hash=sha256:9436dc58c123f07b230383083855593550c4d301d2532045a17ccf6eca505f6d \
146 154 --hash=sha256:9dd6e9b1a913d096ac95d0399bd737e00f2af1e1594a787e00f7975778c8b2bf \
147 155 --hash=sha256:ace010325c787c378afd7f7c1ac66b26313b3344628652eacd149bdd23c68841 \
148 156 --hash=sha256:b47a43177a5e65b771b80db71e7be76c0ba23cc8aa73eeeb089ed5219cdbe27d \
149 157 --hash=sha256:b797515be8743b771aa868f83563f789bbd4b236659ba52243b735d80b29ed93 \
150 158 --hash=sha256:b7993704f1a4b204e71debe6095150d43b2ee6150fa4f44d6d966ec356a8d61f \
151 159 --hash=sha256:d5c65bdf4484872c4af3150aeebe101ba560dcfb34488d9a8ff8dbcd21079647 \
152 160 --hash=sha256:d81eddcb12d608cc08081fa88d046c78afb1bf8107e6feab5d43503fea74a635 \
153 161 --hash=sha256:dc862056f76443a0db4509116c5cd480fe1b6a2d45512a653f9a855cc0517456 \
154 162 --hash=sha256:ecc771ab628ea281517e24fd2c52e8f31c41e66652d07599ad8818abaad38cda \
155 163 --hash=sha256:f200755768dc19c6f4e2b672421e0ebb3dd54c38d5a4f262b872d8cfcc9e93b5 \
156 164 --hash=sha256:f21756997ad8ef815d8ef3d34edd98804ab5ea337feedcd62fb52d22bf531281 \
157 165 --hash=sha256:fc13a9524bc18b6fb6e0dbec3533ba0496bbed167c56d0aabefd965584557d80
158 166 # via yarl
159 167 packaging==21.0 \
160 168 --hash=sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7 \
161 169 --hash=sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14
162 170 # via pytest
163 171 pluggy==0.13.1 \
164 172 --hash=sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0 \
165 173 --hash=sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d
166 174 # via pytest
167 175 py==1.10.0 \
168 176 --hash=sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3 \
169 177 --hash=sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a
170 178 # via pytest
171 179 pycparser==2.21 \
172 180 --hash=sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9 \
173 181 --hash=sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206
174 182 # via cffi
175 pygit2==1.7.1 ; python_version >= "3" \
183 pygit2==1.7.1 \
176 184 --hash=sha256:2c9e95efb86c0b32cc07c26be3d179e851ca4a7899c47fef63c4203963144f5e \
177 185 --hash=sha256:3ddacbf461652d3d4900382f821d9fbd5ae2dedecd7862b5245842419ad0ccba \
178 186 --hash=sha256:4cb0414df6089d0072ebe93ff2f34730737172dd5f0e72289567d06a6caf09c0 \
179 187 --hash=sha256:56e960dc74f4582bfa3ca17a1a9d542732fc93b5cf8f82574c235d06b2d61eae \
180 188 --hash=sha256:6b17ab922c2a2d99b30ab9222472b07732bf7261d9f9655a4ea23b4c700049d8 \
181 189 --hash=sha256:73a7b471f22cb59e8729016de1f447c472b3b2c1cc2b622194e5e3b48a7f5776 \
182 190 --hash=sha256:761a8850e33822796c1c24d411d5cc2460c04e1a74b04ae8560efd3596bbd6bd \
183 191 --hash=sha256:7c467e81158f5827b3bca6362e5cc9b92857eff9de65034d338c1f18524b09be \
184 192 --hash=sha256:7c56e10592e62610a19bd3e2a633aafe3488c57b906c7c2fde0299937f0f0b2f \
185 193 --hash=sha256:7cc2a8e29cc9598310a78cf58b70d9331277cf374802be8f97d97c4a9e5d8387 \
186 194 --hash=sha256:812670f7994f31778e873a9eced29d2bbfa91674e8be0ab1e974c8a4bda9cbab \
187 195 --hash=sha256:8cdb0b1d6c3d24b44f340fed143b16e64ba23fe2a449f1a5db87aaf9339a9dbe \
188 196 --hash=sha256:91b77a305d8d18b649396e66e832d654cd593a3d29b5728f753f254a04533812 \
189 197 --hash=sha256:a75bcde32238c77eb0cf7d9698a5aa899408d7ad999a5920a29a7c4b80fdeaa7 \
190 198 --hash=sha256:b060240cf3038e7a0706bbfc5436dd03b8d5ac797ac1d512b613f4d04b974c80 \
191 199 --hash=sha256:cdfa61c0428a8182e5a6a1161c017b824cd511574f080a40b10d6413774eb0ca \
192 200 --hash=sha256:d7faa29558436decc2e78110f38d6677eb366b683ba5cdc2803d47195711165d \
193 201 --hash=sha256:d831825ad9c3b3c28e6b3ef8a2401ad2d3fd4db5455427ff27175a7e254e2592 \
194 202 --hash=sha256:df4c477bdfac85d32a1e3180282cd829a0980aa69be9bd0f7cbd4db1778ca72b \
195 203 --hash=sha256:eced3529bafcaaac015d08dfaa743b3cbad37fcd5b13ae9d280b8b7f716ec5ce \
196 204 --hash=sha256:fec17e2da668e6bb192d777417aad9c7ca924a166d0a0b9a81a11e00362b1bc7
197 205 # via -r contrib/packaging/requirements-windows.txt.in
198 206 pygments==2.7.1 \
199 207 --hash=sha256:307543fe65c0947b126e83dd5a61bd8acbd84abec11f43caebaf5534cbc17998 \
200 208 --hash=sha256:926c3f319eda178d1bd90851e4317e6d8cdb5e292a3386aac9bd75eca29cf9c7
201 209 # via -r contrib/packaging/requirements-windows.txt.in
202 210 pyparsing==2.4.7 \
203 211 --hash=sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1 \
204 212 --hash=sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b
205 213 # via packaging
206 214 pytest==6.2.4 \
207 215 --hash=sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b \
208 216 --hash=sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890
209 217 # via pytest-vcr
210 218 pytest-vcr==1.0.2 \
211 219 --hash=sha256:23ee51b75abbcc43d926272773aae4f39f93aceb75ed56852d0bf618f92e1896
212 220 # via -r contrib/packaging/requirements-windows.txt.in
213 221 pywin32-ctypes==0.2.0 \
214 222 --hash=sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942 \
215 223 --hash=sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98
216 224 # via
217 225 # -r contrib/packaging/requirements-windows.txt.in
218 226 # keyring
219 227 pyyaml==5.4.1 \
220 228 --hash=sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf \
221 229 --hash=sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696 \
222 230 --hash=sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393 \
223 231 --hash=sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77 \
224 232 --hash=sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922 \
225 233 --hash=sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5 \
226 234 --hash=sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8 \
227 235 --hash=sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10 \
228 236 --hash=sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc \
229 237 --hash=sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018 \
230 238 --hash=sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e \
231 239 --hash=sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253 \
232 240 --hash=sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347 \
233 241 --hash=sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183 \
234 242 --hash=sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541 \
235 243 --hash=sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb \
236 244 --hash=sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185 \
237 245 --hash=sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc \
238 246 --hash=sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db \
239 247 --hash=sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa \
240 248 --hash=sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46 \
241 249 --hash=sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122 \
242 250 --hash=sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b \
243 251 --hash=sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63 \
244 252 --hash=sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df \
245 253 --hash=sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc \
246 254 --hash=sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247 \
247 255 --hash=sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6 \
248 256 --hash=sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0
249 257 # via vcrpy
250 258 six==1.16.0 \
251 259 --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
252 260 --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
253 261 # via vcrpy
254 262 toml==0.10.2 \
255 263 --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \
256 264 --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f
257 265 # via pytest
258 266 typing-extensions==3.10.0.0 \
259 267 --hash=sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497 \
260 268 --hash=sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342 \
261 269 --hash=sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84
262 270 # via yarl
263 271 urllib3==1.25.11 \
264 272 --hash=sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2 \
265 273 --hash=sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e
266 274 # via dulwich
267 275 vcrpy==4.1.1 \
268 276 --hash=sha256:12c3fcdae7b88ecf11fc0d3e6d77586549d4575a2ceee18e82eee75c1f626162 \
269 277 --hash=sha256:57095bf22fc0a2d99ee9674cdafebed0f3ba763018582450706f7d3a74fff599
270 278 # via pytest-vcr
271 279 windows-curses==2.3.0 \
272 280 --hash=sha256:170c0d941c2e0cdf864e7f0441c1bdf0709232bf4aa7ce7f54d90fc76a4c0504 \
273 281 --hash=sha256:4d5fb991d1b90a41c2332f02241a1f84c8a1e6bc8f6e0d26f532d0da7a9f7b51 \
274 282 --hash=sha256:7a35eda4cb120b9e1a5ae795f3bc06c55b92c9d391baba6be1903285a05f3551 \
275 283 --hash=sha256:935be95cfdb9213f6f5d3d5bcd489960e3a8fbc9b574e7b2e8a3a3cc46efff49 \
276 284 --hash=sha256:a3a63a0597729e10f923724c2cf972a23ea677b400d2387dee1d668cf7116177 \
277 285 --hash=sha256:c860f596d28377e47f322b7382be4d3573fd76d1292234996bb7f72e0bc0ed0d \
278 286 --hash=sha256:cc5fa913780d60f4a40824d374a4f8ca45b4e205546e83a2d85147315a57457e \
279 287 --hash=sha256:d5cde8ec6d582aa77af791eca54f60858339fb3f391945f9cad11b1ab71062e3 \
280 288 --hash=sha256:e913dc121446d92b33fe4f5bcca26d3a34e4ad19f2af160370d57c3d1e93b4e1 \
281 289 --hash=sha256:fbc2131cec57e422c6660e6cdb3420aff5be5169b8e45bb7c471f884b0590a2b
282 290 # via -r contrib/packaging/requirements-windows.txt.in
283 291 wrapt==1.12.1 \
284 292 --hash=sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7
285 293 # via vcrpy
286 294 yarl==1.6.3 \
287 295 --hash=sha256:00d7ad91b6583602eb9c1d085a2cf281ada267e9a197e8b7cae487dadbfa293e \
288 296 --hash=sha256:0355a701b3998dcd832d0dc47cc5dedf3874f966ac7f870e0f3a6788d802d434 \
289 297 --hash=sha256:15263c3b0b47968c1d90daa89f21fcc889bb4b1aac5555580d74565de6836366 \
290 298 --hash=sha256:2ce4c621d21326a4a5500c25031e102af589edb50c09b321049e388b3934eec3 \
291 299 --hash=sha256:31ede6e8c4329fb81c86706ba8f6bf661a924b53ba191b27aa5fcee5714d18ec \
292 300 --hash=sha256:324ba3d3c6fee56e2e0b0d09bf5c73824b9f08234339d2b788af65e60040c959 \
293 301 --hash=sha256:329412812ecfc94a57cd37c9d547579510a9e83c516bc069470db5f75684629e \
294 302 --hash=sha256:4736eaee5626db8d9cda9eb5282028cc834e2aeb194e0d8b50217d707e98bb5c \
295 303 --hash=sha256:4953fb0b4fdb7e08b2f3b3be80a00d28c5c8a2056bb066169de00e6501b986b6 \
296 304 --hash=sha256:4c5bcfc3ed226bf6419f7a33982fb4b8ec2e45785a0561eb99274ebbf09fdd6a \
297 305 --hash=sha256:547f7665ad50fa8563150ed079f8e805e63dd85def6674c97efd78eed6c224a6 \
298 306 --hash=sha256:5b883e458058f8d6099e4420f0cc2567989032b5f34b271c0827de9f1079a424 \
299 307 --hash=sha256:63f90b20ca654b3ecc7a8d62c03ffa46999595f0167d6450fa8383bab252987e \
300 308 --hash=sha256:68dc568889b1c13f1e4745c96b931cc94fdd0defe92a72c2b8ce01091b22e35f \
301 309 --hash=sha256:69ee97c71fee1f63d04c945f56d5d726483c4762845400a6795a3b75d56b6c50 \
302 310 --hash=sha256:6d6283d8e0631b617edf0fd726353cb76630b83a089a40933043894e7f6721e2 \
303 311 --hash=sha256:72a660bdd24497e3e84f5519e57a9ee9220b6f3ac4d45056961bf22838ce20cc \
304 312 --hash=sha256:73494d5b71099ae8cb8754f1df131c11d433b387efab7b51849e7e1e851f07a4 \
305 313 --hash=sha256:7356644cbed76119d0b6bd32ffba704d30d747e0c217109d7979a7bc36c4d970 \
306 314 --hash=sha256:8a9066529240171b68893d60dca86a763eae2139dd42f42106b03cf4b426bf10 \
307 315 --hash=sha256:8aa3decd5e0e852dc68335abf5478a518b41bf2ab2f330fe44916399efedfae0 \
308 316 --hash=sha256:97b5bdc450d63c3ba30a127d018b866ea94e65655efaf889ebeabc20f7d12406 \
309 317 --hash=sha256:9ede61b0854e267fd565e7527e2f2eb3ef8858b301319be0604177690e1a3896 \
310 318 --hash=sha256:b2e9a456c121e26d13c29251f8267541bd75e6a1ccf9e859179701c36a078643 \
311 319 --hash=sha256:b5dfc9a40c198334f4f3f55880ecf910adebdcb2a0b9a9c23c9345faa9185721 \
312 320 --hash=sha256:bafb450deef6861815ed579c7a6113a879a6ef58aed4c3a4be54400ae8871478 \
313 321 --hash=sha256:c49ff66d479d38ab863c50f7bb27dee97c6627c5fe60697de15529da9c3de724 \
314 322 --hash=sha256:ce3beb46a72d9f2190f9e1027886bfc513702d748047b548b05dab7dfb584d2e \
315 323 --hash=sha256:d26608cf178efb8faa5ff0f2d2e77c208f471c5a3709e577a7b3fd0445703ac8 \
316 324 --hash=sha256:d597767fcd2c3dc49d6eea360c458b65643d1e4dbed91361cf5e36e53c1f8c96 \
317 325 --hash=sha256:d5c32c82990e4ac4d8150fd7652b972216b204de4e83a122546dce571c1bdf25 \
318 326 --hash=sha256:d8d07d102f17b68966e2de0e07bfd6e139c7c02ef06d3a0f8d2f0f055e13bb76 \
319 327 --hash=sha256:e46fba844f4895b36f4c398c5af062a9808d1f26b2999c58909517384d5deda2 \
320 328 --hash=sha256:e6b5460dc5ad42ad2b36cca524491dfcaffbfd9c8df50508bddc354e787b8dc2 \
321 329 --hash=sha256:f040bcc6725c821a4c0665f3aa96a4d0805a7aaf2caf266d256b8ed71b9f041c \
322 330 --hash=sha256:f0b059678fd549c66b89bed03efcabb009075bd131c248ecdf087bdb6faba24a \
323 331 --hash=sha256:fcbb48a93e8699eae920f8d92f7160c03567b421bc17362a9ffbbd706a816f71
324 332 # via vcrpy
325 333 zipp==3.4.0 \
326 334 --hash=sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108 \
327 335 --hash=sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb
328 336 # via importlib-metadata
@@ -1,1000 +1,1016 b''
1 1 # __init__.py - fsmonitor initialization and overrides
2 2 #
3 3 # Copyright 2013-2016 Facebook, Inc.
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''Faster status operations with the Watchman file monitor (EXPERIMENTAL)
9 9
10 10 Integrates the file-watching program Watchman with Mercurial to produce faster
11 11 status results.
12 12
13 13 On a particular Linux system, for a real-world repository with over 400,000
14 14 files hosted on ext4, vanilla `hg status` takes 1.3 seconds. On the same
15 15 system, with fsmonitor it takes about 0.3 seconds.
16 16
17 17 fsmonitor requires no configuration -- it will tell Watchman about your
18 18 repository as necessary. You'll need to install Watchman from
19 19 https://facebook.github.io/watchman/ and make sure it is in your PATH.
20 20
21 21 fsmonitor is incompatible with the largefiles and eol extensions, and
22 22 will disable itself if any of those are active.
23 23
24 24 The following configuration options exist:
25 25
26 26 ::
27 27
28 28 [fsmonitor]
29 29 mode = {off, on, paranoid}
30 30
31 31 When `mode = off`, fsmonitor will disable itself (similar to not loading the
32 32 extension at all). When `mode = on`, fsmonitor will be enabled (the default).
33 33 When `mode = paranoid`, fsmonitor will query both Watchman and the filesystem,
34 34 and ensure that the results are consistent.
35 35
36 36 ::
37 37
38 38 [fsmonitor]
39 39 timeout = (float)
40 40
41 41 A value, in seconds, that determines how long fsmonitor will wait for Watchman
42 42 to return results. Defaults to `2.0`.
43 43
44 44 ::
45 45
46 46 [fsmonitor]
47 47 blacklistusers = (list of userids)
48 48
49 49 A list of usernames for which fsmonitor will disable itself altogether.
50 50
51 51 ::
52 52
53 53 [fsmonitor]
54 54 walk_on_invalidate = (boolean)
55 55
56 56 Whether or not to walk the whole repo ourselves when our cached state has been
57 57 invalidated, for example when Watchman has been restarted or .hgignore rules
58 58 have been changed. Walking the repo in that case can result in competing for
59 59 I/O with Watchman. For large repos it is recommended to set this value to
60 60 false. You may wish to set this to true if you have a very fast filesystem
61 61 that can outpace the IPC overhead of getting the result data for the full repo
62 62 from Watchman. Defaults to false.
63 63
64 64 ::
65 65
66 66 [fsmonitor]
67 67 warn_when_unused = (boolean)
68 68
69 69 Whether to print a warning during certain operations when fsmonitor would be
70 70 beneficial to performance but isn't enabled.
71 71
72 72 ::
73 73
74 74 [fsmonitor]
75 75 warn_update_file_count = (integer)
76 76 # or when mercurial is built with rust support
77 77 warn_update_file_count_rust = (integer)
78 78
79 79 If ``warn_when_unused`` is set and fsmonitor isn't enabled, a warning will
80 80 be printed during working directory updates if this many files will be
81 81 created.
82 82 '''
83 83
84 84 # Platforms Supported
85 85 # ===================
86 86 #
87 87 # **Linux:** *Stable*. Watchman and fsmonitor are both known to work reliably,
88 88 # even under severe loads.
89 89 #
90 90 # **Mac OS X:** *Stable*. The Mercurial test suite passes with fsmonitor
91 91 # turned on, on case-insensitive HFS+. There has been a reasonable amount of
92 92 # user testing under normal loads.
93 93 #
94 94 # **Solaris, BSD:** *Alpha*. watchman and fsmonitor are believed to work, but
95 95 # very little testing has been done.
96 96 #
97 97 # **Windows:** *Alpha*. Not in a release version of watchman or fsmonitor yet.
98 98 #
99 99 # Known Issues
100 100 # ============
101 101 #
102 102 # * fsmonitor will disable itself if any of the following extensions are
103 103 # enabled: largefiles, inotify, eol; or if the repository has subrepos.
104 104 # * fsmonitor will produce incorrect results if nested repos that are not
105 105 # subrepos exist. *Workaround*: add nested repo paths to your `.hgignore`.
106 106 #
107 107 # The issues related to nested repos and subrepos are probably not fundamental
108 108 # ones. Patches to fix them are welcome.
109 109
110 110
111 111 import codecs
112 112 import os
113 113 import stat
114 114 import sys
115 115 import tempfile
116 116 import weakref
117 117
118 118 from mercurial.i18n import _
119 119 from mercurial.node import hex
120
120 121 from mercurial.pycompat import open
121 122 from mercurial import (
122 123 context,
123 124 encoding,
124 125 error,
125 126 extensions,
126 127 localrepo,
127 128 merge,
128 129 pathutil,
129 130 pycompat,
130 131 registrar,
131 132 scmutil,
132 133 util,
133 134 )
135
136 # no-check-code because we're accessing private information only public in pure
137 from mercurial.pure import parsers
134 138 from mercurial import match as matchmod
135 139 from mercurial.utils import (
136 140 hashutil,
137 141 stringutil,
138 142 )
139 143
140 144 from . import (
141 145 pywatchman,
142 146 state,
143 147 watchmanclient,
144 148 )
145 149
146 150 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
147 151 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
148 152 # be specifying the version(s) of Mercurial they are tested with, or
149 153 # leave the attribute unspecified.
150 154 testedwith = b'ships-with-hg-core'
151 155
152 156 configtable = {}
153 157 configitem = registrar.configitem(configtable)
154 158
155 159 configitem(
156 160 b'fsmonitor',
157 161 b'mode',
158 162 default=b'on',
159 163 )
160 164 configitem(
161 165 b'fsmonitor',
162 166 b'walk_on_invalidate',
163 167 default=False,
164 168 )
165 169 configitem(
166 170 b'fsmonitor',
167 171 b'timeout',
168 172 default=b'2',
169 173 )
170 174 configitem(
171 175 b'fsmonitor',
172 176 b'blacklistusers',
173 177 default=list,
174 178 )
175 179 configitem(
176 180 b'fsmonitor',
177 181 b'watchman_exe',
178 182 default=b'watchman',
179 183 )
180 184 configitem(
181 185 b'fsmonitor',
182 186 b'verbose',
183 187 default=True,
184 188 experimental=True,
185 189 )
186 190 configitem(
187 191 b'experimental',
188 192 b'fsmonitor.transaction_notify',
189 193 default=False,
190 194 )
191 195
192 196 # This extension is incompatible with the following blacklisted extensions
193 197 # and will disable itself when encountering one of these:
194 198 _blacklist = [b'largefiles', b'eol']
195 199
196 200
197 201 def debuginstall(ui, fm):
198 202 fm.write(
199 203 b"fsmonitor-watchman",
200 204 _(b"fsmonitor checking for watchman binary... (%s)\n"),
201 205 ui.configpath(b"fsmonitor", b"watchman_exe"),
202 206 )
203 207 root = tempfile.mkdtemp()
204 208 c = watchmanclient.client(ui, root)
205 209 err = None
206 210 try:
207 211 v = c.command(b"version")
208 212 fm.write(
209 213 b"fsmonitor-watchman-version",
210 214 _(b" watchman binary version %s\n"),
211 215 pycompat.bytestr(v["version"]),
212 216 )
213 217 except watchmanclient.Unavailable as e:
214 218 err = stringutil.forcebytestr(e)
215 219 fm.condwrite(
216 220 err,
217 221 b"fsmonitor-watchman-error",
218 222 _(b" watchman binary missing or broken: %s\n"),
219 223 err,
220 224 )
221 225 return 1 if err else 0
222 226
223 227
224 228 def _handleunavailable(ui, state, ex):
225 229 """Exception handler for Watchman interaction exceptions"""
226 230 if isinstance(ex, watchmanclient.Unavailable):
227 231 # experimental config: fsmonitor.verbose
228 232 if ex.warn and ui.configbool(b'fsmonitor', b'verbose'):
229 233 if b'illegal_fstypes' not in stringutil.forcebytestr(ex):
230 234 ui.warn(stringutil.forcebytestr(ex) + b'\n')
231 235 if ex.invalidate:
232 236 state.invalidate()
233 237 # experimental config: fsmonitor.verbose
234 238 if ui.configbool(b'fsmonitor', b'verbose'):
235 239 ui.log(
236 240 b'fsmonitor',
237 241 b'Watchman unavailable: %s\n',
238 242 stringutil.forcebytestr(ex.msg),
239 243 )
240 244 else:
241 245 ui.log(
242 246 b'fsmonitor',
243 247 b'Watchman exception: %s\n',
244 248 stringutil.forcebytestr(ex),
245 249 )
246 250
247 251
248 252 def _hashignore(ignore):
249 253 """Calculate hash for ignore patterns and filenames
250 254
251 255 If this information changes between Mercurial invocations, we can't
252 256 rely on Watchman information anymore and have to re-scan the working
253 257 copy.
254 258
255 259 """
256 260 sha1 = hashutil.sha1()
257 261 sha1.update(pycompat.byterepr(ignore))
258 262 return pycompat.sysbytes(sha1.hexdigest())
259 263
260 264
261 265 _watchmanencoding = pywatchman.encoding.get_local_encoding()
262 266 _fsencoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
263 267 _fixencoding = codecs.lookup(_watchmanencoding) != codecs.lookup(_fsencoding)
264 268
265 269
266 270 def _watchmantofsencoding(path):
267 271 """Fix path to match watchman and local filesystem encoding
268 272
269 273 watchman's paths encoding can differ from filesystem encoding. For example,
270 274 on Windows, it's always utf-8.
271 275 """
272 276 try:
273 277 decoded = path.decode(_watchmanencoding)
274 278 except UnicodeDecodeError as e:
275 279 raise error.Abort(
276 280 stringutil.forcebytestr(e), hint=b'watchman encoding error'
277 281 )
278 282
279 283 try:
280 284 encoded = decoded.encode(_fsencoding, 'strict')
281 285 except UnicodeEncodeError as e:
282 286 raise error.Abort(stringutil.forcebytestr(e))
283 287
284 288 return encoded
285 289
286 290
287 291 def overridewalk(orig, self, match, subrepos, unknown, ignored, full=True):
288 292 """Replacement for dirstate.walk, hooking into Watchman.
289 293
290 294 Whenever full is False, ignored is False, and the Watchman client is
291 295 available, use Watchman combined with saved state to possibly return only a
292 296 subset of files."""
293 297
294 298 def bail(reason):
295 299 self._ui.debug(b'fsmonitor: fallback to core status, %s\n' % reason)
296 300 return orig(match, subrepos, unknown, ignored, full=True)
297 301
298 302 if full:
299 303 return bail(b'full rewalk requested')
300 304 if ignored:
301 305 return bail(b'listing ignored files')
302 306 if not self._watchmanclient.available():
303 307 return bail(b'client unavailable')
304 308 state = self._fsmonitorstate
305 309 clock, ignorehash, notefiles = state.get()
306 310 if not clock:
307 311 if state.walk_on_invalidate:
308 312 return bail(b'no clock')
309 313 # Initial NULL clock value, see
310 314 # https://facebook.github.io/watchman/docs/clockspec.html
311 315 clock = b'c:0:0'
312 316 notefiles = []
313 317
314 318 ignore = self._ignore
315 319 dirignore = self._dirignore
316 320 if unknown:
317 321 if _hashignore(ignore) != ignorehash and clock != b'c:0:0':
318 322 # ignore list changed -- can't rely on Watchman state any more
319 323 if state.walk_on_invalidate:
320 324 return bail(b'ignore rules changed')
321 325 notefiles = []
322 326 clock = b'c:0:0'
323 327 else:
324 328 # always ignore
325 329 ignore = util.always
326 330 dirignore = util.always
327 331
328 332 matchfn = match.matchfn
329 333 matchalways = match.always()
330 334 dmap = self._map
331 335 if util.safehasattr(dmap, b'_map'):
332 336 # for better performance, directly access the inner dirstate map if the
333 337 # standard dirstate implementation is in use.
334 338 dmap = dmap._map
339
340 has_mtime = parsers.DIRSTATE_V2_HAS_MTIME
341 mtime_is_ambiguous = parsers.DIRSTATE_V2_MTIME_SECOND_AMBIGUOUS
342 mask = has_mtime | mtime_is_ambiguous
343
344 # All entries that may not be clean
335 345 nonnormalset = {
336 346 f
337 347 for f, e in self._map.items()
338 if e._v1_state() != b"n" or e._v1_mtime() == -1
348 if not e.maybe_clean
349 # same as "not has_time or has_ambiguous_time", but factored to only
350 # need a single access to flags for performance.
351 # `mask` removes all irrelevant bits, then we flip the `mtime` bit so
352 # its `true` value is NOT having a mtime, then check if either bit
353 # is set.
354 or bool((e.v2_data()[0] & mask) ^ has_mtime)
339 355 }
340 356
341 357 copymap = self._map.copymap
342 358 getkind = stat.S_IFMT
343 359 dirkind = stat.S_IFDIR
344 360 regkind = stat.S_IFREG
345 361 lnkkind = stat.S_IFLNK
346 362 join = self._join
347 363 normcase = util.normcase
348 364 fresh_instance = False
349 365
350 366 exact = skipstep3 = False
351 367 if match.isexact(): # match.exact
352 368 exact = True
353 369 dirignore = util.always # skip step 2
354 370 elif match.prefix(): # match.match, no patterns
355 371 skipstep3 = True
356 372
357 373 if not exact and self._checkcase:
358 374 # note that even though we could receive directory entries, we're only
359 375 # interested in checking if a file with the same name exists. So only
360 376 # normalize files if possible.
361 377 normalize = self._normalizefile
362 378 skipstep3 = False
363 379 else:
364 380 normalize = None
365 381
366 382 # step 1: find all explicit files
367 383 results, work, dirsnotfound = self._walkexplicit(match, subrepos)
368 384
369 385 skipstep3 = skipstep3 and not (work or dirsnotfound)
370 386 work = [d for d in work if not dirignore(d[0])]
371 387
372 388 if not work and (exact or skipstep3):
373 389 for s in subrepos:
374 390 del results[s]
375 391 del results[b'.hg']
376 392 return results
377 393
378 394 # step 2: query Watchman
379 395 try:
380 396 # Use the user-configured timeout for the query.
381 397 # Add a little slack over the top of the user query to allow for
382 398 # overheads while transferring the data
383 399 self._watchmanclient.settimeout(state.timeout + 0.1)
384 400 result = self._watchmanclient.command(
385 401 b'query',
386 402 {
387 403 b'fields': [b'mode', b'mtime', b'size', b'exists', b'name'],
388 404 b'since': clock,
389 405 b'expression': [
390 406 b'not',
391 407 [
392 408 b'anyof',
393 409 [b'dirname', b'.hg'],
394 410 [b'name', b'.hg', b'wholename'],
395 411 ],
396 412 ],
397 413 b'sync_timeout': int(state.timeout * 1000),
398 414 b'empty_on_fresh_instance': state.walk_on_invalidate,
399 415 },
400 416 )
401 417 except Exception as ex:
402 418 _handleunavailable(self._ui, state, ex)
403 419 self._watchmanclient.clearconnection()
404 420 return bail(b'exception during run')
405 421 else:
406 422 # We need to propagate the last observed clock up so that we
407 423 # can use it for our next query
408 424 state.setlastclock(pycompat.sysbytes(result[b'clock']))
409 425 if result[b'is_fresh_instance']:
410 426 if state.walk_on_invalidate:
411 427 state.invalidate()
412 428 return bail(b'fresh instance')
413 429 fresh_instance = True
414 430 # Ignore any prior noteable files from the state info
415 431 notefiles = []
416 432
417 433 # for file paths which require normalization and we encounter a case
418 434 # collision, we store our own foldmap
419 435 if normalize:
420 436 foldmap = {normcase(k): k for k in results}
421 437
422 438 switch_slashes = pycompat.ossep == b'\\'
423 439 # The order of the results is, strictly speaking, undefined.
424 440 # For case changes on a case insensitive filesystem we may receive
425 441 # two entries, one with exists=True and another with exists=False.
426 442 # The exists=True entries in the same response should be interpreted
427 443 # as being happens-after the exists=False entries due to the way that
428 444 # Watchman tracks files. We use this property to reconcile deletes
429 445 # for name case changes.
430 446 for entry in result[b'files']:
431 447 fname = entry[b'name']
432 448
433 449 # Watchman always give us a str. Normalize to bytes on Python 3
434 450 # using Watchman's encoding, if needed.
435 451 if not isinstance(fname, bytes):
436 452 fname = fname.encode(_watchmanencoding)
437 453
438 454 if _fixencoding:
439 455 fname = _watchmantofsencoding(fname)
440 456
441 457 if switch_slashes:
442 458 fname = fname.replace(b'\\', b'/')
443 459 if normalize:
444 460 normed = normcase(fname)
445 461 fname = normalize(fname, True, True)
446 462 foldmap[normed] = fname
447 463 fmode = entry[b'mode']
448 464 fexists = entry[b'exists']
449 465 kind = getkind(fmode)
450 466
451 467 if b'/.hg/' in fname or fname.endswith(b'/.hg'):
452 468 return bail(b'nested-repo-detected')
453 469
454 470 if not fexists:
455 471 # if marked as deleted and we don't already have a change
456 472 # record, mark it as deleted. If we already have an entry
457 473 # for fname then it was either part of walkexplicit or was
458 474 # an earlier result that was a case change
459 475 if (
460 476 fname not in results
461 477 and fname in dmap
462 478 and (matchalways or matchfn(fname))
463 479 ):
464 480 results[fname] = None
465 481 elif kind == dirkind:
466 482 if fname in dmap and (matchalways or matchfn(fname)):
467 483 results[fname] = None
468 484 elif kind == regkind or kind == lnkkind:
469 485 if fname in dmap:
470 486 if matchalways or matchfn(fname):
471 487 results[fname] = entry
472 488 elif (matchalways or matchfn(fname)) and not ignore(fname):
473 489 results[fname] = entry
474 490 elif fname in dmap and (matchalways or matchfn(fname)):
475 491 results[fname] = None
476 492
477 493 # step 3: query notable files we don't already know about
478 494 # XXX try not to iterate over the entire dmap
479 495 if normalize:
480 496 # any notable files that have changed case will already be handled
481 497 # above, so just check membership in the foldmap
482 498 notefiles = {
483 499 normalize(f, True, True)
484 500 for f in notefiles
485 501 if normcase(f) not in foldmap
486 502 }
487 503 visit = {
488 504 f
489 505 for f in notefiles
490 506 if (f not in results and matchfn(f) and (f in dmap or not ignore(f)))
491 507 }
492 508
493 509 if not fresh_instance:
494 510 if matchalways:
495 511 visit.update(f for f in nonnormalset if f not in results)
496 512 visit.update(f for f in copymap if f not in results)
497 513 else:
498 514 visit.update(
499 515 f for f in nonnormalset if f not in results and matchfn(f)
500 516 )
501 517 visit.update(f for f in copymap if f not in results and matchfn(f))
502 518 else:
503 519 if matchalways:
504 520 visit.update(f for f, st in dmap.items() if f not in results)
505 521 visit.update(f for f in copymap if f not in results)
506 522 else:
507 523 visit.update(
508 524 f for f, st in dmap.items() if f not in results and matchfn(f)
509 525 )
510 526 visit.update(f for f in copymap if f not in results and matchfn(f))
511 527
512 528 audit = pathutil.pathauditor(self._root, cached=True).check
513 529 auditpass = [f for f in visit if audit(f)]
514 530 auditpass.sort()
515 531 auditfail = visit.difference(auditpass)
516 532 for f in auditfail:
517 533 results[f] = None
518 534
519 535 nf = iter(auditpass)
520 536 for st in util.statfiles([join(f) for f in auditpass]):
521 537 f = next(nf)
522 538 if st or f in dmap:
523 539 results[f] = st
524 540
525 541 for s in subrepos:
526 542 del results[s]
527 543 del results[b'.hg']
528 544 return results
529 545
530 546
531 547 def overridestatus(
532 548 orig,
533 549 self,
534 550 node1=b'.',
535 551 node2=None,
536 552 match=None,
537 553 ignored=False,
538 554 clean=False,
539 555 unknown=False,
540 556 listsubrepos=False,
541 557 ):
542 558 listignored = ignored
543 559 listclean = clean
544 560 listunknown = unknown
545 561
546 562 def _cmpsets(l1, l2):
547 563 try:
548 564 if b'FSMONITOR_LOG_FILE' in encoding.environ:
549 565 fn = encoding.environ[b'FSMONITOR_LOG_FILE']
550 566 f = open(fn, b'wb')
551 567 else:
552 568 fn = b'fsmonitorfail.log'
553 569 f = self.vfs.open(fn, b'wb')
554 570 except (IOError, OSError):
555 571 self.ui.warn(_(b'warning: unable to write to %s\n') % fn)
556 572 return
557 573
558 574 try:
559 575 for i, (s1, s2) in enumerate(zip(l1, l2)):
560 576 if set(s1) != set(s2):
561 577 f.write(b'sets at position %d are unequal\n' % i)
562 578 f.write(b'watchman returned: %r\n' % s1)
563 579 f.write(b'stat returned: %r\n' % s2)
564 580 finally:
565 581 f.close()
566 582
567 583 if isinstance(node1, context.changectx):
568 584 ctx1 = node1
569 585 else:
570 586 ctx1 = self[node1]
571 587 if isinstance(node2, context.changectx):
572 588 ctx2 = node2
573 589 else:
574 590 ctx2 = self[node2]
575 591
576 592 working = ctx2.rev() is None
577 593 parentworking = working and ctx1 == self[b'.']
578 594 match = match or matchmod.always()
579 595
580 596 # Maybe we can use this opportunity to update Watchman's state.
581 597 # Mercurial uses workingcommitctx and/or memctx to represent the part of
582 598 # the workingctx that is to be committed. So don't update the state in
583 599 # that case.
584 600 # HG_PENDING is set in the environment when the dirstate is being updated
585 601 # in the middle of a transaction; we must not update our state in that
586 602 # case, or we risk forgetting about changes in the working copy.
587 603 updatestate = (
588 604 parentworking
589 605 and match.always()
590 606 and not isinstance(ctx2, (context.workingcommitctx, context.memctx))
591 607 and b'HG_PENDING' not in encoding.environ
592 608 )
593 609
594 610 try:
595 611 if self._fsmonitorstate.walk_on_invalidate:
596 612 # Use a short timeout to query the current clock. If that
597 613 # takes too long then we assume that the service will be slow
598 614 # to answer our query.
599 615 # walk_on_invalidate indicates that we prefer to walk the
600 616 # tree ourselves because we can ignore portions that Watchman
601 617 # cannot and we tend to be faster in the warmer buffer cache
602 618 # cases.
603 619 self._watchmanclient.settimeout(0.1)
604 620 else:
605 621 # Give Watchman more time to potentially complete its walk
606 622 # and return the initial clock. In this mode we assume that
607 623 # the filesystem will be slower than parsing a potentially
608 624 # very large Watchman result set.
609 625 self._watchmanclient.settimeout(self._fsmonitorstate.timeout + 0.1)
610 626 startclock = self._watchmanclient.getcurrentclock()
611 627 except Exception as ex:
612 628 self._watchmanclient.clearconnection()
613 629 _handleunavailable(self.ui, self._fsmonitorstate, ex)
614 630 # boo, Watchman failed. bail
615 631 return orig(
616 632 node1,
617 633 node2,
618 634 match,
619 635 listignored,
620 636 listclean,
621 637 listunknown,
622 638 listsubrepos,
623 639 )
624 640
625 641 if updatestate:
626 642 # We need info about unknown files. This may make things slower the
627 643 # first time, but whatever.
628 644 stateunknown = True
629 645 else:
630 646 stateunknown = listunknown
631 647
632 648 if updatestate:
633 649 ps = poststatus(startclock)
634 650 self.addpostdsstatus(ps)
635 651
636 652 r = orig(
637 653 node1, node2, match, listignored, listclean, stateunknown, listsubrepos
638 654 )
639 655 modified, added, removed, deleted, unknown, ignored, clean = r
640 656
641 657 if not listunknown:
642 658 unknown = []
643 659
644 660 # don't do paranoid checks if we're not going to query Watchman anyway
645 661 full = listclean or match.traversedir is not None
646 662 if self._fsmonitorstate.mode == b'paranoid' and not full:
647 663 # run status again and fall back to the old walk this time
648 664 self.dirstate._fsmonitordisable = True
649 665
650 666 # shut the UI up
651 667 quiet = self.ui.quiet
652 668 self.ui.quiet = True
653 669 fout, ferr = self.ui.fout, self.ui.ferr
654 670 self.ui.fout = self.ui.ferr = open(os.devnull, b'wb')
655 671
656 672 try:
657 673 rv2 = orig(
658 674 node1,
659 675 node2,
660 676 match,
661 677 listignored,
662 678 listclean,
663 679 listunknown,
664 680 listsubrepos,
665 681 )
666 682 finally:
667 683 self.dirstate._fsmonitordisable = False
668 684 self.ui.quiet = quiet
669 685 self.ui.fout, self.ui.ferr = fout, ferr
670 686
671 687 # clean isn't tested since it's set to True above
672 688 with self.wlock():
673 689 _cmpsets(
674 690 [modified, added, removed, deleted, unknown, ignored, clean],
675 691 rv2,
676 692 )
677 693 modified, added, removed, deleted, unknown, ignored, clean = rv2
678 694
679 695 return scmutil.status(
680 696 modified, added, removed, deleted, unknown, ignored, clean
681 697 )
682 698
683 699
684 700 class poststatus:
685 701 def __init__(self, startclock):
686 702 self._startclock = pycompat.sysbytes(startclock)
687 703
688 704 def __call__(self, wctx, status):
689 705 clock = wctx.repo()._fsmonitorstate.getlastclock() or self._startclock
690 706 hashignore = _hashignore(wctx.repo().dirstate._ignore)
691 707 notefiles = (
692 708 status.modified
693 709 + status.added
694 710 + status.removed
695 711 + status.deleted
696 712 + status.unknown
697 713 )
698 714 wctx.repo()._fsmonitorstate.set(clock, hashignore, notefiles)
699 715
700 716
701 717 def makedirstate(repo, dirstate):
702 718 class fsmonitordirstate(dirstate.__class__):
703 719 def _fsmonitorinit(self, repo):
704 720 # _fsmonitordisable is used in paranoid mode
705 721 self._fsmonitordisable = False
706 722 self._fsmonitorstate = repo._fsmonitorstate
707 723 self._watchmanclient = repo._watchmanclient
708 724 self._repo = weakref.proxy(repo)
709 725
710 726 def walk(self, *args, **kwargs):
711 727 orig = super(fsmonitordirstate, self).walk
712 728 if self._fsmonitordisable:
713 729 return orig(*args, **kwargs)
714 730 return overridewalk(orig, self, *args, **kwargs)
715 731
716 732 def rebuild(self, *args, **kwargs):
717 733 self._fsmonitorstate.invalidate()
718 734 return super(fsmonitordirstate, self).rebuild(*args, **kwargs)
719 735
720 736 def invalidate(self, *args, **kwargs):
721 737 self._fsmonitorstate.invalidate()
722 738 return super(fsmonitordirstate, self).invalidate(*args, **kwargs)
723 739
724 740 dirstate.__class__ = fsmonitordirstate
725 741 dirstate._fsmonitorinit(repo)
726 742
727 743
728 744 def wrapdirstate(orig, self):
729 745 ds = orig(self)
730 746 # only override the dirstate when Watchman is available for the repo
731 747 if util.safehasattr(self, b'_fsmonitorstate'):
732 748 makedirstate(self, ds)
733 749 return ds
734 750
735 751
736 752 def extsetup(ui):
737 753 extensions.wrapfilecache(
738 754 localrepo.localrepository, b'dirstate', wrapdirstate
739 755 )
740 756 if pycompat.isdarwin:
741 757 # An assist for avoiding the dangling-symlink fsevents bug
742 758 extensions.wrapfunction(os, b'symlink', wrapsymlink)
743 759
744 760 extensions.wrapfunction(merge, b'_update', wrapupdate)
745 761
746 762
747 763 def wrapsymlink(orig, source, link_name):
748 764 """if we create a dangling symlink, also touch the parent dir
749 765 to encourage fsevents notifications to work more correctly"""
750 766 try:
751 767 return orig(source, link_name)
752 768 finally:
753 769 try:
754 770 os.utime(os.path.dirname(link_name), None)
755 771 except OSError:
756 772 pass
757 773
758 774
759 775 class state_update:
760 776 """This context manager is responsible for dispatching the state-enter
761 777 and state-leave signals to the watchman service. The enter and leave
762 778 methods can be invoked manually (for scenarios where context manager
763 779 semantics are not possible). If parameters oldnode and newnode are None,
764 780 they will be populated based on current working copy in enter and
765 781 leave, respectively. Similarly, if the distance is none, it will be
766 782 calculated based on the oldnode and newnode in the leave method."""
767 783
768 784 def __init__(
769 785 self,
770 786 repo,
771 787 name,
772 788 oldnode=None,
773 789 newnode=None,
774 790 distance=None,
775 791 partial=False,
776 792 ):
777 793 self.repo = repo.unfiltered()
778 794 self.name = name
779 795 self.oldnode = oldnode
780 796 self.newnode = newnode
781 797 self.distance = distance
782 798 self.partial = partial
783 799 self._lock = None
784 800 self.need_leave = False
785 801
786 802 def __enter__(self):
787 803 self.enter()
788 804
789 805 def enter(self):
790 806 # Make sure we have a wlock prior to sending notifications to watchman.
791 807 # We don't want to race with other actors. In the update case,
792 808 # merge.update is going to take the wlock almost immediately. We are
793 809 # effectively extending the lock around several short sanity checks.
794 810 if self.oldnode is None:
795 811 self.oldnode = self.repo[b'.'].node()
796 812
797 813 if self.repo.currentwlock() is None:
798 814 if util.safehasattr(self.repo, b'wlocknostateupdate'):
799 815 self._lock = self.repo.wlocknostateupdate()
800 816 else:
801 817 self._lock = self.repo.wlock()
802 818 self.need_leave = self._state(b'state-enter', hex(self.oldnode))
803 819 return self
804 820
805 821 def __exit__(self, type_, value, tb):
806 822 abort = True if type_ else False
807 823 self.exit(abort=abort)
808 824
809 825 def exit(self, abort=False):
810 826 try:
811 827 if self.need_leave:
812 828 status = b'failed' if abort else b'ok'
813 829 if self.newnode is None:
814 830 self.newnode = self.repo[b'.'].node()
815 831 if self.distance is None:
816 832 self.distance = calcdistance(
817 833 self.repo, self.oldnode, self.newnode
818 834 )
819 835 self._state(b'state-leave', hex(self.newnode), status=status)
820 836 finally:
821 837 self.need_leave = False
822 838 if self._lock:
823 839 self._lock.release()
824 840
825 841 def _state(self, cmd, commithash, status=b'ok'):
826 842 if not util.safehasattr(self.repo, b'_watchmanclient'):
827 843 return False
828 844 try:
829 845 self.repo._watchmanclient.command(
830 846 cmd,
831 847 {
832 848 b'name': self.name,
833 849 b'metadata': {
834 850 # the target revision
835 851 b'rev': commithash,
836 852 # approximate number of commits between current and target
837 853 b'distance': self.distance if self.distance else 0,
838 854 # success/failure (only really meaningful for state-leave)
839 855 b'status': status,
840 856 # whether the working copy parent is changing
841 857 b'partial': self.partial,
842 858 },
843 859 },
844 860 )
845 861 return True
846 862 except Exception as e:
847 863 # Swallow any errors; fire and forget
848 864 self.repo.ui.log(
849 865 b'watchman', b'Exception %s while running %s\n', e, cmd
850 866 )
851 867 return False
852 868
853 869
854 870 # Estimate the distance between two nodes
855 871 def calcdistance(repo, oldnode, newnode):
856 872 anc = repo.changelog.ancestor(oldnode, newnode)
857 873 ancrev = repo[anc].rev()
858 874 distance = abs(repo[oldnode].rev() - ancrev) + abs(
859 875 repo[newnode].rev() - ancrev
860 876 )
861 877 return distance
862 878
863 879
864 880 # Bracket working copy updates with calls to the watchman state-enter
865 881 # and state-leave commands. This allows clients to perform more intelligent
866 882 # settling during bulk file change scenarios
867 883 # https://facebook.github.io/watchman/docs/cmd/subscribe.html#advanced-settling
868 884 def wrapupdate(
869 885 orig,
870 886 repo,
871 887 node,
872 888 branchmerge,
873 889 force,
874 890 ancestor=None,
875 891 mergeancestor=False,
876 892 labels=None,
877 893 matcher=None,
878 894 **kwargs
879 895 ):
880 896
881 897 distance = 0
882 898 partial = True
883 899 oldnode = repo[b'.'].node()
884 900 newnode = repo[node].node()
885 901 if matcher is None or matcher.always():
886 902 partial = False
887 903 distance = calcdistance(repo.unfiltered(), oldnode, newnode)
888 904
889 905 with state_update(
890 906 repo,
891 907 name=b"hg.update",
892 908 oldnode=oldnode,
893 909 newnode=newnode,
894 910 distance=distance,
895 911 partial=partial,
896 912 ):
897 913 return orig(
898 914 repo,
899 915 node,
900 916 branchmerge,
901 917 force,
902 918 ancestor,
903 919 mergeancestor,
904 920 labels,
905 921 matcher,
906 922 **kwargs
907 923 )
908 924
909 925
910 926 def repo_has_depth_one_nested_repo(repo):
911 927 for f in repo.wvfs.listdir():
912 928 if os.path.isdir(os.path.join(repo.root, f, b'.hg')):
913 929 msg = b'fsmonitor: sub-repository %r detected, fsmonitor disabled\n'
914 930 repo.ui.debug(msg % f)
915 931 return True
916 932 return False
917 933
918 934
919 935 def reposetup(ui, repo):
920 936 # We don't work with largefiles or inotify
921 937 exts = extensions.enabled()
922 938 for ext in _blacklist:
923 939 if ext in exts:
924 940 ui.warn(
925 941 _(
926 942 b'The fsmonitor extension is incompatible with the %s '
927 943 b'extension and has been disabled.\n'
928 944 )
929 945 % ext
930 946 )
931 947 return
932 948
933 949 if repo.local():
934 950 # We don't work with subrepos either.
935 951 #
936 952 # if repo[None].substate can cause a dirstate parse, which is too
937 953 # slow. Instead, look for a file called hgsubstate,
938 954 if repo.wvfs.exists(b'.hgsubstate') or repo.wvfs.exists(b'.hgsub'):
939 955 return
940 956
941 957 if repo_has_depth_one_nested_repo(repo):
942 958 return
943 959
944 960 fsmonitorstate = state.state(repo)
945 961 if fsmonitorstate.mode == b'off':
946 962 return
947 963
948 964 try:
949 965 client = watchmanclient.client(repo.ui, repo.root)
950 966 except Exception as ex:
951 967 _handleunavailable(ui, fsmonitorstate, ex)
952 968 return
953 969
954 970 repo._fsmonitorstate = fsmonitorstate
955 971 repo._watchmanclient = client
956 972
957 973 dirstate, cached = localrepo.isfilecached(repo, b'dirstate')
958 974 if cached:
959 975 # at this point since fsmonitorstate wasn't present,
960 976 # repo.dirstate is not a fsmonitordirstate
961 977 makedirstate(repo, dirstate)
962 978
963 979 class fsmonitorrepo(repo.__class__):
964 980 def status(self, *args, **kwargs):
965 981 orig = super(fsmonitorrepo, self).status
966 982 return overridestatus(orig, self, *args, **kwargs)
967 983
968 984 def wlocknostateupdate(self, *args, **kwargs):
969 985 return super(fsmonitorrepo, self).wlock(*args, **kwargs)
970 986
971 987 def wlock(self, *args, **kwargs):
972 988 l = super(fsmonitorrepo, self).wlock(*args, **kwargs)
973 989 if not ui.configbool(
974 990 b"experimental", b"fsmonitor.transaction_notify"
975 991 ):
976 992 return l
977 993 if l.held != 1:
978 994 return l
979 995 origrelease = l.releasefn
980 996
981 997 def staterelease():
982 998 if origrelease:
983 999 origrelease()
984 1000 if l.stateupdate:
985 1001 l.stateupdate.exit()
986 1002 l.stateupdate = None
987 1003
988 1004 try:
989 1005 l.stateupdate = None
990 1006 l.stateupdate = state_update(self, name=b"hg.transaction")
991 1007 l.stateupdate.enter()
992 1008 l.releasefn = staterelease
993 1009 except Exception as e:
994 1010 # Swallow any errors; fire and forget
995 1011 self.ui.log(
996 1012 b'watchman', b'Exception in state update %s\n', e
997 1013 )
998 1014 return l
999 1015
1000 1016 repo.__class__ = fsmonitorrepo
@@ -1,350 +1,352 b''
1 1 """grant Mercurial the ability to operate on Git repositories. (EXPERIMENTAL)
2 2
3 3 This is currently super experimental. It probably will consume your
4 4 firstborn a la Rumpelstiltskin, etc.
5 5 """
6 6
7 7
8 8 import os
9 9
10 10 from mercurial.i18n import _
11 11
12 12 from mercurial import (
13 13 commands,
14 14 error,
15 15 extensions,
16 16 localrepo,
17 17 pycompat,
18 18 registrar,
19 19 requirements as requirementsmod,
20 20 scmutil,
21 21 store,
22 22 util,
23 23 )
24 24
25 25 from . import (
26 26 dirstate,
27 27 gitlog,
28 28 gitutil,
29 29 index,
30 30 )
31 31
32 32 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
33 33 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
34 34 # be specifying the version(s) of Mercurial they are tested with, or
35 35 # leave the attribute unspecified.
36 36 testedwith = b'ships-with-hg-core'
37 37
38 38 configtable = {}
39 39 configitem = registrar.configitem(configtable)
40 40 # git.log-index-cache-miss: internal knob for testing
41 41 configitem(
42 42 b"git",
43 43 b"log-index-cache-miss",
44 44 default=False,
45 45 )
46 46
47 47 getversion = gitutil.pygit2_version
48 48
49 49
50 50 # TODO: extract an interface for this in core
51 51 class gitstore: # store.basicstore):
52 52 def __init__(self, path, vfstype):
53 53 self.vfs = vfstype(path)
54 54 self.opener = self.vfs
55 55 self.path = self.vfs.base
56 56 self.createmode = store._calcmode(self.vfs)
57 57 # above lines should go away in favor of:
58 58 # super(gitstore, self).__init__(path, vfstype)
59 59
60 60 self.git = gitutil.get_pygit2().Repository(
61 61 os.path.normpath(os.path.join(path, b'..', b'.git'))
62 62 )
63 63 self._progress_factory = lambda *args, **kwargs: None
64 64 self._logfn = lambda x: None
65 65
66 66 @util.propertycache
67 67 def _db(self):
68 68 # We lazy-create the database because we want to thread a
69 69 # progress callback down to the indexing process if it's
70 70 # required, and we don't have a ui handle in makestore().
71 71 return index.get_index(self.git, self._logfn, self._progress_factory)
72 72
73 73 def join(self, f):
74 74 """Fake store.join method for git repositories.
75 75
76 76 For the most part, store.join is used for @storecache
77 77 decorators to invalidate caches when various files
78 78 change. We'll map the ones we care about, and ignore the rest.
79 79 """
80 80 if f in (b'00changelog.i', b'00manifest.i'):
81 81 # This is close enough: in order for the changelog cache
82 82 # to be invalidated, HEAD will have to change.
83 83 return os.path.join(self.path, b'HEAD')
84 84 elif f == b'lock':
85 85 # TODO: we probably want to map this to a git lock, I
86 86 # suspect index.lock. We should figure out what the
87 87 # most-alike file is in git-land. For now we're risking
88 88 # bad concurrency errors if another git client is used.
89 89 return os.path.join(self.path, b'hgit-bogus-lock')
90 90 elif f in (b'obsstore', b'phaseroots', b'narrowspec', b'bookmarks'):
91 91 return os.path.join(self.path, b'..', b'.hg', f)
92 92 raise NotImplementedError(b'Need to pick file for %s.' % f)
93 93
94 94 def changelog(self, trypending, concurrencychecker):
95 95 # TODO we don't have a plan for trypending in hg's git support yet
96 96 return gitlog.changelog(self.git, self._db)
97 97
98 98 def manifestlog(self, repo, storenarrowmatch):
99 99 # TODO handle storenarrowmatch and figure out if we need the repo arg
100 100 return gitlog.manifestlog(self.git, self._db)
101 101
102 102 def invalidatecaches(self):
103 103 pass
104 104
105 105 def write(self, tr=None):
106 106 # normally this handles things like fncache writes, which we don't have
107 107 pass
108 108
109 109
110 110 def _makestore(orig, requirements, storebasepath, vfstype):
111 111 if b'git' in requirements:
112 112 if not os.path.exists(os.path.join(storebasepath, b'..', b'.git')):
113 113 raise error.Abort(
114 114 _(
115 115 b'repository specified git format in '
116 116 b'.hg/requires but has no .git directory'
117 117 )
118 118 )
119 119 # Check for presence of pygit2 only here. The assumption is that we'll
120 120 # run this code iff we'll later need pygit2.
121 121 if gitutil.get_pygit2() is None:
122 122 raise error.Abort(
123 123 _(
124 124 b'the git extension requires the Python '
125 125 b'pygit2 library to be installed'
126 126 )
127 127 )
128 128
129 129 return gitstore(storebasepath, vfstype)
130 130 return orig(requirements, storebasepath, vfstype)
131 131
132 132
133 133 class gitfilestorage:
134 134 def file(self, path):
135 135 if path[0:1] == b'/':
136 136 path = path[1:]
137 137 return gitlog.filelog(self.store.git, self.store._db, path)
138 138
139 139
140 140 def _makefilestorage(orig, requirements, features, **kwargs):
141 141 store = kwargs['store']
142 142 if isinstance(store, gitstore):
143 143 return gitfilestorage
144 144 return orig(requirements, features, **kwargs)
145 145
146 146
147 147 def _setupdothg(ui, path):
148 148 dothg = os.path.join(path, b'.hg')
149 149 if os.path.exists(dothg):
150 150 ui.warn(_(b'git repo already initialized for hg\n'))
151 151 else:
152 152 os.mkdir(os.path.join(path, b'.hg'))
153 153 # TODO is it ok to extend .git/info/exclude like this?
154 154 with open(
155 155 os.path.join(path, b'.git', b'info', b'exclude'), 'ab'
156 156 ) as exclude:
157 157 exclude.write(b'\n.hg\n')
158 158 with open(os.path.join(dothg, b'requires'), 'wb') as f:
159 159 f.write(b'git\n')
160 160
161 161
162 162 _BMS_PREFIX = 'refs/heads/'
163 163
164 164
165 165 class gitbmstore:
166 166 def __init__(self, gitrepo):
167 167 self.gitrepo = gitrepo
168 168 self._aclean = True
169 169 self._active = gitrepo.references['HEAD'] # git head, not mark
170 170
171 171 def __contains__(self, name):
172 172 return (
173 173 _BMS_PREFIX + pycompat.fsdecode(name)
174 174 ) in self.gitrepo.references
175 175
176 176 def __iter__(self):
177 177 for r in self.gitrepo.listall_references():
178 178 if r.startswith(_BMS_PREFIX):
179 179 yield pycompat.fsencode(r[len(_BMS_PREFIX) :])
180 180
181 181 def __getitem__(self, k):
182 182 return (
183 183 self.gitrepo.references[_BMS_PREFIX + pycompat.fsdecode(k)]
184 184 .peel()
185 185 .id.raw
186 186 )
187 187
188 188 def get(self, k, default=None):
189 189 try:
190 190 if k in self:
191 191 return self[k]
192 192 return default
193 193 except gitutil.get_pygit2().InvalidSpecError:
194 194 return default
195 195
196 196 @property
197 197 def active(self):
198 198 h = self.gitrepo.references['HEAD']
199 199 if not isinstance(h.target, str) or not h.target.startswith(
200 200 _BMS_PREFIX
201 201 ):
202 202 return None
203 203 return pycompat.fsencode(h.target[len(_BMS_PREFIX) :])
204 204
205 205 @active.setter
206 206 def active(self, mark):
207 githead = mark is not None and (_BMS_PREFIX + mark) or None
207 githead = None
208 if mark is not None:
209 githead = _BMS_PREFIX + pycompat.fsdecode(mark)
208 210 if githead is not None and githead not in self.gitrepo.references:
209 211 raise AssertionError(b'bookmark %s does not exist!' % mark)
210 212
211 213 self._active = githead
212 214 self._aclean = False
213 215
214 216 def _writeactive(self):
215 217 if self._aclean:
216 218 return
217 219 self.gitrepo.references.create('HEAD', self._active, True)
218 220 self._aclean = True
219 221
220 222 def names(self, node):
221 223 r = []
222 224 for ref in self.gitrepo.listall_references():
223 225 if not ref.startswith(_BMS_PREFIX):
224 226 continue
225 227 if self.gitrepo.references[ref].peel().id.raw != node:
226 228 continue
227 229 r.append(pycompat.fsencode(ref[len(_BMS_PREFIX) :]))
228 230 return r
229 231
230 232 # Cleanup opportunity: this is *identical* to core's bookmarks store.
231 233 def expandname(self, bname):
232 234 if bname == b'.':
233 235 if self.active:
234 236 return self.active
235 237 raise error.RepoLookupError(_(b"no active bookmark"))
236 238 return bname
237 239
238 240 def applychanges(self, repo, tr, changes):
239 241 """Apply a list of changes to bookmarks"""
240 242 # TODO: this should respect transactions, but that's going to
241 243 # require enlarging the gitbmstore to know how to do in-memory
242 244 # temporary writes and read those back prior to transaction
243 245 # finalization.
244 246 for name, node in changes:
245 247 if node is None:
246 248 self.gitrepo.references.delete(
247 249 _BMS_PREFIX + pycompat.fsdecode(name)
248 250 )
249 251 else:
250 252 self.gitrepo.references.create(
251 253 _BMS_PREFIX + pycompat.fsdecode(name),
252 254 gitutil.togitnode(node),
253 255 force=True,
254 256 )
255 257
256 258 def checkconflict(self, mark, force=False, target=None):
257 githead = _BMS_PREFIX + mark
259 githead = _BMS_PREFIX + pycompat.fsdecode(mark)
258 260 cur = self.gitrepo.references['HEAD']
259 261 if githead in self.gitrepo.references and not force:
260 262 if target:
261 263 if self.gitrepo.references[githead] == target and target == cur:
262 264 # re-activating a bookmark
263 265 return []
264 266 # moving a bookmark - forward?
265 267 raise NotImplementedError
266 268 raise error.Abort(
267 269 _(b"bookmark '%s' already exists (use -f to force)") % mark
268 270 )
269 271 if len(mark) > 3 and not force:
270 272 try:
271 273 shadowhash = scmutil.isrevsymbol(self._repo, mark)
272 274 except error.LookupError: # ambiguous identifier
273 275 shadowhash = False
274 276 if shadowhash:
275 277 self._repo.ui.warn(
276 278 _(
277 279 b"bookmark %s matches a changeset hash\n"
278 280 b"(did you leave a -r out of an 'hg bookmark' "
279 281 b"command?)\n"
280 282 )
281 283 % mark
282 284 )
283 285 return []
284 286
285 287
286 288 def init(orig, ui, dest=b'.', **opts):
287 289 if opts.get('git', False):
288 290 path = util.abspath(dest)
289 291 # TODO: walk up looking for the git repo
290 292 _setupdothg(ui, path)
291 293 return 0
292 294 return orig(ui, dest=dest, **opts)
293 295
294 296
295 297 def reposetup(ui, repo):
296 298 if repo.local() and isinstance(repo.store, gitstore):
297 299 orig = repo.__class__
298 300 repo.store._progress_factory = repo.ui.makeprogress
299 301 if ui.configbool(b'git', b'log-index-cache-miss'):
300 302 repo.store._logfn = repo.ui.warn
301 303
302 304 class gitlocalrepo(orig):
303 305 def _makedirstate(self):
304 306 v2_req = requirementsmod.DIRSTATE_V2_REQUIREMENT
305 307 use_dirstate_v2 = v2_req in self.requirements
306 308
307 309 # TODO narrow support here
308 310 return dirstate.gitdirstate(
309 311 self.ui,
310 312 self.vfs,
311 313 self.store.git,
312 314 use_dirstate_v2,
313 315 )
314 316
315 317 def commit(self, *args, **kwargs):
316 318 ret = orig.commit(self, *args, **kwargs)
317 319 if ret is None:
318 320 # there was nothing to commit, so we should skip
319 321 # the index fixup logic we'd otherwise do.
320 322 return None
321 323 tid = self.store.git[gitutil.togitnode(ret)].tree.id
322 324 # DANGER! This will flush any writes staged to the
323 325 # index in Git, but we're sidestepping the index in a
324 326 # way that confuses git when we commit. Alas.
325 327 self.store.git.index.read_tree(tid)
326 328 self.store.git.index.write()
327 329 return ret
328 330
329 331 @property
330 332 def _bookmarks(self):
331 333 return gitbmstore(self.store.git)
332 334
333 335 repo.__class__ = gitlocalrepo
334 336 return repo
335 337
336 338
337 339 def _featuresetup(ui, supported):
338 340 # don't die on seeing a repo with the git requirement
339 341 supported |= {b'git'}
340 342
341 343
342 344 def extsetup(ui):
343 345 extensions.wrapfunction(localrepo, b'makestore', _makestore)
344 346 extensions.wrapfunction(localrepo, b'makefilestorage', _makefilestorage)
345 347 # Inject --git flag for `hg init`
346 348 entry = extensions.wrapcommand(commands.table, b'init', init)
347 349 entry[1].extend(
348 350 [(b'', b'git', None, b'setup up a git repository instead of hg')]
349 351 )
350 352 localrepo.featuresetupfuncs.add(_featuresetup)
@@ -1,401 +1,402 b''
1 1 import contextlib
2 2 import os
3 3
4 4 from mercurial.node import sha1nodeconstants
5 5 from mercurial import (
6 6 dirstatemap,
7 7 error,
8 8 extensions,
9 9 match as matchmod,
10 10 pycompat,
11 11 scmutil,
12 12 util,
13 13 )
14 14 from mercurial.dirstateutils import (
15 15 timestamp,
16 16 )
17 17 from mercurial.interfaces import (
18 18 dirstate as intdirstate,
19 19 util as interfaceutil,
20 20 )
21 21
22 22 from . import gitutil
23 23
24 24
25 25 DirstateItem = dirstatemap.DirstateItem
26 26 propertycache = util.propertycache
27 27 pygit2 = gitutil.get_pygit2()
28 28
29 29
30 30 def readpatternfile(orig, filepath, warn, sourceinfo=False):
31 31 if not (b'info/exclude' in filepath or filepath.endswith(b'.gitignore')):
32 32 return orig(filepath, warn, sourceinfo=False)
33 33 result = []
34 34 warnings = []
35 35 with open(filepath, 'rb') as fp:
36 36 for l in fp:
37 37 l = l.strip()
38 38 if not l or l.startswith(b'#'):
39 39 continue
40 40 if l.startswith(b'!'):
41 41 warnings.append(b'unsupported ignore pattern %s' % l)
42 42 continue
43 43 if l.startswith(b'/'):
44 44 result.append(b'rootglob:' + l[1:])
45 45 else:
46 46 result.append(b'relglob:' + l)
47 47 return result, warnings
48 48
49 49
50 50 extensions.wrapfunction(matchmod, b'readpatternfile', readpatternfile)
51 51
52 52
53 53 _STATUS_MAP = {}
54 54 if pygit2:
55 55 _STATUS_MAP = {
56 56 pygit2.GIT_STATUS_CONFLICTED: b'm',
57 57 pygit2.GIT_STATUS_CURRENT: b'n',
58 58 pygit2.GIT_STATUS_IGNORED: b'?',
59 59 pygit2.GIT_STATUS_INDEX_DELETED: b'r',
60 60 pygit2.GIT_STATUS_INDEX_MODIFIED: b'n',
61 61 pygit2.GIT_STATUS_INDEX_NEW: b'a',
62 62 pygit2.GIT_STATUS_INDEX_RENAMED: b'a',
63 63 pygit2.GIT_STATUS_INDEX_TYPECHANGE: b'n',
64 64 pygit2.GIT_STATUS_WT_DELETED: b'r',
65 65 pygit2.GIT_STATUS_WT_MODIFIED: b'n',
66 66 pygit2.GIT_STATUS_WT_NEW: b'?',
67 67 pygit2.GIT_STATUS_WT_RENAMED: b'a',
68 68 pygit2.GIT_STATUS_WT_TYPECHANGE: b'n',
69 69 pygit2.GIT_STATUS_WT_UNREADABLE: b'?',
70 70 pygit2.GIT_STATUS_INDEX_MODIFIED | pygit2.GIT_STATUS_WT_MODIFIED: b'm',
71 71 }
72 72
73 73
74 74 @interfaceutil.implementer(intdirstate.idirstate)
75 75 class gitdirstate:
76 76 def __init__(self, ui, vfs, gitrepo, use_dirstate_v2):
77 77 self._ui = ui
78 78 self._root = os.path.dirname(vfs.base)
79 79 self._opener = vfs
80 80 self.git = gitrepo
81 81 self._plchangecallbacks = {}
82 82 # TODO: context.poststatusfixup is bad and uses this attribute
83 83 self._dirty = False
84 84 self._mapcls = dirstatemap.dirstatemap
85 85 self._use_dirstate_v2 = use_dirstate_v2
86 86
87 87 @propertycache
88 88 def _map(self):
89 89 """Return the dirstate contents (see documentation for dirstatemap)."""
90 90 self._map = self._mapcls(
91 91 self._ui,
92 92 self._opener,
93 93 self._root,
94 94 sha1nodeconstants,
95 95 self._use_dirstate_v2,
96 96 )
97 97 return self._map
98 98
99 99 def p1(self):
100 100 try:
101 101 return self.git.head.peel().id.raw
102 102 except pygit2.GitError:
103 103 # Typically happens when peeling HEAD fails, as in an
104 104 # empty repository.
105 105 return sha1nodeconstants.nullid
106 106
107 107 def p2(self):
108 108 # TODO: MERGE_HEAD? something like that, right?
109 109 return sha1nodeconstants.nullid
110 110
111 111 def setparents(self, p1, p2=None):
112 112 if p2 is None:
113 113 p2 = sha1nodeconstants.nullid
114 114 assert p2 == sha1nodeconstants.nullid, b'TODO merging support'
115 115 self.git.head.set_target(gitutil.togitnode(p1))
116 116
117 117 @util.propertycache
118 118 def identity(self):
119 119 return util.filestat.frompath(
120 120 os.path.join(self._root, b'.git', b'index')
121 121 )
122 122
123 123 def branch(self):
124 124 return b'default'
125 125
126 126 def parents(self):
127 127 # TODO how on earth do we find p2 if a merge is in flight?
128 128 return self.p1(), sha1nodeconstants.nullid
129 129
130 130 def __iter__(self):
131 131 return (pycompat.fsencode(f.path) for f in self.git.index)
132 132
133 133 def items(self):
134 134 for ie in self.git.index:
135 135 yield ie.path, None # value should be a DirstateItem
136 136
137 137 # py2,3 compat forward
138 138 iteritems = items
139 139
140 140 def __getitem__(self, filename):
141 141 try:
142 142 gs = self.git.status_file(filename)
143 143 except KeyError:
144 144 return b'?'
145 145 return _STATUS_MAP[gs]
146 146
147 147 def __contains__(self, filename):
148 148 try:
149 149 gs = self.git.status_file(filename)
150 150 return _STATUS_MAP[gs] != b'?'
151 151 except KeyError:
152 152 return False
153 153
154 154 def status(self, match, subrepos, ignored, clean, unknown):
155 155 listclean = clean
156 156 # TODO handling of clean files - can we get that from git.status()?
157 157 modified, added, removed, deleted, unknown, ignored, clean = (
158 158 [],
159 159 [],
160 160 [],
161 161 [],
162 162 [],
163 163 [],
164 164 [],
165 165 )
166 166
167 167 try:
168 168 mtime_boundary = timestamp.get_fs_now(self._opener)
169 169 except OSError:
170 170 # In largefiles or readonly context
171 171 mtime_boundary = None
172 172
173 173 gstatus = self.git.status()
174 174 for path, status in gstatus.items():
175 175 path = pycompat.fsencode(path)
176 176 if not match(path):
177 177 continue
178 178 if status == pygit2.GIT_STATUS_IGNORED:
179 179 if path.endswith(b'/'):
180 180 continue
181 181 ignored.append(path)
182 182 elif status in (
183 183 pygit2.GIT_STATUS_WT_MODIFIED,
184 184 pygit2.GIT_STATUS_INDEX_MODIFIED,
185 185 pygit2.GIT_STATUS_WT_MODIFIED
186 186 | pygit2.GIT_STATUS_INDEX_MODIFIED,
187 187 ):
188 188 modified.append(path)
189 189 elif status == pygit2.GIT_STATUS_INDEX_NEW:
190 190 added.append(path)
191 191 elif status == pygit2.GIT_STATUS_WT_NEW:
192 192 unknown.append(path)
193 193 elif status == pygit2.GIT_STATUS_WT_DELETED:
194 194 deleted.append(path)
195 195 elif status == pygit2.GIT_STATUS_INDEX_DELETED:
196 196 removed.append(path)
197 197 else:
198 198 raise error.Abort(
199 199 b'unhandled case: status for %r is %r' % (path, status)
200 200 )
201 201
202 202 if listclean:
203 203 observed = set(
204 204 modified + added + removed + deleted + unknown + ignored
205 205 )
206 206 index = self.git.index
207 207 index.read()
208 208 for entry in index:
209 209 path = pycompat.fsencode(entry.path)
210 210 if not match(path):
211 211 continue
212 212 if path in observed:
213 213 continue # already in some other set
214 214 if path[-1] == b'/':
215 215 continue # directory
216 216 clean.append(path)
217 217
218 218 # TODO are we really always sure of status here?
219 219 return (
220 220 False,
221 221 scmutil.status(
222 222 modified, added, removed, deleted, unknown, ignored, clean
223 223 ),
224 224 mtime_boundary,
225 225 )
226 226
227 227 def flagfunc(self, buildfallback):
228 228 # TODO we can do better
229 229 return buildfallback()
230 230
231 231 def getcwd(self):
232 232 # TODO is this a good way to do this?
233 233 return os.path.dirname(
234 234 os.path.dirname(pycompat.fsencode(self.git.path))
235 235 )
236 236
237 237 def get_entry(self, path):
238 238 """return a DirstateItem for the associated path"""
239 239 entry = self._map.get(path)
240 240 if entry is None:
241 241 return DirstateItem()
242 242 return entry
243 243
244 244 def normalize(self, path):
245 245 normed = util.normcase(path)
246 246 assert normed == path, b"TODO handling of case folding: %s != %s" % (
247 247 normed,
248 248 path,
249 249 )
250 250 return path
251 251
252 252 @property
253 253 def _checklink(self):
254 254 return util.checklink(os.path.dirname(pycompat.fsencode(self.git.path)))
255 255
256 256 def copies(self):
257 257 # TODO support copies?
258 258 return {}
259 259
260 260 # # TODO what the heck is this
261 261 _filecache = set()
262 262
263 263 def pendingparentchange(self):
264 264 # TODO: we need to implement the context manager bits and
265 265 # correctly stage/revert index edits.
266 266 return False
267 267
268 268 def write(self, tr):
269 269 # TODO: call parent change callbacks
270 270
271 271 if tr:
272 272
273 273 def writeinner(category):
274 274 self.git.index.write()
275 275
276 276 tr.addpending(b'gitdirstate', writeinner)
277 277 else:
278 278 self.git.index.write()
279 279
280 280 def pathto(self, f, cwd=None):
281 281 if cwd is None:
282 282 cwd = self.getcwd()
283 283 # TODO core dirstate does something about slashes here
284 284 assert isinstance(f, bytes)
285 285 r = util.pathto(self._root, cwd, f)
286 286 return r
287 287
288 288 def matches(self, match):
289 289 for x in self.git.index:
290 290 p = pycompat.fsencode(x.path)
291 291 if match(p):
292 292 yield p
293 293
294 294 def set_clean(self, f, parentfiledata):
295 295 """Mark a file normal and clean."""
296 296 # TODO: for now we just let libgit2 re-stat the file. We can
297 297 # clearly do better.
298 298
299 299 def set_possibly_dirty(self, f):
300 300 """Mark a file normal, but possibly dirty."""
301 301 # TODO: for now we just let libgit2 re-stat the file. We can
302 302 # clearly do better.
303 303
304 304 def walk(self, match, subrepos, unknown, ignored, full=True):
305 305 # TODO: we need to use .status() and not iterate the index,
306 306 # because the index doesn't force a re-walk and so `hg add` of
307 307 # a new file without an intervening call to status will
308 308 # silently do nothing.
309 309 r = {}
310 310 cwd = self.getcwd()
311 311 for path, status in self.git.status().items():
312 312 if path.startswith('.hg/'):
313 313 continue
314 314 path = pycompat.fsencode(path)
315 315 if not match(path):
316 316 continue
317 317 # TODO construct the stat info from the status object?
318 318 try:
319 319 s = os.stat(os.path.join(cwd, path))
320 320 except FileNotFoundError:
321 321 continue
322 322 r[path] = s
323 323 return r
324 324
325 325 def savebackup(self, tr, backupname):
326 326 # TODO: figure out a strategy for saving index backups.
327 327 pass
328 328
329 329 def restorebackup(self, tr, backupname):
330 330 # TODO: figure out a strategy for saving index backups.
331 331 pass
332 332
333 def set_tracked(self, f):
333 def set_tracked(self, f, reset_copy=False):
334 # TODO: support copies and reset_copy=True
334 335 uf = pycompat.fsdecode(f)
335 336 if uf in self.git.index:
336 337 return False
337 338 index = self.git.index
338 339 index.read()
339 340 index.add(uf)
340 341 index.write()
341 342 return True
342 343
343 344 def add(self, f):
344 345 index = self.git.index
345 346 index.read()
346 347 index.add(pycompat.fsdecode(f))
347 348 index.write()
348 349
349 350 def drop(self, f):
350 351 index = self.git.index
351 352 index.read()
352 353 fs = pycompat.fsdecode(f)
353 354 if fs in index:
354 355 index.remove(fs)
355 356 index.write()
356 357
357 358 def set_untracked(self, f):
358 359 index = self.git.index
359 360 index.read()
360 361 fs = pycompat.fsdecode(f)
361 362 if fs in index:
362 363 index.remove(fs)
363 364 index.write()
364 365 return True
365 366 return False
366 367
367 368 def remove(self, f):
368 369 index = self.git.index
369 370 index.read()
370 371 index.remove(pycompat.fsdecode(f))
371 372 index.write()
372 373
373 374 def copied(self, path):
374 375 # TODO: track copies?
375 376 return None
376 377
377 378 def prefetch_parents(self):
378 379 # TODO
379 380 pass
380 381
381 382 def update_file(self, *args, **kwargs):
382 383 # TODO
383 384 pass
384 385
385 386 @contextlib.contextmanager
386 387 def parentchange(self):
387 388 # TODO: track this maybe?
388 389 yield
389 390
390 391 def addparentchangecallback(self, category, callback):
391 392 # TODO: should this be added to the dirstate interface?
392 393 self._plchangecallbacks[category] = callback
393 394
394 395 def clearbackup(self, tr, backupname):
395 396 # TODO
396 397 pass
397 398
398 399 def setbranch(self, branch):
399 400 raise error.Abort(
400 401 b'git repos do not support branches. try using bookmarks'
401 402 )
@@ -1,546 +1,570 b''
1 1 from mercurial.i18n import _
2 2
3 3 from mercurial.node import (
4 4 bin,
5 5 hex,
6 6 nullrev,
7 7 sha1nodeconstants,
8 8 )
9 9 from mercurial import (
10 10 ancestor,
11 11 changelog as hgchangelog,
12 12 dagop,
13 13 encoding,
14 14 error,
15 15 manifest,
16 16 pycompat,
17 17 )
18 18 from mercurial.interfaces import (
19 19 repository,
20 20 util as interfaceutil,
21 21 )
22 22 from mercurial.utils import stringutil
23 23 from . import (
24 24 gitutil,
25 25 index,
26 26 manifest as gitmanifest,
27 27 )
28 28
29 29 pygit2 = gitutil.get_pygit2()
30 30
31 31
32 32 class baselog: # revlog.revlog):
33 33 """Common implementations between changelog and manifestlog."""
34 34
35 35 def __init__(self, gr, db):
36 36 self.gitrepo = gr
37 37 self._db = db
38 38
39 39 def __len__(self):
40 40 return int(
41 41 self._db.execute('SELECT COUNT(*) FROM changelog').fetchone()[0]
42 42 )
43 43
44 44 def rev(self, n):
45 45 if n == sha1nodeconstants.nullid:
46 46 return -1
47 47 t = self._db.execute(
48 48 'SELECT rev FROM changelog WHERE node = ?', (gitutil.togitnode(n),)
49 49 ).fetchone()
50 50 if t is None:
51 51 raise error.LookupError(n, b'00changelog.i', _(b'no node %d'))
52 52 return t[0]
53 53
54 54 def node(self, r):
55 55 if r == nullrev:
56 56 return sha1nodeconstants.nullid
57 57 t = self._db.execute(
58 58 'SELECT node FROM changelog WHERE rev = ?', (r,)
59 59 ).fetchone()
60 60 if t is None:
61 61 raise error.LookupError(r, b'00changelog.i', _(b'no node'))
62 62 return bin(t[0])
63 63
64 64 def hasnode(self, n):
65 65 t = self._db.execute(
66 66 'SELECT node FROM changelog WHERE node = ?',
67 67 (pycompat.sysstr(n),),
68 68 ).fetchone()
69 69 return t is not None
70 70
71 71
72 72 class baselogindex:
73 73 def __init__(self, log):
74 74 self._log = log
75 75
76 76 def has_node(self, n):
77 77 return self._log.rev(n) != -1
78 78
79 79 def __len__(self):
80 80 return len(self._log)
81 81
82 82 def __getitem__(self, idx):
83 83 p1rev, p2rev = self._log.parentrevs(idx)
84 84 # TODO: it's messy that the index leaks so far out of the
85 85 # storage layer that we have to implement things like reading
86 86 # this raw tuple, which exposes revlog internals.
87 87 return (
88 88 # Pretend offset is just the index, since we don't really care.
89 89 idx,
90 90 # Same with lengths
91 91 idx, # length
92 92 idx, # rawsize
93 93 -1, # delta base
94 94 idx, # linkrev TODO is this right?
95 95 p1rev,
96 96 p2rev,
97 97 self._log.node(idx),
98 98 )
99 99
100 100
101 101 # TODO: an interface for the changelog type?
102 102 class changelog(baselog):
103 103 # TODO: this appears to be an enumerated type, and should probably
104 104 # be part of the public changelog interface
105 105 _copiesstorage = b'extra'
106 106
107 107 def __contains__(self, rev):
108 108 try:
109 109 self.node(rev)
110 110 return True
111 111 except error.LookupError:
112 112 return False
113 113
114 114 def __iter__(self):
115 115 return iter(range(len(self)))
116 116
117 117 @property
118 118 def filteredrevs(self):
119 119 # TODO: we should probably add a refs/hg/ namespace for hidden
120 120 # heads etc, but that's an idea for later.
121 121 return set()
122 122
123 123 @property
124 124 def index(self):
125 125 return baselogindex(self)
126 126
127 127 @property
128 128 def nodemap(self):
129 129 r = {
130 130 bin(v[0]): v[1]
131 131 for v in self._db.execute('SELECT node, rev FROM changelog')
132 132 }
133 133 r[sha1nodeconstants.nullid] = nullrev
134 134 return r
135 135
136 136 def tip(self):
137 137 t = self._db.execute(
138 138 'SELECT node FROM changelog ORDER BY rev DESC LIMIT 1'
139 139 ).fetchone()
140 140 if t:
141 141 return bin(t[0])
142 142 return sha1nodeconstants.nullid
143 143
144 144 def revs(self, start=0, stop=None):
145 145 if stop is None:
146 146 stop = self.tiprev()
147 147 t = self._db.execute(
148 148 'SELECT rev FROM changelog '
149 149 'WHERE rev >= ? AND rev <= ? '
150 150 'ORDER BY REV ASC',
151 151 (start, stop),
152 152 )
153 153 return (int(r[0]) for r in t)
154 154
155 155 def tiprev(self):
156 156 t = self._db.execute(
157 157 'SELECT rev FROM changelog ' 'ORDER BY REV DESC ' 'LIMIT 1'
158 158 ).fetchone()
159 159
160 160 if t is not None:
161 161 return t[0]
162 162 return -1
163 163
164 164 def _partialmatch(self, id):
165 165 if sha1nodeconstants.wdirhex.startswith(id):
166 166 raise error.WdirUnsupported
167 167 candidates = [
168 168 bin(x[0])
169 169 for x in self._db.execute(
170 170 'SELECT node FROM changelog WHERE node LIKE ?',
171 171 (pycompat.sysstr(id + b'%'),),
172 172 )
173 173 ]
174 174 if sha1nodeconstants.nullhex.startswith(id):
175 175 candidates.append(sha1nodeconstants.nullid)
176 176 if len(candidates) > 1:
177 177 raise error.AmbiguousPrefixLookupError(
178 178 id, b'00changelog.i', _(b'ambiguous identifier')
179 179 )
180 180 if candidates:
181 181 return candidates[0]
182 182 return None
183 183
184 184 def flags(self, rev):
185 185 return 0
186 186
187 187 def shortest(self, node, minlength=1):
188 188 nodehex = hex(node)
189 189 for attempt in range(minlength, len(nodehex) + 1):
190 190 candidate = nodehex[:attempt]
191 191 matches = int(
192 192 self._db.execute(
193 193 'SELECT COUNT(*) FROM changelog WHERE node LIKE ?',
194 194 (pycompat.sysstr(candidate + b'%'),),
195 195 ).fetchone()[0]
196 196 )
197 197 if matches == 1:
198 198 return candidate
199 199 return nodehex
200 200
201 201 def headrevs(self, revs=None):
202 202 realheads = [
203 203 int(x[0])
204 204 for x in self._db.execute(
205 205 'SELECT rev FROM changelog '
206 206 'INNER JOIN heads ON changelog.node = heads.node'
207 207 )
208 208 ]
209 209 if revs:
210 210 return sorted([r for r in revs if r in realheads])
211 211 return sorted(realheads)
212 212
213 213 def changelogrevision(self, nodeorrev):
214 214 # Ensure we have a node id
215 215 if isinstance(nodeorrev, int):
216 216 n = self.node(nodeorrev)
217 217 else:
218 218 n = nodeorrev
219 219 extra = {b'branch': b'default'}
220 220 # handle looking up nullid
221 221 if n == sha1nodeconstants.nullid:
222 222 return hgchangelog._changelogrevision(
223 223 extra=extra, manifest=sha1nodeconstants.nullid
224 224 )
225 225 hn = gitutil.togitnode(n)
226 226 # We've got a real commit!
227 227 files = [
228 228 r[0]
229 229 for r in self._db.execute(
230 230 'SELECT filename FROM changedfiles '
231 231 'WHERE node = ? and filenode != ?',
232 232 (hn, gitutil.nullgit),
233 233 )
234 234 ]
235 235 filesremoved = [
236 236 r[0]
237 237 for r in self._db.execute(
238 238 'SELECT filename FROM changedfiles '
239 239 'WHERE node = ? and filenode = ?',
240 240 (hn, gitutil.nullgit),
241 241 )
242 242 ]
243 243 c = self.gitrepo[hn]
244 244 return hgchangelog._changelogrevision(
245 245 manifest=n, # pretend manifest the same as the commit node
246 246 user=b'%s <%s>'
247 247 % (c.author.name.encode('utf8'), c.author.email.encode('utf8')),
248 248 date=(c.author.time, -c.author.offset * 60),
249 249 files=files,
250 250 # TODO filesadded in the index
251 251 filesremoved=filesremoved,
252 252 description=c.message.encode('utf8'),
253 253 # TODO do we want to handle extra? how?
254 254 extra=extra,
255 255 )
256 256
257 257 def ancestors(self, revs, stoprev=0, inclusive=False):
258 258 revs = list(revs)
259 259 tip = self.rev(self.tip())
260 260 for r in revs:
261 261 if r > tip:
262 262 raise IndexError(b'Invalid rev %r' % r)
263 263 return ancestor.lazyancestors(
264 264 self.parentrevs, revs, stoprev=stoprev, inclusive=inclusive
265 265 )
266 266
267 267 # Cleanup opportunity: this is *identical* to the revlog.py version
268 268 def descendants(self, revs):
269 269 return dagop.descendantrevs(revs, self.revs, self.parentrevs)
270 270
271 271 def incrementalmissingrevs(self, common=None):
272 272 """Return an object that can be used to incrementally compute the
273 273 revision numbers of the ancestors of arbitrary sets that are not
274 274 ancestors of common. This is an ancestor.incrementalmissingancestors
275 275 object.
276 276
277 277 'common' is a list of revision numbers. If common is not supplied, uses
278 278 nullrev.
279 279 """
280 280 if common is None:
281 281 common = [nullrev]
282 282
283 283 return ancestor.incrementalmissingancestors(self.parentrevs, common)
284 284
285 def findmissingrevs(self, common=None, heads=None):
286 """Return the revision numbers of the ancestors of heads that
287 are not ancestors of common.
288
289 More specifically, return a list of revision numbers corresponding to
290 nodes N such that every N satisfies the following constraints:
291
292 1. N is an ancestor of some node in 'heads'
293 2. N is not an ancestor of any node in 'common'
294
295 The list is sorted by revision number, meaning it is
296 topologically sorted.
297
298 'heads' and 'common' are both lists of revision numbers. If heads is
299 not supplied, uses all of the revlog's heads. If common is not
300 supplied, uses nullid."""
301 if common is None:
302 common = [nullrev]
303 if heads is None:
304 heads = self.headrevs()
305
306 inc = self.incrementalmissingrevs(common=common)
307 return inc.missingancestors(heads)
308
285 309 def findmissing(self, common=None, heads=None):
286 310 """Return the ancestors of heads that are not ancestors of common.
287 311
288 312 More specifically, return a list of nodes N such that every N
289 313 satisfies the following constraints:
290 314
291 315 1. N is an ancestor of some node in 'heads'
292 316 2. N is not an ancestor of any node in 'common'
293 317
294 318 The list is sorted by revision number, meaning it is
295 319 topologically sorted.
296 320
297 321 'heads' and 'common' are both lists of node IDs. If heads is
298 322 not supplied, uses all of the revlog's heads. If common is not
299 323 supplied, uses nullid."""
300 324 if common is None:
301 325 common = [sha1nodeconstants.nullid]
302 326 if heads is None:
303 327 heads = self.heads()
304 328
305 329 common = [self.rev(n) for n in common]
306 330 heads = [self.rev(n) for n in heads]
307 331
308 332 inc = self.incrementalmissingrevs(common=common)
309 333 return [self.node(r) for r in inc.missingancestors(heads)]
310 334
311 335 def children(self, node):
312 336 """find the children of a given node"""
313 337 c = []
314 338 p = self.rev(node)
315 339 for r in self.revs(start=p + 1):
316 340 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
317 341 if prevs:
318 342 for pr in prevs:
319 343 if pr == p:
320 344 c.append(self.node(r))
321 345 elif p == nullrev:
322 346 c.append(self.node(r))
323 347 return c
324 348
325 349 def reachableroots(self, minroot, heads, roots, includepath=False):
326 350 return dagop._reachablerootspure(
327 351 self.parentrevs, minroot, roots, heads, includepath
328 352 )
329 353
330 354 # Cleanup opportunity: this is *identical* to the revlog.py version
331 355 def isancestor(self, a, b):
332 356 a, b = self.rev(a), self.rev(b)
333 357 return self.isancestorrev(a, b)
334 358
335 359 # Cleanup opportunity: this is *identical* to the revlog.py version
336 360 def isancestorrev(self, a, b):
337 361 if a == nullrev:
338 362 return True
339 363 elif a == b:
340 364 return True
341 365 elif a > b:
342 366 return False
343 367 return bool(self.reachableroots(a, [b], [a], includepath=False))
344 368
345 369 def parentrevs(self, rev):
346 370 n = self.node(rev)
347 371 hn = gitutil.togitnode(n)
348 372 if hn != gitutil.nullgit:
349 373 c = self.gitrepo[hn]
350 374 else:
351 375 return nullrev, nullrev
352 376 p1 = p2 = nullrev
353 377 if c.parents:
354 378 p1 = self.rev(c.parents[0].id.raw)
355 379 if len(c.parents) > 2:
356 380 raise error.Abort(b'TODO octopus merge handling')
357 381 if len(c.parents) == 2:
358 382 p2 = self.rev(c.parents[1].id.raw)
359 383 return p1, p2
360 384
361 385 # Private method is used at least by the tags code.
362 386 _uncheckedparentrevs = parentrevs
363 387
364 388 def commonancestorsheads(self, a, b):
365 389 # TODO the revlog verson of this has a C path, so we probably
366 390 # need to optimize this...
367 391 a, b = self.rev(a), self.rev(b)
368 392 return [
369 393 self.node(n)
370 394 for n in ancestor.commonancestorsheads(self.parentrevs, a, b)
371 395 ]
372 396
373 397 def branchinfo(self, rev):
374 398 """Git doesn't do named branches, so just put everything on default."""
375 399 return b'default', False
376 400
377 401 def delayupdate(self, tr):
378 402 # TODO: I think we can elide this because we're just dropping
379 403 # an object in the git repo?
380 404 pass
381 405
382 406 def add(
383 407 self,
384 408 manifest,
385 409 files,
386 410 desc,
387 411 transaction,
388 412 p1,
389 413 p2,
390 414 user,
391 415 date=None,
392 416 extra=None,
393 417 p1copies=None,
394 418 p2copies=None,
395 419 filesadded=None,
396 420 filesremoved=None,
397 421 ):
398 422 parents = []
399 423 hp1, hp2 = gitutil.togitnode(p1), gitutil.togitnode(p2)
400 424 if p1 != sha1nodeconstants.nullid:
401 425 parents.append(hp1)
402 426 if p2 and p2 != sha1nodeconstants.nullid:
403 427 parents.append(hp2)
404 428 assert date is not None
405 429 timestamp, tz = date
406 430 sig = pygit2.Signature(
407 431 encoding.unifromlocal(stringutil.person(user)),
408 432 encoding.unifromlocal(stringutil.email(user)),
409 433 int(timestamp),
410 434 -int(tz // 60),
411 435 )
412 436 oid = self.gitrepo.create_commit(
413 437 None, sig, sig, desc, gitutil.togitnode(manifest), parents
414 438 )
415 439 # Set up an internal reference to force the commit into the
416 440 # changelog. Hypothetically, we could even use this refs/hg/
417 441 # namespace to allow for anonymous heads on git repos, which
418 442 # would be neat.
419 443 self.gitrepo.references.create(
420 444 'refs/hg/internal/latest-commit', oid, force=True
421 445 )
422 446 # Reindex now to pick up changes. We omit the progress
423 447 # and log callbacks because this will be very quick.
424 448 index._index_repo(self.gitrepo, self._db)
425 449 return oid.raw
426 450
427 451
428 452 class manifestlog(baselog):
429 453 nodeconstants = sha1nodeconstants
430 454
431 455 def __getitem__(self, node):
432 456 return self.get(b'', node)
433 457
434 458 def get(self, relpath, node):
435 459 if node == sha1nodeconstants.nullid:
436 460 # TODO: this should almost certainly be a memgittreemanifestctx
437 461 return manifest.memtreemanifestctx(self, relpath)
438 462 commit = self.gitrepo[gitutil.togitnode(node)]
439 463 t = commit.tree
440 464 if relpath:
441 465 parts = relpath.split(b'/')
442 466 for p in parts:
443 467 te = t[p]
444 468 t = self.gitrepo[te.id]
445 469 return gitmanifest.gittreemanifestctx(self.gitrepo, t)
446 470
447 471
448 472 @interfaceutil.implementer(repository.ifilestorage)
449 473 class filelog(baselog):
450 474 def __init__(self, gr, db, path):
451 475 super(filelog, self).__init__(gr, db)
452 476 assert isinstance(path, bytes)
453 477 self.path = path
454 478 self.nullid = sha1nodeconstants.nullid
455 479
456 480 def read(self, node):
457 481 if node == sha1nodeconstants.nullid:
458 482 return b''
459 483 return self.gitrepo[gitutil.togitnode(node)].data
460 484
461 485 def lookup(self, node):
462 486 if len(node) not in (20, 40):
463 487 node = int(node)
464 488 if isinstance(node, int):
465 489 assert False, b'todo revnums for nodes'
466 490 if len(node) == 40:
467 491 node = bin(node)
468 492 hnode = gitutil.togitnode(node)
469 493 if hnode in self.gitrepo:
470 494 return node
471 495 raise error.LookupError(self.path, node, _(b'no match found'))
472 496
473 497 def cmp(self, node, text):
474 498 """Returns True if text is different than content at `node`."""
475 499 return self.read(node) != text
476 500
477 501 def add(self, text, meta, transaction, link, p1=None, p2=None):
478 502 assert not meta # Should we even try to handle this?
479 503 return self.gitrepo.create_blob(text).raw
480 504
481 505 def __iter__(self):
482 506 for clrev in self._db.execute(
483 507 '''
484 508 SELECT rev FROM changelog
485 509 INNER JOIN changedfiles ON changelog.node = changedfiles.node
486 510 WHERE changedfiles.filename = ? AND changedfiles.filenode != ?
487 511 ''',
488 512 (pycompat.fsdecode(self.path), gitutil.nullgit),
489 513 ):
490 514 yield clrev[0]
491 515
492 516 def linkrev(self, fr):
493 517 return fr
494 518
495 519 def rev(self, node):
496 520 row = self._db.execute(
497 521 '''
498 522 SELECT rev FROM changelog
499 523 INNER JOIN changedfiles ON changelog.node = changedfiles.node
500 524 WHERE changedfiles.filename = ? AND changedfiles.filenode = ?''',
501 525 (pycompat.fsdecode(self.path), gitutil.togitnode(node)),
502 526 ).fetchone()
503 527 if row is None:
504 528 raise error.LookupError(self.path, node, _(b'no such node'))
505 529 return int(row[0])
506 530
507 531 def node(self, rev):
508 532 maybe = self._db.execute(
509 533 '''SELECT filenode FROM changedfiles
510 534 INNER JOIN changelog ON changelog.node = changedfiles.node
511 535 WHERE changelog.rev = ? AND filename = ?
512 536 ''',
513 537 (rev, pycompat.fsdecode(self.path)),
514 538 ).fetchone()
515 539 if maybe is None:
516 540 raise IndexError('gitlog %r out of range %d' % (self.path, rev))
517 541 return bin(maybe[0])
518 542
519 543 def parents(self, node):
520 544 gn = gitutil.togitnode(node)
521 545 gp = pycompat.fsdecode(self.path)
522 546 ps = []
523 547 for p in self._db.execute(
524 548 '''SELECT p1filenode, p2filenode FROM changedfiles
525 549 WHERE filenode = ? AND filename = ?
526 550 ''',
527 551 (gn, gp),
528 552 ).fetchone():
529 553 if p is None:
530 554 commit = self._db.execute(
531 555 "SELECT node FROM changedfiles "
532 556 "WHERE filenode = ? AND filename = ?",
533 557 (gn, gp),
534 558 ).fetchone()[0]
535 559 # This filelog is missing some data. Build the
536 560 # filelog, then recurse (which will always find data).
537 561 commit = commit.decode('ascii')
538 562 index.fill_in_filelog(self.gitrepo, self._db, commit, gp, gn)
539 563 return self.parents(node)
540 564 else:
541 565 ps.append(bin(p))
542 566 return ps
543 567
544 568 def renamed(self, node):
545 569 # TODO: renames/copies
546 570 return False
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now