ya-git-jira 1.3.0 → 1.5.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/bin/git-bump.ts +7 -4
- package/bin/{git-jira-issues.ts → git-jira-issue-list.ts} +10 -7
- package/bin/git-jira-issue-show.ts +40 -0
- package/bin/git-jira-issue.ts +14 -25
- package/bin/git-jira-start.ts +8 -5
- package/bin/git-jira.ts +9 -6
- package/bin/git-lab-group-list.ts +35 -0
- package/bin/git-lab-group.ts +25 -0
- package/bin/git-lab-merge-active.ts +41 -0
- package/bin/git-lab-merge-todo.ts +36 -0
- package/bin/git-lab-merge-train-list.ts +26 -0
- package/bin/git-lab-merge-train.ts +25 -0
- package/bin/git-lab-merge.ts +29 -0
- package/bin/git-lab-namespace-list.ts +27 -0
- package/bin/git-lab-namespace.ts +24 -0
- package/bin/{git-lab-projects.ts → git-lab-project-list.ts} +13 -10
- package/bin/git-lab-project-pipeline-list.ts +46 -0
- package/bin/git-lab-project-pipeline.ts +24 -0
- package/bin/git-lab-project-whereami.ts +43 -0
- package/bin/git-lab-project.ts +28 -0
- package/bin/git-lab-whoami.ts +8 -5
- package/bin/git-lab.ts +18 -8
- package/bin/gitj.ts +7 -5
- package/build.ts +1 -1
- package/bun.lockb +0 -0
- package/dist/bin/git-bump.js +55 -17
- package/dist/bin/{git-jira-issues.js → git-jira-issue-list.js} +71 -28
- package/dist/bin/{git-lab-mergetrain.js → git-jira-issue-show.js} +100 -43
- package/dist/bin/git-jira-issue.js +106 -33
- package/dist/bin/git-jira-start.js +70 -27
- package/dist/bin/git-jira.js +117 -59
- package/dist/bin/git-lab-group-list.js +2793 -0
- package/dist/bin/git-lab-group.js +2805 -0
- package/dist/bin/git-lab-groups.js +1992 -0
- package/dist/bin/git-lab-merge-active.js +2798 -0
- package/dist/bin/git-lab-merge-todo.js +2793 -0
- package/dist/bin/git-lab-merge-train-list.js +2754 -0
- package/dist/bin/git-lab-merge-train.js +2766 -0
- package/dist/bin/git-lab-merge.js +2865 -0
- package/dist/bin/git-lab-namespace-list.js +1967 -0
- package/dist/bin/git-lab-namespace.js +1979 -0
- package/dist/bin/git-lab-namespaces.js +1984 -0
- package/dist/bin/git-lab-project-list.js +2761 -0
- package/dist/bin/git-lab-project-pipeline-list.js +2800 -0
- package/dist/bin/git-lab-project-pipeline.js +2812 -0
- package/dist/bin/git-lab-project-whereami.js +2764 -0
- package/dist/bin/git-lab-project.js +2805 -0
- package/dist/bin/git-lab-projects-list.js +1996 -0
- package/dist/bin/git-lab-projects-whereami.js +1999 -0
- package/dist/bin/git-lab-projects.js +138 -35
- package/dist/bin/git-lab-whoami.js +100 -56
- package/dist/bin/git-lab.js +1123 -58
- package/dist/bin/gitj.js +1260 -172
- package/dist/index.js +881 -35
- package/index.ts +1 -1
- package/lib/gitlab/api.ts +46 -0
- package/lib/gitlab/config.ts +21 -0
- package/lib/gitlab/dlog.ts +2 -0
- package/lib/gitlab/group.ts +12 -0
- package/lib/gitlab/index.ts +8 -0
- package/lib/gitlab/merge-request.ts +31 -0
- package/lib/gitlab/merge-trains.ts +19 -0
- package/lib/gitlab/namespace.ts +12 -0
- package/lib/gitlab/pipeline.ts +32 -0
- package/lib/gitlab/project.ts +78 -0
- package/lib/gitlab/user.ts +13 -0
- package/lib/is_main.ts +21 -2
- package/lib/jira.ts +12 -7
- package/lib/package.ts +35 -0
- package/package.json +21 -4
- package/tests/all-help.test.ts +46 -0
- package/tests/git.test.ts +8 -7
- package/tests/gitj.test.ts +1 -1
- package/lib/gitlab.ts +0 -86
package/index.ts
CHANGED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { JSONValue } from "../json"
|
|
2
|
+
import { getGitlabConfig } from "./config"
|
|
3
|
+
|
|
4
|
+
function getNextLink(link: string | null): string | undefined {
|
|
5
|
+
if (!link) {
|
|
6
|
+
return undefined
|
|
7
|
+
}
|
|
8
|
+
const regex = /<([^>]+)>; rel="next"/
|
|
9
|
+
const match = link.match(regex)
|
|
10
|
+
const next = match ? match[1] : undefined
|
|
11
|
+
return next
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function gitlabApi(endpoint: string): Promise<JSONValue> {
|
|
15
|
+
if (endpoint.startsWith("/")) {
|
|
16
|
+
console.warn(`gitlabApi: endpoint ${endpoint} starts with /, removing it`)
|
|
17
|
+
endpoint = endpoint.slice(1)
|
|
18
|
+
}
|
|
19
|
+
const method = "GET"
|
|
20
|
+
const { host, token } = await getGitlabConfig()
|
|
21
|
+
const base = `https://${host}/api/v4`
|
|
22
|
+
const requested = 100
|
|
23
|
+
const sep = endpoint.includes('?') ? '&' : '?'
|
|
24
|
+
const uri = `${base}/${endpoint}${sep}per_page=${requested}`
|
|
25
|
+
const headers = new Headers()
|
|
26
|
+
headers.append("Accept", "application/json")
|
|
27
|
+
headers.append('Private-Token', token)
|
|
28
|
+
const options = {
|
|
29
|
+
method,
|
|
30
|
+
headers,
|
|
31
|
+
}
|
|
32
|
+
let request = new Request(uri, options)
|
|
33
|
+
const response = await fetch(request)
|
|
34
|
+
let link = getNextLink(response.headers.get('Link'))
|
|
35
|
+
let partial = (await response.json()) as Array<JSONValue>
|
|
36
|
+
let result: Array<JSONValue> = partial
|
|
37
|
+
while (partial.length == requested && link)
|
|
38
|
+
{
|
|
39
|
+
let request = new Request(link, options)
|
|
40
|
+
const next_response = await fetch(request)
|
|
41
|
+
link = getNextLink(next_response.headers.get('Link'))
|
|
42
|
+
partial = (await next_response.json()) as Array<JSONValue>
|
|
43
|
+
result = result.concat(partial)
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { getConfig } from "../git"
|
|
2
|
+
|
|
3
|
+
export interface GitlabConfig {
|
|
4
|
+
host: string
|
|
5
|
+
user: string
|
|
6
|
+
token: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const hostP = getConfig("gitlab.host")
|
|
10
|
+
const userP = getConfig("user.email")
|
|
11
|
+
const tokenP = getConfig("gitlab.token")
|
|
12
|
+
|
|
13
|
+
export async function getGitlabConfig(): Promise<GitlabConfig> {
|
|
14
|
+
const host = await hostP
|
|
15
|
+
if (!host) throw new Error("gitlab.host not in git config")
|
|
16
|
+
const user = await userP
|
|
17
|
+
if (!user) throw new Error("user.email not in git config")
|
|
18
|
+
const token = await tokenP
|
|
19
|
+
if (!token) throw new Error("gitlab.token not in git config")
|
|
20
|
+
return { host, user, token }
|
|
21
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { gitlabApi } from './api'
|
|
2
|
+
import { JSONValue } from '../json'
|
|
3
|
+
|
|
4
|
+
export type Group = JSONValue & {
|
|
5
|
+
id: number
|
|
6
|
+
name: string
|
|
7
|
+
full_path: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function getGroups(): Promise<Array<Group>> {
|
|
11
|
+
return await gitlabApi(`groups`) as Array<Group>
|
|
12
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { projectScopedGet } from './project'
|
|
2
|
+
import type { JSONValue } from '../json'
|
|
3
|
+
import { whoami } from './user'
|
|
4
|
+
import { gitlabApi } from './api'
|
|
5
|
+
|
|
6
|
+
export type MergeRequest = JSONValue & {
|
|
7
|
+
id: number
|
|
8
|
+
title : string
|
|
9
|
+
description : string
|
|
10
|
+
state : string
|
|
11
|
+
source_branch: string
|
|
12
|
+
target_branch: string
|
|
13
|
+
web_url: string
|
|
14
|
+
merge_status: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function getMergeRequest(id: string): Promise<MergeRequest> {
|
|
18
|
+
return await projectScopedGet(`merge_requests/${id}`) as MergeRequest
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function getMyMergeRequestsInProgress() : Promise<Array<MergeRequest>>
|
|
22
|
+
{
|
|
23
|
+
const me = await whoami()
|
|
24
|
+
return await gitlabApi(`merge_requests?state=opened&author_id=${me.id}`) as Array<MergeRequest>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function getMyMergeRequestsToReview() : Promise<Array<MergeRequest>>
|
|
28
|
+
{
|
|
29
|
+
const me = await whoami()
|
|
30
|
+
return await gitlabApi(`merge_requests?state=opened&reviewer_id=${me.id}`) as Array<MergeRequest>
|
|
31
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { projectScopedGet } from './project'
|
|
2
|
+
import type { JSONValue } from '../json'
|
|
3
|
+
|
|
4
|
+
export type MergeTrain = JSONValue & {
|
|
5
|
+
id: number
|
|
6
|
+
merge_request: {
|
|
7
|
+
title: string
|
|
8
|
+
web_url: string
|
|
9
|
+
}
|
|
10
|
+
user: {
|
|
11
|
+
username: string
|
|
12
|
+
}
|
|
13
|
+
target_branch: string
|
|
14
|
+
status: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function getMergeTrains(): Promise<MergeTrain[]> {
|
|
18
|
+
return await projectScopedGet(`merge_trains`) as MergeTrain[]
|
|
19
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { gitlabApi } from "./api"
|
|
2
|
+
import { JSONValue } from "../json"
|
|
3
|
+
|
|
4
|
+
type Namespace = JSONValue & {
|
|
5
|
+
id: number
|
|
6
|
+
name: string
|
|
7
|
+
full_path: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function getNamespaces(): Promise<Namespace[]> {
|
|
11
|
+
return await gitlabApi(`namespaces`) as Namespace[]
|
|
12
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { dlog } from "./dlog"
|
|
2
|
+
import { projectScopedGet } from "./project"
|
|
3
|
+
import { whoami } from "./user"
|
|
4
|
+
import type { JSONValue } from "../json"
|
|
5
|
+
|
|
6
|
+
export type Pipeline = JSONValue & {
|
|
7
|
+
id: number
|
|
8
|
+
status: string
|
|
9
|
+
ref: string
|
|
10
|
+
sha: string
|
|
11
|
+
web_url: string
|
|
12
|
+
updated_at: string // datetime string like "2021-03-18T15:00:00.000Z"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type PipelineStatus = 'success' | 'running'
|
|
16
|
+
|
|
17
|
+
export interface GetPipelineOptions {
|
|
18
|
+
days: number
|
|
19
|
+
status: PipelineStatus
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function getProjectPipelines(options: GetPipelineOptions): Promise<Array<Pipeline>> {
|
|
23
|
+
const { days, status } = options
|
|
24
|
+
const me = await whoami()
|
|
25
|
+
const username = me.username
|
|
26
|
+
const date = new Date()
|
|
27
|
+
const pastDate = date.getDate() - days;
|
|
28
|
+
date.setDate(pastDate)
|
|
29
|
+
const updated = date.toISOString()
|
|
30
|
+
dlog(`updated: ${updated}`)
|
|
31
|
+
return await projectScopedGet(`pipelines?status=${status}&username=${username}&updated_after=${updated}`) as Array<Pipeline>
|
|
32
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { getGitlabConfig } from "./config"
|
|
2
|
+
import { getRemote } from "../git"
|
|
3
|
+
import { gitlabApi } from "./api"
|
|
4
|
+
import { type JSONValue } from "../json"
|
|
5
|
+
import path from 'node:path'
|
|
6
|
+
import { dlog } from "./dlog"
|
|
7
|
+
|
|
8
|
+
export type Project = JSONValue & {
|
|
9
|
+
id: number
|
|
10
|
+
name: string
|
|
11
|
+
path: string
|
|
12
|
+
path_with_namespace: string
|
|
13
|
+
visibility: string
|
|
14
|
+
ssh_url_to_repo: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function getProjects(match: string): Promise<Array<Project>> {
|
|
18
|
+
let search = ''
|
|
19
|
+
if (match) {
|
|
20
|
+
const m = encodeURIComponent(match)
|
|
21
|
+
search = `&search=${m}`
|
|
22
|
+
}
|
|
23
|
+
const projects = await gitlabApi(`projects?membership=true&simple=true${search}`)
|
|
24
|
+
if (!projects) {
|
|
25
|
+
throw new Error(`No projects!`)
|
|
26
|
+
} else if (!Array.isArray(projects)) {
|
|
27
|
+
console.log(projects)
|
|
28
|
+
throw new Error(`Projects is not an array!`)
|
|
29
|
+
}
|
|
30
|
+
const projs = projects as Array<Project>
|
|
31
|
+
|
|
32
|
+
const filtered = projs.filter((p: Project): boolean => {
|
|
33
|
+
return p.path_with_namespace.toLowerCase().includes(match.toLowerCase())
|
|
34
|
+
})
|
|
35
|
+
return filtered
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// git@gitlab.com:etagen-internal/linear-generator-config.git
|
|
39
|
+
export async function findProject(ssh_url: string): Promise<Project | undefined> {
|
|
40
|
+
const parts = ssh_url.split(':')
|
|
41
|
+
if (parts.length != 2) {
|
|
42
|
+
throw new Error(`${ssh_url} is invalid, could not be split into two parts at :`)
|
|
43
|
+
}
|
|
44
|
+
const name = path.basename(parts[1], '.git')
|
|
45
|
+
|
|
46
|
+
const projects = await getProjects(name) as Array<Project>
|
|
47
|
+
const project = projects.find((p: Project): boolean => {
|
|
48
|
+
return p.ssh_url_to_repo === ssh_url
|
|
49
|
+
})
|
|
50
|
+
return project
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function projectScopedGet(endpoint: string): Promise<JSONValue> {
|
|
54
|
+
if (endpoint.startsWith("/")) {
|
|
55
|
+
console.warn(`gitlabApi: endpoint ${endpoint} starts with /, removing it`)
|
|
56
|
+
endpoint = endpoint.slice(1)
|
|
57
|
+
}
|
|
58
|
+
const method = "GET"
|
|
59
|
+
const { host, token } = await getGitlabConfig()
|
|
60
|
+
const remote = await getRemote()
|
|
61
|
+
const project = await findProject(remote)
|
|
62
|
+
if (!project) {
|
|
63
|
+
throw new Error(`Could not find project for remote ${remote}`)
|
|
64
|
+
}
|
|
65
|
+
const base = `https://${host}/api/v4/projects/${project.id}`
|
|
66
|
+
const uri = `${base}/${endpoint}`
|
|
67
|
+
dlog(`projectScopedGet uri: ${uri}`)
|
|
68
|
+
const headers = new Headers()
|
|
69
|
+
headers.append("Accept", "application/json")
|
|
70
|
+
headers.append('Private-Token', token)
|
|
71
|
+
const options = {
|
|
72
|
+
method,
|
|
73
|
+
headers,
|
|
74
|
+
}
|
|
75
|
+
const request = new Request(uri, options)
|
|
76
|
+
const response = await fetch(request)
|
|
77
|
+
return await response.json()
|
|
78
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { JSONValue } from "../json"
|
|
2
|
+
import { gitlabApi } from "./api"
|
|
3
|
+
|
|
4
|
+
export type User = JSONValue & {
|
|
5
|
+
id: number
|
|
6
|
+
name: string
|
|
7
|
+
username: string
|
|
8
|
+
email: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function whoami(): Promise<User> {
|
|
12
|
+
return await gitlabApi("user") as User
|
|
13
|
+
}
|
package/lib/is_main.ts
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
+
|
|
3
|
+
function justBase(filename: string): string {
|
|
4
|
+
const ext = path.extname(filename)
|
|
5
|
+
const base = path.basename(filename, ext)
|
|
6
|
+
return base
|
|
7
|
+
}
|
|
8
|
+
|
|
2
9
|
export function isMain(self: string): boolean {
|
|
3
|
-
const
|
|
4
|
-
|
|
10
|
+
const arg1 = Bun.argv[1]
|
|
11
|
+
const argv1Base = justBase(arg1)
|
|
12
|
+
const selfBase = justBase(self)
|
|
13
|
+
const result = argv1Base === selfBase
|
|
14
|
+
// if (result) {
|
|
15
|
+
// console.log({
|
|
16
|
+
// arg1,
|
|
17
|
+
// self,
|
|
18
|
+
// argv1Base,
|
|
19
|
+
// selfBase,
|
|
20
|
+
// result,
|
|
21
|
+
// })
|
|
22
|
+
// }
|
|
23
|
+
return result
|
|
5
24
|
}
|
package/lib/jira.ts
CHANGED
|
@@ -25,11 +25,14 @@ export async function getJiraConfig(): Promise<JiraConfig> {
|
|
|
25
25
|
return { host, token }
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
export async function
|
|
28
|
+
export async function jiraApi(endpoint: string): Promise<JSONValue> {
|
|
29
|
+
if (endpoint.startsWith("/")) {
|
|
30
|
+
console.warn(`jiraApi: endpoint ${endpoint} starts with /`)
|
|
31
|
+
endpoint = endpoint.slice(1)
|
|
32
|
+
}
|
|
29
33
|
const method = "GET"
|
|
30
34
|
const { host, token } = await getJiraConfig()
|
|
31
|
-
const
|
|
32
|
-
const uri = `${base}/${endpoint}`
|
|
35
|
+
const uri = `https://${host}/rest/api/3/${endpoint}`
|
|
33
36
|
const auth = `Basic ${token}`
|
|
34
37
|
const headers = new Headers()
|
|
35
38
|
headers.append("Authorization", auth)
|
|
@@ -40,11 +43,13 @@ export async function get(endpoint: string): Promise<JSONValue> {
|
|
|
40
43
|
}
|
|
41
44
|
const request = new Request(uri, options)
|
|
42
45
|
const response = await fetch(request)
|
|
43
|
-
|
|
46
|
+
const result = await response.json()
|
|
47
|
+
return result;
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
export async function getIssue(issue: string): Promise<Issue> {
|
|
47
|
-
|
|
51
|
+
const result = await jiraApi(`issue/${issue}`) as Issue
|
|
52
|
+
return result
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
type Myself = JSONValue & {
|
|
@@ -52,7 +57,7 @@ type Myself = JSONValue & {
|
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
export async function getMyself(): Promise<Myself> {
|
|
55
|
-
return await
|
|
60
|
+
return await jiraApi("/myself") as Myself
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
type SearchResponse = JSONValue & {
|
|
@@ -63,6 +68,6 @@ export async function myUnresolvedIssues(): Promise<Array<Issue>> {
|
|
|
63
68
|
const myself = await getMyself()
|
|
64
69
|
const myselfId = myself.accountId
|
|
65
70
|
const jql = `assignee = ${myselfId} AND resolution = Unresolved`
|
|
66
|
-
const issues = await
|
|
71
|
+
const issues = await jiraApi(`/search?jql=${encodeURIComponent(jql)}`) as SearchResponse
|
|
67
72
|
return issues.issues
|
|
68
73
|
}
|
package/lib/package.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import fs from 'node:fs'
|
|
4
|
+
|
|
5
|
+
export function findPackageJson()
|
|
6
|
+
{
|
|
7
|
+
const cwd = import.meta.dir
|
|
8
|
+
let dir = cwd
|
|
9
|
+
while (dir !== '/') {
|
|
10
|
+
const packageJson = path.join(dir, 'package.json')
|
|
11
|
+
if (fs.existsSync(packageJson)) {
|
|
12
|
+
return packageJson
|
|
13
|
+
}
|
|
14
|
+
dir = path.dirname(dir)
|
|
15
|
+
}
|
|
16
|
+
return null
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function getPackageJson()
|
|
20
|
+
{
|
|
21
|
+
const packagePath = findPackageJson()
|
|
22
|
+
if (!packagePath) {
|
|
23
|
+
throw new Error(`No package.json found in ${import.meta.dir} or any parent directory`)
|
|
24
|
+
}
|
|
25
|
+
const packageJsonText = fs.readFileSync(packagePath, 'utf8')
|
|
26
|
+
return JSON.parse(packageJsonText)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const packageJsonPromise = getPackageJson()
|
|
30
|
+
|
|
31
|
+
export async function getPackageVersion()
|
|
32
|
+
{
|
|
33
|
+
const packageJson = await packageJsonPromise
|
|
34
|
+
return packageJson.version
|
|
35
|
+
}
|
package/package.json
CHANGED
|
@@ -1,16 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ya-git-jira",
|
|
3
3
|
"description": "git extensions for Jira integration. Assumes bun is installed.",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.5.0",
|
|
5
5
|
"module": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
8
|
"git-bump": "dist/bin/git-bump.js",
|
|
9
|
+
"git-jira-issue-list": "dist/bin/git-jira-issue-list.js",
|
|
10
|
+
"git-jira-issue-show": "dist/bin/git-jira-issue-show.js",
|
|
9
11
|
"git-jira-issue": "dist/bin/git-jira-issue.js",
|
|
10
|
-
"git-jira-issues": "dist/bin/git-jira-issues.js",
|
|
11
12
|
"git-jira-start": "dist/bin/git-jira-start.js",
|
|
12
13
|
"git-jira": "dist/bin/git-jira.js",
|
|
13
|
-
"git-lab-
|
|
14
|
+
"git-lab-group-list": "dist/bin/git-lab-group-list.js",
|
|
15
|
+
"git-lab-group": "dist/bin/git-lab-group.js",
|
|
16
|
+
"git-lab-merge-active": "dist/bin/git-lab-merge-active.js",
|
|
17
|
+
"git-lab-merge-todo": "dist/bin/git-lab-merge-todo.js",
|
|
18
|
+
"git-lab-merge-train-list": "dist/bin/git-lab-merge-train-list.js",
|
|
19
|
+
"git-lab-merge-train": "dist/bin/git-lab-merge-train.js",
|
|
20
|
+
"git-lab-merge": "dist/bin/git-lab-merge.js",
|
|
21
|
+
"git-lab-namespace-list": "dist/bin/git-lab-namespace-list.js",
|
|
22
|
+
"git-lab-namespace": "dist/bin/git-lab-namespace.js",
|
|
23
|
+
"git-lab-project-list": "dist/bin/git-lab-project-list.js",
|
|
24
|
+
"git-lab-project-pipeline": "dist/bin/git-lab-project-pipeline.js",
|
|
25
|
+
"git-lab-project-pipeline-list": "dist/bin/git-lab-project-pipeline-list.js",
|
|
26
|
+
"git-lab-project-whereami": "dist/bin/git-lab-project-whereami.js",
|
|
27
|
+
"git-lab-project": "dist/bin/git-lab-project.js",
|
|
14
28
|
"git-lab-whoami": "dist/bin/git-lab-whoami.js",
|
|
15
29
|
"git-lab": "dist/bin/git-lab.js",
|
|
16
30
|
"gitj": "dist/bin/gitj.js"
|
|
@@ -21,7 +35,9 @@
|
|
|
21
35
|
},
|
|
22
36
|
"devDependencies": {
|
|
23
37
|
"@types/commander": "^2.12.2",
|
|
24
|
-
"
|
|
38
|
+
"@types/debug": "^4.1.8",
|
|
39
|
+
"bun-types": "latest",
|
|
40
|
+
"typescript": "latest"
|
|
25
41
|
},
|
|
26
42
|
"directories": {
|
|
27
43
|
"lib": "lib"
|
|
@@ -32,6 +48,7 @@
|
|
|
32
48
|
"dependencies": {
|
|
33
49
|
"commander": "^11.0.0",
|
|
34
50
|
"cuid": "^3.0.0",
|
|
51
|
+
"debug": "^4.3.4",
|
|
35
52
|
"glob": "^10.3.4"
|
|
36
53
|
},
|
|
37
54
|
"main": "dist/start.js dist/bump.js",
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { readdirSync } from 'fs';
|
|
2
|
+
import { describe, expect, test } from 'bun:test';
|
|
3
|
+
import { doCommand } from '..';
|
|
4
|
+
|
|
5
|
+
import { getPackageJson, getPackageVersion } from '../lib/package'
|
|
6
|
+
const version = await getPackageVersion()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
describe('bin scripts', () => {
|
|
10
|
+
const binDir = './bin';
|
|
11
|
+
|
|
12
|
+
const scripts = readdirSync(binDir).filter((file) => {
|
|
13
|
+
return file.endsWith('.ts');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe('--help', () => {
|
|
17
|
+
scripts.forEach((script) => {
|
|
18
|
+
const stem = script.split('/').pop()?.split('-').pop()?.split('.').shift();
|
|
19
|
+
test(`"${script} --help" should contain 'Usage: ${stem}'"`, async () => {
|
|
20
|
+
const output = await doCommand(['bun', 'run', `${binDir}/${script}`, '--help']);
|
|
21
|
+
expect(output).toContain(`Usage: ${stem}`);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('--version', () => {
|
|
27
|
+
scripts.forEach((script) => {
|
|
28
|
+
test(`"${script} --version" should contain '${version}'"`, async () => {
|
|
29
|
+
const output = await doCommand(['bun', 'run', `${binDir}/${script}`, '--version']);
|
|
30
|
+
expect(output).toContain(version);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('all bin/* exist in package.json', async () => {
|
|
36
|
+
const packageJson = await getPackageJson();
|
|
37
|
+
const bin = packageJson.bin;
|
|
38
|
+
scripts.forEach((script) => {
|
|
39
|
+
test(`"${script}" should be in package.json`, () => {
|
|
40
|
+
expect(script).toEndWith('.ts')
|
|
41
|
+
script = script.slice(0, -3)
|
|
42
|
+
expect(bin[script]).toBeDefined();
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
})
|
|
46
|
+
});
|
package/tests/git.test.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { test, expect } from "bun:test"
|
|
2
|
-
import { findProject
|
|
2
|
+
import { findProject } from "../lib/gitlab/project"
|
|
3
|
+
import { getRemote } from "../lib/git"
|
|
3
4
|
|
|
4
5
|
test("testing works", async (): Promise<void> => {
|
|
5
6
|
expect(true).toBe(true)
|
|
@@ -13,13 +14,13 @@ test("getRemote", async (): Promise<void> => {
|
|
|
13
14
|
test("findProject linear-generator", async (): Promise<void> => {
|
|
14
15
|
const ssh_url = "git@gitlab.com:etagen-internal/linear-generator.git"
|
|
15
16
|
const project = await findProject(ssh_url)
|
|
16
|
-
expect(project
|
|
17
|
-
expect(project
|
|
18
|
-
})
|
|
17
|
+
expect(project?.ssh_url_to_repo).toBe(ssh_url)
|
|
18
|
+
expect(project?.id).toBe(4053065)
|
|
19
|
+
}, 15000)
|
|
19
20
|
|
|
20
21
|
test("findProject eta-lib/base", async (): Promise<void> => {
|
|
21
22
|
const ssh_url = "git@gitlab.com:etagen-internal/eta-lib/base.git"
|
|
22
23
|
const project = await findProject(ssh_url)
|
|
23
|
-
expect(project
|
|
24
|
-
expect(project
|
|
25
|
-
})
|
|
24
|
+
expect(project?.ssh_url_to_repo).toBe(ssh_url)
|
|
25
|
+
expect(project?.id).toBe(42470523)
|
|
26
|
+
}, 15000)
|
package/tests/gitj.test.ts
CHANGED
|
@@ -10,7 +10,7 @@ test("gitj works", async (): Promise<void> => {
|
|
|
10
10
|
const { out, code }: SpawnResult = await spawn(["bun", "run", "bin/gitj.ts"])
|
|
11
11
|
expect(out).toMatch("Usage:")
|
|
12
12
|
expect(out).toMatch("Bump the version number in the current branch")
|
|
13
|
-
expect(out).toMatch("
|
|
13
|
+
expect(out).toMatch("Commands for working with Jira")
|
|
14
14
|
expect(code).toBe(0)
|
|
15
15
|
})
|
|
16
16
|
|
package/lib/gitlab.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { getConfig, getRemote } from "./git"
|
|
2
|
-
import type { JSONValue } from "./json"
|
|
3
|
-
import path from 'node:path'
|
|
4
|
-
|
|
5
|
-
export interface GitlabConfig {
|
|
6
|
-
host: string
|
|
7
|
-
user: string
|
|
8
|
-
token: string
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export async function getGitlabConfig(): Promise<GitlabConfig> {
|
|
12
|
-
const host = await getConfig("gitlab.host")
|
|
13
|
-
if (!host) throw new Error("gitlab.host not in git config")
|
|
14
|
-
const user = await getConfig("user.email")
|
|
15
|
-
if (!user) throw new Error("user.email not in git config")
|
|
16
|
-
const token = await getConfig("gitlab.token")
|
|
17
|
-
if (!token) throw new Error("gitlab.token not in git config")
|
|
18
|
-
return { host, user, token }
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export async function get(endpoint: string): Promise<JSONValue> {
|
|
22
|
-
const method = "GET"
|
|
23
|
-
const { host, token } = await getGitlabConfig()
|
|
24
|
-
const base = `https://${host}/api/v4`
|
|
25
|
-
const uri = `${base}/${endpoint}`
|
|
26
|
-
const headers = new Headers()
|
|
27
|
-
headers.append("Accept", "application/json")
|
|
28
|
-
headers.append('Private-Token', token)
|
|
29
|
-
const options = {
|
|
30
|
-
method,
|
|
31
|
-
headers,
|
|
32
|
-
}
|
|
33
|
-
const request = new Request(uri, options)
|
|
34
|
-
const response = await fetch(request)
|
|
35
|
-
return await response.json()
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export type User = JSONValue & {
|
|
39
|
-
id: number
|
|
40
|
-
name: string
|
|
41
|
-
username: string
|
|
42
|
-
email: string
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export async function whoami(): Promise<User> {
|
|
46
|
-
return await get("/user") as User
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export type Project = JSONValue & {
|
|
50
|
-
id: number
|
|
51
|
-
name: string
|
|
52
|
-
path: string
|
|
53
|
-
path_with_namespace: string
|
|
54
|
-
visibility: string
|
|
55
|
-
ssh_url_to_repo: string
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export async function getProjects(paths: string[]): Promise<Array<Project>> {
|
|
59
|
-
let search: string = ""
|
|
60
|
-
if (paths.length > 0) {
|
|
61
|
-
search = '&search=' + paths.join(",")
|
|
62
|
-
}
|
|
63
|
-
return await get(`/projects?visibility=private&membership=true&simple=true${search}`) as Array<Project>
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// git@gitlab.com:etagen-internal/linear-generator-config.git
|
|
67
|
-
export async function findProject(ssh_url: string): Promise<Project> {
|
|
68
|
-
const parts = ssh_url.split(':')
|
|
69
|
-
if (parts.length != 2) {
|
|
70
|
-
throw new Error(`${ssh_url} is invalid, could not be split into two parts at :`)
|
|
71
|
-
}
|
|
72
|
-
const name = path.basename(parts[1], '.git')
|
|
73
|
-
|
|
74
|
-
const projects = await getProjects([name])
|
|
75
|
-
const project = projects.find((p: Project): boolean => {
|
|
76
|
-
return p.ssh_url_to_repo === ssh_url
|
|
77
|
-
})
|
|
78
|
-
if (!project) {
|
|
79
|
-
throw new Error(`No project with ssh_url_to_repo ${ssh_url} found`)
|
|
80
|
-
}
|
|
81
|
-
return project
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export async function getMergeRequest(id: string): Promise<JSONValue> {
|
|
85
|
-
return await get(`/merge_requests/${id}`)
|
|
86
|
-
}
|