qiwi / multi-semantic-release

Proof of concept that wraps semantic-release to work with monorepos.
BSD Zero Clause License
86 stars 34 forks source link

Bug: bumping dependencies when there wasn't an update on them #31

Closed davikawasaki closed 3 years ago

davikawasaki commented 3 years ago

I'm having some problems when bumping dependencies.

My mono repo project has simply two packages: app and lib, whereas app depends on lib.

I started a new semantic release for a change only on the app package, which triggered the tag creation for that specific package:

image

Problem is that when running the workflow that calls multi-semantic-release, the updateDeps method bumps the lib dependency, even though it wasn't changed at all:

image

This meant having a lib tag bound in the app/package.json dependencies that is non-existent, which crashed the next workflow:

image

Version being used: 3.11.0 Repository: https://github.com/davikawasaki/multi-semantic-release-monorepo

antongolub commented 3 years ago

@davikawasaki,

I've just added pkg-prefixes to some debug notes (msr@3.12.0). Could you run your scenario again to make logs a bit clearer?

davikawasaki commented 3 years ago

@antongolub I've executed again. Here are the logs from the last workflow executed:

> multi-semantic-release-monorepo@0.0.0 semantic-release /home/runner/work/multi-semantic-release-monorepo/multi-semantic-release-monorepo
> multi-semantic-release --sequential-init --debug --first-parent

