Skip to content

Commit

Permalink
feat: support nodejs org folder on post-release (#878)
Browse files Browse the repository at this point in the history
* feat: support nodejs org folder on post-release

* fix: adjust pre-release to use updateWebsiteBanner
  • Loading branch information
RafaelGSS authored Dec 3, 2024
1 parent eddb205 commit 5fe1456
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 38 deletions.
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

0 comments on commit 5fe1456

Please sign in to comment.