-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(upgrade-verify): check-issues executor
- Loading branch information
Showing
8 changed files
with
244 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,5 +22,8 @@ | |
".": { | ||
"require": "./package.json" | ||
} | ||
}, | ||
"dependencies": { | ||
"@nx/devkit": "16.10.0" | ||
} | ||
} |
99 changes: 99 additions & 0 deletions
99
packages/upgrade-verify/src/executors/check-issues/executor.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import { ExecutorContext, logger } from '@nx/devkit'; | ||
import * as fsPromises from 'fs/promises'; | ||
import executor from './executor'; | ||
import { CheckIssuesExecutorSchema } from './schema'; | ||
|
||
describe('CheckIssues Executor', () => { | ||
let context: ExecutorContext; | ||
let options: CheckIssuesExecutorSchema; | ||
|
||
beforeEach(() => { | ||
options = {}; | ||
|
||
globalThis.fetch = jest.fn().mockImplementation(async (url) => { | ||
if (url === 'https://api.github.com/repos/ziacik/nx-tools/issues/1') { | ||
return new Response(JSON.stringify({ state: 'closed' })); | ||
} else if (url === 'https://api.github.com/repos/ziacik/nx-tools/issues/12') { | ||
return new Response(JSON.stringify({ state: 'open' })); | ||
} else if (url === 'https://api.github.com/repos/ziacik/nx-tools/issues/135') { | ||
return new Response(JSON.stringify({ state: 'open' })); | ||
} else { | ||
throw new Error('Unexpected url'); | ||
} | ||
}); | ||
|
||
jest.spyOn(logger, 'info').mockImplementation(); | ||
jest.spyOn(fsPromises, 'readFile').mockImplementation(async () => { | ||
const error: NodeJS.ErrnoException = new Error('ENOENT'); | ||
error.code = 'ENOENT'; | ||
throw error; | ||
}); | ||
|
||
context = createContext(); | ||
}); | ||
|
||
it('tries to read ISSUES.md file in the workspace root', async () => { | ||
await executor(options, context); | ||
expect(fsPromises.readFile).toHaveBeenCalledWith('/some/root/ISSUES.md', 'utf8'); | ||
}); | ||
|
||
it('just returns success if there is no ISSUES.md file in the workspace root', async () => { | ||
const output = await executor(options, context); | ||
expect(logger.info).toHaveBeenCalledWith("There is no 'ISSUES.md' file in the workspace root."); | ||
expect(output.success).toBe(true); | ||
}); | ||
|
||
it('tells that no issues have been closed if no issues have been closed', async () => { | ||
jest.mocked(fsPromises.readFile).mockResolvedValue(`# Issues | ||
https://github.com/ziacik/nx-tools/issues/12 | ||
[Some issue](https://github.com/ziacik/nx-tools/issues/135) | ||
`); | ||
const output = await executor(options, context); | ||
|
||
expect(logger.info).not.toHaveBeenCalledWith('Issues which are closed now:'); | ||
expect(logger.info).toHaveBeenCalledWith('No issues have been closed.'); | ||
|
||
expect(output.success).toBe(true); | ||
}); | ||
|
||
it('checks all github issues found in ISSUES.md file in workspace root', async () => { | ||
jest.mocked(fsPromises.readFile).mockResolvedValue(`# Issues | ||
[https://github.com/ziacik/nx-tools/issues/1](https://github.com/ziacik/nx-tools/issues/1) | ||
https://github.com/ziacik/nx-tools/issues/12 | ||
[Some issue](https://github.com/ziacik/nx-tools/issues/135) | ||
`); | ||
const output = await executor(options, context); | ||
expect(fetch).toHaveBeenCalledWith('https://api.github.com/repos/ziacik/nx-tools/issues/1'); | ||
expect(fetch).toHaveBeenCalledWith('https://api.github.com/repos/ziacik/nx-tools/issues/12'); | ||
expect(fetch).toHaveBeenCalledWith('https://api.github.com/repos/ziacik/nx-tools/issues/135'); | ||
expect(fetch).toHaveBeenCalledTimes(3); | ||
expect(output.success).toBe(true); | ||
}); | ||
|
||
it('lists all issues which are in the list and have become closed already', async () => { | ||
jest.mocked(fsPromises.readFile).mockResolvedValue(`# Issues | ||
[https://github.com/ziacik/nx-tools/issues/1](https://github.com/ziacik/nx-tools/issues/1) | ||
https://github.com/ziacik/nx-tools/issues/12 | ||
[Some issue](https://github.com/ziacik/nx-tools/issues/135) | ||
`); | ||
const output = await executor(options, context); | ||
expect(logger.info).toHaveBeenCalledWith('Issues which are closed now:'); | ||
expect(logger.info).toHaveBeenCalledWith('- [https://github.com/ziacik/nx-tools/issues/1](https://github.com/ziacik/nx-tools/issues/1)'); | ||
expect(logger.info).not.toHaveBeenCalledWith('- https://github.com/ziacik/nx-tools/issues/12'); | ||
expect(logger.info).not.toHaveBeenCalledWith('- https://github.com/ziacik/nx-tools/issues/135'); | ||
expect(logger.info).toHaveBeenCalledTimes(2); | ||
expect(output.success).toBe(true); | ||
}); | ||
}); | ||
|
||
function createContext(): ExecutorContext { | ||
return { | ||
root: '/some/root', | ||
cwd: '.', | ||
isVerbose: false, | ||
projectName: 'my-project', | ||
}; | ||
} |
70 changes: 70 additions & 0 deletions
70
packages/upgrade-verify/src/executors/check-issues/executor.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { ExecutorContext, logger } from '@nx/devkit'; | ||
import { readFile } from 'fs/promises'; | ||
import { join } from 'path'; | ||
import { CheckIssuesExecutorSchema } from './schema'; | ||
|
||
const GITHUB_ISSUE_REGEX = /https:\/\/github.com\/(?<owner>[^/]+)\/(?<repo>[^/]+)\/issues\/(?<issueNumber>\d+)/; | ||
|
||
export default async function runExecutor(options: CheckIssuesExecutorSchema, context: ExecutorContext) { | ||
const issuesMd = await tryLoadIssuesMd(context.root); | ||
|
||
if (issuesMd != null) { | ||
const issueMdLines = issuesMd.split('\n'); | ||
await printClosedIssueLines(issueMdLines); | ||
} else { | ||
logger.info("There is no 'ISSUES.md' file in the workspace root."); | ||
} | ||
|
||
return { | ||
success: true, | ||
}; | ||
} | ||
|
||
async function printClosedIssueLines(issueMdLines: string[]) { | ||
const resolvedLines = await Promise.all(issueMdLines.map(resolveIssueMdLine)); | ||
const closedLines = resolvedLines.filter((resolvedLine) => resolvedLine.result === 'closed'); | ||
|
||
if (closedLines.length > 0) { | ||
logger.info('Issues which are closed now:'); | ||
|
||
for (const closedLine of closedLines) { | ||
logger.info(`- ${closedLine.issueMdLine}`); | ||
} | ||
} else { | ||
logger.info('No issues have been closed.'); | ||
} | ||
} | ||
|
||
async function resolveIssueMdLine(issueMdLine: string): Promise<{ issueMdLine: string; result: 'noissue' | 'active' | 'closed' }> { | ||
const githubLink = issueMdLine.match(GITHUB_ISSUE_REGEX); | ||
|
||
if (!githubLink) { | ||
return { issueMdLine, result: 'noissue' }; | ||
} | ||
|
||
const [, owner, repo, issueNumber] = githubLink; | ||
|
||
const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/issues/${issueNumber}`); | ||
const { state } = await response.json(); | ||
|
||
return { | ||
issueMdLine, | ||
result: state === 'closed' ? 'closed' : 'active', | ||
}; | ||
} | ||
|
||
async function tryLoadIssuesMd(root: string): Promise<string | undefined> { | ||
try { | ||
return await readFile(join(root, 'ISSUES.md'), 'utf8'); | ||
} catch (e) { | ||
if (isNodeError(e) && e.code === 'ENOENT') { | ||
return undefined; | ||
} else { | ||
throw e; | ||
} | ||
} | ||
} | ||
|
||
function isNodeError(error: unknown): error is NodeJS.ErrnoException { | ||
return error instanceof Error && 'code' in error; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export interface CheckIssuesExecutorSchema {} // eslint-disable-line |
9 changes: 9 additions & 0 deletions
9
packages/upgrade-verify/src/executors/check-issues/schema.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"$schema": "http://json-schema.org/schema", | ||
"version": 2, | ||
"title": "CheckIssues executor", | ||
"description": "", | ||
"type": "object", | ||
"properties": {}, | ||
"required": [] | ||
} |