Skip to content

Commit d12a587

Browse files
committed
Merge branch 'updatenotification' into logger
2 parents 2b147bb + 98942d6 commit d12a587

7 files changed

Lines changed: 852 additions & 755 deletions

File tree

.github/workflows/node-ci.js.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@ jobs:
1515
timeout-minutes: 30
1616
strategy:
1717
matrix:
18-
node-version: [12.x, 14.x, 16.x]
18+
node-version: [12.x, 14.x, 16.8]
1919
steps:
2020
- uses: actions/checkout@v2
2121
- name: Use Node.js ${{ matrix.node-version }}
2222
uses: actions/setup-node@v1
2323
with:
2424
node-version: ${{ matrix.node-version }}
2525
- run: |
26+
node -v
2627
Xvfb :99 -screen 0 1024x768x16 &
2728
export DISPLAY=:99
2829
npm install

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ _This release is scheduled to be released on 2021-10-01._
3030
- Fix undefined error with ignoreToday option in weather module (#2620).
3131
- Fix time zone correction in calendar module when the date hour is equal to the time zone correction value (#2632).
3232
- Fix black cursor on startup when using electron.
33+
- Fix update notification not working for own repository (#2644).
3334

3435
## [2.16.0] - 2021-07-01
3536

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
const util = require("util");
2+
const exec = util.promisify(require("child_process").exec);
3+
const fs = require("fs");
4+
const path = require("path");
5+
6+
class gitHelper {
7+
constructor() {
8+
this.gitRepos = [];
9+
this.baseDir = path.normalize(__dirname + "/../../../");
10+
if (process.env.JEST_WORKER_ID === undefined) {
11+
this.Log = require("logger");
12+
} else {
13+
// if we are running with jest
14+
this.Log = require("../../../tests/unit/mocks/logger.js");
15+
}
16+
}
17+
18+
getRefRegex(branch) {
19+
return new RegExp("s*([a-z,0-9]+[.][.][a-z,0-9]+) " + branch, "g");
20+
}
21+
22+
async execShell(command) {
23+
let res = { stdout: "", stderr: "" };
24+
const { stdout, stderr } = await exec(command);
25+
26+
res.stdout = stdout;
27+
res.stderr = stderr;
28+
return res;
29+
}
30+
31+
async isGitRepo(moduleFolder) {
32+
let res = await this.execShell("cd " + moduleFolder + " && git remote -v");
33+
if (res.stderr) {
34+
this.Log.error("Failed to fetch git data for " + moduleFolder + ": " + res.stderr);
35+
return false;
36+
} else {
37+
return true;
38+
}
39+
}
40+
41+
add(moduleName) {
42+
let moduleFolder = this.baseDir;
43+
if (moduleName !== "default") {
44+
moduleFolder = moduleFolder + "modules/" + moduleName;
45+
}
46+
try {
47+
this.Log.info("Checking git for module: " + moduleName);
48+
// Throws error if file doesn't exist
49+
fs.statSync(path.join(moduleFolder, ".git"));
50+
// Fetch the git or throw error if no remotes
51+
if (this.isGitRepo(moduleFolder)) {
52+
// Folder has .git and has at least one git remote, watch this folder
53+
this.gitRepos.unshift({ module: moduleName, folder: moduleFolder });
54+
}
55+
} catch (err) {
56+
// Error when directory .git doesn't exist or doesn't have any remotes
57+
// This module is not managed with git, skip
58+
}
59+
}
60+
61+
async getStatusInfo(repo) {
62+
let gitInfo = {
63+
module: repo.module,
64+
// commits behind:
65+
behind: 0,
66+
// branch name:
67+
current: "",
68+
// current hash:
69+
hash: "",
70+
// remote branch:
71+
tracking: "",
72+
isBehindInStatus: false
73+
};
74+
let res;
75+
if (repo.module === "default") {
76+
// the hash is only needed for the mm repo
77+
res = await this.execShell("cd " + repo.folder + " && git rev-parse HEAD");
78+
if (res.stderr) {
79+
this.Log.error("Failed to get current commit hash for " + repo.module + ": " + res.stderr);
80+
}
81+
gitInfo.hash = res.stdout;
82+
}
83+
res = await this.execShell("cd " + repo.folder + " && git status -sb");
84+
if (res.stderr) {
85+
this.Log.error("Failed to get git status for " + repo.module + ": " + res.stderr);
86+
// exit without git status info
87+
return;
88+
}
89+
// only the first line of stdout is evaluated
90+
let status = res.stdout.split("\n")[0];
91+
// examples for status:
92+
// ## develop...origin/develop
93+
// ## master...origin/master [behind 8]
94+
status = status.match(/(?![.#])([^.]*)/g);
95+
// examples for status:
96+
// [ ' develop', 'origin/develop', '' ]
97+
// [ ' master', 'origin/master [behind 8]', '' ]
98+
gitInfo.current = status[0].trim();
99+
status = status[1].split(" ");
100+
// examples for status:
101+
// [ 'origin/develop' ]
102+
// [ 'origin/master', '[behind', '8]' ]
103+
gitInfo.tracking = status[0].trim();
104+
if (status[2]) {
105+
// git fetch was already called before so `git status -sb` delivers already the behind number
106+
gitInfo.behind = parseInt(status[2].substring(0, status[2].length - 1));
107+
gitInfo.isBehindInStatus = true;
108+
}
109+
return gitInfo;
110+
}
111+
112+
async getRepoInfo(repo) {
113+
let gitInfo = await this.getStatusInfo(repo);
114+
if (!gitInfo) {
115+
return;
116+
}
117+
if (gitInfo.isBehindInStatus) {
118+
return gitInfo;
119+
}
120+
let res = await this.execShell("cd " + repo.folder + " && git fetch --dry-run");
121+
// example output:
122+
// From https://github.com/MichMich/MagicMirror
123+
// e40ddd4..06389e3 develop -> origin/develop
124+
// here the result is in stderr (this is a git default, don't ask why ...)
125+
const matches = res.stderr.match(this.getRefRegex(gitInfo.current));
126+
if (!matches || !matches[0]) {
127+
// no refs found, nothing to do
128+
return;
129+
}
130+
// get behind with refs
131+
res = await this.execShell("cd " + repo.folder + " && git rev-list --ancestry-path --count " + matches[0]);
132+
gitInfo.behind = parseInt(res.stdout);
133+
return gitInfo;
134+
}
135+
136+
async getStatus() {
137+
const gitResultList = [];
138+
for (let repo of this.gitRepos) {
139+
const gitInfo = await this.getStatusInfo(repo);
140+
if (gitInfo) {
141+
gitResultList.unshift(gitInfo);
142+
}
143+
}
144+
145+
return gitResultList;
146+
}
147+
148+
async getRepos() {
149+
const gitResultList = [];
150+
for (let repo of this.gitRepos) {
151+
const gitInfo = await this.getRepoInfo(repo);
152+
if (gitInfo) {
153+
gitResultList.unshift(gitInfo);
154+
}
155+
}
156+
157+
return gitResultList;
158+
}
159+
}
160+
161+
module.exports.gitHelper = gitHelper;

modules/default/updatenotification/node_helper.js

Lines changed: 7 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
const SimpleGit = require("simple-git");
2-
const simpleGits = [];
3-
const fs = require("fs");
4-
const path = require("path");
1+
const GitHelper = require(__dirname + "/git_helper.js");
52
const defaultModules = require(__dirname + "/../defaultmodules.js");
6-
const Log = require("logger");
73
const NodeHelper = require("node_helper");
84

95
module.exports = NodeHelper.create({
@@ -12,32 +8,19 @@ module.exports = NodeHelper.create({
128
updateTimer: null,
139
updateProcessStarted: false,
1410

11+
gitHelper: new GitHelper.gitHelper(),
12+
1513
start: function () {},
1614

1715
configureModules: async function (modules) {
1816
// Push MagicMirror itself , biggest chance it'll show up last in UI and isn't overwritten
1917
// others will be added in front
2018
// this method returns promises so we can't wait for every one to resolve before continuing
21-
simpleGits.push({ module: "default", git: this.createGit(path.normalize(__dirname + "/../../../")) });
19+
this.gitHelper.add("default");
2220

2321
for (let moduleName in modules) {
2422
if (!this.ignoreUpdateChecking(moduleName)) {
25-
// Default modules are included in the main MagicMirror repo
26-
let moduleFolder = path.normalize(__dirname + "/../../" + moduleName);
27-
28-
try {
29-
Log.info("Checking git for module: " + moduleName);
30-
// Throws error if file doesn't exist
31-
fs.statSync(path.join(moduleFolder, ".git"));
32-
// Fetch the git or throw error if no remotes
33-
let git = await this.resolveRemote(moduleFolder);
34-
// Folder has .git and has at least one git remote, watch this folder
35-
simpleGits.unshift({ module: moduleName, git: git });
36-
} catch (err) {
37-
// Error when directory .git doesn't exist or doesn't have any remotes
38-
// This module is not managed with git, skip
39-
continue;
40-
}
23+
this.gitHelper.add(moduleName);
4124
}
4225
}
4326
},
@@ -54,35 +37,9 @@ module.exports = NodeHelper.create({
5437
}
5538
},
5639

57-
resolveRemote: async function (moduleFolder) {
58-
let git = this.createGit(moduleFolder);
59-
let remotes = await git.getRemotes(true);
60-
61-
if (remotes.length < 1 || remotes[0].name.length < 1) {
62-
throw new Error("No valid remote for folder " + moduleFolder);
63-
}
64-
65-
return git;
66-
},
67-
6840
performFetch: async function () {
69-
for (let sg of simpleGits) {
70-
try {
71-
let fetchData = await sg.git.fetch(["--dry-run"]).status();
72-
let logData = await sg.git.log({ "-1": null });
73-
74-
if (logData.latest && "hash" in logData.latest) {
75-
this.sendSocketNotification("STATUS", {
76-
module: sg.module,
77-
behind: fetchData.behind,
78-
current: fetchData.current,
79-
hash: logData.latest.hash,
80-
tracking: fetchData.tracking
81-
});
82-
}
83-
} catch (err) {
84-
Log.error("Failed to fetch git data for " + sg.module + ": " + err);
85-
}
41+
for (let gitInfo of await this.gitHelper.getRepos()) {
42+
this.sendSocketNotification("STATUS", gitInfo);
8643
}
8744

8845
this.scheduleNextFetch(this.config.updateInterval);
@@ -100,10 +57,6 @@ module.exports = NodeHelper.create({
10057
}, delay);
10158
},
10259

103-
createGit: function (folder) {
104-
return SimpleGit({ baseDir: folder, timeout: { block: this.config.timeout } });
105-
},
106-
10760
ignoreUpdateChecking: function (moduleName) {
10861
// Should not check for updates for default modules
10962
if (defaultModules.indexOf(moduleName) >= 0) {

0 commit comments

Comments
 (0)