Skip to content
Permalink
Browse files

fix(version): Support git clients that do not support `git push --ato…

…mic`

Support for `--atomic` was added in 2015, but apparently 5 years isn't enough time for CI providers to upgrade their shit.
  • Loading branch information
evocateur committed Dec 29, 2019
1 parent b90f4d3 commit 2b9b210c0b6ac69853ffb01f0dbac9109ab419c5
Showing with 82 additions and 2 deletions.
  1. +65 −1 commands/version/__tests__/git-push.test.js
  2. +17 −1 commands/version/lib/git-push.js
@@ -1,9 +1,22 @@
"use strict";

const execa = require("execa");
const childProcess = require("@lerna/child-process");
const cloneFixture = require("@lerna-test/clone-fixture")(__dirname);
const gitPush = require("../lib/git-push");

async function listRemoteTags(cwd) {
return execa.stdout("git", ["ls-remote", "--tags", "--refs", "--quiet"], { cwd });
}

beforeEach(() => {
jest.spyOn(childProcess, "exec");
});

afterEach(() => {
jest.restoreAllMocks();
});

test("gitPush", async () => {
const { cwd } = await cloneFixture("root-manifest-only");

@@ -14,8 +27,59 @@ test("gitPush", async () => {

await gitPush("origin", "master", { cwd });

const list = await execa.stdout("git", ["ls-remote", "--tags", "--refs", "--quiet"], { cwd });
expect(childProcess.exec).toHaveBeenLastCalledWith(
"git",
["push", "--follow-tags", "--no-verify", "--atomic", "origin", "master"],
{ cwd }
);

const list = await listRemoteTags(cwd);
expect(list).toMatch("v1.2.3");
expect(list).toMatch("foo@2.3.1");
expect(list).toMatch("bar@3.2.1");
});

test("remote that does not support --atomic", async () => {
const { cwd } = await cloneFixture("root-manifest-only");

await execa("git", ["commit", "--allow-empty", "-m", "change"], { cwd });
await execa("git", ["tag", "v4.5.6", "-m", "v4.5.6"], { cwd });

// the first time the command is executed, simulate remote error
childProcess.exec.mockImplementationOnce(async () => {
throw new Error(
[
"Command failed: git push --follow-tags --atomic --no-verify origin master",
"fatal: the receiving end does not support --atomic push",
].join("\n")
);
});

// this call should _not_ throw
await gitPush("origin", "master", { cwd });

expect(childProcess.exec).toHaveBeenCalledTimes(2);
expect(childProcess.exec).toHaveBeenLastCalledWith(
"git",
["push", "--follow-tags", "--no-verify", "origin", "master"],
{ cwd }
);

const list = await listRemoteTags(cwd);
expect(list).toMatch("v4.5.6");
});

test("unexpected git error", async () => {
const { cwd } = await cloneFixture("root-manifest-only");

childProcess.exec.mockImplementationOnce(async () => {
throw new Error(
[
"Command failed: git push --follow-tags --atomic --no-verify origin master",
"fatal: some unexpected error",
].join("\n")
);
});

await expect(gitPush("origin", "master", { cwd })).rejects.toThrowError(/some unexpected error/);
});
@@ -8,5 +8,21 @@ module.exports = gitPush;
function gitPush(remote, branch, opts) {
log.silly("gitPush", remote, branch);

return childProcess.exec("git", ["push", "--follow-tags", "--atomic", "--no-verify", remote, branch], opts);
return childProcess
.exec("git", ["push", "--follow-tags", "--no-verify", "--atomic", remote, branch], opts)
.catch(error => {
// @see https://github.com/sindresorhus/execa/blob/v1.0.0/index.js#L159-L179
// the error message _should_ be on stderr, but I don't trust Windows to do anything right
if (/fatal:(.*)--atomic/.test(error.message)) {
// --atomic is only supported in git >=2.4.0, which some crusty CI environments deem unnecessary to upgrade.
// so let's try again without attempting to pass an option that is almost 5 years old as of this writing...
log.warn("gitPush", "failed to pass --atomic (unsupported by remote), attempting non-atomic push");
log.verbose("", error.message);

return childProcess.exec("git", ["push", "--follow-tags", "--no-verify", remote, branch], opts);
}

// ensure unexpected errors still break chain
throw error;
});
}

0 comments on commit 2b9b210

Please sign in to comment.
You can’t perform that action at this time.