dhoulb / multi-semantic-release

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

Leftover git tags after deployment failure #144

Open franky47 opened 1 year ago

franky47 commented 1 year ago

I'm having issues with publishing packages on NPM (hitting 403s, currently investigating tokens, auth et al), and I noticed that MSR applies Git tags before attempting to publish on NPM.

If NPM were to be down for some reason, or any other retry-able failure, one would have to manually remove those git tags from the repo (and hope nobody pulled them in the meantime and re-pushes them later), so that MSR can pick the unpublished changes again in the next CI/CD run.

Wouldn't it make more sense to apply Git tags only after all publish steps have succeeded?

Here's my MSR configuration in the root package.json:

"release": {
    "plugins": [
      [
        "@semantic-release/commit-analyzer",
        {
          "releaseRules": [
            {
              "type": "refactor",
              "release": "patch"
            },
            {
              "type": "perf",
              "release": "patch"
            },
            {
              "type": "chore",
              "scope": "deps",
              "release": "patch"
            },
            {
              "type": "docs",
              "scope": "README",
              "release": "patch"
            }
          ]
        }
      ],
      "@semantic-release/release-notes-generator",
      "@semantic-release/npm",
      // internal package to turn release notes into a GitHub Actions summary output
      // source: https://github.com/SocialGouv/e2esdk/blob/beta/config/semantic-release/src/index.ts
      "@socialgouv/e2esdk-semantic-release"
    ],
    "branches": [
      "main",
      {
        "name": "beta",
        "prerelease": true
      }
    ]
  }
antongolub commented 1 year ago

Hey, @franky47,

MSR applies Git tags before attempting to publish on NPM

I'm afraid, not msr, but semrel does:

await plugins.verifyRelease(context);

  nextRelease.notes = await plugins.generateNotes(context);

  await plugins.prepare(context);

if (options.dryRun) {
    logger.warn(`Skip ${nextRelease.gitTag} tag creation in dry-run mode`);
  } else {
    // Create the tag before calling the publish plugins as some require the tag to exists
    await tag(nextRelease.gitTag, nextRelease.gitHead, { cwd, env });
    await addNote({ channels: [nextRelease.channel] }, nextRelease.gitHead, { cwd, env });
    await push(options.repositoryUrl, { cwd, env });
    await pushNotes(options.repositoryUrl, { cwd, env });
    logger.success(`Created tag ${nextRelease.gitTag}`);
  }

  const releases = await plugins.publish(context);
  context.releases.push(...releases);

But there's a hack for your case. For example, you can invoke any custom cmd via semre/exec on the prepare step. Smth like:

{
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    ...
    ["@semantic-release/exec", {
      "prepareCmd": "npm publish"
    }],
  ]
}

To disable default npm's plugin publish task you can rewrite your release config in a step-focused notation (but I'm not sure if this syntax is still supported by semrel>=19):

{
plugins: [
            '@semantic-release/commit-analyzer',
            '@semantic-release/release-notes-generator',
            '@semantic-release/npm',
            '@semantic-release/github'
          ],
          verifyConditions: [
            '@semantic-release/github',
            [
              '@qiwi/semantic-release-gh-pages-plugin',
              {
                enterprise: 'true',
                src: 'dist/web'
              }
            ]
          ],
          publish: [
            {
              path: '@semantic-release/exec',
              cmd: 'nuget pack dist/package -Version ${nextRelease.version}'
            },
            {
              path: '@semantic-release/github',
              assets: '*.nupkg'
            },
            [
              '@qiwi/semantic-release-gh-pages-plugin',
              {
                enterprise: 'true',
                src: 'dist/web'
              }
            ]
}
franky47 commented 1 year ago

Thanks for the quick answer, I wasn't sure where the git tagging operation took place.

Having NPM publish in the prepare phase would indeed solve this issue, but may bring some more later on, will look into it.

I believe my 403 errors with NPM may also be related to this dangling Git tag issue: at some point in the past, some NPM publish commands failed, and tags were left to pile up. Now MSR is referring those unreleased tags to bump internal workspace dependencies, for which there are no published packages, and the publish of the dependant package fails.

antongolub commented 1 year ago

The only way to check if everything is ok with npm registry and token is to publish smth. You can push a snapshot as a part of prepare step: npm version ${version}-snapshot && npm publish --tag snapshot. This may capture 403 exceptions.