pressship 0.1.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/LICENSE +21 -0
- package/README.md +160 -0
- package/dist/auth/login.d.ts +1 -0
- package/dist/auth/login.js +39 -0
- package/dist/auth/login.js.map +1 -0
- package/dist/auth/logout.d.ts +1 -0
- package/dist/auth/logout.js +12 -0
- package/dist/auth/logout.js.map +1 -0
- package/dist/auth/session.d.ts +12 -0
- package/dist/auth/session.js +72 -0
- package/dist/auth/session.js.map +1 -0
- package/dist/auth/whoami.d.ts +17 -0
- package/dist/auth/whoami.js +108 -0
- package/dist/auth/whoami.js.map +1 -0
- package/dist/checks/plugin-check-environment.d.ts +16 -0
- package/dist/checks/plugin-check-environment.js +209 -0
- package/dist/checks/plugin-check-environment.js.map +1 -0
- package/dist/checks/plugin-check.d.ts +16 -0
- package/dist/checks/plugin-check.js +176 -0
- package/dist/checks/plugin-check.js.map +1 -0
- package/dist/checks/readme-validator.d.ts +16 -0
- package/dist/checks/readme-validator.js +84 -0
- package/dist/checks/readme-validator.js.map +1 -0
- package/dist/checks/summary.d.ts +3 -0
- package/dist/checks/summary.js +73 -0
- package/dist/checks/summary.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +66 -0
- package/dist/cli.js.map +1 -0
- package/dist/package/archive.d.ts +12 -0
- package/dist/package/archive.js +64 -0
- package/dist/package/archive.js.map +1 -0
- package/dist/package/ignore.d.ts +2 -0
- package/dist/package/ignore.js +48 -0
- package/dist/package/ignore.js.map +1 -0
- package/dist/plugin/discover.d.ts +2 -0
- package/dist/plugin/discover.js +67 -0
- package/dist/plugin/discover.js.map +1 -0
- package/dist/plugin/headers.d.ts +3 -0
- package/dist/plugin/headers.js +54 -0
- package/dist/plugin/headers.js.map +1 -0
- package/dist/plugin/readme.d.ts +3 -0
- package/dist/plugin/readme.js +103 -0
- package/dist/plugin/readme.js.map +1 -0
- package/dist/svn/release.d.ts +16 -0
- package/dist/svn/release.js +147 -0
- package/dist/svn/release.js.map +1 -0
- package/dist/types.d.ts +55 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/ui.d.ts +15 -0
- package/dist/ui.js +50 -0
- package/dist/ui.js.map +1 -0
- package/dist/utils/format.d.ts +2 -0
- package/dist/utils/format.js +21 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/paths.d.ts +9 -0
- package/dist/utils/paths.js +48 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/slug.d.ts +2 -0
- package/dist/utils/slug.js +15 -0
- package/dist/utils/slug.js.map +1 -0
- package/dist/wordpress-org/submit.d.ts +13 -0
- package/dist/wordpress-org/submit.js +169 -0
- package/dist/wordpress-org/submit.js.map +1 -0
- package/package.json +56 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { login } from "./auth/login.js";
|
|
4
|
+
import { logout } from "./auth/logout.js";
|
|
5
|
+
import { whoami } from "./auth/whoami.js";
|
|
6
|
+
import { submit } from "./wordpress-org/submit.js";
|
|
7
|
+
import { release } from "./svn/release.js";
|
|
8
|
+
const program = new Command();
|
|
9
|
+
program
|
|
10
|
+
.name("pressship")
|
|
11
|
+
.description("Submit and release WordPress.org plugins from the command line.")
|
|
12
|
+
.version("0.1.0");
|
|
13
|
+
program
|
|
14
|
+
.command("login")
|
|
15
|
+
.description("Open a browser and save a WordPress.org login session.")
|
|
16
|
+
.action(run(login));
|
|
17
|
+
program
|
|
18
|
+
.command("logout")
|
|
19
|
+
.description("Remove the saved WordPress.org browser session.")
|
|
20
|
+
.action(run(logout));
|
|
21
|
+
program
|
|
22
|
+
.command("whoami")
|
|
23
|
+
.description("Print the WordPress.org username for the saved login session.")
|
|
24
|
+
.option("--json", "Print account details as JSON")
|
|
25
|
+
.action((options) => run(() => whoami(options))());
|
|
26
|
+
program
|
|
27
|
+
.command("submit")
|
|
28
|
+
.description("Validate, package, and submit a plugin zip to WordPress.org for review.")
|
|
29
|
+
.argument("[plugin-path]", "Path to the WordPress plugin directory")
|
|
30
|
+
.option("--dry-run", "Run validation and packaging without uploading")
|
|
31
|
+
.option("--skip-plugin-check", "Skip `wp plugin check`")
|
|
32
|
+
.option("--skip-readme-validator", "Skip the remote WordPress.org readme validator")
|
|
33
|
+
.option("--output-dir <path>", "Directory where the submission zip should be written")
|
|
34
|
+
.option("--wp-path <path>", "WordPress installation path for `wp plugin check`")
|
|
35
|
+
.option("--ignore <glob>", "Ignore files in the package; repeat for multiple globs", collectValues, [])
|
|
36
|
+
.option("-y, --yes", "Continue without interactive confirmations where possible")
|
|
37
|
+
.action((pluginPath, options) => run(() => submit(pluginPath, options))());
|
|
38
|
+
program
|
|
39
|
+
.command("release")
|
|
40
|
+
.description("Publish an approved plugin release to WordPress.org SVN trunk and tags.")
|
|
41
|
+
.argument("[plugin-path]", "Path to the WordPress plugin directory")
|
|
42
|
+
.option("--slug <slug>", "Approved WordPress.org plugin slug")
|
|
43
|
+
.option("--version <version>", "Version tag to create")
|
|
44
|
+
.option("--svn-dir <path>", "Local SVN working copy directory")
|
|
45
|
+
.option("--username <username>", "WordPress.org SVN username")
|
|
46
|
+
.option("-m, --message <message>", "SVN commit message")
|
|
47
|
+
.option("--ignore <glob>", "Ignore files in the SVN release; repeat for multiple globs", collectValues, [])
|
|
48
|
+
.option("--dry-run", "Print the SVN command plan without changing SVN")
|
|
49
|
+
.option("-y, --yes", "Commit without the final confirmation prompt")
|
|
50
|
+
.action((pluginPath, options) => run(() => release(pluginPath, options))());
|
|
51
|
+
await program.parseAsync(process.argv);
|
|
52
|
+
function run(action) {
|
|
53
|
+
return async () => {
|
|
54
|
+
try {
|
|
55
|
+
await action();
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
59
|
+
process.exitCode = 1;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function collectValues(value, previous) {
|
|
64
|
+
return [...previous, value];
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PackageResult, PluginProject } from "../types.js";
|
|
2
|
+
export type PackageOptions = {
|
|
3
|
+
outputDir?: string;
|
|
4
|
+
ignore?: string[];
|
|
5
|
+
};
|
|
6
|
+
export type StageResult = {
|
|
7
|
+
path: string;
|
|
8
|
+
files: string[];
|
|
9
|
+
};
|
|
10
|
+
export declare function createPluginZip(project: PluginProject, options?: PackageOptions): Promise<PackageResult>;
|
|
11
|
+
export declare function listPackageFiles(rootDir: string, options?: Pick<PackageOptions, "ignore">): Promise<string[]>;
|
|
12
|
+
export declare function stagePluginDirectory(project: PluginProject, options?: PackageOptions): Promise<StageResult>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createWriteStream } from "node:fs";
|
|
2
|
+
import { cp, mkdir, rm, stat } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import fg from "fast-glob";
|
|
5
|
+
import { ZipFile } from "yazl";
|
|
6
|
+
import { getDefaultBuildDir } from "../utils/paths.js";
|
|
7
|
+
import { createIgnoreFilter } from "./ignore.js";
|
|
8
|
+
const maxSubmissionSizeBytes = 10 * 1024 * 1024;
|
|
9
|
+
export async function createPluginZip(project, options = {}) {
|
|
10
|
+
const outputDir = path.resolve(options.outputDir ?? getDefaultBuildDir());
|
|
11
|
+
await mkdir(outputDir, { recursive: true });
|
|
12
|
+
const zipPath = path.join(outputDir, `${project.slug}.zip`);
|
|
13
|
+
const files = await listPackageFiles(project.rootDir, { ignore: options.ignore });
|
|
14
|
+
await writeZip(project.rootDir, project.slug, files, zipPath);
|
|
15
|
+
const zipStat = await stat(zipPath);
|
|
16
|
+
if (zipStat.size > maxSubmissionSizeBytes) {
|
|
17
|
+
throw new Error(`Submission zip is larger than 10 MB (${zipStat.size} bytes). WordPress.org requires uploads under 10 MB.`);
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
zipPath,
|
|
21
|
+
sizeBytes: zipStat.size,
|
|
22
|
+
files,
|
|
23
|
+
topLevelFolder: project.slug
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export async function listPackageFiles(rootDir, options = {}) {
|
|
27
|
+
const includeFile = await createIgnoreFilter(rootDir, options.ignore);
|
|
28
|
+
const files = await fg("**/*", {
|
|
29
|
+
cwd: rootDir,
|
|
30
|
+
onlyFiles: true,
|
|
31
|
+
dot: true,
|
|
32
|
+
unique: true
|
|
33
|
+
});
|
|
34
|
+
return files.filter(includeFile).sort();
|
|
35
|
+
}
|
|
36
|
+
export async function stagePluginDirectory(project, options = {}) {
|
|
37
|
+
const stageRoot = path.join(path.resolve(options.outputDir ?? getDefaultBuildDir()), "plugin-check");
|
|
38
|
+
const stagePath = path.join(stageRoot, project.slug);
|
|
39
|
+
const files = await listPackageFiles(project.rootDir, { ignore: options.ignore });
|
|
40
|
+
await rm(stagePath, { recursive: true, force: true });
|
|
41
|
+
await mkdir(stagePath, { recursive: true });
|
|
42
|
+
for (const file of files) {
|
|
43
|
+
const source = path.join(project.rootDir, file);
|
|
44
|
+
const destination = path.join(stagePath, file);
|
|
45
|
+
await mkdir(path.dirname(destination), { recursive: true });
|
|
46
|
+
await cp(source, destination);
|
|
47
|
+
}
|
|
48
|
+
return { path: stagePath, files };
|
|
49
|
+
}
|
|
50
|
+
async function writeZip(rootDir, topLevelFolder, files, zipPath) {
|
|
51
|
+
await new Promise((resolve, reject) => {
|
|
52
|
+
const output = createWriteStream(zipPath);
|
|
53
|
+
const archive = new ZipFile();
|
|
54
|
+
output.on("close", resolve);
|
|
55
|
+
output.on("error", reject);
|
|
56
|
+
archive.outputStream.on("error", reject);
|
|
57
|
+
archive.outputStream.pipe(output);
|
|
58
|
+
for (const relativePath of files) {
|
|
59
|
+
archive.addFile(path.join(rootDir, relativePath), path.posix.join(topLevelFolder, relativePath.split(path.sep).join("/")));
|
|
60
|
+
}
|
|
61
|
+
archive.end();
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=archive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archive.js","sourceRoot":"","sources":["../../src/package/archive.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import ignore from "ignore";
|
|
4
|
+
import { pathExists } from "../utils/paths.js";
|
|
5
|
+
export const defaultIgnorePatterns = [
|
|
6
|
+
".DS_Store",
|
|
7
|
+
".git",
|
|
8
|
+
".git/**",
|
|
9
|
+
".gitignore",
|
|
10
|
+
".github",
|
|
11
|
+
".github/**",
|
|
12
|
+
".idea",
|
|
13
|
+
".idea/**",
|
|
14
|
+
".vscode",
|
|
15
|
+
".vscode/**",
|
|
16
|
+
".env",
|
|
17
|
+
".env.*",
|
|
18
|
+
"node_modules",
|
|
19
|
+
"node_modules/**",
|
|
20
|
+
"dist",
|
|
21
|
+
"dist/**",
|
|
22
|
+
"build",
|
|
23
|
+
"build/**",
|
|
24
|
+
"coverage",
|
|
25
|
+
"coverage/**",
|
|
26
|
+
"tests",
|
|
27
|
+
"tests/**",
|
|
28
|
+
"*.log",
|
|
29
|
+
"*.zip",
|
|
30
|
+
".pressportignore",
|
|
31
|
+
".pressshipignore"
|
|
32
|
+
];
|
|
33
|
+
export async function createIgnoreFilter(rootDir, extraPatterns = []) {
|
|
34
|
+
const matcher = ignore().add(defaultIgnorePatterns).add(extraPatterns);
|
|
35
|
+
const legacyIgnoreFile = path.join(rootDir, ".pressportignore");
|
|
36
|
+
const ignoreFile = path.join(rootDir, ".pressshipignore");
|
|
37
|
+
if (pathExists(legacyIgnoreFile)) {
|
|
38
|
+
matcher.add(await readFile(legacyIgnoreFile, "utf8"));
|
|
39
|
+
}
|
|
40
|
+
if (pathExists(ignoreFile)) {
|
|
41
|
+
matcher.add(await readFile(ignoreFile, "utf8"));
|
|
42
|
+
}
|
|
43
|
+
return (relativePath) => {
|
|
44
|
+
const normalized = relativePath.split(path.sep).join("/");
|
|
45
|
+
return !matcher.ignores(normalized);
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=ignore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ignore.js","sourceRoot":"","sources":["../../src/package/ignore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,WAAW;IACX,MAAM;IACN,SAAS;IACT,YAAY;IACZ,SAAS;IACT,YAAY;IACZ,OAAO;IACP,UAAU;IACV,SAAS;IACT,YAAY;IACZ,MAAM;IACN,QAAQ;IACR,cAAc;IACd,iBAAiB;IACjB,MAAM;IACN,SAAS;IACT,OAAO;IACP,UAAU;IACV,UAAU;IACV,aAAa;IACb,OAAO;IACP,UAAU;IACV,OAAO;IACP,OAAO;IACP,kBAAkB;IAClB,kBAAkB;CACnB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,OAAe,EAAE,gBAA0B,EAAE;IACpF,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IACvE,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;IAChE,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;IAE1D,IAAI,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,MAAM,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,CAAC;IACxD,CAAC;IACD,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3B,OAAO,CAAC,GAAG,CAAC,MAAM,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;IAClD,CAAC;IAED,OAAO,CAAC,YAAoB,EAAW,EAAE;QACvC,MAAM,UAAU,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC1D,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACtC,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fg from "fast-glob";
|
|
4
|
+
import { assertPluginHeaders, parsePluginHeaders } from "./headers.js";
|
|
5
|
+
import { parseReadme } from "./readme.js";
|
|
6
|
+
import { inferSlug } from "../utils/slug.js";
|
|
7
|
+
const ignoredDirectories = ["node_modules", "vendor", ".git", "dist", "build", "tests"];
|
|
8
|
+
export async function discoverPluginProject(inputPath) {
|
|
9
|
+
const rootDir = path.resolve(inputPath);
|
|
10
|
+
const mainFile = await findMainPluginFile(rootDir);
|
|
11
|
+
const mainContents = await readFile(mainFile, "utf8");
|
|
12
|
+
const headers = assertPluginHeaders(parsePluginHeaders(mainContents));
|
|
13
|
+
const readmePath = await findReadme(rootDir);
|
|
14
|
+
const readme = readmePath ? parseReadme(await readFile(readmePath, "utf8")) : undefined;
|
|
15
|
+
const slug = inferSlug(headers.pluginName, headers.textDomain);
|
|
16
|
+
return {
|
|
17
|
+
rootDir,
|
|
18
|
+
mainFile,
|
|
19
|
+
headers,
|
|
20
|
+
readmePath,
|
|
21
|
+
readme,
|
|
22
|
+
slug,
|
|
23
|
+
version: headers.version
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
async function findMainPluginFile(rootDir) {
|
|
27
|
+
const phpFiles = await fg("**/*.php", {
|
|
28
|
+
cwd: rootDir,
|
|
29
|
+
absolute: true,
|
|
30
|
+
onlyFiles: true,
|
|
31
|
+
ignore: ignoredDirectories.map((directory) => `${directory}/**`)
|
|
32
|
+
});
|
|
33
|
+
const candidates = [];
|
|
34
|
+
for (const file of phpFiles) {
|
|
35
|
+
const contents = await readFile(file, "utf8");
|
|
36
|
+
const headers = parsePluginHeaders(contents);
|
|
37
|
+
if (headers.pluginName) {
|
|
38
|
+
candidates.push({ file, score: scoreMainFile(rootDir, file) });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
candidates.sort((a, b) => b.score - a.score);
|
|
42
|
+
if (!candidates[0]) {
|
|
43
|
+
throw new Error(`No plugin main file found in ${rootDir}.`);
|
|
44
|
+
}
|
|
45
|
+
return candidates[0].file;
|
|
46
|
+
}
|
|
47
|
+
async function findReadme(rootDir) {
|
|
48
|
+
const matches = await fg(["readme.txt", "README.txt"], {
|
|
49
|
+
cwd: rootDir,
|
|
50
|
+
absolute: true,
|
|
51
|
+
onlyFiles: true,
|
|
52
|
+
caseSensitiveMatch: false
|
|
53
|
+
});
|
|
54
|
+
return matches[0];
|
|
55
|
+
}
|
|
56
|
+
function scoreMainFile(rootDir, file) {
|
|
57
|
+
const relative = path.relative(rootDir, file);
|
|
58
|
+
let score = 0;
|
|
59
|
+
if (!relative.includes(path.sep)) {
|
|
60
|
+
score += 10;
|
|
61
|
+
}
|
|
62
|
+
if (path.basename(file, ".php") === path.basename(rootDir)) {
|
|
63
|
+
score += 5;
|
|
64
|
+
}
|
|
65
|
+
return score;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=discover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover.js","sourceRoot":"","sources":["../../src/plugin/discover.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const headerMap = new Map([
|
|
2
|
+
["Plugin Name", "pluginName"],
|
|
3
|
+
["Plugin URI", "pluginUri"],
|
|
4
|
+
["Description", "description"],
|
|
5
|
+
["Version", "version"],
|
|
6
|
+
["Author", "author"],
|
|
7
|
+
["Author URI", "authorUri"],
|
|
8
|
+
["Text Domain", "textDomain"],
|
|
9
|
+
["Domain Path", "domainPath"],
|
|
10
|
+
["Requires at least", "requiresAtLeast"],
|
|
11
|
+
["Requires PHP", "requiresPhp"],
|
|
12
|
+
["Update URI", "updateUri"],
|
|
13
|
+
["License", "license"],
|
|
14
|
+
["License URI", "licenseUri"]
|
|
15
|
+
]);
|
|
16
|
+
export function parsePluginHeaders(contents) {
|
|
17
|
+
const headerBlock = contents.slice(0, 8192);
|
|
18
|
+
const headers = {};
|
|
19
|
+
for (const [label, key] of headerMap) {
|
|
20
|
+
const pattern = new RegExp(`^[ \\t/*#@]*${escapeRegExp(label)}\\s*:\\s*(.+?)\\s*$`, "im");
|
|
21
|
+
const match = headerBlock.match(pattern);
|
|
22
|
+
if (match?.[1]) {
|
|
23
|
+
headers[key] = normalizeHeaderValue(match[1]);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return headers;
|
|
27
|
+
}
|
|
28
|
+
export function assertPluginHeaders(headers) {
|
|
29
|
+
if (!headers.pluginName) {
|
|
30
|
+
throw new Error("No WordPress plugin header found. Expected a PHP file with `Plugin Name:`.");
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
pluginName: headers.pluginName,
|
|
34
|
+
pluginUri: headers.pluginUri,
|
|
35
|
+
description: headers.description,
|
|
36
|
+
version: headers.version,
|
|
37
|
+
author: headers.author,
|
|
38
|
+
authorUri: headers.authorUri,
|
|
39
|
+
textDomain: headers.textDomain,
|
|
40
|
+
domainPath: headers.domainPath,
|
|
41
|
+
requiresAtLeast: headers.requiresAtLeast,
|
|
42
|
+
requiresPhp: headers.requiresPhp,
|
|
43
|
+
updateUri: headers.updateUri,
|
|
44
|
+
license: headers.license,
|
|
45
|
+
licenseUri: headers.licenseUri
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function normalizeHeaderValue(value) {
|
|
49
|
+
return value.replace(/\s+\*\/$/, "").trim();
|
|
50
|
+
}
|
|
51
|
+
function escapeRegExp(value) {
|
|
52
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=headers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headers.js","sourceRoot":"","sources":["../../src/plugin/headers.ts"],"names":[],"mappings":"AAEA,MAAM,SAAS,GAAG,IAAI,GAAG,CAA8B;IACrD,CAAC,aAAa,EAAE,YAAY,CAAC;IAC7B,CAAC,YAAY,EAAE,WAAW,CAAC;IAC3B,CAAC,aAAa,EAAE,aAAa,CAAC;IAC9B,CAAC,SAAS,EAAE,SAAS,CAAC;IACtB,CAAC,QAAQ,EAAE,QAAQ,CAAC;IACpB,CAAC,YAAY,EAAE,WAAW,CAAC;IAC3B,CAAC,aAAa,EAAE,YAAY,CAAC;IAC7B,CAAC,aAAa,EAAE,YAAY,CAAC;IAC7B,CAAC,mBAAmB,EAAE,iBAAiB,CAAC;IACxC,CAAC,cAAc,EAAE,aAAa,CAAC;IAC/B,CAAC,YAAY,EAAE,WAAW,CAAC;IAC3B,CAAC,SAAS,EAAE,SAAS,CAAC;IACtB,CAAC,aAAa,EAAE,YAAY,CAAC;CAC9B,CAAC,CAAC;AAEH,MAAM,UAAU,kBAAkB,CAAC,QAAgB;IACjD,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAC5C,MAAM,OAAO,GAA2B,EAAE,CAAC;IAE3C,KAAK,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,SAAS,EAAE,CAAC;QACrC,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,eAAe,YAAY,CAAC,KAAK,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAC;QAC1F,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACzC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAA+B;IACjE,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,4EAA4E,CAAC,CAAC;IAChG,CAAC;IAED,OAAO;QACL,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,eAAe,EAAE,OAAO,CAAC,eAAe;QACxC,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,UAAU,EAAE,OAAO,CAAC,UAAU;KAC/B,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAa;IACzC,OAAO,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AAC9C,CAAC;AAED,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACtD,CAAC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const readmeHeaderMap = {
|
|
2
|
+
contributors: "contributors",
|
|
3
|
+
tags: "tags",
|
|
4
|
+
"requires at least": "requiresAtLeast",
|
|
5
|
+
"tested up to": "testedUpTo",
|
|
6
|
+
"stable tag": "stableTag",
|
|
7
|
+
"requires php": "requiresPhp",
|
|
8
|
+
license: "license",
|
|
9
|
+
"license uri": "licenseUri"
|
|
10
|
+
};
|
|
11
|
+
export function parseReadme(contents) {
|
|
12
|
+
const lines = contents.split(/\r?\n/);
|
|
13
|
+
const metadata = {};
|
|
14
|
+
const title = lines.find((line) => /^===\s+.+?\s+===$/.test(line.trim()));
|
|
15
|
+
if (title) {
|
|
16
|
+
metadata.name = title.replace(/^===\s+/, "").replace(/\s+===$/, "").trim();
|
|
17
|
+
}
|
|
18
|
+
for (const line of lines) {
|
|
19
|
+
const match = line.match(/^([^:\n]+):\s*(.*?)\s*$/);
|
|
20
|
+
if (!match) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const key = readmeHeaderMap[match[1].trim().toLowerCase()];
|
|
24
|
+
if (!key) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const value = match[2].trim();
|
|
28
|
+
if (key === "contributors" || key === "tags") {
|
|
29
|
+
metadata[key] = splitCsv(value);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
metadata[key] = value;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return metadata;
|
|
36
|
+
}
|
|
37
|
+
export function validateReadmeLocally(contents, metadata) {
|
|
38
|
+
const findings = [];
|
|
39
|
+
if (!metadata.name) {
|
|
40
|
+
findings.push({
|
|
41
|
+
severity: "error",
|
|
42
|
+
code: "readme.missing_title",
|
|
43
|
+
message: "readme.txt must start with a plugin title like `=== Plugin Name ===`."
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
if (!metadata.contributors || metadata.contributors.length === 0) {
|
|
47
|
+
findings.push({
|
|
48
|
+
severity: "warning",
|
|
49
|
+
code: "readme.missing_contributors",
|
|
50
|
+
message: "readme.txt should include a Contributors header."
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (!metadata.tags || metadata.tags.length === 0) {
|
|
54
|
+
findings.push({
|
|
55
|
+
severity: "warning",
|
|
56
|
+
code: "readme.missing_tags",
|
|
57
|
+
message: "readme.txt should include Tags for the plugin directory."
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
else if (metadata.tags.length > 5) {
|
|
61
|
+
findings.push({
|
|
62
|
+
severity: "error",
|
|
63
|
+
code: "readme.too_many_tags",
|
|
64
|
+
message: "WordPress.org allows at most 5 readme tags."
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
if (!metadata.stableTag) {
|
|
68
|
+
findings.push({
|
|
69
|
+
severity: "error",
|
|
70
|
+
code: "readme.missing_stable_tag",
|
|
71
|
+
message: "readme.txt must include a Stable tag header."
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
if (!metadata.requiresAtLeast) {
|
|
75
|
+
findings.push({
|
|
76
|
+
severity: "warning",
|
|
77
|
+
code: "readme.missing_requires_at_least",
|
|
78
|
+
message: "readme.txt should include a Requires at least header."
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (!metadata.testedUpTo) {
|
|
82
|
+
findings.push({
|
|
83
|
+
severity: "warning",
|
|
84
|
+
code: "readme.missing_tested_up_to",
|
|
85
|
+
message: "readme.txt should include a Tested up to header."
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
if (!/^==\s+Description\s+==/im.test(contents)) {
|
|
89
|
+
findings.push({
|
|
90
|
+
severity: "warning",
|
|
91
|
+
code: "readme.missing_description_section",
|
|
92
|
+
message: "readme.txt should include a Description section."
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return findings;
|
|
96
|
+
}
|
|
97
|
+
function splitCsv(value) {
|
|
98
|
+
return value
|
|
99
|
+
.split(",")
|
|
100
|
+
.map((item) => item.trim())
|
|
101
|
+
.filter(Boolean);
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=readme.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readme.js","sourceRoot":"","sources":["../../src/plugin/readme.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { CommandPlan } from "../types.js";
|
|
3
|
+
declare const releaseOptionsSchema: z.ZodObject<{
|
|
4
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
5
|
+
version: z.ZodOptional<z.ZodString>;
|
|
6
|
+
svnDir: z.ZodOptional<z.ZodString>;
|
|
7
|
+
username: z.ZodOptional<z.ZodString>;
|
|
8
|
+
message: z.ZodOptional<z.ZodString>;
|
|
9
|
+
dryRun: z.ZodDefault<z.ZodBoolean>;
|
|
10
|
+
yes: z.ZodDefault<z.ZodBoolean>;
|
|
11
|
+
ignore: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
12
|
+
}, z.core.$strip>;
|
|
13
|
+
export type ReleaseOptions = z.input<typeof releaseOptionsSchema>;
|
|
14
|
+
export declare function release(pluginPath: string | undefined, rawOptions: ReleaseOptions): Promise<void>;
|
|
15
|
+
export declare function createReleaseCommandPlan(slug: string, svnDir: string, version: string, message: string, username?: string): CommandPlan[];
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { confirm, input } from "@inquirer/prompts";
|
|
2
|
+
import { execa } from "execa";
|
|
3
|
+
import { cp, mkdir, readdir, rm } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { listPackageFiles } from "../package/archive.js";
|
|
7
|
+
import { discoverPluginProject } from "../plugin/discover.js";
|
|
8
|
+
import { ui } from "../ui.js";
|
|
9
|
+
import { pathExists } from "../utils/paths.js";
|
|
10
|
+
const releaseOptionsSchema = z.object({
|
|
11
|
+
slug: z.string().optional(),
|
|
12
|
+
version: z.string().optional(),
|
|
13
|
+
svnDir: z.string().optional(),
|
|
14
|
+
username: z.string().optional(),
|
|
15
|
+
message: z.string().optional(),
|
|
16
|
+
dryRun: z.boolean().default(false),
|
|
17
|
+
yes: z.boolean().default(false),
|
|
18
|
+
ignore: z.array(z.string()).default([])
|
|
19
|
+
});
|
|
20
|
+
export async function release(pluginPath, rawOptions) {
|
|
21
|
+
const options = releaseOptionsSchema.parse(rawOptions);
|
|
22
|
+
ui.intro(options.dryRun ? "Dry-run SVN release" : "Release plugin to WordPress.org SVN");
|
|
23
|
+
const rootDir = path.resolve(pluginPath ?? (await input({ message: "Plugin directory", default: process.cwd() })));
|
|
24
|
+
const project = await ui.task("Discovering WordPress plugin", () => discoverPluginProject(rootDir), (value) => `Discovered ${value.headers.pluginName}`);
|
|
25
|
+
const slug = options.slug ?? project.slug;
|
|
26
|
+
const version = options.version ?? project.version;
|
|
27
|
+
if (!version) {
|
|
28
|
+
throw new Error("Could not infer a plugin version. Pass --version or add Version to the plugin header.");
|
|
29
|
+
}
|
|
30
|
+
const svnDir = path.resolve(options.svnDir ?? path.join(process.cwd(), ".pressship-svn", slug));
|
|
31
|
+
const message = options.message ?? `Release ${slug} ${version}`;
|
|
32
|
+
const plan = createReleaseCommandPlan(slug, svnDir, version, message, options.username);
|
|
33
|
+
ui.section("Release");
|
|
34
|
+
ui.keyValue("SVN", ui.path(`https://plugins.svn.wordpress.org/${slug}`));
|
|
35
|
+
ui.keyValue("Working copy", ui.path(svnDir));
|
|
36
|
+
ui.keyValue("Version", ui.value(version));
|
|
37
|
+
if (options.dryRun) {
|
|
38
|
+
ui.section("Dry-run command plan");
|
|
39
|
+
for (const command of plan) {
|
|
40
|
+
console.log(` ${ui.muted(formatCommand(command))}`);
|
|
41
|
+
}
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
await ui.task("Preparing SVN working copy", () => ensureWorkingCopy(slug, svnDir, options.username));
|
|
45
|
+
await ui.task("Syncing plugin files to trunk", () => syncTrunk(rootDir, path.join(svnDir, "trunk"), options.ignore));
|
|
46
|
+
await ui.task("Adding changed files to SVN", () => runSvn(["add", "--force", "trunk"], svnDir));
|
|
47
|
+
await ui.task("Removing deleted files from SVN", () => deleteMissingFiles(svnDir));
|
|
48
|
+
await ui.task(`Creating tag ${version}`, () => createTag(version, svnDir));
|
|
49
|
+
const status = await runSvn(["status"], svnDir, { capture: true });
|
|
50
|
+
ui.section("SVN status");
|
|
51
|
+
console.log(status || ui.muted("No SVN changes detected."));
|
|
52
|
+
if (!status.trim()) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (!options.yes) {
|
|
56
|
+
const shouldCommit = await confirm({ message: "Commit these SVN changes?", default: false });
|
|
57
|
+
if (!shouldCommit) {
|
|
58
|
+
throw new Error("SVN release cancelled before commit.");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
await ui.task("Committing SVN release", () => runSvn(["commit", "-m", message, ...usernameArgs(options.username)], svnDir));
|
|
62
|
+
}
|
|
63
|
+
export function createReleaseCommandPlan(slug, svnDir, version, message, username) {
|
|
64
|
+
return [
|
|
65
|
+
{
|
|
66
|
+
command: "svn",
|
|
67
|
+
args: ["checkout", `https://plugins.svn.wordpress.org/${slug}`, svnDir, ...usernameArgs(username)]
|
|
68
|
+
},
|
|
69
|
+
{ command: "svn", args: ["add", "--force", "trunk"], cwd: svnDir },
|
|
70
|
+
{ command: "svn", args: ["copy", "trunk", `tags/${version}`], cwd: svnDir },
|
|
71
|
+
{ command: "svn", args: ["status"], cwd: svnDir },
|
|
72
|
+
{ command: "svn", args: ["commit", "-m", message, ...usernameArgs(username)], cwd: svnDir }
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
async function ensureWorkingCopy(slug, svnDir, username) {
|
|
76
|
+
await mkdir(path.dirname(svnDir), { recursive: true });
|
|
77
|
+
try {
|
|
78
|
+
await runSvn(["info"], svnDir, { capture: true });
|
|
79
|
+
await runSvn(["update"], svnDir);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
await runSvn(["checkout", `https://plugins.svn.wordpress.org/${slug}`, svnDir, ...usernameArgs(username)], process.cwd());
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function syncTrunk(pluginRoot, trunkDir, ignore) {
|
|
86
|
+
await mkdir(trunkDir, { recursive: true });
|
|
87
|
+
await emptyDirectory(trunkDir);
|
|
88
|
+
const files = await listPackageFiles(pluginRoot, { ignore });
|
|
89
|
+
for (const file of files) {
|
|
90
|
+
const source = path.join(pluginRoot, file);
|
|
91
|
+
const destination = path.join(trunkDir, file);
|
|
92
|
+
await mkdir(path.dirname(destination), { recursive: true });
|
|
93
|
+
await cp(source, destination);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async function emptyDirectory(directory) {
|
|
97
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
98
|
+
for (const entry of entries) {
|
|
99
|
+
if (entry.name === ".svn") {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
await rm(path.join(directory, entry.name), { recursive: true, force: true });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async function deleteMissingFiles(svnDir) {
|
|
106
|
+
const status = await runSvn(["status"], svnDir, { capture: true });
|
|
107
|
+
const missing = status
|
|
108
|
+
.split(/\r?\n/)
|
|
109
|
+
.filter((line) => line.startsWith("!"))
|
|
110
|
+
.map((line) => line.slice(8).trim())
|
|
111
|
+
.filter(Boolean);
|
|
112
|
+
for (const file of missing) {
|
|
113
|
+
await runSvn(["delete", file], svnDir);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async function createTag(version, svnDir) {
|
|
117
|
+
const tagPath = path.join(svnDir, "tags", version);
|
|
118
|
+
if (pathExists(tagPath)) {
|
|
119
|
+
throw new Error(`SVN tag tags/${version} already exists. Choose a new version; tags should not be overwritten.`);
|
|
120
|
+
}
|
|
121
|
+
await mkdir(path.dirname(tagPath), { recursive: true });
|
|
122
|
+
await runSvn(["copy", "trunk", `tags/${version}`], svnDir);
|
|
123
|
+
}
|
|
124
|
+
async function runSvn(args, cwd, options = {}) {
|
|
125
|
+
const result = await execa("svn", args, {
|
|
126
|
+
cwd,
|
|
127
|
+
reject: false,
|
|
128
|
+
stdout: options.capture ? "pipe" : "inherit",
|
|
129
|
+
stderr: options.capture ? "pipe" : "inherit"
|
|
130
|
+
});
|
|
131
|
+
if (result.exitCode !== 0) {
|
|
132
|
+
throw new Error(result.stderr || result.stdout || `svn ${args.join(" ")} failed.`);
|
|
133
|
+
}
|
|
134
|
+
return `${result.stdout ?? ""}\n${result.stderr ?? ""}`.trim();
|
|
135
|
+
}
|
|
136
|
+
function usernameArgs(username) {
|
|
137
|
+
return username ? ["--username", username] : [];
|
|
138
|
+
}
|
|
139
|
+
function formatCommand(command) {
|
|
140
|
+
const prefix = command.cwd ? `(cd ${command.cwd} && ` : "";
|
|
141
|
+
const suffix = command.cwd ? ")" : "";
|
|
142
|
+
return `${prefix}${command.command} ${command.args.map(quoteArg).join(" ")}${suffix}`;
|
|
143
|
+
}
|
|
144
|
+
function quoteArg(value) {
|
|
145
|
+
return /\s/.test(value) ? JSON.stringify(value) : value;
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=release.js.map
|