Skip to content

Commit

Permalink
Merge pull request #44 from dscho/guess-msys2-runtime-release-notes
Browse files Browse the repository at this point in the history
Nicer MSYS2 runtime release notes
  • Loading branch information
dscho authored Sep 27, 2023
2 parents 81c2bcf + d622e0d commit 274da36
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 16 deletions.
27 changes: 27 additions & 0 deletions GitForWindowsHelper/component-updates.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,38 @@ const needsSeparateARM64Build = package_name => {
].includes(package_name)
}

const guessCygwinReleaseNotesURL = async (version) => {
const { fetchHTML } = require('./https-request')
const html = await fetchHTML('https://cygwin.com')
const match = html.match(new RegExp(`The most recent version of the Cygwin DLL is[^]*?<a href=['"]?([^"' ]*)[^>]*>${version}</a>`))
if (match) return match[1]

// Sometimes Cygwin updates the home page a bit later than we'd want, let's
// find the announcement on the mailing list directly in that case:
const inboxPrefix = 'https://inbox.sourceware.org/cygwin-announce/'
const search = await fetchHTML(`${inboxPrefix}?q=cygwin-${version}`)
const searchMatch = search.match(new RegExp(`<a\\b(?:[^>]*)\\shref=['"]?([^'" ]+)[^>]*>cygwin ${version}-1</a>`))
if (searchMatch) return `${inboxPrefix}${searchMatch[1]}`

throw new Error(`Could not determine Cygwin Release Notes URL for version ${version}`)
}

