@wpelevator/wp-release 0.15.0 → 0.17.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.md +366 -39
- package/dist/changeset-config.d.ts +8 -0
- package/dist/changeset-config.js +3 -0
- package/dist/changeset-release.d.ts +9 -3
- package/dist/changeset-release.js +23 -14
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +12 -0
- package/dist/commands/artifact-signing.d.ts +20 -0
- package/dist/commands/artifact-signing.js +42 -0
- package/dist/commands/build.d.ts +32 -0
- package/dist/commands/build.js +91 -0
- package/dist/commands/changeset-add.d.ts +55 -6
- package/dist/commands/changeset-add.js +12 -3
- package/dist/commands/changeset-args.js +2 -2
- package/dist/commands/changeset-project-args.d.ts +63 -0
- package/dist/commands/changeset-project-args.js +114 -0
- package/dist/commands/changeset-status.d.ts +50 -1
- package/dist/commands/changeset-status.js +4 -2
- package/dist/commands/changeset-tag.d.ts +55 -6
- package/dist/commands/changeset-tag.js +7 -4
- package/dist/commands/changeset-version.d.ts +55 -6
- package/dist/commands/changeset-version.js +4 -2
- package/dist/commands/checksums.d.ts +4 -1
- package/dist/commands/checksums.js +27 -6
- package/dist/commands/i18n-audit.d.ts +2 -0
- package/dist/commands/i18n-audit.js +2 -0
- package/dist/commands/i18n-compile.d.ts +6 -0
- package/dist/commands/i18n-compile.js +76 -0
- package/dist/commands/i18n-json.d.ts +2 -0
- package/dist/commands/i18n-json.js +2 -0
- package/dist/commands/i18n-mo.d.ts +2 -0
- package/dist/commands/i18n-mo.js +2 -0
- package/dist/commands/i18n-php.d.ts +2 -0
- package/dist/commands/i18n-php.js +2 -0
- package/dist/commands/i18n-pot.d.ts +2 -0
- package/dist/commands/i18n-pot.js +2 -0
- package/dist/commands/i18n.d.ts +12 -0
- package/dist/commands/i18n.js +174 -0
- package/dist/commands/package-source.d.ts +43 -0
- package/dist/commands/package-source.js +101 -0
- package/dist/commands/verify.d.ts +7 -1
- package/dist/commands/verify.js +53 -17
- package/dist/commands/zip.d.ts +20 -8
- package/dist/commands/zip.js +109 -124
- package/dist/i18n/atomic-output.d.ts +23 -0
- package/dist/i18n/atomic-output.js +90 -0
- package/dist/i18n/audit.d.ts +7 -0
- package/dist/i18n/audit.js +69 -0
- package/dist/i18n/catalog.d.ts +54 -0
- package/dist/i18n/catalog.js +86 -0
- package/dist/i18n/compile.d.ts +27 -0
- package/dist/i18n/compile.js +71 -0
- package/dist/i18n/extraction.d.ts +45 -0
- package/dist/i18n/extraction.js +105 -0
- package/dist/i18n/files.d.ts +7 -0
- package/dist/i18n/files.js +45 -0
- package/dist/i18n/generate.d.ts +49 -0
- package/dist/i18n/generate.js +240 -0
- package/dist/i18n/javascript.d.ts +7 -0
- package/dist/i18n/javascript.js +143 -0
- package/dist/i18n/metadata.d.ts +16 -0
- package/dist/i18n/metadata.js +117 -0
- package/dist/i18n/options.d.ts +52 -0
- package/dist/i18n/options.js +156 -0
- package/dist/i18n/packages.d.ts +24 -0
- package/dist/i18n/packages.js +86 -0
- package/dist/i18n/php.d.ts +9 -0
- package/dist/i18n/php.js +238 -0
- package/dist/i18n/placeholders.d.ts +9 -0
- package/dist/i18n/placeholders.js +13 -0
- package/dist/i18n/po.d.ts +24 -0
- package/dist/i18n/po.js +180 -0
- package/dist/i18n/pot.d.ts +16 -0
- package/dist/i18n/pot.js +119 -0
- package/dist/i18n/translation-json.d.ts +14 -0
- package/dist/i18n/translation-json.js +96 -0
- package/dist/i18n/translation-php.d.ts +6 -0
- package/dist/i18n/translation-php.js +35 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +16 -0
- package/dist/integrity.d.ts +33 -0
- package/dist/integrity.js +115 -0
- package/dist/output-files.d.ts +7 -0
- package/dist/output-files.js +25 -0
- package/dist/package-build.d.ts +33 -0
- package/dist/package-build.js +112 -0
- package/dist/package-files.d.ts +25 -0
- package/dist/package-files.js +24 -0
- package/dist/package-zip.d.ts +14 -2
- package/dist/package-zip.js +129 -38
- package/dist/release-tags.js +6 -0
- package/dist/single-project.d.ts +32 -0
- package/dist/single-project.js +144 -0
- package/dist/summary.d.ts +9 -0
- package/dist/summary.js +25 -0
- package/dist/zip.d.ts +3 -0
- package/dist/zip.js +16 -6
- package/package.json +20 -3
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { hasFatalDiagnostics } from "../i18n/catalog.js";
|
|
3
|
+
import { extractCatalog, generatePackagePot } from "../i18n/generate.js";
|
|
4
|
+
import { auditCatalog } from "../i18n/audit.js";
|
|
5
|
+
import { resolveI18nPackages } from "../i18n/packages.js";
|
|
6
|
+
import { ValidationError } from "../errors.js";
|
|
7
|
+
import { getBoolean, getString, getStrings } from "./command.js";
|
|
8
|
+
function cliOptions(values) {
|
|
9
|
+
const headers = {};
|
|
10
|
+
for (const header of getStrings(values, 'header')) {
|
|
11
|
+
const index = header.indexOf('=');
|
|
12
|
+
if (0 === index || !header) {
|
|
13
|
+
throw new ValidationError('--header requires NAME=VALUE, with an empty value allowed, or NAME to remove the header.');
|
|
14
|
+
}
|
|
15
|
+
if (-1 === index) {
|
|
16
|
+
headers[header] = false;
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
headers[header.slice(0, index)] = header.slice(index + 1);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const domain = getString(values, 'domain');
|
|
23
|
+
const destination = getString(values, 'destination');
|
|
24
|
+
const fileComment = getString(values, 'file-comment');
|
|
25
|
+
return {
|
|
26
|
+
...(undefined !== domain ? { domain } : {}),
|
|
27
|
+
...(undefined !== destination ? { destination } : {}),
|
|
28
|
+
...(undefined !== fileComment ? { fileComment } : {}),
|
|
29
|
+
...(values.include
|
|
30
|
+
? { include: getStrings(values, 'include') }
|
|
31
|
+
: {}),
|
|
32
|
+
...(values.exclude
|
|
33
|
+
? { exclude: getStrings(values, 'exclude') }
|
|
34
|
+
: {}),
|
|
35
|
+
...(values.suppress
|
|
36
|
+
? { suppress: getStrings(values, 'suppress') }
|
|
37
|
+
: {}),
|
|
38
|
+
...(getBoolean(values, 'strict') ? { strict: true } : {}),
|
|
39
|
+
...(getBoolean(values, 'all-domains') ? { allDomains: true } : {}),
|
|
40
|
+
...(getBoolean(values, 'no-references')
|
|
41
|
+
? { references: false }
|
|
42
|
+
: {}),
|
|
43
|
+
...(getBoolean(values, 'no-legacy-globals')
|
|
44
|
+
? { legacyGlobals: false }
|
|
45
|
+
: {}),
|
|
46
|
+
...(Object.keys(headers).length ? { headers } : {}),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function report(context, diagnostics, sourceDir) {
|
|
50
|
+
for (const diagnostic of diagnostics) {
|
|
51
|
+
context.logger[diagnostic.severity](diagnostic.message, {
|
|
52
|
+
file: resolve(sourceDir, diagnostic.file),
|
|
53
|
+
line: diagnostic.line,
|
|
54
|
+
title: diagnostic.code,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Small command adapters share selection/reporting; domain logic stays in the library.
|
|
60
|
+
* @param operation
|
|
61
|
+
*/
|
|
62
|
+
export function createI18nCommand(operation) {
|
|
63
|
+
return {
|
|
64
|
+
name: `i18n-${operation}`,
|
|
65
|
+
usage: `i18n-${operation} [<source-dir>] [options]`,
|
|
66
|
+
summary: 'pot' === operation
|
|
67
|
+
? 'Extract WordPress source strings into a reproducible POT.'
|
|
68
|
+
: 'Audit WordPress source strings without writing files.',
|
|
69
|
+
help: `Options:
|
|
70
|
+
--package <name> Configured package (extraction overrides allowed)
|
|
71
|
+
--all All enabled configured WordPress packages
|
|
72
|
+
--source-dir <dir> Plugin/theme directory, or positional argument (default .)
|
|
73
|
+
--domain <domain> Select one domain (default Text Domain, then slug)
|
|
74
|
+
--all-domains Explicitly combine domains
|
|
75
|
+
--include <glob> Repeatable source globs, replacing inherited includes
|
|
76
|
+
--exclude <glob> Repeatable exclusion globs, replacing inherited excludes
|
|
77
|
+
--destination <dir> Package-relative POT directory
|
|
78
|
+
--header <NAME=VALUE> Repeatable gettext header override; NAME alone removes it
|
|
79
|
+
${'pot' === operation
|
|
80
|
+
? ' --file-comment <text> Static comment prepended to the POT\n'
|
|
81
|
+
: ''}
|
|
82
|
+
--no-references Omit source references
|
|
83
|
+
--no-legacy-globals JS requires imports or wp.i18n calls
|
|
84
|
+
--strict Warnings fail with exit code 2
|
|
85
|
+
${'pot' === operation
|
|
86
|
+
? ' --output <file.pot> Explicit output relative to cwd; one package only\n --check Compare bytes without writing'
|
|
87
|
+
: ' --suppress <code> Repeatable audit finding codes to suppress'}
|
|
88
|
+
|
|
89
|
+
No PHP, WordPress, WP-CLI or network needed. Distribution file rules do not apply.
|
|
90
|
+
Each package writes atomically; a multi-package run is not a transaction.`,
|
|
91
|
+
options: {
|
|
92
|
+
package: { type: 'string' },
|
|
93
|
+
all: { type: 'boolean' },
|
|
94
|
+
'source-dir': { type: 'string' },
|
|
95
|
+
domain: { type: 'string' },
|
|
96
|
+
'all-domains': { type: 'boolean' },
|
|
97
|
+
include: { type: 'string', multiple: true },
|
|
98
|
+
exclude: { type: 'string', multiple: true },
|
|
99
|
+
destination: { type: 'string' },
|
|
100
|
+
header: { type: 'string', multiple: true },
|
|
101
|
+
'no-references': { type: 'boolean' },
|
|
102
|
+
'no-legacy-globals': { type: 'boolean' },
|
|
103
|
+
strict: { type: 'boolean' },
|
|
104
|
+
...('pot' === operation
|
|
105
|
+
? {
|
|
106
|
+
'file-comment': { type: 'string' },
|
|
107
|
+
output: { type: 'string' },
|
|
108
|
+
check: { type: 'boolean' },
|
|
109
|
+
}
|
|
110
|
+
: { suppress: { type: 'string', multiple: true } }),
|
|
111
|
+
},
|
|
112
|
+
async run(context) {
|
|
113
|
+
const { values, positionals, cwd, configFile, logger } = context;
|
|
114
|
+
if (positionals.length > 1 ||
|
|
115
|
+
(positionals.length && undefined !== values['source-dir'])) {
|
|
116
|
+
throw new ValidationError('Pass only one source directory.');
|
|
117
|
+
}
|
|
118
|
+
const source = getString(values, 'source-dir');
|
|
119
|
+
if (undefined !== source && !source.trim()) {
|
|
120
|
+
throw new ValidationError('--source-dir must not be empty.');
|
|
121
|
+
}
|
|
122
|
+
const output = getString(values, 'output');
|
|
123
|
+
if (undefined !== output && !output.trim()) {
|
|
124
|
+
throw new ValidationError('--output must not be empty.');
|
|
125
|
+
}
|
|
126
|
+
const selected = await resolveI18nPackages({
|
|
127
|
+
cwd,
|
|
128
|
+
configFile,
|
|
129
|
+
package: getString(values, 'package'),
|
|
130
|
+
all: getBoolean(values, 'all'),
|
|
131
|
+
sourceDir: getString(values, 'source-dir') ?? positionals[0],
|
|
132
|
+
options: cliOptions(values),
|
|
133
|
+
validateOutputs: 'pot' === operation,
|
|
134
|
+
...(output ? { outputPath: resolve(cwd, output) } : {}),
|
|
135
|
+
});
|
|
136
|
+
const result = { packages: [] };
|
|
137
|
+
let failed = false;
|
|
138
|
+
for (const pkg of selected) {
|
|
139
|
+
let generated;
|
|
140
|
+
if ('pot' === operation) {
|
|
141
|
+
generated = await generatePackagePot({
|
|
142
|
+
...pkg,
|
|
143
|
+
check: getBoolean(values, 'check'),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
const extracted = await extractCatalog(pkg);
|
|
148
|
+
generated = {
|
|
149
|
+
outputPath: pkg.outputPath,
|
|
150
|
+
messages: extracted.catalog.messages.length,
|
|
151
|
+
files: extracted.files,
|
|
152
|
+
diagnostics: [
|
|
153
|
+
...extracted.diagnostics,
|
|
154
|
+
...auditCatalog(extracted.catalog, pkg.options.suppress),
|
|
155
|
+
],
|
|
156
|
+
written: false,
|
|
157
|
+
current: false,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
result.packages.push({
|
|
161
|
+
...generated,
|
|
162
|
+
...(pkg.package ? { package: pkg.package } : {}),
|
|
163
|
+
});
|
|
164
|
+
failed ||= hasFatalDiagnostics(generated.diagnostics, pkg.options.strict);
|
|
165
|
+
report(context, generated.diagnostics, pkg.sourceDir);
|
|
166
|
+
logger.info(`${pkg.package ?? pkg.sourceDir}: ${generated.messages} messages, ${generated.files} files.`);
|
|
167
|
+
}
|
|
168
|
+
logger.summary(() => `### wp-release i18n-${operation}\n\n${result.packages
|
|
169
|
+
.map((pkg) => `- ${pkg.package ?? pkg.outputPath}: ${pkg.messages} messages, ${pkg.diagnostics.length} diagnostics.`)
|
|
170
|
+
.join('\n')}\n`);
|
|
171
|
+
return { exitCode: failed ? 2 : 0, result };
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { PackageType } from '../checksums.ts';
|
|
2
|
+
import type { FileSelection } from '../file-selection.ts';
|
|
3
|
+
import type { ParsedValues } from './command.ts';
|
|
4
|
+
/** Release source, file selection and package metadata. */
|
|
5
|
+
export interface PackageSource {
|
|
6
|
+
sourceDir: string;
|
|
7
|
+
selection: FileSelection;
|
|
8
|
+
slug: string;
|
|
9
|
+
version: string | null;
|
|
10
|
+
type?: PackageType;
|
|
11
|
+
package?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Get the configured package to release, failing for the options that set the
|
|
15
|
+
* source directory and the files instead.
|
|
16
|
+
*
|
|
17
|
+
* @param values Parsed CLI values.
|
|
18
|
+
* @param positionals Positional arguments.
|
|
19
|
+
* @return The package identifier or path, or undefined without --package.
|
|
20
|
+
* @throws {ValidationError} For an empty --package, or --package with a source directory or file selection option.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getPackageArg(values: ParsedValues, positionals: string[]): string | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Get the files of a source directory from the CLI options.
|
|
25
|
+
*
|
|
26
|
+
* @param values Parsed CLI values.
|
|
27
|
+
* @param positionals Positional arguments.
|
|
28
|
+
* @param cwd Working directory.
|
|
29
|
+
* @return Source directory, file selection, slug and version.
|
|
30
|
+
*/
|
|
31
|
+
export declare function getDirectorySource(values: ParsedValues, positionals: string[], cwd: string): Promise<PackageSource>;
|
|
32
|
+
/**
|
|
33
|
+
* Get the directory of a configured package with the files config of its
|
|
34
|
+
* package group, which its deploys use too.
|
|
35
|
+
*
|
|
36
|
+
* @param values Parsed CLI values.
|
|
37
|
+
* @param cwd Repository root with the wp-release config.
|
|
38
|
+
* @param name Package identifier or path under the configured root.
|
|
39
|
+
* @param configFile Config file, instead of the conventional one.
|
|
40
|
+
* @return Source directory, file selection, slug, version and package.
|
|
41
|
+
* @throws {ValidationError} Without a config or for an unknown package.
|
|
42
|
+
*/
|
|
43
|
+
export declare function getPackageSource(values: ParsedValues, cwd: string, name: string, configFile?: string): Promise<PackageSource>;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { basename, resolve } from 'node:path';
|
|
2
|
+
import { ValidationError } from "../errors.js";
|
|
3
|
+
import { resolveFileSelection } from "../file-selection.js";
|
|
4
|
+
import { getPackageFiles } from "../package-deploy.js";
|
|
5
|
+
import { readPackageMeta } from "../package-meta.js";
|
|
6
|
+
import { checkDirectory, getFileSelectionInput, getSourceDirArg, } from "./args.js";
|
|
7
|
+
import { getBoolean, getString, getStrings } from "./command.js";
|
|
8
|
+
/** Options of a source directory and its files, which --package takes from the config. */
|
|
9
|
+
const SOURCE_FLAGS = [
|
|
10
|
+
'source-dir',
|
|
11
|
+
'distignore',
|
|
12
|
+
'no-distignore',
|
|
13
|
+
'exclude',
|
|
14
|
+
'exclude-gitattributes',
|
|
15
|
+
];
|
|
16
|
+
/**
|
|
17
|
+
* Get the configured package to release, failing for the options that set the
|
|
18
|
+
* source directory and the files instead.
|
|
19
|
+
*
|
|
20
|
+
* @param values Parsed CLI values.
|
|
21
|
+
* @param positionals Positional arguments.
|
|
22
|
+
* @return The package identifier or path, or undefined without --package.
|
|
23
|
+
* @throws {ValidationError} For an empty --package, or --package with a source directory or file selection option.
|
|
24
|
+
*/
|
|
25
|
+
export function getPackageArg(values, positionals) {
|
|
26
|
+
const name = getString(values, 'package')?.trim();
|
|
27
|
+
const conflicts = SOURCE_FLAGS.filter((flag) => undefined !== getString(values, flag) ||
|
|
28
|
+
getBoolean(values, flag) ||
|
|
29
|
+
getStrings(values, flag).length);
|
|
30
|
+
if ('' === name) {
|
|
31
|
+
throw new ValidationError('--package <name> must not be empty.');
|
|
32
|
+
}
|
|
33
|
+
if (name && (positionals.length || conflicts.length)) {
|
|
34
|
+
throw new ValidationError(`--package takes the directory and files of the package from the config, so it can't be combined with ${positionals.length
|
|
35
|
+
? 'a source directory'
|
|
36
|
+
: conflicts.map((flag) => `--${flag}`).join(', ')}.`);
|
|
37
|
+
}
|
|
38
|
+
return name;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Describe the files of a directory with the slug and the version from the
|
|
42
|
+
* options, or from the plugin or theme headers.
|
|
43
|
+
*
|
|
44
|
+
* @param values Parsed CLI values.
|
|
45
|
+
* @param sourceDir Directory to release.
|
|
46
|
+
* @param selection File selection.
|
|
47
|
+
* @param slug Default slug.
|
|
48
|
+
* @return Source directory, file selection, slug, version and type.
|
|
49
|
+
*/
|
|
50
|
+
async function describeSource(values, sourceDir, selection, slug) {
|
|
51
|
+
const name = getString(values, 'slug') ?? slug;
|
|
52
|
+
const meta = await readPackageMeta({ dir: sourceDir, slug: name });
|
|
53
|
+
return {
|
|
54
|
+
sourceDir,
|
|
55
|
+
selection,
|
|
56
|
+
slug: name,
|
|
57
|
+
version: getString(values, 'version') ?? meta?.version ?? null,
|
|
58
|
+
...(meta ? { type: meta.type } : {}),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get the files of a source directory from the CLI options.
|
|
63
|
+
*
|
|
64
|
+
* @param values Parsed CLI values.
|
|
65
|
+
* @param positionals Positional arguments.
|
|
66
|
+
* @param cwd Working directory.
|
|
67
|
+
* @return Source directory, file selection, slug and version.
|
|
68
|
+
*/
|
|
69
|
+
export async function getDirectorySource(values, positionals, cwd) {
|
|
70
|
+
const sourceDir = resolve(cwd, getSourceDirArg(values, positionals));
|
|
71
|
+
await checkDirectory(sourceDir);
|
|
72
|
+
return describeSource(values, sourceDir, resolveFileSelection({
|
|
73
|
+
cwd,
|
|
74
|
+
sourceDir,
|
|
75
|
+
...getFileSelectionInput(values),
|
|
76
|
+
}), basename(cwd));
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get the directory of a configured package with the files config of its
|
|
80
|
+
* package group, which its deploys use too.
|
|
81
|
+
*
|
|
82
|
+
* @param values Parsed CLI values.
|
|
83
|
+
* @param cwd Repository root with the wp-release config.
|
|
84
|
+
* @param name Package identifier or path under the configured root.
|
|
85
|
+
* @param configFile Config file, instead of the conventional one.
|
|
86
|
+
* @return Source directory, file selection, slug, version and package.
|
|
87
|
+
* @throws {ValidationError} Without a config or for an unknown package.
|
|
88
|
+
*/
|
|
89
|
+
export async function getPackageSource(values, cwd, name, configFile) {
|
|
90
|
+
const pkg = await getPackageFiles({
|
|
91
|
+
cwd,
|
|
92
|
+
package: name,
|
|
93
|
+
...(configFile ? { configFile } : {}),
|
|
94
|
+
});
|
|
95
|
+
const source = await describeSource(values, pkg.directory, resolveFileSelection({
|
|
96
|
+
cwd,
|
|
97
|
+
sourceDir: pkg.directory,
|
|
98
|
+
...pkg.files,
|
|
99
|
+
}), basename(pkg.directory));
|
|
100
|
+
return { ...source, package: pkg.name };
|
|
101
|
+
}
|
|
@@ -12,6 +12,12 @@ declare const _default: {
|
|
|
12
12
|
checksums: {
|
|
13
13
|
type: "string";
|
|
14
14
|
};
|
|
15
|
+
'checksums-format': {
|
|
16
|
+
type: "string";
|
|
17
|
+
};
|
|
18
|
+
'checksums-file': {
|
|
19
|
+
type: "string";
|
|
20
|
+
};
|
|
15
21
|
zip: {
|
|
16
22
|
type: "string";
|
|
17
23
|
};
|
|
@@ -28,6 +34,6 @@ declare const _default: {
|
|
|
28
34
|
type: "string";
|
|
29
35
|
};
|
|
30
36
|
};
|
|
31
|
-
run({ values, positionals, cwd, logger }: import("./command.ts").CommandContext): Promise<CommandOutcome<unknown>>;
|
|
37
|
+
run({ values, positionals, cwd, configFile, logger }: import("./command.ts").CommandContext): Promise<CommandOutcome<unknown>>;
|
|
32
38
|
};
|
|
33
39
|
export default _default;
|
package/dist/commands/verify.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { readIntegrity, checkChecksumsFile, resolveManifestCodec, } from "../integrity.js";
|
|
2
|
+
import { loadChangesetConfig } from "../changeset-config.js";
|
|
1
3
|
import { existsSync } from 'node:fs';
|
|
2
4
|
import { mkdir, mkdtemp, readFile, rm } from 'node:fs/promises';
|
|
3
5
|
import { tmpdir } from 'node:os';
|
|
4
6
|
import { basename, isAbsolute, join, posix, resolve } from 'node:path';
|
|
5
|
-
import {
|
|
7
|
+
import { verifyChecksums } from "../checksums.js";
|
|
6
8
|
import { downloadFile, isUrl } from "../download.js";
|
|
7
9
|
import { ignoreOwnFile, ignoreOwnSignature, loadFileRules, parseExclude, } from "../file-selection.js";
|
|
8
10
|
import { ValidationError } from "../errors.js";
|
|
@@ -105,7 +107,7 @@ function getEmbeddedPath(checksumsPath, embedded) {
|
|
|
105
107
|
['.', '..'].includes(path))) {
|
|
106
108
|
throw new ValidationError(`--embedded must be a path inside the plugin with forward slashes, got "${embedded}".`);
|
|
107
109
|
}
|
|
108
|
-
return
|
|
110
|
+
return path;
|
|
109
111
|
}
|
|
110
112
|
/**
|
|
111
113
|
* Get the manifest and signature pairs to look for inside the checked files.
|
|
@@ -180,9 +182,22 @@ async function hasSameContents(original, file) {
|
|
|
180
182
|
*/
|
|
181
183
|
async function checkEmbeddedCopies(input) {
|
|
182
184
|
const { dir, manifest, checksumsPath, embedded, format } = input;
|
|
183
|
-
const
|
|
185
|
+
const path = getEmbeddedPath(checksumsPath, embedded);
|
|
186
|
+
const pairs = getCopyPairs(checksumsPath, path, format).filter((pair) => pair.original !== join(dir, pair.copy) ||
|
|
187
|
+
existsSync(pair.original));
|
|
188
|
+
for (const other of Object.keys(SIGNATURE_FORMATS)) {
|
|
189
|
+
if (other !== format &&
|
|
190
|
+
path &&
|
|
191
|
+
existsSync(getSignaturePath(checksumsPath, other))) {
|
|
192
|
+
pairs.push({
|
|
193
|
+
original: getSignaturePath(checksumsPath, other),
|
|
194
|
+
copy: getSignaturePath(path, other),
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
184
198
|
if (embedded) {
|
|
185
|
-
checkExplicitPairs(pairs,
|
|
199
|
+
checkExplicitPairs(pairs.filter((pair) => pair.original !== join(dir, pair.copy) ||
|
|
200
|
+
existsSync(pair.original)), manifest);
|
|
186
201
|
}
|
|
187
202
|
const found = pairs.filter(({ original, copy }) => existsSync(join(dir, copy)) &&
|
|
188
203
|
(embedded ||
|
|
@@ -336,8 +351,10 @@ export default {
|
|
|
336
351
|
accepted as --source-dir <dir>
|
|
337
352
|
|
|
338
353
|
Options:
|
|
339
|
-
--checksums <file>
|
|
340
|
-
|
|
354
|
+
--checksums-file <file> Relative embedded manifest path (default: checksums.json)
|
|
355
|
+
--checksums-format <name> wordpress-json or sha256sum; custom codecs via --config
|
|
356
|
+
--checksums <file> External checksums manifest from "wp-release checksums" or the
|
|
357
|
+
WordPress.org checksums API (optional; otherwise reads embedded manifest)
|
|
341
358
|
--zip <file|url> Check the files of this ZIP, a local file or an
|
|
342
359
|
http(s) URL such as the ZIP that WordPress.org
|
|
343
360
|
builds for a release, instead of <source-dir>
|
|
@@ -381,6 +398,8 @@ file that isn't a matching copy is reported as extra. See --embedded for
|
|
|
381
398
|
where the copies are looked for.`,
|
|
382
399
|
options: {
|
|
383
400
|
checksums: { type: 'string' },
|
|
401
|
+
'checksums-format': { type: 'string' },
|
|
402
|
+
'checksums-file': { type: 'string' },
|
|
384
403
|
zip: { type: 'string' },
|
|
385
404
|
'root-folder': { type: 'string' },
|
|
386
405
|
embedded: { type: 'string' },
|
|
@@ -389,42 +408,59 @@ where the copies are looked for.`,
|
|
|
389
408
|
...sourceDirArgs,
|
|
390
409
|
exclude: { type: 'string', multiple: true },
|
|
391
410
|
},
|
|
392
|
-
async run({ values, positionals, cwd, logger }) {
|
|
411
|
+
async run({ values, positionals, cwd, configFile, logger }) {
|
|
393
412
|
const zip = getString(values, 'zip');
|
|
394
413
|
const sourceArg = positionals.length
|
|
395
414
|
? getSourceDirArg(values, positionals)
|
|
396
415
|
: getString(values, 'source-dir');
|
|
397
416
|
const checksums = getString(values, 'checksums');
|
|
398
|
-
if (!checksums) {
|
|
399
|
-
throw new ValidationError('Pass the checksums manifest with --checksums <file>.');
|
|
400
|
-
}
|
|
401
417
|
if (zip && sourceArg) {
|
|
402
418
|
throw new ValidationError('Pass either --zip or a source directory, not both.');
|
|
403
419
|
}
|
|
404
|
-
|
|
420
|
+
// Only load caller-selected configuration, never configuration inside the package.
|
|
421
|
+
const loaded = configFile
|
|
422
|
+
? await loadChangesetConfig({ cwd, configFile })
|
|
423
|
+
: undefined;
|
|
424
|
+
const integrity = loaded?.config.integrity;
|
|
425
|
+
const codec = getString(values, 'checksums-format') ?? integrity?.codec;
|
|
426
|
+
const file = checkChecksumsFile(getString(values, 'checksums-file') ??
|
|
427
|
+
integrity?.file ??
|
|
428
|
+
resolveManifestCodec(codec).filename);
|
|
405
429
|
const publicKey = getString(values, 'public-key');
|
|
406
430
|
const format = getString(values, 'signature-format') ?? DEFAULT_SIGNATURE_FORMAT;
|
|
407
431
|
if (!isSignatureFormat(format)) {
|
|
408
432
|
throw new ValidationError(`Unknown signature format ${format}, use ${Object.keys(SIGNATURE_FORMATS).join(', ')}.`);
|
|
409
433
|
}
|
|
410
|
-
if (publicKey) {
|
|
411
|
-
await checkSignature(checksumsPath, publicKey, format);
|
|
412
|
-
logger.info(`Signature of ${checksumsPath} is valid.`);
|
|
413
|
-
}
|
|
414
|
-
const manifest = await readChecksums(checksumsPath);
|
|
415
434
|
const target = await resolveTarget({
|
|
416
435
|
sourceDir: resolve(cwd, sourceArg ?? '.'),
|
|
417
436
|
zip,
|
|
418
437
|
cwd,
|
|
419
438
|
rootFolder: getString(values, 'root-folder'),
|
|
420
439
|
});
|
|
440
|
+
const checksumsPath = checksums
|
|
441
|
+
? resolve(cwd, checksums)
|
|
442
|
+
: join(target.dir, file);
|
|
421
443
|
try {
|
|
444
|
+
if (publicKey) {
|
|
445
|
+
await checkSignature(checksumsPath, publicKey, format);
|
|
446
|
+
if (!checksums) {
|
|
447
|
+
for (const other of Object.keys(SIGNATURE_FORMATS)) {
|
|
448
|
+
if (other !== format &&
|
|
449
|
+
existsSync(getSignaturePath(checksumsPath, other))) {
|
|
450
|
+
await checkSignature(checksumsPath, publicKey, other);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
logger.info(`Signature of ${checksumsPath} is valid.`);
|
|
455
|
+
}
|
|
456
|
+
const manifest = await readIntegrity(checksumsPath, codec);
|
|
422
457
|
return await verifyTarget({
|
|
423
458
|
target,
|
|
424
459
|
manifest,
|
|
425
460
|
checksumsPath,
|
|
426
461
|
exclude: getStrings(values, 'exclude'),
|
|
427
|
-
embedded: getString(values, 'embedded')
|
|
462
|
+
embedded: getString(values, 'embedded') ??
|
|
463
|
+
(checksums ? undefined : file),
|
|
428
464
|
format,
|
|
429
465
|
logger,
|
|
430
466
|
});
|
package/dist/commands/zip.d.ts
CHANGED
|
@@ -8,9 +8,9 @@ export interface ZipResult extends ZipPackageResult {
|
|
|
8
8
|
version: string | null;
|
|
9
9
|
/** Zipped package, with --package. */
|
|
10
10
|
package?: string;
|
|
11
|
-
/**
|
|
11
|
+
/** External signature paths and package-relative embedded manifest signature paths. */
|
|
12
12
|
signatures?: string[];
|
|
13
|
-
/** Base64 public key of the signing key, with
|
|
13
|
+
/** Base64 public key of the signing key, with ZIP or manifest signing. */
|
|
14
14
|
publicKey?: string;
|
|
15
15
|
}
|
|
16
16
|
declare const _default: {
|
|
@@ -19,22 +19,34 @@ declare const _default: {
|
|
|
19
19
|
summary: string;
|
|
20
20
|
help: string;
|
|
21
21
|
options: {
|
|
22
|
-
|
|
22
|
+
'checksums-file': {
|
|
23
|
+
type: "string";
|
|
24
|
+
};
|
|
25
|
+
'checksums-format': {
|
|
26
|
+
type: "string";
|
|
27
|
+
};
|
|
28
|
+
'checksums-algorithms': {
|
|
23
29
|
type: "string";
|
|
30
|
+
multiple: true;
|
|
24
31
|
};
|
|
25
|
-
checksums: {
|
|
32
|
+
'embed-checksums': {
|
|
26
33
|
type: "boolean";
|
|
27
34
|
};
|
|
28
|
-
|
|
35
|
+
'no-embed-checksums': {
|
|
29
36
|
type: "boolean";
|
|
30
37
|
};
|
|
31
|
-
'sign-
|
|
32
|
-
type: "
|
|
33
|
-
|
|
38
|
+
'no-sign-checksums': {
|
|
39
|
+
type: "boolean";
|
|
40
|
+
};
|
|
41
|
+
sign: {
|
|
42
|
+
type: "boolean";
|
|
34
43
|
};
|
|
35
44
|
force: {
|
|
36
45
|
type: "boolean";
|
|
37
46
|
};
|
|
47
|
+
package: {
|
|
48
|
+
type: "string";
|
|
49
|
+
};
|
|
38
50
|
slug: {
|
|
39
51
|
type: "string";
|
|
40
52
|
};
|