Monorepo 自动 release 的思考

Monorepo 自动 release 的思考

简介

最近在 Monorepo 模式下开发开源软件 plus-pro-components 时遇到自动 release 的问题,调研了 Changesets,实际使用发现它发包灵活度太低而且过程繁琐,果断弃用。调研了 rushjs,发现 rush 很规范,但是对于团队来说,上手成本太高,也弃用。于是查看 vue 源码,发现它是自己写的,高度契合项目,对我来说这种解决方案非常完美,于是自己写了一个自动 release 的工具。

实现的效果

  1. 自动查询子包
  2. 按需更新子包(符合 semver 规范 )的版本
  3. 自动生成 changelog
  4. 自动 commitLint (符合 angular 提交规范 )
  5. 自动代码 lint
  6. 自动打 tag
  7. 自动提交代码

核心源码

import path from 'path'
import fs from 'fs'
import semver from 'semver'
import consola from 'consola'
import execa from 'execa'
import { checkbox, select, input } from '@inquirer/prompts'
import { findWorkspacePackages } from '@pnpm/find-workspace-packages'
import { projRoot, pcPackage, projPackage } from '../build/paths'
import { PKG_NAME } from '../build/utils'

type SemverRow = {
  release: semver.ReleaseType
  optionsOrLoose?: boolean | semver.Options | string
  identifier?: string
}

// 打印
const echo = (msg: string) => consola.success(msg)

// 运行脚本
const run = (bin: string, args: string[], opts = {}) =>
  execa(bin, args, { stdio: 'inherit', ...opts })

//  版本列表
const versionIncrements: SemverRow[] = [
  {
    release: 'patch'
  },
  {
    release: 'minor'
  },
  {
    release: 'major'
  },
  {
    release: 'prepatch',
    optionsOrLoose: 'rc',
    identifier: '1'
  },
  {
    release: 'preminor',
    optionsOrLoose: 'rc',
    identifier: '1'
  },
  {
    release: 'premajor',
    optionsOrLoose: 'rc',
    identifier: '1'
  },
  {
    release: 'prerelease',
    optionsOrLoose: 'alpha',
    identifier: '1'
  },
  {
    release: 'prerelease',
    optionsOrLoose: 'beta',
    identifier: '1'
  }
]

// 获取工作空间包
const getWorkspaceList = async (dir = projRoot) => {
  const pkgs = await findWorkspacePackages(projRoot)
  return pkgs
    .filter(pkg => pkg.dir.startsWith(dir))
    .filter(pkg => pkg.manifest.private !== true && pkg.manifest.name)
}

/**
 * 更新版本号
 * @param {string} version
 */
const updatePackage = (version: string, pkgPath: string) => {
  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
  pkg.version = version
  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
}

// 获取版本
const getVersion = async (currentVersion: string) => {
  // 发布版本
  let version: string | null
  const selectChoices = versionIncrements
    .map(item => {
      const value = semver.inc(
        currentVersion,
        item.release,
        item.optionsOrLoose as any,
        item.identifier
      )
      const name = `${item.release} (${value}})`

      return {
        name,
        value
      }
    })
    .concat({ name: 'custom', value: 'custom' })

  version = await select({
    message: 'Select release type',
    choices: selectChoices
  })

  // 自定义版本
  if (version === 'custom') {
    version = await input({ message: 'Enter custom version' })
    // 校验版本
    if (!semver.valid(version)) {
      throw new Error(`Illegal version: ${version}`)
    }
  }

  return version
}

// 提交
async function commit(version?: string) {
  try {
    // 生成changelog
    if (version) {
      await run('npm', ['run', '--name', 'changelog'])
    }

    await run('git', ['add', '-A'])

    // 打tag
    if (version) {
      await run('git', ['tag', '-a', version, '-m', `v${version}`])
    }

    // 规范化提交
    await run('npm', ['run', '--name', 'gitcz'])
    await run('git', ['pull'])

    // push tag
    if (version) {
      await run('git', ['push', '--tags'])
    }
    await run('git', ['push'])
    echo(`\ncommit success ${version}`)
  } catch (error: any) {
    throw new Error(error)
  }
}

const main = async () => {
  const workspaceList = await getWorkspaceList()
  const workspaceNames = workspaceList.map(
    item => item.manifest.name
  ) as string[]
  const workspaceMaps = workspaceList.map(item => ({
    dir: item.dir,
    name: item.manifest.name,
    version: item.manifest.version,
    pkg: path.resolve(item.dir, 'package.json')
  }))

  // 选择需要更新的包
  let selectPackages: string[] = []
  const checkboxChoices = workspaceNames.map(item => ({
    name: item,
    value: item
  }))
  const packages = await checkbox({
    message: 'Which packages would you like to include?',
    choices: [{ name: 'all', value: 'all' }, ...checkboxChoices]
  })

  if (!packages.length) {
    throw new Error('Please select one or more packages!')
  }

  if (packages.includes('all')) {
    selectPackages = workspaceNames
  } else {
    selectPackages = [...packages]
  }

  // 更新版本号
  for (let index = 0; index < selectPackages.length; index++) {
    const name = selectPackages[index]
    const packageInfo = workspaceMaps.find(i => i.name === name)
    const version = await getVersion(packageInfo?.version as string)
    updatePackage(version as string, packageInfo?.pkg as string)
    consola.success(`Successfully updated version ${name}!`)
  }

  if (selectPackages.includes(PKG_NAME)) {
    // 主包更新
    const mainPkg = JSON.parse(fs.readFileSync(pcPackage, 'utf-8'))
    updatePackage(mainPkg.version as string, projPackage)
    commit(mainPkg.version)
  } else {
    commit()
  }
}

