Skip to content

Commit

Permalink
Set the last three digit of the AMP version to be the number of cherr…
Browse files Browse the repository at this point in the history
…y-picks in the branch (#27848)
  • Loading branch information
danielrozenberg authored Apr 24, 2020
1 parent fe8e475 commit b2b850d
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 36 deletions.
33 changes: 29 additions & 4 deletions build-system/common/git.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,12 +169,36 @@ function gitCommitterEmail() {
}

/**
* Returns the timestamp of the latest commit on the local branch.
* @return {number}
* Returns list of commit SHAs and their cherry-pick status from master.
*
* `git cherry <branch>` returns a list of commit SHAs. While the exact
* mechanism is too complicated for this comment (run `git help cherry` for a
* full explanation), the gist of it is that commits that were cherry-picked
* from <branch> are prefixed with '- ', and those that were not are prefixed
* with '+ '.
*
* @return {!Array<{sha: string, isCherryPick: boolean}>}
*/
function gitCherryMaster() {
return getStdout('git cherry master')
.trim()
.split('\n')
.map((line) => ({
isCherryPick: line.substring(0, 2) == '- ',
sha: line.substring(2),
}));
}

/**
* Returns (UTC) time of a commit on the local branch, in %y%m%d%H%M%S format.
*
* @param {string} ref a Git reference (commit SHA, branch name, etc.) for the
* commit to get the time of.
* @return {string}
*/
function gitCommitFormattedTime() {
function gitCommitFormattedTime(ref = 'HEAD') {
return getStdout(
'TZ=UTC git log -1 --pretty="%cd" --date=format-local:%y%m%d%H%M%S'
`TZ=UTC git log ${ref} -1 --pretty="%cd" --date=format-local:%y%m%d%H%M%S`
).trim();
}

Expand Down Expand Up @@ -211,6 +235,7 @@ function gitDiffPath(path, commit) {
module.exports = {
gitBranchCreationPoint,
gitBranchName,
gitCherryMaster,
gitCommitFormattedTime,
gitCommitHash,
gitCommitterEmail,
Expand Down
105 changes: 76 additions & 29 deletions build-system/compile/internal-version.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,50 +16,97 @@
'use strict';

const minimist = require('minimist');
const {gitCommitFormattedTime} = require('../common/git');
const {gitCherryMaster, gitCommitFormattedTime} = require('../common/git');

// Allow leading zeros in --version_override, e.g. 0000000000001
const argv = minimist(process.argv.slice(2), {
string: ['version_override'],
});

/**
* Generates the AMP version number.
*
* Version numbers are determined using the following algorithm:
* - Count the number (<X>) of cherry-picked commits on this branch that came
* from the `master` branch, until reaching `master` or the first commit that
* was added directly on this branch (if the current commit is on `master`'s
* commit history, or only contains new commits that are not cherry-picked
* from `master`, then <X> is 0).
* - Find the commit (<C>) before the last cherry-picked commit from the
* `master` branch (if the current branch is `master`, or otherwise in
* `master`'s commit history, then the current commit is <C>).
* - Find the commit time of <C> (<C>.time). Note that commit time might be
* different from author time! e.g., commit time might be the time that a PR
* was merged into `master`, or a commit was cherry-picked onto the branch;
* author time is when the original author of the commit ran the "git commit"
* command.
* - The version number is <C>.time.format("YYmmDDHHMM", "UTC") + <X> (the
* pseudo-code assumes standard `.strftime` formatting, and <X> gets
* zero-padded until it is three digits long).
* - The maximum number of cherry-picks in a single branch is 999. More than
* that will fail to build. This should never happen.
*
* Examples:
* 1. The version number of a release built from the HEAD commit on `master`,
* where that HEAD commit was committed on April 25, 2020 2:31 PM EDT would
* be `2004251831000`.
* - EDT is UTC-4, so the hour value changes from EDT's 14 to UTC's 18.
* - The last three digits are 000 as this commit is on `master`.
*
* 2. The version number of a release built from a local working branch (e.g.,
* on a developer's workstation) that was split off from a `master` commit
* from May 6, 2021 10:40 AM PDT and has multiple commits that exist only on
* local working branch would be `2105061840000`.
* - PDT is UTC-7, so the hour value changes from PDT's 10 to UTC's 17.
* - The last three digits are 000 as this commit is on a branch that was
* split from `master`, and does not have any cherry-picked commits.
*
* 3. For a release built from a local working branch that was split off from a
* `master` commit from November 9, 2021 11:48 PM PST, and then:
* - had one commit that was cherry-picked from `master`,
* - followed by two commits that were created directly on the branch, the
* last of which was commited on November 23, 2021 6:38 PM PST,
* - followed by twelve commits that were cherry-picked from `master`, then
* its version number would be `2111240238012`.
* - The latest twelve commits are cherry-picks from `master`, and the one
* before them is not, so our last three digits are set to 012.
* - PST is UTC-8, so the hour value changes from PST's 18 to UTC's 2, and
* one day is added.
*
* The version number can be manually overridden by passing --version_override
* to the `gulp build`/`gulp dist` command.
*
* @return {string} AMP version number (always 13 digits long)
*/
function getVersion() {
if (argv.version_override) {
const version = String(argv.version_override);
// #27579: What are allowed version strings...?
if (!/^\d{13}$/.test(version)) {
throw new Error('--version_override only accepts a 13-digit version');
}
return version;
} else {
// Generate a consistent version number by using the commit* time of the
// latest commit on the active branch as the twelve digits. The last,
// thirteenth digit defaults to 0, but can be changed by setting the
// --custom_version_mark flag to a different value. This should rarely be
// used by AMP release engineers.
//
// e.g., the version number of a clean (no uncommited changes) tree that was
// commited on August 1, 2018 at 14:31:11 EDT would be `1808011831110`
// (notice that due to timezone shift, the hour value changes from EDT's 14
// to UTC's 18. The last digit is the default value of 0 as
// --custom_version_mark was not set.)
//
// *Commit time is different from author time! Commit time is the time that
// the PR was merged into master; author time is when the author ran the
// "git commit" command.
const lastCommitFormattedTime = gitCommitFormattedTime();
let lastDigit = 0;
if (argv.custom_version_mark) {
lastDigit = parseInt(argv.custom_version_mark, 10);
if (isNaN(lastDigit) || lastDigit < 0 || lastDigit > 9) {
throw new Error(
`--custom_version_mark is set to ${argv.custom_version_mark}, ` +
'expected value between 0 and 9!'
);
}
}

let numberOfCherryPicks = 0;
const commitCherriesInfo = gitCherryMaster().reverse();
for (const {isCherryPick} of commitCherriesInfo) {
if (!isCherryPick) {
break;
}
return `${lastCommitFormattedTime}${lastDigit}`;
numberOfCherryPicks++;
}
if (numberOfCherryPicks > 999) {
throw new Error(
`This branch has ${numberOfCherryPicks} cherry-picks, which is more than 999, the maximum allowed number of cherry-picks!`
);
}

const lastCommitFormattedTime = gitCommitFormattedTime(
`HEAD~${numberOfCherryPicks}`
).slice(0, -2);

numberOfCherryPicks = String(numberOfCherryPicks).padStart(3, '0');
return `${lastCommitFormattedTime}${numberOfCherryPicks}`;
}

// Used to e.g. references the ads binary from the runtime to get version lock.
Expand Down
1 change: 0 additions & 1 deletion build-system/tasks/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ build.flags = {
core_runtime_only: ' Builds only the core runtime.',
coverage: ' Adds code coverage instrumentation to JS files using istanbul.',
version_override: ' Overrides the version written to AMP_CONFIG',
custom_version_mark: ' Set final digit (0-9) on auto-generated version',
watch: ' Watches for changes in files, re-builds when detected',
define_experiment_constant:
' Builds runtime with the EXPERIMENT constant set to true',
Expand Down
1 change: 0 additions & 1 deletion build-system/tasks/default-task.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ defaultTask.flags = {
disable_nailgun:
" Doesn't use nailgun to invoke closure compiler (much slower)",
version_override: ' Overrides the version written to AMP_CONFIG',
custom_version_mark: ' Set final digit (0-9) on auto-generated version',
host: ' Host to serve the project on. localhost by default.',
port: ' Port to serve the project on. 8000 by default.',
define_experiment_constant:
Expand Down
1 change: 0 additions & 1 deletion build-system/tasks/dist.js
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,6 @@ dist.flags = {
type: ' Points sourcemap to fetch files from the correct GitHub tag',
esm: ' Does not transpile down to ES5',
version_override: ' Override the version written to AMP_CONFIG',
custom_version_mark: ' Set final digit (0-9) on auto-generated version',
watch: ' Watches for changes in files, re-compiles when detected',
closure_concurrency: ' Sets the number of concurrent invocations of closure',
debug: ' Outputs the file contents during compilation lifecycles',
Expand Down

0 comments on commit b2b850d

Please sign in to comment.