@dependabit/github-client 0.1.14 → 0.1.16

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/src/commits.ts DELETED
@@ -1,184 +0,0 @@
1
- /**
2
- * Commit Analysis
3
- * Fetch and analyze commits from GitHub API
4
- */
5
-
6
- import type { GitHubClient } from './client.js';
7
-
8
- export interface CommitInfo {
9
- sha: string;
10
- message: string;
11
- author: {
12
- name: string;
13
- email?: string;
14
- date: string;
15
- };
16
- url?: string;
17
- }
18
-
19
- export interface CommitFile {
20
- filename: string;
21
- status: 'added' | 'removed' | 'modified' | 'renamed' | 'copied' | 'changed' | 'unchanged';
22
- additions?: number;
23
- deletions?: number;
24
- changes?: number;
25
- patch?: string;
26
- }
27
-
28
- export interface CommitDiff {
29
- sha: string;
30
- files: CommitFile[];
31
- }
32
-
33
- export interface ParsedFiles {
34
- added: string[];
35
- modified: string[];
36
- removed: string[];
37
- }
38
-
39
- export interface FetchCommitsOptions {
40
- since?: string;
41
- until?: string;
42
- sha?: string;
43
- path?: string;
44
- per_page?: number;
45
- page?: number;
46
- }
47
-
48
- /**
49
- * Fetch commits from GitHub API
50
- */
51
- export async function fetchCommits(
52
- client: GitHubClient,
53
- owner: string,
54
- repo: string,
55
- options: FetchCommitsOptions = {}
56
- ): Promise<CommitInfo[]> {
57
- const octokit = client.getOctokit();
58
-
59
- const response = await octokit.rest.repos.listCommits({
60
- owner,
61
- repo,
62
- ...options
63
- });
64
-
65
- return response.data.map((commit) => {
66
- const info: CommitInfo = {
67
- sha: commit.sha,
68
- message: commit.commit.message,
69
- author: {
70
- name: commit.commit.author?.name || 'Unknown',
71
- date: commit.commit.author?.date || new Date().toISOString()
72
- }
73
- };
74
-
75
- if (commit.commit.author?.email) {
76
- info.author.email = commit.commit.author.email;
77
- }
78
-
79
- if (commit.html_url) {
80
- info.url = commit.html_url;
81
- }
82
-
83
- return info;
84
- });
85
- }
86
-
87
- /**
88
- * Get detailed diff for a specific commit
89
- */
90
- export async function getCommitDiff(
91
- client: GitHubClient,
92
- owner: string,
93
- repo: string,
94
- sha: string
95
- ): Promise<CommitDiff> {
96
- const octokit = client.getOctokit();
97
-
98
- const response = await octokit.rest.repos.getCommit({
99
- owner,
100
- repo,
101
- ref: sha
102
- });
103
-
104
- return {
105
- sha: response.data.sha,
106
- files: (response.data.files || []).map((file) => {
107
- const commitFile: CommitFile = {
108
- filename: file.filename,
109
- status: file.status as CommitFile['status']
110
- };
111
-
112
- if (file.additions !== undefined) commitFile.additions = file.additions;
113
- if (file.deletions !== undefined) commitFile.deletions = file.deletions;
114
- if (file.changes !== undefined) commitFile.changes = file.changes;
115
- if (file.patch !== undefined) commitFile.patch = file.patch;
116
-
117
- return commitFile;
118
- })
119
- };
120
- }
121
-
122
- /**
123
- * Parse commit files into categorized lists
124
- */
125
- export function parseCommitFiles(files: CommitFile[]): ParsedFiles {
126
- const result: ParsedFiles = {
127
- added: [],
128
- modified: [],
129
- removed: []
130
- };
131
-
132
- for (const file of files) {
133
- if (file.status === 'added') {
134
- result.added.push(file.filename);
135
- } else if (file.status === 'modified' || file.status === 'changed') {
136
- result.modified.push(file.filename);
137
- } else if (file.status === 'removed') {
138
- result.removed.push(file.filename);
139
- }
140
- }
141
-
142
- return result;
143
- }
144
-
145
- /**
146
- * Get commits between two refs
147
- */
148
- export async function getCommitsBetween(
149
- client: GitHubClient,
150
- owner: string,
151
- repo: string,
152
- base: string,
153
- head: string
154
- ): Promise<CommitInfo[]> {
155
- const octokit = client.getOctokit();
156
-
157
- const response = await octokit.rest.repos.compareCommits({
158
- owner,
159
- repo,
160
- base,
161
- head
162
- });
163
-
164
- return response.data.commits.map((commit) => {
165
- const info: CommitInfo = {
166
- sha: commit.sha,
167
- message: commit.commit.message,
168
- author: {
169
- name: commit.commit.author?.name || 'Unknown',
170
- date: commit.commit.author?.date || new Date().toISOString()
171
- }
172
- };
173
-
174
- if (commit.commit.author?.email) {
175
- info.author.email = commit.commit.author.email;
176
- }
177
-
178
- if (commit.html_url) {
179
- info.url = commit.html_url;
180
- }
181
-
182
- return info;
183
- });
184
- }
package/src/feedback.ts DELETED
@@ -1,166 +0,0 @@
1
- /**
2
- * False positive feedback listener for dependency tracking
3
- * Monitors GitHub issue labels to collect user feedback on detections
4
- */
5
-
6
- export interface IssueWithLabels {
7
- number: number;
8
- title: string;
9
- labels: Array<string | { name: string }>;
10
- created_at?: string;
11
- }
12
-
13
- export interface IssueManagerInterface {
14
- listIssues(): Promise<Array<IssueWithLabels>>;
15
- getIssue(issueNumber: number): Promise<IssueWithLabels>;
16
- }
17
-
18
- export interface FeedbackConfig {
19
- truePositiveLabel?: string;
20
- falsePositiveLabel?: string;
21
- }
22
-
23
- export interface FeedbackData {
24
- truePositives: Array<{ number: number; title: string; created_at?: string | undefined }>;
25
- falsePositives: Array<{ number: number; title: string; created_at?: string | undefined }>;
26
- total: number;
27
- }
28
-
29
- export interface FeedbackRate {
30
- falsePositiveRate: number;
31
- truePositiveRate: number;
32
- totalFeedback: number;
33
- }
34
-
35
- export interface CollectOptions {
36
- startDate?: Date;
37
- endDate?: Date;
38
- repository?: string;
39
- }
40
-
41
- /**
42
- * Listener that monitors issue labels for false positive feedback
43
- */
44
- export class FeedbackListener {
45
- private issueManager: IssueManagerInterface;
46
- private truePositiveLabel: string;
47
- private falsePositiveLabel: string;
48
-
49
- constructor(issueManager: IssueManagerInterface, config: FeedbackConfig = {}) {
50
- this.issueManager = issueManager;
51
- this.truePositiveLabel = config.truePositiveLabel || 'true-positive';
52
- this.falsePositiveLabel = config.falsePositiveLabel || 'false-positive';
53
- }
54
-
55
- /**
56
- * Collect feedback from issues with feedback labels
57
- */
58
- async collectFeedback(options: CollectOptions = {}): Promise<FeedbackData> {
59
- const issues = await this.issueManager.listIssues();
60
-
61
- const truePositives: FeedbackData['truePositives'] = [];
62
- const falsePositives: FeedbackData['falsePositives'] = [];
63
-
64
- for (const issue of issues) {
65
- // Filter by date range if specified
66
- if (options.startDate && issue.created_at) {
67
- const issueDate = new Date(issue.created_at);
68
- if (issueDate < options.startDate) continue;
69
- }
70
- if (options.endDate && issue.created_at) {
71
- const issueDate = new Date(issue.created_at);
72
- if (issueDate > options.endDate) continue;
73
- }
74
-
75
- // Filter by repository if specified
76
- if (options.repository && (issue as any).repository !== options.repository) {
77
- continue;
78
- }
79
-
80
- const labels = issue.labels || [];
81
- const labelNames = labels.map((l: string | { name: string }) =>
82
- typeof l === 'string' ? l : l.name
83
- );
84
-
85
- const hasTrue = labelNames.includes(this.truePositiveLabel);
86
- const hasFalse = labelNames.includes(this.falsePositiveLabel);
87
-
88
- // Handle issues with both labels as a special case (log warning but count as true positive)
89
- if (hasTrue && hasFalse) {
90
- console.warn(
91
- `Issue #${issue.number} has both true-positive and false-positive labels. Counting as true-positive.`
92
- );
93
- truePositives.push({
94
- number: issue.number,
95
- title: issue.title,
96
- created_at: issue.created_at
97
- });
98
- } else if (hasTrue) {
99
- truePositives.push({
100
- number: issue.number,
101
- title: issue.title,
102
- created_at: issue.created_at
103
- });
104
- } else if (hasFalse) {
105
- falsePositives.push({
106
- number: issue.number,
107
- title: issue.title,
108
- created_at: issue.created_at
109
- });
110
- }
111
- }
112
-
113
- return {
114
- truePositives,
115
- falsePositives,
116
- total: truePositives.length + falsePositives.length
117
- };
118
- }
119
-
120
- /**
121
- * Calculate false positive rate from collected feedback
122
- */
123
- async getFeedbackRate(options: CollectOptions = {}): Promise<FeedbackRate> {
124
- const feedback = await this.collectFeedback(options);
125
-
126
- if (feedback.total === 0) {
127
- return {
128
- falsePositiveRate: 0,
129
- truePositiveRate: 0,
130
- totalFeedback: 0
131
- };
132
- }
133
-
134
- return {
135
- falsePositiveRate: feedback.falsePositives.length / feedback.total,
136
- truePositiveRate: feedback.truePositives.length / feedback.total,
137
- totalFeedback: feedback.total
138
- };
139
- }
140
-
141
- /**
142
- * Get feedback from recent time window (e.g., last 30 days)
143
- */
144
- async getRecentFeedback(days: number, referenceDate?: Date): Promise<FeedbackData> {
145
- const endDate = referenceDate || new Date();
146
- const startDate = new Date(endDate);
147
- startDate.setDate(startDate.getDate() - days);
148
-
149
- return this.collectFeedback({ startDate, endDate });
150
- }
151
-
152
- /**
153
- * Check if a specific issue has feedback label
154
- */
155
- async monitorIssue(issueNumber: number): Promise<boolean> {
156
- const issue = await this.issueManager.getIssue(issueNumber);
157
- const labels = issue.labels || [];
158
- const labelNames = labels.map((l: string | { name: string }) =>
159
- typeof l === 'string' ? l : l.name
160
- );
161
-
162
- return (
163
- labelNames.includes(this.truePositiveLabel) || labelNames.includes(this.falsePositiveLabel)
164
- );
165
- }
166
- }
package/src/index.ts DELETED
@@ -1,15 +0,0 @@
1
- /**
2
- * @dependabit/github-client - GitHub API client wrapper
3
- */
4
-
5
- export * from './client.js';
6
- export * from './commits.js';
7
- export { IssueManager } from './issues.js';
8
- export type { IssueData, IssueResult, UpdateIssueData } from './issues.js';
9
- export { ReleaseManager } from './releases.js';
10
- export type { Release, ReleaseComparison } from './releases.js';
11
- export { RateLimitHandler } from './rate-limit.js';
12
- export type { RateLimitInfo, RateLimitStatus, BudgetReservation } from './rate-limit.js';
13
- export * from './auth.js';
14
- export { FeedbackListener } from './feedback.js';
15
- export type { FeedbackConfig, FeedbackData, FeedbackRate } from './feedback.js';
package/src/issues.ts DELETED
@@ -1,185 +0,0 @@
1
- /**
2
- * Issue Manager
3
- * Handles GitHub issue creation and management for dependency changes
4
- */
5
-
6
- import { Octokit } from 'octokit';
7
-
8
- export interface IssueData {
9
- owner: string;
10
- repo: string;
11
- title: string;
12
- body: string;
13
- severity: 'breaking' | 'major' | 'minor';
14
- dependency: {
15
- id: string;
16
- url: string;
17
- };
18
- assignee?: string;
19
- }
20
-
21
- export interface IssueResult {
22
- number: number;
23
- url: string;
24
- labels: string[];
25
- assignees?: string[] | undefined;
26
- }
27
-
28
- export interface UpdateIssueData {
29
- owner: string;
30
- repo: string;
31
- issueNumber: number;
32
- body: string;
33
- severity?: 'breaking' | 'major' | 'minor';
34
- append?: boolean;
35
- }
36
-
37
- export class IssueManager {
38
- private octokit: Octokit;
39
-
40
- constructor(auth?: string) {
41
- this.octokit = new Octokit({
42
- auth: auth || process.env['GITHUB_TOKEN']
43
- });
44
- }
45
-
46
- /**
47
- * Creates a new issue for a dependency change
48
- */
49
- async createIssue(data: IssueData): Promise<IssueResult> {
50
- const { owner, repo, title, body, severity, dependency, assignee } = data;
51
-
52
- // Prepare labels
53
- const labels = ['dependabit', `severity:${severity}`, 'dependency-update'];
54
-
55
- // Create the issue
56
- const response = await this.octokit.rest.issues.create({
57
- owner,
58
- repo,
59
- title,
60
- body: this.formatIssueBody(body, dependency),
61
- labels,
62
- ...(assignee && { assignees: [assignee] })
63
- });
64
-
65
- return {
66
- number: response.data.number,
67
- url: response.data.html_url,
68
- labels,
69
- ...(assignee && { assignees: [assignee] })
70
- };
71
- }
72
-
73
- /**
74
- * Finds an existing issue for a dependency
75
- */
76
- async findExistingIssue(params: {
77
- owner: string;
78
- repo: string;
79
- dependencyId: string;
80
- }): Promise<IssueResult | null> {
81
- const { owner, repo, dependencyId } = params;
82
-
83
- try {
84
- // Search for open issues with dependabit label and dependency ID
85
- const query = `repo:${owner}/${repo} is:issue is:open label:dependabit ${dependencyId}`;
86
-
87
- const response = await this.octokit.rest.search.issuesAndPullRequests({
88
- q: query,
89
- per_page: 1
90
- });
91
-
92
- if (response.data.items.length === 0) {
93
- return null;
94
- }
95
-
96
- const issue = response.data.items[0];
97
- if (!issue) {
98
- return null;
99
- }
100
-
101
- return {
102
- number: issue.number,
103
- url: issue.html_url,
104
- labels: issue.labels.map((l) => (typeof l === 'string' ? l : l.name || ''))
105
- };
106
- } catch (error) {
107
- console.error('Error finding existing issue:', error);
108
- return null;
109
- }
110
- }
111
-
112
- /**
113
- * Updates an existing issue
114
- */
115
- async updateIssue(data: UpdateIssueData): Promise<IssueResult> {
116
- const { owner, repo, issueNumber, body, severity, append } = data;
117
-
118
- let finalBody = body;
119
-
120
- // If appending, fetch current body first
121
- if (append) {
122
- const current = await this.octokit.rest.issues.get({
123
- owner,
124
- repo,
125
- issue_number: issueNumber
126
- });
127
- finalBody = `${current.data.body}\n\n---\n\n${body}`;
128
- }
129
-
130
- // Update labels if severity changed
131
- const updateParams: {
132
- owner: string;
133
- repo: string;
134
- issue_number: number;
135
- body: string;
136
- labels?: string[];
137
- } = {
138
- owner,
139
- repo,
140
- issue_number: issueNumber,
141
- body: finalBody
142
- };
143
-
144
- if (severity) {
145
- const current = await this.octokit.rest.issues.get({
146
- owner,
147
- repo,
148
- issue_number: issueNumber
149
- });
150
-
151
- const existingLabels = current.data.labels
152
- .map((l) => (typeof l === 'string' ? l : l.name))
153
- .filter((l): l is string => !!l);
154
-
155
- const severityLabels = ['dependabit', `severity:${severity}`, 'dependency-update'];
156
-
157
- const mergedLabels = Array.from(new Set([...existingLabels, ...severityLabels]));
158
-
159
- updateParams.labels = mergedLabels;
160
- }
161
-
162
- const response = await this.octokit.rest.issues.update(updateParams);
163
-
164
- return {
165
- number: response.data.number,
166
- url: response.data.html_url,
167
- labels: response.data.labels.map((l) => (typeof l === 'string' ? l : l.name || ''))
168
- };
169
- }
170
-
171
- /**
172
- * Formats the issue body with dependency metadata
173
- */
174
- private formatIssueBody(body: string, dependency: { id: string; url: string }): string {
175
- return `${body}
176
-
177
- ---
178
-
179
- **Dependency Information**
180
- - ID: \`${dependency.id}\`
181
- - URL: ${dependency.url}
182
-
183
- *This issue was automatically created by dependabit. Add \`false-positive\` or \`true-positive\` label to provide feedback.*`;
184
- }
185
- }