@wpelevator/wp-release 0.10.0 → 0.12.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.
@@ -0,0 +1,89 @@
1
+ import { readChangelogSection, getLatestChangelogVersion, } from "./changelog.js";
2
+ import { assertKnownPackages, toChangesetPackage, } from "./changeset-packages.js";
3
+ import { ValidationError } from "./errors.js";
4
+ import { compareVersions, isVersion } from "./versions.js";
5
+ /**
6
+ * Pick the packages to tag.
7
+ *
8
+ * @param packages Configured packages.
9
+ * @param names Packages passed with --package, empty for all.
10
+ * @param version Expected version passed with --version, if any.
11
+ * @return Selected packages in the configured order.
12
+ * @throws {ValidationError} For unknown packages, or an expected version without a single package.
13
+ */
14
+ export function selectTagPackages(packages, names, version) {
15
+ if (undefined !== version && 1 !== names.length) {
16
+ throw new ValidationError('An expected version requires exactly one package.');
17
+ }
18
+ assertKnownPackages(packages, names);
19
+ return names.length
20
+ ? packages.filter((pkg) => names.includes(pkg.name))
21
+ : packages;
22
+ }
23
+ /**
24
+ * Get the version to tag. Packages versioned by their git tags have no
25
+ * version file, so the expected version is the version to tag, or else the
26
+ * latest CHANGELOG.md version.
27
+ *
28
+ * @param pkg Configured package.
29
+ * @param expected Expected version from --version, if any.
30
+ * @return Version to tag.
31
+ */
32
+ export async function getTagVersion(pkg, expected) {
33
+ if ('git-tag' !== pkg.versionConfig.from.kind) {
34
+ return pkg.currentVersion;
35
+ }
36
+ return (expected ??
37
+ (await getLatestChangelogVersion(pkg.directory, pkg.currentVersion)));
38
+ }
39
+ /**
40
+ * Find the tag of the newest release before a version.
41
+ *
42
+ * @param repository Repository with the tags.
43
+ * @param prefix Tag prefix of the package, like `plugins/alpha/`.
44
+ * @param version Version being released.
45
+ * @return Previous tag, or undefined for the first release.
46
+ */
47
+ export async function getPreviousTag(repository, prefix, version) {
48
+ const tags = await repository.listTags(prefix);
49
+ return tags
50
+ .filter((tag) => tag.startsWith(prefix))
51
+ .map((tag) => ({ tag, version: tag.slice(prefix.length) }))
52
+ .filter((item) => isVersion(item.version) &&
53
+ compareVersions(item.version, version) < 0)
54
+ .sort((a, b) => compareVersions(b.version, a.version))[0]?.tag;
55
+ }
56
+ /**
57
+ * Build the release notes of a tag from its CHANGELOG.md section, or from the
58
+ * commits since the previous release without one, with the configured
59
+ * formatter.
60
+ *
61
+ * @param repository Repository with the release commits.
62
+ * @param config Repository release configuration.
63
+ * @param pkg Released package.
64
+ * @param release Version, tag and previous tag of the release.
65
+ * @param end Commit of the release: the tag, or HEAD before tagging.
66
+ * @return Release notes.
67
+ */
68
+ export async function getReleaseNotes(repository, config, pkg, release, end) {
69
+ const changelog = await readChangelogSection(pkg.directory, release.version);
70
+ const commits = await repository.getDirectoryCommits(release.previousTag ? `${release.previousTag}..${end}` : end, pkg.directory);
71
+ const notes = changelog ||
72
+ commits
73
+ .map((commit) => `- ${commit.subject} (${commit.shortHash})`)
74
+ .join('\n');
75
+ if (!config.releaseNotes) {
76
+ return notes;
77
+ }
78
+ const formatted = await config.releaseNotes({
79
+ package: toChangesetPackage(pkg),
80
+ ...release,
81
+ changelog,
82
+ commits,
83
+ notes,
84
+ });
85
+ if ('string' !== typeof formatted) {
86
+ throw new ValidationError(`The releaseNotes formatter didn't return a string for package "${pkg.name}".`);
87
+ }
88
+ return formatted.trim();
89
+ }
@@ -0,0 +1,20 @@
1
+ import type { ConfiguredPackage } from './changeset-packages.ts';
2
+ /**
3
+ * Format the tag of a package release.
4
+ *
5
+ * @param format Tag format with {name}, {slug}, {path}, {type} and {version}.
6
+ * @param pkg Configured package.
7
+ * @param version Released version, empty for the tag prefix of the package.
8
+ * @return Tag name.
9
+ */
10
+ export declare function formatTag(format: string, pkg: ConfiguredPackage, version: string): string;
11
+ /**
12
+ * Format the tag prefix of a package before its name is known, for packages
13
+ * versioned by their git tags.
14
+ *
15
+ * @param pattern Tag pattern of the version source.
16
+ * @param packagePath Package path relative to the package root.
17
+ * @param tagType Configured {type} of the package tags.
18
+ * @return Tag prefix.
19
+ */
20
+ export declare function formatTemplatePrefix(pattern: string, packagePath: string, tagType?: string): string;
@@ -0,0 +1,51 @@
1
+ import path from 'node:path';
2
+ /**
3
+ * Format the tag of a package release.
4
+ *
5
+ * @param format Tag format with {name}, {slug}, {path}, {type} and {version}.
6
+ * @param pkg Configured package.
7
+ * @param version Released version, empty for the tag prefix of the package.
8
+ * @return Tag name.
9
+ */
10
+ export function formatTag(format, pkg, version) {
11
+ const type = pkg.group.tagType ?? defaultTagType(pkg.path, pkg.type);
12
+ return format
13
+ .replaceAll('{name}', pkg.name)
14
+ .replaceAll('{slug}', '.' === pkg.path
15
+ ? path.basename(pkg.directory)
16
+ : path.basename(pkg.path))
17
+ .replaceAll('{path}', pkg.path)
18
+ .replaceAll('{type}', type)
19
+ .replaceAll('{version}', version);
20
+ }
21
+ /**
22
+ * Format the tag prefix of a package before its name is known, for packages
23
+ * versioned by their git tags.
24
+ *
25
+ * @param pattern Tag pattern of the version source.
26
+ * @param packagePath Package path relative to the package root.
27
+ * @param tagType Configured {type} of the package tags.
28
+ * @return Tag prefix.
29
+ */
30
+ export function formatTemplatePrefix(pattern, packagePath, tagType) {
31
+ return pattern
32
+ .replaceAll('{path}', packagePath)
33
+ .replaceAll('{type}', tagType ?? defaultTagType(packagePath))
34
+ .replaceAll('{name}', packagePath.split('/').at(-1) ?? packagePath)
35
+ .replaceAll('{slug}', packagePath.split('/').at(-1) ?? packagePath)
36
+ .replaceAll('{version}', '');
37
+ }
38
+ function defaultTagType(packagePath, type) {
39
+ if ('.' === packagePath && type) {
40
+ return type;
41
+ }
42
+ const first = packagePath.split('/')[0] ?? 'package';
43
+ const irregular = {
44
+ actions: 'action',
45
+ plugins: 'plugin',
46
+ themes: 'theme',
47
+ php: 'php',
48
+ js: 'js',
49
+ };
50
+ return irregular[first] ?? first.replace(/s$/, '');
51
+ }
@@ -5,6 +5,7 @@ import { normalizeTag } from "./deploy-options.js";
5
5
  import { ValidationError } from "./errors.js";
