@foundation0/git 1.3.0 → 1.3.1

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/repository.ts CHANGED
@@ -1,151 +1,151 @@
1
- import fsSync from 'node:fs'
2
- import path from 'node:path'
3
-
4
- export interface GitRepositoryIdentity {
5
- owner: string
6
- repo: string
7
- }
8
-
9
- function parseGitRemoteUrl(remoteUrl: string): GitRepositoryIdentity | null {
10
- try {
11
- if (remoteUrl.startsWith('http://') || remoteUrl.startsWith('https://') || remoteUrl.startsWith('ssh://')) {
12
- const parsed = new URL(remoteUrl)
13
- const segments = parsed.pathname.split('/').filter(Boolean)
14
- if (segments.length >= 2) {
15
- const owner = segments[segments.length - 2]
16
- const rawRepo = segments[segments.length - 1]
17
- const repo = rawRepo.replace(/\.git$/, '')
18
- return { owner, repo }
19
- }
20
- }
21
-
22
- if (remoteUrl.startsWith('git@')) {
23
- const parts = remoteUrl.split(':', 2)
24
- if (parts.length === 2) {
25
- const pathParts = parts[1].split('/').filter(Boolean)
26
- if (pathParts.length >= 2) {
27
- const owner = pathParts[pathParts.length - 2]
28
- const repo = pathParts[pathParts.length - 1].replace(/\.git$/, '')
29
- return { owner, repo }
30
- }
31
- }
32
- }
33
- } catch {
34
- return null
35
- }
36
-
37
- return null
38
- }
39
-
40
- function parseGitConfigForIdentity(configPath: string): GitRepositoryIdentity | null {
41
- let cfg = ''
42
- try {
43
- cfg = fsSync.readFileSync(configPath, 'utf8')
44
- } catch {
45
- return null
46
- }
47
-
48
- for (const line of cfg.split(/\r?\n/)) {
49
- const trimmed = line.trim()
50
- if (!trimmed.startsWith('url')) {
51
- continue
52
- }
53
-
54
- const match = trimmed.match(/^url\s*=\s*(.+?)\s*$/)
55
- if (!match) {
56
- continue
57
- }
58
-
59
- const parsed = parseGitRemoteUrl(match[1].trim())
60
- if (parsed) {
61
- return parsed
62
- }
63
- }
64
-
65
- return null
66
- }
67
-
68
- function parseGitDirFromDotGit(dotGitPath: string): string | null {
69
- let contents = ''
70
- try {
71
- contents = fsSync.readFileSync(dotGitPath, 'utf8')
72
- } catch {
73
- return null
74
- }
75
-
76
- const trimmed = contents.trim()
77
- const gitdirMatch = trimmed.match(/^gitdir:\s*(.+)$/i)
78
- if (!gitdirMatch) {
79
- return null
80
- }
81
-
82
- const gitdir = gitdirMatch[1].trim()
83
- if (!gitdir) {
84
- return null
85
- }
86
-
87
- if (path.isAbsolute(gitdir)) {
88
- return gitdir
89
- }
90
-
91
- let cursor = path.dirname(dotGitPath)
92
- while (true) {
93
- const candidate = path.resolve(cursor, gitdir)
94
- try {
95
- const stat = fsSync.statSync(candidate)
96
- if (stat.isDirectory()) {
97
- return candidate
98
- }
99
- } catch {}
100
-
101
- const next = path.dirname(cursor)
102
- if (next === cursor) {
103
- break
104
- }
105
- cursor = next
106
- }
107
-
108
- return path.resolve(path.dirname(dotGitPath), gitdir)
109
- }
110
-
111
- function resolveGitConfigPath(dotGitPath: string): string | null {
112
- let stat: fsSync.Stats
113
- try {
114
- stat = fsSync.statSync(dotGitPath)
115
- } catch {
116
- return null
117
- }
118
-
119
- if (stat.isDirectory()) {
120
- return path.join(dotGitPath, 'config')
121
- }
122
-
123
- if (stat.isFile()) {
124
- const gitDir = parseGitDirFromDotGit(dotGitPath)
125
- if (!gitDir) return null
126
- return path.join(gitDir, 'config')
127
- }
128
-
129
- return null
130
- }
131
-
132
- export function resolveProjectRepoIdentity(projectRoot: string): GitRepositoryIdentity | null {
133
- const candidates = [
134
- path.join(projectRoot, 'code', '.git'),
135
- path.join(projectRoot, '.git'),
136
- ]
137
-
138
- for (const candidate of candidates) {
139
- const configPath = resolveGitConfigPath(candidate)
140
- if (!configPath) {
141
- continue
142
- }
143
-
144
- const parsed = parseGitConfigForIdentity(configPath)
145
- if (parsed) {
146
- return parsed
147
- }
148
- }
149
-
150
- return null
151
- }
1
+ import fsSync from 'node:fs'
2
+ import path from 'node:path'
3
+
4
+ export interface GitRepositoryIdentity {
5
+ owner: string
6
+ repo: string
7
+ }
8
+
9
+ function parseGitRemoteUrl(remoteUrl: string): GitRepositoryIdentity | null {
10
+ try {
11
+ if (remoteUrl.startsWith('http://') || remoteUrl.startsWith('https://') || remoteUrl.startsWith('ssh://')) {
12
+ const parsed = new URL(remoteUrl)
13
+ const segments = parsed.pathname.split('/').filter(Boolean)
14
+ if (segments.length >= 2) {
15
+ const owner = segments[segments.length - 2]
16
+ const rawRepo = segments[segments.length - 1]
17
+ const repo = rawRepo.replace(/\.git$/, '')
18
+ return { owner, repo }
19
+ }
20
+ }
21
+
22
+ if (remoteUrl.startsWith('git@')) {
23
+ const parts = remoteUrl.split(':', 2)
24
+ if (parts.length === 2) {
25
+ const pathParts = parts[1].split('/').filter(Boolean)
26
+ if (pathParts.length >= 2) {
27
+ const owner = pathParts[pathParts.length - 2]
28
+ const repo = pathParts[pathParts.length - 1].replace(/\.git$/, '')
29
+ return { owner, repo }
30
+ }
31
+ }
32
+ }
33
+ } catch {
34
+ return null
35
+ }
36
+
37
+ return null
38
+ }
39
+
40
+ function parseGitConfigForIdentity(configPath: string): GitRepositoryIdentity | null {
41
+ let cfg = ''
42
+ try {
43
+ cfg = fsSync.readFileSync(configPath, 'utf8')
44
+ } catch {
45
+ return null
46
+ }
47
+
48
+ for (const line of cfg.split(/\r?\n/)) {
49
+ const trimmed = line.trim()
50
+ if (!trimmed.startsWith('url')) {
51
+ continue
52
+ }
53
+
54
+ const match = trimmed.match(/^url\s*=\s*(.+?)\s*$/)
55
+ if (!match) {
56
+ continue
57
+ }
58
+
59
+ const parsed = parseGitRemoteUrl(match[1].trim())
60
+ if (parsed) {
61
+ return parsed
62
+ }
63
+ }
64
+
65
+ return null
66
+ }
67
+
68
+ function parseGitDirFromDotGit(dotGitPath: string): string | null {
69
+ let contents = ''
70
+ try {
71
+ contents = fsSync.readFileSync(dotGitPath, 'utf8')
72
+ } catch {
73
+ return null
74
+ }
75
+
76
+ const trimmed = contents.trim()
77
+ const gitdirMatch = trimmed.match(/^gitdir:\s*(.+)$/i)
78
+ if (!gitdirMatch) {
79
+ return null
80
+ }
81
+
82
+ const gitdir = gitdirMatch[1].trim()
83
+ if (!gitdir) {
84
+ return null
85
+ }
86
+
87
+ if (path.isAbsolute(gitdir)) {
88
+ return gitdir
89
+ }
90
+
91
+ let cursor = path.dirname(dotGitPath)
92
+ while (true) {
93
+ const candidate = path.resolve(cursor, gitdir)
94
+ try {
95
+ const stat = fsSync.statSync(candidate)
96
+ if (stat.isDirectory()) {
97
+ return candidate
98
+ }
99
+ } catch {}
100
+
101
+ const next = path.dirname(cursor)
102
+ if (next === cursor) {
103
+ break
104
+ }
105
+ cursor = next
106
+ }
107
+
108
+ return path.resolve(path.dirname(dotGitPath), gitdir)
109
+ }
110
+
111
+ function resolveGitConfigPath(dotGitPath: string): string | null {
112
+ let stat: fsSync.Stats
113
+ try {
114
+ stat = fsSync.statSync(dotGitPath)
115
+ } catch {
116
+ return null
117
+ }
118
+
119
+ if (stat.isDirectory()) {
120
+ return path.join(dotGitPath, 'config')
121
+ }
122
+
123
+ if (stat.isFile()) {
124
+ const gitDir = parseGitDirFromDotGit(dotGitPath)
125
+ if (!gitDir) return null
126
+ return path.join(gitDir, 'config')
127
+ }
128
+
129
+ return null
130
+ }
131
+
132
+ export function resolveProjectRepoIdentity(projectRoot: string): GitRepositoryIdentity | null {
133
+ const candidates = [
134
+ path.join(projectRoot, 'code', '.git'),
135
+ path.join(projectRoot, '.git'),
136
+ ]
137
+
138
+ for (const candidate of candidates) {
139
+ const configPath = resolveGitConfigPath(candidate)
140
+ if (!configPath) {
141
+ continue
142
+ }
143
+
144
+ const parsed = parseGitConfigForIdentity(configPath)
145
+ if (parsed) {
146
+ return parsed
147
+ }
148
+ }
149
+
150
+ return null
151
+ }
package/src/spec-mock.ts CHANGED
@@ -1,45 +1,45 @@
1
- export type GitApiPath = string[]
2
- export type GitApiQuery = string[]
3
- export type GitApiHeaders = string[]
4
-
5
- export interface GitApiMockResponse {
6
- endpoint: string
7
- method: string
8
- path: GitApiPath
9
- query: GitApiQuery
10
- headers: GitApiHeaders
11
- requestParts: string[]
12
- apiVersion: string
13
- }
14
-
15
- export const GIT_API_VERSION = '2022-11-28'
16
-
17
- export interface MockInvocation {
18
- path: GitApiPath
19
- method?: string
20
- query?: GitApiQuery
21
- headers?: GitApiHeaders
22
- apiBase?: string
23
- apiVersion?: string
24
- }
25
-
26
- export function buildGitApiMockResponse(invocation: MockInvocation): GitApiMockResponse {
27
- const method = invocation.method ?? 'GET'
28
- const path = invocation.path
29
- const query = invocation.query ?? []
30
- const headers = invocation.headers ?? []
31
- const apiBase = invocation.apiBase ?? 'https://api.github.com'
32
- const apiVersion = invocation.apiVersion ?? GIT_API_VERSION
33
- const requestParts = [method, ...path, ...query, ...headers]
34
- const endpoint = path.length > 0 ? `${apiBase}/${path.join('/')}` : apiBase
35
-
36
- return {
37
- endpoint,
38
- method,
39
- path: [...path],
40
- query: [...query],
41
- headers: [...headers],
42
- requestParts,
43
- apiVersion,
44
- }
45
- }
1
+ export type GitApiPath = string[]
2
+ export type GitApiQuery = string[]
3
+ export type GitApiHeaders = string[]
4
+
5
+ export interface GitApiMockResponse {
6
+ endpoint: string
7
+ method: string
8
+ path: GitApiPath
9
+ query: GitApiQuery
10
+ headers: GitApiHeaders
11
+ requestParts: string[]
12
+ apiVersion: string
13
+ }
14
+
15
+ export const GIT_API_VERSION = '2022-11-28'
16
+
17
+ export interface MockInvocation {
18
+ path: GitApiPath
19
+ method?: string
20
+ query?: GitApiQuery
21
+ headers?: GitApiHeaders
22
+ apiBase?: string
23
+ apiVersion?: string
24
+ }
25
+
26
+ export function buildGitApiMockResponse(invocation: MockInvocation): GitApiMockResponse {
27
+ const method = invocation.method ?? 'GET'
28
+ const path = invocation.path
29
+ const query = invocation.query ?? []
30
+ const headers = invocation.headers ?? []
31
+ const apiBase = invocation.apiBase ?? 'https://api.github.com'
32
+ const apiVersion = invocation.apiVersion ?? GIT_API_VERSION
33
+ const requestParts = [method, ...path, ...query, ...headers]
34
+ const endpoint = path.length > 0 ? `${apiBase}/${path.join('/')}` : apiBase
35
+
36
+ return {
37
+ endpoint,
38
+ method,
39
+ path: [...path],
40
+ query: [...query],
41
+ headers: [...headers],
42
+ requestParts,
43
+ apiVersion,
44
+ }
45
+ }