|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- const util = require("util");
- const exec = util.promisify(require("child_process").exec);
- const fs = require("fs");
- const path = require("path");
- const Log = require("logger");
-
- class gitHelper {
- constructor() {
- this.gitRepos = [];
- this.baseDir = path.normalize(__dirname + "/../../../");
- }
-
- getRefRegex(branch) {
- return new RegExp("s*([a-z,0-9]+[.][.][a-z,0-9]+) " + branch, "g");
- }
-
- async execShell(command) {
- let res = { stdout: "", stderr: "" };
- const { stdout, stderr } = await exec(command);
-
- res.stdout = stdout;
- res.stderr = stderr;
- return res;
- }
-
- async isGitRepo(moduleFolder) {
- let res = await this.execShell("cd " + moduleFolder + " && git remote -v");
- if (res.stderr) {
- Log.error("Failed to fetch git data for " + moduleFolder + ": " + res.stderr);
- return false;
- } else {
- return true;
- }
- }
-
- add(moduleName) {
- let moduleFolder = this.baseDir;
- if (moduleName !== "default") {
- moduleFolder = moduleFolder + "modules/" + moduleName;
- }
- try {
- Log.info("Checking git for module: " + moduleName);
- // Throws error if file doesn't exist
- fs.statSync(path.join(moduleFolder, ".git"));
- // Fetch the git or throw error if no remotes
- if (this.isGitRepo(moduleFolder)) {
- // Folder has .git and has at least one git remote, watch this folder
- this.gitRepos.unshift({ module: moduleName, folder: moduleFolder });
- }
- } catch (err) {
- // Error when directory .git doesn't exist or doesn't have any remotes
- // This module is not managed with git, skip
- }
- }
-
- async getStatusInfo(repo) {
- let gitInfo = {
- module: repo.module,
- // commits behind:
- behind: 0,
- // branch name:
- current: "",
- // current hash:
- hash: "",
- // remote branch:
- tracking: "",
- isBehindInStatus: false
- };
- let res;
- if (repo.module === "default") {
- // the hash is only needed for the mm repo
- res = await this.execShell("cd " + repo.folder + " && git rev-parse HEAD");
- if (res.stderr) {
- Log.error("Failed to get current commit hash for " + repo.module + ": " + res.stderr);
- }
- gitInfo.hash = res.stdout;
- }
- if (repo.res) {
- // mocking
- res = repo.res;
- } else {
- res = await this.execShell("cd " + repo.folder + " && git status -sb");
- }
- if (res.stderr) {
- Log.error("Failed to get git status for " + repo.module + ": " + res.stderr);
- // exit without git status info
- return;
- }
- // only the first line of stdout is evaluated
- let status = res.stdout.split("\n")[0];
- // examples for status:
- // ## develop...origin/develop
- // ## master...origin/master [behind 8]
- status = status.match(/(?![.#])([^.]*)/g);
- // examples for status:
- // [ ' develop', 'origin/develop', '' ]
- // [ ' master', 'origin/master [behind 8]', '' ]
- gitInfo.current = status[0].trim();
- status = status[1].split(" ");
- // examples for status:
- // [ 'origin/develop' ]
- // [ 'origin/master', '[behind', '8]' ]
- gitInfo.tracking = status[0].trim();
- if (status[2]) {
- // git fetch was already called before so `git status -sb` delivers already the behind number
- gitInfo.behind = parseInt(status[2].substring(0, status[2].length - 1));
- gitInfo.isBehindInStatus = true;
- }
- return gitInfo;
- }
-
- async getRepoInfo(repo) {
- let gitInfo;
- if (repo.gitInfo) {
- // mocking
- gitInfo = repo.gitInfo;
- } else {
- gitInfo = await this.getStatusInfo(repo);
- }
- if (!gitInfo) {
- return;
- }
- if (gitInfo.isBehindInStatus) {
- return gitInfo;
- }
- let res;
- if (repo.res) {
- // mocking
- res = repo.res;
- } else {
- res = await this.execShell("cd " + repo.folder + " && git fetch --dry-run");
- }
- // example output:
- // From https://github.com/MichMich/MagicMirror
- // e40ddd4..06389e3 develop -> origin/develop
- // here the result is in stderr (this is a git default, don't ask why ...)
- const matches = res.stderr.match(this.getRefRegex(gitInfo.current));
- if (!matches || !matches[0]) {
- // no refs found, nothing to do
- return;
- }
- // get behind with refs
- try {
- res = await this.execShell("cd " + repo.folder + " && git rev-list --ancestry-path --count " + matches[0]);
- gitInfo.behind = parseInt(res.stdout);
- return gitInfo;
- } catch (err) {
- Log.error("Failed to get git revisions for " + repo.module + ": " + err);
- }
- }
-
- async getStatus() {
- const gitResultList = [];
- for (let repo of this.gitRepos) {
- const gitInfo = await this.getStatusInfo(repo);
- if (gitInfo) {
- gitResultList.unshift(gitInfo);
- }
- }
-
- return gitResultList;
- }
-
- async getRepos() {
- const gitResultList = [];
- for (let repo of this.gitRepos) {
- const gitInfo = await this.getRepoInfo(repo);
- if (gitInfo) {
- gitResultList.unshift(gitInfo);
- }
- }
-
- return gitResultList;
- }
- }
-
- module.exports.gitHelper = gitHelper;
|