multi-semantic-release version: 3.12.0
semantic-release version: 17.3.0
flags: {
  "sequentialInit": true,
  "debug": true,
  "firstParent": true,
  "deps": {
    "bump": "override",
    "release": "patch"
  },
  "dryRun": false
}
yarn paths [
  '/home/runner/work/multi-semantic-release-monorepo/multi-semantic-release-monorepo/packages/app/package.json',
  '/home/runner/work/multi-semantic-release-monorepo/multi-semantic-release-monorepo/packages/lib/package.json'
]
[5:08:46 PM] › 🎉  Started multirelease! Loading 2 packages...
[5:08:46 PM] › ✔  Loaded package app
[5:08:46 PM] › ✔  Loaded package lib
[5:08:46 PM] › 🎉  Queued 2 packages! Starting release...
2020-12-30T17:08:46.692Z msr:synchronizer [app] is lucky _readyForRelease:app
2020-12-30T17:08:46.692Z msr:synchronizer [app] _readyForRelease:app
2020-12-30T17:08:46.693Z msr:inlinePlugin [app] inlinePlugin created
[5:08:46 PM] [app] › ℹ  Running semantic-release version 17.3.0
[5:08:46 PM] [app] › ✔  Loaded plugin "addChannel" from "@semantic-release/npm"
[5:08:46 PM] [app] › ✔  Loaded plugin "addChannel" from "@semantic-release/github"
[5:08:46 PM] [app] › ✔  Loaded plugin "success" from "@semantic-release/github"
[5:08:46 PM] [app] › ✔  Loaded plugin "fail" from "@semantic-release/github"
[5:08:48 PM] [app] › ✔  Run automated release from branch main on repository https://github.com/davikawasaki/multi-semantic-release-monorepo.git
[5:08:48 PM] [app] › ✔  Allowed to push to the Git repository
[5:08:48 PM] [app] › ℹ  Start step "verifyConditions" of plugin "Inline plugin"
2020-12-30T17:08:48.621Z msr:synchronizer [lib] _readyForRelease:lib
2020-12-30T17:08:48.621Z msr:inlinePlugin [lib] inlinePlugin created
[5:08:48 PM] [lib] › ℹ  Running semantic-release version 17.3.0
2020-12-30T17:08:48.645Z msr:inlinePlugin [app] verified conditions
[5:08:48 PM] [app] › ✔  Completed step "verifyConditions" of plugin "Inline plugin"
[5:08:48 PM] [app] › ℹ  Found git tag app@1.0.0-dev.8 associated with version 1.0.0-dev.8 on branch main
[5:08:48 PM] [app] › ℹ  Found 2 commits since last release
[5:08:48 PM] [app] › ℹ  Start step "analyzeCommits" of plugin "Inline plugin"
2020-12-30T17:08:48.685Z msr:commitsFilter git log filter query: [ '--first-parent', 'main', 'fef5bc1224234fdebe931b72fe97b02c598f43ff..HEAD', '--', 'packages/app' ]
2020-12-30T17:08:48.686Z msr:commitsFilter filtered commits: [
  {
    commit: {
      long: '4f30a6847c3df9e84fe3d1521fdd7edd3b9d356c',
      short: '4f30a68'
    },
    tree: {
      long: 'c3e5d550888093456e988ac3615da20dfe96c774',
      short: 'c3e5d55'
    },
    author: {
      name: 'Davi Kawasaki',
      email: 'davishinjik@gmail.com',
      date: 2020-12-30T17:07:48.000Z
    },
    committer: {
      name: 'Davi Kawasaki',
      email: 'davishinjik@gmail.com',
      date: 2020-12-30T17:07:48.000Z
    },
    subject: 'fix: rename again for debugging',
    body: '',
    hash: '4f30a6847c3df9e84fe3d1521fdd7edd3b9d356c',
    message: 'fix: rename again for debugging',
    gitTags: '(HEAD -> main, origin/main)',
    committerDate: 2020-12-30T17:07:48.000Z
  }
]
[5:08:48 PM] [lib] › ✔  Loaded plugin "addChannel" from "@semantic-release/npm"
[5:08:48 PM] [lib] › ✔  Loaded plugin "addChannel" from "@semantic-release/github"
[5:08:48 PM] [lib] › ✔  Loaded plugin "success" from "@semantic-release/github"
[5:08:48 PM] [lib] › ✔  Loaded plugin "fail" from "@semantic-release/github"
[5:08:50 PM] [lib] › ✔  Run automated release from branch main on repository https://github.com/davikawasaki/multi-semantic-release-monorepo.git
[5:08:50 PM] [lib] › ✔  Allowed to push to the Git repository
[5:08:50 PM] [lib] › ℹ  Start step "verifyConditions" of plugin "Inline plugin"
2020-12-30T17:08:50.376Z msr:synchronizer [ALL] _readyForRelease
2020-12-30T17:08:50.386Z msr:inlinePlugin [lib] verified conditions
[5:08:50 PM] [lib] › ✔  Completed step "verifyConditions" of plugin "Inline plugin"
[5:08:50 PM] [lib] › ℹ  Found git tag lib@1.0.0-dev.6 associated with version 1.0.0-dev.6 on branch main
[5:08:50 PM] [lib] › ℹ  Found 10 commits since last release
[5:08:50 PM] [lib] › ℹ  Start step "analyzeCommits" of plugin "Inline plugin"
2020-12-30T17:08:50.423Z msr:commitsFilter git log filter query: [ '--first-parent', 'main', '72761a75dc5efc0eb9c7b7d56260d242bb73f41e..HEAD', '--', 'packages/lib' ]
2020-12-30T17:08:50.424Z msr:commitsFilter filtered commits: []
2020-12-30T17:08:50.425Z msr:synchronizer [ALL] _analyzed
2020-12-30T17:08:50.426Z msr:inlinePlugin [app] commits analyzed
2020-12-30T17:08:50.426Z msr:inlinePlugin [app] release type: patch
[5:08:50 PM] [app] › ✔  Completed step "analyzeCommits" of plugin "Inline plugin"
2020-12-30T17:08:50.426Z msr:inlinePlugin [lib] commits analyzed
[5:08:50 PM] [lib] › ✔  Completed step "analyzeCommits" of plugin "Inline plugin"
2020-12-30T17:08:50.426Z msr:inlinePlugin [lib] release type: undefined
[5:08:50 PM] [app] › ℹ  The next release version is 1.0.0-dev.9
[5:08:50 PM] [app] › ℹ  Start step "generateNotes" of plugin "Inline plugin"
2020-12-30T17:08:50.446Z msr:synchronizer [ALL] _nextRelease
[5:08:50 PM] [lib] › ℹ  There are no relevant changes, so no new version is released.
2020-12-30T17:08:50.496Z msr:inlinePlugin [app] notes generated
[5:08:50 PM] [app] › ✔  Completed step "generateNotes" of plugin "Inline plugin"
[5:08:50 PM] [app] › ℹ  Start step "prepare" of plugin "Inline plugin"
2020-12-30T17:08:50.499Z msr:synchronizer [app] is lucky _readyForTagging:app
2020-12-30T17:08:50.499Z msr:synchronizer [app] _readyForTagging:app
2020-12-30T17:08:50.500Z msr:updateDeps [app] package.json path= /home/runner/work/multi-semantic-release-monorepo/multi-semantic-release-monorepo/packages/app/package.json
2020-12-30T17:08:50.500Z msr:updateDeps [app] changes= { dependencies: { lib: '1.0.0-dev.6 → 1.0.0-dev.7' } }
v1.0.0-dev.9
npm notice 
npm notice 📦  app@1.0.0-dev.9
npm notice === Tarball Contents === 
npm notice 77B  lib/app.js  
npm notice 750B package.json
npm notice 102B README.md   
npm notice === Tarball Details === 
npm notice name:          app                                     
app-1.0.0-dev.9.tgz
npm notice version:       1.0.0-dev.9                             
npm notice filename:      app-1.0.0-dev.9.tgz                     
npm notice package size:  628 B                                   
npm notice unpacked size: 929 B                                   
npm notice shasum:        1558a80562f919b636ee6207171ebc1f68fa9981
npm notice integrity:     sha512-VJXA3XbTOGnRv[...]W4Mo9Vv+XWtqQ==
npm notice total files:   3                                       
npm notice 
2020-12-30T17:08:54.143Z msr:inlinePlugin [app] prepared
[5:08:54 PM] [app] › ✔  Completed step "prepare" of plugin "Inline plugin"
[5:08:54 PM] [app] › ℹ  Start step "generateNotes" of plugin "Inline plugin"
2020-12-30T17:08:54.161Z msr:synchronizer [ALL] _nextRelease
2020-12-30T17:08:54.176Z msr:inlinePlugin [app] notes generated
[5:08:54 PM] [app] › ✔  Completed step "generateNotes" of plugin "Inline plugin"
[5:08:59 PM] [app] › ✔  Created tag app@1.0.0-dev.9
[5:08:59 PM] [app] › ℹ  Start step "publish" of plugin "Inline plugin"
2020-12-30T17:08:59.848Z msr:synchronizer [ALL] _readyForTagging
2020-12-30T17:09:00.131Z msr:inlinePlugin [app] published
[5:09:00 PM] [app] › ✔  Completed step "publish" of plugin "Inline plugin"
[5:09:00 PM] [app] › ℹ  Start step "success" of plugin "@semantic-release/github"
[5:09:02 PM] [app] › ✔  Completed step "success" of plugin "@semantic-release/github"
[5:09:02 PM] [app] › ✔  Published release 1.0.0-dev.9 on main channel
[5:09:02 PM] › 🎉  Released 1 of 2 packages, semantically!
davikawasaki commented 3 years ago

