Compare commits
711 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00c940f7df | ||
|
|
931b459fbd | ||
|
|
51e71f4a0a | ||
|
|
9b631f2328 | ||
|
|
01f481a9b6 | ||
|
|
0177ab5c87 | ||
|
|
4db35d341c | ||
|
|
3c4a803c97 | ||
|
|
05154dc7ca | ||
|
|
c90b477f60 | ||
|
|
6f18ddbe85 | ||
|
|
dde779a71d | ||
|
|
bd9f66fbc7 | ||
|
|
92f1f40bfa | ||
|
|
48dc1049b3 | ||
|
|
9add530370 | ||
|
|
b77460d871 | ||
|
|
42db41776b | ||
|
|
075439c74f | ||
|
|
fc6c553ece | ||
|
|
59cb3d5df1 | ||
|
|
5c2d958e2b | ||
|
|
2117d6b232 | ||
|
|
5c48c83301 | ||
|
|
af7d331610 | ||
|
|
1fe5a0fffa | ||
|
|
f1e32aab66 | ||
|
|
7698a6ac8e | ||
|
|
063f5a683e | ||
|
|
fd74776abe | ||
|
|
9d89e4f7f5 | ||
|
|
7182b85bd0 | ||
|
|
91f1d0081b | ||
|
|
09c3a8828f | ||
|
|
927a6489f9 | ||
|
|
075eead9fa | ||
|
|
15b0c127f5 | ||
|
|
d0b014543c | ||
|
|
839f500979 | ||
|
|
af72bf0f69 | ||
|
|
405167854b | ||
|
|
f9654bb1f8 | ||
|
|
e3e0553c56 | ||
|
|
248fb1fa69 | ||
|
|
12a2ccf1a5 | ||
|
|
227b8b0061 | ||
|
|
442115632b | ||
|
|
ad82376890 | ||
|
|
a8bff81a7e | ||
|
|
da2821eaac | ||
|
|
c55565ee71 | ||
|
|
ecd5db2952 | ||
|
|
744cd47481 | ||
|
|
12c72e63b1 | ||
|
|
b7edaab387 | ||
|
|
83472cc682 | ||
|
|
08e7fa3486 | ||
|
|
7305530d97 | ||
|
|
b99ac51624 | ||
|
|
01d7fe4597 | ||
|
|
ac0a3aab3e | ||
|
|
7fe23a31b2 | ||
|
|
f50b8002b3 | ||
|
|
7a4669a6aa | ||
|
|
8554d5d791 | ||
|
|
da9934fbdc | ||
|
|
cb6974f263 | ||
|
|
f75684d6f6 | ||
|
|
acf0734c95 | ||
|
|
cfffcad1b8 | ||
|
|
3b74ff2b92 | ||
|
|
2b34e1a9cd | ||
|
|
9664d142ad | ||
|
|
e7106b7393 | ||
|
|
f491ff94cc | ||
|
|
e8987457ab | ||
|
|
7a8150398c | ||
|
|
3c4dbed16b | ||
|
|
7e381e830a | ||
|
|
61dc4a56f9 | ||
|
|
788c445f2b | ||
|
|
97db941749 | ||
|
|
f6cfe7c8a1 | ||
|
|
fb0aaabe53 | ||
|
|
de33967a73 | ||
|
|
65321e39cc | ||
|
|
a3906bf92b | ||
|
|
0a88be7771 | ||
|
|
7068d5f38a | ||
|
|
bf17b4789b | ||
|
|
5e9efbffa3 | ||
|
|
7e516236f5 | ||
|
|
15024c6c8a | ||
|
|
6a93203d68 | ||
|
|
af9663d3e7 | ||
|
|
1d7e3dd570 | ||
|
|
6ef02fbc38 | ||
|
|
604c3da9fe | ||
|
|
75c8dd75e3 | ||
|
|
e5c86a9249 | ||
|
|
e5be570f54 | ||
|
|
6aaf550241 | ||
|
|
2d62ec72a7 | ||
|
|
0273ea8b00 | ||
|
|
97d189f1c8 | ||
|
|
db36f81d24 | ||
|
|
c30d2550ff | ||
|
|
93e286177f | ||
|
|
8ca53326a7 | ||
|
|
7d6c096462 | ||
|
|
f08408ebe5 | ||
|
|
0ad0ab50d0 | ||
|
|
563863005f | ||
|
|
57d71ad6cf | ||
|
|
37ec552f74 | ||
|
|
9e3c79526c | ||
|
|
a015dc42bb | ||
|
|
17afe218fe | ||
|
|
7b204cc611 | ||
|
|
9102f1f9a9 | ||
|
|
f6b53a1167 | ||
|
|
6fcdb05925 | ||
|
|
99b35c7a93 | ||
|
|
fc27a2e906 | ||
|
|
b4d8f1428c | ||
|
|
1b0c604c02 | ||
|
|
581635f40b | ||
|
|
d752086846 | ||
|
|
f23ec9063c | ||
|
|
26b953ce57 | ||
|
|
72babe9157 | ||
|
|
b6ca81821e | ||
|
|
f11cc40ae3 | ||
|
|
95c1a44a0d | ||
|
|
198befb006 | ||
|
|
fc3516acfb | ||
|
|
de9e7f97a4 | ||
|
|
05ad6eb450 | ||
|
|
98d0ac3c90 | ||
|
|
2aa4b4c1f3 | ||
|
|
3798293d3e | ||
|
|
fd76d33421 | ||
|
|
2a4495f7ee | ||
|
|
1775138228 | ||
|
|
8c194ab8b0 | ||
|
|
5ba1c0dbdb | ||
|
|
59ec122940 | ||
|
|
588f63505d | ||
|
|
ef8024c657 | ||
|
|
abbd8235aa | ||
|
|
385de7d33b | ||
|
|
87571a0879 | ||
|
|
af24334264 | ||
|
|
9fc0152cb1 | ||
|
|
35dbbe561a | ||
|
|
50563dcb6e | ||
|
|
edb6e0fd69 | ||
|
|
9334a363c7 | ||
|
|
e0f7ac426c | ||
|
|
1deb6bffd3 | ||
|
|
ae6376edee | ||
|
|
780c164cc8 | ||
|
|
df69c908fe | ||
|
|
eaa90c9fb6 | ||
|
|
e8a88da9e4 | ||
|
|
559ec3956f | ||
|
|
3749125907 | ||
|
|
97d06432f0 | ||
|
|
b13705ba3d | ||
|
|
df77ffde9a | ||
|
|
ef1228dcb4 | ||
|
|
551287ab44 | ||
|
|
113105181d | ||
|
|
627e5dfbb5 | ||
|
|
21604a2cad | ||
|
|
fd6536ea90 | ||
|
|
a42d218962 | ||
|
|
5226d6c568 | ||
|
|
14bee1811c | ||
|
|
7ecee01d20 | ||
|
|
2e4f6ef2ed | ||
|
|
9b43380b05 | ||
|
|
303dade2ef | ||
|
|
113b20f669 | ||
|
|
95d37fc63f | ||
|
|
858a1da35f | ||
|
|
1d62133f4f | ||
|
|
df054ac7f6 | ||
|
|
493b815b7b | ||
|
|
e75a0743f8 | ||
|
|
5573afc8d5 | ||
|
|
8b703fd9ad | ||
|
|
2a97194c75 | ||
|
|
13b6bb7ee3 | ||
|
|
43220f6db6 | ||
|
|
b52e006bfe | ||
|
|
a8b10c64b8 | ||
|
|
9d6b335829 | ||
|
|
9820c80ee2 | ||
|
|
1dd17c6334 | ||
|
|
b6d21c0945 | ||
|
|
aba771e99c | ||
|
|
d9e2d17a26 | ||
|
|
779ac39b36 | ||
|
|
817463c40e | ||
|
|
890905ed0e | ||
|
|
54aad58388 | ||
|
|
37c6560dd6 | ||
|
|
352420b881 | ||
|
|
61d9958a4c | ||
|
|
7275d417b2 | ||
|
|
d835be90cb | ||
|
|
c54489ba6e | ||
|
|
9958baa843 | ||
|
|
97d8660a54 | ||
|
|
b14a6bc29e | ||
|
|
1d29c26284 | ||
|
|
bed9310519 | ||
|
|
814d552be8 | ||
|
|
63ca9671be | ||
|
|
524e48c8e6 | ||
|
|
9b3e6bf1f5 | ||
|
|
4135cfd243 | ||
|
|
be6bcfdb9a | ||
|
|
bb046a12dc | ||
|
|
a430266389 | ||
|
|
62d2b09e3c | ||
|
|
e831f612e8 | ||
|
|
01987990ee | ||
|
|
b3dc4dce9c | ||
|
|
5e9eeaddbd | ||
|
|
a0095496d7 | ||
|
|
77efcad89d | ||
|
|
1d06114f00 | ||
|
|
579fa1ae4c | ||
|
|
fea6b81a53 | ||
|
|
7911d67439 | ||
|
|
efca651429 | ||
|
|
07b5d18441 | ||
|
|
c1d386d932 | ||
|
|
710a2f2c97 | ||
|
|
2e1e486bc6 | ||
|
|
227017625d | ||
|
|
6ea10860b4 | ||
|
|
286347420d | ||
|
|
daa976ca62 | ||
|
|
d05897b724 | ||
|
|
5ebd82dc04 | ||
|
|
ae4322b75f | ||
|
|
a0e6ff9bd1 | ||
|
|
491cd75fe0 | ||
|
|
5be5863693 | ||
|
|
23abb97ccb | ||
|
|
b09d5f3e18 | ||
|
|
a731499024 | ||
|
|
8f5ee9234c | ||
|
|
1f6a105e5c | ||
|
|
b1eb926b7b | ||
|
|
c8d1de3a59 | ||
|
|
aa7e0dd85b | ||
|
|
0092a19812 | ||
|
|
4a6cfedc4a | ||
|
|
8c97fd1d28 | ||
|
|
d9fe3e73d5 | ||
|
|
59a4b3bc31 | ||
|
|
0161c98d65 | ||
|
|
d3ffa2c5f2 | ||
|
|
0a6859a1d7 | ||
|
|
69db99636b | ||
|
|
aab1a1e50a | ||
|
|
19646a97af | ||
|
|
f59a35d8d2 | ||
|
|
c911806062 | ||
|
|
b440c3a820 | ||
|
|
98cabc44e4 | ||
|
|
dd94ea5b45 | ||
|
|
b012e615ba | ||
|
|
603f82ffc2 | ||
|
|
56f75160f3 | ||
|
|
a860cf8e43 | ||
|
|
504dda2092 | ||
|
|
1df8b6edfb | ||
|
|
ddfa5d394d | ||
|
|
a69820e4ca | ||
|
|
369db3fea5 | ||
|
|
4a03fa65d1 | ||
|
|
a633fb6dea | ||
|
|
1b6cd70247 | ||
|
|
29fa237dfb | ||
|
|
61044d91a8 | ||
|
|
eb728f7ef2 | ||
|
|
1bdc402b7b | ||
|
|
127a84b4e1 | ||
|
|
2e1acec3c0 | ||
|
|
672a01b385 | ||
|
|
ad63c5a525 | ||
|
|
3a79346ce0 | ||
|
|
bf729df7fe | ||
|
|
b785840d91 | ||
|
|
e8410b948d | ||
|
|
3f09ba92bc | ||
|
|
35dc6d6687 | ||
|
|
5b4ce75e13 | ||
|
|
dc6b66c32f | ||
|
|
5f7f5b74ca | ||
|
|
7b5157aa89 | ||
|
|
bd0ed7f474 | ||
|
|
2da6702991 | ||
|
|
56d8fa5d29 | ||
|
|
4e5e48ce9f | ||
|
|
e0063ebc9b | ||
|
|
27e4599c94 | ||
|
|
edd7d00104 | ||
|
|
092e2e0a3d | ||
|
|
a6d19abc73 | ||
|
|
cf7a55b3e8 | ||
|
|
d09f6fb7cc | ||
|
|
98f6b3e685 | ||
|
|
d5f4fbcdb5 | ||
|
|
1ddae40fb2 | ||
|
|
872dc3521b | ||
|
|
2521f2ed12 | ||
|
|
dc95b1ded5 | ||
|
|
f4e49bf0ff | ||
|
|
e55fc3b74d | ||
|
|
73f83cc97e | ||
|
|
6e79858f41 | ||
|
|
e2764179bc | ||
|
|
9b449e9a28 | ||
|
|
9ed32e6bec | ||
|
|
38417878fc | ||
|
|
f395a955a2 | ||
|
|
9aafaf865e | ||
|
|
63bd1fbf02 | ||
|
|
feb3e64839 | ||
|
|
65bb30558e | ||
|
|
cdad7edf15 | ||
|
|
40e094dcdd | ||
|
|
bebe69d483 | ||
|
|
26877ffd52 | ||
|
|
70e731e066 | ||
|
|
acecb4bf69 | ||
|
|
d3635208f6 | ||
|
|
b9656b1e22 | ||
|
|
4577fb7e1a | ||
|
|
c41c3c81c8 | ||
|
|
eea38a08c5 | ||
|
|
f730f2b242 | ||
|
|
1aec1ce7d0 | ||
|
|
b5e8fecfd0 | ||
|
|
7d1b66c13f | ||
|
|
4ad0e2ffd6 | ||
|
|
d5c41d29af | ||
|
|
a9b6529bcb | ||
|
|
d550804cf5 | ||
|
|
d68545cb29 | ||
|
|
d01203daad | ||
|
|
5418d3048e | ||
|
|
465098cc9a | ||
|
|
e1a330a0a6 | ||
|
|
f79cd76cec | ||
|
|
aa3b97b056 | ||
|
|
8fb0620bc8 | ||
|
|
f0de34a60b | ||
|
|
1016a47b51 | ||
|
|
1d77b2c9da | ||
|
|
10a3a05e70 | ||
|
|
b023c79d5c | ||
|
|
09510479e2 | ||
|
|
0be142e4e2 | ||
|
|
0b1ba6bf43 | ||
|
|
49e0172316 | ||
|
|
74faee887e | ||
|
|
bdc2695caf | ||
|
|
32ee946670 | ||
|
|
5e0fd0b839 | ||
|
|
ab7be0b940 | ||
|
|
37e5c464d6 | ||
|
|
f445604e5e | ||
|
|
923148d841 | ||
|
|
9ec2b9cf51 | ||
|
|
7d6c150ecd | ||
|
|
a94512a3fb | ||
|
|
9e5213f074 | ||
|
|
944a128b53 | ||
|
|
fc76163dd1 | ||
|
|
98429bf89e | ||
|
|
9fe9efe465 | ||
|
|
ea2196b039 | ||
|
|
892cae5599 | ||
|
|
a25a31c105 | ||
|
|
49c8928a09 | ||
|
|
b7b14c2241 | ||
|
|
46fd732ee6 | ||
|
|
2db0cec443 | ||
|
|
162016884c | ||
|
|
e00f40537b | ||
|
|
56e653140b | ||
|
|
5f0605a5d9 | ||
|
|
58a935b31d | ||
|
|
a97fd486c3 | ||
|
|
157ce7ac69 | ||
|
|
f9eea85577 | ||
|
|
81a3bcee4f | ||
|
|
05c27ed164 | ||
|
|
b261d97ed2 | ||
|
|
538c150950 | ||
|
|
aa025b0d46 | ||
|
|
0bc3d9ebf5 | ||
|
|
0ee0790295 | ||
|
|
211e960d58 | ||
|
|
544dcde4c5 | ||
|
|
f4340da478 | ||
|
|
4c4bb65be8 | ||
|
|
bf3a3379ae | ||
|
|
d54f027f50 | ||
|
|
719917d445 | ||
|
|
96545c97d0 | ||
|
|
185546b8ea | ||
|
|
fcf041024f | ||
|
|
4dbc5412a5 | ||
|
|
4fe90ea3b3 | ||
|
|
0c4277e5b9 | ||
|
|
2813ad36b8 | ||
|
|
0de6b748bb | ||
|
|
a766279867 | ||
|
|
498683ff4c | ||
|
|
1301605db5 | ||
|
|
0992841673 | ||
|
|
f789e366f2 | ||
|
|
57c5f6cd2c | ||
|
|
4d96d2dd02 | ||
|
|
10f2e9300a | ||
|
|
798238df4d | ||
|
|
1d1fb6ca31 | ||
|
|
8656530049 | ||
|
|
fc7919e172 | ||
|
|
075986b2e3 | ||
|
|
685db33b25 | ||
|
|
90013780e0 | ||
|
|
97c201836f | ||
|
|
dd75062da7 | ||
|
|
b8e3e4994e | ||
|
|
264d33823b | ||
|
|
22d3411e6e | ||
|
|
1b6a78cd00 | ||
|
|
925c18300f | ||
|
|
b3d902e33f | ||
|
|
d8c9f10bc1 | ||
|
|
ef3d3a34e2 | ||
|
|
d29514c995 | ||
|
|
f5c2aaf28a | ||
|
|
d2484d0b8d | ||
|
|
6af7aefe00 | ||
|
|
ff808881ee | ||
|
|
1dc94277c2 | ||
|
|
8407b2dd7c | ||
|
|
d7838633e6 | ||
|
|
bf40d41c86 | ||
|
|
8b4f2a6c50 | ||
|
|
dfa5fefa7f | ||
|
|
3d5e1d2f3d | ||
|
|
14ae3cf6d6 | ||
|
|
2da22e2fc2 | ||
|
|
ff66e2635a | ||
|
|
801d769877 | ||
|
|
d57f995e87 | ||
|
|
6609f712e7 | ||
|
|
3907802e09 | ||
|
|
5288645e33 | ||
|
|
e72a10153e | ||
|
|
1066a8ca74 | ||
|
|
f245b4f5ce | ||
|
|
ed76417ba3 | ||
|
|
80195ca2a0 | ||
|
|
5b7d076a90 | ||
|
|
e60e2236ea | ||
|
|
2e97079a5e | ||
|
|
3211451b21 | ||
|
|
d0e8a1f0df | ||
|
|
c3cca85184 | ||
|
|
a2623e1352 | ||
|
|
3d8a91aab0 | ||
|
|
9f0e67a673 | ||
|
|
138aaed05d | ||
|
|
3edfc46307 | ||
|
|
be9ae57a8e | ||
|
|
4f97ea8a5d | ||
|
|
8825678ca9 | ||
|
|
70b5a9deeb | ||
|
|
eb1a21265f | ||
|
|
8845bb9b4b | ||
|
|
189e91e6ca | ||
|
|
442d06fc69 | ||
|
|
c9c76983de | ||
|
|
3c4f45bcb6 | ||
|
|
db3a6efaf3 | ||
|
|
7d3b1ad2b9 | ||
|
|
e76cdf4d71 | ||
|
|
18ac355df3 | ||
|
|
c0bf82d3ff | ||
|
|
a1f4cbc568 | ||
|
|
10c609348f | ||
|
|
88f94d5b61 | ||
|
|
92f0b31196 | ||
|
|
83e7e9db8d | ||
|
|
3f21b0b45d | ||
|
|
d4a2b3f4d1 | ||
|
|
d4a83c1c6f | ||
|
|
b0024080bd | ||
|
|
c937b88978 | ||
|
|
0f2223ddf0 | ||
|
|
364fc517cc | ||
|
|
25671e2134 | ||
|
|
1e30a3c7fe | ||
|
|
06d5ecd597 | ||
|
|
b18596fc57 | ||
|
|
7082937b62 | ||
|
|
4091ff8e36 | ||
|
|
0fa1bdf7d2 | ||
|
|
df4d71b0c8 | ||
|
|
18d5a99af5 | ||
|
|
6be176b4e3 | ||
|
|
97a0969583 | ||
|
|
a2e4ef904b | ||
|
|
f99a7a5ecd | ||
|
|
f21426d23d | ||
|
|
3f5e649295 | ||
|
|
13857d59dc | ||
|
|
260c25acf3 | ||
|
|
4ef1425299 | ||
|
|
8e48255ef8 | ||
|
|
e10868cd20 | ||
|
|
9bffe3cf24 | ||
|
|
0add43f8db | ||
|
|
cc00da9325 | ||
|
|
49d203ac57 | ||
|
|
55b3182799 | ||
|
|
4e45554585 | ||
|
|
ba735d8b57 | ||
|
|
517f4afb31 | ||
|
|
441e706746 | ||
|
|
7c4e65a5be | ||
|
|
e042152681 | ||
|
|
2e042e03b2 | ||
|
|
d6c0e67bf0 | ||
|
|
e75ff52ec1 | ||
|
|
00e7cf9a06 | ||
|
|
82f8ac50de | ||
|
|
880375a6d1 | ||
|
|
02bd9fc2d1 | ||
|
|
cbb5b6e4a5 | ||
|
|
d3bd369420 | ||
|
|
4aabe2ea5e | ||
|
|
591bfc3320 | ||
|
|
60bdeebcc6 | ||
|
|
800f3c3cb6 | ||
|
|
71b195d9a9 | ||
|
|
e3b105f1cd | ||
|
|
5c0f73c535 | ||
|
|
849e225a2d | ||
|
|
02026e0bb6 | ||
|
|
12aef95ba2 | ||
|
|
6b206e59a5 | ||
|
|
7b0e5b89ed | ||
|
|
3ef0db7512 | ||
|
|
92f9cb01f3 | ||
|
|
4abb407b5c | ||
|
|
54574e5cc3 | ||
|
|
a046cdafa3 | ||
|
|
0d70d3727d | ||
|
|
0ded20fcb7 | ||
|
|
06cec822f0 | ||
|
|
41f2420533 | ||
|
|
2f6c4e3696 | ||
|
|
d0145c6f38 | ||
|
|
d9cb45f466 | ||
|
|
d62935935f | ||
|
|
e92d27549a | ||
|
|
b9c3a11a95 | ||
|
|
33dec0f486 | ||
|
|
26df16ed13 | ||
|
|
171b2c47dd | ||
|
|
1e87707b2e | ||
|
|
b9c4b88a6b | ||
|
|
c2d902bd1e | ||
|
|
0ff4d9d0b1 | ||
|
|
118165bf72 | ||
|
|
54607aed6f | ||
|
|
36bb3957ab | ||
|
|
18a9d67e37 | ||
|
|
7906b7bc5f | ||
|
|
98b8fbc8cf | ||
|
|
61ac597cba | ||
|
|
a79284b65e | ||
|
|
40fbf1bd41 | ||
|
|
59e54917af | ||
|
|
765e160ba2 | ||
|
|
efb6de3435 | ||
|
|
fb6eada410 | ||
|
|
4247a5d0d9 | ||
|
|
e1816c165b | ||
|
|
873d40d1c3 | ||
|
|
52f736fb77 | ||
|
|
dd60106b5d | ||
|
|
340e10f765 | ||
|
|
301f3219e5 | ||
|
|
4a003878e2 | ||
|
|
834771878e | ||
|
|
ede61387c6 | ||
|
|
4c5b4f03e8 | ||
|
|
e397f18316 | ||
|
|
a600626b83 | ||
|
|
213f42a8b6 | ||
|
|
9ea66a1658 | ||
|
|
1f03ece1a4 | ||
|
|
2b71c69732 | ||
|
|
3b6e145b23 | ||
|
|
c7a4805e5c | ||
|
|
bf062fb6d4 | ||
|
|
4da4583668 | ||
|
|
9efec5b233 | ||
|
|
7d6e91f56e | ||
|
|
507b5dcfb3 | ||
|
|
8ec005f7de | ||
|
|
b5afbe385f | ||
|
|
2a5ef04397 | ||
|
|
58184679ca | ||
|
|
bf00299bc7 | ||
|
|
fc9981335b | ||
|
|
eda5ff4d3f | ||
|
|
54d74f6f0b | ||
|
|
c689c08b9a | ||
|
|
0e99f25fea | ||
|
|
a6d5bde059 | ||
|
|
b22563b3b8 | ||
|
|
72de11b8ca | ||
|
|
cae6133f7e | ||
|
|
a348df4c47 | ||
|
|
940dbbe9c3 | ||
|
|
8a42592ff7 | ||
|
|
956cde308e | ||
|
|
37c1d9bab1 | ||
|
|
e6cd193bf4 | ||
|
|
bd826bb7f9 | ||
|
|
ba676eb937 | ||
|
|
0ae6c977aa | ||
|
|
e389bf902f | ||
|
|
f6311e73cc | ||
|
|
55c512942d | ||
|
|
71cf36a300 | ||
|
|
e4e17ff606 | ||
|
|
81afecdb0e | ||
|
|
f1cb2fc6d6 | ||
|
|
96b64e1c78 | ||
|
|
4d58f2b4c7 | ||
|
|
ab9452969b | ||
|
|
aea508573b | ||
|
|
068c697be9 | ||
|
|
7a31089c4b | ||
|
|
d50fcaa94e | ||
|
|
7f8dfc17a0 | ||
|
|
fb47a04f55 | ||
|
|
da3dd1e324 | ||
|
|
c3e465d644 | ||
|
|
d8d6620c5f | ||
|
|
9853d0f6ef | ||
|
|
9d723fd487 | ||
|
|
ef7ad4f111 | ||
|
|
c59d1e67ab | ||
|
|
9ab9c0c70d | ||
|
|
f369ca39f7 | ||
|
|
804b8f38b8 | ||
|
|
dd23917e3e | ||
|
|
b604d9b47e | ||
|
|
1e5a4a6849 | ||
|
|
64656c3c98 | ||
|
|
61ba85e1f3 | ||
|
|
61ffc4220c | ||
|
|
866408015e | ||
|
|
651e815b42 | ||
|
|
645a300112 | ||
|
|
e0a3c7f2bc | ||
|
|
3dbefda6ed | ||
|
|
73eddc2386 | ||
|
|
6f9dfcc3c1 | ||
|
|
93d352f3d8 | ||
|
|
7546b080ea | ||
|
|
6988b6f034 | ||
|
|
de2cb1a3bc | ||
|
|
08f01ea1b3 | ||
|
|
0df30e43c6 | ||
|
|
67b33d9b8f | ||
|
|
42dfd0a4bb | ||
|
|
25052220a4 | ||
|
|
48e96cd399 | ||
|
|
ca016f1dd1 | ||
|
|
a4fe7dd119 | ||
|
|
8d2df468ee | ||
|
|
2830cb19fe | ||
|
|
8487b2c4af | ||
|
|
720a90fe93 | ||
|
|
623fa87d5c | ||
|
|
4ad50bf8cf | ||
|
|
efd02a015a | ||
|
|
75a2bbdfac | ||
|
|
52102ee792 | ||
|
|
58dae07b5f | ||
|
|
d0e33c748b | ||
|
|
08da58aa1e | ||
|
|
1072db632e |
2
.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[target.x86_64-pc-windows-msvc]
|
||||
rustflags = ["-C", "target-feature=+crt-static"]
|
||||
@@ -17,10 +17,10 @@
|
||||
"cli",
|
||||
"hooks",
|
||||
"i18n",
|
||||
"jotai",
|
||||
"native",
|
||||
"templates",
|
||||
"y-indexeddb",
|
||||
"y-provider",
|
||||
"debug",
|
||||
"storage",
|
||||
"infra",
|
||||
|
||||
@@ -9,3 +9,6 @@ ENABLE_NEW_SETTING_UNSTABLE_API=
|
||||
ENABLE_NOTIFICATION_CENTER=
|
||||
ENABLE_CLOUD=
|
||||
ENABLE_MOVE_DATABASE=
|
||||
SHOULD_REPORT_TRACE=
|
||||
TRACE_REPORT_ENDPOINT=
|
||||
CAPTCHA_SITE_KEY=
|
||||
@@ -7,10 +7,10 @@ affine-out
|
||||
_next
|
||||
lib
|
||||
.eslintrc.js
|
||||
packages/i18n/src/i18n-generated.ts
|
||||
e2e-dist-*
|
||||
static
|
||||
web-static
|
||||
public
|
||||
packages/sdk/src/*.d.ts
|
||||
packages/sdk/src/*.js
|
||||
packages/common/sdk/src/*.d.ts
|
||||
packages/common/sdk/src/*.js
|
||||
packages/frontend/i18n/src/i18n-generated.ts
|
||||
|
||||
77
.eslintrc.js
@@ -31,35 +31,50 @@ const createPattern = packageName => [
|
||||
message: 'Use `useNavigateHelper` instead',
|
||||
importNames: ['useNavigate'],
|
||||
},
|
||||
{
|
||||
group: ['next-auth/react'],
|
||||
message: "Import hooks from 'use-current-user.tsx'",
|
||||
// useSession is type unsafe
|
||||
importNames: ['useSession'],
|
||||
},
|
||||
{
|
||||
group: ['next-auth/react'],
|
||||
message: "Import hooks from 'cloud-utils.ts'",
|
||||
importNames: ['signIn', 'signOut'],
|
||||
},
|
||||
{
|
||||
group: ['yjs'],
|
||||
message: 'Do not use this API because it has a bug',
|
||||
importNames: ['mergeUpdates'],
|
||||
},
|
||||
{
|
||||
group: ['@affine/env/constant'],
|
||||
message:
|
||||
'Do not import from @affine/env/constant. Use `environment.isDesktop` instead',
|
||||
importNames: ['isDesktop'],
|
||||
},
|
||||
];
|
||||
|
||||
const allPackages = [
|
||||
'packages/cli',
|
||||
'packages/component',
|
||||
'packages/debug',
|
||||
'packages/env',
|
||||
'packages/graphql',
|
||||
'packages/hooks',
|
||||
'packages/i18n',
|
||||
'packages/jotai',
|
||||
'packages/native',
|
||||
'packages/infra',
|
||||
'packages/sdk',
|
||||
'packages/templates',
|
||||
'packages/theme',
|
||||
'packages/workspace',
|
||||
'packages/y-indexeddb',
|
||||
'apps/web',
|
||||
'apps/server',
|
||||
'apps/electron',
|
||||
'apps/storybook',
|
||||
'plugins/copilot',
|
||||
'plugins/bookmark-block',
|
||||
'packages/backend/server',
|
||||
'packages/frontend/component',
|
||||
'packages/frontend/web',
|
||||
'packages/frontend/electron',
|
||||
'packages/frontend/graphql',
|
||||
'packages/frontend/hooks',
|
||||
'packages/frontend/i18n',
|
||||
'packages/frontend/native',
|
||||
'packages/frontend/templates',
|
||||
'packages/frontend/workspace',
|
||||
'packages/common/debug',
|
||||
'packages/common/env',
|
||||
'packages/common/infra',
|
||||
'packages/common/sdk',
|
||||
'packages/common/theme',
|
||||
'packages/common/y-indexeddb',
|
||||
'packages/plugins/copilot',
|
||||
'tools/cli',
|
||||
'tests/storybook',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -72,7 +87,7 @@ const config = {
|
||||
version: 'detect',
|
||||
},
|
||||
next: {
|
||||
rootDir: 'apps/web',
|
||||
rootDir: 'packages/frontend/core',
|
||||
},
|
||||
},
|
||||
extends: [
|
||||
@@ -112,10 +127,12 @@ const config = {
|
||||
'no-constant-binary-expression': 'error',
|
||||
'no-constructor-return': 'error',
|
||||
'react/prop-types': 'off',
|
||||
'react/jsx-no-useless-fragment': 'error',
|
||||
'@typescript-eslint/consistent-type-imports': 'error',
|
||||
'@typescript-eslint/no-non-null-assertion': 'error',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'@typescript-eslint/await-thenable': 'error',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
@@ -160,6 +177,17 @@ const config = {
|
||||
message: 'Use `useNavigateHelper` instead',
|
||||
importNames: ['useNavigate'],
|
||||
},
|
||||
{
|
||||
group: ['next-auth/react'],
|
||||
message: "Import hooks from 'use-current-user.tsx'",
|
||||
// useSession is type unsafe
|
||||
importNames: ['useSession'],
|
||||
},
|
||||
{
|
||||
group: ['next-auth/react'],
|
||||
message: "Import hooks from 'cloud-utils.ts'",
|
||||
importNames: ['signIn', 'signOut'],
|
||||
},
|
||||
{
|
||||
group: ['yjs'],
|
||||
message: 'Do not use this API because it has a bug',
|
||||
@@ -175,6 +203,7 @@ const config = {
|
||||
ignore: ['^\\[[a-zA-Z0-9-_]+\\]\\.tsx$'],
|
||||
},
|
||||
],
|
||||
'unicorn/no-unnecessary-await': 'error',
|
||||
'sonarjs/no-all-duplicated-branches': 'error',
|
||||
'sonarjs/no-element-overwrite': 'error',
|
||||
'sonarjs/no-empty-collection': 'error',
|
||||
@@ -194,7 +223,7 @@ const config = {
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: 'apps/server/**/*.ts',
|
||||
files: 'packages/backend/server/**/*.ts',
|
||||
rules: {
|
||||
'@typescript-eslint/consistent-type-imports': 0,
|
||||
},
|
||||
@@ -225,6 +254,7 @@ const config = {
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/no-misused-promises': ['error'],
|
||||
'i/no-extraneous-dependencies': ['error'],
|
||||
},
|
||||
})),
|
||||
{
|
||||
@@ -251,6 +281,7 @@ const config = {
|
||||
],
|
||||
'@typescript-eslint/no-floating-promises': 0,
|
||||
'@typescript-eslint/no-misused-promises': 0,
|
||||
'@typescript-eslint/no-restricted-imports': 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
2
.github/CLA.md
vendored
@@ -60,3 +60,5 @@ Example:
|
||||
- Moeyua, @moeyua, 2023/04/22
|
||||
- Shishu, @shishudesu, 2023/05/19
|
||||
- Kushagra Singh, @kush002, 2023/06/28
|
||||
- Sarvesh Kumar, @sarvesh521 2023/08/25
|
||||
- 微扰理论 Qinghao Huang, @wfnuser 2023/09/29
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/BUG-REPORT.yml
vendored
@@ -26,8 +26,8 @@ body:
|
||||
- Windows x64
|
||||
- Linux
|
||||
- Web (app.affine.pro)
|
||||
- Web (stage.affine.pro)
|
||||
- Web (dev.affine.live)
|
||||
- Web (affine.fail)
|
||||
- Web (insider.affine.pro)
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
|
||||
29
.github/actions/build-rust/action.yml
vendored
@@ -4,6 +4,9 @@ inputs:
|
||||
target:
|
||||
description: 'Cargo target'
|
||||
required: true
|
||||
package:
|
||||
description: 'Package to build'
|
||||
required: true
|
||||
nx_token:
|
||||
description: 'Nx Cloud access token'
|
||||
required: false
|
||||
@@ -16,6 +19,8 @@ runs:
|
||||
with:
|
||||
toolchain: stable
|
||||
targets: ${{ inputs.target }}
|
||||
env:
|
||||
CARGO_INCREMENTAL: '1'
|
||||
|
||||
- name: Cache cargo
|
||||
uses: actions/cache@v3
|
||||
@@ -31,7 +36,7 @@ runs:
|
||||
if: ${{ inputs.target != 'x86_64-unknown-linux-gnu' && inputs.target != 'aarch64-unknown-linux-gnu' }}
|
||||
shell: bash
|
||||
run: |
|
||||
yarn nx build @affine/native --target ${{ inputs.target }}
|
||||
yarn workspace ${{ inputs.package }} nx build ${{ inputs.package }} --target ${{ inputs.target }}
|
||||
env:
|
||||
NX_CLOUD_ACCESS_TOKEN: ${{ inputs.nx_token }}
|
||||
|
||||
@@ -44,9 +49,14 @@ runs:
|
||||
run: |
|
||||
export CC=x86_64-unknown-linux-gnu-gcc
|
||||
export CC_x86_64_unknown_linux_gnu=x86_64-unknown-linux-gnu-gcc
|
||||
yarn nx build @affine/native --target ${{ inputs.target }}
|
||||
chmod -R 777 node_modules/.cache
|
||||
chmod -R 777 target
|
||||
export RUSTFLAGS="-C debuginfo=1"
|
||||
yarn workspace ${{ inputs.package }} nx build ${{ inputs.package }} --target ${{ inputs.target }}
|
||||
if [ -d "node_modules/.cache" ]; then
|
||||
chmod -R 777 node_modules/.cache
|
||||
fi
|
||||
if [ -d "target" ]; then
|
||||
chmod -R 777 target;
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
if: ${{ inputs.target == 'aarch64-unknown-linux-gnu' }}
|
||||
@@ -55,6 +65,11 @@ runs:
|
||||
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64
|
||||
options: --user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache -v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}:/build -w /build -e NX_CLOUD_ACCESS_TOKEN=${{ inputs.nx_token }}
|
||||
run: |
|
||||
yarn nx build @affine/native --target ${{ inputs.target }}
|
||||
chmod -R 777 node_modules/.cache
|
||||
chmod -R 777 target
|
||||
export RUSTFLAGS="-C debuginfo=1"
|
||||
yarn workspace ${{ inputs.package }} nx build ${{ inputs.package }} --target ${{ inputs.target }}
|
||||
if [ -d "node_modules/.cache" ]; then
|
||||
chmod -R 777 node_modules/.cache
|
||||
fi
|
||||
if [ -d "target" ]; then
|
||||
chmod -R 777 target;
|
||||
fi
|
||||
|
||||
50
.github/actions/deploy/action.yml
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
name: 'Deploy to Cluster'
|
||||
description: 'Deploy AFFiNE Cloud to cluster'
|
||||
inputs:
|
||||
build-type:
|
||||
description: 'Align with App build type, canary|beta|stable|internal'
|
||||
default: 'canary'
|
||||
gcp-project-number:
|
||||
description: 'GCP project number'
|
||||
required: true
|
||||
gcp-project-id:
|
||||
description: 'GCP project id'
|
||||
required: true
|
||||
service-account:
|
||||
description: 'Service account'
|
||||
cluster-name:
|
||||
description: 'Cluster name'
|
||||
cluster-location:
|
||||
description: 'Cluster location'
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Setup Git short hash
|
||||
shell: bash
|
||||
run: |
|
||||
echo "GIT_SHORT_HASH=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV"
|
||||
- uses: azure/setup-helm@v3
|
||||
- id: auth
|
||||
uses: google-github-actions/auth@v1
|
||||
with:
|
||||
workload_identity_provider: 'projects/${{ inputs.gcp-project-number }}/locations/global/workloadIdentityPools/github-actions/providers/github-actions-helm-deploy'
|
||||
service_account: '${{ inputs.service-account }}'
|
||||
token_format: 'access_token'
|
||||
project_id: '${{ inputs.gcp-project-id }}'
|
||||
|
||||
- name: 'Setup gcloud cli'
|
||||
uses: 'google-github-actions/setup-gcloud@v1'
|
||||
with:
|
||||
install_components: 'gke-gcloud-auth-plugin'
|
||||
|
||||
- id: get-gke-credentials
|
||||
shell: bash
|
||||
run: |
|
||||
gcloud container clusters get-credentials ${{ inputs.cluster-name }} --region ${{ inputs.cluster-location }} --project ${{ inputs.gcp-project-id }}
|
||||
|
||||
- name: Deploy
|
||||
shell: bash
|
||||
run: node ./.github/actions/deploy/deploy.mjs
|
||||
env:
|
||||
BUILD_TYPE: '${{ inputs.build-type }}'
|
||||
127
.github/actions/deploy/deploy.mjs
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
import { execSync } from 'node:child_process';
|
||||
|
||||
const {
|
||||
BUILD_TYPE,
|
||||
DEPLOY_HOST,
|
||||
CANARY_DEPLOY_HOST,
|
||||
GIT_SHORT_HASH,
|
||||
DATABASE_URL,
|
||||
DATABASE_USERNAME,
|
||||
DATABASE_PASSWORD,
|
||||
DATABASE_NAME,
|
||||
R2_ACCOUNT_ID,
|
||||
R2_ACCESS_KEY_ID,
|
||||
R2_SECRET_ACCESS_KEY,
|
||||
R2_BUCKET,
|
||||
ENABLE_CAPTCHA,
|
||||
CAPTCHA_TURNSTILE_SECRET,
|
||||
OAUTH_EMAIL_SENDER,
|
||||
OAUTH_EMAIL_LOGIN,
|
||||
OAUTH_EMAIL_PASSWORD,
|
||||
AFFINE_GOOGLE_CLIENT_ID,
|
||||
AFFINE_GOOGLE_CLIENT_SECRET,
|
||||
CLOUD_SQL_IAM_ACCOUNT,
|
||||
GCLOUD_CONNECTION_NAME,
|
||||
GCLOUD_CLOUD_SQL_INTERNAL_ENDPOINT,
|
||||
REDIS_HOST,
|
||||
REDIS_PASSWORD,
|
||||
STRIPE_API_KEY,
|
||||
STRIPE_WEBHOOK_KEY,
|
||||
} = process.env;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const buildType = BUILD_TYPE || 'canary';
|
||||
|
||||
const isProduction = buildType === 'stable';
|
||||
const isBeta = buildType === 'beta';
|
||||
|
||||
const createHelmCommand = ({ isDryRun }) => {
|
||||
const flag = isDryRun ? '--dry-run' : '--atomic';
|
||||
const imageTag = `${buildType}-${GIT_SHORT_HASH}`;
|
||||
const staticIpName = isProduction
|
||||
? 'affine-cluster-production'
|
||||
: isBeta
|
||||
? 'affine-cluster-beta'
|
||||
: 'affine-cluster-dev';
|
||||
const redisAndPostgres =
|
||||
isProduction || isBeta
|
||||
? [
|
||||
`--set-string global.database.url=${DATABASE_URL}`,
|
||||
`--set-string global.database.user=${DATABASE_USERNAME}`,
|
||||
`--set-string global.database.password=${DATABASE_PASSWORD}`,
|
||||
`--set-string global.database.name=${DATABASE_NAME}`,
|
||||
`--set global.database.gcloud.enabled=true`,
|
||||
`--set-string global.database.gcloud.connectionName="${GCLOUD_CONNECTION_NAME}"`,
|
||||
`--set-string global.database.gcloud.cloudSqlInternal="${GCLOUD_CLOUD_SQL_INTERNAL_ENDPOINT}"`,
|
||||
`--set-string global.redis.host="${REDIS_HOST}"`,
|
||||
`--set-string global.redis.password="${REDIS_PASSWORD}"`,
|
||||
]
|
||||
: [];
|
||||
const serviceAnnotations =
|
||||
isProduction || isBeta
|
||||
? [
|
||||
`--set-json web.service.annotations=\"{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }\"`,
|
||||
`--set-json graphql.serviceAccount.annotations=\"{ \\"iam.gke.io/gcp-service-account\\": \\"${CLOUD_SQL_IAM_ACCOUNT}\\" }\"`,
|
||||
`--set-json graphql.service.annotations=\"{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }\"`,
|
||||
`--set-json sync.serviceAccount.annotations=\"{ \\"iam.gke.io/gcp-service-account\\": \\"${CLOUD_SQL_IAM_ACCOUNT}\\" }\"`,
|
||||
`--set-json sync.service.annotations=\"{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }\"`,
|
||||
]
|
||||
: [];
|
||||
const webReplicaCount = isProduction ? 3 : isBeta ? 2 : 2;
|
||||
const graphqlReplicaCount = isProduction ? 10 : isBeta ? 10 : 2;
|
||||
const syncReplicaCount = isProduction ? 10 : isBeta ? 10 : 2;
|
||||
const namespace = isProduction ? 'production' : isBeta ? 'beta' : 'dev';
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const host = DEPLOY_HOST || CANARY_DEPLOY_HOST;
|
||||
const deployCommand = [
|
||||
`helm upgrade --install affine .github/helm/affine`,
|
||||
`--namespace ${namespace}`,
|
||||
`--set global.ingress.enabled=true`,
|
||||
`--set-json global.ingress.annotations=\"{ \\"kubernetes.io/ingress.class\\": \\"gce\\", \\"kubernetes.io/ingress.allow-http\\": \\"true\\", \\"kubernetes.io/ingress.global-static-ip-name\\": \\"${staticIpName}\\" }\"`,
|
||||
`--set-string global.ingress.host="${host}"`,
|
||||
...redisAndPostgres,
|
||||
`--set web.replicaCount=${webReplicaCount}`,
|
||||
`--set-string web.image.tag="${imageTag}"`,
|
||||
`--set graphql.replicaCount=${graphqlReplicaCount}`,
|
||||
`--set-string graphql.image.tag="${imageTag}"`,
|
||||
`--set graphql.app.host=${host}`,
|
||||
`--set graphql.app.captcha.enabled=${ENABLE_CAPTCHA}`,
|
||||
`--set-string graphql.app.captcha.turnstile.secret="${CAPTCHA_TURNSTILE_SECRET}"`,
|
||||
`--set graphql.app.objectStorage.r2.enabled=true`,
|
||||
`--set-string graphql.app.objectStorage.r2.accountId="${R2_ACCOUNT_ID}"`,
|
||||
`--set-string graphql.app.objectStorage.r2.accessKeyId="${R2_ACCESS_KEY_ID}"`,
|
||||
`--set-string graphql.app.objectStorage.r2.secretAccessKey="${R2_SECRET_ACCESS_KEY}"`,
|
||||
`--set-string graphql.app.objectStorage.r2.bucket="${R2_BUCKET}"`,
|
||||
`--set-string graphql.app.oauth.email.sender="${OAUTH_EMAIL_SENDER}"`,
|
||||
`--set-string graphql.app.oauth.email.login="${OAUTH_EMAIL_LOGIN}"`,
|
||||
`--set-string graphql.app.oauth.email.password="${OAUTH_EMAIL_PASSWORD}"`,
|
||||
`--set-string graphql.app.oauth.google.enabled=true`,
|
||||
`--set-string graphql.app.oauth.google.clientId="${AFFINE_GOOGLE_CLIENT_ID}"`,
|
||||
`--set-string graphql.app.oauth.google.clientSecret="${AFFINE_GOOGLE_CLIENT_SECRET}"`,
|
||||
`--set-string graphql.app.payment.stripe.apiKey="${STRIPE_API_KEY}"`,
|
||||
`--set-string graphql.app.payment.stripe.webhookKey="${STRIPE_WEBHOOK_KEY}"`,
|
||||
`--set graphql.app.experimental.enableJwstCodec=true`,
|
||||
`--set graphql.app.features.earlyAccessPreview=false`,
|
||||
`--set sync.replicaCount=${syncReplicaCount}`,
|
||||
`--set-string sync.image.tag="${imageTag}"`,
|
||||
...serviceAnnotations,
|
||||
`--version "0.0.0-${buildType}.${GIT_SHORT_HASH}" --timeout 10m`,
|
||||
flag,
|
||||
].join(' ');
|
||||
return deployCommand;
|
||||
};
|
||||
|
||||
const output = execSync(createHelmCommand({ isDryRun: true }), {
|
||||
encoding: 'utf-8',
|
||||
stdio: ['inherit', 'pipe', 'inherit'],
|
||||
});
|
||||
const templates = output
|
||||
.split('---')
|
||||
.filter(yml => !yml.split('\n').some(line => line.trim() === 'kind: Secret'))
|
||||
.join('---');
|
||||
console.log(templates);
|
||||
|
||||
execSync(createHelmCommand({ isDryRun: false }), {
|
||||
encoding: 'utf-8',
|
||||
stdio: 'inherit',
|
||||
});
|
||||
16
.github/actions/setup-maker/action.yml
vendored
@@ -1,16 +0,0 @@
|
||||
name: Setup maker
|
||||
description: 'Setup maker dmg for electron'
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: 'Install @electron-forge/maker-dmg'
|
||||
if: runner.os == 'macos'
|
||||
shell: bash
|
||||
working-directory: ./apps/electron
|
||||
run: yarn add @electron-forge/maker-dmg --dev
|
||||
env:
|
||||
HUSKY: '0'
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
|
||||
ELECTRON_SKIP_BINARY_DOWNLOAD: '1'
|
||||
SENTRYCLI_SKIP_DOWNLOAD: '1'
|
||||
53
.github/actions/setup-node/action.yml
vendored
@@ -21,6 +21,21 @@ inputs:
|
||||
description: 'set nmMode to hardlinks-local in .yarnrc.yml'
|
||||
required: false
|
||||
default: 'true'
|
||||
build-infra:
|
||||
description: 'Build infra'
|
||||
required: false
|
||||
default: 'true'
|
||||
build-plugins:
|
||||
description: 'Build plugins'
|
||||
required: false
|
||||
default: 'true'
|
||||
nmHoistingLimits:
|
||||
description: 'Set nmHoistingLimits in .yarnrc.yml'
|
||||
required: false
|
||||
enableScripts:
|
||||
description: 'Set enableScripts in .yarnrc.yml'
|
||||
required: false
|
||||
default: 'true'
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
@@ -38,26 +53,38 @@ runs:
|
||||
shell: bash
|
||||
run: yarn config set nmMode hardlinks-local
|
||||
|
||||
- name: Set nmHoistingLimits
|
||||
if: ${{ inputs.nmHoistingLimits }}
|
||||
shell: bash
|
||||
run: yarn config set nmHoistingLimits ${{ inputs.nmHoistingLimits }}
|
||||
|
||||
- name: Set enableScripts
|
||||
if: ${{ inputs.enableScripts == 'false' }}
|
||||
shell: bash
|
||||
run: yarn config set enableScripts false
|
||||
|
||||
- name: yarn install
|
||||
if: ${{ inputs.package-install == 'true' }}
|
||||
continue-on-error: true
|
||||
shell: bash
|
||||
run: yarn install ${{ inputs.extra-flags }}
|
||||
run: yarn ${{ inputs.extra-flags }}
|
||||
env:
|
||||
HUSKY: '0'
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
|
||||
ELECTRON_SKIP_BINARY_DOWNLOAD: '1'
|
||||
SENTRYCLI_SKIP_DOWNLOAD: '1'
|
||||
DEBUG: '*'
|
||||
|
||||
- name: yarn install (try again)
|
||||
if: ${{ steps.install.outcome == 'failure' }}
|
||||
shell: bash
|
||||
run: yarn install ${{ inputs.extra-flags }}
|
||||
run: yarn ${{ inputs.extra-flags }}
|
||||
env:
|
||||
HUSKY: '0'
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
|
||||
ELECTRON_SKIP_BINARY_DOWNLOAD: '1'
|
||||
SENTRYCLI_SKIP_DOWNLOAD: '1'
|
||||
DEBUG: '*'
|
||||
|
||||
- name: Get installed Playwright version
|
||||
id: playwright-version
|
||||
@@ -76,7 +103,7 @@ runs:
|
||||
if: ${{ inputs.playwright-install == 'true' }}
|
||||
with:
|
||||
path: '~/.cache/ms-playwright'
|
||||
key: '${{ runner.os }}-playwright-${{ steps.playwright-version.outputs.version }}'
|
||||
key: '${{ runner.os }}-${{ runner.arch }}-playwright-${{ steps.playwright-version.outputs.version }}'
|
||||
# As a fallback, if the Playwright version has changed, try use the
|
||||
# most recently cached version. There's a good chance that at least one
|
||||
# of the browser binary versions haven't been updated, so Playwright can
|
||||
@@ -86,14 +113,14 @@ runs:
|
||||
# date cache, but still let Playwright decide if it needs to download
|
||||
# new binaries or not.
|
||||
restore-keys: |
|
||||
${{ runner.os }}-playwright-
|
||||
${{ runner.os }}-${{ runner.arch }}-playwright-
|
||||
|
||||
# If the Playwright browser binaries weren't able to be restored, we tell
|
||||
# paywright to install everything for us.
|
||||
# playwright to install everything for us.
|
||||
- name: Install Playwright's dependencies
|
||||
shell: bash
|
||||
if: inputs.playwright-install == 'true' && steps.playwright-cache.outputs.cache-hit != 'true'
|
||||
run: yarn playwright install --with-deps
|
||||
if: inputs.playwright-install == 'true'
|
||||
run: yarn playwright install --with-deps chromium
|
||||
|
||||
- name: Get installed Electron version
|
||||
id: electron-version
|
||||
@@ -107,17 +134,23 @@ runs:
|
||||
if: ${{ inputs.electron-install == 'true' }}
|
||||
with:
|
||||
path: 'node_modules/.cache/electron'
|
||||
key: '${{ runner.os }}-electron-${{ steps.electron-version.outputs.version }}'
|
||||
key: '${{ runner.os }}-{{ runner.arch }}-electron-${{ steps.electron-version.outputs.version }}'
|
||||
restore-keys: |
|
||||
${{ runner.os }}-electron-
|
||||
${{ runner.os }}-{{ runner.arch }}-electron-
|
||||
|
||||
- name: Install Electron binary
|
||||
shell: bash
|
||||
if: inputs.electron-install == 'true'
|
||||
run: node apps/electron/node_modules/electron/install.js
|
||||
run: node ./node_modules/electron/install.js
|
||||
env:
|
||||
ELECTRON_OVERRIDE_DIST_PATH: ./node_modules/.cache/electron
|
||||
|
||||
- name: Build Infra
|
||||
shell: bash
|
||||
if: inputs.build-infra == 'true'
|
||||
run: yarn run build:infra
|
||||
|
||||
- name: Build Plugins
|
||||
if: inputs.build-plugins == 'true'
|
||||
shell: bash
|
||||
run: yarn run build:plugins
|
||||
|
||||
31
.github/actions/setup-rust/action.yml
vendored
@@ -1,31 +0,0 @@
|
||||
name: 'AFFiNE Rust setup'
|
||||
description: 'Rust setup, including cache configuration'
|
||||
inputs:
|
||||
target:
|
||||
description: 'Cargo target'
|
||||
required: true
|
||||
toolchain:
|
||||
description: 'Rustup toolchain'
|
||||
required: false
|
||||
default: 'stable'
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Setup Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ inputs.toolchain }}
|
||||
targets: ${{ inputs.target }}
|
||||
|
||||
- name: Cache cargo
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: cargo-cache-${{ runner.os }}-${{ inputs.toolchain }}-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
cargo-cache-${{ runner.os }}-${{ inputs.toolchain }}-
|
||||
22
.github/dependabot.yml
vendored
@@ -2,8 +2,30 @@ version: 2
|
||||
updates:
|
||||
- package-ecosystem: 'npm'
|
||||
directory: '/'
|
||||
groups:
|
||||
all-npm-dependencies:
|
||||
patterns:
|
||||
- '*'
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
versioning-strategy: increase
|
||||
commit-message:
|
||||
prefix: 'chore'
|
||||
- package-ecosystem: 'cargo'
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
versioning-strategy: auto
|
||||
commit-message:
|
||||
prefix: 'chore'
|
||||
groups:
|
||||
all-cargo-dependencies:
|
||||
patterns:
|
||||
- '*'
|
||||
|
||||
- package-ecosystem: 'github-actions'
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
commit-message:
|
||||
prefix: 'ci'
|
||||
|
||||
2
.github/deployment/front/Dockerfile
vendored
@@ -1,6 +1,6 @@
|
||||
FROM openresty/openresty:1.21.4.1-0-buster
|
||||
WORKDIR /app
|
||||
COPY ./apps/core/dist ./dist
|
||||
COPY ./packages/frontend/core/dist ./dist
|
||||
COPY ./.github/deployment/front/nginx.conf /usr/local/openresty/nginx/conf/nginx.conf
|
||||
COPY ./.github/deployment/front/affine.nginx.conf /etc/nginx/conf.d/affine.nginx.conf
|
||||
|
||||
|
||||
2
.github/deployment/front/affine.nginx.conf
vendored
@@ -3,7 +3,7 @@ server {
|
||||
root /app/dist;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/index.html $uri.html =404;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
error_page 404 /404.html;
|
||||
|
||||
2
.github/deployment/node/Dockerfile
vendored
@@ -1,6 +1,6 @@
|
||||
FROM node:18-bookworm-slim
|
||||
|
||||
COPY ./apps/server /app
|
||||
COPY ./packages/backend/server /app
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update && \
|
||||
|
||||
@@ -40,6 +40,7 @@ helm.sh/chart: {{ include "graphql.chart" . }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
monitoring: enabled
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
@@ -75,58 +76,3 @@ key: {{ $secret.data.private }}
|
||||
key: {{ genPrivateKey "ecdsa" | b64enc }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "objectStorage.r2" -}}
|
||||
{{- $secret := lookup "v1" "Secret" .Release.Namespace .Values.app.objectStorage.r2.secretName -}}
|
||||
{{- if $secret -}}
|
||||
{{/*
|
||||
Reusing existing secret data
|
||||
*/}}
|
||||
accountId: {{ $secret.data.accountId }}
|
||||
accessKeyId: {{ $secret.data.accessKeyId }}
|
||||
secretAccessKey: {{ $secret.data.secretAccessKey }}
|
||||
bucket: {{ $secret.data.bucket }}
|
||||
{{- else -}}
|
||||
{{/*
|
||||
Generate new data
|
||||
*/}}
|
||||
accountId: {{ .Values.app.objectStorage.r2.accountId | b64enc }}
|
||||
accessKeyId: {{ .Values.app.objectStorage.r2.accessKeyId | b64enc }}
|
||||
secretAccessKey: {{ .Values.app.objectStorage.r2.secretAccessKey | b64enc }}
|
||||
bucket: {{ .Values.app.objectStorage.r2.bucket | b64enc }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "objectStorage.oauth.google" -}}
|
||||
{{- $secret := lookup "v1" "Secret" .Release.Namespace .Values.app.oauth.google.secretName -}}
|
||||
{{- if $secret -}}
|
||||
{{/*
|
||||
Reusing existing secret data
|
||||
*/}}
|
||||
clientId: {{ $secret.data.clientId }}
|
||||
clientSecret: {{ $secret.data.clientSecret }}
|
||||
{{- else -}}
|
||||
{{/*
|
||||
Generate new data
|
||||
*/}}
|
||||
clientId: "{{ .Values.app.oauth.google.clientId | b64enc }}"
|
||||
clientSecret: "{{ .Values.app.oauth.google.clientSecret | b64enc }}"
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "objectStorage.oauth.github" -}}
|
||||
{{- $secret := lookup "v1" "Secret" .Release.Namespace .Values.app.oauth.github.secretName -}}
|
||||
{{- if $secret -}}
|
||||
{{/*
|
||||
Reusing existing secret data
|
||||
*/}}
|
||||
clientId: {{ $secret.data.clientId }}
|
||||
clientSecret: {{ $secret.data.clientSecret }}
|
||||
{{- else -}}
|
||||
{{/*
|
||||
Generate new data
|
||||
*/}}
|
||||
clientId: "{{ .Values.app.oauth.github.clientId | b64enc }}"
|
||||
clientSecret: "{{ .Values.app.oauth.github.clientSecret | b64enc }}"
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
9
.github/helm/affine/charts/graphql/templates/captcha-secret.yaml
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{{- if .Values.app.captcha.enabled -}}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: "{{ .Values.app.captcha.secretName }}"
|
||||
type: Opaque
|
||||
data:
|
||||
turnstileSecret: {{ .Values.app.captcha.turnstile.secret | b64enc }}
|
||||
{{- end }}
|
||||
@@ -35,13 +35,36 @@ spec:
|
||||
key: key
|
||||
- name: NODE_ENV
|
||||
value: "{{ .Values.env }}"
|
||||
- name: DATABSE_PASSWORD
|
||||
- name: NO_COLOR
|
||||
value: "1"
|
||||
- name: SERVER_FLAVOR
|
||||
value: "graphql"
|
||||
- name: AFFINE_ENV
|
||||
value: "{{ .Release.Namespace }}"
|
||||
- name: NEXTAUTH_URL
|
||||
value: "{{ .Values.global.ingress.host }}"
|
||||
- name: DATABASE_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: pg-postgresql
|
||||
key: postgres-password
|
||||
- name: DATABASE_URL
|
||||
value: postgres://{{ .Values.database.user }}:$(DATABSE_PASSWORD)@{{ .Values.database.url }}:{{ .Values.database.port }}/{{ .Values.database.name }}
|
||||
value: postgres://{{ .Values.global.database.user }}:$(DATABASE_PASSWORD)@{{ .Values.global.database.url }}:{{ .Values.global.database.port }}/{{ .Values.global.database.name }}
|
||||
- name: REDIS_SERVER_ENABLED
|
||||
value: "true"
|
||||
- name: REDIS_SERVER_HOST
|
||||
value: "{{ .Values.global.redis.host }}"
|
||||
- name: REDIS_SERVER_PORT
|
||||
value: "{{ .Values.global.redis.port }}"
|
||||
- name: REDIS_SERVER_USER
|
||||
value: "{{ .Values.global.redis.username }}"
|
||||
- name: REDIS_SERVER_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: redis
|
||||
key: redis-password
|
||||
- name: REDIS_SERVER_DATABASE
|
||||
value: "{{ .Values.global.redis.database }}"
|
||||
- name: AFFINE_SERVER_PORT
|
||||
value: "{{ .Values.service.port }}"
|
||||
- name: AFFINE_SERVER_SUB_PATH
|
||||
@@ -50,6 +73,51 @@ spec:
|
||||
value: "{{ .Values.app.host }}"
|
||||
- name: ENABLE_R2_OBJECT_STORAGE
|
||||
value: "{{ .Values.app.objectStorage.r2.enabled }}"
|
||||
- name: ENABLE_CAPTCHA
|
||||
value: "{{ .Values.app.captcha.enabled }}"
|
||||
- name: FEATURES_EARLY_ACCESS_PREVIEW
|
||||
value: "{{ .Values.app.features.earlyAccessPreview }}"
|
||||
- name: OAUTH_EMAIL_SENDER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: "{{ .Values.app.oauth.email.secretName }}"
|
||||
key: sender
|
||||
- name: OAUTH_EMAIL_LOGIN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: "{{ .Values.app.oauth.email.secretName }}"
|
||||
key: login
|
||||
- name: OAUTH_EMAIL_SERVER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: "{{ .Values.app.oauth.email.secretName }}"
|
||||
key: server
|
||||
- name: OAUTH_EMAIL_PORT
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: "{{ .Values.app.oauth.email.secretName }}"
|
||||
key: port
|
||||
- name: OAUTH_EMAIL_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: "{{ .Values.app.oauth.email.secretName }}"
|
||||
key: password
|
||||
- name: STRIPE_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: "{{ .Values.app.payment.stripe.secretName }}"
|
||||
key: stripeAPIKey
|
||||
- name: STRIPE_WEBHOOK_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: "{{ .Values.app.payment.stripe.secretName }}"
|
||||
key: stripeWebhookKey
|
||||
- name: DOC_MERGE_INTERVAL
|
||||
value: "{{ .Values.app.doc.mergeInterval }}"
|
||||
{{ if .Values.app.experimental.enableJwstCodec }}
|
||||
- name: DOC_MERGE_USE_JWST_CODEC
|
||||
value: "true"
|
||||
{{ end }}
|
||||
{{ if .Values.app.objectStorage.r2.enabled }}
|
||||
- name: R2_OBJECT_STORAGE_ACCOUNT_ID
|
||||
valueFrom:
|
||||
@@ -72,6 +140,13 @@ spec:
|
||||
name: "{{ .Values.app.objectStorage.r2.secretName }}"
|
||||
key: bucket
|
||||
{{ end }}
|
||||
{{ if .Values.app.captcha.enabled }}
|
||||
- name: CAPTCHA_TURNSTILE_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: "{{ .Values.app.captcha.secretName }}"
|
||||
key: turnstileSecret
|
||||
{{ end }}
|
||||
{{ if .Values.app.oauth.google.enabled }}
|
||||
- name: OAUTH_GOOGLE_CLIENT_ID
|
||||
valueFrom:
|
||||
@@ -112,6 +187,20 @@ spec:
|
||||
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
{{ if .Values.global.database.gcloud.enabled }}
|
||||
- name: cloud-sql-proxy
|
||||
image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.6.0
|
||||
args:
|
||||
- "--structured-logs"
|
||||
- "--auto-iam-authn"
|
||||
- "{{ .Values.global.database.gcloud.connectionName }}"
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
resources:
|
||||
requests:
|
||||
memory: "2Gi"
|
||||
cpu: "1"
|
||||
{{ end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
|
||||
@@ -5,27 +5,36 @@ metadata:
|
||||
labels:
|
||||
{{- include "graphql.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
"helm.sh/hook": pre-install,pre-upgrade
|
||||
"helm.sh/hook": post-install,pre-upgrade
|
||||
"helm.sh/hook-weight": "-1"
|
||||
"helm.sh/hook-delete-policy": before-hook-creation
|
||||
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
serviceAccountName: {{ include "graphql.serviceAccountName" . }}
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
command: ["yarn", "prisma", "migrate", "deploy"]
|
||||
command: ["yarn", "predeploy"]
|
||||
env:
|
||||
- name: NODE_ENV
|
||||
value: "{{ .Values.env }}"
|
||||
- name: DATABSE_PASSWORD
|
||||
- name: AFFINE_ENV
|
||||
value: "{{ .Release.Namespace }}"
|
||||
- name: DATABASE_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: pg-postgresql
|
||||
key: postgres-password
|
||||
{{ if not .Values.global.database.gcloud.enabled }}
|
||||
- name: DATABASE_URL
|
||||
value: postgres://{{ .Values.database.user }}:$(DATABSE_PASSWORD)@{{ .Values.database.url }}:{{ .Values.database.port }}/{{ .Values.database.name }}
|
||||
value: postgres://{{ .Values.global.database.user }}:$(DATABASE_PASSWORD)@{{ .Values.global.database.url }}:{{ .Values.global.database.port }}/{{ .Values.global.database.name }}
|
||||
{{ end }}
|
||||
{{ if .Values.global.database.gcloud.enabled }}
|
||||
- name: DATABASE_URL
|
||||
value: postgres://{{ .Values.global.database.user }}:$(DATABASE_PASSWORD)@{{ .Values.global.database.gcloud.cloudSqlInternal }}:{{ .Values.global.database.port }}/{{ .Values.global.database.name }}
|
||||
{{ end }}
|
||||
resources:
|
||||
requests:
|
||||
cpu: '100m'
|
||||
|
||||
13
.github/helm/affine/charts/graphql/templates/monitoring.yaml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{{- if .Values.global.gke.enabled -}}
|
||||
apiVersion: monitoring.googleapis.com/v1
|
||||
kind: PodMonitoring
|
||||
metadata:
|
||||
name: "{{ .Chart.Name }}-monitoring"
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: "{{ include "graphql.name" . }}"
|
||||
endpoints:
|
||||
- port: {{ .Values.service.port }}
|
||||
interval: 30s
|
||||
{{- end }}
|
||||
@@ -1,10 +0,0 @@
|
||||
{{- if .Values.app.oauth.github.enabled -}}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: "{{ .Values.app.oauth.github.secretName }}"
|
||||
type: Opaque
|
||||
data:
|
||||
{{- ( include "objectStorage.oauth.github" . ) | indent 2 -}}
|
||||
|
||||
{{- end }}
|
||||
@@ -1,10 +0,0 @@
|
||||
{{- if .Values.app.oauth.google.enabled -}}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: "{{ .Values.app.oauth.google.secretName }}"
|
||||
type: Opaque
|
||||
data:
|
||||
{{- ( include "objectStorage.oauth.google" . ) | indent 2 -}}
|
||||
|
||||
{{- end }}
|
||||
33
.github/helm/affine/charts/graphql/templates/oauth.yaml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: "{{ .Values.app.oauth.email.secretName }}"
|
||||
type: Opaque
|
||||
data:
|
||||
sender: "{{ .Values.app.oauth.email.sender | b64enc }}"
|
||||
login: "{{ .Values.app.oauth.email.login | b64enc }}"
|
||||
password: "{{ .Values.app.oauth.email.password | b64enc }}"
|
||||
server: "{{ .Values.app.oauth.email.server | b64enc }}"
|
||||
port: "{{ .Values.app.oauth.email.port | b64enc }}"
|
||||
---
|
||||
{{- if .Values.app.oauth.google.enabled -}}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: "{{ .Values.app.oauth.google.secretName }}"
|
||||
type: Opaque
|
||||
data:
|
||||
clientId: "{{ .Values.app.oauth.google.clientId | b64enc }}"
|
||||
clientSecret: "{{ .Values.app.oauth.google.clientSecret | b64enc }}"
|
||||
{{- end }}
|
||||
---
|
||||
{{- if .Values.app.oauth.github.enabled -}}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: "{{ .Values.app.oauth.github.secretName }}"
|
||||
type: Opaque
|
||||
data:
|
||||
clientId: "{{ .Values.app.oauth.github.clientId | b64enc }}"
|
||||
clientSecret: "{{ .Values.app.oauth.github.clientSecret | b64enc }}"
|
||||
{{- end }}
|
||||
8
.github/helm/affine/charts/graphql/templates/payment.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: "{{ .Values.app.payment.stripe.secretName }}"
|
||||
type: Opaque
|
||||
data:
|
||||
stripeAPIKey: "{{ .Values.app.payment.stripe.apiKey | b64enc }}"
|
||||
stripeWebhookKey: "{{ .Values.app.payment.stripe.webhookKey | b64enc }}"
|
||||
9
.github/helm/affine/charts/graphql/templates/pg-secret.yaml
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{{- if .Values.global.database.password -}}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: pg-postgresql
|
||||
type: Opaque
|
||||
data:
|
||||
postgres-password: {{ .Values.global.database.password | b64enc }}
|
||||
{{- end }}
|
||||
@@ -5,5 +5,8 @@ metadata:
|
||||
name: "{{ .Values.app.objectStorage.r2.secretName }}"
|
||||
type: Opaque
|
||||
data:
|
||||
{{- ( include "objectStorage.r2" . ) | indent 2 -}}
|
||||
accountId: {{ .Values.app.objectStorage.r2.accountId | b64enc }}
|
||||
accessKeyId: {{ .Values.app.objectStorage.r2.accessKeyId | b64enc }}
|
||||
secretAccessKey: {{ .Values.app.objectStorage.r2.secretAccessKey | b64enc }}
|
||||
bucket: {{ .Values.app.objectStorage.r2.bucket | b64enc }}
|
||||
{{- end }}
|
||||
|
||||
9
.github/helm/affine/charts/graphql/templates/redis-secret.yaml
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{{- if .Values.global.redis.password -}}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: redis
|
||||
type: Opaque
|
||||
data:
|
||||
redis-password: {{ .Values.global.redis.password | b64enc }}
|
||||
{{- end }}
|
||||
36
.github/helm/affine/charts/graphql/values.yaml
vendored
@@ -9,20 +9,24 @@ nameOverride: ''
|
||||
fullnameOverride: ''
|
||||
# map to NODE_ENV environment variable
|
||||
env: 'production'
|
||||
database:
|
||||
user: 'postgres'
|
||||
url: 'pg-postgresql'
|
||||
port: '5432'
|
||||
name: 'affine'
|
||||
app:
|
||||
experimental:
|
||||
enableJwstCodec: true
|
||||
# AFFINE_SERVER_SUB_PATH
|
||||
path: ''
|
||||
# AFFINE_SERVER_HOST
|
||||
host: '0.0.0.0'
|
||||
doc:
|
||||
mergeInterval: "3000"
|
||||
jwt:
|
||||
secretName: jwt-private-key
|
||||
# base64 encoded ecdsa private key
|
||||
privateKey: ''
|
||||
captcha:
|
||||
enable: false
|
||||
secretName: captcha
|
||||
turnstile:
|
||||
secret: ''
|
||||
objectStorage:
|
||||
r2:
|
||||
enabled: false
|
||||
@@ -32,6 +36,13 @@ app:
|
||||
secretAccessKey: ''
|
||||
bucket: ''
|
||||
oauth:
|
||||
email:
|
||||
secretName: 'oauth-email'
|
||||
sender: 'noreply@toeverything.info'
|
||||
login: ''
|
||||
password: ''
|
||||
server: 'smtp.gmail.com'
|
||||
port: '465'
|
||||
google:
|
||||
enabled: false
|
||||
secretName: oauth-google
|
||||
@@ -42,6 +53,13 @@ app:
|
||||
secretName: oauth-github
|
||||
clientId: ''
|
||||
clientSecret: ''
|
||||
payment:
|
||||
stripe:
|
||||
secretName: 'stripe'
|
||||
apiKey: ''
|
||||
webhookKey: ''
|
||||
features:
|
||||
earlyAccessPreview: false
|
||||
|
||||
serviceAccount:
|
||||
create: true
|
||||
@@ -55,11 +73,11 @@ podSecurityContext:
|
||||
|
||||
resources:
|
||||
limits:
|
||||
cpu: '2000m'
|
||||
memory: 4Gi
|
||||
cpu: '4'
|
||||
memory: 8Gi
|
||||
requests:
|
||||
cpu: '1000m'
|
||||
memory: 2Gi
|
||||
cpu: '2'
|
||||
memory: 4Gi
|
||||
|
||||
probe:
|
||||
initialDelaySeconds: 20
|
||||
|
||||
23
.github/helm/affine/charts/sync/.helmignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*.orig
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
.vscode/
|
||||
6
.github/helm/affine/charts/sync/Chart.yaml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
apiVersion: v2
|
||||
name: sync
|
||||
description: A Helm chart for Kubernetes
|
||||
type: application
|
||||
version: 0.0.0
|
||||
appVersion: "0.7.0-canary.18"
|
||||
16
.github/helm/affine/charts/sync/templates/NOTES.txt
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
1. Get the application URL by running these commands:
|
||||
{{- if contains "NodePort" .Values.service.type }}
|
||||
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "sync.fullname" . }})
|
||||
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
||||
echo http://$NODE_IP:$NODE_PORT
|
||||
{{- else if contains "LoadBalancer" .Values.service.type }}
|
||||
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
||||
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "sync.fullname" . }}'
|
||||
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "sync.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
|
||||
echo http://$SERVICE_IP:{{ .Values.service.port }}
|
||||
{{- else if contains "ClusterIP" .Values.service.type }}
|
||||
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "sync.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
|
||||
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
|
||||
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
|
||||
{{- end }}
|
||||
63
.github/helm/affine/charts/sync/templates/_helpers.tpl
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "sync.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
If release name contains chart name it will be used as a full name.
|
||||
*/}}
|
||||
{{- define "sync.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "sync.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "sync.labels" -}}
|
||||
helm.sh/chart: {{ include "sync.chart" . }}
|
||||
{{ include "sync.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
monitoring: enabled
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "sync.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "sync.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "sync.serviceAccountName" -}}
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
{{- default (include "sync.fullname" .) .Values.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
110
.github/helm/affine/charts/sync/templates/deployment.yaml
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "sync.fullname" . }}
|
||||
labels:
|
||||
{{- include "sync.labels" . | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "sync.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
{{- with .Values.podAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "sync.selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
serviceAccountName: {{ include "sync.serviceAccountName" . }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
env:
|
||||
- name: NODE_ENV
|
||||
value: "{{ .Values.env }}"
|
||||
- name: NO_COLOR
|
||||
value: "1"
|
||||
- name: SERVER_FLAVOR
|
||||
value: "sync"
|
||||
- name: NEXTAUTH_URL
|
||||
value: "{{ .Values.global.ingress.host }}"
|
||||
- name: AFFINE_ENV
|
||||
value: "{{ .Release.Namespace }}"
|
||||
- name: DATABASE_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: pg-postgresql
|
||||
key: postgres-password
|
||||
- name: DATABASE_URL
|
||||
value: postgres://{{ .Values.global.database.user }}:$(DATABASE_PASSWORD)@{{ .Values.global.database.url }}:{{ .Values.global.database.port }}/{{ .Values.global.database.name }}
|
||||
- name: REDIS_SERVER_ENABLED
|
||||
value: "true"
|
||||
- name: REDIS_SERVER_HOST
|
||||
value: "{{ .Values.global.redis.host }}"
|
||||
- name: REDIS_SERVER_PORT
|
||||
value: "{{ .Values.global.redis.port }}"
|
||||
- name: REDIS_SERVER_USER
|
||||
value: "{{ .Values.global.redis.username }}"
|
||||
- name: REDIS_SERVER_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: redis
|
||||
key: redis-password
|
||||
- name: REDIS_SERVER_DATABASE
|
||||
value: "{{ .Values.global.redis.database }}"
|
||||
- name: AFFINE_SERVER_PORT
|
||||
value: "{{ .Values.service.port }}"
|
||||
- name: AFFINE_SERVER_HOST
|
||||
value: "{{ .Values.app.host }}"
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: {{ .Values.service.port }}
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: http
|
||||
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: http
|
||||
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
{{ if .Values.global.database.gcloud.enabled }}
|
||||
- name: cloud-sql-proxy
|
||||
image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.6.0
|
||||
args:
|
||||
- "--structured-logs"
|
||||
- "--auto-iam-authn"
|
||||
- "{{ .Values.global.database.gcloud.connectionName }}"
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
resources:
|
||||
requests:
|
||||
memory: "2Gi"
|
||||
cpu: "1"
|
||||
{{ end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
13
.github/helm/affine/charts/sync/templates/monitoring.yaml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{{- if .Values.global.gke.enabled -}}
|
||||
apiVersion: monitoring.googleapis.com/v1
|
||||
kind: PodMonitoring
|
||||
metadata:
|
||||
name: "{{ .Chart.Name }}-monitoring"
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: "{{ include "sync.name" . }}"
|
||||
endpoints:
|
||||
- port: {{ .Values.service.port }}
|
||||
interval: 30s
|
||||
{{- end }}
|
||||
19
.github/helm/affine/charts/sync/templates/service.yaml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "sync.fullname" . }}
|
||||
labels:
|
||||
{{- include "sync.labels" . | nindent 4 }}
|
||||
{{- with .Values.service.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
{{- include "sync.selectorLabels" . | nindent 4 }}
|
||||
12
.github/helm/affine/charts/sync/templates/serviceaccount.yaml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{{- if .Values.serviceAccount.create -}}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "sync.serviceAccountName" . }}
|
||||
labels:
|
||||
{{- include "sync.labels" . | nindent 4 }}
|
||||
{{- with .Values.serviceAccount.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
15
.github/helm/affine/charts/sync/templates/tests/test-connection.yaml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: "{{ include "sync.fullname" . }}-test-connection"
|
||||
labels:
|
||||
{{- include "sync.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
"helm.sh/hook": test
|
||||
spec:
|
||||
containers:
|
||||
- name: wget
|
||||
image: busybox
|
||||
command: ['wget']
|
||||
args: ['{{ include "sync.fullname" . }}:{{ .Values.service.port }}']
|
||||
restartPolicy: Never
|
||||
39
.github/helm/affine/charts/sync/values.yaml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
replicaCount: 1
|
||||
image:
|
||||
repository: ghcr.io/toeverything/affine-graphql
|
||||
pullPolicy: IfNotPresent
|
||||
tag: ''
|
||||
|
||||
imagePullSecrets: []
|
||||
nameOverride: ''
|
||||
fullnameOverride: ''
|
||||
# map to NODE_ENV environment variable
|
||||
env: 'production'
|
||||
app:
|
||||
# AFFINE_SERVER_HOST
|
||||
host: '0.0.0.0'
|
||||
|
||||
serviceAccount:
|
||||
create: true
|
||||
annotations: {}
|
||||
name: 'affine-sync'
|
||||
|
||||
podAnnotations: {}
|
||||
|
||||
podSecurityContext:
|
||||
fsGroup: 2000
|
||||
|
||||
resources:
|
||||
limits:
|
||||
cpu: '4'
|
||||
memory: 8Gi
|
||||
requests:
|
||||
cpu: '2'
|
||||
memory: 4Gi
|
||||
|
||||
probe:
|
||||
initialDelaySeconds: 20
|
||||
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
@@ -40,6 +40,7 @@ helm.sh/chart: {{ include "web.chart" . }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
monitoring: enabled
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
|
||||
28
.github/helm/affine/templates/ingress.yaml
vendored
@@ -1,8 +1,8 @@
|
||||
{{- if .Values.ingress.enabled -}}
|
||||
{{- if .Values.global.ingress.enabled -}}
|
||||
{{- $fullName := include "affine.fullname" . -}}
|
||||
{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
|
||||
{{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
|
||||
{{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
|
||||
{{- if and .Values.global.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
|
||||
{{- if not (hasKey .Values.global.ingress.annotations "kubernetes.io/ingress.class") }}
|
||||
{{- $_ := set .Values.global.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||
@@ -17,17 +17,17 @@ metadata:
|
||||
name: {{ $fullName }}
|
||||
labels:
|
||||
{{- include "affine.labels" . | nindent 4 }}
|
||||
{{- with .Values.ingress.annotations }}
|
||||
{{- with .Values.global.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
|
||||
ingressClassName: {{ .Values.ingress.className }}
|
||||
{{- if and .Values.global.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
|
||||
ingressClassName: {{ .Values.global.ingress.className }}
|
||||
{{- end }}
|
||||
{{- if .Values.ingress.tls }}
|
||||
{{- if .Values.global.ingress.tls }}
|
||||
tls:
|
||||
{{- range .Values.ingress.tls }}
|
||||
{{- range .Values.global.ingress.tls }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
@@ -36,9 +36,16 @@ spec:
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- host: "{{ .Values.ingress.host }}"
|
||||
- host: "{{ .Values.global.ingress.host }}"
|
||||
http:
|
||||
paths:
|
||||
- path: /socket.io
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: affine-sync
|
||||
port:
|
||||
number: {{ .Values.sync.service.port }}
|
||||
- path: /graphql
|
||||
pathType: Prefix
|
||||
backend:
|
||||
@@ -60,5 +67,4 @@ spec:
|
||||
name: affine-web
|
||||
port:
|
||||
number: {{ .Values.web.service.port }}
|
||||
|
||||
{{- end }}
|
||||
|
||||
41
.github/helm/affine/values.yaml
vendored
@@ -1,16 +1,43 @@
|
||||
ingress:
|
||||
enabled: false
|
||||
className: ''
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: nginx
|
||||
host: affine.pro
|
||||
tls: []
|
||||
global:
|
||||
ingress:
|
||||
enabled: false
|
||||
className: ''
|
||||
host: affine.pro
|
||||
tls: []
|
||||
database:
|
||||
user: 'postgres'
|
||||
url: 'pg-postgresql'
|
||||
port: '5432'
|
||||
name: 'affine'
|
||||
password: ''
|
||||
gcloud:
|
||||
enabled: false
|
||||
# use for migration
|
||||
cloudSqlInternal: ''
|
||||
connectionName: ''
|
||||
serviceAccount: ''
|
||||
redis:
|
||||
enabled: true
|
||||
host: 'redis-master'
|
||||
port: '6379'
|
||||
username: ''
|
||||
password: ''
|
||||
database: 0
|
||||
gke:
|
||||
enabled: true
|
||||
|
||||
graphql:
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 3000
|
||||
|
||||
sync:
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 3010
|
||||
annotations:
|
||||
cloud.google.com/backend-config: '{"default": "affine-backendconfig"}'
|
||||
|
||||
web:
|
||||
service:
|
||||
type: ClusterIP
|
||||
|
||||
44
.github/labeler.yml
vendored
@@ -1,7 +1,7 @@
|
||||
docs:
|
||||
- 'docs/**/*'
|
||||
- '**/README.md'
|
||||
- 'packages/templates/**/*'
|
||||
- 'packages/frontend/templates/**/*'
|
||||
|
||||
test:
|
||||
- 'tests/**/*'
|
||||
@@ -10,43 +10,39 @@ test:
|
||||
|
||||
mod:dev:
|
||||
- 'scripts/**/*'
|
||||
- 'packages/cli/**/*'
|
||||
- 'packages/debug/**/*'
|
||||
- 'tools/cli/**/*'
|
||||
- 'packages/common/debug/**/*'
|
||||
|
||||
mod:plugin:
|
||||
- 'plugins/**/*'
|
||||
|
||||
plugin:bookmark-block:
|
||||
- 'plugins/bookmark-block/**/*'
|
||||
- 'packages/plugins/**/*'
|
||||
|
||||
plugin:copilot:
|
||||
- 'plugins/copilot/**/*'
|
||||
- 'packages/plugins/copilot/**/*'
|
||||
|
||||
mod:infra:
|
||||
- 'packages/infra/**/*'
|
||||
- 'packages/common/infra/**/*'
|
||||
|
||||
mod:sdk:
|
||||
- 'packages/sdk/**/*'
|
||||
- 'packages/common/sdk/**/*'
|
||||
|
||||
mod:plugin-cli:
|
||||
- 'packages/plugin-cli/**/*'
|
||||
- 'tools/plugin-cli/**/*'
|
||||
|
||||
mod:workspace: 'packages/workspace/**/*'
|
||||
mod:workspace: 'packages/frontend/workspace/**/*'
|
||||
|
||||
mod:i18n: 'packages/i18n/**/*'
|
||||
mod:i18n: 'packages/frontend/i18n/**/*'
|
||||
|
||||
mod:env: 'packages/env/**/*'
|
||||
mod:env: 'packages/common/env/**/*'
|
||||
|
||||
mod:hooks: 'packages/hooks/**/*'
|
||||
mod:hooks: 'packages/frontend/hooks/**/*'
|
||||
|
||||
mod:component: 'packages/component/**/*'
|
||||
mod:component: 'packages/frontend/component/**/*'
|
||||
|
||||
mod:storage: 'packages/storage/**/*'
|
||||
mod:storage: 'packages/backend/storage/**/*'
|
||||
|
||||
mod:native: 'packages/native/**/*'
|
||||
mod:native: 'packages/frontend/native/**/*'
|
||||
|
||||
mod:store:
|
||||
- 'packages/jotai/**/*'
|
||||
- '**/atoms/**/*'
|
||||
|
||||
rust:
|
||||
@@ -57,12 +53,10 @@ rust:
|
||||
- '**/rust-toolchain.toml'
|
||||
- '**/rustfmt.toml'
|
||||
|
||||
package:y-indexeddb: 'packages/y-indexeddb/**/*'
|
||||
package:y-indexeddb: 'packages/common/y-indexeddb/**/*'
|
||||
|
||||
app:core: 'apps/core/**/*'
|
||||
app:core: 'packages/frontend/core/**/*'
|
||||
|
||||
app:electron: 'apps/electron/**/*'
|
||||
app:electron: 'packages/frontend/electron/**/*'
|
||||
|
||||
app:server: 'apps/server/**/*'
|
||||
|
||||
app:docs: 'apps/docs/**/*'
|
||||
app:server: 'packages/backend/server/**/*'
|
||||
|
||||
189
.github/workflows/build-desktop.yml
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
name: Build(Desktop) & Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- v[0-9]+.[0-9]+.x-staging
|
||||
- v[0-9]+.[0-9]+.x
|
||||
paths-ignore:
|
||||
- README.md
|
||||
- .github/**
|
||||
- '!.github/workflows/build-desktop.yml'
|
||||
- '!.github/actions/build-rust/action.yml'
|
||||
- '!.github/actions/setup-node/action.yml'
|
||||
pull_request:
|
||||
merge_group:
|
||||
branches:
|
||||
- master
|
||||
- v[0-9]+.[0-9]+.x-staging
|
||||
- v[0-9]+.[0-9]+.x
|
||||
paths-ignore:
|
||||
- README.md
|
||||
- .github/**
|
||||
- '!.github/workflows/build-desktop.yml'
|
||||
- '!.github/actions/build-rust/action.yml'
|
||||
- '!.github/actions/setup-node/action.yml'
|
||||
|
||||
env:
|
||||
DEBUG: napi:*
|
||||
BUILD_TYPE: canary
|
||||
APP_NAME: affine
|
||||
COVERAGE: true
|
||||
DISTRIBUTION: desktop
|
||||
MACOSX_DEPLOYMENT_TARGET: '10.13'
|
||||
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
||||
|
||||
jobs:
|
||||
build-core:
|
||||
name: Build @affine/core
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
electron-install: false
|
||||
- name: Build Core
|
||||
run: yarn nx build @affine/core
|
||||
- name: Upload core artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: core
|
||||
path: ./packages/frontend/core/dist
|
||||
if-no-files-found: error
|
||||
|
||||
build-native:
|
||||
name: Build Native
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-core
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Build AFFiNE native
|
||||
uses: ./.github/actions/build-rust
|
||||
with:
|
||||
target: x86_64-unknown-linux-gnu
|
||||
package: '@affine/native'
|
||||
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
||||
- name: Run tests
|
||||
run: yarn test
|
||||
working-directory: ./packages/frontend/native
|
||||
|
||||
desktop-test:
|
||||
name: Desktop Test
|
||||
runs-on: ${{ matrix.spec.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
# all combinations: macos-latest x64, macos-latest arm64, windows-latest x64, ubuntu-latest x64
|
||||
matrix:
|
||||
spec:
|
||||
- {
|
||||
os: macos-latest,
|
||||
platform: macos,
|
||||
arch: x64,
|
||||
target: x86_64-apple-darwin,
|
||||
test: true,
|
||||
}
|
||||
- {
|
||||
os: macos-latest,
|
||||
platform: macos,
|
||||
arch: arm64,
|
||||
target: aarch64-apple-darwin,
|
||||
test: false,
|
||||
}
|
||||
- {
|
||||
os: ubuntu-latest,
|
||||
platform: linux,
|
||||
arch: x64,
|
||||
target: x86_64-unknown-linux-gnu,
|
||||
test: true,
|
||||
}
|
||||
- {
|
||||
os: windows-latest,
|
||||
platform: windows,
|
||||
arch: x64,
|
||||
target: x86_64-pc-windows-msvc,
|
||||
test: true,
|
||||
}
|
||||
needs: build-core
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
extra-flags: workspaces focus @affine/electron @affine/monorepo @affine-test/affine-desktop
|
||||
playwright-install: true
|
||||
hard-link-nm: false
|
||||
enableScripts: false
|
||||
|
||||
- name: Build AFFiNE native
|
||||
uses: ./.github/actions/build-rust
|
||||
with:
|
||||
target: ${{ matrix.spec.target }}
|
||||
package: '@affine/native'
|
||||
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
||||
|
||||
- name: Run unit tests
|
||||
if: ${{ matrix.spec.test }}
|
||||
shell: bash
|
||||
run: yarn vitest
|
||||
working-directory: packages/frontend/electron
|
||||
|
||||
- name: Download core artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: core
|
||||
path: packages/frontend/electron/resources/web-static
|
||||
|
||||
- name: Build Desktop Layers
|
||||
run: yarn workspace @affine/electron build
|
||||
|
||||
- name: Run desktop tests
|
||||
if: ${{ matrix.spec.test && matrix.spec.os == 'ubuntu-latest' }}
|
||||
run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn workspace @affine-test/affine-desktop e2e
|
||||
env:
|
||||
COVERAGE: true
|
||||
|
||||
- name: Run desktop tests
|
||||
if: ${{ matrix.spec.test && matrix.spec.os != 'ubuntu-latest' }}
|
||||
run: yarn workspace @affine-test/affine-desktop e2e
|
||||
env:
|
||||
COVERAGE: true
|
||||
|
||||
- name: Make bundle
|
||||
if: ${{ matrix.spec.os == 'macos-latest' && matrix.spec.arch == 'arm64' }}
|
||||
env:
|
||||
SKIP_BUNDLE: true
|
||||
SKIP_WEB_BUILD: true
|
||||
run: yarn workspace @affine/electron make --platform=darwin --arch=arm64
|
||||
|
||||
- name: Output check
|
||||
if: ${{ matrix.spec.os == 'macos-latest' && matrix.spec.arch == 'arm64' }}
|
||||
run: |
|
||||
yarn workspace @affine/electron ts-node ./scripts/macos-arm64-output-check.ts
|
||||
|
||||
- name: Collect code coverage report
|
||||
if: ${{ matrix.spec.test }}
|
||||
run: yarn exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov
|
||||
|
||||
- name: Upload e2e test coverage results
|
||||
if: ${{ matrix.spec.test }}
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./.coverage/lcov.info
|
||||
flags: e2etest-${{ matrix.spec.os }}-${{ matrix.spec.arch }}
|
||||
name: affine
|
||||
fail_ci_if_error: false
|
||||
|
||||
- name: Upload test results
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: test-results-e2e-${{ matrix.spec.os }}-${{ matrix.spec.arch }}
|
||||
path: ./test-results
|
||||
if-no-files-found: ignore
|
||||
311
.github/workflows/build-server.yml
vendored
Normal file
@@ -0,0 +1,311 @@
|
||||
name: Build(Server) & Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- v[0-9]+.[0-9]+.x-staging
|
||||
- v[0-9]+.[0-9]+.x
|
||||
paths-ignore:
|
||||
- README.md
|
||||
- .github/**
|
||||
- '!.github/workflows/build-server.yml'
|
||||
- '!.github/actions/build-rust/action.yml'
|
||||
- '!.github/actions/setup-node/action.yml'
|
||||
pull_request:
|
||||
merge_group:
|
||||
branches:
|
||||
- master
|
||||
- v[0-9]+.[0-9]+.x-staging
|
||||
- v[0-9]+.[0-9]+.x
|
||||
paths-ignore:
|
||||
- README.md
|
||||
- .github/**
|
||||
- '!.github/workflows/build-server.yml'
|
||||
- '!.github/actions/build-rust/action.yml'
|
||||
- '!.github/actions/setup-node/action.yml'
|
||||
|
||||
env:
|
||||
DEBUG: napi:*
|
||||
BUILD_TYPE: canary
|
||||
APP_NAME: affine
|
||||
COVERAGE: true
|
||||
DISTRIBUTION: browser
|
||||
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
||||
|
||||
jobs:
|
||||
build-storage:
|
||||
name: Build Storage
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
RUSTFLAGS: '-C debuginfo=1'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
extra-flags: workspaces focus @affine/storage
|
||||
electron-install: false
|
||||
build-infra: false
|
||||
build-plugins: false
|
||||
- name: Build Rust
|
||||
uses: ./.github/actions/build-rust
|
||||
with:
|
||||
target: 'x86_64-unknown-linux-gnu'
|
||||
package: '@affine/storage'
|
||||
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
||||
- name: Upload storage.node
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: storage.node
|
||||
path: ./packages/backend/storage/storage.node
|
||||
if-no-files-found: error
|
||||
|
||||
server-test:
|
||||
name: Server Test
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-storage
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
env:
|
||||
POSTGRES_PASSWORD: affine
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
mailer:
|
||||
image: mailhog/mailhog
|
||||
ports:
|
||||
- 1025:1025
|
||||
- 8025:8025
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
electron-install: false
|
||||
|
||||
- name: Initialize database
|
||||
run: |
|
||||
psql -h localhost -U postgres -c "CREATE DATABASE affine;"
|
||||
psql -h localhost -U postgres -c "CREATE USER affine WITH PASSWORD 'affine';"
|
||||
psql -h localhost -U postgres -c "ALTER USER affine WITH SUPERUSER;"
|
||||
env:
|
||||
PGPASSWORD: affine
|
||||
|
||||
- name: Generate prisma client
|
||||
run: |
|
||||
yarn workspace @affine/server exec prisma generate
|
||||
yarn workspace @affine/server exec prisma db push
|
||||
env:
|
||||
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||
|
||||
- name: Run init-db script
|
||||
run: yarn workspace @affine/server exec ts-node ./scripts/init-db.ts
|
||||
env:
|
||||
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||
|
||||
- name: Download storage.node
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: storage.node
|
||||
path: ./packages/backend/server
|
||||
|
||||
- name: Run server tests
|
||||
run: yarn workspace @affine/server test:coverage
|
||||
env:
|
||||
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
|
||||
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||
|
||||
- name: Upload server test coverage results
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./packages/backend/server/.coverage/lcov.info
|
||||
flags: server-test
|
||||
name: affine
|
||||
fail_ci_if_error: false
|
||||
|
||||
server-e2e-test:
|
||||
name: Server E2E Test
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-storage
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
env:
|
||||
POSTGRES_PASSWORD: affine
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
mailer:
|
||||
image: mailhog/mailhog
|
||||
ports:
|
||||
- 1025:1025
|
||||
- 8025:8025
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
playwright-install: true
|
||||
|
||||
- name: Initialize database
|
||||
run: |
|
||||
psql -h localhost -U postgres -c "CREATE DATABASE affine;"
|
||||
psql -h localhost -U postgres -c "CREATE USER affine WITH PASSWORD 'affine';"
|
||||
psql -h localhost -U postgres -c "ALTER USER affine WITH SUPERUSER;"
|
||||
env:
|
||||
PGPASSWORD: affine
|
||||
|
||||
- name: Generate prisma client
|
||||
run: |
|
||||
yarn workspace @affine/server exec prisma generate
|
||||
yarn workspace @affine/server exec prisma db push
|
||||
env:
|
||||
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||
|
||||
- name: Run init-db script
|
||||
run: yarn workspace @affine/server exec ts-node ./scripts/init-db.ts
|
||||
env:
|
||||
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||
|
||||
- name: Download storage.node
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: storage.node
|
||||
path: ./packages/backend/server
|
||||
|
||||
- name: Run playwright tests
|
||||
run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn workspace @affine-test/affine-cloud e2e --forbid-only
|
||||
env:
|
||||
COVERAGE: true
|
||||
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||
|
||||
- name: Collect code coverage report
|
||||
run: yarn exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov
|
||||
|
||||
- name: Upload e2e test coverage results
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./.coverage/lcov.info
|
||||
flags: server-e2etest
|
||||
name: affine
|
||||
fail_ci_if_error: false
|
||||
|
||||
- name: Upload test results
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: test-results-e2e-server
|
||||
path: ./tests/affine-cloud/test-results
|
||||
if-no-files-found: ignore
|
||||
|
||||
server-desktop-e2e-test:
|
||||
name: Server Desktop E2E Test
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-storage
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
env:
|
||||
POSTGRES_PASSWORD: affine
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
mailer:
|
||||
image: mailhog/mailhog
|
||||
ports:
|
||||
- 1025:1025
|
||||
- 8025:8025
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
playwright-install: true
|
||||
hard-link-nm: false
|
||||
|
||||
- name: Build AFFiNE native
|
||||
uses: ./.github/actions/build-rust
|
||||
with:
|
||||
target: x86_64-unknown-linux-gnu
|
||||
package: '@affine/native'
|
||||
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
||||
|
||||
- name: Initialize database
|
||||
run: |
|
||||
psql -h localhost -U postgres -c "CREATE DATABASE affine;"
|
||||
psql -h localhost -U postgres -c "CREATE USER affine WITH PASSWORD 'affine';"
|
||||
psql -h localhost -U postgres -c "ALTER USER affine WITH SUPERUSER;"
|
||||
env:
|
||||
PGPASSWORD: affine
|
||||
|
||||
- name: Generate prisma client
|
||||
run: |
|
||||
yarn workspace @affine/server exec prisma generate
|
||||
yarn workspace @affine/server prisma db push
|
||||
env:
|
||||
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||
|
||||
- name: Run init-db script
|
||||
run: yarn workspace @affine/server exec ts-node ./scripts/init-db.ts
|
||||
env:
|
||||
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||
|
||||
- name: Download storage.node
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: storage.node
|
||||
path: ./packages/backend/server
|
||||
|
||||
- name: Build Plugins
|
||||
run: yarn run build:plugins
|
||||
|
||||
- name: Build Desktop Layers
|
||||
run: yarn workspace @affine/electron build:dev
|
||||
|
||||
- name: Run playwright tests
|
||||
run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" yarn workspace @affine-test/affine-desktop-cloud e2e
|
||||
env:
|
||||
COVERAGE: true
|
||||
DEV_SERVER_URL: http://localhost:8080
|
||||
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||
ENABLE_LOCAL_EMAIL: true
|
||||
|
||||
- name: Collect code coverage report
|
||||
run: yarn exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov
|
||||
|
||||
- name: Upload e2e test coverage results
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./.coverage/lcov.info
|
||||
flags: server-e2etest
|
||||
name: affine
|
||||
fail_ci_if_error: false
|
||||
|
||||
- name: Upload test results
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: test-results-e2e-server
|
||||
path: ./tests/affine-cloud/test-results
|
||||
if-no-files-found: ignore
|
||||
503
.github/workflows/build.yml
vendored
@@ -29,7 +29,9 @@ env:
|
||||
DEBUG: napi:*
|
||||
BUILD_TYPE: canary
|
||||
APP_NAME: affine
|
||||
AFFINE_ENV: dev
|
||||
COVERAGE: true
|
||||
DISTRIBUTION: browser
|
||||
MACOSX_DEPLOYMENT_TARGET: '10.13'
|
||||
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
||||
|
||||
@@ -37,10 +39,12 @@ jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
environment: development
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run oxlint
|
||||
# oxlint is fast, so wrong code will fail quickly
|
||||
run: yarn dlx oxlint@latest .
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
@@ -59,221 +63,26 @@ jobs:
|
||||
- name: Run Type Check
|
||||
run: yarn typecheck
|
||||
|
||||
build-prototype:
|
||||
name: Build Prototype
|
||||
check-yarn-binary:
|
||||
name: Check yarn binary
|
||||
runs-on: ubuntu-latest
|
||||
environment: development
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
electron-install: false
|
||||
- name: Build Prototype
|
||||
run: yarn nx build prototype
|
||||
- name: Upload prototype artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: prototype
|
||||
path: ./apps/prototype/dist
|
||||
if-no-files-found: error
|
||||
|
||||
build-server:
|
||||
name: Build Server
|
||||
runs-on: ubuntu-latest
|
||||
environment: development
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
electron-install: false
|
||||
- name: Build Server
|
||||
run: yarn nx build @affine/server
|
||||
- name: Upload server dist
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: server-dist
|
||||
path: ./apps/server/dist
|
||||
if-no-files-found: error
|
||||
|
||||
build-docs:
|
||||
name: Build Docs
|
||||
runs-on: ubuntu-latest
|
||||
environment: development
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
electron-install: false
|
||||
- run: yarn nx build @affine/docs
|
||||
env:
|
||||
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
||||
|
||||
build-storybook:
|
||||
name: Build Storybook
|
||||
runs-on: ubuntu-latest
|
||||
environment: development
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
electron-install: false
|
||||
- run: yarn nx build @affine/storybook
|
||||
env:
|
||||
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
||||
- name: Upload storybook artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: storybook
|
||||
path: ./apps/storybook/storybook-static
|
||||
if-no-files-found: error
|
||||
|
||||
build-core:
|
||||
name: Build @affine/core
|
||||
runs-on: ubuntu-latest
|
||||
environment: development
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Build Plugins
|
||||
run: yarn run build:plugins
|
||||
- name: Build Core
|
||||
run: yarn nx build @affine/core
|
||||
- name: Upload core artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: core
|
||||
path: ./apps/core/dist
|
||||
if-no-files-found: error
|
||||
|
||||
build-storage:
|
||||
name: Build Storage
|
||||
runs-on: ubuntu-latest
|
||||
environment: development
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Setup Rust
|
||||
uses: ./.github/actions/setup-rust
|
||||
with:
|
||||
target: 'x86_64-unknown-linux-gnu'
|
||||
- name: Build Storage
|
||||
run: yarn build:storage
|
||||
- name: Upload storage.node
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: storage.node
|
||||
path: ./packages/storage/storage.node
|
||||
if-no-files-found: error
|
||||
|
||||
server-test:
|
||||
name: Server Test
|
||||
runs-on: ubuntu-latest
|
||||
environment: development
|
||||
needs: build-storage
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
env:
|
||||
POSTGRES_PASSWORD: affine
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Initialize database
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run check
|
||||
run: |
|
||||
psql -h localhost -U postgres -c "CREATE DATABASE affine;"
|
||||
psql -h localhost -U postgres -c "CREATE USER affine WITH PASSWORD 'affine';"
|
||||
psql -h localhost -U postgres -c "ALTER USER affine WITH SUPERUSER;"
|
||||
env:
|
||||
PGPASSWORD: affine
|
||||
- name: Generate prisma client
|
||||
run: |
|
||||
yarn exec prisma generate
|
||||
yarn exec prisma db push
|
||||
working-directory: apps/server
|
||||
env:
|
||||
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||
- name: Run init-db script
|
||||
run: yarn exec ts-node-esm ./scripts/init-db.ts
|
||||
working-directory: apps/server
|
||||
env:
|
||||
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||
- name: Download storage.node
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: storage.node
|
||||
path: ./apps/server
|
||||
- name: Run server tests
|
||||
run: yarn test:coverage
|
||||
working-directory: apps/server
|
||||
env:
|
||||
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
|
||||
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||
- name: Upload server test coverage results
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./apps/server/.coverage/lcov.info
|
||||
flags: server-test
|
||||
name: affine
|
||||
fail_ci_if_error: false
|
||||
|
||||
storybook-test:
|
||||
name: Storybook Test
|
||||
runs-on: ubuntu-latest
|
||||
environment: development
|
||||
needs: [build-storybook]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
playwright-install: true
|
||||
electron-install: false
|
||||
- name: Download storybook artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: storybook
|
||||
path: ./apps/storybook/storybook-static
|
||||
- name: Run storybook tests
|
||||
working-directory: ./apps/storybook
|
||||
run: |
|
||||
yarn exec concurrently -k -s first -n "SB,TEST" -c "magenta,blue" "yarn exec serve ./storybook-static -l 6006" "yarn exec wait-on tcp:6006 && yarn test"
|
||||
yarn set version $(node -e "console.log(require('./package.json').packageManager.split('@')[1])")
|
||||
git diff --exit-code
|
||||
|
||||
e2e-plugin-test:
|
||||
name: E2E Plugin Test
|
||||
runs-on: ubuntu-latest
|
||||
environment: development
|
||||
needs: build-core
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
playwright-install: true
|
||||
electron-install: false
|
||||
- name: Download core artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: core
|
||||
path: ./apps/core/dist
|
||||
- name: Run playwright tests
|
||||
run: yarn e2e --forbid-only
|
||||
working-directory: tests/affine-plugin
|
||||
@@ -299,49 +108,6 @@ jobs:
|
||||
path: ./test-results
|
||||
if-no-files-found: ignore
|
||||
|
||||
e2e-prototype-test:
|
||||
name: E2E Prototype Test
|
||||
runs-on: ubuntu-latest
|
||||
environment: development
|
||||
needs: build-prototype
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
playwright-install: true
|
||||
electron-install: false
|
||||
- name: Download prototype artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: prototype
|
||||
path: ./apps/prototype/dist
|
||||
- name: Run playwright tests
|
||||
run: yarn e2e --forbid-only
|
||||
working-directory: tests/affine-prototype
|
||||
env:
|
||||
COVERAGE: true
|
||||
|
||||
# - name: Collect code coverage report
|
||||
# run: yarn exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov
|
||||
|
||||
# - name: Upload e2e test coverage results
|
||||
# uses: codecov/codecov-action@v3
|
||||
# with:
|
||||
# token: ${{ secrets.CODECOV_TOKEN }}
|
||||
# files: ./.coverage/lcov.info
|
||||
# flags: e2etest-prototype
|
||||
# name: affine
|
||||
# fail_ci_if_error: false
|
||||
|
||||
- name: Upload test results
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: test-results-e2e-prototype
|
||||
path: ./test-results
|
||||
if-no-files-found: ignore
|
||||
|
||||
e2e-test:
|
||||
name: E2E Test
|
||||
runs-on: ubuntu-latest
|
||||
@@ -349,21 +115,13 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shard: [1, 2, 3, 4, 5]
|
||||
environment: development
|
||||
needs: build-core
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
playwright-install: true
|
||||
electron-install: false
|
||||
- name: Download core artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: core
|
||||
path: ./apps/core/dist
|
||||
|
||||
- name: Run playwright tests
|
||||
run: yarn e2e --forbid-only --shard=${{ matrix.shard }}/${{ strategy.job-total }}
|
||||
@@ -394,175 +152,42 @@ jobs:
|
||||
e2e-migration-test:
|
||||
name: E2E Migration Test
|
||||
runs-on: ubuntu-latest
|
||||
environment: development
|
||||
needs: build-core
|
||||
strategy:
|
||||
matrix:
|
||||
spec:
|
||||
- { package: 0.7.0-canary.18 }
|
||||
- { package: 0.8.0-canary.7 }
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
playwright-install: true
|
||||
electron-install: false
|
||||
|
||||
- name: Download core artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: core
|
||||
path: ./apps/core/dist
|
||||
|
||||
- name: Unzip
|
||||
run: yarn unzip
|
||||
working-directory: ./tests/affine-legacy/${{ matrix.spec.package }}
|
||||
|
||||
- name: Run playwright tests
|
||||
run: yarn e2e --forbid-only
|
||||
working-directory: ./tests/affine-legacy/${{ matrix.spec.package }}
|
||||
run: yarn workspace @affine-test/affine-migration e2e --forbid-only
|
||||
|
||||
- name: Upload test results
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: test-results-e2e-migration-${{ matrix.spec.package }}
|
||||
path: ./tests/affine-legacy/${{ matrix.spec.package }}/test-results
|
||||
if-no-files-found: ignore
|
||||
|
||||
desktop-test:
|
||||
name: Desktop Test
|
||||
runs-on: ${{ matrix.spec.os }}
|
||||
environment: development
|
||||
strategy:
|
||||
fail-fast: false
|
||||
# all combinations: macos-latest x64, macos-latest arm64, windows-latest x64, ubuntu-latest x64
|
||||
matrix:
|
||||
spec:
|
||||
- {
|
||||
os: macos-latest,
|
||||
platform: macos,
|
||||
arch: x64,
|
||||
target: x86_64-apple-darwin,
|
||||
test: true,
|
||||
}
|
||||
- {
|
||||
os: macos-latest,
|
||||
platform: macos,
|
||||
arch: arm64,
|
||||
target: aarch64-apple-darwin,
|
||||
test: false,
|
||||
}
|
||||
- {
|
||||
os: ubuntu-latest,
|
||||
platform: linux,
|
||||
arch: x64,
|
||||
target: x86_64-unknown-linux-gnu,
|
||||
test: true,
|
||||
}
|
||||
- {
|
||||
os: windows-latest,
|
||||
platform: windows,
|
||||
arch: x64,
|
||||
target: x86_64-pc-windows-msvc,
|
||||
test: true,
|
||||
}
|
||||
needs: build-core
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
playwright-install: true
|
||||
hard-link-nm: false
|
||||
- name: Build AFFiNE native
|
||||
uses: ./.github/actions/build-rust
|
||||
with:
|
||||
target: ${{ matrix.spec.target }}
|
||||
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
||||
- name: Run unit tests
|
||||
if: ${{ matrix.spec.test }}
|
||||
shell: bash
|
||||
run: yarn vitest
|
||||
working-directory: ./apps/electron
|
||||
|
||||
- name: Download core artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: core
|
||||
path: apps/electron/resources/web-static
|
||||
|
||||
- name: Build Plugins
|
||||
run: yarn run build:plugins
|
||||
|
||||
- name: Build Desktop Layers
|
||||
run: yarn workspace @affine/electron build
|
||||
|
||||
- name: Upload desktop dist
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: dist-${{ matrix.spec.platform }}-${{ matrix.spec.arch }}
|
||||
path: ./apps/electron/dist
|
||||
|
||||
- name: Run desktop tests
|
||||
if: ${{ matrix.spec.test && matrix.spec.os == 'ubuntu-latest' }}
|
||||
run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn workspace @affine/electron test
|
||||
env:
|
||||
COVERAGE: true
|
||||
|
||||
- name: Run desktop tests
|
||||
if: ${{ matrix.spec.test && matrix.spec.os != 'ubuntu-latest' }}
|
||||
run: yarn workspace @affine/electron test
|
||||
env:
|
||||
COVERAGE: true
|
||||
|
||||
- name: Make bundle
|
||||
if: ${{ matrix.spec.os == 'macos-latest' && matrix.spec.arch == 'arm64' }}
|
||||
env:
|
||||
SKIP_BUNDLE: true
|
||||
run: yarn workspace @affine/electron make --platform=darwin --arch=arm64
|
||||
|
||||
- name: Bundle output check
|
||||
if: ${{ matrix.spec.os == 'macos-latest' && matrix.spec.arch == 'arm64' }}
|
||||
run: |
|
||||
yarn ts-node-esm ./scripts/macos-arm64-output-check.mts
|
||||
working-directory: apps/electron
|
||||
|
||||
- name: Collect code coverage report
|
||||
if: ${{ matrix.spec.test }}
|
||||
run: yarn exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov
|
||||
|
||||
- name: Upload e2e test coverage results
|
||||
if: ${{ matrix.spec.test }}
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./.coverage/lcov.info
|
||||
flags: e2etest-${{ matrix.spec.os }}-${{ matrix.spec.arch }}
|
||||
name: affine
|
||||
fail_ci_if_error: false
|
||||
|
||||
- name: Upload test results
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: test-results-e2e-${{ matrix.spec.os }}-${{ matrix.spec.arch }}
|
||||
path: ./test-results
|
||||
name: test-results-e2e-migration
|
||||
path: ./tests/affine-migration/test-results
|
||||
if-no-files-found: ignore
|
||||
|
||||
unit-test:
|
||||
name: Unit Test
|
||||
runs-on: ubuntu-latest
|
||||
environment: development
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
electron-install: false
|
||||
|
||||
- name: Build AFFiNE native
|
||||
uses: ./.github/actions/build-rust
|
||||
with:
|
||||
target: x86_64-unknown-linux-gnu
|
||||
package: '@affine/native'
|
||||
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
||||
|
||||
- name: Unit Test
|
||||
run: yarn nx test:coverage @affine/monorepo
|
||||
|
||||
@@ -574,79 +199,3 @@ jobs:
|
||||
flags: unittest
|
||||
name: affine
|
||||
fail_ci_if_error: false
|
||||
|
||||
build-docker:
|
||||
if: github.ref == 'refs/heads/master'
|
||||
name: Build Docker
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build-server
|
||||
- build-core
|
||||
- build-storage
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Download core artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: core
|
||||
path: ./apps/core/dist
|
||||
- name: Download server dist
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: server-dist
|
||||
path: ./apps/server/dist
|
||||
- name: Download storage.node
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: storage.node
|
||||
path: ./apps/server
|
||||
- name: Setup Git short hash
|
||||
run: |
|
||||
echo "GIT_SHORT_HASH=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV"
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
logout: false
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Build front Dockerfile
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
pull: true
|
||||
platforms: linux/amd64,linux/arm64
|
||||
provenance: true
|
||||
file: .github/deployment/front/Dockerfile
|
||||
tags: ghcr.io/toeverything/affine-front:${{ env.GIT_SHORT_HASH }},ghcr.io/toeverything/affine-front:latest
|
||||
|
||||
# setup node without cache configuration
|
||||
# Prisma cache is not compatible with docker build cache
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
registry-url: https://npm.pkg.github.com
|
||||
scope: '@toeverything'
|
||||
|
||||
- name: Install Node.js dependencies
|
||||
run: yarn workspaces focus @affine/server --production
|
||||
|
||||
- name: Generate Prisma client
|
||||
run: yarn workspace @affine/server prisma generate
|
||||
|
||||
- name: Build graphql Dockerfile
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
pull: true
|
||||
platforms: linux/amd64,linux/arm64
|
||||
provenance: true
|
||||
file: .github/deployment/node/Dockerfile
|
||||
tags: ghcr.io/toeverything/affine-graphql:${{ env.GIT_SHORT_HASH }},ghcr.io/toeverything/affine-graphql:latest
|
||||
|
||||
2
.github/workflows/cache-cleanup.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Cleanup
|
||||
run: |
|
||||
|
||||
4
.github/workflows/cancel.yml
vendored
@@ -11,8 +11,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 2
|
||||
steps:
|
||||
- uses: styfle/cancel-workflow-action@0.11.0
|
||||
- uses: styfle/cancel-workflow-action@0.12.0
|
||||
with:
|
||||
# See https://api.github.com/repos/toeverything/AFFiNE/actions/workflows
|
||||
workflow_id: 44038251, 61883931, 65188160
|
||||
workflow_id: 44038251, 61883931, 65188160, 66789140
|
||||
access_token: ${{ github.token }}
|
||||
|
||||
2
.github/workflows/codeql.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
||||
242
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
name: Deploy
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
flavor:
|
||||
description: 'Build type (canary, beta, or stable)'
|
||||
type: string
|
||||
default: canary
|
||||
|
||||
env:
|
||||
BUILD_TYPE: canary
|
||||
APP_NAME: affine
|
||||
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
||||
|
||||
jobs:
|
||||
build-server:
|
||||
name: Build Server
|
||||
runs-on: ubuntu-latest
|
||||
environment: ${{ github.event.inputs.flavor }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
electron-install: false
|
||||
- name: Build Server
|
||||
run: yarn workspace @affine/server build
|
||||
- name: Upload server dist
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: server-dist
|
||||
path: ./packages/backend/server/dist
|
||||
if-no-files-found: error
|
||||
build-core:
|
||||
name: Build @affine/core
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Build Plugins
|
||||
run: yarn run build:plugins
|
||||
- name: Build Core
|
||||
run: yarn nx build @affine/core
|
||||
env:
|
||||
R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }}
|
||||
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
|
||||
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
|
||||
BUILD_TYPE_OVERRIDE: ${{ github.event.inputs.flavor }}
|
||||
SHOULD_REPORT_TRACE: true
|
||||
TRACE_REPORT_ENDPOINT: ${{ secrets.TRACE_REPORT_ENDPOINT }}
|
||||
CAPTCHA_SITE_KEY: ${{ secrets.CAPTCHA_SITE_KEY }}
|
||||
- name: Upload core artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: core
|
||||
path: ./packages/frontend/core/dist
|
||||
if-no-files-found: error
|
||||
|
||||
build-storage:
|
||||
name: Build Storage
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Setup Rust
|
||||
uses: ./.github/actions/build-rust
|
||||
with:
|
||||
target: 'x86_64-unknown-linux-gnu'
|
||||
package: '@affine/storage'
|
||||
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
||||
- name: Upload storage.node
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: storage.node
|
||||
path: ./packages/backend/storage/storage.node
|
||||
if-no-files-found: error
|
||||
|
||||
build-storage-arm64:
|
||||
name: Build Storage arm64
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Setup Rust
|
||||
uses: ./.github/actions/build-rust
|
||||
with:
|
||||
target: 'aarch64-unknown-linux-gnu'
|
||||
package: '@affine/storage'
|
||||
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
||||
- name: Upload storage.node
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: storage.arm64.node
|
||||
path: ./packages/backend/storage/storage.node
|
||||
if-no-files-found: error
|
||||
|
||||
build-docker:
|
||||
name: Build Docker
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build-server
|
||||
- build-core
|
||||
- build-storage
|
||||
- build-storage-arm64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download core artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: core
|
||||
path: ./packages/frontend/core/dist
|
||||
- name: Download server dist
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: server-dist
|
||||
path: ./packages/backend/server/dist
|
||||
- name: Download storage.node
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: storage.node
|
||||
path: ./packages/backend/server
|
||||
- name: Download storage.node arm64
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: storage.arm64.node
|
||||
path: ./packages/backend/storage
|
||||
- name: move storage.arm64.node
|
||||
run: mv ./packages/backend/storage/storage.node ./packages/backend/server/storage.arm64.node
|
||||
- name: Setup env
|
||||
run: |
|
||||
echo "GIT_SHORT_HASH=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV"
|
||||
if [ -z "${{ inputs.flavor }}" ]
|
||||
then
|
||||
echo "RELEASE_FLAVOR=canary" >> "$GITHUB_ENV"
|
||||
else
|
||||
echo "RELEASE_FLAVOR=${{ inputs.flavor }}" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
logout: false
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Build front Dockerfile
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
pull: true
|
||||
platforms: linux/amd64,linux/arm64
|
||||
provenance: true
|
||||
file: .github/deployment/front/Dockerfile
|
||||
tags: ghcr.io/toeverything/affine-front:${{env.RELEASE_FLAVOR}}-${{ env.GIT_SHORT_HASH }},ghcr.io/toeverything/affine-front:${{env.RELEASE_FLAVOR}}
|
||||
|
||||
# setup node without cache configuration
|
||||
# Prisma cache is not compatible with docker build cache
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
registry-url: https://npm.pkg.github.com
|
||||
scope: '@toeverything'
|
||||
|
||||
- name: Install Node.js dependencies
|
||||
run: |
|
||||
yarn config set --json supportedArchitectures.cpu '["x64", "arm64"]'
|
||||
yarn workspaces focus @affine/server --production
|
||||
|
||||
- name: Generate Prisma client
|
||||
run: yarn workspace @affine/server prisma generate
|
||||
|
||||
- name: Build graphql Dockerfile
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
pull: true
|
||||
platforms: linux/amd64,linux/arm64
|
||||
provenance: true
|
||||
file: .github/deployment/node/Dockerfile
|
||||
tags: ghcr.io/toeverything/affine-graphql:${{env.RELEASE_FLAVOR}}-${{ env.GIT_SHORT_HASH }},ghcr.io/toeverything/affine-graphql:${{env.RELEASE_FLAVOR}}
|
||||
|
||||
deploy:
|
||||
name: Deploy to cluster
|
||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||
environment: ${{ github.event.inputs.flavor }}
|
||||
permissions:
|
||||
contents: 'write'
|
||||
id-token: 'write'
|
||||
needs:
|
||||
- build-docker
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Deploy to dev
|
||||
uses: ./.github/actions/deploy
|
||||
with:
|
||||
build-type: ${{ github.event.inputs.flavor }}
|
||||
gcp-project-number: ${{ secrets.GCP_PROJECT_NUMBER }}
|
||||
gcp-project-id: ${{ secrets.GCP_PROJECT_ID }}
|
||||
service-account: ${{ secrets.GCP_HELM_DEPLOY_SERVICE_ACCOUNT }}
|
||||
cluster-name: ${{ secrets.GCP_CLUSTER_NAME }}
|
||||
cluster-location: ${{ secrets.GCP_CLUSTER_LOCATION }}
|
||||
env:
|
||||
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
||||
CANARY_DEPLOY_HOST: ${{ secrets.CANARY_DEPLOY_HOST }}
|
||||
R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }}
|
||||
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
|
||||
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
|
||||
R2_BUCKET: ${{ secrets.R2_BUCKET }}
|
||||
ENABLE_CAPTCHA: true
|
||||
CAPTCHA_TURNSTILE_SECRET: ${{ secrets.CAPTCHA_TURNSTILE_SECRET }}
|
||||
OAUTH_EMAIL_SENDER: ${{ secrets.OAUTH_EMAIL_SENDER }}
|
||||
OAUTH_EMAIL_LOGIN: ${{ secrets.OAUTH_EMAIL_LOGIN }}
|
||||
OAUTH_EMAIL_PASSWORD: ${{ secrets.OAUTH_EMAIL_PASSWORD }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
AFFINE_GOOGLE_CLIENT_ID: ${{ secrets.AFFINE_GOOGLE_CLIENT_ID }}
|
||||
AFFINE_GOOGLE_CLIENT_SECRET: ${{ secrets.AFFINE_GOOGLE_CLIENT_SECRET }}
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
DATABASE_USERNAME: ${{ secrets.DATABASE_USERNAME }}
|
||||
DATABASE_PASSWORD: ${{ secrets.DATABASE_PASSWORD }}
|
||||
DATABASE_NAME: ${{ secrets.DATABASE_NAME }}
|
||||
GCLOUD_CONNECTION_NAME: ${{ secrets.GCLOUD_CONNECTION_NAME }}
|
||||
GCLOUD_CLOUD_SQL_INTERNAL_ENDPOINT: ${{ secrets.GCLOUD_CLOUD_SQL_INTERNAL_ENDPOINT }}
|
||||
REDIS_HOST: ${{ secrets.REDIS_HOST }}
|
||||
REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD }}
|
||||
CLOUD_SQL_IAM_ACCOUNT: ${{ secrets.CLOUD_SQL_IAM_ACCOUNT }}
|
||||
STRIPE_API_KEY: ${{ secrets.STRIPE_API_KEY }}
|
||||
STRIPE_WEBHOOK_KEY: ${{ secrets.STRIPE_WEBHOOK_KEY }}
|
||||
30
.github/workflows/dispatch-deploy.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: Dispatch Deploy by tag
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+-canary.[0-9]+'
|
||||
|
||||
jobs:
|
||||
dispatch-deploy-by-tag:
|
||||
runs-on: ubuntu-latest
|
||||
name: Setup deploy environment
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
extra-flags: 'workspaces focus @affine/monorepo'
|
||||
hard-link-nm: false
|
||||
electron-install: false
|
||||
build-infra: false
|
||||
build-plugins: false
|
||||
- name: Setup output value
|
||||
id: flavor
|
||||
run: |
|
||||
node -e "const env = require('semver').parse('${{ github.ref_name }}').prerelease[0] ?? 'stable'; console.log(`flavor=${env}`)" >> "$GITHUB_OUTPUT"
|
||||
- name: dispatch deploy
|
||||
uses: benc-uk/workflow-dispatch@v1
|
||||
with:
|
||||
workflow: deploy.yml
|
||||
inputs: '{ "flavor": "${{ steps.flavor.outputs.flavor }}" }'
|
||||
4
.github/workflows/helm-releaser.yml
vendored
@@ -11,12 +11,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Checkout Helm chart repo
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: toeverything/helm-charts
|
||||
path: .helm-chart-repo
|
||||
|
||||
12
.github/workflows/languages-sync.yml
vendored
@@ -4,13 +4,13 @@ on:
|
||||
push:
|
||||
branches: ['master']
|
||||
paths:
|
||||
- 'packages/i18n/**'
|
||||
- 'packages/frontend/i18n/**'
|
||||
- '.github/workflows/languages-sync.yml'
|
||||
- '!.github/actions/setup-node/action.yml'
|
||||
pull_request_target:
|
||||
branches: ['master']
|
||||
paths:
|
||||
- 'packages/i18n/**'
|
||||
- 'packages/frontend/i18n/**'
|
||||
- '.github/workflows/languages-sync.yml'
|
||||
- '!.github/actions/setup-node/action.yml'
|
||||
workflow_dispatch:
|
||||
@@ -19,19 +19,17 @@ jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Check Language Key
|
||||
if: github.ref != 'refs/heads/master'
|
||||
working-directory: ./packages/i18n
|
||||
run: yarn run sync-languages:check
|
||||
run: yarn workspace @affine/i18n run sync-languages:check
|
||||
env:
|
||||
TOLGEE_API_KEY: ${{ secrets.TOLGEE_API_KEY }}
|
||||
|
||||
- name: Sync Languages
|
||||
if: github.ref == 'refs/heads/master'
|
||||
working-directory: ./packages/i18n
|
||||
run: yarn run sync-languages
|
||||
run: yarn workspace @affine/i18n run sync-languages
|
||||
env:
|
||||
TOLGEE_API_KEY: ${{ secrets.TOLGEE_API_KEY }}
|
||||
|
||||
119
.github/workflows/nightly-build.yml
vendored
@@ -1,6 +1,16 @@
|
||||
name: Build Canary Desktop App on Staging Branch
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
channel_override:
|
||||
description: 'channel type (canary, beta, or stable)'
|
||||
type: choice
|
||||
default: beta
|
||||
options:
|
||||
- canary
|
||||
- beta
|
||||
- stable
|
||||
push:
|
||||
branches:
|
||||
# 0.6.x-staging
|
||||
@@ -12,7 +22,6 @@ on:
|
||||
- .github/**
|
||||
- '!.github/workflows/nightly-build.yml'
|
||||
- '!.github/actions/build-rust/action.yml'
|
||||
- '!.github/actions/setup-rust/action.yml'
|
||||
- '!.github/actions/setup-node/action.yml'
|
||||
|
||||
permissions:
|
||||
@@ -27,27 +36,28 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
# BUILD_TYPE => app icon, app name, etc
|
||||
BUILD_TYPE: internal
|
||||
# BUILD_TYPE_OVERRIDE => channel type (canary, beta, or stable) - get the channel type (the api configs)
|
||||
BUILD_TYPE_OVERRIDE: ${{ github.event.inputs.channel_override || 'beta' }}
|
||||
|
||||
jobs:
|
||||
set-build-version:
|
||||
runs-on: ubuntu-latest
|
||||
environment: production
|
||||
outputs:
|
||||
version: 0.0.0-internal.${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: toeverything/set-build-version@latest
|
||||
- id: version
|
||||
run: echo ::set-output name=version::${{ env.BUILD_VERSION }}
|
||||
|
||||
before-make:
|
||||
runs-on: ubuntu-latest
|
||||
environment: production
|
||||
needs:
|
||||
- set-build-version
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Setup @sentry/cli
|
||||
@@ -55,7 +65,7 @@ jobs:
|
||||
- name: Replace Version
|
||||
run: ./scripts/set-version.sh ${{ needs.set-build-version.outputs.version }}
|
||||
- name: generate-assets
|
||||
working-directory: apps/electron
|
||||
working-directory: packages/frontend/electron
|
||||
run: yarn generate-assets
|
||||
env:
|
||||
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
||||
@@ -68,39 +78,31 @@ jobs:
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: core
|
||||
path: apps/electron/resources/web-static
|
||||
path: packages/frontend/electron/resources/web-static
|
||||
|
||||
make-distribution:
|
||||
environment: production
|
||||
strategy:
|
||||
# all combinations: macos-latest x64, macos-latest arm64, windows-latest x64, ubuntu-latest x64
|
||||
# all combinations: macos-latest x64, macos-latest arm64, ubuntu-latest x64
|
||||
# For windows, we need a separate approach
|
||||
matrix:
|
||||
spec:
|
||||
- {
|
||||
os: macos-latest,
|
||||
platform: darwin,
|
||||
arch: x64,
|
||||
target: x86_64-apple-darwin,
|
||||
}
|
||||
- {
|
||||
os: macos-latest,
|
||||
platform: darwin,
|
||||
arch: arm64,
|
||||
target: aarch64-apple-darwin,
|
||||
}
|
||||
- {
|
||||
os: ubuntu-latest,
|
||||
platform: linux,
|
||||
arch: x64,
|
||||
target: x86_64-unknown-linux-gnu,
|
||||
}
|
||||
- {
|
||||
os: windows-latest,
|
||||
platform: win32,
|
||||
arch: x64,
|
||||
target: x86_64-pc-windows-msvc,
|
||||
}
|
||||
runs-on: ${{ matrix.spec.os }}
|
||||
- runner: macos-latest
|
||||
platform: darwin
|
||||
arch: x64
|
||||
target: x86_64-apple-darwin
|
||||
- runner: macos-latest
|
||||
platform: darwin
|
||||
arch: arm64
|
||||
target: aarch64-apple-darwin
|
||||
- runner: ubuntu-latest
|
||||
platform: linux
|
||||
arch: x64
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- runner: windows-latest
|
||||
platform: win32
|
||||
arch: x64
|
||||
target: x86_64-pc-windows-msvc
|
||||
runs-on: ${{ matrix.spec.runner }}
|
||||
needs:
|
||||
- before-make
|
||||
- set-build-version
|
||||
@@ -110,24 +112,38 @@ jobs:
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
SKIP_GENERATE_ASSETS: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
timeout-minutes: 10
|
||||
if: ${{ matrix.spec.platform == 'darwin' }}
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Setup Maker
|
||||
with:
|
||||
extra-flags: workspaces focus @affine/electron @affine/monorepo
|
||||
hard-link-nm: false
|
||||
build-plugins: false
|
||||
nmHoistingLimits: workspaces
|
||||
enableScripts: false
|
||||
- name: Setup Node.js
|
||||
timeout-minutes: 10
|
||||
uses: ./.github/actions/setup-maker
|
||||
if: ${{ matrix.spec.platform != 'darwin' }}
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
extra-flags: workspaces focus @affine/electron @affine/monorepo
|
||||
hard-link-nm: false
|
||||
build-plugins: false
|
||||
nmHoistingLimits: workspaces
|
||||
- name: Build AFFiNE native
|
||||
uses: ./.github/actions/build-rust
|
||||
with:
|
||||
target: ${{ matrix.spec.target }}
|
||||
package: '@affine/native'
|
||||
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
||||
- name: Replace Version
|
||||
run: ./scripts/set-version.sh ${{ needs.set-build-version.outputs.version }}
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: core
|
||||
path: apps/electron/resources/web-static
|
||||
path: packages/frontend/electron/resources/web-static
|
||||
|
||||
- name: Build Plugins
|
||||
run: yarn run build:plugins
|
||||
@@ -144,28 +160,32 @@ jobs:
|
||||
|
||||
- name: make
|
||||
run: yarn workspace @affine/electron make --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
|
||||
env:
|
||||
SKIP_PLUGIN_BUILD: 1
|
||||
SKIP_WEB_BUILD: 1
|
||||
HOIST_NODE_MODULES: 1
|
||||
|
||||
- name: Save artifacts (mac)
|
||||
if: ${{ matrix.spec.platform == 'darwin' }}
|
||||
run: |
|
||||
mkdir -p builds
|
||||
mv apps/electron/out/*/make/*.dmg ./builds/affine-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.dmg
|
||||
mv apps/electron/out/*/make/zip/darwin/${{ matrix.spec.arch }}/*.zip ./builds/affine-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.zip
|
||||
mv packages/frontend/electron/out/*/make/*.dmg ./builds/affine-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.dmg
|
||||
mv packages/frontend/electron/out/*/make/zip/darwin/${{ matrix.spec.arch }}/*.zip ./builds/affine-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.zip
|
||||
- name: Save artifacts (windows)
|
||||
if: ${{ matrix.spec.platform == 'win32' }}
|
||||
run: |
|
||||
mkdir -p builds
|
||||
mv apps/electron/out/*/make/zip/win32/x64/AFFiNE*-win32-x64-*.zip ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.zip
|
||||
mv apps/electron/out/*/make/squirrel.windows/x64/*.exe ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.exe
|
||||
mv apps/electron/out/*/make/squirrel.windows/x64/*.msi ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.msi
|
||||
mv apps/electron/out/*/make/squirrel.windows/x64/*.nupkg ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.nupkg
|
||||
mv packages/frontend/electron/out/*/make/zip/win32/x64/AFFiNE*-win32-x64-*.zip ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.zip
|
||||
mv packages/frontend/electron/out/*/make/squirrel.windows/x64/*.exe ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.exe
|
||||
mv packages/frontend/electron/out/*/make/squirrel.windows/x64/*.msi ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.msi
|
||||
mv packages/frontend/electron/out/*/make/squirrel.windows/x64/*.nupkg ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.nupkg
|
||||
|
||||
- name: Save artifacts (linux)
|
||||
if: ${{ matrix.spec.platform == 'linux' }}
|
||||
run: |
|
||||
mkdir -p builds
|
||||
mv apps/electron/out/*/make/zip/linux/x64/*.zip ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.zip
|
||||
mv apps/electron/out/*/make/AppImage/x64/*.AppImage ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.AppImage
|
||||
mv packages/frontend/electron/out/*/make/zip/linux/x64/*.zip ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.zip
|
||||
mv packages/frontend/electron/out/*/make/AppImage/x64/*.AppImage ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.AppImage
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
@@ -180,7 +200,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download Artifacts (macos-x64)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
@@ -202,13 +222,12 @@ jobs:
|
||||
name: affine-linux-x64-builds
|
||||
path: ./
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Generate Release yml
|
||||
run: |
|
||||
cp ./apps/electron/scripts/generate-yml.js .
|
||||
node generate-yml.js
|
||||
node ./packages/frontend/electron/scripts/generate-yml.js
|
||||
env:
|
||||
RELEASE_VERSION: ${{ needs.set-build-version.outputs.version }}
|
||||
- name: Create Release Draft
|
||||
|
||||
54
.github/workflows/nx.yml
vendored
@@ -1,54 +0,0 @@
|
||||
name: NX
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- v[0-9]+.[0-9]+.x-staging
|
||||
- v[0-9]+.[0-9]+.x
|
||||
paths-ignore:
|
||||
- README.md
|
||||
- .github/**
|
||||
- '!.github/workflows/nx.yml'
|
||||
- '!.github/actions/build-rust/action.yml'
|
||||
- '!.github/actions/setup-node/action.yml'
|
||||
pull_request:
|
||||
merge_group:
|
||||
branches:
|
||||
- master
|
||||
- v[0-9]+.[0-9]+.x-staging
|
||||
- v[0-9]+.[0-9]+.x
|
||||
paths-ignore:
|
||||
- README.md
|
||||
- .github/**
|
||||
- '!.github/workflows/nx.yml'
|
||||
- '!.github/actions/build-rust/action.yml'
|
||||
- '!.github/actions/setup-node/action.yml'
|
||||
|
||||
jobs:
|
||||
main:
|
||||
name: Nx Cloud - Main Job
|
||||
uses: nrwl/ci/.github/workflows/nx-cloud-main.yml@v0.13.0
|
||||
with:
|
||||
runs-on: macos-latest
|
||||
main-branch-name: master
|
||||
number-of-agents: 5
|
||||
init-commands: |
|
||||
yarn exec nx-cloud start-ci-run --stop-agents-after="build" --agent-count=5
|
||||
environment-variables: |
|
||||
BUILD_TYPE=canary
|
||||
# parallel-commands: |
|
||||
# yarn exec nx-cloud record -- yarn exec nx format:check
|
||||
parallel-commands-on-agents: |
|
||||
yarn exec nx affected --target=build --parallel=5
|
||||
timeout: 60
|
||||
|
||||
agents:
|
||||
name: Nx Cloud - Agents
|
||||
uses: nrwl/ci/.github/workflows/nx-cloud-agents.yml@v0.13.0
|
||||
with:
|
||||
runs-on: macos-latest
|
||||
number-of-agents: 5
|
||||
environment-variables: |
|
||||
BUILD_TYPE=canary
|
||||
timeout: 60
|
||||
2
.github/workflows/pr-auto-assign.yml
vendored
@@ -9,4 +9,4 @@ jobs:
|
||||
add-reviews:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: kentaro-m/auto-assign-action@v1.2.4
|
||||
- uses: kentaro-m/auto-assign-action@v1.2.5
|
||||
|
||||
6
.github/workflows/pr-title-lint.yml
vendored
@@ -17,7 +17,9 @@ jobs:
|
||||
name: Check pull request title
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- run: echo "${{ github.event.pull_request.title }}" | npx commitlint -g ./.commitlintrc.json
|
||||
with:
|
||||
electron-install: false
|
||||
- run: echo "${{ github.event.pull_request.title }}" | yarn dlx commitlint -g ./.commitlintrc.json
|
||||
|
||||
53
.github/workflows/publish-storybook.yml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
name: Publish Storybook
|
||||
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=4096
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- README.md
|
||||
- .github/**
|
||||
- packages/backend/server
|
||||
- packages/frontend/electron
|
||||
- '!.github/workflows/publish-storybook.yml'
|
||||
|
||||
jobs:
|
||||
publish-storybook:
|
||||
name: Publish Storybook
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.merge_commit_sha }}
|
||||
# This is required to fetch all commits for chromatic
|
||||
fetch-depth: 0
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
electron-install: false
|
||||
- name: Build Plugins
|
||||
run: yarn run build:plugins
|
||||
- uses: chromaui/action-next@v1
|
||||
with:
|
||||
workingDir: tests/storybook
|
||||
buildScriptName: build
|
||||
exitOnceUploaded: true
|
||||
onlyChanged: false
|
||||
diagnostics: true
|
||||
env:
|
||||
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
||||
NODE_OPTIONS: ${{ env.NODE_OPTIONS }}
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: chromatic-build-artifacts-${{ github.run_id }}
|
||||
path: |
|
||||
chromatic-diagnostics.json
|
||||
**/build-storybook.log
|
||||
271
.github/workflows/release-desktop-app.yml
vendored
@@ -40,11 +40,10 @@ env:
|
||||
jobs:
|
||||
before-make:
|
||||
runs-on: ubuntu-latest
|
||||
environment: production
|
||||
outputs:
|
||||
RELEASE_VERSION: ${{ steps.get-canary-version.outputs.RELEASE_VERSION }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Setup @sentry/cli
|
||||
@@ -54,12 +53,12 @@ jobs:
|
||||
if: ${{ github.ref_type == 'tag' }}
|
||||
run: |
|
||||
TAG_VERSION=${GITHUB_REF#refs/tags/v}
|
||||
PACKAGE_VERSION=$(node -p "require('./apps/electron/package.json').version")
|
||||
PACKAGE_VERSION=$(node -p "require('./packages/frontend/electron/package.json').version")
|
||||
if [ "$TAG_VERSION" != "$PACKAGE_VERSION" ]; then
|
||||
echo "Tag version ($TAG_VERSION) does not match package.json version ($PACKAGE_VERSION)"
|
||||
exit 1
|
||||
fi
|
||||
echo "RELEASE_VERSION=$(node -p "require('./apps/electron/package.json').version")" >> $GITHUB_OUTPUT
|
||||
echo "RELEASE_VERSION=$(node -p "require('./packages/frontend/electron/package.json').version")" >> $GITHUB_OUTPUT
|
||||
- name: generate-assets
|
||||
run: yarn workspace @affine/electron generate-assets
|
||||
env:
|
||||
@@ -67,44 +66,33 @@ jobs:
|
||||
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
RELEASE_VERSION: ${{ github.event.inputs.version || steps.get-canary-version.outputs.RELEASE_VERSION }}
|
||||
SKIP_PLUGIN_BUILD: 'true'
|
||||
|
||||
- name: Upload core artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: core
|
||||
path: apps/electron/resources/web-static
|
||||
path: packages/frontend/electron/resources/web-static
|
||||
|
||||
make-distribution:
|
||||
environment: production
|
||||
strategy:
|
||||
# all combinations: macos-latest x64, macos-latest arm64, windows-latest x64, ubuntu-latest x64
|
||||
# all combinations: macos-latest x64, macos-latest arm64, ubuntu-latest x64
|
||||
# For windows, we need a separate approach
|
||||
matrix:
|
||||
spec:
|
||||
- {
|
||||
os: macos-latest,
|
||||
platform: darwin,
|
||||
arch: x64,
|
||||
target: x86_64-apple-darwin,
|
||||
}
|
||||
- {
|
||||
os: macos-latest,
|
||||
platform: darwin,
|
||||
arch: arm64,
|
||||
target: aarch64-apple-darwin,
|
||||
}
|
||||
- {
|
||||
os: ubuntu-latest,
|
||||
platform: linux,
|
||||
arch: x64,
|
||||
target: x86_64-unknown-linux-gnu,
|
||||
}
|
||||
- {
|
||||
os: windows-latest,
|
||||
platform: win32,
|
||||
arch: x64,
|
||||
target: x86_64-pc-windows-msvc,
|
||||
}
|
||||
runs-on: ${{ matrix.spec.os }}
|
||||
- runner: macos-latest
|
||||
platform: darwin
|
||||
arch: x64
|
||||
target: x86_64-apple-darwin
|
||||
- runner: macos-latest
|
||||
platform: darwin
|
||||
arch: arm64
|
||||
target: aarch64-apple-darwin
|
||||
- runner: ubuntu-latest
|
||||
platform: linux
|
||||
arch: x64
|
||||
target: x86_64-unknown-linux-gnu
|
||||
runs-on: ${{ matrix.spec.runner }}
|
||||
needs: before-make
|
||||
env:
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
@@ -112,25 +100,26 @@ jobs:
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
SKIP_GENERATE_ASSETS: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
timeout-minutes: 10
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Setup Maker
|
||||
timeout-minutes: 10
|
||||
uses: ./.github/actions/setup-maker
|
||||
with:
|
||||
extra-flags: workspaces focus @affine/electron @affine/monorepo
|
||||
hard-link-nm: false
|
||||
build-plugins: false
|
||||
nmHoistingLimits: workspaces
|
||||
enableScripts: false
|
||||
- name: Build AFFiNE native
|
||||
uses: ./.github/actions/build-rust
|
||||
with:
|
||||
target: ${{ matrix.spec.target }}
|
||||
package: '@affine/native'
|
||||
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: core
|
||||
path: apps/electron/resources/web-static
|
||||
|
||||
- name: Build Plugins
|
||||
run: yarn run build:plugins
|
||||
path: packages/frontend/electron/resources/web-static
|
||||
|
||||
- name: Build Desktop Layers
|
||||
run: yarn workspace @affine/electron build
|
||||
@@ -144,28 +133,185 @@ jobs:
|
||||
|
||||
- name: make
|
||||
run: yarn workspace @affine/electron make --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
|
||||
env:
|
||||
SKIP_PLUGIN_BUILD: 1
|
||||
SKIP_WEB_BUILD: 1
|
||||
HOIST_NODE_MODULES: 1
|
||||
|
||||
- name: Save artifacts (mac)
|
||||
if: ${{ matrix.spec.platform == 'darwin' }}
|
||||
run: |
|
||||
mkdir -p builds
|
||||
mv apps/electron/out/*/make/*.dmg ./builds/affine-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.dmg
|
||||
mv apps/electron/out/*/make/zip/darwin/${{ matrix.spec.arch }}/*.zip ./builds/affine-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.zip
|
||||
- name: Save artifacts (windows)
|
||||
if: ${{ matrix.spec.platform == 'win32' }}
|
||||
run: |
|
||||
mkdir -p builds
|
||||
mv apps/electron/out/*/make/zip/win32/x64/AFFiNE*-win32-x64-*.zip ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.zip
|
||||
mv apps/electron/out/*/make/squirrel.windows/x64/*.exe ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.exe
|
||||
mv apps/electron/out/*/make/squirrel.windows/x64/*.msi ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.msi
|
||||
mv apps/electron/out/*/make/squirrel.windows/x64/*.nupkg ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.nupkg
|
||||
|
||||
mv packages/frontend/electron/out/*/make/*.dmg ./builds/affine-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.dmg
|
||||
mv packages/frontend/electron/out/*/make/zip/darwin/${{ matrix.spec.arch }}/*.zip ./builds/affine-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.zip
|
||||
- name: Save artifacts (linux)
|
||||
if: ${{ matrix.spec.platform == 'linux' }}
|
||||
run: |
|
||||
mkdir -p builds
|
||||
mv apps/electron/out/*/make/zip/linux/x64/*.zip ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.zip
|
||||
mv apps/electron/out/*/make/AppImage/x64/*.AppImage ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.AppImage
|
||||
mv packages/frontend/electron/out/*/make/zip/linux/x64/*.zip ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.zip
|
||||
mv packages/frontend/electron/out/*/make/AppImage/x64/*.AppImage ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.AppImage
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: affine-${{ matrix.spec.platform }}-${{ matrix.spec.arch }}-builds
|
||||
path: builds
|
||||
|
||||
package-distribution-windows:
|
||||
strategy:
|
||||
# all combinations: macos-latest x64, macos-latest arm64, ubuntu-latest x64
|
||||
# For windows, we need a separate approach
|
||||
matrix:
|
||||
spec:
|
||||
- runner: windows-latest
|
||||
platform: win32
|
||||
arch: x64
|
||||
target: x86_64-pc-windows-msvc
|
||||
runs-on: ${{ matrix.spec.runner }}
|
||||
needs: before-make
|
||||
outputs:
|
||||
FILES_TO_BE_SIGNED: ${{ steps.get_files_to_be_signed.outputs.FILES_TO_BE_SIGNED }}
|
||||
env:
|
||||
SKIP_GENERATE_ASSETS: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
timeout-minutes: 10
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
extra-flags: workspaces focus @affine/electron @affine/monorepo
|
||||
hard-link-nm: false
|
||||
build-plugins: false
|
||||
nmHoistingLimits: workspaces
|
||||
- name: Build AFFiNE native
|
||||
uses: ./.github/actions/build-rust
|
||||
with:
|
||||
target: ${{ matrix.spec.target }}
|
||||
package: '@affine/native'
|
||||
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: core
|
||||
path: packages/frontend/electron/resources/web-static
|
||||
|
||||
- name: Build Plugins
|
||||
run: yarn run build:plugins
|
||||
|
||||
- name: Build Desktop Layers
|
||||
run: yarn workspace @affine/electron build
|
||||
|
||||
- name: package
|
||||
run: yarn workspace @affine/electron package --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
|
||||
env:
|
||||
SKIP_PLUGIN_BUILD: 1
|
||||
SKIP_WEB_BUILD: 1
|
||||
HOIST_NODE_MODULES: 1
|
||||
|
||||
- name: get all files to be signed
|
||||
id: get_files_to_be_signed
|
||||
run: |
|
||||
Set-Variable -Name FILES_TO_BE_SIGNED -Value ((Get-ChildItem -Path packages/frontend/electron/out -Recurse -File | Where-Object { $_.Extension -in @(".exe", ".node", ".dll", ".msi") } | ForEach-Object { '"' + $_.FullName.Replace((Get-Location).Path + '\packages\frontend\electron\out\', '') + '"' }) -join ' ')
|
||||
"FILES_TO_BE_SIGNED=$FILES_TO_BE_SIGNED" >> $env:GITHUB_OUTPUT
|
||||
echo $FILES_TO_BE_SIGNED
|
||||
|
||||
- name: Zip artifacts for faster upload
|
||||
run: Compress-Archive -CompressionLevel Fastest -Path packages/frontend/electron/out/* -DestinationPath archive.zip
|
||||
|
||||
- name: Save packaged artifacts for signing
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: packaged-${{ matrix.spec.platform }}-${{ matrix.spec.arch }}
|
||||
path: |
|
||||
archive.zip
|
||||
!**/*.map
|
||||
|
||||
sign-packaged-artifacts-windows:
|
||||
needs: package-distribution-windows
|
||||
uses: ./.github/workflows/windows-signer.yml
|
||||
with:
|
||||
files: ${{ needs.package-distribution-windows.outputs.FILES_TO_BE_SIGNED }}
|
||||
artifact-name: packaged-win32-x64
|
||||
|
||||
make-windows-installer:
|
||||
needs: sign-packaged-artifacts-windows
|
||||
strategy:
|
||||
# all combinations: macos-latest x64, macos-latest arm64, ubuntu-latest x64
|
||||
# For windows, we need a separate approach
|
||||
matrix:
|
||||
spec:
|
||||
- runner: windows-latest
|
||||
platform: win32
|
||||
arch: x64
|
||||
target: x86_64-pc-windows-msvc
|
||||
runs-on: ${{ matrix.spec.runner }}
|
||||
outputs:
|
||||
FILES_TO_BE_SIGNED: ${{ steps.get_files_to_be_signed.outputs.FILES_TO_BE_SIGNED }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
timeout-minutes: 10
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Download and overwrite packaged artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: signed-packaged-${{ matrix.spec.platform }}-${{ matrix.spec.arch }}
|
||||
path: .
|
||||
- name: unzip file
|
||||
run: Expand-Archive -Path signed.zip -DestinationPath packages/frontend/electron/out
|
||||
|
||||
- name: Make squirrel.windows installer
|
||||
run: yarn workspace @affine/electron make-squirrel --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
|
||||
|
||||
- name: Zip artifacts for faster upload
|
||||
run: Compress-Archive -CompressionLevel Fastest -Path packages/frontend/electron/out/${{ env.BUILD_TYPE }}/make/* -DestinationPath archive.zip
|
||||
|
||||
- name: get all files to be signed
|
||||
id: get_files_to_be_signed
|
||||
run: |
|
||||
Set-Variable -Name FILES_TO_BE_SIGNED -Value ((Get-ChildItem -Path packages/frontend/electron/out/${{ env.BUILD_TYPE }}/make -Recurse -File | Where-Object { $_.Extension -in @(".exe", ".node", ".dll", ".msi") } | ForEach-Object { '"' + $_.FullName.Replace((Get-Location).Path + '\packages\frontend\electron\out\${{ env.BUILD_TYPE }}\make\', '') + '"' }) -join ' ')
|
||||
"FILES_TO_BE_SIGNED=$FILES_TO_BE_SIGNED" >> $env:GITHUB_OUTPUT
|
||||
echo $FILES_TO_BE_SIGNED
|
||||
|
||||
- name: Save installer for signing
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: installer-${{ matrix.spec.platform }}-${{ matrix.spec.arch }}
|
||||
path: archive.zip
|
||||
|
||||
sign-installer-artifacts-windows:
|
||||
needs: make-windows-installer
|
||||
uses: ./.github/workflows/windows-signer.yml
|
||||
with:
|
||||
files: ${{ needs.make-windows-installer.outputs.FILES_TO_BE_SIGNED }}
|
||||
artifact-name: installer-win32-x64
|
||||
|
||||
finalize-installer-windows:
|
||||
needs: sign-installer-artifacts-windows
|
||||
strategy:
|
||||
# all combinations: macos-latest x64, macos-latest arm64, ubuntu-latest x64
|
||||
# For windows, we need a separate approach
|
||||
matrix:
|
||||
spec:
|
||||
- runner: windows-latest
|
||||
platform: win32
|
||||
arch: x64
|
||||
target: x86_64-pc-windows-msvc
|
||||
runs-on: ${{ matrix.spec.runner }}
|
||||
steps:
|
||||
- name: Download and overwrite installer artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: signed-installer-${{ matrix.spec.platform }}-${{ matrix.spec.arch }}
|
||||
path: .
|
||||
- name: unzip file
|
||||
run: Expand-Archive -Path signed.zip -DestinationPath packages/frontend/electron/out/${{ env.BUILD_TYPE }}/make
|
||||
|
||||
- name: Save artifacts
|
||||
run: |
|
||||
mkdir -p builds
|
||||
mv packages/frontend/electron/out/*/make/zip/win32/x64/AFFiNE*-win32-x64-*.zip ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.zip
|
||||
mv packages/frontend/electron/out/*/make/squirrel.windows/x64/*.exe ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.exe
|
||||
mv packages/frontend/electron/out/*/make/squirrel.windows/x64/*.msi ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.msi
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
@@ -174,11 +320,17 @@ jobs:
|
||||
path: builds
|
||||
|
||||
release:
|
||||
needs: [before-make, make-distribution]
|
||||
needs: [before-make, make-distribution, finalize-installer-windows]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: core
|
||||
path: web-static
|
||||
- name: Zip web-static
|
||||
run: zip -r web-static.zip web-static
|
||||
- name: Download Artifacts (macos-x64)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
@@ -199,19 +351,16 @@ jobs:
|
||||
with:
|
||||
name: affine-linux-x64-builds
|
||||
path: ./
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Generate Release yml
|
||||
run: |
|
||||
cp ./apps/electron/scripts/generate-yml.js .
|
||||
node generate-yml.js
|
||||
node ./packages/frontend/electron/scripts/generate-yml.js
|
||||
env:
|
||||
RELEASE_VERSION: ${{ github.event.inputs.version || needs.before-make.outputs.RELEASE_VERSION }}
|
||||
- name: Create Release Draft
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
with:
|
||||
name: ${{ github.event.inputs.version || needs.before-make.outputs.RELEASE_VERSION }}
|
||||
body: ''
|
||||
@@ -222,8 +371,6 @@ jobs:
|
||||
./*.zip
|
||||
./*.dmg
|
||||
./*.exe
|
||||
./*.nupkg
|
||||
./RELEASES
|
||||
./*.AppImage
|
||||
./*.apk
|
||||
./*.yml
|
||||
|
||||
148
.github/workflows/release.yml
vendored
@@ -5,15 +5,161 @@ on:
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
BUILD_TYPE: stable
|
||||
APP_NAME: affine
|
||||
COVERAGE: false
|
||||
DISTRIBUTION: browser
|
||||
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Try publishing npm@latest release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Try publishing to NPM
|
||||
run: ./scripts/publish.sh
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
build-core:
|
||||
name: Build @affine/core
|
||||
runs-on: ubuntu-latest
|
||||
environment: development
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Build Plugins
|
||||
run: yarn run build:plugins
|
||||
- name: Build Core
|
||||
run: yarn nx build @affine/core
|
||||
- name: Upload core artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: core
|
||||
path: ./packages/frontend/core/dist
|
||||
if-no-files-found: error
|
||||
|
||||
build-server:
|
||||
name: Build Server
|
||||
runs-on: ubuntu-latest
|
||||
environment: development
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
electron-install: false
|
||||
- name: Build Server
|
||||
run: yarn nx build @affine/server
|
||||
- name: Upload server dist
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: server-dist
|
||||
path: ./packages/backend/server/dist
|
||||
if-no-files-found: error
|
||||
|
||||
build-storage:
|
||||
name: Build Storage
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
RUSTFLAGS: '-C debuginfo=1'
|
||||
environment: development
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Setup Rust
|
||||
uses: ./.github/actions/build-rust
|
||||
with:
|
||||
target: 'x86_64-unknown-linux-gnu'
|
||||
package: '@affine/storage'
|
||||
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
||||
- name: Upload storage.node
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: storage.node
|
||||
path: ./packages/backend/storage/storage.node
|
||||
if-no-files-found: error
|
||||
|
||||
build-docker:
|
||||
if: github.ref == 'refs/heads/master'
|
||||
name: Build Docker
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build-server
|
||||
- build-core
|
||||
- build-storage
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download core artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: core
|
||||
path: ./packages/frontend/core/dist
|
||||
- name: Download server dist
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: server-dist
|
||||
path: ./packages/backend/server/dist
|
||||
- name: Download storage.node
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: storage.node
|
||||
path: ./packages/backend/server
|
||||
- name: Setup Git short hash
|
||||
run: |
|
||||
echo "GIT_SHORT_HASH=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV"
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
logout: false
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Build front Dockerfile
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
pull: true
|
||||
platforms: linux/amd64,linux/arm64
|
||||
provenance: true
|
||||
file: .github/deployment/front/Dockerfile
|
||||
tags: ghcr.io/toeverything/affine-front:${{ env.GIT_SHORT_HASH }},ghcr.io/toeverything/affine-front:latest
|
||||
|
||||
# setup node without cache configuration
|
||||
# Prisma cache is not compatible with docker build cache
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
registry-url: https://npm.pkg.github.com
|
||||
scope: '@toeverything'
|
||||
|
||||
- name: Install Node.js dependencies
|
||||
run: yarn workspaces focus @affine/server --production
|
||||
|
||||
- name: Generate Prisma client
|
||||
run: yarn workspace @affine/server prisma generate
|
||||
|
||||
- name: Build graphql Dockerfile
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
pull: true
|
||||
platforms: linux/amd64,linux/arm64
|
||||
provenance: true
|
||||
file: .github/deployment/node/Dockerfile
|
||||
tags: ghcr.io/toeverything/affine-graphql:${{ env.GIT_SHORT_HASH }},ghcr.io/toeverything/affine-graphql:latest
|
||||
|
||||
42
.github/workflows/windows-signer.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: Windows Signer
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
artifact-name:
|
||||
required: true
|
||||
type: string
|
||||
files:
|
||||
required: true
|
||||
type: string
|
||||
jobs:
|
||||
sign:
|
||||
runs-on: [self-hosted, win-signer]
|
||||
env:
|
||||
ARCHIVE_DIR: ${{ github.run_id }}-${{ github.run_attempt }}-${{ inputs.artifact-name }}
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ${{ inputs.artifact-name }}
|
||||
path: ${{ env.ARCHIVE_DIR }}
|
||||
- name: unzip file
|
||||
shell: cmd
|
||||
# 7za is pre-installed on the signer machine
|
||||
run: |
|
||||
cd ${{ env.ARCHIVE_DIR }}
|
||||
md out
|
||||
7za x archive.zip -y -oout
|
||||
- name: sign
|
||||
shell: cmd
|
||||
run: |
|
||||
cd ${{ env.ARCHIVE_DIR }}/out
|
||||
signtool sign /tr http://timestamp.sectigo.com /td sha256 /fd sha256 /a ${{ inputs.files }}
|
||||
- name: zip file
|
||||
shell: cmd
|
||||
run: |
|
||||
cd ${{ env.ARCHIVE_DIR }}
|
||||
7za a signed.zip .\out\*
|
||||
- name: upload
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: signed-${{ inputs.artifact-name }}
|
||||
path: ${{ env.ARCHIVE_DIR }}/signed.zip
|
||||
9
.github/workflows/workers.yml
vendored
@@ -5,7 +5,7 @@ on:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- packages/workers/**
|
||||
- tools/workers/**
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
@@ -13,10 +13,11 @@ jobs:
|
||||
name: Deploy
|
||||
environment: production
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Publish
|
||||
uses: cloudflare/wrangler-action@2.0.0
|
||||
uses: cloudflare/wrangler-action@v3.3.2
|
||||
with:
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
workingDirectory: 'packages/workers'
|
||||
workingDirectory: 'tools/workers'
|
||||
packageManager: 'yarn'
|
||||
|
||||
3
.gitignore
vendored
@@ -19,7 +19,7 @@
|
||||
node_modules
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
**/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
@@ -77,3 +77,4 @@ target
|
||||
tsconfig.node.tsbuildinfo
|
||||
lib
|
||||
affine.db
|
||||
apps/web/next-routes.conf
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"version": 1,
|
||||
"list": [
|
||||
{
|
||||
"input": "./packages/i18n/src/resources/en.json",
|
||||
"output": "./packages/i18n/src/i18n-generated",
|
||||
"input": "./packages/frontend/i18n/src/resources/en.json",
|
||||
"output": "./packages/frontend/i18n/src/i18n-generated",
|
||||
"parser": {
|
||||
"type": "i18next",
|
||||
"contextSeparator": "$",
|
||||
|
||||
@@ -2,15 +2,22 @@ yarn.lock
|
||||
target
|
||||
lib
|
||||
test-results
|
||||
packages/i18n/src/i18n-generated.ts
|
||||
packages/graphql/src/graphql/index.ts
|
||||
.next
|
||||
out
|
||||
dist
|
||||
.yarn
|
||||
tests/affine-legacy/0.7.0-canary.18/static
|
||||
.github/helm
|
||||
_next
|
||||
storybook-static
|
||||
web-static
|
||||
public
|
||||
packages/backend/server/src/schema.gql
|
||||
packages/frontend/i18n/src/i18n-generated.ts
|
||||
packages/frontend/graphql/src/graphql/index.ts
|
||||
tests/affine-legacy/**/static
|
||||
|
||||
# auto-generated by NAPI-RS
|
||||
# fixme(@joooye34): need script to check and generate ignore list here
|
||||
packages/backend/storage/index.d.ts
|
||||
packages/frontend/native/index.d.ts
|
||||
packages/frontend/native/index.js
|
||||
|
||||
16
.vscode/launch.template.json
vendored
@@ -2,16 +2,24 @@
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "yarn run dev",
|
||||
"name": "Run Dev",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
"command": "yarn run dev"
|
||||
},
|
||||
{
|
||||
"command": "yarn run dev:local",
|
||||
"name": "Run Dev Locally",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
"command": "yarn run dev:local"
|
||||
},
|
||||
{
|
||||
"name": "Launch AFFiNE Cloud",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "yarn",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"runtimeArgs": ["workspace", "@affine/server", "dev"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
10
.vscode/settings.template.json
vendored
@@ -29,15 +29,7 @@
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"vitest.include": [
|
||||
"packages/**/*.spec.ts",
|
||||
"packages/**/*.spec.tsx",
|
||||
"apps/web/**/*.spec.ts",
|
||||
"apps/web/**/*.spec.tsx",
|
||||
"apps/electron/src/**/*.spec.ts",
|
||||
"tests/unit/**/*.spec.ts",
|
||||
"tests/unit/**/*.spec.tsx"
|
||||
],
|
||||
"vitest.include": ["packages/**/*.spec.ts", "packages/**/*.spec.tsx"],
|
||||
"rust-analyzer.check.extraEnv": {
|
||||
"DATABASE_URL": "sqlite:affine.db"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/dist/util/forge-config.js b/dist/util/forge-config.js
|
||||
index 3466ac1a340c8dfe5ea8997178961e8328457d68..ceb33770db48df80e4355e6bac12e8c99162d7bc 100644
|
||||
--- a/dist/util/forge-config.js
|
||||
+++ b/dist/util/forge-config.js
|
||||
@@ -130,7 +130,7 @@ exports.default = async (dir) => {
|
||||
try {
|
||||
// The loaded "config" could potentially be a static forge config, ESM module or async function
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
- const loaded = require(path_1.default.resolve(dir, forgeConfig));
|
||||
+ const loaded = await import(require('node:url').pathToFileURL(path_1.default.join(dir, forgeConfig)))
|
||||
const maybeForgeConfig = 'default' in loaded ? loaded.default : loaded;
|
||||
forgeConfig = typeof maybeForgeConfig === 'function' ? await maybeForgeConfig() : maybeForgeConfig;
|
||||
}
|
||||
15
.yarn/patches/next-auth-npm-4.23.2-5f0e551bc7.patch
Normal file
@@ -0,0 +1,15 @@
|
||||
diff --git a/package.json b/package.json
|
||||
index 26dcf8217f3e221e4c53722f14d29bb788332772..57a66dcb0943b9dd5cdaac2eaffccd9225a6b735 100644
|
||||
--- a/package.json
|
||||
+++ b/package.json
|
||||
@@ -34,6 +34,10 @@
|
||||
"./adapters": {
|
||||
"types": "./adapters.d.ts"
|
||||
},
|
||||
+ "./core": {
|
||||
+ "types": "./core/index.d.ts",
|
||||
+ "default": "./core/index.js"
|
||||
+ },
|
||||
"./jwt": {
|
||||
"types": "./jwt/index.d.ts",
|
||||
"default": "./jwt/index.js"
|
||||
541
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
550
.yarn/plugins/@yarnpkg/plugin-version.cjs
vendored
874
.yarn/releases/yarn-3.6.0.cjs
vendored
893
.yarn/releases/yarn-4.0.1.cjs
vendored
Executable file
14
.yarnrc.yml
@@ -1,3 +1,7 @@
|
||||
compressionLevel: mixed
|
||||
|
||||
enableGlobalCache: true
|
||||
|
||||
nmMode: hardlinks-local
|
||||
|
||||
nodeLinker: node-modules
|
||||
@@ -8,12 +12,4 @@ npmPublishAccess: public
|
||||
|
||||
npmPublishRegistry: 'https://registry.npmjs.org'
|
||||
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||
spec: '@yarnpkg/plugin-interactive-tools'
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-version.cjs
|
||||
spec: '@yarnpkg/plugin-version'
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
|
||||
spec: '@yarnpkg/plugin-workspace-tools'
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.6.0.cjs
|
||||
yarnPath: .yarn/releases/yarn-4.0.1.cjs
|
||||
|
||||
2059
Cargo.lock
generated
@@ -1,5 +1,10 @@
|
||||
[workspace]
|
||||
members = ["./packages/native", "./packages/native/schema"]
|
||||
resolver = "2"
|
||||
members = [
|
||||
"./packages/frontend/native",
|
||||
"./packages/frontend/native/schema",
|
||||
"./packages/backend/storage",
|
||||
]
|
||||
|
||||
[profile.dev.package.sqlx-macros]
|
||||
opt-level = 3
|
||||
|
||||
409
LICENSE
@@ -1,384 +1,25 @@
|
||||
# Mozilla Public License Version 2.0
|
||||
|
||||
Copyright (c) TOEVERYTHING PTE. LTD. and its affiliates.
|
||||
|
||||
1. Definitions
|
||||
|
||||
---
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
|
||||
---
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
|
||||
---
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
---
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
|
||||
---
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
---
|
||||
|
||||
- *
|
||||
- 6. Disclaimer of Warranty \*
|
||||
- ------------------------- \*
|
||||
- *
|
||||
- Covered Software is provided under this License on an "as is" \*
|
||||
- basis, without warranty of any kind, either expressed, implied, or \*
|
||||
- statutory, including, without limitation, warranties that the \*
|
||||
- Covered Software is free of defects, merchantable, fit for a \*
|
||||
- particular purpose or non-infringing. The entire risk as to the \*
|
||||
- quality and performance of the Covered Software is with You. \*
|
||||
- Should any Covered Software prove defective in any respect, You \*
|
||||
- (not any Contributor) assume the cost of any necessary servicing, \*
|
||||
- repair, or correction. This disclaimer of warranty constitutes an \*
|
||||
- essential part of this License. No use of any Covered Software is \*
|
||||
- authorized under this License except under this disclaimer. \*
|
||||
- *
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
- *
|
||||
- 7. Limitation of Liability \*
|
||||
- -------------------------- \*
|
||||
- *
|
||||
- Under no circumstances and under no legal theory, whether tort \*
|
||||
- (including negligence), contract, or otherwise, shall any \*
|
||||
- Contributor, or anyone who distributes Covered Software as \*
|
||||
- permitted above, be liable to You for any direct, indirect, \*
|
||||
- special, incidental, or consequential damages of any character \*
|
||||
- including, without limitation, damages for lost profits, loss of \*
|
||||
- goodwill, work stoppage, computer failure or malfunction, or any \*
|
||||
- and all other commercial damages or losses, even if such party \*
|
||||
- shall have been informed of the possibility of such damages. This \*
|
||||
- limitation of liability shall not apply to liability for death or \*
|
||||
- personal injury resulting from such party's negligence to the \*
|
||||
- extent applicable law prohibits such limitation. Some \*
|
||||
- jurisdictions do not allow the exclusion or limitation of \*
|
||||
- incidental or consequential damages, so this exclusion and \*
|
||||
- limitation may not apply to You. \*
|
||||
- *
|
||||
|
||||
---
|
||||
|
||||
8. Litigation
|
||||
|
||||
---
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
|
||||
---
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
|
||||
---
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
## Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
## Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
Copyright (c) 2022-present TOEVERYTHING PTE. LTD. and its affiliates.
|
||||
|
||||
Portions of this software are licensed as follows:
|
||||
|
||||
- All content that resides under the "packages/backend/server" directory of this repository, if that directory exists, is licensed under the license defined in "packages/backend/server/LICENSE".
|
||||
- All third party components incorporated into the AFFiNE Software are licensed under the original license provided by the owner of the applicable component.
|
||||
- Content outside of the above mentioned directories or restrictions above is available under the "MIT" license as defined in "LICENSE-MIT".
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
21
LICENSE-MIT
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2022-present TOEVERYTHING PTE. LTD. and its affiliates.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
70
README.md
@@ -31,6 +31,7 @@
|
||||
[![blocksuite-icon]](https://github.com/toeverything/blocksuite)
|
||||
[![Rust-version-icon]](https://www.rust-lang.org/)
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2Ftoeverything%2FAFFiNE?ref=badge_shield)
|
||||
[](https://github.com/toeverything/AFFiNE/actions/workflows/deploy.yml)
|
||||
|
||||
</div>
|
||||
|
||||
@@ -77,7 +78,7 @@ Star us, and you will receive all releases notifications from GitHub without any
|
||||
|
||||
- **Hyper merged** — Write, draw and plan all at once. Assemble any blocks you love on any canvas you like to enjoy seamless transitions between workflows with AFFiNE.
|
||||
- **Privacy focussed** — AFFiNE is built with your privacy in mind and is one of our key concerns. We want you to keep control of your data, allowing you to store it as you like, where you like while still being able to freely edit and view your data on-demand.
|
||||
- **Offline-first** - With your privacy in mind we also decided to go offline-first. This means that AFFiNE can be used offline, whether you want to view or edit, with support for conflict-free merging when you are back online.
|
||||
- **Offline-first** — With your privacy in mind we also decided to go offline-first. This means that AFFiNE can be used offline, whether you want to view or edit, with support for conflict-free merging when you are back online.
|
||||
- **Clean, intuitive design** — With AFFiNE you can concentrate on editing with a clean and modern interface. Which is responsive, so it looks great on tablets too, and mobile support is coming in the future.
|
||||
- **Modern Block Editor with Markdown support** — A modern block editor can help you not only for docs, but slides and tables as well. When you write in AFFiNE you can use Markdown syntax which helps create an easier editing experience, that can be experienced with just a keyboard. And this allows you to export your data cleanly into Markdown.
|
||||
- **Collaboration** — Whether you want to collaborate with yourself across multiple devices, or work together with others, support for collaboration and multiplayer is out-of-the-box, which makes it easy for teams to get started with AFFiNE.
|
||||
@@ -106,11 +107,12 @@ If you have questions, you are welcome to contact us. One of the best places to
|
||||
|
||||
## Ecosystem
|
||||
|
||||
| Name | | |
|
||||
| --------------------------------------------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [@affine/component](https://affine-storybook.vercel.app/) | AFFiNE Component Resources | [](https://affine-storybook.vercel.app/) |
|
||||
| [@toeverything/y-indexeddb](packages/y-indexeddb) | IndexedDB database adapter for Yjs | [](https://www.npmjs.com/package/@toeverything/y-indexeddb) |
|
||||
| [@toeverything/theme](packages/theme) | AFFiNE theme | [](https://www.npmjs.com/package/@toeverything/theme) |
|
||||
| Name | | |
|
||||
| ----------------------------------------------------------------------------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [@toeverything/component](https://github.com/toeverything/design/tree/main/packages/components) | Toeverything Shared Component Resources | |
|
||||
| [@affine/component](packages/frontend/component) | AFFiNE Component Resources | [](https://affine-storybook.vercel.app/) |
|
||||
| [@toeverything/y-indexeddb](packages/common/y-indexeddb) | IndexedDB database adapter for Yjs | [](https://www.npmjs.com/package/@toeverything/y-indexeddb) |
|
||||
| [@toeverything/theme](packages/common/theme) | AFFiNE theme | [](https://www.npmjs.com/package/@toeverything/theme) |
|
||||
|
||||
## Plugins
|
||||
|
||||
@@ -118,16 +120,16 @@ If you have questions, you are welcome to contact us. One of the best places to
|
||||
>
|
||||
> (Currently, the plugin system is under heavy development. You will see the plugin system in the canary release.)
|
||||
|
||||
- [@affine/sdk](./packages/sdk) - SDK for developing plugins
|
||||
- [@affine/plugin-cli](./packages/plugin-cli) - CLI for developing plugins
|
||||
- [@affine/sdk](./packages/common/sdk) - SDK for developing plugins
|
||||
- [@affine/plugin-cli](./tools/plugin-cli) - CLI for developing plugins
|
||||
|
||||
| Official Plugin | Description |
|
||||
| ----------------------------------------------------- | ----------------------------------------- |
|
||||
| [@affine/bookmark-plugin](plugins/bookmark) | A block for bookmarking a website |
|
||||
| [@affine/copilot-plugin](plugins/copilot) | AI Copilot that help you document writing |
|
||||
| [@affine/image-preview-plugin](plugins/image-preview) | Component for previewing an image |
|
||||
| Official Plugin | Description | Status |
|
||||
| ---------------------------------------------------------------- | ----------------------------------------- | ------ |
|
||||
| [@affine/copilot-plugin](./packages/plugins/copilot) | AI Copilot that help you document writing | 🚧 |
|
||||
| [@affine/image-preview-plugin](./packages/plugins/image-preview) | Component for previewing an image | ✅ |
|
||||
| [@affine/outline](./packages/plugins/outline) | Outline for your document | ✅ |
|
||||
|
||||
## Thanks
|
||||
## Upstreams
|
||||
|
||||
We would also like to give thanks to open-source projects that make AFFiNE possible:
|
||||
|
||||
@@ -144,7 +146,7 @@ We would also like to give thanks to open-source projects that make AFFiNE possi
|
||||
|
||||
Thanks a lot to the community for providing such powerful and simple libraries, so that we can focus more on the implementation of the product logic, and we hope that in the future our projects will also provide a more easy-to-use knowledge base for everyone.
|
||||
|
||||
# Contributors
|
||||
## Contributors
|
||||
|
||||
We would like to express our gratitude to all the individuals who have already contributed to AFFiNE! If you have any AFFiNE-related project, documentation, tool or template, please feel free to contribute it by submitting a pull request to our curated list on GitHub: [awesome-affine](https://github.com/toeverything/awesome-affine).
|
||||
|
||||
@@ -152,8 +154,30 @@ We would like to express our gratitude to all the individuals who have already c
|
||||
<img alt="contributors" src="https://opencollective.com/affine/contributors.svg?width=890&button=false" />
|
||||
</a>
|
||||
|
||||
## Data Compatibility
|
||||
|
||||
Data compatibility is a very important issue for us. We will try our best to ensure that the data is compatible with the previous version.
|
||||
|
||||
If you encounter any problems when upgrading the version, please feel free to [contact us](mailto:developer@toeverything.info).
|
||||
|
||||
| AFFiNE Version | Export/Import workspace | Data auto migration |
|
||||
| --------------- | ----------------------- | ------------------- |
|
||||
| <= 0.5.4 | ❌️ | ❌ |
|
||||
| 0.6.x | ✅️ | ✅ |
|
||||
| 0.7.x | ✅️ | ✅ |
|
||||
| 0.8.x (current) | ✅ | ✅ |
|
||||
| 0.9.x (next) | 🚧 | 🚧 |
|
||||
|
||||
- ❌️: Not compatible
|
||||
- ✅: Compatible
|
||||
- 🚧: Work in progress
|
||||
|
||||
## Self-Host
|
||||
|
||||
> We know that the self-host version has been out of date for a long time.
|
||||
>
|
||||
> We are working hard to get this updated to the latest version, you can try our desktop version first.
|
||||
|
||||
Get started with Docker and deploy your own feature-rich, restriction-free deployment of AFFiNE.
|
||||
We are working hard to get this updated to the latest version, you can keep an eye on the [latest packages].
|
||||
|
||||
@@ -178,11 +202,19 @@ See [BUILDING.md] for instructions on how to build AFFiNE from source code.
|
||||
We welcome contributions from everyone.
|
||||
See [docs/contributing/tutorial.md](./docs/contributing/tutorial.md) for details.
|
||||
|
||||
## Thanks
|
||||
|
||||
<a href="https://www.chromatic.com/"><img src="https://user-images.githubusercontent.com/321738/84662277-e3db4f80-af1b-11ea-88f5-91d67a5e59f6.png" width="153" height="30" alt="Chromatic" /></a>
|
||||
|
||||
Thanks to [Chromatic](https://www.chromatic.com/) for providing the visual testing platform that helps us review UI changes and catch visual regressions.
|
||||
|
||||
## License
|
||||
|
||||
See [LICENSE] for details.
|
||||
|
||||
[all-contributors-badge]: https://img.shields.io/github/all-contributors/toeverything/AFFiNE/master?color=orange
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2Ftoeverything%2FAFFiNE?ref=badge_large)
|
||||
|
||||
[all-contributors-badge]: https://img.shields.io/github/contributors/toeverything/AFFiNE
|
||||
[license]: ./LICENSE
|
||||
[building.md]: ./docs/BUILDING.md
|
||||
[update page]: https://affine.pro/blog?tag=Release%20Note
|
||||
@@ -194,7 +226,5 @@ See [LICENSE] for details.
|
||||
[codecov]: https://codecov.io/gh/toeverything/affine/branch/master/graphs/badge.svg?branch=master
|
||||
[node-version-icon]: https://img.shields.io/badge/node-%3E=18.16.1-success
|
||||
[typescript-version-icon]: https://img.shields.io/github/package-json/dependency-version/toeverything/affine/dev/typescript
|
||||
[react-version-icon]: https://img.shields.io/github/package-json/dependency-version/toeverything/AFFiNE/react?filename=apps%2Fcore%2Fpackage.json&color=rgb(97%2C228%2C251)
|
||||
[blocksuite-icon]: https://img.shields.io/github/package-json/dependency-version/toeverything/AFFiNE/@blocksuite/store?color=6880ff&filename=apps%2Fcore%2Fpackage.json&label=blocksuite
|
||||
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2Ftoeverything%2FAFFiNE?ref=badge_large)
|
||||
[react-version-icon]: https://img.shields.io/github/package-json/dependency-version/toeverything/AFFiNE/react?filename=packages%2Ffrontend%2Fcore%2Fpackage.json&color=rgb(97%2C228%2C251)
|
||||
[blocksuite-icon]: https://img.shields.io/github/package-json/dependency-version/toeverything/AFFiNE/@blocksuite/store?color=6880ff&filename=packages%2Ffrontend%2Fcore%2Fpackage.json&label=blocksuite
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
# Apps structure
|
||||
|
||||
> This is the structure of the `apps` directory.
|
||||
|
||||
## docs
|
||||
|
||||
AFFiNE Developer Documentation using [waku](https://github.com/dai-shi/waku).
|
||||
|
||||
## electron
|
||||
|
||||
> `core` needs to be built before electron.
|
||||
|
||||
AFFiNE Desktop (macOS, Linux and Windows Distribution) using [Electron](https://www.electronjs.org/).
|
||||
|
||||
## server
|
||||
|
||||
Server using [Nest.js](https://nestjs.com/).
|
||||
|
||||
## storybook
|
||||
|
||||
Storybook using [Storybook](https://storybook.js.org/).
|
||||
|
||||
## prototype
|
||||
|
||||
AFFiNE Prototype using [React.js](https://reactjs.org/) + [Vite](https://vitejs.dev/).
|
||||
|
||||
## core
|
||||
|
||||
AFFiNE Core Application using [React.js](https://reactjs.org/) + [Webpack](https://webpack.js.org/).
|
||||
@@ -1,12 +0,0 @@
|
||||
import type { BuildFlags } from '@affine/cli/config';
|
||||
|
||||
export function computeCacheKey(buildFlags: BuildFlags) {
|
||||
return [
|
||||
'1',
|
||||
'node' + process.version,
|
||||
buildFlags.mode,
|
||||
buildFlags.distribution,
|
||||
buildFlags.channel,
|
||||
...(buildFlags.localBlockSuite ? [buildFlags.localBlockSuite] : []),
|
||||
].join('-');
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
{
|
||||
"name": "@affine/core",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"version": "0.8.0-canary.21",
|
||||
"scripts": {
|
||||
"build": "yarn -T run build-core",
|
||||
"dev": "yarn -T run dev-core",
|
||||
"static-server": "ts-node-esm ./server.mts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@affine-test/fixtures": "workspace:*",
|
||||
"@affine/component": "workspace:*",
|
||||
"@affine/debug": "workspace:*",
|
||||
"@affine/env": "workspace:*",
|
||||
"@affine/graphql": "workspace:*",
|
||||
"@affine/i18n": "workspace:*",
|
||||
"@affine/jotai": "workspace:*",
|
||||
"@affine/templates": "workspace:*",
|
||||
"@affine/workspace": "workspace:*",
|
||||
"@blocksuite/block-std": "0.0.0-20230811201552-f37162ea-nightly",
|
||||
"@blocksuite/blocks": "0.0.0-20230811201552-f37162ea-nightly",
|
||||
"@blocksuite/editor": "0.0.0-20230811201552-f37162ea-nightly",
|
||||
"@blocksuite/global": "0.0.0-20230811201552-f37162ea-nightly",
|
||||
"@blocksuite/icons": "^2.1.31",
|
||||
"@blocksuite/lit": "0.0.0-20230811201552-f37162ea-nightly",
|
||||
"@blocksuite/store": "0.0.0-20230811201552-f37162ea-nightly",
|
||||
"@dnd-kit/core": "^6.0.8",
|
||||
"@dnd-kit/sortable": "^7.0.2",
|
||||
"@emotion/cache": "^11.11.0",
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/server": "^11.11.0",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@mui/material": "^5.14.4",
|
||||
"@react-hookz/web": "^23.1.0",
|
||||
"@toeverything/components": "^0.0.10",
|
||||
"@types/lodash.throttle": "^4.1.7",
|
||||
"async-call-rpc": "^6.3.1",
|
||||
"cmdk": "^0.2.0",
|
||||
"css-spring": "^4.1.0",
|
||||
"cssnano": "^6.0.1",
|
||||
"graphql": "^16.7.1",
|
||||
"intl-segmenter-polyfill-rs": "^0.1.5",
|
||||
"jotai": "^2.3.1",
|
||||
"jotai-devtools": "^0.6.1",
|
||||
"lit": "^2.8.0",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"lottie-web": "^5.12.2",
|
||||
"mini-css-extract-plugin": "^2.7.6",
|
||||
"next-themes": "^0.2.1",
|
||||
"postcss-loader": "^7.3.3",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-is": "18.2.0",
|
||||
"react-resizable-panels": "^0.0.54",
|
||||
"react-router-dom": "^6.15.0",
|
||||
"rxjs": "^7.8.1",
|
||||
"ses": "^0.18.7",
|
||||
"swr": "2.2.1",
|
||||
"y-protocols": "^1.0.5",
|
||||
"yjs": "^13.6.7",
|
||||
"zod": "^3.21.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@perfsee/webpack": "^1.8.4",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
|
||||
"@sentry/webpack-plugin": "^2.6.2",
|
||||
"@svgr/webpack": "^8.0.1",
|
||||
"@swc/core": "^1.3.76",
|
||||
"@types/webpack-env": "^1.18.1",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"css-loader": "^6.8.1",
|
||||
"express": "^4.18.2",
|
||||
"html-webpack-plugin": "^5.5.3",
|
||||
"raw-loader": "^4.0.2",
|
||||
"source-map-loader": "^4.0.1",
|
||||
"style-loader": "^3.3.3",
|
||||
"swc-loader": "^0.2.3",
|
||||
"swc-plugin-coverage-instrument": "^0.0.20",
|
||||
"thread-loader": "^4.0.2",
|
||||
"ts-node": "^10.9.1",
|
||||
"webpack": "^5.88.2",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^4.15.1",
|
||||
"webpack-merge": "^5.9.0"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 5.4 KiB |
@@ -1,18 +0,0 @@
|
||||
// static server for web app
|
||||
import express from 'express';
|
||||
const app = express();
|
||||
|
||||
const PORT = process.env.PORT || 8080;
|
||||
|
||||
app.use('/', express.static('dist'));
|
||||
|
||||
app.get('/*', (req, res) => {
|
||||
if (req.url.startsWith('/plugins')) {
|
||||
res.sendFile(req.url, { root: 'dist' });
|
||||
}
|
||||
res.sendFile('index.html', { root: 'dist' });
|
||||
});
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server is running on port ${PORT}`);
|
||||
});
|
||||
@@ -1,547 +0,0 @@
|
||||
import { DebugLogger } from '@affine/debug';
|
||||
import type {
|
||||
CallbackMap,
|
||||
ExpectedLayout,
|
||||
LayoutNode,
|
||||
PluginContext,
|
||||
} from '@affine/sdk/entry';
|
||||
import { AffineFormatBarWidget } from '@blocksuite/blocks';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
import {
|
||||
addCleanup,
|
||||
pluginEditorAtom,
|
||||
pluginHeaderItemAtom,
|
||||
pluginSettingAtom,
|
||||
pluginWindowAtom,
|
||||
} from '@toeverything/infra/__internal__/plugin';
|
||||
import {
|
||||
contentLayoutAtom,
|
||||
currentPageAtom,
|
||||
currentWorkspaceAtom,
|
||||
rootStore,
|
||||
} from '@toeverything/infra/atom';
|
||||
import { atom } from 'jotai';
|
||||
import { Provider } from 'jotai/react';
|
||||
import { createElement, type PropsWithChildren } from 'react';
|
||||
|
||||
import { createFetch } from './endowments/fercher';
|
||||
import { createTimers } from './endowments/timer';
|
||||
import { setupImportsMap } from './setup-imports-map';
|
||||
|
||||
const dynamicImportKey = '$h_import';
|
||||
|
||||
const permissionLogger = new DebugLogger('plugins:permission');
|
||||
const importLogger = new DebugLogger('plugins:import');
|
||||
|
||||
const pushLayoutAtom = atom<
|
||||
null,
|
||||
// fixme: check plugin name here
|
||||
[pluginName: string, create: (root: HTMLElement) => () => void],
|
||||
void
|
||||
>(null, (_, set, pluginName, callback) => {
|
||||
set(pluginWindowAtom, items => ({
|
||||
...items,
|
||||
[pluginName]: callback,
|
||||
}));
|
||||
set(contentLayoutAtom, layout => {
|
||||
if (layout === 'editor') {
|
||||
return {
|
||||
direction: 'horizontal',
|
||||
first: 'editor',
|
||||
second: pluginName,
|
||||
splitPercentage: 70,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...layout,
|
||||
direction: 'horizontal',
|
||||
first: 'editor',
|
||||
second: {
|
||||
direction: 'horizontal',
|
||||
// fixme: incorrect type here
|
||||
first: layout.second,
|
||||
second: pluginName,
|
||||
splitPercentage: 70,
|
||||
},
|
||||
} as ExpectedLayout;
|
||||
}
|
||||
});
|
||||
addCleanup(pluginName, () => {
|
||||
set(deleteLayoutAtom, pluginName);
|
||||
});
|
||||
});
|
||||
|
||||
const deleteLayoutAtom = atom<null, [string], void>(null, (_, set, id) => {
|
||||
set(pluginWindowAtom, items => {
|
||||
const newItems = { ...items };
|
||||
delete newItems[id];
|
||||
return newItems;
|
||||
});
|
||||
const removeLayout = (layout: LayoutNode): LayoutNode => {
|
||||
if (layout === 'editor') {
|
||||
return 'editor';
|
||||
} else {
|
||||
if (typeof layout === 'string') {
|
||||
return layout as ExpectedLayout;
|
||||
}
|
||||
if (layout.first === id) {
|
||||
return layout.second;
|
||||
} else if (layout.second === id) {
|
||||
return layout.first;
|
||||
} else {
|
||||
return removeLayout(layout.second);
|
||||
}
|
||||
}
|
||||
};
|
||||
set(contentLayoutAtom, layout => {
|
||||
if (layout === 'editor') {
|
||||
return 'editor';
|
||||
} else {
|
||||
if (typeof layout === 'string') {
|
||||
return layout as ExpectedLayout;
|
||||
}
|
||||
if (layout.first === id) {
|
||||
return layout.second as ExpectedLayout;
|
||||
} else if (layout.second === id) {
|
||||
return layout.first as ExpectedLayout;
|
||||
} else {
|
||||
return removeLayout(layout.second) as ExpectedLayout;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// module -> importName -> updater[]
|
||||
export const _rootImportsMap = new Map<string, Map<string, any>>();
|
||||
const rootImportsMapSetupPromise = setupImportsMap(_rootImportsMap, {
|
||||
react: import('react'),
|
||||
'react/jsx-runtime': import('react/jsx-runtime'),
|
||||
'react-dom': import('react-dom'),
|
||||
'react-dom/client': import('react-dom/client'),
|
||||
jotai: import('jotai'),
|
||||
'jotai/utils': import('jotai/utils'),
|
||||
swr: import('swr'),
|
||||
'@affine/component': import('@affine/component'),
|
||||
'@blocksuite/icons': import('@blocksuite/icons'),
|
||||
'@affine/sdk/entry': {
|
||||
rootStore: rootStore,
|
||||
currentWorkspaceAtom: currentWorkspaceAtom,
|
||||
currentPageAtom: currentPageAtom,
|
||||
pushLayoutAtom: pushLayoutAtom,
|
||||
deleteLayoutAtom: deleteLayoutAtom,
|
||||
},
|
||||
'@blocksuite/global/utils': import('@blocksuite/global/utils'),
|
||||
'@toeverything/infra/atom': import('@toeverything/infra/atom'),
|
||||
'@toeverything/components/button': import('@toeverything/components/button'),
|
||||
});
|
||||
|
||||
// pluginName -> module -> importName -> updater[]
|
||||
export const _pluginNestedImportsMap = new Map<
|
||||
string,
|
||||
Map<string, Map<string, any>>
|
||||
>();
|
||||
|
||||
const pluginImportsFunctionMap = new Map<string, (imports: any) => void>();
|
||||
export const createImports = (pluginName: string) => {
|
||||
if (pluginImportsFunctionMap.has(pluginName)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return pluginImportsFunctionMap.get(pluginName)!;
|
||||
}
|
||||
const imports = (
|
||||
newUpdaters: [string, [string, ((val: any) => void)[]][]][]
|
||||
) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const currentImportMap = _pluginNestedImportsMap.get(pluginName)!;
|
||||
importLogger.debug('currentImportMap', pluginName, currentImportMap);
|
||||
|
||||
for (const [module, moduleUpdaters] of newUpdaters) {
|
||||
importLogger.debug('imports module', module, moduleUpdaters);
|
||||
let moduleImports = _rootImportsMap.get(module);
|
||||
if (!moduleImports) {
|
||||
moduleImports = currentImportMap.get(module);
|
||||
}
|
||||
if (moduleImports) {
|
||||
for (const [importName, importUpdaters] of moduleUpdaters) {
|
||||
const updateImport = (value: any) => {
|
||||
for (const importUpdater of importUpdaters) {
|
||||
importUpdater(value);
|
||||
}
|
||||
};
|
||||
if (moduleImports.has(importName)) {
|
||||
const val = moduleImports.get(importName);
|
||||
updateImport(val);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.error(
|
||||
'cannot find module in plugin import map',
|
||||
module,
|
||||
currentImportMap,
|
||||
_pluginNestedImportsMap
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
pluginImportsFunctionMap.set(pluginName, imports);
|
||||
return imports;
|
||||
};
|
||||
|
||||
const abortController = new AbortController();
|
||||
|
||||
const pluginFetch = createFetch({});
|
||||
const timer = createTimers(abortController.signal);
|
||||
|
||||
const sharedGlobalThis = Object.assign(Object.create(null), timer, {
|
||||
Object: globalThis.Object,
|
||||
fetch: pluginFetch,
|
||||
ReadableStream: globalThis.ReadableStream,
|
||||
Symbol: globalThis.Symbol,
|
||||
Error: globalThis.Error,
|
||||
TypeError: globalThis.TypeError,
|
||||
RangeError: globalThis.RangeError,
|
||||
console: globalThis.console,
|
||||
crypto: globalThis.crypto,
|
||||
});
|
||||
|
||||
const dynamicImportMap = new Map<
|
||||
string,
|
||||
(moduleName: string) => Promise<any>
|
||||
>();
|
||||
|
||||
export const createOrGetDynamicImport = (
|
||||
baseUrl: string,
|
||||
pluginName: string
|
||||
) => {
|
||||
if (dynamicImportMap.has(pluginName)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return dynamicImportMap.get(pluginName)!;
|
||||
}
|
||||
const dynamicImport = async (moduleName: string): Promise<any> => {
|
||||
const codeUrl = `${baseUrl}/${moduleName}`;
|
||||
const analysisUrl = `${baseUrl}/${moduleName}.json`;
|
||||
const response = await fetch(codeUrl);
|
||||
const analysisResponse = await fetch(analysisUrl);
|
||||
const analysis = await analysisResponse.json();
|
||||
const exports = analysis.exports as string[];
|
||||
const code = await response.text();
|
||||
const moduleCompartment = new Compartment(
|
||||
createOrGetGlobalThis(
|
||||
pluginName,
|
||||
// use singleton here to avoid infinite loop
|
||||
createOrGetDynamicImport(pluginName, baseUrl)
|
||||
)
|
||||
);
|
||||
const entryPoint = moduleCompartment.evaluate(code, {
|
||||
__evadeHtmlCommentTest__: true,
|
||||
});
|
||||
const moduleExports = {} as Record<string, any>;
|
||||
const setVarProxy = new Proxy(
|
||||
{},
|
||||
{
|
||||
get(_, p: string): any {
|
||||
return (newValue: any) => {
|
||||
moduleExports[p] = newValue;
|
||||
};
|
||||
},
|
||||
}
|
||||
);
|
||||
entryPoint({
|
||||
imports: createImports(pluginName),
|
||||
liveVar: setVarProxy,
|
||||
onceVar: setVarProxy,
|
||||
});
|
||||
importLogger.debug('import', moduleName, exports, moduleExports);
|
||||
return moduleExports;
|
||||
};
|
||||
dynamicImportMap.set(pluginName, dynamicImport);
|
||||
return dynamicImport;
|
||||
};
|
||||
|
||||
const globalThisMap = new Map<string, any>();
|
||||
|
||||
export const createOrGetGlobalThis = (
|
||||
pluginName: string,
|
||||
dynamicImport: (moduleName: string) => Promise<any>
|
||||
) => {
|
||||
if (globalThisMap.has(pluginName)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return globalThisMap.get(pluginName)!;
|
||||
}
|
||||
const pluginGlobalThis = Object.assign(
|
||||
Object.create(null),
|
||||
sharedGlobalThis,
|
||||
{
|
||||
// fixme: vite build output bundle will have this, we should remove it
|
||||
process: Object.freeze({
|
||||
env: {
|
||||
NODE_ENV: process.env.NODE_ENV,
|
||||
},
|
||||
}),
|
||||
// dynamic import function
|
||||
[dynamicImportKey]: dynamicImport,
|
||||
// UNSAFE: React will read `window` and `document`
|
||||
window: new Proxy(
|
||||
{},
|
||||
{
|
||||
get(_, key) {
|
||||
permissionLogger.debug(`${pluginName} is accessing window`, key);
|
||||
if (sharedGlobalThis[key]) return sharedGlobalThis[key];
|
||||
const result = Reflect.get(window, key);
|
||||
if (typeof result === 'function') {
|
||||
if (result === ShadowRoot) {
|
||||
return result;
|
||||
}
|
||||
return function (...args: any[]) {
|
||||
permissionLogger.debug(
|
||||
`${pluginName} is calling window`,
|
||||
key,
|
||||
args
|
||||
);
|
||||
return result.apply(window, args);
|
||||
};
|
||||
}
|
||||
permissionLogger.debug('window', key, result);
|
||||
return result;
|
||||
},
|
||||
}
|
||||
),
|
||||
document: new Proxy(
|
||||
{},
|
||||
{
|
||||
get(_, key) {
|
||||
permissionLogger.debug(`${pluginName} is accessing document`, key);
|
||||
if (sharedGlobalThis[key]) return sharedGlobalThis[key];
|
||||
const result = Reflect.get(document, key);
|
||||
if (typeof result === 'function') {
|
||||
return function (...args: any[]) {
|
||||
permissionLogger.debug(
|
||||
`${pluginName} is calling window`,
|
||||
key,
|
||||
args
|
||||
);
|
||||
return result.apply(document, args);
|
||||
};
|
||||
}
|
||||
permissionLogger.debug('document', key, result);
|
||||
return result;
|
||||
},
|
||||
}
|
||||
),
|
||||
navigator: {
|
||||
userAgent: navigator.userAgent,
|
||||
},
|
||||
|
||||
MouseEvent: globalThis.MouseEvent,
|
||||
KeyboardEvent: globalThis.KeyboardEvent,
|
||||
CustomEvent: globalThis.CustomEvent,
|
||||
|
||||
// copilot uses these
|
||||
Date: globalThis.Date,
|
||||
Math: globalThis.Math,
|
||||
URL: globalThis.URL,
|
||||
URLSearchParams: globalThis.URLSearchParams,
|
||||
Headers: globalThis.Headers,
|
||||
TextEncoder: globalThis.TextEncoder,
|
||||
TextDecoder: globalThis.TextDecoder,
|
||||
Request: globalThis.Request,
|
||||
|
||||
// image-preview uses these
|
||||
Blob: globalThis.Blob,
|
||||
ClipboardItem: globalThis.ClipboardItem,
|
||||
|
||||
// vue uses these
|
||||
Element: globalThis.Element,
|
||||
SVGElement: globalThis.SVGElement,
|
||||
|
||||
// fixme: use our own db api
|
||||
indexedDB: globalThis.indexedDB,
|
||||
IDBRequest: globalThis.IDBRequest,
|
||||
IDBDatabase: globalThis.IDBDatabase,
|
||||
IDBCursorWithValue: globalThis.IDBCursorWithValue,
|
||||
IDBFactory: globalThis.IDBFactory,
|
||||
IDBKeyRange: globalThis.IDBKeyRange,
|
||||
IDBOpenDBRequest: globalThis.IDBOpenDBRequest,
|
||||
IDBTransaction: globalThis.IDBTransaction,
|
||||
IDBObjectStore: globalThis.IDBObjectStore,
|
||||
IDBIndex: globalThis.IDBIndex,
|
||||
IDBCursor: globalThis.IDBCursor,
|
||||
IDBVersionChangeEvent: globalThis.IDBVersionChangeEvent,
|
||||
}
|
||||
);
|
||||
pluginGlobalThis.global = pluginGlobalThis;
|
||||
globalThisMap.set(pluginName, pluginGlobalThis);
|
||||
return pluginGlobalThis;
|
||||
};
|
||||
|
||||
export const setupPluginCode = async (
|
||||
baseUrl: string,
|
||||
pluginName: string,
|
||||
filename: string
|
||||
) => {
|
||||
await rootImportsMapSetupPromise;
|
||||
if (!_pluginNestedImportsMap.has(pluginName)) {
|
||||
_pluginNestedImportsMap.set(pluginName, new Map());
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const currentImportMap = _pluginNestedImportsMap.get(pluginName)!;
|
||||
const isMissingPackage = (name: string) =>
|
||||
_rootImportsMap.has(name) && !currentImportMap.has(name);
|
||||
|
||||
const bundleAnalysis = await fetch(`${baseUrl}/${filename}.json`).then(res =>
|
||||
res.json()
|
||||
);
|
||||
const moduleExports = bundleAnalysis.exports as Record<string, [string]>;
|
||||
const moduleImports = bundleAnalysis.imports as string[];
|
||||
const moduleReexports = bundleAnalysis.reexports as Record<
|
||||
string,
|
||||
[localName: string, exportedName: string][]
|
||||
>;
|
||||
await Promise.all(
|
||||
moduleImports.map(name => {
|
||||
if (isMissingPackage(name)) {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
importLogger.debug('missing package', name);
|
||||
return setupPluginCode(baseUrl, pluginName, name);
|
||||
}
|
||||
})
|
||||
);
|
||||
const code = await fetch(`${baseUrl}/${filename.replace(/^\.\//, '')}`).then(
|
||||
res => res.text()
|
||||
);
|
||||
importLogger.debug('evaluating', filename);
|
||||
const moduleCompartment = new Compartment(
|
||||
createOrGetGlobalThis(
|
||||
pluginName,
|
||||
// use singleton here to avoid infinite loop
|
||||
createOrGetDynamicImport(baseUrl, pluginName)
|
||||
)
|
||||
);
|
||||
const entryPoint = moduleCompartment.evaluate(code, {
|
||||
__evadeHtmlCommentTest__: true,
|
||||
});
|
||||
const moduleExportsMap = new Map<string, any>();
|
||||
const setVarProxy = new Proxy(
|
||||
{},
|
||||
{
|
||||
get(_, p: string): any {
|
||||
return (newValue: any) => {
|
||||
moduleExportsMap.set(p, newValue);
|
||||
};
|
||||
},
|
||||
}
|
||||
);
|
||||
currentImportMap.set(filename, moduleExportsMap);
|
||||
entryPoint({
|
||||
imports: createImports(pluginName),
|
||||
liveVar: setVarProxy,
|
||||
onceVar: setVarProxy,
|
||||
});
|
||||
|
||||
for (const [newExport, [originalExport]] of Object.entries(moduleExports)) {
|
||||
if (newExport === originalExport) continue;
|
||||
const value = moduleExportsMap.get(originalExport);
|
||||
moduleExportsMap.set(newExport, value);
|
||||
moduleExportsMap.delete(originalExport);
|
||||
}
|
||||
|
||||
for (const [name, reexports] of Object.entries(moduleReexports)) {
|
||||
const targetExports = currentImportMap.get(filename);
|
||||
const moduleExports = currentImportMap.get(name);
|
||||
assertExists(targetExports);
|
||||
assertExists(moduleExports);
|
||||
for (const [exportedName, localName] of reexports) {
|
||||
const exportedValue: any = moduleExports.get(exportedName);
|
||||
assertExists(exportedValue);
|
||||
targetExports.set(localName, exportedValue);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const PluginProvider = ({ children }: PropsWithChildren) =>
|
||||
createElement(
|
||||
Provider,
|
||||
{
|
||||
store: rootStore,
|
||||
},
|
||||
children
|
||||
);
|
||||
|
||||
const entryLogger = new DebugLogger('plugin:entry');
|
||||
|
||||
export const evaluatePluginEntry = (pluginName: string) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const currentImportMap = _pluginNestedImportsMap.get(pluginName)!;
|
||||
const pluginExports = currentImportMap.get('index.js');
|
||||
assertExists(pluginExports);
|
||||
const entryFunction = pluginExports.get('entry');
|
||||
const cleanup = entryFunction(<PluginContext>{
|
||||
register: (part, callback) => {
|
||||
entryLogger.info(`Registering ${pluginName} to ${part}`);
|
||||
if (part === 'headerItem') {
|
||||
rootStore.set(pluginHeaderItemAtom, items => ({
|
||||
...items,
|
||||
[pluginName]: callback as CallbackMap['headerItem'],
|
||||
}));
|
||||
addCleanup(pluginName, () => {
|
||||
rootStore.set(pluginHeaderItemAtom, items => {
|
||||
const newItems = { ...items };
|
||||
delete newItems[pluginName];
|
||||
return newItems;
|
||||
});
|
||||
});
|
||||
} else if (part === 'editor') {
|
||||
rootStore.set(pluginEditorAtom, items => ({
|
||||
...items,
|
||||
[pluginName]: callback as CallbackMap['editor'],
|
||||
}));
|
||||
addCleanup(pluginName, () => {
|
||||
rootStore.set(pluginEditorAtom, items => {
|
||||
const newItems = { ...items };
|
||||
delete newItems[pluginName];
|
||||
return newItems;
|
||||
});
|
||||
});
|
||||
} else if (part === 'setting') {
|
||||
rootStore.set(pluginSettingAtom, items => ({
|
||||
...items,
|
||||
[pluginName]: callback as CallbackMap['setting'],
|
||||
}));
|
||||
addCleanup(pluginName, () => {
|
||||
rootStore.set(pluginSettingAtom, items => {
|
||||
const newItems = { ...items };
|
||||
delete newItems[pluginName];
|
||||
return newItems;
|
||||
});
|
||||
});
|
||||
} else if (part === 'formatBar') {
|
||||
const register = (widget: AffineFormatBarWidget) => {
|
||||
const div = document.createElement('div');
|
||||
const root = widget.root;
|
||||
const cleanup = (callback as CallbackMap['formatBar'])(
|
||||
div,
|
||||
widget.page,
|
||||
() => {
|
||||
return root.selectionManager.value;
|
||||
}
|
||||
);
|
||||
addCleanup(pluginName, () => {
|
||||
AffineFormatBarWidget.customElements.delete(register);
|
||||
cleanup();
|
||||
});
|
||||
return div;
|
||||
};
|
||||
AffineFormatBarWidget.customElements.add(register);
|
||||
} else {
|
||||
throw new Error(`Unknown part: ${part}`);
|
||||
}
|
||||
},
|
||||
utils: {
|
||||
PluginProvider,
|
||||
},
|
||||
});
|
||||
if (typeof cleanup !== 'function') {
|
||||
throw new Error('Plugin entry must return a function');
|
||||
}
|
||||
addCleanup(pluginName, cleanup);
|
||||
};
|
||||
@@ -1,125 +0,0 @@
|
||||
import { DebugLogger } from '@affine/debug';
|
||||
import {
|
||||
builtinPluginPaths,
|
||||
enabledPluginAtom,
|
||||
invokeCleanup,
|
||||
pluginPackageJson,
|
||||
} from '@toeverything/infra/__internal__/plugin';
|
||||
import { loadedPluginNameAtom, rootStore } from '@toeverything/infra/atom';
|
||||
import { packageJsonOutputSchema } from '@toeverything/infra/type';
|
||||
import type { z } from 'zod';
|
||||
|
||||
import { evaluatePluginEntry, setupPluginCode } from './plugins/setup';
|
||||
|
||||
const logger = new DebugLogger('register-plugins');
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line no-var
|
||||
var __pluginPackageJson__: unknown[];
|
||||
}
|
||||
|
||||
Object.defineProperty(globalThis, '__pluginPackageJson__', {
|
||||
get() {
|
||||
return rootStore.get(pluginPackageJson);
|
||||
},
|
||||
});
|
||||
|
||||
rootStore.sub(enabledPluginAtom, () => {
|
||||
const added = new Set<string>();
|
||||
const removed = new Set<string>();
|
||||
const enabledPlugin = new Set(rootStore.get(enabledPluginAtom));
|
||||
enabledPlugin.forEach(pluginName => {
|
||||
if (!enabledPluginSet.has(pluginName)) {
|
||||
added.add(pluginName);
|
||||
}
|
||||
});
|
||||
enabledPluginSet.forEach(pluginName => {
|
||||
if (!enabledPlugin.has(pluginName)) {
|
||||
removed.add(pluginName);
|
||||
}
|
||||
});
|
||||
// update plugins
|
||||
enabledPluginSet.clear();
|
||||
enabledPlugin.forEach(pluginName => {
|
||||
enabledPluginSet.add(pluginName);
|
||||
});
|
||||
added.forEach(pluginName => {
|
||||
evaluatePluginEntry(pluginName);
|
||||
});
|
||||
removed.forEach(pluginName => {
|
||||
invokeCleanup(pluginName);
|
||||
});
|
||||
});
|
||||
const enabledPluginSet = new Set(rootStore.get(enabledPluginAtom));
|
||||
const loadedAssets = new Set<string>();
|
||||
|
||||
// we will load all plugins in parallel from builtinPlugins
|
||||
export const pluginRegisterPromise = Promise.all(
|
||||
[...builtinPluginPaths].map(url => {
|
||||
return fetch(`${url}/package.json`)
|
||||
.then(async res => {
|
||||
const packageJson = (await res.json()) as z.infer<
|
||||
typeof packageJsonOutputSchema
|
||||
>;
|
||||
packageJsonOutputSchema.parse(packageJson);
|
||||
const {
|
||||
name: pluginName,
|
||||
affinePlugin: {
|
||||
release,
|
||||
entry: { core },
|
||||
assets,
|
||||
},
|
||||
} = packageJson;
|
||||
rootStore.set(pluginPackageJson, json => [...json, packageJson]);
|
||||
logger.debug(`registering plugin ${pluginName}`);
|
||||
logger.debug(`package.json: ${packageJson}`);
|
||||
if (!release && !runtimeConfig.enablePlugin) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
const baseURL = url;
|
||||
const entryURL = `${baseURL}/${core}`;
|
||||
rootStore.set(loadedPluginNameAtom, prev => [...prev, pluginName]);
|
||||
await setupPluginCode(baseURL, pluginName, core);
|
||||
console.log(`prepareImports for ${pluginName} done`);
|
||||
await fetch(entryURL).then(async () => {
|
||||
if (assets.length > 0) {
|
||||
await Promise.all(
|
||||
assets.map(async (asset: string) => {
|
||||
// todo(himself65): add assets into shadow dom
|
||||
if (loadedAssets.has(asset)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (asset.endsWith('.css')) {
|
||||
loadedAssets.add(asset);
|
||||
const res = await fetch(`${baseURL}/${asset}`);
|
||||
if (res.ok) {
|
||||
// todo: how to put css file into sandbox?
|
||||
return res.text().then(text => {
|
||||
const style = document.createElement('style');
|
||||
style.setAttribute('plugin-id', pluginName);
|
||||
style.textContent = text;
|
||||
document.head.appendChild(style);
|
||||
});
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
if (!enabledPluginSet.has(pluginName)) {
|
||||
logger.debug(`plugin ${pluginName} is not enabled`);
|
||||
} else {
|
||||
logger.debug(`plugin ${pluginName} is enabled`);
|
||||
evaluatePluginEntry(pluginName);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(`error when fetch plugin from ${url}`, e);
|
||||
});
|
||||
})
|
||||
).then(() => {
|
||||
console.info('All plugins loaded');
|
||||
});
|
||||
@@ -1,188 +0,0 @@
|
||||
import {
|
||||
migrateDatabaseBlockTo3,
|
||||
migrateToSubdoc,
|
||||
} from '@affine/env/blocksuite';
|
||||
import { setupGlobal } from '@affine/env/global';
|
||||
import type {
|
||||
LocalIndexedDBDownloadProvider,
|
||||
WorkspaceAdapter,
|
||||
} from '@affine/env/workspace';
|
||||
import { WorkspaceFlavour, WorkspaceVersion } from '@affine/env/workspace';
|
||||
import type { RootWorkspaceMetadata } from '@affine/workspace/atom';
|
||||
import {
|
||||
type RootWorkspaceMetadataV2,
|
||||
rootWorkspacesMetadataAtom,
|
||||
workspaceAdaptersAtom,
|
||||
} from '@affine/workspace/atom';
|
||||
import { globalBlockSuiteSchema } from '@affine/workspace/manager';
|
||||
import {
|
||||
migrateLocalBlobStorage,
|
||||
upgradeV1ToV2,
|
||||
} from '@affine/workspace/migration';
|
||||
import { createIndexedDBDownloadProvider } from '@affine/workspace/providers';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
import { rootStore } from '@toeverything/infra/atom';
|
||||
|
||||
import { WorkspaceAdapters } from '../adapters/workspace';
|
||||
|
||||
async function tryMigration() {
|
||||
const value = localStorage.getItem('jotai-workspaces');
|
||||
if (value) {
|
||||
try {
|
||||
const metadata = JSON.parse(value) as RootWorkspaceMetadata[];
|
||||
const promises: Promise<void>[] = [];
|
||||
const newMetadata = [...metadata];
|
||||
metadata.forEach(oldMeta => {
|
||||
if (!('version' in oldMeta)) {
|
||||
const adapter = WorkspaceAdapters[oldMeta.flavour];
|
||||
assertExists(adapter);
|
||||
const upgrade = async () => {
|
||||
if (oldMeta.flavour !== WorkspaceFlavour.LOCAL) {
|
||||
console.warn('not supported');
|
||||
return;
|
||||
}
|
||||
const workspace = await adapter.CRUD.get(oldMeta.id);
|
||||
if (!workspace) {
|
||||
console.warn('cannot find workspace', oldMeta.id);
|
||||
return;
|
||||
}
|
||||
const doc = workspace.blockSuiteWorkspace.doc;
|
||||
const provider = createIndexedDBDownloadProvider(
|
||||
workspace.id,
|
||||
doc,
|
||||
{
|
||||
awareness:
|
||||
workspace.blockSuiteWorkspace.awarenessStore.awareness,
|
||||
}
|
||||
) as LocalIndexedDBDownloadProvider;
|
||||
provider.sync();
|
||||
await provider.whenReady;
|
||||
const newDoc = migrateToSubdoc(doc);
|
||||
if (doc === newDoc) {
|
||||
console.log('doc not changed');
|
||||
return;
|
||||
}
|
||||
const newWorkspace = upgradeV1ToV2(workspace);
|
||||
await migrateDatabaseBlockTo3(
|
||||
newWorkspace.blockSuiteWorkspace.doc,
|
||||
globalBlockSuiteSchema
|
||||
);
|
||||
|
||||
const newId = await adapter.CRUD.create(
|
||||
newWorkspace.blockSuiteWorkspace
|
||||
);
|
||||
|
||||
await adapter.CRUD.delete(workspace as any);
|
||||
console.log('migrated', oldMeta.id, newId);
|
||||
const index = newMetadata.findIndex(meta => meta.id === oldMeta.id);
|
||||
newMetadata[index] = {
|
||||
...oldMeta,
|
||||
id: newId,
|
||||
version: WorkspaceVersion.DatabaseV3,
|
||||
};
|
||||
await migrateLocalBlobStorage(workspace.id, newId);
|
||||
console.log('migrate to v2');
|
||||
};
|
||||
|
||||
// create a new workspace and push it to metadata
|
||||
promises.push(upgrade());
|
||||
} else if (oldMeta.version < WorkspaceVersion.DatabaseV3) {
|
||||
const adapter = WorkspaceAdapters[oldMeta.flavour];
|
||||
assertExists(adapter);
|
||||
promises.push(
|
||||
(async () => {
|
||||
if (oldMeta.flavour !== WorkspaceFlavour.LOCAL) {
|
||||
console.warn('not supported');
|
||||
return;
|
||||
}
|
||||
const workspace = await adapter.CRUD.get(oldMeta.id);
|
||||
if (workspace) {
|
||||
const provider = createIndexedDBDownloadProvider(
|
||||
workspace.id,
|
||||
workspace.blockSuiteWorkspace.doc,
|
||||
{
|
||||
awareness:
|
||||
workspace.blockSuiteWorkspace.awarenessStore.awareness,
|
||||
}
|
||||
) as LocalIndexedDBDownloadProvider;
|
||||
provider.sync();
|
||||
await provider.whenReady;
|
||||
await migrateDatabaseBlockTo3(
|
||||
workspace.blockSuiteWorkspace.doc,
|
||||
globalBlockSuiteSchema
|
||||
);
|
||||
}
|
||||
const index = newMetadata.findIndex(
|
||||
meta => meta.id === oldMeta.id
|
||||
);
|
||||
newMetadata[index] = {
|
||||
...oldMeta,
|
||||
version: WorkspaceVersion.DatabaseV3,
|
||||
};
|
||||
console.log('migrate to v3');
|
||||
})()
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.all(promises)
|
||||
.then(() => {
|
||||
console.log('migration done');
|
||||
})
|
||||
.catch(e => {
|
||||
console.error('migration failed', e);
|
||||
})
|
||||
.finally(() => {
|
||||
localStorage.setItem('jotai-workspaces', JSON.stringify(newMetadata));
|
||||
window.dispatchEvent(new CustomEvent('migration-done'));
|
||||
window.$migrationDone = true;
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('error when migrating data', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createFirstAppData() {
|
||||
const createFirst = (): RootWorkspaceMetadataV2[] => {
|
||||
const Plugins = Object.values(WorkspaceAdapters).sort(
|
||||
(a, b) => a.loadPriority - b.loadPriority
|
||||
);
|
||||
|
||||
return Plugins.flatMap(Plugin => {
|
||||
return Plugin.Events['app:init']?.().map(
|
||||
id =>
|
||||
<RootWorkspaceMetadataV2>{
|
||||
id,
|
||||
flavour: Plugin.flavour,
|
||||
version: WorkspaceVersion.DatabaseV3,
|
||||
}
|
||||
);
|
||||
}).filter((ids): ids is RootWorkspaceMetadataV2 => !!ids);
|
||||
};
|
||||
if (localStorage.getItem('is-first-open') !== null) {
|
||||
return;
|
||||
}
|
||||
const result = createFirst();
|
||||
console.info('create first workspace', result);
|
||||
localStorage.setItem('is-first-open', 'false');
|
||||
rootStore.set(rootWorkspacesMetadataAtom, result);
|
||||
}
|
||||
|
||||
export async function setup() {
|
||||
rootStore.set(
|
||||
workspaceAdaptersAtom,
|
||||
WorkspaceAdapters as Record<
|
||||
WorkspaceFlavour,
|
||||
WorkspaceAdapter<WorkspaceFlavour>
|
||||
>
|
||||
);
|
||||
|
||||
console.log('setup global');
|
||||
setupGlobal();
|
||||
|
||||
createFirstAppData();
|
||||
await tryMigration();
|
||||
await rootStore.get(rootWorkspacesMetadataAtom);
|
||||
console.log('setup done');
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import { initEmptyPage } from '@affine/env/blocksuite';
|
||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||
import { getOrCreateWorkspace } from '@affine/workspace/manager';
|
||||
import type { EditorContainer } from '@blocksuite/editor';
|
||||
import type { Page } from '@blocksuite/store';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { BlockSuiteEditor } from '../../blocksuite/block-suite-editor';
|
||||
|
||||
const blockSuiteWorkspace = getOrCreateWorkspace(
|
||||
'test',
|
||||
WorkspaceFlavour.LOCAL
|
||||
);
|
||||
|
||||
const page = blockSuiteWorkspace.createPage({ id: 'page0' });
|
||||
|
||||
const Editor = () => {
|
||||
const onLoad = useCallback((page: Page, editor: EditorContainer) => {
|
||||
// @ts-expect-error
|
||||
globalThis.page = page;
|
||||
// @ts-expect-error
|
||||
globalThis.editor = editor;
|
||||
return () => void 0;
|
||||
}, []);
|
||||
|
||||
if (!page) {
|
||||
return <>loading...</>;
|
||||
}
|
||||
return (
|
||||
<BlockSuiteEditor
|
||||
page={page}
|
||||
mode="page"
|
||||
onInit={initEmptyPage}
|
||||
onLoad={onLoad}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Editor;
|
||||
@@ -1,53 +0,0 @@
|
||||
import { Modal, ModalWrapper, Wrapper } from '@affine/component';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { CloseIcon } from '@blocksuite/icons';
|
||||
import { Button, IconButton } from '@toeverything/components/button';
|
||||
|
||||
import { Content, ContentTitle, Header, StyleTips } from './style';
|
||||
|
||||
interface EnableAffineCloudModalProps {
|
||||
open: boolean;
|
||||
onConfirm: () => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const EnableAffineCloudModal = ({
|
||||
onConfirm,
|
||||
open,
|
||||
onClose,
|
||||
}: EnableAffineCloudModalProps) => {
|
||||
const t = useAFFiNEI18N();
|
||||
|
||||
return (
|
||||
<Modal open={open} onClose={onClose} data-testid="logout-modal">
|
||||
<ModalWrapper width={560} height={292}>
|
||||
<Header>
|
||||
<IconButton onClick={onClose}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</Header>
|
||||
<Content>
|
||||
<ContentTitle>{t['Enable AFFiNE Cloud']()}?</ContentTitle>
|
||||
<StyleTips>{t['Enable AFFiNE Cloud Description']()}</StyleTips>
|
||||
{/* <StyleTips>{t('Retain cached cloud data')}</StyleTips> */}
|
||||
<Wrapper width={284} margin="auto">
|
||||
<Button
|
||||
data-testid="confirm-enable-affine-cloud-button"
|
||||
type="primary"
|
||||
block
|
||||
onClick={onConfirm}
|
||||
style={{
|
||||
marginBottom: '16px',
|
||||
}}
|
||||
>
|
||||
{t['Sign in and Enable']()}
|
||||
</Button>
|
||||
<Button onClick={onClose} block>
|
||||
{t['Not now']()}
|
||||
</Button>
|
||||
</Wrapper>
|
||||
</Content>
|
||||
</ModalWrapper>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
@@ -1,31 +0,0 @@
|
||||
import { styled } from '@affine/component';
|
||||
|
||||
export const Header = styled('div')({
|
||||
height: '44px',
|
||||
display: 'flex',
|
||||
flexDirection: 'row-reverse',
|
||||
paddingRight: '10px',
|
||||
paddingTop: '10px',
|
||||
flexShrink: 0,
|
||||
});
|
||||
|
||||
export const Content = styled('div')({
|
||||
textAlign: 'center',
|
||||
});
|
||||
|
||||
export const ContentTitle = styled('h1')({
|
||||
fontSize: '20px',
|
||||
lineHeight: '28px',
|
||||
fontWeight: 600,
|
||||
textAlign: 'center',
|
||||
});
|
||||
|
||||
export const StyleTips = styled('div')(() => {
|
||||
return {
|
||||
userSelect: 'none',
|
||||
width: '400px',
|
||||
margin: 'auto',
|
||||
marginBottom: '32px',
|
||||
marginTop: '12px',
|
||||
};
|
||||
});
|
||||
@@ -1,80 +0,0 @@
|
||||
import { Menu, MenuItem, MenuTrigger, styled } from '@affine/component';
|
||||
import { LOCALES } from '@affine/i18n';
|
||||
import { useI18N } from '@affine/i18n';
|
||||
import type { ButtonProps } from '@toeverything/components/button';
|
||||
import type { ReactElement } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const StyledListItem = styled(MenuItem)(() => ({
|
||||
width: '132px',
|
||||
height: '38px',
|
||||
textTransform: 'capitalize',
|
||||
}));
|
||||
|
||||
interface LanguageMenuContentProps {
|
||||
currentLanguage?: string;
|
||||
}
|
||||
|
||||
const LanguageMenuContent = ({ currentLanguage }: LanguageMenuContentProps) => {
|
||||
const i18n = useI18N();
|
||||
const changeLanguage = useCallback(
|
||||
(event: string) => {
|
||||
return i18n.changeLanguage(event);
|
||||
},
|
||||
[i18n]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{LOCALES.map(option => {
|
||||
return (
|
||||
<StyledListItem
|
||||
key={option.name}
|
||||
active={currentLanguage === option.originalName}
|
||||
title={option.name}
|
||||
onClick={() => {
|
||||
changeLanguage(option.tag).catch(err => {
|
||||
throw new Error('Failed to change language', err);
|
||||
});
|
||||
}}
|
||||
>
|
||||
{option.originalName}
|
||||
</StyledListItem>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
interface LanguageMenuProps {
|
||||
triggerProps?: ButtonProps;
|
||||
}
|
||||
|
||||
export const LanguageMenu = ({ triggerProps }: LanguageMenuProps) => {
|
||||
const i18n = useI18N();
|
||||
|
||||
const currentLanguage = LOCALES.find(item => item.tag === i18n.language);
|
||||
|
||||
return (
|
||||
<Menu
|
||||
content={
|
||||
(
|
||||
<LanguageMenuContent
|
||||
currentLanguage={currentLanguage?.originalName}
|
||||
/>
|
||||
) as ReactElement
|
||||
}
|
||||
placement="bottom-end"
|
||||
trigger="click"
|
||||
disablePortal={true}
|
||||
>
|
||||
<MenuTrigger
|
||||
data-testid="language-menu-button"
|
||||
style={{ textTransform: 'capitalize' }}
|
||||
{...triggerProps}
|
||||
>
|
||||
{currentLanguage?.originalName}
|
||||
</MenuTrigger>
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||