build(docs-infra): compute previous version list (#39689)

Previously we hand coded the list of previous major versions
that are displayed in the left navigation.

Now these are generated from the tags in GitHub.

Closes #39688

PR Close #39689
This commit is contained in:
Pete Bacon Darwin 2020-11-14 13:02:40 +00:00 committed by atscott
parent 7d68f92315
commit e4028ae5c4
5 changed files with 130 additions and 39 deletions

View File

@ -1060,35 +1060,5 @@
}
]
}
],
"docVersions": [
{
"title": "v9",
"url": "https://v9.angular.io/"
},
{
"title": "v8",
"url": "https://v8.angular.io/"
},
{
"title": "v7",
"url": "https://v7.angular.io/"
},
{
"title": "v6",
"url": "https://v6.angular.io/"
},
{
"title": "v5",
"url": "https://v5.angular.io/"
},
{
"title": "v4",
"url": "https://v4.angular.io/"
},
{
"title": "v2",
"url": "https://v2.angular.io/"
}
]
}

View File

@ -8,6 +8,7 @@
const path = require('path');
const Package = require('dgeni').Package;
const gitPackage = require('dgeni-packages/git');
const jsdocPackage = require('dgeni-packages/jsdoc');
const nunjucksPackage = require('dgeni-packages/nunjucks');
const linksPackage = require('../links-package');
@ -19,7 +20,7 @@ const postProcessPackage = require('dgeni-packages/post-process-html');
const { PROJECT_ROOT, CONTENTS_PATH, OUTPUT_PATH, DOCS_OUTPUT_PATH, TEMPLATES_PATH, AIO_PATH, requireFolder } = require('../config');
module.exports = new Package('angular-base', [
jsdocPackage, nunjucksPackage, linksPackage, examplesPackage, targetPackage, remarkPackage, postProcessPackage
gitPackage, jsdocPackage, nunjucksPackage, linksPackage, examplesPackage, targetPackage, remarkPackage, postProcessPackage
])
// Register the processors
@ -37,6 +38,7 @@ module.exports = new Package('angular-base', [
.factory(require('./readers/json'))
.factory(require('./services/copyFolder'))
.factory(require('./services/getImageDimensions'))
.factory(require('./services/getPreviousMajorVersions'))
.factory(require('./services/auto-link-filters/filterPipes'))
.factory(require('./services/auto-link-filters/filterAmbiguousDirectiveAliases'))
.factory(require('./services/auto-link-filters/ignoreHttpInUrls'))

View File

@ -0,0 +1,54 @@
'use strict';
const child = require('child_process');
const semver = require('semver');
const versionMatcher = /refs\/tags\/(\d+.+)$/mg;
/**
* Get a collection of all the previous "last major" versions sorted by semantic version.
*
* @param packageInfo injected from dgeni-packages/git
* @param versionInfo injected from dgeni-packages/git
* @returns an array of SemVer objects
*/
module.exports = function getPreviousMajorVersions(packageInfo, versionInfo) {
return () => {
// always use the remote tags as the local clone might not contain all commits when cloned with
// `git clone --depth=...`
const repoUrl = packageInfo.repository.url;
const tagResults = child.spawnSync('git', ['ls-remote', '--tags', repoUrl], {encoding: 'utf8'});
if (tagResults.status !== 0) {
return [];
}
const majorVersions = {};
tagResults.stdout.replace(versionMatcher, (_, tag) => {
const version = semver.parse(tag);
// Not interested in tags that do not match semver format.
if (version === null) {
return;
}
// Not interested in pre-release versions.
if (version.prerelease !== null && version.prerelease.length > 0) {
return;
}
// Only interested in versions that are earlier than the current major.
if (version.major >= versionInfo.currentVersion.major) {
return;
}
const currentMajor = majorVersions[version.major];
if (currentMajor === undefined || semver.compare(version, currentMajor) === 1) {
// This version is newer than the currently captured version for this major.
majorVersions[version.major] = version;
}
});
// Sort them in descending order
return semver.sort(Object.values(majorVersions)).reverse();
};
};

View File

@ -0,0 +1,63 @@
const child = require('child_process');
const Dgeni = require('dgeni');
const semver = require('semver');
const basePackage = require('../index');
describe('getPreviousMajorVersions', () => {
let getPreviousMajorVersions;
beforeEach(() => {
const mockPackage = new Dgeni.Package('mock-package', [basePackage])
.factory('versionInfo', mockVersionInfo)
.factory('packageInfo', mockPackageInfo);
const dgeni = new Dgeni([mockPackage]);
const injector = dgeni.configureInjector();
getPreviousMajorVersions = injector.get('getPreviousMajorVersions');
});
it('should spawn a child process to git', () => {
spyOn(child, 'spawnSync').and.returnValue({status: 0, stdout: ''});
getPreviousMajorVersions();
expect(child.spawnSync).toHaveBeenCalledWith('git', ['ls-remote', '--tags', 'SOME_GIT_URL'], {
encoding: 'utf8'
});
});
it('should return an empty list for a failed git command', () => {
spyOn(child, 'spawnSync').and.returnValue({status: 1});
expect(getPreviousMajorVersions()).toEqual([]);
});
it('should return an empty list for no tags', () => {
spyOn(child, 'spawnSync').and.returnValue({status: 0, stdout: ''});
expect(getPreviousMajorVersions()).toEqual([]);
});
it('should return an array of latest major versions with major greater than current', () => {
spyOn(child, 'spawnSync').and.returnValue({
status: 0,
stdout: `
refs/pull/655
refs/tags/some-tag
refs/tags/3.8.1
refs/tags/4.2.9
refs/tags/4.2.10
refs/tags/5.6.1
refs/tags/6.1.1
`
});
expect(getPreviousMajorVersions()).toEqual([
semver('4.2.10'),
semver('3.8.1'),
]);
});
});
function mockVersionInfo() {
return {currentVersion: new semver('5.1.0')};
}
function mockPackageInfo() {
return {repository: {url: 'SOME_GIT_URL'}};
}

View File

@ -1,9 +1,8 @@
module.exports = function processNavigationMap(versionInfo, log) {
module.exports = function processNavigationMap(versionInfo, getPreviousMajorVersions, log) {
return {
$runAfter: ['paths-computed'],
$runBefore: ['rendering-docs'],
$process: function(docs) {
const navigationDoc = docs.find(doc => doc.docType === 'navigation-json');
if (!navigationDoc) {
@ -24,6 +23,9 @@ module.exports = function processNavigationMap(versionInfo, log) {
throw new Error('processNavigationMap failed');
}
navigationDoc.data['docVersions'] = getPreviousMajorVersions().map(
v => ({title: `v${v.major}`, url: `https://v${v.major}.angular.io/`}));
// Add in the version data in a "secret" field to be extracted in the docs app
navigationDoc.data['__versionInfo'] = versionInfo.currentVersion;
}
@ -32,13 +34,13 @@ module.exports = function processNavigationMap(versionInfo, log) {
function walk(node, map, path) {
let errors = [];
for(const key in node) {
for (const key in node) {
const child = node[key];
if (child !== null) { // null is allowed
if (key === 'url') {
const url = child.replace(/#.*$/, ''); // strip hash
if (isRelative(url) && !map[url]) {
errors.push({ path: path.join('.'), url });
errors.push({path: path.join('.'), url});
}
} else if (typeof child !== 'string') {
errors = errors.concat(walk(child, map, path.concat([key])));