Skip to content

Commit

Permalink
docs(versions): provide version menu for navigating to versioned docs
Browse files Browse the repository at this point in the history
Refs angular#918 provides version menu (with customizable min-version cutoff)
that navigates to docs based on a simple string format.

This approach was based on that provided in the base angular.js repo,
and some of the code was ported from that repo.

Port version configuration fetching from main angular.js project

Version Configuration is gathered on doc generation

Remove debug statements

Show version list in docs menu

Only show 0.8.3 and greater in version list

Only generate urls for docs for 0.8.0 and greater

Filter based on docUrl instead of version number

Fix spacing per guidelines

Add accessibility to version dropdown

Add placeholder text instead of aria-label

Add more spaces before braces

Support CDNVersion

Make docs url and minimum version configurable

Navigate to a new URL when the version select changes
  • Loading branch information
Adam Pearson committed Mar 13, 2015
1 parent 5fab400 commit 503f5f8
Show file tree
Hide file tree
Showing 8 changed files with 272 additions and 4 deletions.
205 changes: 205 additions & 0 deletions config/version-info.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
'use strict';

var fs = require('fs');
var path = require('path');
var shell = require('shelljs');
var semver = require('semver');
var _ = require('lodash');

var currentPackage, previousVersions, cdnVersion, gitRepoInfo;


/**
* Load information about this project from the package.json
* @return {Object} The package information
*/
var getPackage = function() {
// Search up the folder hierarchy for the first package.json
var packageFolder = path.resolve('.');
while (!fs.existsSync(path.join(packageFolder, 'package.json'))) {
var parent = path.dirname(packageFolder);
if (parent === packageFolder) { break; }
packageFolder = parent;
}
return JSON.parse(fs.readFileSync(path.join(packageFolder,'package.json'), 'UTF-8'));
};


/**
* Parse the github URL for useful information
* @return {Object} An object containing the github owner and repository name
*/
var getGitRepoInfo = function() {
var GITURL_REGEX = /^https:\/\/github.com\/([^\/]+)\/(.+).git$/;
var match = GITURL_REGEX.exec(currentPackage.repository.url);
var git = {
owner: match[1],
repo: match[2]
};
return git;
};



/**
* Extract the code name from the tagged commit's message - it should contain the text of the form:
* "codename(some-code-name)"
* @param {String} tagName Name of the tag to look in for the codename
* @return {String} The codename if found, otherwise null/undefined
*/
var getCodeName = function(tagName) {
var gitCatOutput = shell.exec('git cat-file -p ' + tagName, {silent:true}).output;
var tagMessage = gitCatOutput.match(/^.*codename.*$/mg)[0];
var codeName = tagMessage && tagMessage.match(/codename\((.*)\)/)[1];
if (!codeName) {
throw new Error("Could not extract release code name. The message of tag " + tagName +
" must match '*codename(some release name)*'");
}
return codeName;
};


/**
* Compute a build segment for the version, from the Jenkins build number and current commit SHA
* @return {String} The build segment of the version
*/
function getBuild() {
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
return 'sha.' + hash;
}


/**
* If the current commit is tagged as a version get that version
* @return {SemVer} The version or null
*/
var getTaggedVersion = function() {
var gitTagResult = shell.exec('git describe --exact-match', {silent:true});

if (gitTagResult.code === 0) {
var tag = gitTagResult.output.trim();
var version = semver.parse(tag);

if (version && semver.satisfies(version, currentPackage.branchVersion)) {
version.codeName = getCodeName(tag);
version.full = version.version;
version.branch = 'v' + currentPackage.branchPattern.replace('*', 'x');
return version;
}
}

return null;
};

/**
* Get a collection of all the previous versions sorted by semantic version
* @return {Array.<SemVer>} The collection of previous versions
*/
var getPreviousVersions = function() {
// always use the remote tags as the local clone might
// not contain all commits when cloned with git clone --depth=...
// Needed e.g. for Travis
var repo_url = currentPackage.repository.url;
var tagResults = shell.exec('git ls-remote --tags ' + repo_url,
{silent: true});

// default document location template
var docDefaultUrlTemplate = 'http://code.angularjs.org/material/:version/docs';
var docMinimumVersionRequirement = '>=0.8.0';

if (tagResults.code === 0) {
return _(tagResults.output.match(/v[0-9].*[0-9]$/mg))
.map(function(tag) {
var version = semver.parse(tag);
return version;
})
.filter()
.map(function(version) {
version.docsUrl = null;
if(semver.satisfies(version, docMinimumVersionRequirement)) {

version.docsUrl = docDefaultUrlTemplate.replace(":version", version.version)
}
return version;
})
.sort(semver.compare)
.value();
} else {
return [];
}
};

