diff --git a/.github/workflows/docker-deploy.yml b/.github/workflows/docker-deploy.yml index ac95f4cc96..451055e68c 100644 --- a/.github/workflows/docker-deploy.yml +++ b/.github/workflows/docker-deploy.yml @@ -61,19 +61,25 @@ jobs: username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - uses: pnpm/action-setup@v2 - - name: Install frontend dependencies + - name: Install static dependencies run: | git submodule update --init NODE_ENV=production pnpm --filter cherrypick-js install NODE_ENV=production pnpm --filter frontend install - - name: Build frontend + - name: Build static run: | - sed -i "s/outDir: __dirname + '\/..\/..\/built\/_vite_'/outDir: __dirname + '\/..\/..\/vite'/" packages/frontend/vite.config.ts + NODE_ENV=production pnpm build-pre NODE_ENV=production pnpm --filter cherrypick-js build NODE_ENV=production pnpm --filter frontend build - sed -i "s/outDir: __dirname + '\/..\/..\/vite'/outDir: __dirname + '\/..\/..\/built\/_vite_'/" packages/frontend/vite.config.ts - - name: Deploy frontend - run: echo "${{ secrets.UPLOAD_SCRIPT }}" | base64 -d | node + NODE_ENV=production pnpm build-locales + - name: Deploy static + run: | + mv built/_frontend_dist_ built/assets + mv built/_vite_ built/vite + mv built ${{ env.CLIENT_ASSETS_DIR }} + echo "${{ secrets.UPLOAD_SCRIPT }}" | base64 -d | node + mv ${{ env.CLIENT_ASSETS_DIR }}/vite ${{ env.CLIENT_ASSETS_DIR }}/_vite_ + mv ${{ env.CLIENT_ASSETS_DIR }}/assets ${{ env.CLIENT_ASSETS_DIR }}/_frontend_dist_ - name: Build and Push to Container registry uses: docker/build-push-action@v5 env: diff --git a/deploy.Dockerfile b/deploy.Dockerfile index 7d28c0846c..1fc3d68a6f 100644 --- a/deploy.Dockerfile +++ b/deploy.Dockerfile @@ -33,9 +33,11 @@ RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \ COPY --link . ./ RUN git submodule update --init -RUN sed -i '/packages\/frontend/ s/^/# /' pnpm-workspace.yaml +RUN sed -i '/packages\/frontend/ s/^/# /' pnpm-workspace.yaml \ + && sed -i '/packages\/cherrypick-js/ s/^/# /' pnpm-workspace.yaml RUN pnpm build -RUN sed -i '/packages\/frontend/ s/^# //' pnpm-workspace.yaml +RUN sed -i '/packages\/frontend/ s/^# //' pnpm-workspace.yaml \ + && sed -i '/packages\/cherrypick-js/ s/^# //' pnpm-workspace.yaml RUN rm -rf .git/ # build native dependencies for target platform @@ -88,7 +90,8 @@ COPY --chown=cherrypick:cherrypick --from=native-builder /cherrypick/packages/ba COPY --chown=cherrypick:cherrypick --from=native-builder /cherrypick/fluent-emojis /cherrypick/fluent-emojis COPY --chown=cherrypick:cherrypick . ./ -RUN mv ./vite ./built/_vite_ +RUN cp -rf ./${ARGS_CLIENT_ASSETS_DIR}/* ./built \ + && rm -rf ./${ARGS_CLIENT_ASSETS_DIR} ENV LD_PRELOAD=/usr/local/lib/libjemalloc.so ENV MALLOC_CONF=background_thread:true,metadata_thp:auto,dirty_decay_ms:30000,muzzy_decay_ms:30000 diff --git a/package.json b/package.json index 121c5dc572..c859bf1c51 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,10 @@ "scripts": { "build-pre": "node ./scripts/build-pre.js", "build-assets": "node ./scripts/build-assets.mjs", + "build-assets-d": "node ./scripts/build-assets-d.mjs", + "build-locales": "node ./scripts/build-locales.mjs", "build": "pnpm build-pre && pnpm -r build && pnpm build-assets", + "build-d": "pnpm -r build && pnpm build-assets-d", "build-storybook": "pnpm --filter frontend build-storybook", "start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js", "start:docker": "pnpm check:connect && cd packages/backend && exec node ./built/boot/entry.js", diff --git a/packages/sw/build.js b/packages/sw/build.js index 55fab5088f..648cbe7c3d 100644 --- a/packages/sw/build.js +++ b/packages/sw/build.js @@ -13,6 +13,9 @@ const watch = process.argv[2]?.includes('watch'); const __dirname = fileURLToPath(new URL('.', import.meta.url)) +const CLIENT_ASSETS_BASE_URL = process.env.CLIENT_ASSETS_BASE_URL; +const CLIENT_ASSETS_DIR = process.env.CLIENT_ASSETS_DIR; + console.log('Starting SW building...'); /** @type {esbuild.BuildOptions} */ @@ -26,6 +29,7 @@ const buildOptions = { _PERF_PREFIX_: JSON.stringify('CherryPick:'), _VERSION_: JSON.stringify(meta.version), _BASEDMISSKEYVERSION_: JSON.stringify(meta.basedMisskeyVersion), + _CLIENT_ASSETS_BASE_URL_: JSON.stringify(CLIENT_ASSETS_BASE_URL && CLIENT_ASSETS_DIR ? `${CLIENT_ASSETS_BASE_URL}/${CLIENT_ASSETS_DIR}` : ""), }, entryPoints: [`${__dirname}/src/sw.ts`], format: 'esm', diff --git a/packages/sw/src/@types/global.d.ts b/packages/sw/src/@types/global.d.ts index 59015bb3be..9152b43d14 100644 --- a/packages/sw/src/@types/global.d.ts +++ b/packages/sw/src/@types/global.d.ts @@ -12,3 +12,4 @@ declare const _BASEDMISSKEYVERSION_: string; declare const _ENV_: string; declare const _DEV_: boolean; declare const _PERF_PREFIX_: string; +declare const _CLIENT_ASSETS_BASE_URL_: string; diff --git a/packages/sw/src/scripts/lang.ts b/packages/sw/src/scripts/lang.ts index 13dd496d9e..6584aaa4a5 100644 --- a/packages/sw/src/scripts/lang.ts +++ b/packages/sw/src/scripts/lang.ts @@ -31,7 +31,7 @@ class SwLang { private async _fetch(): Promise> { // Service Workerは何度も起動しそのたびにlocaleを読み込むので、CacheStorageを使う - const localeUrl = `/assets/locales/${await this.lang}.${_VERSION_}.json`; + const localeUrl = `${_CLIENT_ASSETS_BASE_URL_}/assets/locales/${await this.lang}.${_VERSION_}.json`; let localeRes = await caches.match(localeUrl); // _DEV_がtrueの場合は常に最新化 diff --git a/scripts/build-assets-d.mjs b/scripts/build-assets-d.mjs new file mode 100644 index 0000000000..b22f492cbd --- /dev/null +++ b/scripts/build-assets-d.mjs @@ -0,0 +1,70 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import * as fs from 'node:fs/promises'; +import * as path from 'node:path'; +import cssnano from 'cssnano'; +import postcss from 'postcss'; +import * as terser from 'terser'; + +import locales from '../locales/index.js'; + +async function copyFrontendFonts() { + await fs.cp('./packages/frontend/node_modules/three/examples/fonts', './built/_frontend_dist_/fonts', { dereference: true, recursive: true }); +} + +async function copyBackendViews() { + await fs.cp('./packages/backend/src/server/web/views', './packages/backend/built/server/web/views', { recursive: true }); +} + +async function buildBackendScript() { + await fs.mkdir('./packages/backend/built/server/web', { recursive: true }); + + for (const file of [ + './packages/backend/src/server/web/boot.js', + './packages/backend/src/server/web/bios.js', + './packages/backend/src/server/web/cli.js' + ]) { + let source = await fs.readFile(file, { encoding: 'utf-8' }); + source = source.replaceAll('LANGS', JSON.stringify(Object.keys(locales))); + const { code } = await terser.minify(source, { toplevel: true }); + await fs.writeFile(`./packages/backend/built/server/web/${path.basename(file)}`, code); + } +} + +async function buildBackendStyle() { + await fs.mkdir('./packages/backend/built/server/web', { recursive: true }); + + for (const file of [ + './packages/backend/src/server/web/style.css', + './packages/backend/src/server/web/bios.css', + './packages/backend/src/server/web/cli.css', + './packages/backend/src/server/web/error.css' + ]) { + const source = await fs.readFile(file, { encoding: 'utf-8' }); + const { css } = await postcss([cssnano({ zindex: false })]).process(source, { from: undefined }); + await fs.writeFile(`./packages/backend/built/server/web/${path.basename(file)}`, css); + } +} + +async function build() { + await Promise.all([ + copyFrontendFonts(), + copyBackendViews(), + buildBackendScript(), + buildBackendStyle(), + ]); +} + +await build(); + +if (process.argv.includes("--watch")) { + const watcher = fs.watch('./packages', { recursive: true }); + for await (const event of watcher) { + if (/^[a-z]+\/src/.test(event.filename)) { + await build(); + } + } +} diff --git a/scripts/build-locales.mjs b/scripts/build-locales.mjs new file mode 100644 index 0000000000..808321da54 --- /dev/null +++ b/scripts/build-locales.mjs @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import * as fs from 'node:fs/promises'; +import locales from '../locales/index.js'; +import generateDTS from '../locales/generateDTS.js'; +import meta from '../package.json' assert { type: "json" }; + +async function copyFrontendLocales() { + generateDTS(); + + await fs.mkdir('./built/_frontend_dist_/locales', { recursive: true }); + + const v = { '_version_': meta.version }; + + for (const [lang, locale] of Object.entries(locales)) { + await fs.writeFile(`./built/_frontend_dist_/locales/${lang}.${meta.version}.json`, JSON.stringify({ ...locale, ...v }), 'utf-8'); + } +} + +async function build() { + await Promise.all([ + copyFrontendLocales(), + ]); +} + +await build(); + +if (process.argv.includes("--watch")) { + const watcher = fs.watch('./packages', { recursive: true }); + for await (const event of watcher) { + if (/^[a-z]+\/src/.test(event.filename)) { + await build(); + } + } +}