Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent invalid links in update task script #316

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 24 additions & 25 deletions scripts/release/asana-update-tasks.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
/* eslint-disable no-undef */
/* eslint-disable camelcase */
const Asana = require('asana')
const {replaceAllInString} = require('./release-utils.js')
const {getLink} = require('./release-utils.js')
const {wrapInLi} = require('./release-utils.js')
const {replaceAllInString, getLink, wrapInLi} = require('./release-utils.js')

const ASANA_ACCESS_TOKEN = process.env.ASANA_ACCESS_TOKEN
const prUrls = {
android: process.env.ANDROID_PR_URL || 'error',
bsk: process.env.BSK_PR_URL || 'error',
ios: process.env.IOS_PR_URL || 'error',
macos: process.env.MACOS_PR_URL || 'error',
windows: process.env.WINDOWS_PR_URL || 'error'
android: process.env.ANDROID_PR_URL,
bsk: process.env.BSK_PR_URL,
ios: process.env.IOS_PR_URL,
macos: process.env.MACOS_PR_URL,
windows: process.env.WINDOWS_PR_URL
}
const asanaOutputRaw = process.env.ASANA_OUTPUT || '{}'
const asanaOutput = JSON.parse(asanaOutputRaw)
const asanaOutputRaw = process.env.ASANA_OUTPUT

let asana

Expand All @@ -27,35 +22,39 @@ const setupAsana = () => {
}

