1
+ import fs from 'fs'
1
2
import https from 'https'
3
+ import unzipper from 'unzipper'
2
4
3
5
async function fetchJSONFromURL < T > ( url : string ) : Promise < T > {
4
6
return new Promise < T > ( ( resolve , reject ) => {
@@ -29,32 +31,104 @@ async function fetchJSONFromURL<T>(url: string): Promise<T> {
29
31
} )
30
32
}
31
33
34
+ function mkdirp ( directoryPath : string ) : void {
35
+ try {
36
+ const stat = fs . statSync ( directoryPath )
37
+ if ( stat . isDirectory ( ) ) {
38
+ return
39
+ }
40
+ throw new Error ( `${ directoryPath } exists, but is not a directory` )
41
+ } catch ( e ) {
42
+ if ( ! e || e . code !== 'ENOENT' ) {
43
+ throw e
44
+ }
45
+ }
46
+ fs . mkdirSync ( directoryPath , { recursive : true } )
47
+ }
48
+
49
+ async function unzip (
50
+ url : string ,
51
+ stripPrefix : string ,
52
+ outputDirectory : string ,
53
+ verbose : boolean | number
54
+ ) : Promise < void > {
55
+ let progress =
56
+ verbose === false
57
+ ? ( ) : void => { }
58
+ : ( path : string ) : void => {
59
+ path === undefined || process . stderr . write ( `${ path } \n` )
60
+ }
61
+ if ( typeof verbose === 'number' ) {
62
+ let counter = 0
63
+ progress = ( path ?: string ) : void => {
64
+ if ( path === undefined || ++ counter % verbose === 0 ) {
65
+ process . stderr . write ( `${ counter } items extracted\n` )
66
+ }
67
+ }
68
+ }
69
+ mkdirp ( outputDirectory )
70
+ return new Promise < void > ( ( resolve , reject ) => {
71
+ https . get ( url , res =>
72
+ res
73
+ . pipe ( unzipper . Parse ( ) )
74
+ . on ( 'entry' , entry => {
75
+ if ( ! entry . path . startsWith ( stripPrefix ) ) {
76
+ process . stderr . write (
77
+ `warning: skipping ${ entry . path } because it does not start with ${ stripPrefix } \n`
78
+ )
79
+ }
80
+ const entryPath = `${ outputDirectory } /${ entry . path . substring (
81
+ stripPrefix . length
82
+ ) } `
83
+ progress ( entryPath )
84
+ if ( entryPath . endsWith ( '/' ) ) {
85
+ mkdirp ( entryPath . replace ( / \/ $ / , '' ) )
86
+ entry . autodrain ( )
87
+ } else {
88
+ entry . pipe ( fs . createWriteStream ( `${ entryPath } ` ) )
89
+ }
90
+ } )
91
+ . on ( 'error' , reject )
92
+ . on ( 'finish' , progress )
93
+ . on ( 'finish' , resolve )
94
+ )
95
+ } )
96
+ }
97
+
32
98
export async function get (
33
99
flavor : string ,
34
100
architecture : string
35
101
) : Promise < {
36
102
id : string
103
+ download : (
104
+ outputDirectory : string ,
105
+ verbose ?: number | boolean
106
+ ) => Promise < void >
37
107
} > {
38
108
if ( ! [ 'x86_64' , 'i686' ] . includes ( architecture ) ) {
39
109
throw new Error ( `Unsupported architecture: ${ architecture } ` )
40
110
}
41
111
42
112
let definitionId : number
113
+ let artifactName : string
43
114
switch ( flavor ) {
44
115
case 'minimal' :
45
116
if ( architecture === 'i686' ) {
46
117
throw new Error ( `Flavor "minimal" is only available for x86_64` )
47
118
}
48
119
definitionId = 22
120
+ artifactName = 'git-sdk-64-minimal'
49
121
break
50
122
case 'makepkg-git' :
51
123
if ( architecture === 'i686' ) {
52
124
throw new Error ( `Flavor "makepkg-git" is only available for x86_64` )
53
125
}
54
126
definitionId = 29
127
+ artifactName = 'git-sdk-64-makepkg-git'
55
128
break
56
129
case 'build-installers' :
57
130
definitionId = architecture === 'i686' ? 30 : 29
131
+ artifactName = `git-sdk-${ architecture === 'i686' ? 32 : 64 } -${ flavor } `
58
132
break
59
133
default :
60
134
throw new Error ( `Unknown flavor: '${ flavor } ` )
@@ -63,12 +137,30 @@ export async function get(
63
137
const baseURL = 'https://dev.azure.com/git-for-windows/git/_apis/build/builds'
64
138
const data = await fetchJSONFromURL < {
65
139
count : number
66
- value : [ { id : string } ]
140
+ value : [ { id : string ; downloadURL : string } ]
67
141
} > (
68
142
`${ baseURL } ?definitions=${ definitionId } &statusFilter=completed&resultFilter=succeeded&$top=1`
69
143
)
70
144
if ( data . count !== 1 ) {
71
145
throw new Error ( `Unexpected number of builds: ${ data . count } ` )
72
146
}
73
- return { id : data . value [ 0 ] . id }
147
+ const id = `${ artifactName } -${ data . value [ 0 ] . id } `
148
+ const download = async (
149
+ outputDirectory : string ,
150
+ verbose : number | boolean = false
151
+ ) : Promise < void > => {
152
+ const data2 = await fetchJSONFromURL < {
153
+ count : number
154
+ value : [ { name : string ; resource : { downloadUrl : string } } ]
155
+ } > ( `${ baseURL } /${ data . value [ 0 ] . id } /artifacts` )
156
+ const filtered = data2 . value . filter ( e => e . name === artifactName )
157
+ if ( filtered . length !== 1 ) {
158
+ throw new Error (
159
+ `Could not find ${ artifactName } in ${ JSON . stringify ( data2 , null , 4 ) } `
160
+ )
161
+ }
162
+ const url = filtered [ 0 ] . resource . downloadUrl
163
+ await unzip ( url , `${ artifactName } /` , outputDirectory , verbose )
164
+ }
165
+ return { download, id}
74
166
}
0 commit comments