I'm also debugging with a new test case on multiSemanticRelease.test.js:

test("Two separate releases (changes in only one package in second release with prereleases)", async () => {
  const packages = ["packages/c/", "packages/d/"];

  // Create Git repo with copy of Yarn workspaces fixture.
  const cwd = gitInit("master", "release");
  copyDirectory(`test/fixtures/yarnWorkspaces2Packages/`, cwd);
  const sha1 = gitCommitAll(cwd, "feat: Initial release");
  gitInitOrigin(cwd, "release");
  gitPush(cwd);

  let stdout = new WritableStreamBuffer();
  let stderr = new WritableStreamBuffer();

  // Call multiSemanticRelease()
  // Doesn't include plugins that actually publish.
  const multiSemanticRelease = require("../../");
  let result = await multiSemanticRelease(
      packages.map((folder) => `${folder}package.json`),
      {
          branches: [{ name: "master", prerelease: "dev" }, { name: "release" }],
      },
      { cwd, stdout, stderr }
  );

  // Add new testing files for a new release.
  createNewTestingFiles(["packages/c/"], cwd);
  const sha = gitCommitAll(cwd, "feat: New release on package c only");
  gitPush(cwd);

  // Capture output.
  stdout = new WritableStreamBuffer();
  stderr = new WritableStreamBuffer();

  // Call multiSemanticRelease() for a second release
  // Doesn't include plugins that actually publish.
  result = await multiSemanticRelease(
      packages.map((folder) => `${folder}package.json`),
      {
          branches: [{ name: "master", prerelease: "dev" }, { name: "release" }],
      },
      { cwd, stdout, stderr }
  );

  // Get stdout and stderr output.
  const err = stderr.getContentsAsString("utf8");
  expect(err).toBe(false);
  const out = stdout.getContentsAsString("utf8");
  expect(out).toMatch("Started multirelease! Loading 2 packages...");
  expect(out).toMatch("Loaded package msr-test-c");
  expect(out).toMatch("Loaded package msr-test-d");
  expect(out).toMatch("Queued 2 packages! Starting release...");
  expect(out).toMatch("Created tag msr-test-c@1.0.0-dev.2");
  expect(out).toMatch("Released 1 of 2 packages, semantically!");

  // D.
  expect(result[0].name).toBe("msr-test-c");
  expect(result[0].result.lastRelease).toEqual({
      channels: ["master"],
      gitHead: sha1,
      gitTag: "msr-test-c@1.0.0-dev.1",
      name: "msr-test-c@1.0.0-dev.1",
      version: "1.0.0-dev.1",
  });
  expect(result[0].result.nextRelease).toMatchObject({
      gitHead: sha,
      gitTag: "msr-test-c@1.0.0-dev.2",
      type: "minor",
      version: "1.0.0-dev.2",
  });

  expect(result[0].result.nextRelease.notes).toMatch("# msr-test-c [1.0.0-dev.2]");
  expect(result[0].result.nextRelease.notes).toMatch("### Features\n\n* New release on package c only");
  expect(result[0].result.nextRelease.notes).not.toMatch("### Dependencies");

  // ONLY 1 time.
  expect(result).toHaveLength(2);

  // Check manifests.
  expect(require(`${cwd}/packages/c/package.json`)).toMatchObject({
      dependencies: {
          "msr-test-d": "1.0.0-dev.1",
      },
  });
});

