gitpadi 2.0.5 → 2.0.7
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/cli.js +92 -33
- package/dist/commands/repos.js +17 -3
- package/dist/core/github.js +4 -0
- package/package.json +1 -1
- package/src/cli.ts +99 -34
- package/src/commands/repos.ts +17 -4
- package/src/core/github.ts +5 -0
package/dist/cli.js
CHANGED
|
@@ -12,7 +12,7 @@ import { execSync } from 'child_process';
|
|
|
12
12
|
import boxen from 'boxen';
|
|
13
13
|
import { createSpinner } from 'nanospinner';
|
|
14
14
|
import { Octokit } from '@octokit/rest';
|
|
15
|
-
import { initGitHub, setRepo, getOwner, getRepo, getOctokit, saveConfig, getToken } from './core/github.js';
|
|
15
|
+
import { initGitHub, setRepo, getOwner, getRepo, getOctokit, saveConfig, getToken, getAuthenticatedUser, getRepoPermissions } from './core/github.js';
|
|
16
16
|
import * as issues from './commands/issues.js';
|
|
17
17
|
import * as prs from './commands/prs.js';
|
|
18
18
|
import * as repos from './commands/repos.js';
|
|
@@ -20,6 +20,7 @@ import * as contributors from './commands/contributors.js';
|
|
|
20
20
|
import * as releases from './commands/releases.js';
|
|
21
21
|
import * as contribute from './commands/contribute.js';
|
|
22
22
|
const VERSION = '2.0.0';
|
|
23
|
+
let targetConfirmed = false;
|
|
23
24
|
// ── Styling ────────────────────────────────────────────────────────────
|
|
24
25
|
const cyber = gradient(['#ff00ff', '#00ffff', '#ff00ff']);
|
|
25
26
|
const neon = gradient(['#00ff87', '#60efff']);
|
|
@@ -157,35 +158,102 @@ async function ensureAuthenticated() {
|
|
|
157
158
|
/**
|
|
158
159
|
* Ensures we have a target repository (Owner/Repo)
|
|
159
160
|
*/
|
|
160
|
-
async function ensureTargetRepo() {
|
|
161
|
+
async function ensureTargetRepo(force = false) {
|
|
161
162
|
let owner = getOwner();
|
|
162
163
|
let repo = getRepo();
|
|
163
|
-
if (owner && repo) {
|
|
164
|
-
|
|
164
|
+
if (!force && owner && repo) {
|
|
165
|
+
if (targetConfirmed)
|
|
166
|
+
return;
|
|
167
|
+
const { confirm } = await inquirer.prompt([{
|
|
168
|
+
type: 'confirm',
|
|
169
|
+
name: 'confirm',
|
|
170
|
+
message: cyan('🎯 Targeting ') + bold(`${owner}/${repo}`) + cyan('. Correct?'),
|
|
171
|
+
default: true
|
|
172
|
+
}]);
|
|
173
|
+
if (confirm) {
|
|
174
|
+
targetConfirmed = true;
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
165
177
|
}
|
|
166
178
|
console.log(neon('\n 📦 Project Targeting — which repo are we working on?\n'));
|
|
167
|
-
|
|
168
|
-
|
|
179
|
+
// 1. Get the Owner/Org
|
|
180
|
+
const { targetOwner } = await inquirer.prompt([{
|
|
169
181
|
type: 'input',
|
|
170
|
-
name: '
|
|
182
|
+
name: 'targetOwner',
|
|
171
183
|
message: cyan('👤 GitHub Owner/Org:'),
|
|
172
|
-
default: owner ||
|
|
173
|
-
validate: (v) => v.length > 0 || 'Required',
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
type: 'input',
|
|
177
|
-
name: 'repo',
|
|
178
|
-
message: cyan('📦 Repository name:'),
|
|
179
|
-
default: repo || '',
|
|
184
|
+
default: owner || await getAuthenticatedUser(),
|
|
180
185
|
validate: (v) => v.length > 0 || 'Required',
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
186
|
+
}]);
|
|
187
|
+
// 2. Fetch Repos for that Owner
|
|
188
|
+
const spin = createSpinner(dim(`Fetching repositories for ${targetOwner}...`)).start();
|
|
189
|
+
const fetchedRepos = await repos.listRepos({ owner: targetOwner, silent: true });
|
|
190
|
+
if (fetchedRepos.length > 0) {
|
|
191
|
+
spin.success({ text: green(`Found ${fetchedRepos.length} repositories for ${targetOwner}`) });
|
|
192
|
+
const { selectedRepo } = await inquirer.prompt([{
|
|
193
|
+
type: 'list',
|
|
194
|
+
name: 'selectedRepo',
|
|
195
|
+
message: cyan('📦 Select Repository:'),
|
|
196
|
+
choices: [
|
|
197
|
+
...fetchedRepos.map((r) => ({ name: r.name, value: r.name })),
|
|
198
|
+
new inquirer.Separator(),
|
|
199
|
+
{ name: '✍️ Enter manually...', value: '__manual__' }
|
|
200
|
+
],
|
|
201
|
+
loop: false
|
|
202
|
+
}]);
|
|
203
|
+
if (selectedRepo !== '__manual__') {
|
|
204
|
+
repo = selectedRepo;
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
const { manualRepo } = await inquirer.prompt([{
|
|
208
|
+
type: 'input',
|
|
209
|
+
name: 'manualRepo',
|
|
210
|
+
message: cyan('📦 Repository name:'),
|
|
211
|
+
validate: (v) => v.length > 0 || 'Required',
|
|
212
|
+
}]);
|
|
213
|
+
repo = manualRepo;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
spin.warn({ text: yellow(`No public repositories found for ${targetOwner}.`) });
|
|
218
|
+
const { manualRepo } = await inquirer.prompt([{
|
|
219
|
+
type: 'input',
|
|
220
|
+
name: 'manualRepo',
|
|
221
|
+
message: cyan('📦 Enter Repository name manually:'),
|
|
222
|
+
validate: (v) => v.length > 0 || 'Required',
|
|
223
|
+
}]);
|
|
224
|
+
repo = manualRepo;
|
|
225
|
+
}
|
|
226
|
+
setRepo(targetOwner, repo);
|
|
227
|
+
saveConfig({ token: getToken(), owner: targetOwner, repo: repo });
|
|
228
|
+
targetConfirmed = true;
|
|
229
|
+
// 4. Permission Check (for Maintainer safety)
|
|
230
|
+
const checkSpin = createSpinner(dim('Checking maintainer permissions...')).start();
|
|
231
|
+
try {
|
|
232
|
+
const perms = await getRepoPermissions(targetOwner, repo);
|
|
233
|
+
if (!perms.push && !perms.admin) {
|
|
234
|
+
checkSpin.warn({ text: yellow(`Limited Access → ${cyan(`${targetOwner}/${repo}`)}`) });
|
|
235
|
+
console.log(boxen(chalk.yellow(`🛡️ Maintainer Mode Warning\n\n`) +
|
|
236
|
+
chalk.dim(`You do not have push/admin permissions for this repository.\n`) +
|
|
237
|
+
chalk.dim(`Most maintainer actions (bulk issues, merging PRs) will fail.`), { padding: 1, borderColor: 'yellow', borderStyle: 'round' }));
|
|
238
|
+
const { proceed } = await inquirer.prompt([{
|
|
239
|
+
type: 'confirm',
|
|
240
|
+
name: 'proceed',
|
|
241
|
+
message: 'Continue anyway?',
|
|
242
|
+
default: false
|
|
243
|
+
}]);
|
|
244
|
+
if (!proceed) {
|
|
245
|
+
targetConfirmed = false;
|
|
246
|
+
throw new BackToMenu();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
checkSpin.success({ text: green(`Permissions verified for ${cyan(`${targetOwner}/${repo}`)}`) });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
checkSpin.warn({ text: yellow('Could not verify permissions (API limit or private)') });
|
|
255
|
+
}
|
|
256
|
+
console.log(green(`\n ✅ Locked in → ${cyan(`${targetOwner}/${repo}`)}\n`));
|
|
189
257
|
}
|
|
190
258
|
// ── Mode Selector ──────────────────────────────────────────────────────
|
|
191
259
|
async function mainMenu() {
|
|
@@ -333,16 +401,7 @@ async function maintainerMenu() {
|
|
|
333
401
|
if (category === 'back')
|
|
334
402
|
break;
|
|
335
403
|
if (category === 'switch') {
|
|
336
|
-
await
|
|
337
|
-
const a = await inquirer.prompt([
|
|
338
|
-
{ type: 'input', name: 'owner', message: cyan('👤 New Owner/Org:'), default: getOwner() },
|
|
339
|
-
{ type: 'input', name: 'repo', message: cyan('📦 New Repo:'), default: getRepo() },
|
|
340
|
-
]);
|
|
341
|
-
setRepo(a.owner, a.repo);
|
|
342
|
-
const s = createSpinner(dim('Switching...')).start();
|
|
343
|
-
await sleep(400);
|
|
344
|
-
s.success({ text: green(`Now targeting ${cyan(`${a.owner}/${a.repo}`)}`) });
|
|
345
|
-
});
|
|
404
|
+
await ensureTargetRepo(true);
|
|
346
405
|
continue;
|
|
347
406
|
}
|
|
348
407
|
if (category === 'issues')
|
package/dist/commands/repos.js
CHANGED
|
@@ -91,9 +91,23 @@ export async function listRepos(opts) {
|
|
|
91
91
|
const spinner = !opts.silent ? ora('Fetching repos...').start() : null;
|
|
92
92
|
const octokit = getOctokit();
|
|
93
93
|
try {
|
|
94
|
-
let repos;
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
let repos = [];
|
|
95
|
+
const owner = opts.org || opts.owner;
|
|
96
|
+
if (owner) {
|
|
97
|
+
// Check if it's an org or user
|
|
98
|
+
try {
|
|
99
|
+
const { data: entity } = await octokit.users.getByUsername({ username: owner });
|
|
100
|
+
if (entity.type === 'Organization') {
|
|
101
|
+
({ data: repos } = await octokit.repos.listForOrg({ org: owner, per_page: opts.limit || 100, sort: 'updated' }));
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
({ data: repos } = await octokit.repos.listForUser({ username: owner, per_page: opts.limit || 100, sort: 'updated' }));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// Fallback to org if lookup fails (might be a private org only visible to token)
|
|
109
|
+
({ data: repos } = await octokit.repos.listForOrg({ org: owner, per_page: opts.limit || 100, sort: 'updated' }));
|
|
110
|
+
}
|
|
97
111
|
}
|
|
98
112
|
else {
|
|
99
113
|
({ data: repos } = await octokit.repos.listForAuthenticatedUser({ per_page: opts.limit || 100, sort: 'updated' }));
|
package/dist/core/github.js
CHANGED
|
@@ -94,6 +94,10 @@ export async function getRepoDetails(owner, repo) {
|
|
|
94
94
|
const { data } = await getOctokit().repos.get({ owner, repo });
|
|
95
95
|
return data;
|
|
96
96
|
}
|
|
97
|
+
export async function getRepoPermissions(owner, repo) {
|
|
98
|
+
const data = await getRepoDetails(owner, repo);
|
|
99
|
+
return data.permissions || { admin: false, push: false, pull: true };
|
|
100
|
+
}
|
|
97
101
|
export async function getLatestCheckRuns(owner, repo, ref) {
|
|
98
102
|
const octokit = getOctokit();
|
|
99
103
|
const { data: checks } = await octokit.checks.listForRef({ owner, repo, ref });
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { execSync } from 'child_process';
|
|
|
15
15
|
import boxen from 'boxen';
|
|
16
16
|
import { createSpinner } from 'nanospinner';
|
|
17
17
|
import { Octokit } from '@octokit/rest';
|
|
18
|
-
import { initGitHub, setRepo, getOwner, getRepo, getOctokit, loadConfig, saveConfig, getToken } from './core/github.js';
|
|
18
|
+
import { initGitHub, setRepo, getOwner, getRepo, getOctokit, loadConfig, saveConfig, getToken, getAuthenticatedUser, getRepoPermissions } from './core/github.js';
|
|
19
19
|
|
|
20
20
|
import * as issues from './commands/issues.js';
|
|
21
21
|
import * as prs from './commands/prs.js';
|
|
@@ -25,6 +25,7 @@ import * as releases from './commands/releases.js';
|
|
|
25
25
|
import * as contribute from './commands/contribute.js';
|
|
26
26
|
|
|
27
27
|
const VERSION = '2.0.0';
|
|
28
|
+
let targetConfirmed = false;
|
|
28
29
|
|
|
29
30
|
// ── Styling ────────────────────────────────────────────────────────────
|
|
30
31
|
const cyber = gradient(['#ff00ff', '#00ffff', '#ff00ff']);
|
|
@@ -178,40 +179,113 @@ async function ensureAuthenticated() {
|
|
|
178
179
|
/**
|
|
179
180
|
* Ensures we have a target repository (Owner/Repo)
|
|
180
181
|
*/
|
|
181
|
-
async function ensureTargetRepo() {
|
|
182
|
+
async function ensureTargetRepo(force = false) {
|
|
182
183
|
let owner = getOwner();
|
|
183
184
|
let repo = getRepo();
|
|
184
185
|
|
|
185
|
-
if (owner && repo) {
|
|
186
|
-
return;
|
|
186
|
+
if (!force && owner && repo) {
|
|
187
|
+
if (targetConfirmed) return;
|
|
188
|
+
|
|
189
|
+
const { confirm } = await inquirer.prompt([{
|
|
190
|
+
type: 'confirm',
|
|
191
|
+
name: 'confirm',
|
|
192
|
+
message: cyan('🎯 Targeting ') + bold(`${owner}/${repo}`) + cyan('. Correct?'),
|
|
193
|
+
default: true
|
|
194
|
+
}]);
|
|
195
|
+
if (confirm) {
|
|
196
|
+
targetConfirmed = true;
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
187
199
|
}
|
|
188
200
|
|
|
189
201
|
console.log(neon('\n 📦 Project Targeting — which repo are we working on?\n'));
|
|
190
202
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
203
|
+
// 1. Get the Owner/Org
|
|
204
|
+
const { targetOwner } = await inquirer.prompt([{
|
|
205
|
+
type: 'input',
|
|
206
|
+
name: 'targetOwner',
|
|
207
|
+
message: cyan('👤 GitHub Owner/Org:'),
|
|
208
|
+
default: owner || await getAuthenticatedUser(),
|
|
209
|
+
validate: (v: string) => v.length > 0 || 'Required',
|
|
210
|
+
}]);
|
|
211
|
+
|
|
212
|
+
// 2. Fetch Repos for that Owner
|
|
213
|
+
const spin = createSpinner(dim(`Fetching repositories for ${targetOwner}...`)).start();
|
|
214
|
+
const fetchedRepos = await repos.listRepos({ owner: targetOwner, silent: true });
|
|
215
|
+
|
|
216
|
+
if (fetchedRepos.length > 0) {
|
|
217
|
+
spin.success({ text: green(`Found ${fetchedRepos.length} repositories for ${targetOwner}`) });
|
|
218
|
+
|
|
219
|
+
const { selectedRepo } = await inquirer.prompt([{
|
|
220
|
+
type: 'list',
|
|
221
|
+
name: 'selectedRepo',
|
|
222
|
+
message: cyan('📦 Select Repository:'),
|
|
223
|
+
choices: [
|
|
224
|
+
...fetchedRepos.map((r: any) => ({ name: r.name, value: r.name })),
|
|
225
|
+
new inquirer.Separator(),
|
|
226
|
+
{ name: '✍️ Enter manually...', value: '__manual__' }
|
|
227
|
+
],
|
|
228
|
+
loop: false
|
|
229
|
+
}]);
|
|
230
|
+
|
|
231
|
+
if (selectedRepo !== '__manual__') {
|
|
232
|
+
repo = selectedRepo;
|
|
233
|
+
} else {
|
|
234
|
+
const { manualRepo } = await inquirer.prompt([{
|
|
235
|
+
type: 'input',
|
|
236
|
+
name: 'manualRepo',
|
|
237
|
+
message: cyan('📦 Repository name:'),
|
|
238
|
+
validate: (v: string) => v.length > 0 || 'Required',
|
|
239
|
+
}]);
|
|
240
|
+
repo = manualRepo;
|
|
241
|
+
}
|
|
242
|
+
} else {
|
|
243
|
+
spin.warn({ text: yellow(`No public repositories found for ${targetOwner}.`) });
|
|
244
|
+
const { manualRepo } = await inquirer.prompt([{
|
|
200
245
|
type: 'input',
|
|
201
|
-
name: '
|
|
202
|
-
message: cyan('📦 Repository name:'),
|
|
203
|
-
default: repo || '',
|
|
246
|
+
name: 'manualRepo',
|
|
247
|
+
message: cyan('📦 Enter Repository name manually:'),
|
|
204
248
|
validate: (v: string) => v.length > 0 || 'Required',
|
|
205
|
-
}
|
|
206
|
-
|
|
249
|
+
}]);
|
|
250
|
+
repo = manualRepo;
|
|
251
|
+
}
|
|
207
252
|
|
|
208
|
-
setRepo(
|
|
209
|
-
saveConfig({ token: getToken(), owner:
|
|
253
|
+
setRepo(targetOwner, repo as string);
|
|
254
|
+
saveConfig({ token: getToken(), owner: targetOwner, repo: repo as string });
|
|
255
|
+
targetConfirmed = true;
|
|
210
256
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
257
|
+
// 4. Permission Check (for Maintainer safety)
|
|
258
|
+
const checkSpin = createSpinner(dim('Checking maintainer permissions...')).start();
|
|
259
|
+
try {
|
|
260
|
+
const perms = await getRepoPermissions(targetOwner, repo as string);
|
|
261
|
+
if (!perms.push && !perms.admin) {
|
|
262
|
+
checkSpin.warn({ text: yellow(`Limited Access → ${cyan(`${targetOwner}/${repo}`)}`) });
|
|
263
|
+
console.log(boxen(
|
|
264
|
+
chalk.yellow(`🛡️ Maintainer Mode Warning\n\n`) +
|
|
265
|
+
chalk.dim(`You do not have push/admin permissions for this repository.\n`) +
|
|
266
|
+
chalk.dim(`Most maintainer actions (bulk issues, merging PRs) will fail.`),
|
|
267
|
+
{ padding: 1, borderColor: 'yellow', borderStyle: 'round' }
|
|
268
|
+
));
|
|
269
|
+
|
|
270
|
+
const { proceed } = await inquirer.prompt([{
|
|
271
|
+
type: 'confirm',
|
|
272
|
+
name: 'proceed',
|
|
273
|
+
message: 'Continue anyway?',
|
|
274
|
+
default: false
|
|
275
|
+
}]);
|
|
276
|
+
|
|
277
|
+
if (!proceed) {
|
|
278
|
+
targetConfirmed = false;
|
|
279
|
+
throw new BackToMenu();
|
|
280
|
+
}
|
|
281
|
+
} else {
|
|
282
|
+
checkSpin.success({ text: green(`Permissions verified for ${cyan(`${targetOwner}/${repo}`)}`) });
|
|
283
|
+
}
|
|
284
|
+
} catch {
|
|
285
|
+
checkSpin.warn({ text: yellow('Could not verify permissions (API limit or private)') });
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
console.log(green(`\n ✅ Locked in → ${cyan(`${targetOwner}/${repo}`)}\n`));
|
|
215
289
|
}
|
|
216
290
|
|
|
217
291
|
// ── Mode Selector ──────────────────────────────────────────────────────
|
|
@@ -365,16 +439,7 @@ async function maintainerMenu() {
|
|
|
365
439
|
if (category === 'back') break;
|
|
366
440
|
|
|
367
441
|
if (category === 'switch') {
|
|
368
|
-
await
|
|
369
|
-
const a = await inquirer.prompt([
|
|
370
|
-
{ type: 'input', name: 'owner', message: cyan('👤 New Owner/Org:'), default: getOwner() },
|
|
371
|
-
{ type: 'input', name: 'repo', message: cyan('📦 New Repo:'), default: getRepo() },
|
|
372
|
-
]);
|
|
373
|
-
setRepo(a.owner, a.repo);
|
|
374
|
-
const s = createSpinner(dim('Switching...')).start();
|
|
375
|
-
await sleep(400);
|
|
376
|
-
s.success({ text: green(`Now targeting ${cyan(`${a.owner}/${a.repo}`)}`) });
|
|
377
|
-
});
|
|
442
|
+
await ensureTargetRepo(true);
|
|
378
443
|
continue;
|
|
379
444
|
}
|
|
380
445
|
|
package/src/commands/repos.ts
CHANGED
|
@@ -101,14 +101,27 @@ export async function setTopics(name: string, topics: string[], opts: { org?: st
|
|
|
101
101
|
} catch (e: any) { spinner.fail(e.message); }
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
export async function listRepos(opts: { org?: string; limit?: number; silent?: boolean }) {
|
|
104
|
+
export async function listRepos(opts: { org?: string; owner?: string; limit?: number; silent?: boolean }) {
|
|
105
105
|
const spinner = !opts.silent ? ora('Fetching repos...').start() : null;
|
|
106
106
|
const octokit = getOctokit();
|
|
107
107
|
|
|
108
108
|
try {
|
|
109
|
-
let repos: any[];
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
let repos: any[] = [];
|
|
110
|
+
const owner = opts.org || opts.owner;
|
|
111
|
+
|
|
112
|
+
if (owner) {
|
|
113
|
+
// Check if it's an org or user
|
|
114
|
+
try {
|
|
115
|
+
const { data: entity } = await octokit.users.getByUsername({ username: owner });
|
|
116
|
+
if (entity.type === 'Organization') {
|
|
117
|
+
({ data: repos } = await octokit.repos.listForOrg({ org: owner, per_page: opts.limit || 100, sort: 'updated' }));
|
|
118
|
+
} else {
|
|
119
|
+
({ data: repos } = await octokit.repos.listForUser({ username: owner, per_page: opts.limit || 100, sort: 'updated' }));
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
// Fallback to org if lookup fails (might be a private org only visible to token)
|
|
123
|
+
({ data: repos } = await octokit.repos.listForOrg({ org: owner, per_page: opts.limit || 100, sort: 'updated' }));
|
|
124
|
+
}
|
|
112
125
|
} else {
|
|
113
126
|
({ data: repos } = await octokit.repos.listForAuthenticatedUser({ per_page: opts.limit || 100, sort: 'updated' }));
|
|
114
127
|
}
|
package/src/core/github.ts
CHANGED
|
@@ -114,6 +114,11 @@ export async function getRepoDetails(owner: string, repo: string) {
|
|
|
114
114
|
return data;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
export async function getRepoPermissions(owner: string, repo: string) {
|
|
118
|
+
const data = await getRepoDetails(owner, repo);
|
|
119
|
+
return data.permissions || { admin: false, push: false, pull: true };
|
|
120
|
+
}
|
|
121
|
+
|
|
117
122
|
export async function getLatestCheckRuns(owner: string, repo: string, ref: string) {
|
|
118
123
|
const octokit = getOctokit();
|
|
119
124
|
const { data: checks } = await octokit.checks.listForRef({ owner, repo, ref });
|