Skip to content

Commit 27a7f30

Browse files
committed
Implement a function to determine the ID of the Azure Pipelines runs
We want to download Git for Windows' SDK artifacts from the respective Azure Pipelines. To that end, it is sure handy if we can identify the latest successful run of them. Signed-off-by: Johannes Schindelin <[email protected]>
1 parent 081bb76 commit 27a7f30

File tree

3 files changed

+157
-1
lines changed

3 files changed

+157
-1
lines changed

.vscode/settings.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
{
22
"cSpell.ignoreRegExpList": [
3-
"mingw-w64-git"
3+
"makepkg-git",
4+
"mingw-w64-git",
5+
"vstfs://.*"
46
],
57
"cSpell.words": [
68
"Pacman",

__tests__/downloader.test.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import {ClientRequest, IncomingMessage} from 'http'
2+
import https from 'https'
3+
import {mocked} from 'ts-jest/utils'
4+
import {get} from '../src/downloader'
5+
6+
const buildIdResponse = {
7+
count: 1,
8+
value: [
9+
{
10+
_links: [Object],
11+
properties: {},
12+
tags: [],
13+
validationResults: [],
14+
plans: [Array],
15+
triggerInfo: [Object],
16+
id: 71000,
17+
buildNumber: '71000',
18+
status: 'completed',
19+
result: 'succeeded',
20+
queueTime: '2021-02-16T03:11:20.8026424Z',
21+
startTime: '2021-02-16T03:11:35.5385517Z',
22+
finishTime: '2021-02-16T03:42:07.4413436Z',
23+
url:
24+
'https://dev.azure.com/Git-for-Windows/f3317b6a-fa67-40d4-9a33-b652e06943df/_apis/build/Builds/71000',
25+
definition: [Object],
26+
project: [Object],
27+
uri: 'vstfs:///Build/Build/71000',
28+
sourceBranch: 'refs/heads/main',
29+
sourceVersion: 'c1d940ae0b2de75e18f642e76750f19b0f92dbc6',
30+
priority: 'normal',
31+
reason: 'individualCI',
32+
requestedFor: [Object],
33+
requestedBy: [Object],
34+
lastChangedDate: '2021-02-16T03:42:07.653Z',
35+
lastChangedBy: [Object],
36+
orchestrationPlan: [Object],
37+
logs: [Object],
38+
repository: [Object],
39+
keepForever: true,
40+
retainedByRelease: false,
41+
triggeredByBuild: null
42+
}
43+
]
44+
}
45+
46+
jest.mock('https')
47+
48+
declare type Callback = (data?: object) => void
49+
50+
test('can obtain build ID', async () => {
51+
mocked(https.request).mockImplementation(
52+
(
53+
_url,
54+
_options,
55+
callback: ((res: IncomingMessage) => void) | undefined
56+
): ClientRequest => {
57+
const res = {
58+
statusCode: 200,
59+
on: (eventType: string, eventCallback: Callback): object => {
60+
switch (eventType) {
61+
case 'data':
62+
eventCallback(Buffer.from(JSON.stringify(buildIdResponse)))
63+
break
64+
case 'end':
65+
eventCallback()
66+
break
67+
}
68+
return res
69+
}
70+
} as IncomingMessage
71+
expect(callback).not.toBeUndefined()
72+
callback && callback(res)
73+
const req = {} as ClientRequest
74+
Object.assign(req, {on: () => {}})
75+
return req
76+
}
77+
)
78+
const {id} = await get('minimal', 'x86_64')
79+
expect(id).toEqual(71000)
80+
})

src/downloader.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import https from 'https'
2+
3+
async function fetchJSONFromURL<T>(url: string): Promise<T> {
4+
return new Promise<T>((resolve, reject) => {
5+
https
6+
.request(url, {}, res => {
7+
if (res.statusCode !== 200) {
8+
reject(
9+
new Error(
10+
`Got code ${res.statusCode}, URL: ${url}, message: ${res.statusMessage}`
11+
)
12+
)
13+
return
14+
}
15+
const data: Uint8Array[] = []
16+
res
17+
.on('data', (chunk: Uint8Array) => data.push(chunk))
18+
.on('end', () => {
19+
try {
20+
resolve(JSON.parse(Buffer.concat(data).toString('utf-8')))
21+
} catch (e) {
22+
reject(e)
23+
}
24+
})
25+
.on('error', e => reject(e))
26+
})
27+
.on('error', e => reject(e))
28+
.end()
29+
})
30+
}
31+
32+
export async function get(
33+
flavor: string,
34+
architecture: string
35+
): Promise<{
36+
id: string
37+
}> {
38+
if (!['x86_64', 'i686'].includes(architecture)) {
39+
throw new Error(`Unsupported architecture: ${architecture}`)
40+
}
41+
42+
let definitionId: number
43+
switch (flavor) {
44+
case 'minimal':
45+
if (architecture === 'i686') {
46+
throw new Error(`Flavor "minimal" is only available for x86_64`)
47+
}
48+
definitionId = 22
49+
break
50+
case 'makepkg-git':
51+
if (architecture === 'i686') {
52+
throw new Error(`Flavor "makepkg-git" is only available for x86_64`)
53+
}
54+
definitionId = 29
55+
break
56+
case 'build-installers':
57+
definitionId = architecture === 'i686' ? 30 : 29
58+
break
59+
default:
60+
throw new Error(`Unknown flavor: '${flavor}`)
61+
}
62+
63+
const baseURL = 'https://dev.azure.com/git-for-windows/git/_apis/build/builds'
64+
const data = await fetchJSONFromURL<{
65+
count: number
66+
value: [{id: string}]
67+
}>(
68+
`${baseURL}?definitions=${definitionId}&statusFilter=completed&resultFilter=succeeded&$top=1`
69+
)
70+
if (data.count !== 1) {
71+
throw new Error(`Unexpected number of builds: ${data.count}`)
72+
}
73+
return {id: data.value[0].id}
74+
}

0 commit comments

Comments
 (0)