Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Monorepo support #85

Open
6 tasks
Tracked by #135
pi0 opened this issue Mar 24, 2023 · 8 comments · May be fixed by #97
Open
6 tasks
Tracked by #135

Monorepo support #85

pi0 opened this issue Mar 24, 2023 · 8 comments · May be fixed by #97
Assignees

Comments

@pi0
Copy link
Member

pi0 commented Mar 24, 2023

Moving from #18 also awesome works in #45 by @leo91000 and #83 by @aa900031

@itpropro: Provide some way to generate changelogs only for one or more specific subfolders/repos in a monoprepo structure.

These are smaller tasks breakdown in order to support mono repos progressively since this is a rather big change in changelogen.

  • Configurable git commit template (Custom commit message like [skip ci] #82)
  • Monorepo utils (workspace (monorepo) utils pkg-types#117)
  • Expose a new workspaces config object (auto-detected with resolveWorkspace from pkg-types)
  • A new subDir config defaulting to / and when is set, filters commits only relevant to this subpath and also operates package.json and CHANGELOG.md in {rootDir}/{subDir} for updating
  • Update CLI --release to use workspace config when is set/detected and operate release on each monorepo project with baseDir set (using sorted/ordered graph resolved by pkg-types)
  • Extend scopeMap (auto added scopes using scope map config #86) with workspace config to automatically apply scoped changeloges

Note: PRs are more than welcome to implement each task. If you see an improvement in the roadmap for implementation please comment below. 🙏🏼

@aa900031
Copy link
Contributor

Configurable git commit template

I believe that this feature has been completed in this PR #68

@aa900031
Copy link
Contributor

Expose a new workspaces config object (auto-detected with resolveWorkspace from pkg-types)

Are the values in workspaces in the config file the same as package.json workspaces ?

@aa900031
Copy link
Contributor

A new subDir config defaulting to / and when is set, filters commits only relevant to this subpath and also operates package.json and CHANGELOG.md in {rootDir}/{subDir} for updating

Why there is this usecase? or user how to use that?

@aa900031
Copy link
Contributor

I think there is another feature that can be implemented - "A filter for monorepos".

Here is the usecase:

We are using pnpm to manage a monorepo, but we only want to automatically generate changelogs for certain packages. Specifically, we need to generate changelogs for awesome-pkg, @awesome-pkg/core, and @awesome-pkg/shared, but not for playground or example.

@pi0
Copy link
Member Author

pi0 commented Mar 26, 2023

Are the values in workspaces in the config file the same as package.json workspaces ?

I think we might return an object from resolveWorkspace like { root: '', type: '', workspaces: string[] }

Why there is this usecase? or user how to use that?

Subdir can be used by CLI to run changelog update against each package. Also manually by user to read config from root but update changeloges in a subdir only (even without workspace support)

I think there is another feature that can be implemented - "A filter for monorepos".
Here is the usecase:
We are using pnpm to manage a monorepo, but we only want to automatically generate changelogs for certain packages. Specifically, we need to generate changelogs for awesome-pkg, @awesome-pkg/core, and @awesome-pkg/shared, but not for playground or example.

Yes this also 👍🏼

@azat-io
Copy link

azat-io commented Jun 21, 2023

Any progress?
Is it works with pnpm workspaces?

@aa900031
Copy link
Contributor

aa900031 commented Jun 21, 2023

Hi @azat-io, currently is pending, because I waiting for those PR's to merge

@LouisMazel
Copy link
Contributor

LouisMazel commented Nov 26, 2023

Tips

To manage my monorepo versions and have the advantages of changelogen (beautiful changelog and release publish to github), I use lerna at first, and then I use changelogen to generate and publish the changelog:

  1. Bump version without changelog generation with lerna: lerna version (lerna will bump the version in package.json files if needed, create a commit to push it, create and push the new tag). My lerna config bellow
  2. Then, generate the new changelog with changelogen in a custom script, amend the previous commit of lerna and push it to github to create a new release. Script bellow.

lerna.json

{
  "loglevel": "verbose",
  "version": "0.47.26",
  "yes": true,
  "command": {
    "version": {
      "allowBranch": ["master"],
      "message": "chore(release): bump version %v",
      "conventionalCommits": true,
      "forceGitTag": false,
      "changelog": false,
      "push": true,
      "gitTagVersion": true,
      "tagVersionPrefix": "v",
      "commitHooks": true
    }
  },
  "npmClient": "pnpm",
  "$schema": "node_modules/lerna/schemas/lerna-schema.json"
}

``

import { exec } from 'node:child_process'
import { existsSync, promises as fsp } from 'node:fs'
import { generateMarkDown, getGitDiff, parseCommits, loadChangelogConfig, syncGithubRelease } from 'changelogen'

async function execPromise(command: string): Promise<{ stdout: string; stderr: string }> {
  return await new Promise((resolve, reject) => {
    exec(command, (error, stdout, stderr) => {
      if (error) {
        console.error(`🔴 [cli](${command}) Execution failed - ${error.message}.`)
        reject(error)
      } else {
        resolve({ stdout, stderr })
      }
    })
  })
}

async function updateChangelog() {
  const { stdout: lastTag } = await execPromise("git tag --sort=-v:refname | sed -n '1p'")
  const { stdout: penultimateTag } = await execPromise("git tag --sort=-v:refname | sed -n '2p'")

  const lastTagTrimed = lastTag.trim()
  const penultimateTagTrimed = penultimateTag.trim()

  const config = await loadChangelogConfig(process.cwd(), {
    from: penultimateTagTrimed,
    to: lastTagTrimed,
  })

  const rawCommits = await getGitDiff(penultimateTagTrimed, lastTagTrimed)
  const commits = parseCommits(rawCommits, config).filter((commit) => {
    return (
      config.types[commit.type] &&
      !(commit.type === 'chore' && (commit.scope === 'deps' || commit.scope === 'release') && !commit.isBreaking)
    )
  })

  const newChangelog = await generateMarkDown(commits, config)

  let changelogMD: string

  if (typeof config.output === 'string' && existsSync(config.output)) {
    changelogMD = await fsp.readFile(config.output, 'utf8')
  } else {
    changelogMD = '# Changelog\n\n'
  }

  const lastEntry = changelogMD.match(/^###?\s+.*$/m)

  if (lastEntry) {
    changelogMD = changelogMD.slice(0, lastEntry.index) + newChangelog + '\n\n' + changelogMD.slice(lastEntry.index)
  } else {
    changelogMD += '\n' + newChangelog + '\n\n'
  }

  await fsp.writeFile(config.output as string, changelogMD)

  const changelogWithoutTitle = newChangelog.split('\n').slice(2).join('\n')

  console.log(changelogWithoutTitle)

  await execPromise(`git add -u`)
  await execPromise(`git commit --amend --no-edit`)
  await execPromise(`git push origin HEAD --force`)

  try {
    await syncGithubRelease(config, {
      version: lastTagTrimed.replace('v', ''),
      body: changelogWithoutTitle,
    })

    console.log('Release pushed to GitHub.')
  } catch (error: any) {
    console.error('error', error)
  }
}

updateChangelog()

package.json

{
  "scripts": {
    "release": "pnnp release:bump-version && pnpm release:changelogen",
    "release:bump-version": "lerna version",
    "release:changelogen": "ts-node ./changelog-generate.ts"
  },
}

You have to run

pnpm release

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants