Compare commits

..

85 Commits

Author SHA1 Message Date
Peng Xiao
a143379161 fix(electron): remove cors headers hack (#5581) 2024-01-12 16:49:16 +08:00
regischen
8e7dedfe82 feat: bump blocksuite (#5575) 2024-01-12 12:43:56 +08:00
EYHN
d25a8547d0 refactor(core): move page list to core (#5556) 2024-01-12 12:43:45 +08:00
Peng Xiao
4d16229fea chore(core): remove affine/cmdk package (#5552)
patch cmdk based on https://github.com/pengx17/cmdk/tree/patch-1
fix https://github.com/toeverything/AFFiNE/issues/5548
2024-01-12 12:43:35 +08:00
EYHN
99371be7e8 fix(core): workspace not found after import (#5571)
close TOV-393
2024-01-12 11:05:59 +08:00
李华桥
34ed8dd7a5 Merge branch 'canary' into stable 2024-01-10 10:59:28 +08:00
李华桥
39b7b671b1 Merge branch 'canary' into stable 2024-01-09 19:44:52 +08:00
李华桥
207b56d5af Merge branch 'canary' into stable 2024-01-09 17:16:17 +08:00
DarkSky
9e94e7195b fix: use absolute path in gql client (#5454) (#5462) 2023-12-29 16:02:29 +08:00
Peng Xiao
de951c8779 fix(core): enable page history for beta/stable (#5415) 2023-12-27 14:39:59 +08:00
EYHN
fd37026ca5 fix(component): fix font display on safari (#5393)
before

![CleanShot 2023-12-25 at 13.09.26.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/g3jz87HxbjOJpXV3FPT7/4fe08951-67bb-4050-ba14-94391db1cac1.png)

after

![CleanShot 2023-12-25 at 13.09.13.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/g3jz87HxbjOJpXV3FPT7/fbfb17ec-b871-4746-9d3c-d24f850ecca1.png)
2023-12-27 14:39:50 +08:00
JimmFly
4fd5812a89 fix(core): avatars are not aligned (#5404) 2023-12-26 20:43:08 +08:00
Peng Xiao
d01e987ecc fix(core): trash page footer display issue (#5402)
Before

![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/eb5e5b18-c4a2-469b-8763-be34c39ba736.png)

After

![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/7b3ef339-0cb5-44fe-9e75-cec0e97d28b7.png)
2023-12-26 20:42:54 +08:00
Joooye_34
d87c218c0b fix(electron): set stable base url to app.affine.pro (#5401)
close TOV-282
2023-12-26 20:42:41 +08:00
Peng Xiao
a5bf5cc244 fix(core): about setting blink issue (#5399) 2023-12-26 20:42:33 +08:00
Peng Xiao
16bcd6e76b fix(core): workpace list blink issue on open (#5400) 2023-12-26 20:42:19 +08:00
JimmFly
2e2ace8472 chore(core): add background color to questionnaire (#5396) 2023-12-26 20:42:06 +08:00
Cats Juice
37cff8fe8d fix(core): correct title of onboarding article-2 (#5387) 2023-12-26 20:41:58 +08:00
DarkSky
70ab3b4916 fix: use prefix in electron to prevent formdata bug (#5395) 2023-12-26 20:41:47 +08:00
EYHN
f42ba54578 fix(core): fix flickering workspace list (#5391) 2023-12-26 20:41:36 +08:00
EYHN
a67c8181fc fix(workspace): fix svg file with xml header (#5388) 2023-12-26 20:41:28 +08:00
regischen
613efbded9 feat: bump blocksuite (#5386) 2023-12-26 20:41:18 +08:00
李华桥
549419d102 Merge branch 'canary' into stable 2023-12-22 16:29:51 +08:00
李华桥
21c42f8771 Merge branch 'canary' into stable 2023-12-22 01:29:30 +08:00
李华桥
9012adda7a Merge branch 'canary' into stable 2023-12-21 18:42:56 +08:00
李华桥
fb442e9055 Merge branch 'canary' into stable 2023-12-21 16:22:57 +08:00
李华桥
a231474dd2 Merge branch 'canary' into stable 2023-12-21 14:26:01 +08:00
李华桥
833b42000b Merge branch 'canary' into stable 2023-12-20 16:36:44 +08:00
李华桥
7690c48710 Merge branch 'canary' into stable 2023-12-20 16:32:36 +08:00
DarkSky
579828a700 fix: use secure websocket (#5297) 2023-12-13 22:28:04 +08:00
DarkSky
746db2ccfc feat: only follow serverUrlPrefix at redirect to client (#5295) 2023-12-13 20:37:20 +08:00
李华桥
eff344a9c1 Merge branch 'canary' into stable 2023-12-12 16:45:47 +08:00
李华桥
c89ebab596 Merge branch 'canary' into stable 2023-12-12 11:04:33 +08:00
liuyi
62f4421b7c fix(server): avoid updates persist forever (#5258) 2023-12-11 17:42:25 +08:00
李华桥
42383dbd29 Merge branch 'canary' into stable 2023-12-10 21:04:15 +08:00
李华桥
120e7397ba Merge branch 'canary' into stable 2023-12-01 16:12:17 +08:00
李华桥
24123ad01c Revert "Revert "Merge remote-tracking branch 'origin/canary' into stable""
This reverts commit 89197bacef.
2023-12-01 13:29:43 +08:00
李华桥
ad50320391 v0.10.3 2023-12-01 12:52:15 +08:00
李华桥
eb21a60dda v0.10.3-beta.7 2023-12-01 12:12:20 +08:00
Joooye_34
c0e3be2d40 fix(core): rerender error boundary when route change and improve sentry report (#5147) 2023-12-01 04:04:44 +00:00
李华桥
09d3b72358 v0.10.3-beta.6 2023-11-30 23:02:26 +08:00
Joooye_34
246e16c6c0 fix(infra): compatibility logic follow blocksuite (#5143) 2023-11-30 23:01:38 +08:00
李华桥
dc279d062b v0.10.3-beta.5 2023-11-30 16:49:55 +08:00
Joooye_34
47d5f9e1c2 fix(infra): use blocksuite api to check compatibility (#5137) 2023-11-30 08:48:13 +00:00
Joooye_34
a226eb8d5f fix(core): expose catched editor load error (#5133) 2023-11-29 20:31:35 +08:00
Joooye_34
908c4e1a6f ci: add sentry env when frontend assets build (#5131) 2023-11-29 10:03:49 +00:00
李华桥
1d0bcc80a0 v0.10.3-beta.4 2023-11-29 16:14:06 +08:00
Joooye_34
50010bd824 fix(core): implement editor timeout and report error from boundary (#5105) 2023-11-29 08:10:38 +00:00
liuyi
c0ede1326d fix(server): wrong OTEL config (#5084) 2023-11-29 11:19:13 +08:00
李华桥
89197bacef Revert "Merge remote-tracking branch 'origin/canary' into stable"
This reverts commit 992ed89a89, reversing
changes made to d272d7922d.
2023-11-29 11:18:45 +08:00
李华桥
f97d323ab5 Revert "Revert "refactor(server): standarderlize metrics and trace with OTEL (#5054)""
This reverts commit c1cd1713b9.
2023-11-29 11:07:28 +08:00
EYHN
2acb219dcc fix(workspace): filter awareness from other workspace (#5093) 2023-11-28 16:47:45 +08:00
LongYinan
992ed89a89 Merge remote-tracking branch 'origin/canary' into stable 2023-11-28 15:12:52 +08:00
李华桥
d272d7922d v0.10.3-beta.2 2023-11-25 23:50:40 +08:00
李华桥
c1cd1713b9 Revert "refactor(server): standarderlize metrics and trace with OTEL (#5054)"
This reverts commit 91efca107a.
2023-11-25 23:50:39 +08:00
李华桥
b20e91bee0 v0.10.3-beta.1 2023-11-25 14:14:40 +08:00
李华桥
9a4e5ec8c3 Merge branch 'canary' into stable 2023-11-25 14:14:14 +08:00
李华桥
2019838ae7 v0.10.3-beta.0 2023-11-24 11:39:23 +08:00
李华桥
30ff25f400 Merge branch 'canary' into stable 2023-11-23 23:40:32 +08:00
李华桥
e766208c18 chore: reset merge wrong codes 2023-11-23 22:53:06 +08:00
李华桥
8742f28148 Merge branch 'canary' into stable 2023-11-23 21:31:42 +08:00
LongYinan
cd291bb60e build: remove useless source-map-loader to speedup webpack (#4910) 2023-11-20 10:52:28 +08:00
LongYinan
62c0efcfd1 fix(core): handle the getSession network error properly (#4909)
If network offline or API error happens, the `session` returned by the `useSession` hook will be null, so we can't assume it is not null.

There should be following changes:
1. create a page in ErrorBoundary to let the user refetch the session.
2. The `SessionProvider` stop to pull the new session once the session is null, we need to figure out a way to pull the new session when the network is back or the user click the refetch button.
2023-11-17 16:50:48 +08:00
liuyi
87248b3337 fix(server): all viewers can share public link (#4968) 2023-11-17 12:34:15 +08:00
Joooye_34
00c940f7df chore: bump affine version to 0.10.2 (#4959) 2023-11-16 15:48:37 +08:00
Flrande
931b459fbd chore: bump blocksuite (#4958) 2023-11-16 14:27:39 +08:00
LongYinan
51e71f4a0a ci: prevent error if rust build is cached by nx (#4951)
If Rust build was cached by nx, only the output file will be presented. The chmod command will be failed in this case like: https://github.com/toeverything/AFFiNE/actions/runs/6874496337/job/18697360212
2023-11-16 10:31:51 +08:00
Peng Xiao
9b631f2328 fix(infra): page id compat fix for page ids in workspace.meta (#4950)
since we strip `page:` in keys of workspacedoc.spaces, we should also strip the prefix in meta.pages as well.
2023-11-15 17:36:08 +08:00
LongYinan
01f481a9b6 ci: only disable postinstall on macOS in nightly desktop build (#4938) 2023-11-14 23:00:30 +08:00
Joooye_34
0177ab5c87 fix(infra): workspace migration without blockVersions (#4936) 2023-11-14 14:38:11 +01:00
Peng Xiao
4db35d341c perf(component): use png instead of svg for rendering noise svg (#4935) 2023-11-14 11:52:51 +00:00
DarkSky
3c4a803c97 fix: change password token check (#4934) (#4932) 2023-11-14 11:15:54 +00:00
LongYinan
05154dc7ca ci: disable postinstall in nightly desktop build (#4930)
Should be part of https://github.com/toeverything/AFFiNE/pull/4885
2023-11-14 14:13:55 +08:00
Peng Xiao
c90b477f60 fix(core): change server url of stable to insider (#4902) (#4926) 2023-11-14 12:05:52 +08:00
李华桥
6f18ddbe85 v0.10.1 2023-11-13 19:49:26 +08:00
LongYinan
dde779a71d test(e2e): add subdoc migration test (#4921)
test(e2e): add subdoc migration test

fix: remove .only
2023-11-13 18:00:40 +08:00
Peng Xiao
bd9f66fbc7 fix(infra): compatibility fix for space prefix (#4912)
It seems there are some cases that [this upstream PR](https://github.com/toeverything/blocksuite/pull/4747) will cause data loss.

Because of some historical reasons, the page id could be different with its doc id.
It might be caused by subdoc migration in the following (not 100% sure if all white screen issue is caused by it) 0714c12703/packages/common/infra/src/blocksuite/index.ts (L538-L540)

In version 0.10, page id in spaces no longer has prefix "space:"
The data flow for fetching a doc's updates is:
- page id in `meta.pages` -> find `${page-id}` in `doc.spaces` -> `doc` -> `doc.guid`
if `doc` is not found in `doc.spaces`, a new doc will be created and its `doc.guid` is the same with its pageId
- because of guid logic change, the doc that previously prefixed with `space:` will not be found in `doc.spaces`
- when fetching the rows of this doc using the doc id === page id,
  it will return EMPTY since there is no updates associated with the page id

The provided fix in the PR will patch the `spaces` field of the root doc so that after 0.10 the page doc can still be found in the `spaces` map. It shall apply to both of the idb & sqlite datasources.

Special thanks to @lawvs 's db file for investigation!
2023-11-13 17:57:56 +08:00
liuyi
92f1f40bfa fix(server): wrap updates applying in a transaction (#4922) 2023-11-13 08:49:30 +00:00
LongYinan
48dc1049b3 Merge pull request #4913 from toeverything/darksky/cleanup-depolyment
chore: cleanup deployment
2023-11-12 11:20:02 +08:00
DarkSky
9add530370 chore: cleanup deployment 2023-11-12 11:03:25 +08:00
LongYinan
b77460d871 Merge pull request #4908 from toeverything/61/hotfix-websocket-payload
fix(server): increase server acceptable websocket payload size
2023-11-10 22:01:48 +08:00
forehalo
42db41776b fix(server): increase server acceptable websocket payload size 2023-11-10 21:31:45 +08:00
李华桥
075439c74f fix(core): change server url of stable to insider 2023-11-10 18:32:53 +08:00
Yifeng Wang
fc6c553ece chore: bump theme (#4904)
Co-authored-by: 李华桥 <joooye1991@gmail.com>
2023-11-10 15:40:38 +08:00
Joooye_34
59cb3d5df1 fix(core): change server url of stable to insider (#4902) 2023-11-10 14:50:57 +08:00
4089 changed files with 114694 additions and 303132 deletions

View File

@@ -1,4 +1,2 @@
[target.x86_64-pc-windows-msvc]
rustflags = ["-C", "target-feature=+crt-static"]
[target.aarch64-pc-windows-msvc]
rustflags = ["-C", "target-feature=+crt-static"]

View File

@@ -8,8 +8,5 @@ corepack prepare yarn@stable --activate
# install dependencies
yarn install
# Build Server Dependencies
yarn workspace @affine/server-native build
# Create database
yarn workspace @affine/server prisma db push
yarn workspace @affine/server prisma db push

View File

@@ -21,6 +21,5 @@
}
},
"updateContentCommand": "bash ./.devcontainer/build.sh",
"postCreateCommand": "bash ./.devcontainer/setup-user.sh",
"postStartCommand": ["yarn dev", "yarn workspace @affine/server dev"]
"postCreateCommand": "bash ./.devcontainer/setup-user.sh"
}

View File

@@ -1,9 +1,7 @@
set -e
if [ -v GRAPHITE_TOKEN ];then
gt auth --token $GRAPHITE_TOKEN
fi
git fetch origin canary:canary --depth=1
git fetch
git branch canary -t origin/canary
gt init --trunk canary

View File

@@ -1,7 +1,14 @@
ENABLE_PLUGIN=
ENABLE_TEST_PROPERTIES=
ENABLE_BC_PROVIDER=
CHANGELOG_URL=
ENABLE_PRELOADING=
ENABLE_NEW_SETTING_MODAL=
ENABLE_SQLITE_PROVIDER=
ENABLE_NEW_SETTING_UNSTABLE_API=
ENABLE_CAPTCHA=
CAPTCHA_SITE_KEY=
ENABLE_ENHANCE_SHARE_MODE=
ALLOW_LOCAL_WORKSPACE=
DEBUG_JOTAI=
ENABLE_NOTIFICATION_CENTER=
ENABLE_CLOUD=
ENABLE_MOVE_DATABASE=
SHOULD_REPORT_TRACE=
TRACE_REPORT_ENDPOINT=
CAPTCHA_SITE_KEY=

View File

@@ -12,5 +12,3 @@ static
web-static
public
packages/frontend/i18n/src/i18n-generated.ts
packages/frontend/i18n/src/i18n-completenesses.json
packages/frontend/templates/*.gen.ts

View File

@@ -1,4 +1,4 @@
const { join } = require('node:path');
const { resolve } = require('node:path');
const createPattern = packageName => [
{
@@ -31,11 +31,27 @@ 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 `BUILD_CONFIG.isElectron` instead',
importNames: ['isElectron'],
'Do not import from @affine/env/constant. Use `environment.isDesktop` instead',
importNames: ['isDesktop'],
},
];
@@ -43,19 +59,19 @@ const allPackages = [
'packages/backend/server',
'packages/frontend/component',
'packages/frontend/core',
'packages/frontend/apps/electron',
'packages/frontend/apps/web',
'packages/frontend/apps/mobile',
'packages/frontend/electron',
'packages/frontend/graphql',
'packages/frontend/i18n',
'packages/frontend/native',
'packages/frontend/templates',
'packages/frontend/track',
'packages/frontend/workspace',
'packages/common/debug',
'packages/common/env',
'packages/common/infra',
'packages/common/theme',
'packages/common/y-indexeddb',
'tools/cli',
'tests/storybook',
];
/**
@@ -88,17 +104,16 @@ const config = {
},
ecmaVersion: 'latest',
sourceType: 'module',
project: join(__dirname, 'tsconfig.eslint.json'),
project: resolve(__dirname, './tsconfig.eslint.json'),
},
plugins: [
'react',
'@typescript-eslint',
'simple-import-sort',
'sonarjs',
'import-x',
'i',
'unused-imports',
'unicorn',
'rxjs',
],
rules: {
'array-callback-return': 'error',
@@ -131,7 +146,6 @@ const config = {
'unused-imports/no-unused-imports': 'error',
'simple-import-sort/imports': 'error',
'simple-import-sort/exports': 'error',
'import-x/no-duplicates': 'error',
'@typescript-eslint/ban-ts-comment': [
'error',
{
@@ -160,6 +174,27 @@ const config = {
message: "Import from '@blocksuite/global/utils'",
importNames: ['assertExists', 'assertEquals'],
},
{
group: ['react-router-dom'],
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'],
},
],
},
],
@@ -182,7 +217,6 @@ const config = {
'unicorn/no-useless-promise-resolve-reject': 'error',
'unicorn/no-new-array': 'error',
'unicorn/new-for-builtins': 'error',
'unicorn/prefer-node-protocol': 'error',
'sonarjs/no-all-duplicated-branches': 'error',
'sonarjs/no-element-overwrite': 'error',
'sonarjs/no-empty-collection': 'error',
@@ -199,21 +233,6 @@ const config = {
'sonarjs/no-collection-size-mischeck': 'error',
'sonarjs/no-useless-catch': 'error',
'sonarjs/no-identical-functions': 'error',
'rxjs/finnish': [
'error',
{
functions: false,
methods: false,
strict: true,
types: {
'^LiveData$': true,
// some yjs classes are Observables, but they don't need to be in Finnish notation
'^Doc$': false, // yjs Doc
'^Awareness$': false, // yjs Awareness
'^UndoManager$': false, // yjs UndoManager
},
},
],
},
overrides: [
{
@@ -229,7 +248,10 @@ const config = {
},
},
...allPackages.map(pkg => ({
files: [`${pkg}/src/**/*.ts`, `${pkg}/src/**/*.tsx`, `${pkg}/**/*.mjs`],
files: [`${pkg}/src/**/*.ts`, `${pkg}/src/**/*.tsx`],
parserOptions: {
project: resolve(__dirname, './tsconfig.eslint.json'),
},
rules: {
'@typescript-eslint/no-restricted-imports': [
'error',
@@ -246,12 +268,11 @@ const config = {
],
'@typescript-eslint/no-misused-promises': ['error'],
'@typescript-eslint/prefer-readonly': 'error',
'import-x/no-extraneous-dependencies': ['error'],
'i/no-extraneous-dependencies': ['error'],
'react-hooks/exhaustive-deps': [
'warn',
{
additionalHooks:
'(useAsyncCallback|useCatchEventCallback|useDraggable|useDropTarget|useRefEffect)',
additionalHooks: 'useAsyncCallback',
},
],
},

View File

@@ -7,8 +7,6 @@ body:
attributes:
value: |
Thanks for taking the time to fill out this bug report!
Check out this [link](https://github.com/toeverything/AFFiNE/blob/canary/docs/issue-triaging.md)
to learn how we manage issues and when your issue will be processed.
- type: textarea
id: what-happened
attributes:
@@ -43,14 +41,6 @@ body:
- Firefox
- Safari
- Other
- type: checkboxes
id: selfhost
attributes:
label: Are you self-hosting?
description: >
If you are self-hosting, please check the box and provide information about your setup.
options:
- label: 'Yes'
- type: textarea
id: logs
attributes:
@@ -63,3 +53,11 @@ body:
description: |
Links? References? Anything that will give us more context about the issue you are encountering!
Tip: You can attach images here
- type: checkboxes
attributes:
label: Are you willing to submit a PR?
description: >
(Optional) We encourage you to submit a [Pull Request](https://github.com/toeverything/affine/pulls) (PR) to help improve AFFiNE for everyone, especially if you have a good understanding of how to implement a fix or feature.
See the AFFiNE [Contributing Guide](https://github.com/toeverything/affine/blob/canary/CONTRIBUTING.md) to get started.
options:
- label: Yes I'd like to help by submitting a PR!

View File

@@ -37,7 +37,7 @@ runs:
echo "TARGET_CC=clang" >> "$GITHUB_ENV"
- name: Cache cargo
uses: actions/cache@v4
uses: actions/cache@v3
with:
path: |
~/.cargo/registry/index/
@@ -49,7 +49,7 @@ runs:
- name: Build
shell: bash
run: |
yarn workspace ${{ inputs.package }} build --target ${{ inputs.target }} --use-napi-cross
yarn workspace ${{ inputs.package }} nx build ${{ inputs.package }} --target ${{ inputs.target }} --use-napi-cross
env:
NX_CLOUD_ACCESS_TOKEN: ${{ inputs.nx_token }}
DEBUG: 'napi:*'

View File

@@ -1,36 +0,0 @@
name: 'Auth to Cluster'
description: 'Auth to the GCP Cluster'
inputs:
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:
- id: auth
uses: google-github-actions/auth@v2
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@v2'
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 }}

View File

@@ -1,36 +0,0 @@
name: 'Run Copilot E2E Test'
description: 'Run Copilot E2E Test'
inputs:
script:
description: 'Script to run'
default: 'yarn workspace @affine-test/affine-cloud-copilot e2e --forbid-only'
required: false
openai-key:
description: 'OpenAI secret key'
required: true
fal-key:
description: 'Fal secret key'
required: true
runs:
using: 'composite'
steps:
- name: Prepare Server Test Environment
uses: ./.github/actions/server-test-env
- name: Server Copilot E2E Test
shell: bash
run: ${{ inputs.script }}
env:
COPILOT: true
DEV_SERVER_URL: http://localhost:8080
COPILOT_OPENAI_API_KEY: ${{ inputs.openai-key }}
COPILOT_FAL_API_KEY: ${{ inputs.fal-key }}
- name: Upload test results
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: test-results-e2e-server-copilot
path: ./test-results
if-no-files-found: ignore

View File

@@ -24,14 +24,24 @@ runs:
shell: bash
run: |
echo "GIT_SHORT_HASH=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV"
- name: 'Auth to cluster'
uses: './.github/actions/cluster-auth'
- uses: azure/setup-helm@v3
- id: auth
uses: google-github-actions/auth@v2
with:
gcp-project-number: '${{ inputs.gcp-project-number }}'
gcp-project-id: '${{ inputs.gcp-project-id }}'
service-account: '${{ inputs.service-account }}'
cluster-name: '${{ inputs.cluster-name }}'
cluster-location: '${{ inputs.cluster-location }}'
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@v2'
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

View File

@@ -13,14 +13,11 @@ const {
R2_ACCOUNT_ID,
R2_ACCESS_KEY_ID,
R2_SECRET_ACCESS_KEY,
ENABLE_CAPTCHA,
CAPTCHA_TURNSTILE_SECRET,
METRICS_CUSTOMER_IO_TOKEN,
COPILOT_OPENAI_API_KEY,
COPILOT_FAL_API_KEY,
COPILOT_UNSPLASH_API_KEY,
MAILER_SENDER,
MAILER_USER,
MAILER_PASSWORD,
OAUTH_EMAIL_SENDER,
OAUTH_EMAIL_LOGIN,
OAUTH_EMAIL_PASSWORD,
AFFINE_GOOGLE_CLIENT_ID,
AFFINE_GOOGLE_CLIENT_SECRET,
CLOUD_SQL_IAM_ACCOUNT,
@@ -40,42 +37,6 @@ const isProduction = buildType === 'stable';
const isBeta = buildType === 'beta';
const isInternal = buildType === 'internal';
const replicaConfig = {
stable: {
web: 3,
graphql: Number(process.env.PRODUCTION_GRAPHQL_REPLICA) || 3,
sync: Number(process.env.PRODUCTION_SYNC_REPLICA) || 3,
renderer: Number(process.env.PRODUCTION_RENDERER_REPLICA) || 3,
},
beta: {
web: 2,
graphql: Number(process.env.BETA_GRAPHQL_REPLICA) || 2,
sync: Number(process.env.BETA_SYNC_REPLICA) || 2,
renderer: Number(process.env.BETA_RENDERER_REPLICA) || 2,
},
canary: {
web: 2,
graphql: 2,
sync: 2,
renderer: 2,
},
};
const cpuConfig = {
beta: {
web: '300m',
graphql: '1',
sync: '1',
renderer: '300m',
},
canary: {
web: '300m',
graphql: '1',
sync: '1',
renderer: '300m',
},
};
const createHelmCommand = ({ isDryRun }) => {
const flag = isDryRun ? '--dry-run' : '--atomic';
const imageTag = `${buildType}-${GIT_SHORT_HASH}`;
@@ -103,18 +64,9 @@ const createHelmCommand = ({ isDryRun }) => {
`--set-json cloud-sql-proxy.nodeSelector=\"{ \\"iam.gke.io/gke-metadata-server-enabled\\": \\"true\\" }\"`,
]
: [];
const cpu = cpuConfig[buildType];
const resources = cpu
? [
`--set web.resources.requests.cpu="${cpu.web}"`,
`--set graphql.resources.requests.cpu="${cpu.graphql}"`,
`--set sync.resources.requests.cpu="${cpu.sync}"`,
]
: [];
const replica = replicaConfig[buildType] || replicaConfig.canary;
const webReplicaCount = isProduction ? 3 : isBeta ? 2 : 2;
const graphqlReplicaCount = isProduction ? 10 : isBeta ? 5 : 2;
const syncReplicaCount = isProduction ? 10 : isBeta ? 5 : 2;
const namespace = isProduction
? 'production'
: isBeta
@@ -127,47 +79,35 @@ const createHelmCommand = ({ isDryRun }) => {
const deployCommand = [
`helm upgrade --install affine .github/helm/affine`,
`--namespace ${namespace}`,
`--set-string global.app.buildType="${buildType}"`,
`--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\\": \\"${STATIC_IP_NAME}\\" }\"`,
`--set-string global.ingress.host="${host}"`,
`--set global.objectStorage.r2.enabled=true`,
`--set-string global.objectStorage.r2.accountId="${R2_ACCOUNT_ID}"`,
`--set-string global.objectStorage.r2.accessKeyId="${R2_ACCESS_KEY_ID}"`,
`--set-string global.objectStorage.r2.secretAccessKey="${R2_SECRET_ACCESS_KEY}"`,
`--set-string global.version="${APP_VERSION}"`,
...redisAndPostgres,
`--set web.replicaCount=${replica.web}`,
`--set web.replicaCount=${webReplicaCount}`,
`--set-string web.image.tag="${imageTag}"`,
`--set graphql.replicaCount=${replica.graphql}`,
`--set graphql.replicaCount=${graphqlReplicaCount}`,
`--set-string graphql.image.tag="${imageTag}"`,
`--set graphql.app.host=${host}`,
`--set graphql.app.captcha.enabled=true`,
`--set graphql.app.captcha.enabled=${ENABLE_CAPTCHA}`,
`--set-string graphql.app.captcha.turnstile.secret="${CAPTCHA_TURNSTILE_SECRET}"`,
`--set graphql.app.copilot.enabled=true`,
`--set-string graphql.app.copilot.openai.key="${COPILOT_OPENAI_API_KEY}"`,
`--set-string graphql.app.copilot.fal.key="${COPILOT_FAL_API_KEY}"`,
`--set-string graphql.app.copilot.unsplash.key="${COPILOT_UNSPLASH_API_KEY}"`,
`--set-string graphql.app.mailer.sender="${MAILER_SENDER}"`,
`--set-string graphql.app.mailer.user="${MAILER_USER}"`,
`--set-string graphql.app.mailer.password="${MAILER_PASSWORD}"`,
`--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.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.metrics.enabled=true`,
`--set-string graphql.app.metrics.customerIo.token="${METRICS_CUSTOMER_IO_TOKEN}"`,
`--set graphql.app.experimental.enableJwstCodec=${namespace === 'dev'}`,
`--set graphql.app.experimental.enableJwstCodec=true`,
`--set graphql.app.features.earlyAccessPreview=false`,
`--set graphql.app.features.syncClientVersionCheck=true`,
`--set sync.replicaCount=${replica.sync}`,
`--set sync.replicaCount=${syncReplicaCount}`,
`--set-string sync.image.tag="${imageTag}"`,
`--set-string renderer.image.tag="${imageTag}"`,
`--set renderer.app.host=${host}`,
`--set renderer.replicaCount=${replica.renderer}`,
...serviceAnnotations,
...resources,
`--timeout 10m`,
flag,
].join(' ');

View File

@@ -0,0 +1,22 @@
name: 'Download core artifacts'
description: 'Download core artifacts and extract to dist'
inputs:
path:
description: 'Path to extract'
required: true
runs:
using: 'composite'
steps:
- name: Download tar.gz
uses: actions/download-artifact@v4
with:
name: core
path: .
- name: Extract core artifacts
shell: bash
run: |
mkdir -p ${{ inputs.path }}
tar -xvf dist.tar.gz --directory ${{ inputs.path }}
rm dist.tar.gz

View File

@@ -1,22 +0,0 @@
name: 'Download core artifacts'
description: 'Download core artifacts and extract to dist'
inputs:
path:
description: 'Path to extract'
required: true
runs:
using: 'composite'
steps:
- name: Download tar.gz
uses: actions/download-artifact@v4
with:
name: web
path: .
- name: Extract core artifacts
shell: bash
run: |
mkdir -p ${{ inputs.path }}
tar -xvf dist.tar.gz --directory ${{ inputs.path }}
rm dist.tar.gz

View File

@@ -1,21 +0,0 @@
name: 'Prepare Server Test Environment'
description: 'Prepare Server Test Environment'
runs:
using: 'composite'
steps:
- name: Initialize database
shell: bash
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: Run init-db script
shell: bash
run: |
yarn workspace @affine/server exec prisma generate
yarn workspace @affine/server exec prisma db push
yarn workspace @affine/server data-migration run

View File

@@ -17,10 +17,6 @@ inputs:
description: 'Download the Electron binary'
required: false
default: 'true'
corepack-install:
description: 'Install CorePack'
required: false
default: 'false'
hard-link-nm:
description: 'set nmMode to hardlinks-local in .yarnrc.yml'
required: false
@@ -46,11 +42,6 @@ runs:
registry-url: https://npm.pkg.github.com
scope: '@toeverything'
- name: Init CorePack
if: ${{ inputs.corepack-install == 'true' }}
shell: bash
run: corepack enable
- name: Set nmMode
if: ${{ inputs.hard-link-nm == 'false' }}
shell: bash
@@ -72,7 +63,7 @@ runs:
run: node -e "const p = $(yarn config cacheFolder --json).effective; console.log('yarn_global_cache=' + p)" >> $GITHUB_OUTPUT
- name: Cache non-full yarn cache on Linux
uses: actions/cache@v4
uses: actions/cache@v3
if: ${{ inputs.full-cache != 'true' && runner.os == 'Linux' }}
with:
path: |
@@ -84,7 +75,7 @@ runs:
# and the decompression performance on Windows is very terrible
# so we reduce the number of cached files on non-Linux systems by remove node_modules from cache path.
- name: Cache non-full yarn cache on non-Linux
uses: actions/cache@v4
uses: actions/cache@v3
if: ${{ inputs.full-cache != 'true' && runner.os != 'Linux' }}
with:
path: |
@@ -92,7 +83,7 @@ runs:
key: node_modules-cache-${{ github.job }}-${{ runner.os }}
- name: Cache full yarn cache on Linux
uses: actions/cache@v4
uses: actions/cache@v3
if: ${{ inputs.full-cache == 'true' && runner.os == 'Linux' }}
with:
path: |
@@ -101,7 +92,7 @@ runs:
key: node_modules-cache-full-${{ runner.os }}
- name: Cache full yarn cache on non-Linux
uses: actions/cache@v4
uses: actions/cache@v3
if: ${{ inputs.full-cache == 'true' && runner.os != 'Linux' }}
with:
path: |
@@ -143,7 +134,7 @@ runs:
# Note: Playwright's cache directory is hard coded because that's what it
# says to do in the docs. There doesn't appear to be a command that prints
# it out for us.
- uses: actions/cache@v4
- uses: actions/cache@v3
id: playwright-cache
if: ${{ inputs.playwright-install == 'true' }}
with:
@@ -165,7 +156,7 @@ runs:
- name: Install Playwright's dependencies
shell: bash
if: inputs.playwright-install == 'true'
run: yarn playwright install --with-deps chromium webkit
run: yarn playwright install --with-deps chromium
env:
PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/node_modules/.cache/ms-playwright
@@ -176,7 +167,7 @@ runs:
run: |
echo "version=$(yarn why --json electron | grep -h 'workspace:.' | jq --raw-output '.children[].locator' | sed -e 's/@playwright\/test@.*://' | head -n 1)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4
- uses: actions/cache@v3
id: electron-cache
if: ${{ inputs.electron-install == 'true' }}
with:

View File

@@ -1,28 +0,0 @@
name: 'Rust setup'
description: 'Rust setup, including cache configuration'
inputs:
components:
description: 'Cargo components'
required: false
targets:
description: 'Cargo target'
required: false
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.targets }}
components: ${{ inputs.components }}
- name: Add Targets
if: ${{ inputs.targets }}
run: rustup target add ${{ inputs.targets }}
shell: bash
- uses: Swatinem/rust-cache@v2

View File

@@ -17,7 +17,7 @@ runs:
PACKAGE_VERSION=$(node -p "require('./package.json').version")
TIME_VERSION=$(date +%Y%m%d%H%M)
GIT_SHORT_HASH=$(git rev-parse --short HEAD)
APP_VERSION=$PACKAGE_VERSION-nightly-$GIT_SHORT_HASH
APP_VERSION=$PACKAGE_VERSION-nightly-$TIME_VERSION-$GIT_SHORT_HASH
fi
echo $APP_VERSION
echo "APP_VERSION=$APP_VERSION" >> "$GITHUB_OUTPUT"

View File

@@ -1,8 +1,6 @@
FROM openresty/openresty:1.27.1.1-0-buster
FROM openresty/openresty:1.21.4.3-0-buster
WORKDIR /app
COPY ./packages/frontend/apps/web/dist ./dist
COPY ./packages/frontend/admin/dist ./admin
COPY ./packages/frontend/apps/mobile/dist ./mobile
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

View File

@@ -1,42 +1,13 @@
server {
listen 8080;
location /admin {
root /app/;
index index.html;
try_files $uri/index.html $uri/ $uri /admin/index.html;
}
listen 8080;
root /app/dist;
set $app_root_path /app/dist/;
set $mobile_root /app/dist/;
set_by_lua $affine_env 'return os.getenv("AFFINE_ENV")';
location / {
try_files $uri $uri/ /index.html;
}
if ($affine_env = "dev") {
set $mobile_root /app/mobile/;
}
# https://gist.github.com/mariusom/6683dc52b1cad1a1f372e908bdb209d0
if ($http_user_agent ~* "(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino") {
set $app_root_path $mobile_root;
}
if ($http_user_agent ~* "^(1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-)") {
set $app_root_path $mobile_root;
}
location ~ ^/(_plugin|assets|imgs|js|plugins|static)/ {
root $app_root_path;
try_files $uri $uri/ =404;
}
location / {
root $app_root_path;
index index.html;
try_files $uri $uri/ /index.html;
add_header Cache-Control "private, no-cache, no-store, max-age=0, must-revalidate";
}
error_page 404 /404.html;
location = /404.html {
internal;
}
error_page 404 /404.html;
location = /404.html {
internal;
}
}

View File

@@ -1,15 +1,14 @@
worker_processes 4;
worker_processes 4;
error_log /var/log/nginx/error.log warn;
pcre_jit on;
env AFFINE_ENV;
events {
worker_connections 1024;
worker_connections 1024;
}
http {
include mime.types;
log_format main '$remote_addr [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
include /etc/nginx/conf.d/*.conf;
include mime.types;
log_format main '$remote_addr [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
include /etc/nginx/conf.d/*.conf;
}

View File

@@ -1,13 +1,10 @@
FROM node:20-bookworm-slim
FROM node:18-bookworm-slim
COPY ./packages/backend/server /app
COPY ./packages/frontend/apps/web/dist /app/static
COPY ./packages/frontend/admin/dist /app/static/admin
COPY ./packages/frontend/apps/mobile/dist /app/static/mobile
WORKDIR /app
RUN apt-get update && \
apt-get install -y --no-install-recommends openssl && \
rm -rf /var/lib/apt/lists/*
CMD ["node", "--import", "./scripts/register.js", "./dist/index.js"]
CMD ["node", "--es-module-specifier-resolution=node", "./dist/index.js"]

View File

@@ -1,60 +0,0 @@
services:
affine:
image: ghcr.io/toeverything/affine-graphql:stable
container_name: affine_selfhosted
command:
['sh', '-c', 'node ./scripts/self-host-predeploy && node ./dist/index.js']
ports:
- '3010:3010'
- '5555:5555'
depends_on:
redis:
condition: service_healthy
postgres:
condition: service_healthy
volumes:
# custom configurations
- ~/.affine/self-host/config:/root/.affine/config
# blob storage
- ~/.affine/self-host/storage:/root/.affine/storage
logging:
driver: 'json-file'
options:
max-size: '1000m'
restart: unless-stopped
environment:
- NODE_OPTIONS="--import=./scripts/register.js"
- AFFINE_CONFIG_PATH=/root/.affine/config
- REDIS_SERVER_HOST=redis
- DATABASE_URL=postgres://affine:affine@postgres:5432/affine
- NODE_ENV=production
# Telemetry allows us to collect data on how you use the affine. This data will helps us improve the app and provide better features.
# Uncomment next line if you wish to quit telemetry.
# - TELEMETRY_ENABLE=false
redis:
image: redis
container_name: affine_redis
restart: unless-stopped
volumes:
- ~/.affine/self-host/redis:/data
healthcheck:
test: ['CMD', 'redis-cli', '--raw', 'incr', 'ping']
interval: 10s
timeout: 5s
retries: 5
postgres:
image: postgres:16
container_name: affine_postgres
restart: unless-stopped
volumes:
- ~/.affine/self-host/postgres:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U affine']
interval: 10s
timeout: 5s
retries: 5
environment:
POSTGRES_USER: affine
POSTGRES_PASSWORD: affine
POSTGRES_DB: affine
PGDATA: /var/lib/postgresql/data/pgdata

View File

@@ -3,4 +3,4 @@ name: affine
description: AFFiNE cloud chart
type: application
version: 0.0.0
appVersion: "0.18.0"
appVersion: "0.11.0"

View File

@@ -3,7 +3,7 @@ name: graphql
description: AFFiNE GraphQL server
type: application
version: 0.0.0
appVersion: "0.18.0"
appVersion: "0.11.0"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0

View File

@@ -61,3 +61,18 @@ Create the name of the service account to use
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{- define "jwt.key" -}}
{{- $secret := lookup "v1" "Secret" .Release.Namespace .Values.app.jwt.secretName -}}
{{- if and $secret $secret.data.private -}}
{{/*
Reusing existing secret data
*/}}
key: {{ $secret.data.private }}
{{- else -}}
{{/*
Generate new data
*/}}
key: {{ genPrivateKey "ecdsa" | b64enc }}
{{- end -}}
{{- end -}}

View File

@@ -1,11 +0,0 @@
{{- if .Values.app.copilot.enabled -}}
apiVersion: v1
kind: Secret
metadata:
name: "{{ .Values.app.copilot.secretName }}"
type: Opaque
data:
openaiSecret: {{ .Values.app.copilot.openai.key | b64enc }}
falSecret: {{ .Values.app.copilot.fal.key | b64enc }}
unsplashSecret: {{ .Values.app.copilot.unsplash.key | b64enc }}
{{- end }}

View File

@@ -28,10 +28,10 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: AFFINE_PRIVATE_KEY
- name: AUTH_PRIVATE_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.global.secret.secretName }}"
name: "{{ .Values.app.jwt.secretName }}"
key: key
- name: NODE_ENV
value: "{{ .Values.env }}"
@@ -39,12 +39,12 @@ spec:
value: "--max-old-space-size=4096"
- name: NO_COLOR
value: "1"
- name: DEPLOYMENT_TYPE
value: "affine"
- name: SERVER_FLAVOR
value: "graphql"
- name: AFFINE_ENV
value: "{{ .Release.Namespace }}"
- name: NEXTAUTH_URL
value: "{{ .Values.global.ingress.host }}"
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
@@ -73,39 +73,37 @@ spec:
value: "{{ .Values.app.path }}"
- name: AFFINE_SERVER_HOST
value: "{{ .Values.app.host }}"
- name: AFFINE_SERVER_HTTPS
value: "{{ .Values.app.https }}"
- name: ENABLE_R2_OBJECT_STORAGE
value: "{{ .Values.global.objectStorage.r2.enabled }}"
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: FEATURES_SYNC_CLIENT_VERSION_CHECK
value: "{{ .Values.app.features.syncClientVersionCheck }}"
- name: MAILER_HOST
- name: OAUTH_EMAIL_SENDER
valueFrom:
secretKeyRef:
name: "{{ .Values.app.mailer.secretName }}"
key: host
- name: MAILER_PORT
valueFrom:
secretKeyRef:
name: "{{ .Values.app.mailer.secretName }}"
key: port
- name: MAILER_USER
valueFrom:
secretKeyRef:
name: "{{ .Values.app.mailer.secretName }}"
key: user
- name: MAILER_PASSWORD
valueFrom:
secretKeyRef:
name: "{{ .Values.app.mailer.secretName }}"
key: password
- name: MAILER_SENDER
valueFrom:
secretKeyRef:
name: "{{ .Values.app.mailer.secretName }}"
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:
@@ -122,21 +120,21 @@ spec:
- name: DOC_MERGE_USE_JWST_CODEC
value: "true"
{{ end }}
{{ if .Values.global.objectStorage.r2.enabled }}
{{ if .Values.app.objectStorage.r2.enabled }}
- name: R2_OBJECT_STORAGE_ACCOUNT_ID
valueFrom:
secretKeyRef:
name: "{{ .Values.global.objectStorage.r2.secretName }}"
name: "{{ .Values.app.objectStorage.r2.secretName }}"
key: accountId
- name: R2_OBJECT_STORAGE_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: "{{ .Values.global.objectStorage.r2.secretName }}"
name: "{{ .Values.app.objectStorage.r2.secretName }}"
key: accessKeyId
- name: R2_OBJECT_STORAGE_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.global.objectStorage.r2.secretName }}"
name: "{{ .Values.app.objectStorage.r2.secretName }}"
key: secretAccessKey
{{ end }}
{{ if .Values.app.captcha.enabled }}
@@ -146,26 +144,7 @@ spec:
name: "{{ .Values.app.captcha.secretName }}"
key: turnstileSecret
{{ end }}
{{ if .Values.app.copilot.enabled }}
- name: COPILOT_OPENAI_API_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.app.copilot.secretName }}"
key: openaiSecret
- name: COPILOT_FAL_API_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.app.copilot.secretName }}"
key: falSecret
- name: COPILOT_UNSPLASH_API_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.app.copilot.secretName }}"
key: unsplashSecret
{{ end }}
{{ if .Values.app.oauth.google.enabled }}
- name: OAUTH_GOOGLE_ENABLED
value: "true"
- name: OAUTH_GOOGLE_CLIENT_ID
valueFrom:
secretKeyRef:
@@ -189,25 +168,18 @@ spec:
name: "{{ .Values.app.oauth.github.secretName }}"
key: clientSecret
{{ end }}
{{ if .Values.app.metrics.enabled }}
- name: METRICS_CUSTOMER_IO_TOKEN
valueFrom:
secretKeyRef:
name: "{{ .Values.app.metrics.secretName }}"
key: customerIoSecret
{{ end }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
livenessProbe:
httpGet:
path: /info
path: /
port: http
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
readinessProbe:
httpGet:
path: /info
path: /
port: http
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
resources:

View File

@@ -0,0 +1,7 @@
apiVersion: v1
kind: Secret
metadata:
name: "{{ .Values.app.jwt.secretName }}"
type: Opaque
data:
{{- ( include "jwt.key" . ) | indent 2 -}}

View File

@@ -1,13 +0,0 @@
{{- if .Values.app.mailer.secretName -}}
apiVersion: v1
kind: Secret
metadata:
name: "{{ .Values.app.mailer.secretName }}"
type: Opaque
data:
host: "{{ .Values.app.mailer.host | b64enc }}"
port: "{{ .Values.app.mailer.port | b64enc }}"
user: "{{ .Values.app.mailer.user | b64enc }}"
password: "{{ .Values.app.mailer.password | b64enc }}"
sender: "{{ .Values.app.mailer.sender | b64enc }}"
{{- end }}

View File

@@ -1,9 +0,0 @@
{{- if .Values.app.metrics.enabled -}}
apiVersion: v1
kind: Secret
metadata:
name: "{{ .Values.app.metrics.secretName }}"
type: Opaque
data:
customerIoSecret: {{ .Values.app.metrics.customerIo.token | b64enc }}
{{- end }}

View File

@@ -22,8 +22,6 @@ spec:
value: "{{ .Values.env }}"
- name: AFFINE_ENV
value: "{{ .Release.Namespace }}"
- name: DEPLOYMENT_TYPE
value: "affine"
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
@@ -37,21 +35,21 @@ spec:
- 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 }}
{{ if .Values.global.objectStorage.r2.enabled }}
{{ if .Values.app.objectStorage.r2.enabled }}
- name: R2_OBJECT_STORAGE_ACCOUNT_ID
valueFrom:
secretKeyRef:
name: "{{ .Values.global.objectStorage.r2.secretName }}"
name: "{{ .Values.app.objectStorage.r2.secretName }}"
key: accountId
- name: R2_OBJECT_STORAGE_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: "{{ .Values.global.objectStorage.r2.secretName }}"
name: "{{ .Values.app.objectStorage.r2.secretName }}"
key: accessKeyId
- name: R2_OBJECT_STORAGE_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.global.objectStorage.r2.secretName }}"
name: "{{ .Values.app.objectStorage.r2.secretName }}"
key: secretAccessKey
{{ end }}
resources:

View File

@@ -1,3 +1,15 @@
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

View File

@@ -0,0 +1,11 @@
{{- if .Values.app.objectStorage.r2.enabled -}}
apiVersion: v1
kind: Secret
metadata:
name: "{{ .Values.app.objectStorage.r2.secretName }}"
type: Opaque
data:
accountId: {{ .Values.app.objectStorage.r2.accountId | b64enc }}
accessKeyId: {{ .Values.app.objectStorage.r2.accessKeyId | b64enc }}
secretAccessKey: {{ .Values.app.objectStorage.r2.secretAccessKey | b64enc }}
{{- end }}

View File

@@ -1,18 +0,0 @@
{{- $privateKey := default (genPrivateKey "ecdsa") .Values.global.secret.privateKey | b64enc | quote }}
{{- if not .Values.global.secret.privateKey }}
{{- $existingKey := (lookup "v1" "Secret" .Release.Namespace .Values.global.secret.secretName) }}
{{- if $existingKey }}
{{- $privateKey = index $existingKey.data "key" }}
{{- end -}}
{{- end -}}
apiVersion: v1
kind: Secret
metadata:
name: {{ .Values.global.secret.secretName }}
annotations:
"helm.sh/resource-policy": "keep"
type: Opaque
data:
key: {{ $privateKey }}

View File

@@ -4,10 +4,6 @@ metadata:
name: {{ include "graphql.fullname" . }}
labels:
{{- include "graphql.labels" . | nindent 4 }}
{{- with .Values.service.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.service.type }}
ports:

View File

@@ -16,20 +16,32 @@ app:
path: ''
# AFFINE_SERVER_HOST
host: '0.0.0.0'
https: true
doc:
mergeInterval: "3000"
jwt:
secretName: jwt-private-key
# base64 encoded ecdsa private key
privateKey: ''
captcha:
enabled: false
enable: false
secretName: captcha
turnstile:
secret: ''
copilot:
enabled: false
secretName: copilot
openai:
key: ''
objectStorage:
r2:
enabled: false
secretName: r2
accountId: ''
accessKeyId: ''
secretAccessKey: ''
oauth:
email:
secretName: 'oauth-email'
sender: 'noreply@toeverything.info'
login: ''
password: ''
server: 'smtp.gmail.com'
port: '465'
google:
enabled: false
secretName: oauth-google
@@ -40,18 +52,6 @@ app:
secretName: oauth-github
clientId: ''
clientSecret: ''
mailer:
secretName: 'mailer'
host: 'smtp.gmail.com'
port: '465'
user: ''
password: ''
sender: 'noreply@toeverything.info'
metrics:
enabled: false
secretName: 'metrics'
customerIo:
token: ''
payment:
stripe:
secretName: 'stripe'
@@ -59,7 +59,6 @@ app:
webhookKey: ''
features:
earlyAccessPreview: false
syncClientVersionCheck: false
serviceAccount:
create: true

View File

@@ -1,11 +0,0 @@
apiVersion: v2
name: renderer
description: AFFiNE renderer server
type: application
version: 0.0.0
appVersion: "0.16.0"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0
repository: "file://../gcloud-sql-proxy"
condition: .global.database.gcloud.enabled

View File

@@ -1,16 +0,0 @@
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 "renderer.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 "renderer.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "renderer.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 "renderer.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 }}

View File

@@ -1,63 +0,0 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "renderer.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 "renderer.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 "renderer.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "renderer.labels" -}}
helm.sh/chart: {{ include "renderer.chart" . }}
{{ include "renderer.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 "renderer.selectorLabels" -}}
app.kubernetes.io/name: {{ include "renderer.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "renderer.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "renderer.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@@ -1,124 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "renderer.fullname" . }}
labels:
{{- include "renderer.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "renderer.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "renderer.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "renderer.serviceAccountName" . }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: AFFINE_PRIVATE_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.global.secret.secretName }}"
key: key
- name: NODE_ENV
value: "{{ .Values.env }}"
- name: NODE_OPTIONS
value: "--max-old-space-size=4096"
- name: NO_COLOR
value: "1"
- name: DEPLOYMENT_TYPE
value: "affine"
- name: SERVER_FLAVOR
value: "renderer"
- 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_SUB_PATH
value: "{{ .Values.app.path }}"
- name: AFFINE_SERVER_HOST
value: "{{ .Values.app.host }}"
- name: AFFINE_SERVER_HTTPS
value: "{{ .Values.app.https }}"
- name: ENABLE_R2_OBJECT_STORAGE
value: "{{ .Values.global.objectStorage.r2.enabled }}"
{{ if .Values.global.objectStorage.r2.enabled }}
- name: R2_OBJECT_STORAGE_ACCOUNT_ID
valueFrom:
secretKeyRef:
name: "{{ .Values.global.objectStorage.r2.secretName }}"
key: accountId
- name: R2_OBJECT_STORAGE_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: "{{ .Values.global.objectStorage.r2.secretName }}"
key: accessKeyId
- name: R2_OBJECT_STORAGE_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.global.objectStorage.r2.secretName }}"
key: secretAccessKey
{{ end }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
livenessProbe:
httpGet:
path: /info
port: http
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
readinessProbe:
httpGet:
path: /info
port: http
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@@ -1,19 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "graphql.fullname" . }}
labels:
{{- include "graphql.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 "graphql.selectorLabels" . | nindent 4 }}

View File

@@ -1,12 +0,0 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "graphql.serviceAccountName" . }}
labels:
{{- include "graphql.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}

View File

@@ -1,15 +0,0 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "renderer.fullname" . }}-test-connection"
labels:
{{- include "renderer.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "renderer.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never

View File

@@ -1,38 +0,0 @@
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_SUB_PATH
path: ''
# AFFINE_SERVER_HOST
host: '0.0.0.0'
https: true
serviceAccount:
create: true
annotations: {}
name: 'affine-renderer'
podAnnotations: {}
podSecurityContext:
fsGroup: 2000
resources:
requests:
cpu: '4'
memory: 4Gi
probe:
initialDelaySeconds: 20
nodeSelector: {}
tolerations: []
affinity: {}

View File

@@ -3,7 +3,7 @@ name: sync
description: AFFiNE Sync Server
type: application
version: 0.0.0
appVersion: "0.18.0"
appVersion: "0.11.0"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0

View File

@@ -32,19 +32,14 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: AFFINE_PRIVATE_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.global.secret.secretName }}"
key: key
- name: NODE_ENV
value: "{{ .Values.env }}"
- name: NO_COLOR
value: "1"
- name: DEPLOYMENT_TYPE
value: "affine"
- name: SERVER_FLAVOR
value: "sync"
- name: NEXTAUTH_URL
value: "{{ .Values.global.ingress.host }}"
- name: AFFINE_ENV
value: "{{ .Release.Namespace }}"
- name: DATABASE_PASSWORD

View File

@@ -12,6 +12,7 @@ env: 'production'
app:
# AFFINE_SERVER_HOST
host: '0.0.0.0'
serviceAccount:
create: true
annotations: {}

View File

@@ -27,9 +27,6 @@ spec:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: AFFINE_ENV
value: "{{ .Release.Namespace }}"
ports:
- name: http
containerPort: {{ .Values.service.port }}

View File

@@ -1,9 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-runtime-config
data:
web-assets-manifest: |-
{{ .Files.Get "web-assets-manifest.json" | nindent 4 }}
mobile-assets-manifest: |-
{{ .Files.Get "mobile-assets-manifest.json" | nindent 4 }}

View File

@@ -60,13 +60,6 @@ spec:
name: affine-graphql
port:
number: {{ .Values.graphql.service.port }}
- path: /workspace
pathType: Prefix
backend:
service:
name: affine-renderer
port:
number: {{ .Values.renderer.service.port }}
- path: /
pathType: Prefix
backend:

View File

@@ -1,11 +0,0 @@
{{- if .Values.global.objectStorage.r2.enabled -}}
apiVersion: v1
kind: Secret
metadata:
name: "{{ .Values.global.objectStorage.r2.secretName }}"
type: Opaque
data:
accountId: {{ .Values.global.objectStorage.r2.accountId | b64enc }}
accessKeyId: {{ .Values.global.objectStorage.r2.accessKeyId | b64enc }}
secretAccessKey: {{ .Values.global.objectStorage.r2.secretAccessKey | b64enc }}
{{- end }}

View File

@@ -1,14 +1,9 @@
global:
app:
buildType: 'stable'
ingress:
enabled: false
className: ''
host: affine.pro
tls: []
secret:
secretName: 'server-private-key'
privateKey: ''
database:
user: 'postgres'
url: 'pg-postgresql'
@@ -30,13 +25,6 @@ global:
username: ''
password: ''
database: 0
objectStorage:
r2:
enabled: false
secretName: r2
accountId: ''
accessKeyId: ''
secretAccessKey: ''
gke:
enabled: true
@@ -44,22 +32,13 @@ graphql:
service:
type: ClusterIP
port: 3000
annotations:
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
sync:
service:
type: ClusterIP
port: 3010
annotations:
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
renderer:
service:
type: ClusterIP
port: 3000
annotations:
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
cloud.google.com/backend-config: '{"default": "affine-backendconfig"}'
web:
service:

View File

@@ -1,10 +0,0 @@
apiVersion: cloud.google.com/v1
kind: BackendConfig
metadata:
name: "affine-api-backendconfig"
spec:
healthCheck:
timeoutSec: 1
type: HTTP
requestPath: /info

21
.github/labeler.yml vendored
View File

@@ -29,6 +29,16 @@ mod:plugin-cli:
- any-glob-to-any-file:
- 'tools/plugin-cli/**/*'
mod:workspace:
- changed-files:
- any-glob-to-any-file:
- 'packages/common/workspace/**/*'
mod:workspace-impl:
- changed-files:
- any-glob-to-any-file:
- 'packages/frontend/workspace-impl/**/*'
mod:i18n:
- changed-files:
- any-glob-to-any-file:
@@ -44,10 +54,10 @@ mod:component:
- any-glob-to-any-file:
- 'packages/frontend/component/**/*'
mod:server-native:
mod:storage:
- changed-files:
- any-glob-to-any-file:
- 'packages/backend/native/**/*'
- 'packages/backend/storage/**/*'
mod:native:
- changed-files:
@@ -69,6 +79,11 @@ rust:
- '**/rust-toolchain.toml'
- '**/rustfmt.toml'
package:y-indexeddb:
- changed-files:
- any-glob-to-any-file:
- 'packages/common/y-indexeddb/**/*'
app:core:
- changed-files:
- any-glob-to-any-file:
@@ -77,7 +92,7 @@ app:core:
app:electron:
- changed-files:
- any-glob-to-any-file:
- 'packages/frontend/apps/electron/**/*'
- 'packages/frontend/electron/**/*'
app:server:
- changed-files:

59
.github/renovate.json vendored
View File

@@ -12,59 +12,66 @@
"**/__fixtures__/**"
],
"packageRules": [
{
"matchPackageNames": ["napi", "napi-build", "napi-derive"],
"rangeStrategy": "replace",
"groupName": "napi-rs"
},
{
"matchPackagePatterns": ["^eslint", "^@typescript-eslint"],
"rangeStrategy": "replace",
"groupName": "linter"
},
{
"matchDepNames": ["oxlint"],
"matchPackagePatterns": ["^@nestjs"],
"rangeStrategy": "replace",
"groupName": "oxlint"
"groupName": "nestjs"
},
{
"groupName": "blocksuite",
"matchPackagePatterns": ["^@opentelemetry"],
"rangeStrategy": "replace",
"groupName": "opentelemetry"
},
{
"matchPackageNames": [
"@prisma/client",
"@prisma/instrumentation",
"prisma"
],
"rangeStrategy": "replace",
"groupName": "prisma"
},
{
"matchPackagePatterns": ["^@electron-forge"],
"rangeStrategy": "replace",
"groupName": "electron-forge"
},
{
"groupName": "blocksuite-nightly",
"matchPackagePatterns": ["^@blocksuite"],
"excludePackageNames": ["@blocksuite/icons"],
"rangeStrategy": "replace",
"changelogUrl": "https://github.com/toeverything/blocksuite/blob/master/packages/blocks/CHANGELOG.md"
"followTag": "nightly"
},
{
"groupName": "all non-major dependencies",
"groupSlug": "all-minor-patch",
"matchPackagePatterns": ["*"],
"excludePackagePatterns": ["^@blocksuite/", "oxlint"],
"excludePackagePatterns": ["^@blocksuite/"],
"matchUpdateTypes": ["minor", "patch"]
},
{
"groupName": "rust toolchain",
"matchManagers": ["custom.regex"],
"matchDepNames": ["rustc"]
},
{
"groupName": "nestjs",
"matchPackagePatterns": ["^@nestjs"]
"matchPackagePatterns": ["*"],
"rangeStrategy": "replace",
"excludePackagePatterns": ["^@blocksuite/"]
}
],
"commitMessagePrefix": "chore: ",
"commitMessageAction": "bump up",
"commitMessageTopic": "{{depName}} version",
"ignoreDeps": [],
"postUpdateOptions": ["yarnDedupeHighest"],
"lockFileMaintenance": {
"enabled": true,
"extends": ["schedule:weekly"]
},
"customManagers": [
{
"customType": "regex",
"fileMatch": ["^rust-toolchain\\.toml?$"],
"matchStrings": [
"channel\\s*=\\s*\"(?<currentValue>\\d+\\.\\d+(\\.\\d+)?)\""
],
"depNameTemplate": "rustc",
"packageNameTemplate": "rust-lang/rust",
"datasourceTemplate": "github-releases"
}
]
}
}

View File

@@ -1,290 +0,0 @@
name: Build Images
on:
workflow_call:
inputs:
flavor:
type: string
required: true
env:
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
permissions:
contents: 'write'
id-token: 'write'
packages: 'write'
jobs:
build-server:
name: Build Server
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
extra-flags: workspaces focus @affine/server
- name: Build Server
run: yarn workspace @affine/server build
- name: Upload server dist
uses: actions/upload-artifact@v4
with:
name: server-dist
path: ./packages/backend/server/dist
if-no-files-found: error
build-web:
name: Build @affine/web
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.flavor }}
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Build Core
run: yarn nx build @affine/web --skip-nx-cache
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: ${{ github.event.inputs.flavor }}
CAPTCHA_SITE_KEY: ${{ secrets.CAPTCHA_SITE_KEY }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: 'affine-web'
SENTRY_RELEASE: ${{ steps.version.outputs.APP_VERSION }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
PERFSEE_TOKEN: ${{ secrets.PERFSEE_TOKEN }}
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
- name: Upload web artifact
uses: actions/upload-artifact@v4
with:
name: web
path: ./packages/frontend/apps/web/dist
if-no-files-found: error
build-admin:
name: Build @affine/admin
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.flavor }}
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Build Admin
run: yarn nx build @affine/admin --skip-nx-cache
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: ${{ github.event.inputs.flavor }}
CAPTCHA_SITE_KEY: ${{ secrets.CAPTCHA_SITE_KEY }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: 'affine-admin'
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
PERFSEE_TOKEN: ${{ secrets.PERFSEE_TOKEN }}
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
- name: Upload admin artifact
uses: actions/upload-artifact@v4
with:
name: admin
path: ./packages/frontend/admin/dist
if-no-files-found: error
build-mobile:
name: Build @affine/mobile
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.flavor }}
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Build Mobile
run: yarn nx build @affine/mobile --skip-nx-cache
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: ${{ github.event.inputs.flavor }}
CAPTCHA_SITE_KEY: ${{ secrets.CAPTCHA_SITE_KEY }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: 'affine-mobile'
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
PERFSEE_TOKEN: ${{ secrets.PERFSEE_TOKEN }}
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
- name: Upload mobile artifact
uses: actions/upload-artifact@v4
with:
name: mobile
path: ./packages/frontend/apps/mobile/dist
if-no-files-found: error
build-server-native:
name: Build Server native - ${{ matrix.targets.name }}
runs-on: ubuntu-latest
strategy:
matrix:
targets:
- name: x86_64-unknown-linux-gnu
file: server-native.node
- name: aarch64-unknown-linux-gnu
file: server-native.arm64.node
- name: armv7-unknown-linux-gnueabihf
file: server-native.armv7.node
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
extra-flags: workspaces focus @affine/server-native
- name: Build Rust
uses: ./.github/actions/build-rust
with:
target: ${{ matrix.targets.name }}
package: '@affine/server-native'
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
- name: Upload ${{ matrix.targets.file }}
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.targets.file }}
path: ./packages/backend/native/server-native.node
if-no-files-found: error
build-images:
name: Build Images
runs-on: ubuntu-latest
needs:
- build-server
- build-web
- build-mobile
- build-admin
- build-server-native
steps:
- uses: actions/checkout@v4
- name: Download server dist
uses: actions/download-artifact@v4
with:
name: server-dist
path: ./packages/backend/server/dist
- name: Download server-native.node
uses: actions/download-artifact@v4
with:
name: server-native.node
path: ./packages/backend/server
- name: Download server-native.node arm64
uses: actions/download-artifact@v4
with:
name: server-native.arm64.node
path: ./packages/backend/native
- name: Download server-native.node arm64
uses: actions/download-artifact@v4
with:
name: server-native.armv7.node
path: .
- name: move server-native files
run: |
mv ./packages/backend/native/server-native.node ./packages/backend/server/server-native.arm64.node
mv server-native.node ./packages/backend/server/server-native.armv7.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
# 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: Download web artifact
uses: actions/download-artifact@v4
with:
name: web
path: ./packages/frontend/apps/web/dist
- name: Download mobile artifact
uses: actions/download-artifact@v4
with:
name: mobile
path: ./packages/frontend/apps/mobile/dist
- name: Download admin artifact
uses: actions/download-artifact@v4
with:
name: admin
path: ./packages/frontend/admin/dist
- name: Install Node.js dependencies
run: |
yarn config set --json supportedArchitectures.cpu '["x64", "arm64", "arm"]'
yarn config set --json supportedArchitectures.libc '["glibc"]'
yarn workspaces focus @affine/server --production
- name: Generate Prisma client
run: yarn workspace @affine/server prisma generate
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Build front Dockerfile
uses: docker/build-push-action@v6
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}}
- name: Build graphql Dockerfile
uses: docker/build-push-action@v6
with:
context: .
push: true
pull: true
platforms: linux/amd64,linux/arm64,linux/arm/v7
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}}

View File

@@ -1,25 +0,0 @@
name: Build Selfhost Image
on:
workflow_dispatch:
inputs:
flavor:
description: 'Select distribution to build'
type: choice
default: canary
options:
- canary
- beta
- stable
permissions:
contents: 'write'
id-token: 'write'
packages: 'write'
jobs:
build-image:
name: Build Image
uses: ./.github/workflows/build-images.yml
with:
flavor: ${{ github.event.inputs.flavor }}

View File

@@ -4,8 +4,6 @@ on:
push:
branches:
- canary
- beta
- stable
- v[0-9]+.[0-9]+.x-staging
- v[0-9]+.[0-9]+.x
paths-ignore:
@@ -21,7 +19,6 @@ env:
MACOSX_DEPLOYMENT_TARGET: '10.13'
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/node_modules/.cache/ms-playwright
DEPLOYMENT_TYPE: affine
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -90,7 +87,7 @@ jobs:
electron-install: false
full-cache: true
- name: Run i18n codegen
run: yarn workspace @affine/i18n build
run: yarn i18n-codegen gen
- name: Run ESLint
run: yarn lint:eslint --max-warnings=0
- name: Run Prettier
@@ -98,8 +95,6 @@ jobs:
run: |
git checkout .yarnrc.yml
yarn lint:prettier
- name: Yarn Dedupe
run: yarn dedupe --check
- name: Run Type Check
run: yarn typecheck
@@ -117,8 +112,7 @@ jobs:
name: E2E Test
runs-on: ubuntu-latest
env:
DISTRIBUTION: web
IN_CI_TEST: true
DISTRIBUTION: browser
strategy:
fail-fast: false
matrix:
@@ -143,41 +137,11 @@ jobs:
path: ./test-results
if-no-files-found: ignore
e2e-mobile-test:
name: E2E Mobile Test
runs-on: ubuntu-latest
env:
DISTRIBUTION: mobile
IN_CI_TEST: true
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4, 5]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
playwright-install: true
electron-install: false
full-cache: true
- name: Run playwright tests
run: yarn workspace @affine-test/affine-mobile e2e --forbid-only --shard=${{ matrix.shard }}/${{ strategy.job-total }}
- name: Upload test results
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: test-results-e2e-mobile-${{ matrix.shard }}
path: ./test-results
if-no-files-found: ignore
e2e-migration-test:
name: E2E Migration Test
runs-on: ubuntu-latest
env:
DISTRIBUTION: web
DISTRIBUTION: browser
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
@@ -195,7 +159,7 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: test-results-e2e-migration
path: ./test-results
path: ./tests/affine-migration/test-results
if-no-files-found: ignore
unit-test:
@@ -204,13 +168,13 @@ jobs:
needs:
- build-native
env:
DISTRIBUTION: web
DISTRIBUTION: browser
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: true
electron-install: false
full-cache: true
- name: Download affine.linux-x64-gnu.node
@@ -223,7 +187,7 @@ jobs:
run: yarn nx test:coverage @affine/monorepo
- name: Upload unit test coverage results
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./.coverage/store/lcov.info
@@ -242,8 +206,8 @@ jobs:
spec:
- { os: ubuntu-latest, target: x86_64-unknown-linux-gnu }
- { os: windows-latest, target: x86_64-pc-windows-msvc }
- { os: macos-14, target: x86_64-apple-darwin }
- { os: macos-14, target: aarch64-apple-darwin }
- { os: macos-latest, target: x86_64-apple-darwin }
- { os: macos-latest, target: aarch64-apple-darwin }
steps:
- uses: actions/checkout@v4
@@ -271,8 +235,8 @@ jobs:
path: ./packages/frontend/native/${{ steps.filename.outputs.filename }}
if-no-files-found: error
build-server-native:
name: Build Server native
build-storage:
name: Build Storage
runs-on: ubuntu-latest
env:
CARGO_PROFILE_RELEASE_DEBUG: '1'
@@ -281,23 +245,23 @@ jobs:
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
extra-flags: workspaces focus @affine/server-native
extra-flags: workspaces focus @affine/storage
electron-install: false
- name: Build Rust
uses: ./.github/actions/build-rust
with:
target: 'x86_64-unknown-linux-gnu'
package: '@affine/server-native'
package: '@affine/storage'
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
- name: Upload server-native.node
- name: Upload storage.node
uses: actions/upload-artifact@v4
with:
name: server-native.node
path: ./packages/backend/native/server-native.node
name: storage.node
path: ./packages/backend/storage/storage.node
if-no-files-found: error
build-electron-renderer:
name: Build @affine/electron renderer
build-core:
name: Build @affine/core
runs-on: ubuntu-latest
steps:
@@ -307,28 +271,24 @@ jobs:
with:
electron-install: false
full-cache: true
- name: Build Electron renderer
- name: Build Core
# always skip cache because its fast, and cache configuration is always changing
run: yarn build
env:
DISTRIBUTION: desktop
- name: zip web
run: tar -czf dist.tar.gz --directory=packages/frontend/apps/electron/renderer/dist .
- name: Upload web artifact
run: yarn nx build @affine/core --skip-nx-cache
- name: zip core
run: tar -czf dist.tar.gz --directory=packages/frontend/core/dist .
- name: Upload core artifact
uses: actions/upload-artifact@v4
with:
name: web
name: core
path: dist.tar.gz
if-no-files-found: error
server-test:
name: Server Test
runs-on: ubuntu-latest
needs: build-server-native
needs: build-storage
env:
NODE_ENV: test
DISTRIBUTION: web
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
DISTRIBUTION: browser
services:
postgres:
image: postgres
@@ -355,23 +315,42 @@ jobs:
electron-install: false
full-cache: true
- name: Download server-native.node
- name: Download storage.node
uses: actions/download-artifact@v4
with:
name: server-native.node
name: storage.node
path: ./packages/backend/server
- name: Prepare Server Test Environment
uses: ./.github/actions/server-test-env
- 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 data-migration run
yarn workspace @affine/server exec node --loader ts-node/esm/transpile-only ./scripts/init-db.ts
env:
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
- name: Run server tests
run: yarn workspace @affine/server test:coverage
env:
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
COPILOT_OPENAI_API_KEY: 'use_fake_openai_api_key'
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
- name: Upload server test coverage results
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/backend/server/.coverage/lcov.info
@@ -379,176 +358,12 @@ jobs:
name: affine
fail_ci_if_error: false
server-native-test:
name: Run server native tests
runs-on: ubuntu-latest
env:
RUSTFLAGS: -D warnings
CARGO_TERM_COLOR: always
steps:
- uses: actions/checkout@v4
- name: Setup Rust
uses: ./.github/actions/setup-rust
- name: Install latest nextest release
uses: taiki-e/install-action@nextest
- name: Run tests
run: cargo nextest run --release
copilot-api-test:
name: Server Copilot Api Test
runs-on: ubuntu-latest
needs:
- build-server-native
env:
NODE_ENV: test
DISTRIBUTION: web
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
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: Check blocksuite update
id: check-blocksuite-update
env:
BASE_REF: ${{ github.base_ref }}
run: |
if node ./scripts/detect-blocksuite-update.mjs "$BASE_REF"; then
echo "skip=false" >> $GITHUB_OUTPUT
else
echo "skip=true" >> $GITHUB_OUTPUT
fi
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
backend:
- 'packages/backend/server/src/**'
- name: Setup Node.js
if: ${{ steps.check-blocksuite-update.outputs.skip != 'true' || steps.filter.outputs.backend == 'true' }}
uses: ./.github/actions/setup-node
with:
electron-install: false
full-cache: true
- name: Download server-native.node
if: ${{ steps.check-blocksuite-update.outputs.skip != 'true' || steps.filter.outputs.backend == 'true' }}
uses: actions/download-artifact@v4
with:
name: server-native.node
path: ./packages/backend/server
- name: Prepare Server Test Environment
if: ${{ steps.check-blocksuite-update.outputs.skip != 'true' || steps.filter.outputs.backend == 'true' }}
uses: ./.github/actions/server-test-env
- name: Run server tests
if: ${{ steps.check-blocksuite-update.outputs.skip != 'true' || steps.filter.outputs.backend == 'true' }}
run: yarn workspace @affine/server test:copilot:coverage --forbid-only
env:
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
COPILOT_OPENAI_API_KEY: ${{ secrets.COPILOT_OPENAI_API_KEY }}
COPILOT_FAL_API_KEY: ${{ secrets.COPILOT_FAL_API_KEY }}
- name: Upload server test coverage results
if: ${{ steps.check-blocksuite-update.outputs.skip != 'true' || steps.filter.outputs.backend == 'true' }}
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/backend/server/.coverage/lcov.info
flags: server-test
name: affine
fail_ci_if_error: false
copilot-e2e-test:
name: Server Copilot E2E Test
runs-on: ubuntu-latest
env:
DISTRIBUTION: web
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
IN_CI_TEST: true
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3]
shardTotal: [3]
needs:
- build-server-native
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@v4
- name: Check blocksuite update
id: check-blocksuite-update
env:
BASE_REF: ${{ github.base_ref }}
run: |
if node ./scripts/detect-blocksuite-update.mjs "$BASE_REF"; then
echo "skip=false" >> $GITHUB_OUTPUT
else
echo "skip=true" >> $GITHUB_OUTPUT
fi
- name: Setup Node.js
if: ${{ steps.check-blocksuite-update.outputs.skip != 'true' }}
uses: ./.github/actions/setup-node
with:
playwright-install: true
electron-install: false
hard-link-nm: false
- name: Download server-native.node
if: ${{ steps.check-blocksuite-update.outputs.skip != 'true' }}
uses: actions/download-artifact@v4
with:
name: server-native.node
path: ./packages/backend/server
- name: Run Copilot E2E Test ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
if: ${{ steps.check-blocksuite-update.outputs.skip != 'true' }}
uses: ./.github/actions/copilot-test
with:
script: yarn workspace @affine-test/affine-cloud-copilot e2e --forbid-only --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
openai-key: ${{ secrets.COPILOT_OPENAI_API_KEY }}
fal-key: ${{ secrets.COPILOT_FAL_API_KEY }}
server-e2e-test:
name: ${{ matrix.tests.name }}
runs-on: ubuntu-latest
env:
DISTRIBUTION: web
DISTRIBUTION: browser
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
IN_CI_TEST: true
strategy:
fail-fast: false
matrix:
@@ -562,13 +377,9 @@ jobs:
- name: 'Server Desktop E2E Test'
script: |
yarn workspace @affine/electron build:dev
# Workaround for Electron apps failing to initialize on Ubuntu 24.04 due to AppArmor restrictions
# Disables unprivileged user namespaces restriction to allow Electron apps to run
# Reference: https://github.com/electron/electron/issues/42510
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn workspace @affine-test/affine-desktop-cloud e2e
needs:
- build-server-native
- build-storage
- build-native
services:
postgres:
@@ -596,10 +407,10 @@ jobs:
playwright-install: true
hard-link-nm: false
- name: Download server-native.node
- name: Download storage.node
uses: actions/download-artifact@v4
with:
name: server-native.node
name: storage.node
path: ./packages/backend/server
- name: Download affine.linux-x64-gnu.node
@@ -608,23 +419,39 @@ jobs:
name: affine.linux-x64-gnu.node
path: ./packages/frontend/native
- name: Prepare Server Test Environment
uses: ./.github/actions/server-test-env
- 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 data-migration run
yarn workspace @affine/server exec node --loader ts-node/esm/transpile-only ./scripts/init-db.ts
- name: ${{ matrix.tests.name }}
run: |
${{ matrix.tests.script }}
env:
DEV_SERVER_URL: http://localhost:8080
COPILOT_OPENAI_API_KEY: 1
COPILOT_FAL_API_KEY: 1
ENABLE_LOCAL_EMAIL: true
- name: Upload test results
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: test-results-e2e-server
path: ./test-results
path: ./tests/affine-cloud/test-results
if-no-files-found: ignore
desktop-test:
@@ -632,21 +459,22 @@ jobs:
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-14,
os: macos-latest,
platform: macos,
arch: x64,
target: x86_64-apple-darwin,
test: false,
test: true,
}
- {
os: macos-14,
os: macos-latest,
platform: macos,
arch: arm64,
target: aarch64-apple-darwin,
test: true,
test: false,
}
- {
os: ubuntu-latest,
@@ -663,7 +491,7 @@ jobs:
test: true,
}
needs:
- build-electron-renderer
- build-core
- build-native
steps:
- uses: actions/checkout@v4
@@ -694,51 +522,32 @@ jobs:
shell: bash
run: yarn workspace @affine/electron vitest
- name: Download web artifact
uses: ./.github/actions/download-web
- name: Download core artifact
uses: ./.github/actions/download-core
with:
path: packages/frontend/apps/electron/resources/web-static
path: packages/frontend/electron/resources/web-static
- name: Build Desktop Layers
run: yarn workspace @affine/electron build
- name: Run desktop tests
if: ${{ matrix.spec.os == 'ubuntu-latest' }}
run: |
# Workaround for Electron apps failing to initialize on Ubuntu 24.04 due to AppArmor restrictions
# Disables unprivileged user namespaces restriction to allow Electron apps to run
# Reference: https://github.com/electron/electron/issues/42510
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn workspace @affine-test/affine-desktop e2e
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
- name: Run desktop tests
if: ${{ matrix.spec.test && matrix.spec.os != 'ubuntu-latest' }}
run: yarn workspace @affine-test/affine-desktop e2e
- name: Make bundle (macOS)
if: ${{ matrix.spec.target == 'aarch64-apple-darwin' }}
- name: Make bundle
if: ${{ matrix.spec.os == 'macos-latest' && matrix.spec.arch == 'arm64' }}
env:
SKIP_BUNDLE: true
SKIP_WEB_BUILD: true
HOIST_NODE_MODULES: 1
run: yarn workspace @affine/electron package --platform=darwin --arch=arm64
- name: Make Bundle (Linux)
run: |
sudo add-apt-repository universe
sudo apt install -y libfuse2 elfutils flatpak flatpak-builder
flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak update
# some flatpak deps need git protocol.file.allow
git config --global protocol.file.allow always
yarn workspace @affine/electron make --platform=linux --arch=x64
if: ${{ matrix.spec.target == 'x86_64-unknown-linux-gnu' }}
env:
SKIP_WEB_BUILD: 1
HOIST_NODE_MODULES: 1
- name: Output check
if: ${{ matrix.spec.os == 'macos-14' && matrix.spec.arch == 'arm64' }}
if: ${{ matrix.spec.os == 'macos-latest' && matrix.spec.arch == 'arm64' }}
run: |
yarn workspace @affine/electron exec node --loader ts-node/esm/transpile-only ./scripts/macos-arm64-output-check.ts
@@ -749,39 +558,3 @@ jobs:
name: test-results-e2e-${{ matrix.spec.os }}-${{ matrix.spec.arch }}
path: ./test-results
if-no-files-found: ignore
test-build-mobile-app:
uses: ./.github/workflows/release-mobile.yml
with:
build-type: canary
build-target: development
secrets: inherit
permissions:
id-token: 'write'
test-done:
needs:
- analyze
- lint
- check-yarn-binary
- e2e-test
- e2e-mobile-test
- e2e-migration-test
- unit-test
- build-native
- build-server-native
- build-electron-renderer
- server-test
- server-native-test
- copilot-api-test
- copilot-e2e-test
- server-e2e-test
- desktop-test
- test-build-mobile-app
if: always()
runs-on: ubuntu-latest
name: 3, 2, 1 Launch
steps:
- run: exit 1
# Thank you, next https://github.com/vercel/next.js/blob/canary/.github/workflows/build_and_test.yml#L379
if: ${{ always() && (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) }}

View File

@@ -1,29 +0,0 @@
name: Copilot Test Automatically
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+-canary.[0-9]+'
schedule:
- cron: '0 8 * * *'
workflow_dispatch:
permissions:
actions: write
jobs:
dispatch-test:
runs-on: ubuntu-latest
name: Setup Test
steps:
- name: dispatch test by tag
if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }}
uses: benc-uk/workflow-dispatch@v1
with:
workflow: copilot-test.yml
- name: dispatch test by schedule
if: ${{ github.event_name == 'schedule' }}
uses: benc-uk/workflow-dispatch@v1
with:
workflow: copilot-test.yml
ref: canary

View File

@@ -1,190 +0,0 @@
name: Copilot Cron Test
on:
workflow_dispatch:
env:
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/node_modules/.cache/ms-playwright
jobs:
build-server-native:
name: Build Server native
runs-on: ubuntu-latest
env:
CARGO_PROFILE_RELEASE_DEBUG: '1'
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
extra-flags: workspaces focus @affine/server-native
electron-install: false
- name: Build Rust
uses: ./.github/actions/build-rust
with:
target: 'x86_64-unknown-linux-gnu'
package: '@affine/server-native'
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
- name: Upload server-native.node
uses: actions/upload-artifact@v4
with:
name: server-native.node
path: ./packages/backend/native/server-native.node
if-no-files-found: error
copilot-api-test:
name: Server Copilot Api Test
runs-on: ubuntu-latest
needs:
- build-server-native
env:
NODE_ENV: test
DISTRIBUTION: web
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
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
electron-install: false
full-cache: true
- name: Download server-native.node
uses: actions/download-artifact@v4
with:
name: server-native.node
path: ./packages/backend/server
- name: Prepare Server Test Environment
uses: ./.github/actions/server-test-env
- name: Run server tests
run: yarn workspace @affine/server test:copilot:coverage --forbid-only
env:
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
COPILOT_OPENAI_API_KEY: ${{ secrets.COPILOT_OPENAI_API_KEY }}
COPILOT_FAL_API_KEY: ${{ secrets.COPILOT_FAL_API_KEY }}
- name: Upload server test coverage results
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/backend/server/.coverage/lcov.info
flags: server-test
name: affine
fail_ci_if_error: false
copilot-e2e-test:
name: Server Copilot E2E Test
runs-on: ubuntu-latest
env:
DISTRIBUTION: web
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
IN_CI_TEST: true
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3]
shardTotal: [3]
needs:
- build-server-native
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@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
playwright-install: true
electron-install: false
hard-link-nm: false
- name: Download server-native.node
uses: actions/download-artifact@v4
with:
name: server-native.node
path: ./packages/backend/server
- name: Run Copilot E2E Test ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
uses: ./.github/actions/copilot-test
with:
script: yarn workspace @affine-test/affine-cloud-copilot e2e --forbid-only --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
openai-key: ${{ secrets.COPILOT_OPENAI_API_KEY }}
fal-key: ${{ secrets.COPILOT_FAL_API_KEY }}
test-done:
needs:
- copilot-api-test
- copilot-e2e-test
if: always()
runs-on: ubuntu-latest
name: Post test result message
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
extra-flags: 'workspaces focus @affine/copilot-result'
electron-install: false
- name: Post Success event to a Slack channel
if: ${{ always() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }}
run: node ./tools/copilot-result/index.js
env:
CHANNEL_ID: ${{ secrets.RELEASE_SLACK_CHNNEL_ID }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
BRANCH_SHA: ${{ github.sha }}
BRANCH_NAME: ${{ github.ref }}
COPILOT_RESULT: success
- name: Post Failed event to a Slack channel
id: failed-slack
if: ${{ always() && contains(needs.*.result, 'failure') }}
run: node ./tools/copilot-result/index.js
env:
CHANNEL_ID: ${{ secrets.RELEASE_SLACK_CHNNEL_ID }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
BRANCH_SHA: ${{ github.sha }}
BRANCH_NAME: ${{ github.ref }}
COPILOT_RESULT: failed
- name: Post Cancel event to a Slack channel
id: cancel-slack
if: ${{ always() && contains(needs.*.result, 'cancelled') && !contains(needs.*.result, 'failure') }}
run: node ./tools/copilot-result/index.js
env:
CHANNEL_ID: ${{ secrets.RELEASE_SLACK_CHNNEL_ID }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
BRANCH_SHA: ${{ github.sha }}
BRANCH_NAME: ${{ github.ref }}
COPILOT_RESULT: canceled

View File

@@ -7,11 +7,6 @@ on:
schedule:
- cron: '0 9 * * *'
permissions:
contents: write
pull-requests: write
actions: write
jobs:
dispatch-deploy:
runs-on: ubuntu-latest

View File

@@ -13,68 +13,211 @@ on:
- stable
- internal
env:
APP_NAME: affine
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
permissions:
contents: 'write'
id-token: 'write'
packages: 'write'
jobs:
output-prev-version:
name: Output previous version
build-server:
name: Build Server
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.flavor }}
outputs:
prev: ${{ steps.print.outputs.version }}
namespace: ${{ steps.print.outputs.namespace }}
steps:
- uses: actions/checkout@v4
- name: Auth to Cluster
uses: './.github/actions/cluster-auth'
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
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 }}
- name: Output previous version
id: print
electron-install: false
- name: Build Server
run: yarn workspace @affine/server build
- name: Upload server dist
uses: actions/upload-artifact@v4
with:
name: server-dist
path: ./packages/backend/server/dist
if-no-files-found: error
build-core:
name: Build @affine/core
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.flavor }}
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Build Core
run: yarn nx build @affine/core --skip-nx-cache
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: ${{ github.event.inputs.flavor }}
SHOULD_REPORT_TRACE: true
TRACE_REPORT_ENDPOINT: ${{ secrets.TRACE_REPORT_ENDPOINT }}
CAPTCHA_SITE_KEY: ${{ secrets.CAPTCHA_SITE_KEY }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
- name: Upload core artifact
uses: actions/upload-artifact@v4
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 Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
- 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@v4
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 Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Build 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@v4
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@v4
with:
name: core
path: ./packages/frontend/core/dist
- name: Download server dist
uses: actions/download-artifact@v4
with:
name: server-dist
path: ./packages/backend/server/dist
- name: Download storage.node
uses: actions/download-artifact@v4
with:
name: storage.node
path: ./packages/backend/server
- name: Download storage.node arm64
uses: actions/download-artifact@v4
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: |
namespace=""
if [ "${{ github.event.inputs.flavor }}" = "canary" ]; then
namespace="dev"
elif [ "${{ github.event.inputs.flavor }}" = "beta" ]; then
namespace="beta"
elif [ "${{ github.event.inputs.flavor }}" = "stable" ]; then
namespace="production"
echo "GIT_SHORT_HASH=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV"
if [ -z "${{ inputs.flavor }}" ]
then
echo "RELEASE_FLAVOR=canary" >> "$GITHUB_ENV"
else
echo "Invalid flavor: ${{ github.event.inputs.flavor }}"
exit 1
echo "RELEASE_FLAVOR=${{ inputs.flavor }}" >> "$GITHUB_ENV"
fi
echo "Namespace set to: $namespace"
- 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}}
# Get the previous version from the deployment
prev_version=$(kubectl get deployment -n $namespace affine-graphql -o=jsonpath='{.spec.template.spec.containers[0].image}' | awk -F '-' '{print $3}')
# 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'
echo "Previous version: $prev_version"
echo "version=$prev_version" >> $GITHUB_OUTPUT
echo "namesapce=$namespace" >> $GITHUB_OUTPUT
- name: Install Node.js dependencies
run: |
yarn config set --json supportedArchitectures.cpu '["x64", "arm64"]'
yarn workspaces focus @affine/server --production
build-images:
name: Build Images
uses: ./.github/workflows/build-images.yml
secrets: inherit
with:
flavor: ${{ github.event.inputs.flavor }}
- 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-images
- build-docker
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -97,14 +240,11 @@ jobs:
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 }}
ENABLE_CAPTCHA: true
CAPTCHA_TURNSTILE_SECRET: ${{ secrets.CAPTCHA_TURNSTILE_SECRET }}
COPILOT_OPENAI_API_KEY: ${{ secrets.COPILOT_OPENAI_API_KEY }}
COPILOT_FAL_API_KEY: ${{ secrets.COPILOT_FAL_API_KEY }}
COPILOT_UNSPLASH_API_KEY: ${{ secrets.COPILOT_UNSPLASH_API_KEY }}
METRICS_CUSTOMER_IO_TOKEN: ${{ secrets.METRICS_CUSTOMER_IO_TOKEN }}
MAILER_SENDER: ${{ secrets.OAUTH_EMAIL_SENDER }}
MAILER_USER: ${{ secrets.OAUTH_EMAIL_LOGIN }}
MAILER_PASSWORD: ${{ secrets.OAUTH_EMAIL_PASSWORD }}
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 }}
@@ -120,84 +260,3 @@ jobs:
STRIPE_API_KEY: ${{ secrets.STRIPE_API_KEY }}
STRIPE_WEBHOOK_KEY: ${{ secrets.STRIPE_WEBHOOK_KEY }}
STATIC_IP_NAME: ${{ secrets.STATIC_IP_NAME }}
deploy-done:
needs:
- output-prev-version
- build-images
- deploy
if: always()
runs-on: ubuntu-latest
name: Post deploy message
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/checkout@v4
with:
repository: toeverything/blocksuite
path: blocksuite
fetch-depth: 0
fetch-tags: true
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
extra-flags: 'workspaces focus @affine/changelog'
electron-install: false
- name: Output deployed info
if: ${{ always() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }}
id: set_info
run: |
if [ "${{ github.event.inputs.flavor }}" = "canary" ]; then
echo "deployed_url=https://affine.fail" >> $GITHUB_OUTPUT
elif [ "${{ github.event.inputs.flavor }}" = "beta" ]; then
echo "deployed_url=https://insider.affine.pro" >> $GITHUB_OUTPUT
elif [ "${{ github.event.inputs.flavor }}" = "stable" ]; then
echo "deployed_url=https://app.affine.pro" >> $GITHUB_OUTPUT
else
exit 1
fi
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: Post Success event to a Slack channel
if: ${{ always() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }}
run: node ./tools/changelog/index.js
env:
CHANNEL_ID: ${{ secrets.RELEASE_SLACK_CHNNEL_ID }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
DEPLOYED_URL: ${{ steps.set_info.outputs.deployed_url }}
PREV_VERSION: ${{ needs.output-prev-version.outputs.prev }}
NAMESPACE: ${{ needs.output-prev-version.outputs.namespace }}
DEPLOYMENT: 'SERVER'
FLAVOR: ${{ github.event.inputs.flavor }}
BLOCKSUITE_REPO_PATH: ${{ github.workspace }}/blocksuite
- name: Post Failed event to a Slack channel
id: failed-slack
uses: slackapi/slack-github-action@v2.0.0
if: ${{ always() && contains(needs.*.result, 'failure') }}
with:
method: chat.postMessage
token: ${{ secrets.SLACK_BOT_TOKEN }}
payload: |
channel: ${{ secrets.RELEASE_SLACK_CHNNEL_ID }}
text: "<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Backend deploy failed `${{ github.event.inputs.flavor }}`>"
blocks:
- type: section
text:
type: mrkdwn
text: "<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Backend deploy failed `${{ github.event.inputs.flavor }}`>"
- name: Post Cancel event to a Slack channel
id: cancel-slack
uses: slackapi/slack-github-action@v2.0.0
if: ${{ always() && contains(needs.*.result, 'cancelled') && !contains(needs.*.result, 'failure') }}
with:
token: ${{ secrets.SLACK_BOT_TOKEN }}
method: chat.postMessage
payload: |
channel: ${{ secrets.RELEASE_SLACK_CHNNEL_ID }}
text: "<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Backend deploy cancelled `${{ github.event.inputs.flavor }}`>"
blocks:
- type: section
text:
type: mrkdwn
text: "<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Backend deploy cancelled `${{ github.event.inputs.flavor }}`>"

View File

@@ -24,7 +24,7 @@ jobs:
token: ${{ secrets.HELM_RELEASER_TOKEN }}
- name: Install Helm
uses: azure/setup-helm@v4
uses: azure/setup-helm@v3
- name: Install chart releaser
run: |

35
.github/workflows/languages-sync.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: Languages Sync
on:
push:
branches: ['canary']
paths:
- 'packages/frontend/i18n/**'
- '.github/workflows/languages-sync.yml'
- '!.github/actions/setup-node/action.yml'
pull_request_target:
branches: ['canary']
paths:
- 'packages/frontend/i18n/**'
- '.github/workflows/languages-sync.yml'
- '!.github/actions/setup-node/action.yml'
workflow_dispatch:
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Check Language Key
if: github.ref != 'refs/heads/canary'
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/canary'
run: yarn workspace @affine/i18n run sync-languages
env:
TOLGEE_API_KEY: ${{ secrets.TOLGEE_API_KEY }}

View File

@@ -9,4 +9,4 @@ jobs:
add-reviews:
runs-on: ubuntu-latest
steps:
- uses: kentaro-m/auto-assign-action@v2.0.0
- uses: kentaro-m/auto-assign-action@v1.2.5

View File

@@ -25,7 +25,4 @@ jobs:
node-version-file: '.nvmrc'
- name: Install dependencies
run: yarn workspaces focus @affine/commitlint-config
- name: Check PR title
env:
TITLE: ${{ github.event.pull_request.title }}
run: echo "$TITLE" | yarn workspace @affine/commitlint-config commitlint -g ./.commitlintrc.json
- run: echo "${{ github.event.pull_request.title }}" | yarn workspace @affine/commitlint-config commitlint -g ./.commitlintrc.json

51
.github/workflows/publish-storybook.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
name: Publish Storybook
env:
NODE_OPTIONS: --max-old-space-size=4096
on:
workflow_dispatch:
push:
branches:
- canary
pull_request:
branches:
- canary
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
- 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@v4
if: always()
with:
name: chromatic-build-artifacts-${{ github.run_id }}
path: |
chromatic-diagnostics.json
**/build-storybook.log

View File

@@ -1,38 +0,0 @@
name: Release Desktop/Mobile Automatically
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+-canary.[0-9]+'
schedule:
- cron: '0 9 * * *'
permissions:
contents: write
pull-requests: write
actions: write
jobs:
dispatch-release-desktop:
runs-on: ubuntu-latest
name: Setup Release Desktop
steps:
- name: dispatch desktop release by tag
if: ${{ github.event_name == 'push' }}
uses: benc-uk/workflow-dispatch@v1
with:
workflow: release-desktop.yml
inputs: '{ "build-type": "canary", "is-draft": false, "is-pre-release": true }'
- name: dispatch desktop release by schedule
if: ${{ github.event_name == 'schedule' }}
uses: benc-uk/workflow-dispatch@v1
with:
workflow: release-desktop.yml
inputs: '{ "build-type": "canary", "is-draft": false, "is-pre-release": true }'
ref: canary
- name: dispatch desktop release by tag
uses: benc-uk/workflow-dispatch@v1
with:
workflow: release-mobile.yml
inputs: '{ "build-type": "canary", "build-target": "distribution" }'

View File

@@ -0,0 +1,27 @@
name: Release Desktop Automatically
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+-canary.[0-9]+'
schedule:
- cron: '0 9 * * *'
jobs:
dispatch-release-desktop:
runs-on: ubuntu-latest
name: Setup Release Desktop
steps:
- name: dispatch desktop release by tag
if: ${{ github.event_name == 'push' }}
uses: benc-uk/workflow-dispatch@v1
with:
workflow: release-desktop.yml
inputs: '{ "build-type": "canary", "is-draft": false, "is-pre-release": true }'
- name: dispatch desktop release by schedule
if: ${{ github.event_name == 'schedule' }}
uses: benc-uk/workflow-dispatch@v1
with:
workflow: release-desktop.yml
inputs: '{ "build-type": "canary", "is-draft": false, "is-pre-release": true }'
ref: canary

View File

@@ -27,12 +27,10 @@ permissions:
actions: write
contents: write
security-events: write
id-token: write
attestations: write
env:
BUILD_TYPE: ${{ github.event.inputs.build-type }}
DEBUG: 'affine:*,napi:*'
DEBUG: napi:*
APP_NAME: affine
MACOSX_DEPLOYMENT_TARGET: '10.13'
@@ -55,29 +53,30 @@ jobs:
run: yarn workspace @affine/electron generate-assets
env:
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: 'affine'
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
SENTRY_RELEASE: ${{ steps.version.outputs.APP_VERSION }}
RELEASE_VERSION: ${{ steps.version.outputs.APP_VERSION }}
SKIP_PLUGIN_BUILD: 'true'
SKIP_NX_CACHE: 'true'
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
- name: Upload web artifact
- name: Upload core artifact
uses: actions/upload-artifact@v4
with:
name: web
path: packages/frontend/apps/electron/resources/web-static
name: core
path: packages/frontend/electron/resources/web-static
make-distribution:
strategy:
# all combinations: macos-latest x64, macos-latest arm64, ubuntu-latest x64
# For windows, we need a separate approach
matrix:
spec:
- runner: macos-14
- runner: macos-latest
platform: darwin
arch: x64
target: x86_64-apple-darwin
- runner: macos-14
- runner: macos-latest
platform: darwin
arch: arm64
target: aarch64-apple-darwin
@@ -87,18 +86,11 @@ jobs:
target: x86_64-unknown-linux-gnu
runs-on: ${{ matrix.spec.runner }}
needs: before-make
environment: ${{ github.event.inputs.build-type }}
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
SKIP_GENERATE_ASSETS: 1
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: 'affine'
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
SENTRY_RELEASE: ${{ needs.before-make.outputs.RELEASE_VERSION }}
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
steps:
- uses: actions/checkout@v4
- name: Setup Version
@@ -120,70 +112,39 @@ jobs:
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
- uses: actions/download-artifact@v4
with:
name: web
path: packages/frontend/apps/electron/resources/web-static
name: core
path: packages/frontend/electron/resources/web-static
- name: Build Desktop Layers
run: yarn workspace @affine/electron build
- name: Signing By Apple Developer ID
if: ${{ matrix.spec.platform == 'darwin' }}
uses: apple-actions/import-codesign-certs@v3
uses: apple-actions/import-codesign-certs@v2
with:
p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
- name: Install additional dependencies on Linux
if: ${{ matrix.spec.platform == 'linux' }}
run: |
sudo add-apt-repository universe
sudo apt install -y libfuse2 elfutils flatpak flatpak-builder
flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak update
# some flatpak deps need git protocol.file.allow
git config --global protocol.file.allow always
- 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
DEBUG: '*'
- name: signing DMG
if: ${{ matrix.spec.platform == 'darwin' }}
run: |
codesign --force --sign "Developer ID Application: TOEVERYTHING PTE. LTD." packages/frontend/apps/electron/out/${{ env.BUILD_TYPE }}/make/AFFiNE.dmg
- name: Save artifacts (mac)
if: ${{ matrix.spec.platform == 'darwin' }}
run: |
mkdir -p builds
mv packages/frontend/apps/electron/out/*/make/*.dmg ./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.dmg
mv packages/frontend/apps/electron/out/*/make/zip/darwin/${{ matrix.spec.arch }}/*.zip ./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ 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 (linux)
if: ${{ matrix.spec.platform == 'linux' }}
run: |
mkdir -p builds
mv packages/frontend/apps/electron/out/*/make/zip/linux/${{ matrix.spec.arch }}/*.zip ./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ matrix.spec.arch }}.zip
mv packages/frontend/apps/electron/out/*/make/*.AppImage ./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ matrix.spec.arch }}.appimage
mv packages/frontend/apps/electron/out/*/make/deb/${{ matrix.spec.arch }}/*.deb ./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ matrix.spec.arch }}.deb
mv packages/frontend/apps/electron/out/*/make/flatpak/*/*.flatpak ./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ matrix.spec.arch }}.flatpak
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
- uses: actions/attest-build-provenance@v1
if: ${{ matrix.spec.platform == 'darwin' }}
with:
subject-path: |
./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.zip
./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.dmg
- uses: actions/attest-build-provenance@v1
if: ${{ matrix.spec.platform == 'linux' }}
with:
subject-path: |
./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-x64.zip
./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-x64.appimage
./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-x64.deb
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
@@ -191,31 +152,21 @@ jobs:
path: builds
package-distribution-windows:
environment: ${{ github.event.inputs.build-type }}
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
- runner: windows-latest
platform: win32
arch: arm64
target: aarch64-pc-windows-msvc
runs-on: ${{ matrix.spec.runner }}
needs: before-make
outputs:
FILES_TO_BE_SIGNED_x64: ${{ steps.get_files_to_be_signed.outputs.FILES_TO_BE_SIGNED_x64 }}
FILES_TO_BE_SIGNED_arm64: ${{ steps.get_files_to_be_signed.outputs.FILES_TO_BE_SIGNED_arm64 }}
FILES_TO_BE_SIGNED: ${{ steps.get_files_to_be_signed.outputs.FILES_TO_BE_SIGNED }}
env:
SKIP_GENERATE_ASSETS: 1
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: 'affine'
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
SENTRY_RELEASE: ${{ needs.before-make.outputs.RELEASE_VERSION }}
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
steps:
- uses: actions/checkout@v4
- name: Setup Version
@@ -236,8 +187,8 @@ jobs:
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
- uses: actions/download-artifact@v4
with:
name: web
path: packages/frontend/apps/electron/resources/web-static
name: core
path: packages/frontend/electron/resources/web-static
- name: Build Desktop Layers
run: yarn workspace @affine/electron build
@@ -245,18 +196,19 @@ jobs:
- 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/apps/electron/out -Recurse -File | Where-Object { $_.Extension -in @(".exe", ".node", ".dll", ".msi") } | ForEach-Object { '"' + $_.FullName.Replace((Get-Location).Path + '\packages\frontend\apps\electron\out\', '') + '"' }) -join ' ')
"FILES_TO_BE_SIGNED_${{ matrix.spec.arch }}=$FILES_TO_BE_SIGNED" >> $env:GITHUB_OUTPUT
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/apps/electron/out/* -DestinationPath archive.zip
run: Compress-Archive -CompressionLevel Fastest -Path packages/frontend/electron/out/* -DestinationPath archive.zip
- name: Save packaged artifacts for signing
uses: actions/upload-artifact@v4
@@ -266,71 +218,51 @@ jobs:
archive.zip
!**/*.map
sign-packaged-artifacts-windows_x64:
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_x64 }}
files: ${{ needs.package-distribution-windows.outputs.FILES_TO_BE_SIGNED }}
artifact-name: packaged-win32-x64
sign-packaged-artifacts-windows_arm64:
needs: package-distribution-windows
uses: ./.github/workflows/windows-signer.yml
with:
files: ${{ needs.package-distribution-windows.outputs.FILES_TO_BE_SIGNED_arm64 }}
artifact-name: packaged-win32-arm64
make-windows-installer:
needs:
- sign-packaged-artifacts-windows_x64
- sign-packaged-artifacts-windows_arm64
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:
- platform: win32
- runner: windows-latest
platform: win32
arch: x64
- platform: win32
arch: arm64
runs-on: windows-latest
target: x86_64-pc-windows-msvc
runs-on: ${{ matrix.spec.runner }}
outputs:
FILES_TO_BE_SIGNED_x64: ${{ steps.get_files_to_be_signed.outputs.FILES_TO_BE_SIGNED_x64 }}
FILES_TO_BE_SIGNED_arm64: ${{ steps.get_files_to_be_signed.outputs.FILES_TO_BE_SIGNED_arm64 }}
FILES_TO_BE_SIGNED: ${{ steps.get_files_to_be_signed.outputs.FILES_TO_BE_SIGNED }}
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- 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
nmHoistingLimits: workspaces
env:
npm_config_arch: ${{ matrix.spec.arch }}
- name: Download and overwrite packaged artifacts
uses: actions/download-artifact@v4
with:
name: signed-packaged-${{ matrix.spec.platform }}-${{ matrix.spec.arch }}
path: .
- name: unzip file
run: Expand-Archive -Path signed.zip -DestinationPath packages/frontend/apps/electron/out
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: Make nsis.windows installer
run: yarn workspace @affine/electron make-nsis --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
- name: Zip artifacts for faster upload
run: Compress-Archive -CompressionLevel Fastest -Path packages/frontend/apps/electron/out/${{ env.BUILD_TYPE }}/make/* -DestinationPath archive.zip
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/apps/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\apps\electron\out\${{ env.BUILD_TYPE }}\make\', '') + '"' }) -join ' ')
"FILES_TO_BE_SIGNED_${{ matrix.spec.arch }}=$FILES_TO_BE_SIGNED" >> $env:GITHUB_OUTPUT
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
@@ -339,36 +271,24 @@ jobs:
name: installer-${{ matrix.spec.platform }}-${{ matrix.spec.arch }}
path: archive.zip
sign-installer-artifacts-windows-x64:
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_x64 }}
files: ${{ needs.make-windows-installer.outputs.FILES_TO_BE_SIGNED }}
artifact-name: installer-win32-x64
sign-installer-artifacts-windows-arm64:
needs: make-windows-installer
uses: ./.github/workflows/windows-signer.yml
with:
files: ${{ needs.make-windows-installer.outputs.FILES_TO_BE_SIGNED_arm64 }}
artifact-name: installer-win32-arm64
finalize-installer-windows:
needs:
[
sign-installer-artifacts-windows-x64,
sign-installer-artifacts-windows-arm64,
before-make,
]
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
- runner: windows-latest
platform: win32
arch: arm64
target: x86_64-pc-windows-msvc
runs-on: ${{ matrix.spec.runner }}
steps:
- name: Download and overwrite installer artifacts
@@ -377,21 +297,14 @@ jobs:
name: signed-installer-${{ matrix.spec.platform }}-${{ matrix.spec.arch }}
path: .
- name: unzip file
run: Expand-Archive -Path signed.zip -DestinationPath packages/frontend/apps/electron/out/${{ env.BUILD_TYPE }}/make
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/apps/electron/out/*/make/zip/win32/${{ matrix.spec.arch }}/AFFiNE*-win32-${{ matrix.spec.arch }}-*.zip ./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-windows-${{ matrix.spec.arch }}.zip
mv packages/frontend/apps/electron/out/*/make/squirrel.windows/${{ matrix.spec.arch }}/*.exe ./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-windows-${{ matrix.spec.arch }}.exe
mv packages/frontend/apps/electron/out/*/make/nsis.windows/${{ matrix.spec.arch }}/*.exe ./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-windows-${{ matrix.spec.arch }}.nsis.exe
- uses: actions/attest-build-provenance@v1
with:
subject-path: |
./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-windows-${{ matrix.spec.arch }}.zip
./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-windows-${{ matrix.spec.arch }}.exe
./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-windows-${{ matrix.spec.arch }}.nsis.exe
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@v4
@@ -407,7 +320,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: web
name: core
path: web-static
- name: Zip web-static
run: zip -r web-static.zip web-static
@@ -426,11 +339,6 @@ jobs:
with:
name: affine-win32-x64-builds
path: ./
- name: Download Artifacts (windows-arm64)
uses: actions/download-artifact@v4
with:
name: affine-win32-arm64-builds
path: ./
- name: Download Artifacts (linux-x64)
uses: actions/download-artifact@v4
with:
@@ -438,15 +346,15 @@ jobs:
path: ./
- uses: actions/setup-node@v4
with:
node-version: 20
node-version: 18
- name: Generate Release yml
run: |
node ./packages/frontend/apps/electron/scripts/generate-yml.js
node ./packages/frontend/electron/scripts/generate-yml.js
env:
RELEASE_VERSION: ${{ needs.before-make.outputs.RELEASE_VERSION }}
- name: Create Release Draft
if: ${{ github.ref_type == 'tag' }}
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v1
with:
name: ${{ needs.before-make.outputs.RELEASE_VERSION }}
body: ''
@@ -457,14 +365,12 @@ jobs:
./*.zip
./*.dmg
./*.exe
./*.appimage
./*.deb
./*.flatpak
./*.AppImage
./*.apk
./*.yml
- name: Create Nightly Release Draft
if: ${{ github.ref_type == 'branch' }}
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
with:
@@ -481,8 +387,6 @@ jobs:
./*.zip
./*.dmg
./*.exe
./*.appimage
./*.deb
./*.AppImage
./*.apk
./*.flatpak
./*.yml

View File

@@ -1,211 +0,0 @@
name: Release Mobile App
on:
workflow_call:
inputs:
build-target:
description: 'Build Target'
type: string
required: true
build-type:
description: 'Build Type'
type: string
required: true
workflow_dispatch:
inputs:
build-target:
description: 'Build Target'
type: choice
required: true
default: distribution
options:
- development
- distribution
build-type:
description: 'Build Type'
type: choice
required: true
default: canary
options:
- canary
- beta
- stable
env:
BUILD_TYPE: ${{ inputs.build-type || github.event.inputs.build-type }}
BUILD_TARGET: ${{ inputs.build-target || github.event.inputs.build-target }}
DEBUG: napi:*
KEYCHAIN_NAME: ${{ github.workspace }}/signing_temp
jobs:
build-ios-web:
runs-on: ubuntu-latest
environment: ${{ inputs.build-type || github.event.inputs.build-type }}
outputs:
RELEASE_VERSION: ${{ steps.version.outputs.APP_VERSION }}
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Setup @sentry/cli
uses: ./.github/actions/setup-sentry
- name: Build Mobile
run: yarn nx build @affine/ios --skip-nx-cache
env:
PUBLIC_PATH: '/'
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: 'affine'
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
SENTRY_RELEASE: ${{ steps.version.outputs.APP_VERSION }}
RELEASE_VERSION: ${{ steps.version.outputs.APP_VERSION }}
SKIP_NX_CACHE: 'true'
- name: Upload ios artifact
uses: actions/upload-artifact@v4
with:
name: ios
path: packages/frontend/apps/ios/dist
build-android-web:
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.build-type || inputs.build-type }}
outputs:
RELEASE_VERSION: ${{ steps.version.outputs.APP_VERSION }}
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Setup @sentry/cli
uses: ./.github/actions/setup-sentry
- name: Build Mobile
run: yarn nx build @affine/android --skip-nx-cache
env:
PUBLIC_PATH: '/'
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: 'affine'
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
SENTRY_RELEASE: ${{ steps.version.outputs.APP_VERSION }}
RELEASE_VERSION: ${{ steps.version.outputs.APP_VERSION }}
SKIP_NX_CACHE: 'true'
- name: Upload android artifact
uses: actions/upload-artifact@v4
with:
name: android
path: packages/frontend/apps/android/dist
ios:
runs-on: macos-latest
needs:
- build-ios-web
steps:
- uses: actions/checkout@v4
- name: Download mobile artifact
uses: actions/download-artifact@v4
with:
name: ios
path: packages/frontend/apps/ios/dist
- name: Setup Node.js
uses: ./.github/actions/setup-node
timeout-minutes: 10
with:
extra-flags: workspaces focus @affine/ios
playwright-install: false
electron-install: false
hard-link-nm: false
enableScripts: false
- name: Cap sync
run: yarn workspace @affine/ios cap sync
- name: Signing By Apple Developer ID
uses: apple-actions/import-codesign-certs@v3
id: import-codesign-certs
with:
p12-file-base64: ${{ secrets.CERTIFICATES_P12_MOBILE }}
p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD_MOBILE }}
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable
- name: Testflight
if: ${{ env.BUILD_TYPE != 'stable' }}
working-directory: packages/frontend/apps/ios/App
run: |
echo -n "${{ env.BUILD_PROVISION_PROFILE }}" | base64 --decode -o $PP_PATH
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles
fastlane beta
env:
BUILD_PROVISION_PROFILE: ${{ secrets.BUILD_PROVISION_PROFILE }}
PP_PATH: ${{ runner.temp }}/build_pp.mobileprovision
APPLE_STORE_CONNECT_API_KEY_ID: ${{ secrets.APPLE_STORE_CONNECT_API_KEY_ID }}
APPLE_STORE_CONNECT_API_ISSUER_ID: ${{ secrets.APPLE_STORE_CONNECT_API_ISSUER_ID }}
APPLE_STORE_CONNECT_API_KEY: ${{ secrets.APPLE_STORE_CONNECT_API_KEY }}
android:
runs-on: ubuntu-latest
permissions:
id-token: 'write'
needs:
- build-android-web
steps:
- uses: actions/checkout@v4
- name: Download mobile artifact
uses: actions/download-artifact@v4
with:
name: android
path: packages/frontend/apps/android/dist
- name: Setup Node.js
uses: ./.github/actions/setup-node
timeout-minutes: 10
with:
extra-flags: workspaces focus @affine/android @affine/playstore-auto-bump
playwright-install: false
electron-install: false
hard-link-nm: false
enableScripts: false
- name: Cap sync
run: yarn workspace @affine/android cap sync
- name: Auth gcloud
id: auth
uses: google-github-actions/auth@v2
if: ${{ env.BUILD_TARGET == 'distribution' }}
with:
workload_identity_provider: 'projects/${{ secrets.GCP_PROJECT_NUMBER }}/locations/global/workloadIdentityPools/github-actions/providers/github-actions-helm-deploy'
service_account: '${{ secrets.GCP_HELM_DEPLOY_SERVICE_ACCOUNT }}'
token_format: 'access_token'
project_id: '${{ secrets.GCP_PROJECT_ID }}'
access_token_scopes: 'https://www.googleapis.com/auth/androidpublisher'
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- name: Auto increment version code
id: bump
if: ${{ env.BUILD_TARGET == 'distribution' }}
run: yarn workspace @affine/playstore-auto-bump bump
env:
GOOGLE_APPLICATION_CREDENTIALS: ${{ steps.auth.outputs.credentials_file_path }}
- name: Build
run: |
echo -n "${{ env.AFFINE_ANDROID_SIGN_KEYSTORE }}" | base64 --decode > packages/frontend/apps/android/affine.keystore
yarn workspace @affine/android cap build android
env:
AFFINE_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.AFFINE_ANDROID_KEYSTORE_PASSWORD }}
AFFINE_ANDROID_KEYSTORE_ALIAS_PASSWORD: ${{ secrets.AFFINE_ANDROID_KEYSTORE_ALIAS_PASSWORD }}
AFFINE_ANDROID_SIGN_KEYSTORE: ${{ secrets.AFFINE_ANDROID_SIGN_KEYSTORE }}
- name: Upload to Google Play
uses: r0adkll/upload-google-play@v1
if: ${{ env.BUILD_TARGET == 'distribution' }}
with:
serviceAccountJson: ${{ steps.auth.outputs.credentials_file_path }}
packageName: app.affine.pro
releaseFiles: packages/frontend/apps/android/App/app/build/outputs/bundle/release/app-release-signed.aab
track: internal
status: draft
existingEditId: ${{ steps.bump.outputs.EDIT_ID }}

View File

@@ -1,42 +0,0 @@
name: Sync I18n with Crowdin
on:
push:
branches:
- canary
paths:
- 'packages/frontend/i18n/**'
workflow_dispatch:
jobs:
synchronize-with-crowdin:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Crowdin action
uses: crowdin/github-action@v2
with:
upload_sources: true
upload_translations: true
download_translations: true
auto_approve_imported: true
import_eq_suggestions: true
export_only_approved: true
skip_untranslated_strings: true
localization_branch_name: l10n_crowdin_translations
create_pull_request: true
pull_request_title: 'chore(i18n): sync translations'
pull_request_body: 'New Crowdin translations by [Crowdin GH Action](https://github.com/crowdin/github-action)'
pull_request_base_branch_name: 'canary'
config: packages/frontend/i18n/crowdin.yml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}

View File

@@ -15,7 +15,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Publish
uses: cloudflare/wrangler-action@v3.12.1
uses: cloudflare/wrangler-action@v3.4.0
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}

4
.gitignore vendored
View File

@@ -59,6 +59,7 @@ Thumbs.db
.vercel
out/
storybook-static
i18n-generated.ts
test-results
playwright-report
@@ -78,6 +79,3 @@ lib
affine.db
apps/web/next-routes.conf
.nx
packages/frontend/templates/edgeless
packages/frontend/core/public/static/templates

View File

@@ -1 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
yarn lint-staged && yarn lint:ox

View File

@@ -3,8 +3,8 @@
"version": 1,
"list": [
{
"input": "./src/resources/en.json",
"output": "./src/i18n-generated",
"input": "./packages/frontend/i18n/src/resources/en.json",
"output": "./packages/frontend/i18n/src/i18n-generated",
"parser": {
"type": "i18next",
"contextSeparator": "$",

2
.nvmrc
View File

@@ -1 +1 @@
20.18.0
18

View File

@@ -12,17 +12,15 @@ storybook-static
web-static
public
packages/backend/server/src/schema.gql
packages/backend/server/src/fundamentals/error/errors.gen.ts
packages/frontend/i18n/src/i18n-generated.ts
packages/frontend/i18n/src/i18n-completenesses.json
packages/frontend/graphql/src/graphql/index.ts
tests/affine-legacy/**/static
.yarnrc.yml
packages/frontend/templates/*.gen.ts
packages/frontend/templates/templates.gen.ts
packages/frontend/templates/onboarding
# auto-generated by NAPI-RS
# fixme(@joooye34): need script to check and generate ignore list here
packages/backend/native/index.d.ts
packages/backend/storage/index.d.ts
packages/frontend/native/index.d.ts
packages/frontend/native/index.js

View File

@@ -1,7 +1,9 @@
include = ["./*.toml", "./packages/**/*.toml"]
exclude = ["node_modules/**/*.toml"]
[formatting]
align_entries = true
column_width = 180
reorder_arrays = true
reorder_keys = true
[[rule]]
keys = ["dependencies", "*-dependencies"]
[rule.formatting]
align_entries = true
indent_tables = true
reorder_keys = true

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,15 @@
diff --git a/package.json b/package.json
index ca30bca63196b923fa5a27eb85ce2ee890222d36..39e9d08dea40f25568a39bfbc0154458d32c8a66 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,10 @@
"types": "./index.d.ts",
"default": "./index.js"
},
+ "./core": {
+ "types": "./core/index.d.ts",
+ "default": "./core/index.js"
+ },
"./adapters": {
"types": "./adapters.d.ts"
},

File diff suppressed because one or more lines are too long

View File

@@ -1,39 +0,0 @@
diff --git a/dist/yjs.cjs b/dist/yjs.cjs
index d2dc06ae11a6eb44f8c8445d4298c0e89c3e4da2..a30ab04fa9f3b77666939caa88335c68c40f194c 100644
--- a/dist/yjs.cjs
+++ b/dist/yjs.cjs
@@ -414,7 +414,7 @@ const equalDeleteSets = (ds1, ds2) => {
*/
-const generateNewClientId = random__namespace.uint32;
+const generateNewClientId = random__namespace.uint53;
/**
* @typedef {Object} DocOpts
diff --git a/dist/yjs.mjs b/dist/yjs.mjs
index 20c9e58c32bcb6bc714200a2561fd1f542c49523..14267e5e36d9781ca3810d5b70ff8c051dac779e 100644
--- a/dist/yjs.mjs
+++ b/dist/yjs.mjs
@@ -378,7 +378,7 @@ const equalDeleteSets = (ds1, ds2) => {
*/
-const generateNewClientId = random.uint32;
+const generateNewClientId = random.uint53;
/**
* @typedef {Object} DocOpts
diff --git a/src/utils/Doc.js b/src/utils/Doc.js
index 62643617c86e57c64dd9babdb792fa8888357ec0..4df5048ab12af1ae0f1154da67f06dce1fda7b49 100644
--- a/src/utils/Doc.js
+++ b/src/utils/Doc.js
@@ -20,7 +20,7 @@ import * as map from 'lib0/map'
import * as array from 'lib0/array'
import * as promise from 'lib0/promise'
-export const generateNewClientId = random.uint32
+export const generateNewClientId = random.uint53
/**
* @typedef {Object} DocOpts

893
.yarn/releases/yarn-4.0.2.cjs vendored Executable file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -12,4 +12,4 @@ npmPublishAccess: public
npmPublishRegistry: "https://registry.npmjs.org"
yarnPath: .yarn/releases/yarn-4.5.1.cjs
yarnPath: .yarn/releases/yarn-4.0.2.cjs

2455
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,42 +1,16 @@
[workspace]
members = [
"./packages/backend/native",
"./packages/common/native",
"./packages/frontend/native",
"./packages/frontend/native/schema"
]
resolver = "2"
[workspace.dependencies]
affine_common = { path = "./packages/common/native" }
anyhow = "1"
chrono = "0.4"
dotenv = "0.15"
file-format = { version = "0.26", features = ["reader"] }
mimalloc = "0.1"
napi = { version = "3.0.0-alpha.12", features = ["async", "chrono_date", "error_anyhow", "napi9", "serde"] }
napi-build = { version = "2" }
napi-derive = { version = "3.0.0-alpha.12" }
notify = { version = "7", features = ["serde"] }
once_cell = "1"
parking_lot = "0.12"
rand = "0.8"
rayon = "1.10"
serde = "1"
serde_json = "1"
sha3 = "0.10"
sqlx = { version = "0.8", default-features = false, features = ["chrono", "macros", "migrate", "runtime-tokio", "sqlite", "tls-rustls"] }
tiktoken-rs = "0.6"
tokio = "1.37"
uuid = "1.8"
v_htmlescape = "0.15"
y-octo = { git = "https://github.com/y-crdt/y-octo.git", branch = "main" }
members = [
"./packages/frontend/native",
"./packages/frontend/native/schema",
"./packages/backend/storage",
]
[profile.dev.package.sqlx-macros]
opt-level = 3
[profile.release]
lto = true
codegen-units = 1
lto = true
opt-level = 3
strip = "symbols"
opt-level = 3
strip = "symbols"

196
README.md
View File

@@ -1,92 +1,90 @@
<div align="center">
<h1 style="border-bottom: none">
<b><a href="https://affine.pro">AFFiNE.Pro</a></b><br />
<b><a href="https://affine.pro">AFFiNE.PRO</a></b><br />
Write, Draw and Plan All at Once
<br>
</h1>
<a href="https://affine.pro/download">
<img alt="affine logo" src="https://cdn.affine.pro/Github_hero_image1.png" style="width: 100%">
</a>
<br/>
<p align="center">
A privacy-focused, local-first, open-source, and ready-to-use alternative for Notion & Miro. <br />
One hyper-fused platform for wildly creative minds.
<p>
One hyper-fused platform for wildly creative minds. <br />
A privacy-focussed, local-first, open-source, and ready-to-use alternative for Notion & Miro.
</p>
<br/>
<br/>
<a href="https://www.producthunt.com/posts/affine-3?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-affine&#0045;3" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=440671&theme=light" alt="AFFiNE - One&#0032;app&#0032;for&#0032;all&#0032;&#0045;&#0032;Where&#0032;Notion&#0032;meets&#0032;Miro | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
<br/>
<br/>
</div>
<div align="center">
<a href="https://affine.pro">Home Page</a> |
<a href="https://discord.gg/whd5mjYqVw">Discord</a> |
<a href="https://app.affine.pro">Live Demo</a> |
<a href="https://affine.pro/blog/">Blog</a> |
<a href="https://docs.affine.pro/docs/">Documentation</a>
</div>
<br/>
[![AFFiNE Web](<https://img.shields.io/badge/-Try%20It%20Online%20%E2%86%92-rgb(84,56,255)?style=flat-square&logoColor=white&logo=affine>)](https://app.affine.pro)
[![AFFiNE macOS M1/M2 Chip](https://img.shields.io/badge/-macOS_M_Chip%20%E2%86%92-black?style=flat-square&logo=apple&logoColor=white)](https://affine.pro/download)
[![AFFiNE macOS x64](https://img.shields.io/badge/-macOS_x86%20%E2%86%92-black?style=flat-square&logo=apple&logoColor=white)](https://affine.pro/download)
[![AFFiNE Window x64](https://img.shields.io/badge/-Windows%20%E2%86%92-blue?style=flat-square&logo=windows&logoColor=white)](https://affine.pro/download)
[![AFFiNE Linux](https://img.shields.io/badge/-Linux%20%E2%86%92-yellow?style=flat-square&logo=linux&logoColor=white)](https://affine.pro/download)
[![Releases](https://img.shields.io/github/downloads/toeverything/AFFiNE/total)](https://github.com/toeverything/AFFiNE/releases/latest)
[![stars-icon]](https://github.com/toeverything/AFFiNE)
[![All Contributors][all-contributors-badge]](#contributors)
[![codecov]](https://codecov.io/gh/toeverything/AFFiNE)
[![Node-version-icon]](https://nodejs.org/)
[![TypeScript-version-icon]](https://www.typescriptlang.org/)
[![React-version-icon]](https://reactjs.org/)
[![blocksuite-icon]](https://github.com/toeverything/blocksuite)
[![Rust-version-icon]](https://www.rust-lang.org/)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ftoeverything%2FAFFiNE.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Ftoeverything%2FAFFiNE?ref=badge_shield)
[![Deploy](https://github.com/toeverything/AFFiNE/actions/workflows/deploy.yml/badge.svg)](https://github.com/toeverything/AFFiNE/actions/workflows/deploy.yml)
</div>
---
<div align="center">
<a href="http://affine.pro"><img src="https://img.shields.io/badge/-AFFiNE-06449d?style=social&logo=affine" height=25></a>
&nbsp;
<a href="https://community.affine.pro"><img src="https://img.shields.io/badge/-Community-424549?style=social&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAXNJREFUWEftlitLRUEURtdVEVExWUx2qxgNVouoXYtNDP4Tw20WtftAsItZrHaTYBJREZ98MAc248wcZxi4CGfSeezHmm/23kyPAa/egPPTAXQK/FsFBP7ldVDRZoqcgO9I+2bHy3ZIJBfTCPCZM1tqAxwBmzUBrNQNbEx+5b0B5oEN4NCBrAMnMaiUAuPAs3HU82TLEZwBqwGbaJ4UgKQ8CFR6SoEl4LIWwCJwZQCegKkWBWLHVKSActvdzgG3DqitDf3/VQBskBDALrDnAKXUo3ueAF5KinAf2DKOmnzD7l214bdbA6hC1XHZNQa8hSBC0hwDa57xDHDvvvWB7ciOZoE79+8CWPbsBGc769eFxJdWIKcuyIdRoG3W7AAC1dJkHDIOo8B78+4rEBo8r4AkLFk6Jk3HaeDBBTgHVmIAfpJUz+cAFXVBreQCvQYW/lqEjV1NAMUMqpAaxQMHyDnjYtuS+0BxstwaqJooFqxToFPgB5FuPCEB6XK2AAAAAElFTkSuQmCC" height=25></a>
&nbsp;
<a href="https://discord.com/invite/yz6tGVsf5p"><img src="https://img.shields.io/badge/-Discord-424549?style=social&logo=discord" height=25></a>
&nbsp;
<a href="https://t.me/affineworkos"><img src="https://img.shields.io/badge/-Telegram-red?style=social&logo=telegram" height=25></a>
&nbsp;
<a href="https://twitter.com/AffineOfficial"><img src="https://img.shields.io/badge/-Twitter-red?style=social&logo=twitter" height=25></a>
&nbsp;
<a href="https://medium.com/@affineworkos"><img src="https://img.shields.io/badge/-Medium-red?style=social&logo=medium" height=25></a>
</div>
<br />
<div align="center">
<em>Docs, canvas and tables are hyper-merged with AFFiNE - just like the word affine (əˈɪn | a-fine).</em>
</div>
<br />
<div align="center">
<img src="https://github.com/toeverything/AFFiNE/assets/79301703/49a426bb-8d2b-4216-891a-fa5993642253" style="width: 100%"/>
</div>
![img_v2_37a7cc04-ab3f-4405-ae9a-f84ceb4c948g](https://user-images.githubusercontent.com/79301703/230892907-5fd5c0c5-1665-4d75-8a35-744e0afc36a5.gif)
## Join our community
Before we tell you how to get started with AFFiNE, we'd like to shamelessly plug our awesome user and developer communities across [official social platforms](https://community.affine.pro/c/start-here/)! Once youre familiar with using the software, maybe you will share your wisdom with others and even consider joining the [AFFiNE Ambassador program](https://community.affine.pro/c/start-here/affine-ambassador) to help spread AFFiNE to the world.
## Getting started & staying tuned with us.
Star us, and you will receive all release notifications from GitHub without any delay!
⚠️ Please note that AFFiNE is still under active development and is not yet ready for production use. ⚠️
<img src="https://user-images.githubusercontent.com/79301703/230891830-0110681e-8c7e-483b-b6d9-9e42b291b9ef.gif" style="width: 100%"/>
[![affine.pro](https://img.shields.io/static/v1?label=Try%20it%20Online&logo=affine&message=%E2%86%92&style=for-the-badge)](https://app.affine.pro) No installation or registration required! Head over to our website and try it out now.
## What is AFFiNE
[![community.affine.pro](https://img.shields.io/static/v1?label=Join%20the%20community&message=%E2%86%92&style=for-the-badge)](https://community.affine.pro) Our wonderful community, where you can meet and engage with the team, developers and other like-minded enthusiastic user of AFFiNE.
[AFFiNE](https://affine.pro) is an open-source, all-in-one workspace and an operating system for all the building blocks that assemble your knowledge base and much more -- wiki, knowledge management, presentation and digital assets. It's a better alternative to Notion and Miro.
Star us, and you will receive all releases notifications from GitHub without any delay!
![rbU3YmmsQT](https://user-images.githubusercontent.com/79301703/230891830-0110681e-8c7e-483b-b6d9-9e42b291b9ef.gif)
## Features
**A true canvas for blocks in any form. Docs and whiteboard are now fully merged.**
- **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.
- **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.
- **Choice of multiple languages** — Thanks to community contributions AFFiNE offers support for multiple languages. If you don't find your language or would like to suggest some changes we welcome your contributions.
- Many editor apps claim to be a canvas for productivity, but AFFiNE is one of the very few which allows you to put any building block on an edgeless canvas -- rich text, sticky notes, any embedded web pages, multi-view databases, linked pages, shapes and even slides. We have it all.
**Multimodal AI partner ready to kick in any work**
- Write up professional work report? Turn an outline into expressive and presentable slides? Summary an article into a well-structured mindmap? Sorting your job plan and backlog for tasks? Or... draw and code prototype apps and web pages directly all with one prompt? With you, [AFFiNE AI](https://affine.pro/ai) pushes your creativity to the edge of your imagination,just like [Canvas AI](https://affine.pro/blog/best-canvas-ai) to generate mind map for brainstorming.
**Local-first & Real-time collaborative**
- We love the idea of local-first that you always own your data on your disk, in spite of the cloud. Furthermore, AFFiNE supports real-time sync and collaborations on web and cross-platform clients.
**Self-host & Shape your own AFFiNE**
- You have the freedom to manage, self-host, fork and build your own AFFiNE. Plugin community and third-party blocks are coming soon. More tractions on [Blocksuite](https://blocksuite.io). Check there to learn how to [self-host AFFiNE](https://docs.affine.pro/docs/self-host-affine).
## Acknowledgement
“We shape our tools and thereafter our tools shape us”. A lot of pioneers have inspired us along the way, e.g.:
- Quip & Notion with their great concept of “everything is a block”
- Trello with their Kanban
- Airtable & Miro with their no-code programmable datasheets
- Miro & Whimiscal with their edgeless visual whiteboard
- Remote & Capacities with their object-based tag system
There is a large overlap of their atomic “building blocks” between these apps. They are not open source, nor do they have a plugin system like Vscode for contributors to customize. We want to have something that contains all the features we love and also goes one step even further.
Thanks for checking us out, we appreciate your interest and sincerely hope that AFFiNE resonates with you! 🎵 Checking https://affine.pro/ for more details ions.
![img_v2_3a4ee0da-6dd7-48cb-8f19-5411f86768ag](https://user-images.githubusercontent.com/79301703/230893796-dc707955-e4e5-4a42-a3c9-18d1ea754f6f.gif)
## Contributing
@@ -103,53 +101,23 @@ For **bug reports**, **feature requests** and other **suggestions** you can also
For **translation** and **language support** you can visit our [i18n General Space](https://community.affine.pro/c/i18n-general).
Looking for **other ways to contribute** and wondering where to start? Check out the [AFFiNE Ambassador program](https://community.affine.pro/c/start-here/affine-ambassador), we work closely with passionate community members and provide them with a wide range of support and resources.
Looking for **others ways to contribute** and wondering where to start? Check out the [AFFiNE Ambassador program](https://community.affine.pro/c/start-here/affine-ambassador), we work closely with passionate community members and provide them with a wide-range of support and resources.
If you have questions, you are welcome to contact us. One of the best places to get more info and learn more is in the [AFFiNE Community](https://community.affine.pro) where you can engage with other like-minded individuals.
## Templates
AFFiNE now provides pre-built [templates](https://affine.pro/templates) from our team. Following are the Top 10 most popular templates among AFFiNE users,if you want to contribute, you can contribute your own template so other people can use it too.
- [vision board template](https://affine.pro/templates/category-vision-board-template)
- [one pager template](https://affine.pro/templates/category-one-pager-template-free)
- [sample lesson plan math template](https://affine.pro/templates/sample-lesson-plan-math-template)
- [grr lesson plan template free](https://affine.pro/templates/grr-lesson-plan-template-free)
- [free editable lesson plan template for pre k](https://affine.pro/templates/free-editable-lesson-plan-template-for-pre-k)
- [high note collection planners](https://affine.pro/templates/high-note-collection-planners)
- [digital planner](https://affine.pro/templates/category-digital-planner)
- [ADHD Planner](https://affine.pro/templates/adhd-planner)
- [Reading Log](https://affine.pro/templates/reading-log)
- [Cornell Notes Template](https://affine.pro/templates/category-cornell-notes-template)
## Blog
Welcome to the AFFiNE blog section! Here, youll find the latest insights, tips, and guides on how to maximize your experience with AFFiNE and AFFiNE AI, the leading Canvas AI tool for flexible note-taking and creative organization.
- [vision board template](https://affine.pro/blog/8-free-printable-vision-board-templates-examples-2023)
- [itinerary template](https://affine.pro/blog/free-customized-travel-itinerary-planner-templates)
- [one pager template](https://affine.pro/blog/top-12-one-pager-examples-how-to-create-your-own)
- [cornell notes template](https://affine.pro/blog/the-cornell-notes-template-and-system-learning-tips)
- [swot chart template](https://affine.pro/blog/top-10-free-editable-swot-analysis-template-examples)
- [apps like luna task](https://affine.pro/blog/apps-like-luna-task)
- [note taking ai from rough notes to mind map](https://affine.pro/blog/dynamic-AI-notes)
- [canvas ai](https://affine.pro/blog/best-canvas-ai)
- [one pager](https://affine.pro/blog/top-12-one-pager-examples-how-to-create-your-own)
- [SOP Template](https://affine.pro/blog/how-to-write-sop-step-by-step-guide-5-best-free-tools-templates)
- [Chore Chart](https://affine.pro/blog/10-best-free-chore-chart-templates-kids-adults)
## Ecosystem
| Name | | |
| ------------------------------------------------ | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| [@affine/component](packages/frontend/component) | AFFiNE Component Resources | ![](https://img.shields.io/codecov/c/github/toeverything/affine?style=flat-square) |
| [@toeverything/theme](packages/common/theme) | AFFiNE theme | [![](https://img.shields.io/npm/dm/@toeverything/theme?style=flat-square&color=eee)](https://www.npmjs.com/package/@toeverything/theme) |
| Name | | |
| -------------------------------------------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| [@affine/component](packages/frontend/component) | AFFiNE Component Resources | [![](https://img.shields.io/codecov/c/github/toeverything/affine?style=flat-square)](https://affine-storybook.vercel.app/) |
| [@toeverything/y-indexeddb](packages/common/y-indexeddb) | IndexedDB database adapter for Yjs | [![](https://img.shields.io/npm/dm/@toeverything/y-indexeddb?style=flat-square&color=eee)](https://www.npmjs.com/package/@toeverything/y-indexeddb) |
| [@toeverything/theme](packages/common/theme) | AFFiNE theme | [![](https://img.shields.io/npm/dm/@toeverything/theme?style=flat-square&color=eee)](https://www.npmjs.com/package/@toeverything/theme) |
## Upstreams
We would also like to give thanks to open-source projects that make AFFiNE possible:
- [Blocksuite](https://github.com/toeverything/BlockSuite) - 💠 BlockSuite is the open-source collaborative editor project behind AFFiNE.
- [blocksuite](https://github.com/toeverything/BlockSuite) - 💠 BlockSuite is the open-source collaborative editor project behind AFFiNE.
- [OctoBase](https://github.com/toeverything/OctoBase) - 🐙 OctoBase is the open-source database behind AFFiNE, local-first, yet collaborative. A light-weight, scalable, data engine written in Rust.
- [yjs](https://github.com/yjs/yjs) - Fundamental support of CRDTs for our implementation on state management and data sync.
- [electron](https://github.com/electron/electron) - Build cross-platform desktop apps with JavaScript, HTML, and CSS.
@@ -170,17 +138,44 @@ 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
Begin with Docker to deploy your own feature-rich, unrestricted version of AFFiNE. Our team is diligently updating to the latest version. For more information on how to self-host AFFiNE, please refer to our [documentation](https://docs.affine.pro/docs/self-host-affine).
> 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].
## Hiring
Some amazing companies, including AFFiNE, are looking for developers! Are you interested in joining AFFiNE or its partners? Check out our Discord channel for some of the latest jobs available.
Some amazing companies including AFFiNE are looking for developers! Are you interested in helping build with AFFiNE and/or its partners? Check out some of the latest [jobs available].
## Upgrading
For upgrading information, please see our [update page].
## Feature Request
For feature requests, please see [community.affine.pro](https://community.affine.pro/c/feature-requests/).
For feature request, please see [community.affine.pro](https://community.affine.pro/c/feature-requests/).
## Building
@@ -206,14 +201,10 @@ Thanks to [Chromatic](https://www.chromatic.com/) for providing the visual testi
## License
### Editions
- AFFiNE Community Edition (CE) is the current available version, it's free for self-host under the MIT license.
- AFFiNE Enterprise Edition (EE) is yet to be published, it will have more advanced features and enterprise-oriented offerings, including but not exclusive to rebranding and SSO, advanced admin and audit, etc., you may refer to https://affine.pro/pricing for more information
See [LICENSE] for details.
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ftoeverything%2FAFFiNE.svg?type=large)](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
@@ -221,6 +212,7 @@ See [LICENSE] for details.
[jobs available]: ./docs/jobs.md
[latest packages]: https://github.com/toeverything/AFFiNE/pkgs/container/affine-self-hosted
[contributor license agreement]: https://github.com/toeverything/affine/edit/canary/.github/CLA.md
[rust-version-icon]: https://img.shields.io/badge/Rust-1.75.0-dea584
[stars-icon]: https://img.shields.io/github/stars/toeverything/AFFiNE.svg?style=flat&logo=github&colorB=red&label=stars
[codecov]: https://codecov.io/gh/toeverything/affine/branch/canary/graphs/badge.svg?branch=canary
[node-version-icon]: https://img.shields.io/badge/node-%3E=18.16.1-success

View File

@@ -1,29 +0,0 @@
# Security Policy
## Supported Versions
We recommend users to always use the latest major version. Security updates will be provided for the current major version until the next major version is released.
| Version | Supported |
| --------------- | ------------------ |
| 0.17.x (stable) | :white_check_mark: |
| < 0.17.x | :x: |
## Reporting a Vulnerability
We welcome you to provide us with bug reports via and email at [security@toeverything.info](mailto:security@toeverything.info). We expect your report to contain at least the following for us to evaluate and reproduce:
1. Using platform and version, for example:
- macos arm64 0.12.0-canary-202402220729-0868ac6
- app.affine.pro 0.12.0-canary-202402220729-0868ac6
2. A sets of video or screenshot containing the reproduce steps that proves you successfully exploited the vulnerability, preferably including the time and software version of the successful exploit.
3. Your classification or analysis of the vulnerability (optional)
Since we are an open source project, we also welcome you to provide corresponding fix PRs.
We will provide bounties for vulnerabilities involving user information leakage, permission leakage, and unauthorized code execution. For other types of vulnerabilities, we will determine specific rewards based on the evaluation results.
If the vulnerability is caused by a library we depend on, we encourage you to submit a security report to the corresponding dependent library at the same time to benefit more users.

View File

@@ -2,7 +2,7 @@
> **Warning**:
>
> This document is not guaranteed to be up-to-date.
> This document has not been updated for a while.
> If you find any outdated information, please feel free to open an issue or submit a PR.
> **Note**
@@ -27,7 +27,7 @@ We suggest develop our product under node.js LTS(Long-term support) version
install [Node LTS version](https://nodejs.org/en/download)
> Up to now, the major node.js version is 20.x
> Up to now, the major node.js version is 18.x
#### Option 2: Use node version manager
@@ -76,7 +76,7 @@ Once Developer Mode is enabled, execute the following command with administrator
```sh
# Enable symbolic links
git config --global core.symlinks true
# Clone the repository
# Clone the repository, also need to be run with administrator privileges
git clone https://github.com/toeverything/AFFiNE
```
@@ -93,7 +93,7 @@ yarn workspace @affine/native build
### Build Server Dependencies
```sh
yarn workspace @affine/server-native build
yarn workspace @affine/storage build
```
## Testing

View File

@@ -1 +1,93 @@
# Please visit https://docs.affine.pro/docs/contributing
# Welcome to our contributing guide <!-- omit in toc -->
Thank you for investing your time in contributing to our project! Any contribution you make will be reflected on our GitHub :sparkles:.
Read our [Code of Conduct](./CODE_OF_CONDUCT.md) to keep our community approachable and respectable. Join our [Discord](https://discord.com/invite/yz6tGVsf5p) server for more.
In this guide you will get an overview of the contribution workflow from opening an issue, creating a PR, reviewing, and merging the PR.
Use the table of contents icon on the top left corner of this document to get to a specific section of this guide quickly.
## New contributor guide
Currently we have two versions of AFFiNE:
- [AFFiNE Pre-Alpha](https://livedemo.affine.pro/). This version uses the branch `Pre-Alpha`, it is no longer actively developed but contains some different functions and features.
- [AFFiNE Alpha](https://pathfinder.affine.pro/). This version uses the `canary` branch, this is the latest version under active development.
To get an overview of the project, read the [README](../README.md). Here are some resources to help you get started with open source contributions:
- [Finding ways to contribute to open source on GitHub](https://docs.github.com/en/get-started/exploring-projects-on-github/finding-ways-to-contribute-to-open-source-on-github)
- [Set up Git](https://docs.github.com/en/get-started/quickstart/set-up-git)
- [GitHub flow](https://docs.github.com/en/get-started/quickstart/github-flow)
- [Collaborating with pull requests](https://docs.github.com/en/github/collaborating-with-pull-requests)
## Getting started
Check to see what [types of contributions](types-of-contributions.md) we accept before making changes. Some of them don't even require writing a single line of code :sparkles:.
### Issues
#### Create a new issue or feature request
If you spot a problem, [search if an issue already exists](https://docs.github.com/en/github/searching-for-information-on-github/searching-on-github/searching-issues-and-pull-requests#search-by-the-title-body-or-comments). If a related issue doesn't exist, you can open a new issue using a relevant [issue form](https://github.com/toeverything/AFFiNE/issues/new/choose).
#### Solve an issue
Scan through our [existing issues](https://github.com/toeverything/AFFiNE/issues) to find one that interests you. You can narrow down the search using `labels` as filters. See our [Labels](https://github.com/toeverything/AFFiNE/labels) for more information. As a general rule, we dont assign issues to anyone. If you find an issue to work on, you are welcome to open a PR with a fix.
### Make Changes
#### Make changes in the UI
Click **Make a contribution** at the bottom of any docs page to make small changes such as a typo, sentence fix, or a broken link. This takes you to the `.md` file where you can make your changes and [create a pull request](#pull-request) for a review.
#### Make changes in a codespace
For more information about using a codespace for working on GitHub documentation, see "[Working in a codespace](https://github.com/github/docs/blob/main/contributing/codespace.md)."
#### Make changes locally
1. [Install Git LFS](https://docs.github.com/en/github/managing-large-files/versioning-large-files/installing-git-large-file-storage).
2. Fork the repository.
- Using GitHub Desktop:
- [Getting started with GitHub Desktop](https://docs.github.com/en/desktop/installing-and-configuring-github-desktop/getting-started-with-github-desktop) will guide you through setting up Desktop.
- Once Desktop is set up, you can use it to [fork the repo](https://docs.github.com/en/desktop/contributing-and-collaborating-using-github-desktop/cloning-and-forking-repositories-from-github-desktop)!
- Using the command line:
- [Fork the repo](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo#fork-an-example-repository) so that you can make your changes without affecting the original project until you're ready to merge them.
3. Install or update to **Node.js v16**.
4. Create a working branch and start with your changes!
### Commit your update
Commit the changes once you are happy with them.
Reach out the community members for necessary help.
Once your changes are ready, don't forget to self-review to speed up the review process:zap:.
### Pull Request
When you're finished with the changes, create a pull request, also known as a PR.
- Fill the "Ready for review" template so that we can review your PR. This template helps reviewers understand your changes as well as the purpose of your pull request.
- Don't forget to [link PR to issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) if you are solving one.
- Enable the checkbox to [allow maintainer edits](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork) so the branch can be updated for a merge.
Once you submit your PR, a Docs team member will review your proposal. We may ask questions or request for additional information.
- We may ask for changes to be made before a PR can be merged, either using [suggested changes](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/incorporating-feedback-in-your-pull-request) or pull request comments. You can apply suggested changes directly through the UI. You can make any other changes in your fork, then commit them to your branch.
- As you update your PR and apply changes, mark each conversation as [resolved](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/commenting-on-a-pull-request#resolving-conversations).
- If you run into any merge issues, checkout this [git tutorial](https://github.com/skills/resolve-merge-conflicts) to help you resolve merge conflicts and other issues.
### Your PR is merged!
Congratulations :tada::tada: The AFFiNE team thanks you :sparkles:.
Once your PR is merged, your contributions will be publicly visible on our GitHub.
Now that you are part of the AFFiNE community, see how else you can join and help over at [GitBook](https://docs.affine.pro/affine/)

View File

@@ -1,10 +1,5 @@
# Building AFFiNE Desktop Client App
> **Warning**:
>
> This document is not guaranteed to be up-to-date.
> If you find any outdated information, please feel free to open an issue or submit a PR.
## Table of Contents
- [Prerequisites](#prerequisites)
@@ -12,100 +7,35 @@
- [Build](#build)
- [CI](#ci)
## Things you may need to know before getting started
Building the desktop client app for the moment is a bit more complicated than building the web app. The client right now is an Electron app that wraps the prebuilt web app, with parts of the native modules written in Rust, which means we have the following source modules to build a desktop client app:
1. `packages/frontend/core`: the web app
2. `packages/frontend/native`: the native modules written in Rust (mostly the sqlite bindings)
3. `packages/frontend/electron`: the Electron app (containing main & helper process, and the electron entry point in `packages/frontend/electron/renderer`)
#3 is dependent on #1 and #2, and relies on electron-forge to make the final app & installer. To get a deep understanding of how the desktop client app is built, you may want to read the workflow file in [release-desktop.yml](/.github/workflows/release-desktop.yml).
Due to [some limitations of Electron builder](https://github.com/yarnpkg/berry/issues/4804), you may need to have two separate yarn config for building the core and the desktop client app:
1. build frontend (with default yarn settings)
2. build electron (reinstall with hoisting off)
We will explain the steps in the following sections.
## Prerequisites
Before you start building AFFiNE Desktop Client Application, please following the same steps in [BUILDING#Prerequisites](./BUILDING.md#prerequisites) to install Node.js and Rust.
Before you start building AFFiNE Desktop Client Application, please [install Rust toolchain first](https://www.rust-lang.org/learn/get-started).
On Windows, you must enable symbolic links this code repo. See [#### Windows](./BUILDING.md#Windows).
Note that if you encounter any issues with installing Rust and crates, try following [this guide (zh-CN)](https://course.rs/first-try/slowly-downloading.html) to set up alternative registries.
## Build, package & make the desktop client app
## Development
### 0. Build the native modules
To run AFFiNE Desktop Client Application locally, run the following commands:
Please refer to `Build Native Dependencies` section in [BUILDING.md](./BUILDING.md#Build-Native-Dependencies) to build the native modules.
### 1. Build the core
On Mac & Linux
```shell
BUILD_TYPE=canary SKIP_NX_CACHE=1 yarn workspace @affine/electron generate-assets
```
On Windows (powershell)
```powershell
$env:BUILD_TYPE="canary"
$env:SKIP_NX_CACHE=1
$env:DISTRIBUTION=desktop
$env:SKIP_WEB_BUILD=1
yarn build --skip-nx-cache
```
### 2. Re-config yarn, clean up the node_modules and reinstall the dependencies
As we said before, you need to reinstall the dependencies with hoisting off. You can do this by running the following command:
```shell
yarn config set nmMode classic
yarn config set nmHoistingLimits workspaces
```
Then, clean up all node_modules and reinstall the dependencies:
On Mac & Linux
```shell
find . -name 'node_modules' -type d -prune -exec rm -rf '{}' +
```sh
# in repo root
yarn install
yarn dev
# in packages/frontend/native
yarn build
# in packages/frontend/electron
yarn dev
```
On Windows (powershell)
Now you should see the Electron app window popping up shortly.
```powershell
dir -Path . -Filter node_modules -recurse | foreach {echo $_.fullname; rm -r -Force $_.fullname}
yarn install
```
## Build
### 3. Build the desktop client app installer
To build the desktop client application, run `yarn make` in `packages/frontend/electron`.
#### Mac & Linux
Note: you need to comment out `osxSign` and `osxNotarize` in `forge.config.js` to skip signing and notarizing the app.
```shell
BUILD_TYPE=canary SKIP_WEB_BUILD=1 HOIST_NODE_MODULES=1 yarn workspace @affine/electron make
```
#### Windows
Making the windows installer is a bit different. Right now we provide two installer options: squirrel and nsis.
```powershell
$env:BUILD_TYPE="canary"
$env:SKIP_WEB_BUILD=1
$env:HOIST_NODE_MODULES=1
yarn workspace @affine/electron package
yarn workspace @affine/electron make-squirrel
yarn workspace @affine/electron make-nsis
```
Note: you may want to comment out `osxSign` and `osxNotarize` in `forge.config.js` to avoid signing and notarizing the app.
Once the build is complete, you can find the paths to the binaries in the terminal output.

View File

@@ -0,0 +1,256 @@
# Behind the code - Code Design and Architecture of the AFFiNE platform
## Introduction
This document delves into the design and architecture of the AFFiNE platform, providing insights for developers interested in contributing to AFFiNE or gaining a better understanding of our design principles.
## Addressing the Challenge
AFFiNE is a platform designed to be the next-generation collaborative knowledge base for professionals. It is local-first, yet collaborative; It is robust as a foundational platform, yet friendly to extend. We believe that a knowledge base that truly meets the needs of professionals in different scenarios should be open-source and open to the community. By using AFFiNE, people can take full control of their data and workflow, thus achieving data sovereignty.
To do so, we should have a stable plugin system that is easy to use by the community and a well-modularized editor for customizability. Let's list the challenges from the perspective of data modeling, UI and feature plugins, and cross-platform support.
### Data might come from anywhere and go anywhere, in spite of the cloud
AFFiNE provides users with flexibility and control over their data storage. Our platform is designed to prioritize user ownership of data, which means data in AFFiNE is always accessible from local devices like a laptop's local file or the browser's indexedDB. In the mean while, data can also be stored in centralised cloud-native way.
Thanks to our use of CRDTs (Conflict-free Replicated Data Types), data in AFFiNE is always conflict-free, similar to a auto-resolve-conflict Git. This means that data synchronization, sharing, and real-time collaboration are seamless and can occur across any network layer so long as the data as passed. As a result, developers do not need to worry about whether the data was generated locally or remotely, as CRDTs treat both equally.
While a server-centric backend is supported with AFFiNE, it is not suggested. By having a local-first architecture, AFFiNE users can have real-time responsive UI, optimal performance and effortlessly synchronize data across multiple devices and locations. This includes peer-to-peer file replication, storing file in local or cloud storage, saving it to a server-side database, or using AFFiNE Cloud for real-time collaboration and synchronization.
### Customizable UI and features
AFFiNE is a platform that allows users to customize the UI and features of each part.
We need to consider the following cases:
- Pluggable features: Some features can be disabled or enabled. For example, individuals who use AFFiNE for personal purposes may not need authentication or collaboration features. On the other hand, enterprise users may require authentication and strong security.
- SDK for the developers, the developers can modify or build their own feature or UI plugins, such as AI writing support, self-hosted databases, or domain-specific editable blocks.
### Diverse platforms
AFFiNE supports various platforms, including desktop, mobile, and web while being local-first. However, it's important to note that certain features may differ on different platforms, and it's also possible for data and editor versions to become mismatched.
## The solution
### Loading Mechanism
The AFFiNE is built on the web platform, meaning that most code runs on the JavaScript runtime(v8, QuickJS).
Some interfaces, like in the Desktop, will be implemented in the native code like Rust.
But eventually, the main logic of AFFiNE is running on the JavaScript runtime. Since it is a single-threaded runtime, we need to ensure that the code is running in a non-blocking way.
Some logic has to be running in the blocking way.
We have to set up the environment before starting the core.
And for the Workspace, like local workspace or cloud workspace, we have to load the data from the storage before rendering the UI.
During this period, there will be transition animation and skeleton UI.
```mermaid
graph LR
subgraph Interactive unavailable
A[Loading] --> B[Setup Environment]
B --> C[Loading Initial Data]
C --> D[Skeleton UI]
end
D --> E[Render UI]
E --> F[Async fetching Data] --> E
```
In this way, we need to boost the performance of the loading process.
The initial data is the most costly part of the process.
We must ensure that the initial data is loaded as quickly as possible.
Here is an obvious conclusion that only one Workspace is active simultaneously in one browser.
So we need to load the data of the active Workspace as the initial data.
And other workspaces can be loaded in the background asynchronously.
For example, the local Workspace is saved in the browser's indexedDB.
One way to boost the performance is to use the Web Worker to load the data in the background.
Here is one pseudocode:
```tsx
// worker.ts
import { openDB } from 'idb';
const db = await openDB('local-db' /* ... */);
const data = await db.getAll('data');
self.postMessage(data);
// main.ts
const worker = new Worker('./worker.ts', { type: 'module' });
await new Promise<Data>(resolve => {
worker.addEventListener('message', e => resolve(e.data));
});
// ready to render the UI
renderUI(data);
```
We use React Suspense to deal with the initial data loading in the real code.
```tsx
import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai';
const currentWorkspaceIdAtom = atom(null);
const currentWorkspaceAtom = atom<Workspace>(async get => {
const workspaceId = await get(currentWorkspaceIdAtom);
// async load the workspace data
return Workspace;
});
const Workspace = () => {
const currentWorkspace = useAtomValue(currentWorkspaceAtom);
return <WorkspaceUI workspace={currentWorkspace} />;
};
const App = () => {
const router = useRouter();
const workspaceId = router.query.workspaceId;
const [currentWorkspaceId, set] = useAtom(currentWorkspaceIdAtom);
if (!currentWorkspaceId) {
set(workspaceId);
return <Loading />;
}
return (
<Suspense fallback={<Skeleton />}>
<Workspace />
</Suspense>
);
};
```
### Data Storage and UI Rendering
We assume that the data is stored in different places and loaded differently.
In the current version, we have two places to store the data: local and Cloud storage.
The local storage is the browser's indexedDB, the default storage for the local Workspace.
The cloud storage is the AFFiNE Cloud, which is the default storage for the cloud workspace.
But since the Time to Interactive(TTI) is the most important metric for performance and user experience,
all initial data is loaded in the indexedDB.
And other data will be loaded and updated in the background.
With this design concept, we have the following data structure:
```ts
import { Workspace as Store } from '@blocksuite/store';
interface Provider {
type: 'local-indexeddb' | 'affine-cloud' | 'desktop-sqlite';
background: boolean; // if the provider is background, we will load the data in the background
necessary: boolean; // if the provider is necessary, we will block the UI rendering until this provider is ready
}
interface Workspace {
id: string;
store: Store;
providers: Provider[];
}
```
The `provider` is a connector that bridges the current data in memory and the data in another place.
You can combine different providers to build different data storage and loading strategy.
For example, if there is only `affine-cloud`,
the data will be only loaded from the Cloud and not saved in the local storage,
which might be useful for the enterprise user.
Also, we want to distinguish the different types of Workspace.
Even though the providers are enough for the Workspace, when we display the Workspace in the UI, we need to know the type of Workspace.
AFFiNE Cloud Workspace needs user authentication; the local Workspace does not need it.
And there should have a way to create, read, update, and delete the Workspace.
Hence, we combine all details of the Workspace as we mentioned above into the `WorkspacePlugin` type.
```ts
import React from 'react';
interface UI<WorkspaceType> {
DetailPage: React.FC<UIProps<WorkspaceType>>;
SettingPage: React.FC<UIProps<WorkspaceType>>;
SettingPage: React.FC<UIProps<WorkspaceType>>;
}
interface CRUD<WorkspaceType> {
create: () => Promise<WorkspaceType>;
read: (id: string) => Promise<WorkspaceType>;
list: () => Promise<WorkspaceType[]>;
delete: (Workspace: WorkspaceType) => Promise<WorkspaceType>;
}
interface WorkspacePlugin<WorkspaceType> {
type: WorkspaceType;
ui: UI<WorkspaceType>;
crud: CRUD<WorkspaceType>;
}
```
```mermaid
graph TB
WorkspaceCRUD --> Cloud
WorkspaceCRUD --> SelfHostCloud
subgraph Remote
Cloud[AFFiNE Cloud]
SelfHostCloud[Self Host AFFiNE Server]
end
subgraph Computer
WorkspaceCRUD --> DesktopSqlite[Desktop Sqlite]
subgraph JavaScript Runtime
IndexedDB[IndexedDB]
WorkspaceCRUD --> IndexedDB
subgraph Next.js
Entry((entry point))
Entry --> NextApp[Next.js App]
NextApp --> App[App]
end
subgraph Workspace Runtime
App[App] --> WorkspaceUI
WorkspacePlugin[Workspace Plugin]
WorkspacePlugin[Workspace Plugin] --> WorkspaceUI
WorkspacePlugin[Workspace Plugin] --> WorkspaceCRUD[Workspace CRUD]
WorkspaceUI[Workspace UI] --> WorkspaceCRUD
WorkspaceUI -->|async init| Provider
Provider -->|update ui| WorkspaceUI
Provider -->|update data| WorkspaceCRUD
end
end
end
```
Notice that we do not assume the Workspace UI has to be written in React.js(for now, it has to be),
In the future, we can support other UI frameworks instead, like Vue and Svelte.
### Workspace Loading Details
```mermaid
flowchart TD
subgraph JavaScript Runtime
subgraph Next.js
Start((entry point)) -->|setup environment| OnMount{On mount}
OnMount -->|empty data| Init[Init Workspaces]
Init --> LoadData
OnMount -->|already have data| LoadData>Load data]
LoadData --> CurrentWorkspace[Current workspace]
LoadData --> Workspaces[Workspaces]
Workspaces --> Providers[Providers]
subgraph React
Router([Router]) -->|sync `query.workspaceId`| CurrentWorkspace
CurrentWorkspace -->|sync `currentWorkspaceId`| Router
CurrentWorkspace -->|render| WorkspaceUI[Workspace UI]
end
end
Providers -->|push new update| Persistence[(Persistence)]
Persistence -->|patch workspace| Providers
end
```

View File

@@ -29,6 +29,13 @@ It includes the global constants, browser and system check.
This package should be imported at the very beginning of the entry point.
### `@affine/workspace`
Current we have two workspace plugin:
- `local` for local workspace, which is the default workspace type.
- `affine` for cloud workspace, which is the workspace type for AFFiNE Cloud with OctoBase backend.
#### Design principles
- Each workspace plugin has its state and is isolated from other workspace plugins.
@@ -53,3 +60,13 @@ yarn dev
### `@affine/electron`
See [building desktop client app](../building-desktop-client-app.md).
### `@affine/storybook`
```shell
yarn workspace @affine/storybook storybook
```
## What's next?
- [Behind the code](./behind-the-code.md)

View File

@@ -1,10 +1,5 @@
This document explains how to start server (@affine/server) locally with Docker
> **Warning**:
>
> This document is not guaranteed to be up-to-date.
> If you find any outdated information, please feel free to open an issue or submit a PR.
## Run postgresql in docker
```
@@ -29,7 +24,7 @@ docker run --rm --name mailhog -p 1025:1025 -p 8025:8025 mailhog/mailhog
```
docker ps
docker exec -it affine-postgres psql -U postgres ## `affine-postgres` is the container name from the previous step
docker exec -it CONTAINER_ID psql -U postgres ## change container_id
```
### in the terminal, following the example to user & table
@@ -54,24 +49,28 @@ postgres=# \du
### Set the following config to `packages/backend/server/.env`
In the following setup, we assume you have postgres server running at localhost:5432 and mailhog running at localhost:1025.
When logging in via email, you will see the mail arriving at localhost:8025 in a browser.
```
DATABASE_URL="postgresql://affine:affine@localhost:5432/affine"
MAILER_SENDER="noreply@toeverything.info"
MAILER_USER="auth"
MAILER_PASSWORD="auth"
MAILER_HOST="localhost"
MAILER_PORT="1025"
NEXTAUTH_URL="http://localhost:8080/"
```
You may need additional env for auth login. You may want to put your own one if you are not part of the AFFiNE team
For email login & password, please refer to https://nodemailer.com/usage/using-gmail/
```
OAUTH_EMAIL_SENDER=
OAUTH_EMAIL_LOGIN=
OAUTH_EMAIL_PASSWORD=
OAUTH_GOOGLE_ENABLED="true"
OAUTH_GOOGLE_CLIENT_ID=
OAUTH_GOOGLE_CLIENT_SECRET=
```
## Prepare prisma
```
yarn workspace @affine/server prisma db push
yarn workspace @affine/server data-migration run
```
Note, you may need to do it again if db schema changed.
@@ -86,7 +85,7 @@ yarn workspace @affine/server prisma studio
```
# build native
yarn workspace @affine/server-native build
yarn workspace @affine/storage build
yarn workspace @affine/native build
```
@@ -96,12 +95,6 @@ yarn workspace @affine/native build
yarn workspace @affine/server dev
```
when server started, it will created a default user:
email: dev@affine.pro
name: Dev User
password: dev
## start core (web)
```

Some files were not shown because too many files have changed in this diff Show More