GitHub|一起编写个多用途 Github Action 吧!( 三 )


6. 发布到 github marketplace
在手机上下载微软的 Authenticator 软件 , 然后扫描 Github 的 Two factor 绑定的二维码 , 这样你的 Github Action 就被顺利的发布到了 插件市场 里了 。
庆祝一下你的成功吧!
开始进阶之旅
当然笔者远不止想介绍这么多 , 不然标题的 多用途 三个字就没提现出来 。
接下来我们同时要把这个包的主逻辑抽离出来 , 发布成 npm 包 , 再通过 mock 的上下文 , 构建单元测试用例 。 具体怎么做呢?
核心其实很简单:代码分割 和 条件编译
0. 条件编译
我们开发者对这个再熟悉不过了 , 通过条件编译可以直接去除一些 unreachable code , 比如我们发布成 npm 包给用户用 , 自然是不需要 @actions/core 和 @actions/github 的 。那么就可以在打包时直接把它们干掉 。
实现它的手段很多 , 比如 webpack.DefinePlugin , @rollup/plugin-replace , esbuild#define 等等 。
1. 代码分割
这个借助打包工具也很容易实现 , 比如我们原先引入是用静态写法:
import { getActionOptions  from './action'
接下来我们改为 async/await动态引入
async function mian() {  const { getActionOptions  = await import('./action')
通过这种方式 , 打包工具除了默认的 output 配置 , 会生成 [name
.js 的 entryFile 外 , 还会生成一些 [name
-[hash
.js 的 chunkFile , 来交给运行时动态加载 。
2. 添加条件变量 , 并统筹
这里我们添加一个 __isAction__ 的布尔值变量
declare var __isAction__: boolean
对于 action 和 npm 的不同 , 主要在于它们的入参出参方式不同 , 还有上下文不同 。
那么我们就可以根据这 2 点 , 进行编译时重载:
3. 重载获取参数
我们获取参数就可以这么写:
export async function getOptions (  options?: UserDefinedOptions): Promise<UserDefinedOptions> {  let opt: Partial<UserDefinedOptions>  if (__isAction__) {    const { getActionOptions  = await import('./action')    opt = getActionOptions()   else {    opt = options    return defu<Partial<UserDefinedOptions> UserDefinedOptions>(    opt    getDefaults()  ) as UserDefinedOptions
这样在打包时就能确定代码的走向 。
4. 重载获取
我们获取 Octokit 实例就可以这么写:
const { token  = optionslet octokitif (__isAction__) {  const { github  = await import('./action')  octokit = github.getOctokit(token) else {  const { Octokit  = await import('@octokit/rest') // require()  octokit = new Octokit({    auth: token  )
这样 action 走 @actions/github , 默认情况下走 @octokit/rest , 获得的 Octokit 也是一致的 。
5. 更改打包配置
我们添加 BUILD_TARGET 环境变量 , 当值为 action 打包 Action , 默认为 npm 包 。
这样我们很容易可以编写出这样的 rollup.config.js:
import typescript from '@rollup/plugin-typescript'import { nodeResolve  from '@rollup/plugin-node-resolve'import commonjs from '@rollup/plugin-commonjs'import json from '@rollup/plugin-json'import pkg from './package.json'import replace from '@rollup/plugin-replace'import { terser  from 'rollup-plugin-terser'const isDev = process.env.NODE_ENV === 'development'const isAction = process.env.BUILD_TARGET === 'action'/** @type {import('rollup').OutputOptions */const npmOutput = {  file: pkg.main  format: 'cjs'  sourcemap: isDev  exports: 'auto'/** @type {import('rollup').OutputOptions */const actionOutput = {  dir: 'lib'  format: 'cjs'  exports: 'auto'/** @type {import('rollup').RollupOptions */const config = {  input: 'src/index.ts'  output: isAction ? actionOutput : npmOutput  plugins: [    isAction ? terser() : undefined    replace({      preventAssignment: true      values: {        __isAction__: JSON.stringify(isAction)          )    json()    nodeResolve({      preferBuiltins: true    )    commonjs()    typescript({      tsconfig: isAction ? './tsconfig.action.json' : './tsconfig.build.json'      sourceMap: isDev    )