main()
  .then(() => {
    console.log('success')
  })
  .catch(err => {
    console.log(err)
  })

运行效果

$ yarn release
yarn run v1.22.19
$ tsx scripts/release/index.ts
? Which packages would you like to include? (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)
>( ) all
 ( ) @plus-pro-components/echarts
 ( ) @plus-pro-components/eslint-config
 ( ) plus-pro-components
 ( ) @plus-pro-components/utils

? Which packages would you like to include? plus-pro-components
? Select release type (Use arrow keys)
> patch (0.0.1})
  minor (0.1.0})
  major (1.0.0})
  prepatch (0.0.2-rc.1})
  preminor (0.1.0-rc.1})
  premajor (1.0.0-rc.1})
  prerelease (0.0.1-alpha.4})
(Move up and down to reveal more choices)
...

源码地址

源码地址

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 6678应用程序release加载自动是指在软件开发过程中,当某个版本的应用程序开发完成后,它会被发布到目标设备或系统中,并且能够在用户启动设备或系统时自动加载运行。 在发布过程中,开发团队会对应用程序进行一系列的测试、修复Bug和性能优化等工作,确保应用程序能够稳定运行并满足用户需求。一旦开发团队确定应用程序达到发布的标准,他们会选择一个合适的时间点将应用程序发布到目标设备或系统中。 当用户启动设备或系统时,系统会自动加载已发布的最新版本的应用程序。用户不需要手动安装、更新或加载应用程序,系统会自动完成这些操作。这样可以方便用户,减少用户的操作步骤,提高用户体验。 对于开发团队来说,实现自动加载的机制可以帮助他们更好地管理和分发不同版本的应用程序。他们可以通过设置自动加载的脚本或配置文件,指定需要加载的应用程序版本和更新策略。 总之,6678应用程序release加载自动是通过发布最新版本的应用程序,并自动加载到目标设备或系统中,减少用户操作,提高用户体验的一种机制。 ### 回答2: "6678应用程序release加载自动"的意思是应用程序的发布版本会自动加载。 在软件开发过程中,当应用程序开发完成并通过各种测试后,会发布一个版本供用户使用。发布版本通常会经过一系列的准备工作,如代码整理、性能优化、修复bug等。一旦发布版本准备好,开发人员会将其发布到适当的渠道,让用户可以下载和使用。 自动加载是一种方便用户的功能。一般来说,当用户打开一个应用程序时,他们期望能够立即使用最新版本的应用。为了实现这一点,开发人员可以将自动更新的功能添加到应用程序中。这意味着一旦发布了一个新的版本,应用程序会自动检测到并提示用户进行更新。用户可以选择立即更新或稍后更新,但无论选择什么,应用程序都会在后台自动下载并安装更新。 6678应用程序的发布版本自动加载是为了提高用户体验和便利性。用户不需要手动检查更新,也不需要到应用商店下载新版本。当他们打开应用程序时,他们会看到一个提示框,告诉他们有一个新版本可用,并询问他们是否愿意更新。这样,用户可以始终使用最新的功能和修复。 总之,“6678应用程序release加载自动”是指该应用程序的发布版本会自动检查和加载,并通知用户更新。这个功能可以确保用户在使用应用程序时始终能够享受到最新版本的功能和修复。 ### 回答3: 6678应用程序的release是指发布或推出该应用程序的最终版本,供用户下载和使用。release是软件开发团队在开发过程中的一个重要阶段,表示该应用程序已经经过一系列测试和优化,并具备了足够的稳定性和功能完整性。 加载自动是指该应用程序有自动加载的功能,即用户在下载并安装完毕后,无需进行手动的启动操作,应用程序会自动运行。加载自动可以提高用户的使用便利性,减少用户的操作负担,提升应用程序的用户体验。 在6678应用程序的release版本中,开发团队会确保加载自动的功能正常运行,并充分测试确保应用程序的自动加载不会出现错误或异常情况。这需要开发人员进行充分的测试和调试工作,以确保自动加载的稳定性和可靠性。 当用户下载并安装6678应用程序的release版本后,只需要点击应用程序的图标,即可自动加载和运行。用户无需手动输入命令或进行其他操作来启动应用程序,大大方便了用户的使用。 总之,6678应用程序的release加载自动意味着该应用程序已经发布,并且具备了自动加载的功能,用户只需下载并安装,即可方便地使用该应用程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaofei0627

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值