Skip to content

Commit

Permalink
fix: detect missing git identity (#83)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlCalzone committed Jul 26, 2021
1 parent 66c67d4 commit 0b9d28b
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 4 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
Placeholder for the next version (at the beginning of the line):
## **WORK IN PROGRESS**
-->
## **WORK IN PROGRESS**
* The script now detects a missing git identity and provides help on how to configure it

## 2.2.0 (2021-07-01)
* Added an automated check of the Github Actions workflow file to spot potential errors that could fail a release

Expand Down
113 changes: 113 additions & 0 deletions build/git.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkGitIdentity = exports.gitStatus = void 0;
const child_process_1 = require("child_process");
function getExecOptions(cwd) {
return { cwd, encoding: "utf8" };
}
function getUpstream(cwd) {
return child_process_1.execSync("git rev-parse --abbrev-ref --symbolic-full-name @{u}", getExecOptions(cwd)).trim();
}
function getCommitDifferences(cwd, remote) {
// if upstream hard configured we use it
const output = child_process_1.execSync(`git rev-list --left-right --count HEAD...${remote || getUpstream(cwd)}`, getExecOptions(cwd)).trim();
// something like "1\t0"
return output.split("\t", 2).map(Number);
}
function hasUncommittedChanges(cwd) {
const output = child_process_1.execSync(`git status --porcelain`, getExecOptions(cwd)).trim();
return output !== "";
}
/** Locale-independent check for uncommitted changes and commit differences between local and remote branches */
function gitStatus(cwd, remote) {
const [localDiff, remoteDiff] = getCommitDifferences(cwd, remote);
if (localDiff > 0 && remoteDiff > 0) {
return "diverged";
}
else if (localDiff === 0 && remoteDiff > 0) {
return "behind";
}
else if (hasUncommittedChanges(cwd)) {
return "uncommitted";
} /* if (remote === 0) */
else {
return localDiff === 0 ? "up-to-date" : "ahead";
}
}
exports.gitStatus = gitStatus;
function checkGitIdentity(cwd) {
try {
const username = child_process_1.execSync("git config --get user.name", getExecOptions(cwd)).trim();
const email = child_process_1.execSync("git config --get user.email", getExecOptions(cwd)).trim();
return username !== "" && email !== "";
}
catch (e) {
return false;
}
}
exports.checkGitIdentity = checkGitIdentity;
// // check if there are untracked changes
// const gitStatus = execSync("git status", { cwd: rootDir, encoding: "utf8" });
// if (/have diverged/.test(gitStatus)) {
// if (!isDryRun) {
// fail(
// colors.red(
// "Cannot continue, the local branch has diverged from the git repo!",
// ),
// );
// } else {
// console.log(
// colors.red(
// "This is a dry run. The full run would fail due to a diverged branch\n",
// ),
// );
// }
// } else if (!lerna && !/working tree clean/.test(gitStatus)) {
// if (!isDryRun && !allChanges) {
// fail(
// colors.red(
// `Cannot continue, the local branch has uncommitted changes! Add them to a separate commit first or add the "--all" option to include them in the release commit.`,
// ),
// );
// } else {
// if (allChanges) {
// console.warn(
// colors.yellow(
// `Your branch has uncommitted changes that will be included in the release commit!
// Consider adding them to a separate commit first.
// `,
// ),
// );
// } else {
// console.log(
// colors.red(
// `This is a dry run. The full run would fail due to uncommitted changes.
// Add them to a separate commit first or add the "--all" option to include them in the release commit.
// `,
// ),
// );
// }
// }
// } else if (/Your branch is behind/.test(gitStatus)) {
// if (!isDryRun) {
// fail(
// colors.red(
// "Cannot continue, the local branch is behind the remote changes!",
// ),
// );
// } else {
// console.log(
// colors.red(
// "This is a dry run. The full run would fail due to the local branch being behind\n",
// ),
// );
// }
// } else if (
// /Your branch is up\-to\-date/.test(gitStatus) ||
// /Your branch is ahead/.test(gitStatus)
// ) {
// // all good
// if (!lerna) {
// console.log(colors.green("git status is good - I can continue..."));
// }
// }
27 changes: 25 additions & 2 deletions build/release.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,12 @@ const safe_1 = __importDefault(require("colors/safe"));
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const semver = __importStar(require("semver"));
const os = __importStar(require("os"));
const yargs_1 = __importDefault(require("yargs"));
const tools_1 = require("./tools");
const translate_1 = require("./translate");
const parseArgs_1 = require("./parseArgs");
const gitStatus_1 = require("./gitStatus");
const git_1 = require("./git");
const yarn_1 = require("./yarn");
(async () => {
var _a, _b;
Expand Down Expand Up @@ -84,6 +85,28 @@ const yarn_1 = require("./yarn");
fail("Missing property version from lerna.json!");
}
}
// Check that git can push
if (!git_1.checkGitIdentity(rootDir)) {
let message = safe_1.default.red((isDryRun
? "This is a dry run. The full run would fail because "
: "Cannot continue because ") +
`no git identity is configured for the current user ${safe_1.default.bold(safe_1.default.blue(os.userInfo().username))}!`);
message += `\n
Please tell git who you are, either globally using
${safe_1.default.blue(`git config --global user.name "Your Name"
git config --global user.email "your@e-mail.com"`)}
or only for this folder
${safe_1.default.blue(`git config user.name "Your Name"
git config user.email "your@e-mail.com"`)}
Note: If the current folder belongs to a different user than ${safe_1.default.bold(safe_1.default.blue(os.userInfo().username))}, you might have to switch to that user first before changing the global config.
`;
if (isDryRun)
console.log(message);
else
fail(message);
}
// ensure that the release workflow does not check for base_ref
// This is pretty specific to ioBroker's release workflow, but better than silently failing
const workflowPath = path.join(rootDir, ".github/workflows/test-and-release.yml");
Expand Down Expand Up @@ -171,7 +194,7 @@ You can suppress this check with the ${safe_1.default.bold("--no-workflow-check"
fail(safe_1.default.red("Cannot continue, the changelog for the next version is empty!"));
}
// check if there are untracked changes
const branchStatus = gitStatus_1.gitStatus(rootDir, remote);
const branchStatus = git_1.gitStatus(rootDir, remote);
if (branchStatus === "diverged") {
if (!isDryRun) {
fail(safe_1.default.red("Cannot continue, both the remote and the local repo have different changes! Please merge the remote changes first."));
Expand Down
10 changes: 10 additions & 0 deletions src/gitStatus.ts → src/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ export function gitStatus(cwd: string, remote?: string): GitStatus {
}
}

export function checkGitIdentity(cwd: string): boolean {
try {
const username = execSync("git config --get user.name", getExecOptions(cwd)).trim();
const email = execSync("git config --get user.email", getExecOptions(cwd)).trim();
return username !== "" && email !== "";
} catch (e) {
return false;
}
}

// // check if there are untracked changes
// const gitStatus = execSync("git status", { cwd: rootDir, encoding: "utf8" });
// if (/have diverged/.test(gitStatus)) {
Expand Down
34 changes: 32 additions & 2 deletions src/release.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import colors from "colors/safe";
import * as fs from "fs";
import * as path from "path";
import * as semver from "semver";
import * as os from "os";
import yargs from "yargs";
import {
cleanChangelogForNews,
Expand All @@ -36,7 +37,7 @@ import {
} from "./tools";
import { translateText } from "./translate";
import { parseArgs } from "./parseArgs";
import { gitStatus } from "./gitStatus";
import { checkGitIdentity, gitStatus } from "./git";
import { getChangedWorkspaces } from "./yarn";

(async () => {
Expand Down Expand Up @@ -82,6 +83,33 @@ import { getChangedWorkspaces } from "./yarn";
}
}

// Check that git can push
if (!checkGitIdentity(rootDir)) {
let message = colors.red(
(isDryRun
? "This is a dry run. The full run would fail because "
: "Cannot continue because ") +
`no git identity is configured for the current user ${colors.bold(
colors.blue(os.userInfo().username),
)}!`,
);
message += `\n
Please tell git who you are, either globally using
${colors.blue(`git config --global user.name "Your Name"
git config --global user.email "your@e-mail.com"`)}
or only for this folder
${colors.blue(`git config user.name "Your Name"
git config user.email "your@e-mail.com"`)}
Note: If the current folder belongs to a different user than ${colors.bold(
colors.blue(os.userInfo().username),
)}, you might have to switch to that user first before changing the global config.
`;
if (isDryRun) console.log(message);
else fail(message);
}

// ensure that the release workflow does not check for base_ref
// This is pretty specific to ioBroker's release workflow, but better than silently failing
const workflowPath = path.join(
Expand Down Expand Up @@ -112,7 +140,9 @@ import { getChangedWorkspaces } from "./yarn";
Remove this line to fix it:
${colors.inverse(line)}
You can suppress this check with the ${colors.bold("--no-workflow-check")} flag.`);
You can suppress this check with the ${colors.bold(
"--no-workflow-check",
)} flag.`);
}
}

Expand Down

0 comments on commit 0b9d28b

Please sign in to comment.