This is the failed results:

 ● multiSemanticRelease() › Two separate releases (changes in only one package in second release with prereleases)

    expect(received).toMatchObject(expected)

    - Expected  - 1
    + Received  + 1

      Object {
        "dependencies": Object {
    -     "msr-test-d": "1.0.0-dev.1",
    +     "msr-test-d": "1.0.0-dev.2",
        },
      }

      347 | 
      348 |         // Check manifests.
    > 349 |         expect(require(`${cwd}/packages/c/package.json`)).toMatchObject({
          |                                                           ^
      350 |             dependencies: {
      351 |                 "msr-test-d": "1.0.0-dev.1",
      352 |             },

      at Object.<anonymous> (test/lib/multiSemanticRelease.test.js:349:53)
          at runMicrotasks (<anonymous>)
antongolub commented 3 years ago

@davikawasaki,

Interesting... Could you attach test/fixtures/yarnWorkspaces2Packages/ fixtures?

davikawasaki commented 3 years ago

@antongolub Sure thing. It's similar to test/fixtures/yarnWorkspaces fixture, except the packages (should contain only packages c and d):

image

davikawasaki commented 3 years ago

@antongolub I'm finishing a PR that is fixing this problem, will send it to you max tomorrow.

davikawasaki commented 3 years ago

@antongolub Added the PR. Don't know if tests cover all edge cases, but can you dbl check it?

antongolub commented 3 years ago

@davikawasaki ,

Thanks a lot for the contribution. Patched version will be available asap. Could you also push these changes to upstream repo - https://github.com/dhoulb/multi-semantic-release?

davikawasaki commented 3 years ago

@antongolub sure. Will do it max tomorrow.