mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-17 06:16:59 +08:00
Compare commits
2 Commits
v2026.2.14
...
l-sun/enab
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac76e5b949 | ||
|
|
0bc1005b96 |
@@ -5,14 +5,7 @@ rustflags = ["-C", "target-feature=+crt-static"]
|
|||||||
[target.'cfg(target_os = "linux")']
|
[target.'cfg(target_os = "linux")']
|
||||||
rustflags = ["-C", "link-args=-Wl,--warn-unresolved-symbols"]
|
rustflags = ["-C", "link-args=-Wl,--warn-unresolved-symbols"]
|
||||||
[target.'cfg(target_os = "macos")']
|
[target.'cfg(target_os = "macos")']
|
||||||
rustflags = [
|
rustflags = ["-C", "link-args=-Wl,-undefined,dynamic_lookup,-no_fixup_chains", "-C", "link-args=-all_load", "-C", "link-args=-weak_framework ScreenCaptureKit"]
|
||||||
"-C",
|
|
||||||
"link-args=-Wl,-undefined,dynamic_lookup,-no_fixup_chains",
|
|
||||||
"-C",
|
|
||||||
"link-args=-all_load",
|
|
||||||
"-C",
|
|
||||||
"link-args=-weak_framework ScreenCaptureKit",
|
|
||||||
]
|
|
||||||
# https://sourceware.org/bugzilla/show_bug.cgi?id=21032
|
# https://sourceware.org/bugzilla/show_bug.cgi?id=21032
|
||||||
# https://sourceware.org/bugzilla/show_bug.cgi?id=21031
|
# https://sourceware.org/bugzilla/show_bug.cgi?id=21031
|
||||||
# https://github.com/rust-lang/rust/issues/134820
|
# https://github.com/rust-lang/rust/issues/134820
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ yarn install
|
|||||||
|
|
||||||
# Build Server Dependencies
|
# Build Server Dependencies
|
||||||
yarn affine @affine/server-native build
|
yarn affine @affine/server-native build
|
||||||
|
yarn affine @affine/reader build
|
||||||
|
|
||||||
# Create database
|
# Create database
|
||||||
yarn affine @affine/server prisma migrate reset -f
|
yarn affine @affine/server prisma migrate reset -f
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ version: '3.8'
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
security_opt:
|
|
||||||
- no-new-privileges:true
|
|
||||||
image: mcr.microsoft.com/devcontainers/base:bookworm
|
image: mcr.microsoft.com/devcontainers/base:bookworm
|
||||||
volumes:
|
volumes:
|
||||||
- ../..:/workspaces:cached
|
- ../..:/workspaces:cached
|
||||||
|
|||||||
@@ -148,11 +148,6 @@
|
|||||||
"description": "Whether allow new registrations.\n@default true",
|
"description": "Whether allow new registrations.\n@default true",
|
||||||
"default": true
|
"default": true
|
||||||
},
|
},
|
||||||
"allowSignupForOauth": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Whether allow new registrations via configured oauth.\n@default true",
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"requireEmailDomainVerification": {
|
"requireEmailDomainVerification": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Whether require email domain record verification before accessing restricted resources.\n@default false",
|
"description": "Whether require email domain record verification before accessing restricted resources.\n@default false",
|
||||||
@@ -195,11 +190,6 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Configuration for mailer module",
|
"description": "Configuration for mailer module",
|
||||||
"properties": {
|
"properties": {
|
||||||
"SMTP.name": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Name of the email server (e.g. your domain name)\n@default \"AFFiNE Server\"\n@environment `MAILER_SERVERNAME`",
|
|
||||||
"default": "AFFiNE Server"
|
|
||||||
},
|
|
||||||
"SMTP.host": {
|
"SMTP.host": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Host of the email server (e.g. smtp.gmail.com)\n@default \"\"\n@environment `MAILER_HOST`",
|
"description": "Host of the email server (e.g. smtp.gmail.com)\n@default \"\"\n@environment `MAILER_HOST`",
|
||||||
@@ -222,12 +212,12 @@
|
|||||||
},
|
},
|
||||||
"SMTP.sender": {
|
"SMTP.sender": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Sender of all the emails (e.g. \"AFFiNE Self Hosted <noreply@example.com>\")\n@default \"AFFiNE Self Hosted <noreply@example.com>\"\n@environment `MAILER_SENDER`",
|
"description": "Sender of all the emails (e.g. \"AFFiNE Team <noreply@affine.pro>\")\n@default \"\"\n@environment `MAILER_SENDER`",
|
||||||
"default": "AFFiNE Self Hosted <noreply@example.com>"
|
"default": ""
|
||||||
},
|
},
|
||||||
"SMTP.ignoreTLS": {
|
"SMTP.ignoreTLS": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Whether ignore email server's TLS certificate verification. Enable it for self-signed certificates.\n@default false\n@environment `MAILER_IGNORE_TLS`",
|
"description": "Whether ignore email server's TSL certification verification. Enable it for self-signed certificates.\n@default false\n@environment `MAILER_IGNORE_TLS`",
|
||||||
"default": false
|
"default": false
|
||||||
},
|
},
|
||||||
"fallbackDomains": {
|
"fallbackDomains": {
|
||||||
@@ -235,11 +225,6 @@
|
|||||||
"description": "The emails from these domains are always sent using the fallback SMTP server.\n@default []",
|
"description": "The emails from these domains are always sent using the fallback SMTP server.\n@default []",
|
||||||
"default": []
|
"default": []
|
||||||
},
|
},
|
||||||
"fallbackSMTP.name": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Name of the fallback email server (e.g. your domain name)\n@default \"AFFiNE Server\"",
|
|
||||||
"default": "AFFiNE Server"
|
|
||||||
},
|
|
||||||
"fallbackSMTP.host": {
|
"fallbackSMTP.host": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Host of the email server (e.g. smtp.gmail.com)\n@default \"\"",
|
"description": "Host of the email server (e.g. smtp.gmail.com)\n@default \"\"",
|
||||||
@@ -262,12 +247,12 @@
|
|||||||
},
|
},
|
||||||
"fallbackSMTP.sender": {
|
"fallbackSMTP.sender": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Sender of all the emails (e.g. \"AFFiNE Self Hosted <noreply@example.com>\")\n@default \"\"",
|
"description": "Sender of all the emails (e.g. \"AFFiNE Team <noreply@affine.pro>\")\n@default \"\"",
|
||||||
"default": ""
|
"default": ""
|
||||||
},
|
},
|
||||||
"fallbackSMTP.ignoreTLS": {
|
"fallbackSMTP.ignoreTLS": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Whether ignore email server's TLS certificate verification. Enable it for self-signed certificates.\n@default false",
|
"description": "Whether ignore email server's TSL certification verification. Enable it for self-signed certificates.\n@default false",
|
||||||
"default": false
|
"default": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -337,42 +322,8 @@
|
|||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "The config for the S3 compatible storage provider.",
|
"description": "The config for the s3 compatible storage provider. directly passed to aws-sdk client.\n@link https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html",
|
||||||
"properties": {
|
"properties": {
|
||||||
"endpoint": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The S3 compatible endpoint. Example: \"https://s3.us-east-1.amazonaws.com\" or \"https://<account>.r2.cloudflarestorage.com\"."
|
|
||||||
},
|
|
||||||
"region": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The region for the storage provider. Example: \"us-east-1\" or \"auto\" for R2."
|
|
||||||
},
|
|
||||||
"forcePathStyle": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Whether to use path-style bucket addressing."
|
|
||||||
},
|
|
||||||
"requestTimeoutMs": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Request timeout in milliseconds."
|
|
||||||
},
|
|
||||||
"minPartSize": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Minimum multipart part size in bytes."
|
|
||||||
},
|
|
||||||
"presign": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Presigned URL behavior configuration.",
|
|
||||||
"properties": {
|
|
||||||
"expiresInSeconds": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Expiration time in seconds for presigned URLs."
|
|
||||||
},
|
|
||||||
"signContentTypeForPut": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Whether to sign Content-Type for presigned PUT."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"credentials": {
|
"credentials": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "The credentials for the s3 compatible storage provider.",
|
"description": "The credentials for the s3 compatible storage provider.",
|
||||||
@@ -382,9 +333,6 @@
|
|||||||
},
|
},
|
||||||
"secretAccessKey": {
|
"secretAccessKey": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
|
||||||
"sessionToken": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -406,42 +354,8 @@
|
|||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "The config for the S3 compatible storage provider.",
|
"description": "The config for the s3 compatible storage provider. directly passed to aws-sdk client.\n@link https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html",
|
||||||
"properties": {
|
"properties": {
|
||||||
"endpoint": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The S3 compatible endpoint. Example: \"https://s3.us-east-1.amazonaws.com\" or \"https://<account>.r2.cloudflarestorage.com\"."
|
|
||||||
},
|
|
||||||
"region": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The region for the storage provider. Example: \"us-east-1\" or \"auto\" for R2."
|
|
||||||
},
|
|
||||||
"forcePathStyle": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Whether to use path-style bucket addressing."
|
|
||||||
},
|
|
||||||
"requestTimeoutMs": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Request timeout in milliseconds."
|
|
||||||
},
|
|
||||||
"minPartSize": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Minimum multipart part size in bytes."
|
|
||||||
},
|
|
||||||
"presign": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Presigned URL behavior configuration.",
|
|
||||||
"properties": {
|
|
||||||
"expiresInSeconds": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Expiration time in seconds for presigned URLs."
|
|
||||||
},
|
|
||||||
"signContentTypeForPut": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Whether to sign Content-Type for presigned PUT."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"credentials": {
|
"credentials": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "The credentials for the s3 compatible storage provider.",
|
"description": "The credentials for the s3 compatible storage provider.",
|
||||||
@@ -451,9 +365,6 @@
|
|||||||
},
|
},
|
||||||
"secretAccessKey": {
|
"secretAccessKey": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
|
||||||
"sessionToken": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -471,7 +382,7 @@
|
|||||||
},
|
},
|
||||||
"urlPrefix": {
|
"urlPrefix": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The custom domain URL prefix for the cloudflare r2 storage provider.\nWhen `enabled=true` and `urlPrefix` + `signKey` are provided, the server will:\n- Redirect GET requests to this custom domain with an HMAC token.\n- Return upload URLs under `/api/storage/*` for uploads.\nPresigned/upload proxy TTL is 1 hour.\nsee https://developers.cloudflare.com/waf/custom-rules/use-cases/configure-token-authentication/ to configure it.\nExample value: \"https://storage.example.com\"\nExample rule: is_timed_hmac_valid_v0(\"your_secret\", http.request.uri, 10800, http.request.timestamp.sec, 6)"
|
"description": "The presigned url prefix for the cloudflare r2 storage provider.\nsee https://developers.cloudflare.com/waf/custom-rules/use-cases/configure-token-authentication/ to configure it.\nExample value: \"https://storage.example.com\"\nExample rule: is_timed_hmac_valid_v0(\"your_secret\", http.request.uri, 10800, http.request.timestamp.sec, 6)"
|
||||||
},
|
},
|
||||||
"signKey": {
|
"signKey": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -532,42 +443,8 @@
|
|||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "The config for the S3 compatible storage provider.",
|
"description": "The config for the s3 compatible storage provider. directly passed to aws-sdk client.\n@link https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html",
|
||||||
"properties": {
|
"properties": {
|
||||||
"endpoint": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The S3 compatible endpoint. Example: \"https://s3.us-east-1.amazonaws.com\" or \"https://<account>.r2.cloudflarestorage.com\"."
|
|
||||||
},
|
|
||||||
"region": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The region for the storage provider. Example: \"us-east-1\" or \"auto\" for R2."
|
|
||||||
},
|
|
||||||
"forcePathStyle": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Whether to use path-style bucket addressing."
|
|
||||||
},
|
|
||||||
"requestTimeoutMs": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Request timeout in milliseconds."
|
|
||||||
},
|
|
||||||
"minPartSize": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Minimum multipart part size in bytes."
|
|
||||||
},
|
|
||||||
"presign": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Presigned URL behavior configuration.",
|
|
||||||
"properties": {
|
|
||||||
"expiresInSeconds": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Expiration time in seconds for presigned URLs."
|
|
||||||
},
|
|
||||||
"signContentTypeForPut": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Whether to sign Content-Type for presigned PUT."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"credentials": {
|
"credentials": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "The credentials for the s3 compatible storage provider.",
|
"description": "The credentials for the s3 compatible storage provider.",
|
||||||
@@ -577,9 +454,6 @@
|
|||||||
},
|
},
|
||||||
"secretAccessKey": {
|
"secretAccessKey": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
|
||||||
"sessionToken": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -601,42 +475,8 @@
|
|||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "The config for the S3 compatible storage provider.",
|
"description": "The config for the s3 compatible storage provider. directly passed to aws-sdk client.\n@link https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html",
|
||||||
"properties": {
|
"properties": {
|
||||||
"endpoint": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The S3 compatible endpoint. Example: \"https://s3.us-east-1.amazonaws.com\" or \"https://<account>.r2.cloudflarestorage.com\"."
|
|
||||||
},
|
|
||||||
"region": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The region for the storage provider. Example: \"us-east-1\" or \"auto\" for R2."
|
|
||||||
},
|
|
||||||
"forcePathStyle": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Whether to use path-style bucket addressing."
|
|
||||||
},
|
|
||||||
"requestTimeoutMs": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Request timeout in milliseconds."
|
|
||||||
},
|
|
||||||
"minPartSize": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Minimum multipart part size in bytes."
|
|
||||||
},
|
|
||||||
"presign": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Presigned URL behavior configuration.",
|
|
||||||
"properties": {
|
|
||||||
"expiresInSeconds": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Expiration time in seconds for presigned URLs."
|
|
||||||
},
|
|
||||||
"signContentTypeForPut": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Whether to sign Content-Type for presigned PUT."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"credentials": {
|
"credentials": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "The credentials for the s3 compatible storage provider.",
|
"description": "The credentials for the s3 compatible storage provider.",
|
||||||
@@ -646,9 +486,6 @@
|
|||||||
},
|
},
|
||||||
"secretAccessKey": {
|
"secretAccessKey": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
|
||||||
"sessionToken": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -666,7 +503,7 @@
|
|||||||
},
|
},
|
||||||
"urlPrefix": {
|
"urlPrefix": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The custom domain URL prefix for the cloudflare r2 storage provider.\nWhen `enabled=true` and `urlPrefix` + `signKey` are provided, the server will:\n- Redirect GET requests to this custom domain with an HMAC token.\n- Return upload URLs under `/api/storage/*` for uploads.\nPresigned/upload proxy TTL is 1 hour.\nsee https://developers.cloudflare.com/waf/custom-rules/use-cases/configure-token-authentication/ to configure it.\nExample value: \"https://storage.example.com\"\nExample rule: is_timed_hmac_valid_v0(\"your_secret\", http.request.uri, 10800, http.request.timestamp.sec, 6)"
|
"description": "The presigned url prefix for the cloudflare r2 storage provider.\nsee https://developers.cloudflare.com/waf/custom-rules/use-cases/configure-token-authentication/ to configure it.\nExample value: \"https://storage.example.com\"\nExample rule: is_timed_hmac_valid_v0(\"your_secret\", http.request.uri, 10800, http.request.timestamp.sec, 6)"
|
||||||
},
|
},
|
||||||
"signKey": {
|
"signKey": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -743,11 +580,6 @@
|
|||||||
"description": "Multiple hosts the server will accept requests from.\n@default []",
|
"description": "Multiple hosts the server will accept requests from.\n@default []",
|
||||||
"default": []
|
"default": []
|
||||||
},
|
},
|
||||||
"listenAddr": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The address to listen on (e.g., 0.0.0.0 for IPv4, :: for IPv6).\n@default \"0.0.0.0\"\n@environment `LISTEN_ADDR`",
|
|
||||||
"default": "0.0.0.0"
|
|
||||||
},
|
|
||||||
"port": {
|
"port": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"description": "Which port the server will listen on.\n@default 3010\n@environment `AFFINE_SERVER_PORT`",
|
"description": "Which port the server will listen on.\n@default 3010\n@environment `AFFINE_SERVER_PORT`",
|
||||||
@@ -764,6 +596,11 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Configuration for flags module",
|
"description": "Configuration for flags module",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"earlyAccessControl": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Only allow users with early access features to access the app\n@default false",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
"allowGuestDemoWorkspace": {
|
"allowGuestDemoWorkspace": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Whether allow guest users to create demo workspaces.\n@default true",
|
"description": "Whether allow guest users to create demo workspaces.\n@default true",
|
||||||
@@ -782,45 +619,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"telemetry": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Configuration for telemetry module",
|
|
||||||
"properties": {
|
|
||||||
"allowedOrigin": {
|
|
||||||
"type": "array",
|
|
||||||
"description": "Allowed origins for telemetry collection.\n@default [\"localhost\",\"127.0.0.1\"]",
|
|
||||||
"default": [
|
|
||||||
"localhost",
|
|
||||||
"127.0.0.1"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ga4.measurementId": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "GA4 Measurement ID for Measurement Protocol.\n@default \"\"\n@environment `GA4_MEASUREMENT_ID`",
|
|
||||||
"default": ""
|
|
||||||
},
|
|
||||||
"ga4.apiSecret": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "GA4 API secret for Measurement Protocol.\n@default \"\"\n@environment `GA4_API_SECRET`",
|
|
||||||
"default": ""
|
|
||||||
},
|
|
||||||
"dedupe.ttlHours": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Telemetry dedupe TTL in hours.\n@default 24",
|
|
||||||
"default": 24
|
|
||||||
},
|
|
||||||
"dedupe.maxEntries": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Telemetry dedupe max entries.\n@default 100000",
|
|
||||||
"default": 100000
|
|
||||||
},
|
|
||||||
"batch.maxEvents": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Max events per telemetry batch.\n@default 25",
|
|
||||||
"default": 25
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"client": {
|
"client": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Configuration for client module",
|
"description": "Configuration for client module",
|
||||||
@@ -832,108 +630,8 @@
|
|||||||
},
|
},
|
||||||
"versionControl.requiredVersion": {
|
"versionControl.requiredVersion": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Allowed version range of the app that allowed to access the server. Requires 'client/versionControl.enabled' to be true to take effect.\n@default \">=0.25.0\"",
|
"description": "Allowed version range of the app that allowed to access the server. Requires 'client/versionControl.enabled' to be true to take effect.\n@default \">=0.20.0\"",
|
||||||
"default": ">=0.25.0"
|
"default": ">=0.20.0"
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"calendar": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Configuration for calendar module",
|
|
||||||
"properties": {
|
|
||||||
"google": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Google Calendar integration config\n@default {\"enabled\":false,\"clientId\":\"\",\"clientSecret\":\"\",\"externalWebhookUrl\":\"\",\"webhookVerificationToken\":\"\"}\n@link https://developers.google.com/calendar/api/guides/push",
|
|
||||||
"properties": {
|
|
||||||
"enabled": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"clientId": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"clientSecret": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"externalWebhookUrl": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"webhookVerificationToken": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"default": {
|
|
||||||
"enabled": false,
|
|
||||||
"clientId": "",
|
|
||||||
"clientSecret": "",
|
|
||||||
"externalWebhookUrl": "",
|
|
||||||
"webhookVerificationToken": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"caldav": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "CalDAV integration config\n@default {\"enabled\":false,\"allowCustomProvider\":false,\"providers\":[],\"allowInsecureHttp\":false,\"allowedHosts\":[],\"blockPrivateNetwork\":true,\"requestTimeoutMs\":10000,\"maxRedirects\":5}",
|
|
||||||
"properties": {
|
|
||||||
"enabled": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"allowCustomProvider": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"providers": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"label": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"serverUrl": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"authType": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"requiresAppPassword": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"docsUrl": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"allowInsecureHttp": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"allowedHosts": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"blockPrivateNetwork": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"requestTimeoutMs": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"maxRedirects": {
|
|
||||||
"type": "number"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"default": {
|
|
||||||
"enabled": false,
|
|
||||||
"allowCustomProvider": false,
|
|
||||||
"providers": [],
|
|
||||||
"allowInsecureHttp": false,
|
|
||||||
"allowedHosts": [],
|
|
||||||
"blockPrivateNetwork": true,
|
|
||||||
"requestTimeoutMs": 10000,
|
|
||||||
"maxRedirects": 5
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -971,16 +669,16 @@
|
|||||||
},
|
},
|
||||||
"scenarios": {
|
"scenarios": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Use custom models in scenarios and override default settings.\n@default {\"override_enabled\":false,\"scenarios\":{\"audio_transcribing\":\"gemini-2.5-flash\",\"chat\":\"gemini-2.5-flash\",\"embedding\":\"gemini-embedding-001\",\"image\":\"gpt-image-1\",\"rerank\":\"gpt-4.1\",\"coding\":\"claude-sonnet-4-5@20250929\",\"complex_text_generation\":\"gpt-4o-2024-08-06\",\"quick_decision_making\":\"gpt-5-mini\",\"quick_text_generation\":\"gemini-2.5-flash\",\"polish_and_summarize\":\"gemini-2.5-flash\"}}",
|
"description": "Use custom models in scenarios and override default settings.\n@default {\"override_enabled\":false,\"scenarios\":{\"audio_transcribing\":\"gemini-2.5-flash\",\"chat\":\"claude-sonnet-4@20250514\",\"embedding\":\"gemini-embedding-001\",\"image\":\"gpt-image-1\",\"rerank\":\"gpt-4.1\",\"coding\":\"claude-sonnet-4@20250514\",\"complex_text_generation\":\"gpt-4o-2024-08-06\",\"quick_decision_making\":\"gpt-5-mini\",\"quick_text_generation\":\"gemini-2.5-flash\",\"polish_and_summarize\":\"gemini-2.5-flash\"}}",
|
||||||
"default": {
|
"default": {
|
||||||
"override_enabled": false,
|
"override_enabled": false,
|
||||||
"scenarios": {
|
"scenarios": {
|
||||||
"audio_transcribing": "gemini-2.5-flash",
|
"audio_transcribing": "gemini-2.5-flash",
|
||||||
"chat": "gemini-2.5-flash",
|
"chat": "claude-sonnet-4@20250514",
|
||||||
"embedding": "gemini-embedding-001",
|
"embedding": "gemini-embedding-001",
|
||||||
"image": "gpt-image-1",
|
"image": "gpt-image-1",
|
||||||
"rerank": "gpt-4.1",
|
"rerank": "gpt-4.1",
|
||||||
"coding": "claude-sonnet-4-5@20250929",
|
"coding": "claude-sonnet-4@20250514",
|
||||||
"complex_text_generation": "gpt-4o-2024-08-06",
|
"complex_text_generation": "gpt-4o-2024-08-06",
|
||||||
"quick_decision_making": "gpt-5-mini",
|
"quick_decision_making": "gpt-5-mini",
|
||||||
"quick_text_generation": "gemini-2.5-flash",
|
"quick_text_generation": "gemini-2.5-flash",
|
||||||
@@ -1155,42 +853,8 @@
|
|||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "The config for the S3 compatible storage provider.",
|
"description": "The config for the s3 compatible storage provider. directly passed to aws-sdk client.\n@link https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html",
|
||||||
"properties": {
|
"properties": {
|
||||||
"endpoint": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The S3 compatible endpoint. Example: \"https://s3.us-east-1.amazonaws.com\" or \"https://<account>.r2.cloudflarestorage.com\"."
|
|
||||||
},
|
|
||||||
"region": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The region for the storage provider. Example: \"us-east-1\" or \"auto\" for R2."
|
|
||||||
},
|
|
||||||
"forcePathStyle": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Whether to use path-style bucket addressing."
|
|
||||||
},
|
|
||||||
"requestTimeoutMs": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Request timeout in milliseconds."
|
|
||||||
},
|
|
||||||
"minPartSize": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Minimum multipart part size in bytes."
|
|
||||||
},
|
|
||||||
"presign": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Presigned URL behavior configuration.",
|
|
||||||
"properties": {
|
|
||||||
"expiresInSeconds": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Expiration time in seconds for presigned URLs."
|
|
||||||
},
|
|
||||||
"signContentTypeForPut": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Whether to sign Content-Type for presigned PUT."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"credentials": {
|
"credentials": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "The credentials for the s3 compatible storage provider.",
|
"description": "The credentials for the s3 compatible storage provider.",
|
||||||
@@ -1200,9 +864,6 @@
|
|||||||
},
|
},
|
||||||
"secretAccessKey": {
|
"secretAccessKey": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
|
||||||
"sessionToken": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1224,42 +885,8 @@
|
|||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "The config for the S3 compatible storage provider.",
|
"description": "The config for the s3 compatible storage provider. directly passed to aws-sdk client.\n@link https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html",
|
||||||
"properties": {
|
"properties": {
|
||||||
"endpoint": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The S3 compatible endpoint. Example: \"https://s3.us-east-1.amazonaws.com\" or \"https://<account>.r2.cloudflarestorage.com\"."
|
|
||||||
},
|
|
||||||
"region": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The region for the storage provider. Example: \"us-east-1\" or \"auto\" for R2."
|
|
||||||
},
|
|
||||||
"forcePathStyle": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Whether to use path-style bucket addressing."
|
|
||||||
},
|
|
||||||
"requestTimeoutMs": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Request timeout in milliseconds."
|
|
||||||
},
|
|
||||||
"minPartSize": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Minimum multipart part size in bytes."
|
|
||||||
},
|
|
||||||
"presign": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Presigned URL behavior configuration.",
|
|
||||||
"properties": {
|
|
||||||
"expiresInSeconds": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Expiration time in seconds for presigned URLs."
|
|
||||||
},
|
|
||||||
"signContentTypeForPut": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Whether to sign Content-Type for presigned PUT."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"credentials": {
|
"credentials": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "The credentials for the s3 compatible storage provider.",
|
"description": "The credentials for the s3 compatible storage provider.",
|
||||||
@@ -1269,9 +896,6 @@
|
|||||||
},
|
},
|
||||||
"secretAccessKey": {
|
"secretAccessKey": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
|
||||||
"sessionToken": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1289,7 +913,7 @@
|
|||||||
},
|
},
|
||||||
"urlPrefix": {
|
"urlPrefix": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The custom domain URL prefix for the cloudflare r2 storage provider.\nWhen `enabled=true` and `urlPrefix` + `signKey` are provided, the server will:\n- Redirect GET requests to this custom domain with an HMAC token.\n- Return upload URLs under `/api/storage/*` for uploads.\nPresigned/upload proxy TTL is 1 hour.\nsee https://developers.cloudflare.com/waf/custom-rules/use-cases/configure-token-authentication/ to configure it.\nExample value: \"https://storage.example.com\"\nExample rule: is_timed_hmac_valid_v0(\"your_secret\", http.request.uri, 10800, http.request.timestamp.sec, 6)"
|
"description": "The presigned url prefix for the cloudflare r2 storage provider.\nsee https://developers.cloudflare.com/waf/custom-rules/use-cases/configure-token-authentication/ to configure it.\nExample value: \"https://storage.example.com\"\nExample rule: is_timed_hmac_valid_v0(\"your_secret\", http.request.uri, 10800, http.request.timestamp.sec, 6)"
|
||||||
},
|
},
|
||||||
"signKey": {
|
"signKey": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -1469,33 +1093,18 @@
|
|||||||
},
|
},
|
||||||
"apiKey": {
|
"apiKey": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "[Deprecated] Stripe API key. Use payment.stripe.apiKey instead.\n@default \"\"\n@environment `STRIPE_API_KEY`",
|
"description": "Stripe API key to enable payment service.\n@default \"\"\n@environment `STRIPE_API_KEY`",
|
||||||
"default": ""
|
"default": ""
|
||||||
},
|
},
|
||||||
"webhookKey": {
|
"webhookKey": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "[Deprecated] Stripe webhook key. Use payment.stripe.webhookKey instead.\n@default \"\"\n@environment `STRIPE_WEBHOOK_KEY`",
|
"description": "Stripe webhook key to enable payment service.\n@default \"\"\n@environment `STRIPE_WEBHOOK_KEY`",
|
||||||
"default": ""
|
"default": ""
|
||||||
},
|
},
|
||||||
"stripe": {
|
"stripe": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Stripe sdk options and credentials\n@default {\"apiKey\":\"\",\"webhookKey\":\"\"}\n@link https://docs.stripe.com/api",
|
"description": "Stripe sdk options\n@default {}\n@link https://docs.stripe.com/api",
|
||||||
"default": {
|
"default": {}
|
||||||
"apiKey": "",
|
|
||||||
"webhookKey": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"revenuecat": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "RevenueCat integration configs\n@default {\"enabled\":false,\"apiKey\":\"\",\"projectId\":\"\",\"webhookAuth\":\"\",\"environment\":\"production\",\"productMap\":{}}\n@link https://www.revenuecat.com/docs/",
|
|
||||||
"default": {
|
|
||||||
"enabled": false,
|
|
||||||
"apiKey": "",
|
|
||||||
"projectId": "",
|
|
||||||
"webhookAuth": "",
|
|
||||||
"environment": "production",
|
|
||||||
"productMap": {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
.git
|
|
||||||
.github/**/*.md
|
|
||||||
.gitignore
|
|
||||||
|
|
||||||
# Local dependency/build artifacts
|
|
||||||
/node_modules
|
|
||||||
/target
|
|
||||||
|
|
||||||
# Yarn v4 artifacts (not needed for image packaging)
|
|
||||||
/.yarn/cache
|
|
||||||
/.yarn/unplugged
|
|
||||||
/.yarn/install-state.gz
|
|
||||||
/.pnp.*
|
|
||||||
|
|
||||||
# Test artifacts
|
|
||||||
/test-results
|
|
||||||
/playwright-report
|
|
||||||
/coverage
|
|
||||||
/.coverage
|
|
||||||
|
|
||||||
# OS noise
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# Sourcemaps (keep server sourcemap for backend stacktraces)
|
|
||||||
**/*.map
|
|
||||||
!packages/backend/server/dist/main.js.map
|
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
# Editor configuration, see http://editorconfig.org
|
# Editor configuration, see http://editorconfig.org
|
||||||
root = true
|
root = true
|
||||||
|
|
||||||
[*.rs]
|
|
||||||
max_line_length = 120
|
|
||||||
[*]
|
[*]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
|||||||
8
.github/ISSUE_TEMPLATE/BUG-REPORT.yml
vendored
8
.github/ISSUE_TEMPLATE/BUG-REPORT.yml
vendored
@@ -74,11 +74,3 @@ body:
|
|||||||
description: |
|
description: |
|
||||||
Links? References? Anything that will give us more context about the issue you are encountering!
|
Links? References? Anything that will give us more context about the issue you are encountering!
|
||||||
Tip: You can attach images here
|
Tip: You can attach images here
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Is your content generated by AI?
|
|
||||||
description: >
|
|
||||||
(Required) Please confirm that the content you submit was not generated by AI or only minimally edited by AI.
|
|
||||||
If an administrator believes the post contains a large amount of AI-generated content, they may directly close the question.
|
|
||||||
options:
|
|
||||||
- label: I confirm that the content I submitted was **not** generated by AI / **merely contained minimal** AI edits.
|
|
||||||
|
|||||||
9
.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml
vendored
9
.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml
vendored
@@ -2,6 +2,7 @@ name: Feature Request
|
|||||||
description: Suggest a feature or improvement
|
description: Suggest a feature or improvement
|
||||||
title: '[Feature Request]: '
|
title: '[Feature Request]: '
|
||||||
labels: ['feat', 'story']
|
labels: ['feat', 'story']
|
||||||
|
assignees: ['hwangdev97']
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
@@ -34,11 +35,3 @@ body:
|
|||||||
See the AFFiNE [Contributing Guide](https://github.com/toeverything/affine/blob/canary/CONTRIBUTING.md) to get started.
|
See the AFFiNE [Contributing Guide](https://github.com/toeverything/affine/blob/canary/CONTRIBUTING.md) to get started.
|
||||||
options:
|
options:
|
||||||
- label: Yes I'd like to help by submitting a PR!
|
- label: Yes I'd like to help by submitting a PR!
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Is your content generated by AI?
|
|
||||||
description: >
|
|
||||||
(Required) Please confirm that the content you submit was not generated by AI or only minimally edited by AI.
|
|
||||||
If an administrator believes the post contains a large amount of AI-generated content, they may directly close the question.
|
|
||||||
options:
|
|
||||||
- label: I confirm that the content I submitted was **not** generated by AI / **merely contained minimal** AI edits.
|
|
||||||
|
|||||||
6
.github/actions/build-rust/action.yml
vendored
6
.github/actions/build-rust/action.yml
vendored
@@ -75,11 +75,7 @@ runs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
if: ${{ runner.os != 'Windows' && inputs.no-build != 'true' }}
|
if: ${{ runner.os != 'Windows' && inputs.no-build != 'true' }}
|
||||||
run: |
|
run: |
|
||||||
if [[ "${{ inputs.target }}" == "x86_64-unknown-linux-gnu" ]]; then
|
yarn workspace ${{ inputs.package }} build --target ${{ inputs.target }} --use-napi-cross
|
||||||
yarn workspace ${{ inputs.package }} build --target ${{ inputs.target }}
|
|
||||||
else
|
|
||||||
yarn workspace ${{ inputs.package }} build --target ${{ inputs.target }} --use-napi-cross
|
|
||||||
fi
|
|
||||||
env:
|
env:
|
||||||
DEBUG: 'napi:*'
|
DEBUG: 'napi:*'
|
||||||
|
|
||||||
|
|||||||
101
.github/actions/deploy/deploy.mjs
vendored
101
.github/actions/deploy/deploy.mjs
vendored
@@ -25,30 +25,47 @@ const buildType = BUILD_TYPE || 'canary';
|
|||||||
|
|
||||||
const isProduction = buildType === 'stable';
|
const isProduction = buildType === 'stable';
|
||||||
const isBeta = buildType === 'beta';
|
const isBeta = buildType === 'beta';
|
||||||
const isCanary = buildType === 'canary';
|
|
||||||
const isInternal = buildType === 'internal';
|
const isInternal = buildType === 'internal';
|
||||||
const isSpotEnabled = isBeta || isCanary;
|
|
||||||
|
|
||||||
const replicaConfig = {
|
const replicaConfig = {
|
||||||
stable: {
|
stable: {
|
||||||
front: Number(process.env.PRODUCTION_FRONT_REPLICA) || 2,
|
web: 2,
|
||||||
graphql: Number(process.env.PRODUCTION_GRAPHQL_REPLICA) || 2,
|
graphql: Number(process.env.PRODUCTION_GRAPHQL_REPLICA) || 2,
|
||||||
|
sync: Number(process.env.PRODUCTION_SYNC_REPLICA) || 2,
|
||||||
|
renderer: Number(process.env.PRODUCTION_RENDERER_REPLICA) || 2,
|
||||||
|
doc: Number(process.env.PRODUCTION_DOC_REPLICA) || 2,
|
||||||
},
|
},
|
||||||
beta: {
|
beta: {
|
||||||
front: Number(process.env.BETA_FRONT_REPLICA) || 1,
|
web: 1,
|
||||||
graphql: Number(process.env.BETA_GRAPHQL_REPLICA) || 1,
|
graphql: Number(process.env.BETA_GRAPHQL_REPLICA) || 1,
|
||||||
|
sync: Number(process.env.BETA_SYNC_REPLICA) || 1,
|
||||||
|
renderer: Number(process.env.BETA_RENDERER_REPLICA) || 1,
|
||||||
|
doc: Number(process.env.BETA_DOC_REPLICA) || 1,
|
||||||
|
},
|
||||||
|
canary: {
|
||||||
|
web: 1,
|
||||||
|
graphql: 1,
|
||||||
|
sync: 1,
|
||||||
|
renderer: 1,
|
||||||
|
doc: 1,
|
||||||
},
|
},
|
||||||
canary: { front: 1, graphql: 1 },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const cpuConfig = {
|
const cpuConfig = {
|
||||||
beta: { front: '1', graphql: '1' },
|
beta: {
|
||||||
canary: { front: '500m', graphql: '1' },
|
web: '300m',
|
||||||
};
|
graphql: '1',
|
||||||
|
sync: '1',
|
||||||
const memoryConfig = {
|
doc: '1',
|
||||||
beta: { front: '2Gi', graphql: '1Gi' },
|
renderer: '300m',
|
||||||
canary: { front: '512Mi', graphql: '512Mi' },
|
},
|
||||||
|
canary: {
|
||||||
|
web: '300m',
|
||||||
|
graphql: '1',
|
||||||
|
sync: '1',
|
||||||
|
doc: '1',
|
||||||
|
renderer: '300m',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const createHelmCommand = ({ isDryRun }) => {
|
const createHelmCommand = ({ isDryRun }) => {
|
||||||
@@ -72,47 +89,32 @@ const createHelmCommand = ({ isDryRun }) => {
|
|||||||
`--set-string global.indexer.endpoint="${AFFINE_INDEXER_SEARCH_ENDPOINT}"`,
|
`--set-string global.indexer.endpoint="${AFFINE_INDEXER_SEARCH_ENDPOINT}"`,
|
||||||
`--set-string global.indexer.apiKey="${AFFINE_INDEXER_SEARCH_API_KEY}"`,
|
`--set-string global.indexer.apiKey="${AFFINE_INDEXER_SEARCH_API_KEY}"`,
|
||||||
];
|
];
|
||||||
const cloudSqlNodeSelector = isBeta
|
|
||||||
? `{ \\"iam.gke.io/gke-metadata-server-enabled\\": \\"true\\", \\"cloud.google.com/gke-spot\\": \\"true\\" }`
|
|
||||||
: `{ \\"iam.gke.io/gke-metadata-server-enabled\\": \\"true\\" }`;
|
|
||||||
const serviceAnnotations = [
|
const serviceAnnotations = [
|
||||||
`--set-json front.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
|
`--set-json web.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
|
||||||
`--set-json graphql.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
|
`--set-json graphql.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
|
||||||
|
`--set-json sync.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
|
||||||
|
`--set-json doc.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
|
||||||
].concat(
|
].concat(
|
||||||
isProduction || isBeta || isInternal
|
isProduction || isBeta || isInternal
|
||||||
? [
|
? [
|
||||||
`--set-json front.services.web.annotations="{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }"`,
|
`--set-json web.service.annotations="{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }"`,
|
||||||
`--set-json front.services.sync.annotations="{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }"`,
|
|
||||||
`--set-json front.services.renderer.annotations="{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }"`,
|
|
||||||
`--set-json graphql.service.annotations="{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }"`,
|
`--set-json graphql.service.annotations="{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }"`,
|
||||||
|
`--set-json sync.service.annotations="{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }"`,
|
||||||
`--set-json cloud-sql-proxy.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${CLOUD_SQL_IAM_ACCOUNT}\\" }"`,
|
`--set-json cloud-sql-proxy.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${CLOUD_SQL_IAM_ACCOUNT}\\" }"`,
|
||||||
`--set-json cloud-sql-proxy.nodeSelector="${cloudSqlNodeSelector}"`,
|
`--set-json cloud-sql-proxy.nodeSelector="{ \\"iam.gke.io/gke-metadata-server-enabled\\": \\"true\\" }"`,
|
||||||
]
|
]
|
||||||
: []
|
: []
|
||||||
);
|
);
|
||||||
const spotNodeSelector = `{ \\"cloud.google.com/gke-spot\\": \\"true\\" }`;
|
|
||||||
const spotScheduling = isSpotEnabled
|
|
||||||
? [
|
|
||||||
`--set-json front.nodeSelector="${spotNodeSelector}"`,
|
|
||||||
`--set-json graphql.nodeSelector="${spotNodeSelector}"`,
|
|
||||||
]
|
|
||||||
: [];
|
|
||||||
|
|
||||||
const cpu = cpuConfig[buildType];
|
const cpu = cpuConfig[buildType];
|
||||||
const memory = memoryConfig[buildType];
|
const resources = cpu
|
||||||
let resources = [];
|
? [
|
||||||
if (cpu) {
|
`--set web.resources.requests.cpu="${cpu.web}"`,
|
||||||
resources = resources.concat([
|
`--set graphql.resources.requests.cpu="${cpu.graphql}"`,
|
||||||
`--set front.resources.requests.cpu="${cpu.front}"`,
|
`--set sync.resources.requests.cpu="${cpu.sync}"`,
|
||||||
`--set graphql.resources.requests.cpu="${cpu.graphql}"`,
|
`--set doc.resources.requests.cpu="${cpu.doc}"`,
|
||||||
]);
|
]
|
||||||
}
|
: [];
|
||||||
if (memory) {
|
|
||||||
resources = resources.concat([
|
|
||||||
`--set front.resources.requests.memory="${memory.front}"`,
|
|
||||||
`--set graphql.resources.requests.memory="${memory.graphql}"`,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const replica = replicaConfig[buildType] || replicaConfig.canary;
|
const replica = replicaConfig[buildType] || replicaConfig.canary;
|
||||||
|
|
||||||
@@ -128,7 +130,6 @@ const createHelmCommand = ({ isDryRun }) => {
|
|||||||
.split(',')
|
.split(',')
|
||||||
.map(host => host.trim())
|
.map(host => host.trim())
|
||||||
.filter(host => host);
|
.filter(host => host);
|
||||||
const primaryHost = hosts[0] || '0.0.0.0';
|
|
||||||
const deployCommand = [
|
const deployCommand = [
|
||||||
`helm upgrade --install affine .github/helm/affine`,
|
`helm upgrade --install affine .github/helm/affine`,
|
||||||
`--namespace ${namespace}`,
|
`--namespace ${namespace}`,
|
||||||
@@ -143,14 +144,20 @@ const createHelmCommand = ({ isDryRun }) => {
|
|||||||
`--set-string global.version="${APP_VERSION}"`,
|
`--set-string global.version="${APP_VERSION}"`,
|
||||||
...redisAndPostgres,
|
...redisAndPostgres,
|
||||||
...indexerOptions,
|
...indexerOptions,
|
||||||
`--set front.replicaCount=${replica.front}`,
|
`--set web.replicaCount=${replica.web}`,
|
||||||
`--set-string front.image.tag="${imageTag}"`,
|
`--set-string web.image.tag="${imageTag}"`,
|
||||||
`--set-string front.app.host="${primaryHost}"`,
|
|
||||||
`--set graphql.replicaCount=${replica.graphql}`,
|
`--set graphql.replicaCount=${replica.graphql}`,
|
||||||
`--set-string graphql.image.tag="${imageTag}"`,
|
`--set-string graphql.image.tag="${imageTag}"`,
|
||||||
`--set-string graphql.app.host="${primaryHost}"`,
|
`--set graphql.app.host=${hosts[0]}`,
|
||||||
|
`--set sync.replicaCount=${replica.sync}`,
|
||||||
|
`--set-string sync.image.tag="${imageTag}"`,
|
||||||
|
`--set-string renderer.image.tag="${imageTag}"`,
|
||||||
|
`--set renderer.app.host=${hosts[0]}`,
|
||||||
|
`--set renderer.replicaCount=${replica.renderer}`,
|
||||||
|
`--set-string doc.image.tag="${imageTag}"`,
|
||||||
|
`--set doc.app.host=${hosts[0]}`,
|
||||||
|
`--set doc.replicaCount=${replica.doc}`,
|
||||||
...serviceAnnotations,
|
...serviceAnnotations,
|
||||||
...spotScheduling,
|
|
||||||
...resources,
|
...resources,
|
||||||
`--timeout 10m`,
|
`--timeout 10m`,
|
||||||
flag,
|
flag,
|
||||||
|
|||||||
5
.github/actions/server-test-env/action.yml
vendored
5
.github/actions/server-test-env/action.yml
vendored
@@ -4,6 +4,11 @@ description: 'Prepare Server Test Environment'
|
|||||||
runs:
|
runs:
|
||||||
using: 'composite'
|
using: 'composite'
|
||||||
steps:
|
steps:
|
||||||
|
- name: Bundle @affine/reader
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
yarn affine @affine/reader build
|
||||||
|
|
||||||
- name: Initialize database
|
- name: Initialize database
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
13
.github/deployment/front/Dockerfile
vendored
Normal file
13
.github/deployment/front/Dockerfile
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
FROM openresty/openresty:1.27.1.1-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 ./.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
|
||||||
|
|
||||||
|
RUN mkdir -p /var/log/nginx && \
|
||||||
|
rm /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
CMD ["/usr/local/openresty/bin/openresty", "-g", "daemon off;"]
|
||||||
42
.github/deployment/front/affine.nginx.conf
vendored
Normal file
42
.github/deployment/front/affine.nginx.conf
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
server {
|
||||||
|
listen 8080;
|
||||||
|
location /admin {
|
||||||
|
root /app/;
|
||||||
|
index index.html;
|
||||||
|
try_files $uri/index.html $uri/ $uri /admin/index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
set $app_root_path /app/dist/;
|
||||||
|
set $mobile_root /app/dist/;
|
||||||
|
set_by_lua $affine_env 'return os.getenv("AFFINE_ENV")';
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
.github/deployment/front/nginx.conf
vendored
Normal file
15
.github/deployment/front/nginx.conf
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
worker_processes 4;
|
||||||
|
error_log /var/log/nginx/error.log warn;
|
||||||
|
pcre_jit on;
|
||||||
|
env AFFINE_ENV;
|
||||||
|
events {
|
||||||
|
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;
|
||||||
|
}
|
||||||
21
.github/deployment/node/Dockerfile
vendored
21
.github/deployment/node/Dockerfile
vendored
@@ -1,28 +1,11 @@
|
|||||||
# syntax=docker/dockerfile:1.7
|
FROM node:22-bookworm-slim
|
||||||
|
|
||||||
FROM node:22-bookworm-slim AS assets
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY ./packages/backend/server /app
|
COPY ./packages/backend/server /app
|
||||||
COPY ./packages/frontend/apps/web/dist /app/static
|
COPY ./packages/frontend/apps/web/dist /app/static
|
||||||
COPY ./packages/frontend/admin/dist /app/static/admin
|
COPY ./packages/frontend/admin/dist /app/static/admin
|
||||||
COPY ./packages/frontend/apps/mobile/dist /app/static/mobile
|
COPY ./packages/frontend/apps/mobile/dist /app/static/mobile
|
||||||
|
|
||||||
# Keep server sourcemap for stacktraces, but don't ship frontend/node_modules sourcemaps.
|
|
||||||
ARG TARGETARCH
|
|
||||||
ARG TARGETVARIANT
|
|
||||||
# Needed for Prisma engine resolution (and potential engine download during cleanup).
|
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install -y --no-install-recommends openssl ca-certificates && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
RUN AFFINE_DOCKER_CLEAN=1 TARGETARCH="${TARGETARCH}" TARGETVARIANT="${TARGETVARIANT}" node ./scripts/docker-clean.mjs
|
|
||||||
|
|
||||||
FROM node:22-bookworm-slim
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY --from=assets /app /app
|
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y --no-install-recommends openssl libjemalloc2 && \
|
apt-get install -y --no-install-recommends openssl libjemalloc2 && \
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
@@ -30,6 +13,4 @@ RUN apt-get update && \
|
|||||||
# Enable jemalloc by preloading the library
|
# Enable jemalloc by preloading the library
|
||||||
ENV LD_PRELOAD=libjemalloc.so.2
|
ENV LD_PRELOAD=libjemalloc.so.2
|
||||||
|
|
||||||
EXPOSE 3010
|
|
||||||
|
|
||||||
CMD ["node", "./dist/main.js"]
|
CMD ["node", "./dist/main.js"]
|
||||||
|
|||||||
2
.github/helm/affine/Chart.yaml
vendored
2
.github/helm/affine/Chart.yaml
vendored
@@ -3,4 +3,4 @@ name: affine
|
|||||||
description: AFFiNE cloud chart
|
description: AFFiNE cloud chart
|
||||||
type: application
|
type: application
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
appVersion: "0.26.1"
|
appVersion: "0.22.4"
|
||||||
|
|||||||
2
.github/helm/affine/charts/doc/Chart.yaml
vendored
2
.github/helm/affine/charts/doc/Chart.yaml
vendored
@@ -3,7 +3,7 @@ name: doc
|
|||||||
description: AFFiNE doc server
|
description: AFFiNE doc server
|
||||||
type: application
|
type: application
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
appVersion: "0.26.1"
|
appVersion: "0.22.4"
|
||||||
dependencies:
|
dependencies:
|
||||||
- name: gcloud-sql-proxy
|
- name: gcloud-sql-proxy
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
1. Get the application URL by running these commands:
|
1. Get the application URL by running these commands:
|
||||||
{{- if contains "NodePort" .Values.services.sync.type }}
|
{{- if contains "NodePort" .Values.service.type }}
|
||||||
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ .Values.services.sync.name }})
|
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "doc.fullname" . }})
|
||||||
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
||||||
echo http://$NODE_IP:$NODE_PORT
|
echo http://$NODE_IP:$NODE_PORT
|
||||||
{{- else if contains "LoadBalancer" .Values.services.sync.type }}
|
{{- else if contains "LoadBalancer" .Values.service.type }}
|
||||||
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
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 {{ .Values.services.sync.name }}'
|
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "doc.fullname" . }}'
|
||||||
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ .Values.services.sync.name }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
|
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "doc.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
|
||||||
echo http://$SERVICE_IP:{{ .Values.services.sync.port }}
|
echo http://$SERVICE_IP:{{ .Values.service.port }}
|
||||||
{{- else if contains "ClusterIP" .Values.services.sync.type }}
|
{{- else if contains "ClusterIP" .Values.service.type }}
|
||||||
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "front.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
|
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "doc.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}")
|
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"
|
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||||
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
|
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
|
||||||
63
.github/helm/affine/charts/doc/templates/_helpers.tpl
vendored
Normal file
63
.github/helm/affine/charts/doc/templates/_helpers.tpl
vendored
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
{{/*
|
||||||
|
Expand the name of the chart.
|
||||||
|
*/}}
|
||||||
|
{{- define "doc.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 "doc.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 "doc.chart" -}}
|
||||||
|
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Common labels
|
||||||
|
*/}}
|
||||||
|
{{- define "doc.labels" -}}
|
||||||
|
helm.sh/chart: {{ include "doc.chart" . }}
|
||||||
|
{{ include "doc.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 "doc.selectorLabels" -}}
|
||||||
|
app.kubernetes.io/name: {{ include "doc.name" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create the name of the service account to use
|
||||||
|
*/}}
|
||||||
|
{{- define "doc.serviceAccountName" -}}
|
||||||
|
{{- if .Values.serviceAccount.create }}
|
||||||
|
{{- default (include "doc.fullname" .) .Values.global.docService.name }}
|
||||||
|
{{- else }}
|
||||||
|
{{- default "default" .Values.global.docService.name }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
118
.github/helm/affine/charts/doc/templates/deployment.yaml
vendored
Normal file
118
.github/helm/affine/charts/doc/templates/deployment.yaml
vendored
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: {{ include "doc.fullname" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "doc.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
replicas: {{ .Values.replicaCount }}
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
{{- include "doc.selectorLabels" . | nindent 6 }}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
{{- with .Values.podAnnotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
labels:
|
||||||
|
{{- include "doc.selectorLabels" . | nindent 8 }}
|
||||||
|
spec:
|
||||||
|
{{- with .Values.imagePullSecrets }}
|
||||||
|
imagePullSecrets:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
serviceAccountName: {{ include "doc.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: "{{ .Values.global.deployment.type }}"
|
||||||
|
- name: DEPLOYMENT_PLATFORM
|
||||||
|
value: "{{ .Values.global.deployment.platform }}"
|
||||||
|
- name: SERVER_FLAVOR
|
||||||
|
value: "doc"
|
||||||
|
- 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.host }}:{{ .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_INDEXER_SEARCH_PROVIDER
|
||||||
|
value: "{{ .Values.global.indexer.provider }}"
|
||||||
|
- name: AFFINE_INDEXER_SEARCH_ENDPOINT
|
||||||
|
value: "{{ .Values.global.indexer.endpoint }}"
|
||||||
|
- name: AFFINE_INDEXER_SEARCH_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: indexer
|
||||||
|
key: indexer-apiKey
|
||||||
|
- name: AFFINE_SERVER_PORT
|
||||||
|
value: "{{ .Values.global.docService.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 }}"
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: {{ .Values.global.docService.port }}
|
||||||
|
protocol: TCP
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /info
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
|
||||||
|
timeoutSeconds: {{ .Values.probe.timeoutSeconds }}
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /info
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
|
||||||
|
timeoutSeconds: {{ .Values.probe.timeoutSeconds }}
|
||||||
|
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 }}
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ .Values.global.docService.name }}
|
name: {{ include "doc.fullname" . }}
|
||||||
labels:
|
labels:
|
||||||
{{- include "front.labels" . | nindent 4 }}
|
{{- include "doc.labels" . | nindent 4 }}
|
||||||
{{- with .Values.services.doc.annotations }}
|
{{- with .Values.service.annotations }}
|
||||||
annotations:
|
annotations:
|
||||||
{{- toYaml . | nindent 4 }}
|
{{- toYaml . | nindent 4 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
spec:
|
spec:
|
||||||
type: {{ .Values.services.doc.type }}
|
type: {{ .Values.service.type }}
|
||||||
ports:
|
ports:
|
||||||
- port: {{ .Values.global.docService.port }}
|
- port: {{ .Values.global.docService.port }}
|
||||||
targetPort: http
|
targetPort: http
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
name: http
|
name: http
|
||||||
selector:
|
selector:
|
||||||
{{- include "front.selectorLabels" . | nindent 4 }}
|
{{- include "doc.selectorLabels" . | nindent 4 }}
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ServiceAccount
|
kind: ServiceAccount
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ include "front.serviceAccountName" . }}
|
name: {{ include "doc.serviceAccountName" . }}
|
||||||
labels:
|
labels:
|
||||||
{{- include "front.labels" . | nindent 4 }}
|
{{- include "doc.labels" . | nindent 4 }}
|
||||||
{{- with .Values.serviceAccount.annotations }}
|
{{- with .Values.serviceAccount.annotations }}
|
||||||
annotations:
|
annotations:
|
||||||
{{- toYaml . | nindent 4 }}
|
{{- toYaml . | nindent 4 }}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Pod
|
kind: Pod
|
||||||
metadata:
|
metadata:
|
||||||
name: "{{ include "front.fullname" . }}-test-connection"
|
name: "{{ include "doc.fullname" . }}-test-connection"
|
||||||
labels:
|
labels:
|
||||||
{{- include "front.labels" . | nindent 4 }}
|
{{- include "doc.labels" . | nindent 4 }}
|
||||||
annotations:
|
annotations:
|
||||||
"helm.sh/hook": test
|
"helm.sh/hook": test
|
||||||
spec:
|
spec:
|
||||||
@@ -11,5 +11,5 @@ spec:
|
|||||||
- name: wget
|
- name: wget
|
||||||
image: busybox
|
image: busybox
|
||||||
command: ['wget']
|
command: ['wget']
|
||||||
args: ['{{ .Values.services.sync.name }}:{{ .Values.services.sync.port }}']
|
args: ['{{ include "doc.fullname" . }}:{{ .Values.global.docService.port }}']
|
||||||
restartPolicy: Never
|
restartPolicy: Never
|
||||||
5
.github/helm/affine/charts/doc/values.yaml
vendored
5
.github/helm/affine/charts/doc/values.yaml
vendored
@@ -30,12 +30,9 @@ podSecurityContext:
|
|||||||
fsGroup: 2000
|
fsGroup: 2000
|
||||||
|
|
||||||
resources:
|
resources:
|
||||||
limits:
|
|
||||||
cpu: '1'
|
|
||||||
memory: 4Gi
|
|
||||||
requests:
|
requests:
|
||||||
cpu: '1'
|
cpu: '1'
|
||||||
memory: 2Gi
|
memory: 4Gi
|
||||||
|
|
||||||
probe:
|
probe:
|
||||||
initialDelaySeconds: 20
|
initialDelaySeconds: 20
|
||||||
|
|||||||
@@ -1,118 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: {{ include "front.fullname" . }}
|
|
||||||
labels:
|
|
||||||
{{- include "front.labels" . | nindent 4 }}
|
|
||||||
spec:
|
|
||||||
replicas: {{ .Values.replicaCount }}
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
{{- include "front.selectorLabels" . | nindent 6 }}
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
{{- with .Values.podAnnotations }}
|
|
||||||
annotations:
|
|
||||||
{{- toYaml . | nindent 8 }}
|
|
||||||
{{- end }}
|
|
||||||
labels:
|
|
||||||
{{- include "front.selectorLabels" . | nindent 8 }}
|
|
||||||
spec:
|
|
||||||
{{- with .Values.imagePullSecrets }}
|
|
||||||
imagePullSecrets:
|
|
||||||
{{- toYaml . | nindent 8 }}
|
|
||||||
{{- end }}
|
|
||||||
serviceAccountName: {{ include "front.serviceAccountName" . }}
|
|
||||||
securityContext:
|
|
||||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
|
||||||
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: "{{ .Values.nodeOptions }}"
|
|
||||||
- name: NO_COLOR
|
|
||||||
value: "1"
|
|
||||||
- name: DEPLOYMENT_TYPE
|
|
||||||
value: "{{ .Values.global.deployment.type }}"
|
|
||||||
- name: DEPLOYMENT_PLATFORM
|
|
||||||
value: "{{ .Values.global.deployment.platform }}"
|
|
||||||
- name: SERVER_FLAVOR
|
|
||||||
value: "front"
|
|
||||||
- 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.host }}:{{ .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_INDEXER_SEARCH_PROVIDER
|
|
||||||
value: "{{ .Values.global.indexer.provider }}"
|
|
||||||
- name: AFFINE_INDEXER_SEARCH_ENDPOINT
|
|
||||||
value: "{{ .Values.global.indexer.endpoint }}"
|
|
||||||
- name: AFFINE_INDEXER_SEARCH_API_KEY
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: indexer
|
|
||||||
key: indexer-apiKey
|
|
||||||
- name: AFFINE_SERVER_PORT
|
|
||||||
value: "{{ .Values.app.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 }}"
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
containerPort: {{ .Values.app.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 }}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: {{ .Values.services.renderer.name }}
|
|
||||||
labels:
|
|
||||||
{{- include "front.labels" . | nindent 4 }}
|
|
||||||
{{- with .Values.services.renderer.annotations }}
|
|
||||||
annotations:
|
|
||||||
{{- toYaml . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
spec:
|
|
||||||
type: {{ .Values.services.renderer.type }}
|
|
||||||
ports:
|
|
||||||
- port: {{ .Values.services.renderer.port }}
|
|
||||||
targetPort: http
|
|
||||||
protocol: TCP
|
|
||||||
name: http
|
|
||||||
selector:
|
|
||||||
{{- include "front.selectorLabels" . | nindent 4 }}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: {{ .Values.services.sync.name }}
|
|
||||||
labels:
|
|
||||||
{{- include "front.labels" . | nindent 4 }}
|
|
||||||
{{- with .Values.services.sync.annotations }}
|
|
||||||
annotations:
|
|
||||||
{{- toYaml . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
spec:
|
|
||||||
type: {{ .Values.services.sync.type }}
|
|
||||||
ports:
|
|
||||||
- port: {{ .Values.services.sync.port }}
|
|
||||||
targetPort: http
|
|
||||||
protocol: TCP
|
|
||||||
name: http
|
|
||||||
selector:
|
|
||||||
{{- include "front.selectorLabels" . | nindent 4 }}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: {{ .Values.services.web.name }}
|
|
||||||
labels:
|
|
||||||
{{- include "front.labels" . | nindent 4 }}
|
|
||||||
{{- with .Values.services.web.annotations }}
|
|
||||||
annotations:
|
|
||||||
{{- toYaml . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
spec:
|
|
||||||
type: {{ .Values.services.web.type }}
|
|
||||||
ports:
|
|
||||||
- port: {{ .Values.services.web.port }}
|
|
||||||
targetPort: http
|
|
||||||
protocol: TCP
|
|
||||||
name: http
|
|
||||||
selector:
|
|
||||||
{{- include "front.selectorLabels" . | nindent 4 }}
|
|
||||||
66
.github/helm/affine/charts/front/values.yaml
vendored
66
.github/helm/affine/charts/front/values.yaml
vendored
@@ -1,66 +0,0 @@
|
|||||||
replicaCount: 1
|
|
||||||
image:
|
|
||||||
repository: ghcr.io/toeverything/affine
|
|
||||||
pullPolicy: IfNotPresent
|
|
||||||
tag: ''
|
|
||||||
|
|
||||||
imagePullSecrets: []
|
|
||||||
nameOverride: ''
|
|
||||||
fullnameOverride: ''
|
|
||||||
# map to NODE_ENV environment variable
|
|
||||||
env: 'production'
|
|
||||||
nodeOptions: '--max-old-space-size=3072'
|
|
||||||
app:
|
|
||||||
# AFFINE_SERVER_PORT
|
|
||||||
port: 3010
|
|
||||||
# AFFINE_SERVER_SUB_PATH
|
|
||||||
path: ''
|
|
||||||
# AFFINE_SERVER_HOST
|
|
||||||
host: '0.0.0.0'
|
|
||||||
https: true
|
|
||||||
serviceAccount:
|
|
||||||
create: true
|
|
||||||
annotations: {}
|
|
||||||
name: 'affine-front'
|
|
||||||
|
|
||||||
podAnnotations: {}
|
|
||||||
|
|
||||||
podSecurityContext:
|
|
||||||
fsGroup: 2000
|
|
||||||
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
cpu: '1'
|
|
||||||
memory: 2Gi
|
|
||||||
requests:
|
|
||||||
cpu: '1'
|
|
||||||
memory: 2Gi
|
|
||||||
|
|
||||||
probe:
|
|
||||||
initialDelaySeconds: 20
|
|
||||||
|
|
||||||
services:
|
|
||||||
sync:
|
|
||||||
name: affine-sync
|
|
||||||
type: ClusterIP
|
|
||||||
port: 3010
|
|
||||||
annotations:
|
|
||||||
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
|
|
||||||
renderer:
|
|
||||||
name: affine-renderer
|
|
||||||
type: ClusterIP
|
|
||||||
port: 3000
|
|
||||||
annotations:
|
|
||||||
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
|
|
||||||
web:
|
|
||||||
name: affine-web
|
|
||||||
type: ClusterIP
|
|
||||||
port: 8080
|
|
||||||
annotations: {}
|
|
||||||
doc:
|
|
||||||
type: ClusterIP
|
|
||||||
annotations: {}
|
|
||||||
|
|
||||||
nodeSelector: {}
|
|
||||||
tolerations: []
|
|
||||||
affinity: {}
|
|
||||||
@@ -3,7 +3,7 @@ name: graphql
|
|||||||
description: AFFiNE GraphQL server
|
description: AFFiNE GraphQL server
|
||||||
type: application
|
type: application
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
appVersion: "0.26.1"
|
appVersion: "0.22.4"
|
||||||
dependencies:
|
dependencies:
|
||||||
- name: gcloud-sql-proxy
|
- name: gcloud-sql-proxy
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
|
|||||||
@@ -27,11 +27,8 @@ podSecurityContext:
|
|||||||
fsGroup: 2000
|
fsGroup: 2000
|
||||||
|
|
||||||
resources:
|
resources:
|
||||||
limits:
|
|
||||||
cpu: '1'
|
|
||||||
memory: 4Gi
|
|
||||||
requests:
|
requests:
|
||||||
cpu: '1'
|
cpu: '2'
|
||||||
memory: 2Gi
|
memory: 2Gi
|
||||||
|
|
||||||
probe:
|
probe:
|
||||||
|
|||||||
11
.github/helm/affine/charts/renderer/Chart.yaml
vendored
Normal file
11
.github/helm/affine/charts/renderer/Chart.yaml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: v2
|
||||||
|
name: renderer
|
||||||
|
description: AFFiNE renderer server
|
||||||
|
type: application
|
||||||
|
version: 0.0.0
|
||||||
|
appVersion: "0.22.4"
|
||||||
|
dependencies:
|
||||||
|
- name: gcloud-sql-proxy
|
||||||
|
version: 0.0.0
|
||||||
|
repository: "file://../gcloud-sql-proxy"
|
||||||
|
condition: .global.database.gcloud.enabled
|
||||||
16
.github/helm/affine/charts/renderer/templates/NOTES.txt
vendored
Normal file
16
.github/helm/affine/charts/renderer/templates/NOTES.txt
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
1. Get the application URL by running these commands:
|
||||||
|
{{- if contains "NodePort" .Values.service.type }}
|
||||||
|
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "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 }}
|
||||||
63
.github/helm/affine/charts/renderer/templates/_helpers.tpl
vendored
Normal file
63
.github/helm/affine/charts/renderer/templates/_helpers.tpl
vendored
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
{{/*
|
||||||
|
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 }}
|
||||||
118
.github/helm/affine/charts/renderer/templates/deployment.yaml
vendored
Normal file
118
.github/helm/affine/charts/renderer/templates/deployment.yaml
vendored
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
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=2048"
|
||||||
|
- name: NO_COLOR
|
||||||
|
value: "1"
|
||||||
|
- name: DEPLOYMENT_TYPE
|
||||||
|
value: "{{ .Values.global.deployment.type }}"
|
||||||
|
- name: DEPLOYMENT_PLATFORM
|
||||||
|
value: "{{ .Values.global.deployment.platform }}"
|
||||||
|
- 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.host }}:{{ .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_INDEXER_SEARCH_PROVIDER
|
||||||
|
value: "{{ .Values.global.indexer.provider }}"
|
||||||
|
- name: AFFINE_INDEXER_SEARCH_ENDPOINT
|
||||||
|
value: "{{ .Values.global.indexer.endpoint }}"
|
||||||
|
- name: AFFINE_INDEXER_SEARCH_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: indexer
|
||||||
|
key: indexer-apiKey
|
||||||
|
- 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: DOC_SERVICE_ENDPOINT
|
||||||
|
value: "http://{{ .Values.global.docService.name }}:{{ .Values.global.docService.port }}"
|
||||||
|
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 }}
|
||||||
19
.github/helm/affine/charts/renderer/templates/service.yaml
vendored
Normal file
19
.github/helm/affine/charts/renderer/templates/service.yaml
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
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 }}
|
||||||
12
.github/helm/affine/charts/renderer/templates/serviceaccount.yaml
vendored
Normal file
12
.github/helm/affine/charts/renderer/templates/serviceaccount.yaml
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{{- 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 }}
|
||||||
15
.github/helm/affine/charts/renderer/templates/tests/test-connection.yaml
vendored
Normal file
15
.github/helm/affine/charts/renderer/templates/tests/test-connection.yaml
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
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
|
||||||
38
.github/helm/affine/charts/renderer/values.yaml
vendored
Normal file
38
.github/helm/affine/charts/renderer/values.yaml
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
replicaCount: 1
|
||||||
|
image:
|
||||||
|
repository: ghcr.io/toeverything/affine
|
||||||
|
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: '1'
|
||||||
|
memory: 2Gi
|
||||||
|
|
||||||
|
probe:
|
||||||
|
initialDelaySeconds: 20
|
||||||
|
|
||||||
|
nodeSelector: {}
|
||||||
|
tolerations: []
|
||||||
|
affinity: {}
|
||||||
23
.github/helm/affine/charts/sync/.helmignore
vendored
Normal file
23
.github/helm/affine/charts/sync/.helmignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Patterns to ignore when building packages.
|
||||||
|
# This supports shell glob matching, relative path matching, and
|
||||||
|
# negation (prefixed with !). Only one pattern per line.
|
||||||
|
.DS_Store
|
||||||
|
# Common VCS dirs
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
.bzr/
|
||||||
|
.bzrignore
|
||||||
|
.hg/
|
||||||
|
.hgignore
|
||||||
|
.svn/
|
||||||
|
# Common backup files
|
||||||
|
*.swp
|
||||||
|
*.bak
|
||||||
|
*.tmp
|
||||||
|
*.orig
|
||||||
|
*~
|
||||||
|
# Various IDEs
|
||||||
|
.project
|
||||||
|
.idea/
|
||||||
|
*.tmproj
|
||||||
|
.vscode/
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
apiVersion: v2
|
apiVersion: v2
|
||||||
name: front
|
name: sync
|
||||||
description: AFFiNE front server
|
description: AFFiNE Sync Server
|
||||||
type: application
|
type: application
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
appVersion: "0.26.1"
|
appVersion: "0.22.4"
|
||||||
dependencies:
|
dependencies:
|
||||||
- name: gcloud-sql-proxy
|
- name: gcloud-sql-proxy
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
16
.github/helm/affine/charts/sync/templates/NOTES.txt
vendored
Normal file
16
.github/helm/affine/charts/sync/templates/NOTES.txt
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
1. Get the application URL by running these commands:
|
||||||
|
{{- if contains "NodePort" .Values.service.type }}
|
||||||
|
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "sync.fullname" . }})
|
||||||
|
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
||||||
|
echo http://$NODE_IP:$NODE_PORT
|
||||||
|
{{- else if contains "LoadBalancer" .Values.service.type }}
|
||||||
|
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
||||||
|
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "sync.fullname" . }}'
|
||||||
|
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "sync.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
|
||||||
|
echo http://$SERVICE_IP:{{ .Values.service.port }}
|
||||||
|
{{- else if contains "ClusterIP" .Values.service.type }}
|
||||||
|
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "sync.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
|
||||||
|
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
|
||||||
|
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||||
|
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
|
||||||
|
{{- end }}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{{/*
|
{{/*
|
||||||
Expand the name of the chart.
|
Expand the name of the chart.
|
||||||
*/}}
|
*/}}
|
||||||
{{- define "front.name" -}}
|
{{- define "sync.name" -}}
|
||||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ 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).
|
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.
|
If release name contains chart name it will be used as a full name.
|
||||||
*/}}
|
*/}}
|
||||||
{{- define "front.fullname" -}}
|
{{- define "sync.fullname" -}}
|
||||||
{{- if .Values.fullnameOverride }}
|
{{- if .Values.fullnameOverride }}
|
||||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||||
{{- else }}
|
{{- else }}
|
||||||
@@ -26,16 +26,16 @@ If release name contains chart name it will be used as a full name.
|
|||||||
{{/*
|
{{/*
|
||||||
Create chart name and version as used by the chart label.
|
Create chart name and version as used by the chart label.
|
||||||
*/}}
|
*/}}
|
||||||
{{- define "front.chart" -}}
|
{{- define "sync.chart" -}}
|
||||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
{{/*
|
{{/*
|
||||||
Common labels
|
Common labels
|
||||||
*/}}
|
*/}}
|
||||||
{{- define "front.labels" -}}
|
{{- define "sync.labels" -}}
|
||||||
helm.sh/chart: {{ include "front.chart" . }}
|
helm.sh/chart: {{ include "sync.chart" . }}
|
||||||
{{ include "front.selectorLabels" . }}
|
{{ include "sync.selectorLabels" . }}
|
||||||
{{- if .Chart.AppVersion }}
|
{{- if .Chart.AppVersion }}
|
||||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
@@ -46,17 +46,17 @@ monitoring: enabled
|
|||||||
{{/*
|
{{/*
|
||||||
Selector labels
|
Selector labels
|
||||||
*/}}
|
*/}}
|
||||||
{{- define "front.selectorLabels" -}}
|
{{- define "sync.selectorLabels" -}}
|
||||||
app.kubernetes.io/name: {{ include "front.name" . }}
|
app.kubernetes.io/name: {{ include "sync.name" . }}
|
||||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
{{/*
|
{{/*
|
||||||
Create the name of the service account to use
|
Create the name of the service account to use
|
||||||
*/}}
|
*/}}
|
||||||
{{- define "front.serviceAccountName" -}}
|
{{- define "sync.serviceAccountName" -}}
|
||||||
{{- if .Values.serviceAccount.create }}
|
{{- if .Values.serviceAccount.create }}
|
||||||
{{- default (include "front.fullname" .) .Values.serviceAccount.name }}
|
{{- default (include "sync.fullname" .) .Values.serviceAccount.name }}
|
||||||
{{- else }}
|
{{- else }}
|
||||||
{{- default "default" .Values.serviceAccount.name }}
|
{{- default "default" .Values.serviceAccount.name }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
112
.github/helm/affine/charts/sync/templates/deployment.yaml
vendored
Normal file
112
.github/helm/affine/charts/sync/templates/deployment.yaml
vendored
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: {{ include "sync.fullname" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "sync.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
replicas: {{ .Values.replicaCount }}
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
{{- include "sync.selectorLabels" . | nindent 6 }}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
{{- with .Values.podAnnotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
labels:
|
||||||
|
{{- include "sync.selectorLabels" . | nindent 8 }}
|
||||||
|
spec:
|
||||||
|
{{- with .Values.imagePullSecrets }}
|
||||||
|
imagePullSecrets:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
serviceAccountName: {{ include "sync.serviceAccountName" . }}
|
||||||
|
securityContext:
|
||||||
|
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||||
|
containers:
|
||||||
|
- name: {{ .Chart.Name }}
|
||||||
|
securityContext:
|
||||||
|
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||||
|
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||||
|
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||||
|
env:
|
||||||
|
- name: 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: "{{ .Values.global.deployment.type }}"
|
||||||
|
- name: DEPLOYMENT_PLATFORM
|
||||||
|
value: "{{ .Values.global.deployment.platform }}"
|
||||||
|
- name: SERVER_FLAVOR
|
||||||
|
value: "sync"
|
||||||
|
- 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.host }}:{{ .Values.global.database.port }}/{{ .Values.global.database.name }}
|
||||||
|
- 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_INDEXER_SEARCH_PROVIDER
|
||||||
|
value: "{{ .Values.global.indexer.provider }}"
|
||||||
|
- name: AFFINE_INDEXER_SEARCH_ENDPOINT
|
||||||
|
value: "{{ .Values.global.indexer.endpoint }}"
|
||||||
|
- name: AFFINE_INDEXER_SEARCH_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: indexer
|
||||||
|
key: indexer-apiKey
|
||||||
|
- name: AFFINE_SERVER_PORT
|
||||||
|
value: "{{ .Values.service.port }}"
|
||||||
|
- name: AFFINE_SERVER_HOST
|
||||||
|
value: "{{ .Values.app.host }}"
|
||||||
|
- name: DOC_SERVICE_ENDPOINT
|
||||||
|
value: "http://{{ .Values.global.docService.name }}:{{ .Values.global.docService.port }}"
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: {{ .Values.service.port }}
|
||||||
|
protocol: TCP
|
||||||
|
livenessProbe:
|
||||||
|
tcpSocket:
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
|
||||||
|
readinessProbe:
|
||||||
|
tcpSocket:
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
|
||||||
|
resources:
|
||||||
|
{{- toYaml .Values.resources | nindent 12 }}
|
||||||
|
{{- with .Values.nodeSelector }}
|
||||||
|
nodeSelector:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.affinity }}
|
||||||
|
affinity:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.tolerations }}
|
||||||
|
tolerations:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
19
.github/helm/affine/charts/sync/templates/service.yaml
vendored
Normal file
19
.github/helm/affine/charts/sync/templates/service.yaml
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: {{ include "sync.fullname" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "sync.labels" . | nindent 4 }}
|
||||||
|
{{- with .Values.service.annotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
type: {{ .Values.service.type }}
|
||||||
|
ports:
|
||||||
|
- port: {{ .Values.service.port }}
|
||||||
|
targetPort: http
|
||||||
|
protocol: TCP
|
||||||
|
name: http
|
||||||
|
selector:
|
||||||
|
{{- include "sync.selectorLabels" . | nindent 4 }}
|
||||||
12
.github/helm/affine/charts/sync/templates/serviceaccount.yaml
vendored
Normal file
12
.github/helm/affine/charts/sync/templates/serviceaccount.yaml
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{{- if .Values.serviceAccount.create -}}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: {{ include "sync.serviceAccountName" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "sync.labels" . | nindent 4 }}
|
||||||
|
{{- with .Values.serviceAccount.annotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
15
.github/helm/affine/charts/sync/templates/tests/test-connection.yaml
vendored
Normal file
15
.github/helm/affine/charts/sync/templates/tests/test-connection.yaml
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: "{{ include "sync.fullname" . }}-test-connection"
|
||||||
|
labels:
|
||||||
|
{{- include "sync.labels" . | nindent 4 }}
|
||||||
|
annotations:
|
||||||
|
"helm.sh/hook": test
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: wget
|
||||||
|
image: busybox
|
||||||
|
command: ['wget']
|
||||||
|
args: ['{{ include "sync.fullname" . }}:{{ .Values.service.port }}']
|
||||||
|
restartPolicy: Never
|
||||||
38
.github/helm/affine/charts/sync/values.yaml
vendored
Normal file
38
.github/helm/affine/charts/sync/values.yaml
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
replicaCount: 1
|
||||||
|
image:
|
||||||
|
repository: ghcr.io/toeverything/affine
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
tag: ''
|
||||||
|
|
||||||
|
imagePullSecrets: []
|
||||||
|
nameOverride: ''
|
||||||
|
fullnameOverride: ''
|
||||||
|
# map to NODE_ENV environment variable
|
||||||
|
env: 'production'
|
||||||
|
app:
|
||||||
|
# AFFINE_SERVER_HOST
|
||||||
|
host: '0.0.0.0'
|
||||||
|
serviceAccount:
|
||||||
|
create: true
|
||||||
|
annotations: {}
|
||||||
|
name: 'affine-sync'
|
||||||
|
|
||||||
|
podAnnotations: {}
|
||||||
|
|
||||||
|
podSecurityContext:
|
||||||
|
fsGroup: 2000
|
||||||
|
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: '2'
|
||||||
|
memory: 4Gi
|
||||||
|
requests:
|
||||||
|
cpu: '1'
|
||||||
|
memory: 2Gi
|
||||||
|
|
||||||
|
probe:
|
||||||
|
initialDelaySeconds: 20
|
||||||
|
|
||||||
|
nodeSelector: {}
|
||||||
|
tolerations: []
|
||||||
|
affinity: {}
|
||||||
23
.github/helm/affine/charts/web/.helmignore
vendored
Normal file
23
.github/helm/affine/charts/web/.helmignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Patterns to ignore when building packages.
|
||||||
|
# This supports shell glob matching, relative path matching, and
|
||||||
|
# negation (prefixed with !). Only one pattern per line.
|
||||||
|
.DS_Store
|
||||||
|
# Common VCS dirs
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
.bzr/
|
||||||
|
.bzrignore
|
||||||
|
.hg/
|
||||||
|
.hgignore
|
||||||
|
.svn/
|
||||||
|
# Common backup files
|
||||||
|
*.swp
|
||||||
|
*.bak
|
||||||
|
*.tmp
|
||||||
|
*.orig
|
||||||
|
*~
|
||||||
|
# Various IDEs
|
||||||
|
.project
|
||||||
|
.idea/
|
||||||
|
*.tmproj
|
||||||
|
.vscode/
|
||||||
6
.github/helm/affine/charts/web/Chart.yaml
vendored
Normal file
6
.github/helm/affine/charts/web/Chart.yaml
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
apiVersion: v2
|
||||||
|
name: web
|
||||||
|
description: A Helm chart for Kubernetes
|
||||||
|
type: application
|
||||||
|
version: 0.0.0
|
||||||
|
appVersion: "0.7.0-canary.18"
|
||||||
16
.github/helm/affine/charts/web/templates/NOTES.txt
vendored
Normal file
16
.github/helm/affine/charts/web/templates/NOTES.txt
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
1. Get the application URL by running these commands:
|
||||||
|
{{- if contains "NodePort" .Values.service.type }}
|
||||||
|
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "web.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 "web.fullname" . }}'
|
||||||
|
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "web.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 "web.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
|
||||||
|
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
|
||||||
|
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||||
|
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
|
||||||
|
{{- end }}
|
||||||
63
.github/helm/affine/charts/web/templates/_helpers.tpl
vendored
Normal file
63
.github/helm/affine/charts/web/templates/_helpers.tpl
vendored
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
{{/*
|
||||||
|
Expand the name of the chart.
|
||||||
|
*/}}
|
||||||
|
{{- define "web.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 "web.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 "web.chart" -}}
|
||||||
|
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Common labels
|
||||||
|
*/}}
|
||||||
|
{{- define "web.labels" -}}
|
||||||
|
helm.sh/chart: {{ include "web.chart" . }}
|
||||||
|
{{ include "web.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 "web.selectorLabels" -}}
|
||||||
|
app.kubernetes.io/name: {{ include "web.name" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create the name of the service account to use
|
||||||
|
*/}}
|
||||||
|
{{- define "web.serviceAccountName" -}}
|
||||||
|
{{- if .Values.serviceAccount.create }}
|
||||||
|
{{- default (include "web.fullname" .) .Values.serviceAccount.name }}
|
||||||
|
{{- else }}
|
||||||
|
{{- default "default" .Values.serviceAccount.name }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
60
.github/helm/affine/charts/web/templates/deployment.yaml
vendored
Normal file
60
.github/helm/affine/charts/web/templates/deployment.yaml
vendored
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: {{ include "web.fullname" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "web.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
replicas: {{ .Values.replicaCount }}
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
{{- include "web.selectorLabels" . | nindent 6 }}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
{{- with .Values.podAnnotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
labels:
|
||||||
|
{{- include "web.selectorLabels" . | nindent 8 }}
|
||||||
|
spec:
|
||||||
|
{{- with .Values.imagePullSecrets }}
|
||||||
|
imagePullSecrets:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
serviceAccountName: {{ include "web.serviceAccountName" . }}
|
||||||
|
containers:
|
||||||
|
- 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 }}
|
||||||
|
protocol: TCP
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
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 }}
|
||||||
15
.github/helm/affine/charts/web/templates/service.yaml
vendored
Normal file
15
.github/helm/affine/charts/web/templates/service.yaml
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: {{ include "web.fullname" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "web.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
type: {{ .Values.service.type }}
|
||||||
|
ports:
|
||||||
|
- port: {{ .Values.service.port }}
|
||||||
|
targetPort: http
|
||||||
|
protocol: TCP
|
||||||
|
name: http
|
||||||
|
selector:
|
||||||
|
{{- include "web.selectorLabels" . | nindent 4 }}
|
||||||
12
.github/helm/affine/charts/web/templates/serviceaccount.yaml
vendored
Normal file
12
.github/helm/affine/charts/web/templates/serviceaccount.yaml
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{{- if .Values.serviceAccount.create -}}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: {{ include "web.serviceAccountName" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "web.labels" . | nindent 4 }}
|
||||||
|
{{- with .Values.serviceAccount.annotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
15
.github/helm/affine/charts/web/templates/tests/test-connection.yaml
vendored
Normal file
15
.github/helm/affine/charts/web/templates/tests/test-connection.yaml
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: "{{ include "web.fullname" . }}-test-connection"
|
||||||
|
labels:
|
||||||
|
{{- include "web.labels" . | nindent 4 }}
|
||||||
|
annotations:
|
||||||
|
"helm.sh/hook": test
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: wget
|
||||||
|
image: busybox
|
||||||
|
command: ['wget']
|
||||||
|
args: ['{{ include "web.fullname" . }}:{{ .Values.service.port }}']
|
||||||
|
restartPolicy: Never
|
||||||
37
.github/helm/affine/charts/web/values.yaml
vendored
Normal file
37
.github/helm/affine/charts/web/values.yaml
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
replicaCount: 1
|
||||||
|
|
||||||
|
image:
|
||||||
|
repository: ghcr.io/toeverything/affine-front
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
tag: ""
|
||||||
|
|
||||||
|
imagePullSecrets: []
|
||||||
|
nameOverride: ""
|
||||||
|
fullnameOverride: ""
|
||||||
|
|
||||||
|
serviceAccount:
|
||||||
|
create: true
|
||||||
|
annotations: {}
|
||||||
|
name: "affine-web"
|
||||||
|
|
||||||
|
podAnnotations: {}
|
||||||
|
|
||||||
|
podSecurityContext:
|
||||||
|
fsGroup: 2000
|
||||||
|
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: '500m'
|
||||||
|
memory: 2Gi
|
||||||
|
requests:
|
||||||
|
cpu: '500m'
|
||||||
|
memory: 2Gi
|
||||||
|
|
||||||
|
nodeSelector: {}
|
||||||
|
|
||||||
|
tolerations: []
|
||||||
|
|
||||||
|
affinity: {}
|
||||||
|
|
||||||
|
probe:
|
||||||
|
initialDelaySeconds: 1
|
||||||
12
.github/helm/affine/templates/ingress.yaml
vendored
12
.github/helm/affine/templates/ingress.yaml
vendored
@@ -44,9 +44,9 @@ spec:
|
|||||||
pathType: Prefix
|
pathType: Prefix
|
||||||
backend:
|
backend:
|
||||||
service:
|
service:
|
||||||
name: {{ $.Values.front.services.sync.name }}
|
name: affine-sync
|
||||||
port:
|
port:
|
||||||
number: {{ $.Values.front.services.sync.port }}
|
number: {{ $.Values.sync.service.port }}
|
||||||
- path: /graphql
|
- path: /graphql
|
||||||
pathType: Prefix
|
pathType: Prefix
|
||||||
backend:
|
backend:
|
||||||
@@ -65,15 +65,15 @@ spec:
|
|||||||
pathType: Prefix
|
pathType: Prefix
|
||||||
backend:
|
backend:
|
||||||
service:
|
service:
|
||||||
name: {{ $.Values.front.services.renderer.name }}
|
name: affine-renderer
|
||||||
port:
|
port:
|
||||||
number: {{ $.Values.front.services.renderer.port }}
|
number: {{ $.Values.renderer.service.port }}
|
||||||
- path: /
|
- path: /
|
||||||
pathType: Prefix
|
pathType: Prefix
|
||||||
backend:
|
backend:
|
||||||
service:
|
service:
|
||||||
name: {{ $.Values.front.services.web.name }}
|
name: affine-web
|
||||||
port:
|
port:
|
||||||
number: {{ $.Values.front.services.web.port }}
|
number: {{ $.Values.web.service.port }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|||||||
46
.github/helm/affine/values.yaml
vendored
46
.github/helm/affine/values.yaml
vendored
@@ -47,25 +47,27 @@ graphql:
|
|||||||
annotations:
|
annotations:
|
||||||
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
|
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
|
||||||
|
|
||||||
front:
|
sync:
|
||||||
services:
|
service:
|
||||||
sync:
|
type: ClusterIP
|
||||||
name: affine-sync
|
port: 3010
|
||||||
type: ClusterIP
|
annotations:
|
||||||
port: 3010
|
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
|
||||||
annotations:
|
|
||||||
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
|
renderer:
|
||||||
renderer:
|
service:
|
||||||
name: affine-renderer
|
type: ClusterIP
|
||||||
type: ClusterIP
|
port: 3000
|
||||||
port: 3000
|
annotations:
|
||||||
annotations:
|
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
|
||||||
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
|
|
||||||
web:
|
doc:
|
||||||
name: affine-web
|
service:
|
||||||
type: ClusterIP
|
type: ClusterIP
|
||||||
port: 8080
|
annotations:
|
||||||
doc:
|
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
|
||||||
type: ClusterIP
|
|
||||||
annotations:
|
web:
|
||||||
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
|
service:
|
||||||
|
type: ClusterIP
|
||||||
|
port: 8080
|
||||||
|
|||||||
6
.github/workflows/auto-labeler.yml
vendored
6
.github/workflows/auto-labeler.yml
vendored
@@ -1,10 +1,6 @@
|
|||||||
name: 'Pull Request Labeler'
|
name: 'Pull Request Labeler'
|
||||||
on:
|
on:
|
||||||
pull_request_target:
|
- pull_request_target
|
||||||
types:
|
|
||||||
- opened
|
|
||||||
- reopened
|
|
||||||
- synchronize
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
triage:
|
triage:
|
||||||
|
|||||||
18
.github/workflows/build-images.yml
vendored
18
.github/workflows/build-images.yml
vendored
@@ -45,6 +45,7 @@ jobs:
|
|||||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||||
PERFSEE_TOKEN: ${{ secrets.PERFSEE_TOKEN }}
|
PERFSEE_TOKEN: ${{ secrets.PERFSEE_TOKEN }}
|
||||||
|
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
|
||||||
- name: Upload web artifact
|
- name: Upload web artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -77,6 +78,7 @@ jobs:
|
|||||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||||
PERFSEE_TOKEN: ${{ secrets.PERFSEE_TOKEN }}
|
PERFSEE_TOKEN: ${{ secrets.PERFSEE_TOKEN }}
|
||||||
|
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
|
||||||
- name: Upload admin artifact
|
- name: Upload admin artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -109,6 +111,7 @@ jobs:
|
|||||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||||
PERFSEE_TOKEN: ${{ secrets.PERFSEE_TOKEN }}
|
PERFSEE_TOKEN: ${{ secrets.PERFSEE_TOKEN }}
|
||||||
|
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
|
||||||
- name: Upload mobile artifact
|
- name: Upload mobile artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -184,6 +187,8 @@ jobs:
|
|||||||
path: ./packages/backend/native
|
path: ./packages/backend/native
|
||||||
- name: List server-native files
|
- name: List server-native files
|
||||||
run: ls -alh ./packages/backend/native
|
run: ls -alh ./packages/backend/native
|
||||||
|
- name: Build @affine/reader
|
||||||
|
run: yarn workspace @affine/reader build
|
||||||
- name: Build Server
|
- name: Build Server
|
||||||
run: yarn workspace @affine/server build
|
run: yarn workspace @affine/server build
|
||||||
- name: Upload server dist
|
- name: Upload server dist
|
||||||
@@ -263,7 +268,18 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
app-version: ${{ inputs.app-version }}
|
app-version: ${{ inputs.app-version }}
|
||||||
|
|
||||||
- name: Build backend Dockerfile
|
- 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:${{inputs.build-type}}-${{ inputs.git-short-hash }}
|
||||||
|
|
||||||
|
- name: Build graphql Dockerfile
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
|
|||||||
397
.github/workflows/build-test.yml
vendored
397
.github/workflows/build-test.yml
vendored
@@ -19,7 +19,7 @@ env:
|
|||||||
APP_NAME: affine
|
APP_NAME: affine
|
||||||
AFFINE_ENV: dev
|
AFFINE_ENV: dev
|
||||||
COVERAGE: true
|
COVERAGE: true
|
||||||
MACOSX_DEPLOYMENT_TARGET: '11.6'
|
MACOSX_DEPLOYMENT_TARGET: '10.13'
|
||||||
DEPLOYMENT_TYPE: affine
|
DEPLOYMENT_TYPE: affine
|
||||||
AFFINE_INDEXER_ENABLED: true
|
AFFINE_INDEXER_ENABLED: true
|
||||||
|
|
||||||
@@ -152,6 +152,11 @@ jobs:
|
|||||||
name: server-native.node
|
name: server-native.node
|
||||||
path: ./packages/backend/native
|
path: ./packages/backend/native
|
||||||
|
|
||||||
|
- name: Bundle @affine/reader
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
yarn workspace @affine/reader build
|
||||||
|
|
||||||
- name: Run Check
|
- name: Run Check
|
||||||
run: |
|
run: |
|
||||||
yarn affine init
|
yarn affine init
|
||||||
@@ -182,7 +187,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
shard: [1, 2]
|
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
@@ -210,13 +215,18 @@ jobs:
|
|||||||
e2e-blocksuite-cross-browser-test:
|
e2e-blocksuite-cross-browser-test:
|
||||||
name: E2E BlockSuite Cross Browser Test
|
name: E2E BlockSuite Cross Browser Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
shard: [1, 2]
|
||||||
|
browser: ['chromium', 'firefox', 'webkit']
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
with:
|
with:
|
||||||
playwright-install: true
|
playwright-install: true
|
||||||
playwright-platform: 'chromium,firefox,webkit'
|
playwright-platform: ${{ matrix.browser }}
|
||||||
electron-install: false
|
electron-install: false
|
||||||
full-cache: true
|
full-cache: true
|
||||||
|
|
||||||
@@ -224,64 +234,18 @@ jobs:
|
|||||||
run: yarn workspace @blocksuite/playground build
|
run: yarn workspace @blocksuite/playground build
|
||||||
|
|
||||||
- name: Run playwright tests
|
- name: Run playwright tests
|
||||||
run: |
|
env:
|
||||||
yarn workspace @blocksuite/integration-test test:unit
|
BROWSER: ${{ matrix.browser }}
|
||||||
yarn workspace @affine-test/blocksuite test "cross-platform/" --forbid-only
|
run: yarn workspace @affine-test/blocksuite test "cross-platform/" --forbid-only --shard=${{ matrix.shard }}/${{ strategy.job-total }}
|
||||||
|
|
||||||
- name: Upload test results
|
- name: Upload test results
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: test-results-e2e-bs-cross-browser
|
name: test-results-e2e-bs-cross-browser-${{ matrix.browser }}-${{ matrix.shard }}
|
||||||
path: ./test-results
|
path: ./test-results
|
||||||
if-no-files-found: ignore
|
if-no-files-found: ignore
|
||||||
|
|
||||||
bundler-matrix:
|
|
||||||
name: Bundler Matrix (${{ matrix.bundler }})
|
|
||||||
runs-on: ubuntu-24.04-arm
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
bundler: [webpack, rspack]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: ./.github/actions/setup-node
|
|
||||||
with:
|
|
||||||
playwright-install: false
|
|
||||||
electron-install: false
|
|
||||||
full-cache: true
|
|
||||||
|
|
||||||
- name: Run frontend build matrix
|
|
||||||
env:
|
|
||||||
AFFINE_BUNDLER: ${{ matrix.bundler }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
packages=(
|
|
||||||
"@affine/web"
|
|
||||||
"@affine/mobile"
|
|
||||||
"@affine/ios"
|
|
||||||
"@affine/android"
|
|
||||||
"@affine/admin"
|
|
||||||
"@affine/electron-renderer"
|
|
||||||
)
|
|
||||||
summary="test-results-bundler-${AFFINE_BUNDLER}.txt"
|
|
||||||
: > "$summary"
|
|
||||||
for pkg in "${packages[@]}"; do
|
|
||||||
start=$(date +%s)
|
|
||||||
yarn affine "$pkg" build
|
|
||||||
end=$(date +%s)
|
|
||||||
echo "${pkg},$((end-start))" >> "$summary"
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Upload bundler timing
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: test-results-bundler-${{ matrix.bundler }}
|
|
||||||
path: ./test-results-bundler-${{ matrix.bundler }}.txt
|
|
||||||
if-no-files-found: ignore
|
|
||||||
|
|
||||||
e2e-test:
|
e2e-test:
|
||||||
name: E2E Test
|
name: E2E Test
|
||||||
runs-on: ubuntu-24.04-arm
|
runs-on: ubuntu-24.04-arm
|
||||||
@@ -292,7 +256,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
shard: [1, 2, 3, 4, 5]
|
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
@@ -323,7 +287,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
shard: [1, 2]
|
shard: [1, 2, 3, 4, 5]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
@@ -348,13 +312,13 @@ jobs:
|
|||||||
name: Unit Test
|
name: Unit Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs:
|
needs:
|
||||||
- build-native-linux
|
- build-native
|
||||||
env:
|
env:
|
||||||
DISTRIBUTION: web
|
DISTRIBUTION: web
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
shard: [1, 2, 3]
|
shard: [1, 2, 3, 4, 5]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
@@ -362,7 +326,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
electron-install: true
|
electron-install: true
|
||||||
playwright-install: true
|
playwright-install: true
|
||||||
playwright-platform: 'chromium,firefox,webkit'
|
|
||||||
full-cache: true
|
full-cache: true
|
||||||
|
|
||||||
- name: Download affine.linux-x64-gnu.node
|
- name: Download affine.linux-x64-gnu.node
|
||||||
@@ -383,39 +346,7 @@ jobs:
|
|||||||
name: affine
|
name: affine
|
||||||
fail_ci_if_error: false
|
fail_ci_if_error: false
|
||||||
|
|
||||||
build-native-linux:
|
build-native:
|
||||||
name: Build AFFiNE native (x86_64-unknown-linux-gnu)
|
|
||||||
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/native
|
|
||||||
electron-install: false
|
|
||||||
- name: Setup filename
|
|
||||||
id: filename
|
|
||||||
working-directory: ${{ github.workspace }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
export PLATFORM_ARCH_ABI=$(node -e "console.log(require('@napi-rs/cli').parseTriple('x86_64-unknown-linux-gnu').platformArchABI)")
|
|
||||||
echo "filename=affine.$PLATFORM_ARCH_ABI.node" >> "$GITHUB_OUTPUT"
|
|
||||||
- name: Build AFFiNE native
|
|
||||||
uses: ./.github/actions/build-rust
|
|
||||||
with:
|
|
||||||
target: x86_64-unknown-linux-gnu
|
|
||||||
package: '@affine/native'
|
|
||||||
- name: Upload ${{ steps.filename.outputs.filename }}
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
if: always()
|
|
||||||
with:
|
|
||||||
name: ${{ steps.filename.outputs.filename }}
|
|
||||||
path: ${{ github.workspace }}/packages/frontend/native/${{ steps.filename.outputs.filename }}
|
|
||||||
if-no-files-found: error
|
|
||||||
|
|
||||||
build-native-macos:
|
|
||||||
name: Build AFFiNE native (${{ matrix.spec.target }})
|
name: Build AFFiNE native (${{ matrix.spec.target }})
|
||||||
runs-on: ${{ matrix.spec.os }}
|
runs-on: ${{ matrix.spec.os }}
|
||||||
env:
|
env:
|
||||||
@@ -424,6 +355,7 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
spec:
|
spec:
|
||||||
|
- { os: ubuntu-latest, target: x86_64-unknown-linux-gnu }
|
||||||
- { os: macos-latest, target: x86_64-apple-darwin }
|
- { os: macos-latest, target: x86_64-apple-darwin }
|
||||||
- { os: macos-latest, target: aarch64-apple-darwin }
|
- { os: macos-latest, target: aarch64-apple-darwin }
|
||||||
|
|
||||||
@@ -456,7 +388,7 @@ jobs:
|
|||||||
|
|
||||||
# Split Windows build because it's too slow
|
# Split Windows build because it's too slow
|
||||||
# and other ci jobs required linux native
|
# and other ci jobs required linux native
|
||||||
build-native-windows:
|
build-windows-native:
|
||||||
name: Build AFFiNE native (${{ matrix.spec.target }})
|
name: Build AFFiNE native (${{ matrix.spec.target }})
|
||||||
runs-on: ${{ matrix.spec.os }}
|
runs-on: ${{ matrix.spec.os }}
|
||||||
env:
|
env:
|
||||||
@@ -556,7 +488,7 @@ jobs:
|
|||||||
name: Native Unit Test
|
name: Native Unit Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs:
|
needs:
|
||||||
- build-native-linux
|
- build-native
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
@@ -580,8 +512,8 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
node_index: [0, 1, 2, 3]
|
node_index: [0, 1, 2, 3, 4, 5, 6, 7]
|
||||||
total_nodes: [4]
|
total_nodes: [8]
|
||||||
env:
|
env:
|
||||||
NODE_ENV: test
|
NODE_ENV: test
|
||||||
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||||
@@ -650,6 +582,8 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs:
|
needs:
|
||||||
- build-server-native
|
- build-server-native
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
env:
|
env:
|
||||||
NODE_ENV: test
|
NODE_ENV: test
|
||||||
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||||
@@ -869,6 +803,49 @@ jobs:
|
|||||||
name: fuzz-artifact
|
name: fuzz-artifact
|
||||||
path: packages/common/y-octo/utils/fuzz/artifacts/**/*
|
path: packages/common/y-octo/utils/fuzz/artifacts/**/*
|
||||||
|
|
||||||
|
y-octo-binding-test:
|
||||||
|
name: y-octo binding test on ${{ matrix.settings.target }}
|
||||||
|
runs-on: ${{ matrix.settings.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
settings:
|
||||||
|
- { target: 'x86_64-unknown-linux-gnu', os: 'ubuntu-latest' }
|
||||||
|
- { target: 'aarch64-unknown-linux-gnu', os: 'ubuntu-24.04-arm' }
|
||||||
|
- { target: 'x86_64-apple-darwin', os: 'macos-13' }
|
||||||
|
- { target: 'aarch64-apple-darwin', os: 'macos-latest' }
|
||||||
|
- { target: 'x86_64-pc-windows-msvc', os: 'windows-latest' }
|
||||||
|
- { target: 'aarch64-pc-windows-msvc', os: 'windows-11-arm' }
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: ./.github/actions/setup-node
|
||||||
|
with:
|
||||||
|
extra-flags: workspaces focus @affine-tools/cli @affine/monorepo @y-octo/node
|
||||||
|
electron-install: false
|
||||||
|
- name: Install rustup (Windows 11 ARM)
|
||||||
|
if: matrix.settings.os == 'windows-11-arm'
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
Invoke-WebRequest -Uri "https://static.rust-lang.org/rustup/dist/aarch64-pc-windows-msvc/rustup-init.exe" -OutFile rustup-init.exe
|
||||||
|
.\rustup-init.exe --default-toolchain none -y
|
||||||
|
"$env:USERPROFILE\.cargo\bin" | Out-File -Append -Encoding ascii $env:GITHUB_PATH
|
||||||
|
"CARGO_HOME=$env:USERPROFILE\.cargo" | Out-File -Append -Encoding ascii $env:GITHUB_ENV
|
||||||
|
- name: Install Rust (Windows 11 ARM)
|
||||||
|
if: matrix.settings.os == 'windows-11-arm'
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
rustup install stable
|
||||||
|
rustup target add ${{ matrix.settings.target }}
|
||||||
|
cargo --version
|
||||||
|
- name: Build Rust
|
||||||
|
uses: ./.github/actions/build-rust
|
||||||
|
with:
|
||||||
|
target: ${{ matrix.settings.target }}
|
||||||
|
package: '@y-octo/node'
|
||||||
|
- name: Run tests
|
||||||
|
run: yarn affine @y-octo/node test
|
||||||
|
|
||||||
rust-test:
|
rust-test:
|
||||||
name: Run native tests
|
name: Run native tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -890,51 +867,11 @@ jobs:
|
|||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: cargo nextest run --workspace --exclude affine_server_native --features use-as-lib --release --no-fail-fast
|
run: cargo nextest run --workspace --exclude affine_server_native --features use-as-lib --release --no-fail-fast
|
||||||
|
|
||||||
copilot-test-filter:
|
|
||||||
name: Copilot test filter
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
run-api: ${{ steps.decision.outputs.run_api }}
|
|
||||||
run-e2e: ${{ steps.decision.outputs.run_e2e }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: dorny/paths-filter@v3
|
|
||||||
id: copilot-filter
|
|
||||||
with:
|
|
||||||
filters: |
|
|
||||||
api:
|
|
||||||
- 'packages/backend/server/src/plugins/copilot/**'
|
|
||||||
- 'packages/backend/server/tests/copilot.*'
|
|
||||||
e2e:
|
|
||||||
- 'packages/backend/server/src/plugins/copilot/**'
|
|
||||||
- 'packages/backend/server/tests/copilot.*'
|
|
||||||
- 'packages/frontend/core/src/blocksuite/ai/**'
|
|
||||||
- 'packages/frontend/core/src/modules/workspace-indexer-embedding/**'
|
|
||||||
- 'tests/affine-cloud-copilot/**'
|
|
||||||
|
|
||||||
- name: Decide test scope
|
|
||||||
id: decision
|
|
||||||
run: |
|
|
||||||
if [[ "${{ steps.copilot-filter.outputs.api }}" == "true" ]]; then
|
|
||||||
echo "run_api=true" >> "$GITHUB_OUTPUT"
|
|
||||||
else
|
|
||||||
echo "run_api=false" >> "$GITHUB_OUTPUT"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "${{ steps.copilot-filter.outputs.e2e }}" == "true" ]]; then
|
|
||||||
echo "run_e2e=true" >> "$GITHUB_OUTPUT"
|
|
||||||
else
|
|
||||||
echo "run_e2e=false" >> "$GITHUB_OUTPUT"
|
|
||||||
fi
|
|
||||||
|
|
||||||
copilot-api-test:
|
copilot-api-test:
|
||||||
name: Server Copilot Api Test
|
name: Server Copilot Api Test
|
||||||
if: ${{ needs.copilot-test-filter.outputs.run-api == 'true' }}
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs:
|
needs:
|
||||||
- build-server-native
|
- build-server-native
|
||||||
- copilot-test-filter
|
|
||||||
env:
|
env:
|
||||||
NODE_ENV: test
|
NODE_ENV: test
|
||||||
DISTRIBUTION: web
|
DISTRIBUTION: web
|
||||||
@@ -968,29 +905,53 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- 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: apifilter
|
||||||
|
with:
|
||||||
|
filters: |
|
||||||
|
changed:
|
||||||
|
- 'packages/backend/server/src/plugins/copilot/**'
|
||||||
|
- 'packages/backend/server/tests/copilot.*'
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
|
if: ${{ steps.check-blocksuite-update.outputs.skip != 'true' || steps.apifilter.outputs.changed == 'true' }}
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
with:
|
with:
|
||||||
electron-install: false
|
electron-install: false
|
||||||
full-cache: true
|
full-cache: true
|
||||||
|
|
||||||
- name: Download server-native.node
|
- name: Download server-native.node
|
||||||
|
if: ${{ steps.check-blocksuite-update.outputs.skip != 'true' || steps.apifilter.outputs.changed == 'true' }}
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: server-native.node
|
name: server-native.node
|
||||||
path: ./packages/backend/native
|
path: ./packages/backend/native
|
||||||
|
|
||||||
- name: Prepare Server Test Environment
|
- name: Prepare Server Test Environment
|
||||||
|
if: ${{ steps.check-blocksuite-update.outputs.skip != 'true' || steps.apifilter.outputs.changed == 'true' }}
|
||||||
env:
|
env:
|
||||||
SERVER_CONFIG: ${{ secrets.TEST_SERVER_CONFIG }}
|
SERVER_CONFIG: ${{ secrets.TEST_SERVER_CONFIG }}
|
||||||
uses: ./.github/actions/server-test-env
|
uses: ./.github/actions/server-test-env
|
||||||
|
|
||||||
- name: Run server tests
|
- name: Run server tests
|
||||||
|
if: ${{ steps.check-blocksuite-update.outputs.skip != 'true' || steps.apifilter.outputs.changed == 'true' }}
|
||||||
run: yarn affine @affine/server test:copilot:coverage --forbid-only
|
run: yarn affine @affine/server test:copilot:coverage --forbid-only
|
||||||
env:
|
env:
|
||||||
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
|
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
|
||||||
|
|
||||||
- name: Upload server test coverage results
|
- name: Upload server test coverage results
|
||||||
|
if: ${{ steps.check-blocksuite-update.outputs.skip != 'true' || steps.apifilter.outputs.changed == 'true' }}
|
||||||
uses: codecov/codecov-action@v5
|
uses: codecov/codecov-action@v5
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
@@ -1001,7 +962,6 @@ jobs:
|
|||||||
|
|
||||||
copilot-e2e-test:
|
copilot-e2e-test:
|
||||||
name: Frontend Copilot E2E Test
|
name: Frontend Copilot E2E Test
|
||||||
if: ${{ needs.copilot-test-filter.outputs.run-e2e == 'true' }}
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
DISTRIBUTION: web
|
DISTRIBUTION: web
|
||||||
@@ -1012,11 +972,10 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
shardIndex: [1, 2, 3, 4, 5]
|
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||||
shardTotal: [5]
|
shardTotal: [10]
|
||||||
needs:
|
needs:
|
||||||
- build-server-native
|
- build-server-native
|
||||||
- copilot-test-filter
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: pgvector/pgvector:pg16
|
image: pgvector/pgvector:pg16
|
||||||
@@ -1040,7 +999,30 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- 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: e2efilter
|
||||||
|
with:
|
||||||
|
filters: |
|
||||||
|
changed:
|
||||||
|
- 'packages/backend/server/src/plugins/copilot/**'
|
||||||
|
- 'packages/backend/server/tests/copilot.*'
|
||||||
|
- 'packages/frontend/core/src/blocksuite/ai/**'
|
||||||
|
- 'packages/frontend/core/src/modules/workspace-indexer-embedding/**'
|
||||||
|
- 'tests/affine-cloud-copilot/**'
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
|
if: ${{ steps.check-blocksuite-update.outputs.skip != 'true' || steps.e2efilter.outputs.changed == 'true' }}
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
with:
|
with:
|
||||||
playwright-install: true
|
playwright-install: true
|
||||||
@@ -1049,17 +1031,20 @@ jobs:
|
|||||||
hard-link-nm: false
|
hard-link-nm: false
|
||||||
|
|
||||||
- name: Download server-native.node
|
- name: Download server-native.node
|
||||||
|
if: ${{ steps.check-blocksuite-update.outputs.skip != 'true' || steps.e2efilter.outputs.changed == 'true' }}
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: server-native.node
|
name: server-native.node
|
||||||
path: ./packages/backend/native
|
path: ./packages/backend/native
|
||||||
|
|
||||||
- name: Prepare Server Test Environment
|
- name: Prepare Server Test Environment
|
||||||
|
if: ${{ steps.check-blocksuite-update.outputs.skip != 'true' || steps.e2efilter.outputs.changed == 'true' }}
|
||||||
env:
|
env:
|
||||||
SERVER_CONFIG: ${{ secrets.TEST_SERVER_CONFIG }}
|
SERVER_CONFIG: ${{ secrets.TEST_SERVER_CONFIG }}
|
||||||
uses: ./.github/actions/server-test-env
|
uses: ./.github/actions/server-test-env
|
||||||
|
|
||||||
- name: Run Copilot E2E Test ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
- name: Run Copilot E2E Test ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
||||||
|
if: ${{ steps.check-blocksuite-update.outputs.skip != 'true' || steps.e2efilter.outputs.changed == 'true' }}
|
||||||
uses: ./.github/actions/copilot-test
|
uses: ./.github/actions/copilot-test
|
||||||
with:
|
with:
|
||||||
script: yarn affine @affine-test/affine-cloud-copilot e2e --forbid-only --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
script: yarn affine @affine-test/affine-cloud-copilot e2e --forbid-only --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
||||||
@@ -1069,7 +1054,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs:
|
needs:
|
||||||
- build-server-native
|
- build-server-native
|
||||||
- build-native-linux
|
- build-native
|
||||||
env:
|
env:
|
||||||
DISTRIBUTION: web
|
DISTRIBUTION: web
|
||||||
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||||
@@ -1079,12 +1064,36 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
tests:
|
tests:
|
||||||
- name: 'Cloud E2E Test 1/2'
|
- name: 'Cloud E2E Test 1/10'
|
||||||
shard: 1
|
shard: 1
|
||||||
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=1/2
|
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=1/10
|
||||||
- name: 'Cloud E2E Test 2/2'
|
- name: 'Cloud E2E Test 2/10'
|
||||||
shard: 2
|
shard: 2
|
||||||
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=2/2
|
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=2/10
|
||||||
|
- name: 'Cloud E2E Test 3/10'
|
||||||
|
shard: 3
|
||||||
|
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=3/10
|
||||||
|
- name: 'Cloud E2E Test 4/10'
|
||||||
|
shard: 4
|
||||||
|
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=4/10
|
||||||
|
- name: 'Cloud E2E Test 5/10'
|
||||||
|
shard: 5
|
||||||
|
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=5/10
|
||||||
|
- name: 'Cloud E2E Test 6/10'
|
||||||
|
shard: 6
|
||||||
|
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=6/10
|
||||||
|
- name: 'Cloud E2E Test 7/10'
|
||||||
|
shard: 7
|
||||||
|
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=7/10
|
||||||
|
- name: 'Cloud E2E Test 8/10'
|
||||||
|
shard: 8
|
||||||
|
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=8/10
|
||||||
|
- name: 'Cloud E2E Test 9/10'
|
||||||
|
shard: 9
|
||||||
|
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=9/10
|
||||||
|
- name: 'Cloud E2E Test 10/10'
|
||||||
|
shard: 10
|
||||||
|
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=10/10
|
||||||
- name: 'Cloud Desktop E2E Test'
|
- name: 'Cloud Desktop E2E Test'
|
||||||
shard: desktop
|
shard: desktop
|
||||||
script: |
|
script: |
|
||||||
@@ -1162,9 +1171,7 @@ jobs:
|
|||||||
runs-on: ${{ matrix.spec.os }}
|
runs-on: ${{ matrix.spec.os }}
|
||||||
needs:
|
needs:
|
||||||
- build-electron-renderer
|
- build-electron-renderer
|
||||||
- build-native-linux
|
- build-native
|
||||||
- build-native-macos
|
|
||||||
- build-native-windows
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -1247,6 +1254,84 @@ jobs:
|
|||||||
if: ${{ matrix.spec.test && matrix.spec.os != 'ubuntu-latest' }}
|
if: ${{ matrix.spec.test && matrix.spec.os != 'ubuntu-latest' }}
|
||||||
run: yarn affine @affine-test/affine-desktop e2e
|
run: yarn affine @affine-test/affine-desktop e2e
|
||||||
|
|
||||||
|
- name: Upload test results
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: test-results-e2e-${{ matrix.spec.os }}-${{ matrix.spec.arch }}
|
||||||
|
path: ./test-results
|
||||||
|
if-no-files-found: ignore
|
||||||
|
|
||||||
|
desktop-bundle-check:
|
||||||
|
name: Desktop bundle check (${{ matrix.spec.os }}, ${{ matrix.spec.platform }}, ${{ matrix.spec.arch }}, ${{ matrix.spec.target }}, ${{ matrix.spec.test }})
|
||||||
|
runs-on: ${{ matrix.spec.os }}
|
||||||
|
needs:
|
||||||
|
- build-electron-renderer
|
||||||
|
- build-native
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
spec:
|
||||||
|
- {
|
||||||
|
os: macos-latest,
|
||||||
|
platform: macos,
|
||||||
|
arch: x64,
|
||||||
|
target: x86_64-apple-darwin,
|
||||||
|
test: false,
|
||||||
|
}
|
||||||
|
- {
|
||||||
|
os: macos-latest,
|
||||||
|
platform: macos,
|
||||||
|
arch: arm64,
|
||||||
|
target: aarch64-apple-darwin,
|
||||||
|
test: true,
|
||||||
|
}
|
||||||
|
- {
|
||||||
|
os: ubuntu-latest,
|
||||||
|
platform: linux,
|
||||||
|
arch: x64,
|
||||||
|
target: x86_64-unknown-linux-gnu,
|
||||||
|
test: true,
|
||||||
|
}
|
||||||
|
- {
|
||||||
|
os: windows-latest,
|
||||||
|
platform: windows,
|
||||||
|
arch: x64,
|
||||||
|
target: x86_64-pc-windows-msvc,
|
||||||
|
test: true,
|
||||||
|
}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: ./.github/actions/setup-node
|
||||||
|
timeout-minutes: 10
|
||||||
|
with:
|
||||||
|
extra-flags: workspaces focus @affine/electron @affine/monorepo @affine-test/affine-desktop @affine/nbstore @toeverything/infra
|
||||||
|
playwright-install: true
|
||||||
|
hard-link-nm: false
|
||||||
|
enableScripts: false
|
||||||
|
|
||||||
|
- name: Setup filename
|
||||||
|
id: filename
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
export PLATFORM_ARCH_ABI=$(node -e "console.log(require('@napi-rs/cli').parseTriple('${{ matrix.spec.target }}').platformArchABI)")
|
||||||
|
echo "filename=affine.$PLATFORM_ARCH_ABI.node" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Download ${{ steps.filename.outputs.filename }}
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ steps.filename.outputs.filename }}
|
||||||
|
path: ./packages/frontend/native
|
||||||
|
|
||||||
|
- name: Download web artifact
|
||||||
|
uses: ./.github/actions/download-web
|
||||||
|
with:
|
||||||
|
path: packages/frontend/apps/electron/resources/web-static
|
||||||
|
|
||||||
|
- name: Build Desktop Layers
|
||||||
|
run: yarn affine @affine/electron build
|
||||||
|
|
||||||
- name: Make bundle (macOS)
|
- name: Make bundle (macOS)
|
||||||
if: ${{ matrix.spec.target == 'aarch64-apple-darwin' }}
|
if: ${{ matrix.spec.target == 'aarch64-apple-darwin' }}
|
||||||
env:
|
env:
|
||||||
@@ -1271,7 +1356,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
sudo add-apt-repository universe
|
sudo add-apt-repository universe
|
||||||
sudo apt install -y libfuse2 elfutils flatpak flatpak-builder
|
sudo apt install -y libfuse2 elfutils flatpak flatpak-builder
|
||||||
flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
|
flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
|
||||||
flatpak update
|
flatpak update
|
||||||
# some flatpak deps need git protocol.file.allow
|
# some flatpak deps need git protocol.file.allow
|
||||||
git config --global protocol.file.allow always
|
git config --global protocol.file.allow always
|
||||||
@@ -1286,14 +1371,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
yarn affine @affine/electron node ./scripts/macos-arm64-output-check.ts
|
yarn affine @affine/electron node ./scripts/macos-arm64-output-check.ts
|
||||||
|
|
||||||
- name: Upload test results
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: test-results-e2e-${{ matrix.spec.os }}-${{ matrix.spec.arch }}
|
|
||||||
path: ./test-results
|
|
||||||
if-no-files-found: ignore
|
|
||||||
|
|
||||||
test-done:
|
test-done:
|
||||||
needs:
|
needs:
|
||||||
- analyze
|
- analyze
|
||||||
@@ -1307,22 +1384,22 @@ jobs:
|
|||||||
- e2e-blocksuite-cross-browser-test
|
- e2e-blocksuite-cross-browser-test
|
||||||
- e2e-mobile-test
|
- e2e-mobile-test
|
||||||
- unit-test
|
- unit-test
|
||||||
- build-native-linux
|
- build-native
|
||||||
- build-native-macos
|
- build-windows-native
|
||||||
- build-native-windows
|
|
||||||
- build-server-native
|
- build-server-native
|
||||||
- build-electron-renderer
|
- build-electron-renderer
|
||||||
- native-unit-test
|
- native-unit-test
|
||||||
- miri
|
- miri
|
||||||
- loom
|
- loom
|
||||||
- fuzzing
|
- fuzzing
|
||||||
|
- y-octo-binding-test
|
||||||
- server-test
|
- server-test
|
||||||
- server-e2e-test
|
- server-e2e-test
|
||||||
- rust-test
|
- rust-test
|
||||||
- copilot-test-filter
|
|
||||||
- copilot-api-test
|
- copilot-api-test
|
||||||
- copilot-e2e-test
|
- copilot-e2e-test
|
||||||
- desktop-test
|
- desktop-test
|
||||||
|
- desktop-bundle-check
|
||||||
- cloud-e2e-test
|
- cloud-e2e-test
|
||||||
if: always()
|
if: always()
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
1
.github/workflows/pr-title-lint.yml
vendored
1
.github/workflows/pr-title-lint.yml
vendored
@@ -16,7 +16,6 @@ jobs:
|
|||||||
check-pull-request-title:
|
check-pull-request-title:
|
||||||
name: Check pull request title
|
name: Check pull request title
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.event.action != 'edited' || github.event.changes.title != null }}
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
|
|||||||
225
.github/workflows/release-desktop-platform.yml
vendored
225
.github/workflows/release-desktop-platform.yml
vendored
@@ -1,225 +0,0 @@
|
|||||||
name: Release Desktop Platform
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
build_type:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
app_version:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
git_short_hash:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
runner:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
platform:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
arch:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
target:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
apple_codesign:
|
|
||||||
required: false
|
|
||||||
default: false
|
|
||||||
type: boolean
|
|
||||||
install_linux_deps:
|
|
||||||
required: false
|
|
||||||
default: false
|
|
||||||
type: boolean
|
|
||||||
enable_scripts:
|
|
||||||
required: false
|
|
||||||
default: false
|
|
||||||
type: boolean
|
|
||||||
outputs:
|
|
||||||
files_to_be_signed:
|
|
||||||
description: Files to be signed (Windows only)
|
|
||||||
value: ${{ jobs.build.outputs.files_to_be_signed }}
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
actions: write
|
|
||||||
contents: write
|
|
||||||
security-events: write
|
|
||||||
id-token: write
|
|
||||||
attestations: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ${{ inputs.runner }}
|
|
||||||
outputs:
|
|
||||||
files_to_be_signed: ${{ steps.get_files_to_be_signed.outputs.FILES_TO_BE_SIGNED }}
|
|
||||||
env:
|
|
||||||
BUILD_TYPE: ${{ inputs.build_type }}
|
|
||||||
RELEASE_VERSION: ${{ inputs.app_version }}
|
|
||||||
DEBUG: 'affine:*,napi:*'
|
|
||||||
APP_NAME: affine
|
|
||||||
MACOSX_DEPLOYMENT_TARGET: '12.0'
|
|
||||||
SKIP_GENERATE_ASSETS: 1
|
|
||||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
|
||||||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
|
||||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
|
||||||
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
|
||||||
SENTRY_PROJECT: 'affine'
|
|
||||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
|
||||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
|
||||||
SENTRY_RELEASE: ${{ inputs.app_version }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup Version
|
|
||||||
uses: ./.github/actions/setup-version
|
|
||||||
with:
|
|
||||||
app-version: ${{ inputs.app_version }}
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
timeout-minutes: 10
|
|
||||||
uses: ./.github/actions/setup-node
|
|
||||||
with:
|
|
||||||
extra-flags: workspaces focus @affine/electron @affine/monorepo @affine/nbstore @toeverything/infra
|
|
||||||
hard-link-nm: false
|
|
||||||
nmHoistingLimits: workspaces
|
|
||||||
enableScripts: ${{ inputs.enable_scripts }}
|
|
||||||
|
|
||||||
- name: Build AFFiNE native
|
|
||||||
uses: ./.github/actions/build-rust
|
|
||||||
with:
|
|
||||||
target: ${{ inputs.target }}
|
|
||||||
package: '@affine/native'
|
|
||||||
|
|
||||||
- uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: desktop-web
|
|
||||||
path: packages/frontend/apps/electron/resources/web-static
|
|
||||||
|
|
||||||
- name: Build Desktop Layers
|
|
||||||
run: yarn affine @affine/electron build
|
|
||||||
|
|
||||||
- name: Signing By Apple Developer ID
|
|
||||||
if: ${{ inputs.platform == 'darwin' && inputs.apple_codesign }}
|
|
||||||
uses: apple-actions/import-codesign-certs@v5
|
|
||||||
with:
|
|
||||||
p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
|
|
||||||
p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
|
|
||||||
|
|
||||||
- name: Install additional dependencies on Linux
|
|
||||||
if: ${{ inputs.platform == 'linux' && inputs.install_linux_deps }}
|
|
||||||
run: |
|
|
||||||
df -h
|
|
||||||
sudo add-apt-repository universe
|
|
||||||
sudo apt install -y libfuse2 elfutils flatpak flatpak-builder
|
|
||||||
flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
|
|
||||||
flatpak update
|
|
||||||
# some flatpak deps need git protocol.file.allow
|
|
||||||
git config --global protocol.file.allow always
|
|
||||||
# clean up apt cache to save disk space
|
|
||||||
sudo -E apt-get -y purge azure-cli* zulu* hhvm* llvm* firefox* google* dotnet* aspnetcore* powershell* adoptopenjdk* mysql* php* mongodb* moby* snap* || true
|
|
||||||
sudo -E apt-get -qq autoremove --purge
|
|
||||||
sudo rm -rf /usr/share/dotnet /opt/ghc /opt/hostedtoolcache/CodeQL /usr/local/lib/android
|
|
||||||
sudo apt-get clean
|
|
||||||
rm -rf ~/.cache/yarn ~/.npm
|
|
||||||
df -h
|
|
||||||
|
|
||||||
- name: Remove nbstore node_modules (darwin/linux)
|
|
||||||
if: ${{ inputs.platform != 'win32' }}
|
|
||||||
shell: bash
|
|
||||||
# node_modules of nbstore is not needed for building, and it will make the build process out of memory
|
|
||||||
run: |
|
|
||||||
cargo clean
|
|
||||||
rm -rf packages/frontend/apps/electron/node_modules/@affine/nbstore/node_modules/@blocksuite
|
|
||||||
rm -rf packages/frontend/apps/electron/node_modules/@affine/native/node_modules
|
|
||||||
|
|
||||||
- name: Remove nbstore node_modules (windows)
|
|
||||||
if: ${{ inputs.platform == 'win32' }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
rm -rf packages/frontend/apps/electron/node_modules/@affine/nbstore/node_modules/@blocksuite/affine/node_modules
|
|
||||||
rm -rf packages/frontend/apps/electron/node_modules/@affine/native/node_modules
|
|
||||||
|
|
||||||
- name: make
|
|
||||||
if: ${{ inputs.platform != 'win32' }}
|
|
||||||
run: yarn affine @affine/electron make --platform=${{ inputs.platform }} --arch=${{ inputs.arch }}
|
|
||||||
env:
|
|
||||||
SKIP_WEB_BUILD: 1
|
|
||||||
HOIST_NODE_MODULES: 1
|
|
||||||
NODE_OPTIONS: --max-old-space-size=14384
|
|
||||||
|
|
||||||
- name: package
|
|
||||||
if: ${{ inputs.platform == 'win32' }}
|
|
||||||
run: |
|
|
||||||
yarn affine @affine/electron package --platform=${{ inputs.platform }} --arch=${{ inputs.arch }}
|
|
||||||
env:
|
|
||||||
SKIP_WEB_BUILD: 1
|
|
||||||
HOIST_NODE_MODULES: 1
|
|
||||||
NODE_OPTIONS: --max-old-space-size=14384
|
|
||||||
|
|
||||||
- name: signing DMG
|
|
||||||
if: ${{ inputs.platform == 'darwin' && inputs.apple_codesign }}
|
|
||||||
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: ${{ inputs.platform == 'darwin' }}
|
|
||||||
run: |
|
|
||||||
mkdir -p builds
|
|
||||||
mv packages/frontend/apps/electron/out/*/make/*.dmg ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-macos-${{ inputs.arch }}.dmg
|
|
||||||
mv packages/frontend/apps/electron/out/*/make/zip/darwin/${{ inputs.arch }}/*.zip ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-macos-${{ inputs.arch }}.zip
|
|
||||||
|
|
||||||
- name: Save artifacts (linux)
|
|
||||||
if: ${{ inputs.platform == 'linux' }}
|
|
||||||
run: |
|
|
||||||
mkdir -p builds
|
|
||||||
mv packages/frontend/apps/electron/out/*/make/zip/linux/${{ inputs.arch }}/*.zip ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.zip
|
|
||||||
mv packages/frontend/apps/electron/out/*/make/*.AppImage ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.appimage
|
|
||||||
mv packages/frontend/apps/electron/out/*/make/deb/${{ inputs.arch }}/*.deb ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.deb
|
|
||||||
mv packages/frontend/apps/electron/out/*/make/flatpak/*/*.flatpak ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.flatpak
|
|
||||||
|
|
||||||
- uses: actions/attest-build-provenance@v2
|
|
||||||
if: ${{ inputs.platform == 'darwin' }}
|
|
||||||
with:
|
|
||||||
subject-path: |
|
|
||||||
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-macos-${{ inputs.arch }}.zip
|
|
||||||
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-macos-${{ inputs.arch }}.dmg
|
|
||||||
|
|
||||||
- uses: actions/attest-build-provenance@v2
|
|
||||||
if: ${{ inputs.platform == 'linux' }}
|
|
||||||
with:
|
|
||||||
subject-path: |
|
|
||||||
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.zip
|
|
||||||
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.appimage
|
|
||||||
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.deb
|
|
||||||
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.flatpak
|
|
||||||
|
|
||||||
- name: Upload Artifact
|
|
||||||
if: ${{ inputs.platform == 'darwin' || inputs.platform == 'linux' }}
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: affine-${{ inputs.platform }}-${{ inputs.arch }}-builds
|
|
||||||
path: builds
|
|
||||||
|
|
||||||
- name: get all files to be signed
|
|
||||||
id: get_files_to_be_signed
|
|
||||||
if: ${{ inputs.platform == 'win32' }}
|
|
||||||
shell: pwsh
|
|
||||||
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=$FILES_TO_BE_SIGNED" >> $env:GITHUB_OUTPUT
|
|
||||||
echo $FILES_TO_BE_SIGNED
|
|
||||||
|
|
||||||
- name: Zip artifacts for faster upload
|
|
||||||
if: ${{ inputs.platform == 'win32' }}
|
|
||||||
shell: pwsh
|
|
||||||
run: Compress-Archive -CompressionLevel Fastest -Path packages/frontend/apps/electron/out/* -DestinationPath archive.zip
|
|
||||||
|
|
||||||
- name: Save packaged artifacts for signing
|
|
||||||
if: ${{ inputs.platform == 'win32' }}
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: packaged-${{ inputs.platform }}-${{ inputs.arch }}
|
|
||||||
path: |
|
|
||||||
archive.zip
|
|
||||||
!**/*.map
|
|
||||||
305
.github/workflows/release-desktop.yml
vendored
305
.github/workflows/release-desktop.yml
vendored
@@ -12,21 +12,6 @@ on:
|
|||||||
git-short-hash:
|
git-short-hash:
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
desktop_macos:
|
|
||||||
description: 'Desktop - macOS'
|
|
||||||
required: false
|
|
||||||
default: true
|
|
||||||
type: boolean
|
|
||||||
desktop_windows:
|
|
||||||
description: 'Desktop - Windows'
|
|
||||||
required: false
|
|
||||||
default: true
|
|
||||||
type: boolean
|
|
||||||
desktop_linux:
|
|
||||||
description: 'Desktop - Linux'
|
|
||||||
required: false
|
|
||||||
default: true
|
|
||||||
type: boolean
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
actions: write
|
actions: write
|
||||||
@@ -40,11 +25,10 @@ env:
|
|||||||
RELEASE_VERSION: ${{ inputs.app-version }}
|
RELEASE_VERSION: ${{ inputs.app-version }}
|
||||||
DEBUG: 'affine:*,napi:*'
|
DEBUG: 'affine:*,napi:*'
|
||||||
APP_NAME: affine
|
APP_NAME: affine
|
||||||
MACOSX_DEPLOYMENT_TARGET: '11.6'
|
MACOSX_DEPLOYMENT_TARGET: '10.13'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
before-make:
|
before-make:
|
||||||
if: ${{ inputs.desktop_macos || inputs.desktop_windows || inputs.desktop_linux }}
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
environment: ${{ inputs.build-type }}
|
environment: ${{ inputs.build-type }}
|
||||||
steps:
|
steps:
|
||||||
@@ -66,6 +50,7 @@ jobs:
|
|||||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||||
SENTRY_RELEASE: ${{ inputs.app-version }}
|
SENTRY_RELEASE: ${{ inputs.app-version }}
|
||||||
RELEASE_VERSION: ${{ inputs.app-version }}
|
RELEASE_VERSION: ${{ inputs.app-version }}
|
||||||
|
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
|
||||||
|
|
||||||
- name: Upload web artifact
|
- name: Upload web artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
@@ -73,8 +58,7 @@ jobs:
|
|||||||
name: desktop-web
|
name: desktop-web
|
||||||
path: packages/frontend/apps/electron/resources/web-static
|
path: packages/frontend/apps/electron/resources/web-static
|
||||||
|
|
||||||
make-distribution-macos:
|
make-distribution:
|
||||||
if: ${{ inputs.desktop_macos }}
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -87,90 +71,223 @@ jobs:
|
|||||||
platform: darwin
|
platform: darwin
|
||||||
arch: arm64
|
arch: arm64
|
||||||
target: aarch64-apple-darwin
|
target: aarch64-apple-darwin
|
||||||
needs: before-make
|
|
||||||
uses: ./.github/workflows/release-desktop-platform.yml
|
|
||||||
secrets: inherit
|
|
||||||
with:
|
|
||||||
build_type: ${{ inputs.build-type }}
|
|
||||||
app_version: ${{ inputs.app-version }}
|
|
||||||
git_short_hash: ${{ inputs.git-short-hash }}
|
|
||||||
runner: ${{ matrix.spec.runner }}
|
|
||||||
platform: ${{ matrix.spec.platform }}
|
|
||||||
arch: ${{ matrix.spec.arch }}
|
|
||||||
target: ${{ matrix.spec.target }}
|
|
||||||
apple_codesign: true
|
|
||||||
|
|
||||||
make-distribution-linux:
|
|
||||||
if: ${{ inputs.desktop_linux }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
spec:
|
|
||||||
- runner: ubuntu-latest
|
- runner: ubuntu-latest
|
||||||
platform: linux
|
platform: linux
|
||||||
arch: x64
|
arch: x64
|
||||||
target: x86_64-unknown-linux-gnu
|
target: x86_64-unknown-linux-gnu
|
||||||
|
runs-on: ${{ matrix.spec.runner }}
|
||||||
needs: before-make
|
needs: before-make
|
||||||
uses: ./.github/workflows/release-desktop-platform.yml
|
environment: ${{ inputs.build-type }}
|
||||||
secrets: inherit
|
env:
|
||||||
with:
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||||
build_type: ${{ inputs.build-type }}
|
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||||
app_version: ${{ inputs.app-version }}
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||||
git_short_hash: ${{ inputs.git-short-hash }}
|
SKIP_GENERATE_ASSETS: 1
|
||||||
runner: ${{ matrix.spec.runner }}
|
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
||||||
platform: ${{ matrix.spec.platform }}
|
SENTRY_PROJECT: 'affine'
|
||||||
arch: ${{ matrix.spec.arch }}
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
target: ${{ matrix.spec.target }}
|
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||||
install_linux_deps: true
|
SENTRY_RELEASE: ${{ inputs.app-version }}
|
||||||
|
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup Version
|
||||||
|
uses: ./.github/actions/setup-version
|
||||||
|
with:
|
||||||
|
app-version: ${{ inputs.app-version }}
|
||||||
|
- name: Setup Node.js
|
||||||
|
timeout-minutes: 10
|
||||||
|
uses: ./.github/actions/setup-node
|
||||||
|
with:
|
||||||
|
extra-flags: workspaces focus @affine/electron @affine/monorepo @affine/nbstore @toeverything/infra
|
||||||
|
hard-link-nm: false
|
||||||
|
nmHoistingLimits: workspaces
|
||||||
|
enableScripts: false
|
||||||
|
- name: Build AFFiNE native
|
||||||
|
uses: ./.github/actions/build-rust
|
||||||
|
with:
|
||||||
|
target: ${{ matrix.spec.target }}
|
||||||
|
package: '@affine/native'
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: desktop-web
|
||||||
|
path: packages/frontend/apps/electron/resources/web-static
|
||||||
|
|
||||||
package-distribution-windows-x64:
|
- name: Build Desktop Layers
|
||||||
if: ${{ inputs.desktop_windows }}
|
run: yarn affine @affine/electron build
|
||||||
needs: before-make
|
|
||||||
uses: ./.github/workflows/release-desktop-platform.yml
|
|
||||||
secrets: inherit
|
|
||||||
with:
|
|
||||||
build_type: ${{ inputs.build-type }}
|
|
||||||
app_version: ${{ inputs.app-version }}
|
|
||||||
git_short_hash: ${{ inputs.git-short-hash }}
|
|
||||||
runner: windows-latest
|
|
||||||
platform: win32
|
|
||||||
arch: x64
|
|
||||||
target: x86_64-pc-windows-msvc
|
|
||||||
enable_scripts: true
|
|
||||||
|
|
||||||
package-distribution-windows-arm64:
|
- name: Signing By Apple Developer ID
|
||||||
if: ${{ inputs.desktop_windows }}
|
if: ${{ matrix.spec.platform == 'darwin' }}
|
||||||
|
uses: apple-actions/import-codesign-certs@v5
|
||||||
|
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: Remove nbstore node_modules
|
||||||
|
shell: bash
|
||||||
|
# node_modules of nbstore is not needed for building, and it will make the build process out of memory
|
||||||
|
run: |
|
||||||
|
rm -rf packages/frontend/apps/electron/node_modules/@affine/nbstore/node_modules/@blocksuite
|
||||||
|
rm -rf packages/frontend/apps/electron/node_modules/@affine/native/node_modules
|
||||||
|
|
||||||
|
- name: make
|
||||||
|
run: yarn affine @affine/electron make --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
|
||||||
|
env:
|
||||||
|
SKIP_WEB_BUILD: 1
|
||||||
|
HOIST_NODE_MODULES: 1
|
||||||
|
NODE_OPTIONS: --max-old-space-size=14384
|
||||||
|
|
||||||
|
- 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-${{ env.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-${{ env.RELEASE_VERSION }}-${{ 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-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ matrix.spec.arch }}.zip
|
||||||
|
mv packages/frontend/apps/electron/out/*/make/*.AppImage ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ matrix.spec.arch }}.appimage
|
||||||
|
mv packages/frontend/apps/electron/out/*/make/deb/${{ matrix.spec.arch }}/*.deb ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ matrix.spec.arch }}.deb
|
||||||
|
mv packages/frontend/apps/electron/out/*/make/flatpak/*/*.flatpak ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ matrix.spec.arch }}.flatpak
|
||||||
|
|
||||||
|
- uses: actions/attest-build-provenance@v2
|
||||||
|
if: ${{ matrix.spec.platform == 'darwin' }}
|
||||||
|
with:
|
||||||
|
subject-path: |
|
||||||
|
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.zip
|
||||||
|
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.dmg
|
||||||
|
|
||||||
|
- uses: actions/attest-build-provenance@v2
|
||||||
|
if: ${{ matrix.spec.platform == 'linux' }}
|
||||||
|
with:
|
||||||
|
subject-path: |
|
||||||
|
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-x64.zip
|
||||||
|
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-x64.appimage
|
||||||
|
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-x64.deb
|
||||||
|
- name: Upload Artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: affine-${{ matrix.spec.platform }}-${{ matrix.spec.arch }}-builds
|
||||||
|
path: builds
|
||||||
|
|
||||||
|
package-distribution-windows:
|
||||||
|
environment: ${{ inputs.build-type }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
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
|
needs: before-make
|
||||||
uses: ./.github/workflows/release-desktop-platform.yml
|
outputs:
|
||||||
secrets: inherit
|
FILES_TO_BE_SIGNED_x64: ${{ steps.get_files_to_be_signed.outputs.FILES_TO_BE_SIGNED_x64 }}
|
||||||
with:
|
FILES_TO_BE_SIGNED_arm64: ${{ steps.get_files_to_be_signed.outputs.FILES_TO_BE_SIGNED_arm64 }}
|
||||||
build_type: ${{ inputs.build-type }}
|
env:
|
||||||
app_version: ${{ inputs.app-version }}
|
SKIP_GENERATE_ASSETS: 1
|
||||||
git_short_hash: ${{ inputs.git-short-hash }}
|
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
||||||
runner: windows-latest
|
SENTRY_PROJECT: 'affine'
|
||||||
platform: win32
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
arch: arm64
|
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||||
target: aarch64-pc-windows-msvc
|
SENTRY_RELEASE: ${{ inputs.app-version }}
|
||||||
enable_scripts: true
|
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup Version
|
||||||
|
uses: ./.github/actions/setup-version
|
||||||
|
with:
|
||||||
|
app-version: ${{ inputs.app-version }}
|
||||||
|
- name: Setup Node.js
|
||||||
|
timeout-minutes: 10
|
||||||
|
uses: ./.github/actions/setup-node
|
||||||
|
with:
|
||||||
|
extra-flags: workspaces focus @affine/electron @affine/monorepo @affine/nbstore @toeverything/infra
|
||||||
|
hard-link-nm: false
|
||||||
|
nmHoistingLimits: workspaces
|
||||||
|
- name: Build AFFiNE native
|
||||||
|
uses: ./.github/actions/build-rust
|
||||||
|
with:
|
||||||
|
target: ${{ matrix.spec.target }}
|
||||||
|
package: '@affine/native'
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: desktop-web
|
||||||
|
path: packages/frontend/apps/electron/resources/web-static
|
||||||
|
|
||||||
|
- name: Build Desktop Layers
|
||||||
|
run: yarn affine @affine/electron build
|
||||||
|
|
||||||
|
- name: Remove nbstore node_modules
|
||||||
|
shell: bash
|
||||||
|
# node_modules of nbstore is not needed for building, and it will make the build process out of memory
|
||||||
|
run: |
|
||||||
|
rm -rf packages/frontend/apps/electron/node_modules/@affine/nbstore/node_modules/@blocksuite/affine/node_modules
|
||||||
|
rm -rf packages/frontend/apps/electron/node_modules/@affine/native/node_modules
|
||||||
|
|
||||||
|
- name: package
|
||||||
|
run: |
|
||||||
|
yarn affine @affine/electron package --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
|
||||||
|
env:
|
||||||
|
SKIP_WEB_BUILD: 1
|
||||||
|
HOIST_NODE_MODULES: 1
|
||||||
|
NODE_OPTIONS: --max-old-space-size=14384
|
||||||
|
|
||||||
|
- 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
|
||||||
|
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
|
||||||
|
|
||||||
|
- name: Save packaged artifacts for signing
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: packaged-${{ matrix.spec.platform }}-${{ matrix.spec.arch }}
|
||||||
|
path: |
|
||||||
|
archive.zip
|
||||||
|
!**/*.map
|
||||||
|
|
||||||
sign-packaged-artifacts-windows_x64:
|
sign-packaged-artifacts-windows_x64:
|
||||||
if: ${{ inputs.desktop_windows }}
|
needs: package-distribution-windows
|
||||||
needs: package-distribution-windows-x64
|
|
||||||
uses: ./.github/workflows/windows-signer.yml
|
uses: ./.github/workflows/windows-signer.yml
|
||||||
with:
|
with:
|
||||||
files: ${{ needs.package-distribution-windows-x64.outputs.files_to_be_signed }}
|
files: ${{ needs.package-distribution-windows.outputs.FILES_TO_BE_SIGNED_x64 }}
|
||||||
artifact-name: packaged-win32-x64
|
artifact-name: packaged-win32-x64
|
||||||
|
|
||||||
sign-packaged-artifacts-windows_arm64:
|
sign-packaged-artifacts-windows_arm64:
|
||||||
if: ${{ inputs.desktop_windows }}
|
needs: package-distribution-windows
|
||||||
needs: package-distribution-windows-arm64
|
|
||||||
uses: ./.github/workflows/windows-signer.yml
|
uses: ./.github/workflows/windows-signer.yml
|
||||||
with:
|
with:
|
||||||
files: ${{ needs.package-distribution-windows-arm64.outputs.files_to_be_signed }}
|
files: ${{ needs.package-distribution-windows.outputs.FILES_TO_BE_SIGNED_arm64 }}
|
||||||
artifact-name: packaged-win32-arm64
|
artifact-name: packaged-win32-arm64
|
||||||
|
|
||||||
make-windows-installer:
|
make-windows-installer:
|
||||||
if: ${{ inputs.desktop_windows }}
|
|
||||||
needs:
|
needs:
|
||||||
- sign-packaged-artifacts-windows_x64
|
- sign-packaged-artifacts-windows_x64
|
||||||
- sign-packaged-artifacts-windows_arm64
|
- sign-packaged-artifacts-windows_arm64
|
||||||
@@ -232,7 +349,6 @@ jobs:
|
|||||||
path: archive.zip
|
path: archive.zip
|
||||||
|
|
||||||
sign-installer-artifacts-windows-x64:
|
sign-installer-artifacts-windows-x64:
|
||||||
if: ${{ inputs.desktop_windows }}
|
|
||||||
needs: make-windows-installer
|
needs: make-windows-installer
|
||||||
uses: ./.github/workflows/windows-signer.yml
|
uses: ./.github/workflows/windows-signer.yml
|
||||||
with:
|
with:
|
||||||
@@ -240,7 +356,6 @@ jobs:
|
|||||||
artifact-name: installer-win32-x64
|
artifact-name: installer-win32-x64
|
||||||
|
|
||||||
sign-installer-artifacts-windows-arm64:
|
sign-installer-artifacts-windows-arm64:
|
||||||
if: ${{ inputs.desktop_windows }}
|
|
||||||
needs: make-windows-installer
|
needs: make-windows-installer
|
||||||
uses: ./.github/workflows/windows-signer.yml
|
uses: ./.github/workflows/windows-signer.yml
|
||||||
with:
|
with:
|
||||||
@@ -248,7 +363,6 @@ jobs:
|
|||||||
artifact-name: installer-win32-arm64
|
artifact-name: installer-win32-arm64
|
||||||
|
|
||||||
finalize-installer-windows:
|
finalize-installer-windows:
|
||||||
if: ${{ inputs.desktop_windows }}
|
|
||||||
needs:
|
needs:
|
||||||
[
|
[
|
||||||
sign-installer-artifacts-windows-x64,
|
sign-installer-artifacts-windows-x64,
|
||||||
@@ -296,18 +410,17 @@ jobs:
|
|||||||
path: builds
|
path: builds
|
||||||
|
|
||||||
release:
|
release:
|
||||||
if: ${{ inputs.desktop_macos && inputs.desktop_linux && inputs.desktop_windows }}
|
needs: [before-make, make-distribution, finalize-installer-windows]
|
||||||
needs:
|
|
||||||
[
|
|
||||||
before-make,
|
|
||||||
make-distribution-macos,
|
|
||||||
make-distribution-linux,
|
|
||||||
finalize-installer-windows,
|
|
||||||
]
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: desktop-web
|
||||||
|
path: web-static
|
||||||
|
- name: Zip web-static
|
||||||
|
run: zip -r web-static.zip web-static
|
||||||
- name: Download Artifacts (macos-x64)
|
- name: Download Artifacts (macos-x64)
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -353,4 +466,6 @@ jobs:
|
|||||||
draft: ${{ inputs.build-type == 'stable' }}
|
draft: ${{ inputs.build-type == 'stable' }}
|
||||||
prerelease: ${{ inputs.build-type != 'stable' }}
|
prerelease: ${{ inputs.build-type != 'stable' }}
|
||||||
tag_name: v${{ env.RELEASE_VERSION}}
|
tag_name: v${{ env.RELEASE_VERSION}}
|
||||||
files: ./release/*
|
files: |
|
||||||
|
./release/*
|
||||||
|
./release/.env.example
|
||||||
|
|||||||
4
.github/workflows/release-mobile.yml
vendored
4
.github/workflows/release-mobile.yml
vendored
@@ -39,6 +39,7 @@ jobs:
|
|||||||
run: yarn affine @affine/ios build
|
run: yarn affine @affine/ios build
|
||||||
env:
|
env:
|
||||||
PUBLIC_PATH: '/'
|
PUBLIC_PATH: '/'
|
||||||
|
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
|
||||||
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
||||||
SENTRY_PROJECT: 'affine'
|
SENTRY_PROJECT: 'affine'
|
||||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
@@ -67,6 +68,7 @@ jobs:
|
|||||||
run: yarn affine @affine/android build
|
run: yarn affine @affine/android build
|
||||||
env:
|
env:
|
||||||
PUBLIC_PATH: '/'
|
PUBLIC_PATH: '/'
|
||||||
|
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
|
||||||
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
||||||
SENTRY_PROJECT: 'affine'
|
SENTRY_PROJECT: 'affine'
|
||||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
@@ -108,7 +110,7 @@ jobs:
|
|||||||
enableScripts: false
|
enableScripts: false
|
||||||
- uses: maxim-lobanov/setup-xcode@v1
|
- uses: maxim-lobanov/setup-xcode@v1
|
||||||
with:
|
with:
|
||||||
xcode-version: 26.2
|
xcode-version: 16.4
|
||||||
- name: Install Swiftformat
|
- name: Install Swiftformat
|
||||||
run: brew install swiftformat
|
run: brew install swiftformat
|
||||||
- name: Cap sync
|
- name: Cap sync
|
||||||
|
|||||||
92
.github/workflows/release.yml
vendored
92
.github/workflows/release.yml
vendored
@@ -11,18 +11,8 @@ on:
|
|||||||
required: true
|
required: true
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
desktop_macos:
|
desktop:
|
||||||
description: 'Desktop - macOS'
|
description: 'Release Desktop?'
|
||||||
required: true
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
desktop_windows:
|
|
||||||
description: 'Desktop - Windows'
|
|
||||||
required: true
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
desktop_linux:
|
|
||||||
description: 'Desktop - Linux'
|
|
||||||
required: true
|
required: true
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
@@ -60,68 +50,6 @@ jobs:
|
|||||||
id: prepare
|
id: prepare
|
||||||
uses: ./.github/actions/prepare-release
|
uses: ./.github/actions/prepare-release
|
||||||
|
|
||||||
canary-gate:
|
|
||||||
name: Canary Gate
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs:
|
|
||||||
- prepare
|
|
||||||
outputs:
|
|
||||||
SHOULD_RELEASE: ${{ steps.decide.outputs.SHOULD_RELEASE }}
|
|
||||||
LAST_CANARY_TAG: ${{ steps.decide.outputs.LAST_CANARY_TAG }}
|
|
||||||
LAST_CANARY_SHA: ${{ steps.decide.outputs.LAST_CANARY_SHA }}
|
|
||||||
steps:
|
|
||||||
- name: Decide whether to release
|
|
||||||
id: decide
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const buildType = '${{ needs.prepare.outputs.BUILD_TYPE }}'
|
|
||||||
if (buildType !== 'canary') {
|
|
||||||
core.setOutput('SHOULD_RELEASE', 'true')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const owner = context.repo.owner
|
|
||||||
const repo = context.repo.repo
|
|
||||||
const currentSha = context.sha
|
|
||||||
const canaryTagRe = /^v\d+\.\d+\.\d+-canary\.[0-9a-f]+$/i
|
|
||||||
|
|
||||||
let page = 1
|
|
||||||
const perPage = 100
|
|
||||||
let lastCanary = null
|
|
||||||
|
|
||||||
while (!lastCanary && page <= 10) {
|
|
||||||
const { data } = await github.rest.repos.listTags({
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
per_page: perPage,
|
|
||||||
page,
|
|
||||||
})
|
|
||||||
|
|
||||||
for (const tag of data) {
|
|
||||||
if (canaryTagRe.test(tag.name)) {
|
|
||||||
lastCanary = tag
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.length < perPage) break
|
|
||||||
page++
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!lastCanary) {
|
|
||||||
core.warning('No canary tags found; proceeding with canary release.')
|
|
||||||
core.setOutput('SHOULD_RELEASE', 'true')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
core.setOutput('LAST_CANARY_TAG', lastCanary.name)
|
|
||||||
core.setOutput('LAST_CANARY_SHA', lastCanary.commit.sha)
|
|
||||||
|
|
||||||
const shouldRelease = lastCanary.commit.sha !== currentSha
|
|
||||||
core.info(`Latest canary tag ${lastCanary.name} -> ${lastCanary.commit.sha}; current ${currentSha}; should_release=${shouldRelease}`)
|
|
||||||
core.setOutput('SHOULD_RELEASE', shouldRelease ? 'true' : 'false')
|
|
||||||
|
|
||||||
cloud:
|
cloud:
|
||||||
name: Release Cloud
|
name: Release Cloud
|
||||||
if: ${{ inputs.web || github.event_name != 'workflow_dispatch' }}
|
if: ${{ inputs.web || github.event_name != 'workflow_dispatch' }}
|
||||||
@@ -136,11 +64,9 @@ jobs:
|
|||||||
|
|
||||||
image:
|
image:
|
||||||
name: Release Docker Image
|
name: Release Docker Image
|
||||||
if: ${{ needs.canary-gate.outputs.SHOULD_RELEASE == 'true' }}
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs:
|
needs:
|
||||||
- prepare
|
- prepare
|
||||||
- canary-gate
|
|
||||||
- cloud
|
- cloud
|
||||||
steps:
|
steps:
|
||||||
- uses: trstringer/manual-approval@v1
|
- uses: trstringer/manual-approval@v1
|
||||||
@@ -148,7 +74,7 @@ jobs:
|
|||||||
name: Wait for approval
|
name: Wait for approval
|
||||||
with:
|
with:
|
||||||
secret: ${{ secrets.GITHUB_TOKEN }}
|
secret: ${{ secrets.GITHUB_TOKEN }}
|
||||||
approvers: darkskygit,pengx17,L-Sun,EYHN
|
approvers: forehalo,fengmk2,darkskygit
|
||||||
minimum-approvals: 1
|
minimum-approvals: 1
|
||||||
fail-on-denial: true
|
fail-on-denial: true
|
||||||
issue-title: Please confirm to release docker image
|
issue-title: Please confirm to release docker image
|
||||||
@@ -176,25 +102,15 @@ jobs:
|
|||||||
|
|
||||||
desktop:
|
desktop:
|
||||||
name: Release Desktop
|
name: Release Desktop
|
||||||
if: >-
|
if: ${{ inputs.desktop || github.event_name != 'workflow_dispatch' }}
|
||||||
${{
|
|
||||||
(github.event_name != 'workflow_dispatch' && needs.canary-gate.outputs.SHOULD_RELEASE == 'true') ||
|
|
||||||
inputs.desktop_macos ||
|
|
||||||
inputs.desktop_windows ||
|
|
||||||
inputs.desktop_linux
|
|
||||||
}}
|
|
||||||
needs:
|
needs:
|
||||||
- prepare
|
- prepare
|
||||||
- canary-gate
|
|
||||||
uses: ./.github/workflows/release-desktop.yml
|
uses: ./.github/workflows/release-desktop.yml
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
with:
|
with:
|
||||||
build-type: ${{ needs.prepare.outputs.BUILD_TYPE }}
|
build-type: ${{ needs.prepare.outputs.BUILD_TYPE }}
|
||||||
app-version: ${{ needs.prepare.outputs.APP_VERSION }}
|
app-version: ${{ needs.prepare.outputs.APP_VERSION }}
|
||||||
git-short-hash: ${{ needs.prepare.outputs.GIT_SHORT_HASH }}
|
git-short-hash: ${{ needs.prepare.outputs.GIT_SHORT_HASH }}
|
||||||
desktop_macos: ${{ github.event_name != 'workflow_dispatch' || inputs.desktop_macos }}
|
|
||||||
desktop_windows: ${{ github.event_name != 'workflow_dispatch' || inputs.desktop_windows }}
|
|
||||||
desktop_linux: ${{ github.event_name != 'workflow_dispatch' || inputs.desktop_linux }}
|
|
||||||
|
|
||||||
mobile:
|
mobile:
|
||||||
name: Release Mobile
|
name: Release Mobile
|
||||||
|
|||||||
72
.github/workflows/sync-i18n.yml
vendored
Normal file
72
.github/workflows/sync-i18n.yml
vendored
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
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
|
||||||
|
id: crowdin
|
||||||
|
uses: crowdin/github-action@v2
|
||||||
|
with:
|
||||||
|
upload_sources: true
|
||||||
|
upload_translations: false
|
||||||
|
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 }}
|
||||||
|
i18n-codegen:
|
||||||
|
needs: synchronize-with-crowdin
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: l10n_crowdin_translations
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: ./.github/actions/setup-node
|
||||||
|
with:
|
||||||
|
electron-install: false
|
||||||
|
full-cache: true
|
||||||
|
|
||||||
|
- name: Run i18n codegen
|
||||||
|
run: yarn affine @affine/i18n build
|
||||||
|
|
||||||
|
- name: Commit changes
|
||||||
|
run: |
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||||
|
git add .
|
||||||
|
git commit -m "chore(i18n): i18n codegen"
|
||||||
|
git push origin l10n_crowdin_translations
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -33,9 +33,6 @@ node_modules
|
|||||||
!.vscode/launch.template.json
|
!.vscode/launch.template.json
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
|
|
||||||
# Kiro
|
|
||||||
.kiro
|
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
/.sass-cache
|
/.sass-cache
|
||||||
/connect.lock
|
/connect.lock
|
||||||
@@ -47,7 +44,6 @@ testem.log
|
|||||||
.pnpm-debug.log
|
.pnpm-debug.log
|
||||||
/typings
|
/typings
|
||||||
tsconfig.tsbuildinfo
|
tsconfig.tsbuildinfo
|
||||||
.context
|
|
||||||
|
|
||||||
# System Files
|
# System Files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
14
.taplo.toml
14
.taplo.toml
@@ -1,11 +1,7 @@
|
|||||||
exclude = [
|
include = ["./*.toml", "./packages/**/*.toml"]
|
||||||
"node_modules/**/*.toml",
|
|
||||||
"target/**/*.toml",
|
|
||||||
"packages/frontend/apps/ios/App/Packages/AffineGraphQL/**/*.toml",
|
|
||||||
]
|
|
||||||
|
|
||||||
# https://taplo.tamasfe.dev/configuration/formatter-options.html
|
|
||||||
[formatting]
|
[formatting]
|
||||||
align_entries = true
|
align_entries = true
|
||||||
indent_tables = true
|
column_width = 180
|
||||||
reorder_keys = true
|
reorder_arrays = true
|
||||||
|
reorder_keys = true
|
||||||
|
|||||||
13
.vscode/settings.template.json
vendored
13
.vscode/settings.template.json
vendored
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"prisma.pinToPrisma6": true,
|
"eslint.packageManager": "yarn",
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"editor.formatOnSaveMode": "file",
|
"editor.formatOnSaveMode": "file",
|
||||||
@@ -14,13 +14,11 @@
|
|||||||
"testid",
|
"testid",
|
||||||
"schemars"
|
"schemars"
|
||||||
],
|
],
|
||||||
"explorer.fileNesting.enabled": true,
|
|
||||||
"explorer.fileNesting.patterns": {
|
"explorer.fileNesting.patterns": {
|
||||||
"*.js": "${capture}.js.map, ${capture}.min.js, ${capture}.d.ts, ${capture}.d.ts.map",
|
"*.js": "${capture}.js.map, ${capture}.min.js, ${capture}.d.ts, ${capture}.d.ts.map",
|
||||||
"package.json": ".browserslist*, .circleci*, .codecov, .commitlint*, .cz-config.js, .czrc, .dlint.json, .dprint.json, .editorconfig, .eslint*, eslint.*, .firebase*, .flowconfig, .github*, .gitlab*, .gitpod*, .huskyrc*, .jslint*, .lighthouserc.*, .lintstagedrc*, .markdownlint*, .mocha*, .node-version, .nodemon*, .npm*, .nvmrc, .pm2*, .pnp.*, .pnpm*, .prettier*, .releaserc*, .sentry*, .stackblitz*, .styleci*, .stylelint*, .tazerc*, .textlint*, .tool-versions, .travis*, .versionrc*, .vscode*, .watchman*, .xo-config*, .yamllint*, .yarnrc*, Procfile, api-extractor.json, apollo.config.*, appveyor*, ava.config.*, azure-pipelines*, bower.json, build.config.*, commitlint*, dangerfile*, dlint.json, dprint.json, firebase.json, grunt*, gulp*, histoire.config.*, jasmine.*, jenkins*, jest.config.*, jsconfig.*, karma*, lerna*, lighthouserc.*, lint-staged*, nest-cli.*, netlify*, nodemon*, nx.*, package-lock.json, package.nls*.json, phpcs.xml, playwright.config.*, pm2.*, pnpm*, prettier*, pullapprove*, puppeteer.config.*, pyrightconfig.json, release-tasks.sh, renovate*, rollup.config.*, stylelint*, tsconfig.*, tsdoc.*, tslint*, tsup.config.*, turbo*, typedoc*, unlighthouse*, vercel*, vetur.config.*, vitest.*, webpack*, workspace.json, xo.config.*, yarn*, babel.*, .babelrc, project.json, oxlint.json, nyc.config.*",
|
"package.json": ".browserslist*, .circleci*, .codecov, .commitlint*, .cz-config.js, .czrc, .dlint.json, .dprint.json, .editorconfig, .eslint*, .firebase*, .flowconfig, .github*, .gitlab*, .gitpod*, .huskyrc*, .jslint*, .lighthouserc.*, .lintstagedrc*, .markdownlint*, .mocha*, .node-version, .nodemon*, .npm*, .nvmrc, .pm2*, .pnp.*, .pnpm*, .prettier*, .releaserc*, .sentry*, .stackblitz*, .styleci*, .stylelint*, .tazerc*, .textlint*, .tool-versions, .travis*, .versionrc*, .vscode*, .watchman*, .xo-config*, .yamllint*, .yarnrc*, Procfile, api-extractor.json, apollo.config.*, appveyor*, ava.config.*, azure-pipelines*, bower.json, build.config.*, commitlint*, crowdin*, cypress.*, dangerfile*, dlint.json, dprint.json, firebase.json, grunt*, gulp*, histoire.config.*, jasmine.*, jenkins*, jest.config.*, jsconfig.*, karma*, lerna*, lighthouserc.*, lint-staged*, nest-cli.*, netlify*, nodemon*, nx.*, package-lock.json, package.nls*.json, phpcs.xml, playwright.config.*, pm2.*, pnpm*, prettier*, pullapprove*, puppeteer.config.*, pyrightconfig.json, release-tasks.sh, renovate*, rollup.config.*, stylelint*, tsconfig.*, tsdoc.*, tslint*, tsup.config.*, turbo*, typedoc*, unlighthouse*, vercel*, vetur.config.*, vitest.config.*, webpack*, workspace.json, xo.config.*, yarn*, babel.*, .babelrc, project.json",
|
||||||
"Cargo.toml": "Cargo.lock, rust-toolchain*, rustfmt.toml, .taplo.toml",
|
"Cargo.toml": "Cargo.lock",
|
||||||
"README.md": "LICENSE*, CHANGELOG.md, CODE_OF_CONDUCT.md, CONTRIBUTING.md, SECURITY.md, README.*",
|
"README.md": "LICENSE, CHANGELOG.md, CODE_OF_CONDUCT.md, CONTRIBUTING.md"
|
||||||
".gitignore": ".gitattributes, .dockerignore, .eslintignore, .prettierignore, .stylelintignore, .tslintignore, .yarnignore"
|
|
||||||
},
|
},
|
||||||
"[rust]": {
|
"[rust]": {
|
||||||
"editor.defaultFormatter": "rust-lang.rust-analyzer"
|
"editor.defaultFormatter": "rust-lang.rust-analyzer"
|
||||||
@@ -34,6 +32,5 @@
|
|||||||
"vitest.include": ["packages/**/*.spec.ts", "packages/**/*.spec.tsx"],
|
"vitest.include": ["packages/**/*.spec.ts", "packages/**/*.spec.tsx"],
|
||||||
"rust-analyzer.check.extraEnv": {
|
"rust-analyzer.check.extraEnv": {
|
||||||
"DATABASE_URL": "sqlite:affine.db"
|
"DATABASE_URL": "sqlite:affine.db"
|
||||||
},
|
}
|
||||||
"typescript.tsdk": "node_modules/typescript/lib"
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -12,4 +12,4 @@ npmPublishAccess: public
|
|||||||
|
|
||||||
npmRegistryServer: "https://registry.npmjs.org"
|
npmRegistryServer: "https://registry.npmjs.org"
|
||||||
|
|
||||||
yarnPath: .yarn/releases/yarn-4.12.0.cjs
|
yarnPath: .yarn/releases/yarn-4.9.1.cjs
|
||||||
|
|||||||
2160
Cargo.lock
generated
2160
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
225
Cargo.toml
225
Cargo.toml
@@ -3,6 +3,7 @@ members = [
|
|||||||
"./packages/backend/native",
|
"./packages/backend/native",
|
||||||
"./packages/common/native",
|
"./packages/common/native",
|
||||||
"./packages/common/y-octo/core",
|
"./packages/common/y-octo/core",
|
||||||
|
"./packages/common/y-octo/node",
|
||||||
"./packages/common/y-octo/utils",
|
"./packages/common/y-octo/utils",
|
||||||
"./packages/frontend/mobile-native",
|
"./packages/frontend/mobile-native",
|
||||||
"./packages/frontend/native",
|
"./packages/frontend/native",
|
||||||
@@ -12,124 +13,108 @@ members = [
|
|||||||
]
|
]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
affine_common = { path = "./packages/common/native" }
|
affine_common = { path = "./packages/common/native" }
|
||||||
affine_nbstore = { path = "./packages/frontend/native/nbstore" }
|
affine_nbstore = { path = "./packages/frontend/native/nbstore" }
|
||||||
ahash = "0.8"
|
ahash = "0.8"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
arbitrary = { version = "1.3", features = ["derive"] }
|
arbitrary = { version = "1.3", features = ["derive"] }
|
||||||
assert-json-diff = "2.0"
|
assert-json-diff = "2.0"
|
||||||
async-lock = { version = "3.4.0", features = ["loom"] }
|
async-lock = { version = "3.4.0", features = ["loom"] }
|
||||||
base64-simd = "0.8"
|
base64-simd = "0.8"
|
||||||
bitvec = "1.0"
|
bitvec = "1.0"
|
||||||
block2 = "0.6"
|
block2 = "0.6"
|
||||||
byteorder = "1.5"
|
byteorder = "1.5"
|
||||||
chrono = "0.4"
|
cpal = "0.15"
|
||||||
clap = { version = "4.4", features = ["derive"] }
|
chrono = "0.4"
|
||||||
core-foundation = "0.10"
|
clap = { version = "4.4", features = ["derive"] }
|
||||||
coreaudio-rs = "0.12"
|
core-foundation = "0.10"
|
||||||
cpal = "0.15"
|
coreaudio-rs = "0.12"
|
||||||
criterion = { version = "0.5", features = ["html_reports"] }
|
criterion = { version = "0.5", features = ["html_reports"] }
|
||||||
criterion2 = { version = "3", default-features = false }
|
criterion2 = { version = "3", default-features = false }
|
||||||
crossbeam-channel = "0.5"
|
crossbeam-channel = "0.5"
|
||||||
dispatch2 = "0.3"
|
dispatch2 = "0.3"
|
||||||
docx-parser = { git = "https://github.com/toeverything/docx-parser" }
|
docx-parser = { git = "https://github.com/toeverything/docx-parser" }
|
||||||
dotenvy = "0.15"
|
dotenvy = "0.15"
|
||||||
file-format = { version = "0.28", features = ["reader"] }
|
file-format = { version = "0.26", features = ["reader"] }
|
||||||
homedir = "0.3"
|
homedir = "0.3"
|
||||||
infer = { version = "0.19.0" }
|
infer = { version = "0.19.0" }
|
||||||
lasso = { version = "0.7", features = ["multi-threaded"] }
|
lasso = { version = "0.7", features = ["multi-threaded"] }
|
||||||
lib0 = { version = "0.16", features = ["lib0-serde"] }
|
lib0 = { version = "0.16", features = ["lib0-serde"] }
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
loom = { version = "0.7", features = ["checkpoint"] }
|
loom = { version = "0.7", features = ["checkpoint"] }
|
||||||
memory-indexer = "0.3.0"
|
mimalloc = "0.1"
|
||||||
mimalloc = "0.1"
|
nanoid = "0.4"
|
||||||
mp4parse = "0.17"
|
napi = { version = "3.0.0-beta.3", features = ["async", "chrono_date", "error_anyhow", "napi9", "serde"] }
|
||||||
nanoid = "0.4"
|
napi-build = { version = "2" }
|
||||||
napi = { version = "3.7.0", features = [
|
napi-derive = { version = "3.0.0-beta.3" }
|
||||||
"async",
|
nom = "8"
|
||||||
"chrono_date",
|
notify = { version = "8", features = ["serde"] }
|
||||||
"error_anyhow",
|
objc2 = "0.6"
|
||||||
"napi9",
|
objc2-foundation = "0.3"
|
||||||
"serde",
|
once_cell = "1"
|
||||||
] }
|
ordered-float = "5"
|
||||||
napi-build = { version = "2" }
|
parking_lot = "0.12"
|
||||||
napi-derive = { version = "3.4" }
|
path-ext = "0.1.2"
|
||||||
nom = "8"
|
pdf-extract = { git = "https://github.com/toeverything/pdf-extract", branch = "darksky/improve-font-decoding" }
|
||||||
notify = { version = "8", features = ["serde"] }
|
phf = { version = "0.11", features = ["macros"] }
|
||||||
objc2 = "0.6"
|
proptest = "1.3"
|
||||||
objc2-foundation = "0.3"
|
proptest-derive = "0.5"
|
||||||
once_cell = "1"
|
rand = "0.9"
|
||||||
ordered-float = "5"
|
rand_chacha = "0.9"
|
||||||
parking_lot = "0.12"
|
rand_distr = "0.5"
|
||||||
path-ext = "0.1.2"
|
rayon = "1.10"
|
||||||
pdf-extract = { git = "https://github.com/toeverything/pdf-extract", branch = "darksky/improve-font-decoding" }
|
readability = { version = "0.3.0", default-features = false }
|
||||||
phf = { version = "0.11", features = ["macros"] }
|
regex = "1.10"
|
||||||
proptest = "1.3"
|
rubato = "0.16"
|
||||||
proptest-derive = "0.5"
|
screencapturekit = "0.3"
|
||||||
pulldown-cmark = "0.13"
|
serde = "1"
|
||||||
rand = "0.9"
|
serde_json = "1"
|
||||||
rand_chacha = "0.9"
|
sha3 = "0.10"
|
||||||
rand_distr = "0.5"
|
smol_str = "0.3"
|
||||||
rayon = "1.10"
|
sqlx = { version = "0.8", default-features = false, features = ["chrono", "macros", "migrate", "runtime-tokio", "sqlite", "tls-rustls"] }
|
||||||
readability = { version = "0.3.0", default-features = false }
|
strum_macros = "0.27.0"
|
||||||
regex = "1.10"
|
symphonia = { version = "0.5", features = ["all", "opt-simd"] }
|
||||||
rubato = "0.16"
|
text-splitter = "0.27"
|
||||||
screencapturekit = "0.3"
|
thiserror = "2"
|
||||||
serde = "1"
|
tiktoken-rs = "0.7"
|
||||||
serde_json = "1"
|
tokio = "1.45"
|
||||||
sha3 = "0.10"
|
tree-sitter = { version = "0.25" }
|
||||||
smol_str = "0.3"
|
tree-sitter-c = { version = "0.24" }
|
||||||
sqlx = { version = "0.8", default-features = false, features = [
|
tree-sitter-c-sharp = { version = "0.23" }
|
||||||
"chrono",
|
tree-sitter-cpp = { version = "0.23" }
|
||||||
"macros",
|
tree-sitter-go = { version = "0.23" }
|
||||||
"migrate",
|
tree-sitter-java = { version = "0.23" }
|
||||||
"runtime-tokio",
|
tree-sitter-javascript = { version = "0.23" }
|
||||||
"sqlite",
|
tree-sitter-kotlin-ng = { version = "1.1" }
|
||||||
"tls-rustls",
|
tree-sitter-python = { version = "0.23" }
|
||||||
] }
|
tree-sitter-rust = { version = "0.24" }
|
||||||
strum_macros = "0.27.0"
|
tree-sitter-scala = { version = "0.24" }
|
||||||
symphonia = { version = "0.5", features = ["all", "opt-simd"] }
|
tree-sitter-typescript = { version = "0.23" }
|
||||||
text-splitter = "0.27"
|
uniffi = "0.29"
|
||||||
thiserror = "2"
|
url = { version = "2.5" }
|
||||||
tiktoken-rs = "0.7"
|
uuid = "1.8"
|
||||||
tokio = "1.45"
|
v_htmlescape = "0.15"
|
||||||
tree-sitter = { version = "0.25" }
|
windows = { version = "0.61", features = [
|
||||||
tree-sitter-c = { version = "0.24" }
|
"Win32_Devices_FunctionDiscovery",
|
||||||
tree-sitter-c-sharp = { version = "0.23" }
|
"Win32_UI_Shell_PropertiesSystem",
|
||||||
tree-sitter-cpp = { version = "0.23" }
|
"Win32_Media_Audio",
|
||||||
tree-sitter-go = { version = "0.23" }
|
"Win32_System_Variant",
|
||||||
tree-sitter-java = { version = "0.23" }
|
"Win32_System_Com_StructuredStorage",
|
||||||
tree-sitter-javascript = { version = "0.23" }
|
"Win32_System_Threading",
|
||||||
tree-sitter-kotlin-ng = { version = "1.1" }
|
"Win32_System_ProcessStatus",
|
||||||
tree-sitter-python = { version = "0.23" }
|
"Win32_Foundation",
|
||||||
tree-sitter-rust = { version = "0.24" }
|
"Win32_System_Com",
|
||||||
tree-sitter-scala = { version = "0.24" }
|
"Win32_System_Diagnostics_ToolHelp",
|
||||||
tree-sitter-typescript = { version = "0.23" }
|
] }
|
||||||
uniffi = "0.29"
|
windows-core = { version = "0.61" }
|
||||||
url = { version = "2.5" }
|
y-octo = { path = "./packages/common/y-octo/core" }
|
||||||
uuid = "1.8"
|
y-sync = { version = "0.4" }
|
||||||
v_htmlescape = "0.15"
|
yrs = "0.23.0"
|
||||||
windows = { version = "0.61", features = [
|
|
||||||
"Win32_Devices_FunctionDiscovery",
|
|
||||||
"Win32_Foundation",
|
|
||||||
"Win32_Media_Audio",
|
|
||||||
"Win32_System_Com",
|
|
||||||
"Win32_System_Com_StructuredStorage",
|
|
||||||
"Win32_System_Diagnostics_ToolHelp",
|
|
||||||
"Win32_System_ProcessStatus",
|
|
||||||
"Win32_System_Threading",
|
|
||||||
"Win32_System_Variant",
|
|
||||||
"Win32_UI_Shell_PropertiesSystem",
|
|
||||||
] }
|
|
||||||
windows-core = { version = "0.61" }
|
|
||||||
y-octo = { path = "./packages/common/y-octo/core" }
|
|
||||||
y-sync = { version = "0.4" }
|
|
||||||
yrs = "0.23.0"
|
|
||||||
|
|
||||||
[profile.dev.package.sqlx-macros]
|
[profile.dev.package.sqlx-macros]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
@@ -140,6 +125,6 @@ lto = true
|
|||||||
opt-level = 3
|
opt-level = 3
|
||||||
strip = "symbols"
|
strip = "symbols"
|
||||||
|
|
||||||
# android uniffi bindgen requires symbols
|
# android uniffi bindgen requires symbols
|
||||||
[profile.release.package.affine_mobile_native]
|
[profile.release.package.affine_mobile_native]
|
||||||
strip = "none"
|
strip = "none"
|
||||||
2
LICENSE
2
LICENSE
@@ -2,7 +2,7 @@ Copyright (c) 2022-present TOEVERYTHING PTE. LTD. and its affiliates.
|
|||||||
|
|
||||||
Portions of this software are licensed as follows:
|
Portions of this software are licensed as follows:
|
||||||
|
|
||||||
- All content that resides under the "packages/backend" and "packages/common/native" directory of this repository, if that directory exists, is licensed under the license defined in "packages/backend/server/LICENSE".
|
- All content that resides under the "packages/backend/server" directory of this repository, if that directory exists, is licensed under the license defined in "packages/backend/server/LICENSE".
|
||||||
- All third party components incorporated into the AFFiNE Software are licensed under the original license provided by the owner of the applicable component.
|
- All third party components incorporated into the AFFiNE Software are licensed under the original license provided by the owner of the applicable component.
|
||||||
- Content outside of the above mentioned directories or restrictions above is available under the "MIT" license as defined in "LICENSE-MIT".
|
- Content outside of the above mentioned directories or restrictions above is available under the "MIT" license as defined in "LICENSE-MIT".
|
||||||
|
|
||||||
|
|||||||
41
README.md
41
README.md
@@ -6,7 +6,7 @@
|
|||||||
<br>
|
<br>
|
||||||
</h1>
|
</h1>
|
||||||
<a href="https://affine.pro/download">
|
<a href="https://affine.pro/download">
|
||||||
<img alt="affine logo" src="https://cdn.affine.pro/Github_hero_image2.png" style="width: 100%">
|
<img alt="affine logo" src="https://cdn.affine.pro/Github_hero_image1.png" style="width: 100%">
|
||||||
</a>
|
</a>
|
||||||
<br/>
|
<br/>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@@ -21,6 +21,23 @@
|
|||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
<div align="left" valign="middle">
|
||||||
|
<a href="https://runblaze.dev">
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://www.runblaze.dev/logo_dark.png">
|
||||||
|
<img align="right" src="https://www.runblaze.dev/logo_light.png" height="102px"/>
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<br style="display: none;"/>
|
||||||
|
|
||||||
|
_Special thanks to [Blaze](https://runblaze.dev) for their support of this project. They provide high-performance Apple Silicon macOS and Linux (AMD64 & ARM64) runners for GitHub Actions, greatly reducing our automated build times._
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<a href="https://affine.pro">Home Page</a> |
|
<a href="https://affine.pro">Home Page</a> |
|
||||||
<a href="https://affine.pro/redirect/discord">Discord</a> |
|
<a href="https://affine.pro/redirect/discord">Discord</a> |
|
||||||
@@ -64,7 +81,7 @@ Star us, and you will receive all release notifications from GitHub without any
|
|||||||
|
|
||||||
**Multimodal AI partner ready to kick in any work**
|
**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.
|
- 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**
|
**Local-first & Real-time collaborative**
|
||||||
|
|
||||||
@@ -90,10 +107,10 @@ Thanks for checking us out, we appreciate your interest and sincerely hope that
|
|||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
| Bug Reports | Feature Requests | Questions/Discussions | AFFiNE Community |
|
| Bug Reports | Feature Requests | Questions/Discussions | AFFiNE Community |
|
||||||
| --------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------- |
|
| --------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------- |
|
||||||
| [Create a bug report](https://github.com/toeverything/AFFiNE/issues/new?assignees=&labels=bug%2Cproduct-review&template=BUG-REPORT.yml&title=TITLE) | [Submit a feature request](https://github.com/toeverything/AFFiNE/issues/new?assignees=&labels=feat%2Cproduct-review&template=FEATURE-REQUEST.yml&title=TITLE) | [Check GitHub Discussion](https://github.com/toeverything/AFFiNE/discussions) | [Visit the AFFiNE Community](https://community.affine.pro) |
|
| [Create a bug report](https://github.com/toeverything/AFFiNE/issues/new?assignees=&labels=bug%2Cproduct-review&template=BUG-REPORT.yml&title=TITLE) | [Submit a feature request](https://github.com/toeverything/AFFiNE/issues/new?assignees=&labels=feat%2Cproduct-review&template=FEATURE-REQUEST.yml&title=TITLE) | [Check GitHub Discussion](https://github.com/toeverything/AFFiNE/discussions) | [Vist the AFFiNE Community](https://community.affine.pro) |
|
||||||
| Something isn't working as expected | An idea for a new feature, or improvements | Discuss and ask questions | A place to ask, learn and engage with others |
|
| Something isn't working as expected | An idea for a new feature, or improvements | Discuss and ask questions | A place to ask, learn and engage with others |
|
||||||
|
|
||||||
Calling all developers, testers, tech writers and more! Contributions of all types are more than welcome, you can read more in [docs/types-of-contributions.md](docs/types-of-contributions.md). If you are interested in contributing code, read our [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md) and feel free to check out our GitHub issues to get stuck in to show us what you’re made of.
|
Calling all developers, testers, tech writers and more! Contributions of all types are more than welcome, you can read more in [docs/types-of-contributions.md](docs/types-of-contributions.md). If you are interested in contributing code, read our [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md) and feel free to check out our GitHub issues to get stuck in to show us what you’re made of.
|
||||||
|
|
||||||
@@ -152,10 +169,8 @@ Welcome to the AFFiNE blog section! Here, you’ll find the latest insights, tip
|
|||||||
We would also like to give thanks to open-source projects that make AFFiNE possible:
|
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.
|
||||||
- [y-octo](https://github.com/y-crdt/y-octo) - 🐙 y-octo is a native, high-performance, thread-safe YJS CRDT implementation, serving as the core engine enabling the AFFiNE Client/Server to achieve "local-first" functionality.
|
|
||||||
- [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.
|
- [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.
|
||||||
- [yjs](https://github.com/yjs/yjs) - Fundamental support of CRDTs for our implementation on state management and data sync on web.
|
|
||||||
- [electron](https://github.com/electron/electron) - Build cross-platform desktop apps with JavaScript, HTML, and CSS.
|
- [electron](https://github.com/electron/electron) - Build cross-platform desktop apps with JavaScript, HTML, and CSS.
|
||||||
- [React](https://github.com/facebook/react) - The library for web and native user interfaces.
|
- [React](https://github.com/facebook/react) - The library for web and native user interfaces.
|
||||||
- [napi-rs](https://github.com/napi-rs/napi-rs) - A framework for building compiled Node.js add-ons in Rust via Node-API.
|
- [napi-rs](https://github.com/napi-rs/napi-rs) - A framework for building compiled Node.js add-ons in Rust via Node-API.
|
||||||
@@ -178,8 +193,6 @@ We would like to express our gratitude to all the individuals who have already c
|
|||||||
|
|
||||||
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/self-host-affine).
|
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/self-host-affine).
|
||||||
|
|
||||||
[](https://sealos.io/products/app-store/affine)
|
|
||||||
|
|
||||||
[](https://template.run.claw.cloud/?openapp=system-fastdeploy%3FtemplateName%3Daffine)
|
[](https://template.run.claw.cloud/?openapp=system-fastdeploy%3FtemplateName%3Daffine)
|
||||||
|
|
||||||
## Hiring
|
## Hiring
|
||||||
@@ -206,6 +219,12 @@ See [BUILDING.md] for instructions on how to build AFFiNE from source code.
|
|||||||
We welcome contributions from everyone.
|
We welcome contributions from everyone.
|
||||||
See [docs/contributing/tutorial.md](./docs/contributing/tutorial.md) for details.
|
See [docs/contributing/tutorial.md](./docs/contributing/tutorial.md) for details.
|
||||||
|
|
||||||
|
## Thanks
|
||||||
|
|
||||||
|
<a href="https://www.chromatic.com/"><img src="https://user-images.githubusercontent.com/321738/84662277-e3db4f80-af1b-11ea-88f5-91d67a5e59f6.png" width="153" height="30" alt="Chromatic" /></a>
|
||||||
|
|
||||||
|
Thanks to [Chromatic](https://www.chromatic.com/) for providing the visual testing platform that helps us review UI changes and catch visual regressions.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
### Editions
|
### Editions
|
||||||
|
|||||||
11
SECURITY.md
11
SECURITY.md
@@ -6,14 +6,15 @@ We recommend users to always use the latest major version. Security updates will
|
|||||||
|
|
||||||
| Version | Supported |
|
| Version | Supported |
|
||||||
| --------------- | ------------------ |
|
| --------------- | ------------------ |
|
||||||
| 0.26.x (stable) | :white_check_mark: |
|
| 0.17.x (stable) | :white_check_mark: |
|
||||||
| < 0.26.x | :x: |
|
| < 0.17.x | :x: |
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
We welcome you to provide us with bug reports via and email at [security@toeverything.info](mailto:security@toeverything.info) or submit directly on [GitHub](https://github.com/toeverything/AFFiNE/security), **we encourage you to submit the relevant information directly via GitHub**. We expect your report to contain at least the following for us to evaluate and reproduce:
|
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:
|
1. Using platform and version, for example:
|
||||||
|
|
||||||
- macos arm64 0.12.0-canary-202402220729-0868ac6
|
- macos arm64 0.12.0-canary-202402220729-0868ac6
|
||||||
- app.affine.pro 0.12.0-canary-202402220729-0868ac6
|
- app.affine.pro 0.12.0-canary-202402220729-0868ac6
|
||||||
|
|
||||||
@@ -21,6 +22,8 @@ We welcome you to provide us with bug reports via and email at [security@toevery
|
|||||||
|
|
||||||
3. Your classification or analysis of the vulnerability (optional)
|
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 determine specific rewards based on the evaluation results.
|
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.
|
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.
|
||||||
|
|||||||
@@ -79,7 +79,7 @@
|
|||||||
"@blocksuite/std": "workspace:*",
|
"@blocksuite/std": "workspace:*",
|
||||||
"@blocksuite/store": "workspace:*",
|
"@blocksuite/store": "workspace:*",
|
||||||
"@blocksuite/sync": "workspace:*",
|
"@blocksuite/sync": "workspace:*",
|
||||||
"rxjs": "^7.8.2"
|
"rxjs": "^7.8.1"
|
||||||
},
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts",
|
".": "./src/index.ts",
|
||||||
@@ -296,10 +296,10 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1",
|
"version": "0.22.4",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vanilla-extract/vite-plugin": "^5.0.0",
|
"@vanilla-extract/vite-plugin": "^5.0.0",
|
||||||
"msw": "^2.12.4",
|
"msw": "^2.8.4",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "3.1.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2101,157 +2101,6 @@ describe('html to snapshot', () => {
|
|||||||
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
|
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('paragraph with br should split into multiple blocks', async () => {
|
|
||||||
const html = template(`<p>aaa<br>bbb<br>ccc</p>`);
|
|
||||||
|
|
||||||
const blockSnapshot: BlockSnapshot = {
|
|
||||||
type: 'block',
|
|
||||||
id: 'matchesReplaceMap[0]',
|
|
||||||
flavour: 'affine:note',
|
|
||||||
props: {
|
|
||||||
xywh: '[0,0,800,95]',
|
|
||||||
background: DefaultTheme.noteBackgrounColor,
|
|
||||||
index: 'a0',
|
|
||||||
hidden: false,
|
|
||||||
displayMode: NoteDisplayMode.DocAndEdgeless,
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'matchesReplaceMap[1]',
|
|
||||||
flavour: 'affine:paragraph',
|
|
||||||
props: {
|
|
||||||
type: 'text',
|
|
||||||
text: {
|
|
||||||
'$blocksuite:internal:text$': true,
|
|
||||||
delta: [{ insert: 'aaa' }],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'matchesReplaceMap[2]',
|
|
||||||
flavour: 'affine:paragraph',
|
|
||||||
props: {
|
|
||||||
type: 'text',
|
|
||||||
text: {
|
|
||||||
'$blocksuite:internal:text$': true,
|
|
||||||
delta: [{ insert: 'bbb' }],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'matchesReplaceMap[3]',
|
|
||||||
flavour: 'affine:paragraph',
|
|
||||||
props: {
|
|
||||||
type: 'text',
|
|
||||||
text: {
|
|
||||||
'$blocksuite:internal:text$': true,
|
|
||||||
delta: [{ insert: 'ccc' }],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const htmlAdapter = new HtmlAdapter(createJob(), provider);
|
|
||||||
const rawBlockSnapshot = await htmlAdapter.toBlockSnapshot({
|
|
||||||
file: html,
|
|
||||||
});
|
|
||||||
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('paragraph with br should keep inline styles in each split line', async () => {
|
|
||||||
const html = template(
|
|
||||||
`<p><strong>aaa</strong><br><a href="https://www.google.com/">bbb</a><br><em>ccc</em></p>`
|
|
||||||
);
|
|
||||||
|
|
||||||
const blockSnapshot: BlockSnapshot = {
|
|
||||||
type: 'block',
|
|
||||||
id: 'matchesReplaceMap[0]',
|
|
||||||
flavour: 'affine:note',
|
|
||||||
props: {
|
|
||||||
xywh: '[0,0,800,95]',
|
|
||||||
background: DefaultTheme.noteBackgrounColor,
|
|
||||||
index: 'a0',
|
|
||||||
hidden: false,
|
|
||||||
displayMode: NoteDisplayMode.DocAndEdgeless,
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'matchesReplaceMap[1]',
|
|
||||||
flavour: 'affine:paragraph',
|
|
||||||
props: {
|
|
||||||
type: 'text',
|
|
||||||
text: {
|
|
||||||
'$blocksuite:internal:text$': true,
|
|
||||||
delta: [
|
|
||||||
{
|
|
||||||
insert: 'aaa',
|
|
||||||
attributes: {
|
|
||||||
bold: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'matchesReplaceMap[2]',
|
|
||||||
flavour: 'affine:paragraph',
|
|
||||||
props: {
|
|
||||||
type: 'text',
|
|
||||||
text: {
|
|
||||||
'$blocksuite:internal:text$': true,
|
|
||||||
delta: [
|
|
||||||
{
|
|
||||||
insert: 'bbb',
|
|
||||||
attributes: {
|
|
||||||
link: 'https://www.google.com/',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'matchesReplaceMap[3]',
|
|
||||||
flavour: 'affine:paragraph',
|
|
||||||
props: {
|
|
||||||
type: 'text',
|
|
||||||
text: {
|
|
||||||
'$blocksuite:internal:text$': true,
|
|
||||||
delta: [
|
|
||||||
{
|
|
||||||
insert: 'ccc',
|
|
||||||
attributes: {
|
|
||||||
italic: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const htmlAdapter = new HtmlAdapter(createJob(), provider);
|
|
||||||
const rawBlockSnapshot = await htmlAdapter.toBlockSnapshot({
|
|
||||||
file: html,
|
|
||||||
});
|
|
||||||
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('nested list', async () => {
|
test('nested list', async () => {
|
||||||
const html = template(`<ul><li>111<ul><li>222</li></ul></li></ul>`);
|
const html = template(`<ul><li>111<ul><li>222</li></ul></li></ul>`);
|
||||||
|
|
||||||
@@ -2365,7 +2214,7 @@ describe('html to snapshot', () => {
|
|||||||
|
|
||||||
test('iframe', async () => {
|
test('iframe', async () => {
|
||||||
const html = template(
|
const html = template(
|
||||||
`<iframe width="560" height="315" src="https://www.youtube.com/embed/QDsd0nyzwz0?start=&end=" title="YouTube video player" frameborder="0" allow="fullscreen; autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin"></iframe>`
|
`<iframe width="560" height="315" src="https://www.youtube.com/embed/QDsd0nyzwz0?start=&end=" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>`
|
||||||
);
|
);
|
||||||
|
|
||||||
const blockSnapshot: BlockSnapshot = {
|
const blockSnapshot: BlockSnapshot = {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { MarkdownTransformer } from '@blocksuite/affine/widgets/linked-doc';
|
|
||||||
import {
|
import {
|
||||||
DefaultTheme,
|
DefaultTheme,
|
||||||
NoteDisplayMode,
|
NoteDisplayMode,
|
||||||
@@ -17,15 +16,12 @@ import type {
|
|||||||
SliceSnapshot,
|
SliceSnapshot,
|
||||||
TransformerMiddleware,
|
TransformerMiddleware,
|
||||||
} from '@blocksuite/store';
|
} from '@blocksuite/store';
|
||||||
import { AssetsManager, MemoryBlobCRUD, Schema } from '@blocksuite/store';
|
import { AssetsManager, MemoryBlobCRUD } from '@blocksuite/store';
|
||||||
import { TestWorkspace } from '@blocksuite/store/test';
|
|
||||||
import { describe, expect, test } from 'vitest';
|
import { describe, expect, test } from 'vitest';
|
||||||
|
|
||||||
import { AffineSchemas } from '../../schemas.js';
|
|
||||||
import { createJob } from '../utils/create-job.js';
|
import { createJob } from '../utils/create-job.js';
|
||||||
import { getProvider } from '../utils/get-provider.js';
|
import { getProvider } from '../utils/get-provider.js';
|
||||||
import { nanoidReplacement } from '../utils/nanoid-replacement.js';
|
import { nanoidReplacement } from '../utils/nanoid-replacement.js';
|
||||||
import { testStoreExtensions } from '../utils/store.js';
|
|
||||||
|
|
||||||
const provider = getProvider();
|
const provider = getProvider();
|
||||||
|
|
||||||
@@ -94,39 +90,6 @@ describe('snapshot to markdown', () => {
|
|||||||
expect(target.file).toBe(markdown);
|
expect(target.file).toBe(markdown);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('imports frontmatter metadata into doc meta', async () => {
|
|
||||||
const schema = new Schema().register(AffineSchemas);
|
|
||||||
const collection = new TestWorkspace();
|
|
||||||
collection.storeExtensions = testStoreExtensions;
|
|
||||||
collection.meta.initialize();
|
|
||||||
|
|
||||||
const markdown = `---
|
|
||||||
title: Web developer
|
|
||||||
created: 2018-04-12T09:51:00
|
|
||||||
updated: 2018-04-12T10:00:00
|
|
||||||
tags: [a, b]
|
|
||||||
favorite: true
|
|
||||||
---
|
|
||||||
Hello world
|
|
||||||
`;
|
|
||||||
|
|
||||||
const docId = await MarkdownTransformer.importMarkdownToDoc({
|
|
||||||
collection,
|
|
||||||
schema,
|
|
||||||
markdown,
|
|
||||||
fileName: 'fallback-title',
|
|
||||||
extensions: testStoreExtensions,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(docId).toBeTruthy();
|
|
||||||
const meta = collection.meta.getDocMeta(docId!);
|
|
||||||
expect(meta?.title).toBe('Web developer');
|
|
||||||
expect(meta?.createDate).toBe(Date.parse('2018-04-12T09:51:00'));
|
|
||||||
expect(meta?.updatedDate).toBe(Date.parse('2018-04-12T10:00:00'));
|
|
||||||
expect(meta?.favorite).toBe(true);
|
|
||||||
expect(meta?.tags).toEqual(['a', 'b']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('paragraph', async () => {
|
test('paragraph', async () => {
|
||||||
const blockSnapshot: BlockSnapshot = {
|
const blockSnapshot: BlockSnapshot = {
|
||||||
type: 'block',
|
type: 'block',
|
||||||
@@ -3033,50 +2996,6 @@ describe('markdown to snapshot', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('html inline color span imports to nearest supported text color', async () => {
|
|
||||||
const markdown = `<span style="color: #00afde;">Hello</span>`;
|
|
||||||
const blockSnapshot: BlockSnapshot = {
|
|
||||||
type: 'block',
|
|
||||||
id: 'matchesReplaceMap[0]',
|
|
||||||
flavour: 'affine:note',
|
|
||||||
props: {
|
|
||||||
xywh: '[0,0,800,95]',
|
|
||||||
background: DefaultTheme.noteBackgrounColor,
|
|
||||||
index: 'a0',
|
|
||||||
hidden: false,
|
|
||||||
displayMode: NoteDisplayMode.DocAndEdgeless,
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'matchesReplaceMap[1]',
|
|
||||||
flavour: 'affine:paragraph',
|
|
||||||
props: {
|
|
||||||
type: 'text',
|
|
||||||
text: {
|
|
||||||
'$blocksuite:internal:text$': true,
|
|
||||||
delta: [
|
|
||||||
{
|
|
||||||
insert: 'Hello',
|
|
||||||
attributes: {
|
|
||||||
color: 'var(--affine-v2-text-highlight-fg-blue)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const mdAdapter = new MarkdownAdapter(createJob(), provider);
|
|
||||||
const rawBlockSnapshot = await mdAdapter.toBlockSnapshot({
|
|
||||||
file: markdown,
|
|
||||||
});
|
|
||||||
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('paragraph', async () => {
|
test('paragraph', async () => {
|
||||||
const markdown = `aaa
|
const markdown = `aaa
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,95 +0,0 @@
|
|||||||
import type { AffineTextAttributes } from '@blocksuite/affine-shared/types';
|
|
||||||
import { describe, expect, test } from 'vitest';
|
|
||||||
|
|
||||||
import { insertUrlTextSegments } from '../../../../blocks/database/src/properties/paste-url.js';
|
|
||||||
|
|
||||||
type InsertCall = {
|
|
||||||
range: {
|
|
||||||
index: number;
|
|
||||||
length: number;
|
|
||||||
};
|
|
||||||
text: string;
|
|
||||||
attributes?: AffineTextAttributes;
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('insertUrlTextSegments', () => {
|
|
||||||
test('should replace selected text on first insert and append remaining segments', () => {
|
|
||||||
const insertCalls: InsertCall[] = [];
|
|
||||||
const selectionCalls: Array<{ index: number; length: number } | null> = [];
|
|
||||||
const inlineEditor = {
|
|
||||||
insertText: (
|
|
||||||
range: { index: number; length: number },
|
|
||||||
text: string,
|
|
||||||
attributes?: AffineTextAttributes
|
|
||||||
) => {
|
|
||||||
insertCalls.push({ range, text, attributes });
|
|
||||||
},
|
|
||||||
setInlineRange: (range: { index: number; length: number } | null) => {
|
|
||||||
selectionCalls.push(range);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const inlineRange = { index: 4, length: 6 };
|
|
||||||
const segments = [
|
|
||||||
{ text: 'hi - ' },
|
|
||||||
{ text: 'https://google.com', link: 'https://google.com' },
|
|
||||||
];
|
|
||||||
|
|
||||||
insertUrlTextSegments(inlineEditor, inlineRange, segments);
|
|
||||||
|
|
||||||
expect(insertCalls).toEqual([
|
|
||||||
{
|
|
||||||
range: { index: 4, length: 6 },
|
|
||||||
text: 'hi - ',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
range: { index: 9, length: 0 },
|
|
||||||
text: 'https://google.com',
|
|
||||||
attributes: {
|
|
||||||
link: 'https://google.com',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
expect(selectionCalls).toEqual([{ index: 27, length: 0 }]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should keep insertion range length zero when there is no selected text', () => {
|
|
||||||
const insertCalls: InsertCall[] = [];
|
|
||||||
const selectionCalls: Array<{ index: number; length: number } | null> = [];
|
|
||||||
const inlineEditor = {
|
|
||||||
insertText: (
|
|
||||||
range: { index: number; length: number },
|
|
||||||
text: string,
|
|
||||||
attributes?: AffineTextAttributes
|
|
||||||
) => {
|
|
||||||
insertCalls.push({ range, text, attributes });
|
|
||||||
},
|
|
||||||
setInlineRange: (range: { index: number; length: number } | null) => {
|
|
||||||
selectionCalls.push(range);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const inlineRange = { index: 2, length: 0 };
|
|
||||||
const segments = [
|
|
||||||
{ text: 'prefix ' },
|
|
||||||
{ text: 'https://a.com', link: 'https://a.com' },
|
|
||||||
];
|
|
||||||
|
|
||||||
insertUrlTextSegments(inlineEditor, inlineRange, segments);
|
|
||||||
|
|
||||||
expect(insertCalls).toEqual([
|
|
||||||
{
|
|
||||||
range: { index: 2, length: 0 },
|
|
||||||
text: 'prefix ',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
range: { index: 9, length: 0 },
|
|
||||||
text: 'https://a.com',
|
|
||||||
attributes: {
|
|
||||||
link: 'https://a.com',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
expect(selectionCalls).toEqual([{ index: 22, length: 0 }]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -17,18 +17,18 @@
|
|||||||
"@blocksuite/affine-shared": "workspace:*",
|
"@blocksuite/affine-shared": "workspace:*",
|
||||||
"@blocksuite/affine-widget-slash-menu": "workspace:*",
|
"@blocksuite/affine-widget-slash-menu": "workspace:*",
|
||||||
"@blocksuite/global": "workspace:*",
|
"@blocksuite/global": "workspace:*",
|
||||||
"@blocksuite/icons": "^2.2.17",
|
"@blocksuite/icons": "^2.2.12",
|
||||||
"@blocksuite/std": "workspace:*",
|
"@blocksuite/std": "workspace:*",
|
||||||
"@blocksuite/store": "workspace:*",
|
"@blocksuite/store": "workspace:*",
|
||||||
"@floating-ui/dom": "^1.6.13",
|
"@floating-ui/dom": "^1.6.13",
|
||||||
"@lit/context": "^1.1.2",
|
"@lit/context": "^1.1.2",
|
||||||
"@preact/signals-core": "^1.8.0",
|
"@preact/signals-core": "^1.8.0",
|
||||||
"@toeverything/theme": "^1.1.23",
|
"@toeverything/theme": "^1.1.16",
|
||||||
"file-type": "^21.0.0",
|
"file-type": "^21.0.0",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"minimatch": "^10.1.1",
|
"minimatch": "^10.0.1",
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.1",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts",
|
".": "./src/index.ts",
|
||||||
@@ -41,5 +41,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.22.4"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,20 +19,20 @@
|
|||||||
"@blocksuite/affine-shared": "workspace:*",
|
"@blocksuite/affine-shared": "workspace:*",
|
||||||
"@blocksuite/affine-widget-slash-menu": "workspace:*",
|
"@blocksuite/affine-widget-slash-menu": "workspace:*",
|
||||||
"@blocksuite/global": "workspace:*",
|
"@blocksuite/global": "workspace:*",
|
||||||
"@blocksuite/icons": "^2.2.17",
|
"@blocksuite/icons": "^2.2.12",
|
||||||
"@blocksuite/std": "workspace:*",
|
"@blocksuite/std": "workspace:*",
|
||||||
"@blocksuite/store": "workspace:*",
|
"@blocksuite/store": "workspace:*",
|
||||||
"@lit/context": "^1.1.2",
|
"@lit/context": "^1.1.2",
|
||||||
"@preact/signals-core": "^1.8.0",
|
"@preact/signals-core": "^1.8.0",
|
||||||
"@toeverything/theme": "^1.1.23",
|
"@toeverything/theme": "^1.1.16",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"minimatch": "^10.1.1",
|
"minimatch": "^10.0.1",
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.1",
|
||||||
"yjs": "^13.6.27",
|
"yjs": "^13.6.23",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vitest": "^3.2.4"
|
"vitest": "3.1.3"
|
||||||
},
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts",
|
".": "./src/index.ts",
|
||||||
@@ -45,5 +45,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.22.4"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,21 +18,20 @@
|
|||||||
"@blocksuite/affine-shared": "workspace:*",
|
"@blocksuite/affine-shared": "workspace:*",
|
||||||
"@blocksuite/affine-widget-slash-menu": "workspace:*",
|
"@blocksuite/affine-widget-slash-menu": "workspace:*",
|
||||||
"@blocksuite/global": "workspace:*",
|
"@blocksuite/global": "workspace:*",
|
||||||
"@blocksuite/icons": "^2.2.17",
|
"@blocksuite/icons": "^2.2.12",
|
||||||
"@blocksuite/std": "workspace:*",
|
"@blocksuite/std": "workspace:*",
|
||||||
"@blocksuite/store": "workspace:*",
|
"@blocksuite/store": "workspace:*",
|
||||||
"@emoji-mart/data": "^1.2.1",
|
"@emoji-mart/data": "^1.2.1",
|
||||||
"@emotion/css": "^11.13.5",
|
|
||||||
"@floating-ui/dom": "^1.6.10",
|
"@floating-ui/dom": "^1.6.10",
|
||||||
"@lit/context": "^1.1.2",
|
"@lit/context": "^1.1.2",
|
||||||
"@preact/signals-core": "^1.8.0",
|
"@preact/signals-core": "^1.8.0",
|
||||||
"@toeverything/theme": "^1.1.23",
|
"@toeverything/theme": "^1.1.16",
|
||||||
"@types/mdast": "^4.0.4",
|
"@types/mdast": "^4.0.4",
|
||||||
"emoji-mart": "^5.6.0",
|
"emoji-mart": "^5.6.0",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"minimatch": "^10.1.1",
|
"minimatch": "^10.0.1",
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.1",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts",
|
".": "./src/index.ts",
|
||||||
@@ -45,5 +44,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.22.4"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
import { css } from '@emotion/css';
|
|
||||||
|
|
||||||
export const calloutHostStyles = css({
|
|
||||||
display: 'block',
|
|
||||||
margin: '8px 0',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const calloutBlockContainerStyles = css({
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'flex-start',
|
|
||||||
padding: '5px 10px',
|
|
||||||
borderRadius: '8px',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const calloutEmojiContainerStyles = css({
|
|
||||||
userSelect: 'none',
|
|
||||||
fontSize: '1.2em',
|
|
||||||
width: '24px',
|
|
||||||
height: '24px',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
// marginTop is dynamically set by JavaScript based on first child's height
|
|
||||||
marginBottom: '10px',
|
|
||||||
flexShrink: 0,
|
|
||||||
position: 'relative',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const calloutEmojiStyles = css({
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
':hover': {
|
|
||||||
cursor: 'pointer',
|
|
||||||
opacity: 0.7,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const calloutChildrenStyles = css({
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 0,
|
|
||||||
paddingLeft: '10px',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const iconPickerContainerStyles = css({
|
|
||||||
position: 'absolute',
|
|
||||||
top: '100%',
|
|
||||||
left: 0,
|
|
||||||
zIndex: 1000,
|
|
||||||
background: 'white',
|
|
||||||
border: '1px solid #ccc',
|
|
||||||
borderRadius: '8px',
|
|
||||||
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
|
|
||||||
width: '390px',
|
|
||||||
height: '400px',
|
|
||||||
});
|
|
||||||
@@ -1,191 +1,84 @@
|
|||||||
import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption';
|
import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption';
|
||||||
import {
|
import { createLitPortal } from '@blocksuite/affine-components/portal';
|
||||||
createPopup,
|
|
||||||
popupTargetFromElement,
|
|
||||||
} from '@blocksuite/affine-components/context-menu';
|
|
||||||
import { DefaultInlineManagerExtension } from '@blocksuite/affine-inline-preset';
|
import { DefaultInlineManagerExtension } from '@blocksuite/affine-inline-preset';
|
||||||
import {
|
import { type CalloutBlockModel } from '@blocksuite/affine-model';
|
||||||
type CalloutBlockModel,
|
|
||||||
type ParagraphBlockModel,
|
|
||||||
} from '@blocksuite/affine-model';
|
|
||||||
import { focusTextModel } from '@blocksuite/affine-rich-text';
|
|
||||||
import { EDGELESS_TOP_CONTENTEDITABLE_SELECTOR } from '@blocksuite/affine-shared/consts';
|
import { EDGELESS_TOP_CONTENTEDITABLE_SELECTOR } from '@blocksuite/affine-shared/consts';
|
||||||
import {
|
import {
|
||||||
DocModeProvider,
|
DocModeProvider,
|
||||||
type IconData,
|
ThemeProvider,
|
||||||
IconPickerServiceIdentifier,
|
|
||||||
IconType,
|
|
||||||
} from '@blocksuite/affine-shared/services';
|
} from '@blocksuite/affine-shared/services';
|
||||||
import type { UniComponent } from '@blocksuite/affine-shared/types';
|
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
||||||
import * as icons from '@blocksuite/icons/lit';
|
|
||||||
import type { BlockComponent } from '@blocksuite/std';
|
import type { BlockComponent } from '@blocksuite/std';
|
||||||
import { type Signal } from '@preact/signals-core';
|
import { flip, offset } from '@floating-ui/dom';
|
||||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
import { css, html } from 'lit';
|
||||||
import type { TemplateResult } from 'lit';
|
import { query } from 'lit/decorators.js';
|
||||||
import { html } from 'lit';
|
import { styleMap } from 'lit/directives/style-map.js';
|
||||||
import { type StyleInfo, styleMap } from 'lit/directives/style-map.js';
|
|
||||||
|
|
||||||
import {
|
|
||||||
calloutBlockContainerStyles,
|
|
||||||
calloutChildrenStyles,
|
|
||||||
calloutEmojiContainerStyles,
|
|
||||||
calloutEmojiStyles,
|
|
||||||
calloutHostStyles,
|
|
||||||
} from './callout-block-styles.js';
|
|
||||||
import { IconPickerWrapper } from './icon-picker-wrapper.js';
|
|
||||||
// Copy of renderUniLit and UniLit from affine-data-view
|
|
||||||
export const renderUniLit = <Props, Expose extends NonNullable<unknown>>(
|
|
||||||
uni: UniComponent<Props, Expose> | undefined,
|
|
||||||
props?: Props,
|
|
||||||
options?: {
|
|
||||||
ref?: Signal<Expose | undefined>;
|
|
||||||
style?: Readonly<StyleInfo>;
|
|
||||||
class?: string;
|
|
||||||
}
|
|
||||||
): TemplateResult => {
|
|
||||||
return html` <uni-lit
|
|
||||||
.uni="${uni}"
|
|
||||||
.props="${props}"
|
|
||||||
.ref="${options?.ref}"
|
|
||||||
style=${options?.style ? styleMap(options?.style) : ''}
|
|
||||||
></uni-lit>`;
|
|
||||||
};
|
|
||||||
const getIcon = (icon?: IconData) => {
|
|
||||||
if (!icon) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (icon.type === IconType.Emoji) {
|
|
||||||
return icon.unicode;
|
|
||||||
}
|
|
||||||
if (icon.type === IconType.AffineIcon) {
|
|
||||||
return (
|
|
||||||
icons as Record<string, (props: { style: string }) => TemplateResult>
|
|
||||||
)[`${icon.name}Icon`]?.({ style: `color:${icon.color}` });
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
export class CalloutBlockComponent extends CaptionedBlockComponent<CalloutBlockModel> {
|
export class CalloutBlockComponent extends CaptionedBlockComponent<CalloutBlockModel> {
|
||||||
private _popupCloseHandler: (() => void) | null = null;
|
static override styles = css`
|
||||||
|
:host {
|
||||||
override connectedCallback() {
|
display: block;
|
||||||
super.connectedCallback();
|
margin: 8px 0;
|
||||||
this.classList.add(calloutHostStyles);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getEmojiMarginTop(): string {
|
|
||||||
if (this.model.children.length === 0) {
|
|
||||||
return '10px';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const firstChild = this.model.children[0];
|
.affine-callout-block-container {
|
||||||
const flavour = firstChild.flavour;
|
display: flex;
|
||||||
|
padding: 5px 10px;
|
||||||
const marginTopMap: Record<string, string> = {
|
border-radius: 8px;
|
||||||
'affine:paragraph:h1': '23px',
|
background-color: ${unsafeCSSVarV2('block/callout/background/grey')};
|
||||||
'affine:paragraph:h2': '20px',
|
|
||||||
'affine:paragraph:h3': '16px',
|
|
||||||
'affine:paragraph:h4': '15px',
|
|
||||||
'affine:paragraph:h5': '14px',
|
|
||||||
'affine:paragraph:h6': '13px',
|
|
||||||
};
|
|
||||||
|
|
||||||
// For heading blocks, use the type to determine margin
|
|
||||||
if (flavour === 'affine:paragraph') {
|
|
||||||
const paragraph = firstChild as ParagraphBlockModel;
|
|
||||||
const type = paragraph.props.type$.value;
|
|
||||||
const key = `${flavour}:${type}`;
|
|
||||||
return marginTopMap[key] || '10px';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default for all other block types
|
.affine-callout-emoji-container {
|
||||||
return '10px';
|
margin-right: 10px;
|
||||||
}
|
margin-top: 14px;
|
||||||
|
user-select: none;
|
||||||
private _closeIconPicker() {
|
font-size: 1.2em;
|
||||||
if (this._popupCloseHandler) {
|
width: 24px;
|
||||||
this._popupCloseHandler();
|
height: 24px;
|
||||||
this._popupCloseHandler = null;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
}
|
.affine-callout-emoji:hover {
|
||||||
|
cursor: pointer;
|
||||||
private _toggleIconPicker(event: MouseEvent) {
|
opacity: 0.7;
|
||||||
// If popup is already open, close it
|
|
||||||
if (this._popupCloseHandler) {
|
|
||||||
this._closeIconPicker();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get IconPickerService from the framework
|
.affine-callout-children {
|
||||||
const iconPickerService = this.std.getOptional(IconPickerServiceIdentifier);
|
flex: 1;
|
||||||
if (!iconPickerService) {
|
min-width: 0;
|
||||||
console.warn('IconPickerService not found');
|
padding-left: 10px;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
// Get the uni-component from the service
|
private _emojiMenuAbortController: AbortController | null = null;
|
||||||
const iconPickerComponent = iconPickerService.iconPickerComponent;
|
private readonly _toggleEmojiMenu = () => {
|
||||||
|
if (this._emojiMenuAbortController) {
|
||||||
|
this._emojiMenuAbortController.abort();
|
||||||
|
}
|
||||||
|
this._emojiMenuAbortController = new AbortController();
|
||||||
|
|
||||||
// Create props for the icon picker
|
const theme = this.std.get(ThemeProvider).theme$.value;
|
||||||
const props = {
|
|
||||||
onSelect: (iconData?: IconData) => {
|
createLitPortal({
|
||||||
this.model.props.icon$.value = iconData;
|
template: html`<affine-emoji-menu
|
||||||
this._closeIconPicker(); // Close the picker after selection
|
.theme=${theme}
|
||||||
|
.onEmojiSelect=${(data: any) => {
|
||||||
|
this.model.props.emoji = data.native;
|
||||||
|
}}
|
||||||
|
></affine-emoji-menu>`,
|
||||||
|
portalStyles: {
|
||||||
|
zIndex: 'var(--affine-z-index-popover)',
|
||||||
},
|
},
|
||||||
onClose: () => {
|
container: this.host,
|
||||||
this._closeIconPicker();
|
computePosition: {
|
||||||
},
|
referenceElement: this._emojiButton,
|
||||||
};
|
placement: 'bottom-start',
|
||||||
|
middleware: [flip(), offset(4)],
|
||||||
// Create IconPickerWrapper instance
|
autoUpdate: { animationFrame: true },
|
||||||
const wrapper = new IconPickerWrapper();
|
|
||||||
wrapper.iconPickerComponent = iconPickerComponent;
|
|
||||||
wrapper.props = props;
|
|
||||||
wrapper.style.position = 'absolute';
|
|
||||||
wrapper.style.backgroundColor = cssVarV2.layer.background.overlayPanel;
|
|
||||||
wrapper.style.boxShadow = 'var(--affine-menu-shadow)';
|
|
||||||
wrapper.style.borderRadius = '8px';
|
|
||||||
|
|
||||||
// Create popup target from the clicked element
|
|
||||||
const target = popupTargetFromElement(event.currentTarget as HTMLElement);
|
|
||||||
|
|
||||||
// Create popup
|
|
||||||
this._popupCloseHandler = createPopup(target, wrapper, {
|
|
||||||
onClose: () => {
|
|
||||||
this._popupCloseHandler = null;
|
|
||||||
},
|
},
|
||||||
|
abortController: this._emojiMenuAbortController,
|
||||||
|
closeOnClickAway: true,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
private readonly _handleBlockClick = (event: MouseEvent) => {
|
|
||||||
// Check if the click target is emoji related element
|
|
||||||
const target = event.target as HTMLElement;
|
|
||||||
if (
|
|
||||||
target.closest('.affine-callout-emoji-container') ||
|
|
||||||
target.classList.contains('affine-callout-emoji')
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there's no icon, open icon picker on click
|
|
||||||
const icon = this.model.props.icon$.value;
|
|
||||||
if (!icon) {
|
|
||||||
this._toggleIconPicker(event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only handle clicks when there are no children
|
|
||||||
if (this.model.children.length > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent event bubbling
|
|
||||||
event.stopPropagation();
|
|
||||||
|
|
||||||
// Create a new paragraph block
|
|
||||||
const paragraphId = this.store.addBlock('affine:paragraph', {}, this.model);
|
|
||||||
|
|
||||||
// Focus the new paragraph
|
|
||||||
focusTextModel(this.std, paragraphId);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
get attributeRenderer() {
|
get attributeRenderer() {
|
||||||
@@ -204,6 +97,9 @@ export class CalloutBlockComponent extends CaptionedBlockComponent<CalloutBlockM
|
|||||||
return this.std.get(DefaultInlineManagerExtension.identifier);
|
return this.std.get(DefaultInlineManagerExtension.identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@query('.affine-callout-emoji')
|
||||||
|
private accessor _emojiButton!: HTMLElement;
|
||||||
|
|
||||||
override get topContenteditableElement() {
|
override get topContenteditableElement() {
|
||||||
if (this.std.get(DocModeProvider).getEditorMode() === 'edgeless') {
|
if (this.std.get(DocModeProvider).getEditorMode() === 'edgeless') {
|
||||||
return this.closest<BlockComponent>(
|
return this.closest<BlockComponent>(
|
||||||
@@ -214,39 +110,20 @@ export class CalloutBlockComponent extends CaptionedBlockComponent<CalloutBlockM
|
|||||||
}
|
}
|
||||||
|
|
||||||
override renderBlock() {
|
override renderBlock() {
|
||||||
const icon = this.model.props.icon$.value;
|
const emoji = this.model.props.emoji$.value;
|
||||||
const backgroundColorName = this.model.props.backgroundColorName$.value;
|
|
||||||
const backgroundColor = (
|
|
||||||
cssVarV2.block.callout.background as Record<string, string>
|
|
||||||
)[backgroundColorName ?? ''];
|
|
||||||
|
|
||||||
const iconContent = getIcon(icon);
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div class="affine-callout-block-container">
|
||||||
class="${calloutBlockContainerStyles}"
|
<div
|
||||||
@click=${this._handleBlockClick}
|
@click=${this._toggleEmojiMenu}
|
||||||
style=${styleMap({
|
contenteditable="false"
|
||||||
backgroundColor: backgroundColor ?? 'transparent',
|
class="affine-callout-emoji-container"
|
||||||
})}
|
style=${styleMap({
|
||||||
>
|
display: emoji.length === 0 ? 'none' : undefined,
|
||||||
${iconContent
|
})}
|
||||||
? html`
|
>
|
||||||
<div
|
<span class="affine-callout-emoji">${emoji}</span>
|
||||||
@click=${this._toggleIconPicker}
|
</div>
|
||||||
contenteditable="false"
|
<div class="affine-callout-children">
|
||||||
class="${calloutEmojiContainerStyles}"
|
|
||||||
style=${styleMap({
|
|
||||||
marginTop: this._getEmojiMarginTop(),
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<span class="${calloutEmojiStyles}" data-testid="callout-emoji"
|
|
||||||
>${iconContent}</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: ''}
|
|
||||||
<div class="${calloutChildrenStyles}">
|
|
||||||
${this.renderChildren(this.model)}
|
${this.renderChildren(this.model)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
import {
|
import { CalloutBlockModel } from '@blocksuite/affine-model';
|
||||||
CalloutBlockModel,
|
|
||||||
ParagraphBlockModel,
|
|
||||||
} from '@blocksuite/affine-model';
|
|
||||||
import { matchModels } from '@blocksuite/affine-shared/utils';
|
import { matchModels } from '@blocksuite/affine-shared/utils';
|
||||||
import {
|
import {
|
||||||
BlockSelection,
|
BlockSelection,
|
||||||
@@ -9,46 +6,13 @@ import {
|
|||||||
TextSelection,
|
TextSelection,
|
||||||
} from '@blocksuite/std';
|
} from '@blocksuite/std';
|
||||||
|
|
||||||
import { calloutToParagraphCommand } from './commands/callout-to-paragraph.js';
|
|
||||||
import { splitCalloutCommand } from './commands/split-callout.js';
|
|
||||||
|
|
||||||
export const CalloutKeymapExtension = KeymapExtension(std => {
|
export const CalloutKeymapExtension = KeymapExtension(std => {
|
||||||
return {
|
return {
|
||||||
Enter: ctx => {
|
|
||||||
const text = std.selection.find(TextSelection);
|
|
||||||
if (!text) return false;
|
|
||||||
|
|
||||||
const currentBlock = std.store.getBlock(text.from.blockId);
|
|
||||||
if (!currentBlock) return false;
|
|
||||||
|
|
||||||
// Check if current block is a callout block
|
|
||||||
let calloutBlock = currentBlock;
|
|
||||||
if (!matchModels(currentBlock.model, [CalloutBlockModel])) {
|
|
||||||
// If not, check if the parent is a callout block
|
|
||||||
const parent = std.store.getParent(currentBlock.model);
|
|
||||||
if (!parent || !matchModels(parent, [CalloutBlockModel])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const parentBlock = std.store.getBlock(parent.id);
|
|
||||||
if (!parentBlock) return false;
|
|
||||||
calloutBlock = parentBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.get('keyboardState').raw.preventDefault();
|
|
||||||
std.command
|
|
||||||
.chain()
|
|
||||||
.pipe(splitCalloutCommand, {
|
|
||||||
blockId: calloutBlock.model.id,
|
|
||||||
inlineIndex: text.from.index,
|
|
||||||
currentBlockId: text.from.blockId,
|
|
||||||
})
|
|
||||||
.run();
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
Backspace: ctx => {
|
Backspace: ctx => {
|
||||||
const text = std.selection.find(TextSelection);
|
const text = std.selection.find(TextSelection);
|
||||||
if (text && text.isCollapsed() && text.from.index === 0) {
|
if (text && text.isCollapsed() && text.from.index === 0) {
|
||||||
const event = ctx.get('defaultState').event;
|
const event = ctx.get('defaultState').event;
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
const block = std.store.getBlock(text.from.blockId);
|
const block = std.store.getBlock(text.from.blockId);
|
||||||
if (!block) return false;
|
if (!block) return false;
|
||||||
@@ -56,22 +20,6 @@ export const CalloutKeymapExtension = KeymapExtension(std => {
|
|||||||
if (!parent) return false;
|
if (!parent) return false;
|
||||||
if (!matchModels(parent, [CalloutBlockModel])) return false;
|
if (!matchModels(parent, [CalloutBlockModel])) return false;
|
||||||
|
|
||||||
// Check if current block is a paragraph inside callout
|
|
||||||
if (matchModels(block.model, [ParagraphBlockModel])) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
std.command
|
|
||||||
.chain()
|
|
||||||
.pipe(calloutToParagraphCommand, {
|
|
||||||
id: block.model.id,
|
|
||||||
})
|
|
||||||
.run();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback to selecting the callout block
|
|
||||||
event.preventDefault();
|
|
||||||
std.selection.setGroup('note', [
|
std.selection.setGroup('note', [
|
||||||
std.selection.create(BlockSelection, {
|
std.selection.create(BlockSelection, {
|
||||||
blockId: parent.id,
|
blockId: parent.id,
|
||||||
|
|||||||
@@ -1,86 +0,0 @@
|
|||||||
import {
|
|
||||||
CalloutBlockModel,
|
|
||||||
ParagraphBlockModel,
|
|
||||||
} from '@blocksuite/affine-model';
|
|
||||||
import { focusTextModel } from '@blocksuite/affine-rich-text';
|
|
||||||
import { matchModels } from '@blocksuite/affine-shared/utils';
|
|
||||||
import type { Command } from '@blocksuite/std';
|
|
||||||
import { BlockSelection } from '@blocksuite/std';
|
|
||||||
import { Text } from '@blocksuite/store';
|
|
||||||
|
|
||||||
export const calloutToParagraphCommand: Command<
|
|
||||||
{
|
|
||||||
id: string;
|
|
||||||
stopCapturing?: boolean;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
success: boolean;
|
|
||||||
}
|
|
||||||
> = (ctx, next) => {
|
|
||||||
const { id, stopCapturing = true } = ctx;
|
|
||||||
const std = ctx.std;
|
|
||||||
const doc = std.store;
|
|
||||||
const model = doc.getBlock(id)?.model;
|
|
||||||
|
|
||||||
if (!model || !matchModels(model, [ParagraphBlockModel])) return false;
|
|
||||||
|
|
||||||
const parent = doc.getParent(model);
|
|
||||||
if (!parent || !matchModels(parent, [CalloutBlockModel])) return false;
|
|
||||||
|
|
||||||
if (stopCapturing) std.store.captureSync();
|
|
||||||
|
|
||||||
// Get current block index in callout
|
|
||||||
const currentIndex = parent.children.indexOf(model);
|
|
||||||
const hasText = model.text && model.text.length > 0;
|
|
||||||
|
|
||||||
// Find previous paragraph block in callout
|
|
||||||
let previousBlock = null;
|
|
||||||
for (let i = currentIndex - 1; i >= 0; i--) {
|
|
||||||
const sibling = parent.children[i];
|
|
||||||
if (matchModels(sibling, [ParagraphBlockModel])) {
|
|
||||||
previousBlock = sibling;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (previousBlock && hasText) {
|
|
||||||
// Clone current text content before any operations to prevent data loss
|
|
||||||
const currentText = model.text || new Text();
|
|
||||||
|
|
||||||
// Get previous block text and merge index
|
|
||||||
const previousText = previousBlock.text || new Text();
|
|
||||||
const mergeIndex = previousText.length;
|
|
||||||
|
|
||||||
// Apply each delta from cloned current text to previous block to preserve formatting
|
|
||||||
previousText.join(currentText);
|
|
||||||
|
|
||||||
// Remove current block after text has been merged
|
|
||||||
doc.deleteBlock(model, {
|
|
||||||
deleteChildren: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Focus at merge point in previous block
|
|
||||||
focusTextModel(std, previousBlock.id, mergeIndex);
|
|
||||||
} else if (previousBlock && !hasText) {
|
|
||||||
// Move cursor to end of previous block
|
|
||||||
doc.deleteBlock(model, {
|
|
||||||
deleteChildren: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const previousText = previousBlock.text || new Text();
|
|
||||||
focusTextModel(std, previousBlock.id, previousText.length);
|
|
||||||
} else {
|
|
||||||
// No previous block, select the entire callout
|
|
||||||
doc.deleteBlock(model, {
|
|
||||||
deleteChildren: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
std.selection.setGroup('note', [
|
|
||||||
std.selection.create(BlockSelection, {
|
|
||||||
blockId: parent.id,
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return next({ success: true });
|
|
||||||
};
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
import {
|
|
||||||
CalloutBlockModel,
|
|
||||||
ParagraphBlockModel,
|
|
||||||
} from '@blocksuite/affine-model';
|
|
||||||
import { focusTextModel } from '@blocksuite/affine-rich-text';
|
|
||||||
import { matchModels } from '@blocksuite/affine-shared/utils';
|
|
||||||
import type { Command, EditorHost } from '@blocksuite/std';
|
|
||||||
|
|
||||||
export const splitCalloutCommand: Command<{
|
|
||||||
blockId: string;
|
|
||||||
inlineIndex: number;
|
|
||||||
currentBlockId: string;
|
|
||||||
}> = (ctx, next) => {
|
|
||||||
const { blockId, inlineIndex, currentBlockId, std } = ctx;
|
|
||||||
const host = std.host as EditorHost;
|
|
||||||
const doc = host.store;
|
|
||||||
|
|
||||||
const calloutModel = doc.getBlock(blockId)?.model;
|
|
||||||
if (!calloutModel || !matchModels(calloutModel, [CalloutBlockModel])) {
|
|
||||||
console.error(`block ${blockId} is not a callout block`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentModel = doc.getBlock(currentBlockId)?.model;
|
|
||||||
if (!currentModel) {
|
|
||||||
console.error(`current block ${currentBlockId} not found`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
doc.captureSync();
|
|
||||||
|
|
||||||
if (matchModels(currentModel, [ParagraphBlockModel])) {
|
|
||||||
// User is in a paragraph within the callout's children
|
|
||||||
const afterText = currentModel.props.text.split(inlineIndex);
|
|
||||||
|
|
||||||
// Update the current paragraph's text to keep only the part before cursor
|
|
||||||
doc.transact(() => {
|
|
||||||
currentModel.props.text.delete(
|
|
||||||
inlineIndex,
|
|
||||||
currentModel.props.text.length - inlineIndex
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create a new paragraph block after the current one
|
|
||||||
const parent = doc.getParent(currentModel);
|
|
||||||
if (parent) {
|
|
||||||
const currentIndex = parent.children.indexOf(currentModel);
|
|
||||||
const newParagraphId = doc.addBlock(
|
|
||||||
'affine:paragraph',
|
|
||||||
{
|
|
||||||
text: afterText,
|
|
||||||
},
|
|
||||||
parent,
|
|
||||||
currentIndex + 1
|
|
||||||
);
|
|
||||||
|
|
||||||
if (newParagraphId) {
|
|
||||||
host.updateComplete
|
|
||||||
.then(() => {
|
|
||||||
focusTextModel(std, newParagraphId);
|
|
||||||
})
|
|
||||||
.catch(console.error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If current block is not a paragraph, create a new paragraph in callout
|
|
||||||
const newParagraphId = doc.addBlock(
|
|
||||||
'affine:paragraph',
|
|
||||||
{
|
|
||||||
text: new Text(),
|
|
||||||
},
|
|
||||||
calloutModel
|
|
||||||
);
|
|
||||||
|
|
||||||
if (newParagraphId) {
|
|
||||||
host.updateComplete
|
|
||||||
.then(() => {
|
|
||||||
focusTextModel(std, newParagraphId);
|
|
||||||
})
|
|
||||||
.catch(console.error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
@@ -1,11 +1,24 @@
|
|||||||
|
import { CalloutBlockModel } from '@blocksuite/affine-model';
|
||||||
import { focusBlockEnd } from '@blocksuite/affine-shared/commands';
|
import { focusBlockEnd } from '@blocksuite/affine-shared/commands';
|
||||||
import { isInsideBlockByFlavour } from '@blocksuite/affine-shared/utils';
|
import { FeatureFlagService } from '@blocksuite/affine-shared/services';
|
||||||
|
import {
|
||||||
|
findAncestorModel,
|
||||||
|
isInsideBlockByFlavour,
|
||||||
|
matchModels,
|
||||||
|
} from '@blocksuite/affine-shared/utils';
|
||||||
import { type SlashMenuConfig } from '@blocksuite/affine-widget-slash-menu';
|
import { type SlashMenuConfig } from '@blocksuite/affine-widget-slash-menu';
|
||||||
import { FontIcon } from '@blocksuite/icons/lit';
|
import { FontIcon } from '@blocksuite/icons/lit';
|
||||||
|
|
||||||
import { calloutTooltip } from './tooltips';
|
import { calloutTooltip } from './tooltips';
|
||||||
|
|
||||||
export const calloutSlashMenuConfig: SlashMenuConfig = {
|
export const calloutSlashMenuConfig: SlashMenuConfig = {
|
||||||
|
disableWhen: ({ model }) => {
|
||||||
|
return (
|
||||||
|
findAncestorModel(model, ancestor =>
|
||||||
|
matchModels(ancestor, [CalloutBlockModel])
|
||||||
|
) !== null
|
||||||
|
);
|
||||||
|
},
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
name: 'Callout',
|
name: 'Callout',
|
||||||
@@ -17,11 +30,10 @@ export const calloutSlashMenuConfig: SlashMenuConfig = {
|
|||||||
},
|
},
|
||||||
searchAlias: ['callout'],
|
searchAlias: ['callout'],
|
||||||
group: '0_Basic@9',
|
group: '0_Basic@9',
|
||||||
when: ({ model }) => {
|
when: ({ std, model }) => {
|
||||||
return !isInsideBlockByFlavour(
|
return (
|
||||||
model.store,
|
std.get(FeatureFlagService).getFlag('enable_callout') &&
|
||||||
model,
|
!isInsideBlockByFlavour(model.store, model, 'affine:edgeless-text')
|
||||||
'affine:edgeless-text'
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
action: ({ model, std }) => {
|
action: ({ model, std }) => {
|
||||||
|
|||||||
@@ -1,204 +0,0 @@
|
|||||||
import {
|
|
||||||
createPopup,
|
|
||||||
popupTargetFromElement,
|
|
||||||
} from '@blocksuite/affine-components/context-menu';
|
|
||||||
import { EditorChevronDown } from '@blocksuite/affine-components/toolbar';
|
|
||||||
import { CalloutBlockModel } from '@blocksuite/affine-model';
|
|
||||||
import {
|
|
||||||
ActionPlacement,
|
|
||||||
type IconData,
|
|
||||||
IconPickerServiceIdentifier,
|
|
||||||
type ToolbarAction,
|
|
||||||
type ToolbarActionGroup,
|
|
||||||
type ToolbarModuleConfig,
|
|
||||||
ToolbarModuleExtension,
|
|
||||||
} from '@blocksuite/affine-shared/services';
|
|
||||||
import { DeleteIcon, PaletteIcon, SmileIcon } from '@blocksuite/icons/lit';
|
|
||||||
import { BlockFlavourIdentifier } from '@blocksuite/std';
|
|
||||||
import type { ExtensionType } from '@blocksuite/store';
|
|
||||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
|
||||||
import { html } from 'lit';
|
|
||||||
import { repeat } from 'lit/directives/repeat.js';
|
|
||||||
import { styleMap } from 'lit/directives/style-map.js';
|
|
||||||
|
|
||||||
import { IconPickerWrapper } from '../icon-picker-wrapper.js';
|
|
||||||
|
|
||||||
const colors = [
|
|
||||||
'default',
|
|
||||||
'red',
|
|
||||||
'orange',
|
|
||||||
'yellow',
|
|
||||||
'green',
|
|
||||||
'teal',
|
|
||||||
'blue',
|
|
||||||
'purple',
|
|
||||||
'grey',
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
const backgroundColorAction = {
|
|
||||||
id: 'background-color',
|
|
||||||
label: 'Background Color',
|
|
||||||
tooltip: 'Change background color',
|
|
||||||
icon: PaletteIcon(),
|
|
||||||
run() {
|
|
||||||
// This will be handled by the content function
|
|
||||||
},
|
|
||||||
content(ctx) {
|
|
||||||
const model = ctx.getCurrentModelByType(CalloutBlockModel);
|
|
||||||
if (!model) return null;
|
|
||||||
|
|
||||||
const updateBackground = (color: string) => {
|
|
||||||
ctx.store.updateBlock(model, { backgroundColorName: color });
|
|
||||||
};
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<editor-menu-button
|
|
||||||
.contentPadding=${'8px'}
|
|
||||||
.button=${html`
|
|
||||||
<editor-icon-button
|
|
||||||
aria-label="background"
|
|
||||||
.tooltip=${'Background Color'}
|
|
||||||
>
|
|
||||||
${PaletteIcon()} ${EditorChevronDown}
|
|
||||||
</editor-icon-button>
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
<div data-size="large" data-orientation="vertical">
|
|
||||||
<div class="highlight-heading">Background</div>
|
|
||||||
${repeat(colors, color => {
|
|
||||||
const isDefault = color === 'default';
|
|
||||||
const value = isDefault
|
|
||||||
? null
|
|
||||||
: `var(--affine-text-highlight-${color})`;
|
|
||||||
const displayName = `${color} Background`;
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<editor-menu-action
|
|
||||||
data-testid="background-${color}"
|
|
||||||
@click=${() => updateBackground(color)}
|
|
||||||
>
|
|
||||||
<affine-text-duotone-icon
|
|
||||||
style=${styleMap({
|
|
||||||
'--color': 'var(--affine-text-primary-color)',
|
|
||||||
'--background': value ?? 'transparent',
|
|
||||||
})}
|
|
||||||
></affine-text-duotone-icon>
|
|
||||||
<span class="label capitalize">${displayName}</span>
|
|
||||||
</editor-menu-action>
|
|
||||||
`;
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</editor-menu-button>
|
|
||||||
`;
|
|
||||||
},
|
|
||||||
} satisfies ToolbarAction;
|
|
||||||
|
|
||||||
const iconPickerAction = {
|
|
||||||
id: 'icon-picker',
|
|
||||||
label: 'Icon Picker',
|
|
||||||
tooltip: 'Change icon',
|
|
||||||
icon: SmileIcon(),
|
|
||||||
run() {
|
|
||||||
// This will be handled by the content function
|
|
||||||
},
|
|
||||||
content(ctx) {
|
|
||||||
const model = ctx.getCurrentModelByType(CalloutBlockModel);
|
|
||||||
if (!model) return null;
|
|
||||||
|
|
||||||
const handleIconPickerClick = (event: MouseEvent) => {
|
|
||||||
// Get IconPickerService from the framework
|
|
||||||
const iconPickerService = ctx.std.getOptional(
|
|
||||||
IconPickerServiceIdentifier
|
|
||||||
);
|
|
||||||
if (!iconPickerService) {
|
|
||||||
console.warn('IconPickerService not found');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the uni-component from the service
|
|
||||||
const iconPickerComponent = iconPickerService.iconPickerComponent;
|
|
||||||
|
|
||||||
// Create props for the icon picker
|
|
||||||
const props = {
|
|
||||||
onSelect: (iconData?: IconData) => {
|
|
||||||
// When iconData is undefined (delete icon), set icon to undefined
|
|
||||||
ctx.store.updateBlock(model, { icon: iconData });
|
|
||||||
closeHandler(); // Close the picker after selection
|
|
||||||
},
|
|
||||||
onClose: () => {
|
|
||||||
closeHandler();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create IconPickerWrapper instance
|
|
||||||
const wrapper = new IconPickerWrapper();
|
|
||||||
wrapper.iconPickerComponent = iconPickerComponent;
|
|
||||||
wrapper.props = props;
|
|
||||||
wrapper.style.position = 'absolute';
|
|
||||||
wrapper.style.backgroundColor = cssVarV2.layer.background.overlayPanel;
|
|
||||||
wrapper.style.boxShadow = 'var(--affine-menu-shadow)';
|
|
||||||
wrapper.style.borderRadius = '8px';
|
|
||||||
|
|
||||||
// Create popup target from the clicked element
|
|
||||||
const target = popupTargetFromElement(event.currentTarget as HTMLElement);
|
|
||||||
|
|
||||||
// Create popup
|
|
||||||
const closeHandler = createPopup(target, wrapper, {
|
|
||||||
onClose: () => {
|
|
||||||
// Cleanup if needed
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<editor-icon-button
|
|
||||||
aria-label="icon-picker"
|
|
||||||
.tooltip=${'Change Icon'}
|
|
||||||
@click=${handleIconPickerClick}
|
|
||||||
>
|
|
||||||
${SmileIcon()} ${EditorChevronDown}
|
|
||||||
</editor-icon-button>
|
|
||||||
`;
|
|
||||||
},
|
|
||||||
} satisfies ToolbarAction;
|
|
||||||
|
|
||||||
const builtinToolbarConfig = {
|
|
||||||
actions: [
|
|
||||||
{
|
|
||||||
id: 'style',
|
|
||||||
actions: [backgroundColorAction],
|
|
||||||
} satisfies ToolbarActionGroup<ToolbarAction>,
|
|
||||||
{
|
|
||||||
id: 'icon',
|
|
||||||
actions: [iconPickerAction],
|
|
||||||
} satisfies ToolbarActionGroup<ToolbarAction>,
|
|
||||||
{
|
|
||||||
placement: ActionPlacement.More,
|
|
||||||
id: 'c.delete',
|
|
||||||
label: 'Delete',
|
|
||||||
icon: DeleteIcon(),
|
|
||||||
variant: 'destructive',
|
|
||||||
run(ctx) {
|
|
||||||
const model = ctx.getCurrentModelByType(CalloutBlockModel);
|
|
||||||
if (!model) return;
|
|
||||||
|
|
||||||
ctx.store.deleteBlock(model);
|
|
||||||
|
|
||||||
// Clears
|
|
||||||
ctx.select('note');
|
|
||||||
ctx.reset();
|
|
||||||
},
|
|
||||||
} satisfies ToolbarAction,
|
|
||||||
],
|
|
||||||
} as const satisfies ToolbarModuleConfig;
|
|
||||||
|
|
||||||
export const createBuiltinToolbarConfigExtension = (
|
|
||||||
flavour: string
|
|
||||||
): ExtensionType[] => {
|
|
||||||
return [
|
|
||||||
ToolbarModuleExtension({
|
|
||||||
id: BlockFlavourIdentifier(flavour),
|
|
||||||
config: builtinToolbarConfig,
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
};
|
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
import { CalloutBlockComponent } from './callout-block';
|
import { CalloutBlockComponent } from './callout-block';
|
||||||
import { IconPickerWrapper } from './icon-picker-wrapper';
|
import { EmojiMenu } from './emoji-menu';
|
||||||
|
|
||||||
export function effects() {
|
export function effects() {
|
||||||
customElements.define('affine-callout', CalloutBlockComponent);
|
customElements.define('affine-callout', CalloutBlockComponent);
|
||||||
customElements.define('icon-picker-wrapper', IconPickerWrapper);
|
customElements.define('affine-emoji-menu', EmojiMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
'affine-callout': CalloutBlockComponent;
|
'affine-callout': CalloutBlockComponent;
|
||||||
'icon-picker-wrapper': IconPickerWrapper;
|
'affine-emoji-menu': EmojiMenu;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
34
blocksuite/affine/blocks/callout/src/emoji-menu.ts
Normal file
34
blocksuite/affine/blocks/callout/src/emoji-menu.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { WithDisposable } from '@blocksuite/global/lit';
|
||||||
|
import data from '@emoji-mart/data';
|
||||||
|
import { Picker } from 'emoji-mart';
|
||||||
|
import { html, LitElement, type PropertyValues } from 'lit';
|
||||||
|
import { property, query } from 'lit/decorators.js';
|
||||||
|
|
||||||
|
export class EmojiMenu extends WithDisposable(LitElement) {
|
||||||
|
override firstUpdated(props: PropertyValues) {
|
||||||
|
const result = super.firstUpdated(props);
|
||||||
|
|
||||||
|
const picker = new Picker({
|
||||||
|
data,
|
||||||
|
onEmojiSelect: this.onEmojiSelect,
|
||||||
|
autoFocus: true,
|
||||||
|
theme: this.theme,
|
||||||
|
});
|
||||||
|
this.emojiMenu.append(picker as unknown as Node);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor onEmojiSelect: (data: any) => void = () => {};
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor theme: 'light' | 'dark' = 'light';
|
||||||
|
|
||||||
|
@query('.affine-emoji-menu')
|
||||||
|
accessor emojiMenu!: HTMLElement;
|
||||||
|
|
||||||
|
override render() {
|
||||||
|
return html`<div class="affine-emoji-menu"></div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
import type { IconData } from '@blocksuite/affine-shared/services';
|
|
||||||
import type { UniComponent } from '@blocksuite/affine-shared/types';
|
|
||||||
import { ShadowlessElement } from '@blocksuite/std';
|
|
||||||
import { type Signal } from '@preact/signals-core';
|
|
||||||
import { html, type TemplateResult } from 'lit';
|
|
||||||
import { type StyleInfo, styleMap } from 'lit/directives/style-map.js';
|
|
||||||
|
|
||||||
// Copy of renderUniLit from callout-block.ts
|
|
||||||
const renderUniLit = <Props, Expose extends NonNullable<unknown>>(
|
|
||||||
uni: UniComponent<Props, Expose> | undefined,
|
|
||||||
props?: Props,
|
|
||||||
options?: {
|
|
||||||
ref?: Signal<Expose | undefined>;
|
|
||||||
style?: Readonly<StyleInfo>;
|
|
||||||
class?: string;
|
|
||||||
}
|
|
||||||
): TemplateResult => {
|
|
||||||
return html` <uni-lit
|
|
||||||
.uni="${uni}"
|
|
||||||
.props="${props}"
|
|
||||||
.ref="${options?.ref}"
|
|
||||||
style=${options?.style ? styleMap(options?.style) : ''}
|
|
||||||
></uni-lit>`;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface IconPickerWrapperProps {
|
|
||||||
onSelect?: (iconData?: IconData) => void;
|
|
||||||
onClose?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class IconPickerWrapper extends ShadowlessElement {
|
|
||||||
iconPickerComponent?: UniComponent<IconPickerWrapperProps, any>;
|
|
||||||
props?: IconPickerWrapperProps;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
override render() {
|
|
||||||
if (!this.iconPickerComponent) {
|
|
||||||
return html``;
|
|
||||||
}
|
|
||||||
|
|
||||||
return renderUniLit(this.iconPickerComponent, this.props);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
'icon-picker-wrapper': IconPickerWrapper;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,6 @@ import { literal } from 'lit/static-html.js';
|
|||||||
|
|
||||||
import { CalloutKeymapExtension } from './callout-keymap';
|
import { CalloutKeymapExtension } from './callout-keymap';
|
||||||
import { calloutSlashMenuConfig } from './configs/slash-menu';
|
import { calloutSlashMenuConfig } from './configs/slash-menu';
|
||||||
import { createBuiltinToolbarConfigExtension } from './configs/toolbar';
|
|
||||||
import { effects } from './effects';
|
import { effects } from './effects';
|
||||||
|
|
||||||
export class CalloutViewExtension extends ViewExtensionProvider {
|
export class CalloutViewExtension extends ViewExtensionProvider {
|
||||||
@@ -26,7 +25,6 @@ export class CalloutViewExtension extends ViewExtensionProvider {
|
|||||||
BlockViewExtension('affine:callout', literal`affine-callout`),
|
BlockViewExtension('affine:callout', literal`affine-callout`),
|
||||||
CalloutKeymapExtension,
|
CalloutKeymapExtension,
|
||||||
SlashMenuConfigExtension('affine:callout', calloutSlashMenuConfig),
|
SlashMenuConfigExtension('affine:callout', calloutSlashMenuConfig),
|
||||||
...createBuiltinToolbarConfigExtension('affine:callout'),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,19 +22,19 @@
|
|||||||
"@blocksuite/affine-shared": "workspace:*",
|
"@blocksuite/affine-shared": "workspace:*",
|
||||||
"@blocksuite/affine-widget-slash-menu": "workspace:*",
|
"@blocksuite/affine-widget-slash-menu": "workspace:*",
|
||||||
"@blocksuite/global": "workspace:*",
|
"@blocksuite/global": "workspace:*",
|
||||||
"@blocksuite/icons": "^2.2.17",
|
"@blocksuite/icons": "^2.2.12",
|
||||||
"@blocksuite/std": "workspace:*",
|
"@blocksuite/std": "workspace:*",
|
||||||
"@blocksuite/store": "workspace:*",
|
"@blocksuite/store": "workspace:*",
|
||||||
"@floating-ui/dom": "^1.6.13",
|
"@floating-ui/dom": "^1.6.13",
|
||||||
"@lit/context": "^1.1.2",
|
"@lit/context": "^1.1.2",
|
||||||
"@preact/signals-core": "^1.8.0",
|
"@preact/signals-core": "^1.8.0",
|
||||||
"@toeverything/theme": "^1.1.23",
|
"@toeverything/theme": "^1.1.16",
|
||||||
"@types/mdast": "^4.0.4",
|
"@types/mdast": "^4.0.4",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"minimatch": "^10.1.1",
|
"minimatch": "^10.0.1",
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.1",
|
||||||
"shiki": "^3.19.0",
|
"shiki": "^3.0.0",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts",
|
".": "./src/index.ts",
|
||||||
@@ -48,5 +48,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.22.4"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
export const CODE_BLOCK_DEFAULT_DARK_THEME =
|
export const CODE_BLOCK_DEFAULT_DARK_THEME = import(
|
||||||
import('shiki/themes/dark-plus.mjs');
|
'shiki/themes/dark-plus.mjs'
|
||||||
export const CODE_BLOCK_DEFAULT_LIGHT_THEME =
|
);
|
||||||
import('shiki/themes/light-plus.mjs');
|
export const CODE_BLOCK_DEFAULT_LIGHT_THEME = import(
|
||||||
|
'shiki/themes/light-plus.mjs'
|
||||||
|
);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user