forked from waja/action-debian-package
273 lines
8.5 KiB
JavaScript
273 lines
8.5 KiB
JavaScript
|
'use strict';
|
||
|
|
||
|
Object.defineProperty(exports, '__esModule', { value: true });
|
||
|
|
||
|
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
||
|
|
||
|
var axios = _interopDefault(require('axios'));
|
||
|
var camelcaseKeys = _interopDefault(require('camelcase-keys'));
|
||
|
var luxon = require('luxon');
|
||
|
var R = _interopDefault(require('ramda'));
|
||
|
var pino = _interopDefault(require('pino'));
|
||
|
|
||
|
/**
|
||
|
* Union type representing the architecture defined in part of an OCI image's
|
||
|
* manifest list.
|
||
|
*
|
||
|
* As specified in the Docker Manifest spec, any valid GOARCH values are valid
|
||
|
* image architecture values, and vice versa:
|
||
|
* > The platform object describes the platform which the image in the manifest
|
||
|
* > runs on. A full list of valid operating system and architecture values are
|
||
|
* > listed in the Go language documentation for $GOOS and $GOARCH
|
||
|
* @see https://docs.docker.com/registry/spec/manifest-v2-2/#manifest-list-field-descriptions
|
||
|
*/
|
||
|
|
||
|
(function (Architecture) {
|
||
|
Architecture["i386"] = "386";
|
||
|
Architecture["amd64"] = "amd64";
|
||
|
Architecture["arm"] = "arm";
|
||
|
Architecture["arm64"] = "arm64";
|
||
|
Architecture["mips"] = "mips";
|
||
|
Architecture["mips64"] = "mips64";
|
||
|
Architecture["mips64le"] = "mips64le";
|
||
|
Architecture["mipsle"] = "mipsle";
|
||
|
Architecture["ppc64"] = "ppc64";
|
||
|
Architecture["ppc64le"] = "ppc64le";
|
||
|
Architecture["s390x"] = "s390x";
|
||
|
Architecture["wasm"] = "wasm";
|
||
|
})(exports.Architecture || (exports.Architecture = {}));
|
||
|
/**
|
||
|
* Union type representing the OS defined in part of an OCI image's
|
||
|
* manifest list.
|
||
|
* See the docs for the `Architecture` type above for more info.
|
||
|
*/
|
||
|
|
||
|
|
||
|
var OS;
|
||
|
|
||
|
(function (OS) {
|
||
|
OS["aix"] = "aix";
|
||
|
OS["android"] = "android";
|
||
|
OS["darwin"] = "darwin";
|
||
|
OS["dragonfly"] = "dragonfly";
|
||
|
OS["freebsd"] = "freebsd";
|
||
|
OS["illumos"] = "illumos";
|
||
|
OS["js"] = "js";
|
||
|
OS["linux"] = "linux";
|
||
|
OS["netbsd"] = "netbsd";
|
||
|
OS["openbsd"] = "openbsd";
|
||
|
OS["plan9"] = "plan9";
|
||
|
OS["solaris"] = "solaris";
|
||
|
OS["windows"] = "windows";
|
||
|
})(OS || (OS = {}));
|
||
|
|
||
|
(function (ManifestMediaType) {
|
||
|
ManifestMediaType["Manifest"] = "application/vnd.docker.distribution.manifest.v2+json";
|
||
|
ManifestMediaType["ManifestList"] = "application/vnd.docker.distribution.manifest.list.v2+json";
|
||
|
})(exports.ManifestMediaType || (exports.ManifestMediaType = {}));
|
||
|
|
||
|
var log = /*#__PURE__*/
|
||
|
pino({
|
||
|
base: null,
|
||
|
useLevelLabels: true
|
||
|
});
|
||
|
|
||
|
var DOCKER_HUB_API_ROOT = 'https://hub.docker.com/v2/';
|
||
|
var DOCKER_HUB_API_AUTH_URL = 'https://auth.docker.io/token';
|
||
|
|
||
|
/**
|
||
|
* Currently only supports fetching the manifest for the `latest` tag; in
|
||
|
* reality, we can pass any valid content digest[1] to retrieve the manifest(s)
|
||
|
* for that image.
|
||
|
*
|
||
|
* [1]: https://github.com/opencontainers/distribution-spec/blob/master/spec.md#content-digests
|
||
|
*/
|
||
|
|
||
|
var createManifestListURL = function createManifestListURL(_ref) {
|
||
|
var repo = _ref.repo;
|
||
|
return "https://registry-1.docker.io/v2/" + repo.user + "/" + repo.name + "/manifests/latest";
|
||
|
};
|
||
|
|
||
|
var createUserReposListURL = function createUserReposListURL(user) {
|
||
|
return DOCKER_HUB_API_ROOT + "repositories/" + user;
|
||
|
};
|
||
|
/**
|
||
|
* The OCI distribution spec requires a unique token for each repo manifest queried.
|
||
|
*/
|
||
|
|
||
|
|
||
|
var fetchDockerHubToken = function fetchDockerHubToken(repo) {
|
||
|
try {
|
||
|
var name = repo.name,
|
||
|
user = repo.user;
|
||
|
return Promise.resolve(axios.get(DOCKER_HUB_API_AUTH_URL, {
|
||
|
params: {
|
||
|
scope: "repository:" + user + "/" + name + ":pull",
|
||
|
service: 'registry.docker.io'
|
||
|
}
|
||
|
})).then(function (tokenRequest) {
|
||
|
var token = R.path(['data', 'token'], tokenRequest);
|
||
|
|
||
|
if (!token) {
|
||
|
throw new Error('Unable to retrieve auth token from registry.');
|
||
|
}
|
||
|
|
||
|
return token;
|
||
|
});
|
||
|
} catch (e) {
|
||
|
return Promise.reject(e);
|
||
|
}
|
||
|
};
|
||
|
/**
|
||
|
* Pure function that massages the Docker Hub API response into the
|
||
|
* format we want to return. e.g., only extracting certain fields;
|
||
|
* converting snake_case to camelCase, etc.
|
||
|
*/
|
||
|
|
||
|
var extractRepositoryDetails = function extractRepositoryDetails(repos, lastUpdatedSince) {
|
||
|
if (!repos || R.isEmpty(repos)) {
|
||
|
return [];
|
||
|
}
|
||
|
|
||
|
var parsedRepos = camelcaseKeys(repos);
|
||
|
|
||
|
if (R.isNil(lastUpdatedSince)) {
|
||
|
return parsedRepos;
|
||
|
}
|
||
|
|
||
|
return parsedRepos.filter(function (repo) {
|
||
|
return luxon.DateTime.fromISO(repo.lastUpdated) < lastUpdatedSince;
|
||
|
});
|
||
|
};
|
||
|
/**
|
||
|
* Query a single repository given a repo name and username.
|
||
|
*
|
||
|
* @param user The DockerHub username or org name to query for.
|
||
|
* @param name The DockerHub repo name -- restrict to this single repo.
|
||
|
*/
|
||
|
|
||
|
var queryRepo = function queryRepo(_ref2) {
|
||
|
var name = _ref2.name,
|
||
|
user = _ref2.user;
|
||
|
|
||
|
try {
|
||
|
return Promise.resolve(axios.request({
|
||
|
url: DOCKER_HUB_API_ROOT + "repositories/" + user + "/" + name + "/"
|
||
|
})).then(function (repoResult) {
|
||
|
var repo = R.prop('data', repoResult);
|
||
|
|
||
|
if (repoResult.status !== 200 || !repo || R.isEmpty(repo)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
return camelcaseKeys(repo);
|
||
|
});
|
||
|
} catch (e) {
|
||
|
return Promise.reject(e);
|
||
|
}
|
||
|
};
|
||
|
/**
|
||
|
* Top-level function for querying repositories.
|
||
|
*
|
||
|
* @TODO Rename to just `queryRepos`.
|
||
|
*
|
||
|
* @param user The DockerHub username or org name to query for.
|
||
|
* @param numRepos The number of repos to query (max 100).
|
||
|
* @param lastUpdatedSince Filter by the DateTime at which a repo was last updated.
|
||
|
*/
|
||
|
|
||
|
var queryTopRepos = function queryTopRepos(_ref3) {
|
||
|
var lastUpdatedSince = _ref3.lastUpdatedSince,
|
||
|
_ref3$numRepos = _ref3.numRepos,
|
||
|
numRepos = _ref3$numRepos === void 0 ? 100 : _ref3$numRepos,
|
||
|
user = _ref3.user;
|
||
|
|
||
|
try {
|
||
|
if (numRepos > 100) {
|
||
|
throw new RangeError('Number of repos to query cannot exceed 100.');
|
||
|
}
|
||
|
|
||
|
var listReposURL = createUserReposListURL(user);
|
||
|
return Promise.resolve(axios.get(listReposURL, {
|
||
|
params: {
|
||
|
page: 1,
|
||
|
page_size: numRepos
|
||
|
}
|
||
|
})).then(function (repoResults) {
|
||
|
var repos = R.path(['data', 'results'], repoResults);
|
||
|
return extractRepositoryDetails(repos, lastUpdatedSince);
|
||
|
});
|
||
|
} catch (e) {
|
||
|
return Promise.reject(e);
|
||
|
}
|
||
|
};
|
||
|
/**
|
||
|
* Query image tags.
|
||
|
*/
|
||
|
|
||
|
var queryTags = function queryTags(repo) {
|
||
|
try {
|
||
|
var repoUrl = createUserReposListURL(repo.user);
|
||
|
var tagsUrl = repoUrl + "/" + repo.name + "/tags?page_size=100";
|
||
|
return Promise.resolve(axios.get(tagsUrl)).then(function (tagsResults) {
|
||
|
var tags = R.path(['data', 'results'], tagsResults);
|
||
|
|
||
|
if (!tags || R.isEmpty(tags)) {
|
||
|
return;
|
||
|
} // @ts-ignore
|
||
|
|
||
|
|
||
|
return camelcaseKeys(tags);
|
||
|
});
|
||
|
} catch (e) {
|
||
|
return Promise.reject(e);
|
||
|
}
|
||
|
};
|
||
|
/**
|
||
|
* Queries the Docker Hub API to retrieve a "fat manifest", an object of
|
||
|
* `Content-Type` `application/vnd.docker.distribution.manifest.list.v2+json/`.
|
||
|
* Read up on the Manifest v2, Schema 2 Spec in more detail:
|
||
|
* @see https://github.com/docker/distribution/blob/master/docs/spec/manifest-v2-2.md
|
||
|
* Or the shiny new OCI distribution spec which builds on it:
|
||
|
* @see https://github.com/opencontainers/distribution-spec/blob/f67bc11ba3a083a9c62f8fa53ad14c5bcf2116af/spec.md
|
||
|
*/
|
||
|
|
||
|
var fetchManifestList = function fetchManifestList(repo) {
|
||
|
try {
|
||
|
// Docker Hub requires a unique token for each repo manifest queried.
|
||
|
return Promise.resolve(fetchDockerHubToken(repo)).then(function (token) {
|
||
|
var manifestListURL = createManifestListURL({
|
||
|
repo: repo
|
||
|
});
|
||
|
return Promise.resolve(axios.get(manifestListURL, {
|
||
|
headers: {
|
||
|
Accept: 'application/vnd.docker.distribution.manifest.list.v2+json',
|
||
|
Authorization: "Bearer " + token
|
||
|
}
|
||
|
})).then(function (manifestListResponse) {
|
||
|
// For now, just ignore legacy V1 schema manifests. They have an entirely
|
||
|
// different response shape and it's not worth mucking up the schema to
|
||
|
// support a legacy format.
|
||
|
if (manifestListResponse.data.schemaVersion === 1) {
|
||
|
log.info('Schema version 1 is unsupported.', repo.name);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
return R.path(['data'], manifestListResponse);
|
||
|
});
|
||
|
});
|
||
|
} catch (e) {
|
||
|
return Promise.reject(e);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
exports.DOCKER_HUB_API_AUTH_URL = DOCKER_HUB_API_AUTH_URL;
|
||
|
exports.DOCKER_HUB_API_ROOT = DOCKER_HUB_API_ROOT;
|
||
|
exports.extractRepositoryDetails = extractRepositoryDetails;
|
||
|
exports.fetchDockerHubToken = fetchDockerHubToken;
|
||
|
exports.fetchManifestList = fetchManifestList;
|
||
|
exports.queryRepo = queryRepo;
|
||
|
exports.queryTags = queryTags;
|
||
|
exports.queryTopRepos = queryTopRepos;
|
||
|
//# sourceMappingURL=docker-hub-utils.cjs.development.js.map
|