@ticatec/omniflow-github 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.
Files changed (34) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -0
  3. package/README_CN.md +93 -0
  4. package/dist/artifacts/GitHubContainerPublisher.d.ts +16 -0
  5. package/dist/artifacts/GitHubContainerPublisher.d.ts.map +1 -0
  6. package/dist/artifacts/GitHubContainerPublisher.js +47 -0
  7. package/dist/artifacts/GitHubContainerPublisher.js.map +1 -0
  8. package/dist/artifacts/GitHubMavenPublisher.d.ts +25 -0
  9. package/dist/artifacts/GitHubMavenPublisher.d.ts.map +1 -0
  10. package/dist/artifacts/GitHubMavenPublisher.js +186 -0
  11. package/dist/artifacts/GitHubMavenPublisher.js.map +1 -0
  12. package/dist/artifacts/GitHubNpmPublisher.d.ts +21 -0
  13. package/dist/artifacts/GitHubNpmPublisher.d.ts.map +1 -0
  14. package/dist/artifacts/GitHubNpmPublisher.js +49 -0
  15. package/dist/artifacts/GitHubNpmPublisher.js.map +1 -0
  16. package/dist/artifacts/GitHubReleasePublisher.d.ts +31 -0
  17. package/dist/artifacts/GitHubReleasePublisher.d.ts.map +1 -0
  18. package/dist/artifacts/GitHubReleasePublisher.js +192 -0
  19. package/dist/artifacts/GitHubReleasePublisher.js.map +1 -0
  20. package/dist/client/GitHubClient.d.ts +76 -0
  21. package/dist/client/GitHubClient.d.ts.map +1 -0
  22. package/dist/client/GitHubClient.js +170 -0
  23. package/dist/client/GitHubClient.js.map +1 -0
  24. package/dist/index.d.ts +29 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +38 -0
  27. package/dist/index.js.map +1 -0
  28. package/package.json +48 -0
  29. package/src/artifacts/GitHubContainerPublisher.ts +54 -0
  30. package/src/artifacts/GitHubMavenPublisher.ts +238 -0
  31. package/src/artifacts/GitHubNpmPublisher.ts +66 -0
  32. package/src/artifacts/GitHubReleasePublisher.ts +238 -0
  33. package/src/client/GitHubClient.ts +242 -0
  34. package/src/index.ts +54 -0
