Skip to content

Commit

Permalink
docs: Update release process (#16036)
Browse files Browse the repository at this point in the history
* docs: Update release process

* Removed dateformat dependency

* Ensure blog post is published

* Update templates/formatter-examples.md.ejs

Co-authored-by: Brandon Mills <btmills@users.noreply.github.com>

Co-authored-by: Brandon Mills <btmills@users.noreply.github.com>
  • Loading branch information
nzakas and btmills committed Jun 23, 2022
1 parent 48904fb commit b8e68c1
Show file tree
Hide file tree
Showing 8 changed files with 845 additions and 186 deletions.
230 changes: 48 additions & 182 deletions Makefile.js
Expand Up @@ -12,7 +12,6 @@

const checker = require("npm-license"),
ReleaseOps = require("eslint-release"),
dateformat = require("dateformat"),
fs = require("fs"),
glob = require("glob"),
marked = require("marked"),
Expand All @@ -33,7 +32,7 @@ require("shelljs/make");
* @see https://github.com/shelljs/shelljs/blob/124d3349af42cb794ae8f78fc9b0b538109f7ca7/make.js#L4
* @see https://github.com/DefinitelyTyped/DefinitelyTyped/blob/3aa2d09b6408380598cfb802743b07e1edb725f3/types/shelljs/make.d.ts#L8-L11
*/
const { cat, cd, cp, echo, exec, exit, find, ls, mkdir, pwd, rm, test } = require("shelljs");
const { cat, cd, echo, exec, exit, find, ls, mkdir, pwd, test } = require("shelljs");

//------------------------------------------------------------------------------
// Settings
Expand All @@ -60,8 +59,10 @@ const NODE = "node ", // intentional extra space
TEMP_DIR = "./tmp/",
DEBUG_DIR = "./debug/",
BUILD_DIR = "build",
DOCS_DIR = "../website/docs",
SITE_DIR = "../website/",
SITE_DIR = "../eslint.org",
DOCS_DIR = "./docs",
DOCS_SRC_DIR = path.join(DOCS_DIR, "src"),
DOCS_DATA_DIR = path.join(DOCS_SRC_DIR, "_data"),
PERF_TMP_DIR = path.join(TEMP_DIR, "eslint", "performance"),

// Utilities - intentional extra space at the end of each string
Expand Down Expand Up @@ -144,32 +145,27 @@ function generateBlogPost(releaseInfo, prereleaseMajorVersion) {
now = new Date(),
month = now.getMonth() + 1,
day = now.getDate(),
filename = `../website/_posts/${now.getFullYear()}-${
filename = path.join(SITE_DIR, `src/content/blog/${now.getFullYear()}-${
month < 10 ? `0${month}` : month}-${
day < 10 ? `0${day}` : day}-eslint-v${
releaseInfo.version}-released.md`;
releaseInfo.version}-released.md`);

output.to(filename);
}

