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
[Feature Request] Support NX Monorepos #800
Comments
I suspect there will be other changes needed to some of the release plan functions in order to make this work, but I have not dug into the code far enough to comment on those, but I will come back and update the top of this ticket with affected packages once I know more. I plan on poking around a bit with these proposed solutions using Additional thought... once we have this working, we could create an NX plugin within the org that could be added to an NX repository and would have all of this stuff configured and added by default. I know we already have |
It is also worth noting that I think the best approach for us is to remove the error throwing in our current |
Will likely need #802 |
Isn't "publishable" easily detectable with our usual logic? So the only missing piece here would be to change the directory from which we call into the publish command?
I'm not yet sold on using this dependency - if the logic for finding this stuff is easy then I think it would make sense to reimplement it. I've asked this in the linked PR but perhaps this is a better place for the discussion so gonna ask it here too... is this package always directly installed by Nx users? Do you know if optional peer dependencies are automatically installed by npm? this could have been a blocker for this solution
If we can make things easier for certain use cases - I'm all up for it.
Yeah, maybe... although I'm still somewhat concerned about potential problems with optional peer deps etc. I'm happy to give this more thought later on though. Note that the script runner from manypkg isn't ever part of what you install when using Changesets so that's not a big deal. In here, we only care about finding the package locations etc. |
I personally think that changesets has too much tight coupling of strategies within itself. It should really look to refactor into a plugin based tool where:
|
Anyone figure out how to use changeset with applications managed in NX monorepo with the existing changeset support? |
We recently got started with our Nx monorepo, and didn't want to invest too much time into switching release methods right away (and assumed changesets would just work with Nx). When it didn't I spent some time coming up with a workaround that might be helpful for others to make changesets work. The strategy: creating a dummy lerna.json on the fly to make changesets detect package directories. There are two variants of this: one based on the source directories for the It's definitely a hack, but it does work and doesn't require package patching. Here's the full setup, which involves package.json, project.json for any projects you want to support, a few scripts and customisations for the GH action. With this you just have to run the wrapped changeset script via npm/pnpm/yarn rather than running it directly / via npx. Args still work. {
"name": "@org/your-monrepo",
"version": "0.0.0",
"private": true,
"scripts": {
"changeset": "node ./scripts/changeset-wrap.mjs",
"publish-combo-changeset": "echo \"{}\" > lerna.json && pnpm exec nx run-many -t changeset-prepare-publish && pnpm exec changeset publish"
} {
"name": "my-lib",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/my-lib/src",
"projectType": "library",
"targets": {
"build": {
...
},
"changeset-prepare-publish": {
"cache": false,
"executor": "nx:run-commands",
"dependsOn": ["build"]
"options": {
"command": "node ./utils/changeset-publish-prepare.mjs dist/my-lib"
}
},
...
}
} # In your changeset version / release GH action
- name: Hack Nx support into Changeset
run: node ./scripts/changeset-ci-prepare.mjs
- name: Changeset - create pull request / publish packages
id: changesets
uses: changesets/action@v1.4.5
with:
publish: pnpm publish-combo-changeset
# the default version command is OK thanks to the action script executed before this
/**
* create-src-lerna-json.mjs
*/
import nx from '@nx/devkit'
export default async function createSrcLernaJson() {
const projectRoots = await nx
.createProjectGraphAsync({ exitOnError: true, resetDaemonClient: true })
.then((projectGraph) =>
Object.entries(nx.readProjectsConfigurationFromProjectGraph(projectGraph).projects)
.filter(([, p]) => {
// Filter projects, we filter on a specific target existing. Easy to customise
return 'changeset-prepare-publish' in (p.targets ?? {})
})
.map(([, p]) => `./${p.root}`),
)
const content = {
['not a real lerna.json']: 'This is a dummy lerna.json to support changeset',
packages: projectRoots,
}
fs.writeFileSync('./lerna.json', JSON.stringify(content, null, 2))
return content
} /**
* changeset-ci-prepare.mjs
*/
import createSrcLernaJson from './create-src-lerna-json.mjs'
createSrcLernaJson().then((content) => {
console.log('Created dummy lerna.json:', content)
}) /**
* changeset-wrap.mjs
*/
import fs from 'fs'
import { execSync } from 'child_process'
import createSrcLernaJson from './create-src-lerna-json.mjs'
// Verify we are in root by checking nx.json exists
if (!fs.existsSync('./nx.json')) {
console.log('This script can only run from repo root.')
process.exit(1)
}
createSrcLernaJson()
.then(() => {
// Run changeset, passing all args.
const args = process.argv.slice(2).join(' ')
execSync(`pnpm exec changeset ${args}`, { stdio: 'inherit' })
})
.then(() => {
cleanup()
})
.catch((e) => {
cleanup()
// Exit with error
console.error(e)
process.exit(1)
})
function cleanup() {
// remove lerna.json
fs.unlinkSync('./lerna.json')
}
process.on('SIGINT', function () {
cleanup()
process.exit()
}) /**
* add-to-lerna-json.mjs
*/
import fs from 'fs'
// Verify we are in root by checking nx.json exists
if (!fs.existsSync('./nx.json')) {
console.log('This script can only run from repo root.')
process.exit(1)
}
// get dist path from argv
const distPath = process.argv[2]
const lernaJson = JSON.parse(fs.readFileSync('./lerna.json', 'utf8'))
let existingProjects = lernaJson.packages ?? []
fs.writeFileSync(
'./lerna.json',
JSON.stringify(
{
packages: [...existingProjects, distPath],
},
null,
2,
),
) |
Affected Packages
get-workspaces
Problem A
As of today, in an NX workspace, it is not possible for Changesets to discover any related packages that are configured for the workspace.
There are only two ways of configuring where packages might be in an NX repo:
With the first option, NX relies on a project.json file existing at the root of any packages in the monorepo, which for publishable packages, will also need to have a package.json alongside them.
With the second option, there is a workspace.json file at the root that references all packages in the monorepo.
Proposed solution
@manypkg/get-packages
does not return any packages, check fornx.json
at our monorepo root. If present, usereadWorkspaceConfiguration()
from@nrwl/devkit
to get the workspace tree instead of trying to implement ourselves.@nrwl/devkit
as an optional dependency, which will already be present in any NX monorepo.Problem B
By default, NX does not compile publishable code adjacent to publishable packages, and instead usually has a root
/dist
directory where all build outputs go which are namespaced to the lib/app. This compiled output will also contain a copy of the package.json with all relevant dependencies added to it, and is what we should be publishing.Proposed solution
@nrwl/devkit
to find which package(s) are publishable and publish their assets directly from the configured output directoryIt is also worth noting that there are a couple other issues already opened that are asking for support for this, but are asking for ways to configure all of this by hand, versus using the tool set in
@nrwl/devkit
:nx
monorepo without workspaces #761publishConfig.directory
publish errors out #773The text was updated successfully, but these errors were encountered: