1
- import { AirbyteRecord } from 'faros-airbyte-cdk' ;
1
+ import { AirbyteLogger , AirbyteRecord } from 'faros-airbyte-cdk' ;
2
2
import { Utils } from 'faros-js-client' ;
3
+ import GitUrlParse from 'git-url-parse' ;
3
4
4
5
import { Common } from '../common/common' ;
5
6
import { DestinationModel , DestinationRecord , StreamContext } from '../converter' ;
6
7
import { OctopusConverter } from './common' ;
7
8
9
+ interface ArtifactVCSInfo {
10
+ artifactUid : string ;
11
+ sha ?: string ;
12
+ repository : {
13
+ name : string ;
14
+ organization : {
15
+ uid : string ;
16
+ source : string ;
17
+ } ;
18
+ } ;
19
+ }
20
+
8
21
export class Deployments extends OctopusConverter {
9
22
readonly destinationModels : ReadonlyArray < DestinationModel > = [
10
23
'cicd_Deployment' ,
@@ -18,10 +31,15 @@ export class Deployments extends OctopusConverter {
18
31
const source = this . streamName . source ;
19
32
const deployment = record . record . data ;
20
33
34
+ const deploymentKey = {
35
+ uid : deployment . Id ,
36
+ source,
37
+ } ;
38
+
21
39
res . push ( {
22
40
model : 'cicd_Deployment' ,
23
41
record : {
24
- uid : deployment . Id ,
42
+ ... deploymentKey ,
25
43
application : Common . computeApplication ( deployment . ProjectName ) ,
26
44
url : deployment . Links ?. Self ,
27
45
requestedAt : Utils . toDate ( deployment . Task ?. QueueTime ) ,
@@ -32,13 +50,206 @@ export class Deployments extends OctopusConverter {
32
50
deployment . Task ?. State ,
33
51
deployment . Task ?. ErrorMessage
34
52
) ,
35
- source,
36
53
} ,
37
54
} ) ;
38
55
56
+ const vcsInfo = this . getVCSInfo ( deployment , ctx ) ;
57
+
58
+ for ( const vcs of vcsInfo ) {
59
+ const artifactKey = {
60
+ uid : vcs . artifactUid ,
61
+ repository : {
62
+ uid : vcs . repository . name ,
63
+ organization : vcs . repository . organization ,
64
+ } ,
65
+ } ;
66
+
67
+ res . push ( {
68
+ model : 'cicd_ArtifactDeployment' ,
69
+ record : {
70
+ artifact : artifactKey ,
71
+ deployment : deploymentKey ,
72
+ } ,
73
+ } ) ;
74
+
75
+ if ( vcs . sha ) {
76
+ // Only instantiate artifact if we can link all the way to commit
77
+ res . push ( {
78
+ model : 'cicd_Artifact' ,
79
+ record : artifactKey ,
80
+ } ) ;
81
+ res . push ( {
82
+ model : 'cicd_ArtifactCommitAssociation' ,
83
+ record : {
84
+ artifact : artifactKey ,
85
+ commit : {
86
+ sha : vcs . sha ,
87
+ repository : vcs . repository ,
88
+ } ,
89
+ } ,
90
+ } ) ;
91
+ }
92
+ }
93
+
39
94
return res ;
40
95
}
41
96
97
+ private getVCSInfo ( deployment : any , ctx : StreamContext ) : ArtifactVCSInfo [ ] {
98
+ const deployId = deployment . Id ;
99
+ const logger = ctx . logger ;
100
+
101
+ let commitSha : string ;
102
+ let repoName : string ;
103
+ let orgUid : string ;
104
+ let orgSource : string = this . vcsSource ( ctx ) ;
105
+
106
+ // Attempt to first retrieve VCS information from explicit deployment variables
107
+ for ( const v of deployment . Variables ?? [ ] ) {
108
+ if ( v . Name === 'VCS_COMMIT' ) {
109
+ commitSha = this . getVarValue ( deployId , v , logger ) ;
110
+ } else if ( v . name === 'VCS_REPO' ) {
111
+ repoName = this . getVarValue ( deployId , v , logger ) ;
112
+ } else if ( v . name === 'VCS_ORG' ) {
113
+ orgUid = this . getVarValue ( deployId , v , logger ) ;
114
+ } else if ( v . name === 'VCS_SOURCE' ) {
115
+ orgSource = this . getVarValue ( deployId , v , logger ) ;
116
+ }
117
+ }
118
+
119
+ // If all information provided as variables return it
120
+ if ( commitSha && repoName && orgUid ) {
121
+ return [
122
+ {
123
+ artifactUid : commitSha ,
124
+ sha : commitSha ,
125
+ repository : {
126
+ name : repoName ,
127
+ organization : {
128
+ uid : orgUid ,
129
+ source : orgSource ,
130
+ } ,
131
+ } ,
132
+ } ,
133
+ ] ;
134
+ }
135
+
136
+ const vcsInfo : ArtifactVCSInfo [ ] = [ ] ;
137
+ const hasChangeArray =
138
+ deployment . Changes &&
139
+ Array . isArray ( deployment . Changes ) &&
140
+ deployment . Changes . length > 0 ;
141
+
142
+ if ( hasChangeArray ) {
143
+ // First try to construct the fallback repo from explicit deployment variables
144
+ let fallbackRepoInfo : ArtifactVCSInfo [ 'repository' ] ;
145
+ if ( repoName && orgUid ) {
146
+ fallbackRepoInfo = {
147
+ name : repoName ,
148
+ organization : {
149
+ uid : orgUid ,
150
+ source : orgSource ,
151
+ } ,
152
+ } ;
153
+ }
154
+
155
+ // If fallback not set attempt to retrieve it from repo property in process
156
+ if ( ! fallbackRepoInfo ) {
157
+ for ( const step of deployment . Process ?. Steps ?? [ ] ) {
158
+ for ( const action of step . Actions ?? [ ] ) {
159
+ const repo = action . Properties ?. [ 'repo' ] ;
160
+ if ( repo ) {
161
+ fallbackRepoInfo = this . getRepoInfoFromURL ( deployId , repo , ctx ) ;
162
+ break ;
163
+ }
164
+ }
165
+ if ( fallbackRepoInfo ) {
166
+ break ;
167
+ }
168
+ }
169
+ }
170
+
171
+ // Assume last change was the one deployed
172
+ const change = deployment . Changes . at ( - 1 ) ;
173
+
174
+ // Retrieve repo information from Commits
175
+ for ( const commit of change . Commits ?? [ ] ) {
176
+ const artifactUid = change . Version ;
177
+ const sha = commit . Id ;
178
+ const repository =
179
+ this . getRepoInfoFromURL ( deployId , commit . LinkUrl , ctx ) ??
180
+ fallbackRepoInfo ;
181
+
182
+ if ( repository ) {
183
+ vcsInfo . push ( { artifactUid, sha, repository} ) ;
184
+ }
185
+ }
186
+
187
+ // Commits did not yield any VCS info, use BuildInformation
188
+ if ( ! vcsInfo . length ) {
189
+ for ( const buildInfo of change . BuildInformation ?? [ ] ) {
190
+ const artifactUid = `${ buildInfo . PackageId } :${ buildInfo . Version } ` ;
191
+ const sha = buildInfo . VcsCommitNumber ;
192
+ const repository =
193
+ this . getRepoInfoFromURL ( deployId , buildInfo . VcsRoot , ctx ) ??
194
+ fallbackRepoInfo ;
195
+
196
+ if ( repository ) {
197
+ vcsInfo . push ( { artifactUid, sha, repository} ) ;
198
+ }
199
+ }
200
+ }
201
+
202
+ // If still no info create a dummy artifact using Change.Version
203
+ if ( ! vcsInfo . length && fallbackRepoInfo ) {
204
+ vcsInfo . push ( {
205
+ artifactUid : change . Version ,
206
+ repository : fallbackRepoInfo ,
207
+ } ) ;
208
+ }
209
+ }
210
+
211
+ if ( ! vcsInfo . length ) {
212
+ logger . warn ( `Could not retrieve VCS info for deployment: ${ deployId } ` ) ;
213
+ }
214
+
215
+ return vcsInfo ;
216
+ }
217
+
218
+ private getRepoInfoFromURL (
219
+ deploymentId : string ,
220
+ url : string ,
221
+ ctx : StreamContext
222
+ ) : ArtifactVCSInfo [ 'repository' ] {
223
+ try {
224
+ const parsedUrl = GitUrlParse ( url ) ;
225
+ return {
226
+ name : parsedUrl . name . toLowerCase ( ) ,
227
+ organization : {
228
+ uid : parsedUrl . owner . toLowerCase ( ) ,
229
+ source : this . vcsSource ( ctx ) ,
230
+ } ,
231
+ } ;
232
+ } catch ( err : any ) {
233
+ ctx . logger . warn (
234
+ `Unable to parse VCS information for deployment ${ deploymentId } from repo url: ${ url } `
235
+ ) ;
236
+ }
237
+ }
238
+
239
+ private getVarValue (
240
+ deploymentId : string ,
241
+ variable : { Name : string ; Value : string } ,
242
+ logger : AirbyteLogger
243
+ ) : string | undefined {
244
+ if ( variable . Value ) {
245
+ return variable . Value ;
246
+ }
247
+ logger . warn (
248
+ `Deployment [${ deploymentId } ] had a null value for ${ variable . Name } variable`
249
+ ) ;
250
+ return undefined ;
251
+ }
252
+
42
253
/**
43
254
* Octopus task statuses include:
44
255
* Canceled, Cancelling, Executing, Failed, Queued, Success, TimedOut
0 commit comments