@sentry/junior-github 0.161.0 → 0.163.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/dist/annotations.d.ts +2 -2
- package/dist/index.js +130 -34
- package/dist/sandbox-paths.d.ts +4 -0
- package/dist/workspace-prepare.d.ts +3 -0
- package/package.json +2 -2
package/dist/annotations.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { ConversationAnnotation, ConversationSidebarAnnotation } from "@sentry/junior-plugin-api";
|
|
2
|
-
/**
|
|
3
|
-
export declare function
|
|
2
|
+
/** Return GitHub annotations for a conversation row, newest first. */
|
|
3
|
+
export declare function githubSidebarAnnotations(annotations: ConversationAnnotation[]): ConversationSidebarAnnotation[];
|
package/dist/index.js
CHANGED
|
@@ -730,12 +730,28 @@ import {
|
|
|
730
730
|
pluginToolOutputSchema
|
|
731
731
|
} from "@sentry/junior-plugin-api";
|
|
732
732
|
import { z } from "zod";
|
|
733
|
-
|
|
733
|
+
|
|
734
|
+
// src/sandbox-paths.ts
|
|
735
|
+
var RESERVED_SANDBOX_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
736
|
+
".junior",
|
|
737
|
+
"data",
|
|
738
|
+
"skills"
|
|
739
|
+
]);
|
|
740
|
+
function isReservedSandboxDirectory(path) {
|
|
741
|
+
return RESERVED_SANDBOX_DIRECTORIES.has(path.toLowerCase());
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// src/tools/clone-repository.ts
|
|
734
745
|
var inputSchema = z.object({
|
|
735
746
|
repo: z.string().describe('Repository in "owner/name" format.'),
|
|
736
|
-
directory: z.string().regex(/^[A-Za-z0-9._-]
|
|
737
|
-
|
|
738
|
-
|
|
747
|
+
directory: z.string().regex(/^(?:[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)*)$/).refine(
|
|
748
|
+
(value) => !value.split("/").some((part) => part === "." || part === ".."),
|
|
749
|
+
{
|
|
750
|
+
message: "Directory must be a relative path without . or .. segments."
|
|
751
|
+
}
|
|
752
|
+
).describe(
|
|
753
|
+
"Optional destination directory under the sandbox root. Defaults to repos/{name}."
|
|
754
|
+
).optional()
|
|
739
755
|
}).strict();
|
|
740
756
|
var cloneSchema = z.object({
|
|
741
757
|
path: z.string(),
|
|
@@ -753,7 +769,7 @@ function parseRepo(value) {
|
|
|
753
769
|
return { owner: parts[0], name: parts[1] };
|
|
754
770
|
}
|
|
755
771
|
function defaultDirectory(repoName) {
|
|
756
|
-
return
|
|
772
|
+
return `repos/${repoName}`;
|
|
757
773
|
}
|
|
758
774
|
function commandSignal(signal, timeoutMs) {
|
|
759
775
|
const timeout = AbortSignal.timeout(timeoutMs);
|
|
@@ -802,7 +818,27 @@ function createGitHubCloneRepositoryTool(ctx) {
|
|
|
802
818
|
async execute(input, options) {
|
|
803
819
|
const repo = parseRepo(input.repo);
|
|
804
820
|
const directory = input.directory ?? defaultDirectory(repo.name);
|
|
821
|
+
const rootSegment = directory.split("/")[0] ?? directory;
|
|
822
|
+
if (isReservedSandboxDirectory(rootSegment)) {
|
|
823
|
+
throw new PluginToolInputError(
|
|
824
|
+
`Directory conflicts with a reserved sandbox path: ${directory}`
|
|
825
|
+
);
|
|
826
|
+
}
|
|
805
827
|
const path = `${ctx.sandbox.root}/${directory}`;
|
|
828
|
+
const parentDirectory = directory.includes("/") ? directory.slice(0, directory.lastIndexOf("/")) : void 0;
|
|
829
|
+
if (parentDirectory) {
|
|
830
|
+
const mkdir = await ctx.sandbox.run({
|
|
831
|
+
cmd: "mkdir",
|
|
832
|
+
args: ["-p", "--", `${ctx.sandbox.root}/${parentDirectory}`],
|
|
833
|
+
cwd: ctx.sandbox.root,
|
|
834
|
+
signal: commandSignal(options.signal, 3e4)
|
|
835
|
+
});
|
|
836
|
+
if (mkdir.exitCode !== 0) {
|
|
837
|
+
throw new PluginToolInputError(
|
|
838
|
+
`Failed to create clone parent directory: ${parentDirectory}`
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
806
842
|
const exists = await ctx.sandbox.run({
|
|
807
843
|
cmd: "bash",
|
|
808
844
|
args: ["-c", `test -e "$1"`, "bash", path],
|
|
@@ -4506,13 +4542,6 @@ async function classifyGitHubPullRequestCommitComposition(args) {
|
|
|
4506
4542
|
}
|
|
4507
4543
|
|
|
4508
4544
|
// src/annotations.ts
|
|
4509
|
-
var STATUS_RANK = {
|
|
4510
|
-
warning: 5,
|
|
4511
|
-
open: 4,
|
|
4512
|
-
draft: 3,
|
|
4513
|
-
merged: 2,
|
|
4514
|
-
closed: 1
|
|
4515
|
-
};
|
|
4516
4545
|
var STATUS_ICON = {
|
|
4517
4546
|
warning: "triangle-alert",
|
|
4518
4547
|
open: "circle-dot",
|
|
@@ -4520,31 +4549,40 @@ var STATUS_ICON = {
|
|
|
4520
4549
|
merged: "git-merge",
|
|
4521
4550
|
closed: "circle-x"
|
|
4522
4551
|
};
|
|
4523
|
-
function
|
|
4552
|
+
function isPullRequestUrl(url) {
|
|
4553
|
+
try {
|
|
4554
|
+
return /^\/[^/]+\/[^/]+\/pull\/\d+(?:\/|$)/.test(new URL(url).pathname);
|
|
4555
|
+
} catch {
|
|
4556
|
+
return false;
|
|
4557
|
+
}
|
|
4558
|
+
}
|
|
4559
|
+
function sidebarIconForStatus(status, url) {
|
|
4560
|
+
if (status === "open" && isPullRequestUrl(url)) return "git-pull-request";
|
|
4561
|
+
return STATUS_ICON[status];
|
|
4562
|
+
}
|
|
4563
|
+
function repositoryName(annotation) {
|
|
4524
4564
|
try {
|
|
4525
|
-
const [,
|
|
4526
|
-
return
|
|
4565
|
+
const [, , repo] = new URL(annotation.url).pathname.split("/");
|
|
4566
|
+
return repo || void 0;
|
|
4527
4567
|
} catch {
|
|
4528
4568
|
return void 0;
|
|
4529
4569
|
}
|
|
4530
4570
|
}
|
|
4531
|
-
function
|
|
4532
|
-
|
|
4533
|
-
const
|
|
4534
|
-
const
|
|
4535
|
-
return
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
label: repos.size === 1 ? [...repos.values()][0] : `${repos.size} repos`
|
|
4547
|
-
};
|
|
4571
|
+
function githubSidebarAnnotations(annotations) {
|
|
4572
|
+
return annotations.flatMap((annotation) => {
|
|
4573
|
+
const status = annotation.status;
|
|
4574
|
+
const label = repositoryName(annotation);
|
|
4575
|
+
return status && label ? [
|
|
4576
|
+
{
|
|
4577
|
+
annotation: {
|
|
4578
|
+
icon: sidebarIconForStatus(status, annotation.url),
|
|
4579
|
+
key: annotation.key,
|
|
4580
|
+
label
|
|
4581
|
+
},
|
|
4582
|
+
updatedAt: annotation.updatedAt
|
|
4583
|
+
}
|
|
4584
|
+
] : [];
|
|
4585
|
+
}).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).map(({ annotation }) => annotation);
|
|
4548
4586
|
}
|
|
4549
4587
|
|
|
4550
4588
|
// src/webhooks/check-suite-enrichment.ts
|
|
@@ -4909,6 +4947,63 @@ function linkifyGitHubReferences(text2) {
|
|
|
4909
4947
|
}).join("\n");
|
|
4910
4948
|
}
|
|
4911
4949
|
|
|
4950
|
+
// src/workspace-prepare.ts
|
|
4951
|
+
async function prepareWorkspace(ctx) {
|
|
4952
|
+
const repos = ctx.repos.map((entry) => {
|
|
4953
|
+
const [owner, name, ...rest] = entry.repo.split("/");
|
|
4954
|
+
if (!owner || !name || rest.length > 0) {
|
|
4955
|
+
throw new Error(`Invalid GitHub repository: ${entry.repo}`);
|
|
4956
|
+
}
|
|
4957
|
+
const segments = entry.path.split("/");
|
|
4958
|
+
if (segments.length === 0 || segments.some(
|
|
4959
|
+
(part) => !part || part === "." || part === ".." || !/^[A-Za-z0-9._-]+$/.test(part)
|
|
4960
|
+
) || isReservedSandboxDirectory(segments[0])) {
|
|
4961
|
+
throw new Error(`Invalid workspace checkout path: ${entry.path}`);
|
|
4962
|
+
}
|
|
4963
|
+
return { owner, name, path: entry.path, repo: entry.repo };
|
|
4964
|
+
});
|
|
4965
|
+
const paths = /* @__PURE__ */ new Set();
|
|
4966
|
+
for (const entry of repos) {
|
|
4967
|
+
const key = entry.path.toLowerCase();
|
|
4968
|
+
if (paths.has(key)) {
|
|
4969
|
+
throw new Error(`Workspace checkout path collision: ${entry.path}`);
|
|
4970
|
+
}
|
|
4971
|
+
paths.add(key);
|
|
4972
|
+
}
|
|
4973
|
+
for (const { owner, name, path, repo } of repos) {
|
|
4974
|
+
const parent = path.includes("/") ? path.slice(0, path.lastIndexOf("/")) : void 0;
|
|
4975
|
+
if (parent) {
|
|
4976
|
+
const mkdir = await ctx.sandbox.run({
|
|
4977
|
+
cmd: "mkdir",
|
|
4978
|
+
args: ["-p", "--", parent],
|
|
4979
|
+
cwd: ctx.sandbox.root
|
|
4980
|
+
});
|
|
4981
|
+
if (mkdir.exitCode !== 0) {
|
|
4982
|
+
throw new Error(
|
|
4983
|
+
`GitHub workspace checkout parent failed for ${repo}: ${mkdir.stderr.trim() || `exit ${mkdir.exitCode}`}`
|
|
4984
|
+
);
|
|
4985
|
+
}
|
|
4986
|
+
}
|
|
4987
|
+
const result = await ctx.sandbox.run({
|
|
4988
|
+
cmd: "git",
|
|
4989
|
+
args: [
|
|
4990
|
+
"clone",
|
|
4991
|
+
"--quiet",
|
|
4992
|
+
"--depth=1",
|
|
4993
|
+
"--",
|
|
4994
|
+
`https://github.com/${owner}/${name}.git`,
|
|
4995
|
+
path
|
|
4996
|
+
],
|
|
4997
|
+
cwd: ctx.sandbox.root
|
|
4998
|
+
});
|
|
4999
|
+
if (result.exitCode !== 0) {
|
|
5000
|
+
throw new Error(
|
|
5001
|
+
`GitHub workspace clone failed for ${repo}: ${result.stderr.trim() || `exit ${result.exitCode}`}`
|
|
5002
|
+
);
|
|
5003
|
+
}
|
|
5004
|
+
}
|
|
5005
|
+
}
|
|
5006
|
+
|
|
4912
5007
|
// src/plugin.ts
|
|
4913
5008
|
function githubSmartHttpAccess(upstreamUrl) {
|
|
4914
5009
|
const pathname = upstreamUrl.pathname.toLowerCase();
|
|
@@ -5361,10 +5456,10 @@ function githubPlugin(options = {}) {
|
|
|
5361
5456
|
return {
|
|
5362
5457
|
annotationsByConversationId: Object.fromEntries(
|
|
5363
5458
|
ctx.conversationIds.flatMap((conversationId) => {
|
|
5364
|
-
const
|
|
5459
|
+
const annotations = githubSidebarAnnotations(
|
|
5365
5460
|
ctx.annotationsByConversationId[conversationId] ?? []
|
|
5366
5461
|
);
|
|
5367
|
-
return
|
|
5462
|
+
return annotations.length > 0 ? [[conversationId, annotations]] : [];
|
|
5368
5463
|
})
|
|
5369
5464
|
)
|
|
5370
5465
|
};
|
|
@@ -5451,6 +5546,7 @@ function githubPlugin(options = {}) {
|
|
|
5451
5546
|
tools(ctx) {
|
|
5452
5547
|
return createGitHubTools(ctx);
|
|
5453
5548
|
},
|
|
5549
|
+
workspacePrepare: prepareWorkspace,
|
|
5454
5550
|
async sandboxPrepare(ctx) {
|
|
5455
5551
|
const hooksPath = `${ctx.sandbox.juniorRoot}/git-hooks`;
|
|
5456
5552
|
await ctx.sandbox.writeFile({
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Sandbox root directories reserved for Junior runtime material. */
|
|
2
|
+
export declare const RESERVED_SANDBOX_DIRECTORIES: Set<string>;
|
|
3
|
+
/** True when a checkout path collides with a reserved sandbox root (case-insensitive). */
|
|
4
|
+
export declare function isReservedSandboxDirectory(path: string): boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior-github",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.163.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@sinclair/typebox": "^0.34.49",
|
|
32
32
|
"drizzle-orm": "^0.45.2",
|
|
33
33
|
"zod": "^4.4.3",
|
|
34
|
-
"@sentry/junior-plugin-api": "0.
|
|
34
|
+
"@sentry/junior-plugin-api": "0.163.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "^25.9.1",
|