const asanaUpdateTasks = async () => {
let asanaOutput
try {
asanaOutput = JSON.parse(asanaOutputRaw || '')
} catch (e) {
throw new Error('Unable to parse Asana output JSON')
}

setupAsana()

const platformEntries = Object.entries(asanaOutput)
for (const [platformName, platformObj] of platformEntries) {
// If either are absent we haven't implemented the automation for that platform
// If we're missing required data, either we haven't implemented automation for that platform yet,
// or something went wrong in a previous job
if (!platformObj.taskGid || !prUrls[platformName]) continue

// Get the task
const { html_notes: notes } = await asana.tasks.getTask(platformObj.taskGid, { opt_fields: 'html_notes' })

const prLink = getLink(prUrls[platformName], `${platformObj.displayName} PR`)
/** @type {[[RegExp, string]]} */
const taskDescriptionSubstitutions = [
[/\[\[pr_url]]/, getLink(prUrls[platformName], platformObj.displayName + ' PR')]
[/\[\[pr_url]]/, prLink]
]

let extraContent = ''
if (platformName === 'bsk') {
// On the BSK task we also substitute the ios and macos placeholders
const markup =
`${wrapInLi(getLink(prUrls.ios, 'iOS PR'))}${wrapInLi(getLink(prUrls.macos, 'macOS PR'))}`
taskDescriptionSubstitutions.push(
[/\[\[extra_content]]/, markup]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me know if I've made a mistake here, but it seemed strange that here we didn't replace the wrapping <li>, but below we did -- surely we'd want to replace it both times? (either to replace it with the two bullet points created above, or to remove it completely)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mhm, not 100% sure, but we'll see when it runs.

)
} else {
// For all other platforms we remove the extra placeholder
taskDescriptionSubstitutions.push(
[/<li>\[\[extra_content]]<\/li>/, '']
)
const iosPrLink = prUrls.ios ? getLink(prUrls.ios, 'iOS PR') : 'Error creating iOS PR'
const macosPrLink = prUrls.macos ? getLink(prUrls.macos, 'macOS PR') : 'Error creating macOS PR'
extraContent = `${wrapInLi(iosPrLink)}${wrapInLi(macosPrLink)}`
}

taskDescriptionSubstitutions.push([/<li>\[\[extra_content]]<\/li>/, extraContent])

const updatedNotes = replaceAllInString(notes, taskDescriptionSubstitutions)

await asana.tasks.updateTask(platformObj.taskGid, { html_notes: updatedNotes })
Expand Down
146 changes: 146 additions & 0 deletions scripts/release/tests/__snapshots__/asana-update-tasks.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`when all data is available updates each platforms task notes 1`] = `
[
[
"android-123",
{
"html_notes": "<h2>New release:<h2>
<ol>
<li>version [[version]]</li>
<li>[[commit]]</li>
<li>[[release_url]]</li>
<li><a href=\\"https://github.com/duckduckgo/android/pr/1\\">Android PR</a></li>

</ol>
<h2>Release notes:</h2>
<p>[[notes]]</p>",
},
],
[
"bsk-123",
{
"html_notes": "<h2>New release:<h2>
<ol>
<li>version [[version]]</li>
<li>[[commit]]</li>
<li>[[release_url]]</li>
<li><a href=\\"https://github.com/duckduckgo/BrowserServicesKit/pr/1\\">BrowserServicesKit PR</a></li>
<li><a href=\\"https://github.com/duckduckgo/ios/pr/1\\">iOS PR</a></li><li><a href=\\"https://github.com/duckduckgo/macos-browser/pr/1\\">macOS PR</a></li>
</ol>
<h2>Release notes:</h2>
<p>[[notes]]</p>",
},
],
[
"ios-123",
{
"html_notes": "<h2>New release:<h2>
<ol>
<li>version [[version]]</li>
<li>[[commit]]</li>
<li>[[release_url]]</li>
<li><a href=\\"https://github.com/duckduckgo/ios/pr/1\\">iOS PR</a></li>

</ol>
<h2>Release notes:</h2>
<p>[[notes]]</p>",
},
],
[
"macos-123",
{
"html_notes": "<h2>New release:<h2>
<ol>
<li>version [[version]]</li>
<li>[[commit]]</li>
<li>[[release_url]]</li>
<li><a href=\\"https://github.com/duckduckgo/macos-browser/pr/1\\">macOS PR</a></li>

</ol>
<h2>Release notes:</h2>
<p>[[notes]]</p>",
},
],
[
"windows-123",
{
"html_notes": "<h2>New release:<h2>
<ol>
<li>version [[version]]</li>
<li>[[commit]]</li>
<li>[[release_url]]</li>
<li><a href=\\"https://github.com/duckduckgo/windows-browser/pr/1\\">Windows PR</a></li>

</ol>
<h2>Release notes:</h2>
<p>[[notes]]</p>",
},
],
]
`;

exports[`when not all PR URLs are available updates each platforms task notes except for missing platforms 1`] = `
[
[
"android-123",
{
"html_notes": "<h2>New release:<h2>
<ol>
<li>version [[version]]</li>
<li>[[commit]]</li>
<li>[[release_url]]</li>
<li><a href=\\"https://github.com/duckduckgo/android/pr/1\\">Android PR</a></li>

</ol>
<h2>Release notes:</h2>
<p>[[notes]]</p>",
},
],
[
"bsk-123",
{
"html_notes": "<h2>New release:<h2>
<ol>
<li>version [[version]]</li>
<li>[[commit]]</li>
<li>[[release_url]]</li>
<li><a href=\\"https://github.com/duckduckgo/BrowserServicesKit/pr/1\\">BrowserServicesKit PR</a></li>
<li>Error creating iOS PR</li><li><a href=\\"https://github.com/duckduckgo/macos-browser/pr/1\\">macOS PR</a></li>
</ol>
<h2>Release notes:</h2>
<p>[[notes]]</p>",
},
],
[
"macos-123",
{
"html_notes": "<h2>New release:<h2>
<ol>
<li>version [[version]]</li>
<li>[[commit]]</li>
<li>[[release_url]]</li>
<li><a href=\\"https://github.com/duckduckgo/macos-browser/pr/1\\">macOS PR</a></li>

</ol>
<h2>Release notes:</h2>
<p>[[notes]]</p>",
},
],
[
"windows-123",
{
"html_notes": "<h2>New release:<h2>
<ol>
<li>version [[version]]</li>
<li>[[commit]]</li>
<li>[[release_url]]</li>
<li><a href=\\"https://github.com/duckduckgo/windows-browser/pr/1\\">Windows PR</a></li>

</ol>
<h2>Release notes:</h2>
<p>[[notes]]</p>",
},
],
]
`;
120 changes: 120 additions & 0 deletions scripts/release/tests/asana-update-tasks.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
let mockAsanaClient
jest.mock('asana', () => {
const notes = `<h2>New release:<h2>
<ol>
<li>version [[version]]</li>
<li>[[commit]]</li>
<li>[[release_url]]</li>
<li>[[pr_url]]</li>
<li>[[extra_content]]</li>
</ol>
<h2>Release notes:</h2>
<p>[[notes]]</p>`

mockAsanaClient = {
useAccessToken: () => mockAsanaClient,
tasks: {
getTask: () => ({ html_notes: notes }),
updateTask: jest.fn()
}
}
return { Client: { create: () => mockAsanaClient } }
})

beforeEach(() => {
process.env.ASANA_ACCESS_TOKEN = 'mock-asana-access-token'
process.env.ANDROID_PR_URL = 'https://github.com/duckduckgo/android/pr/1'
process.env.BSK_PR_URL = 'https://github.com/duckduckgo/BrowserServicesKit/pr/1'
process.env.IOS_PR_URL = 'https://github.com/duckduckgo/ios/pr/1'
process.env.MACOS_PR_URL = 'https://github.com/duckduckgo/macos-browser/pr/1'
process.env.WINDOWS_PR_URL = 'https://github.com/duckduckgo/windows-browser/pr/1'
process.env.ASANA_OUTPUT = JSON.stringify({
'android': {
'displayName': 'Android',
'taskGid': 'android-123',
'taskUrl': 'https://example.com/android-task'
},
'bsk': {
'displayName': 'BrowserServicesKit',
'taskGid': 'bsk-123',
'taskUrl': 'https://example.com/bsk-task'
},
'ios': {
'displayName': 'iOS',
'taskGid': 'ios-123',
'taskUrl': 'https://example.com/ios-task'
},
'macos': {
'displayName': 'macOS',
'taskGid': 'macos-123',
'taskUrl': 'https://example.com/macos-task'
},
'extensions': {
'displayName': 'Extensions',
'taskGid': 'extension-123',
'taskUrl': 'https://example.com/extension-task'
},
'windows': {
'displayName': 'Windows',
'taskGid': 'windows-123',
'taskUrl': 'https://example.com/windows-task'
}
})
})

describe('when Asana output is not available', () => {
beforeEach(() => {
process.env.ASANA_OUTPUT = undefined
})

it('displays an error message', async () => {
// @ts-ignore
const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
const mockConsole = jest.spyOn(console, 'error').mockImplementation(() => {})
jest.isolateModules(() => {
require('../asana-update-tasks.js')
})
// Wait for update tasks to run
await new Promise((resolve) => setTimeout(resolve, 200))

expect(mockExit).toHaveBeenCalledTimes(1)
expect(mockConsole).toHaveBeenCalledTimes(1)
expect(mockConsole.mock.calls).toMatchInlineSnapshot(`
[
[
[Error: Unable to parse Asana output JSON],
],
]
`)
})
})

describe('when not all PR URLs are available', () => {
beforeEach(() => {
delete process.env.IOS_PR_URL
})

it('updates each platforms task notes except for missing platforms', async () => {
jest.isolateModules(() => {
require('../asana-update-tasks.js')
})
// Wait for update tasks to run
await new Promise((resolve) => setTimeout(resolve, 200))

expect(mockAsanaClient.tasks.updateTask).toBeCalledTimes(4)
expect(mockAsanaClient.tasks.updateTask.mock.calls).toMatchSnapshot()
})
})

describe('when all data is available', () => {
it('updates each platforms task notes', async () => {
jest.isolateModules(() => {
require('../asana-update-tasks.js')
})
// Wait for update tasks to run
await new Promise((resolve) => setTimeout(resolve, 200))

expect(mockAsanaClient.tasks.updateTask).toBeCalledTimes(5)
expect(mockAsanaClient.tasks.updateTask.mock.calls).toMatchSnapshot()
})
})