Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .changeset/changesets-v3-support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
"changesets-gitlab": major
---

Drop support for Changesets v2 and Node < 22. Bump all `@changesets/*` dependencies to v3 and `@manypkg/get-packages` to v3.

Breaking changes:

- **Node engine requirement** bumped from `>=18.0.0` to `^22.11 || ^24 || >=26` to match Changesets v3
- **`@changesets/*` dependencies** bumped to v3 versions, which are ESM-only
- **`@manypkg/get-packages`** bumped to v3, changing the `Packages` and `Package` types (`tool` is now an object with `type` property, `root` renamed to `rootPackage`, `Package` now requires `relativeDir`)

Bug fixes for Changesets v3 compatibility:

- Handle the new `changeset version` exit code 1 when no unreleased changesets exist
- Detect published packages via the `CHANGESETS_OUTPUT` env var (NDJSON format), falling back to stdout "New tag:" parsing for Changesets v2
- Prevent creating empty release MRs when the version command produces no file changes — fall through to publish instead
- Use `ignoreReturnCode: true` on version and publish commands since v3 may exit non-zero in valid scenarios
3 changes: 1 addition & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ jobs:
strategy:
matrix:
node:
- 18
- 20
- 22
- 24
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
Expand Down
20 changes: 10 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"license": "MIT",
"packageManager": "yarn@4.18.0",
"engines": {
"node": ">=18.0.0"
"node": "^22.11 || ^24 || >=26"
},
"bin": "lib/cli.js",
"main": "./lib/index.cjs",
Expand Down Expand Up @@ -64,14 +64,14 @@
"dependencies": {
"@actions/core": "^2.0.3",
"@actions/exec": "^1.1.1",
"@changesets/assemble-release-plan": "^6.0.6",
"@changesets/config": "^3.1.1",
"@changesets/errors": "^0.2.0",
"@changesets/parse": "^0.4.1",
"@changesets/pre": "^2.0.2",
"@changesets/read": "^0.6.3",
"@changesets/assemble-release-plan": "^7.0.0",
"@changesets/config": "^4.0.0",
"@changesets/errors": "^1.0.0",
"@changesets/parse": "^1.0.0",
"@changesets/pre": "^3.0.0",
"@changesets/read": "^1.0.0",
"@gitbeaker/rest": "^42.2.0",
"@manypkg/get-packages": "^1.1.3",
"@manypkg/get-packages": "^3.1.0",
"commander": "^13.1.0",
"dotenv": "^16.5.0",
"global-agent": "^3.0.0",
Expand All @@ -89,8 +89,8 @@
},
"devDependencies": {
"@1stg/common-config": "^13.0.1",
"@changesets/changelog-github": "^0.5.1",
"@changesets/cli": "^2.29.0",
"@changesets/changelog-github": "^1.0.0",
"@changesets/cli": "^3.0.0",
"@commitlint/cli": "^19.8.0",
"@pkgr/rollup": "^6.0.3",
"@types/global-agent": "^3.0.0",
Expand Down
7 changes: 1 addition & 6 deletions src/comment.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { ValidationError } from '@changesets/errors'
import type {
ComprehensiveRelease,
ReleasePlan,
Expand Down Expand Up @@ -302,11 +301,7 @@
changedFiles: packageChangedFiles,
cwdPrefix,
}).catch((err: unknown) => {
if (err instanceof ValidationError) {
errFromFetchingChangedFiles = `<details><summary>💥 An error occurred when fetching the changed packages and changesets in this MR</summary>\n\n\`\`\`\n${err.message}\n\`\`\`\n\n</details>\n`
} else {
console.error(err)
}
errFromFetchingChangedFiles = `<details><summary>💥 An error occurred when fetching the changed packages and changesets in this MR</summary>\n\n\`\`\`\n${err instanceof Error ? err.message : String(err)}\n\`\`\`\n\n</details>\n`

Check warning on line 304 in src/comment.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'err' will use Object's default stringification format ('[object Object]') when stringified.

See more on https://sonarcloud.io/project/issues?id=un-ts_changesets-gitlab&issues=AaBmKjcAEWCKjdJrAAQq&open=AaBmKjcAEWCKjdJrAAQq&pullRequest=252
Comment thread
philibea marked this conversation as resolved.
return {
changedPackages: ['@fake-scope/fake-pkg'],
releasePlan: null,
Expand Down
49 changes: 30 additions & 19 deletions src/get-changed-packages.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import fs from 'node:fs/promises'
import path from 'node:path'

import assembleReleasePlan from '@changesets/assemble-release-plan'
import { parse as parseConfig } from '@changesets/config'
import parseChangeset from '@changesets/parse'
import { assembleReleasePlan } from '@changesets/assemble-release-plan'
import { validateConfig as parseConfig } from '@changesets/config'
Comment thread
philibea marked this conversation as resolved.
import { parseChangesetFile as parseChangeset } from '@changesets/parse'
import type {
PackageJSON,
PreState,
NewChangeset,
WrittenConfig,
} from '@changesets/types'
import type { Packages, Tool } from '@manypkg/get-packages'
import type { Package, Packages, Tool } from '@manypkg/get-packages'
import micromatch from 'micromatch'
import { parse } from 'yaml'

Expand Down Expand Up @@ -50,13 +50,14 @@ export const getChangedPackages = async ({
}
}

async function getPackage(pkgPath: string) {
async function getPackage(pkgPath: string): Promise<Package> {
const jsonContent = await fetchJsonFile<PackageJSON>(
`${pkgPath}/package.json`,
)
return {
packageJson: jsonContent,
dir: pkgPath,
relativeDir: pkgPath,
}
}

Expand Down Expand Up @@ -109,11 +110,11 @@ export const getChangedPackages = async ({
)
}
}
let tool: { tool: Tool; globs: string[] } | undefined
let tool: { toolType: string; globs: string[] } | undefined

if (isPnpm) {
tool = {
tool: 'pnpm',
toolType: 'pnpm',
globs: (
parse(await fetchTextFile('pnpm-workspace.yaml')) as {
packages: string[]
Expand All @@ -125,27 +126,31 @@ export const getChangedPackages = async ({

if (rootPackageJsonContent.workspaces) {
tool = {
tool: 'yarn',
toolType: 'yarn',
globs: Array.isArray(rootPackageJsonContent.workspaces)
? rootPackageJsonContent.workspaces
: rootPackageJsonContent.workspaces.packages,
}
} else if (rootPackageJsonContent.bolt?.workspaces) {
tool = {
tool: 'bolt',
toolType: 'bolt',
globs: rootPackageJsonContent.bolt.workspaces,
}
}
}

const rootPackageJsonContent = await rootPackageJsonContentsPromise

const rootPackage: Package = {
dir: '/',
relativeDir: '.',
packageJson: rootPackageJsonContent,
}

const packages: Packages = {
root: {
dir: '/',
packageJson: rootPackageJsonContent,
},
tool: tool ? tool.tool : 'root',
rootDir: '/',
rootPackage,
tool: { type: tool ? tool.toolType : 'root' } as Tool,
packages: [],
}

Expand All @@ -160,16 +165,22 @@ export const getChangedPackages = async ({
const matches = micromatch(potentialWorkspaceDirectories, tool.globs)
packages.packages = await Promise.all(matches.map(dir => getPackage(dir)))
} else {
packages.packages.push(packages.root)
packages.packages.push(rootPackage)
}
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- https://github.com/microsoft/TypeScript/issues/9998
if (hasErrored) {
throw new Error('an error occurred when fetching files')
}

const config = await configPromise.then(rawConfig =>
parseConfig(rawConfig, packages),
)
const config = await configPromise.then(rawConfig => {
const result = parseConfig(rawConfig, packages)
if (result.errors) {
throw new Error(
`Failed to parse changeset config: ${result.errors.join(', ')}`,
)
}
return result.config
})

const releasePlan = assembleReleasePlan(
await Promise.all(changesetPromises),
Expand All @@ -179,7 +190,7 @@ export const getChangedPackages = async ({
)

return {
changedPackages: (packages.tool === 'root'
changedPackages: (packages.tool.type === 'root'
? packages.packages
: packages.packages.filter(pkg =>
changedFiles.some(
Expand Down
30 changes: 28 additions & 2 deletions src/git-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,37 @@ export const push = async (
}

export const pushTags = async () => {
await exec('git', ['push', 'origin', '--tags'])
const { stderr, code } = await execWithOutput(
'git',
['push', 'origin', '--tags'],
{ ignoreReturnCode: true },
)
if (code !== 0 && !stderr.includes('already exists')) {
throw new Error(`Failed to push tags: ${stderr}`)
Comment thread
philibea marked this conversation as resolved.
}
}

export const pushTag = async (tag: string) => {
await exec('git', ['push', 'origin', tag])
// Check if the tag exists locally before attempting to push.
// In Changesets v3, the git-tag command may report tags that were
// "skipped (already exist)" on the remote but don't exist locally,
// causing `git push origin <tag>` to fail with "src refspec does not match".
const { code: tagListCode } = await execWithOutput(
'git',
['tag', '-l', tag],
{ ignoreReturnCode: true },
)
if (tagListCode !== 0) {
return
}
const { stderr, code } = await execWithOutput(
'git',
['push', 'origin', tag],
{ ignoreReturnCode: true },
)
if (code !== 0 && !stderr.includes('already exists')) {
throw new Error(`Failed to push tag ${tag}: ${stderr}`)
}
}

export const switchToMaybeExistingBranch = async (branch: string) => {
Expand Down
Loading