saturn-core-cli 0.0.8 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/commands/publish.js +29 -16
- package/lib/changelog.js +114 -0
- package/mapper.js +36 -3
- package/package.json +2 -1
package/commands/publish.js
CHANGED
|
@@ -15,29 +15,41 @@ const SATURN_API_TOKEN =
|
|
|
15
15
|
var exec = require("child-process-promise").exec;
|
|
16
16
|
var spawn = require("child-process-promise").spawn;
|
|
17
17
|
const semverInc = require("semver/functions/inc");
|
|
18
|
-
|
|
19
|
-
try {
|
|
20
|
-
exemptions = require(saturnignore);
|
|
21
|
-
} catch (e) {
|
|
22
|
-
exemptions = [];
|
|
23
|
-
}
|
|
18
|
+
const { buildChangelog } = require("../lib/changelog");
|
|
24
19
|
|
|
25
20
|
async function action(release = "patch", options) {
|
|
26
21
|
try {
|
|
27
|
-
|
|
22
|
+
try {
|
|
23
|
+
exemptions = (await import(saturnignore)).default;
|
|
24
|
+
} catch (e) {
|
|
25
|
+
console.log(e.message, e);
|
|
26
|
+
exemptions = [];
|
|
27
|
+
}
|
|
28
28
|
const packageObj = await fs.readJson(`package.json`);
|
|
29
|
-
|
|
30
|
-
//let version = packageObj.version;
|
|
31
|
-
//}
|
|
29
|
+
console.log("Exemptions", exemptions);
|
|
32
30
|
let version = semverInc(packageObj.version, release);
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
if (!version) {
|
|
32
|
+
throw new Error(`Invalid release type "${release}"`);
|
|
33
|
+
}
|
|
34
|
+
let changelog = options.changelog
|
|
35
|
+
? await buildChangelog(packageObj.version)
|
|
36
|
+
: "";
|
|
37
|
+
let description = [options.message, changelog].filter(Boolean).join("\n\n");
|
|
38
|
+
// The map checksums package.json, so the bump must land first; a dry run leaves it alone.
|
|
39
|
+
if (!options.dry) {
|
|
40
|
+
packageObj.version = version;
|
|
41
|
+
await fs.writeJson(`package.json`, packageObj, { spaces: 2 });
|
|
42
|
+
}
|
|
35
43
|
let map = await new mapper({ exemptions: exemptions }).createMap("./");
|
|
44
|
+
console.log("MAP", map);
|
|
36
45
|
if (options.dry) {
|
|
46
|
+
console.log(`DESCRIPTION for ${version}\n${description}`);
|
|
37
47
|
return;
|
|
38
48
|
}
|
|
49
|
+
|
|
39
50
|
let core = await getCoreId(packageObj.saturn.core);
|
|
40
|
-
await createInstance(map, core.id, version,
|
|
51
|
+
await createInstance(map, core.id, version, description);
|
|
52
|
+
console.log("DONE");
|
|
41
53
|
await doGitAdd();
|
|
42
54
|
await doGitCommit("SC Publish");
|
|
43
55
|
await doGitPush();
|
|
@@ -206,7 +218,7 @@ async function createInstance(map, core, version, message) {
|
|
|
206
218
|
);
|
|
207
219
|
return response.data.data;
|
|
208
220
|
} catch (e) {
|
|
209
|
-
console.log("ERROR", e.message);
|
|
221
|
+
console.log("ERROR", e.message, e);
|
|
210
222
|
}
|
|
211
223
|
}
|
|
212
224
|
|
|
@@ -215,8 +227,9 @@ function command(prog) {
|
|
|
215
227
|
program = prog;
|
|
216
228
|
program
|
|
217
229
|
.command("publish [release]", { isDefault: true })
|
|
218
|
-
.option("-d, --dry
|
|
219
|
-
.option("-m, --message", "Description of the update", "")
|
|
230
|
+
.option("-d, --dry", "Dry run: print the map and description without publishing", false)
|
|
231
|
+
.option("-m, --message <text>", "Description of the update", "")
|
|
232
|
+
.option("--no-changelog", "Leave the commit changelog out of the description")
|
|
220
233
|
.description("Publish Saturn Version")
|
|
221
234
|
.action(action);
|
|
222
235
|
}
|
package/lib/changelog.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
var execFile = require("child-process-promise").execFile;
|
|
2
|
+
|
|
3
|
+
// Saturn Instance descriptions are stored by the manager; keep them to a readable size.
|
|
4
|
+
const MAX_LENGTH = 20000;
|
|
5
|
+
const MAX_LISTED_FILES = 10;
|
|
6
|
+
const PUBLISH_SUBJECT = /^"?SC Publish"?$/;
|
|
7
|
+
const TRAILER = /^(Co-Authored-By|Signed-off-by):/i;
|
|
8
|
+
|
|
9
|
+
async function git(args) {
|
|
10
|
+
let result = await execFile("git", args, { maxBuffer: 10 * 1024 * 1024 });
|
|
11
|
+
return result.stdout;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function tagExists(tag) {
|
|
15
|
+
try {
|
|
16
|
+
await git(["rev-parse", "-q", "--verify", `refs/tags/${tag}`]);
|
|
17
|
+
return true;
|
|
18
|
+
} catch (e) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function resolveBaseTag(previousVersion) {
|
|
24
|
+
if (previousVersion && (await tagExists(previousVersion))) {
|
|
25
|
+
return previousVersion;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
return (await git(["describe", "--tags", "--abbrev=0"])).trim();
|
|
29
|
+
} catch (e) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function readCommits(base) {
|
|
35
|
+
let output = await git([
|
|
36
|
+
"log",
|
|
37
|
+
"--no-merges",
|
|
38
|
+
"--reverse",
|
|
39
|
+
"--format=%h%x1f%s%x1f%b%x1e",
|
|
40
|
+
`${base}..HEAD`,
|
|
41
|
+
]);
|
|
42
|
+
return output
|
|
43
|
+
.split("\x1e")
|
|
44
|
+
.map((record) => record.replace(/^\n/, ""))
|
|
45
|
+
.filter((record) => record.trim())
|
|
46
|
+
.map((record) => {
|
|
47
|
+
let [hash, subject, body = ""] = record.split("\x1f");
|
|
48
|
+
return { hash, subject: subject.trim(), body };
|
|
49
|
+
})
|
|
50
|
+
.filter((commit) => !PUBLISH_SUBJECT.test(commit.subject));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function formatCommit(commit) {
|
|
54
|
+
let lines = commit.body.split("\n").filter((line) => !TRAILER.test(line.trim()));
|
|
55
|
+
while (lines.length && !lines[lines.length - 1].trim()) {
|
|
56
|
+
lines.pop();
|
|
57
|
+
}
|
|
58
|
+
while (lines.length && !lines[0].trim()) {
|
|
59
|
+
lines.shift();
|
|
60
|
+
}
|
|
61
|
+
let body = lines.map((line) => (line.trim() ? ` ${line}` : "")).join("\n");
|
|
62
|
+
return body ? `- ${commit.subject}\n${body}` : `- ${commit.subject}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function uncommittedFiles() {
|
|
66
|
+
let output = await git(["status", "--porcelain"]);
|
|
67
|
+
return output
|
|
68
|
+
.split("\n")
|
|
69
|
+
.filter((line) => line.trim())
|
|
70
|
+
.map((line) => line.slice(3).split(" -> ").pop().replace(/^"|"$/g, ""))
|
|
71
|
+
.filter((file) => file !== "package.json");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function formatUncommitted(files) {
|
|
75
|
+
let listed = files.slice(0, MAX_LISTED_FILES).join(", ");
|
|
76
|
+
let more = files.length > MAX_LISTED_FILES ? ` +${files.length - MAX_LISTED_FILES} more` : "";
|
|
77
|
+
return `- Uncommitted changes in: ${listed}${more}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Builds markdown release notes from the commits since the previous published tag, for the
|
|
82
|
+
* Saturn Instance description. Returns "" when there is no tag to compare against.
|
|
83
|
+
*/
|
|
84
|
+
async function buildChangelog(previousVersion) {
|
|
85
|
+
let base = await resolveBaseTag(previousVersion);
|
|
86
|
+
if (!base) {
|
|
87
|
+
console.log("No previous git tag found, skipping changelog");
|
|
88
|
+
return "";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let entries = (await readCommits(base)).map(formatCommit);
|
|
92
|
+
let files = await uncommittedFiles();
|
|
93
|
+
if (files.length) {
|
|
94
|
+
entries.push(formatUncommitted(files));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let kept = [];
|
|
98
|
+
let length = 0;
|
|
99
|
+
for (let entry of entries) {
|
|
100
|
+
if (length + entry.length > MAX_LENGTH) {
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
kept.push(entry);
|
|
104
|
+
length += entry.length + 1;
|
|
105
|
+
}
|
|
106
|
+
if (kept.length < entries.length) {
|
|
107
|
+
kept.push(`…and ${entries.length - kept.length} more commits`);
|
|
108
|
+
}
|
|
109
|
+
return kept.join("\n");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
module.exports = {
|
|
113
|
+
buildChangelog: buildChangelog,
|
|
114
|
+
};
|
package/mapper.js
CHANGED
|
@@ -2,23 +2,54 @@ let klawSync = require("klaw-sync");
|
|
|
2
2
|
let _ = require("lodash");
|
|
3
3
|
let path = require("path");
|
|
4
4
|
let crypto = require("crypto");
|
|
5
|
-
let fs = require("fs");
|
|
5
|
+
let fs = require("graceful-fs");
|
|
6
6
|
|
|
7
7
|
let exemptions = [];
|
|
8
8
|
let dir_path = ".";
|
|
9
9
|
|
|
10
|
+
/* function generateChecksum(file) {
|
|
11
|
+
return new Promise(function (resolve, reject) {
|
|
12
|
+
try {
|
|
13
|
+
var hash = crypto.createHash("md5");
|
|
14
|
+
var stream = fs.createReadStream(file);
|
|
15
|
+
stream.on("data", function (data) {
|
|
16
|
+
hash.update(data);
|
|
17
|
+
});
|
|
18
|
+
stream.on("end", function () {
|
|
19
|
+
var sha = hash.digest("hex");
|
|
20
|
+
resolve(sha);
|
|
21
|
+
});
|
|
22
|
+
} catch (e) {
|
|
23
|
+
reject(e);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
} */
|
|
10
27
|
function generateChecksum(file) {
|
|
11
28
|
return new Promise(function (resolve, reject) {
|
|
12
29
|
try {
|
|
13
30
|
var hash = crypto.createHash("md5");
|
|
14
31
|
var stream = fs.createReadStream(file);
|
|
32
|
+
|
|
15
33
|
stream.on("data", function (data) {
|
|
16
34
|
hash.update(data);
|
|
17
35
|
});
|
|
36
|
+
|
|
37
|
+
// Listen for the 'end' event to finish the hash calculation
|
|
18
38
|
stream.on("end", function () {
|
|
19
39
|
var sha = hash.digest("hex");
|
|
40
|
+
stream.close(); // Manually close the stream (if necessary)
|
|
20
41
|
resolve(sha);
|
|
21
42
|
});
|
|
43
|
+
|
|
44
|
+
// Listen for the 'close' event to ensure resources are released
|
|
45
|
+
stream.on("close", function () {
|
|
46
|
+
// console.log(`Stream for ${file} closed`);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Handle errors
|
|
50
|
+
stream.on("error", function (error) {
|
|
51
|
+
reject(error);
|
|
52
|
+
});
|
|
22
53
|
} catch (e) {
|
|
23
54
|
reject(e);
|
|
24
55
|
}
|
|
@@ -28,7 +59,9 @@ function generateChecksum(file) {
|
|
|
28
59
|
function isExemptedDirectory(item) {
|
|
29
60
|
let file = _.last(item.path.split(dir_path + "/"));
|
|
30
61
|
let included = _.includes(
|
|
31
|
-
_.map(exemptions, (exem) =>
|
|
62
|
+
_.map(exemptions, (exem) => {
|
|
63
|
+
return _.startsWith(file, exem);
|
|
64
|
+
}),
|
|
32
65
|
true
|
|
33
66
|
);
|
|
34
67
|
return !included;
|
|
@@ -49,10 +82,10 @@ module.exports = class Mapper {
|
|
|
49
82
|
return _.groupBy(
|
|
50
83
|
await Promise.all(
|
|
51
84
|
files.map(async function (file) {
|
|
85
|
+
//console.log("Mapping::", file.path);
|
|
52
86
|
return {
|
|
53
87
|
file: _.last(file.path.split(dir_path + "/")),
|
|
54
88
|
size: file.stats.size,
|
|
55
|
-
|
|
56
89
|
checksum: await generateChecksum(file.path),
|
|
57
90
|
creation_date: file.stats.birthtime,
|
|
58
91
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "saturn-core-cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "Saturn Core CLI",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"colors": "^1.4.0",
|
|
22
22
|
"commander": "^11.1.0",
|
|
23
23
|
"fs-extra": "^11.1.1",
|
|
24
|
+
"graceful-fs": "^4.2.11",
|
|
24
25
|
"klaw-sync": "^6.0.0",
|
|
25
26
|
"lodash": "^4.17.21",
|
|
26
27
|
"pluralize": "^8.0.0",
|