|
1 | | -const path = require("path"); |
2 | | -const { existsSync, rm } = require("fs") |
| 1 | +const { access, rm } = require("fs/promises") |
3 | 2 | const { exec, fork } = require("child_process"); |
| 3 | +const path = require("path"); |
| 4 | + |
4 | 5 | const config = require("./config.json"); |
5 | 6 |
|
6 | | -new class { |
| 7 | +let app; |
7 | 8 |
|
8 | | - constructor() { |
9 | | - this.config = config; |
10 | | - this.start(); |
11 | | - } |
| 9 | +const dir = `${path.dirname(require.main.filename).replaceAll("\\", "/")}/`; |
| 10 | +const repo_dir = dir + "repo/"; |
12 | 11 |
|
13 | | - async start() { |
14 | | - this.validate_config(this.config).then(() => this.scan()).catch(console.error) |
15 | | - } |
| 12 | +start(); |
16 | 13 |
|
17 | | - async sleep(ms) { |
18 | | - return new Promise((resolve) => { |
19 | | - setTimeout(resolve, ms); |
20 | | - }) |
21 | | - } |
| 14 | +async function start() { |
22 | 15 |
|
23 | | - get dir() { |
24 | | - return `${path.dirname(require.main.filename).replaceAll("\\", "/")}/`; |
25 | | - } |
| 16 | + validate_config(config) |
| 17 | + |
| 18 | + .then(() => scan()) |
| 19 | + |
| 20 | + .catch(console.error); |
26 | 21 |
|
27 | | - get repo_dir() { |
28 | | - return this.dir + "repo/"; |
29 | | - } |
| 22 | +} |
30 | 23 |
|
31 | | - get_repo_name(url = this.config.repo) { |
32 | | - const match = url.match(/(https?:\/{2}github\.com\/.+?\/([^\/]+))\/?/); |
33 | | - if (match) return match[1]; |
34 | | - else throw "[@] Repository not provided or does not match expected format."; |
35 | | - } |
| 24 | +async function sleep(ms) { |
36 | 25 |
|
37 | | - async get_version(cwd = this.repo_dir, remote = false) { |
38 | | - return this.execute(`git rev-parse --short --verify ${remote ? "origin/" : ""}${this.config.branch}`, { cwd }).catch(console.warn); |
39 | | - } |
| 26 | + return new Promise((resolve) => setTimeout(resolve, ms)); |
40 | 27 |
|
41 | | - async validate_config() { |
42 | | - const repo = this.config.repo.match(/(https?:\/{2}github\.com\/.+?\/([^\/]+))\/?/); |
43 | | - if (!this.config.repo) throw "[@] Github repository not provided!"; |
44 | | - else this.config.repo = repo[1]; |
| 28 | +} |
45 | 29 |
|
46 | | - if (!this.config.branch) throw "[@] Github branch not provided!"; |
47 | | - if (!this.config.start) throw "[@] Start command not provided!"; |
48 | | - if (!this.config.setup) console.warn("[!] Setup command not provided."); |
49 | 30 |
|
50 | | - if (this.config.frequency > 1000) console.warn("[!] The scan frequency should be in seconds. The value provided is VERY HIGH!") |
51 | | - } |
| 31 | +async function get_version(cwd = repo_dir, remote = false) { |
52 | 32 |
|
53 | | - match_version(cwd = this.repo_dir) { |
54 | | - return new Promise(async (resolve, reject) => { |
55 | | - await this.execute("git remote update", { cwd }).catch((res) => console.log("[-] " + res.split("\n")[1].trim())) |
56 | | - const remote = await this.get_version(cwd, true) |
57 | | - const local = await this.get_version(cwd, false) |
58 | | - if (remote === local) resolve("[-] Local repository matches origin.") |
59 | | - else reject("[!] Local repository does not match origin.") |
60 | | - }) |
61 | | - } |
| 33 | + return execute(`git rev-parse --short --verify ${remote ? "origin/" : ""}${config.branch}`, { cwd }); |
62 | 34 |
|
63 | | - match_origin(cwd = this.repo_dir) { |
64 | | - return new Promise((resolve, reject) => { |
65 | | - this.execute("git remote get-url origin", { cwd }).catch(console.error) |
66 | | - .then((origin) => { |
67 | | - if (origin === this.config.repo) resolve("[-] Local repository matches origin.") |
68 | | - else reject("[!] Local repository does not match origin.") |
69 | | - }) |
70 | | - }) |
71 | | - } |
| 35 | +} |
72 | 36 |
|
73 | | - async fork_app(cwd = this.repo_dir) { |
74 | | - if (this.app && !this.app?.killed) throw "[-] App already alive!" |
75 | | - console.log("[.] Preparing environment.") |
76 | | - await this.execute("npm install", { cwd }).catch(console.warn); |
77 | | - console.log("[.] Initialising fork.") |
78 | | - return this.app = fork(cwd, this.config.start.split(/ +/g)); |
79 | | - } |
| 37 | + |
| 38 | +async function validate_config() { |
| 39 | + |
| 40 | + const repo = config.repo.match(/(https?:\/{2}github\.com\/.+?\/([^\/]+))\/?/); |
| 41 | + |
| 42 | + if (!config.repo) throw new Error("[@] Github repository not provided!") |
| 43 | + |
| 44 | + else config.repo = repo[1]; |
| 45 | + |
| 46 | + if (!config.branch) throw new Error("[@] Github branch not provided!"); |
| 47 | + |
| 48 | + if (!config.start) throw new Error("[@] Start command not provided!"); |
| 49 | + |
| 50 | + if (!config.setup) console.warn("[!] Setup command not provided."); |
| 51 | + |
| 52 | + if (config.frequency > 1000) console.warn("[!] The scan frequency should be in seconds. The value provided is VERY HIGH!"); |
| 53 | + |
| 54 | + return true; |
| 55 | +} |
| 56 | + |
| 57 | + |
| 58 | +async function match_version(cwd = repo_dir) { |
| 59 | + |
| 60 | + await execute("git remote update", { cwd }) |
| 61 | + |
| 62 | + .catch((res) => console.log("[-] " + res.split("\n")[1].trim())); |
| 63 | + |
| 64 | + const remote = await get_version(cwd, true), local = await get_version(cwd, false); |
80 | 65 |
|
81 | | - async clone(repo = this.config.repo, cwd = this.dir) { |
82 | | - return this.execute(`git clone ${repo} repo`, { cwd }).then(console.log); |
83 | | - } |
| 66 | + return remote === local; |
| 67 | +} |
| 68 | + |
| 69 | + |
| 70 | +async function match_origin(cwd = repo_dir) { |
| 71 | + |
| 72 | + const origin = await execute("git remote get-url origin", { cwd }) |
| 73 | + |
| 74 | + .catch(console.error); |
| 75 | + |
| 76 | + return config.repo === origin; |
| 77 | + |
| 78 | +} |
| 79 | + |
| 80 | + |
| 81 | +async function fork_app(cwd = repo_dir) { |
| 82 | + |
| 83 | + if (app && !app?.killed) return console.warn("[!] App has already been terminated."); |
| 84 | + |
| 85 | + console.log("[.] Preparing environment."); |
| 86 | + |
| 87 | + await execute("npm install", { cwd }) |
| 88 | + |
| 89 | + .then(() => { |
| 90 | + |
| 91 | + if (config.verbose) console.log("[+] Environment avaialble for use.") |
| 92 | + |
| 93 | + }) |
| 94 | + |
| 95 | + .catch(console.warn); |
| 96 | + |
| 97 | + console.log("[.] Initialising child process."); |
| 98 | + |
| 99 | + return app = fork(cwd, config.start.split(/ +/g)).addListener("spawn", () => console.log("[+] Process spawned successfully.")); |
| 100 | + |
| 101 | +} |
| 102 | + |
| 103 | + |
| 104 | +async function clone(repo = config.repo, cwd = dir) { |
| 105 | + |
| 106 | + return execute(`git clone ${repo} repo`, { cwd }); |
| 107 | + |
| 108 | +} |
| 109 | + |
| 110 | + |
| 111 | +function execute(command, options = {}) { |
| 112 | + |
| 113 | + return new Promise((resolve, reject) => { |
| 114 | + |
| 115 | + if (config.verbose) console.log("Executing:", command) |
| 116 | + |
| 117 | + exec(command, options, |
| 118 | + |
| 119 | + (err, stdout, stderr) => { |
| 120 | + |
| 121 | + if (err) reject(err); |
84 | 122 |
|
85 | | - async execute(command, options = {}) { |
86 | | - return new Promise((resolve, reject) => { |
87 | | - // console.log(`Executing '${command}'...`); |
88 | | - exec(command, options, (err, stdout, stderr) => { |
89 | | - if (err) reject(err) |
90 | 123 | else if (stderr) reject(stderr); |
| 124 | + |
91 | 125 | else resolve(stdout.trim()); |
92 | | - }) |
93 | | - }) |
94 | | - } |
95 | 126 |
|
96 | | - remove_dir(dir = this.repo_dir) { |
97 | | - return new Promise((resolve, reject) => { |
98 | | - if (this.repo_exists(this.repo_dir)) { |
99 | | - rm(dir, { recursive: true, force: true }, (err) => { |
100 | | - if (err) reject(err); |
101 | | - else resolve("[+] Successfully removed existing repository.") |
102 | | - }); |
103 | | - } |
104 | | - else resolve("[!] Repository folder not found.") |
105 | | - }) |
106 | | - } |
| 127 | + }); |
107 | 128 |
|
108 | | - repo_exists(dir = this.repo_dir) { |
109 | | - return existsSync(dir); |
110 | | - } |
| 129 | + }); |
111 | 130 |
|
112 | | - async reset_app() { |
113 | | - if (this.app && !this.app.killed) { |
114 | | - if (this.app.kill()) console.log("[+] App terminated successfully.") |
115 | | - else console.warn("[!] Something went wrong while terminating app.") |
116 | | - } |
117 | | - await this.remove_dir().then(console.log).catch(console.error) |
118 | | - console.log("[.] Cloning up-to-date repository.") |
119 | | - await this.clone(this.config.repo).then(() => console.log("[+] Repository cloned successfully.")).catch(() => {}); |
120 | | - } |
| 131 | +} |
| 132 | + |
| 133 | + |
| 134 | +function remove_dir(dir = repo_dir) { |
| 135 | + |
| 136 | + return new Promise(async (resolve, reject) => { |
| 137 | + |
| 138 | + rm(dir, { recursive: true, force: true }, |
| 139 | + |
| 140 | + (err) => { |
| 141 | + |
| 142 | + if (err) reject(err); |
121 | 143 |
|
122 | | - async scan() { |
123 | | - if (!this.repo_exists()) await this.reset_app(); |
124 | | - else { |
125 | | - await this.match_origin(this.repo_dir) |
126 | | - .catch(async (res) => { |
127 | | - console.log(res); |
128 | | - await this.reset_app(); |
129 | 144 | }) |
130 | | - .then(async () => await this.match_version().catch(async (res) => { |
131 | | - console.warn(res); |
132 | | - await this.reset_app(); |
133 | | - })) |
| 145 | + |
| 146 | + .then(() => resolve(true)) |
| 147 | + |
| 148 | + }) |
| 149 | + |
| 150 | +} |
| 151 | + |
| 152 | + |
| 153 | +function repo_exists(dir = repo_dir) { |
| 154 | + |
| 155 | + return new Promise((resolve) => { |
| 156 | + |
| 157 | + access(dir) |
| 158 | + |
| 159 | + .then(() => resolve(true)) |
| 160 | + |
| 161 | + .catch(() => { |
| 162 | + |
| 163 | + console.warn("[!] Repository folder not found."); |
| 164 | + |
| 165 | + resolve(false); |
| 166 | + |
| 167 | + }); |
| 168 | + |
| 169 | + }) |
| 170 | + |
| 171 | +} |
| 172 | + |
| 173 | + |
| 174 | +async function reset_app() { |
| 175 | + |
| 176 | + if (app && !app.killed) { |
| 177 | + |
| 178 | + console.log("[.] Attempting app termination."); |
| 179 | + |
| 180 | + if (app.kill()) { |
| 181 | + |
| 182 | + if (config.verbose) console.log("[+] App terminated successfully."); |
| 183 | + |
134 | 184 | } |
135 | 185 |
|
136 | | - await this.fork_app().catch(() => {}) |
| 186 | + else throw new Error("[@] Something went wrong while terminating app."); |
137 | 187 |
|
138 | | - return this.sleep(this.config.frequency * 1000).then(() => this.scan()) |
139 | 188 | } |
140 | 189 |
|
141 | | -}() |
| 190 | + if (config.verbose) console.log("[.] Removing remaining files."); |
| 191 | + |
| 192 | + await remove_dir() |
| 193 | + |
| 194 | + .then(() => { |
| 195 | + |
| 196 | + if (config.verbose) console.log("[+] Outdated files removed."); |
| 197 | + |
| 198 | + }) |
| 199 | + |
| 200 | + .catch(new Error); |
| 201 | + |
| 202 | + console.log("[.] Cloning up-to-date repository."); |
| 203 | + |
| 204 | + await clone(config.repo) |
| 205 | + |
| 206 | + .catch(() => { |
| 207 | + |
| 208 | + if (config.verbose) console.log("[+] Repository cloned successfully.") |
| 209 | + |
| 210 | + }) |
| 211 | + |
| 212 | +} |
| 213 | + |
| 214 | + |
| 215 | +async function scan() { |
| 216 | + |
| 217 | + if (!await repo_exists()) await reset_app(); |
| 218 | + |
| 219 | + else if (!await match_origin() || !await match_version()) await reset_app(); |
| 220 | + |
| 221 | + else if (config.verbose) console.debug("[+] Your branch is on the latest commit.") |
| 222 | + |
| 223 | + if (!app || app.killed) await fork_app(); |
| 224 | + |
| 225 | + return sleep(config.frequency * 1000) |
| 226 | + |
| 227 | + .then(() => scan()); |
| 228 | + |
| 229 | +} |
0 commit comments