2024-06-25 18:59:15 +02:00
|
|
|
// AUTOGENERATED COPYRIGHT HEADER START
|
|
|
|
|
// Copyright (C) 2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
|
|
|
|
// AUTOGENERATED COPYRIGHT HEADER END
|
|
|
|
|
const ignoreList = [
|
|
|
|
|
/^\.git$/gi,
|
|
|
|
|
/^cmake\/clang$/gi,
|
|
|
|
|
/^cmake\/version$/gi,
|
|
|
|
|
/^third-party$/gi,
|
|
|
|
|
]
|
|
|
|
|
const sectionStart = "AUTOGENERATED COPYRIGHT HEADER START";
|
|
|
|
|
const sectionEnd = "AUTOGENERATED COPYRIGHT HEADER END";
|
|
|
|
|
const formatStyleList = {
|
|
|
|
|
"#": {
|
|
|
|
|
files: [
|
|
|
|
|
"cmakelists.txt"
|
|
|
|
|
], exts: [
|
|
|
|
|
".clang-tidy",
|
|
|
|
|
".clang-format",
|
|
|
|
|
".cmake",
|
|
|
|
|
".editorconfig",
|
|
|
|
|
".gitignore",
|
|
|
|
|
".gitmodules",
|
|
|
|
|
".yml",
|
|
|
|
|
".sh"
|
|
|
|
|
],
|
|
|
|
|
prepend: [
|
|
|
|
|
`# ${sectionStart}`,
|
|
|
|
|
],
|
|
|
|
|
append: [
|
|
|
|
|
`# ${sectionEnd}`,
|
|
|
|
|
],
|
|
|
|
|
prefix: "# ",
|
|
|
|
|
suffix: "",
|
|
|
|
|
},
|
|
|
|
|
";": {
|
|
|
|
|
files: [
|
|
|
|
|
], exts: [
|
|
|
|
|
".iss",
|
|
|
|
|
".iss.in",
|
2024-07-05 15:19:18 +02:00
|
|
|
".bb",
|
|
|
|
|
".b3d",
|
|
|
|
|
".b2d",
|
|
|
|
|
".bpl"
|
2024-06-25 18:59:15 +02:00
|
|
|
],
|
|
|
|
|
prepend: [
|
|
|
|
|
`; ${sectionStart}`,
|
|
|
|
|
],
|
|
|
|
|
append: [
|
|
|
|
|
`; ${sectionEnd}`,
|
|
|
|
|
],
|
|
|
|
|
prefix: "; ",
|
|
|
|
|
suffix: "",
|
|
|
|
|
},
|
|
|
|
|
"//": {
|
|
|
|
|
files: [
|
|
|
|
|
], exts: [
|
|
|
|
|
".c",
|
|
|
|
|
".c.in",
|
|
|
|
|
".cpp",
|
|
|
|
|
".cpp.in",
|
|
|
|
|
".h",
|
|
|
|
|
".h.in",
|
|
|
|
|
".hpp",
|
|
|
|
|
".hpp.in",
|
|
|
|
|
".js",
|
|
|
|
|
".rc",
|
|
|
|
|
".rc.in",
|
|
|
|
|
".effect"
|
|
|
|
|
],
|
|
|
|
|
prepend: [
|
|
|
|
|
`// ${sectionStart}`,
|
|
|
|
|
],
|
|
|
|
|
append: [
|
|
|
|
|
`// ${sectionEnd}`,
|
|
|
|
|
],
|
|
|
|
|
prefix: "// ",
|
|
|
|
|
suffix: "",
|
|
|
|
|
},
|
|
|
|
|
"<!---->": {
|
|
|
|
|
files: [
|
|
|
|
|
], exts: [
|
|
|
|
|
".htm",
|
|
|
|
|
".htm.in",
|
|
|
|
|
".html",
|
|
|
|
|
".html.in",
|
|
|
|
|
".xml",
|
|
|
|
|
".xml.in",
|
|
|
|
|
".plist",
|
|
|
|
|
".plist.in",
|
|
|
|
|
".pkgproj",
|
|
|
|
|
".pkgproj.in",
|
|
|
|
|
".md"
|
|
|
|
|
],
|
|
|
|
|
prepend: [
|
|
|
|
|
`<!-- ${sectionStart} -->`,
|
|
|
|
|
],
|
|
|
|
|
append: [
|
|
|
|
|
`<!-- ${sectionEnd} -->`,
|
|
|
|
|
],
|
|
|
|
|
prefix: "<!-- ",
|
|
|
|
|
suffix: " -->",
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
const commitFormat="%aI|%aN <%aE>"
|
|
|
|
|
const debug = false;
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------
|
|
|
|
|
// End of easily modified area, best not touch things beyond here.
|
|
|
|
|
// --------------------------------------------------------------------------------
|
|
|
|
|
const CHILD_PROCESS = require("node:child_process");
|
|
|
|
|
const PROCESS = require("node:process");
|
|
|
|
|
const PATH = require("node:path");
|
|
|
|
|
const FS = require("node:fs");
|
|
|
|
|
const FSPROMISES = require("node:fs/promises");
|
|
|
|
|
const OS = require("os");
|
2024-07-05 15:19:18 +02:00
|
|
|
const READLINE = require('node:readline');
|
2024-06-25 18:59:15 +02:00
|
|
|
|
|
|
|
|
if (!debug)
|
|
|
|
|
console.debug = function() {}
|
|
|
|
|
|
|
|
|
|
class RateLimiter {
|
|
|
|
|
constructor(limit) {
|
|
|
|
|
Object.defineProperty(this, "_maximum", {
|
|
|
|
|
enumerable: true,
|
|
|
|
|
configurable: true,
|
|
|
|
|
writable: true,
|
|
|
|
|
value: 0
|
|
|
|
|
});
|
|
|
|
|
Object.defineProperty(this, "_available", {
|
|
|
|
|
enumerable: true,
|
|
|
|
|
configurable: true,
|
|
|
|
|
writable: true,
|
|
|
|
|
value: 0
|
|
|
|
|
});
|
|
|
|
|
Object.defineProperty(this, "_instances", {
|
|
|
|
|
enumerable: true,
|
|
|
|
|
configurable: true,
|
|
|
|
|
writable: true,
|
|
|
|
|
value: void 0
|
|
|
|
|
});
|
|
|
|
|
if (!limit) {
|
|
|
|
|
this._maximum = Math.ceil(Math.max(1, OS.cpus().length / 3 * 2));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
this._maximum = limit;
|
|
|
|
|
}
|
|
|
|
|
this._available = this._maximum;
|
|
|
|
|
this._instances = [];
|
|
|
|
|
}
|
|
|
|
|
async queue(executor, ...args) {
|
|
|
|
|
while (this._available == 0) {
|
|
|
|
|
await Promise.race(this._instances);
|
|
|
|
|
}
|
|
|
|
|
--this._available;
|
|
|
|
|
const data = {};
|
|
|
|
|
data.task = new Promise((resolve, reject) => {
|
|
|
|
|
try {
|
|
|
|
|
if (executor.constructor.name == "Promise") {
|
|
|
|
|
executor.then((res) => {
|
|
|
|
|
resolve(res);
|
|
|
|
|
}, (err) => {
|
|
|
|
|
reject(err);
|
|
|
|
|
});
|
|
|
|
|
} else if (executor.constructor.name == "AsyncFunction") {
|
|
|
|
|
executor(...args).then((res) => {
|
|
|
|
|
resolve(res);
|
|
|
|
|
}, (err) => {
|
|
|
|
|
reject(err);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
resolve(executor(...args));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (ex) {
|
|
|
|
|
console.error(executor, executor.toString())
|
|
|
|
|
reject(ex);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
data.solver = data.task.finally(() => {
|
|
|
|
|
const taskIndex = this._instances.indexOf(data.task);
|
|
|
|
|
if (taskIndex >= 0) {
|
|
|
|
|
this._instances.splice(taskIndex, 1);
|
|
|
|
|
}
|
|
|
|
|
const solverIndex = this._instances.indexOf(data.solver);
|
|
|
|
|
if (solverIndex >= 0) {
|
|
|
|
|
this._instances.splice(solverIndex, 1);
|
|
|
|
|
}
|
|
|
|
|
++this._available;
|
|
|
|
|
});
|
|
|
|
|
this._instances.push(data.solver);
|
|
|
|
|
return await data.solver;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let abortAllWork = false;
|
2024-07-05 15:19:18 +02:00
|
|
|
let gitRL = new RateLimiter(3);
|
2024-06-25 18:59:15 +02:00
|
|
|
let workRL = new RateLimiter();
|
|
|
|
|
let gitCurrentFiles;
|
|
|
|
|
let gitUserName;
|
|
|
|
|
let gitUserMail;
|
2024-07-05 15:19:18 +02:00
|
|
|
let gitSubmodules;
|
2024-06-25 18:59:15 +02:00
|
|
|
let gitDate = (new Date()).toISOString();
|
|
|
|
|
|
|
|
|
|
/** Run a process asynchronously, returning an array of messages.
|
|
|
|
|
*
|
|
|
|
|
* @param {*} path
|
|
|
|
|
* @param {*} args
|
|
|
|
|
* @param {*} options
|
|
|
|
|
* @returns [ErrorCode, [{type, data, text}, ...]]
|
|
|
|
|
*/
|
|
|
|
|
async function runProcessAsync(path, args, options) {
|
|
|
|
|
console.debug(arguments.callee.name, Array.from(arguments));
|
|
|
|
|
return await new Promise((resolve, reject) => {
|
|
|
|
|
let messages = [];
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
let proc = CHILD_PROCESS.spawn(path, args, Object.assign({
|
|
|
|
|
"cwd": PROCESS.cwd(),
|
|
|
|
|
"encoding": "utf8",
|
|
|
|
|
}, options));
|
|
|
|
|
|
|
|
|
|
let pushOutput = function(type, chunk) {
|
|
|
|
|
if (!chunk) return;
|
|
|
|
|
|
|
|
|
|
let lastMessage = undefined;
|
|
|
|
|
if (messages.length == 0 || messages[messages.length - 1].type !== type) {
|
|
|
|
|
lastMessage = {
|
|
|
|
|
type: type,
|
|
|
|
|
chunks: [],
|
|
|
|
|
length: 0,
|
|
|
|
|
};
|
|
|
|
|
messages.push(lastMessage);
|
|
|
|
|
} else {
|
|
|
|
|
lastMessage = messages[messages.length - 1];
|
|
|
|
|
}
|
|
|
|
|
lastMessage.length += chunk.length;
|
|
|
|
|
lastMessage.chunks.push(chunk);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
proc.stderr.on('data', (chunk) => { pushOutput("error", chunk); });
|
|
|
|
|
proc.stderr.on('close', () => { pushOutput("output", proc.stderr.read()); });
|
|
|
|
|
proc.stdout.on('data', (chunk) => { pushOutput("output", chunk); });
|
|
|
|
|
proc.stdout.on('close', () => { pushOutput("output", proc.stdout.read()); });
|
|
|
|
|
proc.on('error', (err) => {
|
|
|
|
|
reject(err);
|
|
|
|
|
})
|
|
|
|
|
proc.on('exit', (code) => {
|
|
|
|
|
// Merge chunks of messages into a single message.
|
|
|
|
|
for (let message of messages) {
|
|
|
|
|
message.data = Buffer.alloc(message.length);
|
|
|
|
|
let offset = 0;
|
|
|
|
|
for (let chunk of message.chunks) {
|
|
|
|
|
Buffer.from(chunk).copy(message.data, offset, 0);
|
|
|
|
|
offset += chunk.byteLength;
|
|
|
|
|
}
|
|
|
|
|
message.chunks = undefined;
|
|
|
|
|
message.length = undefined;
|
|
|
|
|
message.text = message.data.toString();
|
|
|
|
|
}
|
|
|
|
|
resolve([code, messages]);
|
|
|
|
|
});
|
|
|
|
|
} catch (ex) {
|
|
|
|
|
reject(ex);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function git_isIgnored(path) {
|
|
|
|
|
console.debug(arguments.callee.name, Array.from(arguments));
|
|
|
|
|
let rpath = PATH.relative(process.cwd(), path).replaceAll(PATH.sep, PATH.posix.sep);
|
2024-07-05 15:19:18 +02:00
|
|
|
|
|
|
|
|
// Check manual ignore list.
|
2024-06-25 18:59:15 +02:00
|
|
|
for (let ignore of ignoreList) {
|
|
|
|
|
if (ignore instanceof RegExp) {
|
|
|
|
|
if (ignore.global) {
|
|
|
|
|
let matches = rpath.matchAll(ignore);
|
|
|
|
|
for (let match of matches) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (rpath.match(ignore) !== null) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (rpath.startsWith(ignore)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-05 15:19:18 +02:00
|
|
|
// Check if this happens to be (in) a submodule.
|
|
|
|
|
let modules = await git_subModules()
|
|
|
|
|
for (let module of modules) {
|
|
|
|
|
if (rpath.startsWith(module)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Finally check if git ignores this file.
|
2024-06-25 18:59:15 +02:00
|
|
|
let result = await gitRL.queue(runProcessAsync, "git", ["check-ignore", path], {});
|
|
|
|
|
return (result[0] == 0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function git_getCurrentAuthor() {
|
|
|
|
|
console.debug(arguments.callee.name, Array.from(arguments));
|
|
|
|
|
if (!gitUserName) {
|
|
|
|
|
let result = await gitRL.queue(runProcessAsync, "git", ["config", "user.name"], {});
|
|
|
|
|
gitUserName = result[1][0].text.split('\n')[0];
|
|
|
|
|
}
|
|
|
|
|
if (!gitUserMail) {
|
|
|
|
|
let result = await gitRL.queue(runProcessAsync, "git", ["config", "user.email"], {});
|
|
|
|
|
gitUserMail = result[1][0].text.split('\n')[0];
|
|
|
|
|
}
|
|
|
|
|
return commitFormat.replace("%aI", gitDate).replace("%aN", gitUserName).replace("%aE", gitUserMail);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-05 15:19:18 +02:00
|
|
|
async function git_subModules() {
|
|
|
|
|
if (!gitSubmodules) {
|
|
|
|
|
gitSubmodules = new Set();
|
|
|
|
|
|
|
|
|
|
let gitmodules = PATH.join(process.cwd(), ".gitmodules");
|
|
|
|
|
if ((await FSPROMISES.stat(gitmodules)).isFile()) {
|
|
|
|
|
let gmfs = FS.createReadStream(gitmodules);
|
|
|
|
|
const rl = READLINE.createInterface({
|
|
|
|
|
input: gmfs,
|
|
|
|
|
crlfDelay: Infinity
|
|
|
|
|
});
|
|
|
|
|
for await(const line of rl) {
|
|
|
|
|
let parts = line.trim().split("=");
|
|
|
|
|
if (parts.length > 1) {
|
|
|
|
|
if(parts[0].trim() == "path") {
|
|
|
|
|
gitSubmodules.add(parts[1].trim());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return gitSubmodules;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-25 18:59:15 +02:00
|
|
|
async function git_isInCurrentCommit(file) {
|
|
|
|
|
console.debug(arguments.callee.name, Array.from(arguments));
|
|
|
|
|
if (!gitCurrentFiles) {
|
|
|
|
|
let files = [];
|
|
|
|
|
let result = await gitRL.queue(runProcessAsync, "git", ["diff", "--name-only", "--cached"], {});
|
|
|
|
|
if (result[0] != 0) {
|
|
|
|
|
let output = "";
|
|
|
|
|
for (let message of result[1]) {
|
|
|
|
|
output += message.text;
|
|
|
|
|
}
|
|
|
|
|
throw new Error(output);
|
|
|
|
|
} else {
|
|
|
|
|
lines = "";
|
|
|
|
|
for (let message of result[1]) {
|
|
|
|
|
if (message.type == "output")
|
|
|
|
|
lines += message.text;
|
|
|
|
|
}
|
|
|
|
|
files = lines.split(lines.indexOf("\r\n") >= 0 ? "\r\n" : "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gitCurrentFiles = new Set(files);
|
|
|
|
|
for (let file of files) {
|
|
|
|
|
if (file.length == 0) continue;
|
|
|
|
|
file = PATH.relative(PROCESS.cwd(), PATH.resolve(file));
|
|
|
|
|
gitCurrentFiles.add(file);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Normalize file given to us.
|
|
|
|
|
file = PATH.relative(PROCESS.cwd(), PATH.resolve(file));
|
|
|
|
|
|
|
|
|
|
// Check and return if it's in here.
|
|
|
|
|
return gitCurrentFiles.has(file);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function git_retrieveAuthors(file) {
|
|
|
|
|
console.debug(arguments.callee.name, Array.from(arguments));
|
|
|
|
|
// git --no-pager log --date-order --reverse "--format=format:%aI|%aN <%aE>" -- file
|
|
|
|
|
let lines = [];
|
|
|
|
|
let result = await gitRL.queue(runProcessAsync, "git", ["--no-pager", "log", "--follow", `--format=format:${commitFormat}`, "--", file], {});
|
|
|
|
|
if (result[0] != 0) {
|
|
|
|
|
let output = "";
|
|
|
|
|
for (let message of result[1]) {
|
|
|
|
|
output += message.text;
|
|
|
|
|
}
|
|
|
|
|
throw new Error(output);
|
|
|
|
|
} else {
|
|
|
|
|
let buffered = "";
|
|
|
|
|
for (let message of result[1]) {
|
|
|
|
|
if (message.type == "output")
|
|
|
|
|
buffered += message.text;
|
|
|
|
|
}
|
|
|
|
|
lines = buffered.split(buffered.indexOf("\r\n") >= 0 ? "\r\n" : "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (await git_isInCurrentCommit(file)) {
|
|
|
|
|
lines.push(await git_getCurrentAuthor());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let authors = new Map();
|
|
|
|
|
for (let line of lines) {
|
|
|
|
|
let [date, name] = line.split("|");
|
|
|
|
|
|
|
|
|
|
let author = authors.get(name);
|
|
|
|
|
if (author) {
|
|
|
|
|
let dt = new Date(date)
|
|
|
|
|
if (author.from > dt)
|
|
|
|
|
author.from = dt;
|
|
|
|
|
if (author.to < dt)
|
|
|
|
|
author.to = dt;
|
|
|
|
|
} else {
|
|
|
|
|
authors.set(name, {
|
|
|
|
|
from: new Date(date),
|
|
|
|
|
to: new Date(date),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return authors;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function generateCopyright(file) {
|
|
|
|
|
console.debug(arguments.callee.name, Array.from(arguments));
|
|
|
|
|
let authors = await git_retrieveAuthors(file)
|
|
|
|
|
authors = new Map([...authors].sort((a, b) => {
|
|
|
|
|
if (a[1].from < b[1].from) {
|
|
|
|
|
return -1;
|
|
|
|
|
} else if (a[1].from > b[1].from) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
|
|
|
|
let lines = [];
|
|
|
|
|
for (let entry of authors) {
|
|
|
|
|
let from = entry[1].from.getUTCFullYear();
|
|
|
|
|
let to = entry[1].to.getUTCFullYear();
|
|
|
|
|
lines.push(`Copyright (C) ${from != to ? `${from}-${to}` : to} ${entry[0]}`);
|
|
|
|
|
}
|
|
|
|
|
return lines;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function makeHeader(file, copyright) {
|
|
|
|
|
console.debug(arguments.callee.name, Array.from(arguments));
|
|
|
|
|
let file_name = PATH.basename(file).toLocaleLowerCase();
|
|
|
|
|
let file_exts = file_name.substring(file_name.indexOf("."));
|
|
|
|
|
|
|
|
|
|
let styles = formatStyleList;
|
|
|
|
|
|
|
|
|
|
for (let key in styles) {
|
|
|
|
|
let style = [key, styles[key]];
|
|
|
|
|
if (style[1].files.includes(file_name)
|
|
|
|
|
|| style[1].files.includes(file)
|
|
|
|
|
|| style[1].exts.includes(file_exts)) {
|
|
|
|
|
let header = [];
|
|
|
|
|
header.push(...style[1].prepend);
|
|
|
|
|
for (let line of copyright) {
|
|
|
|
|
header.push(`${style[1].prefix}${line}${style[1].suffix}`);
|
|
|
|
|
}
|
|
|
|
|
header.push(...style[1].append);
|
|
|
|
|
return header;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new Error("Unrecognized file format.")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function updateFile(file) {
|
|
|
|
|
console.debug(arguments.callee.name, Array.from(arguments));
|
|
|
|
|
await workRL.queue(async () => {
|
|
|
|
|
try {
|
|
|
|
|
if (abortAllWork) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copyright information.
|
|
|
|
|
let copyright = await generateCopyright(file);
|
|
|
|
|
let header = undefined;
|
|
|
|
|
try {
|
|
|
|
|
header = makeHeader(file, copyright);
|
|
|
|
|
} catch (ex) {
|
|
|
|
|
console.log(`Skipping file '${file}': ${ex.message}`);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
console.log(`Updating file '${file}'...`);
|
|
|
|
|
|
2024-07-05 15:19:18 +02:00
|
|
|
// ToDo: Do we actually need to read the file first, or can we use the same rw stream?
|
2024-06-25 18:59:15 +02:00
|
|
|
// File contents.
|
2024-07-05 15:19:18 +02:00
|
|
|
let contentBuf = await FSPROMISES.readFile(file);
|
|
|
|
|
let eol = contentBuf.indexOf("\r\n") != -1 ? "\r\n" : "\n";
|
|
|
|
|
let headerBuf = Buffer.from(header.join(eol) + eol, "utf8");
|
2024-06-25 18:59:15 +02:00
|
|
|
|
|
|
|
|
// Find the starting point.
|
2024-07-05 15:19:18 +02:00
|
|
|
let startHeader = contentBuf.indexOf(Buffer.from(header[0], "utf8"));
|
|
|
|
|
if (startHeader != -1) {
|
|
|
|
|
//startHeader = contentBuf.lastIndexOf(eolBuf, startHeader);
|
|
|
|
|
//startHeader += eolb.byteLength;
|
|
|
|
|
}
|
|
|
|
|
console.log(sectionStart, startHeader);
|
2024-06-25 18:59:15 +02:00
|
|
|
|
|
|
|
|
// Find the ending point.
|
2024-07-05 15:19:18 +02:00
|
|
|
let endHeader = contentBuf.lastIndexOf(Buffer.from(header[header.length - 1], "utf8"));
|
|
|
|
|
if (endHeader != -1) {
|
|
|
|
|
endHeader += Buffer.from(header[header.length - 1], "utf8").byteLength;
|
|
|
|
|
endHeader += Buffer.byteLength(eol, "utf8");
|
|
|
|
|
}
|
|
|
|
|
console.log(sectionEnd, endHeader);
|
2024-06-25 18:59:15 +02:00
|
|
|
|
2024-07-05 15:19:18 +02:00
|
|
|
// Last check for early-exit here.
|
2024-06-25 18:59:15 +02:00
|
|
|
if (abortAllWork) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-07-05 15:19:18 +02:00
|
|
|
|
2024-06-25 18:59:15 +02:00
|
|
|
let fd = await FSPROMISES.open(file, "w");
|
2024-07-05 15:19:18 +02:00
|
|
|
if (startHeader == -1 || (endHeader < startHeader)) {
|
|
|
|
|
fd.write(headerBuf, 0, null, 0);
|
|
|
|
|
fd.write(contentBuf, 0, null, headerBuf.byteLength);
|
|
|
|
|
} else {
|
2024-06-25 18:59:15 +02:00
|
|
|
let pos = 0;
|
2024-07-05 15:19:18 +02:00
|
|
|
|
2024-06-25 18:59:15 +02:00
|
|
|
if (startHeader > 0) {
|
2024-07-05 15:19:18 +02:00
|
|
|
fd.write(contentBuf, 0, startHeader - Buffer.byteLength(eol, "utf8").byteLength, 0);
|
2024-06-25 18:59:15 +02:00
|
|
|
pos += startHeader;
|
|
|
|
|
}
|
2024-07-05 15:19:18 +02:00
|
|
|
fd.write(headerBuf, 0, null, pos); pos += headerBuf.byteLength;
|
|
|
|
|
fd.write(contentBuf, endHeader, null, pos);
|
2024-06-25 18:59:15 +02:00
|
|
|
}
|
|
|
|
|
await fd.close();
|
|
|
|
|
} catch (ex) {
|
|
|
|
|
console.error(`Error processing '${file}'!: ${ex}`);
|
|
|
|
|
abortAllWork = true;
|
|
|
|
|
PROCESS.exitCode = 1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function scanPath(path) {
|
|
|
|
|
console.debug(arguments.callee.name, Array.from(arguments));
|
2024-07-05 15:19:18 +02:00
|
|
|
|
2024-06-25 18:59:15 +02:00
|
|
|
// Abort here if the user aborted the process, or if the path is ignored.
|
|
|
|
|
if (abortAllWork) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-05 15:19:18 +02:00
|
|
|
console.log(`Scanning path '${path}'...`);
|
|
|
|
|
|
2024-06-25 18:59:15 +02:00
|
|
|
let promises = [];
|
|
|
|
|
|
|
|
|
|
await workRL.queue(async () => {
|
|
|
|
|
let files = await FSPROMISES.readdir(path, { "withFileTypes": true });
|
|
|
|
|
for (let file of files) {
|
|
|
|
|
if (abortAllWork) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let fullname = PATH.join(path, file.name);
|
|
|
|
|
if (await git_isIgnored(fullname)) {
|
|
|
|
|
console.log(`Ignoring path '${fullname}'...`);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (file.isDirectory()) {
|
|
|
|
|
promises.push(scanPath(fullname));
|
|
|
|
|
} else {
|
|
|
|
|
promises.push(updateFile(fullname));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await Promise.all(promises);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(async function () {
|
|
|
|
|
PROCESS.on("SIGINT", () => {
|
|
|
|
|
abortAllWork = true;
|
|
|
|
|
PROCESS.exitCode = 1;
|
|
|
|
|
console.log("Sanely aborting all pending work...");
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const root_path = PROCESS.cwd();
|
|
|
|
|
let is_git_directory = false;
|
|
|
|
|
|
|
|
|
|
console.debug(root_path, PROCESS.argv, PROCESS.execArgv);
|
|
|
|
|
|
|
|
|
|
var args = PROCESS.argv.slice(2);
|
2024-07-05 15:19:18 +02:00
|
|
|
let promises = new Array();
|
2024-06-25 18:59:15 +02:00
|
|
|
while (args.length > 0) {
|
|
|
|
|
// Try to place ourselves where git actually is.
|
|
|
|
|
while (!is_git_directory) {
|
|
|
|
|
if (abortAllWork) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let entries = await FSPROMISES.readdir(PROCESS.cwd());
|
2024-07-05 15:19:18 +02:00
|
|
|
if (entries.includes(".git") && ((await FSPROMISES.stat(PATH.join(PROCESS.cwd(), ".git"))).isDirectory())) {
|
2024-06-25 18:59:15 +02:00
|
|
|
console.log(`Found .git at '${process.cwd()}'.`);
|
|
|
|
|
is_git_directory = true;
|
|
|
|
|
} else {
|
|
|
|
|
PROCESS.chdir(PATH.resolve(PATH.join(PROCESS.cwd(), "..")));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If that failed, we just exit now.
|
|
|
|
|
if (!is_git_directory) {
|
|
|
|
|
console.error("Failed to figure out where git is, is this even a repository?");
|
|
|
|
|
PROCESS.exitCode = 1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-05 15:19:18 +02:00
|
|
|
if (abortAllWork) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
git_getCurrentAuthor();
|
|
|
|
|
|
2024-06-25 18:59:15 +02:00
|
|
|
// Then proceed with normal work.
|
|
|
|
|
let path = PATH.normalize(PATH.relative(process.cwd(), PATH.resolve(root_path, args[0])));
|
|
|
|
|
|
|
|
|
|
// If we're handed a path, scan said path, otherwise update the file itself.
|
|
|
|
|
let pathStat = await FSPROMISES.stat(path);
|
|
|
|
|
if (pathStat) {
|
|
|
|
|
if (await git_isIgnored(path)) {
|
|
|
|
|
console.log(`Ignoring path '${path}'...`);
|
|
|
|
|
} else if(pathStat.isDirectory()) {
|
2024-07-05 15:19:18 +02:00
|
|
|
//await scanPath(path);
|
|
|
|
|
promises.push(scanPath(path));
|
2024-06-25 18:59:15 +02:00
|
|
|
} else {
|
2024-07-05 15:19:18 +02:00
|
|
|
//await updateFile(path);
|
|
|
|
|
promises.push(updateFile(path));
|
2024-06-25 18:59:15 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Slice off the first argument and continue.
|
|
|
|
|
args = args.slice(1);
|
|
|
|
|
}
|
2024-07-05 15:19:18 +02:00
|
|
|
await Promise.all(promises);
|
2024-06-25 18:59:15 +02:00
|
|
|
console.log("Done");
|
|
|
|
|
})();
|