var getCdnVersion = function() {
return _(previousVersions)
.filter(function(tag) {
return semver.satisfies(tag, currentPackage.branchVersion);
})
.reverse()
.reduce(function(cdnVersion, version) {
if (!cdnVersion) {
// Note: need to use shell.exec and curl here
// as version-infos returns its result synchronously...
var cdnResult = shell.exec('curl https://ajax.googleapis.com/ajax/libs/angular_material/' + version + '/angular-material.min.js ' +
'--head --write-out "%{http_code}" -o /dev/null -silent',
{silent: true});
if (cdnResult.code === 0) {
var statusCode = cdnResult.output.trim();
if (statusCode === '200') {
cdnVersion = version;
}
}
}
return cdnVersion;
}, null);
};

/**
* Get the unstable snapshot version
* @return {SemVer} The snapshot version
*/
var getSnapshotVersion = function() {
var version = _(previousVersions)
.filter(function(tag) {
return semver.satisfies(tag, currentPackage.branchVersion);
})
.last();

if (!version) {
// a snapshot version before the first tag on the branch
version = semver(currentPackage.branchPattern.replace('*','0-beta.1'));
}

// We need to clone to ensure that we are not modifying another version
version = semver(version.raw);

var jenkinsBuild = process.env.TRAVIS_BUILD_NUMBER || process.env.BUILD_NUMBER;
if (!version.prerelease || !version.prerelease.length) {
// last release was a non beta release. Increment the patch level to
// indicate the next release that we will be doing.
// E.g. last release was 1.3.0, then the snapshot will be
// 1.3.1-build.1, which is lesser than 1.3.1 accorind the semver!

// If the last release was a beta release we don't update the
// beta number by purpose, as otherwise the semver comparison
// does not work any more when the next beta is released.
// E.g. don't generate 1.3.0-beta.2.build.1
// as this is bigger than 1.3.0-beta.2 according to semver
version.patch++;
}
version.prerelease = jenkinsBuild ? ['build', jenkinsBuild] : ['local'];
version.build = getBuild();
version.codeName = 'snapshot';
version.isSnapshot = true;
version.format();
version.full = version.version + '+' + version.build;
version.branch = 'master';

return version;
};