6
6
  import { selectFiles } from "./file-selection.js";
7
7
  import { findFileHeaderLine, findReadmeHeaderLine, getLineNumber, readFileStart, } from "./package-meta.js";
8
+ import { escapeRegExp } from "./regexp.js";
8
9
  export const GIT_TAG_FORMAT = '{version}';
9
10
  const TAG_REF_PREFIX = 'refs/tags/';
10
11
  /**
@@ -66,9 +67,6 @@ export function getGitTagVersion(input) {
66
67
  const version = new RegExp(`^${pattern}$`).exec(tag)?.[1];
67
68
  return version ? normalizeTag(version) : null;
68
69
  }
69
- function escapeRegExp(text) {
70
- return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
71
- }
72
70
  /**
73
71
  * Find the values of a PHP constant set with define( 'NAME', '1.2.0' ) or
74
72
  * const NAME = '1.2.0'.
@@ -0,0 +1,36 @@
1
+ import type { PackageVersionSource, PackageVersionTarget } from './changeset-config.ts';
2
+ import type { ConfiguredPackage } from './changeset-packages.ts';
3
+ import type { FileJournal } from './file-journal.ts';
4
+ import type { ReleaseRepository } from './release-repository.ts';
5
+ /**
6
+ * Read the current version of a package from its configured source.
7
+ *
8
+ * @param repository Repository, for versions stored as git tags.
9
+ * @param directory Package directory.
10
+ * @param descriptor Version source.
11
+ * @param relativePath Package path relative to the package root.
12
+ * @param tagType Configured {type} of the package tags.
13
+ * @return Current version.
14
+ * @throws {ValidationError} If the version is missing.
15
+ */
16
+ export declare function readVersion(repository: ReleaseRepository, directory: string, descriptor: PackageVersionSource, relativePath: string, tagType?: string): Promise<string>;
17
+ /**
18
+ * Write a version to a configured version target.
19
+ *
20
+ * @param directory Package directory.
21
+ * @param descriptor Version target.
22
+ * @param version Version to write.
23
+ * @param journal File journal for rolling back.
24
+ * @throws {ValidationError} If the target file has no version to replace.
25
+ */
26
+ export declare function writeVersion(directory: string, descriptor: PackageVersionTarget, version: string, journal: FileJournal): Promise<void>;
27
+ /**
28
+ * Check that every version target of a package has the expected version.
29
+ * Optional targets that are absent are skipped.
30
+ *
31
+ * @param repository Repository, for versions stored as git tags.
32
+ * @param pkg Configured package.
33
+ * @param expected Expected version.
34
+ * @throws {ValidationError} For the first target with another version.
35
+ */
36
+ export declare function verifyVersionTargets(repository: ReleaseRepository, pkg: ConfiguredPackage, expected: string): Promise<void>;
@@ -0,0 +1,195 @@
1
+ import { access, readFile, writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { ValidationError } from "./errors.js";
4
+ import { readJson } from "./json-files.js";
5
+ import { findMainPluginFile } from "./package-meta.js";
6
+ import { escapeRegExp } from "./regexp.js";
7
+ import { formatTemplatePrefix } from "./tag-format.js";
8
+ import { findConstantValues } from "./version-check.js";
9
+ import { compareVersions } from "./versions.js";
10
+ /**
11
+ * Read the current version of a package from its configured source.
12
+ *
13
+ * @param repository Repository, for versions stored as git tags.
14
+ * @param directory Package directory.
15
+ * @param descriptor Version source.
16
+ * @param relativePath Package path relative to the package root.
17
+ * @param tagType Configured {type} of the package tags.
18
+ * @return Current version.
19
+ * @throws {ValidationError} If the version is missing.
20
+ */
21
+ export async function readVersion(repository, directory, descriptor, relativePath, tagType) {
22
+ if ('package-json' === descriptor.kind ||
23
+ 'composer-json' === descriptor.kind) {
24
+ const manifest = await readJson(path.join(directory, descriptor.file));
25
+ const version = manifest[descriptor.field];
26
+ if ('string' === typeof version && version.trim()) {
27
+ return version;
28
+ }
29
+ throw new ValidationError(`Missing ${descriptor.file}#${descriptor.field} in ${directory}.`);
30
+ }
31
+ if ('git-tag' === descriptor.kind) {
32
+ const pattern = descriptor.pattern ?? '{path}/{version}';
33
+ const tags = await repository.listTags(formatTemplatePrefix(pattern, relativePath, tagType));
34
+ const versions = tags
35
+ .map((tag) => tag.match(/(?:^|\/|v)(\d+\.\d+\.\d+)$/)?.[1])
36
+ .filter((item) => Boolean(item));
37
+ versions.sort((a, b) => compareVersions(b, a));
38
+ if (versions[0]) {
39
+ return versions[0];
40
+ }
41
+ throw new ValidationError(`No git tag matches ${pattern} for ${relativePath}.`);
42
+ }
43
+ if ('php-constant' === descriptor.kind) {
44
+ const file = path.join(directory, descriptor.file);
45
+ const contents = await readFile(file, 'utf8');
46
+ const versions = findConstantValues(contents, descriptor.constant);
47
+ if (versions[0]) {
48
+ return versions[0];
49
+ }
50
+ throw new ValidationError(`Missing PHP constant ${descriptor.constant} in ${file}.`);
51
+ }
52
+ const file = path.join(directory, await getVersionFile(directory, descriptor.kind, descriptor.file));
53
+ const contents = await readFile(file, 'utf8');
54
+ const label = 'wordpress-stable-tag' === descriptor.kind ? 'Stable tag' : 'Version';
55
+ const match = new RegExp(`^\\s*\\*?\\s*${label}:\\s*([^\\r\\n]+)`, 'im').exec(contents);
56
+ if (!match) {
57
+ throw new ValidationError(`Missing ${label} header in ${file}.`);
58
+ }
59
+ return match[1]?.trim() ?? '';
60
+ }
61
+ /**
62
+ * Write a version to a configured version target.
63
+ *
64
+ * @param directory Package directory.
65
+ * @param descriptor Version target.
66
+ * @param version Version to write.
67
+ * @param journal File journal for rolling back.
68
+ * @throws {ValidationError} If the target file has no version to replace.
69
+ */
70
+ export async function writeVersion(directory, descriptor, version, journal) {
71
+ if ('package-json' === descriptor.kind ||
72
+ 'composer-json' === descriptor.kind) {
73
+ const file = path.join(directory, descriptor.file);
74
+ const contents = await readFile(file, 'utf8');
75
+ const manifest = JSON.parse(contents);
76
+ if ('composer-json' === descriptor.kind &&
77
+ descriptor.ifPresent &&
78
+ !Object.hasOwn(manifest, descriptor.field)) {
79
+ return;
80
+ }
81
+ manifest[descriptor.field] = version;
82
+ await journal.remember(file);
83
+ await writeFile(file, `${JSON.stringify(manifest, null, '\t')}\n`);
84
+ return;
85
+ }
86
+ if ('php-constant' === descriptor.kind) {
87
+ const file = path.join(directory, descriptor.file);
88
+ const contents = await readFile(file, 'utf8');
89
+ const constant = escapeRegExp(descriptor.constant);
90
+ const definePattern = new RegExp(`(\\bdefine\\s*\\(\\s*(['"])${constant}\\2\\s*,\\s*)(['"])(.*?)\\3`, 'g');
91
+ const declarationPattern = new RegExp(`(\\bconst\\s+${constant}\\s*=\\s*)(['"])(.*?)\\2`, 'g');
92
+ const updated = contents
93
+ .replace(definePattern, (_match, prefix, _nameQuote, quote) => `${prefix}${quote}${version}${quote}`)
94
+ .replace(declarationPattern, (_match, prefix, quote) => `${prefix}${quote}${version}${quote}`);
95
+ if (updated === contents) {
96
+ throw new ValidationError(`Missing PHP constant ${descriptor.constant} in ${file}.`);
97
+ }
98
+ await journal.remember(file);
99
+ await writeFile(file, updated);
100
+ return;
101
+ }
102
+ if ('wordpress-stable-tag' === descriptor.kind &&
103
+ descriptor.ifPresent &&
104
+ !(await versionFileExists(directory, descriptor.kind, descriptor.file))) {
105
+ return;
106
+ }
107
+ const file = path.join(directory, await getVersionFile(directory, descriptor.kind, descriptor.file));
108
+ const contents = await readFile(file, 'utf8');
109
+ const label = 'wordpress-stable-tag' === descriptor.kind ? 'Stable tag' : 'Version';
110
+ const expression = new RegExp(`(^\\s*\\*?\\s*${label}:\\s*)[^\\r\\n]+`, 'im');
111
+ if (!expression.test(contents)) {
112
+ throw new ValidationError(`Missing ${label} header in ${file}.`);
113
+ }
114
+ await journal.remember(file);
115
+ await writeFile(file, contents.replace(expression, `$1${version}`));
116
+ }
117
+ /**
118
+ * Check that every version target of a package has the expected version.
119
+ * Optional targets that are absent are skipped.
120
+ *
121
+ * @param repository Repository, for versions stored as git tags.
122
+ * @param pkg Configured package.
123
+ * @param expected Expected version.
124
+ * @throws {ValidationError} For the first target with another version.
125
+ */
126
+ export async function verifyVersionTargets(repository, pkg, expected) {
127
+ for (const target of pkg.versionConfig.to) {
128
+ let actual;
129
+ if ('composer-json' === target.kind &&
130
+ target.ifPresent &&
131
+ !Object.hasOwn(await readJson(path.join(pkg.directory, target.file)), target.field)) {
132
+ actual = undefined;
133
+ }
134
+ else if ('wordpress-stable-tag' === target.kind &&
135
+ target.ifPresent &&
136
+ !(await versionFileExists(pkg.directory, target.kind, target.file))) {
137
+ actual = undefined;
138
+ }
139
+ else {
140
+ actual = await readVersion(repository, pkg.directory, target, pkg.path, pkg.group.tagType);
141
+ }
142
+ if (undefined !== actual && actual !== expected) {
143
+ throw new ValidationError(`Version mismatch for ${pkg.name}: expected ${expected}, found ${actual}.`);
144
+ }
145
+ }
146
+ }
147
+ async function getVersionFile(directory, kind, configuredFile) {
148
+ if (configuredFile) {
149
+ return configuredFile;
150
+ }
151
+ if ('wordpress-plugin-header' === kind) {
152
+ const file = await findMainPluginFile({ dir: directory });
153
+ if (file) {
154
+ return file;
155
+ }
156
+ throw new ValidationError(`Could not find a plugin header file in ${directory}.`);
157
+ }
158
+ if ('wordpress-stable-tag' === kind) {
159
+ for (const file of ['readme.txt', 'readme.md']) {
160
+ try {
161
+ await access(path.join(directory, file));
162
+ return file;
163
+ }
164
+ catch {
165
+ // Try the next WordPress readme filename.
166
+ }
167
+ }
168
+ throw new ValidationError(`Could not find readme.txt or readme.md in ${directory}.`);
169
+ }
170
+ return 'style.css';
171
+ }
172
+ async function versionFileExists(directory, kind, configuredFile) {
173
+ if (configuredFile) {
174
+ try {
175
+ await access(path.join(directory, configuredFile));
176
+ return true;
177
+ }
178
+ catch {
179
+ return false;
180
+ }
181
+ }
182
+ if ('wordpress-stable-tag' === kind) {
183
+ const files = await Promise.all(['readme.txt', 'readme.md'].map(async (file) => {
184
+ try {
185
+ await access(path.join(directory, file));
186
+ return true;
187
+ }
188
+ catch {
189
+ return false;
190
+ }
191
+ }));
192
+ return files.some(Boolean);
193
+ }
194
+ return true;
195
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Check whether a string is a SemVer version, like 1.2.0 or 1.2.0-rc.1.
3
+ *
4
+ * @param version Version.
5
+ * @return Whether the version is valid.
6
+ */
7
+ export declare function isVersion(version: string): boolean;
8
+ /**
9
+ * Check whether a version has a pre-release part, like 1.0.0-rc.1.
10
+ *
11
+ * @param version Version.
12
+ * @return Whether the version is a valid pre-release.
13
+ */
14
+ export declare function isPrerelease(version: string): boolean;
15
+ /**
16
+ * Compare two versions with SemVer precedence, where a pre-release like
17
+ * 1.0.0-rc.1 comes before 1.0.0. Unlike the semver package, it doesn't throw
18
+ * for versions outside SemVer, like the 1.2 of a custom nextVersion
19
+ * strategy: missing parts count as 0.
20
+ *
21
+ * @param left Version.
22
+ * @param right Version to compare with.
23
+ * @return Negative when left is lower, positive when higher, otherwise 0.
24
+ */
25
+ export declare function compareVersions(left: string, right: string): number;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Stable or pre-release SemVer version without build metadata.
3
+ */
4
+ const VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?$/;
5
+ /**
6
+ * Check whether a string is a SemVer version, like 1.2.0 or 1.2.0-rc.1.
7
+ *
8
+ * @param version Version.
9
+ * @return Whether the version is valid.
10
+ */
11
+ export function isVersion(version) {
12
+ return VERSION_PATTERN.test(version);
13
+ }
14
+ /**
15
+ * Check whether a version has a pre-release part, like 1.0.0-rc.1.
16
+ *
17
+ * @param version Version.
18
+ * @return Whether the version is a valid pre-release.
19
+ */
20
+ export function isPrerelease(version) {
21
+ return Boolean(VERSION_PATTERN.exec(version)?.[4]);
22
+ }
23
+ /**
24
+ * Compare two versions with SemVer precedence, where a pre-release like
25
+ * 1.0.0-rc.1 comes before 1.0.0. Unlike the semver package, it doesn't throw
26
+ * for versions outside SemVer, like the 1.2 of a custom nextVersion
27
+ * strategy: missing parts count as 0.
28
+ *
29
+ * @param left Version.
30
+ * @param right Version to compare with.
31
+ * @return Negative when left is lower, positive when higher, otherwise 0.
32
+ */
33
+ export function compareVersions(left, right) {
34
+ const [leftCore = '', leftPre] = splitPrerelease(left);
35
+ const [rightCore = '', rightPre] = splitPrerelease(right);
36
+ const a = leftCore.split('.').map(Number);
37
+ const b = rightCore.split('.').map(Number);
38
+ const core = (a[0] ?? 0) - (b[0] ?? 0) ||
39
+ (a[1] ?? 0) - (b[1] ?? 0) ||
40
+ (a[2] ?? 0) - (b[2] ?? 0);
41
+ if (core || leftPre === rightPre) {
42
+ return core;
43
+ }
44
+ if (undefined === leftPre || undefined === rightPre) {
45
+ return undefined === leftPre ? 1 : -1;
46
+ }
47
+ return comparePrerelease(leftPre.split('.'), rightPre.split('.'));
48
+ }
49
+ function splitPrerelease(version) {
50
+ const index = version.indexOf('-');
51
+ return -1 === index
52
+ ? [version]
53
+ : [version.slice(0, index), version.slice(index + 1)];
54
+ }
55
+ function comparePrerelease(left, right) {
56
+ const [a, ...leftRest] = left;
57
+ const [b, ...rightRest] = right;
58
+ if (undefined === a || undefined === b) {
59
+ return (undefined === a ? 0 : 1) - (undefined === b ? 0 : 1);
60
+ }
61
+ const order = compareIdentifier(a, b);
62
+ return order || comparePrerelease(leftRest, rightRest);
63
+ }
64
+ function compareIdentifier(a, b) {
65
+ const aNumeric = /^\d+$/.test(a);
66
+ const bNumeric = /^\d+$/.test(b);
67
+ if (aNumeric && bNumeric) {
68
+ return Number(a) - Number(b);
69
+ }
70
+ if (aNumeric || bNumeric) {
71
+ return aNumeric ? -1 : 1;
72
+ }
73
+ return Number(a > b) - Number(a < b);
74
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wpelevator/wp-release",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "Package, checksum, sign, verify and version-check WordPress plugins and themes, and deploy plugins to WordPress.org SVN",
5
5
  "license": "MIT",
6
6
  "repository": {
package/dist/github.d.ts DELETED
@@ -1,24 +0,0 @@
1
- /**
2
- * A GitHub pull request of a commit.
3
- */
4
- export interface GitHubPullRequest {
5
- number: number;
6
- url: string;
7
- /** GitHub handle of the pull request author, without the @. */
8
- author: string;
9
- }
10
- export interface GetGitHubPullRequestInput {
11
- /** Directory of the repository, whose GitHub remote `gh` uses. */
12
- cwd: string;
13
- /** Commit hash. */
14
- commit: string;
15
- }
16
- /**
17
- * Find the pull request that merged a commit, with the authenticated `gh`
18
- * command line client.
19
- *
20
- * @param input Repository directory and commit.
21
- * @return The first pull request of the commit, or undefined without one.
22
- * @throws {ValidationError} If `gh` fails, like without authentication.
23
- */
24
- export declare function getGitHubPullRequest(input: GetGitHubPullRequestInput): Promise<GitHubPullRequest | undefined>;
package/dist/github.js DELETED
@@ -1,30 +0,0 @@
1
- import { execFile } from 'node:child_process';
2
- import { promisify } from 'node:util';
3
- import { ValidationError } from "./errors.js";
4
- const execFileAsync = promisify(execFile);
5
- /**
6
- * Find the pull request that merged a commit, with the authenticated `gh`
7
- * command line client.
8
- *
9
- * @param input Repository directory and commit.
10
- * @return The first pull request of the commit, or undefined without one.
11
- * @throws {ValidationError} If `gh` fails, like without authentication.
12
- */
13
- export async function getGitHubPullRequest(input) {
14
- let stdout;
15
- try {
16
- ({ stdout } = await execFileAsync('gh', ['api', `repos/{owner}/{repo}/commits/${input.commit}/pulls`], { cwd: input.cwd }));
17
- }
18
- catch (error) {
19
- const message = error instanceof Error ? error.message : String(error);
20
- throw new ValidationError(`Could not find the pull request of commit ${input.commit}: ${message}`);
21
- }
22
- const [pullRequest] = JSON.parse(stdout);
23
- return pullRequest
24
- ? {
25
- number: pullRequest.number,
26
- url: pullRequest.html_url,
27
- author: pullRequest.user?.login ?? '',
28
- }
29
- : undefined;
30
- }