Skip to content

Rework publishing to work with all popular package managers - #674

Closed
Andarist wants to merge 6 commits into
mainfrom
publish-refactor
Closed

Andarist wants to merge 6 commits into
mainfrom
publish-refactor

Conversation

@Andarist

@Andarist Andarist commented Nov 20, 2021 •

Copy link
Copy Markdown
Member

fixes #598
fixes #432
fixes #813
fixes #1454
(maybe #1411)

at the moment this is being opened as a draft, to gather initial feedback

The goal of this PR is to delegate as much as possible to yarn berry / pnpm so they can do their magic with rewriting package manifests and stuff

TODO:

cc @schickling @zkochan (might be interesting to you)

@Andarist
Andarist requested a review from emmatown November 20, 2021 21:21
@changeset-bot

changeset-bot Bot commented Nov 20, 2021 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 7608211

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@changesets/cli Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@emmatown emmatown left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should log stdout and stderr when publish fails so people can debug when something goes wrong like we fail to parse the json? (and maybe even land that and the getLastJsonObjectFromString stuff separately to package manager changes?)

[
...publishTool.args,
opts.cwd,
"--json",

@belgattitude belgattitude Nov 25, 2021 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like if yarn 2+ does not support --json, the argument should be conditionally added

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, thank you for spotting that - this is an obvious oversight on my part, I knew that this ain't supported as it makes me handle yarn berry in a different way just a few lines later

just out of curiosity - is using an unknown flag makes yarn throw an error?

@belgattitude belgattitude Nov 25, 2021 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately yes, just tested with your current P/R to be sure (yarn 3.2.0-rc.5):

soluble-io/cache-interop#222 (comment)

BTW I love the butterfly in the yarn output, made me smile :)

🦋  error Unknown Syntax Error: Extraneous positional argument ("/home/sebastien/github/cache-interop/packages/cache-redis").

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you have tested the other case 😅 but this one doesn't work either:

Unknown Syntax Error: Unsupported option name ("--json").

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep 😅, I've been struggling a bit with how to build your P/R to use it...

But that definitely happened and it was nice to see.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will make sure to setup snapshot releases for PRs before we go into re-testing this after I apply the suggested changes

["publish", opts.cwd, "--json", ...publishFlags],
[
...publishTool.args,
opts.cwd,

@belgattitude belgattitude Nov 25, 2021 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opts.cwd

I don't think it will work with yarn 2+, not sure about the other package managers (https://yarnpkg.com/cli/npm/publish has no reference to path)

I think this should work

 const args = [
    ...publishTool.args,
   // "--json",  // <- assuming here we add --json for naything !== yarn 2+
    ...publishFlags,
    ...publishTool.flags
  ];
  let { code, stdout, stderr } = await spawn(
    publishTool.name,
    args,
    {
      // Here we can send the current working directory
      cwd: opts.cwd,
      env: Object.assign({}, process.env, envOverride)
    }
  );

Just tested it and it works on yarn 3. But I'm wondering if it would be cleaner to extract the command and add tests on it. I'll have a look tomorrow to see if I can help

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes - good catch. I got Unknown Syntax Error: Extraneous positional argument ("packages/test"). with this.

I think it's safe to just use { cwd: opts.cwd } for all package managers.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just tested it and it works on yarn 3. But I'm wondering if it would be cleaner to extract the command and add tests on it. I'll have a look tomorrow to see if I can help

Tests for this whole thing would be great. I'm just unsure how to test this best - we really should have some e2e tests here because there are so many little details hidden within those publish commands that we really should invoke those and verify what can be observed somehow.

@belgattitude belgattitude Nov 25, 2021 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally. I would be lazy and just unit test something in the lines of

type Params= {
   pm: 'pnpm' | 'yarn' | 'npm',
   pmVersion?: string,
   tag?: string,
   access?: 'public' | 'private',
   otp?: string
}
const getPublishSpawnParams = (params: Params): {cmd: string, args: string[]} => {
 ///....
}

describe('when pnpm', () => {
   it('should return a pnpm..', () => {
      expect(getPublishSpawnParams({pm: 'pnpm', pmVersion: '6.4' tag: 'main', access: 'public', otp: 'xxx'}).toStrictEqual({
          cmd: 'pnpm',
          args: ['publish', '--access', 'public', '--tag', 'main', '--json', '--no-git-checks']
      })
   }); 
}

describe('when yarn < 2', () => {
   it('should return a npm publish..', () => {
      expect(getPublishSpawnParams({pm: 'yarn', pmVersion: '8.0', tag: 'main', access: 'public', otp: 'xxx'}).toStrictEqual({
          cmd: 'npm',
          args: ['publish', '--access', 'public', '--tag', 'main' '--otp', 'xxx']
      })
   }); 
}

describe('when yarn 2+', () => {
   it('should return a yarn..', () => {
      expect(getPublishSpawnParams({pm: 'yarn', pmVersion: '2.4', tag: 'main', access: 'public', otp: 'xxx'}).toStrictEqual({
          cmd: 'yarn',
          args: ['npm', 'publish', '--access', 'public', '--tag', 'main']
      })
   }); 
}

describe('when yarn 3.2', () => {
   it('should return a yarn..', () => {
      expect(getPublishSpawnParams({pm: 'yarn', pmVersion: "3.2', tag: 'main', access: 'public', otp: 'xxx'}).toStrictEqual({
          cmd: 'yarn',
          // I saw that you've opened a --otp option in yarn, so why not ?
          args: ['npm', 'publish', '--access', 'public', '--tag', 'main', '-otp', 'xxx']
      })
   }); 
}

// + some extras 

Not so sure about how to mock spawn, so that might make sense

I'm bad with the names hey :)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just checking and you're totally right so much small details, that's insane :)

So definitely I would live well with unit tests cause e2e looks a huge effort, especially with minor versions.

I would start with some specs for the most common cases (npm, yarn 1/2+, pnpm 6) and live with a "dirty" function that can be improved later (with things like https://www.npmjs.com/package/compare-versions to adapt the result)

@Andarist

Copy link
Copy Markdown
Member Author

@belgattitude thank you very much for testing this ❤️ I will make necessary changes here soon, I would only want to land this first #676 so I could rebase this one on top of it.

Andarist and others added 3 commits November 26, 2021 14:31
…e path as argument to the command. This makes sure that it will work with Yarn Berry

Co-authored-by: Sébastien Vanvelthem <[email protected]>
Co-authored-by: Sébastien Vanvelthem <[email protected]>
@Andarist

Copy link
Copy Markdown
Member Author

@belgattitude I've rebased this and applied your suggestions. I've also created a PR to add snapshot releases (here). This can only be merged in when we confirm that it has been done in a secure way, so I don't want to rush it. If you could retest this PR "manually" (like you have done before) - I would highly appreciate it.

@belgattitude

belgattitude commented Nov 30, 2021 •

Copy link
Copy Markdown

Hey @Andarist thanks a lot. Here it is

yarn changeset publish
sebastien@seb-thinkpad-x1:~/github/cache-interop$ yarn changeset publish
🦋  warn ===============================IMPORTANT!===============================
🦋  warn You are in prerelease mode so packages will be published to the canary
🦋  warn         dist tag except for packages that have not had normal releases which will be published to latest
🦋  warn ----------------------------------------------------------------------
🦋  info npm info @soluble/cache-interop
🦋  info npm info @soluble/cache-ioredis
🦋  info npm info @soluble/cache-redis
🦋  info npm info @soluble/dsn-parser
🦋  info @soluble/cache-interop is being published because our local version (0.8.2-canary.0) has not been published on npm
🦋  info @soluble/cache-ioredis is being published because our local version (0.8.4-canary.0) has not been published on npm
🦋  info @soluble/cache-redis is being published because our local version (0.8.4-canary.0) has not been published on npm
🦋  info @soluble/dsn-parser is being published because our local version (1.3.3-canary.0) has not been published on npm
🦋  info Publishing "@soluble/cache-interop" at "0.8.2-canary.0"
🦋  info Publishing "@soluble/cache-ioredis" at "0.8.4-canary.0"
🦋  info Publishing "@soluble/cache-redis" at "0.8.4-canary.0"
🦋  info Publishing "@soluble/dsn-parser" at "1.3.3-canary.0"
🦋  info will publish with 'yarn npm publish --access public --tag canary'
🦋  info will publish with 'yarn npm publish --access public --tag canary'
🦋  info will publish with 'yarn npm publish --access public --tag canary'
🦋  info will publish with 'yarn npm publish --access public --tag canary'
🦋  error an error occurred while publishing @soluble/dsn-parser: 
🦋  error ➤ YN0033: No authentication configured for request
🦋  error an error occurred while publishing @soluble/cache-ioredis: 
🦋  error ➤ YN0033: No authentication configured for request
🦋  error an error occurred while publishing @soluble/cache-redis: 
🦋  error ➤ YN0033: No authentication configured for request
🦋  error an error occurred while publishing @soluble/cache-interop: 
🦋  error ➤ YN0033: No authentication configured for request
🦋  error packages failed to publish:
🦋  @soluble/[email protected]
🦋  @soluble/[email protected]
🦋  @soluble/[email protected]
🦋  @soluble/[email protected]

The command is not complaining anymore with yarn 3.2.0-rc.6.

🦋 info will publish with 'yarn npm publish --access public --tag canary'

PS: I've added the log here: https://github.com/atlassian/changesets/pull/682/files just to be sure.

That said it's not a full publish, I'll need --otp for that and can only publish my repo from a gh action for now.

So all look fine. My concerns are related to

  • Logging: As it look probable we run into bugs, I would add some logging about the command used.
  • Prevent regressions: I feel we should still have some kind of unit tests for the command (and pm versions). To ensure future changes does not break things.

Of course all is up to you, I come from different ecosystem so my reflexes often lack perspective and love to learn and share in general.

Let me know I still have few free days, and I'll gladly help.

@Andarist

Copy link
Copy Markdown
Member Author

Logging: As it look probable we run into bugs, I would add some logging about the command used.

That's probably a good call - anything that you would like to see logged beside the information added in your PR here?

Prevent regressions: I feel we should still have some kind of unit tests for the command (and pm versions). To ensure future changes does not break things.

I agree that we need some tests here - I'm just on the fence when it comes to unit tests here. The logic itself is pretty straightforward - the only problem is that I'm not always sure what exact combination of options has to be passed in a specific situation. And when it comes to that I think that comments can be more valuable than unit tests.

I just kinda feel that I would be that more confident about this logic if I would have those unit tests. I'm not a testing expert though and probably many people would say that this would be worthwhile - I'm just not feeling that. Maybe @mitchellhamilton would have some additional opinion about this.

The command is not complaining anymore with yarn 3.2.0-rc.6.

Just to make sure that we are on the same page - everything looks OK now from your end, it's just that publish fails because OTP is required and you are not using the automation token because you are running the publish script locally and not on the CI. Right?

@emmatown

Copy link
Copy Markdown
Member

testing

tbh, what I'd really like is tests that run with all the different package managers against a local registry (probably https://verdaccio.org) with a way to run the tests against actual registries.

@Andarist

Andarist commented Dec 1, 2021

Copy link
Copy Markdown
Member Author

Let's move the tests-related discussion to the dedicated thread. I don't think this is sort of out of the scope of this PR and should be addressed separately.

@Andarist
Andarist marked this pull request as ready for review December 1, 2021 10:48
@Andarist
Andarist requested a review from emmatown December 1, 2021 10:48
@belgattitude

belgattitude commented Dec 1, 2021 •

Copy link
Copy Markdown

anything that you would like to see logged beside the information added in your PR here?

New to the project so I don't know but I would follow the concerns from #674 (review) @mitchellhamilton

Just to make sure that we are on the same page - everything looks OK now from your end, it's just that publish fails because OTP is required

Yep. Don't want to mess too much my repo 😄. A canary of changeset can help for me to test it out fully, but as the "yarn command" is okay, I feel I can be confident enough the tool will do its job.

About tests

Just want to express something about unit. I'm certainly not trying to create a debate, just sharing and if it's not useful, please forget.

  1. Don't test what you don't own.

    I like the idea of e2e, but in this specific case I would be confident that:

    • given the package manager cli receive the correct params it will do the job or it's a bug on their side. They have an e2e suite that runs on many os... spawn (which I don't know about) has it's own tests too, I guess. So for me what is to test is the command we run, less the fact that npm, pnpm, yarn 1.2.3 will actually publish what what we are feeding to it. Command line is somehow a contract. Same idea when I test a server, do I need to test if my express server works or just my handler/use-case (business...) .... There's not big difference when you write the tests, but running them is much easier when you don't need to start the actual express server (time, flacky...).
  2. What kind of unit tests

    For example we could list the commands that are expected by each tool (from their doc). To illustrate (I haven't checked just taken from what I read in this P/R)

    Given Expected
    Yarn 1 --otp npm publish --access public --tag main --otp xxx --json
    Yarn 1 npm publish --access public --tag main --json
    Yarn 2 yarn npm publish --access public --tag main
    Yarn 2 --otp should throw 'Unsupported --otp, upgrade to yarn 3.2+'
    Yarn 3.2 --otp yarn npm publish --access public --tag main --otp xxx
    Pnpm 6 yarn npm publish --access public --tag main --json --no-git-checks
    Npm 6 --otp npm publish --access public --tag main --otp xxx --json
    Npm 8 --otp npm publish --access public --tag main --otp xxx --json

    That could be written in many ways... In this scenario looks a fit for kind of acceptance / BDD / cucumber style. either given/when/expect like here or even with a simple it.each and an array of scenarios.

    That should suffice to prevent regressions, as you said the code is trivial but how much variations will come in the future. Personally as a contributor (If I become, I'll become 😄 ) I would be more confident to make changes.

If you like I could add an example test file in a separate P/R... The only thing is that I don't really know node, so a reviewer/guide will be needed 😄

Hey thanks again

@belgattitude

Copy link
Copy Markdown

@Andarist a little addition. I'm not sure you're aware of it. When I faced the issue with yarn/pnpm workspace: protocol, I just did a quick hack for my own use case. Rather than change npm for publication, I intercepted the pack step (yarn pack, pnpm pack) to generate the archive. see here #585 (comment).

I'm thinking of it now, I guess it's a bit late to tell and I don't want to add confusion here. So feel free to ignore, but it might help to harmonize and have one tool to test for publishing.

@castastrophe

Copy link
Copy Markdown

Any interest in picking this work back up? We love changesets at the Adobe Spectrum CSS project but we're leveraging yarn 4.1.1 and the workspaces:^ syntax and would love to see even a small update to using yarn npm publish instead of npm publish so that the workspace identifiers are correctly replaced in the released code.

@tianyingchun

Copy link
Copy Markdown

any plan of this?

@fmal

fmal commented May 26, 2024 •

Copy link
Copy Markdown

I would highly recommend switching to https://github.com/monoweave/monoweave for Yarn-based projects. I had a great experience with this tool, fills all my versioning/publishing needs in a monorepo.

@tianyingchun

Copy link
Copy Markdown

waiting changesets support workspace:*

@tien

tien commented Jul 17, 2024

Copy link
Copy Markdown

Hey @Andarist sorry for the ping, I got surprised by this pretty bad today 😅

Are you planning on picking this PR up again? Else, one option I can think of is just to remove changeset publish entirely, and delegate publishing logic to the package manager (could actually be the better way to go).

@HaakonSvane

Copy link
Copy Markdown

+1

@bensampaio

Copy link
Copy Markdown

Hey @Andarist, at my company we stumbled upon this issue a few weeks ago and it cost us quite some outages and bugs due to duplicate versions or incorrect resolutions in yarn.lock. I'm surprised to find out that this issue has been there since 2021. It hasn't been fixed and it is also not in the docs anywhere. It could have saved us a lot of headache to know that yarn publish isn't supported. In my opinion, this deserves a warning on your main README file.

Fortunately, we managed to fix this issue with the changes in this PR. So far I didn't get your feedback there. Could you let me know why isn't it an option to merge it?

Most importantly, if you are not fixing this issue, do you have at least a recommendation for people stuck in this problem? At my company, we chose changesets and are willing to help if the effort is manageable. Please let us know if there's anything we can do. And please document this because others should know before they choose this tool.

@kvz

kvz commented Nov 13, 2024

Copy link
Copy Markdown

Would love to see this get landed. Could @transloadit sponsor this effort perhaps?

@HaakonSvane

Copy link
Copy Markdown

I'm all for getting this fixed as it is causing us a lot of headaches as well, but this is a good time to reflect on the current state of open source. Developers of open source libraries are getting paid close to nothing and large corporations are profiting from their work. While we wait for this PR to hopefully resolve, please have a look at Dylan Beattie's excellent presentation at NDC and go tell your bosses to help out developers like these.

https://github.com/sponsors/explore

@kvz

kvz commented Nov 14, 2024

Copy link
Copy Markdown

Is this an ai response? I actually offered to sponsor this. And we allocate 80% of revenue to open source at Transloadit

@HaakonSvane

Copy link
Copy Markdown

Is this an ai response? I actually offered to sponsor this. And we allocate 80% of revenue to open source at Transloadit

There are obviously more people visiting this PR than just yourself. While it is great to hear about how you help the open source community, this is not very common for companies, especially those where IT is not considered part of their core business, to do. I posted my message in the hopes that other frustrated developers that might come across this PR have a look at the presentation and reflect while we wait

@artyomSultanov

Copy link
Copy Markdown

+1

@clagos-facephi

Copy link
Copy Markdown

Having this exact same bug when using workspaces in bun. How can we help to see this fixed?

@imarabinda

Copy link
Copy Markdown

+1

@paulbalaji

Copy link
Copy Markdown

+1

@macalinao

Copy link
Copy Markdown

Hi all, I was able to get Bun workspaces working with changesets.

I wrote a blog post here on my website on how to do this: https://ianm.com/posts/2025-08-18-setting-up-changesets-with-bun-workspaces

@manulera

Copy link
Copy Markdown

+1 cannot get this to work with yarn

@Andarist

Copy link
Copy Markdown
Member Author

This is going to be superseded by #2097

@Andarist Andarist closed this Jun 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet