Compare commits
854 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9d3d9e60f5 | ||
|
ab158fb939 | ||
|
4e3e3e14e5 | ||
|
9f3f74a998 | ||
|
fdd230f353 | ||
|
a9f0d021b1 | ||
|
68203f47be | ||
|
afb85aaab6 | ||
|
c51d02a8af | ||
|
4d66b6ba7b | ||
|
0ccf48690a | ||
|
71e469174f | ||
|
ada5b36a62 | ||
|
0d997c9479 | ||
|
6da97d22f4 | ||
|
6582e3a9e0 | ||
|
61d37b436d | ||
|
9a450730db | ||
|
8681ecade3 | ||
|
0ef86fc2a0 | ||
|
0c4b82f80c | ||
|
d3aa7d9a52 | ||
|
8d815bc317 | ||
|
ff8091bef2 | ||
|
6860b5d57a | ||
|
b1a581d98a | ||
|
913a1adae4 | ||
|
65ba0d2de2 | ||
|
c4cc7279fc | ||
|
8fdb6f52d9 | ||
|
2b490e59a7 | ||
|
e02d4274ee | ||
|
66fce06e44 | ||
|
01971f572b | ||
|
a0c66ca650 | ||
|
fbefe52abb | ||
|
674e63a392 | ||
|
62d4dec1fd | ||
|
3b2373f6f1 | ||
|
d21635ee55 | ||
|
5050bf69f9 | ||
|
ebb79f03b8 | ||
|
10c988127d | ||
|
c273e7f105 | ||
|
7f47c3c8af | ||
|
c99958893e | ||
|
7d1a6fff6e | ||
|
37a0a6ad8f | ||
|
dc930ff36a | ||
|
297f657dc5 | ||
|
4e3e347982 | ||
|
8d463941be | ||
|
34bb401da5 | ||
|
2055eab620 | ||
|
19291d0c1b | ||
|
72803f9f27 | ||
|
a7dd62bc77 | ||
|
9cba06595e | ||
|
8c778477a0 | ||
|
f4dc508c19 | ||
|
eb45eb3a4b | ||
|
0a73b8857d | ||
|
575a56b767 | ||
|
2ce2b748b8 | ||
|
b3cde02330 | ||
|
32645429b2 | ||
|
06ad1978a4 | ||
|
59748f12a2 | ||
|
44e912b239 | ||
|
f5311ae0db | ||
|
1c31d30e66 | ||
|
33bf0d4a65 | ||
|
2bdd170601 | ||
|
d6e26c614c | ||
|
2d95071b6a | ||
|
4eb2085023 | ||
|
509cddc5ff | ||
|
4a5e9d4271 | ||
|
e0d6eaa130 | ||
|
c594817743 | ||
|
e8c66bfad6 | ||
|
00c58e7a9e | ||
|
8b332f9ccc | ||
|
72906bff8d | ||
|
d58df5c70c | ||
|
51928b0075 | ||
|
6941021caa | ||
|
8eccaa61e9 | ||
|
14dfc82a48 | ||
|
39d3eaf801 | ||
|
7e443ac40d | ||
|
ad6cc4fcc7 | ||
|
748340f751 | ||
|
06b021df23 | ||
|
cfb786ab2d | ||
|
c475ff63e6 | ||
|
edd19fc0d0 | ||
|
ea79dfb85d | ||
|
97b999b519 | ||
|
758b7d0f73 | ||
|
4cd4ddb1e7 | ||
|
8ccc3588e7 | ||
|
021a305084 | ||
|
2a47100d6a | ||
|
df07422adb | ||
|
b51fdb46f3 | ||
|
67009fb857 | ||
|
ef30dbc193 | ||
|
f6507f995a | ||
|
a6338e2880 | ||
|
1e0f71450a | ||
|
bb23afcc60 | ||
|
8de5edfab7 | ||
|
8414200262 | ||
|
63c8f8ec58 | ||
|
6988a0fdd1 | ||
|
d7eb539277 | ||
|
982e425d9a | ||
|
2bca024daf | ||
|
43a633fb38 | ||
|
97d9f5a75c | ||
|
95f95c0946 | ||
|
78ba4ea7cf | ||
|
e9723bd7dc | ||
|
82eeca8f57 | ||
|
a1945245b8 | ||
|
e192227297 | ||
|
faf43bd3b0 | ||
|
1d46872fd2 | ||
|
68a995cee7 | ||
|
98d7532d0d | ||
|
e4ddee2604 | ||
|
b3ef12c3fd | ||
|
3affd546e9 | ||
|
272a5ee940 | ||
|
74ef1ae56e | ||
|
0f0b458a19 | ||
|
b3879e13b6 | ||
|
bfd09fa61d | ||
|
b5b74bec9d | ||
|
41eddd813c | ||
|
808e21209a | ||
|
58e4e89e48 | ||
|
b2067ce1f6 | ||
|
99f1b11e75 | ||
|
2dae85807f | ||
|
c575422d9c | ||
|
42f7819374 | ||
|
64f36d910d | ||
|
189bfb2a7a | ||
|
cac8530481 | ||
|
69944e4028 | ||
|
a410d2682b | ||
|
5ce39d36e2 | ||
|
947b37524f | ||
|
17f66fb467 | ||
|
2a21b2adf3 | ||
|
a5dd3017d7 | ||
|
3ab0dcb9e6 | ||
|
cbe32cbc15 | ||
|
18ce1f2fe4 | ||
|
7972a75915 | ||
|
220b8296a9 | ||
|
c2265c3f11 | ||
|
ee8de62770 | ||
|
b2a22a6993 | ||
|
39a8b46854 | ||
|
11b2091e32 | ||
|
2d6b9c1767 | ||
|
6dbb5baf81 | ||
|
b3c3c4a2cc | ||
|
253b69ffe3 | ||
|
016502aad0 | ||
|
5c426257fb | ||
|
b8747996c9 | ||
|
bcc4f6bf1a | ||
|
9d2c1f134c | ||
|
30d3ba3ba8 | ||
|
1fdbd0b326 | ||
|
fb15f21bf5 | ||
|
9fa04be2a3 | ||
|
86f846aba3 | ||
|
e73e13a6e9 | ||
|
2d9a09f792 | ||
|
1d3a215c00 | ||
|
af997dbc51 | ||
|
4bb8671f26 | ||
|
6d62660d26 | ||
|
93688e18a1 | ||
|
d6ae056c94 | ||
|
87ae658773 | ||
|
35743eb905 | ||
|
185a13a2e1 | ||
|
2233c3422a | ||
|
e7f7d5b394 | ||
|
de0093aa8d | ||
|
91bdcb860c | ||
|
f042ca9b90 | ||
|
ca69eb31d3 | ||
|
2e276cbb09 | ||
|
cadc8c0cc1 | ||
|
6771732414 | ||
|
5c5cd266f2 | ||
|
c8aff67064 | ||
|
35efd44a0e | ||
|
a6ac7fe354 | ||
|
a302acc228 | ||
|
a1b1945908 | ||
|
19016b8ab1 | ||
|
6257c1c7ac | ||
|
586045143f | ||
|
b3c5c9685b | ||
|
e2234170a1 | ||
|
9feab4c0c3 | ||
|
ef0384d6ff | ||
|
5326882908 | ||
|
a392a93bdd | ||
|
d7ed08110f | ||
|
de2ae942a7 | ||
|
b02c4b93bb | ||
|
0887691cc4 | ||
|
1c093d71de | ||
|
cb29a86a65 | ||
|
c3da9c5bef | ||
|
679fe5f86b | ||
|
c9cdf293ef | ||
|
3377ffc38a | ||
|
465d8075b5 | ||
|
fa9e86e188 | ||
|
28dce8c51a | ||
|
ff5357cdc0 | ||
|
f42abe18ad | ||
|
ea1ed85694 | ||
|
f984361d9c | ||
|
ddd6987852 | ||
|
aac0355183 | ||
|
5e237ee112 | ||
|
2399909489 | ||
|
540e53eb5f | ||
|
732e0c43be | ||
|
a96666329f | ||
|
80b4820263 | ||
|
b4c58ed929 | ||
|
4e7c3ee9fd | ||
|
bfabdfc6c7 | ||
|
3ffecf5703 | ||
|
f13627a32a | ||
|
9cde932c9a | ||
|
a373e24b2c | ||
|
7076b7ca95 | ||
|
9cd3d90e1c | ||
|
40e5c8da6b | ||
|
c669565edf | ||
|
d91a49e65d | ||
|
bc7a23ddde | ||
|
eb4f0e9c06 | ||
|
3e3314a45e | ||
|
5f7188a647 | ||
|
c788bd499f | ||
|
91385ba8d7 | ||
|
cbf4caf336 | ||
|
fa318a64ce | ||
|
d247c288b1 | ||
|
2381be6921 | ||
|
d385c44404 | ||
|
bda4e0a71c | ||
|
7b718f6e78 | ||
|
9df3b18c31 | ||
|
561460ec7f | ||
|
928859ca76 | ||
|
4b14d20c5e | ||
|
bbab35e4ba | ||
|
a44bad6033 | ||
|
059467725d | ||
|
86bf655842 | ||
|
1850c62d8b | ||
|
45addca89d | ||
|
50734c20cc | ||
|
13d6fc2bf3 | ||
|
6f06182877 | ||
|
71ed7a8618 | ||
|
cad1e03908 | ||
|
4fe5fcf6db | ||
|
0f67506985 | ||
|
ac1a875f42 | ||
|
d629ca932c | ||
|
ca04b245f3 | ||
|
56a7614fd7 | ||
|
4da32ab84e | ||
|
9b22498765 | ||
|
8f2df6a842 | ||
|
e987db46c7 | ||
|
b903c69fac | ||
|
c8668857d1 | ||
|
d7bc3370df | ||
|
95182abd61 | ||
|
67f9c092d2 | ||
|
d5f17dda9c | ||
|
8b4719a13e | ||
|
5a3b762857 | ||
|
c0d7e262b0 | ||
|
be1e16984c | ||
|
380786219e | ||
|
23a9914721 | ||
|
146ec62a0a | ||
|
c70a6a5e80 | ||
|
9d9e9281d5 | ||
|
7a61f07dfd | ||
|
93d139d4bf | ||
|
3ce022bee4 | ||
|
608f87a36c | ||
|
609b39b802 | ||
|
b4a1b9528a | ||
|
c26999ec65 | ||
|
5ef246479b | ||
|
3772bda438 | ||
|
143b656e83 | ||
|
a8039dc076 | ||
|
08a14c80db | ||
|
506e88debe | ||
|
21a7b211d2 | ||
|
12fe99f699 | ||
|
429d528d64 | ||
|
9466fc395b | ||
|
3b6d2404e5 | ||
|
d9cdf1731f | ||
|
31df4da136 | ||
|
0e0f223bdd | ||
|
346d86af82 | ||
|
88d882b5b8 | ||
|
bb849b86de | ||
|
c0d79463ff | ||
|
e4e9cd8197 | ||
|
4a6896d892 | ||
|
bb137c16d6 | ||
|
0e8f55798c | ||
|
ff0e8f7140 | ||
|
a0480f8d11 | ||
|
7b72a5a910 | ||
|
74758fb1d8 | ||
|
b8ca06a42c | ||
|
67e8f52cd6 | ||
|
13104809c1 | ||
|
5929ecbc2e | ||
|
85d868423a | ||
|
478d44c299 | ||
|
c06ac5b020 | ||
|
c9ac12fdd7 | ||
|
04258b9f9b | ||
|
29eb3398a2 | ||
|
ee09612326 | ||
|
6069f91cce | ||
|
c886b72ae7 | ||
|
87e5bb7025 | ||
|
e63d5c32e3 | ||
|
627b93b95b | ||
|
3e32f88c15 | ||
|
17028c5c63 | ||
|
5006f6edc9 | ||
|
986c3e2305 | ||
|
b4a8069cfd | ||
|
1890557d40 | ||
|
f8b5cbc657 | ||
|
d7c667835a | ||
|
33e4f33afe | ||
|
0453b0c002 | ||
|
16e6131f96 | ||
|
b6dc65cc64 | ||
|
e734f52f07 | ||
|
7879506910 | ||
|
8aeeb7e09c | ||
|
19f3f329f2 | ||
|
fd2d3dea10 | ||
|
b46a7b0fa8 | ||
|
f95c394e38 | ||
|
5419be7d31 | ||
|
7541433729 | ||
|
1161f925a4 | ||
|
7eeccfb5f5 | ||
|
3ba5365537 | ||
|
e56918bf47 | ||
|
34fd1dcb70 | ||
|
7d32179038 | ||
|
9c492056d9 | ||
|
7453e70fa0 | ||
|
e1f07420be | ||
|
c0fb64714f | ||
|
3de28377a1 | ||
|
a6c6fde867 | ||
|
883b358427 | ||
|
fd037fcb4d | ||
|
871d293719 | ||
|
55684ebd49 | ||
|
5fdede8511 | ||
|
020f6d1137 | ||
|
2f442d7db3 | ||
|
fe901ab9de | ||
|
a7da98acc1 | ||
|
dbb528aba4 | ||
|
45316bf752 | ||
|
98d756243f | ||
|
435de0f21f | ||
|
b4f905c2aa | ||
|
d9314b7507 | ||
|
72c417da62 | ||
|
b879a9d17b | ||
|
3024bb257c | ||
|
3f1dd832e4 | ||
|
417feeb0f3 | ||
|
6674e267de | ||
|
31fdcc60af | ||
|
66bef2e14f | ||
|
52cf0cc1ca | ||
|
a2a3c022b8 | ||
|
cb1b22f54e | ||
|
8345b7ab93 | ||
|
6a3138152f | ||
|
55bc4c63fa | ||
|
8e188dc946 | ||
|
0ae59a82a6 | ||
|
d27e8d5ba7 | ||
|
2e41de1af9 | ||
|
5e81a754c4 | ||
|
67358c6b7a | ||
|
9d1ce4717b | ||
|
bb907b4de3 | ||
|
5b5700eff4 | ||
|
ac3b4e3065 | ||
|
6ac1c0d732 | ||
|
9d5e94b8d5 | ||
|
9a63dc70ce | ||
|
ede972a1da | ||
|
b8f773a5ef | ||
|
9d6d486c65 | ||
|
4a808fa87b | ||
|
570639a114 | ||
|
63bc184eab | ||
|
eadb04fee9 | ||
|
4b8cf4ccc7 | ||
|
7f1162d29b | ||
|
b13d4da1b1 | ||
|
4dac13923f | ||
|
a4aa01987b | ||
|
2668633974 | ||
|
602c381d6e | ||
|
c014a05504 | ||
|
e5850eb02e | ||
|
30e133ce07 | ||
|
889725b2e0 | ||
|
8a305faa6d | ||
|
e57e6cb2a8 | ||
|
4dd8e87433 | ||
|
8b31237a7f | ||
|
4af9fed338 | ||
|
c4388e134c | ||
|
ec31b82c24 | ||
|
57cb956b76 | ||
|
7205c64e0c | ||
|
698da4b1ad | ||
|
74212da9e2 | ||
|
cb8d6b700e | ||
|
f6b7826520 | ||
|
322a9e6b48 | ||
|
5faf03e3b3 | ||
|
33c5c3269c | ||
|
8a990c2bf7 | ||
|
65c58c18f5 | ||
|
f9b4228033 | ||
|
79eca380f6 | ||
|
9a1cca6283 | ||
|
82f2a448d2 | ||
|
1e0c305508 | ||
|
a5262e4a53 | ||
|
669f836328 | ||
|
ec4063a29d | ||
|
3890a38ca1 | ||
|
e942fca24f | ||
|
bae65ff701 | ||
|
26c68c6b1d | ||
|
6fb0a6b855 | ||
|
4bbeb4b84f | ||
|
0ce57c9b99 | ||
|
4b015bdc4e | ||
|
d9c9ba43e5 | ||
|
57108bdf76 | ||
|
9c00a6ac0a | ||
|
78424776b6 | ||
|
ed45d4f3bb | ||
|
549922f0b9 | ||
|
f03f0ef77b | ||
|
9085005093 | ||
|
f6ecfd3e74 | ||
|
5ca30c7b66 | ||
|
bf1d03f1d4 | ||
|
b8ced0b432 | ||
|
b55df07eba | ||
|
5d08438a7b | ||
|
931d46e6b0 | ||
|
87132d27e6 | ||
|
4b587e41d4 | ||
|
85a44213b7 | ||
|
3290def1ef | ||
|
2e3e7703d3 | ||
|
1a4c904d7f | ||
|
514e3fce22 | ||
|
50210f1ab7 | ||
|
75ed5b4609 | ||
|
66d2f93abf | ||
|
5eec11f70b | ||
|
1956d89c7f | ||
|
db455a1379 | ||
|
d3e079320b | ||
|
51e93941e0 | ||
|
e15c107570 | ||
|
1ef5c37c71 | ||
|
13382c889b | ||
|
44db619451 | ||
|
0fdf8d8318 | ||
|
d77c2cae60 | ||
|
b6ec87d17c | ||
|
7d20329894 | ||
|
27449f4068 | ||
|
cb1311267f | ||
|
fde20f0e8a | ||
|
91673acc30 | ||
|
99a3da809e | ||
|
126c6062e5 | ||
|
853936c771 | ||
|
5a81b6a6cb | ||
|
224cd6c19f | ||
|
82c9dbc1eb | ||
|
658d6957db | ||
|
51d93cd54c | ||
|
34c8eeeaec | ||
|
d9486018a2 | ||
|
3839d37781 | ||
|
df0e26c58e | ||
|
07047288fc | ||
|
448bfc25d4 | ||
|
a6f136fd2c | ||
|
f147fa93f1 | ||
|
d5a790d5d0 | ||
|
5de7bec029 | ||
|
15b219e235 | ||
|
b70bbddce3 | ||
|
649244d6c4 | ||
|
060a0c7d32 | ||
|
b71ae61f09 | ||
|
bc17d47ebc | ||
|
b6999824a7 | ||
|
407a8566df | ||
|
cac9d14e01 | ||
|
5f4159c030 | ||
|
3ca4522f68 | ||
|
430f46d0a2 | ||
|
7f7138f050 | ||
|
16b2378165 | ||
|
c685466d8c | ||
|
eaa578eabd | ||
|
698f49854d | ||
|
11bfa96971 | ||
|
0b86568ad5 | ||
|
95ea214307 | ||
|
7822f4e217 | ||
|
f312134401 | ||
|
baf7d03cf8 | ||
|
83e757465d | ||
|
5aa1d53167 | ||
|
ffcaac5b82 | ||
|
0500bfe9a6 | ||
|
18d3b57748 | ||
|
cbe753bc73 | ||
|
b488771526 | ||
|
fa32269e91 | ||
|
a25ae73031 | ||
|
cd4ea1e50c | ||
|
43c5177187 | ||
|
c19880d859 | ||
|
cdcd5c7b3e | ||
|
6e2cef0db5 | ||
|
8779bbd8b6 | ||
|
76ef7de74f | ||
|
c6009abbcf | ||
|
dbe3571ed4 | ||
|
3e2c4aebf4 | ||
|
766f392189 | ||
|
a24b29271a | ||
|
f13c3db833 | ||
|
8e052f703f | ||
|
e991e841dc | ||
|
c63c952392 | ||
|
653e4dc57d | ||
|
b982da93af | ||
|
dde22f3832 | ||
|
6393de9862 | ||
|
49f11dd3a0 | ||
|
829e89ef3e | ||
|
38a67e6c64 | ||
|
0d2d6c58e8 | ||
|
87f8348a01 | ||
|
bb888a8af5 | ||
|
2d20a04363 | ||
|
79f14b7e7e | ||
|
a8d60d753b | ||
|
d63b9aced2 | ||
|
15bc7e5cc5 | ||
|
89520c016f | ||
|
9bc0dcaa19 | ||
|
b78feec895 | ||
|
e143d7efc2 | ||
|
156ef063ce | ||
|
1238abae2d | ||
|
ec2598e114 | ||
|
c6cfa24dcc | ||
|
b978f7dcf6 | ||
|
68b46aa720 | ||
|
89b4ce1202 | ||
|
02a1534b21 | ||
|
123786193e | ||
|
9a4ea986e1 | ||
|
b88e950f16 | ||
|
bc87f78866 | ||
|
36c1ac0aa4 | ||
|
f0aee87ed5 | ||
|
d4a47ded38 | ||
|
ba8a07c844 | ||
|
5958af86e3 | ||
|
7c3e54335d | ||
|
2bfd33820a | ||
|
bbba7b6c4e | ||
|
26119983d4 | ||
|
1172ab6f1f | ||
|
acc0ce0202 | ||
|
405aaac189 | ||
|
53524d9077 | ||
|
e9b38fc668 | ||
|
a675eb10fb | ||
|
d953e99ae2 | ||
|
71b4c14d69 | ||
|
83e09bb25b | ||
|
c707f998e8 | ||
|
c9ac20f4aa | ||
|
eb37991aa2 | ||
|
26ed1d77de | ||
|
61bde7300f | ||
|
d45b36394a | ||
|
53e7640889 | ||
|
20604df574 | ||
|
2a30d67a40 | ||
|
89d39633ae | ||
|
4dc772d1e4 | ||
|
1daba42f1e | ||
|
c61accedfc | ||
|
9fc928a711 | ||
|
59291eed52 | ||
|
70dbfcb6fb | ||
|
6b31985711 | ||
|
43d5da83e4 | ||
|
a9b9c4bfb6 | ||
|
37be2bf122 | ||
|
0a7bb50533 | ||
|
f2260006bd | ||
|
69ee69c462 | ||
|
ac39f9e6c3 | ||
|
48a4f75272 | ||
|
f4d866db81 | ||
|
4db6ccf315 | ||
|
699ef4c6d5 | ||
|
622e7cc097 | ||
|
80acddbe67 | ||
|
97f7d5a1e7 | ||
|
85bee0c3ad | ||
|
72843c06f9 | ||
|
2a794c37b8 | ||
|
dbb79c0fa0 | ||
|
c240aec711 | ||
|
4488f27603 | ||
|
34c8199eea | ||
|
94941d0387 | ||
|
553000e5f5 | ||
|
7188c3e49f | ||
|
4bfaa80bd3 | ||
|
14c40376ec | ||
|
7c157eccd5 | ||
|
62ddc01cdc | ||
|
d8d0560b9e | ||
|
7c1e85ef4b | ||
|
db818ab4a1 | ||
|
d82546adde | ||
|
c40e682f5d | ||
|
b18c9dc4f8 | ||
|
28edf106c4 | ||
|
3d92087f3a | ||
|
9446095665 | ||
|
0917da59bf | ||
|
dc5307ad75 | ||
|
c117a300aa | ||
|
0c1d4ebf51 | ||
|
7b5ec22e0c | ||
|
b8ab829c11 | ||
|
6f7457f6b3 | ||
|
9964315abe | ||
|
0658be86b8 | ||
|
ce5310db06 | ||
|
01b58d9d75 | ||
|
231bbbfe99 | ||
|
670552ca5f | ||
|
01fb62bf29 | ||
|
d960e18f28 | ||
|
bf40724dd7 | ||
|
1fe311d351 | ||
|
23c8e2cf92 | ||
|
1f46ed39c5 | ||
|
39ca60f168 | ||
|
e5b63604de | ||
|
3ad68ab782 | ||
|
dd2f7ce9cc | ||
|
e89ff49223 | ||
|
79c6d122e2 | ||
|
31eab76632 | ||
|
ddcfab2ddb | ||
|
092dfbff67 | ||
|
0052481d42 | ||
|
c152578783 | ||
|
c3e6f57b08 | ||
|
1f8555b80e | ||
|
e9e1df9e70 | ||
|
19157d94bc | ||
|
a46e4d24b4 | ||
|
5604cd4bf8 | ||
|
a074ff34c7 | ||
|
eb47a5870a | ||
|
d4fe1260c1 | ||
|
23847f6441 | ||
|
55582575f0 | ||
|
9a695da768 | ||
|
852759ae35 | ||
|
1ddf6b1271 | ||
|
ffd32a2ee3 | ||
|
a4dcb9eaf2 | ||
|
aa931b33f4 | ||
|
0c3a160fce | ||
|
e0335bbd2b | ||
|
ddbfdb919e | ||
|
1a5c6da972 | ||
|
0a4a2bbac2 | ||
|
0153e7a690 | ||
|
72baae0e27 | ||
|
f251cd7a3e | ||
|
7a3026efe6 | ||
|
485f47f6d0 | ||
|
d9d08ad0f7 | ||
|
791bca1564 | ||
|
999c23d1ff | ||
|
10ea9dfc99 | ||
|
0a2abe32dc | ||
|
f852896192 | ||
|
c9a08a5e18 | ||
|
c9bde8b1ec | ||
|
d73d6732d5 | ||
|
02536e0f9f | ||
|
7a01f9d009 | ||
|
079fb1c9ec | ||
|
43259d5506 | ||
|
0a8d92f8d4 | ||
|
da7d95dea5 | ||
|
78117d16f2 | ||
|
a095a6fbc7 | ||
|
2549aef4dc | ||
|
fb2aeefd91 | ||
|
ee6d63e6e0 | ||
|
01d374d2b1 | ||
|
f6789150ee | ||
|
f9402f7879 | ||
|
e19f334ebb | ||
|
4f034f83d9 | ||
|
8cb74b202a | ||
|
eed13cdeed | ||
|
c3c914f22f | ||
|
030d56f9af | ||
|
33ca30d6c3 | ||
|
4dd0fb6d8a | ||
|
a6af8a4e0f | ||
|
c0a59cd55e | ||
|
3f4c1263e6 | ||
|
d5e8f1488c | ||
|
2f24a40d99 | ||
|
07ae8d472f | ||
|
b57c65440a | ||
|
e14f660cb1 | ||
|
df6a1c3b4e | ||
|
5a4e3792f4 | ||
|
72d01a05dc | ||
|
d4dc02b0fd | ||
|
e3788257db | ||
|
c1e63c873a | ||
|
e7b455485d | ||
|
c670059523 | ||
|
791c0161df | ||
|
926263deb3 | ||
|
530b26a5ff | ||
|
72cf5fe30e | ||
|
9754719277 | ||
|
bf5d2b80d8 | ||
|
9263add948 | ||
|
22d42cea45 | ||
|
922c8bb373 | ||
|
010279a0d9 | ||
|
b651ebc3c4 | ||
|
eef3c15c17 | ||
|
750bdca04d | ||
|
b1e91e3626 | ||
|
6d18ccc632 | ||
|
ca3aabedce | ||
|
d3bfc4fb87 | ||
|
1411e5a0ff | ||
|
f5650d8a29 | ||
|
4812bff8bd | ||
|
ecf26fccda | ||
|
c33bd7c892 | ||
|
eeb08f63d9 | ||
|
41b65b80d5 | ||
|
4bf61d6d35 | ||
|
f1773c0ba9 | ||
|
c678638ea2 | ||
|
8be574aac8 | ||
|
db5852e9c4 | ||
|
4f8f0fe006 | ||
|
6f04223d01 | ||
|
665905037c | ||
|
8cb402e0fb | ||
|
14d8276529 | ||
|
c15eec53dc | ||
|
b6aefdab7d | ||
|
b8729d4478 | ||
|
b4ee6760eb | ||
|
5cc64bf9ee | ||
|
d6af5ef55b | ||
|
ef189e7f67 | ||
|
d49d166383 | ||
|
31a41f81c3 | ||
|
e7d3e722ce | ||
|
775a20b156 | ||
|
ceddc11c29 | ||
|
915551268c | ||
|
4a451844e6 | ||
|
9b6b770dd7 | ||
|
900572fdae | ||
|
e990291d16 | ||
|
a79d194bbc | ||
|
58c77ad3fb | ||
|
1b9334e37e | ||
|
4cc48c966f | ||
|
553b08636a |
42
.coveragerc
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
# .coveragerc to control coverage.py
|
||||||
|
[run]
|
||||||
|
branch = True
|
||||||
|
source =
|
||||||
|
./tutor
|
||||||
|
./bin
|
||||||
|
omit =
|
||||||
|
*/templates/*
|
||||||
|
|
||||||
|
[report]
|
||||||
|
# Regexes for lines to exclude from consideration
|
||||||
|
exclude_lines =
|
||||||
|
# Have to re-enable the standard pragma
|
||||||
|
pragma: no cover
|
||||||
|
|
||||||
|
# Don't complain about missing debug-only code:
|
||||||
|
def __repr__
|
||||||
|
if self\.debug
|
||||||
|
|
||||||
|
# Don't complain if tests don't hit defensive assertion code:
|
||||||
|
raise AssertionError
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
# Don't complain if non-runnable code isn't run:
|
||||||
|
if 0:
|
||||||
|
if __name__ == .__main__.:
|
||||||
|
|
||||||
|
# Don't complain about abstract methods, they aren't run:
|
||||||
|
@(abc\.)?abstractmethod
|
||||||
|
|
||||||
|
ignore_errors = True
|
||||||
|
show_missing = True
|
||||||
|
skip_empty = True
|
||||||
|
precision = 2
|
||||||
|
|
||||||
|
[html]
|
||||||
|
skip_empty = True
|
||||||
|
show_contexts = True
|
||||||
|
|
||||||
|
[json]
|
||||||
|
pretty_print = True
|
||||||
|
show_contexts = True
|
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -7,7 +7,7 @@ assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<!-- Are you quite sure that you followed the instructions from the Troubleshooting section in the Tutor documentation? https://docs.tutor.overhang.io/troubleshooting.html -->
|
<!-- Are you quite sure that you followed the instructions from the Troubleshooting section in the Tutor documentation? https://docs.tutor.edly.io/troubleshooting.html -->
|
||||||
<!-- If not, please take the time to read them. -->
|
<!-- If not, please take the time to read them. -->
|
||||||
|
|
||||||
**Bug description**
|
**Bug description**
|
||||||
|
|
2
.github/ISSUE_TEMPLATE/question.md
vendored
|
@ -4,6 +4,6 @@ about: This is not the appropriate channel
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Please post on our forums: https://discuss.overhang.io for questions about using `tutor`.
|
Please post on our forums: https://discuss.openedx.org/tag/tutor for questions about using `tutor`.
|
||||||
|
|
||||||
Posts that are not a bug report or a feature/enhancement request will not be addressed on this issue tracker.
|
Posts that are not a bug report or a feature/enhancement request will not be addressed on this issue tracker.
|
||||||
|
|
20
.github/workflows/auto-add-to-project.yml
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
name: Auto Add Issues and Pull Requests to Project
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
issues:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# https://github.com/actions/add-to-project
|
||||||
|
add-to-project:
|
||||||
|
name: Add issue and bugs to project
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/add-to-project@v0.5.0
|
||||||
|
with:
|
||||||
|
project-url: https://github.com/orgs/overhangio/projects/4
|
||||||
|
github-token: ${{ secrets.GH_PROJECT_PERSONAL_ACCESS_TOKEN }}
|
107
.github/workflows/release.yml
vendored
|
@ -1,9 +1,16 @@
|
||||||
|
# This script can be tested locally with act:
|
||||||
|
#
|
||||||
|
# act --secret GITHUB_TOKEN=... --job release
|
||||||
|
#
|
||||||
|
# https://github.com/nektos/act/
|
||||||
|
# To generate a token: https://github.com/settings/tokens (add r/w permissions for "Contents")
|
||||||
name: Release
|
name: Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- '*'
|
- '*'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
|
@ -11,60 +18,78 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-latest
|
# https://github.com/actions/runner-images#available-images
|
||||||
|
# It's important that we build the tutor binaries with the *oldest* possible
|
||||||
|
# OS releases and Python version. See these docs for more information:
|
||||||
|
# https://pyinstaller.org/en/stable/usage.html#making-gnu-linux-apps-forward-compatible
|
||||||
|
- os: ubuntu-20.04
|
||||||
locale: C.UTF-8
|
locale: C.UTF-8
|
||||||
- os: macos-10.15
|
# https://endoflife.date/macos
|
||||||
|
- os: macos-11
|
||||||
locale: en_US.UTF-8
|
locale: en_US.UTF-8
|
||||||
env:
|
env:
|
||||||
LC_ALL: ${{ matrix.locale }}
|
LC_ALL: ${{ matrix.locale }}
|
||||||
LANG: ${{ matrix.locale }}
|
LANG: ${{ matrix.locale }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
##### Setup environment
|
||||||
|
# https://github.com/actions/checkout
|
||||||
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v2
|
# https://github.com/actions/setup-python
|
||||||
|
uses: actions/setup-python@v3
|
||||||
with:
|
with:
|
||||||
python-version: 3.6
|
python-version: 3.8
|
||||||
- name: Upgrade pip
|
cache: 'pip'
|
||||||
run: python -m pip install --upgrade pip setuptools==44.0.0
|
cache-dependency-path: requirements/dev.txt
|
||||||
|
- name: Upgrade pip and setuptools
|
||||||
|
# https://pypi.org/project/pip/
|
||||||
|
# https://pypi.org/project/setuptools/
|
||||||
|
# https://pypi.org/project/wheel/
|
||||||
|
run: python -m pip install --upgrade pip setuptools==65.6.3 wheel
|
||||||
- name: Print info about the current python installation
|
- name: Print info about the current python installation
|
||||||
run: make ci-info
|
run: make ci-info
|
||||||
- name: Install requirements
|
- name: Install requirements
|
||||||
run: make bootstrap-dev-plugins
|
run: make bootstrap-dev-plugins
|
||||||
- name: Run tests
|
|
||||||
run: make tests
|
##### Run tests, generate bundle
|
||||||
|
# - name: Run tests
|
||||||
|
# run: make test
|
||||||
- name: Create bundle
|
- name: Create bundle
|
||||||
run: make bundle
|
run: make bundle
|
||||||
- name: Test bundle
|
# - name: Test bundle
|
||||||
run: make ci-test-bundle
|
# run: make ci-test-bundle
|
||||||
- name: Get release description
|
|
||||||
id: release-description
|
##### Download gh utility: https://github.com/cli/cli/releases
|
||||||
# We must escape multi-line string, as per:
|
# This is unnecessary on GitHub, but useful when running locally with act.
|
||||||
# https://medium.com/agorapulse-stories/how-to-work-with-multiline-string-variables-in-github-actions-23f56447d209
|
# WARNING: this will only work on amd64
|
||||||
|
- name: Check or download gh utility
|
||||||
run: |
|
run: |
|
||||||
make release-description
|
if ! which gh; then
|
||||||
cat release_description.md
|
echo "Downloading 'gh' utility"
|
||||||
description="$(cat release_description.md)"
|
if [ "$(uname -s)" = "Linux" ]; then
|
||||||
description="${description//'%'/'%25'}"
|
curl -L -o gh.tar.gz https://github.com/cli/cli/releases/download/v2.28.0/gh_2.28.0_linux_amd64.tar.gz
|
||||||
description="${description//$'\n'/'%0A'}"
|
tar xzf gh.tar.gz
|
||||||
description="${description//$'\r'/'%0D'}"
|
mv ./gh_2.28.0_linux_amd64/bin/gh /usr/local/bin/gh
|
||||||
echo "::set-output name=text::$description"
|
else
|
||||||
shell: bash
|
curl -L -o gh.zip https://github.com/cli/cli/releases/download/v2.28.0/gh_2.28.0_macOS_amd64.zip
|
||||||
- name: Get release file name
|
unzip xzf gh.zip
|
||||||
id: release-file
|
mv ./gh_2.28.0_macOS_amd64/bin/gh /usr/local/bin/gh
|
||||||
run: echo "::set-output name=filename::tutor-$(uname -s)_$(uname -m)"
|
fi
|
||||||
shell: bash
|
which gh
|
||||||
- name: Debug release variables
|
fi
|
||||||
|
|
||||||
|
##### Create release on GitHub
|
||||||
|
- name: Create or update GitHub release
|
||||||
|
run: scriv github-release --repo=overhangio/tutor
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ github.token }}
|
||||||
|
# scriv command will fail when not on a tag, such as running with act or a
|
||||||
|
# manual trigger.
|
||||||
|
if: ${{ github.ref_type == 'tag' }}
|
||||||
|
- name: Upload release asset to GitHub
|
||||||
run: |
|
run: |
|
||||||
echo "Publish file '${{ steps.release-file.outputs.filename }}' to release ${{ github.ref }}"
|
export FILENAME="tutor-$(uname -s)_$(uname -m)"
|
||||||
echo "================"
|
mv ./dist/tutor $FILENAME
|
||||||
echo "${{ steps.release-description.outputs.text }}"
|
gh release upload --clobber v$(make version) $FILENAME
|
||||||
- name: Upload bundle
|
env:
|
||||||
# https://github.com/marketplace/actions/upload-files-to-a-github-release
|
GH_TOKEN: ${{ github.token }}
|
||||||
uses: svenstaro/upload-release-action@v2
|
|
||||||
with:
|
|
||||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
file: ./dist/tutor
|
|
||||||
asset_name: "${{ steps.release-file.outputs.filename }}"
|
|
||||||
tag: ${{ github.ref }}
|
|
||||||
overwrite: true
|
|
||||||
body: "${{ steps.release-description.outputs.text }}"
|
|
||||||
|
|
8
.github/workflows/sync.yml
vendored
|
@ -2,14 +2,16 @@ name: Sync with private repo
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches: [ master, main, nightly ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
sync:
|
sync:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
- name: Add remote
|
- name: Add remote
|
||||||
run: git remote add overhangio https://${{ secrets.GIT_USERNAME }}:${{ secrets.GIT_PASSWORD }}@git.overhang.io/core/tutor.git
|
run: git remote add overhangio https://${{ secrets.GIT_USERNAME }}:${{ secrets.GIT_PASSWORD }}@git.overhang.io/core/tutor.git
|
||||||
- name: Push
|
- name: Push
|
||||||
run: git push overhangio master
|
run: git push overhangio $GITHUB_REF
|
||||||
|
|
18
.github/workflows/test.yml
vendored
|
@ -2,21 +2,23 @@ name: Run tests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches: [ master, main, nightly ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
branches: [ master, main, nightly ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
tests:
|
tests:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v3
|
||||||
with:
|
with:
|
||||||
python-version: 3.9
|
python-version: 3.8
|
||||||
|
cache: 'pip'
|
||||||
|
cache-dependency-path: requirements/dev.txt
|
||||||
- name: Upgrade pip
|
- name: Upgrade pip
|
||||||
run: python -m pip install --upgrade pip setuptools==44.0.0
|
run: python -m pip install --upgrade pip setuptools
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pip install -r requirements/dev.txt
|
run: pip install -r requirements/dev.txt
|
||||||
- name: Static code analysis
|
- name: Static code analysis
|
||||||
|
@ -29,3 +31,7 @@ jobs:
|
||||||
run: make test-format
|
run: make test-format
|
||||||
- name: Package tests
|
- name: Package tests
|
||||||
run: make test-pythonpackage
|
run: make test-pythonpackage
|
||||||
|
- name: Install docs dependencies
|
||||||
|
run: pip install -r requirements/docs.txt
|
||||||
|
- name: Build docs
|
||||||
|
run: make docs
|
||||||
|
|
5
.gitignore
vendored
|
@ -6,4 +6,7 @@ __pycache__
|
||||||
|
|
||||||
/build/
|
/build/
|
||||||
/dist/
|
/dist/
|
||||||
/release_description.md
|
|
||||||
|
# Unit test/ coverage reports
|
||||||
|
.coverage
|
||||||
|
/htmlcov/
|
||||||
|
|
841
CHANGELOG.md
|
@ -1,4 +1,4 @@
|
||||||
Contribution Guidelines
|
Contribution Guidelines
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
Please check the relevant section of the Tutor docs: `https://docs.tutor.overhang.io/tutor.html#contributing <https://docs.tutor.overhang.io/tutor.html#contributing>`__.
|
Please check the relevant section of the Tutor docs: `https://docs.tutor.edly.io/tutor.html#contributing <https://docs.tutor.edly.io/tutor.html#contributing>`__.
|
||||||
|
|
|
@ -10,13 +10,13 @@
|
||||||
# Because this image is still experimental, and we are not quite sure if it's going to
|
# Because this image is still experimental, and we are not quite sure if it's going to
|
||||||
# be very useful, we do not provide any usage documentation.
|
# be very useful, we do not provide any usage documentation.
|
||||||
|
|
||||||
FROM docker.io/python:3.7-slim-stretch
|
FROM docker.io/python:3.8-slim-stretch
|
||||||
|
|
||||||
# As per https://github.com/docker/compose/issues/3918
|
# As per https://github.com/docker/compose/issues/3918
|
||||||
COPY --from=library/docker:19.03 /usr/local/bin/docker /usr/bin/docker
|
COPY --from=library/docker:19.03 /usr/local/bin/docker /usr/bin/docker
|
||||||
COPY --from=docker/compose:1.24.0 /usr/local/bin/docker-compose /usr/bin/docker-compose
|
COPY --from=docker/compose:1.24.0 /usr/local/bin/docker-compose /usr/bin/docker-compose
|
||||||
|
|
||||||
RUN pip install tutor-openedx
|
RUN pip install tutor
|
||||||
RUN mkdir /opt/tutor
|
RUN mkdir /opt/tutor
|
||||||
ENV TUTOR_ROOT /opt/tutor
|
ENV TUTOR_ROOT /opt/tutor
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,5 @@
|
||||||
include requirements/base.in
|
include requirements/base.in
|
||||||
|
include requirements/plugins.txt
|
||||||
|
include requirements/dev.txt
|
||||||
recursive-include tutor/templates *
|
recursive-include tutor/templates *
|
||||||
|
include tutor/py.typed
|
||||||
|
|
88
Makefile
|
@ -1,11 +1,11 @@
|
||||||
.DEFAULT_GOAL := help
|
.DEFAULT_GOAL := help
|
||||||
.PHONY: docs
|
.PHONY: docs
|
||||||
SRC_DIRS = ./tutor ./tests ./bin
|
SRC_DIRS = ./tutor ./tests ./bin ./docs
|
||||||
BLACK_OPTS = --exclude templates ${SRC_DIRS}
|
BLACK_OPTS = --exclude templates ${SRC_DIRS}
|
||||||
|
|
||||||
###### Development
|
###### Development
|
||||||
|
|
||||||
docs: ## Build html documentation
|
docs: ## Build HTML documentation
|
||||||
$(MAKE) -C docs
|
$(MAKE) -C docs
|
||||||
|
|
||||||
compile-requirements: ## Compile requirements files
|
compile-requirements: ## Compile requirements files
|
||||||
|
@ -18,66 +18,79 @@ upgrade-requirements: ## Upgrade requirements files
|
||||||
pip-compile --upgrade requirements/dev.in
|
pip-compile --upgrade requirements/dev.in
|
||||||
pip-compile --upgrade requirements/docs.in
|
pip-compile --upgrade requirements/docs.in
|
||||||
|
|
||||||
build-pythonpackage: ## Build a python package ready to upload to pypi
|
build-pythonpackage: build-pythonpackage-tutor ## Build Python packages ready to upload to pypi
|
||||||
|
|
||||||
|
build-pythonpackage-tutor: ## Build the "tutor" python package for upload to pypi
|
||||||
python setup.py sdist
|
python setup.py sdist
|
||||||
|
|
||||||
push-pythonpackage: ## Push python package to pypi
|
push-pythonpackage: ## Push python package to pypi
|
||||||
twine upload --skip-existing dist/tutor-openedx-$(shell make version).tar.gz
|
twine upload --skip-existing dist/tutor-$(shell make version).tar.gz
|
||||||
|
|
||||||
test: test-lint test-unit test-types test-format test-pythonpackage ## Run all tests by decreasing order or priority
|
test: test-lint test-unit test-types test-format test-pythonpackage ## Run all tests by decreasing order of priority
|
||||||
|
|
||||||
|
test-static: test-lint test-types test-format ## Run only static tests
|
||||||
|
|
||||||
test-format: ## Run code formatting tests
|
test-format: ## Run code formatting tests
|
||||||
black --check --diff $(BLACK_OPTS)
|
black --check --diff $(BLACK_OPTS)
|
||||||
|
|
||||||
test-lint: ## Run code linting tests
|
test-lint: ## Run code linting tests
|
||||||
pylint --errors-only --enable=unused-import --ignore=templates ${SRC_DIRS}
|
pylint --errors-only --enable=unused-import,unused-argument --ignore=templates --ignore=docs/_ext ${SRC_DIRS}
|
||||||
|
|
||||||
test-unit: ## Run unit tests
|
test-unit: ## Run unit tests
|
||||||
python -m unittest discover tests
|
python -m unittest discover tests
|
||||||
|
|
||||||
test-types: ## Check type definitions
|
test-types: ## Check type definitions
|
||||||
mypy --exclude=templates --ignore-missing-imports --strict tutor/ tests/
|
mypy --exclude=templates --ignore-missing-imports --implicit-reexport --strict ${SRC_DIRS}
|
||||||
|
|
||||||
test-pythonpackage: build-pythonpackage ## Test that package can be uploaded to pypi
|
test-pythonpackage: build-pythonpackage ## Test that package can be uploaded to pypi
|
||||||
twine check dist/tutor-openedx-$(shell make version).tar.gz
|
twine check dist/tutor-$(shell make version).tar.gz
|
||||||
|
|
||||||
|
test-k8s: ## Validate the k8s format with kubectl. Not part of the standard test suite.
|
||||||
|
tutor k8s apply --dry-run=client --validate=true
|
||||||
|
|
||||||
format: ## Format code automatically
|
format: ## Format code automatically
|
||||||
black $(BLACK_OPTS)
|
black $(BLACK_OPTS)
|
||||||
|
|
||||||
|
isort: ## Sort imports. This target is not mandatory because the output may be incompatible with black formatting. Provided for convenience purposes.
|
||||||
|
isort --skip=templates ${SRC_DIRS}
|
||||||
|
|
||||||
|
changelog-entry: ## Create a new changelog entry
|
||||||
|
scriv create
|
||||||
|
|
||||||
|
changelog: ## Collect changelog entries in the CHANGELOG.md file
|
||||||
|
scriv collect
|
||||||
|
|
||||||
|
###### Code coverage
|
||||||
|
|
||||||
|
coverage: ## Run unit-tests before analyzing code coverage and generate report
|
||||||
|
$(MAKE) --keep-going coverage-tests coverage-report
|
||||||
|
|
||||||
|
coverage-tests: ## Run unit-tests and analyze code coverage
|
||||||
|
coverage run -m unittest discover
|
||||||
|
|
||||||
|
coverage-report: ## Generate CLI report for the code coverage
|
||||||
|
coverage report
|
||||||
|
|
||||||
|
coverage-html: coverage-report ## Generate HTML report for the code coverage
|
||||||
|
coverage html
|
||||||
|
|
||||||
|
coverage-browse-report: coverage-html ## Open the HTML report in the browser
|
||||||
|
sensible-browser htmlcov/index.html
|
||||||
|
|
||||||
|
###### Continuous integration tasks
|
||||||
|
|
||||||
|
bundle: ## Bundle the tutor package in a single "dist/tutor" executable
|
||||||
|
pyinstaller tutor.spec
|
||||||
|
|
||||||
bootstrap-dev: ## Install dev requirements
|
bootstrap-dev: ## Install dev requirements
|
||||||
pip install .
|
pip install .
|
||||||
pip install -r requirements/dev.txt
|
pip install -r requirements/dev.txt
|
||||||
|
|
||||||
bootstrap-dev-plugins: bootstrap-dev ## Install dev requirement and all supported plugins
|
bootstrap-dev-plugins: bootstrap-dev ## Install dev requirements and all supported plugins
|
||||||
pip install -r requirements/plugins.txt
|
pip install -r requirements/plugins.txt
|
||||||
|
|
||||||
###### Deployment
|
|
||||||
|
|
||||||
bundle: ## Bundle the tutor package in a single "dist/tutor" executable
|
|
||||||
pyinstaller tutor.spec
|
|
||||||
|
|
||||||
release: test release-unsafe ## Create a release tag and push it to origin
|
|
||||||
release-unsafe:
|
|
||||||
$(MAKE) release-tag release-push TAG=v$(shell make version)
|
|
||||||
release-tag:
|
|
||||||
@echo "=== Creating tag $(TAG)"
|
|
||||||
git tag -d $(TAG) || true
|
|
||||||
git tag $(TAG)
|
|
||||||
release-push:
|
|
||||||
@echo "=== Pushing tag $(TAG) to origin"
|
|
||||||
git push origin
|
|
||||||
git push origin :$(TAG) || true
|
|
||||||
git push origin $(TAG)
|
|
||||||
|
|
||||||
release-description: ## Write the current release description to a file
|
|
||||||
sed "s/TUTOR_VERSION/v$(shell make version)/g" docs/_release_description.md > release_description.md
|
|
||||||
git log -1 --pretty=format:%b >> release_description.md
|
|
||||||
|
|
||||||
###### Continuous integration tasks
|
|
||||||
|
|
||||||
pull-base-images: # Manually pull base images
|
pull-base-images: # Manually pull base images
|
||||||
docker image pull docker.io/ubuntu:20.04
|
docker image pull docker.io/ubuntu:20.04
|
||||||
docker image pull docker.io/python:3.7-alpine
|
|
||||||
|
|
||||||
ci-info: ## Print info about environment
|
ci-info: ## Print info about environment
|
||||||
python --version
|
python --version
|
||||||
|
@ -90,8 +103,7 @@ ci-test-bundle: ## Run basic tests on bundle
|
||||||
yes "" | ./dist/tutor config save --interactive
|
yes "" | ./dist/tutor config save --interactive
|
||||||
./dist/tutor config save
|
./dist/tutor config save
|
||||||
./dist/tutor plugins list
|
./dist/tutor plugins list
|
||||||
# ./dist/tutor plugins enable discovery ecommerce figures license minio notes xqueue
|
./dist/tutor plugins enable android discovery ecommerce forum license mfe minio notes webui xqueue
|
||||||
./dist/tutor plugins enable discovery ecommerce license minio notes xqueue
|
|
||||||
./dist/tutor plugins list
|
./dist/tutor plugins list
|
||||||
./dist/tutor license --help
|
./dist/tutor license --help
|
||||||
|
|
||||||
|
@ -102,10 +114,10 @@ ci-bootstrap-images:
|
||||||
###### Additional commands
|
###### Additional commands
|
||||||
|
|
||||||
version: ## Print the current tutor version
|
version: ## Print the current tutor version
|
||||||
@python -c 'import io, os; about = {}; exec(io.open(os.path.join("tutor", "__about__.py"), "rt", encoding="utf-8").read(), about); print(about["__version__"])'
|
@python -c 'import io, os; about = {}; exec(io.open(os.path.join("tutor", "__about__.py"), "rt", encoding="utf-8").read(), about); print(about["__package_version__"])'
|
||||||
|
|
||||||
ESCAPE =
|
ESCAPE =
|
||||||
help: ## Print this help
|
help: ## Print this help
|
||||||
@grep -E '^([a-zA-Z_-]+:.*?## .*|######* .+)$$' Makefile \
|
@grep -E '^([a-zA-Z_-]+:.*?## .*|######* .+)$$' Makefile \
|
||||||
| sed 's/######* \(.*\)/\n $(ESCAPE)[1;31m\1$(ESCAPE)[0m/g' \
|
| sed 's/######* \(.*\)/@ $(ESCAPE)[1;31m\1$(ESCAPE)[0m/g' | tr '@' '\n' \
|
||||||
| awk 'BEGIN {FS = ":.*?## "}; {printf "\033[33m%-30s\033[0m %s\n", $$1, $$2}'
|
| awk 'BEGIN {FS = ":.*?## "}; {printf "\033[33m%-30s\033[0m %s\n", $$1, $$2}'
|
||||||
|
|
83
README.rst
|
@ -1,10 +1,6 @@
|
||||||
.. _readme_intro_start:
|
Tutor: the Docker-based Open edX distribution designed for peace of mind
|
||||||
|
|
||||||
Tutor: the docker-based Open edX distribution designed for peace of mind
|
|
||||||
========================================================================
|
========================================================================
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
.. image:: https://overhang.io/static/img/tutor-logo.svg
|
.. image:: https://overhang.io/static/img/tutor-logo.svg
|
||||||
:alt: Tutor logo
|
:alt: Tutor logo
|
||||||
:width: 500px
|
:width: 500px
|
||||||
|
@ -12,66 +8,80 @@ Tutor: the docker-based Open edX distribution designed for peace of mind
|
||||||
|
|
||||||
|
|
|
|
||||||
|
|
||||||
.. image:: https://github.com/overhangio/tutor/actions/workflows/release.yml/badge.svg
|
.. _readme_intro_start:
|
||||||
:alt: Release build status
|
|
||||||
:target: https://github.com/overhangio/tutor/actions/workflows/release.yml
|
|
||||||
|
|
||||||
.. image:: https://img.shields.io/badge/docs-current-blue.svg?style=flat-square
|
.. image:: https://img.shields.io/static/v1?logo=github&label=Git&style=flat-square&color=brightgreen&message=Source%20code
|
||||||
:alt: Documentation
|
:alt: Source code
|
||||||
:target: https://docs.tutor.overhang.io
|
:target: https://github.com/overhangio/tutor
|
||||||
|
|
||||||
.. image:: https://img.shields.io/github/issues/overhangio/tutor.svg?style=flat-square
|
.. image:: https://img.shields.io/static/v1?logo=discourse&label=Forums&style=flat-square&color=ff0080&message=discuss.openedx.org
|
||||||
:alt: GitHub issues
|
:alt: Forums
|
||||||
:target: https://github.com/overhangio/tutor/issues
|
:target: https://discuss.openedx.org/tag/tutor
|
||||||
|
|
||||||
.. image:: https://img.shields.io/github/issues-closed/overhangio/tutor.svg?colorB=brightgreen&style=flat-square
|
.. image:: https://img.shields.io/static/v1?logo=readthedocs&label=Documentation&style=flat-square&color=blue&message=docs.tutor.edly.io
|
||||||
:alt: GitHub closed issues
|
:alt: Documentation
|
||||||
:target: https://github.com/overhangio/tutor/issues?q=is%3Aclosed
|
:target: https://docs.tutor.edly.io
|
||||||
|
|
||||||
|
.. image:: https://img.shields.io/pypi/v/tutor?logo=python&logoColor=white
|
||||||
|
:alt: PyPI releases
|
||||||
|
:target: https://pypi.org/project/tutor
|
||||||
|
|
||||||
.. image:: https://img.shields.io/github/license/overhangio/tutor.svg?style=flat-square
|
.. image:: https://img.shields.io/github/license/overhangio/tutor.svg?style=flat-square
|
||||||
:alt: AGPL License
|
:alt: AGPL License
|
||||||
:target: https://www.gnu.org/licenses/agpl-3.0.en.html
|
:target: https://www.gnu.org/licenses/agpl-3.0.en.html
|
||||||
|
|
||||||
**Tutor** is a docker-based `Open edX <https://openedx.org>`_ distribution, both for production and local development. The goal of Tutor is to make it easy to deploy, customize, upgrade and scale Open edX. Tutor is reliable, fast, extensible, and it is already used by dozens of Open edX platforms around the world.
|
.. image:: https://img.shields.io/static/v1?logo=twitter&label=Twitter&style=flat-square&color=1d9bf0&message=@overhangio
|
||||||
|
:alt: Follow us on Twitter
|
||||||
|
:target: https://twitter.com/overhangio/
|
||||||
|
|
||||||
Do you need professional assistance setting up or managing your Open edX platform? Overhang.IO provides online support as part of its `Long Term Support (LTS) offering <https://overhang.io/tutor/pricing>`__.
|
.. image:: https://img.shields.io/static/v1?logo=youtube&label=YouTube&style=flat-square&color=ff0000&message=@overhangio
|
||||||
|
:alt: Follow us on Youtube
|
||||||
|
:target: https://www.youtube.com/c/OverhangIO
|
||||||
|
|
||||||
|
**Tutor** is the official Docker-based `Open edX <https://openedx.org>`_ distribution, both for production and local development. The goal of Tutor is to make it easy to deploy, customise, upgrade and scale Open edX. Tutor is reliable, fast, extensible, and it is already used to deploy hundreds of Open edX platforms around the world.
|
||||||
|
|
||||||
|
Do you need professional assistance setting up or managing your Open edX platform? `Edly <https://edly.io>`__ provides online support as part of its `Open edX installation service <https://edly.io/services/open-edx-installation/>`__.
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
|
||||||
* 100% `open source <https://github.com/overhangio/tutor>`__
|
* 100% `open source <https://github.com/overhangio/tutor>`__
|
||||||
* Runs entirely on Docker
|
* Runs entirely on Docker
|
||||||
* World-famous 1-click `installation and upgrades <https://docs.tutor.overhang.io/install.html>`__
|
* World-famous 1-click `installation and upgrades <https://docs.tutor.edly.io/install.html>`__
|
||||||
* Comes with batteries included: `theming <https://github.com/overhangio/indigo>`__, `SCORM <https://github.com/overhangio/openedx-scorm-xblock>`__, `HTTPS <https://docs.tutor.overhang.io/configuration.html#ssl-tls-certificates-for-https-access>`__, `web-based administration interface <https://docs.tutor.overhang.io/extra.html#web-ui>`__, `mobile app <https://docs.tutor.overhang.io/extra.html#mobile-android-application>`__, `custom translations <https://docs.tutor.overhang.io/configuration.html#adding-custom-translations>`__...
|
* Comes with batteries included: `theming <https://github.com/overhangio/indigo>`__, `SCORM <https://github.com/overhangio/openedx-scorm-xblock>`__, `HTTPS <https://docs.tutor.edly.io/configuration.html#ssl-tls-certificates-for-https-access>`__, `web-based administration interface <https://github.com/overhangio/tutor-webui>`__, `mobile app <https://github.com/overhangio/tutor-android>`__, `custom translations <https://docs.tutor.edly.io/configuration.html#adding-custom-translations>`__...
|
||||||
* Extensible architecture with `plugins <https://docs.tutor.overhang.io/plugins.html>`__
|
* Extensible architecture with `plugins <https://docs.tutor.edly.io/plugins/index.html>`__
|
||||||
* Works with `Kubernetes <https://docs.tutor.overhang.io/k8s.html>`__
|
* Works with `Kubernetes <https://docs.tutor.edly.io/k8s.html>`__
|
||||||
* No technical skill required with the `1-click Tutor AWS image <https://docs.tutor.overhang.io/install.html#cloud-deployment>`__
|
* No technical skill required with the `zero-click Tutor AWS image <https://docs.tutor.edly.io/install.html#zero-click-aws-installation>`__
|
||||||
* Amazing plugins available with `Tutor Wizard Edition <https://overhang.io/tutor>`__
|
|
||||||
|
|
||||||
.. _readme_intro_end:
|
.. _readme_intro_end:
|
||||||
|
|
||||||
.. image:: ./docs/img/quickstart.gif
|
.. image:: ./docs/img/launch.webp
|
||||||
:alt: Tutor local quickstart
|
:alt: Tutor local launch
|
||||||
:target: https://terminalizer.com/view/91b0bfdd557
|
:target: https://www.terminalizer.com/view/3a8d55835686
|
||||||
|
|
||||||
Quickstart
|
Quickstart
|
||||||
----------
|
----------
|
||||||
|
|
||||||
1. Install the `latest stable release <https://github.com/overhangio/tutor/releases>`_ of Tutor
|
1. Install the `latest stable release <https://github.com/overhangio/tutor/releases>`_ of Tutor
|
||||||
2. Run ``tutor local quickstart``
|
2. Run ``tutor local launch``
|
||||||
3. You're done!
|
3. You're done!
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
Extensive documentation is available online: https://docs.tutor.overhang.io/
|
Extensive documentation is available: https://docs.tutor.edly.io/
|
||||||
|
|
||||||
|
Is there a problem?
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Please follow the instructions from the `troubleshooting section <https://docs.tutor.edly.io/troubleshooting.html>`__ in the docs.
|
||||||
|
|
||||||
.. _readme_support_start:
|
.. _readme_support_start:
|
||||||
|
|
||||||
Support
|
Support
|
||||||
-------
|
-------
|
||||||
|
|
||||||
To get community support, go to the official discussion forums: https://discuss.overhang.io. For official support, please subscribe to a Long Term Support (LTS) license at https://overhang.io/tutor/pricing.
|
To get community support, go to the official Open edX discussion forum: https://discuss.openedx.org. For official support, `Edly <https://edly.io>`__ provides professional assistance as part of its `Open edX installation service <https://edly.io/services/open-edx-installation/>`__.
|
||||||
|
|
||||||
.. _readme_support_end:
|
.. _readme_support_end:
|
||||||
|
|
||||||
|
@ -80,6 +90,11 @@ To get community support, go to the official discussion forums: https://discuss.
|
||||||
Contributing
|
Contributing
|
||||||
------------
|
------------
|
||||||
|
|
||||||
We welcome contributions to Tutor! To learn how you can contribute, please check the relevant section of the Tutor docs: `https://docs.tutor.overhang.io/tutor.html#contributing <https://docs.tutor.overhang.io/tutor.html#contributing>`__.
|
We welcome contributions to Tutor! To learn how you can contribute, please check the relevant section of the Tutor docs: `https://docs.tutor.edly.io/tutor.html#contributing <https://docs.tutor.edly.io/tutor.html#contributing>`__.
|
||||||
|
|
||||||
.. _readme_contributing_end:
|
.. _readme_contributing_end:
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
This work is licensed under the terms of the `GNU Affero General Public License (AGPL) <https://github.com/overhangio/tutor/blob/master/LICENSE.txt>`_.
|
||||||
|
|
32
bin/main.py
|
@ -1,21 +1,17 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from tutor.plugins import OfficialPlugin
|
from tutor import hooks
|
||||||
|
|
||||||
# Manually install plugins (this is for creating the bundle)
|
|
||||||
for plugin_name in [
|
|
||||||
"discovery",
|
|
||||||
"ecommerce",
|
|
||||||
# "figures",
|
|
||||||
"license",
|
|
||||||
"minio",
|
|
||||||
"notes",
|
|
||||||
"xqueue",
|
|
||||||
]:
|
|
||||||
try:
|
|
||||||
OfficialPlugin.load(plugin_name)
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
from tutor.commands.cli import main
|
from tutor.commands.cli import main
|
||||||
|
from tutor.plugins.v0 import OfficialPlugin
|
||||||
|
|
||||||
main()
|
|
||||||
|
@hooks.Actions.CORE_READY.add()
|
||||||
|
def _discover_official_plugins() -> None:
|
||||||
|
# Manually discover plugins: that's because entrypoint plugins are not properly
|
||||||
|
# detected within the binary bundle.
|
||||||
|
with hooks.Contexts.PLUGINS.enter():
|
||||||
|
OfficialPlugin.discover_all()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Call the regular main function, which will not detect any entrypoint plugin
|
||||||
|
main()
|
||||||
|
|
1
changelog.d/20231214_110904_regis_rsa_error.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
- [Bugfix] Error "'Crypto.PublicKey.RSA.RsaKey object' has no attribute 'dq'" during `tutor config save` was caused by outdated minimum version of the pycryptodome package. To resolve this issue, run `pip install --upgrade pycryptodome`. (by @regisb)
|
|
@ -0,0 +1 @@
|
||||||
|
- [Feature] add `CONFIG_INTERACTIVE` action that allows tutor plugins to interact with the configuration at the time of the interactive questionnaire that happens during tutor local launch. (by @Alec4r).
|
8
changelog.d/scriv.ini
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
[scriv]
|
||||||
|
version = literal: tutor/__about__.py: __version__
|
||||||
|
categories =
|
||||||
|
format = md
|
||||||
|
md_header_level = 2
|
||||||
|
new_fragment_template = file: changelog.d/scriv/new_fragment.${config:format}.j2
|
||||||
|
entry_title_template = file: changelog.d/scriv/entry_title.${config:format}.j2
|
||||||
|
ghrel_template = file: changelog.d/scriv/github_release.${config:format}.j2
|
1
changelog.d/scriv/entry_title.md.j2
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{% if version %}v{{ version }} {% endif %}({{ date.strftime('%Y-%m-%d') }})
|
14
changelog.d/scriv/github_release.md.j2
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
Install this version from pip with:
|
||||||
|
|
||||||
|
pip install "tutor[full]=={{ version.vtext[1:] }}"
|
||||||
|
|
||||||
|
Or download the compiled binaries:
|
||||||
|
|
||||||
|
sudo curl -L "https://github.com/overhangio/tutor/releases/download/{{ version }}/tutor-$(uname -s)_$(uname -m)" -o /usr/local/bin/tutor
|
||||||
|
sudo chmod 0755 /usr/local/bin/tutor
|
||||||
|
|
||||||
|
See the [installation docs](https://docs.tutor.edly.io/install.html) for more installation options and instructions.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
{{ body }}
|
11
changelog.d/scriv/new_fragment.md.j2
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<!--
|
||||||
|
Create a changelog entry for every new user-facing change. Please respect the following instructions:
|
||||||
|
- Indicate breaking changes by prepending an explosion 💥 character.
|
||||||
|
- Prefix your changes with either [Bugfix], [Improvement], [Feature], [Security], [Deprecation].
|
||||||
|
- You may optionally append "(by @<author>)" at the end of the line, where "<author>" is either one (just one)
|
||||||
|
of your GitHub username, real name or affiliated organization. These affiliations will be displayed in
|
||||||
|
the release notes for every release.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- - 💥[Feature] Foobarize the blorginator. This breaks plugins by renaming the `FOO_DO` filter to `BAR_DO`. (by @regisb) -->
|
||||||
|
<!-- - [Improvement] This is a non-breaking change. Life is good. (by @billgates) -->
|
|
@ -1,11 +1,17 @@
|
||||||
.DEFAULT_GOAL := html
|
.DEFAULT_GOAL := html
|
||||||
.PHONY: help
|
.PHONY: help
|
||||||
|
|
||||||
|
build:
|
||||||
|
sphinx-build -b html -a -E -n $(BUILD_ARGS) "." "_build/html"
|
||||||
|
|
||||||
html:
|
html:
|
||||||
sphinx-build -b html -a -E "." "_build/html"
|
$(MAKE) build BUILD_ARGS="-W --keep-going"
|
||||||
|
|
||||||
browse:
|
browse:
|
||||||
sensible-browser _build/html/index.html
|
sensible-browser _build/html/index.html
|
||||||
|
|
||||||
watch: html browse
|
watch: build browse
|
||||||
while true; do inotifywait -e modify *.rst */*.rst ../*.rst conf.py; $(MAKE) html; done
|
while true; do $(MAKE) wait-for-change build || true; done
|
||||||
|
|
||||||
|
wait-for-change:
|
||||||
|
inotifywait -e modify $(shell find . -name "*.rst") ../*.rst ../tutor/hooks/*.py conf.py
|
||||||
|
|
14
docs/_ext/tutordocs.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
"""
|
||||||
|
This module is heavily inspired by Django's djangodocs.py:
|
||||||
|
https://github.com/django/django/blob/main/docs/_ext/djangodocs.py
|
||||||
|
"""
|
||||||
|
from sphinx.application import Sphinx
|
||||||
|
|
||||||
|
|
||||||
|
def setup(app: Sphinx) -> None:
|
||||||
|
# https://www.sphinx-doc.org/en/master/extdev/appapi.html#sphinx.application.Sphinx.add_crossref_type
|
||||||
|
app.add_crossref_type(
|
||||||
|
directivename="patch",
|
||||||
|
rolename="patch",
|
||||||
|
indextemplate="pair: %s; patch",
|
||||||
|
)
|
|
@ -1,8 +0,0 @@
|
||||||
Tutor can be installed simply by downloading the compiled binaries:
|
|
||||||
|
|
||||||
sudo curl -L "https://github.com/overhangio/tutor/releases/download/TUTOR_VERSION/tutor-$(uname -s)_$(uname -m)" -o /usr/local/bin/tutor
|
|
||||||
sudo chmod 0755 /usr/local/bin/tutor
|
|
||||||
|
|
||||||
See the [installation docs](https://docs.tutor.overhang.io/install.html) for more installation options and instructions.
|
|
||||||
|
|
||||||
## Changes
|
|
99
docs/conf.py
|
@ -1,5 +1,9 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
import docutils
|
import docutils
|
||||||
import docutils.parsers.rst
|
import docutils.parsers.rst
|
||||||
|
@ -7,8 +11,8 @@ import docutils.parsers.rst
|
||||||
# -- Project information -----------------------------------------------------
|
# -- Project information -----------------------------------------------------
|
||||||
|
|
||||||
project = "Tutor"
|
project = "Tutor"
|
||||||
copyright = ""
|
copyright = "" # pylint: disable=redefined-builtin
|
||||||
author = "Overhang.io"
|
author = "Overhang.IO"
|
||||||
|
|
||||||
# The short X.Y version
|
# The short X.Y version
|
||||||
version = ""
|
version = ""
|
||||||
|
@ -21,10 +25,49 @@ extensions = []
|
||||||
templates_path = ["_templates"]
|
templates_path = ["_templates"]
|
||||||
source_suffix = ".rst"
|
source_suffix = ".rst"
|
||||||
master_doc = "index"
|
master_doc = "index"
|
||||||
language = None
|
language = "en"
|
||||||
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
|
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
|
||||||
pygments_style = None
|
pygments_style = None
|
||||||
|
|
||||||
|
# Autodocumentation of modules
|
||||||
|
extensions.append("sphinx.ext.autodoc")
|
||||||
|
autodoc_typehints = "description"
|
||||||
|
# For the life of me I can't get the docs to compile in nitpicky mode without these
|
||||||
|
# ignore statements. You are most welcome to try and remove them.
|
||||||
|
# To make matters worse, some ignores are only required for some versions of Python,
|
||||||
|
# from 3.8 to 3.10...
|
||||||
|
nitpick_ignore = [
|
||||||
|
# Sphinx does not handle ParamSpec arguments
|
||||||
|
("py:class", "T.args"),
|
||||||
|
("py:class", "T.kwargs"),
|
||||||
|
("py:class", "T2.args"),
|
||||||
|
("py:class", "T2.kwargs"),
|
||||||
|
# Sphinx doesn't know about the following classes
|
||||||
|
("py:class", "click.Command"),
|
||||||
|
("py:class", "t.Any"),
|
||||||
|
("py:class", "t.Callable"),
|
||||||
|
("py:class", "t.Iterator"),
|
||||||
|
("py:class", "t.Optional"),
|
||||||
|
# python 3.10
|
||||||
|
("py:class", "NoneType"),
|
||||||
|
("py:class", "click.core.Command"),
|
||||||
|
]
|
||||||
|
# Resolve type aliases here
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#confval-autodoc_type_aliases
|
||||||
|
autodoc_type_aliases: dict[str, str] = {
|
||||||
|
# python 3.10
|
||||||
|
"T": "tutor.core.hooks.actions.T",
|
||||||
|
"T2": "tutor.core.hooks.filters.T2",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# -- Sphinx-Click configuration
|
||||||
|
# https://sphinx-click.readthedocs.io/
|
||||||
|
extensions.append("sphinx_click")
|
||||||
|
# This is to avoid the addition of the local username to the docs
|
||||||
|
os.environ["HOME"] = "~"
|
||||||
|
# Make sure that sphinx-click can find the tutor module
|
||||||
|
sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
|
||||||
|
|
||||||
# -- Options for HTML output -------------------------------------------------
|
# -- Options for HTML output -------------------------------------------------
|
||||||
html_theme = "sphinx_rtd_theme"
|
html_theme = "sphinx_rtd_theme"
|
||||||
|
@ -42,7 +85,7 @@ html_context = {
|
||||||
html_static_path = ["img"]
|
html_static_path = ["img"]
|
||||||
|
|
||||||
# Custom settings
|
# Custom settings
|
||||||
html_logo = "./img/tutor-logo.png"
|
html_logo = "https://overhang.io/static/img/tutor-logo.svg"
|
||||||
html_favicon = "./img/favicon.png"
|
html_favicon = "./img/favicon.png"
|
||||||
html_show_sourcelink = False
|
html_show_sourcelink = False
|
||||||
html_display_github = True
|
html_display_github = True
|
||||||
|
@ -55,49 +98,51 @@ html_show_copyright = False
|
||||||
|
|
||||||
# Custom variables
|
# Custom variables
|
||||||
here = os.path.abspath(os.path.dirname(__file__))
|
here = os.path.abspath(os.path.dirname(__file__))
|
||||||
about = {}
|
about: Dict[str, str] = {}
|
||||||
with io.open(
|
with io.open(
|
||||||
os.path.join(here, "..", "tutor", "__about__.py"), "rt", encoding="utf-8"
|
os.path.join(here, "..", "tutor", "__about__.py"), "rt", encoding="utf-8"
|
||||||
) as f:
|
) as f:
|
||||||
|
# pylint: disable=exec-used
|
||||||
exec(f.read(), about)
|
exec(f.read(), about)
|
||||||
rst_prolog = """
|
rst_prolog = f"""
|
||||||
.. |tutor_version| replace:: {}
|
.. |tutor_version| replace:: {about["__version__"]}
|
||||||
""".format(
|
"""
|
||||||
about["__version__"],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Custom directives
|
# Custom directives
|
||||||
def youtube(
|
def youtube(
|
||||||
_name,
|
_name: Any,
|
||||||
_args,
|
_args: Any,
|
||||||
_options,
|
_options: Any,
|
||||||
content,
|
content: List[str],
|
||||||
_lineno,
|
_lineno: Any,
|
||||||
_contentOffset,
|
_contentOffset: Any,
|
||||||
_blockText,
|
_blockText: Any,
|
||||||
_state,
|
_state: Any,
|
||||||
_stateMachine,
|
_stateMachine: Any,
|
||||||
):
|
) -> Any:
|
||||||
""" Restructured text extension for inserting youtube embedded videos """
|
"""Restructured text extension for inserting youtube embedded videos"""
|
||||||
if not content:
|
if not content:
|
||||||
return []
|
return []
|
||||||
video_id = content[0]
|
video_id = content[0]
|
||||||
return [
|
return [
|
||||||
docutils.nodes.raw(
|
docutils.nodes.raw(
|
||||||
"",
|
"",
|
||||||
"""
|
f"""
|
||||||
<iframe width="560" height="315"
|
<iframe width="560" height="315"
|
||||||
src="https://www.youtube-nocookie.com/embed/{video_id}"
|
src="https://www.youtube-nocookie.com/embed/{video_id}"
|
||||||
frameborder="0"
|
frameborder="0"
|
||||||
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen>
|
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen>
|
||||||
</iframe>""".format(
|
</iframe>""",
|
||||||
video_id=video_id
|
|
||||||
),
|
|
||||||
format="html",
|
format="html",
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
youtube.content = True
|
# Tutor's own extension
|
||||||
docutils.parsers.rst.directives.register_directive("youtube", youtube)
|
sys.path.append(os.path.join(os.path.dirname(__file__), "_ext"))
|
||||||
|
extensions.append("tutordocs")
|
||||||
|
|
||||||
|
|
||||||
|
setattr(youtube, "content", True)
|
||||||
|
docutils.parsers.rst.directives.register_directive("youtube", youtube) # type: ignore
|
||||||
|
|
|
@ -3,19 +3,19 @@
|
||||||
Configuration and customisation
|
Configuration and customisation
|
||||||
===============================
|
===============================
|
||||||
|
|
||||||
Tutor offers plenty of possibilities for platform customisation out of the box. There are two main ways in which the base Open edX installation can be customized:
|
Tutor offers plenty of possibilities for platform customisation out of the box. There are two main ways in which the base Open edX installation can be customised:
|
||||||
|
|
||||||
a. Modifying the Tutor :ref:`configuration parameters <configuration>`.
|
a. Modifying the Tutor :ref:`configuration parameters <configuration>`.
|
||||||
b. Modifying the :ref:`Open edX docker image <customise>` that runs the Open edX platform.
|
b. Modifying the :ref:`Open edX docker image <customise>` that runs the Open edX platform.
|
||||||
|
|
||||||
This section does not cover :ref:`plugin development <plugins>`. For simple changes, such as modifying the ``*.env.json`` files or the edx-platform settings, *you should not fork edx-platform or tutor*! Instead, you should create a simple :ref:`plugin for Tutor <plugins_yaml>`.
|
This section does not cover :ref:`plugin development <plugins>`. For simple changes, such as modifying the ``*.env.yml`` files or the edx-platform settings, *you should not fork edx-platform or tutor*! Instead, you should create a simple :ref:`plugin for Tutor <plugins_yaml>`.
|
||||||
|
|
||||||
.. _configuration:
|
.. _configuration:
|
||||||
|
|
||||||
Configuration
|
Configuration
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
With Tutor, all Open edX deployment parameters are stored in a single ``config.yml`` file. This is the file that is generated when you run ``tutor local quickstart`` or ``tutor config save``. To view the content of this file, run::
|
With Tutor, all Open edX deployment parameters are stored in a single ``config.yml`` file. This is the file that is generated when you run ``tutor local launch`` or ``tutor config save``. To view the content of this file, run::
|
||||||
|
|
||||||
cat "$(tutor config printroot)/config.yml"
|
cat "$(tutor config printroot)/config.yml"
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ Or from the system environment::
|
||||||
|
|
||||||
export TUTOR_PARAM1=VALUE1
|
export TUTOR_PARAM1=VALUE1
|
||||||
|
|
||||||
Once the base configuration is created or updated, the environment is automatically re-generated. The environment is the set of all files required to manage an Open edX platform: Dockerfile, ``lms.env.json``, settings files, etc. You can view the environment files in the ``env`` folder::
|
Once the base configuration is created or updated, the environment is automatically re-generated. The environment is the set of all files required to manage an Open edX platform: Dockerfile, ``lms.env.yml``, settings files, etc. You can view the environment files in the ``env`` folder::
|
||||||
|
|
||||||
ls "$(tutor config printroot)/env"
|
ls "$(tutor config printroot)/env"
|
||||||
|
|
||||||
|
@ -40,9 +40,6 @@ With an up-to-date environment, Tutor is ready to launch an Open edX platform an
|
||||||
Individual service activation
|
Individual service activation
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
- ``RUN_LMS`` (default: ``true``)
|
|
||||||
- ``RUN_CMS`` (default: ``true``)
|
|
||||||
- ``RUN_FORUM`` (default: ``true``)
|
|
||||||
- ``RUN_ELASTICSEARCH`` (default: ``true``)
|
- ``RUN_ELASTICSEARCH`` (default: ``true``)
|
||||||
- ``RUN_MONGODB`` (default: ``true``)
|
- ``RUN_MONGODB`` (default: ``true``)
|
||||||
- ``RUN_MYSQL`` (default: ``true``)
|
- ``RUN_MYSQL`` (default: ``true``)
|
||||||
|
@ -61,10 +58,48 @@ Custom images
|
||||||
*************
|
*************
|
||||||
|
|
||||||
- ``DOCKER_IMAGE_OPENEDX`` (default: ``"{{ DOCKER_REGISTRY }}overhangio/openedx:{{ TUTOR_VERSION }}"``)
|
- ``DOCKER_IMAGE_OPENEDX`` (default: ``"{{ DOCKER_REGISTRY }}overhangio/openedx:{{ TUTOR_VERSION }}"``)
|
||||||
- ``DOCKER_IMAGE_ANDROID`` (default: ``"{{ DOCKER_REGISTRY }}overhangio/openedx-android:{{ TUTOR_VERSION }}"``)
|
|
||||||
- ``DOCKER_IMAGE_FORUM`` (default: ``"{{ DOCKER_REGISTRY }}overhangio/openedx-forum:{{ TUTOR_VERSION }}"``)
|
|
||||||
|
|
||||||
These configuration parameters define which image to run for each service. By default, the docker image tag matches the Tutor version it was built with.
|
This configuration parameter defines the name of the Docker image to run for the lms and cms containers. By default, the Docker image tag matches the Tutor version it was built with.
|
||||||
|
|
||||||
|
- ``DOCKER_IMAGE_OPENEDX_DEV`` (default: ``"openedx-dev:{{ TUTOR_VERSION }}"``)
|
||||||
|
|
||||||
|
This configuration parameter defines the name of the Docker image to run the development version of the lms and cms containers. By default, the Docker image tag matches the Tutor version it was built with.
|
||||||
|
|
||||||
|
.. https://hub.docker.com/r/devture/exim-relay/tags
|
||||||
|
|
||||||
|
- ``DOCKER_IMAGE_CADDY`` (default: ``"docker.io/caddy:2.6.2"``)
|
||||||
|
|
||||||
|
This configuration parameter defines which Caddy Docker image to use.
|
||||||
|
|
||||||
|
- ``DOCKER_IMAGE_ELASTICSEARCH`` (default: ``"docker.io/elasticsearch:7.17.9"``)
|
||||||
|
|
||||||
|
This configuration parameter defines which Elasticsearch Docker image to use.
|
||||||
|
|
||||||
|
- ``DOCKER_IMAGE_MONGODB`` (default: ``"docker.io/mongo:4.4.22"``)
|
||||||
|
|
||||||
|
This configuration parameter defines which MongoDB Docker image to use.
|
||||||
|
|
||||||
|
.. https://hub.docker.com/_/mysql/tags?page=1&name=8.0
|
||||||
|
|
||||||
|
- ``DOCKER_IMAGE_MYSQL`` (default: ``"docker.io/mysql:8.1.0"``)
|
||||||
|
|
||||||
|
This configuration parameter defines which MySQL Docker image to use.
|
||||||
|
|
||||||
|
.. https://hub.docker.com/_/redis/tags
|
||||||
|
|
||||||
|
- ``DOCKER_IMAGE_REDIS`` (default: ``"docker.io/redis:7.0.11"``)
|
||||||
|
|
||||||
|
This configuration parameter defines which Redis Docker image to use.
|
||||||
|
|
||||||
|
.. https://hub.docker.com/r/devture/exim-relay/tags
|
||||||
|
|
||||||
|
- ``DOCKER_IMAGE_SMTP`` (default: ``"docker.io/devture/exim-relay:4.96-r1-0``)
|
||||||
|
|
||||||
|
This configuration parameter defines which Simple Mail Transfer Protocol (SMTP) Docker image to use.
|
||||||
|
|
||||||
|
- ``DOCKER_IMAGE_PERMISSIONS`` (default: ``"{{ DOCKER_REGISTRY }}overhangio/openedx-permissions:{{ TUTOR_VERSION }}"``)
|
||||||
|
|
||||||
|
This configuration parameter defines the Docker image to be used for setting file permissions. The default image sets all containers to be run as unprivileged users.
|
||||||
|
|
||||||
Custom registry
|
Custom registry
|
||||||
***************
|
***************
|
||||||
|
@ -77,22 +112,95 @@ You may want to pull/push images from/to a custom docker registry. For instance,
|
||||||
|
|
||||||
(the trailing ``/`` is important)
|
(the trailing ``/`` is important)
|
||||||
|
|
||||||
|
.. _openedx_configuration:
|
||||||
|
|
||||||
|
Compose
|
||||||
|
*******
|
||||||
|
|
||||||
|
- ``DOCKER_COMPOSE_VERSION`` (default: ``"3.7"``)
|
||||||
|
|
||||||
|
This configuration parameter sets the version of Docker Compose to be used to build all containers.
|
||||||
|
|
||||||
|
- ``DEV_PROJECT_NAME`` (default: ``"{{ TUTOR_APP }}_dev"``)
|
||||||
|
|
||||||
|
This configuration parameter sets the Development version of the Docker Compose project name.
|
||||||
|
|
||||||
|
- ``LOCAL_PROJECT_NAME`` (default: ``"{{ TUTOR_APP }}_local"``)
|
||||||
|
|
||||||
|
This configuration parameter sets the Local version of the Docker Compose project name.
|
||||||
|
|
||||||
Open edX customisation
|
Open edX customisation
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
- ``OPENEDX_COMMON_VERSION`` (default: ``"open-release/koa.2"``)
|
- ``EDX_PLATFORM_REPOSITORY`` (default: ``"https://github.com/openedx/edx-platform.git"``)
|
||||||
|
|
||||||
|
This defines the git repository from which you install Open edX platform code. If you run an Open edX fork with custom patches, set this to your own git repository. You may also override this configuration parameter at build time, by providing a ``--build-arg`` option.
|
||||||
|
|
||||||
|
- ``OPENEDX_COMMON_VERSION`` (default: ``"open-release/quince.1"``, or ``master`` in :ref:`nightly <nightly>`)
|
||||||
|
|
||||||
This defines the default version that will be pulled from all Open edX git repositories.
|
This defines the default version that will be pulled from all Open edX git repositories.
|
||||||
|
|
||||||
|
- ``EDX_PLATFORM_VERSION`` (default: the value of ``OPENEDX_COMMON_VERSION``)
|
||||||
|
|
||||||
|
This defines the version that will be pulled from just the Open edX platform git repositories. You may also override this configuration parameter at build time, by providing a ``--build-arg`` option.
|
||||||
|
|
||||||
- ``OPENEDX_CMS_UWSGI_WORKERS`` (default: ``2``)
|
- ``OPENEDX_CMS_UWSGI_WORKERS`` (default: ``2``)
|
||||||
- ``OPENEDX_LMS_UWSGI_WORKERS`` (default: ``2``)
|
- ``OPENEDX_LMS_UWSGI_WORKERS`` (default: ``2``)
|
||||||
|
|
||||||
By default there are 2 `uwsgi worker processes <https://uwsgi-docs.readthedocs.io/en/latest/Options.html#processes>`__ to serve requests for the LMS and the CMS. However, each workers requires upwards of 500 Mb of RAM. You should reduce this value to 1 if your computer/server does not have enough memory.
|
By default, there are 2 `uwsgi worker processes <https://uwsgi-docs.readthedocs.io/en/latest/Options.html#processes>`__ to serve requests for the LMS and the CMS. However, each worker requires upwards of 500 Mb of RAM. You should reduce this value to 1 if your computer/server does not have enough memory.
|
||||||
|
|
||||||
- ``OPENEDX_CELERY_REDIS_DB`` (default: ``0``)
|
- ``OPENEDX_CELERY_REDIS_DB`` (default: ``0``)
|
||||||
- ``OPENEDX_CACHE_REDIS_DB`` (default: ``1``)
|
- ``OPENEDX_CACHE_REDIS_DB`` (default: ``1``)
|
||||||
|
|
||||||
These two configuration parameters define which redis database to use for Open edX cache and celery task.
|
These two configuration parameters define which Redis database to use for Open edX cache and celery task.
|
||||||
|
|
||||||
|
.. _openedx_extra_pip_requirements:
|
||||||
|
|
||||||
|
- ``OPENEDX_EXTRA_PIP_REQUIREMENTS`` (default: ``[]``)
|
||||||
|
|
||||||
|
Define extra pip packages that are going to be installed for edx-platform.
|
||||||
|
|
||||||
|
- ``NPM_REGISTRY`` (default: ``"https://registry.npmjs.org/"``)
|
||||||
|
|
||||||
|
This defines the registry from which you'll be pulling NPM packages when building Docker images. Like ``EDX_PLATFORM_REPOSITORY``, this can be overridden at build time with a ``--build-arg`` option.
|
||||||
|
|
||||||
|
- ``OPENEDX_AWS_ACCESS_KEY`` (default: ``""``)
|
||||||
|
|
||||||
|
This configuration parameter sets the Django setting ``AWS_ACCESS_KEY_ID`` in edx-platform's LMS, CMS, envs, and production.py for use by the library django-storages with Amazon S3.
|
||||||
|
|
||||||
|
- ``OPENEDX_AWS_SECRET_ACCESS_KEY`` (default: ``""``)
|
||||||
|
|
||||||
|
This configuration parameter sets the Django setting ``AWS_SECRET_ACCESS_KEY`` in edx-platform's LMS, CMS, envs, and production.py for use by the library django-storages with Amazon S3.
|
||||||
|
|
||||||
|
- ``OPENEDX_MYSQL_DATABASE`` (default: ``"openedx"``)
|
||||||
|
|
||||||
|
This configuration parameter sets the name of the MySQL Database to be used by the Open edX Instance.
|
||||||
|
|
||||||
|
- ``OPENEDX_MYSQL_USERNAME`` (default: ``"openedx"``)
|
||||||
|
|
||||||
|
This configuration parameter sets the username associated with the MySQL Database.
|
||||||
|
|
||||||
|
CMS OAUTH2 SSO
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- ``CMS_OAUTH2_KEY_SSO`` (default: ``"cms-sso"``)
|
||||||
|
|
||||||
|
This defines the Studio's (CMS) OAUTH 2.0 Login (Key or Client ID) for SSO in the production environment.
|
||||||
|
|
||||||
|
- ``CMS_OAUTH2_KEY_SSO_DEV`` (default: ``"cms-sso-dev"``)
|
||||||
|
|
||||||
|
This defines the Studio's (CMS) OAUTH 2.0 Login (Key or Client ID) for SSO in the development environment.
|
||||||
|
|
||||||
|
For more information, see `Enabling OAuth for Studio login <https://github.com/openedx/edx-platform/blob/master/docs/guides/studio_oauth.rst>`__.
|
||||||
|
|
||||||
|
JWTs
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
- ``JWT_COMMON_AUDIENCE`` (default: ``"openedx"``)
|
||||||
|
- ``JWT_COMMON_ISSUER`` (default: ``"{% if ENABLE_HTTPS %}https{% else %}http{% endif %}://{{ LMS_HOST }}/oauth2"``)
|
||||||
|
- ``JWT_COMMON_SECRET_KEY`` (default: ``"{{ OPENEDX_SECRET_KEY }}"``)
|
||||||
|
|
||||||
|
These configuration parameters are rendered into the ``JWT_AUTH`` dictionary with keys ``JWT_AUDIENCE``, ``JWT_ISSUER``, and ``JWT_SECRET_KEY``, respectively. These parameters may be changed in order to create a custom user login for testing purposes.
|
||||||
|
|
||||||
Vendor services
|
Vendor services
|
||||||
~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~
|
||||||
|
@ -100,16 +208,10 @@ Vendor services
|
||||||
Caddy
|
Caddy
|
||||||
*****
|
*****
|
||||||
|
|
||||||
- ``RUN_CADDY`` (default: ``true``)
|
- ``CADDY_HTTP_PORT`` (default: ``80``)
|
||||||
|
- ``ENABLE_WEB_PROXY`` (default: ``true``)
|
||||||
|
|
||||||
`Caddy <https://caddyserver.com>`__ is a web server used in Tutor as a web proxy for the generation of SSL/TLS certificates at runtime. If ``RUN_CADDY`` is set to ``false`` then we assume that SSL termination does not occur in the Caddy container, and thus the ``caddy`` container is not started.
|
`Caddy <https://caddyserver.com>`__ is a web server used in Tutor both as a web proxy and for the generation of SSL/TLS certificates at runtime. Port indicated by ``CADDY_HTTP_PORT`` is exposed on the host, in addition to port 443. If ``ENABLE_WEB_PROXY`` is set to ``false`` then we assume that SSL termination does not occur in the Caddy container and only ``CADDY_HTTP_PORT`` is exposed on the host.
|
||||||
|
|
||||||
Nginx
|
|
||||||
*****
|
|
||||||
|
|
||||||
- ``NGINX_HTTP_PORT`` (default: ``80``)
|
|
||||||
|
|
||||||
Nginx is used to route web traffic to the various applications and to serve static assets. When ``RUN_CADDY`` is false, the ``NGINX_HTTP_PORT`` is exposed on the host.
|
|
||||||
|
|
||||||
MySQL
|
MySQL
|
||||||
*****
|
*****
|
||||||
|
@ -120,7 +222,7 @@ MySQL
|
||||||
- ``MYSQL_ROOT_USERNAME`` (default: ``"root"``)
|
- ``MYSQL_ROOT_USERNAME`` (default: ``"root"``)
|
||||||
- ``MYSQL_ROOT_PASSWORD`` (default: randomly generated) Note that you are responsible for creating the root user if you are using a managed database.
|
- ``MYSQL_ROOT_PASSWORD`` (default: randomly generated) Note that you are responsible for creating the root user if you are using a managed database.
|
||||||
|
|
||||||
By default, a running Open edX platform deployed with Tutor includes all necessary 3rd-party services, such as MySQL, MongoDb, etc. But it's also possible to store data on a separate database, such as `Amazon RDS <https://aws.amazon.com/rds/>`_. For instance, to store data on an external MySQL database, set the following configuration::
|
By default, a running Open edX platform deployed with Tutor includes all necessary 3rd-party services, such as MySQL, MongoDb, etc. But it's also possible to store data on a separate database, such as `Amazon RDS <https://aws.amazon.com/rds/>`_. For instance, to store data on an external MySQL database set the following configuration::
|
||||||
|
|
||||||
RUN_MYSQL: false
|
RUN_MYSQL: false
|
||||||
MYSQL_HOST: yourhost
|
MYSQL_HOST: yourhost
|
||||||
|
@ -138,15 +240,21 @@ Elasticsearch
|
||||||
- ``ELASTICSEARCH_PORT`` (default: ``9200``)
|
- ``ELASTICSEARCH_PORT`` (default: ``9200``)
|
||||||
- ``ELASTICSEARCH_HEAP_SIZE`` (default: ``"1g"``)
|
- ``ELASTICSEARCH_HEAP_SIZE`` (default: ``"1g"``)
|
||||||
|
|
||||||
Mongodb
|
MongoDB
|
||||||
*******
|
*******
|
||||||
|
|
||||||
- ``RUN_MONGODB`` (default: ``true``)
|
- ``RUN_MONGODB`` (default: ``true``)
|
||||||
- ``MONGODB_HOST`` (default: ``"mongodb"``)
|
|
||||||
- ``MONGODB_DATABASE`` (default: ``"openedx"``)
|
- ``MONGODB_DATABASE`` (default: ``"openedx"``)
|
||||||
|
- ``MONGODB_HOST`` (default: ``"mongodb"``)
|
||||||
|
- ``MONGODB_PASSWORD`` (default: ``""``)
|
||||||
- ``MONGODB_PORT`` (default: ``27017``)
|
- ``MONGODB_PORT`` (default: ``27017``)
|
||||||
- ``MONGODB_USERNAME`` (default: ``""``)
|
- ``MONGODB_USERNAME`` (default: ``""``)
|
||||||
- ``MONGODB_PASSWORD`` (default: ``""``)
|
- ``MONGODB_USE_SSL`` (default: ``false``)
|
||||||
|
- ``MONGODB_REPLICA_SET`` (default: ``""``)
|
||||||
|
- ``MONGODB_AUTH_MECHANISM`` (default: ``""``)
|
||||||
|
- ``MONGODB_AUTH_SOURCE`` (default: ``"admin"``)
|
||||||
|
|
||||||
|
Note that most of these settings will have to be modified to connect to a MongoDB cluster that runs separately of Tutor, such as `Atlas <https://www.mongodb.com/atlas>`__. In particular, the authentication source, mechanism and the SSL connection parameters should not be specified as part of the `host URI <https://www.mongodb.com/docs/manual/reference/connection-string/>`__ but as separate Tutor settings. Supported values for ``MONGODB_AUTH_MECHANISM`` are the same as for pymongo (see the `pymongo documentation <https://pymongo.readthedocs.io/en/stable/examples/authentication.html>`__).
|
||||||
|
|
||||||
Redis
|
Redis
|
||||||
*****
|
*****
|
||||||
|
@ -164,7 +272,7 @@ SMTP
|
||||||
|
|
||||||
- ``RUN_SMTP`` (default: ``true``)
|
- ``RUN_SMTP`` (default: ``true``)
|
||||||
- ``SMTP_HOST`` (default: ``"smtp"``)
|
- ``SMTP_HOST`` (default: ``"smtp"``)
|
||||||
- ``SMTP_PORT`` (default: ``25``)
|
- ``SMTP_PORT`` (default: ``8025``)
|
||||||
- ``SMTP_USERNAME`` (default: ``""``)
|
- ``SMTP_USERNAME`` (default: ``""``)
|
||||||
- ``SMTP_PASSWORD`` (default: ``""``)
|
- ``SMTP_PASSWORD`` (default: ``""``)
|
||||||
- ``SMTP_USE_TLS`` (default: ``false``)
|
- ``SMTP_USE_TLS`` (default: ``false``)
|
||||||
|
@ -177,22 +285,40 @@ SSL/TLS certificates for HTTPS access
|
||||||
|
|
||||||
- ``ENABLE_HTTPS`` (default: ``false``)
|
- ``ENABLE_HTTPS`` (default: ``false``)
|
||||||
|
|
||||||
By activating this feature, a free SSL/TLS certificate from the `Let's Encrypt <https://letsencrypt.org/>`_ certificate authority will be created for your platform. With this feature, **your platform will no longer be accessible in HTTP**. Calls to http urls will be redirected to https url.
|
When ``ENABLE_HTTPS`` is ``true``, the whole Open edX platform will be reconfigured to work with "https" URIs. Calls to "http" URIs will be redirected to "https". By default, SSL/TLS certificates will automatically be generated by Tutor (thanks to `Caddy <https://caddyserver.com/>`__) from the `Let's Encrypt <https://letsencrypt.org/>`_ certificate authority.
|
||||||
|
|
||||||
The following DNS records must exist and point to your server::
|
The following DNS records must exist and point to your server::
|
||||||
|
|
||||||
LMS_HOST (e.g: myopenedx.com)
|
LMS_HOST (e.g: myopenedx.com)
|
||||||
preview.LMS_HOST (e.g: preview.myopenedx.com)
|
PREVIEW_LMS_HOST (e.g: preview.myopenedx.com)
|
||||||
CMS_HOST (e.g: studio.myopenedx.com)
|
CMS_HOST (e.g: studio.myopenedx.com)
|
||||||
|
|
||||||
Thus, **this feature will (probably) not work in development** because the DNS records will (probably) not point to your development machine.
|
Thus, **this feature will (probably) not work in development** because the DNS records will (probably) not point to your development machine.
|
||||||
|
|
||||||
The SSL/TLS certificates will automatically be generated and updated by the Caddy proxy server container at runtime. Thus, as of v11.0.0 you no longer have to generate the certificates manually.
|
If you would like to perform SSL/TLS termination with your own custom certificates, you will have to keep ``ENABLE_HTTPS=true`` and turn off the Caddy load balancing with ``ENABLE_WEB_PROXY=false``. See the corresponding :ref:`tutorial <web_proxy>` for more information.
|
||||||
|
|
||||||
.. _customise:
|
.. _customise:
|
||||||
|
|
||||||
.. _custom_openedx_docker_image:
|
.. _custom_openedx_docker_image:
|
||||||
|
|
||||||
|
Kubernetes
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
- ``K8S_NAMESPACE`` (default: ``"openedx"``)
|
||||||
|
|
||||||
|
This configuration parameter sets the Kubernetes Namespace.
|
||||||
|
|
||||||
|
Miscellaneous Project Settings
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- ``CONTACT_EMAIL`` (default: ``"contact@{{ LMS_HOST }}"``)
|
||||||
|
|
||||||
|
This configuration parameter sets the Contact Email.
|
||||||
|
|
||||||
|
- ``PLATFORM_NAME`` (default: ``"My Open edX"``)
|
||||||
|
|
||||||
|
This configuration parameter sets the Platform Name.
|
||||||
|
|
||||||
Custom Open edX docker image
|
Custom Open edX docker image
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
|
@ -206,17 +332,16 @@ The following sections describe how to modify various aspects of the docker imag
|
||||||
|
|
||||||
tutor local stop
|
tutor local stop
|
||||||
|
|
||||||
The custom image will be used the next time you run ``tutor local quickstart`` or ``tutor local start``. Do not attempt to run ``tutor local restart``! Restarting will not pick up the new image and will continue to use the old image.
|
The custom image will be used the next time you run ``tutor local launch`` or ``tutor local start``. Do not attempt to run ``tutor local restart``! Restarting will not pick up the new image and will continue to use the old image.
|
||||||
|
|
||||||
openedx Docker Image build arguments
|
"openedx" Docker image build arguments
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
When building the "openedx" Docker image, it is possible to specify a few `arguments <https://docs.docker.com/engine/reference/builder/#arg>`__:
|
When building the "openedx" Docker image, it is possible to specify a few `arguments <https://docs.docker.com/engine/reference/builder/#arg>`__:
|
||||||
|
|
||||||
- ``EDX_PLATFORM_REPOSITORY`` (default: ``"https://github.com/edx/edx-platform.git"``)
|
- ``EDX_PLATFORM_REPOSITORY`` (default: ``"{{ EDX_PLATFORM_REPOSITORY }}"``)
|
||||||
- ``EDX_PLATFORM_VERSION`` (default: ``"open-release/koa.3"``)
|
- ``EDX_PLATFORM_VERSION`` (default: ``"{{ EDX_PLATFORM_VERSION }}"``, which if unset defaults to ``{{ OPENEDX_COMMON_VERSION }}``)
|
||||||
- ``EDX_PLATFORM_VERSION_DATE`` (default: ``"20200227"``)
|
- ``NPM_REGISTRY`` (default: ``"{{ NPM_REGISTRY }}"``)
|
||||||
- ``NPM_REGISTRY`` (default: ``"https://registry.npmjs.org/"``)
|
|
||||||
|
|
||||||
These arguments can be specified from the command line, `very much like Docker <https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables---build-arg>`__. For instance::
|
These arguments can be specified from the command line, `very much like Docker <https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables---build-arg>`__. For instance::
|
||||||
|
|
||||||
|
@ -225,56 +350,27 @@ These arguments can be specified from the command line, `very much like Docker <
|
||||||
Adding custom themes
|
Adding custom themes
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Comprehensive theming is enabled by default, but only the default theme is compiled. `Indigo <https://github.com/overhangio/indigo>`__ is a better, ready-to-run theme which you can start using today.
|
See :ref:`the corresponding tutorial <theming>`.
|
||||||
|
|
||||||
To compile your own theme, add it to the ``env/build/openedx/themes/`` folder::
|
|
||||||
|
|
||||||
git clone https://github.com/me/myopenedxtheme.git "$(tutor config printroot)/env/build/openedx/themes/myopenedxtheme"
|
|
||||||
|
|
||||||
The ``themes`` folder should have the following structure::
|
|
||||||
|
|
||||||
openedx/themes/
|
|
||||||
mycustomtheme1/
|
|
||||||
cms/
|
|
||||||
...
|
|
||||||
lms/
|
|
||||||
...
|
|
||||||
mycustomtheme2/
|
|
||||||
...
|
|
||||||
|
|
||||||
Then you must rebuild the openedx Docker image::
|
|
||||||
|
|
||||||
tutor images build openedx
|
|
||||||
|
|
||||||
Finally, you should enable your theme with the :ref:`settheme command <settheme>`.
|
|
||||||
|
|
||||||
.. _custom_extra_xblocks:
|
.. _custom_extra_xblocks:
|
||||||
|
|
||||||
Installing extra xblocks and requirements
|
Installing extra xblocks and requirements
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Would you like to include custom xblocks, or extra requirements to your Open edX platform? Additional requirements can be added to the ``env/build/openedx/requirements/private.txt`` file. For instance, to include the `polling xblock from Opencraft <https://github.com/open-craft/xblock-poll/>`_::
|
Would you like to include custom xblocks, or extra requirements to your Open edX platform? Additional requirements can be added to the ``OPENEDX_EXTRA_PIP_REQUIREMENTS`` parameter in the :ref:`config file <configuration>`. For instance, to include the `polling xblock from Opencraft <https://github.com/open-craft/xblock-poll/>`_::
|
||||||
|
|
||||||
echo "git+https://github.com/open-craft/xblock-poll.git" >> "$(tutor config printroot)/env/build/openedx/requirements/private.txt"
|
tutor config save --append OPENEDX_EXTRA_PIP_REQUIREMENTS=git+https://github.com/open-craft/xblock-poll.git
|
||||||
|
|
||||||
Then, the ``openedx`` docker image must be rebuilt::
|
Then, the ``openedx`` docker image must be rebuilt::
|
||||||
|
|
||||||
tutor images build openedx
|
tutor images build openedx
|
||||||
|
|
||||||
To install xblocks from a private repository that requires authentication, you must first clone the repository inside the ``openedx/requirements`` folder on the host::
|
|
||||||
|
|
||||||
git clone git@github.com:me/myprivaterepo.git "$(tutor config printroot)/env/build/openedx/requirements/myprivaterepo"
|
|
||||||
|
|
||||||
Then, declare your extra requirements with the ``-e`` flag in ``openedx/requirements/private.txt``::
|
|
||||||
|
|
||||||
echo "-e ./myprivaterepo" >> "$(tutor config printroot)/env/build/openedx/requirements/private.txt"
|
|
||||||
|
|
||||||
.. _edx_platform_fork:
|
.. _edx_platform_fork:
|
||||||
|
|
||||||
Running a fork of ``edx-platform``
|
Running a fork of ``edx-platform``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
You may want to run your own flavor of edx-platform instead of the `official version <https://github.com/edx/edx-platform/>`_. To do so, you will have to re-build the openedx image with the proper environment variables pointing to your repository and version::
|
You may want to run your own flavor of edx-platform instead of the `official version <https://github.com/openedx/edx-platform/>`_. To do so, you will have to re-build the openedx image with the proper environment variables pointing to your repository and version::
|
||||||
|
|
||||||
tutor images build openedx \
|
tutor images build openedx \
|
||||||
--build-arg EDX_PLATFORM_REPOSITORY=https://mygitrepo/edx-platform.git \
|
--build-arg EDX_PLATFORM_REPOSITORY=https://mygitrepo/edx-platform.git \
|
||||||
|
@ -286,16 +382,16 @@ Note that your edx-platform version must be a fork of the latest release **tag**
|
||||||
|
|
||||||
If you don't create your fork from this tag, you *will* have important compatibility issues with other services. In particular:
|
If you don't create your fork from this tag, you *will* have important compatibility issues with other services. In particular:
|
||||||
|
|
||||||
- Do not try to run a fork from an older (pre-Koa) version of edx-platform: this will simply not work.
|
- Do not try to run a fork from an older (pre-Quince) version of edx-platform: this will simply not work.
|
||||||
- Do not try to run a fork from the edx-platform master branch: there is a 99% probability that it will fail.
|
- Do not try to run a fork from the edx-platform master branch: there is a 99% probability that it will fail.
|
||||||
- Do not try to run a fork from the open-release/koa.master branch: Tutor will attempt to apply security and bug fix patches that might already be included in the open-release/koa.master but which were not yet applied to the latest release tag. Patch application will thus fail if you base your fork from the open-release/koa.master branch.
|
- Do not try to run a fork from the open-release/quince.master branch: Tutor will attempt to apply security and bug fix patches that might already be included in the open-release/quince.master but which were not yet applied to the latest release tag. Patch application will thus fail if you base your fork from the open-release/quince.master branch.
|
||||||
|
|
||||||
.. _i18n:
|
.. _i18n:
|
||||||
|
|
||||||
Adding custom translations
|
Adding custom translations
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
If you are not running Open edX in English, chances are that some strings will not be properly translated. In most cases, this is because not enough contributors have helped translate Open edX in your language. It happens! With Tutor, available translated languages include those that come bundled with `edx-platform <https://github.com/edx/edx-platform/tree/open-release/koa.master/conf/locale>`__ as well as those from `openedx-i18n <https://github.com/openedx/openedx-i18n/tree/master/edx-platform/locale>`__.
|
If you are not running Open edX in English (``LANGUAGE_CODE`` default: ``"en"``), chances are that some strings will not be properly translated. In most cases, this is because not enough contributors have helped translate Open edX into your language. It happens! With Tutor, available translated languages include those that come bundled with `edx-platform <https://github.com/openedx/edx-platform/tree/open-release/quince.master/conf/locale>`__ as well as those from `openedx-i18n <https://github.com/openedx/openedx-i18n/tree/master/edx-platform/locale>`__.
|
||||||
|
|
||||||
Tutor offers a relatively simple mechanism to add custom translations to the openedx Docker image. You should create a folder that corresponds to your language code in the "build/openedx/locale" folder of the Tutor environment. This folder should contain a "LC_MESSAGES" folder. For instance::
|
Tutor offers a relatively simple mechanism to add custom translations to the openedx Docker image. You should create a folder that corresponds to your language code in the "build/openedx/locale" folder of the Tutor environment. This folder should contain a "LC_MESSAGES" folder. For instance::
|
||||||
|
|
||||||
|
@ -316,9 +412,9 @@ Then, add a "django.po" file there that will contain your custom translations::
|
||||||
.. warning::
|
.. warning::
|
||||||
Don't forget to specify the file ``Content-Type`` when adding message strings with non-ASCII characters; otherwise a ``UnicodeDecodeError`` will be raised during compilation.
|
Don't forget to specify the file ``Content-Type`` when adding message strings with non-ASCII characters; otherwise a ``UnicodeDecodeError`` will be raised during compilation.
|
||||||
|
|
||||||
The "String to translate" part should match *exactly* the string that you would like to translate. You cannot make it up! The best way to find this string is to copy-paste it from the `upstream django.po file for the English language <https://github.com/edx/edx-platform/blob/open-release/koa.master/conf/locale/en/LC_MESSAGES/django.po>`__.
|
The "String to translate" part should match *exactly* the string that you would like to translate. You cannot make it up! The best way to find this string is to copy-paste it from the `upstream django.po file for the English language <https://github.com/openedx/edx-platform/blob/open-release/quince.master/conf/locale/en/LC_MESSAGES/django.po>`__.
|
||||||
|
|
||||||
If you cannot find the string to translate in this file, then it means that you are trying to translate a string that is used in some piece of javascript code. Those strings are stored in a different file named "djangojs.po". You can check it out `in the edx-platform repo as well <https://github.com/edx/edx-platform/blob/open-release/koa.master/conf/locale/en/LC_MESSAGES/djangojs.po>`__. Your custom javascript strings should also be stored in a "djangojs.po" file that should be placed in the same directory.
|
If you cannot find the string to translate in this file, then it means that you are trying to translate a string that is used in some piece of javascript code. Those strings are stored in a different file named "djangojs.po". You can check it out `in the edx-platform repo as well <https://github.com/openedx/edx-platform/blob/open-release/quince.master/conf/locale/en/LC_MESSAGES/djangojs.po>`__. Your custom javascript strings should also be stored in a "djangojs.po" file that should be placed in the same directory.
|
||||||
|
|
||||||
To recap, here is an example. To translate a few strings in French, both from django.po and djangojs.po, we would have the following file hierarchy::
|
To recap, here is an example. To translate a few strings in French, both from django.po and djangojs.po, we would have the following file hierarchy::
|
||||||
|
|
||||||
|
@ -350,9 +446,9 @@ And djangojs.po::
|
||||||
|
|
||||||
Then you will have to re-build the openedx Docker image::
|
Then you will have to re-build the openedx Docker image::
|
||||||
|
|
||||||
tutor images build openedx openedx-dev
|
tutor images build openedx
|
||||||
|
|
||||||
Beware that this will take a long time! Unfortunately it's difficult to accelerate this process, as translation files need to be compiled prior to collecting the assets. In development it's possible to accelerate the iteration loop -- but that exercise is left to the reader.
|
Beware that this will take a long time! Unfortunately, it's difficult to accelerate this process, as translation files need to be compiled before collecting the assets. In development it's possible to accelerate the iteration loop -- but that exercise is left to the reader.
|
||||||
|
|
||||||
|
|
||||||
Running a different ``openedx`` Docker image
|
Running a different ``openedx`` Docker image
|
||||||
|
@ -366,4 +462,18 @@ By default, Tutor runs the `overhangio/openedx <https://hub.docker.com/r/overhan
|
||||||
|
|
||||||
(See the relevant :ref:`configuration parameters <docker_images>`.)
|
(See the relevant :ref:`configuration parameters <docker_images>`.)
|
||||||
|
|
||||||
The customised Docker image tag value will then be used by Tutor to run the platform, for instance when running ``tutor local quickstart``.
|
The customised Docker image tag value will then be used by Tutor to run the platform, for instance when running ``tutor local launch``.
|
||||||
|
|
||||||
|
|
||||||
|
Passing custom docker build options
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
You can set a limited set of Docker build options via ``tutor images build`` command. In some situations it might be necessary to tweak the docker build command, ex- setting up build caching using buildkit.
|
||||||
|
In these situations, you can set ``--docker-arg`` flag in the ``tutor images build`` command. You can set any `supported options <https://docs.docker.com/engine/reference/commandline/build/#options>`_ in the docker build command, For example::
|
||||||
|
|
||||||
|
tutor images build openedx \
|
||||||
|
--build-arg BUILDKIT_INLINE_CACHE=1 \
|
||||||
|
--docker-arg="--cache-from" \
|
||||||
|
--docker-arg="docker.io/myusername/openedx:mytag"
|
||||||
|
|
||||||
|
This will result in passing the ``--cache-from`` option with the value ``docker.io/myusername/openedx:mytag`` to the docker build command.
|
||||||
|
|
369
docs/dev.rst
|
@ -5,37 +5,84 @@ Open edX development
|
||||||
|
|
||||||
In addition to running Open edX in production, Tutor can be used for local development of Open edX. This means that it is possible to hack on Open edX without setting up a Virtual Machine. Essentially, this replaces the devstack provided by edX.
|
In addition to running Open edX in production, Tutor can be used for local development of Open edX. This means that it is possible to hack on Open edX without setting up a Virtual Machine. Essentially, this replaces the devstack provided by edX.
|
||||||
|
|
||||||
The following commands assume you have previously launched a :ref:`local <local>` Open edX platform. If you have not done so already, you should run::
|
For detailed explanations on how to work on edx-platform and its dependencies, see the :ref:`edx_platform` tutorial.
|
||||||
|
|
||||||
tutor local quickstart
|
.. _edx_platform_dev_env:
|
||||||
|
|
||||||
In order to run the platform in development mode, you **must** answer no ("n") to the question "Are you configuring a production platform".
|
First-time setup
|
||||||
|
----------------
|
||||||
|
|
||||||
Note that the local.overhang.io `domain <https://dnschecker.org/#A/local.overhang.io>`__ and its `subdomains <https://dnschecker.org/#CNAME/studio.local.overhang.io>`__ all point to 127.0.0.1. This is just a domain name that was setup to conveniently access a locally running Open edX platform.
|
Firstly, either :ref:`install Tutor <install>` (for development against the named releases of Open edX) or :ref:`install Tutor Nightly <nightly>` (for development against Open edX's master branches).
|
||||||
|
|
||||||
Once the local platform has been configured, you should stop it so that it does not interfere with the development environment::
|
Then, optionally, tell Tutor to use a local fork of edx-platform::
|
||||||
|
|
||||||
tutor local stop
|
tutor mounts add ./edx-platform
|
||||||
|
|
||||||
Finally, you should build the ``openedx-dev`` docker image::
|
Then, launch the developer platform setup process::
|
||||||
|
|
||||||
tutor images build openedx-dev
|
tutor images build openedx-dev
|
||||||
|
tutor dev launch
|
||||||
|
|
||||||
This ``openedx-dev`` development image differs from the ``openedx`` production image:
|
This will perform several tasks. It will:
|
||||||
|
|
||||||
- The user that runs inside the container has the same UID as the user on the host, in order to avoid permission problems inside mounted volumes (and in particular in the edx-platform repository).
|
* build the "openedx-dev" Docker image, which is based on the "openedx" production image but is `specialized for developer usage`_ (eventually with your fork),
|
||||||
- Additional python and system requirements are installed for convenient debugging: `ipython <https://ipython.org/>`__, `ipdb <https://pypi.org/project/ipdb/>`__, vim, telnet.
|
* stop any existing locally-running Tutor containers,
|
||||||
- The edx-platform `development requirements <https://github.com/edx/edx-platform/blob/open-release/koa.master/requirements/edx/development.in>`__ are installed.
|
* disable HTTPS,
|
||||||
|
* set ``LMS_HOST`` to `local.edly.io <http://local.edly.io>`_ (a convenience domain that simply `points at 127.0.0.1 <https://dnschecker.org/#A/local.edly.io>`_),
|
||||||
|
* prompt for a platform details (with suitable defaults),
|
||||||
|
* start LMS, CMS, supporting services, and any plugged-in services,
|
||||||
|
* ensure databases are created and migrated, and
|
||||||
|
* run service initialization scripts, such as service user creation and Waffle configuration.
|
||||||
|
|
||||||
Since the ``openedx-dev`` is based upon the ``openedx`` docker image, it should be re-built every time the ``openedx`` docker image is modified.
|
Additionally, when a local clone of edx-platform is bind-mounted, it will:
|
||||||
|
|
||||||
Run a local development webserver
|
* re-run setup.py,
|
||||||
---------------------------------
|
* clean-reinstall Node modules, and
|
||||||
|
* regenerate static assets.
|
||||||
|
|
||||||
::
|
Once setup is complete, the platform will be running in the background:
|
||||||
|
|
||||||
tutor dev runserver lms # Access the lms at http://local.overhang.io:8000
|
* LMS will be accessible at `http://local.edly.io:8000 <http://local.edly.io:8000>`_.
|
||||||
tutor dev runserver cms # Access the cms at http://studio.local.overhang.io:8001
|
* CMS will be accessible at `http://studio.local.edly.io:8001 <http://studio.local.edly.io:8001>`_.
|
||||||
|
* Plugged-in services should be accessible at their documented URLs.
|
||||||
|
|
||||||
|
Now, use the ``tutor dev ...`` command-line interface to manage the development environment. Some common commands are described below.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
If you've added your edx-platform to the bind-mounted folders, you can remove at any time by running::
|
||||||
|
|
||||||
|
tutor mounts remove ./edx-platform
|
||||||
|
|
||||||
|
At any time, check your configuration by running::
|
||||||
|
|
||||||
|
tutor mounts list
|
||||||
|
|
||||||
|
Read more about bind-mounts :ref:`below <bind_mounts>`.
|
||||||
|
|
||||||
|
Stopping the platform
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
To bring down the platform's containers, simply run::
|
||||||
|
|
||||||
|
tutor dev stop
|
||||||
|
|
||||||
|
Starting the platform back up
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
Once first-time setup has been performed with ``launch``, the platform can be started going forward with the lighter-weight ``start -d`` command, which brings up containers *detached* (that is: in the background), but does not perform any initialization tasks::
|
||||||
|
|
||||||
|
tutor dev start -d
|
||||||
|
|
||||||
|
Or, to start with platform with containers *attached* (that is: in the foreground, the current terminal), omit the ``-d`` flag::
|
||||||
|
|
||||||
|
tutor dev start
|
||||||
|
|
||||||
|
When running containers attached, stop the platform with ``Ctrl+c``, or switch to detached mode using ``Ctrl+z``.
|
||||||
|
|
||||||
|
Finally, the platform can also be started back up with ``launch``. It will take longer than ``start``, but it will ensure that config is applied, databases are provisioned & migrated, plugins are fully initialized, and (if applicable) the bind-mounted edx-platform is set up. Notably, ``launch`` is idempotent, so it is always safe to run it again without risk to data. Including the ``--pullimages`` flag will also ensure that container images are up-to-date::
|
||||||
|
|
||||||
|
tutor dev launch --pullimages
|
||||||
|
|
||||||
Running arbitrary commands
|
Running arbitrary commands
|
||||||
--------------------------
|
--------------------------
|
||||||
|
@ -52,46 +99,140 @@ To open a python shell in the LMS or CMS, run::
|
||||||
|
|
||||||
You can then import edx-platform and django modules and execute python code.
|
You can then import edx-platform and django modules and execute python code.
|
||||||
|
|
||||||
To collect assets, you can use the ``openedx-assets`` command that ships with Tutor::
|
To rebuild assets, you can use the ``openedx-assets`` command that ships with Tutor::
|
||||||
|
|
||||||
tutor dev run lms openedx-assets build --env=dev
|
tutor dev run lms openedx-assets build --env=dev
|
||||||
|
|
||||||
|
|
||||||
|
.. _specialized for developer usage:
|
||||||
|
|
||||||
|
Rebuilding the openedx-dev image
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
The ``openedx-dev`` Docker image is based on the same ``openedx`` image used by ``tutor local ...`` to run LMS and CMS. However, it has a few differences to make it more convenient for developers:
|
||||||
|
|
||||||
|
- The user that runs inside the container has the same UID as the user on the host, to avoid permission problems inside mounted volumes (and in particular in the edx-platform repository).
|
||||||
|
- Additional Python and system requirements are installed for convenient debugging: `ipython <https://ipython.org/>`__, `ipdb <https://pypi.org/project/ipdb/>`__, vim, telnet.
|
||||||
|
- The edx-platform `development requirements <https://github.com/openedx/edx-platform/blob/open-release/quince.master/requirements/edx/development.in>`__ are installed.
|
||||||
|
|
||||||
|
|
||||||
|
If you are using a custom ``openedx`` image, then you will need to rebuild ``openedx-dev`` every time you modify ``openedx``. To so, run::
|
||||||
|
|
||||||
|
tutor images build openedx-dev
|
||||||
|
|
||||||
|
Alternatively, the image will be automatically rebuilt every time you run::
|
||||||
|
|
||||||
|
tutor dev launch
|
||||||
|
|
||||||
|
|
||||||
.. _bind_mounts:
|
.. _bind_mounts:
|
||||||
|
|
||||||
Bind-mount container directories
|
Sharing directories with containers
|
||||||
--------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
It may sometimes be convenient to mount container directories on the host, for instance: for editing and debugging. Tutor provides different solutions to this problem.
|
It may sometimes be convenient to mount container directories on the host, for instance: for editing and debugging. Tutor provides different solutions to this problem.
|
||||||
|
|
||||||
Bind-mount from the "volumes/" directory
|
.. _persistent_mounts:
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Tutor makes it easy to create a bind-mount from an existing container. First, copy the contents of a container directory with the ``bindmount`` command. For instance, to copy the virtual environment of the "lms" container::
|
Persistent bind-mounted volumes with ``tutor mounts``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
tutor dev bindmount lms /openedx/venv
|
``tutor mounts`` is a set of Tutor command to manage bind-mounted host directories. Directories are mounted `both` at build time and run time:
|
||||||
|
|
||||||
This command recursively copies the contents of the ``/opendedx/venv`` directory to ``$(tutor config printroot)/volumes/venv``. The code of any Python dependency can then be edited -- for instance, you can then add a ``import ipdb; ipdb.set_trace()`` statement for step-by-step debugging, or implement a custom feature.
|
- At build time: some of the host directories will be added the `Docker build context <https://docs.docker.com/engine/reference/commandline/buildx_build/#build-context>`__. This makes it possible to transparently build a Docker image using a locally checked-out repository.
|
||||||
|
- At run time: host directories will be bind-mounted in running containers, using either an automatic or a manual configuration.
|
||||||
|
|
||||||
Then, bind-mount the directory back in the container with the ``--volume`` option::
|
|
||||||
|
|
||||||
tutor dev runserver --volume=/openedx/venv lms
|
After some directories have been added with ``tutor mounts add``, all ``tutor dev`` and ``tutor local`` commands will make use of these bind-mount volumes.
|
||||||
|
|
||||||
Notice how the ``--volume=/openedx/venv`` option differs from `Docker syntax <https://docs.docker.com/storage/volumes/#choose-the--v-or---mount-flag>`__? Tutor recognizes this syntax and automatically converts this option to ``--volume=/path/to/tutor/root/volumes/venv:/openedx/venv``, which is recognized by Docker.
|
Values passed to ``tutor mounts add ...`` can take one of two forms. The first is explicit::
|
||||||
|
|
||||||
.. note::
|
tutor mounts add lms:/path/to/edx-platform:/openedx/edx-platform
|
||||||
The ``bindmount`` command and the ``--volume=/...`` option syntax are available both for the ``tutor local`` and ``tutor dev`` commands.
|
|
||||||
|
The second is implicit::
|
||||||
|
|
||||||
|
tutor mounts add /path/to/edx-platform
|
||||||
|
|
||||||
|
With the explicit form, the value means "bind-mount the host folder /path/to/edx-platform to /openedx/edx-platform in the lms container at run time".
|
||||||
|
|
||||||
|
If you use the explicit format, you will quickly realise that you usually want to bind-mount folders in multiple containers at a time. For instance, you will want to bind-mount the edx-platform repository in the "cms" container, but also the "lms-worker" and "cms-worker" containers. To do that, write instead::
|
||||||
|
|
||||||
|
# each service is added to a coma-separated list
|
||||||
|
tutor mounts add lms,cms,lms-worker,cms-worker:/path/to/edx-platform:/openedx/edx-platform
|
||||||
|
|
||||||
|
This command line is a bit cumbersome. In addition, with this explicit form, the edx-platform repository will *not* be added to the build context at build time. But Tutor can be smart about bind-mounting folders to the right containers in the right place when you use the implicit form of the ``tutor mounts add`` command. For instance, the following implicit form can be used instead of the explicit form above::
|
||||||
|
|
||||||
|
tutor mounts add /path/to/edx-platform
|
||||||
|
|
||||||
|
With this implicit form, the edx-platform repo will be bind-mounted in the containers at run time, just like with the explicit form. But in addition, the edx-platform will also automatically be added to the Docker image at build time.
|
||||||
|
|
||||||
|
To check whether you have used the correct syntax, you should run ``tutor mounts list``. This command will indicate whether your folders will be bind-mounted at build time, run time, or both. For instance::
|
||||||
|
|
||||||
|
$ tutor mounts add /path/to/edx-platform
|
||||||
|
$ tutor mounts list
|
||||||
|
- name: /path/to/edx-platform
|
||||||
|
build_mounts:
|
||||||
|
- image: openedx
|
||||||
|
context: edx-platform
|
||||||
|
- image: openedx-dev
|
||||||
|
context: edx-platform
|
||||||
|
compose_mounts:
|
||||||
|
- service: lms
|
||||||
|
container_path: /openedx/edx-platform
|
||||||
|
- service: cms
|
||||||
|
container_path: /openedx/edx-platform
|
||||||
|
- service: lms-worker
|
||||||
|
container_path: /openedx/edx-platform
|
||||||
|
- service: cms-worker
|
||||||
|
container_path: /openedx/edx-platform
|
||||||
|
- service: lms-job
|
||||||
|
container_path: /openedx/edx-platform
|
||||||
|
- service: cms-job
|
||||||
|
container_path: /openedx/edx-platform
|
||||||
|
|
||||||
|
So, when should you *not* be using the implicit form? That would be when Tutor does not know where to bind-mount your host folders. For instance, if you wanted to bind-mount your edx-platform virtual environment located in ``~/venvs/edx-platform``, you should not write ``mounts add ~/venvs/edx-platform``, because that folder would be mounted in a way that would override the edx-platform repository in the container. Instead, you should write::
|
||||||
|
|
||||||
|
tutor mounts add lms:~/venvs/edx-platform:/openedx/venv
|
||||||
|
|
||||||
|
Verify the configuration with the ``list`` command::
|
||||||
|
|
||||||
|
$ tutor mounts list
|
||||||
|
- name: lms:~/venvs/edx-platform:/openedx/venv
|
||||||
|
build_mounts: []
|
||||||
|
compose_mounts:
|
||||||
|
- service: lms
|
||||||
|
container_path: /openedx/venv
|
||||||
|
|
||||||
|
.. note:: Remember to setup your edx-platform repository for development! See :ref:`edx_platform_dev_env`.
|
||||||
|
|
||||||
|
Copy files from containers to the local filesystem
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Sometimes, you may want to modify some of the files inside a container for which you don't have a copy on the host. A typical example is when you want to troubleshoot a Python dependency that is installed inside the application virtual environment. In such cases, you want to first copy the contents of the virtual environment from the container to the local filesystem. To that end, Tutor provides the ``tutor dev copyfrom`` command. First, copy the contents of the container folder to the local filesystem::
|
||||||
|
|
||||||
|
tutor dev copyfrom lms /openedx/venv ~
|
||||||
|
|
||||||
|
Then, bind-mount that folder back in the container with the ``MOUNTS`` setting (described :ref:`above <persistent_mounts>`)::
|
||||||
|
|
||||||
|
tutor mounts add lms:~/venv:/openedx/venv
|
||||||
|
|
||||||
|
You can then edit the files in ``~/venv`` on your local filesystem and see the changes live in your "lms" container.
|
||||||
|
|
||||||
Manual bind-mount to any directory
|
Manual bind-mount to any directory
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
The above solution may not work for you if you already have an existing directory, outside of the "volumes/" directory, which you would like mounted in one of your containers. For instance, you may want to mount your copy of the `edx-platform <https://github.com/edx/edx-platform/>`__ repository. In such cases, you can simply use the ``-v/--volume`` `Docker option <https://docs.docker.com/storage/volumes/#choose-the--v-or---mount-flag>`__::
|
.. warning:: Manually bind-mounting volumes with the ``--volume`` option makes it difficult to simultaneously bind-mount to multiple containers. Also, the ``--volume`` options are not compatible with ``start`` commands. As an alternative, you should consider following the instructions above: :ref:`persistent_mounts`.
|
||||||
|
|
||||||
|
The above solution may not work for you if you already have an existing directory, outside of the "volumes/" directory, which you would like mounted in one of your containers. For instance, you may want to mount your copy of the `edx-platform <https://github.com/openedx/edx-platform/>`__ repository. In such cases, you can simply use the ``-v/--volume`` `Docker option <https://docs.docker.com/storage/volumes/#choose-the--v-or---mount-flag>`__::
|
||||||
|
|
||||||
tutor dev run --volume=/path/to/edx-platform:/openedx/edx-platform lms bash
|
tutor dev run --volume=/path/to/edx-platform:/openedx/edx-platform lms bash
|
||||||
|
|
||||||
Override docker-compose volumes
|
Override docker-compose volumes
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
The above solutions require that you explicitly pass the ``-v/--volume`` to every ``run`` or ``runserver`` command, which may be inconvenient. Also, these solutions are not compatible with the ``start`` command. To address these issues, you can create a ``docker-compose.override.yml`` file that will specify custom volumes to be used with all ``dev`` commands::
|
.. warning:: While the option described below "works", it will only bind-mount directories at run-time. In many cases you really want to bind-mount directories at build-time. For instance: when working on edx-platform requirements. As an alternative, you should consider following the instructions above: :ref:`persistent_mounts`.
|
||||||
|
|
||||||
|
Adding items to the ``MOUNTS`` setting effectively adds new bind-mount volumes to the ``docker-compose.yml`` files. But you might want to have more control over your volumes, such as adding read-only options, or customising other fields of the different services. To address these issues, you can create a ``docker-compose.override.yml`` file that will specify custom volumes to be used with all ``dev`` commands::
|
||||||
|
|
||||||
vim "$(tutor config printroot)/env/dev/docker-compose.override.yml"
|
vim "$(tutor config printroot)/env/dev/docker-compose.override.yml"
|
||||||
|
|
||||||
|
@ -101,170 +242,18 @@ You are then free to bind-mount any directory to any container. For instance, to
|
||||||
services:
|
services:
|
||||||
lms:
|
lms:
|
||||||
volumes:
|
volumes:
|
||||||
- /path/to/edx-platform/:/openedx/edx-platform
|
- /path/to/edx-platform:/openedx/edx-platform
|
||||||
cms:
|
cms:
|
||||||
volumes:
|
volumes:
|
||||||
- /path/to/edx-platform/:/openedx/edx-platform
|
- /path/to/edx-platform:/openedx/edx-platform
|
||||||
lms-worker:
|
lms-worker:
|
||||||
volumes:
|
volumes:
|
||||||
- /path/to/edx-platform/:/openedx/edx-platform
|
- /path/to/edx-platform:/openedx/edx-platform
|
||||||
cms-worker:
|
cms-worker:
|
||||||
volumes:
|
volumes:
|
||||||
- /path/to/edx-platform/:/openedx/edx-platform
|
- /path/to/edx-platform:/openedx/edx-platform
|
||||||
|
|
||||||
This override file will be loaded when running any ``tutor dev ..`` command. The edx-platform repo mounted at the specified path will be automatically mounted inside all LMS and CMS containers. With this file, you should no longer specify the ``-v/--volume`` option from the command line with the ``run`` or ``runserver`` commands.
|
This override file will be loaded when running any ``tutor dev ..`` command. The edx-platform repo mounted at the specified path will be automatically mounted inside all LMS and CMS containers.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
The ``tutor local`` commands loads the ``docker-compose.override.yml`` file from the ``$(tutor config printroot)/env/local/docker-compose.override.yml`` directory.
|
The ``tutor local`` commands load the ``docker-compose.override.yml`` file from the ``$(tutor config printroot)/env/local/docker-compose.override.yml`` directory. One-time jobs from initialisation commands load the ``local/docker-compose.jobs.override.yml`` and ``dev/docker-compose.jobs.override.yml``.
|
||||||
|
|
||||||
Point to a local edx-platform
|
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
Following the instructions :ref:`above <bind_mounts>` on how to bind-mount directories from the host above, you may mount your own `edx-platform <https://github.com/edx/edx-platform/>`__ fork in your containers by running either::
|
|
||||||
|
|
||||||
# Mount from the volumes/ directory
|
|
||||||
tutor dev bindmount lms /openedx/edx-platform
|
|
||||||
tutor dev runserver --volume=/openedx/edx-platform lms
|
|
||||||
|
|
||||||
# Mount from an arbitrary directory
|
|
||||||
tutor dev runserver --volume=/path/to/edx-platform:/openedx/edx-platform lms
|
|
||||||
|
|
||||||
# Add your own volumes to $(tutor config printroot)/env/dev/docker-compose.override.yml
|
|
||||||
tutor dev runserver lms
|
|
||||||
|
|
||||||
Prepare the edx-platform repo
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
If you choose any but the first solution above, you will have to make sure that your fork works with Tutor.
|
|
||||||
|
|
||||||
First of all, you should make sure that you are working off the ``open-release/koa.3`` tag. See the :ref:`fork edx-platform section <edx_platform_fork>` for more information.
|
|
||||||
|
|
||||||
Then, you should run the following commands::
|
|
||||||
|
|
||||||
# Run bash in the lms container
|
|
||||||
tutor dev run [--volume=...] lms bash
|
|
||||||
|
|
||||||
# Compile local python requirements
|
|
||||||
pip install --requirement requirements/edx/development.txt
|
|
||||||
|
|
||||||
# Install nodejs packages in node_modules/
|
|
||||||
npm install
|
|
||||||
|
|
||||||
# Rebuild static assets
|
|
||||||
openedx-assets build --env=dev
|
|
||||||
|
|
||||||
|
|
||||||
Debug edx-platform
|
|
||||||
~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
To debug a local edx-platform repository, add a ``import ipdb; ipdb.set_trace()`` breakpoint anywhere in your code and run::
|
|
||||||
|
|
||||||
tutor dev runserver [--volume=...] lms
|
|
||||||
|
|
||||||
XBlock and edx-platform plugin development
|
|
||||||
------------------------------------------
|
|
||||||
|
|
||||||
In some cases you will have to develop features for packages that are pip-installed next to edx-platform. This is quite easy with Tutor. Just add your packages to the ``$(tutor config printroot)/env/build/openedx/requirements/private.txt`` file. To avoid re-building the openedx Docker image at every change, you should add your package in editable mode. For instance::
|
|
||||||
|
|
||||||
echo "-e ./mypackage" >> "$(tutor config printroot)/env/build/openedx/requirements/private.txt"
|
|
||||||
|
|
||||||
The ``requirements`` folder should have the following content::
|
|
||||||
|
|
||||||
env/build/openedx/requirements/
|
|
||||||
private.txt
|
|
||||||
mypackage/
|
|
||||||
setup.py
|
|
||||||
...
|
|
||||||
|
|
||||||
You will have to re-build the openedx Docker image once::
|
|
||||||
|
|
||||||
tutor images build openedx
|
|
||||||
|
|
||||||
You should then run the development server as usual, with ``runserver``. Every change made to the ``mypackage`` folder will be picked up and the development server will be automatically reloaded.
|
|
||||||
|
|
||||||
.. _theming:
|
|
||||||
|
|
||||||
Customised themes
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
With Tutor, it's pretty easy to develop your own themes. Start by placing your files inside the ``env/build/openedx/themes`` directory. For instance, you could start from the ``edx.org`` theme present inside the ``edx-platform`` repository::
|
|
||||||
|
|
||||||
cp -r /path/to/edx-platform/themes/edx.org "$(tutor config printroot)/env/build/openedx/themes/"
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
You should not create a soft link here. If you do, it will trigger a ``Theme not found in any of the themes dirs`` error. This is because soft links are not properly resolved from inside docker containers.
|
|
||||||
|
|
||||||
Then, run a local webserver::
|
|
||||||
|
|
||||||
tutor dev runserver lms
|
|
||||||
|
|
||||||
The LMS can then be accessed at http://local.overhang.io:8000. You will then have to :ref:`enable that theme <settheme>` for the development domain names::
|
|
||||||
|
|
||||||
tutor dev settheme mythemename local.overhang.io:8000 studio.local.overhang.io:8001
|
|
||||||
|
|
||||||
Re-build development docker image (and compile assets)::
|
|
||||||
|
|
||||||
tutor images build openedx-dev
|
|
||||||
|
|
||||||
Watch the themes folders for changes (in a different terminal)::
|
|
||||||
|
|
||||||
tutor dev run watchthemes
|
|
||||||
|
|
||||||
Make changes to some of the files inside the theme directory: the theme assets should be automatically recompiled and visible at http://local.overhang.io:8000.
|
|
||||||
|
|
||||||
Custom edx-platform settings
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
By default, tutor settings files are mounted inside the docker images at ``/openedx/edx-platform/lms/envs/tutor/`` and ``/openedx/edx-platform/cms/envs/tutor/``. In the various ``dev`` commands, the default ``edx-platform`` settings module is set to ``tutor.development`` and you don't have to do anything to set up these settings.
|
|
||||||
|
|
||||||
If, for some reason, you want to use different settings, you will need to define the ``TUTOR_EDX_PLATFORM_SETTINGS`` environment variable.
|
|
||||||
|
|
||||||
For instance, let's assume you have created the ``/path/to/edx-platform/lms/envs/mysettings.py`` and ``/path/to/edx-platform/cms/envs/mysettings.py`` modules. These settings should be pretty similar to the following files::
|
|
||||||
|
|
||||||
$(tutor config printroot)/env/apps/openedx/tutor/lms/development.py
|
|
||||||
$(tutor config printroot)/env/apps/openedx/tutor/cms/development.py
|
|
||||||
|
|
||||||
Alternatively, the ``mysettings.py`` files can import the tutor development settings::
|
|
||||||
|
|
||||||
# Beginning of mysettings.py
|
|
||||||
from .tutor.development import *
|
|
||||||
|
|
||||||
You should then specify the settings to use on the host::
|
|
||||||
|
|
||||||
export TUTOR_EDX_PLATFORM_SETTINGS=mysettings
|
|
||||||
|
|
||||||
From then on, all ``dev`` commands will use the ``mysettings`` module. For instance::
|
|
||||||
|
|
||||||
tutor dev runserver lms
|
|
||||||
|
|
||||||
Running edx-platform unit tests
|
|
||||||
-------------------------------
|
|
||||||
|
|
||||||
It's possible to run the full set of unit tests that ship with `edx-platform <https://github.com/edx/edx-platform/>`__. To do so, you should first build the "test" target of the "openedx-dev" Docker image::
|
|
||||||
|
|
||||||
tutor images build --target=test openedx-dev
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
Don't forget to re-build the development image afterwards if you'd like to run ``dev`` commands again! To do so, run ``tutor images build openedx-dev`` after you are done testing.
|
|
||||||
|
|
||||||
Then, run unit tests with ``pytest`` commands::
|
|
||||||
|
|
||||||
# Run a test container
|
|
||||||
tutor dev run lms bash
|
|
||||||
|
|
||||||
# Run tests on common apps
|
|
||||||
unset DJANGO_SETTINGS_MODULE
|
|
||||||
export EDXAPP_TEST_MONGO_HOST=mongodb
|
|
||||||
pytest common
|
|
||||||
pytest openedx
|
|
||||||
|
|
||||||
# Run tests on LMS
|
|
||||||
export DJANGO_SETTINGS_MODULE=lms.envs.tutor.test
|
|
||||||
pytest lms
|
|
||||||
|
|
||||||
# Run tests on CMS
|
|
||||||
export DJANGO_SETTINGS_MODULE=cms.envs.tutor.test
|
|
||||||
pytest cms
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
Getting all edx-platform unit tests to pass on Tutor is currently a work-in-progress. Some unit tests are still failing. If you manage to fix some of these, please report your findings in the `Tutor forums <https://discuss.overhang.io>`__.
|
|
||||||
|
|
|
@ -3,4 +3,4 @@
|
||||||
.. parsed-literal::
|
.. parsed-literal::
|
||||||
|
|
||||||
sudo curl -L "\ https\ ://github.com/overhangio/tutor/releases/download/v\ |tutor_version|/tutor-$(uname -s)_$(uname -m)" -o /usr/local/bin/tutor
|
sudo curl -L "\ https\ ://github.com/overhangio/tutor/releases/download/v\ |tutor_version|/tutor-$(uname -s)_$(uname -m)" -o /usr/local/bin/tutor
|
||||||
sudo chmod 0755 /usr/local/bin/tutor
|
sudo chmod 0755 /usr/local/bin/tutor
|
3
docs/download/pip.rst
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.. parsed-literal::
|
||||||
|
|
||||||
|
pip install "tutor[full]"
|
|
@ -1,76 +0,0 @@
|
||||||
.. _extra:
|
|
||||||
|
|
||||||
Extra features
|
|
||||||
==============
|
|
||||||
|
|
||||||
.. _webui:
|
|
||||||
|
|
||||||
Web UI
|
|
||||||
------
|
|
||||||
|
|
||||||
Tutor comes with a web user interface (UI) that allows you to administer your Open edX platform remotely. It's especially convenient for remote administration of the platform.
|
|
||||||
|
|
||||||
Launching the web UI
|
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
tutor webui start
|
|
||||||
|
|
||||||
You can then access the interface at http://localhost:3737, or http://youserverurl:3737.
|
|
||||||
|
|
||||||
.. image:: img/webui.png
|
|
||||||
|
|
||||||
All ``tutor`` commands can be executed from this web UI: you just don't need to prefix the commands with ``tutor``. For instance, to deploy a local Open edX instance, run::
|
|
||||||
|
|
||||||
local quickstart
|
|
||||||
|
|
||||||
instead of ``tutor local quickstart``.
|
|
||||||
|
|
||||||
Authentication
|
|
||||||
~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
**WARNING** Once you launch the web UI, it is accessible by everyone, which means that your Open edX platform is at risk. If you are planning to leave the web UI up for a long time, you should setup a user and password for authentication::
|
|
||||||
|
|
||||||
tutor webui configure
|
|
||||||
|
|
||||||
.. _mobile:
|
|
||||||
|
|
||||||
Mobile Android application
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
With Tutor, you can build an Android mobile application for your platform. To build the application in debug mode, run::
|
|
||||||
|
|
||||||
tutor android build debug
|
|
||||||
|
|
||||||
The ``.apk`` file will then be available in ``$(tutor config printroot)/data/android``. Transfer it to an Android phone to install the application. You should be able to sign in and view available courses.
|
|
||||||
|
|
||||||
Building a custom Android app
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The Android app is built from the `official edx-app-android repository <https://github.com/edx/edx-app-android/>`__. To change this repository or the app version, you can simply build a different docker image with::
|
|
||||||
|
|
||||||
tutor images build \
|
|
||||||
--build-arg ANDROID_APP_REPOSITORY=https://github.com/mycustomfork/edx-app-android \
|
|
||||||
--build-arg ANDROID_APP_VERSION=master \
|
|
||||||
android
|
|
||||||
|
|
||||||
Releasing an Android app
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
**Note**: this is an untested feature.
|
|
||||||
|
|
||||||
Releasing an Android app on the Play Store requires to build the app in release mode. To do so, edit the ``$TUTOR_ROOT/config.yml`` configuration file and define the following variables::
|
|
||||||
|
|
||||||
ANDROID_RELEASE_STORE_PASSWORD
|
|
||||||
ANDROID_RELEASE_KEY_PASSWORD
|
|
||||||
ANDROID_RELEASE_KEY_ALIAS
|
|
||||||
|
|
||||||
Then, place your keystore file in ``$(tutor config printroot)/env/android/app.keystore``. Finally, build the application with::
|
|
||||||
|
|
||||||
tutor android build release
|
|
||||||
|
|
||||||
Customising the Android app
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Customising the application, such as the logo or the background image, is currently not supported. If you are interested by this feature, please tell us about it in the Tutor `discussion forums <https://discuss.overhang.io>`_.
|
|
31
docs/faq.rst
|
@ -6,9 +6,9 @@ FAQ
|
||||||
What is Tutor?
|
What is Tutor?
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
Tutor is an open source distribution of `Open edX <https://open.edx.org>`_. It uses the original code from the various Open edX repositories, such as `edx-platform <https://github.com/edx/edx-platform/>`_, `cs_comments_service <https://github.com/edx/cs_comments_service>`_, etc. and packages everything in a way that makes it very easy to install, administer and upgrade Open edX. In particular, all services are run inside Docker containers.
|
Tutor is an open source distribution of `Open edX <https://open.edx.org>`_. It uses the original code from the various Open edX repositories, such as `edx-platform <https://github.com/openedx/edx-platform/>`_, `cs_comments_service <https://github.com/openedx/cs_comments_service>`_, etc. and packages everything in a way that makes it very easy to install, administer and upgrade Open edX. In particular, all services are run inside Docker containers.
|
||||||
|
|
||||||
Tutor makes it possible to deploy Open edX locally, with `docker-compose <https://docs.docker.com/compose/overview/>`_ or on an existing `Kubernetes cluster <http://kubernetes.io/>`_.
|
Tutor makes it possible to deploy Open edX locally, with `docker-compose <https://docs.docker.com/compose/overview/>`_ or on an existing `Kubernetes cluster <http://kubernetes.io/>`_. Want to learn more? Take a look at the :ref:`getting started concepts <intro>`.
|
||||||
|
|
||||||
What is the purpose of Tutor?
|
What is the purpose of Tutor?
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
@ -20,44 +20,39 @@ To make it possible to deploy, administer and upgrade Open edX anywhere, easily.
|
||||||
What's the difference with the official "native" installation?
|
What's the difference with the official "native" installation?
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
|
|
||||||
The `native installation <https://openedx.atlassian.net/wiki/spaces/OpenOPS/pages/146440579/Native+Open+edX+Ubuntu+16.04+64+bit+Installation>`_ maintained by edX relies on `Ansible scripts <https://github.com/edx/configuration/>`_ to deploy Open edX on one or multiple servers. These scripts suffer from a couple issues that Tutor tries to address:
|
The `native installation <https://openedx.atlassian.net/wiki/spaces/OpenOPS/pages/146440579/Native+Open+edX+Ubuntu+16.04+64+bit+Installation>`_ maintained by edX relies on `Ansible scripts <https://github.com/openedx/configuration/>`_ to deploy Open edX on one or multiple servers. These scripts suffer from a couple of issues that Tutor tries to address:
|
||||||
|
|
||||||
1. Complexity: the scripts contain close to 35k lines of code spread over 780 files. They are really hard to understand, debug, and modify, and they are extremly slow. As a consequence, Open edX is often wrongly perceived as a project that is overly complex to manage. In contrast, Tutor generates mostly ``Dockerfile`` and ``docker-compose.yml`` files that make it easy to understand what is going on. Also, the whole installation should take about 10 minutes.
|
1. Complexity: the scripts contain close to 35k lines of code spread over 780 files. They are really hard to understand, debug, and modify, and they are extremely slow. As a consequence, Open edX is often wrongly perceived as a project that is overly complex to manage. In contrast, Tutor generates mostly ``Dockerfile`` and ``docker-compose.yml`` files that make it easy to understand what is going on. Also, the whole installation should take about 10 minutes.
|
||||||
2. Isolation from the OS: Tutor barely needs to touch your server because the entire platform is packaged inside Docker containers. You are thus free to run other services on your server without fear of indirectly crashing your Open edX platform.
|
2. Isolation from the OS: Tutor barely needs to touch your server because the entire platform is packaged inside Docker containers. You are thus free to run other services on your server without fear of indirectly crashing your Open edX platform.
|
||||||
3. Compatibility: Open edX is only compatible with Ubuntu 16.04, but that shouldn't mean you are forced to run this specific OS. With Tutor, you can deploy Open edX on just any server you like: Ubuntu 18.04, Red Hat, Debian... All docker-compatible platforms are supported.
|
3. Compatibility: Open edX is only compatible with Ubuntu 16.04, but that shouldn't mean you are forced to run this specific OS. With Tutor, you can deploy Open edX on just any server you like: Ubuntu 18.04, Red Hat, Debian... All docker-compatible platforms are supported.
|
||||||
4. Security: because you are no longer bound to a single OS, with Tutor you are now free to install security-related upgrades as soon as they become available.
|
4. Security: because you are no longer bound to a single OS, with Tutor you are now free to install security-related upgrades as soon as they become available.
|
||||||
5. Portability: Tutor makes it easy to move your platform from one server to another. Just zip-compress your Tutor project root, send it to another server and you're done.
|
5. Portability: Tutor makes it easy to move your platform from one server to another. Just zip-compress your Tutor project root, send it to another server and you're done.
|
||||||
|
|
||||||
There are also many features that are not included in the native installation, such as a :ref:`web user interface <webui>` for remotely installing the platform, :ref:`Kubernetes deployment <k8s>`, additional languages, etc. You'll discover these differences as you explore Tutor :)
|
Many features that are not included in the native installation, such as a `web user interface <https://github.com/overhangio/tutor-webui>`__ for remotely installing the platform, :ref:`Kubernetes deployment <k8s>`, additional languages, etc. You'll discover these differences as you explore Tutor :)
|
||||||
|
|
||||||
What's the difference with the official devstack?
|
What's the difference with the official devstack?
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
|
||||||
The `devstack <https://github.com/edx/devstack>`_ is meant for development only, not for production deployment. Tutor can be used both for production deployment and :ref:`locally hacking on Open edX <development>`.
|
The `devstack <https://github.com/openedx/devstack>`_ is meant for development only, not for production deployment. Tutor can be used both for production deployment and :ref:`locally hacking on Open edX <development>`.
|
||||||
|
|
||||||
Is Tutor officially supported by edX?
|
Is Tutor officially supported by edX?
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
No. Tutor is developed independently from edX. That means that the folks at edX.org are *not* responsible for troubleshooting issues of this project. Please don't bother Ned ;-)
|
Yes: as of the Open edX Maple release (December 9th 2021), Tutor is the only officially supported installation method for Open edX: see the `official installation instructions <https://edx.readthedocs.io/projects/edx-installing-configuring-and-running/en/open-release-quince.master/installation/index.html>`__.
|
||||||
|
|
||||||
What features are missing from Tutor?
|
What features are missing from Tutor?
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
Tutor tries very hard to support all major Open edX features, notably in the form of :ref:`plugins <existing_plugins>`. In particular, the discovery and ecommerce services, once unavailable in Tutor, can now be easily installed via plugins. If you are interested in sponsoring the development of a new plugin, please `get in touch <mailto:worktogether@overhang.io>`__!
|
Tutor tries very hard to support all major Open edX features, notably in the form of :ref:`plugins <existing_plugins>`. If you are interested in sponsoring the development of a new plugin, please `get in touch <mailto:worktogether@overhang.io>`__!
|
||||||
|
|
||||||
It should be noted that the `Analytics <https://github.com/edx/edx-analytics-pipeline>`__ stack is currently unsupported, and will likely stay so in the future, as it would require a tremendous amount of work to containerize all the components. For generating great-looking analytics reports, we recommend the `Figures plugin <https://github.com/overhangio/tutor-figures>`__.
|
It should be noted that the `Insights <https://github.com/openedx/edx-analytics-pipeline>`__ stack is currently unsupported, because of its complexity, lack of support, and extensibility. To replace it, we developed `Cairn <https://github.com/overhangio/tutor-cairn>`__ the next-generation analytics solution for Open edX. You should check it out 😉
|
||||||
|
|
||||||
Are there people already running this in production?
|
Are there people already running this in production?
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
|
|
||||||
Yes, many of them. There is no way to count precisely how many running Open edX platforms were deployed with Tutor, but from feedback collected directly from real users, there must be dozens, if not hundreds. Tutor is also used by some Open edX providers who are hosting platforms for their customers.
|
Yes: system administrators all around the world use Tutor to run their Open edX platforms, from single-class school teachers to renowned universities, Open edX SaaS providers, and nation-wide learning platforms.
|
||||||
|
|
||||||
Why should I trust software written by some random guy on the Internet?
|
Why should I trust your software?
|
||||||
-----------------------------------------------------------------------
|
---------------------------------
|
||||||
|
|
||||||
You shouldn't :) Tutor is actively maintained by `Overhang.IO <https://overhang.io>`_, a France-based company founded by `Régis Behmo <https://github.com/regisb/>`_. Régis has been working on Tutor since early 2018; he has been a contributor of the Open edX project since 2015. In particular, he has worked for 2 years at `FUN-MOOC <https://www.fun-mooc.fr/>`_, one of the top 5 largest Open edX platforms in the world. He presented several talks at the Open edX conferences:
|
You shouldn't :) Tutor is actively maintained by `Edly <https://edly.io>`__, a US-based ed-tech company facilitating over 40 million learners worldwide through its eLearning solutions. With a credible engineering team that has won clients' hearts globally led by `Régis Behmo <https://github.com/regisb/>`__, Tutor has empowered numerous edtech ventures over the years. Additionally, Tutor is a `community-led project <https://github.com/overhangio/tutor>`__ with many contributions from its :ref:`project maintainers <maintainers>`.
|
||||||
|
|
||||||
- *Deploying a robust, scalable Open edX platform in 1 click (or less) with Tutor*, March 2019 (`video <https://www.youtube.com/watch?v=Oqc7c-3qFc4>`_, `slides <https://regisb.github.io/openedx2019/>`_)
|
|
||||||
- *Videofront: a Self-Hosted YouTube*, June 2017 (`video <https://www.youtube.com/watch?v=e7bJchJrmP8&t=5m53s>`__, `slides <http://regisb.github.io/openedx-conference-2017/>`__)
|
|
||||||
- *Open edX 101: A Source Code Review*, June 2016 (`video <https://www.youtube.com/watch?v=DVku7Y7XQII>`__, `slides <http://regisb.github.io/openedx-conference-2016/>`__)
|
|
||||||
- *FUN: Life in the Avant-Garde*, Oct. 2015 (`video <https://www.youtube.com/watch?v=V1EBo1l8BgY>`__, `slides <http://regisb.github.io/openedx-conference-2015/>`__)
|
|
||||||
|
|
|
@ -5,8 +5,8 @@ Getting started
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
|
install
|
||||||
intro
|
intro
|
||||||
quickstart
|
quickstart
|
||||||
install
|
whatnext
|
||||||
whatnext
|
|
||||||
|
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 11 KiB |
BIN
docs/img/launch.webp
Normal file
After Width: | Height: | Size: 9.0 MiB |
Before Width: | Height: | Size: 12 KiB |
BIN
docs/img/portainer.png
Normal file
After Width: | Height: | Size: 215 KiB |
Before Width: | Height: | Size: 18 MiB |
Before Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 200 KiB |
|
@ -1,10 +1,13 @@
|
||||||
|
Tutor: the Docker-based Open edX distribution designed for peace of mind
|
||||||
|
========================================================================
|
||||||
|
|
||||||
.. include:: ../README.rst
|
.. include:: ../README.rst
|
||||||
:start-after: _readme_intro_start:
|
:start-after: _readme_intro_start:
|
||||||
:end-before: _readme_intro_end:
|
:end-before: _readme_intro_end:
|
||||||
|
|
||||||
.. image:: ./img/quickstart.gif
|
.. image:: ./img/launch.webp
|
||||||
:alt: Tutor local quickstart
|
:alt: Tutor local launch
|
||||||
:target: https://terminalizer.com/view/91b0bfdd557
|
:target: https://www.terminalizer.com/view/3a8d55835686
|
||||||
|
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
|
@ -22,12 +25,23 @@
|
||||||
gettingstarted
|
gettingstarted
|
||||||
run
|
run
|
||||||
configuration
|
configuration
|
||||||
plugins
|
plugins/index
|
||||||
extra
|
reference/index
|
||||||
|
tutorials/index
|
||||||
troubleshooting
|
troubleshooting
|
||||||
tutor
|
tutor
|
||||||
faq
|
faq
|
||||||
|
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
:caption: Project links
|
||||||
|
|
||||||
|
Source code <https://github.com/overhangio/tutor>
|
||||||
|
Community forums <https://discuss.openedx.org/tag/tutor>
|
||||||
|
Pypi releases <https://pypi.org/project/tutor>
|
||||||
|
Changelog <https://github.com/overhangio/tutor/blob/master/CHANGELOG.md>
|
||||||
|
|
||||||
Source code
|
Source code
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
@ -48,8 +62,6 @@ This work is licensed under the terms of the `GNU Affero General Public License
|
||||||
|
|
||||||
The AGPL license covers the Tutor code, including the Dockerfiles, but not the content of the Docker images which can be downloaded from https://hub.docker.com. Software other than Tutor provided with the docker images retain their original license.
|
The AGPL license covers the Tutor code, including the Dockerfiles, but not the content of the Docker images which can be downloaded from https://hub.docker.com. Software other than Tutor provided with the docker images retain their original license.
|
||||||
|
|
||||||
The Tutor plugin system is licensed under the terms of the `Apache License, Version 2.0 <https://opensource.org/licenses/Apache-2.0>`__.
|
The Tutor plugin and hooks system is licensed under the terms of the `Apache License, Version 2.0 <https://opensource.org/licenses/Apache-2.0>`__.
|
||||||
|
|
||||||
The :ref:`Tutor Web UI <webui>` depends on the `Gotty <https://github.com/yudai/gotty/>`_ binary, which is provided under the terms of the `MIT license <https://github.com/yudai/gotty/blob/master/LICENSE>`_.
|
|
||||||
|
|
||||||
© 2021 Tutor is a registered trademark of SASU NULI NULI. All Rights Reserved.
|
© 2021 Tutor is a registered trademark of SASU NULI NULI. All Rights Reserved.
|
||||||
|
|
136
docs/install.rst
|
@ -1,23 +1,24 @@
|
||||||
.. _install:
|
.. _install:
|
||||||
|
|
||||||
Install Tutor
|
Installing Tutor
|
||||||
=============
|
================
|
||||||
|
|
||||||
.. _requirements:
|
.. _requirements:
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* Supported OS: Tutor runs on any 64-bit, UNIX-based system. It was also reported to work on Windows.
|
* Supported OS: Tutor runs on any 64-bit, UNIX-based OS. It was also reported to work on Windows (with `WSL 2 <https://docs.microsoft.com/en-us/windows/wsl/install>`__).
|
||||||
|
* Architecture: Both AMD64 and ARM64 are supported.
|
||||||
* Required software:
|
* Required software:
|
||||||
|
|
||||||
- `Docker <https://docs.docker.com/engine/installation/>`__: v18.06.0+
|
- `Docker <https://docs.docker.com/engine/installation/>`__: v24.0.5+ (with BuildKit 0.11+)
|
||||||
- `Docker Compose <https://docs.docker.com/compose/install/>`__: v1.22.0+
|
- `Docker Compose <https://docs.docker.com/compose/install/>`__: v2.0.0+
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
Do not attempt to simply run ``apt-get install docker docker-compose`` on older Ubuntu platforms, such as 16.04 (Xenial), as you will get older versions of these utilities.
|
Do not attempt to simply run ``apt-get install docker docker-compose`` on older Ubuntu platforms, such as 16.04 (Xenial), as you will get older versions of these utilities.
|
||||||
|
|
||||||
* Ports 80 and 443 should be open. If other web services run on these ports, check the section on :ref:`how to setup a web proxy <web_proxy>`.
|
* Ports 80 and 443 should be open. If other web services run on these ports, check the tutorial on :ref:`how to setup a web proxy <web_proxy>`.
|
||||||
* Hardware:
|
* Hardware:
|
||||||
|
|
||||||
- Minimum configuration: 4 GB RAM, 2 CPU, 8 GB disk space
|
- Minimum configuration: 4 GB RAM, 2 CPU, 8 GB disk space
|
||||||
|
@ -26,56 +27,56 @@ Requirements
|
||||||
.. note::
|
.. note::
|
||||||
On Mac OS, by default, containers are allocated 2 GB of RAM, which is not enough. You should follow `these instructions from the official Docker documentation <https://docs.docker.com/docker-for-mac/#advanced>`__ to allocate at least 4-5 GB to the Docker daemon. If the deployment fails because of insufficient memory during database migrations, check the :ref:`relevant section in the troubleshooting guide <migrations_killed>`.
|
On Mac OS, by default, containers are allocated 2 GB of RAM, which is not enough. You should follow `these instructions from the official Docker documentation <https://docs.docker.com/docker-for-mac/#advanced>`__ to allocate at least 4-5 GB to the Docker daemon. If the deployment fails because of insufficient memory during database migrations, check the :ref:`relevant section in the troubleshooting guide <migrations_killed>`.
|
||||||
|
|
||||||
.. _install_binary:
|
Download
|
||||||
|
--------
|
||||||
|
|
||||||
Direct binary download
|
Choose **one** of the installation methods below. If you install Tutor in different ways, you will end up with multiple ``tutor`` executables, which is going to be very confusing. At any time, you can check the path to your ``tutor`` executable by running ``which tutor``.
|
||||||
----------------------
|
|
||||||
|
|
||||||
The latest binaries can be downloaded from https://github.com/overhangio/tutor/releases. From the command line:
|
Python package
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. include:: cli_download.rst
|
.. include:: download/pip.rst
|
||||||
|
|
||||||
This is the simplest and recommended installation method for most people. Note however that you will not be able to use custom plugins with this pre-compiled binary. The only plugins you can use with this approach are those that are already bundled with the binary: see the :ref:`existing plugins <existing_plugins>`.
|
Check the "tutor" package on Pypi: https://pypi.org/project/tutor. You will need Python >= 3.6 with pip and the libyaml development headers. On Ubuntu, these requirements can be installed by running::
|
||||||
|
|
||||||
.. _install_source:
|
|
||||||
|
|
||||||
Alternative installation methods
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
If you would like to inspect the Tutor source code, you are most welcome to install Tutor from `Pypi <https://pypi.org/project/tutor-openedx/>`_ or directly from `the Github repository <https://github.com/overhangio/tutor>`_. You will need python >= 3.6 with pip and the libyaml development headers. On Ubuntu, these requirements can be installed by running::
|
|
||||||
|
|
||||||
sudo apt install python3 python3-pip libyaml-dev
|
sudo apt install python3 python3-pip libyaml-dev
|
||||||
|
|
||||||
Installing from pypi
|
.. _install_binary:
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
::
|
Binary release
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
pip install tutor-openedx
|
The latest binaries can be downloaded from https://github.com/overhangio/tutor/releases. From the command line:
|
||||||
|
|
||||||
|
.. include:: download/binary.rst
|
||||||
|
|
||||||
|
This is the simplest and recommended installation method for most people who do not have Python 3 on their machine. Note however that **you will not be able to use custom plugins** with this pre-compiled binary. The only plugins you can use with this approach are those that are already bundled with the binary: see the :ref:`existing plugins <existing_plugins>`.
|
||||||
|
|
||||||
|
.. _install_source:
|
||||||
|
|
||||||
Installing from source
|
Installing from source
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
::
|
To inspect the Tutor source code, install Tutor from `the Github repository <https://github.com/overhangio/tutor>`__::
|
||||||
|
|
||||||
git clone https://github.com/overhangio/tutor
|
git clone https://github.com/overhangio/tutor
|
||||||
cd tutor
|
cd tutor
|
||||||
pip install -e .
|
pip install -e .
|
||||||
|
|
||||||
DNS records
|
Configuring DNS records
|
||||||
-----------
|
-----------------------
|
||||||
|
|
||||||
When running a server in production, it is necessary to define `DNS records <https://en.wikipedia.org/wiki/Domain_Name_System#Resource_records>`__ which will make it possible to access your Open edX platform by name in your browser. The precise procedure to create DNS records vary from one provider to the next and is beyond the scope of these docs. You should create a record of type A with a name equal to your LMS hostname (given by ``tutor config printvalue LMS_HOST``) and a value that indicates the IP address of your server. Applications other than the LMS, such as the studio, ecommerce, etc. typically reside in subdomains of the LMS. Thus, you should also create a CNAME record to point all subdomains of the LMS to the LMS_HOST.
|
When running a server in production, it is necessary to define `DNS records <https://en.wikipedia.org/wiki/Domain_Name_System#Resource_records>`__ which will make it possible to access your Open edX platform by name in your browser. The precise procedure to create DNS records varies from one provider to the next and is beyond the scope of these docs. You should create a record of type A with a name equal to your LMS hostname (given by ``tutor config printvalue LMS_HOST``) and a value that indicates the IP address of your server. Applications other than the LMS, such as the studio, ecommerce, etc. typically reside in subdomains of the LMS. Thus, you should also create a CNAME record to point all subdomains of the LMS to the LMS_HOST.
|
||||||
|
|
||||||
For instance, the demo Open edX server that runs at http://demo.openedx.overhang.io has the following DNS records::
|
For instance, to run an Open edX server at https://learn.mydomain.com on a server with IP address 1.1.1.1, you would need to configure the following DNS records::
|
||||||
|
|
||||||
demo.openedx 1800 IN A 172.105.89.208
|
learn 1800 IN A 1.1.1.1
|
||||||
*.demo.openedx 1800 IN CNAME demo.openedx.overhang.io.
|
*.learn 1800 IN CNAME learn.mydomain.com.
|
||||||
|
|
||||||
.. _cloud_install:
|
.. _cloud_install:
|
||||||
|
|
||||||
Zero-click AWS installation
|
Zero-click AWS installation
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
---------------------------
|
||||||
|
|
||||||
Tutor can be launched on Amazon Web Services very quickly with the `official Tutor AMI <https://aws.amazon.com/marketplace/pp/B07PV3TB8X>`__. Shell access is not required, as all configuration will happen through the Tutor web user interface. For detailed installation instructions, we recommend watching the following video:
|
Tutor can be launched on Amazon Web Services very quickly with the `official Tutor AMI <https://aws.amazon.com/marketplace/pp/B07PV3TB8X>`__. Shell access is not required, as all configuration will happen through the Tutor web user interface. For detailed installation instructions, we recommend watching the following video:
|
||||||
|
|
||||||
|
@ -86,42 +87,77 @@ Tutor can be launched on Amazon Web Services very quickly with the `official Tut
|
||||||
Upgrading
|
Upgrading
|
||||||
---------
|
---------
|
||||||
|
|
||||||
With Tutor, it is very easy to upgrade to a more recent Open edX or Tutor release. Just install the latest ``tutor`` version (using either methods above) and run the ``quickstart`` command again. If you have :ref:`customised <configuration_customisation>` your docker images, you will have to re-build them prior to running ``quickstart``.
|
To upgrade your Open edX site or benefit from the latest features and bug fixes, you should simply upgrade Tutor. Start by backing up your data and reading the `release notes <https://docs.openedx.org/en/latest/community/release_notes/>`_ for the current release.
|
||||||
|
|
||||||
``quickstart`` should take care of automatically running the upgrade process. If for some reason you need to *manually* upgrade from an Open edX release to the next, you should run ``tutor local upgrade``. For instance, to upgrade from Juniper to Koa, run::
|
Next, upgrade the "tutor" package and its dependencies::
|
||||||
|
|
||||||
tutor local upgrade --from=juniper
|
pip install --upgrade "tutor[full]"
|
||||||
|
|
||||||
|
Then run the ``launch`` command again. Depending on your deployment target, run one of::
|
||||||
|
|
||||||
|
tutor local launch # for local installations
|
||||||
|
tutor dev launch # for local development installations
|
||||||
|
tutor k8s launch # for Kubernetes installation
|
||||||
|
|
||||||
|
Upgrading with custom Docker images
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
If you run :ref:`customised <configuration_customisation>` Docker images, you need to rebuild them before running ``launch``::
|
||||||
|
|
||||||
|
tutor config save
|
||||||
|
tutor images build all # specify here the images that you need to build
|
||||||
|
tutor local launch
|
||||||
|
|
||||||
|
Upgrading to a new Open edX release
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Major Open edX releases are published twice a year, in June and December, by the Open edX `Build/Test/Release working group <https://discuss.openedx.org/c/working-groups/build-test-release/30>`__. When a new Open edX release comes out, Tutor gets a major version bump (see :ref:`versioning`). Such an upgrade typically includes multiple breaking changes. Any upgrade is final because downgrading is not supported. Thus, when upgrading your platform from one major version to the next, it is strongly recommended to do the following:
|
||||||
|
|
||||||
|
1. Read the changes listed in the `CHANGELOG.md <https://github.com/overhangio/tutor/blob/master/CHANGELOG.md>`__ file. Breaking changes are identified by a "💥".
|
||||||
|
2. Perform a backup (see the :ref:`backup tutorial <backup_tutorial>`). On a local installation, this is typically done with::
|
||||||
|
|
||||||
|
tutor local stop
|
||||||
|
sudo rsync -avr "$(tutor config printroot)"/ /tmp/tutor-backup/
|
||||||
|
|
||||||
|
3. If you created custom plugins, make sure that they are compatible with the newer release.
|
||||||
|
4. Test the new release in a sandboxed environment.
|
||||||
|
5. If you are running edx-platform, or some other repository from a custom branch, then you should rebase (and test) your changes on top of the latest release tag (see :ref:`edx_platform_fork`).
|
||||||
|
|
||||||
|
The process for upgrading from one major release to the next works similarly to any other upgrade, with the ``launch`` command (see above). The single difference is that if the ``launch`` command detects that your tutor environment was generated with an older release, it will perform a few release-specific upgrade steps. These extra upgrade steps will be performed just once. But they will be ignored if you updated your local environment (for instance: with ``tutor config save``) before running ``launch``. This situation typically occurs if you need to re-build some Docker images (see above). In such a case, you should make use of the ``upgrade`` command. For instance, to upgrade a local installation from Palm to Quince and rebuild some Docker images, run::
|
||||||
|
|
||||||
|
tutor config save
|
||||||
|
tutor images build all # list the images that should be rebuilt here
|
||||||
|
tutor local upgrade --from=palm
|
||||||
|
tutor local launch
|
||||||
|
|
||||||
.. _autocomplete:
|
.. _autocomplete:
|
||||||
|
|
||||||
Autocomplete
|
Shell autocompletion
|
||||||
------------
|
--------------------
|
||||||
|
|
||||||
Tutor is built on top of `Click <https://click.palletsprojects.com>`_, which is a great library for building command line interface (CLI) tools. As such, Tutor benefits from all Click features, including `auto-completion <https://click.palletsprojects.com/en/7.x/bashcomplete/>`_. After installing Tutor, auto-completion can be enabled by running::
|
Tutor is built on top of `Click <https://click.palletsprojects.com>`_, which is a great library for building command line interface (CLI) tools. As such, Tutor benefits from all Click features, including `auto-completion <https://click.palletsprojects.com/en/8.x/bashcomplete/>`_. After installing Tutor, auto-completion can be enabled in bash by running::
|
||||||
|
|
||||||
_TUTOR_COMPLETE=source tutor >> ~/.bashrc
|
_TUTOR_COMPLETE=bash_source tutor >> ~/.bashrc
|
||||||
|
|
||||||
If you are running zsh, run instead::
|
If you are running zsh, run instead::
|
||||||
|
|
||||||
_TUTOR_COMPLETE=source_zsh tutor >> ~/.zshrc
|
_TUTOR_COMPLETE=zsh_source tutor >> ~/.zshrc
|
||||||
|
|
||||||
After opening a new shell, you can test auto-completion by typing::
|
After opening a new shell, you can test auto-completion by typing::
|
||||||
|
|
||||||
tutor <tab><tab>
|
tutor <tab><tab>
|
||||||
|
|
||||||
.. include:: podman.rst
|
|
||||||
|
|
||||||
Uninstallation
|
Uninstallation
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
It is fairly easy to completely uninstall Tutor and to delete the Open edX platforms that is running locally.
|
It is fairly easy to completely uninstall Tutor and to delete the Open edX platforms that are running locally.
|
||||||
|
|
||||||
First of all, stop any locally-running platform::
|
First of all, stop any locally-running platform and remove all Tutor containers::
|
||||||
|
|
||||||
tutor local stop
|
tutor local dc down --remove-orphans
|
||||||
tutor dev stop
|
tutor dev dc down --remove-orphans
|
||||||
|
|
||||||
Then, delete all data associated to your Open edX platform::
|
Then, delete all data associated with your Open edX platform::
|
||||||
|
|
||||||
# WARNING: this step is irreversible
|
# WARNING: this step is irreversible
|
||||||
sudo rm -rf "$(tutor config printroot)"
|
sudo rm -rf "$(tutor config printroot)"
|
||||||
|
@ -129,7 +165,13 @@ Then, delete all data associated to your Open edX platform::
|
||||||
Finally, uninstall Tutor itself::
|
Finally, uninstall Tutor itself::
|
||||||
|
|
||||||
# If you installed tutor from source
|
# If you installed tutor from source
|
||||||
pip uninstall tutor-openedx
|
pip uninstall tutor
|
||||||
|
|
||||||
# If you downloaded the tutor binary
|
# If you downloaded the tutor binary
|
||||||
sudo rm /usr/local/bin/tutor
|
sudo rm /usr/local/bin/tutor
|
||||||
|
|
||||||
|
# Optionally, you may want to remove Tutor plugins installed.
|
||||||
|
# You can get a list of the installed plugins:
|
||||||
|
pip freeze | grep tutor
|
||||||
|
# You can then remove them using the following command:
|
||||||
|
pip uninstall <plugin-name>
|
||||||
|
|
|
@ -6,7 +6,7 @@ Concepts
|
||||||
What is Open edX?
|
What is Open edX?
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
`Open edX <http://open.edx.org/>`_ is a thriving open source project, backed by a great community, for running an online learning platform at scale. Open edX comes with an LMS (Learning Management System) where students access course contents, a CMS (Content Management System) that course staff uses to design courses, and a few other components to provide more services to students, course staff and platform administrators.
|
`Open edX <http://open.edx.org/>`_ is a thriving open source project, backed by a great community, for running an online learning platform at scale. Open edX comes with an LMS (Learning Management System) where students access course contents, a CMS (Content Management System) that course staff uses to design courses, and a few other components to provide more services to students, course staff, and platform administrators.
|
||||||
|
|
||||||
Should I use Open edX?
|
Should I use Open edX?
|
||||||
----------------------
|
----------------------
|
||||||
|
@ -18,80 +18,120 @@ Open edX competitors include `Moodle <https://moodle.org/>`__, `Instructure's Ca
|
||||||
* Multiple extension points for comprehensive customization
|
* Multiple extension points for comprehensive customization
|
||||||
* Modern, intuitive user interface to keep students engaged
|
* Modern, intuitive user interface to keep students engaged
|
||||||
|
|
||||||
Open edX is a safe bet: it is backed by edX.org, a US-based non-profit that is committed to open source and which runs Open edX to service its millions of learners. With Open edX you can be sure that the features you need will be available. If it's good enough for Harvard, the MIT or the French government, then it will probably also work for you.
|
Open edX is a safe bet: it is backed by edX.org, a US-based non-profit that is committed to open source and which runs Open edX to service its millions of learners. With Open edX you can be sure that the features you need will be available. If it's good enough for Harvard, the MIT, or the French government, then it will probably also work for you.
|
||||||
|
|
||||||
Should I self-host Open edX or rely on a hosting provider?
|
Should I self-host Open edX or rely on a hosting provider?
|
||||||
----------------------------------------------------------
|
----------------------------------------------------------
|
||||||
|
|
||||||
Third-party Open edX providers can provide you with custom, closed-source features that they developed internally. However, their pricing is usually per-seat: that makes it difficult to predict how much running Open edX will actually cost you if you don't know in advance how many students will connect to your platform. And once you start scaling up and adding many students, running the platform will become very expensive.
|
Third-party Open edX providers can provide you with custom, closed-source features that they developed internally. However, their pricing is usually per-seat: that makes it difficult to predict how much running Open edX will actually cost you if you don't know in advance how many students will connect to your platform. And once you start scaling up and adding many students, running the platform will become very expensive.
|
||||||
|
|
||||||
On the other hand, running Open edX on your own servers will help you keep your costs under control. Because you own your servers and data, you will always be able to migrate your platform, either to a different cloud provider or an Open edX service provider. This is the true power of open source.
|
On the other hand, running Open edX on your own servers will help you keep your costs under control. Because you own your servers and data, you will always be able to migrate your platform, either to a different cloud provider or an Open edX service provider. This is the true power of the open source.
|
||||||
|
|
||||||
Should I use Tutor?
|
Should I use Tutor?
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
Running software on premises is cheaper only if your management costs don't go through the roof. You do not want to hire a full-time devops team just for managing your online learning platform. This is why we created Tutor: to make it easy to run a state-of-the-art online learning platform without breaking the bank. Historically, it's always been difficult to install Open edX with the native installation scripts. For instance, there are no official instructions for upgrading an existing Open edX platform: the `recommended approach <https://docs.bitnami.com/azure/apps/edx/administration/upgrade/>`__ is to backup all data, trash the server and create a new one. As a consequence, people tend not to upgrade their platform and keep running on deprecated releases. Tutor makes it possible even to non-technical users to launch, manage and upgrade Open edX at any scale. Should you choose at some point that Tutor is not the right solution for you, you always have an escape route: because Tutor is open source software, you can easily dump your data and switch to a different installation method. But we are confident you will not do that 😉
|
Running software on-premises is cheaper only if your management costs don't go through the roof. You do not want to hire a full-time devops team just for managing your online learning platform. This is why we created Tutor: to make it easy to run a state-of-the-art online learning platform without breaking the bank. Historically, it's always been difficult to install Open edX with native installation scripts. For instance, there are no official instructions for upgrading an existing Open edX platform: the `recommended approach <https://docs.bitnami.com/azure/apps/edx/administration/upgrade/>`__ is to backup all data, trash the server, and create a new one. As a consequence, people tend not to upgrade their platform and keep running on deprecated releases. Tutor makes it possible even for non-technical users to launch, manage and upgrade Open edX at any scale. Should you choose at some point that Tutor is not the right solution for you, you always have an escape route: because Tutor is open source software, you can easily dump your data and switch to a different installation method. But we are confident you will not do that 😉
|
||||||
|
|
||||||
To learn more about Tutor, watch this 7-minute lightning talk that was made at the 2019 Open edX conference in San Diego, CA (with `slides <https://regisb.github.io/openedx2019/>`_):
|
To learn more about Tutor, watch this 7-minute lightning talk that was made at the 2019 Open edX conference in San Diego, CA (with `slides <https://regisb.github.io/openedx2019/>`_):
|
||||||
|
|
||||||
.. youtube:: Oqc7c-3qFc4
|
.. youtube:: Oqc7c-3qFc4
|
||||||
|
|
||||||
How does Tutor work, technically speaking?
|
How does Tutor simplify Open edX deployment?
|
||||||
------------------------------------------
|
--------------------------------------------
|
||||||
|
|
||||||
Tutor simplifies the deployment of Open edX by:
|
Tutor simplifies the deployment of Open edX by:
|
||||||
|
|
||||||
1. Separating the configuration logic from the deployment platforms.
|
1. Separating the configuration logic from the deployment platforms.
|
||||||
2. Running application processes in cleanly separated `docker containers <https://www.docker.com/resources/what-container>`_.
|
2. Running application processes in cleanly separated `docker containers <https://www.docker.com/resources/what-container>`_.
|
||||||
3. Providing user-friendly, reliable commands for common administration tasks, including upgrades and monitoring.
|
3. Providing user-friendly, reliable commands for common administration tasks, including upgrades and monitoring.
|
||||||
4. Using a simple :ref:`plugin system <plugins>` that makes it easy to extend and customize Open edX.
|
4. Using a simple :ref:`plugin system <plugins>` that makes it easy to extend and customise Open edX.
|
||||||
|
|
||||||
.. image:: https://overhang.io/static/img/openedx-plus-docker-is-tutor.png
|
.. image:: https://overhang.io/static/img/openedx-plus-docker-is-tutor.png
|
||||||
:alt: Open edX + Docker = Tutor
|
:alt: Open edX + Docker = Tutor
|
||||||
:width: 500px
|
:width: 500px
|
||||||
:align: center
|
:align: center
|
||||||
|
|
||||||
Because Docker containers are becoming an industry-wide standard, that means that with Tutor it becomes possible to run Open edX anywhere: for now, Tutor supports deploying on a local server, with `docker-compose <https://docs.docker.com/compose/overview/>`_, and in a large cluster, with `Kubernetes <http://kubernetes.io/>`_. But in the future, Tutor may support other deployment platforms.
|
Because Docker containers are becoming an industry-wide standard, that means that with Tutor it becomes possible to run Open edX anywhere: for now, Tutor supports deploying on a local server, with `docker compose <https://docs.docker.com/compose/overview/>`_, and in a large cluster, with `Kubernetes <http://kubernetes.io/>`_. But in the future, Tutor may support other deployment platforms.
|
||||||
|
|
||||||
Where can I try Open edX and Tutor?
|
Where can I try Open edX and Tutor?
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
A demo Open edX platform is available at https://demo.openedx.overhang.io. This platform was deployed using Tutor and the `Indigo theme <https://github.com/overhangio/indigo>`__. Feel free to play around with the following credentials:
|
A sandbox Open edX platform is available at https://sandbox.openedx.edly.io. This platform was deployed using Tutor and the `Indigo theme <https://github.com/overhangio/indigo>`__. Feel free to play around with the following credentials:
|
||||||
|
|
||||||
* Admin user: username=admin email=admin@overhang.io password=admin
|
* Admin user: username=admin email=admin@overhang.io password=admin
|
||||||
* Student user: username=student email=student@overhang.io password=student
|
* Student user: username=student email=student@overhang.io password=student
|
||||||
|
|
||||||
The Android mobile application for this website can be downloaded at this url: http://demo.openedx.overhang.io/static/mobile/app.apk
|
The Android mobile application for this demo platform can be downloaded at this url: https://mobile.sandbox.openedx.edly.io/app.apk
|
||||||
|
|
||||||
Urls:
|
Urls:
|
||||||
|
|
||||||
* LMS: https://demo.openedx.overhang.io
|
* LMS: https://sandbox.openedx.edly.io
|
||||||
* Analytics (from the `Figures plugin <https://pypi.org/project/tutor-figures/>`__): https://demo.openedx.overhang.io/figures
|
* Studio (CMS): https://studio.sandbox.openedx.edly.io
|
||||||
* Studio (CMS): https://studio.demo.openedx.overhang.io
|
|
||||||
|
|
||||||
The platform is reset every day at 9:00 AM, `Paris (France) time <https://time.is/Paris>`__, so feel free to try and break things as much as you want.
|
The platform is reset every day at 9:00 AM, `Paris (France) time <https://time.is/Paris>`__, so feel free to try and break things as much as you want.
|
||||||
|
|
||||||
|
.. _how_does_tutor_work:
|
||||||
|
|
||||||
How does Tutor work?
|
How does Tutor work?
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
Tutor is a piece of software that takes care of exactly three things:
|
||||||
|
|
||||||
|
1. Project configuration: user-specific settings (such as secrets) are stored in a single ``config.yml`` file.
|
||||||
|
2. Template rendering: all the files that are necessary to run your platform are generated from a set of templates and user-specific settings.
|
||||||
|
3. Command-line interface (CLI): frequently-used administration commands are gathered in a convenient, unified CLI.
|
||||||
|
|
||||||
You can experiment with Tutor very quickly: start by `installing <install>`_ Tutor. Then run::
|
You can experiment with Tutor very quickly: start by `installing <install>`_ Tutor. Then run::
|
||||||
|
|
||||||
tutor config save --interactive
|
$ tutor config save --interactive
|
||||||
|
|
||||||
This command does two things:
|
Then, to view the result of the above command::
|
||||||
|
|
||||||
1. Generate a ``config.yml`` configuration file: this file contains core :ref:`configuration parameters <configuration>` for your Open edX platforms, such as passwords and feature flags.
|
$ cd "$(tutor config printroot)"
|
||||||
2. Generate an ``env/`` folder, which we call the Tutor "environment", and which contains all the files that are necessary to run an Open edX platform: these are mostly Open edX configuration files.
|
$ ls
|
||||||
|
config.yml env
|
||||||
|
|
||||||
All these files are stored in a single folder, called the Tutor project root. On Linux, this folder is in ``~/.local/share/tutor``. On Mac OS it is ``~/Library/Application Support/tutor``.
|
The ``config.yml`` file contains your user-specific Open edX settings (item #1 above). The ``env/`` folder contains the rendered templates which will be used to run your Open edX platform (item #2). For instance, the ``env/local`` folder contains the ``docker-compose.yml`` file to run Open edX locally.
|
||||||
|
|
||||||
The values from ``config.yml`` are used to generate the environment files in ``env/``. As a consequence, **every time the values from** ``config.yml`` **are modified, the environment must be regenerated**. This can be done with::
|
The values from ``config.yml`` are used to generate the environment files in ``env/``. As a consequence, **every time the values from** ``config.yml`` **are modified, the environment must be regenerated** with ``tutor config save``..
|
||||||
|
|
||||||
tutor config save
|
Because the Tutor environment is generated entirely from the values in ``config.yml``, you can ``rm -rf`` the ``env/`` folder at any time and re-create it with ``tutor config save``. Another consequence is that **any manual change made to a file in** ``env/`` **will be overwritten by** ``tutor config save`` **commands**. Consider yourself warned!
|
||||||
|
|
||||||
Another consequence is that **any manual change made to a file in** ``env/`` **will be overwritten by** ``tutor config save`` **commands**. Consider yourself warned!
|
You can now take advantage of the Tutor-powered CLI (item #3) to bootstrap your Open edX platform::
|
||||||
|
|
||||||
|
tutor local launch
|
||||||
|
|
||||||
|
Under the hood, Tutor simply runs ``docker compose`` and ``docker`` commands to launch your platform. These commands are printed in the standard output, such that you are free to replicate the same behaviour by simply copying/pasting the same commands.
|
||||||
|
|
||||||
|
How do I navigate Tutor's command-line interface?
|
||||||
|
-------------------------------------------------
|
||||||
|
|
||||||
|
Tutor commands are structured in an easy-to-follow hierarchy. At the top level, there are command trees for image and configuration management::
|
||||||
|
|
||||||
|
tutor config ...
|
||||||
|
tutor images ...
|
||||||
|
|
||||||
|
as well as command trees for each mode in which Tutor can run::
|
||||||
|
|
||||||
|
tutor local ... # Commands for managing a local Open edX deployment.
|
||||||
|
tutor k8s ... # Commands for managing a Kubernetes Open edX deployment.
|
||||||
|
tutor dev ... # Commands for hacking on Open edX in development mode.
|
||||||
|
|
||||||
|
Within each mode, Tutor has subcommands for managing that type of Open edX instance. Many of them are common between modes, such as ``launch``, ``start``, ``stop``, ``exec``, and ``logs``. For example::
|
||||||
|
|
||||||
|
tutor local logs # View logs of a local deployment.
|
||||||
|
tutor k8s logs # View logs of a Kubernetes-managed deployment.
|
||||||
|
tutor dev logs # View logs of a development platform.
|
||||||
|
|
||||||
|
Many commands can be further parameterized to specify their target and options, for example::
|
||||||
|
|
||||||
|
tutor local logs cms # View logs of the CMS container in a local deployment.
|
||||||
|
tutor k8s logs mysql # View logs of MySQL in Kubernetes-managed deployment.
|
||||||
|
tutor dev logs lms --tail 10 # View ten lines of logs of the LMS container in development mode.
|
||||||
|
|
||||||
|
And that's it! You do not need to understand Tutor's entire command-line interface to get started. Using the ``--help`` option that's available on every command, it is easy to learn as you go. For an in-depth guide, you can also explore the `CLI Reference <reference/index.rst>`_.
|
||||||
|
|
||||||
I'm ready, where do I start?
|
I'm ready, where do I start?
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
Right :ref:`here <gettingstarted>`!
|
Right :ref:`here <gettingstarted>`!
|
||||||
|
|
90
docs/k8s.rst
|
@ -20,24 +20,36 @@ Tutor was tested with server version 1.14.1 and client 1.14.3.
|
||||||
Memory
|
Memory
|
||||||
~~~~~~
|
~~~~~~
|
||||||
|
|
||||||
In the following, we assume you have access to a working Kubernetes cluster. `kubectl` should use your cluster configuration by default. To launch a cluster locally, you may try out Minikube. Just follow the `official installation instructions <https://kubernetes.io/docs/setup/minikube/>`_.
|
In the following, we assume you have access to a working Kubernetes cluster. ``kubectl`` should use your cluster configuration by default. To launch a cluster locally, you may try out Minikube. Just follow the `official installation instructions <https://kubernetes.io/docs/setup/minikube/>`__.
|
||||||
|
|
||||||
The Kubernetes cluster should have at least 4Gb of RAM on each node. When running Minikube, the virtual machine should have that much allocated memory. See below for an example with VirtualBox:
|
The Kubernetes cluster should have at least 4Gb of RAM on each node. When running Minikube, the virtual machine should have that much-allocated memory. See below for an example with VirtualBox:
|
||||||
|
|
||||||
.. image:: img/virtualbox-minikube-system.png
|
.. image:: img/virtualbox-minikube-system.png
|
||||||
:alt: Virtualbox memory settings for Minikube
|
:alt: Virtualbox memory settings for Minikube
|
||||||
|
|
||||||
Ingress controller and SSL/TLS certificates
|
Load Balancer and SSL/TLS certificates
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
As of Tutor v11, it is no longer required to setup an Ingress controller to access your platform. Instead Caddy exposes a LoadBalancer service and SSL/TLS certificates are transparently generated at runtime.
|
By default, Tutor deploys a `LoadBalancer <https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer>`__ service that exposes the Caddy deployment to the outside world. As in the local installation, this service is responsible for transparently generating SSL/TLS certificates at runtime. You will need to point your DNS records to this LoadBalancer object before the platform can work correctly. Thus, you should first start the Caddy load balancer, with::
|
||||||
|
|
||||||
S3-like object storage with `MinIO <https://www.minio.io/>`_
|
tutor k8s start caddy
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Like many web applications, Open edX needs to persist data. In particular, it needs to persist files uploaded by students and course designers. In the local installation, these files are persisted to disk, on the host filesystem. But on Kubernetes, it is difficult to share a single filesystem between different pods. This would require persistent volume claims with `ReadWriteMany` access mode, and these are difficult to setup.
|
Get the external IP of this service::
|
||||||
|
|
||||||
Luckily, there is another solution: at `edx.org <edx.org>`_, uploaded files are persisted on AWS S3: Open edX is compatible out-of-the-box with the S3 API for storing user-generated files. The problem with S3 is that it introduces a dependency on AWS. To solve this problem, Tutor comes with a plugin that emulates the S3 API but stores files on premises. This is achieved thanks to `MinIO <https://www.minio.io/>`_. If you want to deploy a production platform to Kubernetes, you will most certainly need to enable the ``minio`` plugin::
|
kubectl --namespace openedx get services/caddy
|
||||||
|
|
||||||
|
Use this external IP to configure your DNS records. Once the DNS records are configured, you should verify that the Caddy container has properly generated the SSL/TLS certificates by checking the container logs::
|
||||||
|
|
||||||
|
tutor k8s logs -f caddy
|
||||||
|
|
||||||
|
If for some reason, you would like to deploy your own load balancer, you should set ``ENABLE_WEB_PROXY=false`` just like in the :ref:`local installation <web_proxy>`. Then, point your load balancer at the "caddy" service, which will be a `ClusterIP <https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types>`__.
|
||||||
|
|
||||||
|
S3-like object storage with `MinIO <https://www.minio.io/>`__
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Like many web applications, Open edX needs to persist data. In particular, it needs to persist files uploaded by students and course designers. In the local installation, these files are persisted to disk, on the host filesystem. But on Kubernetes, it is difficult to share a single filesystem between different pods. This would require persistent volume claims with `ReadWriteMany` access mode, and these are difficult to set up.
|
||||||
|
|
||||||
|
Luckily, there is another solution: at `edx.org <edx.org>`_, uploaded files are persisted on AWS S3: Open edX is compatible out-of-the-box with the S3 API for storing user-generated files. The problem with S3 is that it introduces a dependency on AWS. To solve this problem, Tutor comes with a plugin that emulates the S3 API but stores files on-premises. This is achieved thanks to `MinIO <https://www.minio.io/>`__. If you want to deploy a production platform to Kubernetes, you will most certainly need to enable the ``minio`` plugin::
|
||||||
|
|
||||||
tutor plugins enable minio
|
tutor plugins enable minio
|
||||||
|
|
||||||
|
@ -46,18 +58,22 @@ The "minio.LMS_HOST" domain name will have to point to your Kubernetes cluster.
|
||||||
Kubernetes dashboard
|
Kubernetes dashboard
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
This is not a requirement per se, but it's very convenient to have a visual interface of the Kubernetes cluster. We suggest the official `Kubernetes dashboard <https://github.com/kubernetes/dashboard/>`_. Depending on your Kubernetes provider, you may need to install a dashboard yourself. There are generic instructions on the `project's README <https://github.com/kubernetes/dashboard/blob/master/README.md>`_. AWS provides `specific instructions <https://docs.aws.amazon.com/eks/latest/userguide/dashboard-tutorial.html>`_.
|
This is not a requirement per se, but it's very convenient to have a visual interface of the Kubernetes cluster. We suggest the official `Kubernetes dashboard <https://github.com/kubernetes/dashboard/>`__. Depending on your Kubernetes provider, you may need to install a dashboard yourself. There are general instructions on the `project's README <https://github.com/kubernetes/dashboard/blob/master/README.md>`__. AWS provides `specific instructions <https://docs.aws.amazon.com/eks/latest/userguide/dashboard-tutorial.html>`__.
|
||||||
|
|
||||||
On Minikube, the dashboard is already installed. To access the dashboard, run::
|
On Minikube, the dashboard is already installed. To access the dashboard, run::
|
||||||
|
|
||||||
minikube dashboard
|
minikube dashboard
|
||||||
|
|
||||||
|
Lastly, Tutor itself provides a rudimentary listing of your cluster's nodes, workloads, and services::
|
||||||
|
|
||||||
|
tutor k8s status
|
||||||
|
|
||||||
Technical details
|
Technical details
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
Under the hood, Tutor wraps ``kubectl`` commands to interact with the cluster. The various commands called by Tutor are printed in the console, so that you can reproduce and modify them yourself.
|
Under the hood, Tutor wraps ``kubectl`` commands to interact with the cluster. The various commands called by Tutor are printed in the console so that you can reproduce and modify them yourself.
|
||||||
|
|
||||||
Basically, the whole platform is described in manifest files stored in ``$(tutor config printroot)/env/k8s``. There is also a ``kustomization.yml`` file at the project root for `declarative application management <https://kubectl.docs.kubernetes.io/pages/app_management/apply.html>`_. This allows us to start and update resources with commands similar to ``kubectl apply -k $(tutor config printroot) --selector=...`` (see the ``kubectl apply`` `official documentation <https://kubectl.docs.kubernetes.io/pages/app_management/apply.html>`_).
|
Basically, the whole platform is described in manifest files stored in ``$(tutor config printroot)/env/k8s``. There is also a ``kustomization.yml`` file at the project root for `declarative application management <https://kubectl.docs.kubernetes.io/guides/config_management/introduction/#declarative-application-management>`__. This allows us to start and update resources with commands similar to ``kubectl apply -k $(tutor config printroot) --selector=...`` (see the ``kubectl apply`` `official documentation <https://kubectl.docs.kubernetes.io/references/kubectl/apply/>`__).
|
||||||
|
|
||||||
The other benefit of ``kubectl apply`` is that it allows you to customise the Kubernetes resources as much as you want. For instance, the default Tutor configuration can be extended by a ``kustomization.yml`` file stored in ``$(tutor config printroot)/env-custom/`` and which would start with::
|
The other benefit of ``kubectl apply`` is that it allows you to customise the Kubernetes resources as much as you want. For instance, the default Tutor configuration can be extended by a ``kustomization.yml`` file stored in ``$(tutor config printroot)/env-custom/`` and which would start with::
|
||||||
|
|
||||||
|
@ -67,21 +83,21 @@ The other benefit of ``kubectl apply`` is that it allows you to customise the Ku
|
||||||
- ../env/
|
- ../env/
|
||||||
...
|
...
|
||||||
|
|
||||||
To learn more about "kustomizations", refer to the `official documentation <https://kubectl.docs.kubernetes.io/pages/app_customization/introduction.html>`__.
|
To learn more about "kustomizations", refer to the `official documentation <https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/>`__.
|
||||||
|
|
||||||
Quickstart
|
Quickstart
|
||||||
----------
|
----------
|
||||||
|
|
||||||
Launch the platform on Kubernetes in one command::
|
Launch the platform on Kubernetes in one command::
|
||||||
|
|
||||||
tutor k8s quickstart
|
tutor k8s launch
|
||||||
|
|
||||||
All Kubernetes resources are associated to the "openedx" namespace. If you don't see anything in the Kubernetes dashboard, you are probably looking at the wrong namespace... 😉
|
All Kubernetes resources are associated with the "openedx" namespace. If you don't see anything in the Kubernetes dashboard, you are probably looking at the wrong namespace... 😉
|
||||||
|
|
||||||
.. image:: img/k8s-dashboard.png
|
.. image:: img/k8s-dashboard.png
|
||||||
:alt: Kubernetes dashboard ("openedx" namespace)
|
:alt: Kubernetes dashboard ("openedx" namespace)
|
||||||
|
|
||||||
The same ``tutor k8s quickstart`` command can be used to upgrade the cluster to the latest version.
|
The same ``tutor k8s launch`` command can be used to upgrade the cluster to the latest version.
|
||||||
|
|
||||||
Other commands
|
Other commands
|
||||||
--------------
|
--------------
|
||||||
|
@ -90,15 +106,24 @@ As with the :ref:`local installation <local>`, there are multiple commands to ru
|
||||||
|
|
||||||
tutor k8s -h
|
tutor k8s -h
|
||||||
|
|
||||||
In particular, the `tutor k8s start` command restarts and reconfigures all services by running ``kubectl apply``. That means that you can delete containers, deployments or just any other kind of resources, and Tutor will re-create them automatically. You should just beware of not deleting any persistent data stored in persistent volume claims. For instance, to restart from a "blank slate", run::
|
In particular, the ``tutor k8s start`` command restarts and reconfigures all services by running ``kubectl apply``. That means that you can delete containers, deployments, or just any other kind of resources, and Tutor will re-create them automatically. You should just beware of not deleting any persistent data stored in persistent volume claims. For instance, to restart from a "blank slate", run::
|
||||||
|
|
||||||
tutor k8s stop
|
tutor k8s stop
|
||||||
tutor k8s start
|
tutor k8s start
|
||||||
|
|
||||||
All non-persisting data will be deleted, and then re-created.
|
All non-persisting data will be deleted, and then re-created.
|
||||||
|
|
||||||
Guides
|
Common tasks
|
||||||
------
|
------------
|
||||||
|
|
||||||
|
Executing commands inside service pods
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The Tutor and plugin documentation usually often instructions to execute some ``tutor local run ...`` commands. These commands are only valid when running Tutor locally with docker compose, and will not work on Kubernetes. Instead, you should run ``tutor k8s exec ...`` commands. Arguments and options should be identical.
|
||||||
|
|
||||||
|
For instance, to run a Python shell in the lms container, run::
|
||||||
|
|
||||||
|
tutor k8s exec lms ./manage.py lms shell
|
||||||
|
|
||||||
Running a custom "openedx" Docker image
|
Running a custom "openedx" Docker image
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -112,8 +137,33 @@ Some Tutor plugins and customization procedures require that the "openedx" image
|
||||||
Updating docker images
|
Updating docker images
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Kubernetes does not provide a single command for updating docker images out of the box. A `commonly used trick <https://github.com/kubernetes/kubernetes/issues/33664>`_ is to modify an innocuous label on all resources::
|
Kubernetes does not provide a single command for updating docker images out of the box. A `commonly used trick <https://github.com/kubernetes/kubernetes/issues/33664>`__ is to modify an innocuous label on all resources::
|
||||||
|
|
||||||
kubectl patch -k "$(tutor config printroot)/env" --patch "{\"spec\": {\"template\": {\"metadata\": {\"labels\": {\"date\": \"`date +'%Y%m%d-%H%M%S'`\"}}}}}"
|
kubectl patch -k "$(tutor config printroot)/env" --patch "{\"spec\": {\"template\": {\"metadata\": {\"labels\": {\"date\": \"`date +'%Y%m%d-%H%M%S'`\"}}}}}"
|
||||||
|
|
||||||
|
|
||||||
|
.. _customizing_kubernetes_sources:
|
||||||
|
|
||||||
|
Customizing Kubernetes resources
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Plugins can customize any Kubernetes resource in Tutor by overriding the definition of the resource with a :patch:`k8s-override` patch. For example, to change the volume size for MongoDB from ``5Gi`` to ``10Gi``, add the following to the plugin:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
# myplugin/tutormyplugin/patches/k8s-override
|
||||||
|
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: mongodb
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: volume
|
||||||
|
app.kubernetes.io/name: mongodb
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Gi
|
||||||
|
|
||||||
|
|
178
docs/local.rst
|
@ -5,6 +5,11 @@ Local deployment
|
||||||
|
|
||||||
This method is for deploying Open edX locally on a single server, where docker images are orchestrated with `docker-compose <https://docs.docker.com/compose/overview/>`_.
|
This method is for deploying Open edX locally on a single server, where docker images are orchestrated with `docker-compose <https://docs.docker.com/compose/overview/>`_.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
As of v16.0.0, Tutor now uses the ``docker compose`` subcommand instead of the separate ``docker-compose`` command.
|
||||||
|
|
||||||
|
.. _tutor_root:
|
||||||
|
|
||||||
In the following, environment and data files will be generated in a user-specific project folder which will be referred to as the "**project root**". On Linux, the default project root is ``~/.local/share/tutor``. An alternative project root can be defined by passing the ``--root=...`` option to the ``tutor`` command, or defining the ``TUTOR_ROOT=...`` environment variable::
|
In the following, environment and data files will be generated in a user-specific project folder which will be referred to as the "**project root**". On Linux, the default project root is ``~/.local/share/tutor``. An alternative project root can be defined by passing the ``--root=...`` option to the ``tutor`` command, or defining the ``TUTOR_ROOT=...`` environment variable::
|
||||||
|
|
||||||
tutor --root=/path/to/tutorroot run ...
|
tutor --root=/path/to/tutorroot run ...
|
||||||
|
@ -12,24 +17,21 @@ In the following, environment and data files will be generated in a user-specifi
|
||||||
export TUTOR_ROOT=/path/to/tutorroot
|
export TUTOR_ROOT=/path/to/tutorroot
|
||||||
tutor run ...
|
tutor run ...
|
||||||
|
|
||||||
.. note::
|
|
||||||
As of v10.0.0, a locally-running Open edX platform can no longer be accessed from http://localhost or http://studio.localhost. Instead, when running ``tutor local quickstart``, you must now decide whether you are running a platform that will be used in production. If not, the platform will be automatically be bound to http://local.overhang.io and http://studio.local.overhang.io, which are domain names that point to 127.0.0.1 (localhost). This change was made to facilitate internal communication between Docker containers.
|
|
||||||
|
|
||||||
Main commands
|
Main commands
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
All available commands can be listed by running::
|
All available commands can be listed by running::
|
||||||
|
|
||||||
tutor local help
|
tutor local --help
|
||||||
|
|
||||||
All-in-one command
|
All-in-one command
|
||||||
~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
A fully-functional platform can be configured and run in one command::
|
A fully-functional platform can be configured and run in one command::
|
||||||
|
|
||||||
tutor local quickstart
|
tutor local launch
|
||||||
|
|
||||||
But you may want to run commands one at a time: it's faster when you need to run only part of the local deployment process, and it helps you understand how your platform works. In the following we decompose the ``quickstart`` command.
|
But you may want to run commands one at a time: it's faster when you need to run only part of the local deployment process, and it helps you understand how your platform works. In the following, we decompose the ``launch`` command.
|
||||||
|
|
||||||
Configuration
|
Configuration
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
@ -38,7 +40,7 @@ Configuration
|
||||||
|
|
||||||
tutor config save --interactive
|
tutor config save --interactive
|
||||||
|
|
||||||
This is the only non-automatic step in the installation process. You will be asked various questions about your Open edX platform and appropriate configuration files will be generated. If you would like to automate this step then you should run ``tutor config save --interactive`` once. After that, there will be a ``config.yml`` file at the root of the project folder: this file contains all the configuration values for your platform, such as randomly generated passwords, domain names, etc.
|
This is the only non-automatic step in the installation process. You will be asked various questions about your Open edX platform and appropriate configuration files will be generated. If you would like to automate this step then you should run ``tutor config save --interactive`` once. This will generate a ``config.yml`` file in the **project root**. This file contains all the configuration values for your platform, such as randomly generated passwords, domain names, etc. The location of the **project root** can be found by running ``tutor config printroot``. See :ref:`section above <tutor_root>`.
|
||||||
|
|
||||||
If you want to run a fully automated installation, upload the ``config.yml`` file to wherever you want to run Open edX. You can then entirely skip the configuration step.
|
If you want to run a fully automated installation, upload the ``config.yml`` file to wherever you want to run Open edX. You can then entirely skip the configuration step.
|
||||||
|
|
||||||
|
@ -75,7 +77,7 @@ Service initialisation
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
tutor local init
|
tutor local do init
|
||||||
|
|
||||||
This command should be run just once. It will initialise all applications in a running platform. In particular, this will create the required databases tables and apply database migrations for all applications.
|
This command should be run just once. It will initialise all applications in a running platform. In particular, this will create the required databases tables and apply database migrations for all applications.
|
||||||
|
|
||||||
|
@ -96,9 +98,17 @@ Finally, tracking logs that store `user events <https://edx.readthedocs.io/proje
|
||||||
$(tutor config printroot)/data/lms/logs/tracking.log
|
$(tutor config printroot)/data/lms/logs/tracking.log
|
||||||
$(tutor config printroot)/data/cms/logs/tracking.log
|
$(tutor config printroot)/data/cms/logs/tracking.log
|
||||||
|
|
||||||
|
Status
|
||||||
|
~~~~~~
|
||||||
|
|
||||||
Extra commands
|
You can view your platform's containers::
|
||||||
--------------
|
|
||||||
|
tutor local status
|
||||||
|
|
||||||
|
Notice the **State** column in the output. It will tell you whether each container is starting, restarting, running (``Up``), cleanly stopped (``Exit 0``), or stopped on error (``Exit N``, where N ≠ 0).
|
||||||
|
|
||||||
|
Common tasks
|
||||||
|
------------
|
||||||
|
|
||||||
.. _createuser:
|
.. _createuser:
|
||||||
|
|
||||||
|
@ -107,18 +117,18 @@ Creating a new user with staff and admin rights
|
||||||
|
|
||||||
You will most certainly need to create a user to administer the platform. Just run::
|
You will most certainly need to create a user to administer the platform. Just run::
|
||||||
|
|
||||||
tutor local createuser --staff --superuser yourusername user@email.com
|
tutor local do createuser --staff --superuser yourusername user@email.com
|
||||||
|
|
||||||
You will asked to set the user password interactively.
|
You will be asked to set the user password interactively.
|
||||||
|
|
||||||
.. _democourse:
|
.. _democourse:
|
||||||
|
|
||||||
Importing the demo course
|
Importing the demo course
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
After a fresh installation, your platform will not have a single course. To import the `Open edX demo course <https://github.com/edx/edx-demo-course>`_, run::
|
After a fresh installation, your platform will not have a single course. To import the `Open edX demo course <https://github.com/openedx/edx-demo-course>`_, run::
|
||||||
|
|
||||||
tutor local importdemocourse
|
tutor local do importdemocourse
|
||||||
|
|
||||||
.. _settheme:
|
.. _settheme:
|
||||||
|
|
||||||
|
@ -127,9 +137,7 @@ Setting a new theme
|
||||||
|
|
||||||
The default Open edX theme is rather bland, so Tutor makes it easy to switch to a different theme::
|
The default Open edX theme is rather bland, so Tutor makes it easy to switch to a different theme::
|
||||||
|
|
||||||
tutor local settheme mytheme $(tutor config printvalue LMS_HOST) $(tutor config printvalue CMS_HOST)
|
tutor local do settheme mytheme
|
||||||
|
|
||||||
Notice that we pass the hostnames of the LMS and the CMS to the ``settheme`` command: this is because in Open edX, themes are assigned per domain name.
|
|
||||||
|
|
||||||
Out of the box, only the default "open-edx" theme is available. We also developed `Indigo, a beautiful, customizable theme <https://github.com/overhangio/indigo>`__ which is easy to install with Tutor.
|
Out of the box, only the default "open-edx" theme is available. We also developed `Indigo, a beautiful, customizable theme <https://github.com/overhangio/indigo>`__ which is easy to install with Tutor.
|
||||||
|
|
||||||
|
@ -152,138 +160,14 @@ After modifying Open edX settings, for instance when running ``tutor config save
|
||||||
|
|
||||||
tutor local exec lms reload-uwsgi
|
tutor local exec lms reload-uwsgi
|
||||||
|
|
||||||
.. _portainer:
|
|
||||||
|
|
||||||
Docker container web UI with `Portainer <https://portainer.io/>`__
|
Customizing the deployed services
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Portainer is a web UI for managing docker containers. It lets you view your entire Open edX platform at a glace. Try it! It's really cool::
|
You might want to customise the docker-compose services listed in ``$(tutor config printroot)/env/local/docker-compose.yml``. To do so, you should create a ``docker-compose.override.yml`` file in that same folder::
|
||||||
|
|
||||||
docker run --rm \
|
vim $(tutor config printroot)/env/local/docker-compose.override.yml
|
||||||
--volume=/var/run/docker.sock:/var/run/docker.sock \
|
|
||||||
--volume=/tmp/portainer:/data \
|
|
||||||
-p 9000:9000 \
|
|
||||||
portainer/portainer:latest --bind=:9000
|
|
||||||
|
|
||||||
.. .. image:: https://portainer.io/images/screenshots/portainer.gif
|
The values in this file will override the values from ``docker-compose.yml`` and ``docker-compose.prod.yml``, as explained in the `docker-compose documentation <https://docs.docker.com/compose/extends/#adding-and-overriding-configuration>`__.
|
||||||
..:alt: Portainer demo
|
|
||||||
|
|
||||||
You can then view the portainer UI at `http://localhost:9000 <http://localhost:9000>`_. You will be asked to define a password for the admin user. Then, select a "Local environment" to work on; hit "Connect" and select the "local" group to view all running containers.
|
Similarly, the job service configuration can be overridden by creating a ``docker-compose.jobs.override.yml`` file in that same folder.
|
||||||
|
|
||||||
Among many other things, you'll be able to view the logs for each container, which is really useful.
|
|
||||||
|
|
||||||
Guides
|
|
||||||
------
|
|
||||||
|
|
||||||
.. _web_proxy:
|
|
||||||
|
|
||||||
Running Open edX behind a web proxy
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The containerized web server ([Caddy](caddyserver.com/)) needs to listen to ports 80 and 443 on the host. If there is already a webserver running on the host, such as Apache or Nginx, the caddy container will not be able to start. Tutor supports running behind a web proxy. To do so, add the following configuration::
|
|
||||||
|
|
||||||
tutor config save --set RUN_CADDY=false --set NGINX_HTTP_PORT=81
|
|
||||||
|
|
||||||
In this example, the nginx container port would be mapped to 81 instead of 80. You must then configure the web proxy on the host. As of v11.0.0, configuration files are no longer provided for automatic configuration of your web proxy. Basically, you should setup a reverse proxy to `localhost:NGINX_HTTP_PORT` from the following hosts: LMS_HOST, preview.LMS_HOST, CMS_HOST, as well as any additional host exposed by your plugins.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
In this setup, the Nginx HTTP port will be exposed to the world. Make sure to configure your server firewall to block unwanted connections to your server's `NGINX_HTTP_PORT`. Alternatively, you can configure the Nginx container to accept only local connections::
|
|
||||||
|
|
||||||
tutor config save --set NGINX_HTTP_PORT=127.0.0.1:81
|
|
||||||
|
|
||||||
Running multiple Open edX platforms on a single server
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
With Tutor, it is easy to run multiple Open edX instances on a single server. To do so, the following configuration parameters must be different for all platforms:
|
|
||||||
|
|
||||||
- ``TUTOR_ROOT``: so that configuration, environment and data are not mixed up between platforms.
|
|
||||||
- ``LOCAL_PROJECT_NAME``: the various docker-compose projects cannot share the same name.
|
|
||||||
- ``NGINX_HTTP_PORT``: ports cannot be shared by two different containers.
|
|
||||||
- ``LMS_HOST``, ``CMS_HOST``: the different platforms must be accessible from different domain (or subdomain) names.
|
|
||||||
|
|
||||||
In addition, a web proxy must be setup on the host, as described :ref:`above <web_proxy>`.
|
|
||||||
|
|
||||||
As an example, here is how to launch two different platforms, with nginx running as a web proxy::
|
|
||||||
|
|
||||||
# platform 1
|
|
||||||
export TUTOR_ROOT=~/openedx/site1
|
|
||||||
tutor config save --interactive --set RUN_CADDY=false --set LOCAL_PROJECT_NAME=tutor_site1 --set NGINX_HTTP_PORT=81
|
|
||||||
tutor local quickstart
|
|
||||||
sudo ln -s "$(tutor config printroot)/env/local/proxy/nginx/openedx.conf" /etc/nginx/sites-enabled/site1.conf
|
|
||||||
|
|
||||||
# platform 2
|
|
||||||
export TUTOR_ROOT=~/openedx/site2
|
|
||||||
tutor config save --interactive --set RUN_CADDY=false --set LOCAL_PROJECT_NAME=tutor_site2 --set NGINX_HTTP_PORT=82
|
|
||||||
tutor local quickstart
|
|
||||||
sudo ln -s "$(tutor config printroot)/env/local/proxy/nginx/openedx.conf" /etc/nginx/sites-enabled/site2.conf
|
|
||||||
|
|
||||||
You should then have two different platforms, completely isolated from one another, running on the same server.
|
|
||||||
|
|
||||||
Loading different production settings for ``edx-platform``
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The default settings module loaded by ``edx-platform`` is ``tutor.production``. The folders ``$(tutor config printroot)/env/apps/openedx/settings/lms`` and ``$(tutor config printroot)/env/apps/openedx/settings/cms`` are mounted as ``edx-platform/lms/envs/tutor`` and ``edx-platform/cms/envs/tutor`` inside the docker containers. Thus, to use your own settings, you must do two things:
|
|
||||||
|
|
||||||
1. Copy your settings files for the lms and the cms to ``$(tutor config printroot)/env/apps/openedx/settings/lms/mysettings.py`` and ``$(tutor config printroot)/env/apps/openedx/settings/cms/mysettings.py``.
|
|
||||||
2. Load your settings by adding ``TUTOR_EDX_PLATFORM_SETTINGS=tutor.mysettings`` to ``$(tutor config printroot)/env/local/.env``.
|
|
||||||
|
|
||||||
Of course, your settings should be compatible with the docker installation. You can get some inspiration from the ``production.py`` settings modules generated by Tutor, or just import it as a base by adding ``from .production import *`` at the top of ``mysettings.py``.
|
|
||||||
|
|
||||||
Upgrading from earlier versions
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Upgrading from v3+
|
|
||||||
******************
|
|
||||||
|
|
||||||
Just upgrade Tutor using your :ref:`favorite installation method <install>` and run quickstart again::
|
|
||||||
|
|
||||||
tutor local quickstart
|
|
||||||
|
|
||||||
Upgrading from v1 or v2
|
|
||||||
***********************
|
|
||||||
|
|
||||||
Versions 1 and 2 of Tutor were organized differently: they relied on many different ``Makefile`` and ``make`` commands instead of a single ``tutor`` executable. To migrate from an earlier version, you should first stop your platform::
|
|
||||||
|
|
||||||
make stop
|
|
||||||
|
|
||||||
Then, install Tutor using one of the :ref:`installation methods <install>`. Then, create the Tutor project root and move your data::
|
|
||||||
|
|
||||||
mkdir -p "$(tutor config printroot)"
|
|
||||||
mv config.json data/ "$(tutor config printroot)"
|
|
||||||
|
|
||||||
Finally, launch your platform with::
|
|
||||||
|
|
||||||
tutor local quickstart
|
|
||||||
|
|
||||||
Backups/Migrating to a different server
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
With Tutor, all data are stored in a single folder. This means that it's extremely easy to migrate an existing platform to a different server. For instance, it's possible to configure a platform locally on a laptop, and then move this platform to a production server.
|
|
||||||
|
|
||||||
1. Make sure `tutor` is installed on both servers with the same version.
|
|
||||||
2. Stop any running platform on server 1::
|
|
||||||
|
|
||||||
tutor local stop
|
|
||||||
|
|
||||||
3. Transfer the configuration, environment and platform data from server 1 to server 2::
|
|
||||||
|
|
||||||
rsync -avr "$(tutor config printroot)/" username@server2:/tmp/tutor/
|
|
||||||
|
|
||||||
4. On server 2, move the data to the right location::
|
|
||||||
|
|
||||||
mv /tmp/tutor "$(tutor config printroot)"
|
|
||||||
|
|
||||||
5. Start the instance with::
|
|
||||||
|
|
||||||
tutor local start -d
|
|
||||||
|
|
||||||
Making database dumps
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
To dump all data from the MySQL and Mongodb databases used on the platform, run the following commands::
|
|
||||||
|
|
||||||
tutor local exec -e MYSQL_ROOT_PASSWORD="$(tutor config printvalue MYSQL_ROOT_PASSWORD)" mysql \
|
|
||||||
sh -c 'mysqldump --all-databases --password=$MYSQL_ROOT_PASSWORD > /var/lib/mysql/dump.sql'
|
|
||||||
tutor local exec mongodb mongodump --out=/data/db/dump.mongodb
|
|
||||||
|
|
||||||
The ``dump.sql`` and ``dump.mongodb`` files will be located in ``$(tutor config printroot)/data/mysql`` and ``$(tutor config printroot)/data/mongodb``.
|
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
.. _plugins:
|
|
||||||
|
|
||||||
Plugins
|
|
||||||
=======
|
|
||||||
|
|
||||||
Tutor comes with a plugin system that allows anyone to customise the deployment of an Open edX platform very easily. The vision behind this plugin system is that users should not have to fork the Tutor repository to customise their deployments. For instance, if you have created a new application that integrates with Open edX, you should not have to describe how to manually patch the platform settings, ``urls.py`` or ``*.env.json`` files. Instead, you can create a "tutor-myapp" plugin for Tutor. Then, users will start using your application in three simple steps::
|
|
||||||
|
|
||||||
# 1) Install the plugin
|
|
||||||
pip install tutor-myapp
|
|
||||||
# 2) Enable the plugin
|
|
||||||
tutor plugins enable myapp
|
|
||||||
# 3) Reconfigure and restart the platform
|
|
||||||
tutor local quickstart
|
|
||||||
|
|
||||||
For simple changes, it may be extremely easy to create a Tutor plugin: even non-technical users may get started with :ref:`simple yaml plugins <plugins_yaml>`.
|
|
||||||
|
|
||||||
In the following we learn how to use and create Tutor plugins.
|
|
||||||
|
|
||||||
Commands
|
|
||||||
--------
|
|
||||||
|
|
||||||
List installed plugins::
|
|
||||||
|
|
||||||
tutor plugins list
|
|
||||||
|
|
||||||
Enable/disable a plugin::
|
|
||||||
|
|
||||||
tutor plugins enable myplugin
|
|
||||||
tutor plugins disable myplugin
|
|
||||||
|
|
||||||
After enabling or disabling a plugin, the environment should be re-generated with::
|
|
||||||
|
|
||||||
tutor config save
|
|
||||||
|
|
||||||
.. _existing_plugins:
|
|
||||||
|
|
||||||
Existing plugins
|
|
||||||
----------------
|
|
||||||
|
|
||||||
- `Course discovery <https://pypi.org/project/tutor-discovery>`__: Deploy an API for interacting with your course catalog
|
|
||||||
- `Ecommerce <https://pypi.org/project/tutor-ecommerce>`__: Sell courses and products on your Open edX platform
|
|
||||||
- `Figures <https://pypi.org/project/tutor-figures>`__: Visualize daily stats about course engagement
|
|
||||||
- `MinIO <https://pypi.org/project/tutor-minio>`__: S3 emulator for object storage and scalable Open edX deployment.
|
|
||||||
- `Notes <https://pypi.org/project/tutor-notes>`__: Allows students to annotate portions of the courseware.
|
|
||||||
- `Xqueue <https://pypi.org/project/tutor-xqueue>`__: for external grading
|
|
||||||
|
|
||||||
Plugin development
|
|
||||||
------------------
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
plugins/api
|
|
||||||
plugins/gettingstarted
|
|
||||||
plugins/examples
|
|
|
@ -1,62 +1,92 @@
|
||||||
.. _plugins_examples:
|
.. _plugins_examples:
|
||||||
|
|
||||||
Examples of Tutor plugins
|
========
|
||||||
=========================
|
Examples
|
||||||
|
========
|
||||||
|
|
||||||
The following are simple examples of :ref:`Tutor plugins <plugins>` that can be used to modify the behaviour of Open edX.
|
The following are simple examples of :ref:`Tutor plugins <plugins>` that can be used to modify the behaviour of Open edX.
|
||||||
|
|
||||||
Skip email validation for new users
|
Skip email validation for new users
|
||||||
-----------------------------------
|
===================================
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
name: skipemailvalidation
|
from tutor import hooks
|
||||||
version: 0.1.0
|
|
||||||
patches:
|
hooks.Filters.ENV_PATCHES.add_item(
|
||||||
common-env-features: |
|
(
|
||||||
"SKIP_EMAIL_VALIDATION": true
|
"common-env-features",
|
||||||
|
"""
|
||||||
|
"SKIP_EMAIL_VALIDATION": true
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
Enable bulk enrollment view in the LMS
|
Enable bulk enrollment view in the LMS
|
||||||
--------------------------------------
|
======================================
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
name: enablebulkenrollmentview
|
from tutor import hooks
|
||||||
version: 0.1.0
|
|
||||||
patches:
|
hooks.Filters.ENV_PATCHES.add_item(
|
||||||
lms-env-features: |
|
(
|
||||||
"ENABLE_BULK_ENROLLMENT_VIEW": true
|
"lms-env-features",
|
||||||
|
"""
|
||||||
|
"ENABLE_BULK_ENROLLMENT_VIEW": true
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
Enable Google Analytics
|
Enable Google Analytics
|
||||||
-----------------------
|
=======================
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
name: googleanalytics
|
from tutor import hooks
|
||||||
version: 0.1.0
|
|
||||||
patches:
|
hooks.Filters.ENV_PATCHES.add_items([
|
||||||
openedx-common-settings: |
|
(
|
||||||
# googleanalytics special settings
|
"openedx-common-settings",
|
||||||
GOOGLE_ANALYTICS_ACCOUNT = "UA-your-account"
|
"GOOGLE_ANALYTICS_4_ID = 'MY-MEASUREMENT-ID'"
|
||||||
GOOGLE_ANALYTICS_TRACKING_ID = "UA-your-tracking-id"
|
),
|
||||||
|
(
|
||||||
|
"mfe-lms-common-settings",
|
||||||
|
"MFE_CONFIG['GOOGLE_ANALYTICS_4_ID'] = 'MY-MEASUREMENT-ID'"
|
||||||
|
),
|
||||||
|
])
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Please be aware that as of May 2023 Google Analytics support has been upgraded from Google Universal Analytics to Google Analytics 4 and you may need to update your configuration as mentioned in the `Open edX docs <https://docs.openedx.org/en/latest/site_ops/how-tos/google-analytics.html>`__.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Enable SAML authentication
|
Enable SAML authentication
|
||||||
--------------------------
|
==========================
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
name: saml
|
from tutor import hooks
|
||||||
version: 0.1.0
|
|
||||||
patches:
|
|
||||||
common-env-features: |
|
|
||||||
"ENABLE_THIRD_PARTY_AUTH" : true
|
|
||||||
|
|
||||||
openedx-lms-common-settings: |
|
hooks.Filters.ENV_PATCHES.add_items([
|
||||||
# saml special settings
|
(
|
||||||
THIRD_PARTY_AUTH_BACKENDS = "third_party_auth.saml.SAMLAuthBackend"
|
"common-env-features",
|
||||||
|
'"ENABLE_THIRD_PARTY_AUTH": true',
|
||||||
openedx-auth: |
|
),
|
||||||
"SOCIAL_AUTH_SAML_SP_PRIVATE_KEY": "yoursecretkey",
|
(
|
||||||
"SOCIAL_AUTH_SAML_SP_PUBLIC_CERT": "yourpubliccert"
|
"openedx-lms-common-settings",
|
||||||
|
"""
|
||||||
|
# saml special settings
|
||||||
|
AUTHENTICATION_BACKENDS += ["common.djangoapps.third_party_auth.saml.SAMLAuthBackend", "django.contrib.auth.backends.ModelBackend"]
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"openedx-auth",
|
||||||
|
"""
|
||||||
|
"SOCIAL_AUTH_SAML_SP_PRIVATE_KEY": "yoursecretkey",
|
||||||
|
"SOCIAL_AUTH_SAML_SP_PUBLIC_CERT": "yourpubliccert"
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
])
|
||||||
|
|
||||||
Do not forget to replace "yoursecretkey" and "yourpubliccert" with your own values.
|
Do not forget to replace "yoursecretkey" and "yourpubliccert" with your own values.
|
||||||
|
|
11
docs/plugins/index.rst
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
=======
|
||||||
|
Plugins
|
||||||
|
=======
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
intro
|
||||||
|
examples
|
||||||
|
v0/index
|
63
docs/plugins/intro.rst
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
.. _plugins:
|
||||||
|
|
||||||
|
============
|
||||||
|
Introduction
|
||||||
|
============
|
||||||
|
|
||||||
|
Tutor comes with a plugin system that allows anyone to customise the deployment of an Open edX platform very easily. The vision behind this plugin system is that users should not have to fork the Tutor repository to customise their deployments. For instance, if you have created a new application that integrates with Open edX, you should not have to describe how to manually patch the platform settings, ``urls.py`` or ``*.env.yml`` files. Instead, you can create a "tutor-myapp" plugin for Tutor. This plugin will be in charge of making changes to the platform settings. Then, users will be able to use your application in three simple steps::
|
||||||
|
|
||||||
|
# 1) Install the plugin
|
||||||
|
pip install tutor-myapp
|
||||||
|
# 2) Enable the plugin
|
||||||
|
tutor plugins enable myapp
|
||||||
|
# 3) Reconfigure and restart the platform
|
||||||
|
tutor local launch
|
||||||
|
|
||||||
|
For simple changes, it may be extremely easy to create a Tutor plugin: even non-technical users may get started with our :ref:`plugin_development_tutorial` tutorial. We also provide a list of :ref:`simple example plugins <plugins_examples>`.
|
||||||
|
|
||||||
|
To learn about the different ways in which plugins can extend Tutor, check out the :ref:`hooks catalog <hooks_catalog>`.
|
||||||
|
|
||||||
|
Plugin commands cheatsheet
|
||||||
|
==========================
|
||||||
|
|
||||||
|
List installed plugins::
|
||||||
|
|
||||||
|
tutor plugins list
|
||||||
|
|
||||||
|
Enable/disable a plugin::
|
||||||
|
|
||||||
|
tutor plugins enable myplugin
|
||||||
|
tutor plugins disable myplugin
|
||||||
|
|
||||||
|
The full plugins CLI is described in the :ref:`reference documentation <cli_plugins>`.
|
||||||
|
|
||||||
|
.. _existing_plugins:
|
||||||
|
|
||||||
|
Existing plugins
|
||||||
|
================
|
||||||
|
|
||||||
|
Many plugins are available from plugin indexes. These indexes are lists of plugins, similar to the `pypi <https://pypi.org>`__ or `npm <npmjs.com/>`__ indexes. By default, Tutor comes with the "main" plugin index. You can check available plugins from this index by running::
|
||||||
|
|
||||||
|
tutor plugins update
|
||||||
|
tutor plugins search
|
||||||
|
|
||||||
|
More plugins can be downloaded from the "contrib" index::
|
||||||
|
|
||||||
|
tutor plugins index add contrib
|
||||||
|
tutor plugins search
|
||||||
|
|
||||||
|
The "main" and "contrib" indexes include a curated list of plugins that are well maintained and introduce useful features to Open edX. These indexes are maintained by `Overhang.IO <https://overhang.io>`__. For more information about these indexes, refer to the official `overhangio/tpi <https://github.com/overhangio/tpi>`__ repository.
|
||||||
|
|
||||||
|
Thanks to these indexes, it is very easy to download and upgrade plugins. For instance, to install the `notes plugin <https://github.com/overhangio/tutor-notes/>`__::
|
||||||
|
|
||||||
|
tutor plugins install notes
|
||||||
|
|
||||||
|
Upgrade all your plugins with::
|
||||||
|
|
||||||
|
tutor plugins upgrade all
|
||||||
|
|
||||||
|
To list indexes that you are downloading plugins from, run::
|
||||||
|
|
||||||
|
tutor plugins index list
|
||||||
|
|
||||||
|
For more information about these indexes, check the `official Tutor plugin indexes (TPI) <https://github.com/overhangio/tpi/>`__ repository.
|
|
@ -1,20 +1,28 @@
|
||||||
Plugin API
|
Plugin API
|
||||||
==========
|
==========
|
||||||
|
|
||||||
Plugins can affect the behaviour of Tutor at multiple levels. First, plugins can define new services with their Docker images, settings and the right initialisation commands. To do so you will have to define custom :ref:`config <plugin_config>`, :ref:`patches <plugin_patches>`, :ref:`hooks <plugin_hooks>` and :ref:`templates <plugin_templates>`. Then, plugins can also extend the CLI by defining their own :ref:`commands <plugin_command>`.
|
.. include:: legacy.rst
|
||||||
|
|
||||||
.. _plugin_config:
|
Plugins can affect the behaviour of Tutor at multiple levels. They can:
|
||||||
|
|
||||||
|
* Add new settings or modify existing ones in the Tutor configuration (see :ref:`config <v0_plugin_config>`).
|
||||||
|
* Add new templates to the Tutor project environment or modify existing ones (see :ref:`patches <v0_plugin_patches>`, :ref:`templates <v0_plugin_templates>` and :ref:`hooks <v0_plugin_hooks>`).
|
||||||
|
* Add custom commands to the Tutor CLI (see :ref:`command <v0_plugin_command>`).
|
||||||
|
|
||||||
|
There exist two different APIs to create Tutor plugins: either with YAML files or Python packages. YAML files are more simple to create but are limited to just configuration and template patches.
|
||||||
|
|
||||||
|
.. _v0_plugin_config:
|
||||||
|
|
||||||
config
|
config
|
||||||
~~~~~~
|
~~~~~~
|
||||||
|
|
||||||
The ``config`` attribute is used to modify existing and add new configuration parameters:
|
The ``config`` attribute is used to modify existing and add new configuration parameters:
|
||||||
|
|
||||||
* ``config["add"]`` are key/values that should be added to the user-specific ``config.yml`` configuration. Add there passwords, secret keys and other values that do not have a default value.
|
* ``config["add"]`` are key/values that should be added to the user-specific ``config.yml`` configuration. Add there the passwords, secret keys, and other values that do not have a reasonable default value for all users.
|
||||||
* ``config["defaults"]`` are default key/values for this plugin. These values will not be added to the ``config.yml`` user file unless users override them manually with ``tutor config save --set ...``.
|
* ``config["defaults"]`` are default key/values for this plugin. These values can be accessed even though they are not added to the ``config.yml`` user file. Users can override them manually with ``tutor config save --set ...``.
|
||||||
* ``config["set"]`` are existing key/values that should be modified. Be very careful what you add there! Plugins may define conflicting values for some parameters.
|
* ``config["set"]`` are existing key/values that should be modified. Be very careful what you add there! Different plugins may define conflicting values for some parameters.
|
||||||
|
|
||||||
"set" and "default" key names will be automatically prefixed with the plugin name, in upper case.
|
"add" and "defaults" key names will be automatically prefixed with the plugin name, in upper case.
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
|
@ -36,22 +44,15 @@ This configuration from the "myplugin" plugin will set the following values:
|
||||||
- ``MYPLUGIN_DOCKER_IMAGE``: this value will by default not be stored in ``config.yml``, but ``tutor config printvalue MYPLUGIN_DOCKER_IMAGE`` will print ``username/imagename:latest``.
|
- ``MYPLUGIN_DOCKER_IMAGE``: this value will by default not be stored in ``config.yml``, but ``tutor config printvalue MYPLUGIN_DOCKER_IMAGE`` will print ``username/imagename:latest``.
|
||||||
- ``MASTER_PASSWORD`` will be set to ``h4cked``. Needless to say, plugin developers should avoid doing this.
|
- ``MASTER_PASSWORD`` will be set to ``h4cked``. Needless to say, plugin developers should avoid doing this.
|
||||||
|
|
||||||
.. _plugin_patches:
|
.. _v0_plugin_patches:
|
||||||
|
|
||||||
patches
|
patches
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
|
||||||
Plugin patches affect the rendered environment templates. In many places the Tutor templates include calls to ``{{ patch("patchname") }}``. This grants plugin developers the possibility to modify the content of rendered templates. Plugins can add content in these places by adding values to the ``patches`` attribute.
|
Plugin patches affect the rendered environment templates. In many places the Tutor templates include calls to ``{{ patch("patchname") }}``. This grants plugin developers the possibility to modify the content of rendered templates. Plugins can add content in these places by adding values to the ``patches`` attribute. See :ref:`patches` for the complete list available patches.
|
||||||
|
|
||||||
.. note::
|
|
||||||
The list of existing patches can be found by searching for `{{ patch(` strings in the Tutor source code::
|
|
||||||
|
|
||||||
git grep "{{ patch"
|
|
||||||
|
|
||||||
The list of patches can also be browsed online `on Github <https://github.com/search?utf8=✓&q={{+patch+repo%3Aoverhangio%2Ftutor+path%3A%2Ftutor%2Ftemplates&type=Code&ref=advsearch&l=&l= 8>`__.
|
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
patches = {
|
patches = {
|
||||||
"local-docker-compose-services": """redis:
|
"local-docker-compose-services": """redis:
|
||||||
image: redis:latest"""
|
image: redis:latest"""
|
||||||
|
@ -60,10 +61,11 @@ Example::
|
||||||
This will add a Redis instance to the services run with ``tutor local`` commands.
|
This will add a Redis instance to the services run with ``tutor local`` commands.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
The ``patches`` attribute can be a callable function instead of a static dict value.
|
In Python plugins, remember that ``patches`` can be a callable function instead of a static dict value.
|
||||||
|
One can use this to dynamically load a list of patch files from a folder.
|
||||||
|
|
||||||
|
|
||||||
.. _plugin_hooks:
|
.. _v0_plugin_hooks:
|
||||||
|
|
||||||
hooks
|
hooks
|
||||||
~~~~~
|
~~~~~
|
||||||
|
@ -76,16 +78,16 @@ Hooks are actions that are run during the lifetime of the platform. For instance
|
||||||
The services that will be run during initialisation should be added to the ``init`` hook, for instance for database creation and migrations.
|
The services that will be run during initialisation should be added to the ``init`` hook, for instance for database creation and migrations.
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
hooks = {
|
hooks = {
|
||||||
"init": ["myservice1", "myservice2"]
|
"init": ["myservice1", "myservice2"]
|
||||||
}
|
}
|
||||||
|
|
||||||
During initialisation, "myservice1" and "myservice2" will be run in sequence with the commands defined in the templates ``myplugin/hooks/myservice1/init`` and ``myplugin/hooks/myservice2/init``.
|
During initialisation, "myservice1" and "myservice2" will be run in sequence with the commands defined in the templates ``myplugin/hooks/myservice1/init`` and ``myplugin/hooks/myservice2/init``.
|
||||||
|
|
||||||
To initialise a "foo" service, Tutor runs the "foo-job" service that is found in the ``env/local/docker-compose.jobs.yml`` file. By default, Tutor comes with a few services in this file: mysql-job, lms-job, cms-job, forum-job. If your plugin requires running custom services during initialisation, you will need to add them to the ``docker-compose.jobs.yml`` template. To do so, just use the "local-docker-compose-jobs-services" patch.
|
To initialise a "foo" service, Tutor runs the "foo-job" service that is found in the ``env/local/docker-compose.jobs.yml`` file. By default, Tutor comes with a few services in this file: mysql-job, lms-job, cms-job. If your plugin requires running custom services during initialisation, you will need to add them to the ``docker-compose.jobs.yml`` template. To do so, just use the "local-docker-compose-jobs-services" patch.
|
||||||
|
|
||||||
In Kubernetes, the approach is the same, except that jobs are implemented as actual job objects in the ``k8s/jobs.yml`` template. To add your own services there, your plugin should implement the "k8s-jobs" patch.
|
In Kubernetes, the approach is the same, except that jobs are implemented as actual job objects in the ``k8s/jobs.yml`` template. To add your services there, your plugin should implement the "k8s-jobs" patch.
|
||||||
|
|
||||||
``pre-init``
|
``pre-init``
|
||||||
++++++++++++
|
++++++++++++
|
||||||
|
@ -102,13 +104,13 @@ Example::
|
||||||
hooks = {
|
hooks = {
|
||||||
"build-image": {"myimage": "myimage:latest"}
|
"build-image": {"myimage": "myimage:latest"}
|
||||||
}
|
}
|
||||||
|
|
||||||
With this hook, users will be able to build the ``myimage:latest`` docker image by running::
|
With this hook, users will be able to build the ``myimage:latest`` docker image by running::
|
||||||
|
|
||||||
tutor images build myimage
|
tutor images build myimage
|
||||||
|
|
||||||
or::
|
or::
|
||||||
|
|
||||||
tutor images build all
|
tutor images build all
|
||||||
|
|
||||||
This assumes that there is a ``Dockerfile`` file in the ``myplugin/build/myimage`` subfolder of the plugin templates directory.
|
This assumes that there is a ``Dockerfile`` file in the ``myplugin/build/myimage`` subfolder of the plugin templates directory.
|
||||||
|
@ -119,43 +121,47 @@ This assumes that there is a ``Dockerfile`` file in the ``myplugin/build/myimage
|
||||||
This hook allows pulling/pushing images from/to a docker registry.
|
This hook allows pulling/pushing images from/to a docker registry.
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
hooks = {
|
hooks = {
|
||||||
"remote-image": {"myimage": "myimage:latest"},
|
"remote-image": {"myimage": "myimage:latest"},
|
||||||
}
|
}
|
||||||
|
|
||||||
With this hook, users will be able to pull and push the ``myimage:latest`` docker image by running::
|
With this hook, users will be able to pull and push the ``myimage:latest`` docker image by running::
|
||||||
|
|
||||||
tutor images pull myimage
|
tutor images pull myimage
|
||||||
tutor images push myimage
|
tutor images push myimage
|
||||||
|
|
||||||
or::
|
or::
|
||||||
|
|
||||||
tutor images pull all
|
tutor images pull all
|
||||||
tutor images push all
|
tutor images push all
|
||||||
|
|
||||||
.. _plugin_templates:
|
.. _v0_plugin_templates:
|
||||||
|
|
||||||
templates
|
templates
|
||||||
~~~~~~~~~
|
~~~~~~~~~
|
||||||
|
|
||||||
In order to define plugin-specific hooks, a plugin should also have a template directory that includes the plugin hooks. The ``templates`` attribute should point to that directory.
|
To define plugin-specific hooks, a plugin should also have a template directory that includes the plugin hooks. The ``templates`` attribute should point to that directory.
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
import os
|
import os
|
||||||
templates = os.path.join(os.path.abspath(os.path.dirname(__file__)), "templates")
|
templates = os.path.join(os.path.abspath(os.path.dirname(__file__)), "templates")
|
||||||
|
|
||||||
With the above declaration, you can store plugin-specific templates in the ``templates/myplugin`` folder next to the ``plugin.py`` file.
|
With the above declaration, you can store plugin-specific templates in the ``templates/myplugin`` folder next to the ``plugin.py`` file.
|
||||||
|
|
||||||
In Tutor, templates are `Jinja2 <https://jinja.palletsprojects.com/en/2.11.x/>`__-formatted files that will be rendered in the Tutor environment (the ``$(tutor config printroot)/env`` folder) when running ``tutor config save``. The environment files are overwritten every time the environment is saved. Plugin developers can create templates that make use of the built-in `Jinja2 API <https://jinja.palletsprojects.com/en/2.11.x/api/>`__. In addition, a couple additional filters are added by Tutor:
|
In Tutor, templates are `Jinja2 <https://jinja.palletsprojects.com/en/2.11.x/>`__-formatted files that will be rendered in the Tutor environment (the ``$(tutor config printroot)/env`` folder) when running ``tutor config save``. The environment files are overwritten every time the environment is saved. Plugin developers can create templates that make use of the built-in `Jinja2 API <https://jinja.palletsprojects.com/en/2.11.x/api/>`__. In addition, a couple of additional filters are added by Tutor:
|
||||||
|
|
||||||
* ``common_domain``: Return the longest common name between two domain names. Example: ``{{ "studio.demo.myopenedx.com"|common_domain("lms.demo.myopenedx.com") }}`` is equal to "demo.myopenedx.com".
|
* ``common_domain``: Return the longest common name between two domain names. Example: ``{{ "studio.demo.myopenedx.com"|common_domain("lms.demo.myopenedx.com") }}`` is equal to "demo.myopenedx.com".
|
||||||
* ``encrypt``: Encrypt an arbitrary string. The encryption process is compatible with `htpasswd <https://httpd.apache.org/docs/2.4/programs/htpasswd.html>`__ verification.
|
* ``encrypt``: Encrypt an arbitrary string. The encryption process is compatible with `htpasswd <https://httpd.apache.org/docs/2.4/programs/htpasswd.html>`__ verification.
|
||||||
* ``list_if``: In a list of ``(value, condition)`` tuples, return the list of ``value`` for which the ``condition`` is true.
|
* ``list_if``: In a list of ``(value, condition)`` tuples, return the list of ``value`` for which the ``condition`` is true.
|
||||||
* ``patch``: See :ref:`patches <plugin_patches>`.
|
* ``long_to_base64``: Base-64 encode a long integer.
|
||||||
|
* ``iter_values_named``: Yield the values of the configuration settings that match a certain pattern. Example: ``{% for value in iter_values_named(prefix="KEY", suffix="SUFFIX")%}...{% endfor %}``. By default, only non-empty values are yielded. To iterate also on empty values, pass the ``allow_empty=True`` argument.
|
||||||
|
* ``patch``: See :ref:`patches <v0_plugin_patches>`.
|
||||||
* ``random_string``: Return a random string of the given length composed of ASCII letters and digits. Example: ``{{ 8|random_string }}``.
|
* ``random_string``: Return a random string of the given length composed of ASCII letters and digits. Example: ``{{ 8|random_string }}``.
|
||||||
* ``reverse_host``: Reverse a domain name (see `reference <https://en.wikipedia.org/wiki/Reverse_domain_name_notation>`__). Example: ``{{ "demo.myopenedx.com"|reverse_host }}`` is equal to "com.myopenedx.demo".
|
* ``reverse_host``: Reverse a domain name (see `reference <https://en.wikipedia.org/wiki/Reverse_domain_name_notation>`__). Example: ``{{ "demo.myopenedx.com"|reverse_host }}`` is equal to "com.myopenedx.demo".
|
||||||
|
* ``rsa_import_key``: Import a PEM-formatted RSA key and return the corresponding object.
|
||||||
|
* ``rsa_private_key``: Export an RSA private key in PEM format.
|
||||||
* ``walk_templates``: Iterate recursively over the templates of the given folder. For instance::
|
* ``walk_templates``: Iterate recursively over the templates of the given folder. For instance::
|
||||||
|
|
||||||
{% for file in "apps/myplugin"|walk_templates %}
|
{% for file in "apps/myplugin"|walk_templates %}
|
||||||
|
@ -167,41 +173,61 @@ When saving the environment, template files that are stored in a template root w
|
||||||
* Binary files with the following extensions: .ico, .jpg, .png, .ttf
|
* Binary files with the following extensions: .ico, .jpg, .png, .ttf
|
||||||
* Files that are stored in a folder named "partials", or one of its subfolders.
|
* Files that are stored in a folder named "partials", or one of its subfolders.
|
||||||
|
|
||||||
.. _plugin_command:
|
.. _v0_plugin_command:
|
||||||
|
|
||||||
command
|
command
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
|
||||||
A plugin can provide custom command line commands. Commands are assumed to be `click.Command <https://click.palletsprojects.com/en/7.x/api/#commands>`__ objects.
|
Python plugins can provide a custom command line interface.
|
||||||
|
The ``command`` attribute is assumed to be a `click.Command <https://click.palletsprojects.com/en/8.0.x/api/#commands>`__ object,
|
||||||
|
and you typically implement them using the `click.command <https://click.palletsprojects.com/en/8.0.x/api/#click.command>`__ decorator.
|
||||||
|
|
||||||
|
You may also use the `click.pass_obj <https://click.palletsprojects.com/en/8.0.x/api/#click.pass_obj>`__ decorator to pass the CLI `context <https://click.palletsprojects.com/en/8.0.x/api/#click.Context>`__, such as when you want to access Tutor configuration settings from your command.
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
from tutor import config as tutor_config
|
||||||
|
|
||||||
@click.command(help="I'm a plugin command")
|
@click.command(help="I'm a plugin command")
|
||||||
def command():
|
@click.pass_obj
|
||||||
|
def command(context):
|
||||||
|
config = tutor_config.load(context.root)
|
||||||
|
lms_host = config["LMS_HOST"]
|
||||||
click.echo("Hello from myplugin!")
|
click.echo("Hello from myplugin!")
|
||||||
|
click.echo(f"My LMS host is {lms_host}")
|
||||||
|
|
||||||
Any user who installs the ``myplugin`` plugin can then run::
|
Any user who installs the ``myplugin`` plugin can then run::
|
||||||
|
|
||||||
$ tutor myplugin
|
$ tutor myplugin
|
||||||
Hello from myplugin!
|
Hello from myplugin!
|
||||||
|
My LMS host is learn.myserver.com
|
||||||
|
|
||||||
|
You can even define subcommands by creating `command groups <https://click.palletsprojects.com/en/8.0.x/api/#click.Group>`__::
|
||||||
|
|
||||||
You can even define subcommands by creating `command groups <https://click.palletsprojects.com/en/7.x/api/#click.Group>`__::
|
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
|
||||||
@click.group(help="I'm a plugin command group")
|
@click.group(help="I'm a plugin command group")
|
||||||
def command():
|
def command():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@click.command(help="I'm a plugin subcommand")
|
@command.command(help="I'm a plugin subcommand")
|
||||||
def dosomething():
|
def dosomething():
|
||||||
click.echo("This subcommand is awesome")
|
click.echo("This subcommand is awesome")
|
||||||
|
|
||||||
This would allow any user to run::
|
This would allow any user to see your sub-commands::
|
||||||
|
|
||||||
|
$ tutor myplugin
|
||||||
|
Usage: tutor myplugin [OPTIONS] COMMAND [ARGS]...
|
||||||
|
|
||||||
|
I'm a plugin command group
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
dosomething I'm a plugin subcommand
|
||||||
|
|
||||||
|
and then run them::
|
||||||
|
|
||||||
$ tutor myplugin dosomething
|
$ tutor myplugin dosomething
|
||||||
This subcommand is awesome
|
This subcommand is awesome
|
||||||
|
|
||||||
See the official `click documentation <https://click.palletsprojects.com/en/7.x/>`__ for more information.
|
See the official `click documentation <https://click.palletsprojects.com/en/8.0.x/>`__ for more information.
|
|
@ -1,6 +1,8 @@
|
||||||
Getting started with plugin development
|
Getting started with plugin development
|
||||||
=======================================
|
=======================================
|
||||||
|
|
||||||
|
.. include:: legacy.rst
|
||||||
|
|
||||||
Plugins can be created in two different ways: either as plain YAML files or installable Python packages. YAML files are great when you need to make minor changes to the default platform, such as modifying settings. For creating more complex applications, it is recommended to create python packages.
|
Plugins can be created in two different ways: either as plain YAML files or installable Python packages. YAML files are great when you need to make minor changes to the default platform, such as modifying settings. For creating more complex applications, it is recommended to create python packages.
|
||||||
|
|
||||||
.. _plugins_yaml:
|
.. _plugins_yaml:
|
||||||
|
@ -9,15 +11,15 @@ YAML file
|
||||||
~~~~~~~~~
|
~~~~~~~~~
|
||||||
|
|
||||||
YAML files that are stored in the tutor plugins root folder will be automatically considered as plugins. The location of the plugin root can be found by running::
|
YAML files that are stored in the tutor plugins root folder will be automatically considered as plugins. The location of the plugin root can be found by running::
|
||||||
|
|
||||||
tutor plugins printroot
|
tutor plugins printroot
|
||||||
|
|
||||||
On Linux, this points to ``~/.local/share/tutor-plugins``. The location of the plugin root folder can be modified by setting the ``TUTOR_PLUGINS_ROOT`` environment variable.
|
On Linux, this points to ``~/.local/share/tutor-plugins``. The location of the plugin root folder can be modified by setting the ``TUTOR_PLUGINS_ROOT`` environment variable.
|
||||||
|
|
||||||
YAML plugins need to define two extra keys: "name" and "version". Custom CLI commands are not supported by YAML plugins.
|
YAML plugins must define two special top-level keys: ``name`` and ``version``. Then, YAML plugins may use two more top-level keys to customise Tutor's behaviour: ``config`` and ``patches``. Custom CLI commands, templates, and hooks are not supported by YAML plugins.
|
||||||
|
|
||||||
Let's create a simple plugin that adds your own `Google Analytics <https://analytics.google.com/>`__ tracking code to your Open edX platform. We need to add the ``GOOGLE_ANALYTICS_ACCOUNT`` and ``GOOGLE_ANALYTICS_TRACKING_ID`` settings to both the LMS and the CMS settings. To do so, we will only have to create the ``openedx-common-settings`` patch, which is shared by the development and the production settings both for the LMS and the CMS. First, create the plugin directory::
|
Let's create a simple plugin that adds your own `Google Analytics <https://analytics.google.com/>`__ tracking code to your Open edX platform. We need to add the ``GOOGLE_ANALYTICS_ACCOUNT`` and ``GOOGLE_ANALYTICS_TRACKING_ID`` settings to both the LMS and the CMS settings. To do so, we will only have to create the ``openedx-common-settings`` patch, which is shared by the development and the production settings both for the LMS and the CMS. First, create the plugin directory::
|
||||||
|
|
||||||
mkdir "$(tutor plugins printroot)"
|
mkdir "$(tutor plugins printroot)"
|
||||||
|
|
||||||
Then add the following content to the plugin file located at ``$(tutor plugins printroot)/myplugin.yml``::
|
Then add the following content to the plugin file located at ``$(tutor plugins printroot)/myplugin.yml``::
|
||||||
|
@ -31,44 +33,57 @@ Then add the following content to the plugin file located at ``$(tutor plugins p
|
||||||
GOOGLE_ANALYTICS_TRACKING_ID = "UA-654321-1"
|
GOOGLE_ANALYTICS_TRACKING_ID = "UA-654321-1"
|
||||||
|
|
||||||
Of course, you should replace your Google Analytics tracking code with your own. You can verify that your plugin is correctly installed, but not enabled yet::
|
Of course, you should replace your Google Analytics tracking code with your own. You can verify that your plugin is correctly installed, but not enabled yet::
|
||||||
|
|
||||||
$ tutor plugins list
|
$ tutor plugins list
|
||||||
googleanalytics@0.1.0 (disabled)
|
googleanalytics@0.1.0 (disabled)
|
||||||
|
|
||||||
You can then enable your newly-created plugin::
|
|
||||||
|
|
||||||
tutor plugins enable googleanalytics
|
|
||||||
|
|
||||||
Update your environment to apply changes from your plugin::
|
You can then enable your newly-created plugin::
|
||||||
|
|
||||||
tutor config save
|
tutor plugins enable googleanalytics
|
||||||
|
|
||||||
You should be able to view your changes in every LMS and CMS settings file::
|
You should be able to view your changes in every LMS and CMS settings file::
|
||||||
|
|
||||||
grep -r googleanalytics "$(tutor config printroot)/env/apps/openedx/settings/"
|
grep -r googleanalytics "$(tutor config printroot)/env/apps/openedx/settings/"
|
||||||
|
|
||||||
Now just restart your platform to start sending tracking events to Google Analytics::
|
Now just restart your platform to start sending tracking events to Google Analytics::
|
||||||
|
|
||||||
tutor local quickstart
|
tutor local launch
|
||||||
|
|
||||||
That's it! And it's very easy to share your plugins. Just upload them to your Github repo and share the url with other users. They will be able to install your plugin by running::
|
That's it! And it's very easy to share your plugins. Just upload them to your Github repo and share the url with other users. They will be able to install your plugin by running::
|
||||||
|
|
||||||
tutor plugins install https://raw.githubusercontent.com/username/yourrepo/master/googleanalytics.yml
|
tutor plugins install https://raw.githubusercontent.com/username/yourrepo/master/googleanalytics.yml
|
||||||
|
|
||||||
Python package
|
Python package
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Creating a plugin as a Python package allows you to define more complex logic and to store your patches in a more structured way. Python Tutor plugins are regular Python packages that define a specific entrypoint: ``tutor.plugin.v0``.
|
Creating a plugin as a Python package allows you to define more complex logic and store your patches in a more structured way. Python Tutor plugins are regular Python packages that define an entry point within the ``tutor.plugin.v0`` group:
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
setup(
|
setup(
|
||||||
...
|
...
|
||||||
entry_points={"tutor.plugin.v0": ["myplugin = myplugin.plugin"]},
|
entry_points={
|
||||||
|
"tutor.plugin.v0": ["myplugin = myplugin.plugin"]
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
The ``myplugin.plugin`` python module should then declare the ``config``, ``hooks``, etc. attributes that will define its behaviour.
|
The ``myplugin/plugin.py`` Python module can then define the attributes ``config``, ``patches``, ``hooks``, and ``templates`` to specify the plugin's behaviour. The attributes may be defined either as dictionaries or as zero-argument callables returning dictionaries; in the latter case, the callable will be evaluated upon plugin load. Finally, the ``command`` attribute can be defined as an instance of ``click.Command`` to define the plugin's command line interface.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
import click
|
||||||
|
|
||||||
|
templates = pkg_resources.resource_filename(...)
|
||||||
|
config = {...}
|
||||||
|
hooks = {...}
|
||||||
|
|
||||||
|
def patches():
|
||||||
|
...
|
||||||
|
|
||||||
|
@click.command(...)
|
||||||
|
def command():
|
||||||
|
...
|
||||||
|
|
||||||
To get started on the right foot, it is strongly recommended to create your first plugin with the `tutor plugin cookiecutter <https://github.com/overhangio/cookiecutter-tutor-plugin>`__::
|
To get started on the right foot, it is strongly recommended to create your first plugin with the `tutor plugin cookiecutter <https://github.com/overhangio/cookiecutter-tutor-plugin>`__::
|
||||||
|
|
11
docs/plugins/v0/index.rst
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
=============
|
||||||
|
Legacy v0 API
|
||||||
|
=============
|
||||||
|
|
||||||
|
.. include:: legacy.rst
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
api
|
||||||
|
gettingstarted
|
1
docs/plugins/v0/legacy.rst
Normal file
|
@ -0,0 +1 @@
|
||||||
|
.. warning:: The v0 plugin API is no longer the recommended way of developing new plugins for Tutor, starting from Tutor v13.2.0. See our :ref:`plugin creation tutorial <plugin_development_tutorial>` to learn more about the v1 plugin API. Existing v0 plugins will remain supported for some time but developers are encouraged to start migrating their plugins as soon as possible to make use of the new API. Please read the `upgrade instructions <https://github.com/overhangio/cookiecutter-tutor-plugin>`__ to upgrade v0 plugins generated with the v0 plugin cookiecutter.
|
|
@ -1,52 +0,0 @@
|
||||||
Running Tutor with Podman
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
You have the option of running Tutor with `Podman <https://podman.io/>`__, instead of the native Docker tools. This has some practical advantages: it does not require a running Docker daemon, and it enables you to run and build Docker images without depending on any system component running ``root``. As such, it is particularly useful for building Tutor images from CI pipelines.
|
|
||||||
|
|
||||||
The ``podman`` CLI aims to be fully compatible with the ``docker`` CLI, and ``podman-compose`` is meant to be a fully-compatible alias of ``docker-compose``. This means that you should be able to use together with Tutor, without making any changes to Tutor itself.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
Since this was written, it was discovered that there are major compatibility issues between ``podman-compose`` and ``docker-compose``. Thus, podman cannot be considered a drop-in replacement of Docker in the context of Tutor -- at least for running Open edX locally.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
You should not attempt to run Tutor with Podman on a system that already has native ``docker`` installed. If you want to switch to ``podman`` using the aliases described here, you should uninstall (or at least stop) the native Docker daemon first.
|
|
||||||
|
|
||||||
|
|
||||||
Enabling Podman
|
|
||||||
~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Podman is supported on a variety of development platforms, see the `installation instructions <https://podman.io/getting-started/installation>`_ for details.
|
|
||||||
|
|
||||||
Once you have installed Podman and its dependencies on the platform of your choice, you'll need to make sure that its ``podman`` binary, usually installed as ``/usr/bin/podman``, is aliased to ``docker``, and is included as such in your system ``$PATH``. On some CentOS and Fedora releases you can install a package named ``podman-docker`` to do this for you, but on other platforms you'll need to take of this yourself.
|
|
||||||
|
|
||||||
- If ``$HOME/bin`` is in your ``$PATH``, you can create a symbolic link there::
|
|
||||||
|
|
||||||
ln -s $(which podman) $HOME/bin/docker
|
|
||||||
|
|
||||||
- If you want to instead make ``docker`` a system-wide alias for ``podman``, you can create your symlink in ``/usr/local/bin``, an action that normally requires ``root`` privileges::
|
|
||||||
|
|
||||||
sudo ln -s $(which podman) /usr/local/bin/docker
|
|
||||||
|
|
||||||
|
|
||||||
Enabling podman-compose
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
``podman-compose`` is available as a package from PyPI, and can thus be installed with ``pip``. See `its README <https://github.com/containers/podman-compose/blob/devel/README.md>`_ for installation instructions. Note that if you have installed Tutor in its own virtualenv, you'll need to run ``pip install podman-compose`` in that same virtualenv.
|
|
||||||
|
|
||||||
Once installed, you'll again need to create a symbolic link that aliases ``docker-compose`` to ``podman-compose``.
|
|
||||||
|
|
||||||
- If you run Tutor and ``podman-compose`` in a virtualenv, create the symlink in that virtualenv's ``bin`` directory: activate the virtualenv, then run::
|
|
||||||
|
|
||||||
ln -s $(which podman-compose) $(dirname $(which podman-compose))/docker-compose
|
|
||||||
|
|
||||||
- If you do not, create the symlink in ``/usr/local/bin``, using ``root`` privileges::
|
|
||||||
|
|
||||||
sudo ln -s $(which podman-compose) /usr/local/bin/docker-compose
|
|
||||||
|
|
||||||
|
|
||||||
Verifying your environment
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Once you have configured your symbolic links as described, you should be able to run ``docker version`` and ``docker-compose --help`` and their output should agree, respectively, with ``podman version`` and ``podman-compose --help``.
|
|
||||||
|
|
||||||
After that, you should be able to use ``tutor local``, ``tutor build``, and other commands as if you had installed the native Docker tools.
|
|
|
@ -3,23 +3,27 @@
|
||||||
Quickstart (1-click install)
|
Quickstart (1-click install)
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
1. `Download <https://github.com/overhangio/tutor/releases>`_ the latest stable release of Tutor and place the ``tutor`` executable in your path. From the command line:
|
1. Install the latest stable release of Tutor from pip:
|
||||||
|
|
||||||
.. include:: cli_download.rst
|
.. include:: download/pip.rst
|
||||||
|
|
||||||
2. Run ``tutor local quickstart``
|
Or `download <https://github.com/overhangio/tutor/releases>`_ the pre-compiled binary and place the ``tutor`` executable in your path:
|
||||||
|
|
||||||
|
.. include:: download/binary.rst
|
||||||
|
|
||||||
|
2. Run ``tutor local launch``
|
||||||
3. You're done!
|
3. You're done!
|
||||||
|
|
||||||
**That's it?**
|
**That's it?**
|
||||||
|
|
||||||
Yes :) This is what happens when you run ``tutor local quickstart``:
|
Yes :) This is what happens when you run ``tutor local launch``:
|
||||||
|
|
||||||
1. You answer a few questions about the :ref:`configuration` of your Open edX platform.
|
1. You answer a few questions about the :ref:`configuration` of your Open edX platform.
|
||||||
2. Configuration files are generated from templates.
|
2. Configuration files are generated from templates.
|
||||||
3. Docker images are downloaded.
|
3. Docker images are downloaded.
|
||||||
4. Docker containers are provisioned.
|
4. Docker containers are provisioned.
|
||||||
5. A full, production-ready Open edX platform (`Koa <https://edx.readthedocs.io/projects/edx-installing-configuring-and-running/en/open-release-koa.master/platform_releases/koa.html>`__ release) is run with docker-compose.
|
5. A full, production-ready Open edX platform (`Quince <https://edx.readthedocs.io/projects/edx-installing-configuring-and-running/en/open-release-quince.master/platform_releases/quince.html>`__ release) is run with docker-compose.
|
||||||
|
|
||||||
The whole procedure should require less than 10 minutes, on a server with a good bandwidth. Note that your host environment will not be affected in any way, since everything runs inside docker containers. Root access is not even necessary.
|
The whole procedure should require less than 10 minutes, on a server with good bandwidth. Note that your host environment will not be affected in any way, since everything runs inside docker containers. Root access is not even necessary.
|
||||||
|
|
||||||
There's a lot more to Tutor than that! To learn more about what you can do with Tutor and Open edX, check out the :ref:`whatnext` section. If the quickstart installation method above somehow didn't work for you, check out the :ref:`troubleshooting` guide.
|
There's a lot more to Tutor than that! To learn more about what you can do with Tutor and Open edX, check out the :ref:`whatnext` section. If the launch installation method above somehow didn't work for you, check out the :ref:`troubleshooting` guide.
|
||||||
|
|
14
docs/reference/api/hooks/actions.rst
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
.. _actions:
|
||||||
|
|
||||||
|
=======
|
||||||
|
Actions
|
||||||
|
=======
|
||||||
|
|
||||||
|
Actions are one of the two types of hooks (the other being :ref:`filters`) that can be used to extend Tutor. Each action represents an event that can occur during the application life cycle. Each action has a name, and callback functions can be attached to it. When an action is triggered, these callback functions are called in sequence. Each callback function can trigger side effects, independently from one another.
|
||||||
|
|
||||||
|
.. autoclass:: tutor.core.hooks.Action
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. The following are only to ensure that the docs build without warnings
|
||||||
|
.. class:: tutor.core.hooks.actions.T
|
||||||
|
.. class:: tutor.types.Config
|
24
docs/reference/api/hooks/catalog.rst
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
.. _hooks_catalog:
|
||||||
|
|
||||||
|
=============
|
||||||
|
Hooks catalog
|
||||||
|
=============
|
||||||
|
|
||||||
|
Tutor can be extended by making use of "hooks". Hooks are either "actions" or "filters". Here, we list all instances of actions and filters that are used across Tutor. Plugin developers can leverage these hooks to modify the behaviour of Tutor.
|
||||||
|
|
||||||
|
The underlying Python hook classes and API are documented :ref:`here <hooks_api>`.
|
||||||
|
|
||||||
|
.. autoclass:: tutor.hooks.Actions
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: tutor.hooks.Filters
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: tutor.hooks.Contexts
|
||||||
|
:members:
|
||||||
|
|
||||||
|
Open edX hooks
|
||||||
|
--------------
|
||||||
|
|
||||||
|
.. automodule:: tutor.plugins.openedx.hooks
|
||||||
|
:members:
|
10
docs/reference/api/hooks/contexts.rst
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
.. _contexts:
|
||||||
|
|
||||||
|
========
|
||||||
|
Contexts
|
||||||
|
========
|
||||||
|
|
||||||
|
Contexts are a feature of the hook-based extension system in Tutor, which allows us to keep track of which components of the code created which callbacks. Contexts are very much an internal concept that most plugin developers should not have to worry about.
|
||||||
|
|
||||||
|
.. autoclass:: tutor.core.hooks.Context
|
||||||
|
.. autofunction:: tutor.core.hooks.contexts::enter
|
15
docs/reference/api/hooks/filters.rst
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
.. _filters:
|
||||||
|
|
||||||
|
=======
|
||||||
|
Filters
|
||||||
|
=======
|
||||||
|
|
||||||
|
Filters are one of the two types of hooks (the other being :ref:`actions`) that can be used to extend Tutor. Filters allow one to modify the application behavior by transforming data. Each filter has a name, and callback functions can be attached to it. When a filter is applied, these callback functions are called in sequence; the result of each callback function is passed as the first argument to the next callback function. The result of the final callback function is returned to the application as the filter's output.
|
||||||
|
|
||||||
|
.. autoclass:: tutor.core.hooks.Filter
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. The following are only to ensure that the docs build without warnings
|
||||||
|
.. class:: tutor.core.hooks.filters.T1
|
||||||
|
.. class:: tutor.core.hooks.filters.T2
|
||||||
|
.. class:: tutor.core.hooks.filters.L
|
14
docs/reference/api/hooks/index.rst
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
.. _hooks_api:
|
||||||
|
|
||||||
|
==========
|
||||||
|
Hook types
|
||||||
|
==========
|
||||||
|
|
||||||
|
This is the Python documentation of the two types of hooks (actions and filters) as well as the contexts system which is used to instrument them. Understanding how Tutor hooks work is useful to create plugins that modify the behaviour of Tutor. However, plugin developers should almost certainly not import these hook types directly. Instead, use the reference :ref:`hooks catalog <hooks_catalog>`.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
actions
|
||||||
|
filters
|
||||||
|
contexts
|
3
docs/reference/cli/config.rst
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.. click:: tutor.commands.config:config_command
|
||||||
|
:prog: tutor config
|
||||||
|
:nested: full
|
3
docs/reference/cli/dev.rst
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.. click:: tutor.commands.dev:dev
|
||||||
|
:prog: tutor dev
|
||||||
|
:nested: full
|
3
docs/reference/cli/images.rst
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.. click:: tutor.commands.images:images_command
|
||||||
|
:prog: tutor images
|
||||||
|
:nested: full
|
13
docs/reference/cli/index.rst
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
Command line interface (CLI)
|
||||||
|
============================
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
tutor
|
||||||
|
config
|
||||||
|
dev
|
||||||
|
images
|
||||||
|
k8s
|
||||||
|
local
|
||||||
|
plugins
|
3
docs/reference/cli/k8s.rst
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.. click:: tutor.commands.k8s:k8s
|
||||||
|
:prog: tutor k8s
|
||||||
|
:nested: full
|
3
docs/reference/cli/local.rst
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.. click:: tutor.commands.local:local
|
||||||
|
:prog: tutor local
|
||||||
|
:nested: full
|
5
docs/reference/cli/plugins.rst
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
.. _cli_plugins:
|
||||||
|
|
||||||
|
.. click:: tutor.commands.plugins:plugins_command
|
||||||
|
:prog: tutor plugins
|
||||||
|
:nested: full
|
3
docs/reference/cli/tutor.rst
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.. click:: tutor.commands.cli:cli
|
||||||
|
:prog: tutor
|
||||||
|
:nested: full
|
11
docs/reference/index.rst
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
Reference
|
||||||
|
=========
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
api/hooks/index
|
||||||
|
api/hooks/catalog
|
||||||
|
patches
|
||||||
|
cli/index
|
||||||
|
indexes
|
158
docs/reference/indexes.rst
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
==============
|
||||||
|
Plugin indexes
|
||||||
|
==============
|
||||||
|
|
||||||
|
Plugin indexes are a great way to have your plugins discovered by other users. Plugin indexes make it easy for other Tutor users to install and upgrade plugins from other developers. Examples include the official indexes, which can be found in the `overhangio/tpi <https://github.com/overhangio/tpi/>`__ repository.
|
||||||
|
|
||||||
|
Index file paths
|
||||||
|
================
|
||||||
|
|
||||||
|
A plugin index is a yaml-formatted file. It can be stored on the web or on your computer. In both cases, the index file location must end with "<current release name>/plugins.yml". For instance, the following are valid index locations if you run the Open edX "Quince" release:
|
||||||
|
|
||||||
|
- https://overhang.io/tutor/main/quince/plugins.yml
|
||||||
|
- ``/path/to/your/local/index/quince/plugins.yml``
|
||||||
|
|
||||||
|
To add either indexes, run the ``tutor plugins index add`` command without the suffix. For instance::
|
||||||
|
|
||||||
|
tutor plugins index add https://overhang.io/tutor/main
|
||||||
|
tutor plugins index add /path/to/your/local/index/
|
||||||
|
|
||||||
|
Your plugin cache should be updated immediately. You can also update the cache at any point by running::
|
||||||
|
|
||||||
|
tutor plugins update
|
||||||
|
|
||||||
|
To view current indexes, run::
|
||||||
|
|
||||||
|
tutor plugins index list
|
||||||
|
|
||||||
|
To remove an index, run::
|
||||||
|
|
||||||
|
tutor plugins index remove <index url>
|
||||||
|
|
||||||
|
Plugin entry syntax
|
||||||
|
===================
|
||||||
|
|
||||||
|
A "plugins.yml" file is a yaml-formatted list of plugin entries. Each plugin entry has two required fields: "name" and "src". For instance, here is a minimal plugin entry::
|
||||||
|
|
||||||
|
- name: mfe
|
||||||
|
src: tutor-mfe
|
||||||
|
|
||||||
|
"name" (required)
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
A plugin name is how it will be referenced when we run ``tutor plugins install <name>`` or ``tutor plugins enable <name>``. It should be concise and easily identifiable, just like a Python or apt package name.
|
||||||
|
|
||||||
|
Plugins with duplicate names will be overridden, depending on the index in which they are declared: indexes further down ``tutor plugins index list`` (which have been added later) will have higher priority.
|
||||||
|
|
||||||
|
.. _plugin_index_src:
|
||||||
|
|
||||||
|
"src" (required)
|
||||||
|
----------------
|
||||||
|
|
||||||
|
A plugin source can be either:
|
||||||
|
|
||||||
|
1. A pip requirement file format specifier (see `reference <https://pip.pypa.io/en/stable/reference/requirements-file-format/>`__).
|
||||||
|
2. The path to a Python file on your computer.
|
||||||
|
3. The URL of a Python file on the web.
|
||||||
|
|
||||||
|
In the first case, the plugin will be installed as a Python package. In the other two cases, the plugin will be installed as a single-file plugin.
|
||||||
|
|
||||||
|
The following "src" attributes are all valid::
|
||||||
|
|
||||||
|
# Pypi package
|
||||||
|
src: tutor-mfe
|
||||||
|
|
||||||
|
# Pypi package with version specification
|
||||||
|
src: tutor-mfe>=42.0.0,<43.0.0
|
||||||
|
|
||||||
|
# Python package from a private index
|
||||||
|
src: |
|
||||||
|
--index-url=https://pip.mymirror.org
|
||||||
|
my-plugin>=10.0
|
||||||
|
|
||||||
|
# Remote git repository
|
||||||
|
src: -e git+https://github.com/myusername/tutor-contrib-myplugin@v27.0.0#egg=tutor-contrib-myplugin
|
||||||
|
|
||||||
|
# Local editable package
|
||||||
|
src: -e /path/to/my/plugin
|
||||||
|
|
||||||
|
"url" (optional)
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Link to the plugin project, where users can learn more about it and ask for support.
|
||||||
|
|
||||||
|
"author" (optional)
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Original author of the plugin. Feel free to include your company name and email address here. For instance: "Leather Face <niceguy@happyfamily.com>".
|
||||||
|
|
||||||
|
"maintainer" (optional)
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Current maintainer of the plugin. Same format as "author".
|
||||||
|
|
||||||
|
"description" (optional)
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Multi-line string that should contain extensive information about your plugin. The full description will be displayed with ``tutor plugins show <name>``. It will also be parsed for a match by ``tutor plugins search <pattern>``. Only the first line will be displayed in the output of ``tutor plugins search``. Make sure to keep the first line below 128 characters.
|
||||||
|
|
||||||
|
|
||||||
|
Examples
|
||||||
|
========
|
||||||
|
|
||||||
|
Manage plugins in development
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
Plugin developers and maintainers often want to install local versions of their plugins. They usually achieve this with ``pip install -e /path/to/tutor-plugin``. We can improve that workflow by creating an index for local plugins::
|
||||||
|
|
||||||
|
# Create the plugin index directory
|
||||||
|
mkdir -p ~/localindex/quince/
|
||||||
|
# Edit the index
|
||||||
|
vim ~/localindex/quince/plugins.yml
|
||||||
|
|
||||||
|
Add the following to the index::
|
||||||
|
|
||||||
|
- name: myplugin1
|
||||||
|
src: -e /path/to/tutor-myplugin1
|
||||||
|
- name: myplugin2
|
||||||
|
src: -e /path/to/tutor-myplugin2
|
||||||
|
|
||||||
|
Then add the index::
|
||||||
|
|
||||||
|
tutor plugins index add ~/localindex/
|
||||||
|
|
||||||
|
Install the plugins::
|
||||||
|
|
||||||
|
tutor plugins install myplugin1 myplugin2
|
||||||
|
|
||||||
|
Re-install all plugins::
|
||||||
|
|
||||||
|
tutor plugins upgrade all
|
||||||
|
|
||||||
|
The latter commands will install from the local index, and not from the remote indexes, because indexes that are added last have higher priority when plugins with the same names are found.
|
||||||
|
|
||||||
|
Install plugins from a private index
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
Plugin authors might want to share plugins with a limited number of users. This is for instance the case when a plugin is for internal use only.
|
||||||
|
|
||||||
|
First, users should have access to the ``plugins.yml`` file. There are different ways to achieve that:
|
||||||
|
|
||||||
|
- Make the index public: after all, it's mostly the plugins which are private.
|
||||||
|
- Grant access to the index from behind a VPN.
|
||||||
|
- Hide the index behing a basic HTTP auth url. The index can then be added with ``tutor plugins index add http://user:password@mycompany.com/index/``.
|
||||||
|
- Download the index to disk, and then add it from the local path: ``tutor plugins index add ../path/to/index``.
|
||||||
|
|
||||||
|
Second, users should be able to install the plugins that are listed in the index. We recommend that the plugins are uploaded to a pip-compatible self-hosted mirror, such as `devpi <https://devpi.net/docs/devpi/devpi/latest/+doc/index.html>`__. Alternatively, packages can be installed from a private Git repository. For instance::
|
||||||
|
|
||||||
|
# Install from private pip index
|
||||||
|
- name: myprivateplugin1
|
||||||
|
src: |
|
||||||
|
--index-url=https://my-pip-index.mycompany.com/
|
||||||
|
tutor-contrib-myprivateplugin
|
||||||
|
|
||||||
|
# Install from private git repository
|
||||||
|
- name: myprivateplugin2
|
||||||
|
src: -e git+https://git.mycompany.com/tutor-contrib-myplugin2.git
|
||||||
|
|
||||||
|
Both examples work because the :ref:`"src" <plugin_index_src>` field supports just any syntax that could also be included in a requirements file installed with ``pip install -r requirements.txt``.
|
388
docs/reference/patches.rst
Normal file
|
@ -0,0 +1,388 @@
|
||||||
|
.. _patches:
|
||||||
|
|
||||||
|
======================
|
||||||
|
Template patch catalog
|
||||||
|
======================
|
||||||
|
|
||||||
|
This is the list of all patches used across Tutor (outside of any plugin). Alternatively, you can search for patches in Tutor templates by grepping the source code::
|
||||||
|
|
||||||
|
git clone https://github.com/overhangio/tutor
|
||||||
|
cd tutor
|
||||||
|
git grep "{{ patch" -- tutor/templates
|
||||||
|
|
||||||
|
Or you can list all available patches with the following command::
|
||||||
|
|
||||||
|
tutor config patches list
|
||||||
|
|
||||||
|
See also `this GitHub search <https://github.com/search?utf8=✓&q={{+patch+repo%3Aoverhangio%2Ftutor+path%3A%2Ftutor%2Ftemplates&type=Code&ref=advsearch&l=&l= 8>`__.
|
||||||
|
|
||||||
|
.. patch:: caddyfile
|
||||||
|
|
||||||
|
``caddyfile``
|
||||||
|
=============
|
||||||
|
|
||||||
|
File: ``apps/caddy/Caddyfile``
|
||||||
|
|
||||||
|
Add here Caddy directives to redirect traffic from the outside to your service containers. You should make use of the "proxy" snippet that simplifies configuration and automatically configures logging. Also, make sure to use the ``$default_site_port`` environment variable to make sure that your service will be accessible both when HTTPS is enabled or disabled. For instance::
|
||||||
|
|
||||||
|
{{ MYPLUGIN_HOST }}{$default_site_port} {
|
||||||
|
import proxy "myservice:8000"
|
||||||
|
}
|
||||||
|
|
||||||
|
See the `Caddy reference documentation <https://caddyserver.com/docs/caddyfile>`__ for more information.
|
||||||
|
|
||||||
|
.. patch:: caddyfile-cms
|
||||||
|
|
||||||
|
``caddyfile-cms``
|
||||||
|
=================
|
||||||
|
|
||||||
|
File: ``apps/caddy/Caddyfile``
|
||||||
|
|
||||||
|
.. patch:: caddyfile-global
|
||||||
|
|
||||||
|
``caddyfile-global``
|
||||||
|
====================
|
||||||
|
|
||||||
|
File: ``apps/caddy/Caddyfile``
|
||||||
|
|
||||||
|
.. patch:: caddyfile-lms
|
||||||
|
|
||||||
|
``caddyfile-lms``
|
||||||
|
=================
|
||||||
|
|
||||||
|
File: ``apps/caddy/Caddyfile``
|
||||||
|
|
||||||
|
.. patch:: cms-env
|
||||||
|
|
||||||
|
``cms-env``
|
||||||
|
===========
|
||||||
|
|
||||||
|
File: ``apps/openedx/config/cms.env.yml``
|
||||||
|
|
||||||
|
.. patch:: cms-env-features
|
||||||
|
|
||||||
|
``cms-env-features``
|
||||||
|
====================
|
||||||
|
|
||||||
|
File: ``apps/openedx/config/cms.env.yml``
|
||||||
|
|
||||||
|
.. patch:: common-env-features
|
||||||
|
|
||||||
|
``common-env-features``
|
||||||
|
=======================
|
||||||
|
|
||||||
|
Files: ``apps/openedx/config/cms.env.yml``, ``apps/openedx/config/lms.env.yml``
|
||||||
|
|
||||||
|
.. patch:: dev-docker-compose-jobs-services
|
||||||
|
|
||||||
|
``dev-docker-compose-jobs-services``
|
||||||
|
====================================
|
||||||
|
|
||||||
|
File: ``dev/docker-compose.jobs.yml``
|
||||||
|
|
||||||
|
.. patch:: k8s-deployments
|
||||||
|
|
||||||
|
``k8s-deployments``
|
||||||
|
===================
|
||||||
|
|
||||||
|
File: ``k8s/deployments.yml``
|
||||||
|
|
||||||
|
.. patch:: k8s-jobs
|
||||||
|
|
||||||
|
``k8s-jobs``
|
||||||
|
============
|
||||||
|
|
||||||
|
File: ``k8s/jobs.yml``
|
||||||
|
|
||||||
|
.. patch:: k8s-override
|
||||||
|
|
||||||
|
``k8s-override``
|
||||||
|
================
|
||||||
|
|
||||||
|
File: ``k8s/override.yml``
|
||||||
|
|
||||||
|
Any Kubernetes resource definition in this patch will override the resource defined by Tutor, provided that their names match. See :ref:`Customizing Kubernetes resources <customizing_kubernetes_sources>` for an example.
|
||||||
|
|
||||||
|
.. patch:: k8s-services
|
||||||
|
|
||||||
|
``k8s-services``
|
||||||
|
================
|
||||||
|
|
||||||
|
File: ``k8s/services.yml``
|
||||||
|
|
||||||
|
.. patch:: k8s-volumes
|
||||||
|
|
||||||
|
``k8s-volumes``
|
||||||
|
===============
|
||||||
|
|
||||||
|
File: ``k8s/volumes.yml``
|
||||||
|
|
||||||
|
.. patch:: kustomization
|
||||||
|
|
||||||
|
``kustomization``
|
||||||
|
=================
|
||||||
|
|
||||||
|
File: ``kustomization.yml``
|
||||||
|
|
||||||
|
.. patch:: kustomization-commonlabels
|
||||||
|
|
||||||
|
``kustomization-commonlabels``
|
||||||
|
==============================
|
||||||
|
|
||||||
|
File: ``kustomization.yml``
|
||||||
|
|
||||||
|
.. patch:: kustomization-configmapgenerator
|
||||||
|
|
||||||
|
``kustomization-configmapgenerator``
|
||||||
|
====================================
|
||||||
|
|
||||||
|
File: ``kustomization.yml``
|
||||||
|
|
||||||
|
.. patch:: kustomization-patches-strategic-merge
|
||||||
|
|
||||||
|
``kustomization-patches-strategic-merge``
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
File: ``kustomization.yml``
|
||||||
|
|
||||||
|
This can be used to add more Kustomization patches that make use of the `strategic merge mechanism <https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/#customizing>`__.
|
||||||
|
|
||||||
|
.. patch:: kustomization-resources
|
||||||
|
|
||||||
|
``kustomization-resources``
|
||||||
|
===========================
|
||||||
|
|
||||||
|
File: ``kustomization.yml``
|
||||||
|
|
||||||
|
.. patch:: lms-env
|
||||||
|
|
||||||
|
``lms-env``
|
||||||
|
===========
|
||||||
|
|
||||||
|
File: ``apps/openedx/config/lms.env.yml``
|
||||||
|
|
||||||
|
.. patch:: lms-env-features
|
||||||
|
|
||||||
|
``lms-env-features``
|
||||||
|
====================
|
||||||
|
|
||||||
|
File: ``apps/openedx/config/lms.env.yml``
|
||||||
|
|
||||||
|
.. patch:: local-docker-compose-caddy-aliases
|
||||||
|
|
||||||
|
``local-docker-compose-caddy-aliases``
|
||||||
|
======================================
|
||||||
|
|
||||||
|
File: ``local/docker-compose.prod.yml``
|
||||||
|
|
||||||
|
.. patch:: local-docker-compose-cms-dependencies
|
||||||
|
|
||||||
|
``local-docker-compose-cms-dependencies``
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
File: ``local/docker-compose.yml``
|
||||||
|
|
||||||
|
.. patch:: local-docker-compose-dev-services
|
||||||
|
|
||||||
|
``local-docker-compose-dev-services``
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
File: ``dev/docker-compose.yml``
|
||||||
|
|
||||||
|
.. patch:: local-docker-compose-jobs-services
|
||||||
|
|
||||||
|
``local-docker-compose-jobs-services``
|
||||||
|
======================================
|
||||||
|
|
||||||
|
File: ``local/docker-compose.jobs.yml``
|
||||||
|
|
||||||
|
.. patch:: local-docker-compose-lms-dependencies
|
||||||
|
|
||||||
|
``local-docker-compose-lms-dependencies``
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
File: ``local/docker-compose.yml``
|
||||||
|
|
||||||
|
.. patch:: local-docker-compose-permissions-command
|
||||||
|
|
||||||
|
``local-docker-compose-permissions-command``
|
||||||
|
============================================
|
||||||
|
|
||||||
|
File: ``apps/permissions/setowners.sh``
|
||||||
|
|
||||||
|
Add commands to this script to set ownership of bind-mounted docker-compose volumes at runtime. See :patch:`local-docker-compose-permissions-volumes`.
|
||||||
|
|
||||||
|
|
||||||
|
.. patch:: local-docker-compose-permissions-volumes
|
||||||
|
|
||||||
|
``local-docker-compose-permissions-volumes``
|
||||||
|
============================================
|
||||||
|
|
||||||
|
File: ``local/docker-compose.yml``
|
||||||
|
|
||||||
|
Add bind-mounted volumes to this patch to set their owners properly. See :patch:`local-docker-compose-permissions-command`.
|
||||||
|
|
||||||
|
.. patch:: local-docker-compose-prod-services
|
||||||
|
|
||||||
|
``local-docker-compose-prod-services``
|
||||||
|
======================================
|
||||||
|
|
||||||
|
File: ``local/docker-compose.prod.yml``
|
||||||
|
|
||||||
|
.. patch:: local-docker-compose-services
|
||||||
|
|
||||||
|
``local-docker-compose-services``
|
||||||
|
=================================
|
||||||
|
|
||||||
|
File: ``local/docker-compose.yml``
|
||||||
|
|
||||||
|
.. patch:: openedx-auth
|
||||||
|
|
||||||
|
``openedx-auth``
|
||||||
|
================
|
||||||
|
|
||||||
|
File: ``apps/openedx/config/partials/auth.yml``
|
||||||
|
|
||||||
|
.. patch:: openedx-cms-common-settings
|
||||||
|
|
||||||
|
``openedx-cms-common-settings``
|
||||||
|
===============================
|
||||||
|
|
||||||
|
File: ``apps/openedx/settings/partials/common_cms.py``
|
||||||
|
|
||||||
|
.. patch:: openedx-cms-development-settings
|
||||||
|
|
||||||
|
``openedx-cms-development-settings``
|
||||||
|
====================================
|
||||||
|
|
||||||
|
File: ``apps/openedx/settings/cms/development.py``
|
||||||
|
|
||||||
|
.. patch:: openedx-cms-production-settings
|
||||||
|
|
||||||
|
``openedx-cms-production-settings``
|
||||||
|
===================================
|
||||||
|
|
||||||
|
File: ``apps/openedx/settings/cms/production.py``
|
||||||
|
|
||||||
|
.. patch:: openedx-common-assets-settings
|
||||||
|
|
||||||
|
``openedx-common-assets-settings``
|
||||||
|
==================================
|
||||||
|
|
||||||
|
File: ``build/openedx/settings/partials/assets.py``
|
||||||
|
|
||||||
|
|
||||||
|
.. patch:: openedx-common-i18n-settings
|
||||||
|
|
||||||
|
``openedx-common-i18n-settings``
|
||||||
|
================================
|
||||||
|
|
||||||
|
File: ``build/openedx/settings/partials/i18n.py``
|
||||||
|
|
||||||
|
.. patch:: openedx-common-settings
|
||||||
|
|
||||||
|
``openedx-common-settings``
|
||||||
|
===========================
|
||||||
|
|
||||||
|
File: ``apps/openedx/settings/partials/common_all.py``
|
||||||
|
|
||||||
|
.. patch:: openedx-dev-dockerfile-post-python-requirements
|
||||||
|
|
||||||
|
``openedx-dev-dockerfile-post-python-requirements``
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
File: ``build/openedx/Dockerfile``
|
||||||
|
|
||||||
|
.. patch:: openedx-development-settings
|
||||||
|
|
||||||
|
``openedx-development-settings``
|
||||||
|
================================
|
||||||
|
|
||||||
|
Files: ``apps/openedx/settings/cms/development.py``, ``apps/openedx/settings/lms/development.py``
|
||||||
|
|
||||||
|
.. patch:: openedx-dockerfile
|
||||||
|
|
||||||
|
``openedx-dockerfile``
|
||||||
|
======================
|
||||||
|
|
||||||
|
File: ``build/openedx/Dockerfile``
|
||||||
|
|
||||||
|
.. patch:: openedx-dockerfile-final
|
||||||
|
|
||||||
|
``openedx-dockerfile-final``
|
||||||
|
============================
|
||||||
|
|
||||||
|
File: ``build/openedx/Dockerfile``
|
||||||
|
|
||||||
|
.. patch:: openedx-dockerfile-git-patches-default
|
||||||
|
|
||||||
|
``openedx-dockerfile-git-patches-default``
|
||||||
|
==========================================
|
||||||
|
|
||||||
|
File: ``build/openedx/Dockerfile``
|
||||||
|
|
||||||
|
.. patch:: openedx-dockerfile-minimal
|
||||||
|
|
||||||
|
``openedx-dockerfile-minimal``
|
||||||
|
==============================
|
||||||
|
|
||||||
|
File: ``build/openedx/Dockerfile``
|
||||||
|
|
||||||
|
.. patch:: openedx-dockerfile-post-git-checkout
|
||||||
|
|
||||||
|
``openedx-dockerfile-post-git-checkout``
|
||||||
|
========================================
|
||||||
|
|
||||||
|
File: ``build/openedx/Dockerfile``
|
||||||
|
|
||||||
|
.. patch:: openedx-dockerfile-post-python-requirements
|
||||||
|
|
||||||
|
``openedx-dockerfile-post-python-requirements``
|
||||||
|
===============================================
|
||||||
|
|
||||||
|
File: ``build/openedx/Dockerfile``
|
||||||
|
|
||||||
|
.. patch:: openedx-dockerfile-pre-assets
|
||||||
|
|
||||||
|
``openedx-dockerfile-pre-assets``
|
||||||
|
=================================
|
||||||
|
|
||||||
|
File: ``build/openedx/Dockerfile``
|
||||||
|
|
||||||
|
.. patch:: openedx-lms-common-settings
|
||||||
|
|
||||||
|
``openedx-lms-common-settings``
|
||||||
|
===============================
|
||||||
|
|
||||||
|
File: ``apps/openedx/settings/partials/common_lms.py``
|
||||||
|
|
||||||
|
Python-formatted LMS settings used both in production and development.
|
||||||
|
|
||||||
|
.. patch:: openedx-lms-development-settings
|
||||||
|
|
||||||
|
``openedx-lms-development-settings``
|
||||||
|
====================================
|
||||||
|
|
||||||
|
File: ``apps/openedx/settings/lms/development.py``
|
||||||
|
|
||||||
|
Python-formatted LMS settings in development. Values defined here override the values from :patch:`openedx-lms-common-settings` or :patch:`openedx-lms-production-settings`.
|
||||||
|
|
||||||
|
.. patch:: openedx-lms-production-settings
|
||||||
|
|
||||||
|
``openedx-lms-production-settings``
|
||||||
|
===================================
|
||||||
|
|
||||||
|
File: ``apps/openedx/settings/lms/production.py``
|
||||||
|
|
||||||
|
Python-formatted LMS settings in production. Values defined here override the values from :patch:`openedx-lms-common-settings`.
|
||||||
|
|
||||||
|
``uwsgi-config``
|
||||||
|
================
|
||||||
|
|
||||||
|
File: ``apps/openedx/settings/uwsgi.ini``
|
||||||
|
|
||||||
|
A .INI formatted file used to extend or override the uWSGI configuration.
|
||||||
|
|
||||||
|
Check the uWSGI documentation for more details about the `.INI format <https://uwsgi-docs.readthedocs.io/en/latest/Configuration.html#ini-files>`__ and the `list of available options <https://uwsgi-docs.readthedocs.io/en/latest/Options.html>`__.
|
||||||
|
|
||||||
|
.. patch:: uwsgi-config
|
|
@ -5,7 +5,7 @@ Running Open edX
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
local
|
local
|
||||||
k8s
|
k8s
|
||||||
dev
|
dev
|
||||||
|
|
|
@ -11,11 +11,12 @@ What should you do if you have a problem?
|
||||||
1. Read the error logs that appear in the console. When running a single server platform as daemon, you can view the logs with the ``tutor local logs`` command. (see :ref:`logging` below)
|
1. Read the error logs that appear in the console. When running a single server platform as daemon, you can view the logs with the ``tutor local logs`` command. (see :ref:`logging` below)
|
||||||
2. Check if your problem already has a solution right here in the :ref:`troubleshooting` section.
|
2. Check if your problem already has a solution right here in the :ref:`troubleshooting` section.
|
||||||
3. Search for your problem in the `open and closed Github issues <https://github.com/overhangio/tutor/issues?utf8=%E2%9C%93&q=is%3Aissue>`_.
|
3. Search for your problem in the `open and closed Github issues <https://github.com/overhangio/tutor/issues?utf8=%E2%9C%93&q=is%3Aissue>`_.
|
||||||
4. Search for your problem in the `community forums <https://discuss.overhang.io>`__.
|
4. Search for your problem in the (now legacy) `Tutor community forums <https://discuss.overhang.io>`__.
|
||||||
5. If, despite all your efforts, you can't solve the problem by yourself, you should discuss it in the `community forums <https://discuss.overhang.io>`__. Please give as much details about your problem as possible! As a rule of thumb, **people will not dedicate more time to solving your problem than you took to write your question**.
|
5. Search for your problem in the `Open edX community forum <https://discuss.openedx.org/>`__.
|
||||||
6. If you are *absolutely* positive that you are facing a technical issue with Tutor, and not with Open edX, not with your server, not your custom configuration, then, and only then, should you open an issue on `Github <https://github.com/overhangio/tutor/issues/new>`__. You *must* follow the instructions from the issue template!!! If you do not follow this procedure, your Github issues will be mercilessly closed 🤯.
|
6. If despite all your efforts, you can't solve the problem by yourself, you should discuss it in the `Open edX community forum <https://discuss.openedx.org>`__. Please give as many details about your problem as possible! As a rule of thumb, **people will not dedicate more time to solving your problem than you took to write your question**. You should tag your topic with "tutor" or the corresponding Tutor plugin name ("tutor-discovery", etc.) in order to notify the maintainers.
|
||||||
|
7. If you are *absolutely* positive that you are facing a technical issue with Tutor, and not with Open edX, not with your server, not your custom configuration, then, and only then, should you open an issue on `Github <https://github.com/overhangio/tutor/issues/>`__. You *must* follow the instructions from the issue template!!! If you do not follow this procedure, your Github issues will be mercilessly closed 🤯.
|
||||||
|
|
||||||
Do you need professional assistance with your tutor-managed Open edX platform? Overhang.IO offers online support as part of its `Long Term Support (LTS) offering <https://overhang.io/tutor/pricing>`__.
|
Do you need professional assistance with your Open edX platform? `Edly <https://edly.io>`__ provides online support as part of its `Open edX installation service <https://edly.io/services/open-edx-installation/>`__.
|
||||||
|
|
||||||
.. _logging:
|
.. _logging:
|
||||||
|
|
||||||
|
@ -23,7 +24,7 @@ Logging
|
||||||
-------
|
-------
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Logs are of paramount importance for debugging Tutor. When asking for help on the `Tutor forums <https://discuss.overhang.io>`__, **you should always include the unedited logs of your app**. You can get those with::
|
Logs are of paramount importance for debugging Tutor. When asking for help on the `Open edX forum <https://discuss.openedx.org>`__, **you should always include the unedited logs of your app**. You can get those with::
|
||||||
|
|
||||||
tutor local logs --tail=100 -f
|
tutor local logs --tail=100 -f
|
||||||
|
|
||||||
|
@ -31,9 +32,9 @@ To view the logs from all containers use the ``tutor local logs`` command, which
|
||||||
|
|
||||||
tutor local logs --follow
|
tutor local logs --follow
|
||||||
|
|
||||||
To view the logs from just one container, for instance the web server::
|
To view the logs from just one container, for instance, the webserver::
|
||||||
|
|
||||||
tutor local logs --follow nginx
|
tutor local logs --follow caddy
|
||||||
|
|
||||||
The last commands produce the logs since the creation of the containers, which can be a lot. Similar to a ``tail -f``, you can run::
|
The last commands produce the logs since the creation of the containers, which can be a lot. Similar to a ``tail -f``, you can run::
|
||||||
|
|
||||||
|
@ -43,10 +44,10 @@ If you'd rather use a graphical user interface for viewing logs, you are encoura
|
||||||
|
|
||||||
.. _webserver:
|
.. _webserver:
|
||||||
|
|
||||||
"Cannot start service nginx: driver failed programming external connectivity"
|
"Cannot start service caddy: driver failed programming external connectivity"
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
The containerized Nginx needs to listen to ports 80 and 443 on the host. If there is already a webserver, such as Apache or Nginx, running on the host, the nginx container will not be able to start. To solve this issue, check the section on :ref:`how to setup a web proxy <web_proxy>`.
|
The containerized Caddy needs to listen to ports 80 and 443 on the host. If there is already a webserver, such as Apache, Caddy, or Nginx, running on the host, the caddy container will not be able to start. To solve this issue, check the section on :ref:`how to setup a web proxy <web_proxy>`.
|
||||||
|
|
||||||
"Couldn't connect to docker daemon"
|
"Couldn't connect to docker daemon"
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
@ -55,24 +56,55 @@ This is not an error with Tutor, but with your Docker installation. This is freq
|
||||||
|
|
||||||
docker run --rm hello-world
|
docker run --rm hello-world
|
||||||
|
|
||||||
If the above command does not work, you should fix your Docker installation. Some people will suggest to run Docker as root, or with ``sudo``; **do not do this**. Instead, what you should probably do is to add your user to the "docker" group. For more information, check out the `official Docker installation instructions <https://docs.docker.com/install/linux/linux-postinstall/#manage-docker-as-a-non-root-user>`__.
|
If the above command does not work, you should fix your Docker installation. Some people will suggest running Docker as root, or with ``sudo``; **do not do this**. Instead, what you should probably do is add your user to the "docker" group. For more information, check out the `official Docker installation instructions <https://docs.docker.com/install/linux/linux-postinstall/#manage-docker-as-a-non-root-user>`__.
|
||||||
|
|
||||||
.. _migrations_killed:
|
.. _migrations_killed:
|
||||||
|
|
||||||
"Running migrations... Killed!" / "Command failed with status 137: docker-compose"
|
"Running migrations... Killed!" / "Command failed with status 137: docker-compose"
|
||||||
----------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
Open edX requires at least 4 GB RAM, in particular to run the SQL migrations. If the ``tutor local quickstart`` command dies after displaying "Running migrations", you most probably need to buy more memory or add swap to your machine. On Docker for Mac OS, by default, containers are allocated at most 2 GB of RAM. You should follow `these instructions from the official Docker documentation <https://docs.docker.com/docker-for-mac/#advanced>`__ to allocate at least 4-5 Gb to the Docker daemon.
|
Open edX requires at least 4 GB RAM, in particular, to run the SQL migrations. If the ``tutor local launch`` command dies after displaying "Running migrations", you most probably need to buy more memory or add swap to your machine.
|
||||||
|
|
||||||
If migrations were killed halfway, there is a good chance that the MySQL database is in a state that is hard to recover from. The easiest way to recover is simply to delete all the MySQL data and restart the quickstart process. After you have allocated more memory to the Docker daemon, run::
|
On macOS, by default, Docker allocates at most 2 GB of RAM to containers. ``launch`` tries to check your current allocation and outputs a warning if it can't find a value of at least 4 GB. You should follow `these instructions from the official Docker documentation <https://docs.docker.com/docker-for-mac/#advanced>`__ to allocate at least 4-5 GB to the Docker daemon.
|
||||||
|
|
||||||
|
If migrations were killed halfway, there is a good chance that the MySQL database is in a state that is hard to recover from. The easiest way to recover is simply to delete all the MySQL data and restart the launch process. After you have allocated more memory to the Docker daemon, run::
|
||||||
|
|
||||||
tutor local stop
|
tutor local stop
|
||||||
sudo rm -rf "$(tutor config printroot)/data/mysql"
|
sudo rm -rf "$(tutor config printroot)/data/mysql"
|
||||||
tutor local quickstart
|
tutor local launch
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
THIS WILL ERASE ALL YOUR DATA! Do not run this on a production instance. This solution is only viable for new Open edX installations.
|
THIS WILL ERASE ALL YOUR DATA! Do not run this on a production instance. This solution is only viable for new Open edX installations.
|
||||||
|
|
||||||
|
"Can't connect to MySQL server on 'mysql:3306' (111)"
|
||||||
|
-----------------------------------------------------
|
||||||
|
|
||||||
|
The most common reason this happens is that you are running two different instances of Tutor simultaneously, causing a port conflict between MySQL containers. Tutor will try to prevent you from doing that (for example, it will stop ``local`` containers if you start ``dev`` ones, and vice versa), but it cannot prevent all edge cases. So, as a first step, stop all possible Tutor platform variants::
|
||||||
|
|
||||||
|
tutor dev stop
|
||||||
|
tutor local stop
|
||||||
|
tutor k8s stop
|
||||||
|
|
||||||
|
And then run your command(s) again, ensuring you're consistently using the correct Tutor variant (``tutor dev``, ``tutor local``, or ``tutor k8s``).
|
||||||
|
|
||||||
|
If that doesn't work, then check if you have any other Docker containers running that may using port 3306::
|
||||||
|
|
||||||
|
docker ps -a
|
||||||
|
|
||||||
|
For example, if you have ever used `Tutor Nightly <https://docs.tutor.edly.io/tutorials/nightly.html>`_, check whether you still have ``tutor_nightly_`` containers running. Conversely, if you're trying to run Tutor Nightly now, check whether you have non-Nightly ``tutor_`` containers running. If so, switch to that other version of Tutor, run ``tutor (dev|local|k8s) stop``, and then switch back to your preferred version of Tutor.
|
||||||
|
|
||||||
|
Alternatively, if there are any other non-Tutor containers using port 3306, then stop and remove them::
|
||||||
|
|
||||||
|
docker stop <container_name>
|
||||||
|
docker rm <container_name>
|
||||||
|
|
||||||
|
Finally, if you've ensured that containers or other programs are making use of port 3306, check the logs of the MySQL container itself::
|
||||||
|
|
||||||
|
tutor (dev|local|k8s) logs mysql
|
||||||
|
|
||||||
|
Check whether the MySQL container is crashing upon startup, and if so, what is causing it to crash.
|
||||||
|
|
||||||
|
|
||||||
Help! The Docker containers are eating all my RAM/CPU/CHEESE
|
Help! The Docker containers are eating all my RAM/CPU/CHEESE
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -80,6 +112,34 @@ You can identify which containers are consuming most resources by running::
|
||||||
|
|
||||||
docker stats
|
docker stats
|
||||||
|
|
||||||
|
In idle mode, the "mysql" container should use ~200MB memory; ~200-300MB for the the "lms" and "cms" containers.
|
||||||
|
|
||||||
|
On some operating systems, such as RedHat, Arch Linux or Fedora, a very high limit of the number of open files (``nofile``) per container may cause the "mysql", "lms" and "cms" containers to use a lot of memory: up to 8-16GB. To check whether you might impacted, run::
|
||||||
|
|
||||||
|
cat /proc/$(pgrep dockerd)/limits | grep "Max open files"
|
||||||
|
|
||||||
|
If the output is 1073741816 or higher, then it is likely that you are affected by `this mysql issue <https://github.com/docker-library/mysql/issues/579>`__. To learn more about the root cause, read `this containerd issue comment <https://github.com/containerd/containerd/pull/7566#issuecomment-1285417325>`__. Basically, the OS is hard-coding a very high limit for the allowed number of open files, and this is causing some containers to fail. To resolve the problem, you should configure the Docker daemon to enforce a lower value, as described `here <https://github.com/docker-library/mysql/issues/579#issuecomment-1432576518>`__. Edit ``/etc/docker/daemon.json`` and add the following contents::
|
||||||
|
|
||||||
|
{
|
||||||
|
"default-ulimits": {
|
||||||
|
"nofile": {
|
||||||
|
"Name": "nofile",
|
||||||
|
"Hard": 1048576,
|
||||||
|
"Soft": 1048576
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Check your configuration is valid with::
|
||||||
|
|
||||||
|
dockerd --validate
|
||||||
|
|
||||||
|
Then restart the Docker service::
|
||||||
|
|
||||||
|
sudo systemctl restart docker.service
|
||||||
|
|
||||||
|
Launch your Open edX platform again with ``tutor local launch``. You should observe normal memory usage.
|
||||||
|
|
||||||
"Build failed running pavelib.servers.lms: Subprocess return code: 1"
|
"Build failed running pavelib.servers.lms: Subprocess return code: 1"
|
||||||
-----------------------------------------------------------------------
|
-----------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -95,19 +155,10 @@ This might occur when you run a ``paver`` command. ``/dev/null`` eats the actual
|
||||||
|
|
||||||
The error produced should help you better understand what is happening.
|
The error produced should help you better understand what is happening.
|
||||||
|
|
||||||
"ValueError: Unable to configure handler 'local'"
|
|
||||||
---------------------------------------------------
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
ValueError: Unable to configure handler 'local': [Errno 2] No such file or directory
|
|
||||||
|
|
||||||
This will occur if you try to run a development environment without patching the LOGGING configuration, as indicated in the `development_` section. Maybe you correctly patched the development settings, but they are not taken into account? For instance, you might have correctly defined the ``TUTOR_EDX_PLATFORM_SETTINGS`` environment variable, but ``paver`` uses the ``devstack`` settings (which does not patch the ``LOGGING`` variable). This is because calling ``paver lms --settings=development`` or ``paver cms --settings=development`` ignores the ``--settings`` argument. Yes, it might be considered an edx-platform bug... Instead, you should run the ``update_assets`` and ``runserver`` commands, as explained above.
|
|
||||||
|
|
||||||
The chosen default language does not display properly
|
The chosen default language does not display properly
|
||||||
-----------------------------------------------------
|
-----------------------------------------------------
|
||||||
|
|
||||||
By default, Open edX comes with a `limited set <https://github.com/edx/edx-platform/blob/master/conf/locale/config.yaml>` of translation/localization files. To complement these languages, we add locales from the `openedx-i18n project <https://github.com/openedx/openedx-i18n/blob/master/edx-platform/locale/config-extra.yaml>`_. But not all supported locales are downloaded. In some cases, the chosen default language will not display properly because if was not packaged in either edx-platform or openedx-i18n. If you feel like your language should be packaged, please `open an issue on the openedx-i18n project <https://github.com/openedx/openedx-i18n/issues>`_.
|
By default, Open edX comes with a `limited set <https://github.com/openedx/edx-platform/blob/master/conf/locale/config.yaml>` of translation/localization files. To complement these languages, we add locales from the `openedx-i18n project <https://github.com/openedx/openedx-i18n/blob/master/edx-platform/locale/config-extra.yaml>`_. But not all supported locales are downloaded. In some cases, the chosen default language will not display properly because it was not packaged in either edx-platform or openedx-i18n. If you feel like your language should be packaged, please `open an issue on the openedx-i18n project <https://github.com/openedx/openedx-i18n/issues>`_.
|
||||||
|
|
||||||
When I make changes to a course in the CMS, they are not taken into account by the LMS
|
When I make changes to a course in the CMS, they are not taken into account by the LMS
|
||||||
--------------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------------
|
||||||
|
@ -117,3 +168,28 @@ This issue should only happen in development mode. Long story short, it can be s
|
||||||
tutor dev run lms ./manage.py lms waffle_switch block_structure.invalidate_cache_on_publish on --create
|
tutor dev run lms ./manage.py lms waffle_switch block_structure.invalidate_cache_on_publish on --create
|
||||||
|
|
||||||
If you'd like to learn more, please take a look at `this Github issue <https://github.com/overhangio/tutor/issues/302>`__.
|
If you'd like to learn more, please take a look at `this Github issue <https://github.com/overhangio/tutor/issues/302>`__.
|
||||||
|
|
||||||
|
High resource consumption on ``tutor images build`` by docker
|
||||||
|
-------------------------------------------------------------
|
||||||
|
|
||||||
|
This issue can occur when building multiple images simultaneously by Docker, issue specifically related to BuildKit.
|
||||||
|
|
||||||
|
|
||||||
|
Create a buildkit.toml configuration file with the following contents::
|
||||||
|
|
||||||
|
[worker.oci]
|
||||||
|
max-parallelism = 2
|
||||||
|
|
||||||
|
This configuration file limits the number of layers built concurrently to 2, which can significantly reduce resource consumption.
|
||||||
|
|
||||||
|
Create a builder that uses this configuration::
|
||||||
|
|
||||||
|
docker buildx create --use --name=<name> --driver=docker-container --config=/path/to/buildkit.toml
|
||||||
|
|
||||||
|
Replace <name> with a suitable name for your builder, and ensure that you specify the correct path to the buildkit.toml configuration file.
|
||||||
|
|
||||||
|
Now build again::
|
||||||
|
|
||||||
|
tutor images build
|
||||||
|
|
||||||
|
All build commands should now make use of the newly configured builder. To later revert to the default builder, run ``docker buildx use default``.
|
||||||
|
|
|
@ -3,20 +3,23 @@
|
||||||
Tutor development
|
Tutor development
|
||||||
=================
|
=================
|
||||||
|
|
||||||
|
Setting up your development environment
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
Start by cloning the Tutor repository::
|
Start by cloning the Tutor repository::
|
||||||
|
|
||||||
git clone https://github.com/overhangio/tutor.git
|
git clone https://github.com/overhangio/tutor.git
|
||||||
cd tutor/
|
cd tutor/
|
||||||
|
|
||||||
Install requirements
|
Install requirements
|
||||||
--------------------
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
pip install -r requirements/dev.txt
|
pip install -r requirements/dev.txt
|
||||||
|
|
||||||
Run tests
|
Run tests
|
||||||
---------
|
~~~~~~~~~
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
@ -25,7 +28,7 @@ Run tests
|
||||||
Yes, there are very few unit tests for now, but this is probably going to change.
|
Yes, there are very few unit tests for now, but this is probably going to change.
|
||||||
|
|
||||||
Code formatting
|
Code formatting
|
||||||
---------------
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Tutor code formatting is enforced by `black <https://black.readthedocs.io/en/stable/>`_. To check whether your code changes conform to formatting standards, run::
|
Tutor code formatting is enforced by `black <https://black.readthedocs.io/en/stable/>`_. To check whether your code changes conform to formatting standards, run::
|
||||||
|
|
||||||
|
@ -39,15 +42,18 @@ Static error detection is performed by `pylint <https://pylint.readthedocs.io/en
|
||||||
|
|
||||||
make test-lint
|
make test-lint
|
||||||
|
|
||||||
Bundle ``tutor`` executable
|
Common developer tasks
|
||||||
---------------------------
|
----------------------
|
||||||
|
|
||||||
|
Generating the ``tutor`` executable binary
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
make bundle
|
make bundle
|
||||||
|
|
||||||
Generating the documentation
|
Generating the documentation
|
||||||
----------------------------
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
@ -60,33 +66,69 @@ You can then browse the documentation with::
|
||||||
make browse
|
make browse
|
||||||
|
|
||||||
Releasing a new version
|
Releasing a new version
|
||||||
-----------------------
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
- Bump the ``__version__`` value in ``tutor/__about__.py``.
|
- Bump the ``__version__`` value in ``tutor/__about__.py``. (see :ref:`versioning` below)
|
||||||
- Replace "Unreleased" by the version name and date in CHANGELOG.md.
|
- Collect changelog entries with ``make changelog``.
|
||||||
- Create a commit with the version changelog.
|
- Create a commit with the version changelog.
|
||||||
- Run ``make release``: this will push to the default repo/branch for the current branch.
|
- Run tests with ``make test``.
|
||||||
|
- Push your changes to the upstream repository.
|
||||||
|
|
||||||
|
.. _versioning:
|
||||||
|
|
||||||
|
Versioning
|
||||||
|
----------
|
||||||
|
|
||||||
|
The versioning format used in Tutor is the following::
|
||||||
|
|
||||||
|
RELEASE.MAJOR.MINOR(-BRANCH)
|
||||||
|
|
||||||
|
When making a new Tutor release, increment the:
|
||||||
|
|
||||||
|
- RELEASE version when a new Open edX release comes out. The new value should match the ordinal value of the first letter of the release name: Aspen 🡒 1, Birch 🡒 2, ... Zebra 🡒 26.
|
||||||
|
- MAJOR version when making a backward-incompatible change (prefixed by "💥" in the changelog, as explained below).
|
||||||
|
- MINOR version when making a backward-compatible change.
|
||||||
|
|
||||||
|
An optional BRANCH suffix may be appended to the release name to indicate that extra changes were added on top of the latest release. For instance, "x.y.z-nightly" corresponds to release x.y.z on top of which extra changes were added to make it compatible with the Open edX master branches (see the :ref:`tutorial on running Tutor Nightly <nightly>`).
|
||||||
|
|
||||||
|
`Officially-supported plugins <https://overhang.io/tutor/plugins>`__ follow the same versioning pattern. As a third-party plugin developer, you are encouraged to use the same pattern to make it immediately clear to your end-users which Open edX versions are supported.
|
||||||
|
|
||||||
|
In Tutor and its officially-supported plugins, certain features, API endpoints, and older depenency versions are periodically deprecated. Generally, warnings are added to the Changelogs and/or the command-line interface one major release before support for any behavior is removed. In order to keep track of pending removals in the source code, comments containing the string ``REMOVE-AFTER-VXX`` should be used, where ``<XX>`` is the last major version that must support the behavior. For example::
|
||||||
|
|
||||||
|
# This has been replaced with SOME_NEW_HOOK (REMOVE-AFTER-V25).
|
||||||
|
SOME_OLD_HOOK = Filter()
|
||||||
|
|
||||||
|
indicates that this filter definition can be removed as soon as Tutor v26.0.0.
|
||||||
|
|
||||||
.. _contributing:
|
.. _contributing:
|
||||||
|
|
||||||
Contributing to Tutor
|
Contributing to Tutor
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
Third-party contributions to Tutor and its plugins are more than welcome! Just make sure to follow these guidelines:
|
Contributions to Tutor and its plugins are highly encouraged. Please adhere to the following guidelines:
|
||||||
|
|
||||||
- Outside of obvious bugs, contributions should be discussed first in the `official Tutor forums <https://discuss.overhang.io>`__.
|
- **General Discussion**: Before addressing anything other than clear-cut bugs, start a discussion on the `official Open edX forum <https://discuss.openedx.org>`__. This facilitates reaching a consensus on a high-level solution.
|
||||||
- Once we agree on a high-level solution, you should open a pull request on the `Tutor repository <https://github.com/overhangio/tutor/pulls>`__ or the corresponding plugin.
|
- **Pull Requests**: For changes to Tutor core or plugin-specific modifications, open a pull request on the `Tutor repository <https://github.com/overhangio/tutor/pulls>`__ or the corresponding plugin repository.
|
||||||
- Write a good Git commit title and message: explain why you are making this change, what problem you are solving and which solution you adopted. Link to the relevant conversation topics in the forums and describe your use case. We *love* long, verbose descriptions :)
|
- **Running Tests and Code Formatting**:
|
||||||
- Make sure that all tests pass by running ``make test`` (see above).
|
- Ensure all tests pass by running ``make test``. This is mandatory for both Tutor core and plugin contributions.
|
||||||
- If your PR is in the Tutor core repository, add an item to the CHANGELOG file, in the "Unreleased" section. Use the same format as the other items::
|
- If formatting tests fail, correct your code format using ``make format``.
|
||||||
|
- **Changelog Entry**: Create a changelog entry for significant changes (excluding reformatting or documentation) by running ``make changelog-entry``. Edit the newly created file following the given formatting instructions. This applies to both Tutor core and plugin changes.
|
||||||
|
- **Commit Messages**: Write clear Git commit titles and messages. Detail the rationale for your changes, the issue being addressed, and your solution. Include links to relevant forum discussions and describe your use case. Detailed explanations are valuable. For commit titles, follow `conventional commits <https://www.conventionalcommits.org>`__ guidelines.Additionally, if your pull request addresses an existing GitHub issue, include 'Close #XXX' in your commit message, where XXX is the issue number.
|
||||||
|
|
||||||
- [TYPE] DESCRIPTION
|
Releasing a new version
|
||||||
|
-----------------------
|
||||||
|
|
||||||
Where "TYPE" is either "Bugfix", "Improvement", "Feature" or "Security". You should add an explosion emoji ("💥") before "[TYPE]" if you are making a breaking change.
|
When releasing a new version:
|
||||||
|
|
||||||
|
- **Version Number**: Update the version number in `__about__.py`. For detailed guidelines on version numbering, refer to the (versioning guidelines :ref:`versioning`).
|
||||||
|
- **Changelog Compilation**: Compile all changelog entries using ``make changelog``.
|
||||||
|
- **Git Commit for Release**: Use the format ``git commit -a -m "vX.Y.Z"`` to indicate the new version in the git commit title.
|
||||||
|
|
||||||
Happy hacking! ☘️
|
Happy hacking! ☘️
|
||||||
|
|
||||||
|
.. _maintainers:
|
||||||
|
|
||||||
Joining the team of Tutor Maintainers
|
Joining the team of Tutor Maintainers
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
We have an open team of volunteers who help support the project. You can read all about it `here <https://discuss.overhang.io/t/the-tutor-maintainer-handbook/1375>`__.
|
We have an open team of volunteers who help support the project. You can read all about it `here <https://discuss.openedx.org/t/tutor-maintainers/7287>`__ -- and we hope that you'll consider joining us 😉
|
||||||
|
|
12
docs/tutorials/arm64.rst
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
.. _arm64:
|
||||||
|
|
||||||
|
Running Tutor on ARM-based systems
|
||||||
|
==================================
|
||||||
|
|
||||||
|
Tutor can be used on ARM64 systems, and official ARM64 docker images are available starting from Tutor v16.
|
||||||
|
|
||||||
|
For older versions of Tutor (v14 or v15), there are several options:
|
||||||
|
|
||||||
|
* Use emulation (via qemu or Rosetta 2) to run x86_64 images. Just make sure your installation of Docker supports emulation and use Tutor as normal. This may be 20%-100% slower than native images, depending on the emulation method.
|
||||||
|
* Use the `unofficial community-maintained ARM64 plugin <https://github.com/open-craft/tutor-contrib-arm64>`_ which will set the required settings for you and which includes unofficial docker images.
|
||||||
|
* Build your own ARM64 images, e.g. using ``tutor images build openedx permissions`` and/or ``tutor images build openedx-dev`` before launching the LMS.
|
36
docs/tutorials/datamigration.rst
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
.. _backup_tutorial:
|
||||||
|
|
||||||
|
Making backups and migrating data
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
With Tutor, all data are stored in a single folder. This means that it's extremely easy to migrate an existing platform to a different server. For instance, it's possible to configure a platform locally on a laptop, and then move this platform to a production server.
|
||||||
|
|
||||||
|
1. Make sure `tutor` is installed on both servers with the same version.
|
||||||
|
2. Stop any running platform on server 1::
|
||||||
|
|
||||||
|
tutor local stop
|
||||||
|
|
||||||
|
3. Transfer the configuration, environment, and platform data from server 1 to server 2::
|
||||||
|
|
||||||
|
sudo rsync -avr "$(tutor config printroot)/" username@server2:/tmp/tutor/
|
||||||
|
|
||||||
|
4. On server 2, move the data to the right location::
|
||||||
|
|
||||||
|
mv /tmp/tutor "$(tutor config printroot)"
|
||||||
|
|
||||||
|
5. Start the instance with::
|
||||||
|
|
||||||
|
tutor local start -d
|
||||||
|
|
||||||
|
Making database dumps
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
To dump all data from the MySQL and Mongodb databases used on the platform, run the following commands::
|
||||||
|
|
||||||
|
tutor local exec \
|
||||||
|
-e USERNAME="$(tutor config printvalue MYSQL_ROOT_USERNAME)" \
|
||||||
|
-e PASSWORD="$(tutor config printvalue MYSQL_ROOT_PASSWORD)" \
|
||||||
|
mysql sh -c 'mysqldump --all-databases --user=$USERNAME --password=$PASSWORD > /var/lib/mysql/dump.sql'
|
||||||
|
tutor local exec mongodb mongodump --out=/data/db/dump.mongodb
|
||||||
|
|
||||||
|
The ``dump.sql`` and ``dump.mongodb`` files will be located in ``$(tutor config printroot)/data/mysql`` and ``$(tutor config printroot)/data/mongodb``.
|
4
docs/tutorials/edx-platform-settings.rst
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
Modifying ``edx-platform`` settings
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
The default settings module loaded by ``edx-platform`` is ``tutor.production`` in production and ``tutor.development`` in development. The folders ``$(tutor config printroot)/env/apps/openedx/settings/lms`` and ``$(tutor config printroot)/env/apps/openedx/settings/cms`` are mounted as ``edx-platform/lms/envs/tutor`` and ``edx-platform/cms/envs/tutor`` inside the docker containers. To modify these settings you must create a plugin that implements one or more of the patch statements in those setting files. See the :ref:`plugin_development_tutorial` tutorial for more information on how to create a plugin.
|
177
docs/tutorials/edx-platform.rst
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
.. _edx_platform:
|
||||||
|
|
||||||
|
Working on edx-platform as a developer
|
||||||
|
======================================
|
||||||
|
|
||||||
|
Tutor supports running in development with ``tutor dev`` commands. Developers frequently need to work on a fork of some repository. The question then becomes: how to make their changes available within the "openedx" Docker container?
|
||||||
|
|
||||||
|
For instance, when troubleshooting an issue in `edx-platform <https://github.com/openedx/edx-platform>`__, we would like to make some changes to a local fork of that repository, and then apply these changes immediately in the "lms" and the "cms" containers (but also "lms-worker", "cms-worker", etc.)
|
||||||
|
|
||||||
|
Similarly, when developing a custom XBlock, we would like to hot-reload any change we make to the XBlock source code within the containers.
|
||||||
|
|
||||||
|
Tutor provides a simple solution to these questions. In both cases, the solution takes the form of a ``tutor mounts add ...`` command.
|
||||||
|
|
||||||
|
Working on the "edx-platform" repository
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
Download the code from the upstream repository::
|
||||||
|
|
||||||
|
cd /my/workspace/edx-plaform
|
||||||
|
git clone https://github.com/openedx/edx-platform .
|
||||||
|
|
||||||
|
Check out the right version of the upstream repository. If you are working on the `current "zebulon" release <https://docs.openedx.org/en/latest/community/release_notes/index.html>`__ of Open edX, then you should checkout the corresponding branch::
|
||||||
|
|
||||||
|
# "zebulon" is an example. You should put the actual release name here.
|
||||||
|
# I.e: aspen, birch, cypress, etc.
|
||||||
|
git checkout open-release/zebulon.master
|
||||||
|
|
||||||
|
On the other hand, if you are working on the Tutor :ref:`"nightly" <nightly>` branch then you should checkout the master branch::
|
||||||
|
|
||||||
|
git checkout master
|
||||||
|
|
||||||
|
Then, mount the edx-platform repository with Tutor::
|
||||||
|
|
||||||
|
tutor mounts add /my/workspace/edx-plaform
|
||||||
|
|
||||||
|
This command does a few "magical" things 🧙 behind the scenes:
|
||||||
|
|
||||||
|
1. Mount the edx-platform repository in the image at build-time. This means that when you run ``tutor images build openedx``, your custom repository will be used instead of the upstream. In particular, any change you've made to the installed requirements, static assets, etc. will be taken into account.
|
||||||
|
2. Mount the edx-platform repository at run time. Thus, when you run ``tutor dev start``, any change you make to the edx-platform repository will be hot-reloaded.
|
||||||
|
|
||||||
|
You can get a glimpse of how these auto-mounts work by running ``tutor mounts list``. It should output something similar to the following::
|
||||||
|
|
||||||
|
$ tutor mounts list
|
||||||
|
- name: /home/data/regis/projets/overhang/repos/edx/edx-platform
|
||||||
|
build_mounts:
|
||||||
|
- image: openedx
|
||||||
|
context: edx-platform
|
||||||
|
- image: openedx-dev
|
||||||
|
context: edx-platform
|
||||||
|
compose_mounts:
|
||||||
|
- service: lms
|
||||||
|
container_path: /openedx/edx-platform
|
||||||
|
- service: cms
|
||||||
|
container_path: /openedx/edx-platform
|
||||||
|
- service: lms-worker
|
||||||
|
container_path: /openedx/edx-platform
|
||||||
|
- service: cms-worker
|
||||||
|
container_path: /openedx/edx-platform
|
||||||
|
- service: lms-job
|
||||||
|
container_path: /openedx/edx-platform
|
||||||
|
- service: cms-job
|
||||||
|
container_path: /openedx/edx-platform
|
||||||
|
|
||||||
|
Working on edx-platform Python dependencies
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
Quite often, developers don't want to work on edx-platform directly, but on a dependency of edx-platform. For instance: an XBlock. This works the same way as above. Let's take the example of the `"edx-ora2" <https://github.com/openedx/edx-ora2>`__ package, for open response assessments. First, clone the Python package::
|
||||||
|
|
||||||
|
cd /my/workspace/edx-ora2
|
||||||
|
git clone https://github.com/openedx/edx-ora2 .
|
||||||
|
|
||||||
|
Then, check out the right version of the package. This is the version that is indicated in the `edx-platform/requirements/edx/base.txt <https://github.com/openedx/edx-platform/blob/open-release/quince.master/requirements/edx/base.txt>`__. Be careful that the version that is currently in use in your version of edx-platform is **not necessarily the head of the master branch**::
|
||||||
|
|
||||||
|
git checkout <my-version-tag-or-branch>
|
||||||
|
|
||||||
|
Then, mount this repository::
|
||||||
|
|
||||||
|
tutor mounts add /my/workspace/edx-ora2
|
||||||
|
|
||||||
|
Verify that your repository is properly bind-mounted by running ``tutor mounts list``::
|
||||||
|
|
||||||
|
$ tutor mounts list
|
||||||
|
- name: /my/workspace/edx-ora2
|
||||||
|
build_mounts:
|
||||||
|
- image: openedx
|
||||||
|
context: mnt-edx-ora2
|
||||||
|
- image: openedx-dev
|
||||||
|
context: mnt-edx-ora2
|
||||||
|
compose_mounts:
|
||||||
|
- service: lms
|
||||||
|
container_path: /mnt/edx-ora2
|
||||||
|
- service: cms
|
||||||
|
container_path: /mnt/edx-ora2
|
||||||
|
- service: lms-worker
|
||||||
|
container_path: /mnt/edx-ora2
|
||||||
|
- service: cms-worker
|
||||||
|
container_path: /mnt/edx-ora2
|
||||||
|
- service: lms-job
|
||||||
|
container_path: /mnt/edx-ora2
|
||||||
|
- service: cms-job
|
||||||
|
container_path: /mnt/edx-ora2
|
||||||
|
|
||||||
|
You should then re-build the "openedx" Docker image to pick up your changes::
|
||||||
|
|
||||||
|
tutor images build openedx-dev
|
||||||
|
|
||||||
|
Then, whenever you run ``tutor dev start``, the "lms" and "cms" container should automatically hot-reload your changes.
|
||||||
|
|
||||||
|
To push your changes in production, you should do the same with ``tutor local`` and the "openedx" image::
|
||||||
|
|
||||||
|
tutor images build openedx
|
||||||
|
tutor local start -d
|
||||||
|
|
||||||
|
Debugging with breakpoints
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
To debug a local edx-platform repository, first, start development in detached mode (with ``-d``), add a `python breakpoint <https://docs.python.org/3/library/functions.html#breakpoint>`__ with ``breakpoint()`` anywhere in the code. Then, attach to the applicable service's container by running ``start`` (without ``-d``) followed by the service's name::
|
||||||
|
|
||||||
|
# Start in detached mode:
|
||||||
|
tutor dev start -d
|
||||||
|
|
||||||
|
# Debugging LMS:
|
||||||
|
tutor dev start lms
|
||||||
|
|
||||||
|
# Or, debugging CMS:
|
||||||
|
tutor dev start cms
|
||||||
|
|
||||||
|
Running edx-platform unit tests
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
It's possible to run the full set of unit tests that ship with `edx-platform <https://github.com/openedx/edx-platform/>`__. To do so, run a shell in the LMS development container::
|
||||||
|
|
||||||
|
tutor dev run lms bash
|
||||||
|
|
||||||
|
Then, run unit tests with ``pytest`` commands::
|
||||||
|
|
||||||
|
# Run tests on common apps
|
||||||
|
unset DJANGO_SETTINGS_MODULE
|
||||||
|
unset SERVICE_VARIANT
|
||||||
|
export EDXAPP_TEST_MONGO_HOST=mongodb
|
||||||
|
pytest common
|
||||||
|
pytest openedx
|
||||||
|
pytest xmodule
|
||||||
|
|
||||||
|
# Run tests on LMS
|
||||||
|
export DJANGO_SETTINGS_MODULE=lms.envs.tutor.test
|
||||||
|
pytest lms
|
||||||
|
|
||||||
|
# Run tests on CMS
|
||||||
|
export DJANGO_SETTINGS_MODULE=cms.envs.tutor.test
|
||||||
|
pytest cms
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Getting all edx-platform unit tests to pass on Tutor is currently a work-in-progress. Some unit tests are still failing. If you manage to fix some of these, please report your findings in the `Open edX forum <https://discuss.openedx.org/tag/tutor>`__.
|
||||||
|
|
||||||
|
What if my edx-platform package is not automatically bind-mounted?
|
||||||
|
------------------------------------------------------------------
|
||||||
|
|
||||||
|
It is quite possible that your package is not automatically recognized and bind-mounted by Tutor. Out of the box, Tutor defines a set of regular expressions: if your package name matches this regular expression, it will be automatically bind-mounted. But if it does not, you have to tell Tutor about it.
|
||||||
|
|
||||||
|
To do so, you will need to create a :ref:`Tutor plugin <plugin_development_tutorial>` that implements the :py:data:`tutor.hooks.Filters.MOUNTED_DIRECTORIES` filter::
|
||||||
|
|
||||||
|
import tutor import hooks
|
||||||
|
hooks.Filters.MOUNTED_DIRECTORIES.add_item(("openedx", "my-package"))
|
||||||
|
|
||||||
|
After you implement and enable that plugin, ``tutor mounts list`` should display your directory among the bind-mounted directories.
|
||||||
|
|
||||||
|
Do I have to re-build the "openedx" Docker image after every change?
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
|
No, you don't. Re-building the "openedx" Docker image may take a while, and you don't want to run this command every time you make a change to your local repositories. Because your host directory is bind-mounted in the containers at runtime, your changes will be automatically applied to the container. If you run ``tutor dev`` commands, then your changes will be automatically picked up.
|
||||||
|
|
||||||
|
If you run ``tutor local`` commands (for instance: when debugging a production instance) then your changes will *not* be automatically picked up. In such a case you should manually restart the containers::
|
||||||
|
|
||||||
|
tutor local restart lms cms lms-worker cms-worker
|
||||||
|
|
||||||
|
Re-building the "openedx" image should only be necessary when you want to push your changes to a Docker registry, then pull them on a remote server.
|
42
docs/tutorials/google-smtp.rst
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
Using Google Mail as an SMTP server
|
||||||
|
===================================
|
||||||
|
|
||||||
|
By default, Tutor comes with a simple SMTP server for sending emails. Such a server has an important limitation: it does not implement mailing good practices, such as DKIM or SPF. As a consequence. the emails you send might be flagged as spam by their recipients. Thus, you might want to disable the SMTP server and run your own, for instance using your Google Mail account.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
Google Mail SMTP servers come with their own set of limitations. For instance, you are limited to sending 500 emails a day. Reference: https://support.google.com/mail/answer/22839
|
||||||
|
|
||||||
|
You should authorize third-party to access your Google Mail account. In your Google Mail account, select "Manage Account", "Security", and turn on "Less Secure App Access". Check the Google documentation for more information on "less secure apps": https://support.google.com/accounts/answer/6010255
|
||||||
|
|
||||||
|
Then, check that you can reach the Google Mail SMTP service from your own server::
|
||||||
|
|
||||||
|
$ telnet smtp.gmail.com 587
|
||||||
|
|
||||||
|
If you get ``Connected to smtp.gmail.com.`` then it means that you can successfully reach the Google Mail SMTP servers. If not, you will have to reconfigure your firewall.
|
||||||
|
|
||||||
|
To exit the ``telnet`` shell, type ``ctrl+]``, then ``ctrl+d``.
|
||||||
|
|
||||||
|
Then, disable the SMTP server that comes with Tutor::
|
||||||
|
|
||||||
|
$ tutor config save --set RUN_SMTP=false
|
||||||
|
|
||||||
|
Configure credentials to access your SMTP server::
|
||||||
|
|
||||||
|
$ tutor config save \
|
||||||
|
--set SMTP_HOST=smtp.gmail.com \
|
||||||
|
--set SMTP_PORT=587 \
|
||||||
|
--set SMTP_USE_SSL=false \
|
||||||
|
--set SMTP_USE_TLS=true \
|
||||||
|
--set SMTP_USERNAME=YOURUSERNAME@gmail.com \
|
||||||
|
--set SMTP_PASSWORD='YOURPASSWORD'
|
||||||
|
|
||||||
|
Don't forget to replace your email address and password in the prompt above. If your email password contains special characters, you might have to escape them.
|
||||||
|
|
||||||
|
Then, restart your platform::
|
||||||
|
|
||||||
|
$ tutor local launch
|
||||||
|
|
||||||
|
That's it! You can send a test email with the following command::
|
||||||
|
|
||||||
|
$ tutor local run --no-deps lms ./manage.py lms shell -c \
|
||||||
|
"from django.core.mail import send_mail; send_mail('test subject', 'test message', 'YOURUSERNAME@gmail.com', ['YOURRECIPIENT@domain.com'])"
|
33
docs/tutorials/index.rst
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
Tutorials
|
||||||
|
=========
|
||||||
|
|
||||||
|
Open edX customization
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
plugin
|
||||||
|
theming
|
||||||
|
edx-platform
|
||||||
|
edx-platform-settings
|
||||||
|
google-smtp
|
||||||
|
nightly
|
||||||
|
|
||||||
|
System administration
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
scale
|
||||||
|
portainer
|
||||||
|
podman
|
||||||
|
proxy
|
||||||
|
datamigration
|
||||||
|
multiplatforms
|
||||||
|
oldreleases
|
||||||
|
arm64
|
||||||
|
|
||||||
|
.. Note: maybe we should create a dedicated tutorial category in the Open edX forum?
|
||||||
|
.. Other tutorials can be found in the official Tutor forums, `in the "Tutorials" category <https://discuss.overhang.io/c/tutor/tutorials/13>`__.
|
11
docs/tutorials/multiplatforms.rst
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
Running multiple Open edX platforms on a single server
|
||||||
|
======================================================
|
||||||
|
|
||||||
|
With Tutor, it is easy to run multiple Open edX instances on a single server. To do so, the following configuration parameters must be different for all platforms:
|
||||||
|
|
||||||
|
- ``TUTOR_ROOT``: so that configuration, environment, and data are not mixed up between platforms.
|
||||||
|
- ``LOCAL_PROJECT_NAME``: the various docker-compose projects cannot share the same name.
|
||||||
|
- ``CADDY_HTTP_PORT``: exposed ports cannot be shared by two different containers.
|
||||||
|
- ``LMS_HOST``, ``CMS_HOST``: the different platforms must be accessible from different domain (or subdomain) names.
|
||||||
|
|
||||||
|
In addition, a web proxy must be set up on the host, as described :ref:`in the corresponding tutorial <web_proxy>`.
|
64
docs/tutorials/nightly.rst
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
.. _nightly:
|
||||||
|
|
||||||
|
Running Open edX on the master branch ("nightly")
|
||||||
|
=================================================
|
||||||
|
|
||||||
|
Tutor was designed to make it easy for everyone to run the latest release of Open edX. But sometimes, you want to run the latest, bleeding-edge version of Open edX. This is what we call "running master", as opposed to running the release branch. Running the master branch in production is strongly **not** recommended unless you are an Open edX expert and you really know what you are doing. But Open edX developers frequently need to run the master branch locally to implement and test new features. Thus, Tutor makes it easy to run Open edX on the master branch: this is called "Tutor Nightly".
|
||||||
|
|
||||||
|
Installing Tutor Nightly
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Running Tutor Nightly requires more than setting a few configuration variables: because there are so many Open edX settings, version numbers, etc. which may change between the latest release and the current master branch, Tutor Nightly is actually maintained as a separate branch of the Tutor repository. To install Tutor Nightly, you should install Tutor from the "nightly" branch of the source repository. To do so, run::
|
||||||
|
|
||||||
|
git clone --branch=nightly https://github.com/overhangio/tutor.git
|
||||||
|
pip install -e "./tutor[full]"
|
||||||
|
|
||||||
|
As usual, it is strongly recommended to run the command above in a `Python virtual environment <https://docs.python.org/3/tutorial/venv.html>`__.
|
||||||
|
|
||||||
|
In addition to installing Tutor Nightly itself, this will install automatically the nightly versions of all official Tutor plugins (which are enumerated in `plugins.txt <https://github.com/overhangio/tutor/tree/nightly/requirements/plugins.txt>`_). Alternatively, if you wish to hack on an official plugin or install a custom plugin, you can clone that plugin's repository and install it. For instance::
|
||||||
|
|
||||||
|
git clone --branch=nightly https://github.com/myorganization/tutor-contrib-myplugin.git
|
||||||
|
pip install -e ./tutor-contrib-myplugin
|
||||||
|
|
||||||
|
Once Tutor Nightly is installed, you can run the usual ``tutor`` commands::
|
||||||
|
|
||||||
|
tutor dev launch
|
||||||
|
tutor dev run lms bash
|
||||||
|
# ... and so on
|
||||||
|
|
||||||
|
Upgrading to the latest version of Open edX
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
To pull the latest upstream changes, you should first upgrade Tutor Nightly::
|
||||||
|
|
||||||
|
cd ./tutor
|
||||||
|
git pull
|
||||||
|
|
||||||
|
Then, you will have to generate a more recent version of the nightly Docker images. Images for running Tutor Nightly are published daily to docker.io (see `here <https://hub.docker.com/r/overhangio/openedx/tags?page=1&ordering=last_updated&name=nightly>`__). You can fetch the latest images with::
|
||||||
|
|
||||||
|
tutor images pull all
|
||||||
|
|
||||||
|
Alternatively, you may want to build the images yourself. As usual, this is done with::
|
||||||
|
|
||||||
|
tutor images build all
|
||||||
|
|
||||||
|
However, these images include the application master branch at the point in time when the image was built. The Docker layer caching mechanism might cause the ``git clone`` step from the build to be skipped. In such cases, you will have to bypass the caching mechanism with::
|
||||||
|
|
||||||
|
tutor images build --no-cache all
|
||||||
|
|
||||||
|
Running Tutor Nightly alongside the latest release
|
||||||
|
--------------------------------------------------
|
||||||
|
|
||||||
|
When running Tutor Nightly, you usually do not want to override your existing Tutor installation. That's why a Tutor Nightly installation has the following differences from a regular release installation:
|
||||||
|
|
||||||
|
- The default Tutor project root is different in Tutor Nightly. By default it is set to ``~/.local/share/tutor-nightly`` on Linux (instead of ``~/.local/share/tutor``). To modify this location check the :ref:`corresponding documentation <tutor_root>`.
|
||||||
|
- The plugins root is set to ``~/.local/share/tutor-nightly-plugins`` on Linux (instead of ``~/.local/share/tutor-plugins``). This location may be modified by setting the ``TUTOR_PLUGINS_ROOT`` environment variable.
|
||||||
|
- The default docker-compose project name is set to ``tutor_nightly_local`` (instead of ``tutor_local``). This value may be modified by manually setting the ``LOCAL_PROJECT_NAME``.
|
||||||
|
|
||||||
|
Making changes to Tutor Nightly
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
In general pull requests should be open on the "master" branch of Tutor: the "master" branch is automatically merged on the "nightly" branch at every commit, such that changes made to Tutor releases find their way to Tutor Nightly as soon as they are merged. However, sometimes you want to make changes to Tutor Nightly exclusively, and not to the Tutor releases. This might be the case for instance when upgrading the running version of a third-party service (for instance: Elasticsearch, MySQL), or when the master branch requires specific changes. In that case, you should follow the instructions from the :ref:`contributing` section of the docs, with the following differences:
|
||||||
|
|
||||||
|
- Open your pull request on top of the "nightly" branch instead of "master".
|
||||||
|
- Add a description of your changes by creating a changelog entry with `make changelog-entry`, as in the master branch.
|
25
docs/tutorials/oldreleases.rst
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
Upgrading from older releases
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
Upgrading from v3+
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Just upgrade Tutor using your :ref:`favorite installation method <install>` and run launch again::
|
||||||
|
|
||||||
|
tutor local launch
|
||||||
|
|
||||||
|
Upgrading from v1 or v2
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Versions 1 and 2 of Tutor were organized differently: they relied on many different ``Makefile`` and ``make`` commands instead of a single ``tutor`` executable. To migrate from an earlier version, you should first stop your platform::
|
||||||
|
|
||||||
|
make stop
|
||||||
|
|
||||||
|
Then, install Tutor using one of the :ref:`installation methods <install>`. Then, create the Tutor project root and move your data::
|
||||||
|
|
||||||
|
mkdir -p "$(tutor config printroot)"
|
||||||
|
mv config.json data/ "$(tutor config printroot)"
|
||||||
|
|
||||||
|
Finally, launch your platform with::
|
||||||
|
|
||||||
|
tutor local launch
|
388
docs/tutorials/plugin.rst
Normal file
|
@ -0,0 +1,388 @@
|
||||||
|
.. _plugin_development_tutorial:
|
||||||
|
|
||||||
|
=======================
|
||||||
|
Creating a Tutor plugin
|
||||||
|
=======================
|
||||||
|
|
||||||
|
Tutor plugins are the officially recommended way of customizing the behaviour of Tutor. If Tutor does not do things the way you want, then your first reaction should *not* be to fork Tutor, but instead to figure out whether you can create a plugin that will allow you to achieve what you want.
|
||||||
|
|
||||||
|
You may be thinking that creating a plugin might be overkill for your use case. It's almost certainly not! The stable plugin API guarantees that your changes will keep working even after you upgrade from one major release to the next, with little to no extra work. Also, it allows you to distribute your changes to other users.
|
||||||
|
|
||||||
|
A plugin can be created either as a simple, single Python module (a ``*.py`` file) or as a full-blown Python package. Single Python modules are easier to write, while Python packages can be distributed more easily with ``pip install ...``. We'll start by writing our plugin as a single Python module.
|
||||||
|
|
||||||
|
Plugins work by making extensive use of the Tutor hooks API. The list of available hooks is available from the :ref:`hooks catalog <hooks_catalog>`. Developers who want to understand how hooks work should check the :ref:`hooks API <hooks_api>`.
|
||||||
|
|
||||||
|
Writing a plugin as a single Python module
|
||||||
|
==========================================
|
||||||
|
|
||||||
|
Getting started
|
||||||
|
---------------
|
||||||
|
|
||||||
|
In the following, we'll create a new plugin called "myplugin". We start by creating the plugins root folder::
|
||||||
|
|
||||||
|
$ mkdir -p "$(tutor plugins printroot)"
|
||||||
|
|
||||||
|
Then, create an empty "myplugin.py" file in this folder::
|
||||||
|
|
||||||
|
$ touch "$(tutor plugins printroot)/myplugin.py"
|
||||||
|
|
||||||
|
We can verify that the plugin is correctly detected by running::
|
||||||
|
|
||||||
|
$ tutor plugins list
|
||||||
|
...
|
||||||
|
myplugin (disabled) /home/yourusername/.local/share/tutor-plugins/myplugin.py
|
||||||
|
...
|
||||||
|
|
||||||
|
Our plugin is disabled, for now. To enable it, we run::
|
||||||
|
|
||||||
|
$ tutor plugins enable myplugin
|
||||||
|
Plugin myplugin enabled
|
||||||
|
Configuration saved to /home/yourusername/.local/share/tutor/config.yml
|
||||||
|
Environment generated in /home/yourusername/.local/share/tutor/env
|
||||||
|
|
||||||
|
At this point your environment was updated, but there would not be any change there... because the plugin does not do anything. So let's get started and make some changes.
|
||||||
|
|
||||||
|
Modifying existing files with patches
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
We'll start by modifying some of our Open edX settings files. It's a frequent requirement to modify the ``FEATURES`` setting from the LMS or the CMS in edx-platform. In the legacy native installation, this was done by modifying the ``lms.env.yml`` and ``cms.env.yml`` files. Here we'll modify the Python setting files that define the edx-platform configuration. To achieve that we'll make use of two concepts from the Tutor API: :ref:`patches` and :ref:`filters`.
|
||||||
|
|
||||||
|
If you have not already read :ref:`how_does_tutor_work` now would be a good time ☺️ Tutor uses templates to generate various files, such as settings, Dockerfiles, etc. These templates include ``{{ patch("patch-name") }}`` statements that allow plugins to insert arbitrary content in there. These patches are located at strategic locations. See :ref:`patches` for more information.
|
||||||
|
|
||||||
|
Let's say that we would like to limit access to our brand new Open edX platform. It is not ready for prime-time yet, so we want to prevent users from registering new accounts. There is a feature flag for that in the LMS: `FEATURES['ALLOW_PUBLIC_ACCOUNT_CREATION'] <https://edx.readthedocs.io/projects/edx-platform-technical/en/latest/featuretoggles.html#featuretoggle-FEATURES['ALLOW_PUBLIC_ACCOUNT_CREATION']>`__. By default this flag is set to a true value, enabling anyone to create an account. In the following we'll set it to false.
|
||||||
|
|
||||||
|
Add the following content to the ``myplugin.py`` file that you created earlier::
|
||||||
|
|
||||||
|
from tutor import hooks
|
||||||
|
|
||||||
|
hooks.Filters.ENV_PATCHES.add_item(
|
||||||
|
(
|
||||||
|
"openedx-lms-common-settings",
|
||||||
|
"FEATURES['ALLOW_PUBLIC_ACCOUNT_CREATION'] = False"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
Let's go over these changes one by one::
|
||||||
|
|
||||||
|
from tutor import hooks
|
||||||
|
|
||||||
|
This imports the ``hooks`` module from Tutor, which grants us access to ``hooks.Actions`` and ``hooks.Filters`` (among other things).
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
hooks.Filters.ENV_PATCHES.add_item(
|
||||||
|
(
|
||||||
|
<name>,
|
||||||
|
<content>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
This means "add ``<content>`` to the ``{{ patch("<name>") }}`` statement, thanks to the :py:data:`tutor.hooks.Filters.ENV_PATCHES` filter". In our case, we want to modify the LMS settings, both in production and development. The right patch for that is :patch:`openedx-lms-common-settings`. We add one item, which is a single Python-formatted line of code::
|
||||||
|
|
||||||
|
"FEATURES['ALLOW_PUBLIC_ACCOUNT_CREATION'] = False"
|
||||||
|
|
||||||
|
.. note:: Notice how "False" starts with a capital "F"? That's how booleans are created in Python.
|
||||||
|
|
||||||
|
Now, re-render your environment with::
|
||||||
|
|
||||||
|
$ tutor config save
|
||||||
|
|
||||||
|
You can check that the feature was added to your environment::
|
||||||
|
|
||||||
|
$ grep -r ALLOW_PUBLIC_ACCOUNT_CREATION "$(tutor config printroot)/env"
|
||||||
|
/home/yourusername/.local/share/tutor/env/apps/openedx/settings/lms/production.py:FEATURES['ALLOW_PUBLIC_ACCOUNT_CREATION'] = False
|
||||||
|
/home/yourusername/.local/share/tutor/env/apps/openedx/settings/lms/development.py:FEATURES['ALLOW_PUBLIC_ACCOUNT_CREATION'] = False
|
||||||
|
|
||||||
|
Your new settings will be taken into account by restarting your platform::
|
||||||
|
|
||||||
|
$ tutor local restart
|
||||||
|
|
||||||
|
Congratulations! You've created your first working plugin. As you can guess, you can add changes to other files by adding other similar patch statements to your plugin.
|
||||||
|
|
||||||
|
Modifying configuration
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
In the previous section you've learned how to add custom content to the Tutor templates. Now we'll see how to modify the Tutor configuration. Configuration settings can be specified in three ways:
|
||||||
|
|
||||||
|
1. "unique" settings that need to be generated or user-specified, and then preserved in config.yml: such settings do not have reasonable defaults for all users. Examples of such settings include passwords and secret keys, which should be different for every user.
|
||||||
|
2. "default" settings have static fallback values. They are only stored in config.yml when they are modified by users. Most settings belong in this category.
|
||||||
|
3. "override" settings modify configuration from Tutor core or from other plugins. These will be removed and restored to their default values when the plugin is disabled.
|
||||||
|
|
||||||
|
It is very strongly recommended to prefix unique and default settings with the plugin name, in all-caps, such that different plugins with the same configuration do not conflict with one another.
|
||||||
|
|
||||||
|
As an example, we'll make it possible to configure public account creation on the LMS via a Tutor setting. In the previous section we achieved that by creating a patch. Let's modify this patch::
|
||||||
|
|
||||||
|
hooks.Filters.ENV_PATCHES.add_item(
|
||||||
|
(
|
||||||
|
"openedx-lms-common-settings",
|
||||||
|
"FEATURES['ALLOW_PUBLIC_ACCOUNT_CREATION'] = {% if MYPLUGIN_PLATFORM_IS_PUBLIC %}True{% else %}False{% endif %}",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
This new patch makes use of the ``MYPLUGIN_PLATFORM_IS_PUBLIC`` configuration setting, which we need to create. Since this setting is specific to our plugin and should be stored in config.yml only when it's modified, we create it as a "default" setting. We do that with the :py:data:`tutor.hooks.Filters.CONFIG_DEFAULTS` filter::
|
||||||
|
|
||||||
|
hooks.Filters.CONFIG_DEFAULTS.add_item(
|
||||||
|
("MYPLUGIN_PLATFORM_IS_PUBLIC", False)
|
||||||
|
)
|
||||||
|
|
||||||
|
You can check that the new configuration setting was properly defined::
|
||||||
|
|
||||||
|
$ tutor config printvalue MYPLUGIN_PLATFORM_IS_PUBLIC
|
||||||
|
False
|
||||||
|
|
||||||
|
Now you can quickly toggle the public account creation feature by modifying the new setting::
|
||||||
|
|
||||||
|
$ tutor config save --set MYPLUGIN_PLATFORM_IS_PUBLIC=True
|
||||||
|
$ tutor local restart
|
||||||
|
|
||||||
|
|
||||||
|
Adding new templates
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
If you are adding an extra application to your Open edX platform, there is a good chance that you will create a new Docker image with a custom Dockerfile. This new application will have its own settings and build assets, for instance. This means that you need to add new templates to the Tutor environment. To do that, we will create a new subfolder in our plugins folder::
|
||||||
|
|
||||||
|
$ mkdir -p "$(tutor plugins printroot)/templates/myplugin"
|
||||||
|
|
||||||
|
Then we tell Tutor about this new template root thanks to the :py:data:`tutor.hooks.Filters.ENV_TEMPLATE_ROOTS` filter::
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
template_folder = os.path.join(os.path.dirname(__file__), "templates")
|
||||||
|
hooks.Filters.ENV_TEMPLATE_ROOTS.add_item(template_folder)
|
||||||
|
|
||||||
|
We create a "build" subfolder which will contain all assets to build our "myservice" image::
|
||||||
|
|
||||||
|
$ mkdir -p "$(tutor plugins printroot)/templates/myplugin/build/myservice"
|
||||||
|
|
||||||
|
Create the following Dockerfile in ``$(tutor plugins printroot)/templates/myplugin/build/myservice/Dockerfile``::
|
||||||
|
|
||||||
|
FROM docker.io/debian:bullseye-slim
|
||||||
|
CMD echo "what an awesome plugin!"
|
||||||
|
|
||||||
|
Tell Tutor that the "build" folder should be recursively rendered to ``env/plugins/myplugin/build`` with the :py:data:`tutor.hooks.Filters.ENV_TEMPLATE_TARGETS`::
|
||||||
|
|
||||||
|
hooks.Filters.ENV_TEMPLATE_TARGETS.add_item(
|
||||||
|
("myplugin/build", "plugins")
|
||||||
|
)
|
||||||
|
|
||||||
|
At this point you can verify that the Dockerfile template was properly rendered::
|
||||||
|
|
||||||
|
$ cat "$(tutor config printroot)/env/plugins/myplugin/build/myservice/Dockerfile"
|
||||||
|
FROM docker.io/debian:bullseye-slim
|
||||||
|
CMD echo "what an awesome plugin!"
|
||||||
|
|
||||||
|
We would like to build this image by running ``tutor images build myservice``. For that, we use the :py:data:`tutor.hooks.Filters.IMAGES_BUILD` filter::
|
||||||
|
|
||||||
|
hooks.Filters.IMAGES_BUILD.add_item(
|
||||||
|
(
|
||||||
|
"myservice", # same name that will be passed to the `build` command
|
||||||
|
("plugins", "myplugin", "build", "myservice"), # path to the Dockerfile folder
|
||||||
|
"myservice:latest", # Docker image tag
|
||||||
|
(), # custom build arguments that will be passed to the `docker build` command
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
You can now build your image::
|
||||||
|
|
||||||
|
$ tutor images build myservice
|
||||||
|
Building image myservice:latest
|
||||||
|
docker build -t myservice:latest /home/yourusername/.local/share/tutor/env/plugins/myplugin/build/myservice
|
||||||
|
...
|
||||||
|
Successfully tagged myservice:latest
|
||||||
|
|
||||||
|
Similarly, to push/pull your image to/from a Docker registry, implement the :py:data:`tutor.hooks.Filters.IMAGES_PUSH` and :py:data:`tutor.hooks.Filters.IMAGES_PULL` filters::
|
||||||
|
|
||||||
|
hooks.Filters.IMAGES_PUSH.add_item(("myservice", "myservice:latest"))
|
||||||
|
hooks.Filters.IMAGES_PULL.add_item(("myservice", "myservice:latest"))
|
||||||
|
|
||||||
|
You can now run::
|
||||||
|
|
||||||
|
$ tutor images push myservice
|
||||||
|
$ tutor images pull myservice
|
||||||
|
|
||||||
|
The "myservice" container can be automatically run in local installations by implementing the :patch:`local-docker-compose-services` patch::
|
||||||
|
|
||||||
|
hooks.Filters.ENV_PATCHES.add_item(
|
||||||
|
(
|
||||||
|
"local-docker-compose-services",
|
||||||
|
"""
|
||||||
|
myservice:
|
||||||
|
image: myservice:latest
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
You can now run the "myservice" container which will execute the ``CMD`` statement we wrote in the Dockerfile::
|
||||||
|
|
||||||
|
$ tutor config save && tutor local run myservice
|
||||||
|
...
|
||||||
|
Creating tutor_local_myservice_run ... done
|
||||||
|
what an awesome plugin!
|
||||||
|
|
||||||
|
Declaring initialisation tasks
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Services often need to run specific tasks before they can be started. For instance, the LMS and the CMS need to apply database migrations. These commands are written in shell scripts that are executed whenever we run ``launch``. We call these scripts "init tasks". To add a new local initialisation task, we must first add the corresponding service to the ``docker-compose-jobs.yml`` file by implementing the :patch:`local-docker-compose-jobs-services` patch::
|
||||||
|
|
||||||
|
hooks.Filters.ENV_PATCHES.add_item(
|
||||||
|
(
|
||||||
|
"local-docker-compose-jobs-services",
|
||||||
|
"""
|
||||||
|
myservice-job:
|
||||||
|
image: myservice:latest
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
The patch above defined the "myservice-job" container which will run our initialisation task. Make sure that it is applied by updating your environment::
|
||||||
|
|
||||||
|
$ tutor config save
|
||||||
|
|
||||||
|
Next, we create an initialisation task by adding an item to the :py:data:`tutor.hooks.Filters.CLI_DO_INIT_TASKS` filter::
|
||||||
|
|
||||||
|
|
||||||
|
hooks.Filters.CLI_DO_INIT_TASKS.add_item(
|
||||||
|
(
|
||||||
|
"myservice",
|
||||||
|
"""
|
||||||
|
echo "++++++ initialising my plugin..."
|
||||||
|
echo "++++++ done!"
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
Run this initialisation task with::
|
||||||
|
|
||||||
|
$ tutor local do init --limit=myplugin
|
||||||
|
...
|
||||||
|
Running init task: myplugin/tasks/init.sh
|
||||||
|
...
|
||||||
|
Creating tutor_local_myservice-job_run ... done
|
||||||
|
++++++ initialising my plugin...
|
||||||
|
++++++ done!
|
||||||
|
All services initialised.
|
||||||
|
|
||||||
|
Tailoring services for development
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
When you add services via :patch:`local-docker-compose-services`, those services will be available both in local production mode (``tutor local start``) and local development mode (``tutor dev start``). Sometimes, you may wish to further customize a service in ways that would not be suitable for production, but could be helpful for developers. To add in such customizations, implement the :patch:`local-docker-compose-dev-services` patch. For example, we can enable breakpoint debugging on the "myservice" development container by enabling the ``stdin_open`` and ``tty`` options::
|
||||||
|
|
||||||
|
hooks.Filters.ENV_PATCHES.add_item(
|
||||||
|
(
|
||||||
|
"local-docker-compose-dev-services",
|
||||||
|
"""
|
||||||
|
myservice:
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
Final result
|
||||||
|
------------
|
||||||
|
|
||||||
|
Eventually, our plugin is composed of the following files, all stored within the folder indicated by ``tutor plugins printroot`` (on Linux: ``~/.local/share/tutor-plugins``).
|
||||||
|
|
||||||
|
``myplugin.py``
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
import os
|
||||||
|
from tutor import hooks
|
||||||
|
|
||||||
|
# Define extra folder to look for templates and render the content of the "build" folder
|
||||||
|
template_folder = os.path.join(os.path.dirname(__file__), "templates")
|
||||||
|
hooks.Filters.ENV_TEMPLATE_ROOTS.add_item(template_folder)
|
||||||
|
hooks.Filters.ENV_TEMPLATE_TARGETS.add_item(
|
||||||
|
("myplugin/build", "plugins")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Define patches
|
||||||
|
hooks.Filters.ENV_PATCHES.add_item(
|
||||||
|
(
|
||||||
|
"openedx-lms-common-settings",
|
||||||
|
"FEATURES['ALLOW_PUBLIC_ACCOUNT_CREATION'] = False"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
hooks.Filters.ENV_PATCHES.add_item(
|
||||||
|
(
|
||||||
|
"openedx-lms-common-settings",
|
||||||
|
"FEATURES['ALLOW_PUBLIC_ACCOUNT_CREATION'] = {% if MYPLUGIN_PLATFORM_IS_PUBLIC %}True{% else %}False{% endif %}",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
hooks.Filters.ENV_PATCHES.add_item(
|
||||||
|
(
|
||||||
|
"local-docker-compose-services",
|
||||||
|
"""
|
||||||
|
myservice:
|
||||||
|
image: myservice:latest
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
hooks.Filters.ENV_PATCHES.add_item(
|
||||||
|
(
|
||||||
|
"local-docker-compose-jobs-services",
|
||||||
|
"""
|
||||||
|
myservice-job:
|
||||||
|
image: myservice:latest
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
hooks.Filters.ENV_PATCHES.add_item(
|
||||||
|
(
|
||||||
|
"local-docker-compose-dev-services",
|
||||||
|
"""
|
||||||
|
myservice:
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Modify configuration
|
||||||
|
hooks.Filters.CONFIG_DEFAULTS.add_item(
|
||||||
|
("MYPLUGIN_PLATFORM_IS_PUBLIC", False)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Define tasks
|
||||||
|
hooks.Filters.IMAGES_BUILD.add_item(
|
||||||
|
(
|
||||||
|
"myservice",
|
||||||
|
("plugins", "myplugin", "build", "myservice"),
|
||||||
|
"myservice:latest",
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
hooks.Filters.IMAGES_PUSH.add_item(("myservice", "myservice:latest"))
|
||||||
|
hooks.Filters.IMAGES_PULL.add_item(("myservice", "myservice:latest"))
|
||||||
|
hooks.Filters.CLI_DO_INIT_TASKS.add_item(
|
||||||
|
(
|
||||||
|
"myservice",
|
||||||
|
"""
|
||||||
|
echo "++++++ initialising my plugin..."
|
||||||
|
echo "++++++ done!"
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
``templates/myplugin/build/myservice/Dockerfile``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
FROM docker.io/debian:bullseye-slim
|
||||||
|
CMD echo "what an awesome plugin!"
|
||||||
|
|
||||||
|
``templates/myplugin/tasks/init.sh``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
echo "initialising my plugin..."
|
||||||
|
echo "done!"
|
||||||
|
|
||||||
|
Distributing a plugin as a Python package
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
Storing plugins as simple Python modules has the merit of simplicity, but it makes it more difficult to distribute them, either to other users or to remote servers. When your plugin grows more complex, it is recommended to migrate it to a Python package. You should create a package using the `plugin cookiecutter <https://github.com/overhangio/cookiecutter-tutor-plugin>`__. Packages are automatically detected as plugins thanks to the "tutor.plugin.v1" `entry point <https://setuptools.pypa.io/en/latest/userguide/entry_point.html#advertising-behavior>`__. The modules indicated by this entry point will be automatically imported when the plugins are enabled. See the cookiecutter project `README <https://github.com/overhangio/cookiecutter-tutor-plugin/blob/master/README.rst>`__ for more information.
|
95
docs/tutorials/podman.rst
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
Running Tutor with Podman
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
`Podman <https://podman.io/>`_ is a fully featured container engine that is daemonless. It provides a Docker CLI comparable command line that makes it pretty easy for people transitioning over from Docker.
|
||||||
|
|
||||||
|
Simply put, this means that you can do something like: ``alias docker=podman`` and everything will run and behave pretty much as expected.
|
||||||
|
|
||||||
|
As of podman v3.0.0, podman now officially supports ``docker-compose`` via a shim service. This means that you now have the option of running Tutor with Podman, instead of the native Docker tools.
|
||||||
|
|
||||||
|
This has some practical advantages: it does not require a running Docker daemon, and it enables you to run and build Docker images without depending on any system component running as ``root``.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
You should not attempt to run Tutor with Podman on a system that already has native ``docker`` installed. If you want to switch to ``podman`` using the aliases described here, you should uninstall (or at least stop) the native Docker daemon first.
|
||||||
|
|
||||||
|
|
||||||
|
Enabling Podman
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Podman is supported on a variety of development platforms, see the `installation instructions <https://podman.io/getting-started/installation>`_ for details.
|
||||||
|
|
||||||
|
Once you have installed Podman and its dependencies on the platform of your choice, you'll need to make sure that the ``podman`` binary, usually installed as ``/usr/bin/podman``, is aliased to ``docker``.
|
||||||
|
|
||||||
|
On some CentOS and Fedora releases, you can install a package named ``podman-docker`` to do this for you, but on other platforms, you'll need to take of this yourself.
|
||||||
|
|
||||||
|
- To alias ``podman`` to ``docker``, you can simply run this command::
|
||||||
|
|
||||||
|
$ alias docker=podman
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Running this command only makes a temporary alias. For a more permanent alias, you should place that command in your ``bashrc`` or equivalent file.
|
||||||
|
|
||||||
|
Getting docker-compose to work with Podman
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
To allow ``podman`` to work with ``docker-compose``, you'll need to enable a podman socket which pretends to be ``docker``.
|
||||||
|
|
||||||
|
For rootless containers, this requires you to start the ``podman.service`` as a regular user and set the ``DOCKER_HOST`` environment variable. This can be done as follows::
|
||||||
|
|
||||||
|
# To start the podman service
|
||||||
|
$ systemctl --user start podman.service
|
||||||
|
|
||||||
|
# To set the DOCKER_HOST environment variable
|
||||||
|
$ export DOCKER_HOST="unix://$XDG_RUNTIME_DIR/podman/podman.sock"
|
||||||
|
|
||||||
|
If you are running in rootless mode, ``tutor local`` expects a web proxy to be running on port ``80`` or port ``443``. For instructions on how to configure a web proxy, view `this tutorial <https://docs.tutor.edly.io/tutorials/proxy.html>`_.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
As with the previous ``alias`` command, if you'd like to make the ``DOCKER_HOST`` variable permanent, you should put the entire export command in your ``bashrc`` or equivalent file.
|
||||||
|
|
||||||
|
Fixing SELinux Errors
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
Disabling ``SELinux`` or setting it to *permissive mode* on your system is **highly discouraged and will render your system vulnerable.**
|
||||||
|
|
||||||
|
If your system has ``SELinux`` working in enforcing mode, chances are that the SELinux context of the tutor root directory won't be set correctly. This will cause read issues because containers will not be able read files from volumes due to a context mismatch.
|
||||||
|
|
||||||
|
Errors stemming from this will look as follows in the ``sealert`` program::
|
||||||
|
|
||||||
|
"SELinux is preventing caddy from read access on the file Caddyfile."
|
||||||
|
"SELinux is preventing celery from read access on the directory cms."
|
||||||
|
"SELinux is preventing mysqld from add_name access on the directory is_writable."
|
||||||
|
|
||||||
|
You can verify the context mismatch by running::
|
||||||
|
|
||||||
|
$ ls -lZ $(tutor config printroot)
|
||||||
|
|
||||||
|
You'll most likely see something that looks like this::
|
||||||
|
|
||||||
|
-rw-r--r--. 1 tutor tutor unconfined_u:object_r:data_home_t:s0 2145 Jan 6 20:13 config.yml
|
||||||
|
drwxr-xr-x. 2 tutor tutor unconfined_u:object_r:data_home_t:s0 6 Jan 6 20:14 data
|
||||||
|
drwxr-xr-x. 8 tutor tutor unconfined_u:object_r:data_home_t:s0 121 Jan 6 20:14 env
|
||||||
|
|
||||||
|
We're interested in the ``unconfined_u:object_r:data_home_t:s0`` part of that output.
|
||||||
|
|
||||||
|
Notice how the third part of that says ``data_home_t``?
|
||||||
|
|
||||||
|
That's the context type. For tutor to work, we need that part to be set to ``container_file_t``.
|
||||||
|
|
||||||
|
This can be done as follows::
|
||||||
|
|
||||||
|
# Set the SELinux type of the tutor root directory and all of it's subdirectories to `container_file_t`
|
||||||
|
$ sudo semanage fcontext -a -t container_file_t "$(tutor config printroot)(/.*)?"
|
||||||
|
|
||||||
|
# Apply the newly set security context to the directories
|
||||||
|
$ sudo restorecon -RF $(tutor config printroot)
|
||||||
|
|
||||||
|
Running these two commands in a sequence should fix the SELinux errors.
|
||||||
|
|
||||||
|
Verifying your environment
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Once you've set everything up as described, you should be able to run ``docker version`` and ``docker-compose --help`` and get a valid output.
|
||||||
|
|
||||||
|
After that, you should be able to use ``tutor local``, and other commands as if you had installed the native Docker tools.
|
19
docs/tutorials/portainer.rst
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
.. _portainer:
|
||||||
|
|
||||||
|
Running a Docker container web UI with `Portainer <https://portainer.io/>`__
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Portainer is a web UI for managing docker containers. It lets you view your entire Open edX platform at a glace. Try it! It's really cool::
|
||||||
|
|
||||||
|
docker run --rm \
|
||||||
|
--volume=/var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
--volume=/tmp/portainer:/data \
|
||||||
|
-p 9000:9000 \
|
||||||
|
portainer/portainer-ce:latest --bind=:9000
|
||||||
|
|
||||||
|
You can then view the portainer UI at `http://localhost:9000 <http://localhost:9000>`_. You will be asked to define a password for the admin user. Then, select a "Local environment" to work on; hit "Connect" and select the "local" group to view all running containers.
|
||||||
|
|
||||||
|
.. image:: ../img/portainer.png
|
||||||
|
:alt: Portainer demo
|
||||||
|
|
||||||
|
Among many other things, you'll be able to view the logs for each container, which is really useful.
|
32
docs/tutorials/proxy.rst
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
.. _web_proxy:
|
||||||
|
|
||||||
|
Running Open edX behind a web proxy
|
||||||
|
===================================
|
||||||
|
|
||||||
|
In a vanilla deployment of Open edX with Tutor, a web proxy is launched to process incoming web requests. This web proxy is an instance of `Caddy <https://caddyserver.com/>`__ running inside a Docker container. This Docker container listens to ports 80 and 443 on the host.
|
||||||
|
|
||||||
|
Quite often, there is already a web proxy running on the host, and this web proxy also listens to ports 80 and 443. In such a configuration, the Caddy container will not be able to start out of the box. So you should make small changes to the Tutor configuration by running::
|
||||||
|
|
||||||
|
tutor config save --set ENABLE_WEB_PROXY=false --set CADDY_HTTP_PORT=81
|
||||||
|
|
||||||
|
With these changes, Tutor will no longer listen to ports 80 and 443 on the host. In this configuration, the Caddy container will only listen to port 81 on the host. Web requests will follow this path::
|
||||||
|
|
||||||
|
Client → Web proxy (http(s)://yourhost) → Caddy (0.0.0.0:81) → uwsgi (LMS/CMS/...)
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
In this setup, the Caddy HTTP port (81) will be exposed to the world. Make sure to configure your server firewall to block unwanted connections to the Caddy container. Alternatively, you can configure the Caddy container to accept only local connections::
|
||||||
|
|
||||||
|
tutor config save --set ENABLE_WEB_PROXY=false --set CADDY_HTTP_PORT=127.0.0.1:81
|
||||||
|
|
||||||
|
It is then your responsibility to configure the web proxy on the host. There are too many use cases and proxy vendors, so Tutor does not provide configuration files that will work for everyone. You should configure your web proxy to:
|
||||||
|
|
||||||
|
- Capture traffic for the following hostnames: LMS_HOST, PREVIEW_LMS_HOST, CMS_HOST, as well as any additional host exposed by your plugins (MFE_HOST, ECOMMERCE_HOST, etc.). See each plugin documentation for more information.
|
||||||
|
- If SSL/TLS is enabled:
|
||||||
|
- Perform SSL/TLS termination using your own certificates.
|
||||||
|
- Forward http traffic to https.
|
||||||
|
- Set the following headers appropriately: ``X-Forwarded-Proto``, ``X-Forwarded-Port``.
|
||||||
|
- Forward all traffic to ``localhost:81`` (or whatever port indicated by CADDY_HTTP_PORT, see above).
|
||||||
|
- If possible, add support for `HTTP/3 <https://en.wikipedia.org/wiki/HTTP/3>`__, which considerably improves performance for Open edX (see `this comment <https://github.com/overhangio/tutor/issues/845#issuecomment-1566964289>`__).
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
If you want to run Open edX at ``https://...`` urls (as you probably do in production) it is *crucial* that the ``ENABLE_HTTPS`` flag is set to ``true``. If not, the web services will be configured to run at ``http://...`` URLs, and all sorts of trouble will happen. Therefore, make sure to continue answering ``y`` ("yes") to the quickstart dialogue question "Activate SSL/TLS certificates for HTTPS access?".
|
85
docs/tutorials/scale.rst
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
.. _scale:
|
||||||
|
|
||||||
|
Running Open edX at scale
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Does Open edX scale? This is the $10⁶ question when it comes to Tutor and Open edX deployments. The short answer is "yes". The longer answer is also "yes", but the details will very much depend on what we mean by "scaling".
|
||||||
|
|
||||||
|
Depending on the context, "scaling" can imply different things:
|
||||||
|
|
||||||
|
1. `Vertical scaling <https://en.wikipedia.org/wiki/Scalability#VERTICAL-SCALING>`__: increasing platform capacity by allocating more resources to a single server.
|
||||||
|
2. `Horizontal scaling <https://en.wikipedia.org/wiki/Scalability#HORIZONTAL-SCALING>`__: the ability to serve an infinitely increasing number of users with consistent performance and linear costs.
|
||||||
|
3. `High availability (HA) <https://en.wikipedia.org/wiki/High_availability>`__: the ability of the platform to remain fully functional despite one or more components being unavailable.
|
||||||
|
|
||||||
|
All of these can be achieved with Tutor and Open edX, but the method to attain either differs greatly. First of all, the range of available solutions will depend on which deployment target is used. Tutor supports installations of Open edX on a single server with the :ref:`"local" <local>` deployment target, where Docker containers are orchestrated by docker-compose. On a single server, by definition, the server is a single point of failure (`SPOF <https://en.wikipedia.org/wiki/Single_point_of_failure>`__). Thus, high availability is out of the question with a single server. To achieve high availability, it is necessary to deploy to a cluster of multiple servers. But while docker-compose is a great tool for managing single-server deployments, it is simply inappropriate for deploying to a cluster. Tutor also supports deploying to a Kubernetes cluster (see :ref:`k8s`). This is the recommended solution to deploy Open edX "at scale".
|
||||||
|
|
||||||
|
Scaling with a single server
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
Options are limited when it comes to scaling an Open edX platform deployed on a single-server. High availability is out of the question and the number of users that your platform can serve simultaneously will be limited by the server capacity.
|
||||||
|
|
||||||
|
Fortunately, Open edX was designed to run at scale -- most notably at `edX.org <edx.org>`__, but also on large national education platforms. Thus, performance will not be limited by the backend software, but only by the hardware.
|
||||||
|
|
||||||
|
Increasing web server capacity
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
As the server CPU and memory are increased, the request throughput can be increased by adjusting the number of uWSGI workers (see :ref:`configuration docs <openedx_configuration>`). By default, the "lms" and "cms" containers each spawn 2 uWSGI workers. The number of workers should be increased if you observe an increase in the latency of user requests but CPU usage remains below 100%. To increase the number of workers for the LMS and the CMS, run for example::
|
||||||
|
|
||||||
|
tutor config save \
|
||||||
|
--set OPENEDX_LMS_UWSGI_WORKERS=8 \
|
||||||
|
--set OPENEDX_CMS_UWSGI_WORKERS=4
|
||||||
|
tutor local restart lms cms
|
||||||
|
|
||||||
|
The right values will very much depend on your server's available memory and CPU performance, as well as the maximum number of simultaneous users who use your platform. As an example data point, it was reported that a large Open edX platform can serve up to 500k unique users per week on a virtual server with 8 vCPU and 16 GB memory.
|
||||||
|
|
||||||
|
Offloading data storage
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Aside from web workers, the most resource-intensive services are in the data persistence layer. They are, by decreasing resource usage:
|
||||||
|
|
||||||
|
- `Elasticsearch <https://www.elastic.co/elasticsearch/>`__: indexing of course contents and forum topics, mostly for search. Elasticsearch is never a source of truth in Open edX, and the data can thus be trashed and re-created safely.
|
||||||
|
- `MySQL <https://www.mysql.com>`__: structured, consistent data storage which is the default destination of all data.
|
||||||
|
- `MongoDB <https://www.mongodb.com>`__: structured storage of course data.
|
||||||
|
- `Redis <https://redis.io/>`__: caching and asynchronous task management.
|
||||||
|
- `MinIO <https://min.io>`__: S3-like object storage for user-uploaded files, which is enabled by the `tutor-minio <https://github.com/overhangio/tutor-minio>`__ plugin. It is possible to replace MinIO by direct filesystem storage (the default), but scaling will then become much more difficult down the road.
|
||||||
|
|
||||||
|
When attempting to scale a single-server deployment, we recommend starting by offloading some of these stateful data storage components, in the same order of priority. There are multiple benefits:
|
||||||
|
|
||||||
|
1. It will free up some resources both for the web workers and the data storage components.
|
||||||
|
2. It is the first step towards horizontal scaling of the web workers.
|
||||||
|
3. It becomes possible to either install every component as a separate service or rely on 3rd-party SaaS with high availability.
|
||||||
|
|
||||||
|
Moving each of the data storage components is a fairly straightforward process, although details vary for every component. For instance, for the MySQL database, start by disabling the locally running MySQL instance::
|
||||||
|
|
||||||
|
tutor config save --set RUN_MYSQL=false
|
||||||
|
|
||||||
|
Then, migrate the data located at ``$(tutor config printroot)/data/mysql`` to the new MySQL instance. Configure the Open edX platform to point at the new database::
|
||||||
|
|
||||||
|
tutor config save \
|
||||||
|
--set MYSQL_HOST=yourdb.com \
|
||||||
|
--set MYSQL_PORT=3306 \
|
||||||
|
--set MYSQL_ROOT_USERNAME=root \
|
||||||
|
--set MYSQL_ROOT_PASSWORD=p4ssw0rd
|
||||||
|
|
||||||
|
The changes will be taken into account the next time the platform is restarted.
|
||||||
|
|
||||||
|
Beware that moving the data components to dedicated servers has the potential of creating new single points of failure (`SPOF <https://en.wikipedia.org/wiki/Single_point_of_failure>`__). To avoid this situation, each component should be installed as a highly available service (or as a highly available SaaS).
|
||||||
|
|
||||||
|
Scaling with multiple servers
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
Horizontally scaling web services
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
As the number of users of a web platform increases, they put increased pressure on the web workers that respond to their requests. Thus, in most cases, web worker performance is the first bottleneck that system administrators have to face when their service becomes more popular. Initially, any given Kubernetes-based Tutor platform ships with one replica for each deployment. To increase (or reduce) the number of replicas for any given service, run ``tutor k8s scale <name> <number of replicas>``. Behind the scenes, this command will trigger a ``kubectl scale --replicas=...`` command that will seamlessly increase the number of pods for that deployment.
|
||||||
|
|
||||||
|
In Open edX multiple web services are exposed to the outside world. The ones that usually receive the most traffic are, in decreasing order, the LMS, the CMS, and the forum (assuming the `tutor-forum <https://github.com/overhangio/tutor-forum>`__ plugin was enabled). As an example, all three deployment replicas can be scaled by running::
|
||||||
|
|
||||||
|
tutor k8s scale lms 8
|
||||||
|
tutor k8s scale cms 4
|
||||||
|
tutor k8s scale forum 2
|
||||||
|
|
||||||
|
Highly-available architecture, autoscaling, ...
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
There is only so much that Tutor can do for you, and scaling some components falls beyond the scope of Tutor. For instance, it is your responsibility to make sure that your Kubernetes cluster has a `highly available control plane <https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/high-availability/>`__ and `topology <https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/ha-topology/>`__. Also, it is possible to achieve `autoscaling <https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/>`__; but it is your responsibility to setup latency metrics collection and to configure the scaling policies.
|
57
docs/tutorials/theming.rst
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
.. _theming:
|
||||||
|
|
||||||
|
Changing the appearance of Open edX
|
||||||
|
===================================
|
||||||
|
|
||||||
|
Installing a custom theme
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Comprehensive theming is enabled by default, but only the default theme is compiled. `Indigo <https://github.com/overhangio/indigo>`__ is a better, ready-to-run theme that you can start using today.
|
||||||
|
|
||||||
|
To compile your own theme, add it to the ``env/build/openedx/themes/`` folder::
|
||||||
|
|
||||||
|
git clone https://github.com/me/myopenedxtheme.git \
|
||||||
|
"$(tutor config printroot)/env/build/openedx/themes/myopenedxtheme"
|
||||||
|
|
||||||
|
The ``themes`` folder should have the following structure::
|
||||||
|
|
||||||
|
openedx/themes/
|
||||||
|
mycustomtheme1/
|
||||||
|
cms/
|
||||||
|
...
|
||||||
|
lms/
|
||||||
|
...
|
||||||
|
mycustomtheme2/
|
||||||
|
...
|
||||||
|
|
||||||
|
Then you must rebuild the openedx Docker image::
|
||||||
|
|
||||||
|
tutor images build openedx
|
||||||
|
|
||||||
|
Finally, you should enable your theme with the :ref:`settheme command <settheme>`.
|
||||||
|
|
||||||
|
.. _theme_development:
|
||||||
|
|
||||||
|
Developing a new theme
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
With Tutor, it's pretty easy to develop your own themes. Start by placing your files inside the ``env/build/openedx/themes`` directory. For instance, you could start from the ``edx.org`` theme present inside the ``edx-platform`` repository::
|
||||||
|
|
||||||
|
cp -r /path/to/edx-platform/themes/edx.org "$(tutor config printroot)/env/build/openedx/themes/"
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
You should not create a soft link here. If you do, it will trigger a ``Theme not found in any of the themes dirs`` error. This is because soft links are not properly resolved from inside docker containers.
|
||||||
|
|
||||||
|
Then, run a local webserver::
|
||||||
|
|
||||||
|
tutor dev start lms
|
||||||
|
|
||||||
|
The LMS can then be accessed at http://local.edly.io:8000. You will then have to :ref:`enable that theme <settheme>`::
|
||||||
|
|
||||||
|
tutor dev do settheme mythemename
|
||||||
|
|
||||||
|
Watch the themes folders for changes (in a different terminal)::
|
||||||
|
|
||||||
|
tutor dev run watchthemes
|
||||||
|
|
||||||
|
Make changes to some of the files inside the theme directory: the theme assets should be automatically recompiled and visible at http://local.edly.io:8000.
|
|
@ -3,27 +3,27 @@
|
||||||
What next?
|
What next?
|
||||||
==========
|
==========
|
||||||
|
|
||||||
You have gone through the :ref:`Quickstart installation <quickstart>`: at this point you should have a running Open edX platform. If you don't, please follow the instructions from the :ref:`Troubleshooting <troubleshooting>` section.
|
You have gone through the :ref:`Quickstart installation <quickstart>`: at this point, you should have a running Open edX platform. If you don't, please follow the instructions from the :ref:`Troubleshooting <troubleshooting>` section.
|
||||||
|
|
||||||
Logging-in as administrator
|
Logging-in as administrator
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
Out of the box, Tutor does not create any user for you. You will want to create a user yourself with staff and administrator privileges in order to access the studio. There is a :ref:`simple command for that <createuser>`.
|
Out of the box, Tutor does not create any user for you. You will want to create a user yourself with staff and administrator privileges to access the studio. There is a :ref:`simple command for that <createuser>`.
|
||||||
|
|
||||||
Importing a demo course
|
Importing a demo course
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
To get a glimpse of the possibilities of Open edX, we recommend you import the `official demo test course <https://github.com/edx/demo-test-course>`__. Tutor provides a :ref:`simple command for that <democourse>`.
|
To get a glimpse of the possibilities of Open edX, we recommend you import the `official demo test course <https://github.com/openedx/edx-demo-course>`__. Tutor provides a :ref:`simple command for that <democourse>`.
|
||||||
|
|
||||||
Making Open edX look better
|
Making Open edX look better
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
Tutor makes it easy to :ref:`develop <theming>` and :ref:`install <settheme>` your own themes. We also provide `Indigo <https://github.com/overhangio/indigo>`__: a free, customizable theme that you can install today.
|
Tutor makes it easy to :ref:`install <theming>` and :ref:`develop <theme_development>` your own themes. We also provide `Indigo <https://github.com/overhangio/indigo>`__: a free, customizable theme that you can install today.
|
||||||
|
|
||||||
Adding features
|
Adding features
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Check out the Tutor :ref:`plugins <plugins>`, :ref:`extra features <extra>` and :ref:`configuration/customization options <configuration_customisation>`.
|
Check out the Tutor :ref:`plugins <plugins>` and :ref:`configuration/customization options <configuration_customisation>`.
|
||||||
|
|
||||||
Hacking into Open edX
|
Hacking into Open edX
|
||||||
---------------------
|
---------------------
|
||||||
|
@ -35,7 +35,12 @@ Deploying to Kubernetes
|
||||||
|
|
||||||
Yes, Tutor comes with Kubernetes deployment support :ref:`out of the box <k8s>`.
|
Yes, Tutor comes with Kubernetes deployment support :ref:`out of the box <k8s>`.
|
||||||
|
|
||||||
|
Gathering insights and analytics about Open edX
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
Check out `Cairn <https://github.com/overhangio/tutor-cairn>`__, the next-generation analytics solution for Open edX.
|
||||||
|
|
||||||
Meeting the community
|
Meeting the community
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
Ask your questions and chat with the Tutor community on the official community forums: https://discuss.overhang.io
|
Ask your questions and chat with the Tutor community on the official Open edX community forum: https://discuss.openedx.org
|
||||||
|
|
2
pyproject.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools", "wheel"]
|
|
@ -1,13 +1,8 @@
|
||||||
appdirs
|
appdirs
|
||||||
click>=7.0,<8.0
|
click>=8.0
|
||||||
click_repl
|
jinja2>=2.10
|
||||||
mypy
|
|
||||||
pycryptodome
|
|
||||||
jinja2>=2.9,<3.0
|
|
||||||
kubernetes
|
kubernetes
|
||||||
pyyaml>=4.2b1
|
mypy
|
||||||
|
pycryptodome>=3.17.0
|
||||||
# Add constraints until we are compatible with click 8.0
|
pyyaml>=6.0
|
||||||
markupsafe<2.0
|
typing-extensions>=4.4.0
|
||||||
# Installing urllib3==1.26.0 causes compatibility errors with requests==2.24.0
|
|
||||||
urllib3<1.26.0
|
|
||||||
|
|
|
@ -1,87 +1,74 @@
|
||||||
#
|
#
|
||||||
# This file is autogenerated by pip-compile
|
# This file is autogenerated by pip-compile with Python 3.8
|
||||||
# To update, run:
|
# by the following command:
|
||||||
#
|
#
|
||||||
# pip-compile requirements/base.in
|
# pip-compile requirements/base.in
|
||||||
#
|
#
|
||||||
appdirs==1.4.4
|
appdirs==1.4.4
|
||||||
# via -r requirements/base.in
|
# via -r requirements/base.in
|
||||||
cachetools==4.2.2
|
cachetools==5.3.2
|
||||||
# via google-auth
|
# via google-auth
|
||||||
certifi==2020.12.5
|
certifi==2023.7.22
|
||||||
# via
|
# via
|
||||||
# kubernetes
|
# kubernetes
|
||||||
# requests
|
# requests
|
||||||
chardet==4.0.0
|
charset-normalizer==3.3.1
|
||||||
# via requests
|
# via requests
|
||||||
click-repl==0.1.6
|
click==8.1.7
|
||||||
# via -r requirements/base.in
|
# via -r requirements/base.in
|
||||||
click==7.1.2
|
google-auth==2.23.3
|
||||||
# via
|
|
||||||
# -r requirements/base.in
|
|
||||||
# click-repl
|
|
||||||
google-auth==1.30.0
|
|
||||||
# via kubernetes
|
# via kubernetes
|
||||||
idna==2.10
|
idna==3.4
|
||||||
# via requests
|
# via requests
|
||||||
jinja2==2.11.3
|
jinja2==3.1.2
|
||||||
# via -r requirements/base.in
|
# via -r requirements/base.in
|
||||||
kubernetes==12.0.1
|
kubernetes==28.1.0
|
||||||
# via -r requirements/base.in
|
# via -r requirements/base.in
|
||||||
markupsafe==1.1.1
|
markupsafe==2.1.3
|
||||||
# via
|
# via jinja2
|
||||||
# -r requirements/base.in
|
mypy==1.6.1
|
||||||
# jinja2
|
# via -r requirements/base.in
|
||||||
mypy-extensions==0.4.3
|
mypy-extensions==1.0.0
|
||||||
# via mypy
|
# via mypy
|
||||||
mypy==0.812
|
oauthlib==3.2.2
|
||||||
# via -r requirements/base.in
|
|
||||||
oauthlib==3.1.0
|
|
||||||
# via requests-oauthlib
|
|
||||||
prompt-toolkit==3.0.18
|
|
||||||
# via click-repl
|
|
||||||
pyasn1-modules==0.2.8
|
|
||||||
# via google-auth
|
|
||||||
pyasn1==0.4.8
|
|
||||||
# via
|
|
||||||
# pyasn1-modules
|
|
||||||
# rsa
|
|
||||||
pycryptodome==3.10.1
|
|
||||||
# via -r requirements/base.in
|
|
||||||
python-dateutil==2.8.1
|
|
||||||
# via kubernetes
|
|
||||||
pyyaml==5.4.1
|
|
||||||
# via
|
|
||||||
# -r requirements/base.in
|
|
||||||
# kubernetes
|
|
||||||
requests-oauthlib==1.3.0
|
|
||||||
# via kubernetes
|
|
||||||
requests==2.25.1
|
|
||||||
# via
|
# via
|
||||||
# kubernetes
|
# kubernetes
|
||||||
# requests-oauthlib
|
# requests-oauthlib
|
||||||
rsa==4.7.2
|
pyasn1==0.5.0
|
||||||
# via google-auth
|
|
||||||
six==1.16.0
|
|
||||||
# via
|
# via
|
||||||
# click-repl
|
# pyasn1-modules
|
||||||
# google-auth
|
# rsa
|
||||||
# kubernetes
|
pyasn1-modules==0.3.0
|
||||||
# python-dateutil
|
# via google-auth
|
||||||
# websocket-client
|
pycryptodome==3.19.0
|
||||||
typed-ast==1.4.3
|
# via -r requirements/base.in
|
||||||
# via mypy
|
python-dateutil==2.8.2
|
||||||
typing-extensions==3.10.0.0
|
# via kubernetes
|
||||||
# via mypy
|
pyyaml==6.0.1
|
||||||
urllib3==1.25.11
|
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.in
|
# -r requirements/base.in
|
||||||
# kubernetes
|
# kubernetes
|
||||||
# requests
|
requests==2.31.0
|
||||||
wcwidth==0.2.5
|
# via
|
||||||
# via prompt-toolkit
|
# kubernetes
|
||||||
websocket-client==0.59.0
|
# requests-oauthlib
|
||||||
|
requests-oauthlib==1.3.1
|
||||||
|
# via kubernetes
|
||||||
|
rsa==4.9
|
||||||
|
# via google-auth
|
||||||
|
six==1.16.0
|
||||||
|
# via
|
||||||
|
# kubernetes
|
||||||
|
# python-dateutil
|
||||||
|
tomli==2.0.1
|
||||||
|
# via mypy
|
||||||
|
typing-extensions==4.8.0
|
||||||
|
# via
|
||||||
|
# -r requirements/base.in
|
||||||
|
# mypy
|
||||||
|
urllib3==1.26.18
|
||||||
|
# via
|
||||||
|
# kubernetes
|
||||||
|
# requests
|
||||||
|
websocket-client==1.6.4
|
||||||
# via kubernetes
|
# via kubernetes
|
||||||
|
|
||||||
# The following packages are considered to be unsafe in a requirements file:
|
|
||||||
# setuptools
|
|
||||||
|
|
|
@ -1,6 +1,19 @@
|
||||||
-r base.txt
|
-r base.txt
|
||||||
black
|
black
|
||||||
|
coverage
|
||||||
pip-tools
|
pip-tools
|
||||||
pylint
|
pylint
|
||||||
pyinstaller
|
pyinstaller
|
||||||
twine
|
scriv
|
||||||
|
twine
|
||||||
|
|
||||||
|
# Constraints
|
||||||
|
# for compatibility with sphinx-rtd-theme
|
||||||
|
# drop this constraint once sphinx-rtd-theme 2.0.0 comes out
|
||||||
|
# https://github.com/readthedocs/sphinx_rtd_theme/tags
|
||||||
|
docutils<0.19.0
|
||||||
|
|
||||||
|
# Types packages
|
||||||
|
types-docutils
|
||||||
|
types-PyYAML
|
||||||
|
types-setuptools
|
||||||
|
|
|
@ -1,217 +1,239 @@
|
||||||
#
|
#
|
||||||
# This file is autogenerated by pip-compile
|
# This file is autogenerated by pip-compile with Python 3.8
|
||||||
# To update, run:
|
# by the following command:
|
||||||
#
|
#
|
||||||
# pip-compile requirements/dev.in
|
# pip-compile requirements/dev.in
|
||||||
#
|
#
|
||||||
altgraph==0.17
|
altgraph==0.17.4
|
||||||
# via pyinstaller
|
# via pyinstaller
|
||||||
appdirs==1.4.4
|
appdirs==1.4.4
|
||||||
# via
|
# via -r requirements/base.txt
|
||||||
# -r requirements/base.txt
|
astroid==3.0.1
|
||||||
# black
|
|
||||||
astroid==2.5.6
|
|
||||||
# via pylint
|
# via pylint
|
||||||
black==21.5b1
|
attrs==23.1.0
|
||||||
|
# via scriv
|
||||||
|
black==23.10.1
|
||||||
# via -r requirements/dev.in
|
# via -r requirements/dev.in
|
||||||
bleach==3.3.0
|
build==1.0.3
|
||||||
# via readme-renderer
|
# via pip-tools
|
||||||
cachetools==4.2.2
|
cachetools==5.3.2
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# google-auth
|
# google-auth
|
||||||
certifi==2020.12.5
|
certifi==2023.7.22
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# kubernetes
|
# kubernetes
|
||||||
# requests
|
# requests
|
||||||
cffi==1.14.5
|
cffi==1.16.0
|
||||||
# via cryptography
|
# via cryptography
|
||||||
chardet==4.0.0
|
charset-normalizer==3.3.1
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# requests
|
# requests
|
||||||
click-repl==0.1.6
|
click==8.1.7
|
||||||
# via -r requirements/base.txt
|
|
||||||
click==7.1.2
|
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# black
|
# black
|
||||||
# click-repl
|
# click-log
|
||||||
# pip-tools
|
# pip-tools
|
||||||
colorama==0.4.4
|
# scriv
|
||||||
# via twine
|
click-log==0.4.0
|
||||||
cryptography==3.4.7
|
# via scriv
|
||||||
|
coverage==7.3.2
|
||||||
|
# via -r requirements/dev.in
|
||||||
|
cryptography==41.0.7
|
||||||
# via secretstorage
|
# via secretstorage
|
||||||
docutils==0.17.1
|
dill==0.3.7
|
||||||
# via readme-renderer
|
# via pylint
|
||||||
google-auth==1.30.0
|
docutils==0.18.1
|
||||||
|
# via
|
||||||
|
# -r requirements/dev.in
|
||||||
|
# readme-renderer
|
||||||
|
google-auth==2.23.3
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# kubernetes
|
# kubernetes
|
||||||
idna==2.10
|
idna==3.4
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# requests
|
# requests
|
||||||
importlib-metadata==4.0.1
|
importlib-metadata==6.8.0
|
||||||
# via
|
# via
|
||||||
|
# build
|
||||||
# keyring
|
# keyring
|
||||||
# pep517
|
|
||||||
# pyinstaller
|
# pyinstaller
|
||||||
# twine
|
# twine
|
||||||
isort==5.8.0
|
importlib-resources==6.1.1
|
||||||
|
# via keyring
|
||||||
|
isort==5.12.0
|
||||||
# via pylint
|
# via pylint
|
||||||
jeepney==0.6.0
|
jaraco-classes==3.3.0
|
||||||
|
# via keyring
|
||||||
|
jeepney==0.8.0
|
||||||
# via
|
# via
|
||||||
# keyring
|
# keyring
|
||||||
# secretstorage
|
# secretstorage
|
||||||
jinja2==2.11.3
|
jinja2==3.1.2
|
||||||
# via -r requirements/base.txt
|
# via
|
||||||
keyring==23.0.1
|
# -r requirements/base.txt
|
||||||
|
# scriv
|
||||||
|
keyring==24.2.0
|
||||||
# via twine
|
# via twine
|
||||||
kubernetes==12.0.1
|
kubernetes==28.1.0
|
||||||
# via -r requirements/base.txt
|
# via -r requirements/base.txt
|
||||||
lazy-object-proxy==1.6.0
|
markdown-it-py==3.0.0
|
||||||
# via astroid
|
# via
|
||||||
markupsafe==1.1.1
|
# rich
|
||||||
|
# scriv
|
||||||
|
markupsafe==2.1.3
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# jinja2
|
# jinja2
|
||||||
mccabe==0.6.1
|
mccabe==0.7.0
|
||||||
# via pylint
|
# via pylint
|
||||||
mypy-extensions==0.4.3
|
mdurl==0.1.2
|
||||||
|
# via markdown-it-py
|
||||||
|
more-itertools==10.1.0
|
||||||
|
# via jaraco-classes
|
||||||
|
mypy==1.6.1
|
||||||
|
# via -r requirements/base.txt
|
||||||
|
mypy-extensions==1.0.0
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# black
|
# black
|
||||||
# mypy
|
# mypy
|
||||||
mypy==0.812
|
nh3==0.2.14
|
||||||
# via -r requirements/base.txt
|
# via readme-renderer
|
||||||
oauthlib==3.1.0
|
oauthlib==3.2.2
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
|
# kubernetes
|
||||||
# requests-oauthlib
|
# requests-oauthlib
|
||||||
packaging==20.9
|
packaging==23.2
|
||||||
# via bleach
|
# via
|
||||||
pathspec==0.8.1
|
# black
|
||||||
|
# build
|
||||||
|
# pyinstaller
|
||||||
|
pathspec==0.11.2
|
||||||
# via black
|
# via black
|
||||||
pep517==0.10.0
|
pip-tools==7.3.0
|
||||||
# via pip-tools
|
|
||||||
pip-tools==6.1.0
|
|
||||||
# via -r requirements/dev.in
|
# via -r requirements/dev.in
|
||||||
pkginfo==1.7.0
|
pkginfo==1.9.6
|
||||||
# via twine
|
# via twine
|
||||||
prompt-toolkit==3.0.18
|
platformdirs==3.11.0
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# black
|
||||||
# click-repl
|
# pylint
|
||||||
pyasn1-modules==0.2.8
|
pyasn1==0.5.0
|
||||||
# via
|
|
||||||
# -r requirements/base.txt
|
|
||||||
# google-auth
|
|
||||||
pyasn1==0.4.8
|
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# pyasn1-modules
|
# pyasn1-modules
|
||||||
# rsa
|
# rsa
|
||||||
pycparser==2.20
|
pyasn1-modules==0.3.0
|
||||||
|
# via
|
||||||
|
# -r requirements/base.txt
|
||||||
|
# google-auth
|
||||||
|
pycparser==2.21
|
||||||
# via cffi
|
# via cffi
|
||||||
pycryptodome==3.10.1
|
pycryptodome==3.19.0
|
||||||
# via -r requirements/base.txt
|
# via -r requirements/base.txt
|
||||||
pygments==2.9.0
|
pygments==2.16.1
|
||||||
# via readme-renderer
|
# via
|
||||||
pyinstaller-hooks-contrib==2021.1
|
# readme-renderer
|
||||||
|
# rich
|
||||||
|
pyinstaller==6.1.0
|
||||||
|
# via -r requirements/dev.in
|
||||||
|
pyinstaller-hooks-contrib==2023.10
|
||||||
# via pyinstaller
|
# via pyinstaller
|
||||||
pyinstaller==4.3
|
pylint==3.0.2
|
||||||
# via -r requirements/dev.in
|
# via -r requirements/dev.in
|
||||||
pylint==2.8.2
|
pyproject-hooks==1.0.0
|
||||||
# via -r requirements/dev.in
|
# via build
|
||||||
pyparsing==2.4.7
|
python-dateutil==2.8.2
|
||||||
# via packaging
|
|
||||||
python-dateutil==2.8.1
|
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# kubernetes
|
# kubernetes
|
||||||
pyyaml==5.4.1
|
pyyaml==6.0.1
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# kubernetes
|
# kubernetes
|
||||||
readme-renderer==29.0
|
readme-renderer==42.0
|
||||||
# via twine
|
# via twine
|
||||||
regex==2021.4.4
|
requests==2.31.0
|
||||||
# via black
|
|
||||||
requests-oauthlib==1.3.0
|
|
||||||
# via
|
|
||||||
# -r requirements/base.txt
|
|
||||||
# kubernetes
|
|
||||||
requests-toolbelt==0.9.1
|
|
||||||
# via twine
|
|
||||||
requests==2.25.1
|
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# kubernetes
|
# kubernetes
|
||||||
# requests-oauthlib
|
# requests-oauthlib
|
||||||
# requests-toolbelt
|
# requests-toolbelt
|
||||||
|
# scriv
|
||||||
# twine
|
# twine
|
||||||
rfc3986==1.5.0
|
requests-oauthlib==1.3.1
|
||||||
|
# via
|
||||||
|
# -r requirements/base.txt
|
||||||
|
# kubernetes
|
||||||
|
requests-toolbelt==1.0.0
|
||||||
# via twine
|
# via twine
|
||||||
rsa==4.7.2
|
rfc3986==2.0.0
|
||||||
|
# via twine
|
||||||
|
rich==13.6.0
|
||||||
|
# via twine
|
||||||
|
rsa==4.9
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# google-auth
|
# google-auth
|
||||||
secretstorage==3.3.1
|
scriv==1.5.0
|
||||||
|
# via -r requirements/dev.in
|
||||||
|
secretstorage==3.3.3
|
||||||
# via keyring
|
# via keyring
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# bleach
|
|
||||||
# click-repl
|
|
||||||
# google-auth
|
|
||||||
# kubernetes
|
# kubernetes
|
||||||
# python-dateutil
|
# python-dateutil
|
||||||
# readme-renderer
|
tomli==2.0.1
|
||||||
# websocket-client
|
|
||||||
toml==0.10.2
|
|
||||||
# via
|
# via
|
||||||
|
# -r requirements/base.txt
|
||||||
# black
|
# black
|
||||||
# pep517
|
# build
|
||||||
|
# mypy
|
||||||
|
# pip-tools
|
||||||
# pylint
|
# pylint
|
||||||
tqdm==4.60.0
|
# pyproject-hooks
|
||||||
# via twine
|
tomlkit==0.12.1
|
||||||
twine==3.4.1
|
# via pylint
|
||||||
|
twine==4.0.2
|
||||||
# via -r requirements/dev.in
|
# via -r requirements/dev.in
|
||||||
typed-ast==1.4.3
|
types-docutils==0.20.0.3
|
||||||
|
# via -r requirements/dev.in
|
||||||
|
types-pyyaml==6.0.12.12
|
||||||
|
# via -r requirements/dev.in
|
||||||
|
types-setuptools==68.2.0.0
|
||||||
|
# via -r requirements/dev.in
|
||||||
|
typing-extensions==4.8.0
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# astroid
|
# astroid
|
||||||
# black
|
# black
|
||||||
# mypy
|
# mypy
|
||||||
typing-extensions==3.10.0.0
|
# pylint
|
||||||
# via
|
# rich
|
||||||
# -r requirements/base.txt
|
urllib3==1.26.18
|
||||||
# black
|
|
||||||
# importlib-metadata
|
|
||||||
# mypy
|
|
||||||
urllib3==1.25.11
|
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# kubernetes
|
# kubernetes
|
||||||
# requests
|
# requests
|
||||||
wcwidth==0.2.5
|
# twine
|
||||||
# via
|
websocket-client==1.6.4
|
||||||
# -r requirements/base.txt
|
|
||||||
# prompt-toolkit
|
|
||||||
webencodings==0.5.1
|
|
||||||
# via bleach
|
|
||||||
websocket-client==0.59.0
|
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# kubernetes
|
# kubernetes
|
||||||
wrapt==1.12.1
|
wheel==0.41.2
|
||||||
# via astroid
|
# via pip-tools
|
||||||
zipp==3.4.1
|
zipp==3.17.0
|
||||||
# via
|
# via
|
||||||
# importlib-metadata
|
# importlib-metadata
|
||||||
# pep517
|
# importlib-resources
|
||||||
|
|
||||||
# The following packages are considered to be unsafe in a requirements file:
|
# The following packages are considered to be unsafe in a requirements file:
|
||||||
# pip
|
# pip
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
-r base.txt
|
-r base.txt
|
||||||
sphinx
|
# Python 3.8 support was dropped in 7.2.0
|
||||||
|
# https://github.com/sphinx-doc/sphinx/pull/11511
|
||||||
|
sphinx<7.2.0
|
||||||
sphinx-rtd-theme
|
sphinx-rtd-theme
|
||||||
|
sphinx-click
|
||||||
|
|
|
@ -1,163 +1,156 @@
|
||||||
#
|
#
|
||||||
# This file is autogenerated by pip-compile
|
# This file is autogenerated by pip-compile with Python 3.8
|
||||||
# To update, run:
|
# by the following command:
|
||||||
#
|
#
|
||||||
# pip-compile requirements/docs.in
|
# pip-compile requirements/docs.in
|
||||||
#
|
#
|
||||||
alabaster==0.7.12
|
alabaster==0.7.13
|
||||||
# via sphinx
|
# via sphinx
|
||||||
appdirs==1.4.4
|
appdirs==1.4.4
|
||||||
# via -r requirements/base.txt
|
# via -r requirements/base.txt
|
||||||
babel==2.9.1
|
babel==2.13.1
|
||||||
# via sphinx
|
# via sphinx
|
||||||
cachetools==4.2.2
|
cachetools==5.3.2
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# google-auth
|
# google-auth
|
||||||
certifi==2020.12.5
|
certifi==2023.7.22
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# kubernetes
|
# kubernetes
|
||||||
# requests
|
# requests
|
||||||
chardet==4.0.0
|
charset-normalizer==3.3.1
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# requests
|
# requests
|
||||||
click-repl==0.1.6
|
click==8.1.7
|
||||||
# via -r requirements/base.txt
|
|
||||||
click==7.1.2
|
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# click-repl
|
# sphinx-click
|
||||||
docutils==0.16
|
docutils==0.18.1
|
||||||
# via
|
# via
|
||||||
# sphinx
|
# sphinx
|
||||||
|
# sphinx-click
|
||||||
# sphinx-rtd-theme
|
# sphinx-rtd-theme
|
||||||
google-auth==1.30.0
|
google-auth==2.23.3
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# kubernetes
|
# kubernetes
|
||||||
idna==2.10
|
idna==3.4
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# requests
|
# requests
|
||||||
imagesize==1.2.0
|
imagesize==1.4.1
|
||||||
# via sphinx
|
# via sphinx
|
||||||
jinja2==2.11.3
|
importlib-metadata==6.8.0
|
||||||
|
# via sphinx
|
||||||
|
jinja2==3.1.2
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# sphinx
|
# sphinx
|
||||||
kubernetes==12.0.1
|
kubernetes==28.1.0
|
||||||
# via -r requirements/base.txt
|
# via -r requirements/base.txt
|
||||||
markupsafe==1.1.1
|
markupsafe==2.1.3
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# jinja2
|
# jinja2
|
||||||
# sphinx
|
mypy==1.6.1
|
||||||
mypy-extensions==0.4.3
|
# via -r requirements/base.txt
|
||||||
|
mypy-extensions==1.0.0
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# mypy
|
# mypy
|
||||||
mypy==0.812
|
oauthlib==3.2.2
|
||||||
# via -r requirements/base.txt
|
|
||||||
oauthlib==3.1.0
|
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
|
# kubernetes
|
||||||
# requests-oauthlib
|
# requests-oauthlib
|
||||||
packaging==20.9
|
packaging==23.2
|
||||||
# via sphinx
|
# via sphinx
|
||||||
prompt-toolkit==3.0.18
|
pyasn1==0.5.0
|
||||||
# via
|
|
||||||
# -r requirements/base.txt
|
|
||||||
# click-repl
|
|
||||||
pyasn1-modules==0.2.8
|
|
||||||
# via
|
|
||||||
# -r requirements/base.txt
|
|
||||||
# google-auth
|
|
||||||
pyasn1==0.4.8
|
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# pyasn1-modules
|
# pyasn1-modules
|
||||||
# rsa
|
# rsa
|
||||||
pycryptodome==3.10.1
|
pyasn1-modules==0.3.0
|
||||||
|
# via
|
||||||
|
# -r requirements/base.txt
|
||||||
|
# google-auth
|
||||||
|
pycryptodome==3.19.0
|
||||||
# via -r requirements/base.txt
|
# via -r requirements/base.txt
|
||||||
pygments==2.9.0
|
pygments==2.16.1
|
||||||
# via sphinx
|
# via sphinx
|
||||||
pyparsing==2.4.7
|
python-dateutil==2.8.2
|
||||||
# via packaging
|
|
||||||
python-dateutil==2.8.1
|
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# kubernetes
|
# kubernetes
|
||||||
pytz==2021.1
|
pytz==2023.3.post1
|
||||||
# via babel
|
# via babel
|
||||||
pyyaml==5.4.1
|
pyyaml==6.0.1
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# kubernetes
|
# kubernetes
|
||||||
requests-oauthlib==1.3.0
|
requests==2.31.0
|
||||||
# via
|
|
||||||
# -r requirements/base.txt
|
|
||||||
# kubernetes
|
|
||||||
requests==2.25.1
|
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# kubernetes
|
# kubernetes
|
||||||
# requests-oauthlib
|
# requests-oauthlib
|
||||||
# sphinx
|
# sphinx
|
||||||
rsa==4.7.2
|
requests-oauthlib==1.3.1
|
||||||
|
# via
|
||||||
|
# -r requirements/base.txt
|
||||||
|
# kubernetes
|
||||||
|
rsa==4.9
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# google-auth
|
# google-auth
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# click-repl
|
|
||||||
# google-auth
|
|
||||||
# kubernetes
|
# kubernetes
|
||||||
# python-dateutil
|
# python-dateutil
|
||||||
# websocket-client
|
snowballstemmer==2.2.0
|
||||||
snowballstemmer==2.1.0
|
|
||||||
# via sphinx
|
# via sphinx
|
||||||
sphinx-rtd-theme==0.5.2
|
sphinx==7.1.2
|
||||||
# via -r requirements/docs.in
|
|
||||||
sphinx==4.0.1
|
|
||||||
# via
|
# via
|
||||||
# -r requirements/docs.in
|
# -r requirements/docs.in
|
||||||
|
# sphinx-click
|
||||||
# sphinx-rtd-theme
|
# sphinx-rtd-theme
|
||||||
sphinxcontrib-applehelp==1.0.2
|
# sphinxcontrib-jquery
|
||||||
|
sphinx-click==5.0.1
|
||||||
|
# via -r requirements/docs.in
|
||||||
|
sphinx-rtd-theme==1.3.0
|
||||||
|
# via -r requirements/docs.in
|
||||||
|
sphinxcontrib-applehelp==1.0.4
|
||||||
# via sphinx
|
# via sphinx
|
||||||
sphinxcontrib-devhelp==1.0.2
|
sphinxcontrib-devhelp==1.0.2
|
||||||
# via sphinx
|
# via sphinx
|
||||||
sphinxcontrib-htmlhelp==1.0.3
|
sphinxcontrib-htmlhelp==2.0.1
|
||||||
# via sphinx
|
# via sphinx
|
||||||
|
sphinxcontrib-jquery==4.1
|
||||||
|
# via sphinx-rtd-theme
|
||||||
sphinxcontrib-jsmath==1.0.1
|
sphinxcontrib-jsmath==1.0.1
|
||||||
# via sphinx
|
# via sphinx
|
||||||
sphinxcontrib-qthelp==1.0.3
|
sphinxcontrib-qthelp==1.0.3
|
||||||
# via sphinx
|
# via sphinx
|
||||||
sphinxcontrib-serializinghtml==1.1.4
|
sphinxcontrib-serializinghtml==1.1.5
|
||||||
# via sphinx
|
# via sphinx
|
||||||
typed-ast==1.4.3
|
tomli==2.0.1
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# mypy
|
# mypy
|
||||||
typing-extensions==3.10.0.0
|
typing-extensions==4.8.0
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# mypy
|
# mypy
|
||||||
urllib3==1.25.11
|
urllib3==1.26.18
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# kubernetes
|
# kubernetes
|
||||||
# requests
|
# requests
|
||||||
wcwidth==0.2.5
|
websocket-client==1.6.4
|
||||||
# via
|
|
||||||
# -r requirements/base.txt
|
|
||||||
# prompt-toolkit
|
|
||||||
websocket-client==0.59.0
|
|
||||||
# via
|
# via
|
||||||
# -r requirements/base.txt
|
# -r requirements/base.txt
|
||||||
# kubernetes
|
# kubernetes
|
||||||
|
zipp==3.17.0
|
||||||
# The following packages are considered to be unsafe in a requirements file:
|
# via importlib-metadata
|
||||||
# setuptools
|
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
tutor-discovery
|
# change version ranges when upgrading from quince
|
||||||
tutor-ecommerce
|
tutor-android>=17.0.0,<18.0.0
|
||||||
#tutor-figures
|
tutor-cairn>=17.0.0,<18.0.0
|
||||||
tutor-license
|
tutor-credentials>=17.0.0,<18.0.0
|
||||||
tutor-minio
|
tutor-discovery>=17.0.0,<18.0.0
|
||||||
tutor-notes
|
tutor-ecommerce>=17.0.0,<18.0.0
|
||||||
tutor-xqueue
|
tutor-forum>=17.0.0,<18.0.0
|
||||||
|
tutor-jupyter>=17.0.0,<18.0.0
|
||||||
|
tutor-mfe>=17.0.0,<18.0.0
|
||||||
|
tutor-minio>=17.0.0,<18.0.0
|
||||||
|
tutor-notes>=17.0.0,<18.0.0
|
||||||
|
tutor-webui>=17.0.0,<18.0.0
|
||||||
|
tutor-xqueue>=17.0.0,<18.0.0
|
||||||
|
|
50
setup.py
|
@ -1,17 +1,23 @@
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
|
from typing import Dict, List
|
||||||
|
|
||||||
from setuptools import find_packages, setup
|
from setuptools import find_packages, setup
|
||||||
|
|
||||||
HERE = os.path.abspath(os.path.dirname(__file__))
|
HERE = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
||||||
|
|
||||||
def load_readme():
|
def load_readme() -> str:
|
||||||
with io.open(os.path.join(HERE, "README.rst"), "rt", encoding="utf8") as f:
|
with io.open(os.path.join(HERE, "README.rst"), "rt", encoding="utf8") as f:
|
||||||
return f.read()
|
readme = f.read()
|
||||||
|
# Replace img src for publication on pypi
|
||||||
|
return readme.replace(
|
||||||
|
"./docs/img/", "https://github.com/overhangio/tutor/raw/master/docs/img/"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def load_about():
|
def load_about() -> Dict[str, str]:
|
||||||
about = {}
|
about: Dict[str, str] = {}
|
||||||
with io.open(
|
with io.open(
|
||||||
os.path.join(HERE, "tutor", "__about__.py"), "rt", encoding="utf-8"
|
os.path.join(HERE, "tutor", "__about__.py"), "rt", encoding="utf-8"
|
||||||
) as f:
|
) as f:
|
||||||
|
@ -19,39 +25,43 @@ def load_about():
|
||||||
return about
|
return about
|
||||||
|
|
||||||
|
|
||||||
def load_requirements():
|
def load_requirements(filename: str) -> List[str]:
|
||||||
with io.open(
|
with io.open(
|
||||||
os.path.join(HERE, "requirements", "base.in"), "rt", encoding="utf-8"
|
os.path.join(HERE, "requirements", filename), "rt", encoding="utf-8"
|
||||||
) as f:
|
) as f:
|
||||||
return [line.strip() for line in f if is_requirement(line)]
|
return [line.strip() for line in f if is_requirement(line)]
|
||||||
|
|
||||||
|
|
||||||
def is_requirement(line):
|
def is_requirement(line: str) -> bool:
|
||||||
return not (line.strip() == "" or line.startswith("#"))
|
return not (line.strip() == "" or line.startswith("#"))
|
||||||
|
|
||||||
|
|
||||||
ABOUT = load_about()
|
ABOUT = load_about()
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="tutor-openedx",
|
name="tutor",
|
||||||
version=ABOUT["__version__"],
|
version=ABOUT["__package_version__"],
|
||||||
url="https://docs.tutor.overhang.io/",
|
url="https://docs.tutor.edly.io/",
|
||||||
project_urls={
|
project_urls={
|
||||||
"Documentation": "https://docs.tutor.overhang.io/",
|
"Documentation": "https://docs.tutor.edly.io/",
|
||||||
"Code": "https://github.com/overhangio/tutor",
|
"Code": "https://github.com/overhangio/tutor",
|
||||||
"Issue tracker": "https://github.com/overhangio/tutor/issues",
|
"Issue tracker": "https://github.com/overhangio/tutor/issues",
|
||||||
"Community": "https://discuss.overhang.io",
|
"Community": "https://discuss.openedx.org/tag/tutor",
|
||||||
},
|
},
|
||||||
license="AGPLv3",
|
license="AGPLv3",
|
||||||
author="Overhang.io",
|
author="Edly",
|
||||||
author_email="contact@overhang.io",
|
author_email="hello@edly.io",
|
||||||
description="The docker-based Open edX distribution designed for peace of mind",
|
description="The Docker-based Open edX distribution designed for peace of mind",
|
||||||
long_description=load_readme(),
|
long_description=load_readme(),
|
||||||
long_description_content_type="text/x-rst",
|
long_description_content_type="text/x-rst",
|
||||||
packages=find_packages(exclude=["tests*"]),
|
packages=find_packages(exclude=["tests*"]),
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
python_requires=">=3.5",
|
python_requires=">=3.8",
|
||||||
install_requires=load_requirements(),
|
install_requires=load_requirements("base.in"),
|
||||||
|
extras_require={
|
||||||
|
"dev": load_requirements("dev.txt"),
|
||||||
|
"full": load_requirements("plugins.txt"),
|
||||||
|
},
|
||||||
entry_points={"console_scripts": ["tutor=tutor.commands.cli:main"]},
|
entry_points={"console_scripts": ["tutor=tutor.commands.cli:main"]},
|
||||||
classifiers=[
|
classifiers=[
|
||||||
"Development Status :: 5 - Production/Stable",
|
"Development Status :: 5 - Production/Stable",
|
||||||
|
@ -59,11 +69,11 @@ setup(
|
||||||
"License :: OSI Approved :: GNU Affero General Public License v3",
|
"License :: OSI Approved :: GNU Affero General Public License v3",
|
||||||
"Operating System :: OS Independent",
|
"Operating System :: OS Independent",
|
||||||
"Programming Language :: Python",
|
"Programming Language :: Python",
|
||||||
"Programming Language :: Python :: 3.5",
|
|
||||||
"Programming Language :: Python :: 3.6",
|
|
||||||
"Programming Language :: Python :: 3.7",
|
|
||||||
"Programming Language :: Python :: 3.8",
|
"Programming Language :: Python :: 3.8",
|
||||||
"Programming Language :: Python :: 3.9",
|
"Programming Language :: Python :: 3.9",
|
||||||
"Programming Language :: Python :: 3.10",
|
"Programming Language :: Python :: 3.10",
|
||||||
|
"Programming Language :: Python :: 3.11",
|
||||||
|
"Programming Language :: Python :: 3.12",
|
||||||
],
|
],
|
||||||
|
test_suite="tests",
|
||||||
)
|
)
|
||||||
|
|
0
tests/commands/__init__.py
Normal file
40
tests/commands/base.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import click.testing
|
||||||
|
|
||||||
|
from tests.helpers import TestContext, temporary_root
|
||||||
|
from tutor.commands.cli import cli
|
||||||
|
|
||||||
|
|
||||||
|
class TestCommandMixin:
|
||||||
|
"""
|
||||||
|
Run CLI tests in an isolated test root.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def invoke(args: list[str]) -> click.testing.Result:
|
||||||
|
with temporary_root() as root:
|
||||||
|
return TestCommandMixin.invoke_in_root(root, args)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def invoke_in_root(
|
||||||
|
root: str, args: list[str], catch_exceptions: bool = True
|
||||||
|
) -> click.testing.Result:
|
||||||
|
"""
|
||||||
|
Use this method for commands that all need to run in the same root:
|
||||||
|
|
||||||
|
with temporary_root() as root:
|
||||||
|
result1 = self.invoke_in_root(root, ...)
|
||||||
|
result2 = self.invoke_in_root(root, ...)
|
||||||
|
"""
|
||||||
|
runner = click.testing.CliRunner(
|
||||||
|
env={
|
||||||
|
"TUTOR_ROOT": root,
|
||||||
|
"TUTOR_IGNORE_ENTRYPOINT_PLUGINS": "1",
|
||||||
|
"TUTOR_IGNORE_DICT_PLUGINS": "1",
|
||||||
|
},
|
||||||
|
mix_stderr=False,
|
||||||
|
)
|
||||||
|
return runner.invoke(
|
||||||
|
cli, args, obj=TestContext(root), catch_exceptions=catch_exceptions
|
||||||
|
)
|