/**
* Generates a doc page with formatter result examples
* @param {Object} formatterInfo Linting results from each formatter
* @param {string} [prereleaseVersion] The version used for a prerelease. This
* changes where the output is stored.
* @returns {void}
*/
function generateFormatterExamples(formatterInfo, prereleaseVersion) {
function generateFormatterExamples(formatterInfo) {
const output = ejs.render(cat("./templates/formatter-examples.md.ejs"), formatterInfo);
let filename = "../website/docs/user-guide/formatters/index.md",
htmlFilename = "../website/docs/user-guide/formatters/html-formatter-example.html";

if (prereleaseVersion) {
filename = filename.replace("/docs", `/docs/${prereleaseVersion}`);
htmlFilename = htmlFilename.replace("/docs", `/docs/${prereleaseVersion}`);
if (!test("-d", path.dirname(filename))) {
mkdir(path.dirname(filename));
}
const outputDir = path.join(DOCS_SRC_DIR, "user-guide/formatters/"),
filename = path.join(outputDir, "index.md"),
htmlFilename = path.join(outputDir, "html-formatter-example.html");

if (!test("-d", outputDir)) {
mkdir(outputDir);
}

output.to(filename);
Expand All @@ -181,9 +177,8 @@ function generateFormatterExamples(formatterInfo, prereleaseVersion) {
* @returns {void}
*/
function generateRuleIndexPage() {
const legacyWebsiteOutputFile = "../website/_data/rules.yml",
docsSiteOutputFile = "docs/src/_data/rules.json",
docsSiteMetaOutputFile = "docs/src/_data/rules_meta.json",
const docsSiteOutputFile = path.join(DOCS_DATA_DIR, "rules.json"),
docsSiteMetaOutputFile = path.join(DOCS_DATA_DIR, "rules_meta.json"),
ruleTypes = "conf/rule-type-list.json",
ruleTypesData = JSON.parse(cat(path.resolve(ruleTypes)));

Expand Down Expand Up @@ -240,9 +235,6 @@ function generateRuleIndexPage() {
JSON.stringify(ruleTypesData, null, 4).to(docsSiteOutputFile);
JSON.stringify(meta, null, 4).to(docsSiteMetaOutputFile);

const legacyOutput = yaml.dump(ruleTypesData, { sortKeys: true });

legacyOutput.to(legacyWebsiteOutputFile);
}

/**
Expand All @@ -256,26 +248,22 @@ function commitSiteToGit(tag) {

cd(SITE_DIR);
exec("git add -A .");
exec(`git commit -m "Autogenerated new docs and demo at ${dateformat(new Date())}"`);

if (tag) {
exec(`git tag ${tag}`);
}

exec("git fetch origin && git rebase origin/master");
exec(`git commit -m "Added release blog post for ${tag}"`);
exec(`git tag ${tag}`);
exec("git fetch origin && git rebase origin/main");
cd(currentDir);
}

/**
* Publishes the changes in an adjacent `website` repository to the remote. The
* Publishes the changes in an adjacent `eslint.org` repository to the remote. The
* site should already have local commits (e.g. from running `commitSiteToGit`).
* @returns {void}
*/
function publishSite() {
const currentDir = pwd();

cd(SITE_DIR);
exec("git push origin master --tags");
exec("git push origin HEAD --tags");
cd(currentDir);
}

Expand All @@ -294,7 +282,7 @@ function generateRelease() {
commitSiteToGit(`v${releaseInfo.version}`);

echo("Updating commit with docs data");
exec("git add docs/src/_data && git commit --amend --no-edit");
exec("git add docs/ && git commit --amend --no-edit");
exec(`git tag -a -f v${releaseInfo.version} -m ${releaseInfo.version}`);
}

Expand Down Expand Up @@ -636,60 +624,14 @@ target.test = function() {
target.checkLicenses();
};

target.gensite = function(prereleaseVersion) {
echo("Generating eslint.org");

let docFiles = [
"/rules/",
"/user-guide/",
"/maintainer-guide/",
"/developer-guide/",
"/about/"
];

// append version
if (prereleaseVersion) {
docFiles = docFiles.map(docFile => `/${prereleaseVersion}${docFile}`);
}

// 1. create temp and build directory
echo("> Creating a temporary directory (Step 1)");
if (!test("-d", TEMP_DIR)) {
mkdir(TEMP_DIR);
}

// 2. remove old files from the site
echo("> Removing old files (Step 2)");
docFiles.forEach(filePath => {
const fullPath = path.join(DOCS_DIR, filePath),
htmlFullPath = fullPath.replace(".md", ".html");

if (test("-f", fullPath)) {
rm("-rf", fullPath);
target.gensite = function() {
echo("Generating documentation");

if (filePath.includes(".md") && test("-f", htmlFullPath)) {
rm("-rf", htmlFullPath);
}
}
});

// 3. Copy docs folder to a temporary directory
echo("> Copying the docs folder (Step 3)");
docFiles.forEach(filePath => {
const pathToCopy = path.join("docs/src", filePath, "*"),
tempPath = path.join(TEMP_DIR, filePath);

if (!test("-d", tempPath)) {
mkdir(tempPath);
}

cp("-rf", pathToCopy, tempPath);
});
const DOCS_RULES_DIR = path.join(DOCS_SRC_DIR, "rules");
const RULE_VERSIONS_FILE = path.join(DOCS_SRC_DIR, "_data/rule_versions.json");

// special case (for now)
rm("-rf", path.join(TEMP_DIR, "pages"));

let versions = test("-f", "./versions.json") ? JSON.parse(cat("./versions.json")) : {};
// Set up rule version information
let versions = test("-f", RULE_VERSIONS_FILE) ? JSON.parse(cat(RULE_VERSIONS_FILE)) : {};

if (!versions.added) {
versions = {
Expand All @@ -698,117 +640,41 @@ target.gensite = function(prereleaseVersion) {
};
}

const { Linter } = require(".");
const rules = new Linter().getRules();

const RECOMMENDED_TEXT = "\n\n(recommended) The `\"extends\": \"eslint:recommended\"` property in a configuration file enables this rule.";
const FIXABLE_TEXT = "\n\n(fixable) The `--fix` option on the [command line](../user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.";
const HAS_SUGGESTIONS_TEXT = "\n\n(hasSuggestions) Some problems reported by this rule are manually fixable by editor [suggestions](../developer-guide/working-with-rules#providing-suggestions).";

// 4. Loop through all files in temporary directory
process.stdout.write("> Updating files (Steps 4-9): 0/... - ...\r");
const tempFiles = find(TEMP_DIR);
const length = tempFiles.length;
// 1. Update rule meta data by checking rule docs - important to catch removed rules
echo("> Updating rule version meta data (Step 1)");
const ruleDocsFiles = find(DOCS_RULES_DIR);

tempFiles.forEach((filename, i) => {
ruleDocsFiles.forEach((filename, i) => {
if (test("-f", filename) && path.extname(filename) === ".md") {

const rulesUrl = "https://github.com/eslint/eslint/tree/HEAD/lib/rules/",
testsUrl = "https://github.com/eslint/eslint/tree/HEAD/tests/lib/rules/",
docsUrl = "https://github.com/eslint/eslint/tree/HEAD/docs/src/rules/",
baseName = path.basename(filename),
sourceBaseName = `${path.basename(filename, ".md")}.js`,
sourcePath = path.join("lib/rules", sourceBaseName),
ruleName = path.basename(filename, ".md"),
filePath = path.posix.join("docs", path.relative("tmp", filename));
let text = cat(filename);

process.stdout.write(`> Updating files (Steps 4-9): ${i}/${length} - ${filePath + " ".repeat(30)}\r`);

// 5. Prepend page title and layout variables at the top of rules
if (path.dirname(filename).includes("rules")) {

// Find out if the rule requires a special docs portion (e.g. if it is recommended and/or fixable)
const rule = rules.get(ruleName);
const isRecommended = rule && rule.meta.docs.recommended;
const isFixable = rule && rule.meta.fixable;
const hasSuggestions = rule && rule.meta.hasSuggestions;
echo(`> Updating rule version meta data (Step 1: ${i + 1}/${ruleDocsFiles.length}): ${filename}`);

text = text.replace("<!--FIXABLE-->", isFixable ? FIXABLE_TEXT : "")
.replace("<!--SUGGESTIONS-->", hasSuggestions ? HAS_SUGGESTIONS_TEXT : "")
.replace("<!--RECOMMENDED-->", isRecommended ? RECOMMENDED_TEXT : "");
const baseName = path.basename(filename, ".md"),
sourceBaseName = `${baseName}.js`,
sourcePath = path.join("lib/rules", sourceBaseName);

if (!versions.added[baseName]) {
versions.added[baseName] = getFirstVersionOfFile(sourcePath);
}

// 6. Remove .md extension for relative links and change README to empty string
text = text.replace(/\((?!https?:\/\/)([^)]*?)\.md(.*?)\)/gu, "($1$2)").replace("README.html", "");

// 7. Check if there's a trailing white line at the end of the file, if there isn't one, add it
if (!/\n$/u.test(text)) {
text = `${text}\n`;
if (!versions.removed[baseName] && !test("-f", sourcePath)) {
versions.removed[baseName] = getFirstVersionOfDeletion(sourcePath);
}

// 8. Append first version of ESLint rule was added at.
if (filename.includes("rules/")) {
if (!versions.added[baseName]) {
versions.added[baseName] = getFirstVersionOfFile(sourcePath);
}
const added = versions.added[baseName];

if (!versions.removed[baseName] && !test("-f", sourcePath)) {
versions.removed[baseName] = getFirstVersionOfDeletion(sourcePath);
}
const removed = versions.removed[baseName];

text += "\n## Version\n\n";
text += removed
? `This rule was introduced in ESLint ${added} and removed in ${removed}.\n`
: `This rule was introduced in ESLint ${added}.\n`;

text += "\n## Resources\n\n";
if (!removed) {
text += `* [Rule source](${rulesUrl}${sourceBaseName})\n`;
text += `* [Test source](${testsUrl}${sourceBaseName})\n`;
}
text += `* [Documentation source](${docsUrl}${baseName})\n`;
}

// 9. Update content of the file with changes
text.to(filename.replace("README.md", "index.md"));
}
});
JSON.stringify(versions).to("./versions.json");
echo(`> Updating files (Steps 4-9)${" ".repeat(50)}`);

// 10. Copy temporary directory to site's docs folder
echo("> Copying the temporary directory into the site's docs folder (Step 10)");
let outputDir = DOCS_DIR;

if (prereleaseVersion) {
outputDir += `/${prereleaseVersion}`;
if (!test("-d", outputDir)) {
mkdir(outputDir);
}
}
cp("-rf", `${TEMP_DIR}*`, outputDir);

// 11. Generate rules index page
if (prereleaseVersion) {
echo("> Skipping generating rules index page because this is a prerelease (Step 11)");
} else {
echo("> Generating the rules index page (Step 11)");
generateRuleIndexPage();
}
JSON.stringify(versions, null, 4).to(RULE_VERSIONS_FILE);

// 12. Delete temporary directory
echo("> Removing the temporary directory (Step 12)");
rm("-rf", TEMP_DIR);
// 2. Generate rules index page meta data
echo("> Generating the rules index page (Step 2)");
generateRuleIndexPage();

// 13. Create Example Formatter Output Page
echo("> Creating the formatter examples (Step 14)");
generateFormatterExamples(getFormatterResults(), prereleaseVersion);
// 3. Create Example Formatter Output Page
echo("> Creating the formatter examples (Step 3)");
generateFormatterExamples(getFormatterResults());

echo("Done generating eslint.org");
echo("Done generating documentation");
};

target.generateRuleIndexPage = generateRuleIndexPage;
Expand Down

0 comments on commit b8e68c1

Please sign in to comment.