@blogic-cz/agent-tools 0.14.37 → 0.14.38
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/package.json +1 -1
- package/schemas/agent-tools.schema.json +23 -0
- package/src/config/index.ts +1 -0
- package/src/config/loader.ts +7 -0
- package/src/config/types.ts +7 -0
- package/src/gh-tool/pr/core.ts +44 -0
- package/src/gh-tool/service.ts +20 -2
package/package.json
CHANGED
|
@@ -95,6 +95,29 @@
|
|
|
95
95
|
"repo": {
|
|
96
96
|
"type": "string",
|
|
97
97
|
"description": "Repository name."
|
|
98
|
+
},
|
|
99
|
+
"prTitle": {
|
|
100
|
+
"$ref": "#/definitions/GitHubPrTitlePolicy"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"GitHubPrTitlePolicy": {
|
|
105
|
+
"description": "Optional pull request title validation policy for this repository profile.",
|
|
106
|
+
"type": "object",
|
|
107
|
+
"additionalProperties": false,
|
|
108
|
+
"required": ["pattern", "expected"],
|
|
109
|
+
"properties": {
|
|
110
|
+
"pattern": {
|
|
111
|
+
"type": "string",
|
|
112
|
+
"description": "JavaScript regular expression source that valid PR titles must match."
|
|
113
|
+
},
|
|
114
|
+
"expected": {
|
|
115
|
+
"type": "string",
|
|
116
|
+
"description": "Human-readable required title format shown when validation fails."
|
|
117
|
+
},
|
|
118
|
+
"example": {
|
|
119
|
+
"type": "string",
|
|
120
|
+
"description": "Example valid title shown when validation fails."
|
|
98
121
|
}
|
|
99
122
|
}
|
|
100
123
|
},
|
package/src/config/index.ts
CHANGED
package/src/config/loader.ts
CHANGED
|
@@ -141,6 +141,13 @@ const AuditConfigSchema = Schema.Struct({
|
|
|
141
141
|
const GitHubRepoConfigSchema = Schema.Struct({
|
|
142
142
|
owner: Schema.String,
|
|
143
143
|
repo: Schema.String,
|
|
144
|
+
prTitle: Schema.optionalKey(
|
|
145
|
+
Schema.Struct({
|
|
146
|
+
pattern: Schema.String,
|
|
147
|
+
expected: Schema.String,
|
|
148
|
+
example: Schema.optionalKey(Schema.String),
|
|
149
|
+
}),
|
|
150
|
+
),
|
|
144
151
|
});
|
|
145
152
|
|
|
146
153
|
const KNOWN_TOP_LEVEL_KEYS = new Set([
|
package/src/config/types.ts
CHANGED
|
@@ -154,9 +154,16 @@ export type AuditConfig = {
|
|
|
154
154
|
};
|
|
155
155
|
|
|
156
156
|
/** Single GitHub repository configuration */
|
|
157
|
+
export type GitHubPrTitlePolicy = {
|
|
158
|
+
pattern: string;
|
|
159
|
+
expected: string;
|
|
160
|
+
example?: string;
|
|
161
|
+
};
|
|
162
|
+
|
|
157
163
|
export type GitHubRepoConfig = {
|
|
158
164
|
owner: string;
|
|
159
165
|
repo: string;
|
|
166
|
+
prTitle?: GitHubPrTitlePolicy;
|
|
160
167
|
};
|
|
161
168
|
|
|
162
169
|
/**
|
package/src/gh-tool/pr/core.ts
CHANGED
|
@@ -21,6 +21,45 @@ import { runLocalCommand } from "./helpers";
|
|
|
21
21
|
const CHECK_JSON_FIELDS = "name,state,bucket,link";
|
|
22
22
|
const GITHUB_ACTIONS_RUN_ID_RE = /github\.com\/[^/]+\/[^/]+\/actions\/runs\/(\d+)/;
|
|
23
23
|
|
|
24
|
+
const validatePRTitle = Effect.fn("pr.validatePRTitle")(function* (title: string) {
|
|
25
|
+
const gh = yield* GitHubService;
|
|
26
|
+
const repoConfig = yield* gh.getRepoConfig();
|
|
27
|
+
const policy = repoConfig?.prTitle;
|
|
28
|
+
|
|
29
|
+
if (!policy) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const pattern = yield* Effect.try({
|
|
34
|
+
try: () => new RegExp(policy.pattern),
|
|
35
|
+
catch: (error) =>
|
|
36
|
+
new GitHubCommandError({
|
|
37
|
+
command: "pr title validation",
|
|
38
|
+
exitCode: 1,
|
|
39
|
+
stderr: `Invalid PR title policy regex: ${error instanceof Error ? error.message : String(error)}`,
|
|
40
|
+
message: "Invalid PR title policy regex",
|
|
41
|
+
}),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (pattern.test(title)) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const lines = [
|
|
49
|
+
"PR title does not match the required format.",
|
|
50
|
+
`Got: ${title}`,
|
|
51
|
+
`Expected: ${policy.expected}`,
|
|
52
|
+
...(policy.example ? [`Example: ${policy.example}`] : []),
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
return yield* new GitHubCommandError({
|
|
56
|
+
command: "pr title validation",
|
|
57
|
+
exitCode: 1,
|
|
58
|
+
stderr: lines.join("\n"),
|
|
59
|
+
message: lines[0] ?? "PR title does not match the required format.",
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
24
63
|
type WorkflowRunJobsForRerun = {
|
|
25
64
|
databaseId: number;
|
|
26
65
|
jobs: Array<{
|
|
@@ -422,6 +461,7 @@ export const createPR = Effect.fn("pr.createPR")(function* (opts: {
|
|
|
422
461
|
head: string | null;
|
|
423
462
|
}) {
|
|
424
463
|
const gh = yield* GitHubService;
|
|
464
|
+
yield* validatePRTitle(opts.title);
|
|
425
465
|
|
|
426
466
|
// When --head is provided (e.g. GitButler workspace), use `gh pr list --head`
|
|
427
467
|
// to find existing PR since `gh pr view` relies on the current git branch.
|
|
@@ -719,6 +759,10 @@ export const editPR = Effect.fn("pr.editPR")(function* (opts: {
|
|
|
719
759
|
}
|
|
720
760
|
|
|
721
761
|
const gh = yield* GitHubService;
|
|
762
|
+
if (opts.title !== null) {
|
|
763
|
+
yield* validatePRTitle(opts.title);
|
|
764
|
+
}
|
|
765
|
+
|
|
722
766
|
const repo = yield* gh.getRepoInfo();
|
|
723
767
|
|
|
724
768
|
const editArgs = [
|
package/src/gh-tool/service.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
|
|
2
2
|
import { Context, Effect, Layer, Stream } from "effect";
|
|
3
3
|
|
|
4
|
+
import type { GitHubRepoConfig } from "#config";
|
|
4
5
|
import type { RepoInfo } from "./types";
|
|
5
6
|
|
|
6
7
|
import { GH_BINARY } from "./config";
|
|
7
8
|
import { GitHubAuthError, GitHubCommandError, GitHubNotFoundError } from "./errors";
|
|
8
|
-
import { ConfigService, resolveGitHubRepoTarget } from "#config";
|
|
9
|
+
import { ConfigService, getGitHubConfig, resolveGitHubRepoTarget } from "#config";
|
|
9
10
|
|
|
10
11
|
type GhResult = {
|
|
11
12
|
stdout: string;
|
|
@@ -24,6 +25,7 @@ export class GitHubService extends Context.Service<
|
|
|
24
25
|
query: string,
|
|
25
26
|
variables: Record<string, string | number | null>,
|
|
26
27
|
) => Effect.Effect<unknown, GhError>;
|
|
28
|
+
readonly getRepoConfig: () => Effect.Effect<GitHubRepoConfig | undefined, never>;
|
|
27
29
|
readonly getRepoInfo: () => Effect.Effect<RepoInfo, GhError>;
|
|
28
30
|
readonly withRepoTarget: <A, E, R>(
|
|
29
31
|
target: string | null,
|
|
@@ -76,6 +78,22 @@ export class GitHubService extends Context.Service<
|
|
|
76
78
|
return yield* effect.pipe(Effect.provideService(RepoTarget, resolved));
|
|
77
79
|
});
|
|
78
80
|
|
|
81
|
+
const getRepoConfig = Effect.fn("GitHubService.getRepoConfig")(function* () {
|
|
82
|
+
const ghRepo = yield* RepoTarget;
|
|
83
|
+
const repos = config?.github;
|
|
84
|
+
|
|
85
|
+
if (repos && ghRepo) {
|
|
86
|
+
const repoConfig = Object.values(repos).find(
|
|
87
|
+
(repo) => `${repo.owner}/${repo.repo}` === ghRepo,
|
|
88
|
+
);
|
|
89
|
+
if (repoConfig) {
|
|
90
|
+
return repoConfig;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return getGitHubConfig(config);
|
|
95
|
+
});
|
|
96
|
+
|
|
79
97
|
const executeGh = (args: string[]) =>
|
|
80
98
|
Effect.scoped(
|
|
81
99
|
Effect.gen(function* () {
|
|
@@ -242,7 +260,7 @@ export class GitHubService extends Context.Service<
|
|
|
242
260
|
return repoInfo;
|
|
243
261
|
});
|
|
244
262
|
|
|
245
|
-
return { runGh, runGhJson, runGraphQL, getRepoInfo, withRepoTarget };
|
|
263
|
+
return { runGh, runGhJson, runGraphQL, getRepoConfig, getRepoInfo, withRepoTarget };
|
|
246
264
|
}),
|
|
247
265
|
),
|
|
248
266
|
);
|