---
url: /zh/build/script-vs-plugin.md
description: |
  对比 CNB 流水线中脚本任务和插件任务的核心区别， 帮助开发者根据场景选择合适的任务类型。
---
## 概述

在 CNB 流水线中，`Job`（任务）是最小的执行单元，
主要分为**脚本任务**、**插件任务**和**内置任务**三种类型。

* **脚本任务**：以镜像作为执行环境，执行用户指定的 Shell 命令
* **插件任务**：运行插件镜像内置的 `ENTRYPOINT`，通过参数配置行为

两者都支持指定 `image`，初学者容易混淆，本文说明其区别。

## 核心对比

| 特性               | 脚本任务                                                | 插件任务                              |
|--------------------|---------------------------------------------------------|---------------------------------------|
| **执行方式**       | `script` / `commands` Shell 命令                        | 镜像内置 `ENTRYPOINT`                 |
| **执行环境**       | 流水线容器或独立容器                                    | 仅独立容器                            |
| **参数传递**       | 通过 `env` / `imports`                     | 通过 `settings`（→ `PLUGIN_*`）          |
| **自定义环境变量** | ✅ 支持                                     | ❌ 不会传递                               |
| **灵活性**         | 高，可执行任意命令                                      | 由插件开发者预设                      |
| **复用性**         | 与业务强相关                                            | 可分享到插件市场                      |

**执行环境详情**：

* **脚本任务**：未指定 `image` 时在流水线容器中执行；指定 `image` 时在独立容器中执行。
* **插件任务**：必须指定 `image`，始终在独立容器中执行。

## 执行环境与文件共享 {#file-sharing}

* 未配置 `image` 的脚本任务在**流水线容器**中执行
* 配置了 `image` 的脚本任务和插件任务在**独立容器**中执行

只有以下目录在任务间共享，其他目录相互不可见：

* `CNB_BUILD_WORKSPACE`（默认 `/workspace`）
* [`docker.volumes`](./grammar.md#pipeline-docker-volumes) 中声明的目录

::: tip 提示
如果上一个任务产生的文件在下一个任务中找不到，
请确认文件已写入 `CNB_BUILD_WORKSPACE` 或已声明的共享目录。
:::

## 示例

### 脚本任务

```yaml
- name: install dependencies
  image: node:20
  script: npm install
  env:
    NODE_ENV: production
```

### 插件任务

```yaml
- name: npm publish
  image: plugins/npm:1.2.3
  settings:
    username: $NPM_USER
    password: $NPM_PASS
    registry: https://registry.npmjs.org/
```

上述 `settings` 会注入为环境变量：`PLUGIN_USERNAME`、`PLUGIN_PASSWORD`、`PLUGIN_REGISTRY`。

## 环境变量传递规则

**脚本任务**：

* ✅ `env` 声明的自定义环境变量
* ✅ `imports` 导入的环境变量
* ✅ CNB 内置系统环境变量
* 优先级：`Job env` > `Stage env` > `Pipeline env`

**插件任务**：

* ✅ CNB 内置系统环境变量
* ✅ `settings` / `args` 中通过 `$VAR` 引用环境变量
* ❌ `env` / `imports` 声明的自定义环境变量（不会传递）

```yaml
# ✅ settings 中引用环境变量
- name: npm publish
  image: plugins/npm
  settings:
    username: $NPM_USER

# ❌ env 不会传递给插件
- name: npm publish
  image: plugins/npm
  env:
    PLUGIN_USERNAME: $NPM_USER
```

::: details 为什么插件任务不能通过 env 传递环境变量？
插件任务的设计理念是功能模块化，通过 `settings` 明确定义插件接受的参数。
好处在于：

* 插件接口清晰，避免参数混乱
* 插件可以明确声明需要哪些参数
* 避免环境变量命名冲突，提高可维护性
* 防止通过环境变量注入引起安全问题
  :::

## 选择建议

| 场景                              | 推荐       | 原因                                            |
|-----------------------------------|------------|-------------------------------------------------|
| 代码构建和测试                    | 脚本任务   | 灵活控制，适应各种构建工具                      |
| 自定义业务逻辑                    | 脚本任务   | 需要完全控制执行流程                            |
| 消息通知（企微、飞书等）          | 插件任务   | 功能内聚，配置简单                              |
| 通用 CI/CD 操作（发布、部署）     | 插件任务   | 插件市场有成熟方案                              |
| 可复用的通用功能                  | 插件任务   | 可分享到插件市场                                |

::: tip 快速判断

1. 有现成插件满足需求？→ 插件任务
2. 需要灵活执行自定义命令？→ 脚本任务
3. 功能可复用分享？→ 插件任务
4. 与业务强相关？→ 脚本任务
   :::

## 最佳实践

### 版本控制

用到 `image` 的地方，建议指定镜像版本。
使用 `latest` 时镜像更新可能导致流水线行为变化。

```yaml
- name: npm publish
  image: plugins/npm:1.2.3
  settings:
    username: $NPM_USER

- name: build
  image: node:20
  script: npm run build
```

### 安全性

* 使用官方或可信的镜像
* 在密钥仓库中存储敏感信息，避免硬编码
* 定期检查镜像的安全更新

### 复用性

**插件任务**适合封装可复用的功能：

* 消息通知（企业微信、飞书、钉钉等）
* 通用 CI/CD 操作（发布、部署等）
* 代码质量检查（lint、扫描等）

这些功能可发布到 CNB 插件市场，供其他开发者选用。

**脚本任务**适合业务相关的逻辑：

* 代码构建和测试
* 自定义数据处理
* 特定业务流程

## 常见问题

### 脚本任务不指定 image 会在哪里执行？

在流水线容器环境中执行。
流水线容器环境由 `Pipeline.docker.image` 或 `Stage.image` 指定，
详见[构建环境 - 环境作用范围](./build-env.md#环境作用范围)。

### 如何在插件任务中使用环境变量？

在 `settings` 或 `args` 中使用变量替换：

```yaml
- name: npm publish
  image: plugins/npm
  settings:
    username: $NPM_USER
    password: $NPM_PASS
```

### 插件任务可以执行自定义脚本吗？

插件任务可以通过 `settings` 参数传递脚本内容给插件，
但这需要插件本身支持接收并执行脚本。
执行逻辑由插件镜像控制，CNB 不直接介入。

脚本任务则不同，`script` 或 `commands` 是 CNB 语法原生支持的，
由 CNB 在脚本任务指定的 `image` 或流水线容器环境中直接执行。
