在 GitHub Actions 中一行打包网页:Pake Composite Action(tw93/Pake@v3)全解析
【免费下载链接】Pake🤱🏻 Turn any webpage into a desktop app with one command.项目地址: https://gitcode.com/GitHub_Trending/pa/Pake
Pake 是一个基于 Rust + Tauri 的命令行工具,能把任意网页打包成轻量级桌面应用。除了本地运行 CLI 之外,Pake 在仓库根目录内置了一个 Composite Action(docs/pake-action.md的讲解对象),让你的仓库只需添加一个uses: tw93/Pake@v3步骤,就能在 CI 中自动完成环境搭建、构建并产出可分发的安装包。读完本文,你将掌握该 Action 的全部输入/输出参数、各平台产物形态、跨平台矩阵构建写法,以及它内部的调用链与安全设计(环境变量传参、临时文件、换行注入防护),并能将其直接落地到自己的 CI 流程中。
快速开始:一个步骤即可构建
最简单的用法是在 workflow 中添加一个 step:
- name: Build Pake App uses: tw93/Pake@v3 with: url: "https://example.com" name: "MyApp"一个完整的最小 workflow 如下(官方示例):
name: Build Web App on: [push] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: tw93/Pake@v3 with: url: "https://weekly.tw93.fun" name: "WeeklyApp"需要注意的一个前提:该 Composite Action 的构建脚本依赖工作区里的dist/cli.js与src-tauri模板,因此它设计用于在Pake 仓库的 fork(或包含 Pake 完整代码的仓库)中运行——这正是 内建工作流使用指南 建议"Fork 仓库后运行 workflow"的原因。如果你的仓库不含 Pake 源码,Action 里的npm run cli:build(基于 rollup.config.js)将无法产出 CLI。
输入参数(Inputs)逐项说明
docs/pake-action.md 给出的参数表如下,定义源头是 action.yml 第 8–39 行的inputs:段:
| 参数 | 说明 | 必填 | 默认值 |
|---|---|---|---|
url | 要打包的目标 URL | ✅ | — |
name | 应用名称 | ✅ | — |
output-dir | 产物输出目录 | dist | |
icon | 自定义应用图标(URL 或路径) | (未提供时自动抓取站点图标) | |
width | 窗口宽度 | 1200 | |
height | 窗口高度 | 780 | |
debug | 启用调试模式 | false |
几个结合实现与 CLI 文档的补充点:
url/name:url是必填的打包对象;name会原样传给 CLI 的--name。关于应用名在 Windows/macOS 保留空格与大小写、在 Linux 转为小写连字符的规则,见 CLI 使用文档。icon:Action 只在值非空时才追加--icon参数(见下文调用链)。留空时 Pake CLI 会自动抓取网站图标并转换为平台对应格式,完整行为参考 CLI 文档的 icon 章节。width/height:默认值1200/780与 Pake CLI 的默认窗口尺寸一致(action.yml 中default: "1200"、default: "780",与 cli-usage.md 中--width/--height的默认值相互印证)。output-dir:脚本会先mkdir -p该目录,再把构建产物移入(action.yml)。该参数还会经过换行符校验,防止通过注入换行写入额外的GITHUB_OUTPUT键值对(详见"安全设计"一节)。debug:仅在字符串严格等于"true"时追加--debug标志。它对应 CLI 的 debug 选项(构建调试版并输出更多日志),而非浏览器开发者工具开关。
输出(Outputs)
| 输出 | 说明 |
|---|---|
package-path | 生成安装包的路径 |
在 action.yml 中,package-path通过value: ${{ steps.build.outputs.package-path }}从名为build的步骤透出。也就是说,后续步骤可以直接消费它:
- uses: tw93/Pake@v3 id: pake - name: Show result run: echo "Package is at: ${{ steps.pake.outputs.package-path }}"官方示例:自定义图标与多平台矩阵构建
带自定义图标、指定窗口尺寸
- uses: tw93/Pake@v3 with: url: "https://example.com" name: "MyApp" icon: "https://example.com/icon.png" width: 1400 height: 900多平台矩阵构建
利用 GitHub 的 matrix 策略,可以在一次 workflow 中并行构建多个平台:
jobs: build: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - uses: tw93/Pake@v3 with: url: "https://example.com" name: "CrossPlatformApp"文档建议的矩阵写法就是"哪种 runner 产出哪个平台的包",各平台的产物形态如下:
| 平台 | Runner | 产物 |
|---|---|---|
| Linux | ubuntu-latest | .deb包 |
| macOS | macos-latest | .app与.dmg包 |
| Windows | windows-latest | .exe与.msi包 |
一个值得注意的实现细节:Action 在 macOS 上构建时导出了环境变量PAKE_CREATE_APP=1(action.yml)。对照 MacBuilder.ts 的构造逻辑——当process.env.PAKE_CREATE_APP === '1'时,buildFormat被强制设为app(而非默认的dmg),这解释了为什么 Action 的find查找逻辑会"先找文件类安装包、找不到再回退到.app目录":在 CI 的无头环境里,.appbundle 是默认产物形态。
How It Works:Action 内部执行流程
docs/pake-action.md 概述了三步流程,下面对照 action.yml 的runs:段(using: "composite",两个 composite step)逐步展开。
第 1 步:Setup Environment(自动搭建工具链)
对应 action.yml 的 "Setup Environment" 步骤,shell: bash,做了三件事:
- 安装 Node.js 依赖:直接
npm install。 - 按需构建 Pake CLI:仅当
dist/cli.js不存在时执行npm run cli:build。该脚本定义在 package.json 的scripts段("cli:build": "cross-env NODE_ENV=production rollup -c"),产物即bin声明的 CLI 入口dist/cli.js。 - 按需安装 Rust/Cargo:若
cargo不在 PATH 中,用curl --proto '=https' --tlsv1.2 -sSf下载 rustup 安装脚本并静默执行,然后把$HOME/.cargo/bin追加到$GITHUB_PATH供后续步骤使用。这里使用了mktemp生成唯一临时文件存放安装脚本,并以trap 'rm -f "$rustup_init"' EXIT保证退出时清理(action.yml)——这是对共享 runner 上固定路径(如/tmp/rustup-init.sh)竞争覆盖问题的修复。
第 2 步:Build Pake App(拼装并执行 CLI 命令)
对应 action.yml 的 "Build Pake App" 步骤(id: build)。
输入通过环境变量传递,而非行内插值。该步骤先声明env:块,把 7 个 input 映射为INPUT_*环境变量(action.yml):
env: INPUT_URL: ${{ inputs.url }} INPUT_NAME: ${{ inputs.name }} INPUT_ICON: ${{ inputs.icon }} INPUT_WIDTH: ${{ inputs.width }} INPUT_HEIGHT: ${{ inputs.height }} INPUT_DEBUG: ${{ inputs.debug }} INPUT_OUTPUT_DIR: ${{ inputs.output-dir }}这个设计有明确的测试保障:tests/unit/action-input-security.test.ts 会读取action.yml源码并断言——run:脚本中不得出现${{ inputs.* }}行内表达式(正则/\$\{\{\s*inputs\./必须不匹配),且上述 7 条env映射必须逐一存在。其含义是:用户输入永远不会被拼进 shell 命令文本,而是作为环境变量由 shell 在运行时读取,从结构上杜绝了输入内容改写脚本的可能性。
参数拼装逻辑(action.yml):
- 先校验
INPUT_OUTPUT_DIR不含换行符(\n/\r),违规直接报错退出; ARGS=("$INPUT_URL")以 URL 位置参数开头,随后追加--name "$INPUT_NAME";icon仅在非空时追加--icon;--width、--height始终追加(未传时为 action 默认值);debug仅在等于"true"时追加--debug;mkdir -p输出目录并export PAKE_CREATE_APP=1;- 最后以
printf ' %q'打印完整命令(便于在 CI 日志中复核),再执行node dist/cli.js "${ARGS[@]}"。
也就是说,Action 并没有绕过 Pake CLI,而是把它完整包了一层:CLI 内部由 BuilderProvider.ts 按当前 OS 分发到 LinuxBuilder.ts、MacBuilder.ts 或 WinBuilder.ts,真正执行 Tauri 构建。
第 3 步:Package Output(查找并搬运产物)
构建完成后,脚本在src-tauri/target下查找产物(action.yml):
- 先
find文件类安装包:*.deb、*.exe、*.msi、*.dmg,取第一个命中(head -1); - 若未找到,回退查找 macOS 的
.app目录(呼应上面PAKE_CREATE_APP=1的产物形态差异); - 找到后
mv到$INPUT_OUTPUT_DIR/<basename>,移动失败则退回cp -r;写入前再次校验PACKAGE_PATH无换行符,防止污染GITHUB_OUTPUT; - 成功则
printf 'package-path=%s\n' "$PACKAGE_PATH" >> "$GITHUB_OUTPUT"并打印确认;找不到任何产物则打印 "No package found" 并以退出码 1 失败。
以上三条输出相关的断言(id: build、GITHUB_OUTPUT写入格式、output 值映射)同样被 action-input-security.test.ts 固化,属于"文档承诺即代码契约"的验证方式。
安全设计:为什么输入处理要这么讲究
docs/pake-action.md 没有专门展开安全部分,但 action.yml 的实现里体现了几处对 CI 注入场景的防御,值得了解:
- 表达式与脚本分离:
run:脚本中不出现任何${{ inputs.* }},所有输入经env:传递(见上文),测试用例逐条断言该约束; - 换行注入防护:
GITHUB_OUTPUT采用key=value逐行追加格式,若output-dir或最终包路径中含换行符,攻击者可以追加任意键(例如覆盖steps.*.outputs或触发后续步骤执行)。脚本在写入前对两处路径做了$'\n'/$'\r'检查并直接失败退出(action.yml 与 action.yml); - 临时文件唯一化:rustup 安装脚本经
mktemp生成、trap清理,不再使用固定的/tmp/rustup-init.sh(测试显式断言源码中不再包含该路径); - 命令打印与执行分离:
printf ' %q'让日志中的命令与实际执行内容一一对应,便于审计参数是否被意外篡改。
与仓库内建 Workflow 的关系
Pake 项目自身维护了两套基于 GitHub Actions 的构建方式,不要混淆:
- 本文的主角——Composite Action(action.yml):供你自己的项目通过
uses: tw93/Pake@v3引用的通用打包步骤,参数即上文 Inputs 表。 - 项目内建 workflow:位于 .github/workflows/single-app.yaml 的 "Build Single Popular App",支持
workflow_dispatch表单触发,参数更丰富(new_window、incognito、--targets deb,appimage、macOS--targets universal --multi-arch、签名与公证 secrets 等),使用actions/upload-artifact上传产物、打 tag 时推送到 Release。其使用方式见 GitHub Actions Usage。
两者底层调用的是同一个 CLI(node dist/cli.js),差异只在于 workflow 层能传--targets等 Action 未暴露的 CLI 选项。更多 CLI 选项请参考 CLI Usage 与 Advanced Usage(均有中文版本:cli-usage_CN.md、advanced-usage_CN.md)。
小结
- 用法:在(fork 的)Pake 仓库 workflow 里加一步
uses: tw93/Pake@v3,必填url+name,可选icon、width(默认 1200)、height(默认 780)、output-dir(默认dist)、debug;输出package-path供后续步骤使用。 - 原理:composite action 自动完成
npm install→ 按需cli:build→ 按需装 Rust → 环境变量传参调用node dist/cli.js→ 在src-tauri/target中定位.deb/.exe/.msi/.dmg/.app并搬运到输出目录。 - 平台:Ubuntu runner 出
.deb,macOS runner 出.app/.dmg(CI 中默认.app形态),Windows runner 出.exe/.msi;矩阵策略可并行多平台。 - 可验证依据:参数定义见 action.yml,安全约束见 tests/unit/action-input-security.test.ts,macOS 产物形态见 bin/builders/MacBuilder.ts,文档主体见 docs/pake-action.md。
【免费下载链接】Pake🤱🏻 Turn any webpage into a desktop app with one command.项目地址: https://gitcode.com/GitHub_Trending/pa/Pake
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考