Replaced the environment dependency version resolution to use the Docker HTTP API

Since it's possible that the latest git tag isn't a Docker page we should get the latest version directly from Docker instead. This commit adds support for the `/v2/repositories/library/<image>/tags` endpoint and figures out the latest version from the response.
This commit is contained in:
Christopher Allford 2020-09-10 14:26:44 -07:00
parent 07bbf56ace
commit 2e7f1056e0
4 changed files with 218 additions and 9 deletions

View File

@ -5,7 +5,7 @@ if [[ $1 ]]; then
# Set environment variables for Docker
#
if ! [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
WP_VERSION=$(git ls-remote --tags https://github.com/wordpress/wordpress.git | cut -f3 -d/ | grep -E '^\d+\.\d+(.\d+)*$' | tail -n 1)
WP_VERSION=$(./bin/get-latest-docker-tag.js wordpress 5 2> /dev/null)
fi
if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
export WORDPRESS_VERSION=$WP_VERSION
@ -14,8 +14,7 @@ if [[ $1 ]]; then
fi
if ! [[ $TRAVIS_PHP_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
PVER=$(git ls-remote --tags https://github.com/php/php-src.git | cut -f3 -d/ | grep -E '^php-\d+\.\d+(.\d+)*$' | tail -n 1)
TRAVIS_PHP_VERSION=${PVER/php-/}
TRAVIS_PHP_VERSION=$(./bin/get-latest-docker-tag.js php 7 2> /dev/null)
fi
if [[ $TRAVIS_PHP_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
export DC_PHP_VERSION=$TRAVIS_PHP_VERSION
@ -24,8 +23,7 @@ if [[ $1 ]]; then
fi
if ! [[ $TRAVIS_MARIADB_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
MVER=$(git ls-remote --tags https://github.com/mariadb/server.git | cut -f3 -d/ | grep -E '^mariadb-\d\d\.\d+(.\d+)*$' | tail -n 1)
TRAVIS_MARIADB_VERSION=${MVER/mariadb-/}
TRAVIS_MARIADB_VERSION=$(./bin/get-latest-docker-tag.js mariadb 10 2> /dev/null)
fi
if [[ $TRAVIS_MARIADB_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
export DC_MARIADB_VERSION=$TRAVIS_MARIADB_VERSION

120
tests/e2e/env/bin/get-latest-docker-tag.js vendored Executable file
View File

@ -0,0 +1,120 @@
#!/usr/bin/env node
const https = require( 'https' );
const semver = require( 'semver' );
/**
* Fetches the latest tag from a page using the Docker HTTP api.
*
* @param {string} image The image we're looking at the tags for.
* @param {string} nameSearch The string we're searching for in the name.
* @param {number} page The page we want to request.
* @return {Promise} A promise resolving to the JSON content for the page.
*/
async function fetchLatestTagFromPage( image, nameSearch, page ) {
return new Promise(
( resolve, reject ) => {
const queryParams = new URLSearchParams( {
page_size: 100,
ordering: 'last_updated',
page: page,
name: nameSearch
} );
const options = {
hostname: 'hub.docker.com',
port: 443,
path: '/v2/repositories/library/' + image + '/tags?' + queryParams.toString(),
method: 'GET',
};
const req = https.request(options, (res) => {
let data = '';
res.on( 'data', d => { data += d; } );
res.on( 'end', () => {
data = JSON.parse( data );
if ( ! data.count ) {
reject( "No image '" + image + '" found' );
} else {
let latestTag = null;
for ( let tag of data.results ) {
tag.semver = tag.name.match( /^\d+\.\d+(.\d+)*$/ );
if ( ! tag.semver ) {
continue;
}
tag.semver = semver.coerce( tag.semver[0] );
if ( ! latestTag || semver.gt( tag.semver, latestTag.semver ) ) {
latestTag = tag;
}
}
resolve( {
latestTag,
isLastPage: ! data.next,
} );
}
} )
});
req.end();
}
)
}
/**
* Finds the latest version of the image that meets our name criteria.
*
* @param {string} image The image we're searching for.
* @param {string} nameSearch The name we're searching for.
* @return {Promise} A promise resolving to the latest version of a package.
*/
function findLatestVersion( image, nameSearch ) {
let page = 1;
let earliestUpdated = null;
let latestVersion = null;
// Repeat the requests until we've read as many pages as necessary.
const paginationFn = function ( result ) {
// We can save on unnecessarily loading every page by short-circuiting when
// the number of days between the first recorded version and the
// one from this page becomes excessive.
const lastUpdate = Date.parse( result.latestTag.last_updated );
if ( ! earliestUpdated ) {
earliestUpdated = lastUpdate;
} else {
const daysSinceEarliestUpdate = ( earliestUpdated - lastUpdate ) / ( 1000 * 3600 * 24 );
if ( daysSinceEarliestUpdate > 15 ) {
result.isLastPage = true;
}
}
if ( ! latestVersion || semver.gt( result.latestTag.semver, latestVersion ) ) {
latestVersion = result.latestTag.semver;
}
if ( ! result.isLastPage ) {
return fetchLatestTagFromPage( image, nameSearch, ++page ).then( paginationFn );
}
return latestVersion.toString();
};
return fetchLatestTagFromPage( image, nameSearch, page ).then( paginationFn );
}
const image = process.argv[2];
const nameSearch = process.argv[3];
if ( ! image || ! nameSearch ) {
console.error( 'Usage: get-latest-docker-tag.js <image> <name-search>' );
process.exit( 1 );
}
findLatestVersion( image, nameSearch ).then(
( latestVersion ) => {
console.info( latestVersion );
process.exit( 0 );
},
( error ) => {
console.error( error );
process.exit( 1 );
}
)

96
tests/e2e/env/package-lock.json generated vendored
View File

@ -97,6 +97,13 @@
"browserslist": "^4.12.0",
"invariant": "^2.2.4",
"semver": "^5.5.0"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
}
}
},
"@babel/core": {
@ -134,6 +141,11 @@
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
}
}
},
@ -174,6 +186,13 @@
"invariant": "^2.2.4",
"levenary": "^1.1.1",
"semver": "^5.5.0"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
}
}
},
"@babel/helper-create-class-features-plugin": {
@ -980,6 +999,13 @@
"invariant": "^2.2.2",
"levenary": "^1.1.1",
"semver": "^5.5.0"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
}
}
},
"@babel/preset-modules": {
@ -4585,6 +4611,13 @@
"semver": "^5.5.0",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
}
}
},
"crypto-random-string": {
@ -4923,6 +4956,13 @@
"react-is": "^16.13.1",
"react-test-renderer": "^16.0.0-0",
"semver": "^5.7.0"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
}
}
},
"enzyme-adapter-utils": {
@ -4936,6 +4976,13 @@
"object.fromentries": "^2.0.2",
"prop-types": "^15.7.2",
"semver": "^5.7.1"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
}
}
},
"enzyme-shallow-equal": {
@ -8693,6 +8740,13 @@
"requires": {
"pify": "^4.0.1",
"semver": "^5.6.0"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
}
}
},
"makeerror": {
@ -9049,6 +9103,11 @@
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
}
}
},
@ -9086,6 +9145,13 @@
"semver": "^5.5.0",
"shellwords": "^0.1.1",
"which": "^1.3.0"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
}
}
},
"node-pty": {
@ -9112,6 +9178,13 @@
"resolve": "^1.10.0",
"semver": "2 || 3 || 4 || 5",
"validate-npm-package-license": "^3.0.1"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
}
}
},
"normalize-path": {
@ -9382,6 +9455,14 @@
"registry-auth-token": "^3.0.1",
"registry-url": "^3.0.3",
"semver": "^5.1.0"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
}
}
},
"parent-module": {
@ -10220,9 +10301,10 @@
}
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
"version": "7.3.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
"integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
"dev": true
},
"semver-diff": {
"version": "2.1.0",
@ -10231,6 +10313,14 @@
"dev": true,
"requires": {
"semver": "^5.0.3"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
}
}
},
"set-blocking": {

View File

@ -36,7 +36,8 @@
"@babel/polyfill": "^7.8.3",
"@babel/preset-env": "^7.8.4",
"@wordpress/eslint-plugin": "^4.0.0",
"ndb": "^1.1.5"
"ndb": "^1.1.5",
"semver": "^7.3.2"
},
"publishConfig": {
"access": "public"