From 7e097c93f90bda5c2b9cc539a883a996932b8980 Mon Sep 17 00:00:00 2001 From: Ryan Wang Date: Sat, 21 Jun 2025 20:47:16 +0800 Subject: [PATCH] chore: refine plugin development documentation (#503) * chore: refine plugin development documentation Signed-off-by: Ryan Wang * chore: refine plugin development documentation Signed-off-by: Ryan Wang * chore: refine plugin development documentation Signed-off-by: Ryan Wang --------- Signed-off-by: Ryan Wang --- .editorconfig | 9 + .../developer-guide/plugin/basics/ui/build.md | 414 ++++++++++++++++++ .../developer-guide/plugin/basics/ui/entry.md | 79 +++- .../developer-guide/plugin/basics/ui/intro.md | 4 +- docs/developer-guide/plugin/publish.md | 137 ++---- docusaurus.config.js | 6 +- sidebars.js | 1 + .../developer-guide/plugin/basics/ui/build.md | 414 ++++++++++++++++++ .../developer-guide/plugin/basics/ui/entry.md | 79 +++- .../developer-guide/plugin/basics/ui/intro.md | 4 +- .../developer-guide/plugin/publish.md | 137 ++---- versioned_sidebars/version-2.21-sidebars.json | 13 +- 12 files changed, 1028 insertions(+), 269 deletions(-) create mode 100644 .editorconfig create mode 100644 docs/developer-guide/plugin/basics/ui/build.md create mode 100644 versioned_docs/version-2.21/developer-guide/plugin/basics/ui/build.md diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..45b5839 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*.md] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = true \ No newline at end of file diff --git a/docs/developer-guide/plugin/basics/ui/build.md b/docs/developer-guide/plugin/basics/ui/build.md new file mode 100644 index 0000000..081b741 --- /dev/null +++ b/docs/developer-guide/plugin/basics/ui/build.md @@ -0,0 +1,414 @@ +--- +title: 构建 +description: UI 部分的构建说明 +--- + +在 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 模板中,我们已经配置好了 UI 的构建工具和流程,此文档主要说明一些构建细节以及其他可能的构建选项。 + +## 原理 + +Halo 插件的 UI 部分(Console / UC)的实现方式其实很简单,本质上就是构建一个结构固定的大对象,交给 Halo 去解析,其中包括全局注册的组件、路由定义、扩展点等。在 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 模板中,我们使用 `index.ts` 作为入口文件,并在构建之后将 `main.js` 和 `style.css` 放到插件项目的 `src/main/resources/console` 目录中,后续 Halo 在内部会自动合并所有插件的 `main.js` 和 `style.css` 文件,并生成最终的 `bundle.js` 和 `bundle.css` 文件,然后在 Console 和 UC 中加载这两个资源并解析。 + +所以本质上,我们只需要使用支持将 `index.ts` 编译为 `main.js` 和 `style.css` 的工具,然后输出到插件项目的 `src/main/resources/console` 目录中即可,在 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 模板中可以看到,我们提供了一个名为 `@halo-dev/ui-plugin-bundler-kit` 的库,这个库包含了 [Vite](https://vite.dev/) 和 [Rsbuild](https://rsbuild.dev/) 的预配置,插件项目只需要通过简单的配置即可使用。 + +## @halo-dev/ui-plugin-bundler-kit + +在这个库中,我们提供了三个预配置,分别是: + +1. `viteConfig`: Vite 的预配置,[halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 中默认使用的配置 +2. `rsbuildConfig`: Rsbuild 的预配置 +3. `HaloUIPluginBundlerKit`:已过时,迁移方式可以参考下面的文档 + +### viteConfig + +#### 使用 + +如果你想要使用 Vite 构建 UI 部分,那么使用 `viteConfig` 即可,并且已经在 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 中配置,直接使用即可。 + +#### 配置 + +```js +import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit"; + +export default viteConfig({ + vite: { + // 自定义 Vite 配置 + plugins: [ + // 额外的插件(Vue 插件已预配置) + ], + // 其他配置... + }, +}); +``` + +示例: + +1. 添加路径别名 + + ```js + import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit"; + import path from "path"; + + export default viteConfig({ + vite: { + resolve: { + alias: { + "@": path.resolve(__dirname, "src"), + "@components": path.resolve(__dirname, "src/components"), + }, + }, + }, + }); + ``` + +2. 添加额外的 Vite 插件 + + ```js + import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit"; + import { defineConfig } from "vite"; + import UnoCSS from "unocss/vite"; + + export default viteConfig({ + vite: { + plugins: [ + UnoCSS(), // 添加 UnoCSS 插件 + ], + }, + }); + ``` + +### rsbuildConfig + +Rsbuild 是基于 Rspack 开发的上层构建工具,其优势在于兼容 Webpack 生态并且性能优异。我们为什么要选择 Rsbuild 可以查阅 [Vite vs Rsbuild](#vite-vs-rsbuild)。 + +#### 使用 + +因为在 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 中,默认采用 Vite 构建,所以如果想要使用 Rsbuild 构建,需要手动配置,以下是切换到 Rsbuild 的过程: + +安装依赖: + +```bash +pnpm install @halo-dev/ui-plugin-bundler-kit@2.21.1 @rsbuild/core -D +``` + +创建 rsbuild.config.mjs: + +```js +import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit"; + +export default rsbuildConfig() +``` + +更新 package.json: + +```json +{ + "type": "module", + "scripts": { + "dev": "rsbuild build --env-mode development --watch", + "build": "rsbuild build" + } +} +``` + +#### 配置 + +```js +import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit"; + +export default rsbuildConfig({ + rsbuild: { + // 自定义 Rsbuild 配置 + plugins: [ + // 额外的插件(Vue 插件已预配置) + ], + // 其他配置... + }, +}); +``` + +示例: + +1. 添加路径别名 + + ```js + import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit"; + + export default rsbuildConfig({ + rsbuild: { + source: { + alias: { + "@": "./src", + "@components": "./src/components", + }, + }, + }, + }); + ``` + +2. 添加额外的 Rsbuild 插件 + + ```js + import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit"; + import { pluginSass } from "@rsbuild/plugin-sass"; + + export default rsbuildConfig({ + rsbuild: { + plugins: [ + pluginSass(), // 添加 Sass 插件 + ], + }, + }); + ``` + +### HaloUIPluginBundlerKit + +旧版本 [plugin-starter](https://github.com/halo-dev/plugin-starter) 使用的方式,目前已经不再推荐。 + +## 构建输出 + +在 `viteConfig` 和 `rsbuildConfig` 中,已经配置好了开发环境和生产构建的输出目录,分别是: + +- **开发环境**:`build/resources/main/console`,在开发 UI 的过程中,可以使用 `pnpm dev` 来实时查看效果 +- **生产环境**:`ui/build/dist` + +> 需要注意的是,生产构建的目录仅仅是临时目录,最终在使用 Gradle 构建插件时会自动构建 UI 并复制到 `src/main/resources/console` 目录中。 + +## Vite vs Rsbuild{#vite-vs-rsbuild} + +Vite 和 Rsbuild 都是优秀的构建工具,但它们在不同的使用场景下有各自的优势: + +### 何时使用 Rsbuild + +- ✅ **代码分割支持** - Rsbuild 为代码分割和懒加载提供了优秀的支持 +- ✅ **更好的性能** - 对于复杂应用,通常有更快的构建时间和更小的包体积 +- ✅ **动态导入** - 非常适合有重度前端组件的插件 + +**动态导入示例:** + +```typescript +import { definePlugin } from '@halo-dev/console-shared'; +import { defineAsyncComponent } from 'vue'; +import { VLoading } from '@halo-dev/components'; + +export default definePlugin({ + routes: [ + { + parentName: 'Root', + route: { + path: 'demo', + name: 'DemoPage', + // 懒加载重型组件 + component: defineAsyncComponent({ + loader: () => import('./views/DemoPage.vue'), + loadingComponent: VLoading, + }), + }, + }, + ], + extensionPoints: {}, +}); +``` + +### 何时使用 Vite + +- ✅ **Vue 生态友好** - 与 Vue 生态系统工具和插件有更好的集成 +- ✅ **丰富的插件生态** - 有大量可用的 Vite 插件 +- ✅ **简单配置** - 对于直接的使用场景更容易配置 + +### 总结 + +| 特性 | Vite | Rsbuild | +| ---------- | ------ | -------- | +| 代码分割 | ❌ 有限 | ✅ 优秀 | +| Vue 生态 | ✅ 优秀 | ✅ 良好 | +| 构建性能 | ✅ 良好 | ✅ 优秀 | +| 开发体验 | ✅ 优秀 | ✅ 优秀 | +| 插件生态 | ✅ 丰富 | ✅ 增长中 | +| 配置复杂度 | ✅ 简单 | ⚖️ 中等 | + +**建议**:对于有大型前端代码库的复杂插件使用 **Rsbuild**,对于简单插件或需要广泛 Vue 生态系统集成时使用 **Vite**。 + +## 迁移{#migration} + +如果你当前的插件使用的是旧版本的 [plugin-starter](https://github.com/halo-dev/plugin-starter),并且想使用新的 `viteConfig` 和 `rsbuildConfig`,可以参考以下步骤: + +1. 更新 `@halo-dev/ui-plugin-bundler-kit` 至 `2.21.1` 或更高版本 + + ```bash + pnpm install @halo-dev/ui-plugin-bundler-kit@2.21.1 -D + ``` + +2. 更新 `vite.config.ts` 文件 + + ```typescript + import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit"; + + export default viteConfig({ + vite: { + // Vite 配置需要按照原有的配置进行修改,但需要移除 Vue 插件,因为已经内置 + plugins: [ + ], + }, + }); + ``` + +3. 更新项目根目录的 `build.gradle` 文件 + + ```diff + plugins { + id 'java' + - id "com.github.node-gradle.node" version "7.0.2" + - id "io.freefair.lombok" version "8.0.1" + - id "run.halo.plugin.devtools" version "0.2.0" + + id "io.freefair.lombok" version "8.13" + + id "run.halo.plugin.devtools" version "0.6.0" + } + + group 'run.halo.starter' + -sourceCompatibility = JavaVersion.VERSION_17 + + repositories { + mavenCentral() + - maven { url 'https://s01.oss.sonatype.org/content/repositories/releases' } + - maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' } + - maven { url 'https://repo.spring.io/milestone' } + } + + dependencies { + - implementation platform('run.halo.tools.platform:plugin:2.20.0-SNAPSHOT') + + implementation platform('run.halo.tools.platform:plugin:2.21.0') + compileOnly 'run.halo.app:api' + + testImplementation 'run.halo.app:api' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + } + + test { + useJUnitPlatform() + } + + -tasks.withType(JavaCompile).configureEach { + - options.encoding = "UTF-8" + -} + - + -node { + - nodeProjectDir = file("${project.projectDir}/ui") + +java { + + toolchain { + + languageVersion = JavaLanguageVersion.of(21) + + } + } + + -tasks.register('buildFrontend', PnpmTask) { + - args = ['build'] + - dependsOn('installDepsForUI') + +tasks.withType(JavaCompile).configureEach { + + options.encoding = "UTF-8" + + options.release = 21 + } + + -tasks.register('installDepsForUI', PnpmTask) { + - args = ['install'] + +tasks.register('processUiResources', Copy) { + + from project(':ui').tasks.named('buildFrontend') + + into layout.buildDirectory.dir('resources/main/console') + } + + -build { + - // build frontend before build + - tasks.named('compileJava').configure { + - dependsOn('buildFrontend') + - } + +tasks.named('processResources', ProcessResources) { + + dependsOn tasks.named('processUiResources') + } + ``` + +4. 在 ui 或者 console 目录新建 `build.gradle` 文件,内容如下: + + ```gradle + plugins { + id 'base' + id "com.github.node-gradle.node" version "7.1.0" + } + + group 'run.halo.starter.ui' + + tasks.register('buildFrontend', PnpmTask) { + args = ['build'] + dependsOn tasks.named('pnpmInstall') + inputs.dir(layout.projectDirectory.dir('src')) + inputs.files(fileTree( + dir: layout.projectDirectory, + includes: ['*.cjs', '*.ts', '*.js', '*.json', '*.yaml'])) + outputs.dir(layout.buildDirectory.dir('dist')) + shouldRunAfter(tasks.named('check')) + } + + tasks.register('checkFrontend', PnpmTask) { + args = ['test:unit'] + dependsOn tasks.named('pnpmInstall') + } + + tasks.named('check') { + dependsOn tasks.named('checkFrontend') + } + + tasks.named('build') { + dependsOn tasks.named('buildFrontend') + } + ``` + +进行此变更的主要目的是保证 UI 构建的产物不直接输出到源码目录的 resources 目录中,而是通过 Gradle 构建插件时复制到 `src/main/resources/console` 目录中。 + +完整变更过程可参考:[halo-dev/plugin-starter#52](https://github.com/halo-dev/plugin-starter/pull/52) + +如果你不想使用新的 Gradle 构建配置,也可以修改 viteConfig 或 rsbuildConfig 的输出目录,和旧版本保持一致: + +viteConfig: + +```js +import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit"; + +const OUT_DIR_PROD = "../src/main/resources/console"; +const OUT_DIR_DEV = "../build/resources/main/console"; + +export default viteConfig({ + vite: ({ mode }) => { + const isProduction = mode === "production"; + const outDir = isProduction ? OUT_DIR_PROD : OUT_DIR_DEV; + + return { + build: { + outDir, + }, + }; + }, +}); +``` + +rsbuildConfig: + +```js +import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit"; + +const OUT_DIR_PROD = "../src/main/resources/console"; +const OUT_DIR_DEV = "../build/resources/main/console"; + +export default rsbuildConfig({ + rsbuild: ({ envMode }) => { + const isProduction = envMode === "production"; + const outDir = isProduction ? OUT_DIR_PROD : OUT_DIR_DEV; + + return { + output: { + distPath: { + root: outDir, + }, + }, + }; + }, +}); +``` diff --git a/docs/developer-guide/plugin/basics/ui/entry.md b/docs/developer-guide/plugin/basics/ui/entry.md index 0fd26e6..666f520 100644 --- a/docs/developer-guide/plugin/basics/ui/entry.md +++ b/docs/developer-guide/plugin/basics/ui/entry.md @@ -29,28 +29,37 @@ export function definePlugin(plugin: PluginModule): PluginModule { ``` ```ts title="PluginModule" -import type { Component, Ref } from "vue"; -import type { RouteRecordRaw, RouteRecordName } from "vue-router"; -import type { FunctionalPage } from "../states/pages"; -import type { AttachmentSelectProvider } from "../states/attachment-selector"; -import type { EditorProvider, PluginTab } from ".."; -import type { AnyExtension } from "@halo-dev/richtext-editor"; -import type { CommentSubjectRefProvider } from "@/states/comment-subject-ref"; import type { BackupTab } from "@/states/backup"; -import type { PluginInstallationTab } from "@/states/plugin-installation-tabs"; +import type { CommentSubjectRefProvider } from "@/states/comment-subject-ref"; import type { EntityFieldItem } from "@/states/entity"; import type { OperationItem } from "@/states/operation"; +import type { PluginInstallationTab } from "@/states/plugin-installation-tabs"; import type { ThemeListTab } from "@/states/theme-list-tabs"; +import type { UserProfileTab, UserTab } from "@/states/user-tab"; import type { Attachment, Backup, ListedPost, Plugin, Theme, + ListedComment, + ListedReply, + ListedSinglePage, } from "@halo-dev/api-client"; +import type { AnyExtension } from "@halo-dev/richtext-editor"; +import type { Component, Ref } from "vue"; +import type { RouteRecordName, RouteRecordRaw } from "vue-router"; +import type { + DashboardWidgetDefinition, + DashboardWidgetQuickActionItem, + EditorProvider, + PluginTab, +} from ".."; +import type { AttachmentSelectProvider } from "../states/attachment-selector"; +import type { FunctionalPage } from "../states/pages"; export interface RouteRecordAppend { - parentName: RouteRecordName; + parentName: NonNullable; route: RouteRecordRaw; } @@ -80,36 +89,65 @@ export interface ExtensionPoint { "post:list-item:operation:create"?: ( post: Ref - ) => OperationItem[] | Promise[]>; + ) => OperationItem[]; + + "single-page:list-item:operation:create"?: ( + singlePage: Ref + ) => OperationItem[]; + + "comment:list-item:operation:create"?: ( + comment: Ref + ) => OperationItem[]; + + "reply:list-item:operation:create"?: ( + reply: Ref + ) => OperationItem[]; "plugin:list-item:operation:create"?: ( plugin: Ref - ) => OperationItem[] | Promise[]>; + ) => OperationItem[]; "backup:list-item:operation:create"?: ( backup: Ref - ) => OperationItem[] | Promise[]>; + ) => OperationItem[]; "attachment:list-item:operation:create"?: ( attachment: Ref - ) => OperationItem[] | Promise[]>; + ) => OperationItem[]; - "plugin:list-item:field:create"?: ( - plugin: Ref - ) => EntityFieldItem[] | Promise; + "plugin:list-item:field:create"?: (plugin: Ref) => EntityFieldItem[]; - "post:list-item:field:create"?: ( - post: Ref - ) => EntityFieldItem[] | Promise; + "post:list-item:field:create"?: (post: Ref) => EntityFieldItem[]; + + "single-page:list-item:field:create"?: ( + singlePage: Ref + ) => EntityFieldItem[]; "theme:list:tabs:create"?: () => ThemeListTab[] | Promise; "theme:list-item:operation:create"?: ( theme: Ref - ) => OperationItem[] | Promise[]>; + ) => OperationItem[]; + + "user:detail:tabs:create"?: () => UserTab[] | Promise; + + "uc:user:profile:tabs:create"?: () => + | UserProfileTab[] + | Promise; + + "console:dashboard:widgets:create"?: () => + | DashboardWidgetDefinition[] + | Promise; + + "console:dashboard:widgets:internal:quick-action:item:create"?: () => + | DashboardWidgetQuickActionItem[] + | Promise; } export interface PluginModule { + /** + * These components will be registered when plugin is activated. + */ components?: Record; routes?: RouteRecordRaw[] | RouteRecordAppend[]; @@ -118,6 +156,7 @@ export interface PluginModule { extensionPoints?: ExtensionPoint; } + ``` - `components`:组件列表,key 为组件名称,value 为组件对象,在此定义之后,加载插件时会自动注册到 Vue App 全局。 diff --git a/docs/developer-guide/plugin/basics/ui/intro.md b/docs/developer-guide/plugin/basics/ui/intro.md index f6c33d8..d72b6a2 100644 --- a/docs/developer-guide/plugin/basics/ui/intro.md +++ b/docs/developer-guide/plugin/basics/ui/intro.md @@ -7,6 +7,6 @@ Halo 插件体系的 UI 部分可以让开发者在 Console 控制台和 UC 个 在开始之前,建议先熟悉或安装以下库和工具: -1. [Node.js 18+](https://nodejs.org) -2. [pnpm 8+](https://pnpm.io) +1. [Node.js 20+](https://nodejs.org) +2. [pnpm 10+](https://pnpm.io) 3. [Vue.js 3](https://vuejs.org) diff --git a/docs/developer-guide/plugin/publish.md b/docs/developer-guide/plugin/publish.md index db3c98d..34f8479 100644 --- a/docs/developer-guide/plugin/publish.md +++ b/docs/developer-guide/plugin/publish.md @@ -11,126 +11,51 @@ description: 了解如何与我们的社区分享你的插件 ## 自动构建 -如果你是基于 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 创建的插件项目,那么已经包含了适用于 GitHub Action 的 `workflow.yaml` 文件,里面包含了构建插件和发布插件资源到 Release 的步骤,可以根据自己的实际需要进行修改,以下是示例: +如果你是基于 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 创建的插件项目,那么已经包含了适用于 GitHub Action 的 `ci.yaml` 和 `cd.yaml` 文件,里面包含了构建插件和发布插件资源到 Release 的步骤,可以根据自己的实际需要进行修改,以下是示例: ```yaml -name: Build Plugin JAR File +name: CI on: push: branches: - main - paths: - - "**" - - "!**.md" - release: - types: - - created pull_request: branches: - main - paths: - - "**" - - "!**.md" jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - name: Set up JDK 17 - uses: actions/setup-java@v2 - with: - distribution: 'temurin' - cache: 'gradle' - java-version: 17 - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - node-version: 18 - - uses: pnpm/action-setup@v2.0.1 - name: Install pnpm - id: pnpm-install - with: - version: 8 - run_install: false - - name: Get pnpm store directory - id: pnpm-cache - run: | - echo "::set-output name=pnpm_cache_dir::$(pnpm store path)" - - uses: actions/cache@v3 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/widget/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - name: Install Frontend Dependencies - run: | - ./gradlew pnpmInstall - - name: Build with Gradle - run: | - # Set the version with tag name when releasing - version=${{ github.event.release.tag_name }} - version=${version#v} - sed -i "s/version=.*-SNAPSHOT$/version=$version/1" gradle.properties - ./gradlew clean build -x test - - name: Archive plugin-starter jar - uses: actions/upload-artifact@v2 - with: - name: plugin-starter - path: | - build/libs/*.jar - retention-days: 1 - - github-release: - runs-on: ubuntu-latest - needs: build - if: github.event_name == 'release' - steps: - - name: Download plugin-starter jar - uses: actions/download-artifact@v2 - with: - name: plugin-starter - path: build/libs - - name: Get Name of Artifact - id: get_artifact - run: | - ARTIFACT_PATHNAME=$(ls build/libs/*.jar | head -n 1) - ARTIFACT_NAME=$(basename ${ARTIFACT_PATHNAME}) - echo "Artifact pathname: ${ARTIFACT_PATHNAME}" - echo "Artifact name: ${ARTIFACT_NAME}" - echo "ARTIFACT_PATHNAME=${ARTIFACT_PATHNAME}" >> $GITHUB_ENV - echo "ARTIFACT_NAME=${ARTIFACT_NAME}" >> $GITHUB_ENV - echo "RELEASE_ID=${{ github.event.release.id }}" >> $GITHUB_ENV - - name: Upload a Release Asset - uses: actions/github-script@v2 - if: github.event_name == 'release' - with: - github-token: ${{secrets.GITHUB_TOKEN}} - script: | - console.log('environment', process.versions); - - const fs = require('fs').promises; - - const { repo: { owner, repo }, sha } = context; - console.log({ owner, repo, sha }); - - const releaseId = process.env.RELEASE_ID - const artifactPathName = process.env.ARTIFACT_PATHNAME - const artifactName = process.env.ARTIFACT_NAME - console.log('Releasing', releaseId, artifactPathName, artifactName) - - await github.repos.uploadReleaseAsset({ - owner, repo, - release_id: releaseId, - name: artifactName, - data: await fs.readFile(artifactPathName) - }); + ci: + uses: halo-sigs/reusable-workflows/.github/workflows/plugin-ci.yaml@v3 + with: + ui-path: "ui" + pnpm-version: 9 + node-version: 22 + java-version: 21 ``` +```yaml +name: CD + +on: + release: + types: + - published + +jobs: + cd: + uses: halo-sigs/reusable-workflows/.github/workflows/plugin-cd.yaml@v3 + permissions: + contents: write + with: + pnpm-version: 9 + node-version: 22 + java-version: 21 + skip-appstore-release: true +``` + +关于 CI / CD 的更多详细信息,可查阅:[halo-sigs/reusable-workflows](https://github.com/halo-sigs/reusable-workflows) + ## 发布你的插件 用户可以在你的仓库 Release 下载使用,但为了方便让 Halo 的用户知道你的插件,可以在以下渠道发布: diff --git a/docusaurus.config.js b/docusaurus.config.js index 92fad37..d86f43c 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -202,15 +202,13 @@ const config = { prism: { theme: darkCodeTheme, darkTheme: darkCodeTheme, - additionalLanguages: ["java", "json", "sql"], + additionalLanguages: ["java", "json", "sql", "diff"], }, zoom: { selector: ".markdown :not(a) > img", }, }), - plugins: [ - require.resolve("docusaurus-plugin-image-zoom"), - ], + plugins: [require.resolve("docusaurus-plugin-image-zoom")], scripts: [ { src: "https://track.halo.run/api/script.js", diff --git a/sidebars.js b/sidebars.js index e29bc93..40408cd 100644 --- a/sidebars.js +++ b/sidebars.js @@ -175,6 +175,7 @@ module.exports = { items: [ "developer-guide/plugin/basics/ui/intro", "developer-guide/plugin/basics/ui/entry", + "developer-guide/plugin/basics/ui/build", ], }, ], diff --git a/versioned_docs/version-2.21/developer-guide/plugin/basics/ui/build.md b/versioned_docs/version-2.21/developer-guide/plugin/basics/ui/build.md new file mode 100644 index 0000000..081b741 --- /dev/null +++ b/versioned_docs/version-2.21/developer-guide/plugin/basics/ui/build.md @@ -0,0 +1,414 @@ +--- +title: 构建 +description: UI 部分的构建说明 +--- + +在 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 模板中,我们已经配置好了 UI 的构建工具和流程,此文档主要说明一些构建细节以及其他可能的构建选项。 + +## 原理 + +Halo 插件的 UI 部分(Console / UC)的实现方式其实很简单,本质上就是构建一个结构固定的大对象,交给 Halo 去解析,其中包括全局注册的组件、路由定义、扩展点等。在 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 模板中,我们使用 `index.ts` 作为入口文件,并在构建之后将 `main.js` 和 `style.css` 放到插件项目的 `src/main/resources/console` 目录中,后续 Halo 在内部会自动合并所有插件的 `main.js` 和 `style.css` 文件,并生成最终的 `bundle.js` 和 `bundle.css` 文件,然后在 Console 和 UC 中加载这两个资源并解析。 + +所以本质上,我们只需要使用支持将 `index.ts` 编译为 `main.js` 和 `style.css` 的工具,然后输出到插件项目的 `src/main/resources/console` 目录中即可,在 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 模板中可以看到,我们提供了一个名为 `@halo-dev/ui-plugin-bundler-kit` 的库,这个库包含了 [Vite](https://vite.dev/) 和 [Rsbuild](https://rsbuild.dev/) 的预配置,插件项目只需要通过简单的配置即可使用。 + +## @halo-dev/ui-plugin-bundler-kit + +在这个库中,我们提供了三个预配置,分别是: + +1. `viteConfig`: Vite 的预配置,[halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 中默认使用的配置 +2. `rsbuildConfig`: Rsbuild 的预配置 +3. `HaloUIPluginBundlerKit`:已过时,迁移方式可以参考下面的文档 + +### viteConfig + +#### 使用 + +如果你想要使用 Vite 构建 UI 部分,那么使用 `viteConfig` 即可,并且已经在 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 中配置,直接使用即可。 + +#### 配置 + +```js +import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit"; + +export default viteConfig({ + vite: { + // 自定义 Vite 配置 + plugins: [ + // 额外的插件(Vue 插件已预配置) + ], + // 其他配置... + }, +}); +``` + +示例: + +1. 添加路径别名 + + ```js + import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit"; + import path from "path"; + + export default viteConfig({ + vite: { + resolve: { + alias: { + "@": path.resolve(__dirname, "src"), + "@components": path.resolve(__dirname, "src/components"), + }, + }, + }, + }); + ``` + +2. 添加额外的 Vite 插件 + + ```js + import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit"; + import { defineConfig } from "vite"; + import UnoCSS from "unocss/vite"; + + export default viteConfig({ + vite: { + plugins: [ + UnoCSS(), // 添加 UnoCSS 插件 + ], + }, + }); + ``` + +### rsbuildConfig + +Rsbuild 是基于 Rspack 开发的上层构建工具,其优势在于兼容 Webpack 生态并且性能优异。我们为什么要选择 Rsbuild 可以查阅 [Vite vs Rsbuild](#vite-vs-rsbuild)。 + +#### 使用 + +因为在 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 中,默认采用 Vite 构建,所以如果想要使用 Rsbuild 构建,需要手动配置,以下是切换到 Rsbuild 的过程: + +安装依赖: + +```bash +pnpm install @halo-dev/ui-plugin-bundler-kit@2.21.1 @rsbuild/core -D +``` + +创建 rsbuild.config.mjs: + +```js +import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit"; + +export default rsbuildConfig() +``` + +更新 package.json: + +```json +{ + "type": "module", + "scripts": { + "dev": "rsbuild build --env-mode development --watch", + "build": "rsbuild build" + } +} +``` + +#### 配置 + +```js +import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit"; + +export default rsbuildConfig({ + rsbuild: { + // 自定义 Rsbuild 配置 + plugins: [ + // 额外的插件(Vue 插件已预配置) + ], + // 其他配置... + }, +}); +``` + +示例: + +1. 添加路径别名 + + ```js + import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit"; + + export default rsbuildConfig({ + rsbuild: { + source: { + alias: { + "@": "./src", + "@components": "./src/components", + }, + }, + }, + }); + ``` + +2. 添加额外的 Rsbuild 插件 + + ```js + import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit"; + import { pluginSass } from "@rsbuild/plugin-sass"; + + export default rsbuildConfig({ + rsbuild: { + plugins: [ + pluginSass(), // 添加 Sass 插件 + ], + }, + }); + ``` + +### HaloUIPluginBundlerKit + +旧版本 [plugin-starter](https://github.com/halo-dev/plugin-starter) 使用的方式,目前已经不再推荐。 + +## 构建输出 + +在 `viteConfig` 和 `rsbuildConfig` 中,已经配置好了开发环境和生产构建的输出目录,分别是: + +- **开发环境**:`build/resources/main/console`,在开发 UI 的过程中,可以使用 `pnpm dev` 来实时查看效果 +- **生产环境**:`ui/build/dist` + +> 需要注意的是,生产构建的目录仅仅是临时目录,最终在使用 Gradle 构建插件时会自动构建 UI 并复制到 `src/main/resources/console` 目录中。 + +## Vite vs Rsbuild{#vite-vs-rsbuild} + +Vite 和 Rsbuild 都是优秀的构建工具,但它们在不同的使用场景下有各自的优势: + +### 何时使用 Rsbuild + +- ✅ **代码分割支持** - Rsbuild 为代码分割和懒加载提供了优秀的支持 +- ✅ **更好的性能** - 对于复杂应用,通常有更快的构建时间和更小的包体积 +- ✅ **动态导入** - 非常适合有重度前端组件的插件 + +**动态导入示例:** + +```typescript +import { definePlugin } from '@halo-dev/console-shared'; +import { defineAsyncComponent } from 'vue'; +import { VLoading } from '@halo-dev/components'; + +export default definePlugin({ + routes: [ + { + parentName: 'Root', + route: { + path: 'demo', + name: 'DemoPage', + // 懒加载重型组件 + component: defineAsyncComponent({ + loader: () => import('./views/DemoPage.vue'), + loadingComponent: VLoading, + }), + }, + }, + ], + extensionPoints: {}, +}); +``` + +### 何时使用 Vite + +- ✅ **Vue 生态友好** - 与 Vue 生态系统工具和插件有更好的集成 +- ✅ **丰富的插件生态** - 有大量可用的 Vite 插件 +- ✅ **简单配置** - 对于直接的使用场景更容易配置 + +### 总结 + +| 特性 | Vite | Rsbuild | +| ---------- | ------ | -------- | +| 代码分割 | ❌ 有限 | ✅ 优秀 | +| Vue 生态 | ✅ 优秀 | ✅ 良好 | +| 构建性能 | ✅ 良好 | ✅ 优秀 | +| 开发体验 | ✅ 优秀 | ✅ 优秀 | +| 插件生态 | ✅ 丰富 | ✅ 增长中 | +| 配置复杂度 | ✅ 简单 | ⚖️ 中等 | + +**建议**:对于有大型前端代码库的复杂插件使用 **Rsbuild**,对于简单插件或需要广泛 Vue 生态系统集成时使用 **Vite**。 + +## 迁移{#migration} + +如果你当前的插件使用的是旧版本的 [plugin-starter](https://github.com/halo-dev/plugin-starter),并且想使用新的 `viteConfig` 和 `rsbuildConfig`,可以参考以下步骤: + +1. 更新 `@halo-dev/ui-plugin-bundler-kit` 至 `2.21.1` 或更高版本 + + ```bash + pnpm install @halo-dev/ui-plugin-bundler-kit@2.21.1 -D + ``` + +2. 更新 `vite.config.ts` 文件 + + ```typescript + import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit"; + + export default viteConfig({ + vite: { + // Vite 配置需要按照原有的配置进行修改,但需要移除 Vue 插件,因为已经内置 + plugins: [ + ], + }, + }); + ``` + +3. 更新项目根目录的 `build.gradle` 文件 + + ```diff + plugins { + id 'java' + - id "com.github.node-gradle.node" version "7.0.2" + - id "io.freefair.lombok" version "8.0.1" + - id "run.halo.plugin.devtools" version "0.2.0" + + id "io.freefair.lombok" version "8.13" + + id "run.halo.plugin.devtools" version "0.6.0" + } + + group 'run.halo.starter' + -sourceCompatibility = JavaVersion.VERSION_17 + + repositories { + mavenCentral() + - maven { url 'https://s01.oss.sonatype.org/content/repositories/releases' } + - maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' } + - maven { url 'https://repo.spring.io/milestone' } + } + + dependencies { + - implementation platform('run.halo.tools.platform:plugin:2.20.0-SNAPSHOT') + + implementation platform('run.halo.tools.platform:plugin:2.21.0') + compileOnly 'run.halo.app:api' + + testImplementation 'run.halo.app:api' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + } + + test { + useJUnitPlatform() + } + + -tasks.withType(JavaCompile).configureEach { + - options.encoding = "UTF-8" + -} + - + -node { + - nodeProjectDir = file("${project.projectDir}/ui") + +java { + + toolchain { + + languageVersion = JavaLanguageVersion.of(21) + + } + } + + -tasks.register('buildFrontend', PnpmTask) { + - args = ['build'] + - dependsOn('installDepsForUI') + +tasks.withType(JavaCompile).configureEach { + + options.encoding = "UTF-8" + + options.release = 21 + } + + -tasks.register('installDepsForUI', PnpmTask) { + - args = ['install'] + +tasks.register('processUiResources', Copy) { + + from project(':ui').tasks.named('buildFrontend') + + into layout.buildDirectory.dir('resources/main/console') + } + + -build { + - // build frontend before build + - tasks.named('compileJava').configure { + - dependsOn('buildFrontend') + - } + +tasks.named('processResources', ProcessResources) { + + dependsOn tasks.named('processUiResources') + } + ``` + +4. 在 ui 或者 console 目录新建 `build.gradle` 文件,内容如下: + + ```gradle + plugins { + id 'base' + id "com.github.node-gradle.node" version "7.1.0" + } + + group 'run.halo.starter.ui' + + tasks.register('buildFrontend', PnpmTask) { + args = ['build'] + dependsOn tasks.named('pnpmInstall') + inputs.dir(layout.projectDirectory.dir('src')) + inputs.files(fileTree( + dir: layout.projectDirectory, + includes: ['*.cjs', '*.ts', '*.js', '*.json', '*.yaml'])) + outputs.dir(layout.buildDirectory.dir('dist')) + shouldRunAfter(tasks.named('check')) + } + + tasks.register('checkFrontend', PnpmTask) { + args = ['test:unit'] + dependsOn tasks.named('pnpmInstall') + } + + tasks.named('check') { + dependsOn tasks.named('checkFrontend') + } + + tasks.named('build') { + dependsOn tasks.named('buildFrontend') + } + ``` + +进行此变更的主要目的是保证 UI 构建的产物不直接输出到源码目录的 resources 目录中,而是通过 Gradle 构建插件时复制到 `src/main/resources/console` 目录中。 + +完整变更过程可参考:[halo-dev/plugin-starter#52](https://github.com/halo-dev/plugin-starter/pull/52) + +如果你不想使用新的 Gradle 构建配置,也可以修改 viteConfig 或 rsbuildConfig 的输出目录,和旧版本保持一致: + +viteConfig: + +```js +import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit"; + +const OUT_DIR_PROD = "../src/main/resources/console"; +const OUT_DIR_DEV = "../build/resources/main/console"; + +export default viteConfig({ + vite: ({ mode }) => { + const isProduction = mode === "production"; + const outDir = isProduction ? OUT_DIR_PROD : OUT_DIR_DEV; + + return { + build: { + outDir, + }, + }; + }, +}); +``` + +rsbuildConfig: + +```js +import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit"; + +const OUT_DIR_PROD = "../src/main/resources/console"; +const OUT_DIR_DEV = "../build/resources/main/console"; + +export default rsbuildConfig({ + rsbuild: ({ envMode }) => { + const isProduction = envMode === "production"; + const outDir = isProduction ? OUT_DIR_PROD : OUT_DIR_DEV; + + return { + output: { + distPath: { + root: outDir, + }, + }, + }; + }, +}); +``` diff --git a/versioned_docs/version-2.21/developer-guide/plugin/basics/ui/entry.md b/versioned_docs/version-2.21/developer-guide/plugin/basics/ui/entry.md index 0fd26e6..666f520 100644 --- a/versioned_docs/version-2.21/developer-guide/plugin/basics/ui/entry.md +++ b/versioned_docs/version-2.21/developer-guide/plugin/basics/ui/entry.md @@ -29,28 +29,37 @@ export function definePlugin(plugin: PluginModule): PluginModule { ``` ```ts title="PluginModule" -import type { Component, Ref } from "vue"; -import type { RouteRecordRaw, RouteRecordName } from "vue-router"; -import type { FunctionalPage } from "../states/pages"; -import type { AttachmentSelectProvider } from "../states/attachment-selector"; -import type { EditorProvider, PluginTab } from ".."; -import type { AnyExtension } from "@halo-dev/richtext-editor"; -import type { CommentSubjectRefProvider } from "@/states/comment-subject-ref"; import type { BackupTab } from "@/states/backup"; -import type { PluginInstallationTab } from "@/states/plugin-installation-tabs"; +import type { CommentSubjectRefProvider } from "@/states/comment-subject-ref"; import type { EntityFieldItem } from "@/states/entity"; import type { OperationItem } from "@/states/operation"; +import type { PluginInstallationTab } from "@/states/plugin-installation-tabs"; import type { ThemeListTab } from "@/states/theme-list-tabs"; +import type { UserProfileTab, UserTab } from "@/states/user-tab"; import type { Attachment, Backup, ListedPost, Plugin, Theme, + ListedComment, + ListedReply, + ListedSinglePage, } from "@halo-dev/api-client"; +import type { AnyExtension } from "@halo-dev/richtext-editor"; +import type { Component, Ref } from "vue"; +import type { RouteRecordName, RouteRecordRaw } from "vue-router"; +import type { + DashboardWidgetDefinition, + DashboardWidgetQuickActionItem, + EditorProvider, + PluginTab, +} from ".."; +import type { AttachmentSelectProvider } from "../states/attachment-selector"; +import type { FunctionalPage } from "../states/pages"; export interface RouteRecordAppend { - parentName: RouteRecordName; + parentName: NonNullable; route: RouteRecordRaw; } @@ -80,36 +89,65 @@ export interface ExtensionPoint { "post:list-item:operation:create"?: ( post: Ref - ) => OperationItem[] | Promise[]>; + ) => OperationItem[]; + + "single-page:list-item:operation:create"?: ( + singlePage: Ref + ) => OperationItem[]; + + "comment:list-item:operation:create"?: ( + comment: Ref + ) => OperationItem[]; + + "reply:list-item:operation:create"?: ( + reply: Ref + ) => OperationItem[]; "plugin:list-item:operation:create"?: ( plugin: Ref - ) => OperationItem[] | Promise[]>; + ) => OperationItem[]; "backup:list-item:operation:create"?: ( backup: Ref - ) => OperationItem[] | Promise[]>; + ) => OperationItem[]; "attachment:list-item:operation:create"?: ( attachment: Ref - ) => OperationItem[] | Promise[]>; + ) => OperationItem[]; - "plugin:list-item:field:create"?: ( - plugin: Ref - ) => EntityFieldItem[] | Promise; + "plugin:list-item:field:create"?: (plugin: Ref) => EntityFieldItem[]; - "post:list-item:field:create"?: ( - post: Ref - ) => EntityFieldItem[] | Promise; + "post:list-item:field:create"?: (post: Ref) => EntityFieldItem[]; + + "single-page:list-item:field:create"?: ( + singlePage: Ref + ) => EntityFieldItem[]; "theme:list:tabs:create"?: () => ThemeListTab[] | Promise; "theme:list-item:operation:create"?: ( theme: Ref - ) => OperationItem[] | Promise[]>; + ) => OperationItem[]; + + "user:detail:tabs:create"?: () => UserTab[] | Promise; + + "uc:user:profile:tabs:create"?: () => + | UserProfileTab[] + | Promise; + + "console:dashboard:widgets:create"?: () => + | DashboardWidgetDefinition[] + | Promise; + + "console:dashboard:widgets:internal:quick-action:item:create"?: () => + | DashboardWidgetQuickActionItem[] + | Promise; } export interface PluginModule { + /** + * These components will be registered when plugin is activated. + */ components?: Record; routes?: RouteRecordRaw[] | RouteRecordAppend[]; @@ -118,6 +156,7 @@ export interface PluginModule { extensionPoints?: ExtensionPoint; } + ``` - `components`:组件列表,key 为组件名称,value 为组件对象,在此定义之后,加载插件时会自动注册到 Vue App 全局。 diff --git a/versioned_docs/version-2.21/developer-guide/plugin/basics/ui/intro.md b/versioned_docs/version-2.21/developer-guide/plugin/basics/ui/intro.md index f6c33d8..d72b6a2 100644 --- a/versioned_docs/version-2.21/developer-guide/plugin/basics/ui/intro.md +++ b/versioned_docs/version-2.21/developer-guide/plugin/basics/ui/intro.md @@ -7,6 +7,6 @@ Halo 插件体系的 UI 部分可以让开发者在 Console 控制台和 UC 个 在开始之前,建议先熟悉或安装以下库和工具: -1. [Node.js 18+](https://nodejs.org) -2. [pnpm 8+](https://pnpm.io) +1. [Node.js 20+](https://nodejs.org) +2. [pnpm 10+](https://pnpm.io) 3. [Vue.js 3](https://vuejs.org) diff --git a/versioned_docs/version-2.21/developer-guide/plugin/publish.md b/versioned_docs/version-2.21/developer-guide/plugin/publish.md index db3c98d..34f8479 100644 --- a/versioned_docs/version-2.21/developer-guide/plugin/publish.md +++ b/versioned_docs/version-2.21/developer-guide/plugin/publish.md @@ -11,126 +11,51 @@ description: 了解如何与我们的社区分享你的插件 ## 自动构建 -如果你是基于 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 创建的插件项目,那么已经包含了适用于 GitHub Action 的 `workflow.yaml` 文件,里面包含了构建插件和发布插件资源到 Release 的步骤,可以根据自己的实际需要进行修改,以下是示例: +如果你是基于 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 创建的插件项目,那么已经包含了适用于 GitHub Action 的 `ci.yaml` 和 `cd.yaml` 文件,里面包含了构建插件和发布插件资源到 Release 的步骤,可以根据自己的实际需要进行修改,以下是示例: ```yaml -name: Build Plugin JAR File +name: CI on: push: branches: - main - paths: - - "**" - - "!**.md" - release: - types: - - created pull_request: branches: - main - paths: - - "**" - - "!**.md" jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - name: Set up JDK 17 - uses: actions/setup-java@v2 - with: - distribution: 'temurin' - cache: 'gradle' - java-version: 17 - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - node-version: 18 - - uses: pnpm/action-setup@v2.0.1 - name: Install pnpm - id: pnpm-install - with: - version: 8 - run_install: false - - name: Get pnpm store directory - id: pnpm-cache - run: | - echo "::set-output name=pnpm_cache_dir::$(pnpm store path)" - - uses: actions/cache@v3 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/widget/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - name: Install Frontend Dependencies - run: | - ./gradlew pnpmInstall - - name: Build with Gradle - run: | - # Set the version with tag name when releasing - version=${{ github.event.release.tag_name }} - version=${version#v} - sed -i "s/version=.*-SNAPSHOT$/version=$version/1" gradle.properties - ./gradlew clean build -x test - - name: Archive plugin-starter jar - uses: actions/upload-artifact@v2 - with: - name: plugin-starter - path: | - build/libs/*.jar - retention-days: 1 - - github-release: - runs-on: ubuntu-latest - needs: build - if: github.event_name == 'release' - steps: - - name: Download plugin-starter jar - uses: actions/download-artifact@v2 - with: - name: plugin-starter - path: build/libs - - name: Get Name of Artifact - id: get_artifact - run: | - ARTIFACT_PATHNAME=$(ls build/libs/*.jar | head -n 1) - ARTIFACT_NAME=$(basename ${ARTIFACT_PATHNAME}) - echo "Artifact pathname: ${ARTIFACT_PATHNAME}" - echo "Artifact name: ${ARTIFACT_NAME}" - echo "ARTIFACT_PATHNAME=${ARTIFACT_PATHNAME}" >> $GITHUB_ENV - echo "ARTIFACT_NAME=${ARTIFACT_NAME}" >> $GITHUB_ENV - echo "RELEASE_ID=${{ github.event.release.id }}" >> $GITHUB_ENV - - name: Upload a Release Asset - uses: actions/github-script@v2 - if: github.event_name == 'release' - with: - github-token: ${{secrets.GITHUB_TOKEN}} - script: | - console.log('environment', process.versions); - - const fs = require('fs').promises; - - const { repo: { owner, repo }, sha } = context; - console.log({ owner, repo, sha }); - - const releaseId = process.env.RELEASE_ID - const artifactPathName = process.env.ARTIFACT_PATHNAME - const artifactName = process.env.ARTIFACT_NAME - console.log('Releasing', releaseId, artifactPathName, artifactName) - - await github.repos.uploadReleaseAsset({ - owner, repo, - release_id: releaseId, - name: artifactName, - data: await fs.readFile(artifactPathName) - }); + ci: + uses: halo-sigs/reusable-workflows/.github/workflows/plugin-ci.yaml@v3 + with: + ui-path: "ui" + pnpm-version: 9 + node-version: 22 + java-version: 21 ``` +```yaml +name: CD + +on: + release: + types: + - published + +jobs: + cd: + uses: halo-sigs/reusable-workflows/.github/workflows/plugin-cd.yaml@v3 + permissions: + contents: write + with: + pnpm-version: 9 + node-version: 22 + java-version: 21 + skip-appstore-release: true +``` + +关于 CI / CD 的更多详细信息,可查阅:[halo-sigs/reusable-workflows](https://github.com/halo-sigs/reusable-workflows) + ## 发布你的插件 用户可以在你的仓库 Release 下载使用,但为了方便让 Halo 的用户知道你的插件,可以在以下渠道发布: diff --git a/versioned_sidebars/version-2.21-sidebars.json b/versioned_sidebars/version-2.21-sidebars.json index 71be09d..b17665e 100644 --- a/versioned_sidebars/version-2.21-sidebars.json +++ b/versioned_sidebars/version-2.21-sidebars.json @@ -95,11 +95,7 @@ "link": { "type": "generated-index" }, - "items": [ - "contribution/issue", - "contribution/pr", - "contribution/sponsor" - ] + "items": ["contribution/issue", "contribution/pr", "contribution/sponsor"] }, "about" ], @@ -157,7 +153,8 @@ }, "items": [ "developer-guide/plugin/basics/ui/intro", - "developer-guide/plugin/basics/ui/entry" + "developer-guide/plugin/basics/ui/entry", + "developer-guide/plugin/basics/ui/build" ] } ] @@ -319,9 +316,7 @@ "link": { "type": "generated-index" }, - "items": [ - "developer-guide/plugin/examples/todolist" - ] + "items": ["developer-guide/plugin/examples/todolist"] } ] },