mirror of
https://github.com/MichMich/MagicMirror.git
synced 2025-06-27 11:50:00 +00:00
Merge pull request #2651 from khassel/updatenotification
This commit is contained in:
commit
a68aa148b8
3
.github/workflows/node-ci.js.yml
vendored
3
.github/workflows/node-ci.js.yml
vendored
@ -15,7 +15,7 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x, 14.x, 16.x]
|
||||
node-version: [12.x, 14.x, 16.8]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
@ -23,6 +23,7 @@ jobs:
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: |
|
||||
node -v
|
||||
Xvfb :99 -screen 0 1024x768x16 &
|
||||
export DISPLAY=:99
|
||||
npm install
|
||||
|
@ -30,6 +30,7 @@ _This release is scheduled to be released on 2021-10-01._
|
||||
- Fix undefined error with ignoreToday option in weather module (#2620).
|
||||
- Fix time zone correction in calendar module when the date hour is equal to the time zone correction value (#2632).
|
||||
- Fix black cursor on startup when using electron.
|
||||
- Fix update notification not working for own repository (#2644).
|
||||
|
||||
## [2.16.0] - 2021-07-01
|
||||
|
||||
|
161
modules/default/updatenotification/git_helper.js
Normal file
161
modules/default/updatenotification/git_helper.js
Normal file
@ -0,0 +1,161 @@
|
||||
const util = require("util");
|
||||
const exec = util.promisify(require("child_process").exec);
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
class gitHelper {
|
||||
constructor() {
|
||||
this.gitRepos = [];
|
||||
this.baseDir = path.normalize(__dirname + "/../../../");
|
||||
if (process.env.JEST_WORKER_ID === undefined) {
|
||||
this.Log = require("logger");
|
||||
} else {
|
||||
// if we are running with jest
|
||||
this.Log = require("../../../tests/unit/mocks/logger.js");
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
this.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 {
|
||||
this.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) {
|
||||
this.Log.error("Failed to get current commit hash for " + repo.module + ": " + res.stderr);
|
||||
}
|
||||
gitInfo.hash = res.stdout;
|
||||
}
|
||||
res = await this.execShell("cd " + repo.folder + " && git status -sb");
|
||||
if (res.stderr) {
|
||||
this.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 = await this.getStatusInfo(repo);
|
||||
if (!gitInfo) {
|
||||
return;
|
||||
}
|
||||
if (gitInfo.isBehindInStatus) {
|
||||
return gitInfo;
|
||||
}
|
||||
let 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
|
||||
res = await this.execShell("cd " + repo.folder + " && git rev-list --ancestry-path --count " + matches[0]);
|
||||
gitInfo.behind = parseInt(res.stdout);
|
||||
return gitInfo;
|
||||
}
|
||||
|
||||
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;
|
@ -1,9 +1,5 @@
|
||||
const SimpleGit = require("simple-git");
|
||||
const simpleGits = [];
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const GitHelper = require(__dirname + "/git_helper.js");
|
||||
const defaultModules = require(__dirname + "/../defaultmodules.js");
|
||||
const Log = require("logger");
|
||||
const NodeHelper = require("node_helper");
|
||||
|
||||
module.exports = NodeHelper.create({
|
||||
@ -12,32 +8,19 @@ module.exports = NodeHelper.create({
|
||||
updateTimer: null,
|
||||
updateProcessStarted: false,
|
||||
|
||||
gitHelper: new GitHelper.gitHelper(),
|
||||
|
||||
start: function () {},
|
||||
|
||||
configureModules: async function (modules) {
|
||||
// Push MagicMirror itself , biggest chance it'll show up last in UI and isn't overwritten
|
||||
// others will be added in front
|
||||
// this method returns promises so we can't wait for every one to resolve before continuing
|
||||
simpleGits.push({ module: "default", git: this.createGit(path.normalize(__dirname + "/../../../")) });
|
||||
this.gitHelper.add("default");
|
||||
|
||||
for (let moduleName in modules) {
|
||||
if (!this.ignoreUpdateChecking(moduleName)) {
|
||||
// Default modules are included in the main MagicMirror repo
|
||||
let moduleFolder = path.normalize(__dirname + "/../../" + 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
|
||||
let git = await this.resolveRemote(moduleFolder);
|
||||
// Folder has .git and has at least one git remote, watch this folder
|
||||
simpleGits.unshift({ module: moduleName, git: git });
|
||||
} catch (err) {
|
||||
// Error when directory .git doesn't exist or doesn't have any remotes
|
||||
// This module is not managed with git, skip
|
||||
continue;
|
||||
}
|
||||
this.gitHelper.add(moduleName);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -54,35 +37,9 @@ module.exports = NodeHelper.create({
|
||||
}
|
||||
},
|
||||
|
||||
resolveRemote: async function (moduleFolder) {
|
||||
let git = this.createGit(moduleFolder);
|
||||
let remotes = await git.getRemotes(true);
|
||||
|
||||
if (remotes.length < 1 || remotes[0].name.length < 1) {
|
||||
throw new Error("No valid remote for folder " + moduleFolder);
|
||||
}
|
||||
|
||||
return git;
|
||||
},
|
||||
|
||||
performFetch: async function () {
|
||||
for (let sg of simpleGits) {
|
||||
try {
|
||||
let fetchData = await sg.git.fetch(["--dry-run"]).status();
|
||||
let logData = await sg.git.log({ "-1": null });
|
||||
|
||||
if (logData.latest && "hash" in logData.latest) {
|
||||
this.sendSocketNotification("STATUS", {
|
||||
module: sg.module,
|
||||
behind: fetchData.behind,
|
||||
current: fetchData.current,
|
||||
hash: logData.latest.hash,
|
||||
tracking: fetchData.tracking
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
Log.error("Failed to fetch git data for " + sg.module + ": " + err);
|
||||
}
|
||||
for (let gitInfo of await this.gitHelper.getRepos()) {
|
||||
this.sendSocketNotification("STATUS", gitInfo);
|
||||
}
|
||||
|
||||
this.scheduleNextFetch(this.config.updateInterval);
|
||||
@ -100,10 +57,6 @@ module.exports = NodeHelper.create({
|
||||
}, delay);
|
||||
},
|
||||
|
||||
createGit: function (folder) {
|
||||
return SimpleGit({ baseDir: folder, timeout: { block: this.config.timeout } });
|
||||
},
|
||||
|
||||
ignoreUpdateChecking: function (moduleName) {
|
||||
// Should not check for updates for default modules
|
||||
if (defaultModules.indexOf(moduleName) >= 0) {
|
||||
|
1341
package-lock.json
generated
1341
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@ -47,11 +47,11 @@
|
||||
"devDependencies": {
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-jest": "^24.4.0",
|
||||
"eslint-plugin-jsdoc": "^36.0.8",
|
||||
"eslint-plugin-prettier": "^3.4.1",
|
||||
"eslint-plugin-jsdoc": "^36.1.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"express-basic-auth": "^1.2.0",
|
||||
"husky": "^7.0.2",
|
||||
"jest": "^27.1.0",
|
||||
"jest": "^27.1.1",
|
||||
"jsdom": "^17.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"nyc": "^15.1.0",
|
||||
@ -66,7 +66,7 @@
|
||||
"suncalc": "^1.8.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"electron": "^13.2.3"
|
||||
"electron": "^13.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"colors": "^1.4.0",
|
||||
@ -82,8 +82,7 @@
|
||||
"moment": "^2.29.1",
|
||||
"node-fetch": "^2.6.1",
|
||||
"node-ical": "^0.13.0",
|
||||
"simple-git": "^2.45.0",
|
||||
"socket.io": "^4.1.3"
|
||||
"socket.io": "^4.2.0"
|
||||
},
|
||||
"_moduleAliases": {
|
||||
"node_helper": "js/node_helper.js",
|
||||
|
29
tests/unit/functions/updatenotification_spec.js
Normal file
29
tests/unit/functions/updatenotification_spec.js
Normal file
@ -0,0 +1,29 @@
|
||||
const path = require("path");
|
||||
const git_Helper = require("../../../modules/default/updatenotification/git_helper.js");
|
||||
const gitHelper = new git_Helper.gitHelper();
|
||||
gitHelper.add("default");
|
||||
let branch = "";
|
||||
|
||||
describe("Updatenotification", function () {
|
||||
// it is assumed that we are at the HEAD of a branch when running this tests
|
||||
// and we have no foreign modules installed.
|
||||
|
||||
it("should return 0 for repo count", async function () {
|
||||
const arr = await gitHelper.getRepos();
|
||||
expect(arr.length).toBe(0);
|
||||
}, 15000);
|
||||
|
||||
it("should return valid output for git status", async function () {
|
||||
const arr = await gitHelper.getStatus();
|
||||
expect(arr.length).toBe(1);
|
||||
const gitInfo = arr[0];
|
||||
branch = gitInfo.current;
|
||||
expect(gitInfo.current).not.toBe("");
|
||||
}, 15000);
|
||||
|
||||
it("should return no refs for git fetch", async function () {
|
||||
const baseDir = path.normalize(__dirname + "/../../../");
|
||||
const res = await gitHelper.execShell("cd " + baseDir + " && git fetch --dry-run");
|
||||
expect(res.stderr.match(gitHelper.getRefRegex(branch))).toBe(null);
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user