@ticatec/omniflow-nexus 1.0.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/LICENSE +21 -0
- package/README.md +94 -0
- package/README_CN.md +94 -0
- package/dist/client/NexusClient.d.ts +42 -0
- package/dist/client/NexusClient.d.ts.map +1 -0
- package/dist/client/NexusClient.js +106 -0
- package/dist/client/NexusClient.js.map +1 -0
- package/dist/docker/NexusDockerPublisher.d.ts +18 -0
- package/dist/docker/NexusDockerPublisher.d.ts.map +1 -0
- package/dist/docker/NexusDockerPublisher.js +56 -0
- package/dist/docker/NexusDockerPublisher.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/maven/NexusMavenPublisher.d.ts +25 -0
- package/dist/maven/NexusMavenPublisher.d.ts.map +1 -0
- package/dist/maven/NexusMavenPublisher.js +194 -0
- package/dist/maven/NexusMavenPublisher.js.map +1 -0
- package/dist/npm/NexusNpmPublisher.d.ts +17 -0
- package/dist/npm/NexusNpmPublisher.d.ts.map +1 -0
- package/dist/npm/NexusNpmPublisher.js +67 -0
- package/dist/npm/NexusNpmPublisher.js.map +1 -0
- package/dist/raw/NexusRawPublisher.d.ts +28 -0
- package/dist/raw/NexusRawPublisher.d.ts.map +1 -0
- package/dist/raw/NexusRawPublisher.js +174 -0
- package/dist/raw/NexusRawPublisher.js.map +1 -0
- package/package.json +48 -0
- package/src/client/NexusClient.ts +143 -0
- package/src/docker/NexusDockerPublisher.ts +66 -0
- package/src/index.ts +54 -0
- package/src/maven/NexusMavenPublisher.ts +247 -0
- package/src/npm/NexusNpmPublisher.ts +81 -0
- package/src/raw/NexusRawPublisher.ts +217 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { promises as fs } from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import crypto from 'crypto'
|
|
4
|
+
import { BasePublisher } from '@ticatec/omniflow-artifact'
|
|
5
|
+
import type { ArtifactPayload, DownloadOptions, DownloadResult, PublishOptions, RegistryConfig } from '@ticatec/omniflow-artifact'
|
|
6
|
+
import { NexusClient } from '../client/NexusClient.js'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Sonatype Nexus 3 Raw (Generic) Package Repository Publisher.
|
|
10
|
+
*
|
|
11
|
+
* Publishes arbitrary binary artifacts, tarballs (.tar.gz), or zip files
|
|
12
|
+
* to Nexus "raw (hosted)" repositories.
|
|
13
|
+
* Endpoint format: PUT {server}/repository/{repoName}/{path}/{filename}
|
|
14
|
+
*/
|
|
15
|
+
export class NexusRawPublisher extends BasePublisher {
|
|
16
|
+
readonly id = 'nexus-raw'
|
|
17
|
+
readonly name = 'Nexus 3 Raw Repository'
|
|
18
|
+
readonly platform = 'nexus'
|
|
19
|
+
readonly artifactType = 'generic'
|
|
20
|
+
override readonly priority = 20
|
|
21
|
+
|
|
22
|
+
override detect(registry: RegistryConfig): boolean {
|
|
23
|
+
if (registry.type !== 'generic' && registry.type !== 'raw' && registry.type !== 'archive') {
|
|
24
|
+
return false
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const regName = (registry.name || '').toLowerCase()
|
|
28
|
+
if (regName === 'nexus' || regName.startsWith('nexus-') || regName === 'nexus-raw') {
|
|
29
|
+
return true
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const server = (registry.server || '').toLowerCase()
|
|
33
|
+
try {
|
|
34
|
+
const parsed = new URL(server.includes('://') ? server : `https://${server}`)
|
|
35
|
+
const host = parsed.hostname.toLowerCase()
|
|
36
|
+
|
|
37
|
+
// Exclude other known platforms
|
|
38
|
+
if (
|
|
39
|
+
host === 'aliyun.com' ||
|
|
40
|
+
host.endsWith('.aliyun.com') ||
|
|
41
|
+
host.endsWith('.aliyuncs.com') ||
|
|
42
|
+
host === 'gitlab.com' ||
|
|
43
|
+
host.endsWith('.gitlab.com') ||
|
|
44
|
+
host === 'github.com' ||
|
|
45
|
+
host.endsWith('.github.com')
|
|
46
|
+
) {
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return host.includes('nexus') || parsed.pathname.includes('/repository/raw')
|
|
51
|
+
} catch {
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
override buildTargetRef(artifact: ArtifactPayload, registry: RegistryConfig): string {
|
|
57
|
+
const repoName =
|
|
58
|
+
registry.namespace?.trim() ||
|
|
59
|
+
(registry.options?.repository as string | undefined)?.trim() ||
|
|
60
|
+
((registry as any).metadata?.repository as string | undefined)?.trim() ||
|
|
61
|
+
'raw-hosted'
|
|
62
|
+
const version = artifact.tags && artifact.tags.length > 0 ? artifact.tags[0] : 'latest'
|
|
63
|
+
const filename = artifact.source ? path.basename(artifact.source) : artifact.name
|
|
64
|
+
return `${repoName}/${artifact.name}/${version}/${filename}`
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async login(registry: RegistryConfig, _options?: PublishOptions): Promise<void> {
|
|
68
|
+
const creds = this.resolveCredentials(registry)
|
|
69
|
+
if (!creds.token && (!creds.username || !creds.password)) {
|
|
70
|
+
this.log(` ℹ No auth credentials configured for Nexus Raw repository ${registry.server}`)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
override async exists(
|
|
75
|
+
artifact: ArtifactPayload,
|
|
76
|
+
registry: RegistryConfig,
|
|
77
|
+
options?: PublishOptions
|
|
78
|
+
): Promise<boolean> {
|
|
79
|
+
const creds = this.resolveCredentials(registry)
|
|
80
|
+
const client = new NexusClient({
|
|
81
|
+
baseUrl: registry.server,
|
|
82
|
+
username: creds.username,
|
|
83
|
+
password: creds.password,
|
|
84
|
+
token: creds.token,
|
|
85
|
+
timeoutMs: options?.timeoutMs
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
const repoName = registry.namespace || 'raw-hosted'
|
|
89
|
+
const version = artifact.tags && artifact.tags.length > 0 ? artifact.tags[0] : 'latest'
|
|
90
|
+
const packageName = artifact.name
|
|
91
|
+
|
|
92
|
+
const filename = artifact.source ? path.basename(artifact.source) : artifact.name
|
|
93
|
+
const subpath = `${packageName}/${version}`
|
|
94
|
+
const targetUrl = client.getRawArtifactUrl(repoName, subpath, filename)
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
const res = await client.request(targetUrl, { method: 'HEAD' })
|
|
98
|
+
if (res.ok || res.status === 200 || res.status === 204) {
|
|
99
|
+
return true
|
|
100
|
+
}
|
|
101
|
+
if (res.status === 404) {
|
|
102
|
+
return false
|
|
103
|
+
}
|
|
104
|
+
const err = await client.extractErrorMessage(res)
|
|
105
|
+
throw new Error(`Failed to check Raw package existence (${res.status}): ${err}`)
|
|
106
|
+
} catch (err: any) {
|
|
107
|
+
if (err?.message?.includes('Failed to check Raw package existence')) {
|
|
108
|
+
throw err
|
|
109
|
+
}
|
|
110
|
+
throw new Error(`Failed to check Raw package existence: ${client.maskSecrets(err?.message || String(err))}`)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
override async download(
|
|
115
|
+
artifact: ArtifactPayload,
|
|
116
|
+
registry: RegistryConfig,
|
|
117
|
+
dest: string,
|
|
118
|
+
options?: DownloadOptions
|
|
119
|
+
): Promise<DownloadResult> {
|
|
120
|
+
const creds = this.resolveCredentials(registry)
|
|
121
|
+
const client = new NexusClient({
|
|
122
|
+
baseUrl: registry.server,
|
|
123
|
+
username: creds.username,
|
|
124
|
+
password: creds.password,
|
|
125
|
+
token: creds.token,
|
|
126
|
+
timeoutMs: options?.timeoutMs
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
const repoName = registry.namespace || 'raw-hosted'
|
|
130
|
+
const version = artifact.tags?.[0] || 'latest'
|
|
131
|
+
const filename = artifact.source ? path.basename(artifact.source) : artifact.name
|
|
132
|
+
const targetUrl = client.getRawArtifactUrl(repoName, `${artifact.name}/${version}`, filename)
|
|
133
|
+
const masked = client.maskSecrets(targetUrl)
|
|
134
|
+
|
|
135
|
+
this.log(` 📥 Downloading ${artifact.name} ${version} from Nexus: ${masked}`)
|
|
136
|
+
const res = await client.request(targetUrl, { method: 'GET' })
|
|
137
|
+
if (!res.ok) {
|
|
138
|
+
throw await this.downloadError(res, `${artifact.name} ${version} (${filename})`, masked)
|
|
139
|
+
}
|
|
140
|
+
return this.saveResponse(res, dest, masked)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
protected async doPublish(
|
|
144
|
+
artifact: ArtifactPayload,
|
|
145
|
+
registry: RegistryConfig,
|
|
146
|
+
options?: PublishOptions
|
|
147
|
+
): Promise<{
|
|
148
|
+
ref: string
|
|
149
|
+
digest?: string
|
|
150
|
+
metadata?: Record<string, unknown>
|
|
151
|
+
}> {
|
|
152
|
+
const creds = this.resolveCredentials(registry)
|
|
153
|
+
const client = new NexusClient({
|
|
154
|
+
baseUrl: registry.server,
|
|
155
|
+
username: creds.username,
|
|
156
|
+
password: creds.password,
|
|
157
|
+
token: creds.token,
|
|
158
|
+
timeoutMs: options?.timeoutMs
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
const repoName = registry.namespace || 'raw-hosted'
|
|
162
|
+
const version = artifact.tags && artifact.tags.length > 0 ? artifact.tags[0] : 'latest'
|
|
163
|
+
const packageName = artifact.name
|
|
164
|
+
|
|
165
|
+
let filePath = artifact.source
|
|
166
|
+
const stat = await fs.stat(filePath).catch(() => null)
|
|
167
|
+
if (!stat || !stat.isFile()) {
|
|
168
|
+
throw new Error(`Nexus Raw artifact source must be an existing file: ${filePath}`)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const filename = path.basename(filePath)
|
|
172
|
+
const fileBuffer = await fs.readFile(filePath)
|
|
173
|
+
const digest = `sha256:${crypto.createHash('sha256').update(fileBuffer).digest('hex')}`
|
|
174
|
+
|
|
175
|
+
const subpath = `${packageName}/${version}`
|
|
176
|
+
const targetUrl = client.getRawArtifactUrl(repoName, subpath, filename)
|
|
177
|
+
this.log(` 📤 Uploading Raw package file to Nexus: ${client.maskSecrets(targetUrl)}`)
|
|
178
|
+
|
|
179
|
+
if (options?.dryRun) {
|
|
180
|
+
this.log(` [dryRun] Would PUT ${filename} (${fileBuffer.length} bytes) to ${client.maskSecrets(targetUrl)}`)
|
|
181
|
+
return {
|
|
182
|
+
ref: `${repoName}/${subpath}/${filename}`,
|
|
183
|
+
digest,
|
|
184
|
+
metadata: { repoName, subpath, filename, targetUrl, size: fileBuffer.length }
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const res = await client.request(targetUrl, {
|
|
189
|
+
method: 'PUT',
|
|
190
|
+
headers: {
|
|
191
|
+
'Content-Type': 'application/octet-stream'
|
|
192
|
+
},
|
|
193
|
+
body: fileBuffer as unknown as BodyInit
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
if (!res.ok && res.status !== 201 && res.status !== 200) {
|
|
197
|
+
const errText = await client.extractErrorMessage(res)
|
|
198
|
+
throw new Error(`Failed to upload Raw package to Nexus (${res.status}): ${errText}`)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
ref: `${repoName}/${subpath}/${filename}`,
|
|
203
|
+
digest,
|
|
204
|
+
metadata: {
|
|
205
|
+
repoName,
|
|
206
|
+
packageName,
|
|
207
|
+
version,
|
|
208
|
+
filename,
|
|
209
|
+
targetUrl,
|
|
210
|
+
size: fileBuffer.length
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Convenient alias
|
|
217
|
+
export { NexusRawPublisher as NexusGenericPublisher }
|