@@ -0,0 +1,238 @@
1
+ import { promises as fs } from 'fs'
2
+ import path from 'path'
3
+ import crypto from 'crypto'
4
+ import { BasePublisher, resolveMavenArtifactFilenames, mavenPomPathFor, isMavenArtifactFile } from '@ticatec/omniflow-artifact'
5
+ import type { ArtifactPayload, PublishOptions, RegistryConfig } from '@ticatec/omniflow-artifact'
6
+ import { GitHubClient } from '../client/GitHubClient.js'
7
+
8
+ /**
9
+ * GitHub Maven Package Registry Publisher.
10
+ *
11
+ * Endpoint format: PUT https://maven.pkg.github.com/<owner>/<repo>/{groupId}/{artifactId}/{version}/{filename}
12
+ */
13
+ export class GitHubMavenPublisher extends BasePublisher {
14
+ readonly id = 'github-maven'
15
+ readonly name = 'GitHub Packages (Maven)'
16
+ readonly platform = 'github'
17
+ readonly artifactType = 'maven'
18
+ override readonly priority = 20
19
+
20
+ override detect(registry: RegistryConfig): boolean {
21
+ if (registry.type !== 'maven' && registry.type !== 'jar') {
22
+ return false
23
+ }
24
+
25
+ const regName = (registry.name || '').toLowerCase()
26
+ if (regName === 'github' || regName.startsWith('github-')) {
27
+ return true
28
+ }
29
+
30
+ const server = (registry.server || '').toLowerCase()
31
+ try {
32
+ const parsed = new URL(server.includes('://') ? server : `https://${server}`)
33
+ return parsed.hostname === 'maven.pkg.github.com' || GitHubClient.isGitHubHost(parsed.hostname)
34
+ } catch {
35
+ return false
36
+ }
37
+ }
38
+
39
+ override buildTargetRef(artifact: ArtifactPayload, _registry: RegistryConfig): string {
40
+ const version = artifact.tags && artifact.tags.length > 0 ? artifact.tags[0] : '1.0.0'
41
+ const artifactId = (artifact.metadata?.artifactId as string) || artifact.name
42
+ const groupId = (artifact.metadata?.groupId as string)?.trim()
43
+ return groupId ? `${groupId}:${artifactId}:${version}` : `${artifactId}:${version}`
44
+ }
45
+
46
+ protected resolveBaseRepoUrl(registry: RegistryConfig): string {
47
+ let repoPath = registry.namespace || ''
48
+ let server = registry.server || 'https://maven.pkg.github.com'
49
+ if (!/^https?:\/\//i.test(server)) {
50
+ server = `https://${server}`
51
+ }
52
+
53
+ if (server.includes('maven.pkg.github.com') && server.split('/').filter(Boolean).length >= 4) {
54
+ return server.replace(/\/+$/, '')
55
+ }
56
+
57
+ if (!repoPath) {
58
+ throw new Error(`GitHub Maven packages require repository namespace (<owner>/<repo>) to be configured.`)
59
+ }
60
+ return `https://maven.pkg.github.com/${repoPath.replace(/^\/+/, '').replace(/\/+$/, '')}`
61
+ }
62
+
63
+ override async exists(
64
+ artifact: ArtifactPayload,
65
+ registry: RegistryConfig,
66
+ options?: PublishOptions
67
+ ): Promise<boolean> {
68
+ const creds = this.resolveCredentials(registry)
69
+ const token = creds.token || creds.password
70
+
71
+ let baseRepoUrl: string
72
+ try {
73
+ baseRepoUrl = this.resolveBaseRepoUrl(registry)
74
+ } catch {
75
+ return false
76
+ }
77
+
78
+ const client = new GitHubClient({
79
+ token,
80
+ timeoutMs: options?.timeoutMs
81
+ })
82
+
83
+ let jarPath = artifact.source
84
+ if (jarPath) {
85
+ const stat = await fs.stat(jarPath).catch(() => null)
86
+ if (stat?.isDirectory()) {
87
+ const files = await fs.readdir(jarPath).catch(() => [] as string[])
88
+ const jarFile = files.find(
89
+ f => isMavenArtifactFile(f)
90
+ )
91
+ if (jarFile) {
92
+ jarPath = path.join(jarPath, jarFile)
93
+ }
94
+ }
95
+ }
96
+
97
+ const { groupId, artifactId, version, groupPath, jarFilename } = resolveMavenArtifactFilenames(
98
+ artifact,
99
+ jarPath,
100
+ undefined,
101
+ 'GitHub'
102
+ )
103
+
104
+ const probeUrl = `${baseRepoUrl}/${groupPath}/${artifactId}/${version}/${jarFilename}`
105
+
106
+ try {
107
+ const res = await client.request(probeUrl, { method: 'HEAD' })
108
+ if (res.ok || res.status === 200 || res.status === 204) {
109
+ return true
110
+ }
111
+ if (res.status === 404) {
112
+ return false
113
+ }
114
+ const errText = await client.extractErrorMessage(res)
115
+ throw new Error(`Failed to check artifact existence in GitHub Maven registry (${res.status}): ${errText}`)
116
+ } catch (err: any) {
117
+ if (err?.message?.includes('Failed to check artifact existence in GitHub')) {
118
+ throw err
119
+ }
120
+ throw new Error(
121
+ `Failed to check artifact existence in GitHub Maven registry: ${client.maskSecrets(err?.message || String(err))}`
122
+ )
123
+ }
124
+ }
125
+
126
+ async login(registry: RegistryConfig, _options?: PublishOptions): Promise<void> {
127
+ const creds = this.resolveCredentials(registry)
128
+ if (!creds.token && !creds.password) {
129
+ this.log(` ℹ No auth token configured for GitHub Maven repository ${registry.server}`)
130
+ }
131
+ }
132
+
133
+ protected async doPublish(
134
+ artifact: ArtifactPayload,
135
+ registry: RegistryConfig,
136
+ options?: PublishOptions
137
+ ): Promise<{
138
+ ref: string
139
+ digest?: string
140
+ metadata?: Record<string, unknown>
141
+ }> {
142
+ const creds = this.resolveCredentials(registry)
143
+ const token = creds.token || creds.password
144
+ const repoBaseUrl = this.resolveBaseRepoUrl(registry)
145
+
146
+ const client = new GitHubClient({
147
+ token,
148
+ timeoutMs: options?.timeoutMs
149
+ })
150
+
151
+ // Locate jar file
152
+ let jarPath = artifact.source
153
+ const stat = await fs.stat(jarPath).catch(() => null)
154
+ if (stat?.isDirectory()) {
155
+ const files = await fs.readdir(jarPath)
156
+ const jarFile = files.find(f => isMavenArtifactFile(f))
157
+ if (!jarFile) {
158
+ throw new Error(`No jar/war artifact found in directory: ${jarPath}`)
159
+ }
160
+ jarPath = path.join(jarPath, jarFile)
161
+ }
162
+
163
+ const { groupId, artifactId, version, groupPath, jarFilename, pomFilename } = resolveMavenArtifactFilenames(
164
+ artifact,
165
+ jarPath,
166
+ undefined,
167
+ 'GitHub'
168
+ )
169
+
170
+ const jarBuffer = await fs.readFile(jarPath)
171
+ const digest = `sha256:${crypto.createHash('sha256').update(jarBuffer).digest('hex')}`
172
+
173
+ const targetUrl = `${repoBaseUrl}/${groupPath}/${artifactId}/${version}/${jarFilename}`
174
+ this.log(` 📤 Uploading Maven JAR to GitHub: ${targetUrl}`)
175
+
176
+ if (options?.dryRun) {
177
+ this.log(` [dryRun] Would PUT ${jarFilename} (${jarBuffer.length} bytes) to ${targetUrl}`)
178
+ return {
179
+ ref: `${groupId}:${artifactId}:${version}`,
180
+ digest,
181
+ metadata: { targetUrl, size: jarBuffer.length }
182
+ }
183
+ }
184
+
185
+ // The POM goes up first, deliberately.
186
+ //
187
+ // The existence probe checks for the jar/war, so uploading that last is what
188
+ // makes the probe mean "this artifact is complete". With the artifact first, a
189
+ // run that uploaded it and then failed on the POM would leave a coordinate that
190
+ // probes as present: the retry skips it as already published, and the POM never
191
+ // arrives — a jar in the repository forever, with no dependency information.
192
+ // Upload pom if exists
193
+ const pomPath = mavenPomPathFor(jarPath)
194
+ const pomStat = await fs.stat(pomPath).catch(() => null)
195
+ if (pomStat?.isFile()) {
196
+ const pomBuffer = await fs.readFile(pomPath)
197
+ const targetPomFilename = pomFilename || `${artifactId}-${version}.pom`
198
+ const pomTargetUrl = `${repoBaseUrl}/${groupPath}/${artifactId}/${version}/${targetPomFilename}`
199
+ this.log(` 📤 Uploading Maven POM to GitHub: ${pomTargetUrl}`)
200
+
201
+ const pomRes = await client.request(pomTargetUrl, {
202
+ method: 'PUT',
203
+ headers: { 'Content-Type': 'application/xml' },
204
+ body: pomBuffer
205
+ })
206
+
207
+ if (!pomRes.ok && pomRes.status !== 201 && pomRes.status !== 200) {
208
+ const pomErr = await client.extractErrorMessage(pomRes)
209
+ throw new Error(`Failed to upload Maven POM to GitHub (${pomRes.status}): ${pomErr}`)
210
+ }
211
+ }
212
+
213
+ const res = await client.request(targetUrl, {
214
+ method: 'PUT',
215
+ headers: {
216
+ 'Content-Type': 'application/java-archive'
217
+ },
218
+ body: jarBuffer
219
+ })
220
+
221
+ if (!res.ok && res.status !== 201 && res.status !== 200) {
222
+ const errText = await client.extractErrorMessage(res)
223
+ throw new Error(`Failed to upload Maven JAR to GitHub (${res.status}): ${errText}`)
224
+ }
225
+
226
+ return {
227
+ ref: `${groupId}:${artifactId}:${version}`,
228
+ digest,
229
+ metadata: {
230
+ groupId,
231
+ artifactId,
232
+ version,
233
+ targetUrl,
234
+ size: jarBuffer.length
235
+ }
236
+ }
237
+ }
238
+ }
@@ -0,0 +1,66 @@
1
+ import { BaseNpmPublisher } from '@ticatec/omniflow-artifact'
2
+ import type { ArtifactPayload, PublishOptions, RegistryConfig } from '@ticatec/omniflow-artifact'
3
+ import { GitHubClient } from '../client/GitHubClient.js'
4
+
5
+ /**
6
+ * GitHub NPM Package Registry Publisher.
7
+ *
8
+ * Endpoint format: https://npm.pkg.github.com/
9
+ * GitHub requires npm packages to be scoped, e.g. @owner/package-name.
10
+ */
11
+ export class GitHubNpmPublisher extends BaseNpmPublisher {
12
+ readonly id = 'github-npm'
13
+ readonly name = 'GitHub Packages (NPM)'
14
+ readonly platform = 'github'
15
+ override readonly priority = 20
16
+
17
+ override detect(registry: RegistryConfig): boolean {
18
+ if (registry.type !== 'npm') {
19
+ return false
20
+ }
21
+
22
+ const regName = (registry.name || '').toLowerCase()
23
+ if (regName === 'github' || regName.startsWith('github-')) {
24
+ return true
25
+ }
26
+
27
+ const server = (registry.server || '').toLowerCase()
28
+ try {
29
+ const parsed = new URL(server.includes('://') ? server : `https://${server}`)
30
+ const host = parsed.hostname.toLowerCase()
31
+ return host === 'npm.pkg.github.com' || GitHubClient.isGitHubHost(host)
32
+ } catch {
33
+ return false
34
+ }
35
+ }
36
+
37
+ override normalizeRegistryUrl(server: string, _namespace?: string): string {
38
+ let url = (server || '').trim()
39
+ if (!url || url === 'github') {
40
+ return 'https://npm.pkg.github.com/'
41
+ }
42
+
43
+ if (!/^https?:\/\//i.test(url)) {
44
+ url = `https://${url}`
45
+ }
46
+
47
+ return url.endsWith('/') ? url : `${url}/`
48
+ }
49
+
50
+ protected override async doPublish(
51
+ artifact: ArtifactPayload,
52
+ registry: RegistryConfig,
53
+ options?: PublishOptions
54
+ ): Promise<{
55
+ ref: string
56
+ metadata?: Record<string, unknown>
57
+ }> {
58
+ if (!artifact.name.startsWith('@')) {
59
+ this.log(
60
+ ` âš  Notice: Package "${artifact.name}" is not scoped. GitHub Packages requires scoped package names (e.g. @owner/repo).`
61
+ )
62
+ }
63
+
64
+ return super.doPublish(artifact, registry, options)
65
+ }
66
+ }
@@ -0,0 +1,238 @@
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 { GitHubClient } from '../client/GitHubClient.js'
7
+
8
+ /**
9
+ * GitHub Releases (Generic Artifact / Asset) Publisher.
10
+ *
11
+ * Publishes arbitrary binary artifacts, distribution tarballs (.tar.gz),
12
+ * or zip files to GitHub Releases under the specified version tag.
13
+ */
14
+ export class GitHubReleasePublisher extends BasePublisher {
15
+ readonly id = 'github-release'
16
+ readonly name = 'GitHub Releases'
17
+ readonly platform = 'github'
18
+ readonly artifactType = 'generic'
19
+ override readonly priority = 20
20
+
21
+ override detect(registry: RegistryConfig): boolean {
22
+ if (registry.type !== 'generic' && registry.type !== 'archive' && registry.type !== 'release') {
23
+ return false
24
+ }
25
+
26
+ const regName = (registry.name || '').toLowerCase()
27
+ if (regName === 'github' || regName === 'github-release' || regName.startsWith('github-')) {
28
+ return true
29
+ }
30
+
31
+ const server = (registry.server || '').toLowerCase()
32
+ try {
33
+ const parsed = new URL(server.includes('://') ? server : `https://${server}`)
34
+ return GitHubClient.isGitHubHost(parsed.hostname)
35
+ } catch {
36
+ return false
37
+ }
38
+ }
39
+
40
+ override buildTargetRef(artifact: ArtifactPayload, registry: RegistryConfig): string {
41
+ const version = artifact.tags && artifact.tags.length > 0 ? artifact.tags[0] : 'v1.0.0'
42
+ const tagName = version.startsWith('v') || /^\d/.test(version) ? version : `v${version}`
43
+ const filename = artifact.source ? path.basename(artifact.source) : artifact.name
44
+
45
+ try {
46
+ const { owner, repo } = this.resolveOwnerRepo(registry)
47
+ return `${owner}/${repo}@${tagName}#${filename}`
48
+ } catch {
49
+ return `${artifact.name}@${tagName}#${filename}`
50
+ }
51
+ }
52
+
53
+ protected resolveOwnerRepo(registry: RegistryConfig): { owner: string; repo: string } {
54
+ let namespace = registry.namespace || ''
55
+ let owner = ''
56
+ let repo = ''
57
+
58
+ if (namespace.includes('/')) {
59
+ const parts = namespace.split('/')
60
+ owner = parts[0]
61
+ repo = parts[1]
62
+ } else if (registry.server && registry.server.includes('/')) {
63
+ try {
64
+ const parsed = GitHubClient.parseRepo(registry.server)
65
+ owner = parsed.owner
66
+ repo = parsed.repo
67
+ } catch {
68
+ // fall through
69
+ }
70
+ }
71
+
72
+ if (!owner || !repo) {
73
+ throw new Error(
74
+ `GitHub Releases publisher requires repository namespace in "owner/repo" format (got "${registry.namespace || registry.server}").`
75
+ )
76
+ }
77
+
78
+ return { owner, repo }
79
+ }
80
+
81
+ override async exists(
82
+ artifact: ArtifactPayload,
83
+ registry: RegistryConfig,
84
+ options?: PublishOptions
85
+ ): Promise<boolean> {
86
+ const creds = this.resolveCredentials(registry)
87
+ const token = creds.token || creds.password
88
+
89
+ let owner = ''
90
+ let repo = ''
91
+ try {
92
+ const resolved = this.resolveOwnerRepo(registry)
93
+ owner = resolved.owner
94
+ repo = resolved.repo
95
+ } catch {
96
+ return false
97
+ }
98
+
99
+ const client = new GitHubClient({
100
+ baseUrl: registry.server && registry.server.startsWith('http') ? registry.server : undefined,
101
+ token,
102
+ timeoutMs: options?.timeoutMs
103
+ })
104
+
105
+ const version = artifact.tags && artifact.tags.length > 0 ? artifact.tags[0] : 'v1.0.0'
106
+ const tagName = version.startsWith('v') || /^\d/.test(version) ? version : `v${version}`
107
+ const filename = artifact.source ? path.basename(artifact.source) : artifact.name
108
+
109
+ try {
110
+ const release = await client.getReleaseByTag(owner, repo, tagName)
111
+ if (!release) {
112
+ return false
113
+ }
114
+ return Boolean(release.assets?.some(a => a.name === filename))
115
+ } catch (err: any) {
116
+ throw new Error(
117
+ `Failed to check artifact existence in GitHub Releases: ${client.maskSecrets(err?.message || String(err))}`
118
+ )
119
+ }
120
+ }
121
+
122
+ override async download(
123
+ artifact: ArtifactPayload,
124
+ registry: RegistryConfig,
125
+ dest: string,
126
+ options?: DownloadOptions
127
+ ): Promise<DownloadResult> {
128
+ const creds = this.resolveCredentials(registry)
129
+ const { owner, repo } = this.resolveOwnerRepo(registry)
130
+ const client = new GitHubClient({
131
+ baseUrl: registry.server && registry.server.startsWith('http') ? registry.server : undefined,
132
+ token: creds.token || creds.password,
133
+ timeoutMs: options?.timeoutMs
134
+ })
135
+
136
+ const version = artifact.tags?.[0] || 'v1.0.0'
137
+ const tagName = version.startsWith('v') || /^\d/.test(version) ? version : `v${version}`
138
+ const filename = artifact.source ? path.basename(artifact.source) : artifact.name
139
+ const what = `${artifact.name} ${version} (${filename})`
140
+
141
+ const release = await client.getReleaseByTag(owner, repo, tagName)
142
+ const asset = release?.assets?.find(a => a.name === filename)
143
+ if (!asset) {
144
+ throw new Error(`${what} not found in GitHub release ${owner}/${repo}@${tagName}`)
145
+ }
146
+
147
+ // The asset API answers with a redirect to storage when asked for octet-stream
148
+ const assetUrl = client.getRepoApiUrl(owner, repo, `/releases/assets/${asset.id}`)
149
+ this.log(` 📥 Downloading ${what} from GitHub release ${owner}/${repo}@${tagName}`)
150
+ const res = await client.request(assetUrl, { headers: { Accept: 'application/octet-stream' } })
151
+ if (!res.ok) {
152
+ throw await this.downloadError(res, what, assetUrl)
153
+ }
154
+ return this.saveResponse(res, dest, assetUrl)
155
+ }
156
+
157
+ async login(registry: RegistryConfig, _options?: PublishOptions): Promise<void> {
158
+ const creds = this.resolveCredentials(registry)
159
+ if (!creds.token && !creds.password) {
160
+ this.log(` ℹ No auth token configured for GitHub Releases ${registry.server}`)
161
+ }
162
+ }
163
+
164
+ protected async doPublish(
165
+ artifact: ArtifactPayload,
166
+ registry: RegistryConfig,
167
+ options?: PublishOptions
168
+ ): Promise<{
169
+ ref: string
170
+ digest?: string
171
+ metadata?: Record<string, unknown>
172
+ }> {
173
+ const creds = this.resolveCredentials(registry)
174
+ const token = creds.token || creds.password
175
+ const { owner, repo } = this.resolveOwnerRepo(registry)
176
+
177
+ const client = new GitHubClient({
178
+ baseUrl: registry.server && registry.server.startsWith('http') ? registry.server : undefined,
179
+ token,
180
+ timeoutMs: options?.timeoutMs
181
+ })
182
+
183
+ const version = artifact.tags && artifact.tags.length > 0 ? artifact.tags[0] : 'v1.0.0'
184
+ const tagName = version.startsWith('v') || /^\d/.test(version) ? version : `v${version}`
185
+
186
+ let filePath = artifact.source
187
+ const stat = await fs.stat(filePath).catch(() => null)
188
+ if (!stat || !stat.isFile()) {
189
+ throw new Error(`GitHub Release asset source must be an existing file: ${filePath}`)
190
+ }
191
+
192
+ const filename = path.basename(filePath)
193
+ const fileBuffer = await fs.readFile(filePath)
194
+ const digest = `sha256:${crypto.createHash('sha256').update(fileBuffer).digest('hex')}`
195
+
196
+ this.log(` 📤 Uploading release asset "${filename}" to GitHub ${owner}/${repo} release ${tagName}...`)
197
+
198
+ if (options?.dryRun) {
199
+ this.log(` [dryRun] Would upload asset ${filename} (${fileBuffer.length} bytes) to GitHub release ${tagName}`)
200
+ return {
201
+ ref: `${owner}/${repo}@${tagName}#${filename}`,
202
+ digest,
203
+ metadata: { owner, repo, tagName, filename, size: fileBuffer.length }
204
+ }
205
+ }
206
+
207
+ // Get or create release
208
+ let release = await client.getReleaseByTag(owner, repo, tagName)
209
+ if (!release) {
210
+ this.log(` ✨ Creating new GitHub release ${tagName} in ${owner}/${repo}...`)
211
+ release = await client.createRelease(owner, repo, {
212
+ tagName,
213
+ name: artifact.name || tagName,
214
+ body: `Release ${tagName} published via OmniFlow.`
215
+ })
216
+ }
217
+
218
+ const uploaded = await client.uploadReleaseAsset(release.upload_url, filename, fileBuffer)
219
+
220
+ return {
221
+ ref: `${owner}/${repo}@${tagName}#${filename}`,
222
+ digest,
223
+ metadata: {
224
+ owner,
225
+ repo,
226
+ tagName,
227
+ filename,
228
+ assetId: uploaded.id,
229
+ downloadUrl: uploaded.browser_download_url,
230
+ releaseUrl: release.html_url,
231
+ size: fileBuffer.length
232
+ }
233
+ }
234
+ }
235
+ }
236
+
237
+ // Convenient alias
238
+ export { GitHubReleasePublisher as GitHubGenericPublisher }