Compare commits
634 Commits
compat-202
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab95e01e2c | ||
|
|
884eac6294 | ||
|
|
828a7db833 | ||
|
|
ffd2aa47a6 | ||
|
|
5b8cbd3aee | ||
|
|
c6511f1453 | ||
|
|
2aed90d861 | ||
|
|
f44a226c17 | ||
|
|
9fec5ae4a5 | ||
|
|
e6a1b77853 | ||
|
|
839fba5043 | ||
|
|
72b8d262c9 | ||
|
|
1688a55d93 | ||
|
|
3b55b49cf8 | ||
|
|
8b523ca125 | ||
|
|
0c59c8475c | ||
|
|
c2679e20c3 | ||
|
|
32f4fd315f | ||
|
|
67500fa79a | ||
|
|
f8f806b3a7 | ||
|
|
b6e89163ec | ||
|
|
2f5a42d864 | ||
|
|
6e78467603 | ||
|
|
2f65520e14 | ||
|
|
c2fb2055f0 | ||
|
|
520b00ac1b | ||
|
|
06fbff626e | ||
|
|
e66f6b5042 | ||
|
|
e642a804ca | ||
|
|
8782363a88 | ||
|
|
fb8a5e133b | ||
|
|
821143d2e4 | ||
|
|
86371d37fe | ||
|
|
443fc5f04d | ||
|
|
0801e2f424 | ||
|
|
4e863726e4 | ||
|
|
73eba8cb50 | ||
|
|
1f46f9798c | ||
|
|
a8f674b7b1 | ||
|
|
a45d1ba288 | ||
|
|
d7d2cab9fc | ||
|
|
a7295c6c96 | ||
|
|
7a3d99a034 | ||
|
|
a00159406f | ||
|
|
8841d78c13 | ||
|
|
d505c91eab | ||
|
|
6a04aa9131 | ||
|
|
efe789bad1 | ||
|
|
8e404464c3 | ||
|
|
84d3730699 | ||
|
|
02dca77af7 | ||
|
|
c9feb00532 | ||
|
|
f2bf8cf10f | ||
|
|
80aa2bbd4f | ||
|
|
fcff548b87 | ||
|
|
6e8f953607 | ||
|
|
f4f6e75145 | ||
|
|
4b6dbb64df | ||
|
|
514480d156 | ||
|
|
ce36b51e81 | ||
|
|
a92deb6cc5 | ||
|
|
920639956d | ||
|
|
729edc67c1 | ||
|
|
b1f25de9c3 | ||
|
|
c187ec55a4 | ||
|
|
ebd8021730 | ||
|
|
31ed5daa68 | ||
|
|
b2c02427c9 | ||
|
|
1aeff70c96 | ||
|
|
7158cba6eb | ||
|
|
6f90142716 | ||
|
|
65a0ac2e7f | ||
|
|
340e09d248 | ||
|
|
e104c0b4ea | ||
|
|
aa4ae4d183 | ||
|
|
7d4d0b18f4 | ||
|
|
2e7322f23d | ||
|
|
901a30b124 | ||
|
|
0f57e9815e | ||
|
|
3d7fc5378e | ||
|
|
eb249f13a0 | ||
|
|
b29e703427 | ||
|
|
2743ed1f67 | ||
|
|
28d6a2de15 | ||
|
|
b5cfe3f735 | ||
|
|
bd8912c90d | ||
|
|
ccd095535b | ||
|
|
ab3d378b7d | ||
|
|
c5b065e9f1 | ||
|
|
e4a97a9e11 | ||
|
|
07de17ea89 | ||
|
|
e6f7deec5c | ||
|
|
0742a29105 | ||
|
|
1ac237df49 | ||
|
|
3b8fc30eb6 | ||
|
|
e4ecbb396a | ||
|
|
c6f687f253 | ||
|
|
973eafff69 | ||
|
|
509de1f49f | ||
|
|
02e24c9d2d | ||
|
|
1e0fba9959 | ||
|
|
9601c1615e | ||
|
|
ffb4369af8 | ||
|
|
636e325006 | ||
|
|
a9095fc46a | ||
|
|
2ee921f2b3 | ||
|
|
f4c3b8024b | ||
|
|
5bc7e46009 | ||
|
|
4288aad11c | ||
|
|
d43a93adbb | ||
|
|
8cfbbbb1e9 | ||
|
|
0c78566fe9 | ||
|
|
6382fa3d81 | ||
|
|
bff029dca3 | ||
|
|
f069cffa5d | ||
|
|
a71dc70ed7 | ||
|
|
c413fa8fa0 | ||
|
|
b904b2b8f7 | ||
|
|
f6a5d22334 | ||
|
|
0b3846bbb1 | ||
|
|
fcd69663cd | ||
|
|
fc955779f0 | ||
|
|
56e1da3f37 | ||
|
|
8cf986b855 | ||
|
|
324aa43f0a | ||
|
|
e941d775fd | ||
|
|
fcfe63e0df | ||
|
|
bcb203551a | ||
|
|
23adfd300f | ||
|
|
85acfce1b4 | ||
|
|
40f07e2172 | ||
|
|
24e5f192d5 | ||
|
|
ee265de548 | ||
|
|
1b28aba233 | ||
|
|
e4a9d34fff | ||
|
|
32ed7a8ff6 | ||
|
|
3d564658ac | ||
|
|
ee3b772836 | ||
|
|
266e35bf5d | ||
|
|
c189099a27 | ||
|
|
0eaf7c8593 | ||
|
|
183287786d | ||
|
|
3d84e9bce2 | ||
|
|
92ce36dbb8 | ||
|
|
e8e70bfbda | ||
|
|
a173749da5 | ||
|
|
fa8fd9453b | ||
|
|
dfcbe3fc22 | ||
|
|
cb3cf80e1a | ||
|
|
acb08c5179 | ||
|
|
358409a2b8 | ||
|
|
b8354030c5 | ||
|
|
fc57afe7f4 | ||
|
|
869de6218b | ||
|
|
b75ac559e9 | ||
|
|
9777d1b31a | ||
|
|
6c38b5e4ca | ||
|
|
905cb6260f | ||
|
|
d048f2b0e3 | ||
|
|
83ec7d14f3 | ||
|
|
d7ac519e28 | ||
|
|
576370f507 | ||
|
|
4dbee898cb | ||
|
|
a9c765956c | ||
|
|
32569fbef2 | ||
|
|
f2ea9a9d74 | ||
|
|
bf1cff8777 | ||
|
|
d2dbe004ce | ||
|
|
a94c419aba | ||
|
|
d8b6b6bcd5 | ||
|
|
70d35f152a | ||
|
|
65c4f28ffd | ||
|
|
46c213b1c8 | ||
|
|
85e164f34c | ||
|
|
1505d125f1 | ||
|
|
13db302a08 | ||
|
|
c9aa1f179a | ||
|
|
598c7dab43 | ||
|
|
beecd99686 | ||
|
|
e2ffe3b3fd | ||
|
|
c35d5e1d6c | ||
|
|
4745ea30cc | ||
|
|
ce06dac357 | ||
|
|
853ca71ac2 | ||
|
|
00411f17df | ||
|
|
4ade236eb0 | ||
|
|
17753dd8e0 | ||
|
|
99a3600d36 | ||
|
|
657dfbb939 | ||
|
|
544892ff3e | ||
|
|
8ac8e75289 | ||
|
|
1997d0d4f1 | ||
|
|
1c6f58c986 | ||
|
|
324d9db461 | ||
|
|
a19e8b4aaa | ||
|
|
6b50a57526 | ||
|
|
641550dce9 | ||
|
|
88ac02198a | ||
|
|
285abd0b07 | ||
|
|
0d35785d41 | ||
|
|
faa2065026 | ||
|
|
41f67d9b6a | ||
|
|
b24a4109c8 | ||
|
|
5d0d14dde8 | ||
|
|
481fcc9410 | ||
|
|
560ad86b5b | ||
|
|
234c036dc5 | ||
|
|
a919f44505 | ||
|
|
3d6fb8c4b1 | ||
|
|
ebeac0ee43 | ||
|
|
44a357f758 | ||
|
|
8ac69bb501 | ||
|
|
fe880183f9 | ||
|
|
36704ece70 | ||
|
|
de28675f8f | ||
|
|
f2924998e8 | ||
|
|
6bed906751 | ||
|
|
19b3012242 | ||
|
|
e7fff1db9a | ||
|
|
727ce423ec | ||
|
|
f0796785d1 | ||
|
|
17f8e49301 | ||
|
|
08e714e329 | ||
|
|
ca573084d6 | ||
|
|
10c755fe0c | ||
|
|
eb3ce8a146 | ||
|
|
457f55e492 | ||
|
|
6abc293ed9 | ||
|
|
c5b6c7de6f | ||
|
|
41dd3fb110 | ||
|
|
f4fd057d0f | ||
|
|
111c8deb4a | ||
|
|
d062ef035b | ||
|
|
45259b4ebb | ||
|
|
5f815d88a9 | ||
|
|
dd18f558a4 | ||
|
|
083a62374e | ||
|
|
b16bad3975 | ||
|
|
509f88846f | ||
|
|
5ec965e71b | ||
|
|
893f0ebd1d | ||
|
|
fbb13c2b0a | ||
|
|
5064ece68c | ||
|
|
7fb2ff94a6 | ||
|
|
38a2ebf574 | ||
|
|
8c34e94dbb | ||
|
|
7c4543d7ea | ||
|
|
b9ece35287 | ||
|
|
8a27a249c3 | ||
|
|
2deb97b7b5 | ||
|
|
30cc91bfd4 | ||
|
|
1bffb14853 | ||
|
|
9c9e1c4f1e | ||
|
|
a7dd0f9d08 | ||
|
|
79004d15bc | ||
|
|
61ff94de86 | ||
|
|
4f91e89503 | ||
|
|
624fc2ea83 | ||
|
|
b09fc2ee95 | ||
|
|
aeaaee09b6 | ||
|
|
6ae6591048 | ||
|
|
36e6650045 | ||
|
|
e70b9954c6 | ||
|
|
f46582fc46 | ||
|
|
c5b3f50b02 | ||
|
|
431d31df00 | ||
|
|
3aa10ee435 | ||
|
|
c08851ecdc | ||
|
|
9af0cfdf81 | ||
|
|
6ea76cd7a1 | ||
|
|
87782bbdf3 | ||
|
|
a9dbb0ef87 | ||
|
|
d91ea066fb | ||
|
|
0631489d25 | ||
|
|
74b2bca9bf | ||
|
|
83599aec23 | ||
|
|
22ca64c207 | ||
|
|
c1ee06b6ef | ||
|
|
484ff82e71 | ||
|
|
0e2d4eb50d | ||
|
|
6c7fe3de7f | ||
|
|
6c798cfb2b | ||
|
|
507bbe2274 | ||
|
|
e1c924820f | ||
|
|
bec5cc97e1 | ||
|
|
a495cc8c0a | ||
|
|
d92503dec4 | ||
|
|
7fe7114ac8 | ||
|
|
ab3f6eab39 | ||
|
|
38a0b1fae2 | ||
|
|
10e28f4d9d | ||
|
|
f3890a8ddc | ||
|
|
281c072e77 | ||
|
|
855c4f62cc | ||
|
|
63b3251312 | ||
|
|
fa543662ed | ||
|
|
9da4d22dd7 | ||
|
|
943fe9605e | ||
|
|
408d853b20 | ||
|
|
ce16c6365c | ||
|
|
855ce6cb0b | ||
|
|
40c1f97823 | ||
|
|
48d9e1a6ca | ||
|
|
0c41f01441 | ||
|
|
694f258b1f | ||
|
|
e2b3fa301e | ||
|
|
bef73253c7 | ||
|
|
5d950887fe | ||
|
|
9c3251a722 | ||
|
|
be41f8fe4f | ||
|
|
8404d8946d | ||
|
|
1544788406 | ||
|
|
13904d0a72 | ||
|
|
4be1624641 | ||
|
|
0942b8873a | ||
|
|
6fd6c7f0ae | ||
|
|
b7a652b9c6 | ||
|
|
c5404245b3 | ||
|
|
1c78d3eb6a | ||
|
|
3b0ffc3bb9 | ||
|
|
9e091abc8d | ||
|
|
274ef8a975 | ||
|
|
c4fa0766d2 | ||
|
|
19f6fa52d4 | ||
|
|
9478eca866 | ||
|
|
9f04d4c0ba | ||
|
|
6af91304f1 | ||
|
|
23c0c50993 | ||
|
|
3ec36e2e57 | ||
|
|
1bc075401f | ||
|
|
e2a1b4fd0f | ||
|
|
ade69369fb | ||
|
|
e94081df87 | ||
|
|
1479201fd4 | ||
|
|
92f60cc16d | ||
|
|
901b9404b0 | ||
|
|
afd991bcba | ||
|
|
09c69baaec | ||
|
|
b48c1bcd1a | ||
|
|
177f5ab96f | ||
|
|
b0373fd6a3 | ||
|
|
ff7e608d09 | ||
|
|
ef3e81253b | ||
|
|
37c7625568 | ||
|
|
6f33a8aeba | ||
|
|
04092e153c | ||
|
|
76a3d5df80 | ||
|
|
530615fee3 | ||
|
|
a110ce9fb3 | ||
|
|
1015154385 | ||
|
|
feb1679099 | ||
|
|
a7d3ec1816 | ||
|
|
cf35c277ab | ||
|
|
a8d7a70b26 | ||
|
|
f7c497e240 | ||
|
|
a33ec8460f | ||
|
|
636c442fdb | ||
|
|
76976c30e1 | ||
|
|
82c65ec147 | ||
|
|
1eafdcdc85 | ||
|
|
997473863e | ||
|
|
ede911af0a | ||
|
|
955a3b0b65 | ||
|
|
e582a2448d | ||
|
|
33cbdb2693 | ||
|
|
ab0d6ad9e5 | ||
|
|
4c0aba6b0c | ||
|
|
3bc28d38b2 | ||
|
|
d028de4898 | ||
|
|
3360d23e55 | ||
|
|
2c317cb62d | ||
|
|
edb9948fc9 | ||
|
|
f60dc88637 | ||
|
|
d027f04494 | ||
|
|
8961598d1e | ||
|
|
53c6a0e0c6 | ||
|
|
7fee2dc8f3 | ||
|
|
ff532e14d2 | ||
|
|
52a79ad820 | ||
|
|
298b1bb7c9 | ||
|
|
2446713aea | ||
|
|
68f638ea5d | ||
|
|
6f9ecc221f | ||
|
|
ca6094dc79 | ||
|
|
8c3be45763 | ||
|
|
100ab30aef | ||
|
|
ef7a2dd310 | ||
|
|
479faf78d2 | ||
|
|
961453812c | ||
|
|
220db20347 | ||
|
|
c10d2e8799 | ||
|
|
f70b05e374 | ||
|
|
337a024a21 | ||
|
|
c8c6f82645 | ||
|
|
ed5b6f485a | ||
|
|
8b6a61c366 | ||
|
|
9a54fb709d | ||
|
|
43a156ef6c | ||
|
|
88dfbe4ce3 | ||
|
|
f06e792bef | ||
|
|
4dc375d8b2 | ||
|
|
a582a57125 | ||
|
|
8584e3be64 | ||
|
|
87775c009d | ||
|
|
0ec913bbc7 | ||
|
|
95e263d012 | ||
|
|
bc23a404e3 | ||
|
|
dc04cd26ec | ||
|
|
28297541bf | ||
|
|
680c275ef5 | ||
|
|
87cdca377f | ||
|
|
4ee6e61ae1 | ||
|
|
b2e71bb12d | ||
|
|
01f92e63f1 | ||
|
|
dba13b4fda | ||
|
|
e70572e77a | ||
|
|
89860467d4 | ||
|
|
4717c3ee73 | ||
|
|
04128c60eb | ||
|
|
4aa07b3672 | ||
|
|
2aa5b30969 | ||
|
|
6ccb110203 | ||
|
|
64cd39265b | ||
|
|
22a4787dbc | ||
|
|
947fbae8bb | ||
|
|
28113baebf | ||
|
|
d92ba9f441 | ||
|
|
9da52f5009 | ||
|
|
45200bd57f | ||
|
|
9321153b79 | ||
|
|
aaae152392 | ||
|
|
f2dc0e8f6a | ||
|
|
27361b7500 | ||
|
|
2e99af1a3f | ||
|
|
ead5438ebb | ||
|
|
122927662c | ||
|
|
839d44d167 | ||
|
|
ebf3df296f | ||
|
|
cc326b625a | ||
|
|
1d17e8f752 | ||
|
|
f605addb71 | ||
|
|
ab5f985536 | ||
|
|
dddb791c81 | ||
|
|
7f8d794835 | ||
|
|
be174a164a | ||
|
|
0333e62884 | ||
|
|
4c9b779fac | ||
|
|
26f921d7dc | ||
|
|
d03ada5091 | ||
|
|
dffe7486ed | ||
|
|
edb37d1686 | ||
|
|
040ceea2a1 | ||
|
|
f8e1a25fe0 | ||
|
|
8b6392d655 | ||
|
|
aa1fc53b40 | ||
|
|
051b959526 | ||
|
|
bfdb0497fe | ||
|
|
afcc9f1aeb | ||
|
|
b4d8209c90 | ||
|
|
fb2fc88cb9 | ||
|
|
1aadcf9676 | ||
|
|
5a963bd59b | ||
|
|
413cea31aa | ||
|
|
1da736b4b6 | ||
|
|
df19210889 | ||
|
|
9e17b0ed97 | ||
|
|
405628835f | ||
|
|
262e57237b | ||
|
|
252b2e12d3 | ||
|
|
7c6f953bd7 | ||
|
|
16a93c94a0 | ||
|
|
514f535955 | ||
|
|
d29f68ff73 | ||
|
|
43ea0d5f13 | ||
|
|
258fec25d2 | ||
|
|
8fa392ffff | ||
|
|
05a383fd3a | ||
|
|
aaa64a2149 | ||
|
|
efeeb03fc6 | ||
|
|
e56d3b7c22 | ||
|
|
2e28bce3b2 | ||
|
|
b3d807fc54 | ||
|
|
5f045098fe | ||
|
|
84c524e405 | ||
|
|
b2b15e9d56 | ||
|
|
cf499acc36 | ||
|
|
eb88625f93 | ||
|
|
1db2bc6b6d | ||
|
|
8757ad9755 | ||
|
|
4207982e65 | ||
|
|
29e9ccf20a | ||
|
|
480c642325 | ||
|
|
8c670e6db7 | ||
|
|
13c0183a79 | ||
|
|
55938bd5fc | ||
|
|
cf6bb37648 | ||
|
|
7d1d9b17fd | ||
|
|
17385266ee | ||
|
|
9429501407 | ||
|
|
70c40e2be0 | ||
|
|
0936a1a9e3 | ||
|
|
a5ab1d3717 | ||
|
|
96fc3ff31c | ||
|
|
c3df323a9f | ||
|
|
f485a13a25 | ||
|
|
7abbd83587 | ||
|
|
e7c2c386b7 | ||
|
|
0b3f245079 | ||
|
|
df3daac8ad | ||
|
|
011365c89b | ||
|
|
4a7ebf48c4 | ||
|
|
d5aae2097d | ||
|
|
d60e32bce7 | ||
|
|
52296380f1 | ||
|
|
a90d435e61 | ||
|
|
69fbc2ed91 | ||
|
|
34b1188175 | ||
|
|
c90913d563 | ||
|
|
2b4b39a65b | ||
|
|
605eb99853 | ||
|
|
012d2d3d6d | ||
|
|
ce272a9290 | ||
|
|
0279f8fae2 | ||
|
|
779f8be4fe | ||
|
|
445bc3402e | ||
|
|
ed6301c48f | ||
|
|
412230c63a | ||
|
|
e401be4cb0 | ||
|
|
e6c0e7263b | ||
|
|
2c3a57ae90 | ||
|
|
32b5140c75 | ||
|
|
a2cfbf988f | ||
|
|
5145e4fff5 | ||
|
|
59b3f3b180 | ||
|
|
82dbfd9c0b | ||
|
|
a27f751472 | ||
|
|
a14bdf3784 | ||
|
|
90edb8492e | ||
|
|
335cbe4777 | ||
|
|
4a6c7956e4 | ||
|
|
507eeb334a | ||
|
|
f75623f372 | ||
|
|
ebed6f8f67 | ||
|
|
6ecc6202bc | ||
|
|
a6e1baa9b7 | ||
|
|
3ee6acab4f | ||
|
|
0adebb1e23 | ||
|
|
4a87ecdf9e | ||
|
|
84569ea158 | ||
|
|
ea48cf5300 | ||
|
|
191fb66a0d | ||
|
|
ebe09e4e60 | ||
|
|
68514dd80e | ||
|
|
a8ee005622 | ||
|
|
5e93b3cdcd | ||
|
|
7abad8876e | ||
|
|
01a96f2c8d | ||
|
|
e9fa75d144 | ||
|
|
e129437c2b | ||
|
|
e3e97dc47d | ||
|
|
3ff0528865 | ||
|
|
5d3b01f9ce | ||
|
|
2e67ef231a | ||
|
|
25a7633e0c | ||
|
|
03d1d95b6a | ||
|
|
1bfb5dbf2c | ||
|
|
1b05e5f43e | ||
|
|
266ae176b5 | ||
|
|
0b10891002 | ||
|
|
37ae8018e7 | ||
|
|
e56454a147 | ||
|
|
65fc0ab67a | ||
|
|
572b6483d4 | ||
|
|
2bd0b12d51 | ||
|
|
1183250a30 | ||
|
|
6d4185e318 | ||
|
|
130e6a7ff2 | ||
|
|
d851972b36 | ||
|
|
f7768a4abc | ||
|
|
aee530e3e7 | ||
|
|
549423422a | ||
|
|
e9f7c5f48a | ||
|
|
f8e5990992 | ||
|
|
807af25e6b | ||
|
|
8b38448d8a | ||
|
|
786c5d19d2 | ||
|
|
3c204b14dd | ||
|
|
e2bfa36acd | ||
|
|
fb05f01fd4 | ||
|
|
84fcbd9bf6 | ||
|
|
f959d8a91c | ||
|
|
de204db676 | ||
|
|
26d7f37f23 | ||
|
|
3d338748b3 | ||
|
|
b0571c51f5 | ||
|
|
98ea4212a4 | ||
|
|
984daf2c5e | ||
|
|
cba5b9a1dc | ||
|
|
28b18defa4 | ||
|
|
5a4a19c7a6 | ||
|
|
c34156b60b | ||
|
|
58397f74cb | ||
|
|
06adf10e16 | ||
|
|
bcf41ff9f1 | ||
|
|
0228f06e81 | ||
|
|
be66a1f468 | ||
|
|
5bc1fefa0b | ||
|
|
4a18d57e8d | ||
|
|
0d37d6505f | ||
|
|
a580385295 | ||
|
|
8f843c1522 | ||
|
|
87870be936 | ||
|
|
31aed68670 | ||
|
|
dd82961e4c | ||
|
|
c013942d1f | ||
|
|
8342bd93c8 | ||
|
|
8dce4e5f2c | ||
|
|
499c1fbed4 | ||
|
|
6a3b97302c | ||
|
|
1962ac2eec | ||
|
|
ccb3117a38 | ||
|
|
f0d94819a3 | ||
|
|
e9f28c9932 | ||
|
|
6a1e3f3a20 | ||
|
|
bb1e4bb341 | ||
|
|
44df16fd04 | ||
|
|
455a523205 | ||
|
|
a270514eb1 | ||
|
|
7b193e3392 | ||
|
|
44d6f237d5 | ||
|
|
6e2e4d2143 | ||
|
|
268cdcdec1 | ||
|
|
c913f27540 | ||
|
|
e6088af885 |
14
.github/dependabot.yml
vendored
14
.github/dependabot.yml
vendored
@@ -11,22 +11,12 @@ updates:
|
||||
interval: daily
|
||||
|
||||
- package-ecosystem: docker
|
||||
directory: images/nginx
|
||||
directory: images/production
|
||||
schedule:
|
||||
interval: daily
|
||||
|
||||
- package-ecosystem: docker
|
||||
directory: images/worker
|
||||
schedule:
|
||||
interval: daily
|
||||
|
||||
- package-ecosystem: docker
|
||||
directory: images/socketio
|
||||
schedule:
|
||||
interval: daily
|
||||
|
||||
- package-ecosystem: npm
|
||||
directory: images/socketio
|
||||
directory: images/custom
|
||||
schedule:
|
||||
interval: daily
|
||||
|
||||
|
||||
4
.github/scripts/get_latest_tags.py
vendored
4
.github/scripts/get_latest_tags.py
vendored
@@ -9,7 +9,7 @@ import sys
|
||||
from typing import Literal
|
||||
|
||||
Repo = Literal["frappe", "erpnext"]
|
||||
MajorVersion = Literal["12", "13", "14", "develop"]
|
||||
MajorVersion = Literal["12", "13", "14", "15", "develop"]
|
||||
|
||||
|
||||
def get_latest_tag(repo: Repo, version: MajorVersion) -> str:
|
||||
@@ -57,7 +57,7 @@ def main(_args: list[str]) -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--repo", choices=["frappe", "erpnext"], required=True)
|
||||
parser.add_argument(
|
||||
"--version", choices=["12", "13", "14", "develop"], required=True
|
||||
"--version", choices=["12", "13", "14", "15", "develop"], required=True
|
||||
)
|
||||
args = parser.parse_args(_args)
|
||||
|
||||
|
||||
18
.github/scripts/update_example_env.py
vendored
18
.github/scripts/update_example_env.py
vendored
@@ -2,29 +2,25 @@ import os
|
||||
import re
|
||||
|
||||
|
||||
def get_versions():
|
||||
frappe_version = os.getenv("FRAPPE_VERSION")
|
||||
def get_erpnext_version():
|
||||
erpnext_version = os.getenv("ERPNEXT_VERSION")
|
||||
assert frappe_version, "No Frappe version set"
|
||||
assert erpnext_version, "No ERPNext version set"
|
||||
return frappe_version, erpnext_version
|
||||
return erpnext_version
|
||||
|
||||
|
||||
def update_env(frappe_version: str, erpnext_version: str):
|
||||
def update_env(erpnext_version: str):
|
||||
with open("example.env", "r+") as f:
|
||||
content = f.read()
|
||||
for env, var in (
|
||||
("FRAPPE_VERSION", frappe_version),
|
||||
("ERPNEXT_VERSION", erpnext_version),
|
||||
):
|
||||
content = re.sub(rf"{env}=.*", f"{env}={var}", content)
|
||||
content = re.sub(
|
||||
rf"ERPNEXT_VERSION=.*", f"ERPNEXT_VERSION={erpnext_version}", content
|
||||
)
|
||||
f.seek(0)
|
||||
f.truncate()
|
||||
f.write(content)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
update_env(*get_versions())
|
||||
update_env(get_erpnext_version())
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
9
.github/scripts/update_pwd.py
vendored
9
.github/scripts/update_pwd.py
vendored
@@ -13,12 +13,9 @@ def get_versions():
|
||||
def update_pwd(frappe_version: str, erpnext_version: str):
|
||||
with open("pwd.yml", "r+") as f:
|
||||
content = f.read()
|
||||
for image, version in (
|
||||
("frappe/frappe-socketio", frappe_version),
|
||||
("frappe/erpnext-worker", erpnext_version),
|
||||
("frappe/erpnext-nginx", erpnext_version),
|
||||
):
|
||||
content = re.sub(rf"{image}:.*", f"{image}:{version}", content)
|
||||
content = re.sub(
|
||||
rf"frappe/erpnext:.*", f"frappe/erpnext:{erpnext_version}", content
|
||||
)
|
||||
f.seek(0)
|
||||
f.truncate()
|
||||
f.write(content)
|
||||
|
||||
19
.github/workflows/build_bench.yml
vendored
19
.github/workflows/build_bench.yml
vendored
@@ -20,32 +20,39 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Setup QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
image: tonistiigi/binfmt:latest
|
||||
platforms: all
|
||||
|
||||
- name: Setup Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Set Environment Variables
|
||||
run: cat example.env | grep -o '^[^#]*' >> "$GITHUB_ENV"
|
||||
|
||||
- name: Get Bench Latest Version
|
||||
run: echo "LATEST_BENCH_RELEASE=$(curl -s 'https://api.github.com/repos/frappe/bench/releases/latest' | jq -r '.tag_name')" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Build and test
|
||||
uses: docker/bake-action@v2.3.0
|
||||
uses: docker/bake-action@v6.9.0
|
||||
with:
|
||||
source: .
|
||||
targets: bench-test
|
||||
|
||||
- name: Login
|
||||
if: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Push
|
||||
if: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
|
||||
uses: docker/bake-action@v2.3.0
|
||||
uses: docker/bake-action@v6.9.0
|
||||
with:
|
||||
targets: bench
|
||||
push: true
|
||||
|
||||
6
.github/workflows/build_develop.yml
vendored
6
.github/workflows/build_develop.yml
vendored
@@ -5,9 +5,7 @@ on:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- images/nginx/**
|
||||
- images/socketio/**
|
||||
- images/worker/**
|
||||
- images/production/**
|
||||
- overrides/**
|
||||
- tests/**
|
||||
- compose.yaml
|
||||
@@ -28,6 +26,8 @@ jobs:
|
||||
repo: erpnext
|
||||
version: develop
|
||||
push: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
|
||||
python_version: 3.11.6
|
||||
node_version: 20.19.2
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
46
.github/workflows/build_stable.yml
vendored
46
.github/workflows/build_stable.yml
vendored
@@ -5,9 +5,7 @@ on:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- images/nginx/**
|
||||
- images/socketio/**
|
||||
- images/worker/**
|
||||
- images/production/**
|
||||
- overrides/**
|
||||
- tests/**
|
||||
- compose.yaml
|
||||
@@ -19,9 +17,7 @@ on:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- images/nginx/**
|
||||
- images/socketio/**
|
||||
- images/worker/**
|
||||
- images/production/**
|
||||
- overrides/**
|
||||
- tests/**
|
||||
- compose.yaml
|
||||
@@ -34,22 +30,26 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
v13:
|
||||
uses: ./.github/workflows/docker-build-push.yml
|
||||
with:
|
||||
repo: erpnext
|
||||
version: "13"
|
||||
push: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
v14:
|
||||
uses: ./.github/workflows/docker-build-push.yml
|
||||
with:
|
||||
repo: erpnext
|
||||
version: "14"
|
||||
push: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
|
||||
python_version: 3.10.13
|
||||
node_version: 16.20.2
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
v15:
|
||||
uses: ./.github/workflows/docker-build-push.yml
|
||||
with:
|
||||
repo: erpnext
|
||||
version: "15"
|
||||
push: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
|
||||
python_version: 3.11.6
|
||||
node_version: 20.19.2
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
@@ -58,19 +58,19 @@ jobs:
|
||||
name: Update example.env and pwd.yml
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
|
||||
needs: v14
|
||||
needs: v15
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Get latest versions
|
||||
run: python3 ./.github/scripts/get_latest_tags.py --repo erpnext --version 14
|
||||
run: python3 ./.github/scripts/get_latest_tags.py --repo erpnext --version 15
|
||||
|
||||
- name: Update
|
||||
run: |
|
||||
@@ -96,11 +96,11 @@ jobs:
|
||||
name: Release Helm
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
|
||||
needs: v14
|
||||
needs: v15
|
||||
|
||||
steps:
|
||||
- name: Setup deploy key
|
||||
uses: webfactory/ssh-agent@v0.7.0
|
||||
uses: webfactory/ssh-agent@v0.9.1
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.HELM_DEPLOY_KEY }}
|
||||
|
||||
@@ -113,4 +113,4 @@ jobs:
|
||||
run: |
|
||||
git clone git@github.com:frappe/helm.git && cd helm
|
||||
pip install -r release_wizard/requirements.txt
|
||||
./release_wizard/wizard 14 patch --remote origin --ci
|
||||
./release_wizard/wizard 15 patch --remote origin --ci
|
||||
|
||||
42
.github/workflows/docker-build-push.yml
vendored
42
.github/workflows/docker-build-push.yml
vendored
@@ -14,6 +14,14 @@ on:
|
||||
push:
|
||||
required: true
|
||||
type: boolean
|
||||
python_version:
|
||||
required: true
|
||||
type: string
|
||||
description: Python Version
|
||||
node_version:
|
||||
required: true
|
||||
type: string
|
||||
description: NodeJS Version
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME:
|
||||
required: true
|
||||
@@ -26,37 +34,50 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
registry:
|
||||
image: registry:2
|
||||
image: docker.io/registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [amd64, arm64]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Setup QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
image: tonistiigi/binfmt:latest
|
||||
platforms: all
|
||||
|
||||
- name: Setup Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
driver-opts: network=host
|
||||
platforms: linux/${{ matrix.arch }}
|
||||
|
||||
- name: Get latest versions
|
||||
run: python3 ./.github/scripts/get_latest_tags.py --repo ${{ inputs.repo }} --version ${{ inputs.version }}
|
||||
|
||||
- name: Set build args
|
||||
run: |
|
||||
echo "PYTHON_VERSION=${{ inputs.python_version }}" >> "$GITHUB_ENV"
|
||||
echo "NODE_VERSION=${{ inputs.node_version }}" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Build
|
||||
uses: docker/bake-action@v2.3.0
|
||||
uses: docker/bake-action@v6.9.0
|
||||
with:
|
||||
source: .
|
||||
push: true
|
||||
env:
|
||||
REGISTRY_USER: localhost:5000/frappe
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install Docker Compose v2
|
||||
uses: ndeloof/install-compose-action@4a33bc31f327b8231c4f343f6fba704fedc0fa23
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m venv venv
|
||||
@@ -67,13 +88,14 @@ jobs:
|
||||
|
||||
- name: Login
|
||||
if: ${{ inputs.push }}
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Push
|
||||
if: ${{ inputs.push }}
|
||||
uses: docker/bake-action@v2.3.0
|
||||
uses: docker/bake-action@v6.9.0
|
||||
with:
|
||||
push: true
|
||||
set: "*.platform=linux/amd64,linux/arm64"
|
||||
|
||||
16
.github/workflows/lint.yml
vendored
16
.github/workflows/lint.yml
vendored
@@ -13,27 +13,19 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.10"
|
||||
python-version: "3.10.6"
|
||||
|
||||
# For shfmt pre-commit hook
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "^1.14"
|
||||
|
||||
- name: Cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cache/pre-commit
|
||||
~/.cache/pip
|
||||
key: lint-${{ hashFiles('.pre-commit-config.yaml') }}
|
||||
|
||||
- name: Install pre-commit
|
||||
run: pip install -U pre-commit
|
||||
|
||||
|
||||
4
.github/workflows/pre-commit-autoupdate.yml
vendored
4
.github/workflows/pre-commit-autoupdate.yml
vendored
@@ -10,13 +10,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Update pre-commit hooks
|
||||
uses: vrslev/pre-commit-autoupdate@v1.0.0
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
branch: pre-commit-autoupdate
|
||||
title: "chore(deps): Update pre-commit hooks"
|
||||
|
||||
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v7
|
||||
- uses: actions/stale@v10
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: This issue has been automatically marked as stale. You have a week to explain why you believe this is an error.
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -6,7 +6,7 @@ sites
|
||||
|
||||
development/*
|
||||
!development/README.md
|
||||
!development/installer.sh
|
||||
!development/installer.py
|
||||
!development/apps-example.json
|
||||
!development/vscode-example/
|
||||
|
||||
@@ -25,3 +25,6 @@ development/*
|
||||
*.pyc
|
||||
__pycache__
|
||||
venv
|
||||
|
||||
# NodeJS
|
||||
node_modules
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0
|
||||
rev: v5.0.0
|
||||
hooks:
|
||||
- id: check-executables-have-shebangs
|
||||
- id: check-shebang-scripts-are-executable
|
||||
@@ -8,28 +8,30 @@ repos:
|
||||
- id: end-of-file-fixer
|
||||
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.3.1
|
||||
rev: v3.19.1
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py37-plus]
|
||||
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 22.12.0
|
||||
rev: 25.1.0
|
||||
hooks:
|
||||
- id: black
|
||||
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.11.4
|
||||
rev: 6.0.1
|
||||
hooks:
|
||||
- id: isort
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: v3.0.0-alpha.4
|
||||
rev: v4.0.0-alpha.8
|
||||
hooks:
|
||||
- id: prettier
|
||||
additional_dependencies:
|
||||
- prettier@3.5.2
|
||||
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: v2.2.2
|
||||
rev: v2.4.1
|
||||
hooks:
|
||||
- id: codespell
|
||||
args:
|
||||
@@ -47,7 +49,7 @@ repos:
|
||||
types: [shell]
|
||||
|
||||
- repo: https://github.com/shellcheck-py/shellcheck-py
|
||||
rev: v0.9.0.2
|
||||
rev: v0.10.0.1
|
||||
hooks:
|
||||
- id: shellcheck
|
||||
args: [-x]
|
||||
|
||||
@@ -6,7 +6,7 @@ In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
level of experience, education, socioeconomic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
@@ -61,16 +61,21 @@ Run pytest:
|
||||
pytest
|
||||
```
|
||||
|
||||
> We also have `requirements-dev.txt` file that contains development requirements for backend image (you can find it in `images/worker/` directory).
|
||||
|
||||
# Documentation
|
||||
|
||||
Place relevant markdown files in the `docs` directory and index them in README.md located at the root of repo.
|
||||
|
||||
# Wiki
|
||||
|
||||
Add alternatives that can be used optionally along with frappe_docker. Add articles to list on home page as well.
|
||||
|
||||
# Frappe and ERPNext updates
|
||||
|
||||
Each Frappe/ERPNext release triggers new stable images builds as well as bump to helm chart.
|
||||
|
||||
# Maintenance
|
||||
|
||||
In case of new release of Debian. e.g. bullseye to bookworm. Change following files:
|
||||
|
||||
- `images/erpnext/Containerfile` and `images/custom/Containerfile`: Change the files to use new debian release, make sure new python version tag that is available on new debian release image. e.g. 3.9.9 (bullseye) to 3.9.17 (bookworm) or 3.10.5 (bullseye) to 3.10.12 (bookworm). Make sure apt-get packages and wkhtmltopdf version are also upgraded accordingly.
|
||||
- `images/bench/Dockerfile`: Change the files to use new debian release. Make sure apt-get packages and wkhtmltopdf version are also upgraded accordingly.
|
||||
|
||||
Change following files on release of ERPNext
|
||||
|
||||
- `.github/workflows/build_stable.yml`: Add the new release step under `jobs` and remove the unmaintained one. e.g. In case v12, v13 available, v14 will be added and v12 will be removed on release of v14. Also change the `needs:` for later steps to `v14` from `v13`.
|
||||
|
||||
85
README.md
85
README.md
@@ -5,56 +5,85 @@ Everything about [Frappe](https://github.com/frappe/frappe) and [ERPNext](https:
|
||||
|
||||
# Getting Started
|
||||
|
||||
To get started, you need Docker, docker-compose and git setup on your machine. For Docker basics and best practices. Refer Docker [documentation](http://docs.docker.com).
|
||||
After that, clone this repo:
|
||||
To get started you need [Docker](https://docs.docker.com/get-docker/), [docker-compose](https://docs.docker.com/compose/), and [git](https://docs.github.com/en/get-started/getting-started-with-git/set-up-git) setup on your machine. For Docker basics and best practices refer to Docker's [documentation](http://docs.docker.com).
|
||||
|
||||
Once completed, chose one of the following two sections for next steps.
|
||||
|
||||
### Try in Play With Docker
|
||||
|
||||
To play in an already set up sandbox, in your browser, click the button below:
|
||||
|
||||
<a href="https://labs.play-with-docker.com/?stack=https://raw.githubusercontent.com/frappe/frappe_docker/main/pwd.yml">
|
||||
<img src="https://raw.githubusercontent.com/play-with-docker/stacks/master/assets/images/button.png" alt="Try in PWD"/>
|
||||
</a>
|
||||
|
||||
### Try on your Dev environment
|
||||
|
||||
First clone the repo:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/frappe/frappe_docker
|
||||
cd frappe_docker
|
||||
```
|
||||
|
||||
### Try in Play With Docker
|
||||
Then run: `docker compose -f pwd.yml up -d`
|
||||
|
||||
<a href="https://labs.play-with-docker.com/?stack=https://raw.githubusercontent.com/frappe/frappe_docker/main/pwd.yml">
|
||||
<img src="https://raw.githubusercontent.com/play-with-docker/stacks/master/assets/images/button.png" alt="Try in PWD"/>
|
||||
</a>
|
||||
### To run on ARM64 architecture follow this instructions
|
||||
|
||||
After you clone the repo and `cd frappe_docker`, run this command to build multi-architecture images specifically for ARM64.
|
||||
|
||||
`docker buildx bake --no-cache --set "*.platform=linux/arm64"`
|
||||
|
||||
and then
|
||||
|
||||
- add `platform: linux/arm64` to all services in the `pwd.yml`
|
||||
- replace the current specified versions of erpnext image on `pwd.yml` with `:latest`
|
||||
|
||||
Then run: `docker compose -f pwd.yml up -d`
|
||||
|
||||
## Final steps
|
||||
|
||||
Wait for 5 minutes for ERPNext site to be created or check `create-site` container logs before opening browser on port 8080. (username: `Administrator`, password: `admin`)
|
||||
|
||||
# Development
|
||||
If you ran in a Dev Docker environment, to view container logs: `docker compose -f pwd.yml logs -f create-site`. Don't worry about some of the initial error messages, some services take a while to become ready, and then they go away.
|
||||
|
||||
We have baseline for developing in VSCode devcontainer with [frappe/bench](https://github.com/frappe/bench). [Start development](development).
|
||||
# Documentation
|
||||
|
||||
# Production
|
||||
### [Frequently Asked Questions](https://github.com/frappe/frappe_docker/wiki/Frequently-Asked-Questions)
|
||||
|
||||
We provide simple and intuitive production setup with prebuilt Frappe and ERPNext images and compose files. To learn more about those, [read the docs](docs/images-and-compose-files.md).
|
||||
### [Production](#production)
|
||||
|
||||
Also, there's docs to help with deployment:
|
||||
- [List of containers](docs/container-setup/01-overview.md)
|
||||
- [Single Compose Setup](docs/single-compose-setup.md)
|
||||
- [Environment Variables](docs/container-setup/env-variables.md)
|
||||
- [Single Server Example](docs/single-server-example.md)
|
||||
- [Setup Options](docs/setup-options.md)
|
||||
- [Site Operations](docs/site-operations.md)
|
||||
- [Backup and Push Cron Job](docs/backup-and-push-cronjob.md)
|
||||
- [Port Based Multi Tenancy](docs/port-based-multi-tenancy.md)
|
||||
- [Migrate from multi-image setup](docs/migrate-from-multi-image-setup.md)
|
||||
- [running on linux/mac](docs/setup_for_linux_mac.md)
|
||||
- [TLS for local deployment](docs/tls-for-local-deployment.md)
|
||||
|
||||
- Examples:
|
||||
- [Single Server](docs/single-server-example.md)
|
||||
- [Setup options](docs/setup-options.md)
|
||||
- [Kubernetes (frappe/helm)](https://helm.erpnext.com)
|
||||
- [Site operations](docs/site-operations.md).
|
||||
- Other
|
||||
- [backup and push cron jobs](docs/backup-and-push-cronjob.md)
|
||||
- [bench console and vscode debugger](docs/bench-console-and-vscode-debugger.md)
|
||||
- [build version 10](docs/build-version-10-images.md)
|
||||
- [connect to localhost services from containers for local app development](docs/connect-to-localhost-services-from-containers-for-local-app-development.md)
|
||||
- [patch code from images](docs/patch-code-from-images.md)
|
||||
- [port based multi tenancy](docs/port-based-multi-tenancy.md)
|
||||
- [Troubleshoot](docs/troubleshoot.md)
|
||||
### [Custom Images](#custom-images)
|
||||
|
||||
# Custom app
|
||||
- [Custom Apps](docs/container-setup/02-build-setup.md)
|
||||
- [Build Version 10 Images](docs/build-version-10-images.md)
|
||||
|
||||
Learn how to containerize your custom Frappe app(s) in [this guide](custom_app/README.md).
|
||||
### [Development](#development)
|
||||
|
||||
- [Development using containers](docs/development.md)
|
||||
- [Bench Console and VSCode Debugger](docs/bench-console-and-vscode-debugger.md)
|
||||
- [Connect to localhost services](docs/connect-to-localhost-services-from-containers-for-local-app-development.md)
|
||||
|
||||
### [Troubleshoot](docs/troubleshoot.md)
|
||||
|
||||
# Contributing
|
||||
|
||||
If you want to contribute to this repo refer to [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
|
||||
This repository is only for Docker related stuff. You also might want to contribute to:
|
||||
This repository is only for container related stuff. You also might want to contribute to:
|
||||
|
||||
- [Frappe framework](https://github.com/frappe/frappe#contributing),
|
||||
- [ERPNext](https://github.com/frappe/erpnext#contributing),
|
||||
- or [Frappe Bench](https://github.com/frappe/bench).
|
||||
- [Frappe Bench](https://github.com/frappe/bench).
|
||||
|
||||
18
apps.json
Normal file
18
apps.json
Normal file
@@ -0,0 +1,18 @@
|
||||
[
|
||||
{
|
||||
"url": "https://github.com/frappe/erpnext",
|
||||
"branch": "version-15"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/frappe/hrms",
|
||||
"branch": "version-15"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/frappe/helpdesk",
|
||||
"branch": "main"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/frappe/payments",
|
||||
"branch": "version-15"
|
||||
}
|
||||
]
|
||||
237
compose.khaeli.yaml
Normal file
237
compose.khaeli.yaml
Normal file
@@ -0,0 +1,237 @@
|
||||
name: frappe_docker
|
||||
services:
|
||||
backend:
|
||||
depends_on:
|
||||
configurator:
|
||||
condition: service_completed_successfully
|
||||
required: true
|
||||
image: frappe/erpnext:v15.83.0
|
||||
networks:
|
||||
default: null
|
||||
platform: linux/amd64
|
||||
pull_policy: always
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- type: volume
|
||||
source: sites
|
||||
target: /home/frappe/frappe-bench/sites
|
||||
volume: {}
|
||||
configurator:
|
||||
command:
|
||||
- |
|
||||
ls -1 apps > sites/apps.txt; bench set-config -g db_host $$DB_HOST; bench set-config -gp db_port $$DB_PORT; bench set-config -g redis_cache "redis://$$REDIS_CACHE"; bench set-config -g redis_queue "redis://$$REDIS_QUEUE"; bench set-config -g redis_socketio "redis://$$REDIS_QUEUE"; bench set-config -gp socketio_port $$SOCKETIO_PORT;
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
required: true
|
||||
redis-cache:
|
||||
condition: service_started
|
||||
required: true
|
||||
redis-queue:
|
||||
condition: service_started
|
||||
required: true
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
environment:
|
||||
DB_HOST: db
|
||||
DB_PORT: "3306"
|
||||
REDIS_CACHE: redis-cache:6379
|
||||
REDIS_QUEUE: redis-queue:6379
|
||||
SOCKETIO_PORT: "9000"
|
||||
image: frappe/erpnext:v15.83.0
|
||||
networks:
|
||||
default: null
|
||||
platform: linux/amd64
|
||||
pull_policy: always
|
||||
restart: on-failure
|
||||
volumes:
|
||||
- type: volume
|
||||
source: sites
|
||||
target: /home/frappe/frappe-bench/sites
|
||||
volume: {}
|
||||
db:
|
||||
command:
|
||||
- --character-set-server=utf8mb4
|
||||
- --collation-server=utf8mb4_unicode_ci
|
||||
- --skip-character-set-client-handshake
|
||||
- --skip-innodb-read-only-compressed
|
||||
environment:
|
||||
MARIADB_AUTO_UPGRADE: "1"
|
||||
MYSQL_ROOT_PASSWORD: khaelicloud
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- healthcheck.sh
|
||||
- --connect
|
||||
- --innodb_initialized
|
||||
timeout: 5s
|
||||
interval: 5s
|
||||
retries: 5
|
||||
start_period: 5s
|
||||
image: mariadb:11.8
|
||||
networks:
|
||||
default: null
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- type: volume
|
||||
source: db-data
|
||||
target: /var/lib/mysql
|
||||
volume: {}
|
||||
frontend:
|
||||
command:
|
||||
- nginx-entrypoint.sh
|
||||
depends_on:
|
||||
backend:
|
||||
condition: service_started
|
||||
required: true
|
||||
websocket:
|
||||
condition: service_started
|
||||
required: true
|
||||
environment:
|
||||
BACKEND: backend:8000
|
||||
CLIENT_MAX_BODY_SIZE: 50m
|
||||
FRAPPE_SITE_NAME_HEADER: $$host
|
||||
PROXY_READ_TIMEOUT: "120"
|
||||
SOCKETIO: websocket:9000
|
||||
UPSTREAM_REAL_IP_ADDRESS: 127.0.0.1
|
||||
UPSTREAM_REAL_IP_HEADER: X-Forwarded-For
|
||||
UPSTREAM_REAL_IP_RECURSIVE: "off"
|
||||
image: frappe/erpnext:v15.83.0
|
||||
networks:
|
||||
default: null
|
||||
platform: linux/amd64
|
||||
ports:
|
||||
- mode: ingress
|
||||
target: 8080
|
||||
published: "8080"
|
||||
protocol: tcp
|
||||
pull_policy: always
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- type: volume
|
||||
source: sites
|
||||
target: /home/frappe/frappe-bench/sites
|
||||
volume: {}
|
||||
queue-long:
|
||||
command:
|
||||
- bench
|
||||
- worker
|
||||
- --queue
|
||||
- long,default,short
|
||||
depends_on:
|
||||
configurator:
|
||||
condition: service_completed_successfully
|
||||
required: true
|
||||
image: frappe/erpnext:v15.83.0
|
||||
networks:
|
||||
default: null
|
||||
platform: linux/amd64
|
||||
pull_policy: always
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- type: volume
|
||||
source: sites
|
||||
target: /home/frappe/frappe-bench/sites
|
||||
volume: {}
|
||||
queue-short:
|
||||
command:
|
||||
- bench
|
||||
- worker
|
||||
- --queue
|
||||
- short,default
|
||||
depends_on:
|
||||
configurator:
|
||||
condition: service_completed_successfully
|
||||
required: true
|
||||
image: frappe/erpnext:v15.83.0
|
||||
networks:
|
||||
default: null
|
||||
platform: linux/amd64
|
||||
pull_policy: always
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- type: volume
|
||||
source: sites
|
||||
target: /home/frappe/frappe-bench/sites
|
||||
volume: {}
|
||||
redis-cache:
|
||||
image: redis:6.2-alpine
|
||||
networks:
|
||||
default: null
|
||||
restart: unless-stopped
|
||||
redis-queue:
|
||||
image: redis:6.2-alpine
|
||||
networks:
|
||||
default: null
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- type: volume
|
||||
source: redis-queue-data
|
||||
target: /data
|
||||
volume: {}
|
||||
scheduler:
|
||||
command:
|
||||
- bench
|
||||
- schedule
|
||||
depends_on:
|
||||
configurator:
|
||||
condition: service_completed_successfully
|
||||
required: true
|
||||
image: frappe/erpnext:v15.83.0
|
||||
networks:
|
||||
default: null
|
||||
platform: linux/amd64
|
||||
pull_policy: always
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- type: volume
|
||||
source: sites
|
||||
target: /home/frappe/frappe-bench/sites
|
||||
volume: {}
|
||||
websocket:
|
||||
command:
|
||||
- node
|
||||
- /home/frappe/frappe-bench/apps/frappe/socketio.js
|
||||
depends_on:
|
||||
configurator:
|
||||
condition: service_completed_successfully
|
||||
required: true
|
||||
image: frappe/erpnext:v15.83.0
|
||||
networks:
|
||||
default: null
|
||||
platform: linux/amd64
|
||||
pull_policy: always
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- type: volume
|
||||
source: sites
|
||||
target: /home/frappe/frappe-bench/sites
|
||||
volume: {}
|
||||
networks:
|
||||
default:
|
||||
name: frappe_docker_default
|
||||
volumes:
|
||||
db-data:
|
||||
name: frappe_docker_db-data
|
||||
redis-queue-data:
|
||||
name: frappe_docker_redis-queue-data
|
||||
sites:
|
||||
name: frappe_docker_sites
|
||||
x-backend-defaults:
|
||||
depends_on:
|
||||
configurator:
|
||||
condition: service_completed_successfully
|
||||
image: frappe/erpnext:v15.83.0
|
||||
pull_policy: always
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
x-customizable-image:
|
||||
image: frappe/erpnext:v15.83.0
|
||||
pull_policy: always
|
||||
restart: unless-stopped
|
||||
x-depends-on-configurator:
|
||||
depends_on:
|
||||
configurator:
|
||||
condition: service_completed_successfully
|
||||
67
compose.yaml
67
compose.yaml
@@ -1,33 +1,56 @@
|
||||
x-customizable-image: &customizable_image
|
||||
# By default the image used only contains the `frappe` and `erpnext` apps.
|
||||
# See https://github.com/frappe/frappe_docker/blob/main/docs/custom-apps.md
|
||||
# about using custom images.
|
||||
image: ${CUSTOM_IMAGE:-frappe/erpnext}:${CUSTOM_TAG:-$ERPNEXT_VERSION}
|
||||
pull_policy: ${PULL_POLICY:-always}
|
||||
restart: ${RESTART_POLICY:-unless-stopped}
|
||||
|
||||
x-depends-on-configurator: &depends_on_configurator
|
||||
depends_on:
|
||||
configurator:
|
||||
condition: service_completed_successfully
|
||||
|
||||
x-backend-defaults: &backend_defaults
|
||||
<<: *depends_on_configurator
|
||||
image: frappe/frappe-worker:${FRAPPE_VERSION:?No Frappe version set}
|
||||
<<: [*depends_on_configurator, *customizable_image]
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- assets:/home/frappe/frappe-bench/sites/assets:ro
|
||||
|
||||
services:
|
||||
configurator:
|
||||
<<: *backend_defaults
|
||||
command: configure.py
|
||||
platform: linux/amd64
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
# add redis_socketio for backward compatibility
|
||||
command:
|
||||
- >
|
||||
ls -1 apps > sites/apps.txt;
|
||||
bench set-config -g db_host $$DB_HOST;
|
||||
bench set-config -gp db_port $$DB_PORT;
|
||||
bench set-config -g redis_cache "redis://$$REDIS_CACHE";
|
||||
bench set-config -g redis_queue "redis://$$REDIS_QUEUE";
|
||||
bench set-config -g redis_socketio "redis://$$REDIS_QUEUE";
|
||||
bench set-config -gp socketio_port $$SOCKETIO_PORT;
|
||||
environment:
|
||||
DB_HOST: ${DB_HOST}
|
||||
DB_PORT: ${DB_PORT}
|
||||
REDIS_CACHE: ${REDIS_CACHE}
|
||||
REDIS_QUEUE: ${REDIS_QUEUE}
|
||||
REDIS_SOCKETIO: ${REDIS_SOCKETIO}
|
||||
DB_HOST: ${DB_HOST:-}
|
||||
DB_PORT: ${DB_PORT:-}
|
||||
REDIS_CACHE: ${REDIS_CACHE:-}
|
||||
REDIS_QUEUE: ${REDIS_QUEUE:-}
|
||||
SOCKETIO_PORT: 9000
|
||||
depends_on: {}
|
||||
restart: on-failure
|
||||
|
||||
backend:
|
||||
<<: *backend_defaults
|
||||
platform: linux/amd64
|
||||
|
||||
frontend:
|
||||
image: frappe/frappe-nginx:${FRAPPE_VERSION}
|
||||
<<: *customizable_image
|
||||
platform: linux/amd64
|
||||
command:
|
||||
- nginx-entrypoint.sh
|
||||
environment:
|
||||
BACKEND: backend:8000
|
||||
SOCKETIO: websocket:9000
|
||||
@@ -35,38 +58,38 @@ services:
|
||||
UPSTREAM_REAL_IP_ADDRESS: ${UPSTREAM_REAL_IP_ADDRESS:-127.0.0.1}
|
||||
UPSTREAM_REAL_IP_HEADER: ${UPSTREAM_REAL_IP_HEADER:-X-Forwarded-For}
|
||||
UPSTREAM_REAL_IP_RECURSIVE: ${UPSTREAM_REAL_IP_RECURSIVE:-off}
|
||||
PROXY_READ_TIMOUT: ${PROXY_READ_TIMOUT:-120}
|
||||
PROXY_READ_TIMEOUT: ${PROXY_READ_TIMEOUT:-120}
|
||||
CLIENT_MAX_BODY_SIZE: ${CLIENT_MAX_BODY_SIZE:-50m}
|
||||
volumes:
|
||||
- sites:/usr/share/nginx/html/sites
|
||||
- assets:/usr/share/nginx/html/assets
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
depends_on:
|
||||
- backend
|
||||
- websocket
|
||||
|
||||
websocket:
|
||||
<<: *depends_on_configurator
|
||||
image: frappe/frappe-socketio:${FRAPPE_VERSION}
|
||||
<<: [*depends_on_configurator, *customizable_image]
|
||||
platform: linux/amd64
|
||||
command:
|
||||
- node
|
||||
- /home/frappe/frappe-bench/apps/frappe/socketio.js
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
|
||||
queue-short:
|
||||
<<: *backend_defaults
|
||||
command: bench worker --queue short
|
||||
|
||||
queue-default:
|
||||
<<: *backend_defaults
|
||||
command: bench worker --queue default
|
||||
platform: linux/amd64
|
||||
command: bench worker --queue short,default
|
||||
|
||||
queue-long:
|
||||
<<: *backend_defaults
|
||||
command: bench worker --queue long
|
||||
platform: linux/amd64
|
||||
command: bench worker --queue long,default,short
|
||||
|
||||
scheduler:
|
||||
<<: *backend_defaults
|
||||
platform: linux/amd64
|
||||
command: bench schedule
|
||||
|
||||
# ERPNext requires local assets access (Frappe does not)
|
||||
volumes:
|
||||
sites:
|
||||
assets:
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
This is basic configuration for building images and testing custom apps that use Frappe.
|
||||
|
||||
You can see that there's four files in this folder:
|
||||
|
||||
- `backend.Dockerfile`,
|
||||
- `frontend.Dockerfile`,
|
||||
- `docker-bake.hcl`,
|
||||
- `compose.override.yaml`.
|
||||
|
||||
Python code will be built in `backend.Dockerfile`. JS and CSS (and other fancy frontend stuff) files will be built in `frontend.Dockerfile`.
|
||||
|
||||
`docker-bake.hcl` is reference file for [Buildx Bake](https://github.com/docker/buildx/blob/master/docs/reference/buildx_bake.md). It helps to build images without having to remember all build arguments.
|
||||
|
||||
`compose.override.yaml` is [Compose](https://docs.docker.com/compose/compose-file/) override that replaces images from [main compose file](https://github.com/frappe/frappe_docker/blob/main/compose.yaml) so it would use your own images.
|
||||
|
||||
To get started, install Docker and [Buildx](https://github.com/docker/buildx#installing). Then copy all content of this folder (except this README) to your app's root directory. Also copy `compose.yaml` in the root of this repository.
|
||||
|
||||
Before the next step—to build images—replace "custom_app" with your app's name in `docker-bake.hcl`. After that, let's try to build:
|
||||
|
||||
```bash
|
||||
FRAPPE_VERSION=... ERPNEXT_VERSION=... docker buildx bake
|
||||
```
|
||||
|
||||
> 💡 We assume that majority of our users use ERPNext, that's why images in this tutorial are based on ERPNext images. If don't want ERPNext, change base image in Dockerfile and remove ERPNEXT_VERSION from bake file. To know more about steps used to build frontend image read comments in `frontend.Dockerfile`.
|
||||
|
||||
If something goes wrong feel free to leave an issue.
|
||||
|
||||
To test if site works, setup `.env` file (check [example](<(https://github.com/frappe/frappe_docker/blob/main/example.env)>)) and run:
|
||||
|
||||
```bash
|
||||
docker-compose -f compose.yaml -f overrides/compose.noproxy.yaml -f overrides/compose.mariadb.yaml -f overrides/compose.redis.yaml -f custom_app/compose.override.yaml up -d
|
||||
docker-compose exec backend \
|
||||
bench new-site 127.0.0.1 \
|
||||
--mariadb-root-password 123 \
|
||||
--admin-password admin \
|
||||
--install-app <Name of your app>
|
||||
docker-compose restart backend
|
||||
```
|
||||
|
||||
Cool! You just containerized your app!
|
||||
|
||||
## Installing multiple apps
|
||||
|
||||
Backend builds contain `install-app` script that places app where it should be. Each call to script installs given app. Usage: `install-app [APP_NAME]`.
|
||||
|
||||
If you want to install an app from git, clone it locally, COPY in Dockerfile.
|
||||
@@ -1,14 +0,0 @@
|
||||
# syntax=docker/dockerfile:1.3
|
||||
|
||||
ARG ERPNEXT_VERSION
|
||||
FROM frappe/erpnext-worker:${ERPNEXT_VERSION}
|
||||
|
||||
USER root
|
||||
|
||||
ARG APP_NAME
|
||||
COPY . ../apps/${APP_NAME}
|
||||
|
||||
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||
install-app ${APP_NAME}
|
||||
|
||||
USER frappe
|
||||
@@ -1,21 +0,0 @@
|
||||
services:
|
||||
configurator:
|
||||
image: custom_app/worker:${VERSION}
|
||||
|
||||
backend:
|
||||
image: custom_app/worker:${VERSION}
|
||||
|
||||
frontend:
|
||||
image: custom_app/nginx:${VERSION}
|
||||
|
||||
queue-short:
|
||||
image: custom_app/worker:${VERSION}
|
||||
|
||||
queue-default:
|
||||
image: custom_app/worker:${VERSION}
|
||||
|
||||
queue-long:
|
||||
image: custom_app/worker:${VERSION}
|
||||
|
||||
scheduler:
|
||||
image: custom_app/worker:${VERSION}
|
||||
@@ -1,27 +0,0 @@
|
||||
APP_NAME="custom_app"
|
||||
|
||||
variable "FRAPPE_VERSION" {}
|
||||
variable "ERPNEXT_VERSION" {}
|
||||
|
||||
group "default" {
|
||||
targets = ["backend", "frontend"]
|
||||
}
|
||||
|
||||
target "backend" {
|
||||
dockerfile = "backend.Dockerfile"
|
||||
tags = ["custom_app/worker:latest"]
|
||||
args = {
|
||||
"ERPNEXT_VERSION" = ERPNEXT_VERSION
|
||||
"APP_NAME" = APP_NAME
|
||||
}
|
||||
}
|
||||
|
||||
target "frontend" {
|
||||
dockerfile = "frontend.Dockerfile"
|
||||
tags = ["custom_app/nginx:latest"]
|
||||
args = {
|
||||
"FRAPPE_VERSION" = FRAPPE_VERSION
|
||||
"ERPNEXT_VERSION" = ERPNEXT_VERSION
|
||||
"APP_NAME" = APP_NAME
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
ARG FRAPPE_VERSION=version-14
|
||||
# Prepare builder image
|
||||
FROM frappe/bench:latest as assets
|
||||
|
||||
ARG FRAPPE_VERSION=version-14
|
||||
ARG ERPNEXT_VERSION=version-14
|
||||
ARG APP_NAME
|
||||
|
||||
# Setup frappe-bench using FRAPPE_VERSION
|
||||
RUN bench init --version=${FRAPPE_VERSION} --skip-redis-config-generation --verbose --skip-assets /home/frappe/frappe-bench
|
||||
WORKDIR /home/frappe/frappe-bench
|
||||
|
||||
# Comment following if ERPNext is not required
|
||||
RUN bench get-app --branch=${ERPNEXT_VERSION} --skip-assets --resolve-deps erpnext
|
||||
|
||||
# Copy custom app(s)
|
||||
COPY --chown=frappe:frappe . apps/${APP_NAME}
|
||||
|
||||
# Setup dependencies
|
||||
RUN bench setup requirements
|
||||
|
||||
# Build static assets, copy files instead of symlink
|
||||
RUN if [ -z "${ERPNEXT_VERSION##*v14*}" ] || [ "$ERPNEXT_VERSION" = "develop" ]; then \
|
||||
export BUILD_OPTS="--production"; \
|
||||
fi \
|
||||
&& FRAPPE_ENV=production bench build --verbose --hard-link ${BUILD_OPTS}
|
||||
|
||||
|
||||
# Use frappe-nginx image with nginx template and env vars
|
||||
FROM frappe/frappe-nginx:${FRAPPE_VERSION}
|
||||
|
||||
# Remove existing assets
|
||||
USER root
|
||||
RUN rm -fr /usr/share/nginx/html/assets
|
||||
|
||||
# Copy built assets
|
||||
COPY --from=assets /home/frappe/frappe-bench/sites/assets /usr/share/nginx/html/assets
|
||||
|
||||
# Use non-root user
|
||||
USER 1000
|
||||
@@ -2,6 +2,15 @@
|
||||
"name": "Frappe Bench",
|
||||
"forwardPorts": [8000, 9000, 6787],
|
||||
"remoteUser": "frappe",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"ms-python.python",
|
||||
"ms-vscode.live-server",
|
||||
"grapecity.gc-excelviewer",
|
||||
"mtxr.sqltools",
|
||||
"visualstudioexptteam.vscodeintellicode"
|
||||
],
|
||||
"settings": {
|
||||
"terminal.integrated.profiles.linux": {
|
||||
"frappe bash": {
|
||||
@@ -10,16 +19,14 @@
|
||||
},
|
||||
"terminal.integrated.defaultProfile.linux": "frappe bash",
|
||||
"debug.node.autoAttach": "disabled"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dockerComposeFile": "./docker-compose.yml",
|
||||
"service": "frappe",
|
||||
"workspaceFolder": "/workspace/development",
|
||||
"shutdownAction": "stopCompose",
|
||||
"extensions": [
|
||||
"ms-python.python",
|
||||
"ms-vscode.live-server",
|
||||
"grapecity.gc-excelviewer",
|
||||
"mtxr.sqltools",
|
||||
"visualstudioexptteam.vscodeintellicode"
|
||||
"mounts": [
|
||||
"source=${localEnv:HOME}${localEnv:USERPROFILE}/.ssh,target=/home/frappe/.ssh,type=bind,consistency=cached"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
version: "3.7"
|
||||
services:
|
||||
mariadb:
|
||||
image: mariadb:10.6
|
||||
image: docker.io/mariadb:11.8
|
||||
command:
|
||||
- --character-set-server=utf8mb4
|
||||
- --collation-server=utf8mb4_unicode_ci
|
||||
@@ -9,28 +9,43 @@ services:
|
||||
- --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: 123
|
||||
MARIADB_AUTO_UPGRADE: 1
|
||||
volumes:
|
||||
- mariadb-data:/var/lib/mysql
|
||||
|
||||
# Enable PostgreSQL only if you use it, see development/README.md for more information.
|
||||
# postgresql:
|
||||
# image: postgres:11.8
|
||||
# image: postgres:14
|
||||
# environment:
|
||||
# POSTGRES_PASSWORD: 123
|
||||
# volumes:
|
||||
# - postgresql-data:/var/lib/postgresql/data
|
||||
|
||||
# Enable Mailpit if you need to test outgoing mail services
|
||||
# See https://mailpit.axllent.org/
|
||||
# mailpit:
|
||||
# image: axllent/mailpit
|
||||
# volumes:
|
||||
# - mailpit-data:/data
|
||||
# ports:
|
||||
# - 8025:8025
|
||||
# - 1025:1025
|
||||
# environment:
|
||||
# MP_MAX_MESSAGES: 5000
|
||||
# MP_DATA_FILE: /data/mailpit.db
|
||||
# MP_SMTP_AUTH_ACCEPT_ANY: 1
|
||||
# MP_SMTP_AUTH_ALLOW_INSECURE: 1
|
||||
|
||||
redis-cache:
|
||||
image: redis:alpine
|
||||
image: docker.io/redis:alpine
|
||||
|
||||
redis-queue:
|
||||
image: redis:alpine
|
||||
|
||||
redis-socketio:
|
||||
image: redis:alpine
|
||||
image: docker.io/redis:alpine
|
||||
|
||||
frappe:
|
||||
image: frappe/bench:latest
|
||||
image: docker.io/frappe/bench:latest
|
||||
# If you want to build the current bench image the Containerfile is in this Repo.
|
||||
# build: ../images/bench
|
||||
command: sleep infinity
|
||||
environment:
|
||||
- SHELL=/bin/bash
|
||||
@@ -42,7 +57,34 @@ services:
|
||||
ports:
|
||||
- 8000-8005:8000-8005
|
||||
- 9000-9005:9000-9005
|
||||
|
||||
# enable the below service if you need Cypress UI Tests to be executed
|
||||
# Before enabling ensure install_x11_deps.sh has been executed and display variable is exported.
|
||||
# Run install_x11_deps.sh again if DISPLAY is not set
|
||||
# ui-tester:
|
||||
# # pass custom command to start Cypress otherwise it will use the entrypoint
|
||||
# # specified in the Cypress Docker image.
|
||||
# # also pass "--project <folder>" so that when Cypress opens
|
||||
# # it can find file "cypress.json" and show integration specs
|
||||
# # https://on.cypress.io/command-line#cypress-open
|
||||
# entrypoint: 'sleep infinity'
|
||||
# image: "docker.io/cypress/included:latest"
|
||||
# environment:
|
||||
# - SHELL=/bin/bash
|
||||
# # get the IP address of the host machine and allow X11 to accept
|
||||
# # incoming connections from that IP address
|
||||
# # IP=$(ipconfig getifaddr en0) or mac or \
|
||||
# # IP=$($(hostname -I | awk '{print $1}') ) for Ubuntu
|
||||
# # /usr/X11/bin/xhost + $IP
|
||||
# # then pass the environment variable DISPLAY to show Cypress GUI on the host system
|
||||
# # DISPLAY=$IP:0
|
||||
# - DISPLAY
|
||||
# volumes:
|
||||
# # for Cypress to communicate with the X11 server pass this socket file
|
||||
# # in addition to any other mapped volumes
|
||||
# - /tmp/.X11-unix:/tmp/.X11-unix
|
||||
# - ..:/workspace:z,cached
|
||||
# network_mode: "host"
|
||||
volumes:
|
||||
mariadb-data:
|
||||
postgresql-data:
|
||||
#postgresql-data:
|
||||
#mailpit-data:
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
{
|
||||
"client_name": [
|
||||
[
|
||||
{
|
||||
"name": "frappe",
|
||||
"branch": "develop",
|
||||
"upstream": "git@github.com:frappe/frappe.git",
|
||||
"fork": "[your fork]"
|
||||
},
|
||||
{
|
||||
"name": "erpnext",
|
||||
"branch": "develop",
|
||||
"upstream": "git@github.com:frappe/erpnext.git"
|
||||
"url": "https://github.com/frappe/erpnext.git",
|
||||
"branch": "version-15"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
245
development/installer.py
Executable file
245
development/installer.py
Executable file
@@ -0,0 +1,245 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
|
||||
def cprint(*args, level: int = 1):
|
||||
"""
|
||||
logs colorful messages
|
||||
level = 1 : RED
|
||||
level = 2 : GREEN
|
||||
level = 3 : YELLOW
|
||||
|
||||
default level = 1
|
||||
"""
|
||||
CRED = "\033[31m"
|
||||
CGRN = "\33[92m"
|
||||
CYLW = "\33[93m"
|
||||
reset = "\033[0m"
|
||||
message = " ".join(map(str, args))
|
||||
if level == 1:
|
||||
print(CRED, message, reset) # noqa: T001, T201
|
||||
if level == 2:
|
||||
print(CGRN, message, reset) # noqa: T001, T201
|
||||
if level == 3:
|
||||
print(CYLW, message, reset) # noqa: T001, T201
|
||||
|
||||
|
||||
def main():
|
||||
parser = get_args_parser()
|
||||
args = parser.parse_args()
|
||||
init_bench_if_not_exist(args)
|
||||
create_site_in_bench(args)
|
||||
|
||||
|
||||
def get_args_parser():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"-j",
|
||||
"--apps-json",
|
||||
action="store",
|
||||
type=str,
|
||||
help="Path to apps.json, default: apps-example.json",
|
||||
default="apps-example.json",
|
||||
) # noqa: E501
|
||||
parser.add_argument(
|
||||
"-b",
|
||||
"--bench-name",
|
||||
action="store",
|
||||
type=str,
|
||||
help="Bench directory name, default: frappe-bench",
|
||||
default="frappe-bench",
|
||||
) # noqa: E501
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--site-name",
|
||||
action="store",
|
||||
type=str,
|
||||
help="Site name, should end with .localhost, default: development.localhost", # noqa: E501
|
||||
default="development.localhost",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-r",
|
||||
"--frappe-repo",
|
||||
action="store",
|
||||
type=str,
|
||||
help="frappe repo to use, default: https://github.com/frappe/frappe", # noqa: E501
|
||||
default="https://github.com/frappe/frappe",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--frappe-branch",
|
||||
action="store",
|
||||
type=str,
|
||||
help="frappe repo to use, default: version-15", # noqa: E501
|
||||
default="version-15",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--py-version",
|
||||
action="store",
|
||||
type=str,
|
||||
help="python version, default: Not Set", # noqa: E501
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--node-version",
|
||||
action="store",
|
||||
type=str,
|
||||
help="node version, default: Not Set", # noqa: E501
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--verbose",
|
||||
action="store_true",
|
||||
help="verbose output", # noqa: E501
|
||||
)
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--admin-password",
|
||||
action="store",
|
||||
type=str,
|
||||
help="admin password for site, default: admin", # noqa: E501
|
||||
default="admin",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--db-type",
|
||||
action="store",
|
||||
type=str,
|
||||
help="Database type to use (e.g., mariadb or postgres)",
|
||||
default="mariadb", # Set your default database type here
|
||||
)
|
||||
return parser
|
||||
|
||||
|
||||
def init_bench_if_not_exist(args):
|
||||
if os.path.exists(args.bench_name):
|
||||
cprint("Bench already exists. Only site will be created", level=3)
|
||||
return
|
||||
try:
|
||||
env = os.environ.copy()
|
||||
if args.py_version:
|
||||
env["PYENV_VERSION"] = args.py_version
|
||||
init_command = ""
|
||||
if args.node_version:
|
||||
init_command = f"nvm use {args.node_version};"
|
||||
if args.py_version:
|
||||
init_command += f"PYENV_VERSION={args.py_version} "
|
||||
init_command += "bench init "
|
||||
init_command += "--skip-redis-config-generation "
|
||||
init_command += "--verbose " if args.verbose else " "
|
||||
init_command += f"--frappe-path={args.frappe_repo} "
|
||||
init_command += f"--frappe-branch={args.frappe_branch} "
|
||||
init_command += f"--apps_path={args.apps_json} "
|
||||
init_command += args.bench_name
|
||||
command = [
|
||||
"/bin/bash",
|
||||
"-i",
|
||||
"-c",
|
||||
init_command,
|
||||
]
|
||||
subprocess.call(command, env=env, cwd=os.getcwd())
|
||||
cprint("Configuring Bench ...", level=2)
|
||||
cprint("Set db_host", level=3)
|
||||
if args.db_type:
|
||||
cprint(f"Setting db_type to {args.db_type}", level=3)
|
||||
subprocess.call(
|
||||
["bench", "set-config", "-g", "db_type", args.db_type],
|
||||
cwd=os.path.join(os.getcwd(), args.bench_name),
|
||||
)
|
||||
|
||||
cprint("Set redis_cache to redis://redis-cache:6379", level=3)
|
||||
subprocess.call(
|
||||
[
|
||||
"bench",
|
||||
"set-config",
|
||||
"-g",
|
||||
"redis_cache",
|
||||
"redis://redis-cache:6379",
|
||||
],
|
||||
cwd=os.getcwd() + "/" + args.bench_name,
|
||||
)
|
||||
cprint("Set redis_queue to redis://redis-queue:6379", level=3)
|
||||
subprocess.call(
|
||||
[
|
||||
"bench",
|
||||
"set-config",
|
||||
"-g",
|
||||
"redis_queue",
|
||||
"redis://redis-queue:6379",
|
||||
],
|
||||
cwd=os.getcwd() + "/" + args.bench_name,
|
||||
)
|
||||
cprint(
|
||||
"Set redis_socketio to redis://redis-queue:6379 for backward compatibility", # noqa: E501
|
||||
level=3,
|
||||
)
|
||||
subprocess.call(
|
||||
[
|
||||
"bench",
|
||||
"set-config",
|
||||
"-g",
|
||||
"redis_socketio",
|
||||
"redis://redis-queue:6379",
|
||||
],
|
||||
cwd=os.getcwd() + "/" + args.bench_name,
|
||||
)
|
||||
cprint("Set developer_mode", level=3)
|
||||
subprocess.call(
|
||||
["bench", "set-config", "-gp", "developer_mode", "1"],
|
||||
cwd=os.getcwd() + "/" + args.bench_name,
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
cprint(e.output, level=1)
|
||||
|
||||
|
||||
def create_site_in_bench(args):
|
||||
if "mariadb" == args.db_type:
|
||||
cprint("Set db_host", level=3)
|
||||
subprocess.call(
|
||||
["bench", "set-config", "-g", "db_host", "mariadb"],
|
||||
cwd=os.getcwd() + "/" + args.bench_name,
|
||||
)
|
||||
new_site_cmd = [
|
||||
"bench",
|
||||
"new-site",
|
||||
f"--db-root-username=root",
|
||||
f"--db-host=mariadb", # Should match the compose service name
|
||||
f"--db-type={args.db_type}", # Add the selected database type
|
||||
f"--mariadb-user-host-login-scope=%",
|
||||
f"--db-root-password=123", # Replace with your MariaDB password
|
||||
f"--admin-password={args.admin_password}",
|
||||
]
|
||||
else:
|
||||
cprint("Set db_host", level=3)
|
||||
subprocess.call(
|
||||
["bench", "set-config", "-g", "db_host", "postgresql"],
|
||||
cwd=os.getcwd() + "/" + args.bench_name,
|
||||
)
|
||||
new_site_cmd = [
|
||||
"bench",
|
||||
"new-site",
|
||||
f"--db-root-username=root",
|
||||
f"--db-host=postgresql", # Should match the compose service name
|
||||
f"--db-type={args.db_type}", # Add the selected database type
|
||||
f"--db-root-password=123", # Replace with your PostgreSQL password
|
||||
f"--admin-password={args.admin_password}",
|
||||
]
|
||||
apps = os.listdir(f"{os.getcwd()}/{args.bench_name}/apps")
|
||||
apps.remove("frappe")
|
||||
for app in apps:
|
||||
new_site_cmd.append(f"--install-app={app}")
|
||||
new_site_cmd.append(args.site_name)
|
||||
cprint(f"Creating Site {args.site_name} ...", level=2)
|
||||
subprocess.call(
|
||||
new_site_cmd,
|
||||
cwd=os.getcwd() + "/" + args.bench_name,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,160 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Developer Note: Run this script in the /workspace/development directory
|
||||
|
||||
export NVM_DIR=~/.nvm
|
||||
# shellcheck disable=SC1091
|
||||
source $NVM_DIR/nvm.sh
|
||||
|
||||
sudo apt -qq update && sudo apt -qq install jq -y
|
||||
|
||||
get_client_apps() {
|
||||
apps=$(jq ".\"$client\"" apps.json)
|
||||
|
||||
if [ "$apps" == "null" ]; then
|
||||
echo "No apps found for $client"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
validate_bench_exists() {
|
||||
dir="$(pwd)/$bench_name"
|
||||
if [ -d "$dir" ]; then
|
||||
echo "Bench already exists. Only site will be created"
|
||||
is_existing_bench=true
|
||||
else
|
||||
is_existing_bench=false
|
||||
fi
|
||||
}
|
||||
|
||||
validate_branch() {
|
||||
if [ "$app" == "frappe" ] || [ "$app" == "erpnext" ]; then
|
||||
if [ "$branch" != "develop" ] && [ "$branch" != "version-14" ] && [ "$branch" != "version-13" ]; then
|
||||
echo "Branch should be one of develop or version-14 or version-13"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
validate_site() {
|
||||
if [[ ! "$site_name" =~ ^.*\.localhost$ ]]; then
|
||||
echo "Site name should end with .localhost"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$is_existing_bench" = true ]; then
|
||||
validate_site_exists
|
||||
fi
|
||||
}
|
||||
|
||||
validate_site_exists() {
|
||||
dir="$(pwd)/$bench_name/sites/$site_name"
|
||||
if [ -d "$dir" ]; then
|
||||
echo "Site already exists. Exiting"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
validate_app_exists() {
|
||||
dir="$(pwd)/apps/$app"
|
||||
if [ -d "$dir" ]; then
|
||||
echo "App $app already exists."
|
||||
is_app_installed=true
|
||||
else
|
||||
is_app_installed=false
|
||||
fi
|
||||
}
|
||||
|
||||
add_fork() {
|
||||
dir="$(pwd)/apps/$app"
|
||||
if [ "$fork" != "null" ]; then
|
||||
git -C "$dir" remote add fork "$fork"
|
||||
fi
|
||||
}
|
||||
|
||||
install_apps() {
|
||||
initialize_bench=$1
|
||||
|
||||
for row in $(echo "$apps" | jq -r '.[] | @base64'); do
|
||||
# helper function to retrieve values from dict
|
||||
_jq() {
|
||||
echo "${row}" | base64 --decode | jq -r "${1}"
|
||||
}
|
||||
|
||||
app=$(_jq '.name')
|
||||
branch=$(_jq '.branch')
|
||||
upstream=$(_jq '.upstream')
|
||||
fork=$(_jq '.fork')
|
||||
|
||||
if [ "$initialize_bench" = true ] && [ "$app" == "frappe" ]; then
|
||||
init_bench
|
||||
fi
|
||||
if [ "$initialize_bench" = false ]; then
|
||||
get_apps_from_upstream
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
init_bench() {
|
||||
echo "Creating bench $bench_name"
|
||||
|
||||
if [ "$branch" == "develop" ] || [ "$branch" == "version-14" ]; then
|
||||
python_version=python3.10
|
||||
PYENV_VERSION=3.10.5
|
||||
NODE_VERSION=v16
|
||||
elif [ "$branch" == "version-13" ]; then
|
||||
python_version=python3.9
|
||||
PYENV_VERSION=3.9.9
|
||||
NODE_VERSION=v14
|
||||
fi
|
||||
|
||||
nvm use "$NODE_VERSION"
|
||||
PYENV_VERSION="$PYENV_VERSION" bench init --skip-redis-config-generation --frappe-branch "$branch" --python "$python_version" "$bench_name"
|
||||
cd "$bench_name" || exit
|
||||
|
||||
echo "Setting up config"
|
||||
|
||||
bench set-config -g db_host mariadb
|
||||
bench set-config -g redis_cache redis://redis-cache:6379
|
||||
bench set-config -g redis_queue redis://redis-queue:6379
|
||||
bench set-config -g redis_socketio redis://redis-socketio:6379
|
||||
|
||||
./env/bin/pip install honcho
|
||||
}
|
||||
|
||||
get_apps_from_upstream() {
|
||||
validate_app_exists
|
||||
if [ "$is_app_installed" = false ]; then
|
||||
bench get-app --branch "$branch" --resolve-deps "$app" "$upstream" && add_fork
|
||||
fi
|
||||
|
||||
if [ "$app" != "frappe" ]; then
|
||||
all_apps+=("$app")
|
||||
fi
|
||||
}
|
||||
|
||||
echo "Client Name (from apps.json file)?"
|
||||
read -r client && client=${client:-develop_client} && get_client_apps
|
||||
|
||||
echo "Bench Directory Name? (give name of existing bench to just create a new site) (default: frape-bench)"
|
||||
read -r bench_name && bench_name=${bench_name:-frappe-bench} && validate_bench_exists
|
||||
|
||||
echo "Site Name? (should end with .localhost) (default: site1.localhost)"
|
||||
read -r site_name && site_name=${site_name:-site1.localhost} && validate_site
|
||||
|
||||
if [ "$is_existing_bench" = true ]; then
|
||||
cd "$bench_name" || exit
|
||||
else
|
||||
install_apps true
|
||||
fi
|
||||
|
||||
echo "Getting apps from upstream for $client"
|
||||
all_apps=() && install_apps false
|
||||
|
||||
echo "Creating site $site_name"
|
||||
bench new-site "$site_name" --mariadb-root-password 123 --admin-password admin --no-mariadb-socket
|
||||
|
||||
echo "Installing apps to $site_name"
|
||||
bench --site "$site_name" install-app "${all_apps[@]}"
|
||||
|
||||
bench --site "$site_name" set-config developer_mode 1
|
||||
bench --site "$site_name" clear-cache
|
||||
@@ -6,7 +6,7 @@
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Bench Web",
|
||||
"type": "python",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/frappe-bench/apps/frappe/frappe/utils/bench_helper.py",
|
||||
"args": [
|
||||
@@ -17,19 +17,6 @@
|
||||
"--noreload",
|
||||
"--nothreading"
|
||||
],
|
||||
"pythonPath": "${workspaceFolder}/frappe-bench/env/bin/python",
|
||||
"cwd": "${workspaceFolder}/frappe-bench/sites",
|
||||
"env": {
|
||||
"DEV_SERVER": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Bench Default Worker",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/frappe-bench/apps/frappe/frappe/utils/bench_helper.py",
|
||||
"args": ["frappe", "worker", "--queue", "default"],
|
||||
"pythonPath": "${workspaceFolder}/frappe-bench/env/bin/python",
|
||||
"cwd": "${workspaceFolder}/frappe-bench/sites",
|
||||
"env": {
|
||||
"DEV_SERVER": "1"
|
||||
@@ -37,11 +24,21 @@
|
||||
},
|
||||
{
|
||||
"name": "Bench Short Worker",
|
||||
"type": "python",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/frappe-bench/apps/frappe/frappe/utils/bench_helper.py",
|
||||
"args": ["frappe", "worker", "--queue", "short"],
|
||||
"pythonPath": "${workspaceFolder}/frappe-bench/env/bin/python",
|
||||
"cwd": "${workspaceFolder}/frappe-bench/sites",
|
||||
"env": {
|
||||
"DEV_SERVER": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Bench Default Worker",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/frappe-bench/apps/frappe/frappe/utils/bench_helper.py",
|
||||
"args": ["frappe", "worker", "--queue", "default"],
|
||||
"cwd": "${workspaceFolder}/frappe-bench/sites",
|
||||
"env": {
|
||||
"DEV_SERVER": "1"
|
||||
@@ -49,11 +46,10 @@
|
||||
},
|
||||
{
|
||||
"name": "Bench Long Worker",
|
||||
"type": "python",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/frappe-bench/apps/frappe/frappe/utils/bench_helper.py",
|
||||
"args": ["frappe", "worker", "--queue", "long"],
|
||||
"pythonPath": "${workspaceFolder}/frappe-bench/env/bin/python",
|
||||
"cwd": "${workspaceFolder}/frappe-bench/sites",
|
||||
"env": {
|
||||
"DEV_SERVER": "1"
|
||||
@@ -61,21 +57,21 @@
|
||||
},
|
||||
{
|
||||
"name": "Honcho SocketIO Watch Schedule Worker",
|
||||
"type": "python",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"python": "/home/frappe/.pyenv/shims/python",
|
||||
"program": "/home/frappe/.local/bin/honcho",
|
||||
"pythonPath": "${workspaceFolder}/frappe-bench/env/bin/python",
|
||||
"cwd": "${workspaceFolder}/frappe-bench",
|
||||
"console": "internalConsole",
|
||||
"args": [
|
||||
"start",
|
||||
"socketio",
|
||||
"watch",
|
||||
"schedule",
|
||||
"worker_short",
|
||||
"worker_long",
|
||||
"worker_default"
|
||||
]
|
||||
"args": ["start", "socketio", "watch", "schedule", "worker"],
|
||||
"postDebugTask": "Clean Honcho SocketIO Watch Schedule Worker"
|
||||
}
|
||||
],
|
||||
"compounds": [
|
||||
{
|
||||
"name": "Honcho + Web debug",
|
||||
"configurations": ["Bench Web", "Honcho SocketIO Watch Schedule Worker"],
|
||||
"stopAll": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
3
development/vscode-example/settings.json
Normal file
3
development/vscode-example/settings.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"python.defaultInterpreterPath": "${workspaceFolder}/frappe-bench/env/bin/python"
|
||||
}
|
||||
22
development/vscode-example/tasks.json
Normal file
22
development/vscode-example/tasks.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Clean Honcho SocketIO Watch Schedule Worker",
|
||||
"detail": "When stopping the debug process from vscode window, the honcho won't receive the SIGINT signal. This task will send the SIGINT signal to the honcho processes.",
|
||||
"type": "shell",
|
||||
"command": "pkill -SIGINT -f bench; pkill -SIGINT -f socketio",
|
||||
"isBackground": false,
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "silent",
|
||||
"focus": false,
|
||||
"panel": "shared",
|
||||
"showReuseMessage": false,
|
||||
"close": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -5,6 +5,13 @@ variable "REGISTRY_USER" {
|
||||
default = "frappe"
|
||||
}
|
||||
|
||||
variable PYTHON_VERSION {
|
||||
default = "3.11.6"
|
||||
}
|
||||
variable NODE_VERSION {
|
||||
default = "20.19.2"
|
||||
}
|
||||
|
||||
variable "FRAPPE_VERSION" {
|
||||
default = "develop"
|
||||
}
|
||||
@@ -25,6 +32,10 @@ variable "BENCH_REPO" {
|
||||
default = "https://github.com/frappe/bench"
|
||||
}
|
||||
|
||||
variable "LATEST_BENCH_RELEASE" {
|
||||
default = "latest"
|
||||
}
|
||||
|
||||
# Bench image
|
||||
|
||||
target "bench" {
|
||||
@@ -33,7 +44,10 @@ target "bench" {
|
||||
}
|
||||
context = "images/bench"
|
||||
target = "bench"
|
||||
tags = ["frappe/bench:latest"]
|
||||
tags = [
|
||||
"frappe/bench:${LATEST_BENCH_RELEASE}",
|
||||
"frappe/bench:latest",
|
||||
]
|
||||
}
|
||||
|
||||
target "bench-test" {
|
||||
@@ -44,70 +58,56 @@ target "bench-test" {
|
||||
# Main images
|
||||
# Base for all other targets
|
||||
|
||||
group "frappe" {
|
||||
targets = ["frappe-worker", "frappe-nginx", "frappe-socketio"]
|
||||
}
|
||||
|
||||
group "erpnext" {
|
||||
targets = ["erpnext-worker", "erpnext-nginx"]
|
||||
}
|
||||
|
||||
group "default" {
|
||||
targets = ["frappe", "erpnext"]
|
||||
targets = ["erpnext", "base", "build"]
|
||||
}
|
||||
|
||||
function "tag" {
|
||||
params = [repo, version]
|
||||
result = [
|
||||
# Push frappe or erpnext branch as tag
|
||||
"${REGISTRY_USER}/${repo}:${version}",
|
||||
# If `version` param is develop (development build) then use tag `latest`
|
||||
"${version}" == "develop" ? "${REGISTRY_USER}/${repo}:latest" : "${REGISTRY_USER}/${repo}:${version}",
|
||||
# Make short tag for major version if possible. For example, from v13.16.0 make v13.
|
||||
can(regex("(v[0-9]+)[.]", "${version}")) ? "${REGISTRY_USER}/${repo}:${regex("(v[0-9]+)[.]", "${version}")[0]}" : "",
|
||||
# Make short tag for major version if possible. For example, from v13.16.0 make version-13.
|
||||
can(regex("(v[0-9]+)[.]", "${version}")) ? "${REGISTRY_USER}/${repo}:version-${regex("([0-9]+)[.]", "${version}")[0]}" : "",
|
||||
]
|
||||
}
|
||||
|
||||
target "default-args" {
|
||||
args = {
|
||||
FRAPPE_REPO = "${FRAPPE_REPO}"
|
||||
ERPNEXT_REPO = "${ERPNEXT_REPO}"
|
||||
FRAPPE_PATH = "${FRAPPE_REPO}"
|
||||
ERPNEXT_PATH = "${ERPNEXT_REPO}"
|
||||
BENCH_REPO = "${BENCH_REPO}"
|
||||
FRAPPE_VERSION = "${FRAPPE_VERSION}"
|
||||
ERPNEXT_VERSION = "${ERPNEXT_VERSION}"
|
||||
PYTHON_VERSION = can(regex("v13", "${ERPNEXT_VERSION}")) ? "3.9.17" : "3.10.12"
|
||||
NODE_VERSION = can(regex("v13", "${FRAPPE_VERSION}")) ? "14.21.3" : "16.20.1"
|
||||
FRAPPE_BRANCH = "${FRAPPE_VERSION}"
|
||||
ERPNEXT_BRANCH = "${ERPNEXT_VERSION}"
|
||||
PYTHON_VERSION = "${PYTHON_VERSION}"
|
||||
NODE_VERSION = "${NODE_VERSION}"
|
||||
}
|
||||
}
|
||||
|
||||
target "frappe-worker" {
|
||||
target "erpnext" {
|
||||
inherits = ["default-args"]
|
||||
context = "images/worker"
|
||||
target = "frappe"
|
||||
tags = tag("frappe-worker", "${FRAPPE_VERSION}")
|
||||
}
|
||||
|
||||
target "erpnext-worker" {
|
||||
inherits = ["default-args"]
|
||||
context = "images/worker"
|
||||
context = "."
|
||||
dockerfile = "images/production/Containerfile"
|
||||
target = "erpnext"
|
||||
tags = tag("erpnext-worker", "${ERPNEXT_VERSION}")
|
||||
tags = tag("erpnext", "${ERPNEXT_VERSION}")
|
||||
}
|
||||
|
||||
target "frappe-nginx" {
|
||||
target "base" {
|
||||
inherits = ["default-args"]
|
||||
context = "images/nginx"
|
||||
target = "frappe"
|
||||
tags = tag("frappe-nginx", "${FRAPPE_VERSION}")
|
||||
context = "."
|
||||
dockerfile = "images/production/Containerfile"
|
||||
target = "base"
|
||||
tags = tag("base", "${FRAPPE_VERSION}")
|
||||
}
|
||||
|
||||
target "erpnext-nginx" {
|
||||
target "build" {
|
||||
inherits = ["default-args"]
|
||||
context = "images/nginx"
|
||||
target = "erpnext"
|
||||
tags = tag("erpnext-nginx", "${ERPNEXT_VERSION}")
|
||||
}
|
||||
|
||||
target "frappe-socketio" {
|
||||
inherits = ["default-args"]
|
||||
context = "images/socketio"
|
||||
tags = tag("frappe-socketio", "${FRAPPE_VERSION}")
|
||||
context = "."
|
||||
dockerfile = "images/production/Containerfile"
|
||||
target = "build"
|
||||
tags = tag("build", "${ERPNEXT_VERSION}")
|
||||
}
|
||||
|
||||
@@ -5,40 +5,36 @@ Create backup service or stack.
|
||||
version: "3.7"
|
||||
services:
|
||||
backup:
|
||||
image: frappe/erpnext-worker:v14
|
||||
image: frappe/erpnext:${VERSION}
|
||||
entrypoint: ["bash", "-c"]
|
||||
command: |
|
||||
for SITE in $(/home/frappe/frappe-bench/env/bin/python -c "import frappe;print(' '.join(frappe.utils.get_sites()))")
|
||||
do
|
||||
bench --site $SITE backup --with-files
|
||||
push-backup \
|
||||
--site $SITE \
|
||||
--bucket $BUCKET_NAME \
|
||||
--region-name $REGION \
|
||||
--endpoint-url $ENDPOINT_URL \
|
||||
--aws-access-key-id $ACCESS_KEY_ID \
|
||||
--aws-secret-access-key $SECRET_ACCESS_KEY
|
||||
done
|
||||
command:
|
||||
- |
|
||||
bench --site all backup
|
||||
## Uncomment for restic snapshots.
|
||||
# restic snapshots || restic init
|
||||
# restic backup sites
|
||||
## Uncomment to keep only last n=30 snapshots.
|
||||
# restic forget --group-by=paths --keep-last=30 --prune
|
||||
environment:
|
||||
- BUCKET_NAME=erpnext
|
||||
- REGION=us-east-1
|
||||
- ACCESS_KEY_ID=RANDOMACCESSKEY
|
||||
- SECRET_ACCESS_KEY=RANDOMSECRETKEY
|
||||
- ENDPOINT_URL=https://endpoint.url
|
||||
# Set correct environment variables for restic
|
||||
- RESTIC_REPOSITORY=s3:https://s3.endpoint.com/restic
|
||||
- AWS_ACCESS_KEY_ID=access_key
|
||||
- AWS_SECRET_ACCESS_KEY=secret_access_key
|
||||
- RESTIC_PASSWORD=restic_password
|
||||
volumes:
|
||||
- "sites-vol:/home/frappe/frappe-bench/sites"
|
||||
- "sites:/home/frappe/frappe-bench/sites"
|
||||
networks:
|
||||
- erpnext-network
|
||||
|
||||
networks:
|
||||
erpnext-network:
|
||||
external: true
|
||||
name: <your_frappe_docker_project_name>_default
|
||||
name: ${PROJECT_NAME:-erpnext}_default
|
||||
|
||||
volumes:
|
||||
sites-vol:
|
||||
sites:
|
||||
external: true
|
||||
name: <your_frappe_docker_project_name>_sites-vol
|
||||
name: ${PROJECT_NAME:-erpnext}_sites
|
||||
```
|
||||
|
||||
In case of single docker host setup, add crontab entry for backup every 6 hours.
|
||||
@@ -47,6 +43,16 @@ In case of single docker host setup, add crontab entry for backup every 6 hours.
|
||||
0 */6 * * * /usr/local/bin/docker-compose -f /path/to/backup-job.yml up -d > /dev/null
|
||||
```
|
||||
|
||||
Or
|
||||
|
||||
```
|
||||
0 */6 * * * docker compose -p erpnext exec backend bench --site all backup --with-files > /dev/null
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- Make sure `docker-compose` or `docker compose` is available in path during execution.
|
||||
- Change the cron string as per need.
|
||||
- Set the correct project name in place of `erpnext`.
|
||||
- For Docker Swarm add it as a [swarm-cronjob](https://github.com/crazy-max/swarm-cronjob)
|
||||
- Add it as a `CronJob` in case of Kubernetes cluster.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Add the following configuration to `launch.json` `configurations` array to start bench console and use debugger. Replace `mysite.localhost` with appropriate site. Also replace `frappe-bench` with name of the bench directory.
|
||||
Add the following configuration to `launch.json` `configurations` array to start bench console and use debugger. Replace `development.localhost` with appropriate site. Also replace `frappe-bench` with name of the bench directory.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -6,7 +6,7 @@ Add the following configuration to `launch.json` `configurations` array to start
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/frappe-bench/apps/frappe/frappe/utils/bench_helper.py",
|
||||
"args": ["frappe", "--site", "mysite.localhost", "console"],
|
||||
"args": ["frappe", "--site", "development.localhost", "console"],
|
||||
"pythonPath": "${workspaceFolder}/frappe-bench/env/bin/python",
|
||||
"cwd": "${workspaceFolder}/frappe-bench/sites",
|
||||
"env": {
|
||||
|
||||
47
docs/container-setup/01-overview.md
Normal file
47
docs/container-setup/01-overview.md
Normal file
@@ -0,0 +1,47 @@
|
||||
The purpose of this document is to give you an overview of how the Frappe Docker containers are structured.
|
||||
|
||||
# 🐳 Images
|
||||
|
||||
There are **four predefined Dockerfiles** available in the `/images` directory.
|
||||
|
||||
| Dockerfile | Ingredients | Purpose & Use Case |
|
||||
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **bench** | Sets up only the Bench CLI. | Used for **development** or debugging. Provides the command-line tooling but does not include runtime services. |
|
||||
| **custom** | Multi-purpose Python backend built from a plain Python image. Includes everything needed to run a Frappe instance via a Compose setup. Installs apps defined in `apps.json`. | Suitable for **production** and **testing**. Ideal when you need control over dependencies (e.g. trying new Python or Node versions). |
|
||||
| **layered** | Final contents are the same as `custom`, but it is based on **prebuilt images from [Docker Hub](https://hub.docker.com/u/frappe)**. | Great for **production builds** when you’re fine with the dependency versions managed by Frappe. Builds much faster since the base layers are already prepared. |
|
||||
| **production** | Similar to `custom` (built from a Python base image), but installs **only Frappe and ERPNext**. Not customizable with `apps.json`. | Best for **quick starts** or exploration. For real deployments or CI/CD pipelines, `custom` or `layered` are preferred because they offer more flexibility. |
|
||||
|
||||
---
|
||||
|
||||
These images include everything needed to run all processes required by the Frappe framework
|
||||
(see [Bench Procfile reference](https://frappeframework.com/docs/v14/user/en/bench/resources/bench-procfile)).
|
||||
|
||||
- The `bench` image only sets up the CLI tool.
|
||||
- The other images (`custom`, `layered`, and `production`) go further — enabling a nearly **plug-and-play** setup for ERPNext and custom apps.
|
||||
|
||||
> We use [multi-stage builds](https://docs.docker.com/develop/develop-images/multistage-build/) and [Docker Buildx](https://docs.docker.com/engine/reference/commandline/buildx/) to maximize layer reuse and make our builds more efficient.
|
||||
|
||||
# 🏗️ Compose
|
||||
|
||||
Once images are built, containers are orchestrated using a [compose file](https://docs.docker.com/compose/compose-file/). The main compose.yaml provides core services, networking, and volumes for any Frappe setup.
|
||||
|
||||
## 🛠️ Services
|
||||
|
||||
| Service | Role | Purpose |
|
||||
| ---------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **configurator** | Setup | Updates `common_site_config.json` so Frappe knows how to access db and redis. It is executed on every `docker-compose up` (and exited immediately). Other services start after this container exits successfully |
|
||||
| **backend** | Runtime | [Werkzeug server](https://werkzeug.palletsprojects.com/en/2.0.x/) |
|
||||
| **frontend** | Proxy | [nginx](https://www.nginx.com) server that serves JS/CSS assets and routes incoming requests |
|
||||
| **websocket** | Real-time | Node server that runs [Socket.IO](https://socket.io) |
|
||||
| **queue-\_** | Background Jobs | Python servers that run job queues using [rq](https://python-rq.org) |
|
||||
| **scheduler** | Task Automation | Python server that runs tasks on schedule using [schedule](https://schedule.readthedocs.io/en/stable/) |
|
||||
|
||||
## 🧩 Overrides
|
||||
|
||||
Additional functionality can be added using [overrides](https://docs.docker.com/compose/extends/). These files modify existing services or add new ones without changing the main `compose.yaml`.
|
||||
|
||||
Example: The main compose file has no database service, but `compose.mariadb.yaml` adds MariaDB. See [overrider.md](overrider.md) for the complete list of available overrides and how to use them.
|
||||
|
||||
---
|
||||
|
||||
**Next:** [Build Setup →](02-build-setup.md)
|
||||
121
docs/container-setup/02-build-setup.md
Normal file
121
docs/container-setup/02-build-setup.md
Normal file
@@ -0,0 +1,121 @@
|
||||
This guide walks you through building Frappe images from the repository resources.
|
||||
|
||||
# Prerequisites
|
||||
|
||||
- git
|
||||
- docker or podman
|
||||
- docker compose v2 or podman compose
|
||||
|
||||
> Install containerization software according to the official maintainer documentation. Avoid package managers when not recommended, as they frequently cause compatibility issues.
|
||||
|
||||
# Clone this repo
|
||||
|
||||
```bash
|
||||
git clone https://github.com/frappe/frappe_docker
|
||||
cd frappe_docker
|
||||
```
|
||||
|
||||
# Define custom apps
|
||||
|
||||
If you dont want to install specific apps to the image skip this section.
|
||||
|
||||
To include custom apps in your image, create an `apps.json` file in the repository root:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"url": "https://github.com/frappe/erpnext",
|
||||
"branch": "version-15"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/frappe/hrms",
|
||||
"branch": "version-15"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/frappe/helpdesk",
|
||||
"branch": "main"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Then generate a base64-encoded string from this file:
|
||||
|
||||
```bash
|
||||
export APPS_JSON_BASE64=$(base64 -w 0 apps.json)
|
||||
```
|
||||
|
||||
# Build the image
|
||||
|
||||
Choose the appropriate build command based on your container runtime and desired image type. This example builds the `layered` image with the custom `apps.json` you created.
|
||||
|
||||
`Docker`:
|
||||
|
||||
```bash
|
||||
docker build \
|
||||
--build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
|
||||
--build-arg=FRAPPE_BRANCH=version-15 \
|
||||
--build-arg=APPS_JSON_BASE64=$APPS_JSON_BASE64 \
|
||||
--tag=custom:15 \
|
||||
--file=images/layered/Containerfile .
|
||||
```
|
||||
|
||||
`Podman`:
|
||||
|
||||
```bash
|
||||
podman build \
|
||||
--build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
|
||||
--build-arg=FRAPPE_BRANCH=version-15 \
|
||||
--build-arg=APPS_JSON_BASE64=$APPS_JSON_BASE64 \
|
||||
--tag=custom:15 \
|
||||
--file=images/layered/Containerfile .
|
||||
```
|
||||
|
||||
## Build args
|
||||
|
||||
| Arg | Purpose |
|
||||
| -------------------- | --------------------------------------------------------------------------------------------- |
|
||||
| **Frappe Framework** | |
|
||||
| FRAPPE_PATH | Repository URL for Frappe framework source code. Defaults to https://github.com/frappe/frappe |
|
||||
| FRAPPE_BRANCH | Branch to use for Frappe framework. Defaults to version-15 |
|
||||
| **Custom Apps** | |
|
||||
| APPS_JSON_BASE64 | Base64-encoded JSON string from apps.json defining apps to install |
|
||||
| **Dependencies** | |
|
||||
| PYTHON_VERSION | Python version for the base image |
|
||||
| NODE_VERSION | Node.js version |
|
||||
| WKHTMLTOPDF_VERSION | wkhtmltopdf version |
|
||||
| **bench only** | |
|
||||
| DEBIAN_BASE | Debian base version for the bench image, defaults to `bookworm` |
|
||||
| WKHTMLTOPDF_DISTRO | use the specified distro for debian package. Default is `bookworm` |
|
||||
|
||||
# env file
|
||||
|
||||
The compose file requires several environment variables. You can either export them on your system or create a `.env` file.
|
||||
|
||||
```bash
|
||||
cp example.env custom.env
|
||||
```
|
||||
|
||||
Edit `custom.env` to customize variables for your setup. The template includes common variables, but you can add, modify, or remove any as needed. See [env-variables.md](env-variables.md) for detailed descriptions of all available variables.
|
||||
|
||||
# Creating the final compose file
|
||||
|
||||
Combine the base compose file with appropriate overrides for your use case. This example adds MariaDB, Redis, and exposes ports on `:8080`:
|
||||
|
||||
```bash
|
||||
docker compose --env.file example.env \
|
||||
-f compose.yaml \
|
||||
-f overrides/compose.mariadb.yaml \
|
||||
-f overrides/compose.redis.yaml \
|
||||
-f overrides/compose.noproxy.yaml \
|
||||
config > compose.custom.yaml
|
||||
```
|
||||
|
||||
This generates `compose.custom.yaml`, which you'll use to start all containers. Customize the overrides and environment variables according to your requirements.
|
||||
|
||||
> **NOTE**: podman compose is just a wrapper, it uses docker-compose if it is available or podman-compose if not. podman-compose have an issue reading .env files ([Issue](https://github.com/containers/podman-compose/issues/475)) and might create an issue when running the containers.
|
||||
|
||||
---
|
||||
|
||||
**Next:** [Start Setup →](03-start-setup.md)
|
||||
|
||||
**Back:** [Container Overview ←](01-overview.md)
|
||||
42
docs/container-setup/03-start-setup.md
Normal file
42
docs/container-setup/03-start-setup.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# start Container
|
||||
|
||||
Once your compose file is ready, start all containers with a single command:
|
||||
|
||||
```bash
|
||||
docker compose -p frappe -f compose.custom.yaml up -d
|
||||
```
|
||||
|
||||
```bash
|
||||
podman-compose --in-pod=1 --project-name frappe -f compose.custom.yaml up -d
|
||||
```
|
||||
|
||||
The `-p` (or `--project-name`) flag names the project `frappe`, allowing you to easily reference and manage all containers together.
|
||||
|
||||
# Create a site and install apps
|
||||
|
||||
Frappe is now running, but it's not yet configured. You need to create a site and install your apps.
|
||||
|
||||
```bash
|
||||
docker compose -p frappe exec backend bench new-site <sitename> --mariadb-user-host-login-scope='172.%.%.%'
|
||||
docker compose -p frappe exec backend bench --site <sitename> install-app erpnext
|
||||
```
|
||||
|
||||
```bash
|
||||
podman exec -ti erpnext_backend_1 /bin/bash
|
||||
bench new-site <sitename> --mariadb-user-host-login-scope='172.%.%.%'
|
||||
bench --site <sitename> install-app erpnext
|
||||
```
|
||||
|
||||
Replace `<sitename>` with your desired site name.
|
||||
|
||||
> ## Understanding the MariaDB User Scope
|
||||
>
|
||||
> The flag --mariadb-user-host-login-scope='172.%.%.%' allows database connections from any IP address within the 172.0.0.0/8 range. This includes all containers and virtual machines running on your machine.
|
||||
>
|
||||
> **Why is this necessary?** Docker and Podman assign dynamic IP addresses to containers. If you set a fixed IP address instead, database connections will fail when the container restarts and receives a new IP. The wildcard pattern ensures connections always work, regardless of IP changes.
|
||||
>
|
||||
> **Security note:** This scope is sufficient because only the backend container accesses the database. If you need external database access, adjust the scope accordingly, but be cautious with overly permissive settings.
|
||||
|
||||
---
|
||||
|
||||
**Back:** [Build Setup →](02-build-setup.md)
|
||||
112
docs/container-setup/env-variables.md
Normal file
112
docs/container-setup/env-variables.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# Environment Variables Reference
|
||||
|
||||
Environment variables configure your Frappe Docker setup. They can be set directly in the container or defined in a `.env` file referenced by Docker Compose.
|
||||
|
||||
**Getting Started:**
|
||||
|
||||
```bash
|
||||
cp example.env .env
|
||||
```
|
||||
|
||||
Then edit `.env` and set variables according to your needs.
|
||||
|
||||
---
|
||||
|
||||
## Required Variables
|
||||
|
||||
| Variable | Purpose | Example | Notes |
|
||||
| ----------------- | ------------------------------------------------ | -------------------------------- | ---------------------------------------------------------------- |
|
||||
| `FRAPPE_PATH` | Frappe framework path | https://github.com/frappe/frappe | |
|
||||
| `FRAPPE_BRANCH` | Frappe Branch | `version-15` | See [Frappe releases](https://github.com/frappe/frappe/releases) |
|
||||
| `ERPNEXT_VERSION` | ERPNext release version | `v15.67.0` | Required although its never used |
|
||||
| `DB_PASSWORD` | Password for database root (MariaDB or Postgres) | `secure_password_123` | Not needed if using `DB_PASSWORD_SECRETS_FILE` |
|
||||
|
||||
---
|
||||
|
||||
## Database Configuration
|
||||
|
||||
| Variable | Purpose | Default | When to Set |
|
||||
| -------------------------- | ----------------------------------------- | ------------------------------------ | ---------------------------------- |
|
||||
| `DB_PASSWORD` | Database root user password | 123 | Always (unless using secrets file) |
|
||||
| `DB_PASSWORD_SECRETS_FILE` | Path to file containing database password | — | Setup mariadb-secrets overrider |
|
||||
| `DB_HOST` | Database hostname or IP | `db` (service name) | Only if using external database |
|
||||
| `DB_PORT` | Database port | `3306` (MariaDB) / `5432` (Postgres) | Only if using external database |
|
||||
|
||||
---
|
||||
|
||||
## Redis Configuration
|
||||
|
||||
| Variable | Purpose | Default | When to Set |
|
||||
| ------------- | --------------------------------------------------- | ---------------------------- | ------------------------------------- |
|
||||
| `REDIS_CACHE` | Redis hostname for caching | `redis-cache` (service name) | Only if using external Redis instance |
|
||||
| `REDIS_QUEUE` | Redis hostname for job queues and real-time updates | `redis-queue` (service name) | Only if using external Redis instance |
|
||||
|
||||
---
|
||||
|
||||
## HTTPS & SSL Configuration
|
||||
|
||||
| Variable | Purpose | Default | When to Set |
|
||||
| ------------------- | ------------------------------------------------ | ------- | ---------------------------------------- |
|
||||
| `LETSENCRYPT_EMAIL` | Email for Let's Encrypt certificate registration | — | Required if using HTTPS override |
|
||||
| `SITES` | List of domains for SSL certificates | — | Required if using reverse proxy override |
|
||||
|
||||
**Format for `SITES`:**
|
||||
|
||||
```bash
|
||||
# Single site
|
||||
SITES=`mysite.example.com`
|
||||
|
||||
# Wildcard (any subdomain)
|
||||
SITES=`{any:.+}`
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Site Configuration
|
||||
|
||||
| Variable | Purpose | Default | When to Set |
|
||||
| ------------------------- | -------------------------------- | ---------------------------------------- | ----------------------------------------------- |
|
||||
| `FRAPPE_SITE_NAME_HEADER` | Site name for multi-tenant setup | `$host` (resolved from request hostname) | When accessing by IP or need explicit site name |
|
||||
|
||||
**Examples:**
|
||||
|
||||
If your site is named `mysite` but you want to access it via `127.0.0.1`:
|
||||
|
||||
```bash
|
||||
FRAPPE_SITE_NAME_HEADER=mysite
|
||||
```
|
||||
|
||||
If your site is named `example.com` and you access it via that domain, no need to set this (defaults to hostname).
|
||||
|
||||
---
|
||||
|
||||
## Image Configuration
|
||||
|
||||
| Variable | Purpose | Default | Notes |
|
||||
| ---------------- | ------------------------------ | --------------------- | ------------------------------------------------------- |
|
||||
| `CUSTOM_IMAGE` | Custom Docker image repository | Frappe official image | Leave empty to use default |
|
||||
| `CUSTOM_TAG` | Custom Docker image tag | Latest stable | Corresponds to `FRAPPE_VERSION` |
|
||||
| `PULL_POLICY` | Image pull behavior | `always` | Options: `always`, `never`, `if-not-present` |
|
||||
| `RESTART_POLICY` | Container restart behavior | `unless-stopped` | Options: `no`, `always`, `unless-stopped`, `on-failure` |
|
||||
|
||||
---
|
||||
|
||||
## Nginx Proxy Configuration
|
||||
|
||||
| Variable | Purpose | Default | Allowed Values |
|
||||
| ---------------------- | ---------------------------------- | -------------- | -------------------------------------------- |
|
||||
| `BACKEND` | Backend service address and port | `0.0.0.0:8000` | `{host}:{port}` |
|
||||
| `SOCKETIO` | Socket.IO service address and port | `0.0.0.0:9000` | `{host}:{port}` |
|
||||
| `HTTP_PUBLISH_PORT` | Published HTTP port | `8080` | Any available port |
|
||||
| `PROXY_READ_TIMEOUT` | Upstream request timeout | `120s` | Any nginx timeout value (e.g., `300s`, `5m`) |
|
||||
| `CLIENT_MAX_BODY_SIZE` | Maximum upload file size | `50m` | Any nginx size value (e.g., `100m`, `1g`) |
|
||||
|
||||
### Real IP Configuration (Behind Proxy)
|
||||
|
||||
Use these variables when running behind a reverse proxy or load balancer:
|
||||
|
||||
| Variable | Purpose | Default |
|
||||
| ---------------------------- | ------------------------------------------------- | ----------------- |
|
||||
| `UPSTREAM_REAL_IP_ADDRESS` | Trusted upstream IP address for real IP detection | `127.0.0.1` |
|
||||
| `UPSTREAM_REAL_IP_HEADER` | Request header containing client IP | `X-Forwarded-For` |
|
||||
| `UPSTREAM_REAL_IP_RECURSIVE` | Enable recursive IP search | `off` |
|
||||
27
docs/container-setup/overrider.md
Normal file
27
docs/container-setup/overrider.md
Normal file
@@ -0,0 +1,27 @@
|
||||
Overrides extend the base compose.yaml with additional services or modify existing behavior. Include them in your compose command using multiple -f flags.
|
||||
|
||||
```bash
|
||||
docker compose -f compose.yaml -f overrides/compose.mariadb.yaml -f overrides/compose.redis.yaml config > compose.custom.yaml
|
||||
```
|
||||
|
||||
| Overrider | Purpose | Additional Info |
|
||||
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| **Database** | | |
|
||||
| compose.mariadb.yaml | Adds MariaDB database service | set `DB_PASSWORD` or default Password will be used |
|
||||
| compose.mariadb-secrets.yaml | Adds MariaDB with password from a secret file instead of environment variable | Set `DB_PASSWORD_SECRETS_FILE` to the path of your secret file |
|
||||
| compose.mariadb-shared.yaml | Makes MariaDB available on a shared network (mariadb-network) for other services | set `DB_PASSWORD` |
|
||||
| compose.postgres.yaml | Uses PostgreSQL instead of MariaDB as the database | set `DB_PASSWORD` |
|
||||
| **Proxy** | | |
|
||||
| compose.noproxy.yaml | Exposes the application directly on port `:8080` without a reverse proxy | |
|
||||
| compose.proxy.yaml | Uses Traefik as HTTP reverse proxy on port `:80` | You can change the published port by setting `HTTP_PUBLISH_PORT` |
|
||||
| compose.https.yaml | Uses Traefik as HTTPS reverse proxy on Port `:443` with automatic HTTP-to-HTTPS redirect | `SITES` and `LETSENCRYPT_EMAIL` must be set. `HTTP_PUBLISH_PORT` and `HTTPS_PUBLISH_PORT` can be set. |
|
||||
| **Redis** | | |
|
||||
| compose.redis.yaml | Adds Redis service for caching and background job queuing |
|
||||
| **TBD** | **The following overrides are available but lack documentation. If you use them and understand their purpose, please consider contributing to this documentation.** |
|
||||
| compose.backup-cron.yaml | | |
|
||||
| compose.custom-domain-ssl.yaml | | |
|
||||
| compose.custom-domain.yaml | | |
|
||||
| compose.multi-bench-ssl.yaml | | |
|
||||
| compose.multi-bench.yaml | | |
|
||||
| compose.traefik-ssl.yaml | | |
|
||||
| compose.traefik.yaml | | |
|
||||
@@ -14,9 +14,9 @@ It is recommended you allocate at least 4GB of RAM to docker:
|
||||
- [Instructions for macOS](https://docs.docker.com/desktop/settings/mac/#advanced)
|
||||
|
||||
Here is a screenshot showing the relevant setting in the Help Manual
|
||||

|
||||

|
||||
Here is a screenshot showing the settings in Docker Desktop on Mac
|
||||

|
||||

|
||||
|
||||
## Bootstrap Containers for development
|
||||
|
||||
@@ -41,23 +41,23 @@ cp -R development/vscode-example development/.vscode
|
||||
|
||||
## Use VSCode Remote Containers extension
|
||||
|
||||
For most people getting started with Frappe development, the best solution is to use [VSCode Remote - Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers).
|
||||
For most people getting started with Frappe development, the best solution is to use [VSCode Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers).
|
||||
|
||||
Before opening the folder in container, determine the database that you want to use. The default is MariaDB.
|
||||
If you want to use PostgreSQL instead, edit `.devcontainer/docker-compose.yml` and uncomment the section for `postgresql` service, and you may also want to comment `mariadb` as well.
|
||||
|
||||
VSCode should automatically inquire you to install the required extensions, that can also be installed manually as follows:
|
||||
|
||||
- Install Remote - Containers for VSCode
|
||||
- Install Dev Containers for VSCode
|
||||
- through command line `code --install-extension ms-vscode-remote.remote-containers`
|
||||
- clicking on the Install button in the Vistual Studio Marketplace: [Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
|
||||
- clicking on the Install button in the Vistual Studio Marketplace: [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
|
||||
- View: Extensions command in VSCode (Windows: Ctrl+Shift+X; macOS: Cmd+Shift+X) then search for extension `ms-vscode-remote.remote-containers`
|
||||
|
||||
After the extensions are installed, you can:
|
||||
|
||||
- Open frappe_docker folder in VS Code.
|
||||
- `code .`
|
||||
- Launch the command, from Command Palette (Ctrl + Shift + P) `Remote-Containers: Reopen in Container`. You can also click in the bottom left corner to access the remote container menu.
|
||||
- Launch the command, from Command Palette (Ctrl + Shift + P) `Dev Containers: Reopen in Container`. You can also click in the bottom left corner to access the remote container menu.
|
||||
|
||||
Notes:
|
||||
|
||||
@@ -70,6 +70,8 @@ Notes:
|
||||
|
||||
Run the following commands in the terminal inside the container. You might need to create a new terminal in VSCode.
|
||||
|
||||
NOTE: Prior to doing the following, make sure the user is **frappe**.
|
||||
|
||||
```shell
|
||||
bench init --skip-redis-config-generation frappe-bench
|
||||
cd frappe-bench
|
||||
@@ -82,16 +84,16 @@ To setup frappe framework version 14 bench set `PYENV_VERSION` environment varia
|
||||
bench init --skip-redis-config-generation --frappe-branch version-14 frappe-bench
|
||||
# Or set environment versions explicitly
|
||||
nvm use v16
|
||||
PYENV_VERSION=3.10.5 bench init --skip-redis-config-generation --frappe-branch version-14 frappe-bench
|
||||
PYENV_VERSION=3.10.13 bench init --skip-redis-config-generation --frappe-branch version-14 frappe-bench
|
||||
# Switch directory
|
||||
cd frappe-bench
|
||||
```
|
||||
|
||||
To setup frappe framework version 13 bench set `PYENV_VERSION` environment variable to `3.9.9` and use NodeJS version 14,
|
||||
To setup frappe framework version 13 bench set `PYENV_VERSION` environment variable to `3.9.17` and use NodeJS version 14,
|
||||
|
||||
```shell
|
||||
nvm use v14
|
||||
PYENV_VERSION=3.9.9 bench init --skip-redis-config-generation --frappe-branch version-13 frappe-bench
|
||||
PYENV_VERSION=3.9.17 bench init --skip-redis-config-generation --frappe-branch version-13 frappe-bench
|
||||
cd frappe-bench
|
||||
```
|
||||
|
||||
@@ -103,7 +105,7 @@ We need to tell bench to use the right containers instead of localhost. Run the
|
||||
bench set-config -g db_host mariadb
|
||||
bench set-config -g redis_cache redis://redis-cache:6379
|
||||
bench set-config -g redis_queue redis://redis-queue:6379
|
||||
bench set-config -g redis_socketio redis://redis-socketio:6379
|
||||
bench set-config -g redis_socketio redis://redis-queue:6379
|
||||
```
|
||||
|
||||
For any reason the above commands fail, set the values in `common_site_config.json` manually.
|
||||
@@ -113,7 +115,7 @@ For any reason the above commands fail, set the values in `common_site_config.js
|
||||
"db_host": "mariadb",
|
||||
"redis_cache": "redis://redis-cache:6379",
|
||||
"redis_queue": "redis://redis-queue:6379",
|
||||
"redis_socketio": "redis://redis-socketio:6379"
|
||||
"redis_socketio": "redis://redis-queue:6379"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -142,7 +144,7 @@ sed -i '/redis/d' ./Procfile
|
||||
You can create a new site with the following command:
|
||||
|
||||
```shell
|
||||
bench new-site sitename --no-mariadb-socket
|
||||
bench new-site --mariadb-user-host-login-scope=% sitename
|
||||
```
|
||||
|
||||
sitename MUST end with .localhost for trying deployments locally.
|
||||
@@ -150,18 +152,18 @@ sitename MUST end with .localhost for trying deployments locally.
|
||||
for example:
|
||||
|
||||
```shell
|
||||
bench new-site mysite.localhost --no-mariadb-socket
|
||||
bench new-site --mariadb-user-host-login-scope=% development.localhost
|
||||
```
|
||||
|
||||
The same command can be run non-interactively as well:
|
||||
|
||||
```shell
|
||||
bench new-site mysite.localhost --mariadb-root-password 123 --admin-password admin --no-mariadb-socket
|
||||
bench new-site --db-root-password 123 --admin-password admin --mariadb-user-host-login-scope=% development.localhost
|
||||
```
|
||||
|
||||
The command will ask the MariaDB root password. The default root password is `123`.
|
||||
This will create a new site and a `mysite.localhost` directory under `frappe-bench/sites`.
|
||||
The option `--no-mariadb-socket` will configure site's database credentials to work with docker.
|
||||
This will create a new site and a `development.localhost` directory under `frappe-bench/sites`.
|
||||
The option `--mariadb-user-host-login-scope=%` will configure site's database credentials to work with docker.
|
||||
You may need to configure your system /etc/hosts if you're on Linux, Mac, or its Windows equivalent.
|
||||
|
||||
To setup site with PostgreSQL as database use option `--db-type postgres` and `--db-host postgresql`. (Available only v12 onwards, currently NOT available for ERPNext).
|
||||
@@ -169,7 +171,7 @@ To setup site with PostgreSQL as database use option `--db-type postgres` and `-
|
||||
Example:
|
||||
|
||||
```shell
|
||||
bench new-site mypgsql.localhost --db-type postgres --db-host postgresql
|
||||
bench new-site --db-type postgres --db-host postgresql mypgsql.localhost
|
||||
```
|
||||
|
||||
To avoid entering postgresql username and root password, set it in `common_site_config.json`,
|
||||
@@ -186,8 +188,8 @@ Note: If PostgreSQL is not required, the postgresql service / container can be s
|
||||
To develop a new app, the last step will be setting the site into developer mode. Documentation is available at [this link](https://frappe.io/docs/user/en/guides/app-development/how-enable-developer-mode-in-frappe).
|
||||
|
||||
```shell
|
||||
bench --site mysite.localhost set-config developer_mode 1
|
||||
bench --site mysite.localhost clear-cache
|
||||
bench --site development.localhost set-config developer_mode 1
|
||||
bench --site development.localhost clear-cache
|
||||
```
|
||||
|
||||
### Install an app
|
||||
@@ -201,24 +203,29 @@ To install custom app
|
||||
```shell
|
||||
# --branch is optional, use it to point to branch on custom app repository
|
||||
bench get-app --branch version-12 https://github.com/myusername/myapp
|
||||
bench --site mysite.localhost install-app myapp
|
||||
bench --site development.localhost install-app myapp
|
||||
```
|
||||
|
||||
At the time of this writing, the Payments app has been factored out of the Version 14 ERPNext app and is now a separate app. ERPNext will not install without it, however, so we need to specify `--resolve-deps` command line switch to install it.
|
||||
At the time of this writing, the Payments app has been factored out of the Version 14 ERPNext app and is now a separate app. ERPNext will not install it.
|
||||
|
||||
```shell
|
||||
bench get-app --branch version-14 --resolve-deps erpnext
|
||||
bench --site mysite.localhost install-app erpnext
|
||||
bench --site development.localhost install-app erpnext
|
||||
```
|
||||
|
||||
To install ERPNext (from the version-13 branch):
|
||||
|
||||
```shell
|
||||
bench get-app --branch version-13 erpnext
|
||||
bench --site mysite.localhost install-app erpnext
|
||||
bench --site development.localhost install-app erpnext
|
||||
```
|
||||
|
||||
Note: Both frappe and erpnext must be on branch with same name. e.g. version-14
|
||||
You can use the `switch-to-branch` command to align versions if you get an error about mismatching versions.
|
||||
|
||||
```shell
|
||||
bench switch-to-branch version-xx
|
||||
```
|
||||
|
||||
### Start Frappe without debugging
|
||||
|
||||
@@ -229,42 +236,59 @@ bench start
|
||||
```
|
||||
|
||||
You can now login with user `Administrator` and the password you choose when creating the site.
|
||||
Your website will now be accessible at location [mysite.localhost:8000](http://mysite.localhost:8000)
|
||||
Your website will now be accessible at location [development.localhost:8000](http://development.localhost:8000)
|
||||
Note: To start bench with debugger refer section for debugging.
|
||||
|
||||
### Setup bench / new site using script
|
||||
|
||||
Most developers work with numerous clients and versions. Moreover, apps may be required to be installed by everyone on the team working for a client.
|
||||
|
||||
This is simplified using a script to automate the process of creating a new bench / site and installing the required apps.
|
||||
This is simplified using a script to automate the process of creating a new bench / site and installing the required apps. The `Administrator` password for created sites is `admin`.
|
||||
|
||||
Create a copy of apps-example.json and name it apps.json
|
||||
|
||||
```shell
|
||||
cp apps-example.json apps.json
|
||||
```
|
||||
|
||||
Maintain a directory of all client apps in apps.json. Note that Maintaining a fork is optional in apps.json. Also `name` should be app name in apps.json (could be different from repo name).
|
||||
Sample `apps-example.json` is used by default, it installs erpnext on current stable release. To install custom apps, copy the `apps-example.json` to custom json file and make changes to list of apps. Pass this file to the `installer.py` script.
|
||||
|
||||
> You may have apps in private repos which may require ssh access. You may use SSH from your home directory on linux (configurable in docker-compose.yml).
|
||||
|
||||
After you have created apps.json, run the following command:
|
||||
|
||||
```shell
|
||||
bash installer.sh
|
||||
python installer.py #pass --db-type postgres for postgresdb
|
||||
```
|
||||
|
||||
The script will ask for the following information:
|
||||
For command help
|
||||
|
||||
- Client name (from apps.json).
|
||||
- Bench directory name. If you enter existing bench directory name, it will create a new site in that bench. Else it will create a new bench and site.
|
||||
- Site name (should end with `.localhost`).
|
||||
```shell
|
||||
python installer.py --help
|
||||
usage: installer.py [-h] [-j APPS_JSON] [-b BENCH_NAME] [-s SITE_NAME] [-r FRAPPE_REPO] [-t FRAPPE_BRANCH] [-p PY_VERSION] [-n NODE_VERSION] [-v] [-a ADMIN_PASSWORD] [-d DB_TYPE]
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
-j APPS_JSON, --apps-json APPS_JSON
|
||||
Path to apps.json, default: apps-example.json
|
||||
-b BENCH_NAME, --bench-name BENCH_NAME
|
||||
Bench directory name, default: frappe-bench
|
||||
-s SITE_NAME, --site-name SITE_NAME
|
||||
Site name, should end with .localhost, default: development.localhost
|
||||
-r FRAPPE_REPO, --frappe-repo FRAPPE_REPO
|
||||
frappe repo to use, default: https://github.com/frappe/frappe
|
||||
-t FRAPPE_BRANCH, --frappe-branch FRAPPE_BRANCH
|
||||
frappe repo to use, default: version-15
|
||||
-p PY_VERSION, --py-version PY_VERSION
|
||||
python version, default: Not Set
|
||||
-n NODE_VERSION, --node-version NODE_VERSION
|
||||
node version, default: Not Set
|
||||
-v, --verbose verbose output
|
||||
-a ADMIN_PASSWORD, --admin-password ADMIN_PASSWORD
|
||||
admin password for site, default: admin
|
||||
-d DB_TYPE, --db-type DB_TYPE
|
||||
Database type to use (e.g., mariadb or postgres)
|
||||
```
|
||||
|
||||
A new bench and / or site is created for the client with following defaults.
|
||||
|
||||
- MariaDB root password: `123`
|
||||
- Admin password: `admin`
|
||||
|
||||
> To use Postegres DB, comment the mariabdb service and uncomment postegres service.
|
||||
|
||||
### Start Frappe with Visual Studio Code Python Debugging
|
||||
|
||||
To enable Python debugging inside Visual Studio Code, you must first install the `ms-python.python` extension inside the container. This should have already happened automatically, but depending on your VSCode config, you can force it by:
|
||||
@@ -282,8 +306,7 @@ honcho start \
|
||||
watch \
|
||||
schedule \
|
||||
worker_short \
|
||||
worker_long \
|
||||
worker_default
|
||||
worker_long
|
||||
```
|
||||
|
||||
Alternatively you can use the VSCode launch configuration "Honcho SocketIO Watch Schedule Worker" which launches the same command as above.
|
||||
@@ -301,7 +324,7 @@ For advance vscode configuration in the devcontainer, change the config files in
|
||||
You can launch a simple interactive shell console in the terminal with:
|
||||
|
||||
```shell
|
||||
bench --site mysite.localhost console
|
||||
bench --site development.localhost console
|
||||
```
|
||||
|
||||
More likely, you may want to launch VSCode interactive console based on Jupyter kernel.
|
||||
@@ -316,12 +339,12 @@ The first step is installing and updating the required software. Usually the fra
|
||||
|
||||
Then, run the command `Python: Show Python interactive window` from the VSCode command palette.
|
||||
|
||||
Replace `mysite.localhost` with your site and run the following code in a Jupyter cell:
|
||||
Replace `development.localhost` with your site and run the following code in a Jupyter cell:
|
||||
|
||||
```python
|
||||
import frappe
|
||||
|
||||
frappe.init(site='mysite.localhost', sites_path='/workspace/development/frappe-bench/sites')
|
||||
frappe.init(site='development.localhost', sites_path='/workspace/development/frappe-bench/sites')
|
||||
frappe.connect()
|
||||
frappe.local.lang = frappe.db.get_default('lang')
|
||||
frappe.db.connect()
|
||||
@@ -370,3 +393,30 @@ volumes:
|
||||
```
|
||||
|
||||
Access the service by service name from the `frappe` development container. The above service will be accessible via hostname `postgresql`. If ports are published on to host, access it via `localhost:5432`.
|
||||
|
||||
## Using Cypress UI tests
|
||||
|
||||
To run cypress based UI tests in a docker environment, follow the below steps:
|
||||
|
||||
1. Install and setup X11 tooling on VM using the script `install_x11_deps.sh`
|
||||
|
||||
```shell
|
||||
sudo bash ./install_x11_deps.sh
|
||||
```
|
||||
|
||||
This script will install required deps, enable X11Forwarding and restart SSH daemon and export `DISPLAY` variable.
|
||||
|
||||
2. Run X11 service `startx` or `xquartz`
|
||||
3. Start docker compose services.
|
||||
4. SSH into ui-tester service using `docker exec..` command
|
||||
5. Export CYPRESS_baseUrl and other required env variables
|
||||
6. Start Cypress UI console by issuing `cypress run command`
|
||||
|
||||
> More references : [Cypress Official Documentation](https://www.cypress.io/blog/2019/05/02/run-cypress-with-a-single-docker-command)
|
||||
|
||||
> Ensure DISPLAY environment is always exported.
|
||||
|
||||
## Using Mailpit to test mail services
|
||||
|
||||
To use Mailpit just uncomment the service in the docker-compose.yml file.
|
||||
The Interface is then available under port 8025 and the smtp service can be used as mailpit:1025.
|
||||
12
docs/error-nginx-entrypoint-windows.md
Normal file
12
docs/error-nginx-entrypoint-windows.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Resolving Docker `nginx-entrypoint.sh` Script Not Found Error on Windows
|
||||
|
||||
If you're encountering the error `exec /usr/local/bin/nginx-entrypoint.sh: no such file or directory` in a Docker container on Windows, follow these steps to resolve the issue.
|
||||
|
||||
## 1. Check Line Endings
|
||||
|
||||
On Windows, files often have `CRLF` line endings, while Linux systems expect `LF`. This can cause issues when executing shell scripts in Linux containers.
|
||||
|
||||
- **Convert Line Endings using `dos2unix`:**
|
||||
```bash
|
||||
dos2unix resources/nginx-entrypoint.sh
|
||||
```
|
||||
@@ -1,101 +0,0 @@
|
||||
# Images
|
||||
|
||||
There's 4 images that you can find in `/images` directory:
|
||||
|
||||
- `bench`. It is used for development. [Learn more how to start development](../development/README.md).
|
||||
- `nginx`. This image contains JS and CSS assets. Container using this image also routes incoming requests using [nginx](https://www.nginx.com).
|
||||
- `socketio`. Container using this image processes realtime websocket requests using [Socket.IO](https://socket.io).
|
||||
- `worker`. Multi-purpose Python backend. Runs [Werkzeug server](https://werkzeug.palletsprojects.com/en/2.0.x/) with [gunicorn](https://gunicorn.org), queues (via `bench worker`), or schedule (via `bench schedule`).
|
||||
|
||||
> `nginx`, `socketio` and `worker` images — everything we need to be able to run all processes that Frappe framework requires (take a look at [Bench Procfile reference](https://frappeframework.com/docs/v13/user/en/bench/resources/bench-procfile)). We follow [Docker best practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#decouple-applications) and split these processes to different containers.
|
||||
|
||||
> ERPNext images don't have their own Dockerfiles. We use [multi-stage builds](https://docs.docker.com/develop/develop-images/multistage-build/) and [Docker Buildx](https://docs.docker.com/engine/reference/commandline/buildx/) to reuse as much things as possible and make our builds more efficient.
|
||||
|
||||
# Compose files
|
||||
|
||||
After building the images we have to run the containers. The best and simplest way to do this is to use [compose files](https://docs.docker.com/compose/compose-file/).
|
||||
|
||||
We have one main compose file, `compose.yaml`. Services described, networking, volumes are also handled there.
|
||||
|
||||
## Services
|
||||
|
||||
All services are described in `compose.yaml`
|
||||
|
||||
- `configurator`. Updates `common_site_config.json` so Frappe knows how to access db and redis. It is executed on every `docker-compose up` (and exited immediately). Other services start after this container exits successfully.
|
||||
- `backend`. [Werkzeug server](https://werkzeug.palletsprojects.com/en/2.0.x/).
|
||||
- `db`. Optional service that runs [MariaDB](https://mariadb.com) if you also use `overrides/compose.mariadb.yaml` or [Postgres](https://www.postgresql.org) if you also use `overrides/compose.postgres.yaml`.
|
||||
- `redis`. Optional service that runs [Redis](https://redis.io) server with cache, [Socket.IO](https://socket.io) and queues data.
|
||||
- `frontend`. [nginx](https://www.nginx.com) server that serves JS/CSS assets and routes incoming requests.
|
||||
- `proxy`. [Traefik](https://traefik.io/traefik/) proxy. It is here for complicated setups or HTTPS override (with `overrides/compose.https.yaml`).
|
||||
- `websocket`. Node server that runs [Socket.IO](https://socket.io).
|
||||
- `queue-short`, `queue-default`, `queue-long`. Python servers that run job queues using [rq](https://python-rq.org).
|
||||
- `scheduler`. Python server that runs tasks on schedule using [schedule](https://schedule.readthedocs.io/en/stable/).
|
||||
|
||||
## Overrides
|
||||
|
||||
We have several [overrides](https://docs.docker.com/compose/extends/):
|
||||
|
||||
- `overrides/compose.proxy.yaml`. Adds traefik proxy to setup.
|
||||
- `overrides/compose.noproxy.yaml`. Publishes `frontend` ports directly without any proxy.
|
||||
- `overrides/compose.erpnext.yaml`. Replaces all Frappe images with ERPNext ones. ERPNext images are built on top of Frappe ones, so it is safe to replace them.
|
||||
- `overrides/compose.https.yaml`. Automatically sets up Let's Encrypt certificate and redirects all requests to directed to http, to https.
|
||||
- `overrides/compose.mariadb.yaml`. Adds `db` service and sets its image to MariaDB.
|
||||
- `overrides/compose.postgres.yaml`. Adds `db` service and sets its image to Postgres. Note that ERPNext currently doesn't support Postgres.
|
||||
- `overrides/compose.redis.yaml`. Adds `redis` service and sets its image to `redis`.
|
||||
|
||||
It is quite simple to run overrides. All we need to do is to specify compose files that should be used by docker-compose. For example, we want ERPNext:
|
||||
|
||||
```bash
|
||||
# Point to main compose file (compose.yaml) and add one more.
|
||||
docker-compose -f compose.yaml -f overrides/compose.erpnext.yaml config
|
||||
```
|
||||
|
||||
⚠ Make sure to use docker-compose v2 (run `docker-compose -v` to check). If you want to use v1 make sure the correct `$`-signs as they get duplicated by the `config` command!
|
||||
|
||||
That's it! Of course, we also have to setup `.env` before all of that, but that's not the point.
|
||||
|
||||
## Configuration
|
||||
|
||||
We use environment variables to configure our setup. docker-compose uses variables from `.env` file. To get started, copy `example.env` to `.env`.
|
||||
|
||||
### `FRAPPE_VERSION`
|
||||
|
||||
Frappe framework release. You can find all releases [here](https://github.com/frappe/frappe/releases).
|
||||
|
||||
### `DB_PASSWORD`
|
||||
|
||||
Password for MariaDB (or Postgres) database.
|
||||
|
||||
### `DB_HOST`
|
||||
|
||||
Hostname for MariaDB (or Postgres) database. Set only if external service for database is used.
|
||||
|
||||
### `DB_PORT`
|
||||
|
||||
Port for MariaDB (3306) or Postgres (5432) database. Set only if external service for database is used.
|
||||
|
||||
### `REDIS_CACHE`
|
||||
|
||||
Hostname for redis server to store cache. Set only if external service for redis is used.
|
||||
|
||||
### `REDIS_QUEUE`
|
||||
|
||||
Hostname for redis server to store queue data. Set only if external service for redis is used.
|
||||
|
||||
### `REDIS_SOCKETIO`
|
||||
|
||||
Hostname for redis server to store socketio data. Set only if external service for redis is used.
|
||||
|
||||
### `ERPNEXT_VERSION`
|
||||
|
||||
ERPNext [release](https://github.com/frappe/frappe/releases). This variable is required if you use ERPNext override.
|
||||
|
||||
### `LETSENCRYPT_EMAIL`
|
||||
|
||||
Email that used to register https certificate. This one is required only if you use HTTPS override.
|
||||
|
||||
### `FRAPPE_SITE_NAME_HEADER`
|
||||
|
||||
This environment variable is not required. Default value is `$$host` which resolves site by host. For example, if your host is `example.com`, site's name should be `example.com`, or if host is `127.0.0.1` (local debugging), it should be `127.0.0.1` This variable allows to override described behavior. Let's say you create site named `mysite` and do want to access it by `127.0.0.1` host. Than you would set this variable to `mysite`.
|
||||
|
||||
There is other variables not mentioned here. They're somewhat internal and you don't have to worry about them except you want to change main compose file.
|
||||
112
docs/migrate-from-multi-image-setup.md
Normal file
112
docs/migrate-from-multi-image-setup.md
Normal file
@@ -0,0 +1,112 @@
|
||||
## Migrate from multi-image setup
|
||||
|
||||
All the containers now use same image. Use `frappe/erpnext` instead of `frappe/frappe-worker`, `frappe/frappe-nginx` , `frappe/frappe-socketio` , `frappe/erpnext-worker` and `frappe/erpnext-nginx`.
|
||||
|
||||
Now you need to specify command and environment variables for following containers:
|
||||
|
||||
### Frontend
|
||||
|
||||
For `frontend` service to act as static assets frontend and reverse proxy, you need to pass `nginx-entrypoint.sh` as container `command` and `BACKEND` and `SOCKETIO` environment variables pointing `{host}:{port}` for gunicorn and websocket services. Check [environment variables](environment-variables.md)
|
||||
|
||||
Now you only need to mount the `sites` volume at location `/home/frappe/frappe-bench/sites`. No need for `assets` volume and asset population script or steps.
|
||||
|
||||
Example change:
|
||||
|
||||
```yaml
|
||||
# ... removed for brevity
|
||||
frontend:
|
||||
image: frappe/erpnext:${ERPNEXT_VERSION:?ERPNext version not set}
|
||||
command:
|
||||
- nginx-entrypoint.sh
|
||||
environment:
|
||||
BACKEND: backend:8000
|
||||
SOCKETIO: websocket:9000
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
# ... removed for brevity
|
||||
```
|
||||
|
||||
### Websocket
|
||||
|
||||
For `websocket` service to act as socketio backend, you need to pass `["node", "/home/frappe/frappe-bench/apps/frappe/socketio.js"]` as container `command`
|
||||
|
||||
Example change:
|
||||
|
||||
```yaml
|
||||
# ... removed for brevity
|
||||
websocket:
|
||||
image: frappe/erpnext:${ERPNEXT_VERSION:?ERPNext version not set}
|
||||
command:
|
||||
- node
|
||||
- /home/frappe/frappe-bench/apps/frappe/socketio.js
|
||||
# ... removed for brevity
|
||||
```
|
||||
|
||||
### Configurator
|
||||
|
||||
For `configurator` service to act as run once configuration job, you need to pass `["bash", "-c"]` as container `entrypoint` and bash script inline to yaml. There is no `configure.py` in the container now.
|
||||
|
||||
Example change:
|
||||
|
||||
```yaml
|
||||
# ... removed for brevity
|
||||
configurator:
|
||||
image: frappe/erpnext:${ERPNEXT_VERSION:?ERPNext version not set}
|
||||
restart: "no"
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
command:
|
||||
- >
|
||||
bench set-config -g db_host $$DB_HOST;
|
||||
bench set-config -gp db_port $$DB_PORT;
|
||||
bench set-config -g redis_cache "redis://$$REDIS_CACHE";
|
||||
bench set-config -g redis_queue "redis://$$REDIS_QUEUE";
|
||||
bench set-config -gp socketio_port $$SOCKETIO_PORT;
|
||||
environment:
|
||||
DB_HOST: db
|
||||
DB_PORT: "3306"
|
||||
REDIS_CACHE: redis-cache:6379
|
||||
REDIS_QUEUE: redis-queue:6379
|
||||
SOCKETIO_PORT: "9000"
|
||||
# ... removed for brevity
|
||||
```
|
||||
|
||||
### Site Creation
|
||||
|
||||
For `create-site` service to act as run once site creation job, you need to pass `["bash", "-c"]` as container `entrypoint` and bash script inline to yaml. Make sure to use `--mariadb-user-host-login-scope=%` as upstream bench is installed in container.
|
||||
|
||||
The `WORKDIR` has changed to `/home/frappe/frappe-bench` like `bench` setup we are used to. So the path to find `common_site_config.json` has changed to `sites/common_site_config.json`.
|
||||
|
||||
Example change:
|
||||
|
||||
```yaml
|
||||
# ... removed for brevity
|
||||
create-site:
|
||||
image: frappe/erpnext:${ERPNEXT_VERSION:?ERPNext version not set}
|
||||
restart: "no"
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
command:
|
||||
- >
|
||||
wait-for-it -t 120 db:3306;
|
||||
wait-for-it -t 120 redis-cache:6379;
|
||||
wait-for-it -t 120 redis-queue:6379;
|
||||
export start=`date +%s`;
|
||||
until [[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".db_host // empty"` ]] && \
|
||||
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_cache // empty"` ]] && \
|
||||
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_queue // empty"` ]];
|
||||
do
|
||||
echo "Waiting for sites/common_site_config.json to be created";
|
||||
sleep 5;
|
||||
if (( `date +%s`-start > 120 )); then
|
||||
echo "could not find sites/common_site_config.json with required keys";
|
||||
exit 1
|
||||
fi
|
||||
done;
|
||||
echo "sites/common_site_config.json found";
|
||||
bench new-site --mariadb-user-host-login-scope=% --admin-password=admin --db-root-password=admin --install-app erpnext --set-default frontend;
|
||||
|
||||
# ... removed for brevity
|
||||
```
|
||||
@@ -1,34 +0,0 @@
|
||||
Example: https://discuss.erpnext.com/t/sms-two-factor-authentication-otp-msg-change/47835
|
||||
|
||||
Above example needs following Dockerfile based patch
|
||||
|
||||
```Dockerfile
|
||||
FROM frappe/erpnext-worker:v12.17.0
|
||||
|
||||
...
|
||||
USER root
|
||||
RUN sed -i -e "s/Your verification code is/আপনার লগইন কোড/g" /home/frappe/frappe-bench/apps/frappe/frappe/twofactor.py
|
||||
USER frappe
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
Example for `nginx` image,
|
||||
|
||||
```Dockerfile
|
||||
FROM frappe/erpnext-nginx:v13.27.0
|
||||
|
||||
# Hack to use Frappe/ERPNext offline.
|
||||
RUN sed -i 's/navigator.onLine/navigator.onLine||true/' \
|
||||
/usr/share/nginx/html/assets/js/desk.min.js \
|
||||
/usr/share/nginx/html/assets/js/dialog.min.js \
|
||||
/usr/share/nginx/html/assets/js/frappe-web.min.js
|
||||
```
|
||||
|
||||
Alternatively copy the modified source code file directly over `/home/frappe/frappe-bench/apps/frappe/frappe/twofactor.py`
|
||||
|
||||
```Dockerfile
|
||||
...
|
||||
COPY twofactor.py /home/frappe/frappe-bench/apps/frappe/frappe/twofactor.py
|
||||
...
|
||||
```
|
||||
@@ -6,43 +6,64 @@ Remove the traefik service from docker-compose.yml
|
||||
|
||||
### Step 2
|
||||
|
||||
Create nginx config file `/opt/nginx/conf/serve-8001.conf`:
|
||||
Add service for each port that needs to be exposed.
|
||||
|
||||
```
|
||||
server {
|
||||
listen 8001;
|
||||
server_name $http_host;
|
||||
e.g. `port-site-1`, `port-site-2`, `port-site-3`.
|
||||
|
||||
location / {
|
||||
|
||||
rewrite ^(.+)/$ $1 permanent;
|
||||
rewrite ^(.+)/index\.html$ $1 permanent;
|
||||
rewrite ^(.+)\.html$ $1 permanent;
|
||||
|
||||
proxy_set_header X-Frappe-Site-Name mysite.localhost;
|
||||
proxy_set_header Host mysite.localhost;
|
||||
proxy_pass http://frontend;
|
||||
}
|
||||
}
|
||||
```yaml
|
||||
# ... removed for brevity
|
||||
services:
|
||||
# ... removed for brevity
|
||||
port-site-1:
|
||||
image: frappe/erpnext:v14.11.1
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
command:
|
||||
- nginx-entrypoint.sh
|
||||
environment:
|
||||
BACKEND: backend:8000
|
||||
FRAPPE_SITE_NAME_HEADER: site1.local
|
||||
SOCKETIO: websocket:9000
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
ports:
|
||||
- "8080:8080"
|
||||
port-site-2:
|
||||
image: frappe/erpnext:v14.11.1
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
command:
|
||||
- nginx-entrypoint.sh
|
||||
environment:
|
||||
BACKEND: backend:8000
|
||||
FRAPPE_SITE_NAME_HEADER: site2.local
|
||||
SOCKETIO: websocket:9000
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
ports:
|
||||
- "8081:8080"
|
||||
port-site-3:
|
||||
image: frappe/erpnext:v14.11.1
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
command:
|
||||
- nginx-entrypoint.sh
|
||||
environment:
|
||||
BACKEND: backend:8000
|
||||
FRAPPE_SITE_NAME_HEADER: site3.local
|
||||
SOCKETIO: websocket:9000
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
ports:
|
||||
- "8082:8080"
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- Replace the port with any port of choice e.g. `listen 4200;`
|
||||
- Change `mysite.localhost` to site name
|
||||
- Repeat the server blocks for multiple ports and site names to get the effect of port based multi tenancy
|
||||
- For old images use `proxy_pass http://erpnext-nginx` instead of `proxy_pass http://frontend`
|
||||
|
||||
### Step 3
|
||||
|
||||
Run the docker container
|
||||
|
||||
```shell
|
||||
docker run --network=<project-name>_default \
|
||||
-p 8001:8001 \
|
||||
--volume=/opt/nginx/conf/serve-8001.conf:/etc/nginx/conf.d/default.conf -d nginx
|
||||
```
|
||||
|
||||
Note: Change the volumes, network and ports as needed
|
||||
|
||||
With the above example configured site will be accessible on `http://localhost:8001`
|
||||
- Above setup will expose `site1.local`, `site2.local`, `site3.local` on port `8080`, `8081`, `8082` respectively.
|
||||
- Change `site1.local` to site name to serve from bench.
|
||||
- Change the `BACKEND` and `SOCKETIO` environment variables as per your service names.
|
||||
- Make sure `sites:` volume is available as part of yaml.
|
||||
|
||||
@@ -17,7 +17,7 @@ Copy the example docker environment file to `.env`:
|
||||
cp example.env .env
|
||||
```
|
||||
|
||||
Note: To know more about environment variable [read here](./images-and-compose-files.md#configuration). Set the necessary variables in the `.env` file.
|
||||
Note: To know more about environment variable [read here](./environment-variables.md). Set the necessary variables in the `.env` file.
|
||||
|
||||
## Generate docker-compose.yml for variety of setups
|
||||
|
||||
@@ -40,8 +40,7 @@ Instead of `docker compose config`, you can directly use `docker compose up` to
|
||||
|
||||
### Setup Frappe without proxy and external MariaDB and Redis
|
||||
|
||||
In this case make sure you've set `DB_HOST`, `DB_PORT`, `REDIS_CACHE`, `REDIS_QUEUE` and `REDIS_SOCKETIO`
|
||||
environment variables or the `configurator` will fail.
|
||||
In this case make sure you've set `DB_HOST`, `DB_PORT`, `REDIS_CACHE` and `REDIS_QUEUE` environment variables or the `configurator` will fail.
|
||||
|
||||
```sh
|
||||
# Generate YAML
|
||||
@@ -53,14 +52,12 @@ docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -
|
||||
|
||||
### Setup ERPNext with proxy and external MariaDB and Redis
|
||||
|
||||
In this case make sure you've set `DB_HOST`, `DB_PORT`, `REDIS_CACHE`, `REDIS_QUEUE` and `REDIS_SOCKETIO`
|
||||
environment variables or the `configurator` will fail.
|
||||
In this case make sure you've set `DB_HOST`, `DB_PORT`, `REDIS_CACHE` and `REDIS_QUEUE` environment variables or the `configurator` will fail.
|
||||
|
||||
```sh
|
||||
# Generate YAML
|
||||
docker compose -f compose.yaml \
|
||||
-f overrides/compose.proxy.yaml \
|
||||
-f overrides/compose.erpnext.yaml \
|
||||
config > ~/gitops/docker-compose.yml
|
||||
|
||||
# Start containers
|
||||
@@ -69,6 +66,8 @@ docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -
|
||||
|
||||
### Setup Frappe using containerized MariaDB and Redis with Letsencrypt certificates.
|
||||
|
||||
In this case make sure you've set `LETSENCRYPT_EMAIL` and `SITES` environment variables are set or certificates won't work.
|
||||
|
||||
```sh
|
||||
# Generate YAML
|
||||
docker compose -f compose.yaml \
|
||||
@@ -83,10 +82,11 @@ docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -
|
||||
|
||||
### Setup ERPNext using containerized MariaDB and Redis with Letsencrypt certificates.
|
||||
|
||||
In this case make sure you've set `LETSENCRYPT_EMAIL` and `SITES` environment variables are set or certificates won't work.
|
||||
|
||||
```sh
|
||||
# Generate YAML
|
||||
docker compose -f compose.yaml \
|
||||
-f overrides/compose.erpnext.yaml \
|
||||
-f overrides/compose.mariadb.yaml \
|
||||
-f overrides/compose.redis.yaml \
|
||||
-f overrides/compose.https.yaml \
|
||||
@@ -110,20 +110,22 @@ nano .env
|
||||
|
||||
# Pull new images
|
||||
docker compose -f compose.yaml \
|
||||
-f overrides/compose.erpnext.yaml \
|
||||
# ... your other overrides
|
||||
config > ~/gitops/docker-compose.yml
|
||||
|
||||
# Pull images
|
||||
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml pull
|
||||
|
||||
# Stop containers
|
||||
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml down
|
||||
|
||||
# Remove assets volume for repopulation
|
||||
docker volume rm <name of assets volume>
|
||||
|
||||
# Restart containers
|
||||
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -d
|
||||
```
|
||||
|
||||
Note:
|
||||
|
||||
- pull and stop container commands can be skipped if immutable image tags are used
|
||||
- `docker compose up -d` will pull new immutable tags if not found.
|
||||
|
||||
To migrate sites refer [site operations](./site-operations.md#migrate-site)
|
||||
|
||||
226
docs/setup_for_linux_mac.md
Normal file
226
docs/setup_for_linux_mac.md
Normal file
@@ -0,0 +1,226 @@
|
||||
# How to install ERPNext on linux/mac using Frappe_docker ?
|
||||
|
||||
step1: clone the repo
|
||||
|
||||
```
|
||||
git clone https://github.com/frappe/frappe_docker
|
||||
```
|
||||
|
||||
step2: add platform: linux/amd64 to all services in the /pwd.yaml
|
||||
|
||||
here is the update pwd.yml file
|
||||
|
||||
```yml
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
backend:
|
||||
image: frappe/erpnext:v15
|
||||
platform: linux/amd64
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
|
||||
configurator:
|
||||
image: frappe/erpnext:v15
|
||||
platform: linux/amd64
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: none
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
# add redis_socketio for backward compatibility
|
||||
command:
|
||||
- >
|
||||
ls -1 apps > sites/apps.txt;
|
||||
bench set-config -g db_host $$DB_HOST;
|
||||
bench set-config -gp db_port $$DB_PORT;
|
||||
bench set-config -g redis_cache "redis://$$REDIS_CACHE";
|
||||
bench set-config -g redis_queue "redis://$$REDIS_QUEUE";
|
||||
bench set-config -g redis_socketio "redis://$$REDIS_QUEUE";
|
||||
bench set-config -gp socketio_port $$SOCKETIO_PORT;
|
||||
environment:
|
||||
DB_HOST: db
|
||||
DB_PORT: "3306"
|
||||
REDIS_CACHE: redis-cache:6379
|
||||
REDIS_QUEUE: redis-queue:6379
|
||||
SOCKETIO_PORT: "9000"
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
|
||||
create-site:
|
||||
image: frappe/erpnext:v15
|
||||
platform: linux/amd64
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: none
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
command:
|
||||
- >
|
||||
wait-for-it -t 120 db:3306;
|
||||
wait-for-it -t 120 redis-cache:6379;
|
||||
wait-for-it -t 120 redis-queue:6379;
|
||||
export start=`date +%s`;
|
||||
until [[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".db_host // empty"` ]] && \
|
||||
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_cache // empty"` ]] && \
|
||||
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_queue // empty"` ]];
|
||||
do
|
||||
echo "Waiting for sites/common_site_config.json to be created";
|
||||
sleep 5;
|
||||
if (( `date +%s`-start > 120 )); then
|
||||
echo "could not find sites/common_site_config.json with required keys";
|
||||
exit 1
|
||||
fi
|
||||
done;
|
||||
echo "sites/common_site_config.json found";
|
||||
bench new-site --mariadb-user-host-login-scope=% --admin-password=admin --db-root-password=admin --install-app erpnext --set-default frontend;
|
||||
|
||||
db:
|
||||
image: mariadb:10.6
|
||||
platform: linux/amd64
|
||||
healthcheck:
|
||||
test: mysqladmin ping -h localhost --password=admin
|
||||
interval: 1s
|
||||
retries: 20
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
command:
|
||||
- --character-set-server=utf8mb4
|
||||
- --collation-server=utf8mb4_unicode_ci
|
||||
- --skip-character-set-client-handshake
|
||||
- --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: admin
|
||||
volumes:
|
||||
- db-data:/var/lib/mysql
|
||||
|
||||
frontend:
|
||||
image: frappe/erpnext:v15
|
||||
platform: linux/amd64
|
||||
depends_on:
|
||||
- websocket
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
command:
|
||||
- nginx-entrypoint.sh
|
||||
environment:
|
||||
BACKEND: backend:8000
|
||||
FRAPPE_SITE_NAME_HEADER: frontend
|
||||
SOCKETIO: websocket:9000
|
||||
UPSTREAM_REAL_IP_ADDRESS: 127.0.0.1
|
||||
UPSTREAM_REAL_IP_HEADER: X-Forwarded-For
|
||||
UPSTREAM_REAL_IP_RECURSIVE: "off"
|
||||
PROXY_READ_TIMEOUT: 120
|
||||
CLIENT_MAX_BODY_SIZE: 50m
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
ports:
|
||||
- "8080:8080"
|
||||
|
||||
queue-long:
|
||||
image: frappe/erpnext:v15
|
||||
platform: linux/amd64
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
command:
|
||||
- bench
|
||||
- worker
|
||||
- --queue
|
||||
- long,default,short
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
|
||||
queue-short:
|
||||
image: frappe/erpnext:v15
|
||||
platform: linux/amd64
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
command:
|
||||
- bench
|
||||
- worker
|
||||
- --queue
|
||||
- short,default
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
|
||||
redis-queue:
|
||||
image: redis:6.2-alpine
|
||||
platform: linux/amd64
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
volumes:
|
||||
- redis-queue-data:/data
|
||||
|
||||
redis-cache:
|
||||
image: redis:6.2-alpine
|
||||
platform: linux/amd64
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
|
||||
scheduler:
|
||||
image: frappe/erpnext:v15
|
||||
platform: linux/amd64
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
command:
|
||||
- bench
|
||||
- schedule
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
|
||||
websocket:
|
||||
image: frappe/erpnext:v15
|
||||
platform: linux/amd64
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
command:
|
||||
- node
|
||||
- /home/frappe/frappe-bench/apps/frappe/socketio.js
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
|
||||
volumes:
|
||||
db-data:
|
||||
redis-queue-data:
|
||||
sites:
|
||||
logs:
|
||||
```
|
||||
|
||||
step3: run the docker
|
||||
|
||||
```
|
||||
cd frappe_docker
|
||||
```
|
||||
|
||||
```
|
||||
docker-compose -f ./pwd.yml up
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Wait for couple of minutes.
|
||||
|
||||
Open localhost:8080
|
||||
38
docs/single-compose-setup.md
Normal file
38
docs/single-compose-setup.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Single Compose Setup
|
||||
|
||||
This setup is a very simple single compose file that does everything to start required services and a frappe-bench. It is used to start play with docker instance with a site. The file is located in the root of repo and named `pwd.yml`.
|
||||
|
||||
## Services
|
||||
|
||||
### frappe-bench components
|
||||
|
||||
- backend, serves gunicorn backend
|
||||
- frontend, serves static assets through nginx frontend reverse proxies websocket and gunicorn.
|
||||
- queue-long, long default and short rq worker.
|
||||
- queue-short, default and short rq worker.
|
||||
- schedule, event scheduler.
|
||||
- websocket, socketio websocket for realtime communication.
|
||||
|
||||
### Run once configuration
|
||||
|
||||
- configurator, configures `common_site_config.json` to set db and redis hosts.
|
||||
- create-site, creates one site to serve as default site for the frappe-bench.
|
||||
|
||||
### Service dependencies
|
||||
|
||||
- db, mariadb, container with frappe specific configuration.
|
||||
- redis-cache, redis for cache data.
|
||||
- redis-queue, redis for rq data and pub/sub.
|
||||
|
||||
## Volumes
|
||||
|
||||
- sites: Volume for bench data. Common config, all sites, all site configs and site files will be stored here.
|
||||
- logs: Volume for bench logs. all process logs are dumped here. No need to mount it. Each container will create a temporary volume for logs if not specified.
|
||||
|
||||
## Adaptation
|
||||
|
||||
If you understand containers use the `pwd.yml` as a reference to build more complex setup like, single server example, Docker Swarm stack, Kubernetes Helm chart, etc.
|
||||
|
||||
This serves only site called `frontend` through the nginx. `FRAPPE_SITE_NAME_HEADER` is set to `frontend` and a default site called `frontend` is created.
|
||||
|
||||
Change the `$$host` will allow container to accept any host header and serve that site. To escape `$` in compose yaml use it like `$$`. To unset default site remove `currentsite.txt` file from `sites` directory.
|
||||
@@ -41,7 +41,7 @@ chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose
|
||||
|
||||
### Prepare
|
||||
|
||||
Clone `frappe_docker` repo for the needed YAMLs and change the current working director of you shell to the cloned repo.
|
||||
Clone `frappe_docker` repo for the needed YAMLs and change the current working directory of your shell to the cloned repo.
|
||||
|
||||
```shell
|
||||
git clone https://github.com/frappe/frappe_docker
|
||||
@@ -65,7 +65,7 @@ Create a file called `traefik.env` in `~/gitops`
|
||||
```shell
|
||||
echo 'TRAEFIK_DOMAIN=traefik.example.com' > ~/gitops/traefik.env
|
||||
echo 'EMAIL=admin@example.com' >> ~/gitops/traefik.env
|
||||
echo 'HASHED_PASSWORD='$(openssl passwd -apr1 changeit | sed 's/\$/\\\$/g') >> ~/gitops/traefik.env
|
||||
echo "HASHED_PASSWORD='$(openssl passwd -apr1 changeit)'" >> ~/gitops/traefik.env
|
||||
```
|
||||
|
||||
Note:
|
||||
@@ -82,18 +82,20 @@ EMAIL=admin@example.com
|
||||
HASHED_PASSWORD=$apr1$K.4gp7RT$tj9R2jHh0D4Gb5o5fIAzm/
|
||||
```
|
||||
|
||||
If Container does not deploy put the HASHED_PASSWORD in ''.
|
||||
|
||||
Deploy the traefik container with letsencrypt SSL
|
||||
|
||||
```shell
|
||||
docker compose --project-name traefik \
|
||||
--env-file ~/gitops/traefik.env \
|
||||
-f docs/compose/compose.traefik.yaml \
|
||||
-f docs/compose/compose.traefik-ssl.yaml up -d
|
||||
-f overrides/compose.traefik.yaml \
|
||||
-f overrides/compose.traefik-ssl.yaml up -d
|
||||
```
|
||||
|
||||
This will make the traefik dashboard available on `traefik.example.com` and all certificates will reside in `/data/traefik/certificates` on host filesystem.
|
||||
This will make the traefik dashboard available on `traefik.example.com` and all certificates will reside in the Docker volume `cert-data`.
|
||||
|
||||
For LAN setup deploy the traefik container without overriding `docs/compose/compose.traefik-ssl.yaml`.
|
||||
For LAN setup deploy the traefik container without overriding `overrides/compose.traefik-ssl.yaml`.
|
||||
|
||||
### Install MariaDB
|
||||
|
||||
@@ -120,7 +122,7 @@ Note: Change the password from `changeit` to more secure one.
|
||||
Deploy the mariadb container
|
||||
|
||||
```shell
|
||||
docker compose --project-name mariadb --env-file ~/gitops/mariadb.env -f docs/compose/compose.mariadb-shared.yaml up -d
|
||||
docker compose --project-name mariadb --env-file ~/gitops/mariadb.env -f overrides/compose.mariadb-shared.yaml up -d
|
||||
```
|
||||
|
||||
This will make `mariadb-database` service available under `mariadb-network`. Data will reside in `/data/mariadb`.
|
||||
@@ -138,8 +140,8 @@ cp example.env ~/gitops/erpnext-one.env
|
||||
sed -i 's/DB_PASSWORD=123/DB_PASSWORD=changeit/g' ~/gitops/erpnext-one.env
|
||||
sed -i 's/DB_HOST=/DB_HOST=mariadb-database/g' ~/gitops/erpnext-one.env
|
||||
sed -i 's/DB_PORT=/DB_PORT=3306/g' ~/gitops/erpnext-one.env
|
||||
sed -i 's/SITES=`erp.example.com`/SITES=\`one.example.com\`,\`two.example.com\`/g' ~/gitops/erpnext-one.env
|
||||
echo 'ROUTER=erpnext-one' >> ~/gitops/erpnext-one.env
|
||||
echo "SITES=\`one.example.com\`,\`two.example.com\`" >> ~/gitops/erpnext-one.env
|
||||
echo "BENCH_NETWORK=erpnext-one" >> ~/gitops/erpnext-one.env
|
||||
```
|
||||
|
||||
@@ -155,10 +157,9 @@ Create a yaml file called `erpnext-one.yaml` in `~/gitops` directory:
|
||||
docker compose --project-name erpnext-one \
|
||||
--env-file ~/gitops/erpnext-one.env \
|
||||
-f compose.yaml \
|
||||
-f overrides/compose.erpnext.yaml \
|
||||
-f overrides/compose.redis.yaml \
|
||||
-f docs/compose/compose.multi-bench.yaml \
|
||||
-f docs/compose/compose.multi-bench-ssl.yaml config > ~/gitops/erpnext-one.yaml
|
||||
-f overrides/compose.multi-bench.yaml \
|
||||
-f overrides/compose.multi-bench-ssl.yaml config > ~/gitops/erpnext-one.yaml
|
||||
```
|
||||
|
||||
For LAN setup do not override `compose.multi-bench-ssl.yaml`.
|
||||
@@ -176,7 +177,7 @@ Create sites `one.example.com` and `two.example.com`:
|
||||
```shell
|
||||
# one.example.com
|
||||
docker compose --project-name erpnext-one exec backend \
|
||||
bench new-site one.example.com --mariadb-root-password changeit --install-app erpnext --admin-password changeit
|
||||
bench new-site --mariadb-user-host-login-scope=% --db-root-password changeit --install-app erpnext --admin-password changeit one.example.com
|
||||
```
|
||||
|
||||
You can stop here and have a single bench single site setup complete. Continue to add one more site to the current bench.
|
||||
@@ -184,7 +185,7 @@ You can stop here and have a single bench single site setup complete. Continue t
|
||||
```shell
|
||||
# two.example.com
|
||||
docker compose --project-name erpnext-one exec backend \
|
||||
bench new-site two.example.com --mariadb-root-password changeit --install-app erpnext --admin-password changeit
|
||||
bench new-site --mariadb-user-host-login-scope=% --db-root-password changeit --install-app erpnext --admin-password changeit two.example.com
|
||||
```
|
||||
|
||||
#### Create second bench
|
||||
@@ -217,10 +218,9 @@ Create a yaml file called `erpnext-two.yaml` in `~/gitops` directory:
|
||||
docker compose --project-name erpnext-two \
|
||||
--env-file ~/gitops/erpnext-two.env \
|
||||
-f compose.yaml \
|
||||
-f overrides/compose.erpnext.yaml \
|
||||
-f overrides/compose.redis.yaml \
|
||||
-f docs/compose/compose.multi-bench.yaml \
|
||||
-f docs/compose/compose.multi-bench-ssl.yaml config > ~/gitops/erpnext-two.yaml
|
||||
-f overrides/compose.multi-bench.yaml \
|
||||
-f overrides/compose.multi-bench-ssl.yaml config > ~/gitops/erpnext-two.yaml
|
||||
```
|
||||
|
||||
Use the above command after any changes are made to `erpnext-two.env` file to regenerate `~/gitops/erpnext-two.yaml`. e.g. after changing version to migrate the bench.
|
||||
@@ -236,10 +236,10 @@ Create sites `three.example.com` and `four.example.com`:
|
||||
```shell
|
||||
# three.example.com
|
||||
docker compose --project-name erpnext-two exec backend \
|
||||
bench new-site three.example.com --mariadb-root-password changeit --install-app erpnext --admin-password changeit
|
||||
bench new-site --mariadb-user-host-login-scope=% --db-root-password changeit --install-app erpnext --admin-password changeit three.example.com
|
||||
# four.example.com
|
||||
docker compose --project-name erpnext-two exec backend \
|
||||
bench new-site four.example.com --mariadb-root-password changeit --install-app erpnext --admin-password changeit
|
||||
bench new-site --mariadb-user-host-login-scope=% --db-root-password changeit --install-app erpnext --admin-password changeit four.example.com
|
||||
```
|
||||
|
||||
#### Create custom domain to existing site
|
||||
@@ -271,8 +271,8 @@ Generate yaml to reverse proxy:
|
||||
```shell
|
||||
docker compose --project-name custom-one-example \
|
||||
--env-file ~/gitops/custom-one-example.env \
|
||||
-f docs/compose/compose.custom-domain.yaml \
|
||||
-f docs/compose/compose.custom-domain-ssl.yaml config > ~/gitops/custom-one-example.yaml
|
||||
-f overrides/compose.custom-domain.yaml \
|
||||
-f overrides/compose.custom-domain-ssl.yaml config > ~/gitops/custom-one-example.yaml
|
||||
```
|
||||
|
||||
For LAN setup do not override `compose.custom-domain-ssl.yaml`.
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
Note:
|
||||
|
||||
- Wait for the `db` service to start and `configurator` to exit before trying to create a new site. Usually this takes up to 10 seconds.
|
||||
- Also you have to pass `-p <project_name>` if `-p` passed previously eg. `docker-compose -p <project_name> exec (rest of the command)`.
|
||||
|
||||
```sh
|
||||
docker-compose exec backend bench new-site <site-name> --mariadb-root-password <db-password> --admin-password <admin-password>
|
||||
docker-compose exec backend bench new-site --mariadb-user-host-login-scope=% --db-root-password <db-password> --admin-password <admin-password> <site-name>
|
||||
```
|
||||
|
||||
If you need to install some app, specify `--install-app`. To see all options, just run `bench new-site --help`.
|
||||
@@ -24,7 +25,7 @@ docker-compose exec backend bench set-config -g root_password <root-password>
|
||||
Also command is slightly different:
|
||||
|
||||
```sh
|
||||
docker-compose exec backend bench new-site <site-name> --db-type postgres --admin-password <admin-password>
|
||||
docker-compose exec backend bench new-site --mariadb-user-host-login-scope=% --db-type postgres --admin-password <admin-password> <site-name>
|
||||
```
|
||||
|
||||
## Push backup to S3 storage
|
||||
|
||||
25
docs/tls-for-local-deployment.md
Normal file
25
docs/tls-for-local-deployment.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Accessing ERPNext through https on local deployment
|
||||
|
||||
- ERPNext container deployment can be accessed through https easily using Caddy web server, Caddy will be used as reverse proxy and forward traffics to the frontend container.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Caddy
|
||||
- Adding a domain name to hosts file
|
||||
|
||||
#### Installation of caddy webserver
|
||||
|
||||
- Follow the official Caddy website for the installation guide https://caddyserver.com/docs/install
|
||||
After completing the installation open the configuration file of Caddy ( You find the config file in ` /etc/caddy/Caddyfile`), add the following configuration to forward traffics to the ERPNext frontend container
|
||||
|
||||
```js
|
||||
erp.localdev.net {
|
||||
tls internal
|
||||
|
||||
reverse_proxy localhost:8085 {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- Caddy's root certificate must be added to other computers if computers from different networks access the ERPNext through https.
|
||||
@@ -1,13 +1,21 @@
|
||||
1. [Fixing MariaDB issues after rebuilding the container](#fixing-mariadb-issues-after-rebuilding-the-container)
|
||||
1. [Letsencrypt companion not working](#letsencrypt-companion-not-working)
|
||||
1. [docker-compose does not recognize variables from `.env` file](#docker-compose-does-not-recognize-variables-from-env-file)
|
||||
1. [Windows Based Installation](#windows-based-installation)
|
||||
|
||||
### Fixing MariaDB issues after rebuilding the container
|
||||
|
||||
For any reason after rebuilding the container if you are not be able to access MariaDB correctly with the previous configuration. Follow these instructions.
|
||||
For any reason after rebuilding the container if you are not be able to access MariaDB correctly (i.e. `Access denied for user [...]`) with the previous configuration. Follow these instructions.
|
||||
|
||||
The parameter `'db_name'@'%'` needs to be set in MariaDB and permission to the site database suitably assigned to the user.
|
||||
First test for network issues. Manually connect to the database through the `backend` container:
|
||||
|
||||
```
|
||||
docker exec -it frappe_docker-backend-1 bash
|
||||
mysql -uroot -padmin -hdb
|
||||
```
|
||||
|
||||
Replace `root` with the database root user name, `admin` with the root password, and `db` with the service name specified in the docker-compose `.yml` configuration file. If the connection to the database is successful, then the network configuration is correct and you can proceed to the next step. Otherwise, modify the docker-compose `.yml` configuration file, in the `configurator` service's `environment` section, to use the container names (`frappe_docker-db-1`, `frappe_docker-redis-cache-1`, `frappe_docker-redis-queue-1` or as otherwise shown with `docker ps`) instead of the service names and rebuild the containers.
|
||||
|
||||
Then, the parameter `'db_name'@'%'` needs to be set in MariaDB and permission to the site database suitably assigned to the user.
|
||||
|
||||
This step has to be repeated for all sites available under the current bench.
|
||||
Example shows the queries to be executed for site `localhost`
|
||||
@@ -23,35 +31,49 @@ and take note of the parameters `db_name` and `db_password`.
|
||||
Enter MariaDB Interactive shell:
|
||||
|
||||
```shell
|
||||
mysql -uroot -p123 -hmariadb
|
||||
mysql -uroot -padmin -hdb
|
||||
```
|
||||
|
||||
Execute following queries replacing `db_name` and `db_password` with the values found in site_config.json.
|
||||
The parameter `'db_name'@'%'` must not be duplicated. Verify that it is unique with the command:
|
||||
|
||||
```
|
||||
SELECT User, Host FROM mysql.user;
|
||||
```
|
||||
|
||||
Delete duplicated entries, if found, with the following:
|
||||
|
||||
```
|
||||
DROP USER 'db_name'@'host';
|
||||
```
|
||||
|
||||
Modify permissions by executing following queries replacing `db_name` and `db_password` with the values found in site_config.json.
|
||||
|
||||
```sql
|
||||
UPDATE mysql.user SET Host = '%' where User = 'db_name'; FLUSH PRIVILEGES;
|
||||
-- if there is no user created already first try to created it using the next command
|
||||
-- CREATE USER 'db_name'@'%' IDENTIFIED BY 'your_password';
|
||||
-- skip the upgrade command below if you use the create command above
|
||||
UPDATE mysql.global_priv SET Host = '%' where User = 'db_name'; FLUSH PRIVILEGES;
|
||||
SET PASSWORD FOR 'db_name'@'%' = PASSWORD('db_password'); FLUSH PRIVILEGES;
|
||||
GRANT ALL PRIVILEGES ON `db_name`.* TO 'db_name'@'%'; FLUSH PRIVILEGES;
|
||||
GRANT ALL PRIVILEGES ON `db_name`.* TO 'db_name'@'%' IDENTIFIED BY 'db_password' WITH GRANT OPTION; FLUSH PRIVILEGES;
|
||||
EXIT;
|
||||
```
|
||||
|
||||
Note: For MariaDB 10.4 and above use `mysql.global_priv` instead of `mysql.user`.
|
||||
|
||||
### Letsencrypt companion not working
|
||||
|
||||
- Nginx Letsencrypt Companion needs to be setup before starting ERPNext services.
|
||||
- Are domain names in `SITES` variable correct?
|
||||
- Is DNS record configured? `A Name` record needs to point to Public IP of server.
|
||||
- Try Restarting containers.
|
||||
Note: For MariaDB 10.3 and older use `mysql.user` instead of `mysql.global_priv`.
|
||||
|
||||
### docker-compose does not recognize variables from `.env` file
|
||||
|
||||
If you are using old version of `docker-compose` the .env file needs to be located in directory from where the docker-compose command is executed. There may also be difference in official `docker-compose` and the one packaged by distro.
|
||||
If you are using old version of `docker-compose` the .env file needs to be located in directory from where the docker-compose command is executed. There may also be difference in official `docker-compose` and the one packaged by distro. Use `--env-file=.env` if available to explicitly specify the path to file.
|
||||
|
||||
### Windows Based Installation
|
||||
|
||||
- Set environment variable `COMPOSE_CONVERT_WINDOWS_PATHS` e.g. `set COMPOSE_CONVERT_WINDOWS_PATHS=1`
|
||||
- Make the `frappe-mariadb.cnf` read-only for mariadb container to pick it up.
|
||||
- While using docker machine, port-forward the port 80 of VM to port 80 of host machine
|
||||
- While using docker machine, port-forward the ports of VM to ports of host machine. (ports 8080/8000/9000)
|
||||
- Name all the sites ending with `.localhost`. and access it via browser locally. e.g. `http://site1.localhost`
|
||||
- related issue comment https://github.com/frappe/frappe_docker/issues/448#issuecomment-851723912
|
||||
|
||||
### Redo installation
|
||||
|
||||
- If you have made changes and just want to start over again (abandoning all changes), remove all docker
|
||||
- containers
|
||||
- images
|
||||
- volumes
|
||||
- Install a fresh
|
||||
|
||||
1
encoded-apps-b64.txt
Normal file
1
encoded-apps-b64.txt
Normal file
@@ -0,0 +1 @@
|
||||
WwogICAgewogICAgICAgICJ1cmwiOiAiaHR0cHM6Ly9naXRodWIuY29tL2ZyYXBwZS9lcnBuZXh0IiwKICAgICAgICAiYnJhbmNoIjogInZlcnNpb24tMTUiCiAgICB9LAogICAgewogICAgICAgICJ1cmwiOiAiaHR0cHM6Ly9naXRodWIuY29tL2ZyYXBwZS9ocm1zIiwKICAgICAgICAiYnJhbmNoIjogInZlcnNpb24tMTUiCiAgICB9LAogICAgewogICAgICAgICJ1cmwiOiAiaHR0cHM6Ly9naXRodWIuY29tL2ZyYXBwZS9oZWxwZGVzayIsCiAgICAgICAgImJyYW5jaCI6ICJtYWluIgogICAgfSwKICAgIHsKICAgICAgICAidXJsIjogImh0dHBzOi8vZ2l0aHViLmNvbS9mcmFwcGUvcGF5bWVudHMiLAogICAgICAgICJicmFuY2giOiAidmVyc2lvbi0xNSIKICAgIH0KXQ==
|
||||
23
example.env
23
example.env
@@ -1,12 +1,12 @@
|
||||
# Reference: https://github.com/frappe/frappe_docker/blob/main/docs/images-and-compose-files.md
|
||||
# Reference: https://github.com/frappe/frappe_docker/blob/main/docs/environment-variables.md
|
||||
|
||||
FRAPPE_VERSION=v14.22.0
|
||||
|
||||
# Only with ERPNext override
|
||||
ERPNEXT_VERSION=v14.12.1
|
||||
ERPNEXT_VERSION=v15.83.0
|
||||
|
||||
DB_PASSWORD=123
|
||||
|
||||
#Only if you use docker secrets for the db password
|
||||
DB_PASSWORD_SECRETS_FILE=
|
||||
|
||||
# Only if you use external database
|
||||
DB_HOST=
|
||||
DB_PORT=
|
||||
@@ -14,10 +14,9 @@ DB_PORT=
|
||||
# Only if you use external Redis
|
||||
REDIS_CACHE=
|
||||
REDIS_QUEUE=
|
||||
REDIS_SOCKETIO=
|
||||
|
||||
# Only with HTTPS override
|
||||
LETSENCRYPT_EMAIL=mail@example.com
|
||||
LETSENCRYPT_EMAIL=admin@khaeli.com
|
||||
|
||||
# These environment variables are not required.
|
||||
|
||||
@@ -27,6 +26,9 @@ LETSENCRYPT_EMAIL=mail@example.com
|
||||
# and do want to access it by `127.0.0.1` host. Than you would set this variable to `mysite`.
|
||||
FRAPPE_SITE_NAME_HEADER=
|
||||
|
||||
# Default value is `8080`.
|
||||
HTTP_PUBLISH_PORT=
|
||||
|
||||
# Default value is `127.0.0.1`. Set IP address as our trusted upstream address.
|
||||
UPSTREAM_REAL_IP_ADDRESS=
|
||||
|
||||
@@ -41,8 +43,13 @@ UPSTREAM_REAL_IP_RECURSIVE=
|
||||
|
||||
# All Values Allowed by nginx proxy_read_timeout are allowed, default value is 120s
|
||||
# Useful if you have longrunning print formats or slow loading sites
|
||||
PROXY_READ_TIMOUT=
|
||||
PROXY_READ_TIMEOUT=
|
||||
|
||||
# All Values allowed by nginx client_max_body_size are allowed, default value is 50m
|
||||
# Necessary if the upload limit in the frappe application is increased
|
||||
CLIENT_MAX_BODY_SIZE=
|
||||
|
||||
# List of sites for letsencrypt certificates quoted with backtick (`) and separated by comma (,)
|
||||
# More https://doc.traefik.io/traefik/routing/routers/#rule
|
||||
# About acme https://doc.traefik.io/traefik/https/acme/#domain-definition
|
||||
SITES=`erp.khaeli.com`,`hr.khaeli.com`,`helpdesk.khaeli.com`,`payments.khaeli.com`
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
FROM debian:bullseye-slim as bench
|
||||
FROM debian:bookworm-slim AS bench
|
||||
|
||||
LABEL author=frappé
|
||||
|
||||
ARG GIT_REPO=https://github.com/frappe/bench.git
|
||||
ARG GIT_BRANCH=develop
|
||||
ARG GIT_BRANCH=v5.x
|
||||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
|
||||
@@ -18,6 +18,11 @@ RUN apt-get update \
|
||||
fonts-cantarell \
|
||||
xfonts-75dpi \
|
||||
xfonts-base \
|
||||
# weasyprint dependencies
|
||||
libpango-1.0-0 \
|
||||
libharfbuzz0b \
|
||||
libpangoft2-1.0-0 \
|
||||
libpangocairo-1.0-0 \
|
||||
# to work inside the container
|
||||
locales \
|
||||
build-essential \
|
||||
@@ -42,6 +47,7 @@ RUN apt-get update \
|
||||
libsasl2-dev \
|
||||
libtiff5-dev \
|
||||
libwebp-dev \
|
||||
pkg-config \
|
||||
redis-tools \
|
||||
rlwrap \
|
||||
tk8.6-dev \
|
||||
@@ -64,16 +70,18 @@ RUN apt-get update \
|
||||
xz-utils \
|
||||
tk-dev \
|
||||
liblzma-dev \
|
||||
file \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen \
|
||||
&& dpkg-reconfigure --frontend=noninteractive locales
|
||||
|
||||
# Detect arch and install wkhtmltopdf
|
||||
ENV WKHTMLTOPDF_VERSION 0.12.6-1
|
||||
ARG WKHTMLTOPDF_VERSION=0.12.6.1-3
|
||||
ARG WKHTMLTOPDF_DISTRO=bookworm
|
||||
RUN if [ "$(uname -m)" = "aarch64" ]; then export ARCH=arm64; fi \
|
||||
&& if [ "$(uname -m)" = "x86_64" ]; then export ARCH=amd64; fi \
|
||||
&& downloaded_file=wkhtmltox_$WKHTMLTOPDF_VERSION.buster_${ARCH}.deb \
|
||||
&& downloaded_file=wkhtmltox_${WKHTMLTOPDF_VERSION}.${WKHTMLTOPDF_DISTRO}_${ARCH}.deb \
|
||||
&& wget -q https://github.com/wkhtmltopdf/packaging/releases/download/$WKHTMLTOPDF_VERSION/$downloaded_file \
|
||||
&& dpkg -i $downloaded_file \
|
||||
&& rm $downloaded_file
|
||||
@@ -89,25 +97,25 @@ USER frappe
|
||||
WORKDIR /home/frappe
|
||||
|
||||
# Install Python via pyenv
|
||||
ENV PYTHON_VERSION_V13=3.9.9
|
||||
ENV PYTHON_VERSION=3.10.5
|
||||
ENV PYENV_ROOT /home/frappe/.pyenv
|
||||
ENV PATH $PYENV_ROOT/shims:$PYENV_ROOT/bin:$PATH
|
||||
ENV PYTHON_VERSION_V14=3.10.13
|
||||
ENV PYTHON_VERSION=3.11.6
|
||||
ENV PYENV_ROOT=/home/frappe/.pyenv
|
||||
ENV PATH=$PYENV_ROOT/shims:$PYENV_ROOT/bin:$PATH
|
||||
|
||||
# From https://github.com/pyenv/pyenv#basic-github-checkout
|
||||
RUN git clone --depth 1 https://github.com/pyenv/pyenv.git .pyenv \
|
||||
&& pyenv install $PYTHON_VERSION_V13 \
|
||||
&& pyenv install $PYTHON_VERSION_V14 \
|
||||
&& pyenv install $PYTHON_VERSION \
|
||||
&& PYENV_VERSION=$PYTHON_VERSION_V13 pip install --no-cache-dir virtualenv \
|
||||
&& PYENV_VERSION=$PYTHON_VERSION_V14 pip install --no-cache-dir virtualenv \
|
||||
&& PYENV_VERSION=$PYTHON_VERSION pip install --no-cache-dir virtualenv \
|
||||
&& pyenv global $PYTHON_VERSION $PYTHON_VERSION_v13 \
|
||||
&& pyenv global $PYTHON_VERSION $PYTHON_VERSION_v14 \
|
||||
&& sed -Ei -e '/^([^#]|$)/ {a export PYENV_ROOT="/home/frappe/.pyenv" a export PATH="$PYENV_ROOT/bin:$PATH" a ' -e ':a' -e '$!{n;ba};}' ~/.profile \
|
||||
&& echo 'eval "$(pyenv init --path)"' >>~/.profile \
|
||||
&& echo 'eval "$(pyenv init -)"' >>~/.bashrc
|
||||
|
||||
# Clone and install bench in the local user home directory
|
||||
# For development, bench source is located in ~/.bench
|
||||
ENV PATH /home/frappe/.local/bin:$PATH
|
||||
ENV PATH=/home/frappe/.local/bin:$PATH
|
||||
# Skip editable-bench warning
|
||||
# https://github.com/frappe/bench/commit/20560c97c4246b2480d7358c722bc9ad13606138
|
||||
RUN git clone ${GIT_REPO} --depth 1 -b ${GIT_BRANCH} .bench \
|
||||
@@ -116,12 +124,12 @@ RUN git clone ${GIT_REPO} --depth 1 -b ${GIT_BRANCH} .bench \
|
||||
&& echo "export BENCH_DEVELOPER=1" >>/home/frappe/.bashrc
|
||||
|
||||
# Install Node via nvm
|
||||
ENV NODE_VERSION_14=14.19.3
|
||||
ENV NODE_VERSION=16.18.0
|
||||
ENV NVM_DIR /home/frappe/.nvm
|
||||
ENV PATH ${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}
|
||||
ENV NODE_VERSION_14=16.20.2
|
||||
ENV NODE_VERSION=20.19.2
|
||||
ENV NVM_DIR=/home/frappe/.nvm
|
||||
ENV PATH=${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}
|
||||
|
||||
RUN wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash \
|
||||
RUN wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash \
|
||||
&& . ${NVM_DIR}/nvm.sh \
|
||||
&& nvm install ${NODE_VERSION_14} \
|
||||
&& nvm use v${NODE_VERSION_14} \
|
||||
@@ -138,7 +146,7 @@ RUN wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh |
|
||||
|
||||
EXPOSE 8000-8005 9000-9005 6787
|
||||
|
||||
FROM bench as bench-test
|
||||
FROM bench AS bench-test
|
||||
|
||||
# Print version and verify bashrc is properly sourced so that everything works
|
||||
# in the interactive shell and Dockerfile
|
||||
|
||||
159
images/custom/Containerfile
Normal file
159
images/custom/Containerfile
Normal file
@@ -0,0 +1,159 @@
|
||||
ARG PYTHON_VERSION=3.11.6
|
||||
ARG DEBIAN_BASE=bookworm
|
||||
FROM python:${PYTHON_VERSION}-slim-${DEBIAN_BASE} AS base
|
||||
|
||||
COPY resources/nginx-template.conf /templates/nginx/frappe.conf.template
|
||||
COPY resources/nginx-entrypoint.sh /usr/local/bin/nginx-entrypoint.sh
|
||||
|
||||
ARG WKHTMLTOPDF_VERSION=0.12.6.1-3
|
||||
ARG WKHTMLTOPDF_DISTRO=bookworm
|
||||
ARG NODE_VERSION=20.19.2
|
||||
ENV NVM_DIR=/home/frappe/.nvm
|
||||
ENV PATH=${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}
|
||||
|
||||
RUN useradd -ms /bin/bash frappe \
|
||||
&& apt-get update \
|
||||
&& apt-get install --no-install-recommends -y \
|
||||
curl \
|
||||
git \
|
||||
vim \
|
||||
nginx \
|
||||
gettext-base \
|
||||
file \
|
||||
# weasyprint dependencies
|
||||
libpango-1.0-0 \
|
||||
libharfbuzz0b \
|
||||
libpangoft2-1.0-0 \
|
||||
libpangocairo-1.0-0 \
|
||||
# For backups
|
||||
restic \
|
||||
gpg \
|
||||
# MariaDB
|
||||
mariadb-client \
|
||||
less \
|
||||
# Postgres
|
||||
libpq-dev \
|
||||
postgresql-client \
|
||||
# For healthcheck
|
||||
wait-for-it \
|
||||
jq \
|
||||
# NodeJS
|
||||
&& mkdir -p ${NVM_DIR} \
|
||||
&& curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash \
|
||||
&& . ${NVM_DIR}/nvm.sh \
|
||||
&& nvm install ${NODE_VERSION} \
|
||||
&& nvm use v${NODE_VERSION} \
|
||||
&& npm install -g yarn \
|
||||
&& nvm alias default v${NODE_VERSION} \
|
||||
&& rm -rf ${NVM_DIR}/.cache \
|
||||
&& echo 'export NVM_DIR="/home/frappe/.nvm"' >>/home/frappe/.bashrc \
|
||||
&& echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm' >>/home/frappe/.bashrc \
|
||||
&& echo '[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion' >>/home/frappe/.bashrc \
|
||||
# Install wkhtmltopdf with patched qt
|
||||
&& if [ "$(uname -m)" = "aarch64" ]; then export ARCH=arm64; fi \
|
||||
&& if [ "$(uname -m)" = "x86_64" ]; then export ARCH=amd64; fi \
|
||||
&& downloaded_file=wkhtmltox_${WKHTMLTOPDF_VERSION}.${WKHTMLTOPDF_DISTRO}_${ARCH}.deb \
|
||||
&& curl -sLO https://github.com/wkhtmltopdf/packaging/releases/download/$WKHTMLTOPDF_VERSION/$downloaded_file \
|
||||
&& apt-get install -y ./$downloaded_file \
|
||||
&& rm $downloaded_file \
|
||||
# Clean up
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& rm -fr /etc/nginx/sites-enabled/default \
|
||||
&& pip3 install frappe-bench \
|
||||
# Fixes for non-root nginx and logs to stdout
|
||||
&& sed -i '/user www-data/d' /etc/nginx/nginx.conf \
|
||||
&& ln -sf /dev/stdout /var/log/nginx/access.log && ln -sf /dev/stderr /var/log/nginx/error.log \
|
||||
&& touch /run/nginx.pid \
|
||||
&& chown -R frappe:frappe /etc/nginx/conf.d \
|
||||
&& chown -R frappe:frappe /etc/nginx/nginx.conf \
|
||||
&& chown -R frappe:frappe /var/log/nginx \
|
||||
&& chown -R frappe:frappe /var/lib/nginx \
|
||||
&& chown -R frappe:frappe /run/nginx.pid \
|
||||
&& chmod 755 /usr/local/bin/nginx-entrypoint.sh \
|
||||
&& chmod 644 /templates/nginx/frappe.conf.template
|
||||
|
||||
FROM base AS builder
|
||||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
|
||||
# For frappe framework
|
||||
wget \
|
||||
#for building arm64 binaries
|
||||
libcairo2-dev \
|
||||
libpango1.0-dev \
|
||||
libjpeg-dev \
|
||||
libgif-dev \
|
||||
librsvg2-dev \
|
||||
# For psycopg2
|
||||
libpq-dev \
|
||||
# Other
|
||||
libffi-dev \
|
||||
liblcms2-dev \
|
||||
libldap2-dev \
|
||||
libmariadb-dev \
|
||||
libsasl2-dev \
|
||||
libtiff5-dev \
|
||||
libwebp-dev \
|
||||
pkg-config \
|
||||
redis-tools \
|
||||
rlwrap \
|
||||
tk8.6-dev \
|
||||
cron \
|
||||
# For pandas
|
||||
gcc \
|
||||
build-essential \
|
||||
libbz2-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# apps.json includes
|
||||
ARG APPS_JSON_BASE64
|
||||
RUN if [ -n "${APPS_JSON_BASE64}" ]; then \
|
||||
mkdir /opt/frappe && echo "${APPS_JSON_BASE64}" | base64 -d > /opt/frappe/apps.json; \
|
||||
fi
|
||||
|
||||
USER frappe
|
||||
|
||||
ARG FRAPPE_BRANCH=version-15
|
||||
ARG FRAPPE_PATH=https://github.com/frappe/frappe
|
||||
RUN export APP_INSTALL_ARGS="" && \
|
||||
if [ -n "${APPS_JSON_BASE64}" ]; then \
|
||||
export APP_INSTALL_ARGS="--apps_path=/opt/frappe/apps.json"; \
|
||||
fi && \
|
||||
bench init ${APP_INSTALL_ARGS}\
|
||||
--frappe-branch=${FRAPPE_BRANCH} \
|
||||
--frappe-path=${FRAPPE_PATH} \
|
||||
--no-procfile \
|
||||
--no-backups \
|
||||
--skip-redis-config-generation \
|
||||
--verbose \
|
||||
/home/frappe/frappe-bench && \
|
||||
cd /home/frappe/frappe-bench && \
|
||||
echo "{}" > sites/common_site_config.json && \
|
||||
find apps -mindepth 1 -path "*/.git" | xargs rm -fr
|
||||
|
||||
FROM base AS backend
|
||||
|
||||
USER frappe
|
||||
|
||||
COPY --from=builder --chown=frappe:frappe /home/frappe/frappe-bench /home/frappe/frappe-bench
|
||||
|
||||
WORKDIR /home/frappe/frappe-bench
|
||||
|
||||
VOLUME [ \
|
||||
"/home/frappe/frappe-bench/sites", \
|
||||
"/home/frappe/frappe-bench/sites/assets", \
|
||||
"/home/frappe/frappe-bench/logs" \
|
||||
]
|
||||
|
||||
CMD [ \
|
||||
"/home/frappe/frappe-bench/env/bin/gunicorn", \
|
||||
"--chdir=/home/frappe/frappe-bench/sites", \
|
||||
"--bind=0.0.0.0:8000", \
|
||||
"--threads=4", \
|
||||
"--workers=2", \
|
||||
"--worker-class=gthread", \
|
||||
"--worker-tmp-dir=/dev/shm", \
|
||||
"--timeout=120", \
|
||||
"--preload", \
|
||||
"frappe.app:application" \
|
||||
]
|
||||
58
images/layered/Containerfile
Normal file
58
images/layered/Containerfile
Normal file
@@ -0,0 +1,58 @@
|
||||
ARG FRAPPE_BRANCH=version-15
|
||||
|
||||
FROM frappe/build:${FRAPPE_BRANCH} AS builder
|
||||
|
||||
ARG FRAPPE_BRANCH=version-15
|
||||
ARG FRAPPE_PATH=https://github.com/frappe/frappe
|
||||
ARG APPS_JSON_BASE64
|
||||
|
||||
USER root
|
||||
|
||||
RUN if [ -n "${APPS_JSON_BASE64}" ]; then \
|
||||
mkdir /opt/frappe && echo "${APPS_JSON_BASE64}" | base64 -d > /opt/frappe/apps.json; \
|
||||
fi
|
||||
|
||||
USER frappe
|
||||
|
||||
RUN export APP_INSTALL_ARGS="" && \
|
||||
if [ -n "${APPS_JSON_BASE64}" ]; then \
|
||||
export APP_INSTALL_ARGS="--apps_path=/opt/frappe/apps.json"; \
|
||||
fi && \
|
||||
bench init ${APP_INSTALL_ARGS}\
|
||||
--frappe-branch=${FRAPPE_BRANCH} \
|
||||
--frappe-path=${FRAPPE_PATH} \
|
||||
--no-procfile \
|
||||
--no-backups \
|
||||
--skip-redis-config-generation \
|
||||
--verbose \
|
||||
/home/frappe/frappe-bench && \
|
||||
cd /home/frappe/frappe-bench && \
|
||||
echo "{}" > sites/common_site_config.json && \
|
||||
find apps -mindepth 1 -path "*/.git" | xargs rm -fr
|
||||
|
||||
FROM frappe/base:${FRAPPE_BRANCH} AS backend
|
||||
|
||||
USER frappe
|
||||
|
||||
COPY --from=builder --chown=frappe:frappe /home/frappe/frappe-bench /home/frappe/frappe-bench
|
||||
|
||||
WORKDIR /home/frappe/frappe-bench
|
||||
|
||||
VOLUME [ \
|
||||
"/home/frappe/frappe-bench/sites", \
|
||||
"/home/frappe/frappe-bench/sites/assets", \
|
||||
"/home/frappe/frappe-bench/logs" \
|
||||
]
|
||||
|
||||
CMD [ \
|
||||
"/home/frappe/frappe-bench/env/bin/gunicorn", \
|
||||
"--chdir=/home/frappe/frappe-bench/sites", \
|
||||
"--bind=0.0.0.0:8000", \
|
||||
"--threads=4", \
|
||||
"--workers=2", \
|
||||
"--worker-class=gthread", \
|
||||
"--worker-tmp-dir=/dev/shm", \
|
||||
"--timeout=120", \
|
||||
"--preload", \
|
||||
"frappe.app:application" \
|
||||
]
|
||||
@@ -1,62 +0,0 @@
|
||||
FROM frappe/bench:latest as assets_builder
|
||||
|
||||
ARG FRAPPE_VERSION
|
||||
ARG FRAPPE_REPO=https://github.com/frappe/frappe
|
||||
ARG PYTHON_VERSION
|
||||
ARG NODE_VERSION
|
||||
ENV NVM_DIR=/home/frappe/.nvm
|
||||
ENV PATH ${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}
|
||||
RUN PYENV_VERSION=${PYTHON_VERSION} bench init --version=${FRAPPE_VERSION} --frappe-path=${FRAPPE_REPO} --skip-redis-config-generation --verbose --skip-assets /home/frappe/frappe-bench
|
||||
|
||||
WORKDIR /home/frappe/frappe-bench
|
||||
|
||||
|
||||
FROM assets_builder as frappe_assets
|
||||
|
||||
RUN bench setup requirements \
|
||||
&& if [ -z "${FRAPPE_VERSION##*v14*}" ] || [ "$FRAPPE_VERSION" = "develop" ]; then \
|
||||
export BUILD_OPTS="--production";\
|
||||
fi \
|
||||
&& FRAPPE_ENV=production bench build --verbose --hard-link ${BUILD_OPTS}
|
||||
|
||||
|
||||
FROM assets_builder as erpnext_assets
|
||||
|
||||
ARG ERPNEXT_VERSION
|
||||
ARG ERPNEXT_REPO=https://github.com/frappe/erpnext
|
||||
RUN bench get-app --branch=${ERPNEXT_VERSION} --skip-assets --resolve-deps erpnext ${ERPNEXT_REPO}\
|
||||
&& if [ -z "${ERPNEXT_VERSION##*v14*}" ] || [ "$ERPNEXT_VERSION" = "develop" ]; then \
|
||||
export BUILD_OPTS="--production"; \
|
||||
fi \
|
||||
&& FRAPPE_ENV=production bench build --verbose --hard-link ${BUILD_OPTS}
|
||||
|
||||
|
||||
FROM alpine/git as bench
|
||||
|
||||
# Error pages
|
||||
ARG BENCH_REPO=https://github.com/frappe/bench
|
||||
RUN git clone --depth 1 ${BENCH_REPO} /tmp/bench \
|
||||
&& mkdir /out \
|
||||
&& mv /tmp/bench/bench/config/templates/502.html /out \
|
||||
&& touch /out/.build
|
||||
|
||||
FROM nginxinc/nginx-unprivileged:1.23.3-alpine as frappe
|
||||
|
||||
# Set default ENV variables for backwards compatibility
|
||||
ENV PROXY_READ_TIMOUT=120
|
||||
ENV CLIENT_MAX_BODY_SIZE=50m
|
||||
|
||||
# https://github.com/nginxinc/docker-nginx-unprivileged/blob/main/stable/alpine/20-envsubst-on-templates.sh
|
||||
COPY nginx-template.conf /etc/nginx/templates/default.conf.template
|
||||
# https://github.com/nginxinc/docker-nginx-unprivileged/blob/main/stable/alpine/docker-entrypoint.sh
|
||||
COPY entrypoint.sh /docker-entrypoint.d/frappe-entrypoint.sh
|
||||
|
||||
COPY --from=bench /out /usr/share/nginx/html/
|
||||
COPY --from=frappe_assets /home/frappe/frappe-bench/sites/assets /usr/share/nginx/html/assets
|
||||
|
||||
USER 1000
|
||||
|
||||
|
||||
FROM frappe as erpnext
|
||||
|
||||
COPY --from=erpnext_assets /home/frappe/frappe-bench/sites/assets /usr/share/nginx/html/assets
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
# Update timestamp for ".build" file to enable caching assets:
|
||||
# https://github.com/frappe/frappe/blob/52d8e6d952130eea64a9990b9fd5b1f6877be1b7/frappe/utils/__init__.py#L799-L805
|
||||
if [ -d /usr/share/nginx/html/sites ]; then
|
||||
touch /usr/share/nginx/html/sites/.build -r /usr/share/nginx/html/.build
|
||||
fi
|
||||
148
images/production/Containerfile
Normal file
148
images/production/Containerfile
Normal file
@@ -0,0 +1,148 @@
|
||||
ARG PYTHON_VERSION=3.11.6
|
||||
ARG DEBIAN_BASE=bookworm
|
||||
FROM python:${PYTHON_VERSION}-slim-${DEBIAN_BASE} AS base
|
||||
|
||||
ARG WKHTMLTOPDF_VERSION=0.12.6.1-3
|
||||
ARG WKHTMLTOPDF_DISTRO=bookworm
|
||||
ARG NODE_VERSION=20.19.2
|
||||
ENV NVM_DIR=/home/frappe/.nvm
|
||||
ENV PATH=${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}
|
||||
|
||||
RUN useradd -ms /bin/bash frappe \
|
||||
&& apt-get update \
|
||||
&& apt-get install --no-install-recommends -y \
|
||||
curl \
|
||||
git \
|
||||
vim \
|
||||
nginx \
|
||||
gettext-base \
|
||||
file \
|
||||
# weasyprint dependencies
|
||||
libpango-1.0-0 \
|
||||
libharfbuzz0b \
|
||||
libpangoft2-1.0-0 \
|
||||
libpangocairo-1.0-0 \
|
||||
# For backups
|
||||
restic \
|
||||
gpg \
|
||||
# MariaDB
|
||||
mariadb-client \
|
||||
less \
|
||||
# Postgres
|
||||
libpq-dev \
|
||||
postgresql-client \
|
||||
# For healthcheck
|
||||
wait-for-it \
|
||||
jq \
|
||||
# NodeJS
|
||||
&& mkdir -p ${NVM_DIR} \
|
||||
&& curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash \
|
||||
&& . ${NVM_DIR}/nvm.sh \
|
||||
&& nvm install ${NODE_VERSION} \
|
||||
&& nvm use v${NODE_VERSION} \
|
||||
&& npm install -g yarn \
|
||||
&& nvm alias default v${NODE_VERSION} \
|
||||
&& rm -rf ${NVM_DIR}/.cache \
|
||||
&& echo 'export NVM_DIR="/home/frappe/.nvm"' >>/home/frappe/.bashrc \
|
||||
&& echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm' >>/home/frappe/.bashrc \
|
||||
&& echo '[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion' >>/home/frappe/.bashrc \
|
||||
# Install wkhtmltopdf with patched qt
|
||||
&& if [ "$(uname -m)" = "aarch64" ]; then export ARCH=arm64; fi \
|
||||
&& if [ "$(uname -m)" = "x86_64" ]; then export ARCH=amd64; fi \
|
||||
&& downloaded_file=wkhtmltox_${WKHTMLTOPDF_VERSION}.${WKHTMLTOPDF_DISTRO}_${ARCH}.deb \
|
||||
&& curl -sLO https://github.com/wkhtmltopdf/packaging/releases/download/$WKHTMLTOPDF_VERSION/$downloaded_file \
|
||||
&& apt-get install -y ./$downloaded_file \
|
||||
&& rm $downloaded_file \
|
||||
# Clean up
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& rm -fr /etc/nginx/sites-enabled/default \
|
||||
&& pip3 install frappe-bench \
|
||||
# Fixes for non-root nginx and logs to stdout
|
||||
&& sed -i '/user www-data/d' /etc/nginx/nginx.conf \
|
||||
&& ln -sf /dev/stdout /var/log/nginx/access.log && ln -sf /dev/stderr /var/log/nginx/error.log \
|
||||
&& touch /run/nginx.pid \
|
||||
&& chown -R frappe:frappe /etc/nginx/conf.d \
|
||||
&& chown -R frappe:frappe /etc/nginx/nginx.conf \
|
||||
&& chown -R frappe:frappe /var/log/nginx \
|
||||
&& chown -R frappe:frappe /var/lib/nginx \
|
||||
&& chown -R frappe:frappe /run/nginx.pid
|
||||
|
||||
COPY resources/nginx-template.conf /templates/nginx/frappe.conf.template
|
||||
COPY resources/nginx-entrypoint.sh /usr/local/bin/nginx-entrypoint.sh
|
||||
|
||||
FROM base AS build
|
||||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
|
||||
# For frappe framework
|
||||
wget \
|
||||
# For psycopg2
|
||||
libpq-dev \
|
||||
# Other
|
||||
libffi-dev \
|
||||
liblcms2-dev \
|
||||
libldap2-dev \
|
||||
libmariadb-dev \
|
||||
libsasl2-dev \
|
||||
libtiff5-dev \
|
||||
libwebp-dev \
|
||||
pkg-config \
|
||||
redis-tools \
|
||||
rlwrap \
|
||||
tk8.6-dev \
|
||||
cron \
|
||||
# For pandas
|
||||
gcc \
|
||||
build-essential \
|
||||
libbz2-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
USER frappe
|
||||
|
||||
FROM build AS builder
|
||||
|
||||
ARG FRAPPE_BRANCH=version-15
|
||||
ARG FRAPPE_PATH=https://github.com/frappe/frappe
|
||||
ARG ERPNEXT_REPO=https://github.com/frappe/erpnext
|
||||
ARG ERPNEXT_BRANCH=version-15
|
||||
RUN bench init \
|
||||
--frappe-branch=${FRAPPE_BRANCH} \
|
||||
--frappe-path=${FRAPPE_PATH} \
|
||||
--no-procfile \
|
||||
--no-backups \
|
||||
--skip-redis-config-generation \
|
||||
--verbose \
|
||||
/home/frappe/frappe-bench && \
|
||||
cd /home/frappe/frappe-bench && \
|
||||
bench get-app --branch=${ERPNEXT_BRANCH} --resolve-deps erpnext ${ERPNEXT_REPO} && \
|
||||
echo "{}" > sites/common_site_config.json && \
|
||||
find apps -mindepth 1 -path "*/.git" | xargs rm -fr
|
||||
|
||||
FROM base AS erpnext
|
||||
|
||||
USER frappe
|
||||
|
||||
RUN echo "echo \"Commands restricted in prodution container, Read FAQ before you proceed: https://frappe.io/ctr-faq\"" >> ~/.bashrc
|
||||
|
||||
COPY --from=builder --chown=frappe:frappe /home/frappe/frappe-bench /home/frappe/frappe-bench
|
||||
|
||||
WORKDIR /home/frappe/frappe-bench
|
||||
|
||||
VOLUME [ \
|
||||
"/home/frappe/frappe-bench/sites", \
|
||||
"/home/frappe/frappe-bench/sites/assets", \
|
||||
"/home/frappe/frappe-bench/logs" \
|
||||
]
|
||||
|
||||
CMD [ \
|
||||
"/home/frappe/frappe-bench/env/bin/gunicorn", \
|
||||
"--chdir=/home/frappe/frappe-bench/sites", \
|
||||
"--bind=0.0.0.0:8000", \
|
||||
"--threads=4", \
|
||||
"--workers=2", \
|
||||
"--worker-class=gthread", \
|
||||
"--worker-tmp-dir=/dev/shm", \
|
||||
"--timeout=120", \
|
||||
"--preload", \
|
||||
"frappe.app:application" \
|
||||
]
|
||||
@@ -1,34 +0,0 @@
|
||||
FROM alpine/git as builder
|
||||
|
||||
ARG FRAPPE_VERSION
|
||||
ARG FRAPPE_REPO=https://github.com/frappe/frappe
|
||||
RUN apk add -U jq
|
||||
RUN git clone --depth 1 -b ${FRAPPE_VERSION} ${FRAPPE_REPO} /opt/frappe
|
||||
RUN jq --argjson dependencies "$(jq '.dependencies | INDEX( "express", "redis", "socket.io", "superagent" ) as $keep | \
|
||||
del( \
|
||||
. | objects | \
|
||||
.[ \
|
||||
keys_unsorted[] | \
|
||||
select( $keep[ . ] | not ) \
|
||||
] \
|
||||
)' /opt/frappe/package.json)" '.dependencies = $dependencies | del(.scripts.prepare)' /opt/frappe/package.json > /opt/frappe/dependencies.json && \
|
||||
mv /opt/frappe/dependencies.json /opt/frappe/package.json
|
||||
|
||||
# NodeJS LTS
|
||||
FROM node:18-alpine
|
||||
|
||||
RUN addgroup -S frappe \
|
||||
&& adduser -S frappe -G frappe
|
||||
USER frappe
|
||||
|
||||
WORKDIR /home/frappe/frappe-bench
|
||||
RUN mkdir -p sites apps/frappe
|
||||
|
||||
COPY --chown=frappe:frappe --from=builder /opt/frappe/package.json /opt/frappe/socketio.js /opt/frappe/node_utils.js apps/frappe/
|
||||
|
||||
RUN cd apps/frappe \
|
||||
&& npm install --omit=dev
|
||||
|
||||
WORKDIR /home/frappe/frappe-bench/sites
|
||||
|
||||
CMD [ "node", "/home/frappe/frappe-bench/apps/frappe/socketio.js" ]
|
||||
@@ -1,146 +0,0 @@
|
||||
# syntax=docker/dockerfile:1.3
|
||||
|
||||
ARG PYTHON_VERSION
|
||||
FROM python:${PYTHON_VERSION}-slim-bullseye as base
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install --no-install-recommends -y \
|
||||
# Postgres
|
||||
libpq-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN useradd -ms /bin/bash frappe
|
||||
USER frappe
|
||||
RUN mkdir -p /home/frappe/frappe-bench/apps /home/frappe/frappe-bench/logs /home/frappe/frappe-bench/sites /home/frappe/frappe-bench/config
|
||||
WORKDIR /home/frappe/frappe-bench
|
||||
|
||||
USER root
|
||||
RUN pip install --no-cache-dir -U pip wheel \
|
||||
&& python -m venv env \
|
||||
&& env/bin/pip install --no-cache-dir -U pip wheel
|
||||
|
||||
COPY install-app.sh /usr/local/bin/install-app
|
||||
|
||||
|
||||
FROM base as build_deps
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install --no-install-recommends -y \
|
||||
# Install git here because it is not required in production
|
||||
git \
|
||||
# gcc and g++ are required for building different packages across different versions
|
||||
# of Frappe and ERPNext and also on different platforms (for example, linux/arm64).
|
||||
# It is safe to install build deps even if they are not required
|
||||
# because they won't be included in final images.
|
||||
gcc \
|
||||
g++ \
|
||||
# Make is required to build wheels of ERPNext deps in develop branch for linux/arm64
|
||||
make \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
FROM build_deps as frappe_builder
|
||||
|
||||
ARG FRAPPE_VERSION
|
||||
ARG FRAPPE_REPO=https://github.com/frappe/frappe
|
||||
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||
git clone --depth 1 -b ${FRAPPE_VERSION} ${FRAPPE_REPO} apps/frappe \
|
||||
&& install-app frappe \
|
||||
&& env/bin/pip install -U gevent \
|
||||
# Link Frappe's node_modules/ to make Website Theme work
|
||||
&& mkdir -p /home/frappe/frappe-bench/sites/assets/frappe/node_modules \
|
||||
&& ln -s /home/frappe/frappe-bench/sites/assets/frappe/node_modules /home/frappe/frappe-bench/apps/frappe/node_modules
|
||||
|
||||
|
||||
FROM frappe_builder as erpnext_builder
|
||||
|
||||
ARG PAYMENTS_VERSION=develop
|
||||
ARG PAYMENTS_REPO=https://github.com/frappe/payments
|
||||
ARG ERPNEXT_VERSION
|
||||
ARG ERPNEXT_REPO=https://github.com/frappe/erpnext
|
||||
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||
if [ -z "${ERPNEXT_VERSION##*v14*}" ] || [ "$ERPNEXT_VERSION" = "develop" ]; then \
|
||||
git clone --depth 1 -b ${PAYMENTS_VERSION} ${PAYMENTS_REPO} apps/payments && install-app payments; \
|
||||
fi \
|
||||
&& git clone --depth 1 -b ${ERPNEXT_VERSION} ${ERPNEXT_REPO} apps/erpnext \
|
||||
&& install-app erpnext
|
||||
|
||||
FROM base as configured_base
|
||||
|
||||
ARG WKHTMLTOPDF_VERSION=0.12.6-1
|
||||
ARG NODE_VERSION
|
||||
ENV NVM_DIR=/home/frappe/.nvm
|
||||
ENV PATH ${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}
|
||||
RUN apt-get update \
|
||||
# Setup Node lists
|
||||
&& apt-get install --no-install-recommends -y curl git \
|
||||
# NodeJS with NVM
|
||||
&& mkdir -p ${NVM_DIR} \
|
||||
&& curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash \
|
||||
&& . ${NVM_DIR}/nvm.sh \
|
||||
&& nvm install ${NODE_VERSION} \
|
||||
&& nvm use v${NODE_VERSION} \
|
||||
&& npm install -g yarn \
|
||||
&& nvm alias default v${NODE_VERSION} \
|
||||
&& rm -rf ${NVM_DIR}/.cache \
|
||||
&& echo 'export NVM_DIR="/home/frappe/.nvm"' >>~/.bashrc \
|
||||
&& echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm' >> ~/.bashrc \
|
||||
&& echo '[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion' >> ~/.bashrc \
|
||||
# Install wkhtmltopdf with patched qt
|
||||
&& if [ "$(uname -m)" = "aarch64" ]; then export ARCH=arm64; fi \
|
||||
&& if [ "$(uname -m)" = "x86_64" ]; then export ARCH=amd64; fi \
|
||||
&& downloaded_file=wkhtmltox_$WKHTMLTOPDF_VERSION.buster_${ARCH}.deb \
|
||||
&& curl -sLO https://github.com/wkhtmltopdf/packaging/releases/download/$WKHTMLTOPDF_VERSION/$downloaded_file \
|
||||
&& apt-get install -y ./$downloaded_file \
|
||||
&& rm $downloaded_file \
|
||||
# Cleanup
|
||||
&& apt-get purge -y --auto-remove curl \
|
||||
&& apt-get update \
|
||||
&& apt-get install --no-install-recommends -y \
|
||||
# MariaDB
|
||||
mariadb-client \
|
||||
# Postgres
|
||||
postgresql-client \
|
||||
# For healthcheck
|
||||
wait-for-it \
|
||||
jq \
|
||||
# Clean up
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY pretend-bench.sh /usr/local/bin/bench
|
||||
COPY push_backup.py /usr/local/bin/push-backup
|
||||
COPY configure.py patched_bench_helper.py /usr/local/bin/
|
||||
COPY gevent_patch.py /opt/patches/
|
||||
|
||||
WORKDIR /home/frappe/frappe-bench/sites
|
||||
|
||||
CMD [ "/home/frappe/frappe-bench/env/bin/gunicorn", \
|
||||
"--bind=0.0.0.0:8000", \
|
||||
"--threads=4", \
|
||||
"--workers=2", \
|
||||
"--worker-class=gthread", \
|
||||
"--worker-tmp-dir=/dev/shm", \
|
||||
"--timeout=120", \
|
||||
"--preload", \
|
||||
"frappe.app:application" \
|
||||
]
|
||||
|
||||
|
||||
FROM configured_base as frappe
|
||||
|
||||
COPY --from=frappe_builder /home/frappe/frappe-bench/apps/frappe /home/frappe/frappe-bench/apps/frappe
|
||||
COPY --from=frappe_builder /home/frappe/frappe-bench/env /home/frappe/frappe-bench/env
|
||||
COPY --from=frappe_builder /home/frappe/frappe-bench/sites/apps.txt /home/frappe/frappe-bench/sites/
|
||||
|
||||
USER frappe
|
||||
|
||||
|
||||
# Split frappe and erpnext to reduce image size (because of frappe-bench/env/ directory)
|
||||
FROM configured_base as erpnext
|
||||
|
||||
COPY --from=erpnext_builder --chown=frappe:frappe /home/frappe/frappe-bench/apps /home/frappe/frappe-bench/apps
|
||||
COPY --from=erpnext_builder --chown=frappe:frappe /home/frappe/frappe-bench/env /home/frappe/frappe-bench/env
|
||||
COPY --from=erpnext_builder --chown=frappe:frappe /home/frappe/frappe-bench/sites/apps.txt /home/frappe/frappe-bench/sites/
|
||||
|
||||
|
||||
USER frappe
|
||||
@@ -1,56 +0,0 @@
|
||||
#!/usr/local/bin/python
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from typing import Any, TypeVar
|
||||
|
||||
|
||||
def update_config(**values: Any):
|
||||
fname = "common_site_config.json"
|
||||
if not os.path.exists(fname):
|
||||
with open(fname, "a") as f:
|
||||
json.dump({}, f)
|
||||
|
||||
with open(fname, "r+") as f:
|
||||
config: dict[str, Any] = json.load(f)
|
||||
config.update(values)
|
||||
f.seek(0)
|
||||
f.truncate()
|
||||
json.dump(config, f)
|
||||
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
def env(name: str, type_: type[_T] = str) -> _T:
|
||||
value = os.getenv(name)
|
||||
if not value:
|
||||
raise RuntimeError(f'Required environment variable "{name}" not set')
|
||||
try:
|
||||
value = type_(value)
|
||||
except Exception:
|
||||
raise RuntimeError(
|
||||
f'Cannot convert environment variable "{name}" to type "{type_}"'
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
def generate_redis_url(url: str):
|
||||
return f"redis://{url}"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
update_config(
|
||||
db_host=env("DB_HOST"),
|
||||
db_port=env("DB_PORT", int),
|
||||
redis_cache=generate_redis_url(env("REDIS_CACHE")),
|
||||
redis_queue=generate_redis_url(env("REDIS_QUEUE")),
|
||||
redis_socketio=generate_redis_url(env("REDIS_SOCKETIO")),
|
||||
socketio_port=env("SOCKETIO_PORT", int),
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -1,3 +0,0 @@
|
||||
import gevent.monkey
|
||||
|
||||
gevent.monkey.patch_all()
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
set -x
|
||||
|
||||
APP=$1
|
||||
|
||||
cd /home/frappe/frappe-bench
|
||||
|
||||
env/bin/pip install -e "apps/$APP"
|
||||
|
||||
echo "$APP" >>sites/apps.txt
|
||||
@@ -1,48 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import click
|
||||
import click.exceptions
|
||||
import frappe.app
|
||||
import frappe.database.db_manager
|
||||
import frappe.utils.bench_helper
|
||||
|
||||
|
||||
def patch_database_creator():
|
||||
"""
|
||||
We need to interrupt Frappe site database creation to monkeypatch
|
||||
functions that resolve host for user that owns site database.
|
||||
In frappe_docker this was implemented in "new" command:
|
||||
https://github.com/frappe/frappe_docker/blob/c808ad1767feaf793a2d14541ac0f4d9cbab45b3/build/frappe-worker/commands/new.py#L87
|
||||
"""
|
||||
|
||||
frappe.database.db_manager.DbManager.get_current_host = lambda self: "%"
|
||||
|
||||
|
||||
def patch_click_usage_error():
|
||||
bits: tuple[str, ...] = (
|
||||
click.style(
|
||||
"Only Frappe framework bench commands are available in container setup.",
|
||||
fg="yellow",
|
||||
bold=True,
|
||||
),
|
||||
"https://frappeframework.com/docs/v13/user/en/bench/frappe-commands",
|
||||
)
|
||||
notice = "\n".join(bits)
|
||||
|
||||
def format_message(self: click.exceptions.UsageError):
|
||||
if "No such command" in self.message:
|
||||
return f"{notice}\n\n{self.message}"
|
||||
return self.message
|
||||
|
||||
click.exceptions.UsageError.format_message = format_message
|
||||
|
||||
|
||||
def main() -> int:
|
||||
patch_database_creator()
|
||||
patch_click_usage_error()
|
||||
frappe.utils.bench_helper.main()
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# shellcheck disable=SC2068
|
||||
~/frappe-bench/env/bin/python /usr/local/bin/patched_bench_helper.py frappe $@
|
||||
@@ -1,117 +0,0 @@
|
||||
#!/home/frappe/frappe-bench/env/bin/python
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, List, cast
|
||||
|
||||
import boto3
|
||||
import frappe
|
||||
from frappe.utils.backups import BackupGenerator
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from mypy_boto3_s3.service_resource import _Bucket
|
||||
|
||||
|
||||
class Arguments(argparse.Namespace):
|
||||
site: str
|
||||
bucket: str
|
||||
region_name: str
|
||||
endpoint_url: str
|
||||
aws_access_key_id: str
|
||||
aws_secret_access_key: str
|
||||
bucket_directory: str
|
||||
|
||||
|
||||
def _get_files_from_previous_backup(site_name: str) -> list[Path]:
|
||||
frappe.connect(site_name)
|
||||
|
||||
conf = cast(Any, frappe.conf)
|
||||
backup_generator = BackupGenerator(
|
||||
db_name=conf.db_name,
|
||||
user=conf.db_name,
|
||||
password=conf.db_password,
|
||||
db_host=frappe.db.host,
|
||||
db_port=frappe.db.port,
|
||||
db_type=conf.db_type,
|
||||
)
|
||||
recent_backup_files = backup_generator.get_recent_backup(24)
|
||||
|
||||
frappe.destroy()
|
||||
return [Path(f) for f in recent_backup_files if f]
|
||||
|
||||
|
||||
def get_files_from_previous_backup(site_name: str) -> list[Path]:
|
||||
files = _get_files_from_previous_backup(site_name)
|
||||
if not files:
|
||||
print("No backup found that was taken <24 hours ago.")
|
||||
return files
|
||||
|
||||
|
||||
def get_bucket(args: Arguments) -> _Bucket:
|
||||
return boto3.resource(
|
||||
service_name="s3",
|
||||
endpoint_url=args.endpoint_url,
|
||||
region_name=args.region_name,
|
||||
aws_access_key_id=args.aws_access_key_id,
|
||||
aws_secret_access_key=args.aws_secret_access_key,
|
||||
).Bucket(args.bucket)
|
||||
|
||||
|
||||
def upload_file(
|
||||
path: Path, site_name: str, bucket: _Bucket, bucket_directory: str = None
|
||||
) -> None:
|
||||
filename = str(path.absolute())
|
||||
key = str(Path(site_name) / path.name)
|
||||
if bucket_directory:
|
||||
key = bucket_directory + "/" + key
|
||||
print(f"Uploading {key}")
|
||||
bucket.upload_file(Filename=filename, Key=key)
|
||||
os.remove(path)
|
||||
|
||||
|
||||
def push_backup(args: Arguments) -> None:
|
||||
"""Get latest backup files using Frappe utils, push them to S3 and remove local copy"""
|
||||
|
||||
files = get_files_from_previous_backup(args.site)
|
||||
bucket = get_bucket(args)
|
||||
|
||||
for path in files:
|
||||
upload_file(
|
||||
path=path,
|
||||
site_name=args.site,
|
||||
bucket=bucket,
|
||||
bucket_directory=args.bucket_directory,
|
||||
)
|
||||
|
||||
print("Done!")
|
||||
|
||||
|
||||
def parse_args(args: list[str]) -> Arguments:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--site", required=True)
|
||||
parser.add_argument("--bucket", required=True)
|
||||
parser.add_argument("--region-name", required=True)
|
||||
parser.add_argument("--endpoint-url", required=True)
|
||||
# Looking for default AWS credentials variables
|
||||
parser.add_argument(
|
||||
"--aws-access-key-id", required=True, default=os.getenv("AWS_ACCESS_KEY_ID")
|
||||
)
|
||||
parser.add_argument(
|
||||
"--aws-secret-access-key",
|
||||
required=True,
|
||||
default=os.getenv("AWS_SECRET_ACCESS_KEY"),
|
||||
)
|
||||
parser.add_argument("--bucket-directory")
|
||||
return parser.parse_args(args, namespace=Arguments())
|
||||
|
||||
|
||||
def main(args: list[str]) -> int:
|
||||
push_backup(parse_args(args))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
104
install_x11_deps.sh
Executable file
104
install_x11_deps.sh
Executable file
@@ -0,0 +1,104 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
# This script configures X11 forwarding for Linux and macOS systems.
|
||||
# It installs X11, Openbox (on Linux), and checks for XQuartz (on macOS).
|
||||
# It also updates the sshd_config file to enable X11Forwarding and restarts the SSH service.
|
||||
|
||||
# Check if the script is running with root privileges
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "Error: This script requires root privileges. Please run it as a superuser"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Function to restart SSH service (Linux)
|
||||
restart_ssh_linux() {
|
||||
if command -v service >/dev/null 2>&1; then
|
||||
sudo service ssh restart
|
||||
else
|
||||
sudo systemctl restart ssh
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to restart SSH service (macOS)
|
||||
restart_ssh_macos() {
|
||||
launchctl stop com.openssh.sshd
|
||||
launchctl start com.openssh.sshd
|
||||
}
|
||||
|
||||
update_x11_forwarding() {
|
||||
if grep -q "X11Forwarding yes" /etc/ssh/sshd_config; then
|
||||
echo "X11Forwarding is already set to 'yes' in ssh_config."
|
||||
else
|
||||
if [[ "$OSTYPE" == "linux-gnu" ]]; then
|
||||
# Linux: Use sed for Linux
|
||||
sudo sed -i 's/#\?X11Forwarding.*/X11Forwarding yes/' /etc/ssh/sshd_config
|
||||
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
# macOS: Use sed for macOS
|
||||
sudo sed -i -E 's/#X11Forwarding.*/X11Forwarding yes/' /etc/ssh/sshd_config
|
||||
restart_ssh_macos
|
||||
fi
|
||||
if [[ "$OSTYPE" == "linux-gnu" ]]; then
|
||||
restart_ssh_linux
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Determine the operating system
|
||||
if [[ "$OSTYPE" == "linux-gnu" ]]; then
|
||||
# Linux
|
||||
if command -v startx >/dev/null 2>&1; then
|
||||
echo "X11 is already installed."
|
||||
else
|
||||
# Check which package manager is available
|
||||
if command -v apt-get >/dev/null 2>&1; then
|
||||
install_command="sudo apt-get update && sudo apt-get install xorg openbox"
|
||||
elif command -v dnf >/dev/null 2>&1; then
|
||||
install_command="sudo dnf install xorg-x11-server-Xorg openbox"
|
||||
else
|
||||
echo "Error: Unable to determine the package manager. Manual installation required."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
# Check if the installation command is defined
|
||||
if [ -n "$install_command" ]; then
|
||||
# Execute the installation command
|
||||
if $install_command; then
|
||||
echo "X11 and Openbox have been successfully installed."
|
||||
else
|
||||
echo "Error: Failed to install X11 and Openbox."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Error: Unsupported package manager."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Call the function to update X11Forwarding
|
||||
update_x11_forwarding
|
||||
|
||||
# Get the IP address of the host dynamically
|
||||
host_ip=$(hostname -I | awk '{print $1}')
|
||||
xhost + "$host_ip" && xhost + local:
|
||||
# Set the DISPLAY variable to the host IP
|
||||
export DISPLAY="$host_ip:0.0"
|
||||
echo "DISPLAY variable set to $DISPLAY"
|
||||
|
||||
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
# macOS
|
||||
if command -v xquartz >/dev/null 2>&1; then
|
||||
echo "XQuartz is already installed."
|
||||
else
|
||||
echo "Error: XQuartz is required for X11 forwarding on macOS. Please install XQuartz manually."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Call the function to update X11Forwarding
|
||||
update_x11_forwarding
|
||||
|
||||
# Export the DISPLAY variable for macOS
|
||||
export DISPLAY=:0
|
||||
echo "DISPLAY variable set to $DISPLAY"
|
||||
else
|
||||
echo "Error: Unsupported operating system."
|
||||
exit 1
|
||||
fi
|
||||
15
overrides/compose.backup-cron.yaml
Normal file
15
overrides/compose.backup-cron.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
services:
|
||||
cron:
|
||||
image: mcuadros/ofelia:latest
|
||||
depends_on:
|
||||
- scheduler
|
||||
command: daemon --docker
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
|
||||
scheduler:
|
||||
labels:
|
||||
ofelia.enabled: "true"
|
||||
ofelia.job-exec.datecron.schedule: "${BACKUP_CRONSTRING:-@every 6h}"
|
||||
ofelia.job-exec.datecron.command: "bench --site all backup"
|
||||
ofelia.job-exec.datecron.user: "frappe"
|
||||
@@ -1,27 +0,0 @@
|
||||
x-erpnext-backend-image: &erpnext_backend_image
|
||||
image: frappe/erpnext-worker:${ERPNEXT_VERSION:?No ERPNext version set}
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- assets:/home/frappe/frappe-bench/sites/assets:ro
|
||||
|
||||
services:
|
||||
configurator:
|
||||
<<: *erpnext_backend_image
|
||||
|
||||
backend:
|
||||
<<: *erpnext_backend_image
|
||||
|
||||
frontend:
|
||||
image: frappe/erpnext-nginx:${ERPNEXT_VERSION}
|
||||
|
||||
queue-short:
|
||||
<<: *erpnext_backend_image
|
||||
|
||||
queue-default:
|
||||
<<: *erpnext_backend_image
|
||||
|
||||
queue-long:
|
||||
<<: *erpnext_backend_image
|
||||
|
||||
scheduler:
|
||||
<<: *erpnext_backend_image
|
||||
@@ -5,10 +5,11 @@ services:
|
||||
- traefik.http.services.frontend.loadbalancer.server.port=8080
|
||||
- traefik.http.routers.frontend-http.entrypoints=websecure
|
||||
- traefik.http.routers.frontend-http.tls.certresolver=main-resolver
|
||||
- traefik.http.routers.frontend-http.rule=HostRegexp(`{any:.+}`)
|
||||
- traefik.http.routers.frontend-http.rule=Host(${SITES:?List of sites not set})
|
||||
|
||||
proxy:
|
||||
image: traefik:2.5
|
||||
image: traefik:v2.11
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- --providers.docker=true
|
||||
- --providers.docker.exposedbydefault=false
|
||||
@@ -21,8 +22,8 @@ services:
|
||||
- --certificatesResolvers.main-resolver.acme.email=${LETSENCRYPT_EMAIL:?No Let's Encrypt email set}
|
||||
- --certificatesResolvers.main-resolver.acme.storage=/letsencrypt/acme.json
|
||||
ports:
|
||||
- 80:80
|
||||
- 443:443
|
||||
- ${HTTP_PUBLISH_PORT:-80}:80
|
||||
- ${HTTPS_PUBLISH_PORT:-443}:443
|
||||
volumes:
|
||||
- cert-data:/letsencrypt
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
|
||||
11
overrides/compose.mariadb-secrets.yaml
Normal file
11
overrides/compose.mariadb-secrets.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
services:
|
||||
db:
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: !reset null
|
||||
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_password
|
||||
secrets:
|
||||
- db_password
|
||||
|
||||
secrets:
|
||||
db_password:
|
||||
file: ${DB_PASSWORD_SECRETS_FILE:?No db secret file set}
|
||||
@@ -3,12 +3,14 @@ version: "3.3"
|
||||
services:
|
||||
database:
|
||||
container_name: mariadb-database
|
||||
image: mariadb:10.6
|
||||
image: mariadb:11.8
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: mysqladmin ping -h localhost --password=${DB_PASSWORD:-changeit}
|
||||
interval: 1s
|
||||
retries: 15
|
||||
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
||||
start_period: 5s
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
command:
|
||||
- --character-set-server=utf8mb4
|
||||
- --collation-server=utf8mb4_unicode_ci
|
||||
@@ -16,8 +18,9 @@ services:
|
||||
- --skip-innodb-read-only-compressed
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-changeit}
|
||||
MARIADB_AUTO_UPGRADE: 1
|
||||
volumes:
|
||||
- ${HOME}/data/mariadb:/var/lib/mysql
|
||||
- db-data:/var/lib/mysql
|
||||
networks:
|
||||
- mariadb-network
|
||||
|
||||
@@ -25,3 +28,6 @@ networks:
|
||||
mariadb-network:
|
||||
name: mariadb-network
|
||||
external: false
|
||||
|
||||
volumes:
|
||||
db-data:
|
||||
@@ -8,18 +8,22 @@ services:
|
||||
condition: service_healthy
|
||||
|
||||
db:
|
||||
image: mariadb:10.6
|
||||
image: mariadb:11.8
|
||||
healthcheck:
|
||||
test: mysqladmin ping -h localhost --password=${DB_PASSWORD}
|
||||
interval: 1s
|
||||
retries: 15
|
||||
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
||||
start_period: 5s
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- --character-set-server=utf8mb4
|
||||
- --collation-server=utf8mb4_unicode_ci
|
||||
- --skip-character-set-client-handshake
|
||||
- --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:?No db password set}
|
||||
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-123}
|
||||
MARIADB_AUTO_UPGRADE: 1
|
||||
volumes:
|
||||
- db-data:/var/lib/mysql
|
||||
|
||||
|
||||
@@ -26,10 +26,6 @@ services:
|
||||
networks:
|
||||
- bench-network
|
||||
- mariadb-network
|
||||
queue-default:
|
||||
networks:
|
||||
- bench-network
|
||||
- mariadb-network
|
||||
queue-short:
|
||||
networks:
|
||||
- bench-network
|
||||
@@ -38,7 +34,12 @@ services:
|
||||
networks:
|
||||
- bench-network
|
||||
- mariadb-network
|
||||
redis:
|
||||
redis-cache:
|
||||
networks:
|
||||
- bench-network
|
||||
- mariadb-network
|
||||
|
||||
redis-queue:
|
||||
networks:
|
||||
- bench-network
|
||||
- mariadb-network
|
||||
@@ -1,4 +1,4 @@
|
||||
services:
|
||||
frontend:
|
||||
ports:
|
||||
- 8080:8080
|
||||
- ${HTTP_PUBLISH_PORT:-8080}:8080
|
||||
|
||||
@@ -12,7 +12,7 @@ services:
|
||||
environment:
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD:?No db password set}
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql
|
||||
- db-data:/var/lib/postgresql/data
|
||||
|
||||
volumes:
|
||||
db-data:
|
||||
|
||||
@@ -7,13 +7,13 @@ services:
|
||||
- traefik.http.routers.frontend-http.rule=HostRegexp(`{any:.+}`)
|
||||
|
||||
proxy:
|
||||
image: traefik:2.5
|
||||
image: traefik:v2.11
|
||||
command:
|
||||
- --providers.docker
|
||||
- --providers.docker.exposedbydefault=false
|
||||
- --entrypoints.web.address=:80
|
||||
ports:
|
||||
- 80:80
|
||||
- ${HTTP_PUBLISH_PORT:-80}:80
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
userns_mode: host
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
services:
|
||||
configurator:
|
||||
environment:
|
||||
REDIS_CACHE: redis:6379/0
|
||||
REDIS_QUEUE: redis:6379/1
|
||||
REDIS_SOCKETIO: redis:6379/2
|
||||
REDIS_CACHE: redis-cache:6379
|
||||
REDIS_QUEUE: redis-queue:6379
|
||||
depends_on:
|
||||
- redis
|
||||
- redis-cache
|
||||
- redis-queue
|
||||
|
||||
redis:
|
||||
redis-cache:
|
||||
image: redis:6.2-alpine
|
||||
restart: unless-stopped
|
||||
|
||||
redis-queue:
|
||||
image: redis:6.2-alpine
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
- redis-queue-data:/data
|
||||
|
||||
volumes:
|
||||
redis-data:
|
||||
redis-queue-data:
|
||||
|
||||
@@ -2,7 +2,7 @@ services:
|
||||
traefik:
|
||||
labels:
|
||||
# https-redirect middleware to redirect HTTP to HTTPS
|
||||
# It can be re-used by other stacks in other Docker Compose files
|
||||
# It can be reused by other stacks in other Docker Compose files
|
||||
- traefik.http.middlewares.https-redirect.redirectscheme.scheme=https
|
||||
- traefik.http.middlewares.https-redirect.redirectscheme.permanent=true
|
||||
# traefik-http to use the middleware to redirect to https
|
||||
@@ -40,6 +40,9 @@ services:
|
||||
# Enable the Dashboard and API
|
||||
- --api
|
||||
ports:
|
||||
- 443:443
|
||||
- ${HTTPS_PUBLISH_PORT:-443}:443
|
||||
volumes:
|
||||
- ${HOME}/data/traefik/certificates:/certificates
|
||||
- cert-data:/certificates
|
||||
|
||||
volumes:
|
||||
cert-data:
|
||||
@@ -2,7 +2,8 @@ version: "3.3"
|
||||
|
||||
services:
|
||||
traefik:
|
||||
image: "traefik:v2.6"
|
||||
image: "traefik:v2.11"
|
||||
restart: unless-stopped
|
||||
labels:
|
||||
# Enable Traefik for this service, to make it available in the public network
|
||||
- traefik.enable=true
|
||||
@@ -34,7 +35,7 @@ services:
|
||||
# Enable the Dashboard and API
|
||||
- --api
|
||||
ports:
|
||||
- 80:80
|
||||
- ${HTTP_PUBLISH_PORT:-80}:80
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
networks:
|
||||
150
pwd.yml
150
pwd.yml
@@ -2,36 +2,60 @@ version: "3"
|
||||
|
||||
services:
|
||||
backend:
|
||||
image: frappe/erpnext-worker:v14.12.1
|
||||
image: frappe/erpnext:v15.83.0
|
||||
networks:
|
||||
- frappe_network
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- assets:/home/frappe/frappe-bench/sites/assets
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
environment:
|
||||
DB_HOST: db
|
||||
DB_PORT: "3306"
|
||||
MYSQL_ROOT_PASSWORD: admin
|
||||
MARIADB_ROOT_PASSWORD: admin
|
||||
|
||||
configurator:
|
||||
image: frappe/erpnext-worker:v14.12.1
|
||||
image: frappe/erpnext:v15.83.0
|
||||
networks:
|
||||
- frappe_network
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: none
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
command:
|
||||
- configure.py
|
||||
- >
|
||||
ls -1 apps > sites/apps.txt;
|
||||
bench set-config -g db_host $$DB_HOST;
|
||||
bench set-config -gp db_port $$DB_PORT;
|
||||
bench set-config -g redis_cache "redis://$$REDIS_CACHE";
|
||||
bench set-config -g redis_queue "redis://$$REDIS_QUEUE";
|
||||
bench set-config -g redis_socketio "redis://$$REDIS_QUEUE";
|
||||
bench set-config -gp socketio_port $$SOCKETIO_PORT;
|
||||
environment:
|
||||
DB_HOST: db
|
||||
DB_PORT: "3306"
|
||||
REDIS_CACHE: redis-cache:6379
|
||||
REDIS_QUEUE: redis-queue:6379
|
||||
REDIS_SOCKETIO: redis-socketio:6379
|
||||
SOCKETIO_PORT: "9000"
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
|
||||
create-site:
|
||||
image: frappe/erpnext-worker:v14.12.1
|
||||
image: frappe/erpnext:v15.83.0
|
||||
networks:
|
||||
- frappe_network
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
condition: none
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- assets:/home/frappe/frappe-bench/sites/assets
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
@@ -40,28 +64,29 @@ services:
|
||||
wait-for-it -t 120 db:3306;
|
||||
wait-for-it -t 120 redis-cache:6379;
|
||||
wait-for-it -t 120 redis-queue:6379;
|
||||
wait-for-it -t 120 redis-socketio:6379;
|
||||
export start=`date +%s`;
|
||||
until [[ -n `grep -hs ^ common_site_config.json | jq -r ".db_host // empty"` ]] && \
|
||||
[[ -n `grep -hs ^ common_site_config.json | jq -r ".redis_cache // empty"` ]] && \
|
||||
[[ -n `grep -hs ^ common_site_config.json | jq -r ".redis_queue // empty"` ]];
|
||||
until [[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".db_host // empty"` ]] && \
|
||||
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_cache // empty"` ]] && \
|
||||
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_queue // empty"` ]];
|
||||
do
|
||||
echo "Waiting for common_site_config.json to be created";
|
||||
echo "Waiting for sites/common_site_config.json to be created";
|
||||
sleep 5;
|
||||
if (( `date +%s`-start > 120 )); then
|
||||
echo "could not find common_site_config.json with required keys";
|
||||
echo "could not find sites/common_site_config.json with required keys";
|
||||
exit 1
|
||||
fi
|
||||
done;
|
||||
echo "common_site_config.json found";
|
||||
bench new-site frontend --admin-password=admin --db-root-password=admin --install-app payments --install-app erpnext --set-default;
|
||||
echo "sites/common_site_config.json found";
|
||||
bench new-site --mariadb-user-host-login-scope='%' --admin-password=admin --db-root-username=root --db-root-password=admin --install-app erpnext --set-default frontend;
|
||||
|
||||
db:
|
||||
image: mariadb:10.6
|
||||
networks:
|
||||
- frappe_network
|
||||
healthcheck:
|
||||
test: mysqladmin ping -h localhost --password=admin
|
||||
interval: 1s
|
||||
retries: 15
|
||||
retries: 20
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
@@ -72,14 +97,21 @@ services:
|
||||
- --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: admin
|
||||
MARIADB_ROOT_PASSWORD: admin
|
||||
volumes:
|
||||
- db-data:/var/lib/mysql
|
||||
|
||||
frontend:
|
||||
image: frappe/erpnext-nginx:v14.12.1
|
||||
image: frappe/erpnext:v15.83.0
|
||||
networks:
|
||||
- frappe_network
|
||||
depends_on:
|
||||
- websocket
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
command:
|
||||
- nginx-entrypoint.sh
|
||||
environment:
|
||||
BACKEND: backend:8000
|
||||
FRAPPE_SITE_NAME_HEADER: frontend
|
||||
@@ -87,30 +119,18 @@ services:
|
||||
UPSTREAM_REAL_IP_ADDRESS: 127.0.0.1
|
||||
UPSTREAM_REAL_IP_HEADER: X-Forwarded-For
|
||||
UPSTREAM_REAL_IP_RECURSIVE: "off"
|
||||
PROXY_READ_TIMOUT: 120
|
||||
PROXY_READ_TIMEOUT: 120
|
||||
CLIENT_MAX_BODY_SIZE: 50m
|
||||
volumes:
|
||||
- sites:/usr/share/nginx/html/sites
|
||||
- assets:/usr/share/nginx/html/assets
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
ports:
|
||||
- "8080:8080"
|
||||
|
||||
queue-default:
|
||||
image: frappe/erpnext-worker:v14.12.1
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
command:
|
||||
- bench
|
||||
- worker
|
||||
- --queue
|
||||
- default
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- assets:/home/frappe/frappe-bench/sites/assets
|
||||
|
||||
queue-long:
|
||||
image: frappe/erpnext-worker:v14.12.1
|
||||
image: frappe/erpnext:v15.83.0
|
||||
networks:
|
||||
- frappe_network
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
@@ -118,13 +138,18 @@ services:
|
||||
- bench
|
||||
- worker
|
||||
- --queue
|
||||
- long
|
||||
- long,default,short
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- assets:/home/frappe/frappe-bench/sites/assets
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
environment:
|
||||
FRAPPE_REDIS_CACHE: redis://redis-cache:6379
|
||||
FRAPPE_REDIS_QUEUE: redis://redis-queue:6379
|
||||
|
||||
queue-short:
|
||||
image: frappe/erpnext-worker:v14.12.1
|
||||
image: frappe/erpnext:v15.83.0
|
||||
networks:
|
||||
- frappe_network
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
@@ -132,13 +157,18 @@ services:
|
||||
- bench
|
||||
- worker
|
||||
- --queue
|
||||
- short
|
||||
- short,default
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- assets:/home/frappe/frappe-bench/sites/assets
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
environment:
|
||||
FRAPPE_REDIS_CACHE: redis://redis-cache:6379
|
||||
FRAPPE_REDIS_QUEUE: redis://redis-queue:6379
|
||||
|
||||
redis-queue:
|
||||
image: redis:6.2-alpine
|
||||
networks:
|
||||
- frappe_network
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
@@ -147,22 +177,16 @@ services:
|
||||
|
||||
redis-cache:
|
||||
image: redis:6.2-alpine
|
||||
networks:
|
||||
- frappe_network
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
volumes:
|
||||
- redis-cache-data:/data
|
||||
|
||||
redis-socketio:
|
||||
image: redis:6.2-alpine
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
volumes:
|
||||
- redis-socketio-data:/data
|
||||
|
||||
scheduler:
|
||||
image: frappe/erpnext-worker:v14.12.1
|
||||
image: frappe/erpnext:v15.83.0
|
||||
networks:
|
||||
- frappe_network
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
@@ -171,21 +195,31 @@ services:
|
||||
- schedule
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- assets:/home/frappe/frappe-bench/sites/assets
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
|
||||
websocket:
|
||||
image: frappe/frappe-socketio:v14.22.0
|
||||
image: frappe/erpnext:v15.83.0
|
||||
networks:
|
||||
- frappe_network
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
command:
|
||||
- node
|
||||
- /home/frappe/frappe-bench/apps/frappe/socketio.js
|
||||
environment:
|
||||
FRAPPE_REDIS_CACHE: redis://redis-cache:6379
|
||||
FRAPPE_REDIS_QUEUE: redis://redis-queue:6379
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- assets:/home/frappe/frappe-bench/sites/assets
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
|
||||
volumes:
|
||||
assets:
|
||||
db-data:
|
||||
redis-queue-data:
|
||||
redis-cache-data:
|
||||
redis-socketio-data:
|
||||
sites:
|
||||
logs:
|
||||
|
||||
networks:
|
||||
frappe_network:
|
||||
driver: bridge
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
frappe @ git+https://github.com/frappe/frappe.git
|
||||
boto3-stubs[s3]
|
||||
black==22.12.0
|
||||
@@ -1 +1 @@
|
||||
pytest==7.2.0
|
||||
pytest==8.4.2
|
||||
|
||||
52
resources/nginx-entrypoint.sh
Executable file
52
resources/nginx-entrypoint.sh
Executable file
@@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Set variables that do not exist
|
||||
if [[ -z "$BACKEND" ]]; then
|
||||
echo "BACKEND defaulting to 0.0.0.0:8000"
|
||||
export BACKEND=0.0.0.0:8000
|
||||
fi
|
||||
if [[ -z "$SOCKETIO" ]]; then
|
||||
echo "SOCKETIO defaulting to 0.0.0.0:9000"
|
||||
export SOCKETIO=0.0.0.0:9000
|
||||
fi
|
||||
if [[ -z "$UPSTREAM_REAL_IP_ADDRESS" ]]; then
|
||||
echo "UPSTREAM_REAL_IP_ADDRESS defaulting to 127.0.0.1"
|
||||
export UPSTREAM_REAL_IP_ADDRESS=127.0.0.1
|
||||
fi
|
||||
if [[ -z "$UPSTREAM_REAL_IP_HEADER" ]]; then
|
||||
echo "UPSTREAM_REAL_IP_HEADER defaulting to X-Forwarded-For"
|
||||
export UPSTREAM_REAL_IP_HEADER=X-Forwarded-For
|
||||
fi
|
||||
if [[ -z "$UPSTREAM_REAL_IP_RECURSIVE" ]]; then
|
||||
echo "UPSTREAM_REAL_IP_RECURSIVE defaulting to off"
|
||||
export UPSTREAM_REAL_IP_RECURSIVE=off
|
||||
fi
|
||||
if [[ -z "$FRAPPE_SITE_NAME_HEADER" ]]; then
|
||||
# shellcheck disable=SC2016
|
||||
echo 'FRAPPE_SITE_NAME_HEADER defaulting to $host'
|
||||
# shellcheck disable=SC2016
|
||||
export FRAPPE_SITE_NAME_HEADER='$host'
|
||||
fi
|
||||
|
||||
if [[ -z "$PROXY_READ_TIMEOUT" ]]; then
|
||||
echo "PROXY_READ_TIMEOUT defaulting to 120"
|
||||
export PROXY_READ_TIMEOUT=120
|
||||
fi
|
||||
|
||||
if [[ -z "$CLIENT_MAX_BODY_SIZE" ]]; then
|
||||
echo "CLIENT_MAX_BODY_SIZE defaulting to 50m"
|
||||
export CLIENT_MAX_BODY_SIZE=50m
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2016
|
||||
envsubst '${BACKEND}
|
||||
${SOCKETIO}
|
||||
${UPSTREAM_REAL_IP_ADDRESS}
|
||||
${UPSTREAM_REAL_IP_HEADER}
|
||||
${UPSTREAM_REAL_IP_RECURSIVE}
|
||||
${FRAPPE_SITE_NAME_HEADER}
|
||||
${PROXY_READ_TIMEOUT}
|
||||
${CLIENT_MAX_BODY_SIZE}' \
|
||||
</templates/nginx/frappe.conf.template >/etc/nginx/conf.d/frappe.conf
|
||||
|
||||
nginx -g 'daemon off;'
|
||||
@@ -6,16 +6,10 @@ upstream socketio-server {
|
||||
server ${SOCKETIO} fail_timeout=0;
|
||||
}
|
||||
|
||||
# Parse the X-Forwarded-Proto header - if set - defaulting to $scheme.
|
||||
map $http_x_forwarded_proto $proxy_x_forwarded_proto {
|
||||
default $scheme;
|
||||
https https;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8080;
|
||||
server_name $http_host;
|
||||
root /usr/share/nginx/html;
|
||||
server_name ${FRAPPE_SITE_NAME_HEADER};
|
||||
root /home/frappe/frappe-bench/sites;
|
||||
|
||||
proxy_buffer_size 128k;
|
||||
proxy_buffers 4 256k;
|
||||
@@ -37,13 +31,11 @@ server {
|
||||
|
||||
location ~ ^/protected/(.*) {
|
||||
internal;
|
||||
try_files /sites/${FRAPPE_SITE_NAME_HEADER}/$1 =404;
|
||||
try_files /${FRAPPE_SITE_NAME_HEADER}/$1 =404;
|
||||
}
|
||||
|
||||
location /socket.io {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header X-Frappe-Site-Name ${FRAPPE_SITE_NAME_HEADER};
|
||||
@@ -54,36 +46,31 @@ server {
|
||||
}
|
||||
|
||||
location / {
|
||||
rewrite ^(.+)/$ $proxy_x_forwarded_proto://$http_host$1 permanent;
|
||||
rewrite ^(.+)/index\.html$ $proxy_x_forwarded_proto://$http_host$1 permanent;
|
||||
rewrite ^(.+)\.html$ $proxy_x_forwarded_proto://$http_host$1 permanent;
|
||||
rewrite ^(.+)/$ $1 permanent;
|
||||
rewrite ^(.+)/index\.html$ $1 permanent;
|
||||
rewrite ^(.+)\.html$ $1 permanent;
|
||||
|
||||
location ~ ^/files/.*.(htm|html|svg|xml) {
|
||||
add_header Content-disposition "attachment";
|
||||
try_files /sites/${FRAPPE_SITE_NAME_HEADER}/public/$uri @webserver;
|
||||
try_files /${FRAPPE_SITE_NAME_HEADER}/public/$uri @webserver;
|
||||
}
|
||||
|
||||
try_files /sites/${FRAPPE_SITE_NAME_HEADER}/public/$uri @webserver;
|
||||
try_files /${FRAPPE_SITE_NAME_HEADER}/public/$uri @webserver;
|
||||
}
|
||||
|
||||
location @webserver {
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Frappe-Site-Name ${FRAPPE_SITE_NAME_HEADER};
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Use-X-Accel-Redirect True;
|
||||
proxy_read_timeout ${PROXY_READ_TIMOUT};
|
||||
proxy_read_timeout ${PROXY_READ_TIMEOUT};
|
||||
proxy_redirect off;
|
||||
|
||||
proxy_pass http://backend-server;
|
||||
}
|
||||
|
||||
# error pages
|
||||
error_page 502 /502.html;
|
||||
location /502.html {
|
||||
internal;
|
||||
}
|
||||
|
||||
# optimizations
|
||||
sendfile on;
|
||||
keepalive_timeout 15;
|
||||
@@ -1,69 +0,0 @@
|
||||
import os
|
||||
import re
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import boto3
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from mypy_boto3_s3.service_resource import BucketObjectsCollection, _Bucket
|
||||
|
||||
|
||||
def get_bucket() -> "_Bucket":
|
||||
return boto3.resource(
|
||||
service_name="s3",
|
||||
endpoint_url="http://minio:9000",
|
||||
region_name="us-east-1",
|
||||
aws_access_key_id=os.getenv("S3_ACCESS_KEY"),
|
||||
aws_secret_access_key=os.getenv("S3_SECRET_KEY"),
|
||||
).Bucket("frappe")
|
||||
|
||||
|
||||
def get_key_builder():
|
||||
site_name = os.getenv("SITE_NAME")
|
||||
assert site_name
|
||||
|
||||
def builder(key: str, suffix: str) -> bool:
|
||||
return bool(re.match(rf"{site_name}.*{suffix}$", key))
|
||||
|
||||
return builder
|
||||
|
||||
|
||||
def check_keys(objects: "BucketObjectsCollection"):
|
||||
check_key = get_key_builder()
|
||||
|
||||
db = False
|
||||
config = False
|
||||
private_files = False
|
||||
public_files = False
|
||||
|
||||
for obj in objects:
|
||||
if check_key(obj.key, "database.sql.gz"):
|
||||
db = True
|
||||
elif check_key(obj.key, "site_config_backup.json"):
|
||||
config = True
|
||||
elif check_key(obj.key, "private-files.tar"):
|
||||
private_files = True
|
||||
elif check_key(obj.key, "files.tar"):
|
||||
public_files = True
|
||||
|
||||
exc = lambda type_: Exception(f"Didn't push {type_} backup")
|
||||
if not db:
|
||||
raise exc("database")
|
||||
if not config:
|
||||
raise exc("site config")
|
||||
if not private_files:
|
||||
raise exc("private files")
|
||||
if not public_files:
|
||||
raise exc("public files")
|
||||
|
||||
print("All files were pushed to S3!")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
bucket = get_bucket()
|
||||
check_keys(bucket.objects.all())
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -31,7 +31,7 @@ def get_redis_url(addr: str) -> Address:
|
||||
|
||||
def get_addresses(config: dict[str, Any]) -> Iterable[Address]:
|
||||
yield (config["db_host"], config["db_port"])
|
||||
for key in ("redis_cache", "redis_queue", "redis_socketio"):
|
||||
for key in ("redis_cache", "redis_queue"):
|
||||
yield get_redis_url(config[key])
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ def check_cache():
|
||||
|
||||
|
||||
def main() -> int:
|
||||
frappe.connect(site="tests")
|
||||
frappe.connect(site="tests.localhost")
|
||||
check_db()
|
||||
check_cache()
|
||||
return 0
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
services:
|
||||
configurator:
|
||||
image: localhost:5000/frappe/erpnext-worker:${ERPNEXT_VERSION}
|
||||
|
||||
backend:
|
||||
image: localhost:5000/frappe/erpnext-worker:${ERPNEXT_VERSION}
|
||||
|
||||
frontend:
|
||||
image: localhost:5000/frappe/erpnext-nginx:${ERPNEXT_VERSION}
|
||||
|
||||
websocket:
|
||||
image: localhost:5000/frappe/frappe-socketio:${FRAPPE_VERSION}
|
||||
|
||||
queue-short:
|
||||
image: localhost:5000/frappe/erpnext-worker:${ERPNEXT_VERSION}
|
||||
|
||||
queue-default:
|
||||
image: localhost:5000/frappe/erpnext-worker:${ERPNEXT_VERSION}
|
||||
|
||||
queue-long:
|
||||
image: localhost:5000/frappe/erpnext-worker:${ERPNEXT_VERSION}
|
||||
|
||||
scheduler:
|
||||
image: localhost:5000/frappe/erpnext-worker:${ERPNEXT_VERSION}
|
||||
@@ -1,24 +1,21 @@
|
||||
services:
|
||||
configurator:
|
||||
image: localhost:5000/frappe/frappe-worker:${FRAPPE_VERSION}
|
||||
image: localhost:5000/frappe/erpnext:${ERPNEXT_VERSION}
|
||||
|
||||
backend:
|
||||
image: localhost:5000/frappe/frappe-worker:${FRAPPE_VERSION}
|
||||
image: localhost:5000/frappe/erpnext:${ERPNEXT_VERSION}
|
||||
|
||||
frontend:
|
||||
image: localhost:5000/frappe/frappe-nginx:${FRAPPE_VERSION}
|
||||
image: localhost:5000/frappe/erpnext:${ERPNEXT_VERSION}
|
||||
|
||||
websocket:
|
||||
image: localhost:5000/frappe/frappe-socketio:${FRAPPE_VERSION}
|
||||
image: localhost:5000/frappe/erpnext:${ERPNEXT_VERSION}
|
||||
|
||||
queue-short:
|
||||
image: localhost:5000/frappe/frappe-worker:${FRAPPE_VERSION}
|
||||
|
||||
queue-default:
|
||||
image: localhost:5000/frappe/frappe-worker:${FRAPPE_VERSION}
|
||||
image: localhost:5000/frappe/erpnext:${ERPNEXT_VERSION}
|
||||
|
||||
queue-long:
|
||||
image: localhost:5000/frappe/frappe-worker:${FRAPPE_VERSION}
|
||||
image: localhost:5000/frappe/erpnext:${ERPNEXT_VERSION}
|
||||
|
||||
scheduler:
|
||||
image: localhost:5000/frappe/frappe-worker:${FRAPPE_VERSION}
|
||||
image: localhost:5000/frappe/erpnext:${ERPNEXT_VERSION}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
from dataclasses import dataclass
|
||||
@@ -22,12 +23,27 @@ def _add_version_var(name: str, env_path: Path):
|
||||
f.write(f"\n{name}={os.environ[name]}")
|
||||
|
||||
|
||||
def _add_sites_var(env_path: Path):
|
||||
with open(env_path, "r+") as f:
|
||||
content = f.read()
|
||||
content = re.sub(
|
||||
rf"SITES=.*",
|
||||
f"SITES=`tests.localhost`,`test-erpnext-site.localhost`,`test-pg-site.localhost`",
|
||||
content,
|
||||
)
|
||||
f.seek(0)
|
||||
f.truncate()
|
||||
f.write(content)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def env_file(tmp_path_factory: pytest.TempPathFactory):
|
||||
tmp_path = tmp_path_factory.mktemp("frappe-docker")
|
||||
file_path = tmp_path / ".env"
|
||||
shutil.copy("example.env", file_path)
|
||||
|
||||
_add_sites_var(file_path)
|
||||
|
||||
for var in ("FRAPPE_VERSION", "ERPNEXT_VERSION"):
|
||||
_add_version_var(name=var, env_path=file_path)
|
||||
|
||||
@@ -52,14 +68,14 @@ def frappe_setup(compose: Compose):
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def frappe_site(compose: Compose):
|
||||
site_name = "tests"
|
||||
site_name = "tests.localhost"
|
||||
compose.bench(
|
||||
"new-site",
|
||||
# TODO: change to --mariadb-user-host-login-scope=%
|
||||
"--no-mariadb-socket",
|
||||
"--db-root-password=123",
|
||||
"--admin-password=admin",
|
||||
site_name,
|
||||
"--mariadb-root-password",
|
||||
"123",
|
||||
"--admin-password",
|
||||
"admin",
|
||||
)
|
||||
compose("restart", "backend")
|
||||
yield site_name
|
||||
@@ -68,11 +84,7 @@ def frappe_site(compose: Compose):
|
||||
@pytest.fixture(scope="class")
|
||||
def erpnext_setup(compose: Compose):
|
||||
compose.stop()
|
||||
|
||||
args = ["-f", "overrides/compose.erpnext.yaml"]
|
||||
if CI:
|
||||
args += ("-f", "tests/compose.ci-erpnext.yaml")
|
||||
compose(*args, "up", "-d", "--quiet-pull")
|
||||
compose("up", "-d", "--quiet-pull")
|
||||
|
||||
yield
|
||||
compose.stop()
|
||||
@@ -80,23 +92,16 @@ def erpnext_setup(compose: Compose):
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def erpnext_site(compose: Compose):
|
||||
site_name = "test_erpnext_site"
|
||||
site_name = "test-erpnext-site.localhost"
|
||||
args = [
|
||||
"new-site",
|
||||
# TODO: change to --mariadb-user-host-login-scope=%
|
||||
"--no-mariadb-socket",
|
||||
"--db-root-password=123",
|
||||
"--admin-password=admin",
|
||||
"--install-app=erpnext",
|
||||
site_name,
|
||||
"--mariadb-root-password",
|
||||
"123",
|
||||
"--admin-password",
|
||||
"admin",
|
||||
"--install-app",
|
||||
"erpnext",
|
||||
]
|
||||
erpnext_version = os.environ.get("ERPNEXT_VERSION")
|
||||
if erpnext_version in [
|
||||
"develop",
|
||||
"version-14",
|
||||
] or erpnext_version.startswith("v14"):
|
||||
args.append("--install-app=payments")
|
||||
compose.bench(*args)
|
||||
compose("restart", "backend")
|
||||
yield site_name
|
||||
@@ -146,6 +151,7 @@ def s3_service(python_path: str, compose: Compose):
|
||||
subprocess.check_call(cmd)
|
||||
|
||||
compose("cp", "tests/_create_bucket.py", "backend:/tmp")
|
||||
compose.exec("backend", "bench", "pip", "install", "boto3~=1.34.143")
|
||||
compose.exec(
|
||||
"-e",
|
||||
f"S3_ACCESS_KEY={access_key}",
|
||||
|
||||
@@ -10,7 +10,6 @@ from tests.utils import Compose, check_url_content
|
||||
BACKEND_SERVICES = (
|
||||
"backend",
|
||||
"queue-short",
|
||||
"queue-default",
|
||||
"queue-long",
|
||||
"scheduler",
|
||||
)
|
||||
@@ -39,7 +38,7 @@ def assets_cb(text: str):
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("url", "callback"), (("/", index_cb), ("/api/method/version", api_cb))
|
||||
("url", "callback"), (("/", index_cb), ("/api/method/ping", api_cb))
|
||||
)
|
||||
def test_endpoints(url: str, callback: Any, frappe_site: str):
|
||||
check_url_content(
|
||||
@@ -89,44 +88,31 @@ def test_frappe_connections_in_backends(
|
||||
):
|
||||
filename = "_ping_frappe_connections.py"
|
||||
compose("cp", f"tests/{filename}", f"{service}:/tmp/")
|
||||
compose.exec(service, python_path, f"/tmp/{filename}")
|
||||
compose.exec(
|
||||
"-w",
|
||||
"/home/frappe/frappe-bench/sites",
|
||||
service,
|
||||
python_path,
|
||||
f"/tmp/{filename}",
|
||||
)
|
||||
|
||||
|
||||
def test_push_backup(
|
||||
python_path: str,
|
||||
frappe_site: str,
|
||||
s3_service: S3ServiceResult,
|
||||
compose: Compose,
|
||||
):
|
||||
restic_password = "secret"
|
||||
compose.bench("--site", frappe_site, "backup", "--with-files")
|
||||
compose.exec(
|
||||
"backend",
|
||||
"push-backup",
|
||||
"--site",
|
||||
frappe_site,
|
||||
"--bucket",
|
||||
"frappe",
|
||||
"--region-name",
|
||||
"us-east-1",
|
||||
"--endpoint-url",
|
||||
"http://minio:9000",
|
||||
"--aws-access-key-id",
|
||||
s3_service.access_key,
|
||||
"--aws-secret-access-key",
|
||||
s3_service.secret_key,
|
||||
)
|
||||
compose("cp", "tests/_check_backup_files.py", "backend:/tmp")
|
||||
compose.exec(
|
||||
"-e",
|
||||
f"S3_ACCESS_KEY={s3_service.access_key}",
|
||||
"-e",
|
||||
f"S3_SECRET_KEY={s3_service.secret_key}",
|
||||
"-e",
|
||||
f"SITE_NAME={frappe_site}",
|
||||
"backend",
|
||||
python_path,
|
||||
"/tmp/_check_backup_files.py",
|
||||
)
|
||||
restic_args = [
|
||||
"--env=RESTIC_REPOSITORY=s3:http://minio:9000/frappe",
|
||||
f"--env=AWS_ACCESS_KEY_ID={s3_service.access_key}",
|
||||
f"--env=AWS_SECRET_ACCESS_KEY={s3_service.secret_key}",
|
||||
f"--env=RESTIC_PASSWORD={restic_password}",
|
||||
]
|
||||
compose.exec(*restic_args, "backend", "restic", "init")
|
||||
compose.exec(*restic_args, "backend", "restic", "backup", "sites")
|
||||
compose.exec(*restic_args, "backend", "restic", "snapshots")
|
||||
|
||||
|
||||
def test_https(frappe_site: str, compose: Compose):
|
||||
@@ -140,7 +126,7 @@ class TestErpnext:
|
||||
("url", "callback"),
|
||||
(
|
||||
(
|
||||
"/api/method/erpnext.templates.pages.product_search.get_product_list",
|
||||
"/api/method/erpnext.templates.pages.search_help.get_help_results_sections?text=help",
|
||||
api_cb,
|
||||
),
|
||||
("/assets/erpnext/js/setup_wizard.js", assets_cb),
|
||||
@@ -157,7 +143,7 @@ class TestPostgres:
|
||||
def test_site_creation(self, compose: Compose):
|
||||
compose.bench(
|
||||
"new-site",
|
||||
"test_pg_site",
|
||||
"test-pg-site.localhost",
|
||||
"--db-type",
|
||||
"postgres",
|
||||
"--admin-password",
|
||||
|
||||
Reference in New Issue
Block a user