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

feat: support nodejs org folder on post-release #878

Merged
merged 2 commits into from
Dec 3, 2024
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
11 changes: 6 additions & 5 deletions components/git/security.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ const securityOptions = {
type: 'boolean'
},
'post-release': {
describe: 'Create the post-release announcement',
type: 'boolean'
describe: 'Create the post-release announcement to the given nodejs.org folder',
type: 'string'
},
cleanup: {
describe: 'cleanup the security release.',
Expand Down Expand Up @@ -83,7 +83,7 @@ export function builder(yargs) {
'Request CVEs for a security release of Node.js based on' +
' the next-security-release/vulnerabilities.json'
).example(
'git node security --post-release',
'git node security --post-release="../nodejs.org/"',
'Create the post-release announcement on the Nodejs.org repo'
).example(
'git node security --cleanup',
Expand Down Expand Up @@ -164,11 +164,12 @@ async function requestCVEs() {
return hackerOneCve.requestCVEs();
}

async function createPostRelease() {
async function createPostRelease(argv) {
const nodejsOrgFolder = argv['post-release'];
const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
const cli = new CLI(logStream);
const blog = new SecurityBlog(cli);
return blog.createPostRelease();
return blog.createPostRelease(nodejsOrgFolder);
}

async function startSecurityRelease() {
Expand Down
9 changes: 9 additions & 0 deletions docs/git-node.md
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,15 @@ Example:
git node security --pre-release="/path/to/nodejs.org"
```

### `git node security --post-release`

This command creates the post-release announcement for the security release.
Example:

```sh
git node security --post-release="/path/to/nodejs.org"
```

### `git node security --add-report=report-id`

This command adds a HackerOne report to the `vulnerabilities.json`.
Expand Down
84 changes: 51 additions & 33 deletions lib/security_blog.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import {
import auth from './auth.js';
import Request from './request.js';

const kChanged = Symbol('changed');

export default class SecurityBlog extends SecurityRelease {
req;

Expand Down Expand Up @@ -54,24 +52,23 @@ export default class SecurityBlog extends SecurityRelease {

const file = path.resolve(process.cwd(), nodejsOrgFolder, pathToBlogPosts, fileNameExt);
const site = path.resolve(process.cwd(), nodejsOrgFolder, pathToBannerJson);
const siteJson = JSON.parse(fs.readFileSync(site));

const endDate = new Date(data.annoucementDate);
endDate.setDate(endDate.getDate() + 7);
const capitalizedMonth = month[0].toUpperCase() + month.slice(1);
siteJson.websiteBanners.index = {

this.updateWebsiteBanner(site, {
startDate: data.annoucementDate,
endDate: endDate.toISOString(),
text: `${capitalizedMonth} Security Release is available`,
text: `New security releases to be made available ${data.releaseDate}`,
link: `https://nodejs.org/en/blog/vulnerability/${fileName}`,
type: 'warning'
};
});

fs.writeFileSync(file, preRelease);
fs.writeFileSync(site, JSON.stringify(siteJson, null, 2));
cli.ok(`Announcement file created and banner has been updated. Folder: ${nodejsOrgFolder}`);
}

async createPostRelease() {
async createPostRelease(nodejsOrgFolder) {
const { cli } = this;
const credentials = await auth({
github: true,
Expand All @@ -84,7 +81,7 @@ export default class SecurityBlog extends SecurityRelease {
checkoutOnSecurityReleaseBranch(cli, this.repository);

// read vulnerabilities JSON file
const content = this.readVulnerabilitiesJSON(cli);
const content = this.readVulnerabilitiesJSON();
if (!content.releaseDate) {
cli.error('Release date is not set in vulnerabilities.json,' +
' run `git node security --update-date=YYYY/MM/DD` to set the release date.');
Expand All @@ -95,47 +92,54 @@ export default class SecurityBlog extends SecurityRelease {
const releaseDate = new Date(content.releaseDate);
const template = this.getSecurityPostReleaseTemplate();
const data = {
// TODO: read from pre-sec-release
annoucementDate: await this.getAnnouncementDate(cli),
annoucementDate: releaseDate.toISOString(),
releaseDate: this.formatReleaseDate(releaseDate),
affectedVersions: this.getAffectedVersions(content),
vulnerabilities: this.getVulnerabilities(content),
slug: this.getSlug(releaseDate),
author: await this.promptAuthor(cli),
author: 'The Node.js Project',
dependencyUpdates: content.dependencies
};
const postReleaseContent = await this.buildPostRelease(template, data, content);

const pathPreRelease = await this.promptExistingPreRelease(cli);
// read the existing pre-release announcement
let preReleaseContent = fs.readFileSync(pathPreRelease, 'utf-8');
const pathToBlogPosts = path.resolve(nodejsOrgFolder, 'apps/site/pages/en/blog/release');
const pathToBannerJson = path.resolve(nodejsOrgFolder, 'apps/site/site.json');

const preReleasePath = path.resolve(pathToBlogPosts, data.slug + '.md');
let preReleaseContent = this.findExistingPreRelease(preReleasePath);
if (!preReleaseContent) {
cli.error(`Existing pre-release not found! Path: ${preReleasePath} `);
process.exit(1);
}

const postReleaseContent = await this.buildPostRelease(template, data, content);
// cut the part before summary
const preSummary = preReleaseContent.indexOf('# Summary');
if (preSummary !== -1) {
preReleaseContent = preReleaseContent.substring(preSummary);
}

const updatedContent = postReleaseContent + preReleaseContent;

fs.writeFileSync(pathPreRelease, updatedContent);
cli.ok(`Post-release announcement file updated at ${pathPreRelease}`);
const endDate = new Date(data.annoucementDate);
endDate.setDate(endDate.getDate() + 7);
const month = releaseDate.toLocaleString('en-US', { month: 'long' });
const capitalizedMonth = month[0].toUpperCase() + month.slice(1);

// if the vulnerabilities.json has been changed, update the file
if (!content[kChanged]) return;
this.updateVulnerabilitiesJSON(content);
}
this.updateWebsiteBanner(pathToBannerJson, {
startDate: releaseDate,
endDate,
text: `${capitalizedMonth} Security Release is available`
});

async promptExistingPreRelease(cli) {
const pathPreRelease = await cli.prompt(
'Please provide the path of the existing pre-release announcement:', {
questionType: 'input',
defaultAnswer: ''
});
fs.writeFileSync(preReleasePath, updatedContent);
cli.ok(`Announcement file and banner has been updated. Folder: ${nodejsOrgFolder}`);
}

if (!pathPreRelease || !fs.existsSync(path.resolve(pathPreRelease))) {
return this.promptExistingPreRelease(cli);
findExistingPreRelease(filepath) {
if (!fs.existsSync(filepath)) {
return null;
}
return pathPreRelease;

return fs.readFileSync(filepath, 'utf-8');
}

promptAuthor(cli) {
Expand All @@ -146,6 +150,20 @@ export default class SecurityBlog extends SecurityRelease {
});
}

updateWebsiteBanner(siteJsonPath, content) {
const siteJson = JSON.parse(fs.readFileSync(siteJsonPath));

const currentValue = siteJson.websiteBanners.index;
siteJson.websiteBanners.index = {
startDate: content.startDate ?? currentValue.startDate,
endDate: content.endDate ?? currentValue.endDate,
text: content.text ?? currentValue.text,
link: content.link ?? currentValue.link,
type: content.type ?? currentValue.type
};
fs.writeFileSync(siteJsonPath, JSON.stringify(siteJson, null, 2));
}

formatReleaseDate(releaseDate) {
const options = {
weekday: 'long',
Expand Down
Loading