{"meta":{"title":"创建 JavaScript 操作","intro":"在本教程中，你将了解如何使用操作工具包生成 JavaScript 操作。","product":"GitHub Actions","breadcrumbs":[{"href":"/zh/actions","title":"GitHub Actions"},{"href":"/zh/actions/tutorials","title":"教程"},{"href":"/zh/actions/tutorials/create-actions","title":"创建操作"},{"href":"/zh/actions/tutorials/create-actions/create-a-javascript-action","title":"创建 JavaScript 操作"}],"documentType":"article"},"body":"# 创建 JavaScript 操作\n\n在本教程中，你将了解如何使用操作工具包生成 JavaScript 操作。\n\n## 简介\n\n在本指南中，您将了解创建和使用打包的 JavaScript 操作所需的基本组件。 本指南的重点是打包操作所需的组件，因此很少讲操作代码的功能。 操作将在日志文件中打印“Hello World”或“Hello \\[who-to-greet]”（如果您提供自定义名称）。\n\n本指南使用 GitHub Actions 工具包 Node.js 模块来加快开发速度。 有关详细信息，请参阅 [actions/toolkit](https://github.com/actions/toolkit) 存储库。\n\n完成此项目后，您应了解如何构建自己的 JavaScript 操作和在工作流程测试该操作。\n\n要确保您的 JavaScript 操作与所有 GitHub 托管的运行器（Ubuntu、Windows 和 macOS）兼容，您编写的封装 JavaScript 代码应该是纯粹的 JavaScript，不能依赖于其他二进制文件。 JavaScript 操作直接在运行器上运行，并使用运行器映像中已存在的二进制文件。\n\n> \\[!WARNING]\n> 创建工作流和操作时，应始终考虑代码是否可能执行潜在攻击者的不受信任的输入。 某些上下文应被视为不受信任的输入，因为攻击者可能会插入自己的恶意内容。 有关详细信息，请参阅“[安全使用指南](/zh/actions/security-guides/security-hardening-for-github-actions#understanding-the-risk-of-script-injections)”。\n\n## 先决条件\n\n在开始之前，您需要下载 Node.js 并创建公共 GitHub 仓库。\n\n1. 下载并安装 Node.js 20.x，其中包含 npm。\n\n   <https://nodejs.org/en/download/>\n\n2. 在 GitHub 上创建一个新的公共存储库，并将其称为“hello-world-javascript-action”。 有关详细信息，请参阅“[创建新仓库](/zh/repositories/creating-and-managing-repositories/creating-a-new-repository)”。\n\n3. 将仓库克隆到计算机。 有关详细信息，请参阅“[克隆仓库](/zh/repositories/creating-and-managing-repositories/cloning-a-repository)”。\n\n4. 在您的终端中，将目录更改到您的新存储库。\n\n   ```shell copy\n   cd hello-world-javascript-action\n   ```\n\n5. 从你的终端，使用 npm 初始化目录以生成 `package.json` 文件。\n\n   ```shell copy\n   npm init -y\n   ```\n\n## 创建操作元数据文件\n\n使用以下示例代码在 `action.yml` 目录中创建一个名为 `hello-world-javascript-action` 的新文件。 有关详细信息，请参阅“[元数据语法参考](/zh/actions/creating-actions/metadata-syntax-for-github-actions)”。\n\n```yaml copy\nname: Hello World\ndescription: Greet someone and record the time\n\ninputs:\n  who-to-greet: # id of input\n    description: Who to greet\n    required: true\n    default: World\n\noutputs:\n  time: # id of output\n    description: The time we greeted you\n\nruns:\n  using: node20\n  main: dist/index.js\n```\n\n此文件定义 `who-to-greet` 输入和 `time` 输出。 它还告知操作运行程序如何开始运行此 JavaScript 操作。\n\n## 添加操作工具包\n\n操作工具包是 Node.js 包的集合，可让您以更高的一致性快速构建 JavaScript 操作。\n\n工具包 [`@actions/core`](https://github.com/actions/toolkit/tree/main/packages/core) 包为工作流命令、输入和输出变量、退出状态和调试消息提供了一个接口。\n\n该工具包还提供一个 [`@actions/github`](https://github.com/actions/toolkit/tree/main/packages/github) 包，该包返回经过身份验证的 Octokit REST 客户端并访问GitHub Actions上下文。\n\n该工具包提供的不仅仅是 `core` 和 `github` 包。 有关详细信息，请参阅 [actions/toolkit](https://github.com/actions/toolkit) 存储库。\n\n在终端上，安装操作工具包 `core` 和 `github` 包。\n\n```shell copy\nnpm install @actions/core @actions/github\n```\n\n你现在应会看到一个 `node_modules` 目录和一个 `package-lock.json` 文件，它们会追踪任何已安装的依赖项及其版本。 不应将 `node_modules` 目录提交到存储库。\n\n## 编写操作代码\n\n此操作使用工具包获取操作元数据文件中所需的 `who-to-greet` 输入变量，然后在日志的调试消息中打印“Hello \\[who-to-greet]”。 接下来，该脚本会获取当前时间并将其设置为作业中稍后运行的操作可以使用的输出变量。\n\nGitHub Actions 提供有关 Webhook 事件、Git refs、工作流、操作和触发工作流的人员的上下文信息。 要访问上下文信息，你可以使用 `github` 包。 您将编写的操作会将 webhook 事件的有效负载打印到日志中。\n\n使用以下代码添加名为 `src/index.js` 的新文件。\n\n```javascript copy\nimport * as core from \"@actions/core\";\nimport * as github from \"@actions/github\";\n\ntry {\n  // `who-to-greet` input defined in action metadata file\n  const nameToGreet = core.getInput(\"who-to-greet\");\n  core.info(`Hello ${nameToGreet}!`);\n\n  // Get the current time and set it as an output variable\n  const time = new Date().toTimeString();\n  core.setOutput(\"time\", time);\n\n  // Get the JSON webhook payload for the event that triggered the workflow\n  const payload = JSON.stringify(github.context.payload, undefined, 2);\n  core.info(`The event payload: ${payload}`);\n} catch (error) {\n  core.setFailed(error.message);\n}\n```\n\n如果在上述 `index.js` 示例中引发错误，`core.setFailed(error.message);` 将使用操作工具包 [`@actions/core`](https://github.com/actions/toolkit/tree/main/packages/core) 包记录消息并设置失败退出代码。 有关详细信息，请参阅“[为操作设置退出代码](/zh/actions/creating-actions/setting-exit-codes-for-actions)”。\n\n## 创建自述文件\n\n要让人们了解如何使用您的操作，您可以创建自述文件。 自述文件在您计划公开分享操作时最有用，但也是提醒您或您的团队如何使用该操作的绝佳方式。\n\n在 `hello-world-javascript-action` 目录中，创建一个用于指定以下信息的 `README.md` 文件：\n\n* 操作用途的详细说明。\n* 必需的输入和输出参数。\n* 可选输入和输出参数。\n* 操作使用的密钥。\n* 操作使用的环境变量。\n* 在工作流中使用您的操作的示例。\n\n````markdown copy\n# Hello world JavaScript action\n\nThis action prints \"Hello World\" or \"Hello\" + the name of a person to greet to the log.\n\n## Inputs\n\n### `who-to-greet`\n\n**Required** The name of the person to greet. Default `\"World\"`.\n\n## Outputs\n\n### `time`\n\nThe time we greeted you.\n\n## Example usage\n\n```yaml\nuses: actions/hello-world-javascript-action@e76147da8e5c81eaf017dede5645551d4b94427b\nwith:\n  who-to-greet: Mona the Octocat\n```\n````\n\n## 提交、标记和推送操作\n\nGitHub 会在运行时下载工作流中运行的每个操作，并将其作为完整的代码包执行，然后你才能使用 `run` 等工作流命令与运行器机器交互。 这意味着您必须包含运行 JavaScript 代码所需的所有包依赖项。 例如，此操作使用了 `@actions/core` 和 `@actions/github` 包。\n\n签入 `node_modules` 目录可能会导致问题。 作为替代方案，你可以使用诸如 [`rollup.js`](https://github.com/rollup/rollup) 或 [`@vercel/ncc`](https://github.com/vercel/ncc) 之类的工具，将代码和依赖项合并成一个文件以便分发。\n\n1. 通过在终端中运行以下命令来安装 `rollup` 及其插件。\n\n   `npm install --save-dev rollup @rollup/plugin-commonjs @rollup/plugin-node-resolve`\n\n2. 在仓库根目录下创建一个名为 `rollup.config.js` 的新文件，并添加以下代码。\n\n   ```javascript copy\n   import commonjs from \"@rollup/plugin-commonjs\";\n   import { nodeResolve } from \"@rollup/plugin-node-resolve\";\n\n   const config = {\n     input: \"src/index.js\",\n     output: {\n       esModule: true,\n       file: \"dist/index.js\",\n       format: \"es\",\n       sourcemap: true,\n     },\n     plugins: [commonjs(), nodeResolve({ preferBuiltins: true })],\n   };\n\n   export default config;\n   ```\n\n3. 编译 `dist/index.js` 文件。\n\n   `rollup --config rollup.config.js`\n\n   你将看到一个新的 `dist/index.js` 文件，其中包含了你的代码和任何依赖项。\n\n4. 在终端中提交这些更新。\n\n   ```shell copy\n   git add src/index.js dist/index.js rollup.config.js package.json package-lock.json README.md action.yml\n   git commit -m \"Initial commit of my first action\"\n   git tag -a -m \"My first action release\" v1.1\n   git push --follow-tags\n   ```\n\n当你提交并推送代码后，你的更新后的仓库应如下所示：\n\n```text\nhello-world-javascript-action/\n├── action.yml\n├── dist/\n│   └── index.js\n├── package.json\n├── package-lock.json\n├── README.md\n├── rollup.config.js\n└── src/\n    └── index.js\n```\n\n## 在工作流程中测试您的操作\n\n现在，您已准备好在工作流程中测试您的操作。\n\n位于任何存储库内的工作流均可使用公共操作。 当操作位于专用存储库中时，存储库设置决定了该操作是仅在同一存储库中可用，还是在同一用户或组织拥有的其他存储库中也可使用。 有关详细信息，请参阅“[管理存储库的GitHub Actions设置](/zh/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository)”。\n\n### 使用公共操作的示例\n\n此示例显示您的新公共操作如何从外部仓库中运行。\n\n将以下 YAML 复制到 `.github/workflows/main.yml` 处的新文件中，并使用你的用户名和你在上面创建的公共存储库的名称更新 `uses: octocat/hello-world-javascript-action@1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b` 行。 还可以将 `who-to-greet` 输入替换为你的名称。\n\n```yaml copy\non:\n  push:\n    branches:\n      - main\n\njobs:\n  hello_world_job:\n    name: A job to say hello\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Hello world action step\n        id: hello\n        uses: octocat/hello-world-javascript-action@1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b\n        with:\n          who-to-greet: Mona the Octocat\n\n      # Use the output from the `hello` step\n      - name: Get the output time\n        run: echo \"The time was ${{ steps.hello.outputs.time }}\"\n```\n\n当触发此工作流时，运行器将从你的公共存储库下载 `hello-world-javascript-action` 操作，然后执行它。\n\n### 使用私有操作的示例\n\n将工作流代码复制到操作存储库中的 `.github/workflows/main.yml` 文件中。 还可以将 `who-to-greet` 输入替换为你的名称。\n\n```yaml copy\non:\n  push:\n    branches:\n      - main\n\njobs:\n  hello_world_job:\n    name: A job to say hello\n    runs-on: ubuntu-latest\n\n    steps:\n      # To use this repository's private action,\n      # you must check out the repository\n      - name: Checkout\n        uses: actions/checkout@v5\n\n      - name: Hello world action step\n        uses: ./ # Uses an action in the root directory\n        id: hello\n        with:\n          who-to-greet: Mona the Octocat\n\n      # Use the output from the `hello` step\n      - name: Get the output time\n        run: echo \"The time was ${{ steps.hello.outputs.time }}\"\n```\n\n从存储库中，单击“操作”选项卡，然后选择最新的工作流运行。 在“作业”下或可视化图中，单击“表示问候的作业” 。\n\n单击“Hello world action step”，应该会看到“Hello Mona the Octocat”或日志中输出的用于 `who-to-greet` 输入的名称。 要查看时间戳，单击“获取输出时间”。\n\n## 用于创建 JavaScript 操作的模板存储库\n\nGitHub 提供了用于创建 JavaScript 和 TypeScript 操作的模板存储库。 可以使用这些模板快速开始创建包含测试、Lint 分析和其他建议做法的新操作。\n\n* [\n  `javascript-action` 模板存储库](https://github.com/actions/javascript-action)\n* [\n  `typescript-action` 模板存储库](https://github.com/actions/typescript-action)\n\n## GitHub.com\n\n上的示例 JavaScript 操作\n\n可以在 GitHub.com\n上找到许多 JavaScript 操作示例。\n\n* [DevExpress/testcafe-action](https://github.com/DevExpress/testcafe-action)\n* [duckduckgo/privacy-configuration](https://github.com/duckduckgo/privacy-configuration)"}