const guessReleaseNotes = async (context, issue) => {
if (!issue.pull_request
&&issue.labels.filter(label => label.name === 'component-update').length !== 1) throw new Error(`Cannot determine release note from issue ${issue.number}`)
let { package_name, version } = guessComponentUpdateDetails(issue.title, issue.body)

if (package_name === 'msys2-runtime') {
const url = await guessCygwinReleaseNotesURL(version)
const message = `Comes with the MSYS2 runtime (Git for Windows flavor) based on [Cygwin v${version}](${url}).`
return {
type: 'feature',
message,
package: package_name,
version
}
}

const matchURLInIssue = (issue) => {
const pattern = {
bash: /(?:^|\n)(https:\/\/\S+)/, // use the first URL
Expand Down
52 changes: 47 additions & 5 deletions GitForWindowsHelper/https-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,23 @@ const httpsRequest = async (context, hostname, method, requestPath, body, header
})
}

const doesURLReturn404 = async url => {
const parseURL = url => {
const match = url.match(/^https:\/\/([^/]+?)(:\d+)?(\/.*)?$/)
if (!match) throw new Error(`Could not parse URL ${url}`)

const https = require('https')
const options = {
method: 'HEAD',
return {
method: 'GET',
host: match[1],
port: Number.parseInt(match[2] || '443'),
path: match[3] || '/'
}
}

const doesURLReturn404 = async url => {
const options = parseURL(url)
options.method = 'HEAD'

const https = require('https')
return new Promise((resolve, reject) => {
https.request(options, res => {
if (res.error) reject(res.error)
Expand All @@ -82,7 +88,43 @@ const doesURLReturn404 = async url => {
})
}

const fetchHTML = async url => {
const options = parseURL(url)
options.headers = {
'User-Agent': 'GitForWindowsHelper/0.0',
Accept: 'text/html'
}
return new Promise((resolve, reject) => {
try {
const https = require('https')
const req = https.request(options, res => {
res.on('error', e => reject(e))

const chunks = []
res.on('data', data => chunks.push(data))
res.on('end', () => {
const html = Buffer.concat(chunks).toString('utf-8')
if (res.statusCode > 299) {
reject({
statusCode: res.statusCode,
statusMessage: res.statusMessage,
body: html
})
} else {
resolve(html)
}
})
})
req.on('error', err => reject(err))
req.end()
} catch (e) {
reject(e)
}
})
}

module.exports = {
httpsRequest,
doesURLReturn404
doesURLReturn404,
fetchHTML
}
73 changes: 62 additions & 11 deletions __tests__/component-updates.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,46 @@ jest.mock('../GitForWindowsHelper/github-api-request', () => {
return mockGithubApiRequest
})

const mockFetchHTML = {
'https://cygwin.com': `<div>
<h2 class="cartouche">Cygwin version</h2>
<p>
The most recent version of the Cygwin DLL is
<b><a href="https://cygwin.com/pipermail/cygwin-announce/2023-September/011291.html">3.4.9</a></b>.
</p>
<p>
The Cygwin DLL currently works with all recent, commercially released
x86_64 versions of Windows, starting with Windows 7.
</p>
</div>`,
'https://inbox.sourceware.org/cygwin-announce/?q=cygwin-3.4.7': `<html><head><title>cygwin-3.4.7 - search results</title>[... plenty of stuff...]
<pre>1. <b><a
href="875y7c63s1.fsf@Rainer.invalid/">Re-Released: tar-1.34-2</a></b>
- by ASSI @ 2023-06-24 19:47 UTC [4%]
2. <b><a
href="20230616162552.879387-1-corinna-cygwin@cygwin.com/">cygwin 3.4.7-1</a></b>
- by Corinna Vinschen @ 2023-06-16 14:25 UTC [14%]
</pre>[... even more stuff...]</body></html>`
}
const missingURL = 'https://wingit.blob.core.windows.net/x86-64/curl-8.1.2-1-x86_64.pkg.tar.xz'
const missingMinTTYURL = 'https://wingit.blob.core.windows.net/i686/mintty-1~3.6.5-1-i686.pkg.tar.xz'
const bogus32BitMSYS2RuntimeURL = 'https://wingit.blob.core.windows.net/i686/msys2-runtime-3.4.9-1-i686.pkg.tar.xz'
const bogus64BitMSYS2RuntimeURL = 'https://wingit.blob.core.windows.net/x86-64/msys2-runtime-3.3-3.3.7-1-x86_64.pkg.tar.xz'
const mockDoesURLReturn404 = jest.fn(url => [
missingURL, missingMinTTYURL, bogus32BitMSYS2RuntimeURL, bogus64BitMSYS2RuntimeURL
].includes(url))
jest.mock('../GitForWindowsHelper/https-request', () => {
return {
doesURLReturn404: mockDoesURLReturn404,
fetchHTML: jest.fn(url => mockFetchHTML[url])
}
})


test('guessReleaseNotes()', async () => {
const context = { log: jest.fn() }
expect(await guessReleaseNotes(context, {
Expand Down Expand Up @@ -127,20 +167,31 @@ http://www.gnutls.org/news.html#2023-02-10`
package: 'openssl',
version: '3.1.1'
})
})

test('getMissingDeployments()', async () => {
const missingURL = 'https://wingit.blob.core.windows.net/x86-64/curl-8.1.2-1-x86_64.pkg.tar.xz'
const missingMinTTYURL = 'https://wingit.blob.core.windows.net/i686/mintty-1~3.6.5-1-i686.pkg.tar.xz'
const bogus32BitMSYS2RuntimeURL = 'https://wingit.blob.core.windows.net/i686/msys2-runtime-3.4.9-1-i686.pkg.tar.xz'
const bogus64BitMSYS2RuntimeURL = 'https://wingit.blob.core.windows.net/x86-64/msys2-runtime-3.3-3.3.7-1-x86_64.pkg.tar.xz'
const mockDoesURLReturn404 = jest.fn(url => [
missingURL, missingMinTTYURL, bogus32BitMSYS2RuntimeURL, bogus64BitMSYS2RuntimeURL
].includes(url))
jest.mock('../GitForWindowsHelper/https-request', () => {
return { doesURLReturn404: mockDoesURLReturn404 }
expect(await guessReleaseNotes(context, {
labels: [{ name: 'component-update' }],
title: '[New cygwin version] cygwin-3.4.9',
body: `\nCygwin 3.4.9 release\n\nhttps://github.com/cygwin/cygwin/releases/tag/cygwin-3.4.9`
})).toEqual({
type: 'feature',
message: 'Comes with the MSYS2 runtime (Git for Windows flavor) based on [Cygwin v3.4.9](https://cygwin.com/pipermail/cygwin-announce/2023-September/011291.html).',
package: 'msys2-runtime',
version: '3.4.9'
})

expect(await guessReleaseNotes(context, {
labels: [{ name: 'component-update' }],
title: '[New cygwin version] cygwin-3.4.7',
body: `\nCygwin 3.4.7 release\n\nhttps://github.com/cygwin/cygwin/releases/tag/cygwin-3.4.7`
})).toEqual({
type: 'feature',
message: 'Comes with the MSYS2 runtime (Git for Windows flavor) based on [Cygwin v3.4.7](https://inbox.sourceware.org/cygwin-announce/20230616162552.879387-1-corinna-cygwin@cygwin.com/).',
package: 'msys2-runtime',
version: '3.4.7'
})
})

test('getMissingDeployments()', async () => {
expect(await getMissingDeployments('curl', '8.1.2')).toEqual([missingURL])
expect(await getMissingDeployments('mintty', '3.6.5')).toEqual([missingMinTTYURL])
expect(await getMissingDeployments('msys2-runtime', '3.4.9')).toEqual([])
Expand Down

0 comments on commit 274da36

Please sign in to comment.