exports.currentPackage = currentPackage = getPackage();
exports.gitRepoInfo = gitRepoInfo = getGitRepoInfo();
exports.previousVersions = previousVersions = getPreviousVersions();
exports.cdnVersion = cdnVersion = getCdnVersion();
exports.currentVersion = getTaggedVersion() || getSnapshotVersion();
14 changes: 14 additions & 0 deletions docs/app/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ code:not(.highlight) {
.docs-menu > li {
border-top: 1px solid rgba(0, 0, 0, 0.12);
}
.docs-menu > li:nth-child(-n+2) {
border-top: none;
}

.docs-menu .md-button {
border-radius: 0;
color: inherit;
Expand All @@ -176,6 +180,16 @@ code:not(.highlight) {
white-space: normal;
width: 100%;
}

.docs-menu md-select {
margin-top: .5em;
width: 100%;
}

.docs-menu md-select md-select-label {
justify-content: flex-end
}

.docs-menu a.md-button {
display: block;
}
Expand Down
27 changes: 25 additions & 2 deletions docs/app/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -349,18 +349,20 @@ function(SERVICES, COMPONENTS, DEMOS, PAGES, $location, $rootScope) {
'$scope',
'COMPONENTS',
'BUILDCONFIG',
'VERSIONCONFIG',
'$mdSidenav',
'$timeout',
'$mdDialog',
'menu',
'$location',
'$rootScope',
'$log',
function($scope, COMPONENTS, BUILDCONFIG, $mdSidenav, $timeout, $mdDialog, menu, $location, $rootScope, $log) {
function($scope, COMPONENTS, BUILDCONFIG, VERSIONCONFIG, $mdSidenav, $timeout, $mdDialog, menu, $location, $rootScope, $log) {
var self = this;

$scope.COMPONENTS = COMPONENTS;
$scope.BUILDCONFIG = BUILDCONFIG;
$scope.VERSIONCONFIG = VERSIONCONFIG
$scope.menu = menu;

$scope.path = path;
Expand All @@ -378,7 +380,6 @@ function($scope, COMPONENTS, BUILDCONFIG, $mdSidenav, $timeout, $mdDialog, menu,
this.toggleOpen = toggleOpen;
this.autoFocusContent = false;


var mainContentArea = document.querySelector("[role='main']");

// *********************
Expand Down Expand Up @@ -450,6 +451,28 @@ function($scope, COMPONENTS, BUILDCONFIG, $mdSidenav, $timeout, $mdDialog, menu,
}
}])

.controller('DocsVersionsCtrl', [
'$scope',
'$location',
'$window',
'VERSIONCONFIG',
function($scope, $location, $window,VERSIONCONFIG) {
$scope.version = VERSIONCONFIG.currentVersion;
$scope.versions = VERSIONCONFIG.previousVersions.reverse();
$scope.versions.unshift(VERSIONCONFIG.currentVersion);

$scope.availableDocumentationVersions = function(element) {
return element.docsUrl != null || element.isSnapshot;
};

$scope.jumpToDocsVersion = function(version) {
var currentPagePath = $location.path().replace(/\/$/, '');

$window.location = version.docsUrl + currentPagePath;
};

}])

.controller('HomeCtrl', [
'$scope',
'$rootScope',
Expand Down
2 changes: 1 addition & 1 deletion docs/config/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
var _ = require('lodash');
var path = require('canonical-path');
var buildConfig = require('../../config/build.config');

var projectPath = path.resolve(__dirname, '../..');
var packagePath = __dirname;

Expand All @@ -15,6 +14,7 @@ module.exports = new Package('angular-md', [
.processor(require('./processors/componentsData'))
.processor(require('./processors/indexPage'))
.processor(require('./processors/buildConfig'))
.processor(require('./processors/versionConfig'))
.processor(require('./processors/content'))

.config(function(log, templateEngine, templateFinder) {
Expand Down
16 changes: 16 additions & 0 deletions docs/config/processors/versionConfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
var versionConfig = require("../../../config/version-info")
module.exports = function versionConfigProcessor(log) {
return {
$runBefore: ['rendering-docs'],
$runAfter: ['indexPageProcessor'],
$process: process
};

function process(docs) {
docs.push({
template: 'version-config.js',
outputPath: 'js/version-config.js',
versionConfig: versionConfig
});
}
}
5 changes: 5 additions & 0 deletions docs/config/template/index.template.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ <h1 class="md-toolbar-tools">

<md-content flex role="navigation">
<ul class="docs-menu">
<li ng-controller="DocsVersionsCtrl">
<md-select ng-model="version" ng-change="jumpToDocsVersion(version)" placeholder="Version">
<md-option ng-value="v" ng-repeat="v in versions | filter: availableDocumentationVersions">v{{v.version}}</md-option>
</md-select>
</li>
<li ng-repeat="section in menu.sections" class="parent-list-item" ng-class="{'parentActive' : isSectionSelected(section)}">
<h2 class="menu-heading" ng-if="section.type === 'heading'" id="heading_{{ section.name | nospace }}">
{{section.name}}
Expand Down
1 change: 1 addition & 0 deletions docs/config/template/version-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DocsApp.constant('VERSIONCONFIG', {$ doc.versionConfig | json $});
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
{
"name": "angular-material",
"version": "0.8.3",
"branchVersion": "0.8.3",
"branchPattern": "0.8.*",
"repository": {
"url": "git://github.com/angular/material.git"
"url": "https://github.com/angular/material.git"
},
"devDependencies": {
"canonical-path": "0.0.2",
Expand Down Expand Up @@ -41,6 +43,8 @@
"minimist": "^0.1.0",
"mkdirp": "^0.5.0",
"q": "^1.0.1",
"semver": "~4.0.3",
"shelljs": "~0.3.0",
"stream-series": "^0.1.1",
"through2": "^0.6.3"
}
Expand Down

0 comments on commit 503f5f8

Please sign in to comment.