@arc-e-tect/api-only-publisher 0.10.0 → 0.11.0
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/README.adoc +23 -2
- package/package.json +1 -1
- package/src/channels.js +23 -12
- package/src/cli.js +6 -5
- package/src/config.js +9 -1
- package/src/nuget.js +2 -1
- package/src/version-policy.js +4 -3
package/README.adoc
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
:source-highlighter: rouge
|
|
7
7
|
// The released version of this component: its snippets use it. The release
|
|
8
8
|
// workflow updates it; do not change it by hand.
|
|
9
|
-
:api-only-publisher-version: 0.
|
|
9
|
+
:api-only-publisher-version: 0.11.0
|
|
10
10
|
|
|
11
11
|
image:https://github.com/Arc-E-Tect/SoftwareEngineeringDoneRight-API/actions/workflows/nvd-cache-refresh.yml/badge.svg[Vulnerability Scan,link=https://github.com/Arc-E-Tect/SoftwareEngineeringDoneRight-API/actions/workflows/nvd-cache-refresh.yml]
|
|
12
12
|
image:https://img.shields.io/npm/v/@arc-e-tect/api-only-publisher[npm,link=https://www.npmjs.com/package/@arc-e-tect/api-only-publisher]
|
|
@@ -169,7 +169,7 @@ A target whose closure is untouched needs no new version, however much else in t
|
|
|
169
169
|
|`publish [--pre-release <ids>] [--target <t>]... [--channel <c>]... [--out <dir>]`
|
|
170
170
|
|Pack once, then ship those same bytes to every configured channel, or to each `--channel`.
|
|
171
171
|
|
|
172
|
-
|`split --out <dir> [--by kind\|target]`
|
|
172
|
+
|`split [--out <dir>] [--by kind\|target]`
|
|
173
173
|
|Make the library safe to break into separate repositories.
|
|
174
174
|
|===
|
|
175
175
|
|
|
@@ -651,6 +651,16 @@ What happens when the destination already has the version:
|
|
|
651
651
|
A NuGet feed that cannot return the package it has, so there is nothing to compare, fails as a different one would.
|
|
652
652
|
|===
|
|
653
653
|
|
|
654
|
+
[#channel-text]
|
|
655
|
+
=== What a package says about itself
|
|
656
|
+
|
|
657
|
+
The Maven POM, the npm `package.json`, the NuGet `.nuspec` and a GitHub release all describe the contract.
|
|
658
|
+
By default each says `API description documents for <target>.`, and a GitHub release is titled `<target> <version>`.
|
|
659
|
+
`channels.<channel>.description` replaces the description, and `channels.github-release.title` and `notes` the release's title and notes (notes default to the description); `{target}` and `{version}` are filled in, as in `tagFormat`.
|
|
660
|
+
|
|
661
|
+
npm publishes a release under the dist-tag `latest` and a pre-release under `next`.
|
|
662
|
+
`channels.npm.releaseTag` and `prereleaseTag` name others: publishing a patch to an older line under `releaseTag: v1` keeps `latest` on the newest line, which publishing it under `latest` would move back.
|
|
663
|
+
|
|
654
664
|
Credentials are read from the environment, never from configuration.
|
|
655
665
|
`channels.maven.tokenEnv` names the variable, `MAVEN_TOKEN` by default, and `channels.nuget.tokenEnv` the NuGet API key's, `NUGET_API_KEY` by default; a configuration file gets committed, and a credential in a committed file is a credential that has leaked.
|
|
656
666
|
|
|
@@ -671,6 +681,7 @@ channels:
|
|
|
671
681
|
# tokenEnv: NUGET_API_KEY # the variable holding the API key, for a URL
|
|
672
682
|
# authors: Contoso API team # default: idPrefix
|
|
673
683
|
# license: MIT # an SPDX expression; default: UNLICENSED
|
|
684
|
+
# description: "{target} contract" # default: API description documents for {target}.
|
|
674
685
|
----
|
|
675
686
|
|
|
676
687
|
*The package id* is `<idPrefix>.<target in PascalCase>`: target `user-account` with `idPrefix: Contoso.Contracts` is `Contoso.Contracts.UserAccount`, the dotted PascalCase .NET uses for namespaces, from a kebab-case target.
|
|
@@ -775,6 +786,8 @@ defaults:
|
|
|
775
786
|
build:
|
|
776
787
|
staging: build/staging
|
|
777
788
|
dist: build/dist
|
|
789
|
+
# packages: build/packages # where pack and publish write archives; --out overrides
|
|
790
|
+
# split: build/split # where split writes its parts; --out overrides
|
|
778
791
|
reports:
|
|
779
792
|
lint: build/reports/lint
|
|
780
793
|
lint:
|
|
@@ -791,6 +804,14 @@ channels:
|
|
|
791
804
|
file:
|
|
792
805
|
directory: build/publish
|
|
793
806
|
clean: true # keep only the version being published
|
|
807
|
+
# Every channel that describes a package or a release takes `description`, with
|
|
808
|
+
# {target} and {version} filled in; default: API description documents for {target}.
|
|
809
|
+
# npm:
|
|
810
|
+
# releaseTag: latest # the dist-tag of a release; another keeps latest where it is
|
|
811
|
+
# prereleaseTag: next # the dist-tag of a pre-release
|
|
812
|
+
# github-release:
|
|
813
|
+
# title: "{target} {version}"
|
|
814
|
+
# notes: "..." # default: description
|
|
794
815
|
|
|
795
816
|
targets:
|
|
796
817
|
orders:
|
package/package.json
CHANGED
package/src/channels.js
CHANGED
|
@@ -16,6 +16,14 @@ const { isPrerelease, npmDistTag } = require("./version-policy");
|
|
|
16
16
|
const { ChannelError } = require("./channel-error");
|
|
17
17
|
const { publishNuget } = require("./nuget");
|
|
18
18
|
|
|
19
|
+
/** What every package and release says it is, unless `description` says otherwise. */
|
|
20
|
+
const DEFAULT_DESCRIPTION = "API description documents for {target}.";
|
|
21
|
+
|
|
22
|
+
/** `template` with {target} and {version} filled in, as tagFormat has always been. */
|
|
23
|
+
function format(template, manifest) {
|
|
24
|
+
return String(template).replace(/\{target\}/g, manifest.target).replace(/\{version\}/g, manifest.version);
|
|
25
|
+
}
|
|
26
|
+
|
|
19
27
|
|
|
20
28
|
/**
|
|
21
29
|
* `file` -- publish to a local directory.
|
|
@@ -44,7 +52,7 @@ function publishFile(archive, manifest, options, log) {
|
|
|
44
52
|
return { location: archiveDest };
|
|
45
53
|
}
|
|
46
54
|
|
|
47
|
-
function pom(groupId, artifactId, version, packaging) {
|
|
55
|
+
function pom(groupId, artifactId, version, packaging, description = `API description documents for ${artifactId}.`) {
|
|
48
56
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
49
57
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
50
58
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
@@ -54,11 +62,16 @@ function pom(groupId, artifactId, version, packaging) {
|
|
|
54
62
|
<artifactId>${artifactId}</artifactId>
|
|
55
63
|
<version>${version}</version>
|
|
56
64
|
<packaging>${packaging}</packaging>
|
|
57
|
-
<description
|
|
65
|
+
<description>${description.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")}</description>
|
|
58
66
|
</project>
|
|
59
67
|
`;
|
|
60
68
|
}
|
|
61
69
|
|
|
70
|
+
/** The POM's description: configured, or the POM's own default. */
|
|
71
|
+
function pomDescription(options, manifest) {
|
|
72
|
+
return options.description ? format(options.description, manifest) : undefined;
|
|
73
|
+
}
|
|
74
|
+
|
|
62
75
|
/**
|
|
63
76
|
* `maven` -- publish into a Maven repository layout.
|
|
64
77
|
*
|
|
@@ -91,7 +104,7 @@ function publishMaven(archive, manifest, options, log) {
|
|
|
91
104
|
const base = `${artifactId}-${version}`;
|
|
92
105
|
const artifactDest = path.join(dir, `${base}.${extension}`);
|
|
93
106
|
fs.copyFileSync(archive, artifactDest);
|
|
94
|
-
fs.writeFileSync(path.join(dir, `${base}.pom`), pom(groupId, artifactId, version, extension));
|
|
107
|
+
fs.writeFileSync(path.join(dir, `${base}.pom`), pom(groupId, artifactId, version, extension, pomDescription(options, manifest)));
|
|
95
108
|
|
|
96
109
|
// The manifest travels beside the artifact as well as inside it, so a
|
|
97
110
|
// consumer can read provenance without unpacking anything.
|
|
@@ -115,7 +128,7 @@ function publishNpm(archive, manifest, options, log) {
|
|
|
115
128
|
const scope = options.scope;
|
|
116
129
|
const name = scope ? `${scope}/${manifest.target}` : (options.namePrefix || "") + manifest.target;
|
|
117
130
|
const version = manifest.version;
|
|
118
|
-
const distTag = npmDistTag(version);
|
|
131
|
+
const distTag = npmDistTag(version, options);
|
|
119
132
|
|
|
120
133
|
const workDir = fs.mkdtempSync(path.join(os.tmpdir(), "api-only-npm-"));
|
|
121
134
|
try {
|
|
@@ -126,7 +139,7 @@ function publishNpm(archive, manifest, options, log) {
|
|
|
126
139
|
fs.writeFileSync(path.join(workDir, "package.json"), JSON.stringify({
|
|
127
140
|
name,
|
|
128
141
|
version,
|
|
129
|
-
description:
|
|
142
|
+
description: format(options.description || DEFAULT_DESCRIPTION, manifest),
|
|
130
143
|
license: options.license || "UNLICENSED",
|
|
131
144
|
files: fs.readdirSync(workDir).filter((f) => f !== "package.json").sort(),
|
|
132
145
|
// npm defaults a scoped package to restricted. A contract exists to be
|
|
@@ -210,9 +223,7 @@ function publishGithubRelease(archive, manifest, options, log) {
|
|
|
210
223
|
const repository = options.repository;
|
|
211
224
|
if (!repository) throw new ChannelError("the github-release channel requires a repository");
|
|
212
225
|
|
|
213
|
-
const tag = (options.tagFormat || "{target}-v{version}")
|
|
214
|
-
.replace(/\{target\}/g, manifest.target)
|
|
215
|
-
.replace(/\{version\}/g, manifest.version);
|
|
226
|
+
const tag = format(options.tagFormat || "{target}-v{version}", manifest);
|
|
216
227
|
|
|
217
228
|
const gh = (args) => execFileSync("gh", args, { encoding: "utf8", stdio: "pipe" });
|
|
218
229
|
|
|
@@ -225,8 +236,8 @@ function publishGithubRelease(archive, manifest, options, log) {
|
|
|
225
236
|
|
|
226
237
|
if (!exists) {
|
|
227
238
|
const args = ["release", "create", tag, "--repo", repository,
|
|
228
|
-
"--title",
|
|
229
|
-
"--notes",
|
|
239
|
+
"--title", format(options.title || "{target} {version}", manifest),
|
|
240
|
+
"--notes", format(options.notes || options.description || DEFAULT_DESCRIPTION, manifest)];
|
|
230
241
|
// A pre-release is marked as one, so that "latest release" never resolves
|
|
231
242
|
// to a contract that is not finished.
|
|
232
243
|
if (isPrerelease(manifest.version)) args.push("--prerelease");
|
|
@@ -333,7 +344,7 @@ function publishMavenRemote(archive, manifest, options, log) {
|
|
|
333
344
|
}
|
|
334
345
|
await putFile(`${base}.${extension}`, bytes, token, "application/octet-stream");
|
|
335
346
|
await putFile(`${base}.pom`,
|
|
336
|
-
Buffer.from(pom(options.groupId, artifactId, version, extension), "utf8"),
|
|
347
|
+
Buffer.from(pom(options.groupId, artifactId, version, extension, pomDescription(options, manifest)), "utf8"),
|
|
337
348
|
token, "application/xml");
|
|
338
349
|
await putFile(`${base}-manifest.json`,
|
|
339
350
|
Buffer.from(JSON.stringify(manifest, null, 2) + "\n", "utf8"),
|
|
@@ -368,4 +379,4 @@ function publish(archive, manifest, channel, options, log = () => {}) {
|
|
|
368
379
|
return handler(archive, manifest, options || {}, log);
|
|
369
380
|
}
|
|
370
381
|
|
|
371
|
-
module.exports = { publish, CHANNELS, ChannelError, pom };
|
|
382
|
+
module.exports = { publish, CHANNELS, ChannelError, pom, format, DEFAULT_DESCRIPTION };
|
package/src/cli.js
CHANGED
|
@@ -38,7 +38,7 @@ Usage:
|
|
|
38
38
|
api-only-publisher changed --since <ref>
|
|
39
39
|
api-only-publisher pack [--pre-release <ids>] [--target <name>]... [--out <dir>]
|
|
40
40
|
api-only-publisher publish [--pre-release <ids>] [--target <name>]... [--channel <name>]... [--out <dir>]
|
|
41
|
-
api-only-publisher split --out <dir> [--by kind|target]
|
|
41
|
+
api-only-publisher split [--out <dir>] [--by kind|target]
|
|
42
42
|
|
|
43
43
|
Options:
|
|
44
44
|
--target <name> Restrict to one target; repeat for several. Default: all.
|
|
@@ -60,7 +60,8 @@ Options:
|
|
|
60
60
|
--portfolio-paths <target-prefix|none>, --portfolio-location <dir>
|
|
61
61
|
config only: the value of that question; a flag always wins.
|
|
62
62
|
--since <ref> changed only: the git ref to compare the working tree against.
|
|
63
|
-
--out <dir> pack/publish/split: where to write.
|
|
63
|
+
--out <dir> pack/publish/split: where to write, instead of build.packages
|
|
64
|
+
or build.split in apionly.yaml.
|
|
64
65
|
--channel <name> publish only: repeat for several. Default: every configured channel.
|
|
65
66
|
--by <kind|target> split only: the boundary to split along. Default: kind.
|
|
66
67
|
-C <dir> Run as if started in <dir>.
|
|
@@ -407,7 +408,7 @@ async function main(argv, io = {}) {
|
|
|
407
408
|
return 0;
|
|
408
409
|
}
|
|
409
410
|
case "pack": {
|
|
410
|
-
const outDir = path.resolve(options.dir, options.out
|
|
411
|
+
const outDir = options.out ? path.resolve(options.dir, options.out) : config.packagesDir();
|
|
411
412
|
const shipped = shippedTargets(config, targets);
|
|
412
413
|
const versions = versionsOf(config, shipped, options.preRelease);
|
|
413
414
|
const closures = forTargets(config, undefined, shipped);
|
|
@@ -430,7 +431,7 @@ async function main(argv, io = {}) {
|
|
|
430
431
|
return 0;
|
|
431
432
|
}
|
|
432
433
|
case "publish": {
|
|
433
|
-
const outDir = path.resolve(options.dir, options.out
|
|
434
|
+
const outDir = options.out ? path.resolve(options.dir, options.out) : config.packagesDir();
|
|
434
435
|
const configured = config.channels || {};
|
|
435
436
|
const names = options.channels || Object.keys(configured);
|
|
436
437
|
if (names.length === 0) {
|
|
@@ -460,7 +461,7 @@ async function main(argv, io = {}) {
|
|
|
460
461
|
return 0;
|
|
461
462
|
}
|
|
462
463
|
case "split": {
|
|
463
|
-
const outDir = path.resolve(options.dir, options.out
|
|
464
|
+
const outDir = options.out ? path.resolve(options.dir, options.out) : config.splitDir();
|
|
464
465
|
prepare(config);
|
|
465
466
|
const parts = split(config, { by: options.by, outDir, log });
|
|
466
467
|
const duplicated = parts.reduce((n, p) => n + p.imported.length, 0);
|
package/src/config.js
CHANGED
|
@@ -25,7 +25,7 @@ const SOURCES_KEYS = ["root", "openapi", "asyncapi"];
|
|
|
25
25
|
const DEFAULTS_KEYS = ["openapi", "asyncapi", "placeholders"];
|
|
26
26
|
const DEFAULTS_KIND_KEYS = { openapi: ["lint", "outputName", "fragmentPaths"], asyncapi: ["lint", "outputName", "fragmentPaths"] };
|
|
27
27
|
const PLACEHOLDERS_KEYS = ["strict"];
|
|
28
|
-
const BUILD_KEYS = ["staging", "dist"];
|
|
28
|
+
const BUILD_KEYS = ["staging", "dist", "packages", "split"];
|
|
29
29
|
const REPORTS_KEYS = ["lint"];
|
|
30
30
|
const LINT_KEYS = ["unreferenced", "examples"];
|
|
31
31
|
const LINT_EXAMPLES_KEYS = ["openapi", "asyncapi"];
|
|
@@ -255,6 +255,14 @@ function load(configPath) {
|
|
|
255
255
|
stagingDir(kind) {
|
|
256
256
|
return path.join(this.stagingRoot(kind), requireString(this.sources[kind], `sources.${kind}`));
|
|
257
257
|
},
|
|
258
|
+
// Where pack and publish write archives, and split its parts, unless --out says
|
|
259
|
+
// otherwise; relative to this configuration, as every other path here is.
|
|
260
|
+
packagesDir() {
|
|
261
|
+
return path.resolve(this.root, this.build.packages || "build/packages");
|
|
262
|
+
},
|
|
263
|
+
splitDir() {
|
|
264
|
+
return path.resolve(this.root, this.build.split || "build/split");
|
|
265
|
+
},
|
|
258
266
|
distDir(target) {
|
|
259
267
|
const dist = this.build.dist || "dist";
|
|
260
268
|
return path.resolve(this.root, dist, target);
|
package/src/nuget.js
CHANGED
|
@@ -88,7 +88,8 @@ function nuspec(id, manifest, options) {
|
|
|
88
88
|
` <id>${escape(id)}</id>\n` +
|
|
89
89
|
` <version>${escape(manifest.version)}</version>\n` +
|
|
90
90
|
` <authors>${escape(options.authors || options.idPrefix || id)}</authors>\n` +
|
|
91
|
-
` <description
|
|
91
|
+
` <description>${escape((options.description || "API description documents for {target}.")
|
|
92
|
+
.replace(/\{target\}/g, manifest.target).replace(/\{version\}/g, manifest.version))}</description>\n` +
|
|
92
93
|
` <license type="expression">${escape(options.license || "UNLICENSED")}</license>\n` +
|
|
93
94
|
repository +
|
|
94
95
|
" </metadata>\n" +
|
package/src/version-policy.js
CHANGED
|
@@ -44,10 +44,11 @@ function isPrerelease(version) {
|
|
|
44
44
|
* The npm dist-tag a version should be published under.
|
|
45
45
|
*
|
|
46
46
|
* A pre-release goes to `next`, never `latest`, so that `npm install <pkg>`
|
|
47
|
-
* cannot pick one up by accident.
|
|
47
|
+
* cannot pick one up by accident. Both can be named otherwise -- `releaseTag` for a
|
|
48
|
+
* patch to an older line that must not become `latest`, say.
|
|
48
49
|
*/
|
|
49
|
-
function npmDistTag(version) {
|
|
50
|
-
return isPrerelease(version) ?
|
|
50
|
+
function npmDistTag(version, { releaseTag = "latest", prereleaseTag = "next" } = {}) {
|
|
51
|
+
return isPrerelease(version) ? prereleaseTag : releaseTag;
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
/**
|