docs: improve documentations of plugin development (#271)

优化 Halo 插件开发文档。

/kind documentation

```release-note
None
```
wan92hen-patch-1
Ryan Wang 1 year ago committed by GitHub
parent e3dbe69aa4
commit 147952f9c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -74,10 +74,10 @@ gender: male
- @GVK:此注解标识该类为一个自定义模型,同时必须继承 `AbstractExtension`
- kind表示自定义模型所表示的 REST 资源。
- group表示一组公开的资源通常采用域名形式Halo 项目保留使用空组和任何以“*.halo.run”结尾的组名供其单独使用。
选择群组名称时,我们建议选择你的群组或组织拥有的子域,例如“widget.mycompany.com”
- versionAPI 的版本,它与 group 组合使用为 apiVersion=“GROUP/VERSION”例如“api.halo.run/v1alpha1”
- singular: 资源的单数名称,这允许客户端不透明地处理复数和单数,必须全部小写。通常为小写的“kind”
- group表示一组公开的资源通常采用域名形式Halo 项目保留使用空组和任何以 `*.halo.run` 结尾的组名供其单独使用。
选择群组名称时,我们建议选择你的群组或组织拥有的子域,例如 `widget.mycompany.com`
- versionAPI 的版本,它与 group 组合使用为 `apiVersion=GROUP/VERSION`,例如`api.halo.run/v1alpha1`
- singular: 资源的单数名称,这允许客户端不透明地处理复数和单数,必须全部小写,通常为小写的 `kind`
- plural 资源的复数名称,自定义资源在 `/apis/<group>/<version>/.../<plural>` 下提供,必须为全部小写。
- @Schema:属性校验注解,会在创建/修改资源前对资源校验,参考 [schema-validator](https://www.openapi4j.org/schema-validator.html)。
:::

@ -8,21 +8,21 @@ description: 了解如果使用静态资源代理来访问插件中的静态资
例如 `src/main/resources` 下的 `static` 目录下有一张 `halo.jpg`:
1. 首先需要在 `src/main/resources/extensions` 下创建一个 `yaml`,文件名可以任意。
2. 示例配置如下
2. 示例配置如下
```yaml
apiVersion: plugin.halo.run/v1alpha1
kind: ReverseProxy
metadata:
# name 为此资源的唯一标识名称,不允许重复,为了避免与其他插件冲突,推荐带上插件名称前缀
name: my-plugin-fake-reverse-proxy
rules:
- path: /res/**
file:
directory: static
# 如果想代理 static 下所有静态资源则省略 filename 配置
filename: halo.jpg
```
```yaml
apiVersion: plugin.halo.run/v1alpha1
kind: ReverseProxy
metadata:
# name 为此资源的唯一标识名称,不允许重复,为了避免与其他插件冲突,推荐带上插件名称前缀
name: my-plugin-fake-reverse-proxy
rules:
- path: /res/**
file:
directory: static
# 如果想代理 static 下所有静态资源则省略 filename 配置
filename: halo.jpg
```
插件启动后会根据 `/plugins/{plugin-name}/assets/**` 规则生成 API。
因此该 `ReverseProxy` 的访问路径为: `/plugins/my-plugin/assets/res/halo.jpg`

@ -58,15 +58,15 @@ rules:
- `resources` 对应 API 中的 resource 部分,`my-plugin` 表示插件名称。
- `verbs` 表示请求动词,可选值为 "create", "delete", "deletecollection", "get", "list", "patch", "update"。对应的 HTTP 请求方式如下表所示:
| HTTP verb | request verb |
| --------- | ------------------------------------------------------------ |
| POST | create |
| GET, HEAD | get (for individual resources), list (for collections, including full object content), watch (for watching an individual resource or collection of resources) |
| PUT | update |
| PATCH | patch |
| DELETE | delete (for individual resources), deletecollection (for collections) |
| HTTP verb | request verb |
| --------- | ------------------------------------------------------------ |
| POST | create |
| GET, HEAD | get (for individual resources), list (for collections, including full object content), watch (for watching an individual resource or collection of resources) |
| PUT | update |
| PATCH | patch |
| DELETE | delete (for individual resources), deletecollection (for collections) |
`metadata.labels` 中必须包含 `halo.run/role-template: "true"` 以表示它此资源要作为角色模板。
`metadata.labels` 中必须包含 `halo.run/role-template: "true"` 以表示它此资源要作为角色模板。
`metadata.annotations` 中:
@ -75,16 +75,16 @@ rules:
- `rbac.authorization.halo.run/display-name`:模板角色的显示名称,用于展示为用户可读的名称信息。
- `rbac.authorization.halo.run/ui-permissions`:用于控制 UI 权限,规则为 `plugin:{your-plugin-name}:scope-name`,使用示例为在插件前端部分入口文件 `index.ts` 中用于控制菜单是否显示或者控制页面按钮是否展示:
```javascript
{
path: "",
name: "HelloWorld",
component: DefaultView,
meta: {
permissions: ["plugin:my-plugin:person:view"]
```javascript
{
path: "",
name: "HelloWorld",
component: DefaultView,
meta: {
permissions: ["plugin:my-plugin:person:view"]
}
}
}
```
```
以上定义角色模板的方式适合资源型请求,即需要符合以下规则

@ -55,51 +55,52 @@ description: 这个例子展示了如何开发 Todo List 插件
1. 在 `src/main/java` 下创建包,如 `run.halo.tutorial`,在创建一个类 `TodoListPlugin`,它继承自 `BasePlugin` 类内容如下:
```java
package run.halo.tutorial;
import org.pf4j.PluginWrapper;
import org.springframework.stereotype.Component;
import run.halo.app.plugin.BasePlugin;
@Component
public class TodoListPlugin extends BasePlugin {
public TodoListPlugin(PluginWrapper wrapper) {
super(wrapper);
}
}
```
`src/main/java` 下的文件结构如下:
```text
.
└── run
└── halo
└── tutorial
└── TodoListPlugin.java
```
然后在项目目录执行命令
```java
package run.halo.tutorial;
import org.pf4j.PluginWrapper;
import org.springframework.stereotype.Component;
import run.halo.app.plugin.BasePlugin;
@Component
public class TodoListPlugin extends BasePlugin {
public TodoListPlugin(PluginWrapper wrapper) {
super(wrapper);
}
}
```
`src/main/java` 下的文件结构如下:
```text
.
└── run
└── halo
└── tutorial
└── TodoListPlugin.java
```
2. 然后在项目目录执行命令
```shell
./gradlew build
```
```shell
./gradlew build
```
3. 使用 `IntelliJ IDEA` 打开 Halo参考 [Halo 开发环境运行](../../core/run.md) 及 [插件入门](../hello-world.md) 配置插件的运行模式和路径:
使用 `IntelliJ IDEA` 打开 Halo参考 [Halo 开发环境运行](../../core/run.md) 及 [插件入门](../hello-world.md) 配置插件的运行模式和路径:
```yaml
halo:
plugin:
runtime-mode: development
fixed-plugin-path:
# 配置为插件绝对路径
- /Users/guqing/halo-plugin-todolist
```
```yaml
halo:
plugin:
runtime-mode: development
fixed-plugin-path:
# 配置为插件绝对路径
- /Users/guqing/halo-plugin-todolist
```
4. 启动 Halo然后访问 `http://localhost:8090/console`
启动 Halo然后访问 `http://localhost:8090/console`
在插件列表将能看到插件已经被正确启用:
在插件列表将能看到插件已经被正确启用
![plugin-todolist-in-list-view](/img/todolist-in-list.png)
## 创建一个自定义模型
@ -283,13 +284,13 @@ export default definePlugin({
2. ` pnpm install todomvc-app-css `
3. 修改 `console/src/views/DefaultView.vue` 最底部的 `style` 标签。
```diff
- <style>
+ <style scoped>
- @import "https://unpkg.com/todomvc-app-css@2.4.1/index.css";
+ @import "todomvc-app-css/index.css";
</style>
```
```diff
- <style>
+ <style scoped>
- @import "https://unpkg.com/todomvc-app-css@2.4.1/index.css";
+ @import "todomvc-app-css/index.css";
</style>
```
4. 重新 Build 后刷新页面,便能看到目标图所示效果。
@ -307,7 +308,7 @@ pnpm install axios
为了符合最佳实践,将用 TypeScript 改造之前的 todomvc 示例:
1. 创建 types 文件 `console/src/types/index.ts`
创建 types 文件 `console/src/types/index.ts`
```typescript
export interface Metadata {
@ -575,87 +576,9 @@ const handleDelete = (todo: Todo) => {
至此我们就完成了与插件后端 APIs 实现 Todo List 数据交互的部分。
### 使用 Icon
目前 Todo 的菜单还是默认的网格样式 Icon`console/src/index.ts` 文件中配置有一个 `icon: markRaw(IconGrid)`。以此为例说明该如何使用其他 `Icon`
1. 安装 [unplugin-icons](https://github.com/antfu/unplugin-icons)。
```shell
pnpm install -D unplugin-icons
pnpm install -D @iconify/json
pnpm install -D @vue/compiler-sfc
```
2. 编辑 `console/vite.config.ts`,在 `defineConfig``plugins` 中添加配置,修改如下。
```diff
+ import Icons from "unplugin-icons/vite";
// https://vitejs.dev/config/
export default defineConfig({
- plugins: [vue(), vueJsx()],
+ plugins: [vue(), vueJsx(), Icons({ compiler: "vue3" })],
```
3. 在 `console/tsconfig.app.json` 中加入 `unplugni-icons``types` 配置。
```diff
{
// ...
"compilerOptions": {
// ...
"paths": {
"@/*": ["./src/*"]
- }
+ },
+ "types": ["unplugin-icons/types/vue"]
}
}
```
4. 到 [icones](https://icones.js.org/) 搜索你想要使用的图标,并点击它,然后选择 `Unplugin Icons`,会复制到剪贴板。
![unplugin icons selector](/img/unplugin-icons-example.png)
5. 编辑 `console/src/index.ts``import` 区域粘贴,并 `icon` 属性。
```diff
- import { IconGrid } from "@halo-dev/components";
+ import VscodeIconsFileTypeLightTodo from "~icons/vscode-icons/file-type-light-todo";
export default definePlugin({
routes: [
{
// ...
route: {
path: "/todos",
children: [
{
// ...
meta: {
// ...
menu: {
// ...
- icon: markRaw(IconGrid),
+ icon: markRaw(VscodeIconsFileTypeLightTodo),
priority: 0,
},
},
},
],
},
},
],
// ...
});
```
### 用户界面使用静态资源
如果你想在用户界面中使用图片,你可以放到 `console/src/assets` 中,例如 `logo.png`,并将其作为 Todo 的 Logo 放到标题旁边
![todo logo example](/img/todo-logo-check-48.png)
如果你想在用户界面中使用图片,你可以放到 `console/src/assets` 中,例如 `logo.png`,并将其作为 Todo 的 Logo 放到标题旁边。
需要修改 `console/src/views/DefaultView.vue` 示例如下:

@ -3,14 +3,20 @@ title: 入门
description: 了解如何构建你的第一个插件并在 Halo 中使用它。
---
Halo 提供了一个模板仓库用于创建插件:
此文档将帮助你了解如何构建你的第一个插件并在 Halo 中安装和启用。
## 创建插件项目
1. 打开 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter)。
> 这是一个插件的初始模板,你可以基于它来开发自己的插件。
1. 打开 [plugin-starter](https://github.com/halo-dev/plugin-starter)。
2. 点击 `Use this template` -> `Create a new repository`
3. 如图所示填写仓库名后点击 `Create repository from template`
![create-repository-for-hello-world-plugin](/img/create-repository-for-hello-world-plugin.png)
你现在已经基于 Halo 插件模板创建了自己的存储库。接下来,你需要将它 `git clone` 到你的计算机上并使用 `IntelliJ IDEA` 打开它。
![create-repository-for-hello-world-plugin](/img/create-repository-for-hello-world-plugin.png)
你现在已经基于 Halo 插件模板创建了自己的存储库。接下来,你需要将它克隆到你的计算机上并使用 `IntelliJ IDEA` 打开它。
## 运行插件
@ -24,10 +30,7 @@ Halo 提供了一个模板仓库用于创建插件:
或者使用 `IntelliJ IDEA` 提供的 `Gradle build` 即可完成插件项目的构建。
第二步就是使用它。
使用 `IntelliJ IDEA` 打开 Halo参考 [Halo 开发环境运行](../core/run.md)。
然后在 `src/main/resources` 下创建一个 `application-local.yaml` 文件并做如下配置:
然后使用 `IntelliJ IDEA` 打开 Halo参考 [Halo 开发环境运行](../core/run.md),在 `src/main/resources` 下创建一个 `application-local.yaml` 文件并做如下配置:
```yaml
# macOS / Linux
@ -36,7 +39,7 @@ halo:
runtime-mode: development
fixed-plugin-path:
# 配置为插件绝对路径
- /Users/guqing/halo-plugin-hello-world
- /path/to/halo-plugin-hello-world
# Windows
halo:
@ -44,7 +47,7 @@ halo:
runtime-mode: development
fixed-plugin-path:
# 配置为插件绝对路径
- C:\Users\guqing\halo-plugin-hello-world
- C:\path\to\halo-plugin-hello-world
```
使用此 local profile 启动 Halo

@ -2,7 +2,8 @@
title: 介绍
description: 插件开发的准备工作
---
插件是由社区创建的程序或应用程序,用于扩展 Halo 的功能。插件在 Halo 中运行并执行一项或多项用户操作。它们允许用户根据自己的喜好扩展或修改 Halo。
Halo 采用可插拔架构,功能模块之间耦合度低、灵活性提高,支持用户按需安装、卸载插件,操作便捷。同时提供插件开发接口以确保较高扩展性和可维护性,这个系列的文档将帮助你了解如何开发 Halo 插件。
## 插件管理

@ -6,20 +6,20 @@ description: 了解插件从启动到卸载的过程
根据[插件项目文件结构](./structure.md)所展示的 `StarterPlugin.java` 中,具有如下方法:
```java
@Override
public void start() {
System.out.println("插件启动成功!");
}
@Override
public void stop() {
System.out.println("插件停止!");
}
@Override
public void delete() {
System.out.println("插件被删除!");
}
@Override
public void start() {
System.out.println("插件启动成功!");
}
@Override
public void stop() {
System.out.println("插件停止!");
}
@Override
public void delete() {
System.out.println("插件被删除!");
}
```
### 插件启动

@ -6,39 +6,40 @@ description: 了解插件资源文件 plugin.yaml 如何配置
一个典型的插件资源文件 plugin.yaml 如下所示:
```yaml
apiVersion: plugin.halo.run/v1alpha1
kind: Plugin
metadata:
name: hello-world
spec:
enabled: true
requires: ">=2.0.0"
author:
name: halo-dev
website: https://halo.run
logo: https://halo.run/logo
# settingName: hello-world-settings
# configMapName: hello-world-configmap
homepage: https://github.com/guqing/halo-plugin-hello-world
displayName: "插件 Hello world"
description: "插件开发的 hello world用于学习如何开发一个简单的 Halo 插件"
license:
- name: "MIT"
apiVersion: plugin.halo.run/v1alpha1
kind: Plugin
metadata:
name: hello-world
spec:
enabled: true
requires: ">=2.0.0"
author:
name: halo-dev
website: https://halo.run
logo: https://halo.run/logo
# settingName: hello-world-settings
# configMapName: hello-world-configmap
homepage: https://github.com/guqing/halo-plugin-hello-world
displayName: "插件 Hello world"
description: "插件开发的 hello world用于学习如何开发一个简单的 Halo 插件"
license:
- name: "MIT"
```
- `apiVersion``kind`:为固定写法,每个插件写法都是一样的不可变更。
- `metadata.name`:它是插件的唯一标识名,包含不超过 253 个字符,仅包含小写字母、数字或“-”,以字母或数字开头,以字母或数字结尾。
- `metadata.name`:它是插件的唯一标识名,包含不超过 253 个字符,仅包含小写字母、数字或`-`,以字母或数字开头,以字母或数字结尾。
- `spec.enabled`:表示是否要在安装时自动启用插件,仅在插件开发模式下有效。
- `spec.requires`:支持的 Halo 版本,SemVer expression, e.g. ">=2.0.0"
- `spec.requires`:支持的 Halo 版本,遵循 [Semantic Versioning](https://semver.org/lang/zh-CN/) 规范。
- `spec.author`:插件作者的名称和可获得支持的网站地址。
- `spec.logo`:插件 logo可以是域名或相对于项目 src/main/resources 目录的相对文件路径。
- `spec.logo`:插件 logo可以是域名或相对于项目 `src/main/resources` 目录的相对文件路径。
- `spec.settingName`:插件配置表单名称,参考表单定义,不需要表单设置则可删除。
- `spec.configMapName`:表单定义对应的值标识名, 推荐命名为 "插件名-configmap",没有配置 `settingName` 则不需要配置此项。
:::tip
如果你在 plugin.yaml 中配置了 `settingName` 但确没有对应的 `Setting` 自定义模型资源文件,会导致插件无法启动,原因是 `Setting` 模型 `metadata.name` 为你配置的 `settingName` 的资源无法找到。
:::
- `spec.homepage`:通常为插件的 GitHub 仓库链接,或可联系到插件作者或插件官网或帮助中心链接等。
- `spec.displayName`:插件的显示名称,它通常是以少数几个字来概括插件的用途。
- `spec.description`:插件描述,用一段话来介绍插件的用途。
- `spec.license`:插件使用的软件协议,参考:<https://en.wikipedia.org/wiki/Software_license>
:::tip Note
如果你在 plugin.yaml 中配置了 `settingName` 但确没有对应的 `Setting` 自定义模型资源文件,会导致插件无法启动,原因是 `Setting` 模型 `metadata.name` 为你配置的 `settingName` 的资源无法找到。
:::

@ -3,13 +3,13 @@ title: 准备工作
description: 插件开发的准备工作
---
在 Halo 中,插件是使用 Java 和 JavaScript 编写的UI 使用 [Vuejs](https://vuejs.org) 编写。
在 Halo 中,插件是使用 Java 和 JavaScript / TypeScript 编写的UI 使用 [Vuejs](https://vuejs.org) 编写。
在创建你的第一个插件之前,请确保你具备以下条件:
- 你能成功[运行 Halo 2.0.0 及以上版本](../core/run.md)。
- 你应该能够熟练使用 [IntelliJ IDEA](https://www.jetbrains.com/idea/old)。
- 你需要在计算机上安装最新的 LTS 版本的 Node.js。如果你还没有Node.js安装你可以在这里下载 [Node.js 16 LTS](https://nodejs.org/)。
- 你能成功[开发环境运行 Halo](../core/run.md)。
- 你应该能够熟练使用 [IntelliJ IDEA](https://www.jetbrains.com/idea)。
- 你需要在计算机上安装最新的 LTS 版本的 Node.js如果你还没有Node.js安装你可以在这里下载 [Node.js 18 LTS](https://nodejs.org/)。
- 你熟悉 Vue 和 TypeScript。
- 你应该熟悉使用 PNPM 进行包管理,你可以在这里下载 [pnpm 7](https://pnpm.io/)
- Git 是一个版本控制系统,用于跟踪代码的更改您需要 Git 来下载示例插件并发布插件。
- 你应该熟悉使用 Node.js 包管理器
- Git 是一个版本控制系统,用于跟踪代码的更改您需要 Git 来下载示例插件并发布插件。

@ -2,18 +2,139 @@
title: 发布插件
description: 了解如何与我们的社区分享你的插件
---
> 了解如何与我们的社区分享您的扩展。
## 创建你的 Release
了解如何与我们的社区分享你的插件。
当你完成了你的插件并进行充分测试后,切换到插件目录 Build 一次,当没有发生任何错误你就可以推送到 GitHub 并 `Create a new release`
## 创建 Release
然后填写 `Release Tag` 和描述点击创建,项目目录下的 `.github/workflows/workflow.yaml` 文件会被 `GitHub Action` 触发并执行,脚本会自动根据你的 `Release Tag` 修改插件版本号然后在 `Release``Asserts` 中包含打包产物--插件的 JAR 文件
当你完成了你的插件并进行充分测试后,就可以在 GitHub 上创建新的 Release其中版本规范可以参考[版本控制](./introduction.md#版本控制)
## 分享你的插件
## 自动构建
用户可以在你的仓库 Release 下载使用,但为了方便让社区用户看到,你可以在我们的 [awesome-halo](https://github.com/halo-sigs/awesome-halo) 仓库发起一个 Pull Request为此你需要先 Fork [awesome-halo](https://github.com/halo-sigs/awesome-halo) 并按照此仓库的要求添加一行记录是关于你的插件仓库地址和功能描述的,然后推送你的更改并通过 GitHub 向 [awesome-halo](https://github.com/halo-sigs/awesome-halo) 的 `main` 分支发起 Pull Request。
如果你是基于 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 创建的插件项目,那么已经包含了适用于 GitHub Action 的 `workflow.yaml` 文件,里面包含了构建插件和发布插件资源到 Release 的步骤,可以根据自己的实际需要进行修改,以下是示例:
## 等待审核
```yaml
name: Build Plugin JAR File
在你发起 Pull Request 后我们将审查的你的插件并在需要时请求更改。一旦被接受Pull Request 将被合并。
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)
});
```
## 发布你的插件
用户可以在你的仓库 Release 下载使用,但为了方便让 Halo 的用户知道你的插件,可以在以下渠道发布:
1. [halo-sigs/awesome-halo](https://github.com/halo-sigs/awesome-halo):你可以向这个仓库发起一个 PR 提交的插件的信息即可。
2. [Halo 应用市场](https://www.halo.run/store/apps)Halo 官方的应用市场,但目前还不支持开发者注册和发布,如果你想发布到应用市场,可以在 PR 上说明以下,我们会暂时帮你发布。
3. [Halo 论坛](https://bbs.halo.run/t/plugins):你可以在 Halo 官方社区的插件板块发布你的插件。

@ -2,15 +2,16 @@
title: 插件运行模式
description: 了解插件的运行方式
---
Halo 的插件可以在两种模式下运行:`DEVELOPMENT` 和 `DEPLOYMENT`
`DEPLOYMENT`(默认)模式是插件创建的标准工作流程:为每个插件创建一个新的 Gradle 项目,编码插件(声明新的扩展点和/或添加新的扩展),将插件打包成一个 JAR 文件,部署 JAR 文件到 Halo。
这些操作非常耗时,因此引入了 `DEVELOPMENT` 运行时模式。
对于插件开发人员来说,`DEVELOPMENT` 运行时模式的主要优点是不必打包和部署插件。在开发模式下,您可以以简单快速的模式开发插件。
Halo 的插件可以在两种模式下运行:`development` 和 `deployment`
`deployment`(默认)模式是插件创建的标准工作流程:为每个插件创建一个新的 Gradle 项目,编码插件(声明新的扩展点和/或添加新的扩展),将插件打包成一个 JAR 文件,部署 JAR 文件到 Halo。
这些操作非常耗时,因此引入了 `development` 运行时模式。
对于插件开发人员来说,`development` 运行时模式的主要优点是不必打包和部署插件。在开发模式下,您可以以简单快速的模式开发插件。
### 配置
如果你想以 `DEPLOYMENT` 运行插件则做如下配置:
如果你想以 `deployment` 运行插件则做如下配置:
```yaml
halo:
@ -20,7 +21,7 @@ halo:
插件的 `deployment` 模式只允许通过安装 JAR 文件的方式运行插件。
而如果你想以 `DEVELOPMENT` 运行插件或开发插件则将 `runtime-mode` 修改为 `development`,同时配置 `fixed-plugin-path` 为插件项目路径,可以配置多个。
而如果你想以 `development` 运行插件或开发插件则将 `runtime-mode` 修改为 `development`,同时配置 `fixed-plugin-path` 为插件项目路径,可以配置多个。
```yaml
# macOS / Linux
@ -28,7 +29,7 @@ plugin:
runtime-mode: development
fixed-plugin-path:
# 配置为插件绝对路径
- /Users/guqing/halo-plugin-hello-world
- /path/to/halo-plugin-hello-world
# Windows
halo:
@ -36,7 +37,7 @@ halo:
runtime-mode: development
fixed-plugin-path:
# 配置为插件绝对路径
- C:\Users\guqing\halo-plugin-hello-world
- C:\path\to\halo-plugin-hello-world
```
:::tip Note

@ -6,48 +6,42 @@ description: 了解插件的文件结构
新创建的插件项目典型的目录结构如下所示:
```text
.
├── LICENSE
├── README.md
├── console
│ ├── src
│ │ ├── assets
│ │ │ └── logo.svg
│ │ ├── views
│ │ │ └── HomeView.vue
│ │ └── index.ts
│ ├── env.d.ts
│ ├── package.json
│ ├── pnpm-lock.yaml
│ ├── tsconfig.app.json
│ ├── tsconfig.config.json
│ ├── tsconfig.json
│ ├── tsconfig.vitest.json
│ └── vite.config.ts
├── gradle
│   └── .
├── lib
│   └── halo-2.0.0-SNAPSHOT-plain.jar
├── src
│   ├── main
│   │   ├── java
│   │   │   └── run
│   │   │   └── halo
│   │   │   └── starter
│   │   │   └── StarterPlugin.java
│   │   └── resources
│   │   ├── console
│   │   │   ├── main.js
│   │   │   └── style.css
│   │   └── plugin.yaml
│ └── main
│ ├── java
│ │ └── run
│ │ └── halo
│ │ └── starter
│ │ └── StarterPlugin.java
│ └── resources
│ ├── console
│ │ ├── main.js
│ │ └── style.css
│ └── plugin.yaml
├── LICENSE
├── OWNERS
├── README.md
├── build.gradle
├── gradle.properties
├── gradlew
├── gradlew.bat
├── gradle.properties
├── settings.gradle
├── build.gradle
├── console
│   ├── package.json
│   ├── pnpm-lock.yaml
│   ├── src
│   │   ├── assets
│   │   │   └── logo.svg
│   │   ├── components
│   │   │   └── HelloWorld.vue
│   │   ├── index.ts
│   │   ├── styles
│   │   │   └── index.css
│   │   └── views
│   │   └── DefaultView.vue
│   ├── tsconfig.app.json
│   ├── tsconfig.config.json
│   ├── tsconfig.json
│   ├── tsconfig.vitest.json
│   └── vite.config.ts
└── settings.gradle
```
该目录包含了前端和后端两个部分,让我们依次看一下它们中的每一个。
@ -57,13 +51,9 @@ description: 了解插件的文件结构
所有的后端代码都放在 `src` 目录下,它是一个常规的 `Java` 项目目录结构。
- `StarterPlugin.java` 为插件的后端入口文件。
- `resources` 下的 `plugin.yaml` 为插件的资源描述文件,它是必须的。
- `resources/console` 下的两个文件 `main.js``style.css` 是前端插件部分打包时输出的产物。一个插件可以没有前端部分,因此 `resources/console` 同样可以不存在。
`lib/halo-2.0.0-SNAPSHOT-plain.jar` 它是 Halo 的类型依赖,目前使用 `JAR` 文件的方式引入依赖只是暂时的,后续将会改进它,它只作为编译时依赖使用。
### 前端部分
`console` 目录下为插件的前端部分的工程目录,包括了源码、配置文件和静态资源文件。

@ -74,10 +74,10 @@ gender: male
- @GVK:此注解标识该类为一个自定义模型,同时必须继承 `AbstractExtension`
- kind表示自定义模型所表示的 REST 资源。
- group表示一组公开的资源通常采用域名形式Halo 项目保留使用空组和任何以“*.halo.run”结尾的组名供其单独使用。
选择群组名称时,我们建议选择你的群组或组织拥有的子域,例如“widget.mycompany.com”
- versionAPI 的版本,它与 group 组合使用为 apiVersion=“GROUP/VERSION”例如“api.halo.run/v1alpha1”
- singular: 资源的单数名称,这允许客户端不透明地处理复数和单数,必须全部小写。通常为小写的“kind”
- group表示一组公开的资源通常采用域名形式Halo 项目保留使用空组和任何以 `*.halo.run` 结尾的组名供其单独使用。
选择群组名称时,我们建议选择你的群组或组织拥有的子域,例如 `widget.mycompany.com`
- versionAPI 的版本,它与 group 组合使用为 `apiVersion=GROUP/VERSION`,例如`api.halo.run/v1alpha1`
- singular: 资源的单数名称,这允许客户端不透明地处理复数和单数,必须全部小写,通常为小写的 `kind`
- plural 资源的复数名称,自定义资源在 `/apis/<group>/<version>/.../<plural>` 下提供,必须为全部小写。
- @Schema:属性校验注解,会在创建/修改资源前对资源校验,参考 [schema-validator](https://www.openapi4j.org/schema-validator.html)。
:::

@ -8,21 +8,21 @@ description: 了解如果使用静态资源代理来访问插件中的静态资
例如 `src/main/resources` 下的 `static` 目录下有一张 `halo.jpg`:
1. 首先需要在 `src/main/resources/extensions` 下创建一个 `yaml`,文件名可以任意。
2. 示例配置如下
2. 示例配置如下
```yaml
apiVersion: plugin.halo.run/v1alpha1
kind: ReverseProxy
metadata:
# name 为此资源的唯一标识名称,不允许重复,为了避免与其他插件冲突,推荐带上插件名称前缀
name: my-plugin-fake-reverse-proxy
rules:
- path: /res/**
file:
directory: static
# 如果想代理 static 下所有静态资源则省略 filename 配置
filename: halo.jpg
```
```yaml
apiVersion: plugin.halo.run/v1alpha1
kind: ReverseProxy
metadata:
# name 为此资源的唯一标识名称,不允许重复,为了避免与其他插件冲突,推荐带上插件名称前缀
name: my-plugin-fake-reverse-proxy
rules:
- path: /res/**
file:
directory: static
# 如果想代理 static 下所有静态资源则省略 filename 配置
filename: halo.jpg
```
插件启动后会根据 `/plugins/{plugin-name}/assets/**` 规则生成 API。
因此该 `ReverseProxy` 的访问路径为: `/plugins/my-plugin/assets/res/halo.jpg`

@ -58,15 +58,15 @@ rules:
- `resources` 对应 API 中的 resource 部分,`my-plugin` 表示插件名称。
- `verbs` 表示请求动词,可选值为 "create", "delete", "deletecollection", "get", "list", "patch", "update"。对应的 HTTP 请求方式如下表所示:
| HTTP verb | request verb |
| --------- | ------------------------------------------------------------ |
| POST | create |
| GET, HEAD | get (for individual resources), list (for collections, including full object content), watch (for watching an individual resource or collection of resources) |
| PUT | update |
| PATCH | patch |
| DELETE | delete (for individual resources), deletecollection (for collections) |
| HTTP verb | request verb |
| --------- | ------------------------------------------------------------ |
| POST | create |
| GET, HEAD | get (for individual resources), list (for collections, including full object content), watch (for watching an individual resource or collection of resources) |
| PUT | update |
| PATCH | patch |
| DELETE | delete (for individual resources), deletecollection (for collections) |
`metadata.labels` 中必须包含 `halo.run/role-template: "true"` 以表示它此资源要作为角色模板。
`metadata.labels` 中必须包含 `halo.run/role-template: "true"` 以表示它此资源要作为角色模板。
`metadata.annotations` 中:
@ -75,16 +75,16 @@ rules:
- `rbac.authorization.halo.run/display-name`:模板角色的显示名称,用于展示为用户可读的名称信息。
- `rbac.authorization.halo.run/ui-permissions`:用于控制 UI 权限,规则为 `plugin:{your-plugin-name}:scope-name`,使用示例为在插件前端部分入口文件 `index.ts` 中用于控制菜单是否显示或者控制页面按钮是否展示:
```javascript
{
path: "",
name: "HelloWorld",
component: DefaultView,
meta: {
permissions: ["plugin:my-plugin:person:view"]
```javascript
{
path: "",
name: "HelloWorld",
component: DefaultView,
meta: {
permissions: ["plugin:my-plugin:person:view"]
}
}
}
```
```
以上定义角色模板的方式适合资源型请求,即需要符合以下规则

@ -55,51 +55,52 @@ description: 这个例子展示了如何开发 Todo List 插件
1. 在 `src/main/java` 下创建包,如 `run.halo.tutorial`,在创建一个类 `TodoListPlugin`,它继承自 `BasePlugin` 类内容如下:
```java
package run.halo.tutorial;
import org.pf4j.PluginWrapper;
import org.springframework.stereotype.Component;
import run.halo.app.plugin.BasePlugin;
@Component
public class TodoListPlugin extends BasePlugin {
public TodoListPlugin(PluginWrapper wrapper) {
super(wrapper);
}
}
```
`src/main/java` 下的文件结构如下:
```text
.
└── run
└── halo
└── tutorial
└── TodoListPlugin.java
```
然后在项目目录执行命令
```java
package run.halo.tutorial;
import org.pf4j.PluginWrapper;
import org.springframework.stereotype.Component;
import run.halo.app.plugin.BasePlugin;
@Component
public class TodoListPlugin extends BasePlugin {
public TodoListPlugin(PluginWrapper wrapper) {
super(wrapper);
}
}
```
`src/main/java` 下的文件结构如下:
```text
.
└── run
└── halo
└── tutorial
└── TodoListPlugin.java
```
2. 然后在项目目录执行命令
```shell
./gradlew build
```
```shell
./gradlew build
```
3. 使用 `IntelliJ IDEA` 打开 Halo参考 [Halo 开发环境运行](../../core/run.md) 及 [插件入门](../hello-world.md) 配置插件的运行模式和路径:
使用 `IntelliJ IDEA` 打开 Halo参考 [Halo 开发环境运行](../../core/run.md) 及 [插件入门](../hello-world.md) 配置插件的运行模式和路径:
```yaml
halo:
plugin:
runtime-mode: development
fixed-plugin-path:
# 配置为插件绝对路径
- /Users/guqing/halo-plugin-todolist
```
```yaml
halo:
plugin:
runtime-mode: development
fixed-plugin-path:
# 配置为插件绝对路径
- /Users/guqing/halo-plugin-todolist
```
4. 启动 Halo然后访问 `http://localhost:8090/console`
启动 Halo然后访问 `http://localhost:8090/console`
在插件列表将能看到插件已经被正确启用:
在插件列表将能看到插件已经被正确启用
![plugin-todolist-in-list-view](/img/todolist-in-list.png)
## 创建一个自定义模型
@ -283,13 +284,13 @@ export default definePlugin({
2. ` pnpm install todomvc-app-css `
3. 修改 `console/src/views/DefaultView.vue` 最底部的 `style` 标签。
```diff
- <style>
+ <style scoped>
- @import "https://unpkg.com/todomvc-app-css@2.4.1/index.css";
+ @import "todomvc-app-css/index.css";
</style>
```
```diff
- <style>
+ <style scoped>
- @import "https://unpkg.com/todomvc-app-css@2.4.1/index.css";
+ @import "todomvc-app-css/index.css";
</style>
```
4. 重新 Build 后刷新页面,便能看到目标图所示效果。
@ -307,7 +308,7 @@ pnpm install axios
为了符合最佳实践,将用 TypeScript 改造之前的 todomvc 示例:
1. 创建 types 文件 `console/src/types/index.ts`
创建 types 文件 `console/src/types/index.ts`
```typescript
export interface Metadata {
@ -575,87 +576,9 @@ const handleDelete = (todo: Todo) => {
至此我们就完成了与插件后端 APIs 实现 Todo List 数据交互的部分。
### 使用 Icon
目前 Todo 的菜单还是默认的网格样式 Icon`console/src/index.ts` 文件中配置有一个 `icon: markRaw(IconGrid)`。以此为例说明该如何使用其他 `Icon`
1. 安装 [unplugin-icons](https://github.com/antfu/unplugin-icons)。
```shell
pnpm install -D unplugin-icons
pnpm install -D @iconify/json
pnpm install -D @vue/compiler-sfc
```
2. 编辑 `console/vite.config.ts`,在 `defineConfig``plugins` 中添加配置,修改如下。
```diff
+ import Icons from "unplugin-icons/vite";
// https://vitejs.dev/config/
export default defineConfig({
- plugins: [vue(), vueJsx()],
+ plugins: [vue(), vueJsx(), Icons({ compiler: "vue3" })],
```
3. 在 `console/tsconfig.app.json` 中加入 `unplugni-icons``types` 配置。
```diff
{
// ...
"compilerOptions": {
// ...
"paths": {
"@/*": ["./src/*"]
- }
+ },
+ "types": ["unplugin-icons/types/vue"]
}
}
```
4. 到 [icones](https://icones.js.org/) 搜索你想要使用的图标,并点击它,然后选择 `Unplugin Icons`,会复制到剪贴板。
![unplugin icons selector](/img/unplugin-icons-example.png)
5. 编辑 `console/src/index.ts``import` 区域粘贴,并 `icon` 属性。
```diff
- import { IconGrid } from "@halo-dev/components";
+ import VscodeIconsFileTypeLightTodo from "~icons/vscode-icons/file-type-light-todo";
export default definePlugin({
routes: [
{
// ...
route: {
path: "/todos",
children: [
{
// ...
meta: {
// ...
menu: {
// ...
- icon: markRaw(IconGrid),
+ icon: markRaw(VscodeIconsFileTypeLightTodo),
priority: 0,
},
},
},
],
},
},
],
// ...
});
```
### 用户界面使用静态资源
如果你想在用户界面中使用图片,你可以放到 `console/src/assets` 中,例如 `logo.png`,并将其作为 Todo 的 Logo 放到标题旁边
![todo logo example](/img/todo-logo-check-48.png)
如果你想在用户界面中使用图片,你可以放到 `console/src/assets` 中,例如 `logo.png`,并将其作为 Todo 的 Logo 放到标题旁边。
需要修改 `console/src/views/DefaultView.vue` 示例如下:

@ -3,14 +3,20 @@ title: 入门
description: 了解如何构建你的第一个插件并在 Halo 中使用它。
---
Halo 提供了一个模板仓库用于创建插件:
此文档将帮助你了解如何构建你的第一个插件并在 Halo 中安装和启用。
## 创建插件项目
1. 打开 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter)。
> 这是一个插件的初始模板,你可以基于它来开发自己的插件。
1. 打开 [plugin-starter](https://github.com/halo-dev/plugin-starter)。
2. 点击 `Use this template` -> `Create a new repository`
3. 如图所示填写仓库名后点击 `Create repository from template`
![create-repository-for-hello-world-plugin](/img/create-repository-for-hello-world-plugin.png)
你现在已经基于 Halo 插件模板创建了自己的存储库。接下来,你需要将它 `git clone` 到你的计算机上并使用 `IntelliJ IDEA` 打开它。
![create-repository-for-hello-world-plugin](/img/create-repository-for-hello-world-plugin.png)
你现在已经基于 Halo 插件模板创建了自己的存储库。接下来,你需要将它克隆到你的计算机上并使用 `IntelliJ IDEA` 打开它。
## 运行插件
@ -24,10 +30,7 @@ Halo 提供了一个模板仓库用于创建插件:
或者使用 `IntelliJ IDEA` 提供的 `Gradle build` 即可完成插件项目的构建。
第二步就是使用它。
使用 `IntelliJ IDEA` 打开 Halo参考 [Halo 开发环境运行](../core/run.md)。
然后在 `src/main/resources` 下创建一个 `application-local.yaml` 文件并做如下配置:
然后使用 `IntelliJ IDEA` 打开 Halo参考 [Halo 开发环境运行](../core/run.md),在 `src/main/resources` 下创建一个 `application-local.yaml` 文件并做如下配置:
```yaml
# macOS / Linux
@ -36,7 +39,7 @@ halo:
runtime-mode: development
fixed-plugin-path:
# 配置为插件绝对路径
- /Users/guqing/halo-plugin-hello-world
- /path/to/halo-plugin-hello-world
# Windows
halo:
@ -44,7 +47,7 @@ halo:
runtime-mode: development
fixed-plugin-path:
# 配置为插件绝对路径
- C:\Users\guqing\halo-plugin-hello-world
- C:\path\to\halo-plugin-hello-world
```
使用此 local profile 启动 Halo

@ -2,7 +2,8 @@
title: 介绍
description: 插件开发的准备工作
---
插件是由社区创建的程序或应用程序,用于扩展 Halo 的功能。插件在 Halo 中运行并执行一项或多项用户操作。它们允许用户根据自己的喜好扩展或修改 Halo。
Halo 采用可插拔架构,功能模块之间耦合度低、灵活性提高,支持用户按需安装、卸载插件,操作便捷。同时提供插件开发接口以确保较高扩展性和可维护性,这个系列的文档将帮助你了解如何开发 Halo 插件。
## 插件管理

@ -6,20 +6,20 @@ description: 了解插件从启动到卸载的过程
根据[插件项目文件结构](./structure.md)所展示的 `StarterPlugin.java` 中,具有如下方法:
```java
@Override
public void start() {
System.out.println("插件启动成功!");
}
@Override
public void stop() {
System.out.println("插件停止!");
}
@Override
public void delete() {
System.out.println("插件被删除!");
}
@Override
public void start() {
System.out.println("插件启动成功!");
}
@Override
public void stop() {
System.out.println("插件停止!");
}
@Override
public void delete() {
System.out.println("插件被删除!");
}
```
### 插件启动

@ -6,39 +6,40 @@ description: 了解插件资源文件 plugin.yaml 如何配置
一个典型的插件资源文件 plugin.yaml 如下所示:
```yaml
apiVersion: plugin.halo.run/v1alpha1
kind: Plugin
metadata:
name: hello-world
spec:
enabled: true
requires: ">=2.0.0"
author:
name: halo-dev
website: https://halo.run
logo: https://halo.run/logo
# settingName: hello-world-settings
# configMapName: hello-world-configmap
homepage: https://github.com/guqing/halo-plugin-hello-world
displayName: "插件 Hello world"
description: "插件开发的 hello world用于学习如何开发一个简单的 Halo 插件"
license:
- name: "MIT"
apiVersion: plugin.halo.run/v1alpha1
kind: Plugin
metadata:
name: hello-world
spec:
enabled: true
requires: ">=2.0.0"
author:
name: halo-dev
website: https://halo.run
logo: https://halo.run/logo
# settingName: hello-world-settings
# configMapName: hello-world-configmap
homepage: https://github.com/guqing/halo-plugin-hello-world
displayName: "插件 Hello world"
description: "插件开发的 hello world用于学习如何开发一个简单的 Halo 插件"
license:
- name: "MIT"
```
- `apiVersion``kind`:为固定写法,每个插件写法都是一样的不可变更。
- `metadata.name`:它是插件的唯一标识名,包含不超过 253 个字符,仅包含小写字母、数字或“-”,以字母或数字开头,以字母或数字结尾。
- `metadata.name`:它是插件的唯一标识名,包含不超过 253 个字符,仅包含小写字母、数字或`-`,以字母或数字开头,以字母或数字结尾。
- `spec.enabled`:表示是否要在安装时自动启用插件,仅在插件开发模式下有效。
- `spec.requires`:支持的 Halo 版本,SemVer expression, e.g. ">=2.0.0"
- `spec.requires`:支持的 Halo 版本,遵循 [Semantic Versioning](https://semver.org/lang/zh-CN/) 规范。
- `spec.author`:插件作者的名称和可获得支持的网站地址。
- `spec.logo`:插件 logo可以是域名或相对于项目 src/main/resources 目录的相对文件路径。
- `spec.logo`:插件 logo可以是域名或相对于项目 `src/main/resources` 目录的相对文件路径。
- `spec.settingName`:插件配置表单名称,参考表单定义,不需要表单设置则可删除。
- `spec.configMapName`:表单定义对应的值标识名, 推荐命名为 "插件名-configmap",没有配置 `settingName` 则不需要配置此项。
:::tip
如果你在 plugin.yaml 中配置了 `settingName` 但确没有对应的 `Setting` 自定义模型资源文件,会导致插件无法启动,原因是 `Setting` 模型 `metadata.name` 为你配置的 `settingName` 的资源无法找到。
:::
- `spec.homepage`:通常为插件的 GitHub 仓库链接,或可联系到插件作者或插件官网或帮助中心链接等。
- `spec.displayName`:插件的显示名称,它通常是以少数几个字来概括插件的用途。
- `spec.description`:插件描述,用一段话来介绍插件的用途。
- `spec.license`:插件使用的软件协议,参考:<https://en.wikipedia.org/wiki/Software_license>
:::tip Note
如果你在 plugin.yaml 中配置了 `settingName` 但确没有对应的 `Setting` 自定义模型资源文件,会导致插件无法启动,原因是 `Setting` 模型 `metadata.name` 为你配置的 `settingName` 的资源无法找到。
:::

@ -3,13 +3,13 @@ title: 准备工作
description: 插件开发的准备工作
---
在 Halo 中,插件是使用 Java 和 JavaScript 编写的UI 使用 [Vuejs](https://vuejs.org) 编写。
在 Halo 中,插件是使用 Java 和 JavaScript / TypeScript 编写的UI 使用 [Vuejs](https://vuejs.org) 编写。
在创建你的第一个插件之前,请确保你具备以下条件:
- 你能成功[运行 Halo 2.0.0 及以上版本](../core/run.md)。
- 你应该能够熟练使用 [IntelliJ IDEA](https://www.jetbrains.com/idea/old)。
- 你需要在计算机上安装最新的 LTS 版本的 Node.js。如果你还没有Node.js安装你可以在这里下载 [Node.js 16 LTS](https://nodejs.org/)。
- 你能成功[开发环境运行 Halo](../core/run.md)。
- 你应该能够熟练使用 [IntelliJ IDEA](https://www.jetbrains.com/idea)。
- 你需要在计算机上安装最新的 LTS 版本的 Node.js如果你还没有Node.js安装你可以在这里下载 [Node.js 18 LTS](https://nodejs.org/)。
- 你熟悉 Vue 和 TypeScript。
- 你应该熟悉使用 PNPM 进行包管理,你可以在这里下载 [pnpm 7](https://pnpm.io/)
- Git 是一个版本控制系统,用于跟踪代码的更改您需要 Git 来下载示例插件并发布插件。
- 你应该熟悉使用 Node.js 包管理器
- Git 是一个版本控制系统,用于跟踪代码的更改您需要 Git 来下载示例插件并发布插件。

@ -2,18 +2,139 @@
title: 发布插件
description: 了解如何与我们的社区分享你的插件
---
> 了解如何与我们的社区分享您的扩展。
## 创建你的 Release
了解如何与我们的社区分享你的插件。
当你完成了你的插件并进行充分测试后,切换到插件目录 Build 一次,当没有发生任何错误你就可以推送到 GitHub 并 `Create a new release`
## 创建 Release
然后填写 `Release Tag` 和描述点击创建,项目目录下的 `.github/workflows/workflow.yaml` 文件会被 `GitHub Action` 触发并执行,脚本会自动根据你的 `Release Tag` 修改插件版本号然后在 `Release``Asserts` 中包含打包产物--插件的 JAR 文件
当你完成了你的插件并进行充分测试后,就可以在 GitHub 上创建新的 Release其中版本规范可以参考[版本控制](./introduction.md#版本控制)
## 分享你的插件
## 自动构建
用户可以在你的仓库 Release 下载使用,但为了方便让社区用户看到,你可以在我们的 [awesome-halo](https://github.com/halo-sigs/awesome-halo) 仓库发起一个 Pull Request为此你需要先 Fork [awesome-halo](https://github.com/halo-sigs/awesome-halo) 并按照此仓库的要求添加一行记录是关于你的插件仓库地址和功能描述的,然后推送你的更改并通过 GitHub 向 [awesome-halo](https://github.com/halo-sigs/awesome-halo) 的 `main` 分支发起 Pull Request。
如果你是基于 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 创建的插件项目,那么已经包含了适用于 GitHub Action 的 `workflow.yaml` 文件,里面包含了构建插件和发布插件资源到 Release 的步骤,可以根据自己的实际需要进行修改,以下是示例:
## 等待审核
```yaml
name: Build Plugin JAR File
在你发起 Pull Request 后我们将审查的你的插件并在需要时请求更改。一旦被接受Pull Request 将被合并。
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)
});
```
## 发布你的插件
用户可以在你的仓库 Release 下载使用,但为了方便让 Halo 的用户知道你的插件,可以在以下渠道发布:
1. [halo-sigs/awesome-halo](https://github.com/halo-sigs/awesome-halo):你可以向这个仓库发起一个 PR 提交的插件的信息即可。
2. [Halo 应用市场](https://www.halo.run/store/apps)Halo 官方的应用市场,但目前还不支持开发者注册和发布,如果你想发布到应用市场,可以在 PR 上说明以下,我们会暂时帮你发布。
3. [Halo 论坛](https://bbs.halo.run/t/plugins):你可以在 Halo 官方社区的插件板块发布你的插件。

@ -2,15 +2,16 @@
title: 插件运行模式
description: 了解插件的运行方式
---
Halo 的插件可以在两种模式下运行:`DEVELOPMENT` 和 `DEPLOYMENT`
`DEPLOYMENT`(默认)模式是插件创建的标准工作流程:为每个插件创建一个新的 Gradle 项目,编码插件(声明新的扩展点和/或添加新的扩展),将插件打包成一个 JAR 文件,部署 JAR 文件到 Halo。
这些操作非常耗时,因此引入了 `DEVELOPMENT` 运行时模式。
对于插件开发人员来说,`DEVELOPMENT` 运行时模式的主要优点是不必打包和部署插件。在开发模式下,您可以以简单快速的模式开发插件。
Halo 的插件可以在两种模式下运行:`development` 和 `deployment`
`deployment`(默认)模式是插件创建的标准工作流程:为每个插件创建一个新的 Gradle 项目,编码插件(声明新的扩展点和/或添加新的扩展),将插件打包成一个 JAR 文件,部署 JAR 文件到 Halo。
这些操作非常耗时,因此引入了 `development` 运行时模式。
对于插件开发人员来说,`development` 运行时模式的主要优点是不必打包和部署插件。在开发模式下,您可以以简单快速的模式开发插件。
### 配置
如果你想以 `DEPLOYMENT` 运行插件则做如下配置:
如果你想以 `deployment` 运行插件则做如下配置:
```yaml
halo:
@ -20,7 +21,7 @@ halo:
插件的 `deployment` 模式只允许通过安装 JAR 文件的方式运行插件。
而如果你想以 `DEVELOPMENT` 运行插件或开发插件则将 `runtime-mode` 修改为 `development`,同时配置 `fixed-plugin-path` 为插件项目路径,可以配置多个。
而如果你想以 `development` 运行插件或开发插件则将 `runtime-mode` 修改为 `development`,同时配置 `fixed-plugin-path` 为插件项目路径,可以配置多个。
```yaml
# macOS / Linux
@ -28,7 +29,7 @@ plugin:
runtime-mode: development
fixed-plugin-path:
# 配置为插件绝对路径
- /Users/guqing/halo-plugin-hello-world
- /path/to/halo-plugin-hello-world
# Windows
halo:
@ -36,7 +37,7 @@ halo:
runtime-mode: development
fixed-plugin-path:
# 配置为插件绝对路径
- C:\Users\guqing\halo-plugin-hello-world
- C:\path\to\halo-plugin-hello-world
```
:::tip Note

@ -6,48 +6,42 @@ description: 了解插件的文件结构
新创建的插件项目典型的目录结构如下所示:
```text
.
├── LICENSE
├── README.md
├── console
│ ├── src
│ │ ├── assets
│ │ │ └── logo.svg
│ │ ├── views
│ │ │ └── HomeView.vue
│ │ └── index.ts
│ ├── env.d.ts
│ ├── package.json
│ ├── pnpm-lock.yaml
│ ├── tsconfig.app.json
│ ├── tsconfig.config.json
│ ├── tsconfig.json
│ ├── tsconfig.vitest.json
│ └── vite.config.ts
├── gradle
│   └── .
├── lib
│   └── halo-2.0.0-SNAPSHOT-plain.jar
├── src
│   ├── main
│   │   ├── java
│   │   │   └── run
│   │   │   └── halo
│   │   │   └── starter
│   │   │   └── StarterPlugin.java
│   │   └── resources
│   │   ├── console
│   │   │   ├── main.js
│   │   │   └── style.css
│   │   └── plugin.yaml
│ └── main
│ ├── java
│ │ └── run
│ │ └── halo
│ │ └── starter
│ │ └── StarterPlugin.java
│ └── resources
│ ├── console
│ │ ├── main.js
│ │ └── style.css
│ └── plugin.yaml
├── LICENSE
├── OWNERS
├── README.md
├── build.gradle
├── gradle.properties
├── gradlew
├── gradlew.bat
├── gradle.properties
├── settings.gradle
├── build.gradle
├── console
│   ├── package.json
│   ├── pnpm-lock.yaml
│   ├── src
│   │   ├── assets
│   │   │   └── logo.svg
│   │   ├── components
│   │   │   └── HelloWorld.vue
│   │   ├── index.ts
│   │   ├── styles
│   │   │   └── index.css
│   │   └── views
│   │   └── DefaultView.vue
│   ├── tsconfig.app.json
│   ├── tsconfig.config.json
│   ├── tsconfig.json
│   ├── tsconfig.vitest.json
│   └── vite.config.ts
└── settings.gradle
```
该目录包含了前端和后端两个部分,让我们依次看一下它们中的每一个。
@ -57,13 +51,9 @@ description: 了解插件的文件结构
所有的后端代码都放在 `src` 目录下,它是一个常规的 `Java` 项目目录结构。
- `StarterPlugin.java` 为插件的后端入口文件。
- `resources` 下的 `plugin.yaml` 为插件的资源描述文件,它是必须的。
- `resources/console` 下的两个文件 `main.js``style.css` 是前端插件部分打包时输出的产物。一个插件可以没有前端部分,因此 `resources/console` 同样可以不存在。
`lib/halo-2.0.0-SNAPSHOT-plain.jar` 它是 Halo 的类型依赖,目前使用 `JAR` 文件的方式引入依赖只是暂时的,后续将会改进它,它只作为编译时依赖使用。
### 前端部分
`console` 目录下为插件的前端部分的工程目录,包括了源码、配置文件和静态资源文件。

@ -8,7 +8,7 @@ description: 了解如何与我们的社区分享你的插件
当你完成了你的插件并进行充分测试后,切换到插件目录 Build 一次,当没有发生任何错误你就可以推送到 GitHub 并 `Create a new release`
然后填写 `Release Tag` 和描述点击创建,项目目录下的 `.github/workflows/workflow.yaml` 文件会被 `GitHub Action` 触发并执行,脚本会自动根据你的 `Release Tag` 修改插件版本号然后在 `Release``Asserts` 中包含打包产物--插件的 JAR 文件。
然后填写 `Release Tag` 和描述点击创建,项目目录下的 `.github/workflows/workflow.yaml` 文件会被 `GitHub Action` 触发并执行,脚本会自动根据你的 `Release Tag` 修改插件版本号然后在 `Release``Assets` 中包含打包产物--插件的 JAR 文件。
## 分享你的插件

@ -8,7 +8,7 @@ description: 了解如何与我们的社区分享你的插件
当你完成了你的插件并进行充分测试后,切换到插件目录 Build 一次,当没有发生任何错误你就可以推送到 GitHub 并 `Create a new release`
然后填写 `Release Tag` 和描述点击创建,项目目录下的 `.github/workflows/workflow.yaml` 文件会被 `GitHub Action` 触发并执行,脚本会自动根据你的 `Release Tag` 修改插件版本号然后在 `Release``Asserts` 中包含打包产物--插件的 JAR 文件。
然后填写 `Release Tag` 和描述点击创建,项目目录下的 `.github/workflows/workflow.yaml` 文件会被 `GitHub Action` 触发并执行,脚本会自动根据你的 `Release Tag` 修改插件版本号然后在 `Release``Assets` 中包含打包产物--插件的 JAR 文件。
## 分享你的插件

@ -8,7 +8,7 @@ description: 了解如何与我们的社区分享你的插件
当你完成了你的插件并进行充分测试后,切换到插件目录 Build 一次,当没有发生任何错误你就可以推送到 GitHub 并 `Create a new release`
然后填写 `Release Tag` 和描述点击创建,项目目录下的 `.github/workflows/workflow.yaml` 文件会被 `GitHub Action` 触发并执行,脚本会自动根据你的 `Release Tag` 修改插件版本号然后在 `Release``Asserts` 中包含打包产物--插件的 JAR 文件。
然后填写 `Release Tag` 和描述点击创建,项目目录下的 `.github/workflows/workflow.yaml` 文件会被 `GitHub Action` 触发并执行,脚本会自动根据你的 `Release Tag` 修改插件版本号然后在 `Release``Assets` 中包含打包产物--插件的 JAR 文件。
## 分享你的插件

@ -8,7 +8,7 @@ description: 了解如何与我们的社区分享你的插件
当你完成了你的插件并进行充分测试后,切换到插件目录 Build 一次,当没有发生任何错误你就可以推送到 GitHub 并 `Create a new release`
然后填写 `Release Tag` 和描述点击创建,项目目录下的 `.github/workflows/workflow.yaml` 文件会被 `GitHub Action` 触发并执行,脚本会自动根据你的 `Release Tag` 修改插件版本号然后在 `Release``Asserts` 中包含打包产物--插件的 JAR 文件。
然后填写 `Release Tag` 和描述点击创建,项目目录下的 `.github/workflows/workflow.yaml` 文件会被 `GitHub Action` 触发并执行,脚本会自动根据你的 `Release Tag` 修改插件版本号然后在 `Release``Assets` 中包含打包产物--插件的 JAR 文件。
## 分享你的插件

@ -8,7 +8,7 @@ description: 了解如何与我们的社区分享你的插件
当你完成了你的插件并进行充分测试后,切换到插件目录 Build 一次,当没有发生任何错误你就可以推送到 GitHub 并 `Create a new release`
然后填写 `Release Tag` 和描述点击创建,项目目录下的 `.github/workflows/workflow.yaml` 文件会被 `GitHub Action` 触发并执行,脚本会自动根据你的 `Release Tag` 修改插件版本号然后在 `Release``Asserts` 中包含打包产物--插件的 JAR 文件。
然后填写 `Release Tag` 和描述点击创建,项目目录下的 `.github/workflows/workflow.yaml` 文件会被 `GitHub Action` 触发并执行,脚本会自动根据你的 `Release Tag` 修改插件版本号然后在 `Release``Assets` 中包含打包产物--插件的 JAR 文件。
## 分享你的插件

@ -8,7 +8,7 @@ description: 了解如何与我们的社区分享你的插件
当你完成了你的插件并进行充分测试后,切换到插件目录 Build 一次,当没有发生任何错误你就可以推送到 GitHub 并 `Create a new release`
然后填写 `Release Tag` 和描述点击创建,项目目录下的 `.github/workflows/workflow.yaml` 文件会被 `GitHub Action` 触发并执行,脚本会自动根据你的 `Release Tag` 修改插件版本号然后在 `Release``Asserts` 中包含打包产物--插件的 JAR 文件。
然后填写 `Release Tag` 和描述点击创建,项目目录下的 `.github/workflows/workflow.yaml` 文件会被 `GitHub Action` 触发并执行,脚本会自动根据你的 `Release Tag` 修改插件版本号然后在 `Release``Assets` 中包含打包产物--插件的 JAR 文件。
## 分享你的插件

@ -8,7 +8,7 @@ description: 了解如何与我们的社区分享你的插件
当你完成了你的插件并进行充分测试后,切换到插件目录 Build 一次,当没有发生任何错误你就可以推送到 GitHub 并 `Create a new release`
然后填写 `Release Tag` 和描述点击创建,项目目录下的 `.github/workflows/workflow.yaml` 文件会被 `GitHub Action` 触发并执行,脚本会自动根据你的 `Release Tag` 修改插件版本号然后在 `Release``Asserts` 中包含打包产物--插件的 JAR 文件。
然后填写 `Release Tag` 和描述点击创建,项目目录下的 `.github/workflows/workflow.yaml` 文件会被 `GitHub Action` 触发并执行,脚本会自动根据你的 `Release Tag` 修改插件版本号然后在 `Release``Assets` 中包含打包产物--插件的 JAR 文件。
## 分享你的插件

@ -8,7 +8,7 @@ description: 了解如何与我们的社区分享你的插件
当你完成了你的插件并进行充分测试后,切换到插件目录 Build 一次,当没有发生任何错误你就可以推送到 GitHub 并 `Create a new release`
然后填写 `Release Tag` 和描述点击创建,项目目录下的 `.github/workflows/workflow.yaml` 文件会被 `GitHub Action` 触发并执行,脚本会自动根据你的 `Release Tag` 修改插件版本号然后在 `Release``Asserts` 中包含打包产物--插件的 JAR 文件。
然后填写 `Release Tag` 和描述点击创建,项目目录下的 `.github/workflows/workflow.yaml` 文件会被 `GitHub Action` 触发并执行,脚本会自动根据你的 `Release Tag` 修改插件版本号然后在 `Release``Assets` 中包含打包产物--插件的 JAR 文件。
## 分享你的插件

Loading…
Cancel
Save