diff --git a/dist/main.cjs b/dist/main.cjs index eb2283b..3baed63 100644 --- a/dist/main.cjs +++ b/dist/main.cjs @@ -561,10 +561,10 @@ var require_proxy = __commonJS({ })(); if (proxyVar) { try { - return new URL(proxyVar); + return new DecodedURL(proxyVar); } catch (_a) { if (!proxyVar.startsWith("http://") && !proxyVar.startsWith("https://")) - return new URL(`http://${proxyVar}`); + return new DecodedURL(`http://${proxyVar}`); } } else { return void 0; @@ -607,6 +607,19 @@ var require_proxy = __commonJS({ const hostLower = host.toLowerCase(); return hostLower === "localhost" || hostLower.startsWith("127.") || hostLower.startsWith("[::1]") || hostLower.startsWith("[0:0:0:0:0:0:0:1]"); } + var DecodedURL = class extends URL { + constructor(url, base) { + super(url, base); + this._decodedUsername = decodeURIComponent(super.username); + this._decodedPassword = decodeURIComponent(super.password); + } + get username() { + return this._decodedUsername; + } + get password() { + return this._decodedPassword; + } + }; } }); @@ -18141,7 +18154,7 @@ var require_lib = __commonJS({ } const usingSsl = parsedUrl.protocol === "https:"; proxyAgent = new undici_1.ProxyAgent(Object.assign({ uri: proxyUrl2.href, pipelining: !this._keepAlive ? 0 : 1 }, (proxyUrl2.username || proxyUrl2.password) && { - token: `${proxyUrl2.username}:${proxyUrl2.password}` + token: `Basic ${Buffer.from(`${proxyUrl2.username}:${proxyUrl2.password}`).toString("base64")}` })); this._proxyAgentDispatcher = proxyAgent; if (usingSsl && this._ignoreSslError) { @@ -36979,11 +36992,11 @@ var RequestError = class extends Error { response; constructor(message, statusCode, options) { super(message); - if (Error.captureStackTrace) { - Error.captureStackTrace(this, this.constructor); - } this.name = "HttpError"; - this.status = statusCode; + this.status = Number.parseInt(statusCode); + if (Number.isNaN(this.status)) { + this.status = 0; + } if ("response" in options) { this.response = options.response; } @@ -37984,14 +37997,13 @@ var Stack = class _Stack { } }; var LRUCache = class _LRUCache { - // properties coming in from the options of these, only max and maxSize - // really *need* to be protected. The rest can be modified, as they just - // set defaults for various methods. + // options that cannot be changed without disaster #max; #maxSize; #dispose; #disposeAfter; #fetchMethod; + #memoMethod; /** * {@link LRUCache.OptionsBase.ttl} */ @@ -38137,6 +38149,9 @@ var LRUCache = class _LRUCache { get fetchMethod() { return this.#fetchMethod; } + get memoMethod() { + return this.#memoMethod; + } /** * {@link LRUCache.OptionsBase.dispose} (read-only) */ @@ -38150,7 +38165,7 @@ var LRUCache = class _LRUCache { return this.#disposeAfter; } constructor(options) { - const { max = 0, ttl, ttlResolution = 1, ttlAutopurge, updateAgeOnGet, updateAgeOnHas, allowStale, dispose, disposeAfter, noDisposeOnSet, noUpdateTTL, maxSize = 0, maxEntrySize = 0, sizeCalculation, fetchMethod, noDeleteOnFetchRejection, noDeleteOnStaleGet, allowStaleOnFetchRejection, allowStaleOnFetchAbort, ignoreFetchAbort } = options; + const { max = 0, ttl, ttlResolution = 1, ttlAutopurge, updateAgeOnGet, updateAgeOnHas, allowStale, dispose, disposeAfter, noDisposeOnSet, noUpdateTTL, maxSize = 0, maxEntrySize = 0, sizeCalculation, fetchMethod, memoMethod, noDeleteOnFetchRejection, noDeleteOnStaleGet, allowStaleOnFetchRejection, allowStaleOnFetchAbort, ignoreFetchAbort } = options; if (max !== 0 && !isPosInt(max)) { throw new TypeError("max option must be a nonnegative integer"); } @@ -38170,6 +38185,10 @@ var LRUCache = class _LRUCache { throw new TypeError("sizeCalculation set to non-function"); } } + if (memoMethod !== void 0 && typeof memoMethod !== "function") { + throw new TypeError("memoMethod must be a function if defined"); + } + this.#memoMethod = memoMethod; if (fetchMethod !== void 0 && typeof fetchMethod !== "function") { throw new TypeError("fetchMethod must be a function if specified"); } @@ -38240,7 +38259,8 @@ var LRUCache = class _LRUCache { } } /** - * Return the remaining TTL time for a given entry key + * Return the number of ms left in the item's TTL. If item is not in cache, + * returns `0`. Returns `Infinity` if item is in cache without a defined TTL. */ getRemainingTTL(key) { return this.#keyMap.has(key) ? Infinity : 0; @@ -38256,7 +38276,7 @@ var LRUCache = class _LRUCache { if (ttl !== 0 && this.ttlAutopurge) { const t = setTimeout(() => { if (this.#isStale(index)) { - this.delete(this.#keyList[index]); + this.#delete(this.#keyList[index], "expire"); } }, ttl + 1); if (t.unref) { @@ -38493,13 +38513,14 @@ var LRUCache = class _LRUCache { return this.entries(); } /** - * A String value that is used in the creation of the default string description of an object. - * Called by the built-in method Object.prototype.toString. + * A String value that is used in the creation of the default string + * description of an object. Called by the built-in method + * `Object.prototype.toString`. */ [Symbol.toStringTag] = "LRUCache"; /** * Find a value for which the supplied fn method returns a truthy value, - * similar to Array.find(). fn is called as fn(value, key, cache). + * similar to `Array.find()`. fn is called as `fn(value, key, cache)`. */ find(fn, getOptions = {}) { for (const i of this.#indexes()) { @@ -38513,10 +38534,15 @@ var LRUCache = class _LRUCache { } } /** - * Call the supplied function on each item in the cache, in order from - * most recently used to least recently used. fn is called as - * fn(value, key, cache). Does not update age or recenty of use. - * Does not iterate over stale values. + * Call the supplied function on each item in the cache, in order from most + * recently used to least recently used. + * + * `fn` is called as `fn(value, key, cache)`. + * + * If `thisp` is provided, function will be called in the `this`-context of + * the provided object, or the cache if no `thisp` object is provided. + * + * Does not update age or recenty of use, or iterate over stale values. */ forEach(fn, thisp = this) { for (const i of this.#indexes()) { @@ -38548,7 +38574,7 @@ var LRUCache = class _LRUCache { let deleted = false; for (const i of this.#rindexes({ allowStale: true })) { if (this.#isStale(i)) { - this.delete(this.#keyList[i]); + this.#delete(this.#keyList[i], "expire"); deleted = true; } } @@ -38556,9 +38582,15 @@ var LRUCache = class _LRUCache { } /** * Get the extended info about a given entry, to get its value, size, and - * TTL info simultaneously. Like {@link LRUCache#dump}, but just for a - * single key. Always returns stale values, if their info is found in the - * cache, so be sure to check for expired TTLs if relevant. + * TTL info simultaneously. Returns `undefined` if the key is not present. + * + * Unlike {@link LRUCache#dump}, which is designed to be portable and survive + * serialization, the `start` value is always the current timestamp, and the + * `ttl` is a calculated remaining time to live (negative if expired). + * + * Always returns stale values, if their info is found in the cache, so be + * sure to check for expirations (ie, a negative {@link LRUCache.Entry#ttl}) + * if relevant. */ info(key) { const i = this.#keyMap.get(key); @@ -38585,7 +38617,16 @@ var LRUCache = class _LRUCache { } /** * Return an array of [key, {@link LRUCache.Entry}] tuples which can be - * passed to cache.load() + * passed to {@link LRLUCache#load}. + * + * The `start` fields are calculated relative to a portable `Date.now()` + * timestamp, even if `performance.now()` is available. + * + * Stale entries are always included in the `dump`, even if + * {@link LRUCache.OptionsBase.allowStale} is false. + * + * Note: this returns an actual array, not a generator, so it can be more + * easily passed around. */ dump() { const arr = []; @@ -38610,8 +38651,12 @@ var LRUCache = class _LRUCache { } /** * Reset the cache and load in the items in entries in the order listed. - * Note that the shape of the resulting cache may be different if the - * same options are not used in both caches. + * + * The shape of the resulting cache may be different if the same options are + * not used in both caches. + * + * The `start` fields are assumed to be calculated relative to a portable + * `Date.now()` timestamp, even if `performance.now()` is available. */ load(arr) { this.clear(); @@ -38628,6 +38673,30 @@ var LRUCache = class _LRUCache { * * Note: if `undefined` is specified as a value, this is an alias for * {@link LRUCache#delete} + * + * Fields on the {@link LRUCache.SetOptions} options param will override + * their corresponding values in the constructor options for the scope + * of this single `set()` operation. + * + * If `start` is provided, then that will set the effective start + * time for the TTL calculation. Note that this must be a previous + * value of `performance.now()` if supported, or a previous value of + * `Date.now()` if not. + * + * Options object may also include `size`, which will prevent + * calling the `sizeCalculation` function and just use the specified + * number if it is a positive integer, and `noDisposeOnSet` which + * will prevent calling a `dispose` function in the case of + * overwrites. + * + * If the `size` (or return value of `sizeCalculation`) for a given + * entry is greater than `maxEntrySize`, then the item will not be + * added to the cache. + * + * Will update the recency of the entry. + * + * If the value is `undefined`, then this is an alias for + * `cache.delete(key)`. `undefined` is never stored in the cache. */ set(k, v, setOptions = {}) { if (v === void 0) { @@ -38642,7 +38711,7 @@ var LRUCache = class _LRUCache { status.set = "miss"; status.maxEntrySizeExceeded = true; } - this.delete(k); + this.#delete(k, "set"); return this; } let index = this.#size === 0 ? void 0 : this.#keyMap.get(k); @@ -38776,6 +38845,14 @@ var LRUCache = class _LRUCache { * Will return false if the item is stale, even though it is technically * in the cache. * + * Check if a key is in the cache, without updating the recency of + * use. Age is updated if {@link LRUCache.OptionsBase.updateAgeOnHas} is set + * to `true` in either the options or the constructor. + * + * Will return `false` if the item is stale, even though it is technically in + * the cache. The difference can be determined (if it matters) by using a + * `status` argument, and inspecting the `has` field. + * * Will not update item age unless * {@link LRUCache.OptionsBase.updateAgeOnHas} is set. */ @@ -38858,7 +38935,7 @@ var LRUCache = class _LRUCache { if (bf2.__staleWhileFetching) { this.#valList[index] = bf2.__staleWhileFetching; } else { - this.delete(k); + this.#delete(k, "fetch"); } } else { if (options.status) @@ -38884,7 +38961,7 @@ var LRUCache = class _LRUCache { if (this.#valList[index] === p) { const del = !noDelete || bf2.__staleWhileFetching === void 0; if (del) { - this.delete(k); + this.#delete(k, "fetch"); } else if (!allowStaleAborted) { this.#valList[index] = bf2.__staleWhileFetching; } @@ -39022,6 +39099,28 @@ var LRUCache = class _LRUCache { return staleVal ? p.__staleWhileFetching : p.__returned = p; } } + async forceFetch(k, fetchOptions = {}) { + const v = await this.fetch(k, fetchOptions); + if (v === void 0) + throw new Error("fetch() returned undefined"); + return v; + } + memo(k, memoOptions = {}) { + const memoMethod = this.#memoMethod; + if (!memoMethod) { + throw new Error("no memoMethod provided to constructor"); + } + const { context, forceRefresh, ...options } = memoOptions; + const v = this.get(k, options); + if (!forceRefresh && v !== void 0) + return v; + const vv = memoMethod(k, v, { + options, + context + }); + this.set(k, vv, options); + return vv; + } /** * Return a value from the cache. Will update the recency of the cache * entry found. @@ -39041,7 +39140,7 @@ var LRUCache = class _LRUCache { status.get = "stale"; if (!fetching) { if (!noDeleteOnStaleGet) { - this.delete(k); + this.#delete(k, "expire"); } if (status && allowStale) status.returnedStale = true; @@ -39085,16 +39184,20 @@ var LRUCache = class _LRUCache { } /** * Deletes a key out of the cache. + * * Returns true if the key was deleted, false otherwise. */ delete(k) { + return this.#delete(k, "delete"); + } + #delete(k, reason) { let deleted = false; if (this.#size !== 0) { const index = this.#keyMap.get(k); if (index !== void 0) { deleted = true; if (this.#size === 1) { - this.clear(); + this.#clear(reason); } else { this.#removeItemSize(index); const v = this.#valList[index]; @@ -39102,10 +39205,10 @@ var LRUCache = class _LRUCache { v.__abortController.abort(new Error("deleted")); } else if (this.#hasDispose || this.#hasDisposeAfter) { if (this.#hasDispose) { - this.#dispose?.(v, k, "delete"); + this.#dispose?.(v, k, reason); } if (this.#hasDisposeAfter) { - this.#disposed?.push([v, k, "delete"]); + this.#disposed?.push([v, k, reason]); } } this.#keyMap.delete(k); @@ -39139,6 +39242,9 @@ var LRUCache = class _LRUCache { * Clear the cache entirely, throwing away all values. */ clear() { + return this.#clear("delete"); + } + #clear(reason) { for (const index of this.#rindexes({ allowStale: true })) { const v = this.#valList[index]; if (this.#isBackgroundFetch(v)) { @@ -39146,10 +39252,10 @@ var LRUCache = class _LRUCache { } else { const k = this.#keyList[index]; if (this.#hasDispose) { - this.#dispose?.(v, k, "delete"); + this.#dispose?.(v, k, reason); } if (this.#hasDisposeAfter) { - this.#disposed?.push([v, k, "delete"]); + this.#disposed?.push([v, k, reason]); } } } @@ -39702,9 +39808,7 @@ async function main(appId2, privateKey2, owner2, repositories2, core3, createApp let parsedOwner = ""; let parsedRepositoryNames = []; if (!owner2 && repositories2.length === 0) { - const [owner3, repo] = String( - process.env.GITHUB_REPOSITORY - ).split("/"); + const [owner3, repo] = String(process.env.GITHUB_REPOSITORY).split("/"); parsedOwner = owner3; parsedRepositoryNames = [repo]; core3.info( @@ -39721,14 +39825,18 @@ async function main(appId2, privateKey2, owner2, repositories2, core3, createApp parsedOwner = String(process.env.GITHUB_REPOSITORY_OWNER); parsedRepositoryNames = repositories2; core3.info( - `owner not set, creating owner for given repositories "${repositories2.join(",")}" in current owner ("${parsedOwner}")` + `owner not set, creating owner for given repositories "${repositories2.join( + "," + )}" in current owner ("${parsedOwner}")` ); } if (owner2 && repositories2.length > 0) { parsedOwner = owner2; parsedRepositoryNames = repositories2; core3.info( - `owner and repositories set, creating token for repositories "${repositories2.join(",")}" owned by "${owner2}"` + `owner and repositories set, creating token for repositories "${repositories2.join( + "," + )}" owned by "${owner2}"` ); } const auth5 = createAppAuth2({ @@ -39738,23 +39846,36 @@ async function main(appId2, privateKey2, owner2, repositories2, core3, createApp }); let authentication, installationId, appSlug; if (parsedRepositoryNames.length > 0) { - ({ authentication, installationId, appSlug } = await pRetry(() => getTokenFromRepository(request2, auth5, parsedOwner, parsedRepositoryNames), { - onFailedAttempt: (error) => { - core3.info( - `Failed to create token for "${parsedRepositoryNames.join(",")}" (attempt ${error.attemptNumber}): ${error.message}` - ); - }, - retries: 3 - })); + ({ authentication, installationId, appSlug } = await pRetry( + () => getTokenFromRepository( + request2, + auth5, + parsedOwner, + parsedRepositoryNames + ), + { + onFailedAttempt: (error) => { + core3.info( + `Failed to create token for "${parsedRepositoryNames.join( + "," + )}" (attempt ${error.attemptNumber}): ${error.message}` + ); + }, + retries: 3 + } + )); } else { - ({ authentication, installationId, appSlug } = await pRetry(() => getTokenFromOwner(request2, auth5, parsedOwner), { - onFailedAttempt: (error) => { - core3.info( - `Failed to create token for "${parsedOwner}" (attempt ${error.attemptNumber}): ${error.message}` - ); - }, - retries: 3 - })); + ({ authentication, installationId, appSlug } = await pRetry( + () => getTokenFromOwner(request2, auth5, parsedOwner), + { + onFailedAttempt: (error) => { + core3.info( + `Failed to create token for "${parsedOwner}" (attempt ${error.attemptNumber}): ${error.message}` + ); + }, + retries: 3 + } + )); } core3.setSecret(authentication.token); core3.setOutput("token", authentication.token); @@ -39766,19 +39887,11 @@ async function main(appId2, privateKey2, owner2, repositories2, core3, createApp } } async function getTokenFromOwner(request2, auth5, parsedOwner) { - const response = await request2("GET /orgs/{org}/installation", { - org: parsedOwner, + const response = await request2("GET /users/{username}/installation", { + username: parsedOwner, request: { hook: auth5.hook } - }).catch((error) => { - if (error.status !== 404) throw error; - return request2("GET /users/{username}/installation", { - username: parsedOwner, - request: { - hook: auth5.hook - } - }); }); const authentication = await auth5({ type: "installation", diff --git a/dist/post.cjs b/dist/post.cjs index f77a8f1..37b6461 100644 --- a/dist/post.cjs +++ b/dist/post.cjs @@ -560,10 +560,10 @@ var require_proxy = __commonJS({ })(); if (proxyVar) { try { - return new URL(proxyVar); + return new DecodedURL(proxyVar); } catch (_a) { if (!proxyVar.startsWith("http://") && !proxyVar.startsWith("https://")) - return new URL(`http://${proxyVar}`); + return new DecodedURL(`http://${proxyVar}`); } } else { return void 0; @@ -606,6 +606,19 @@ var require_proxy = __commonJS({ const hostLower = host.toLowerCase(); return hostLower === "localhost" || hostLower.startsWith("127.") || hostLower.startsWith("[::1]") || hostLower.startsWith("[0:0:0:0:0:0:0:1]"); } + var DecodedURL = class extends URL { + constructor(url, base) { + super(url, base); + this._decodedUsername = decodeURIComponent(super.username); + this._decodedPassword = decodeURIComponent(super.password); + } + get username() { + return this._decodedUsername; + } + get password() { + return this._decodedPassword; + } + }; } }); @@ -18140,7 +18153,7 @@ var require_lib = __commonJS({ } const usingSsl = parsedUrl.protocol === "https:"; proxyAgent = new undici_1.ProxyAgent(Object.assign({ uri: proxyUrl2.href, pipelining: !this._keepAlive ? 0 : 1 }, (proxyUrl2.username || proxyUrl2.password) && { - token: `${proxyUrl2.username}:${proxyUrl2.password}` + token: `Basic ${Buffer.from(`${proxyUrl2.username}:${proxyUrl2.password}`).toString("base64")}` })); this._proxyAgentDispatcher = proxyAgent; if (usingSsl && this._ignoreSslError) { @@ -36791,11 +36804,11 @@ var RequestError = class extends Error { response; constructor(message, statusCode, options) { super(message); - if (Error.captureStackTrace) { - Error.captureStackTrace(this, this.constructor); - } this.name = "HttpError"; - this.status = statusCode; + this.status = Number.parseInt(statusCode); + if (Number.isNaN(this.status)) { + this.status = 0; + } if ("response" in options) { this.response = options.response; } diff --git a/lib/main.js b/lib/main.js index bc15f95..0f3d07b 100644 --- a/lib/main.js +++ b/lib/main.js @@ -26,9 +26,7 @@ export async function main( // If neither owner nor repositories are set, default to current repository if (!owner && repositories.length === 0) { - const [owner, repo] = String( - process.env.GITHUB_REPOSITORY - ).split("/"); + const [owner, repo] = String(process.env.GITHUB_REPOSITORY).split("/"); parsedOwner = owner; parsedRepositoryNames = [repo]; @@ -52,7 +50,9 @@ export async function main( parsedRepositoryNames = repositories; core.info( - `owner not set, creating owner for given repositories "${repositories.join(',')}" in current owner ("${parsedOwner}")` + `owner not set, creating owner for given repositories "${repositories.join( + "," + )}" in current owner ("${parsedOwner}")` ); } @@ -62,7 +62,9 @@ export async function main( parsedRepositoryNames = repositories; core.info( - `owner and repositories set, creating token for repositories "${repositories.join(',')}" owned by "${owner}"` + `owner and repositories set, creating token for repositories "${repositories.join( + "," + )}" owned by "${owner}"` ); } @@ -76,24 +78,38 @@ export async function main( // If at least one repository is set, get installation ID from that repository if (parsedRepositoryNames.length > 0) { - ({ authentication, installationId, appSlug } = await pRetry(() => getTokenFromRepository(request, auth, parsedOwner, parsedRepositoryNames), { - onFailedAttempt: (error) => { - core.info( - `Failed to create token for "${parsedRepositoryNames.join(',')}" (attempt ${error.attemptNumber}): ${error.message}` - ); - }, - retries: 3, - })); + ({ authentication, installationId, appSlug } = await pRetry( + () => + getTokenFromRepository( + request, + auth, + parsedOwner, + parsedRepositoryNames + ), + { + onFailedAttempt: (error) => { + core.info( + `Failed to create token for "${parsedRepositoryNames.join( + "," + )}" (attempt ${error.attemptNumber}): ${error.message}` + ); + }, + retries: 3, + } + )); } else { // Otherwise get the installation for the owner, which can either be an organization or a user account - ({ authentication, installationId, appSlug } = await pRetry(() => getTokenFromOwner(request, auth, parsedOwner), { - onFailedAttempt: (error) => { - core.info( - `Failed to create token for "${parsedOwner}" (attempt ${error.attemptNumber}): ${error.message}` - ); - }, - retries: 3, - })); + ({ authentication, installationId, appSlug } = await pRetry( + () => getTokenFromOwner(request, auth, parsedOwner), + { + onFailedAttempt: (error) => { + core.info( + `Failed to create token for "${parsedOwner}" (attempt ${error.attemptNumber}): ${error.message}` + ); + }, + retries: 3, + } + )); } // Register the token with the runner as a secret to ensure it is masked in logs @@ -111,23 +127,13 @@ export async function main( } async function getTokenFromOwner(request, auth, parsedOwner) { - // https://docs.github.com/en/rest/apps/apps?apiVersion=2022-11-28#get-an-organization-installation-for-the-authenticated-app - const response = await request("GET /orgs/{org}/installation", { - org: parsedOwner, + // https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#get-a-user-installation-for-the-authenticated-app + // This endpoint works for both users and organizations + const response = await request("GET /users/{username}/installation", { + username: parsedOwner, request: { hook: auth.hook, }, - }).catch((error) => { - /* c8 ignore next */ - if (error.status !== 404) throw error; - - // https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#get-a-user-installation-for-the-authenticated-app - return request("GET /users/{username}/installation", { - username: parsedOwner, - request: { - hook: auth.hook, - }, - }); }); // Get token for for all repositories of the given installation @@ -137,12 +143,17 @@ async function getTokenFromOwner(request, auth, parsedOwner) { }); const installationId = response.data.id; - const appSlug = response.data['app_slug']; + const appSlug = response.data["app_slug"]; return { authentication, installationId, appSlug }; } -async function getTokenFromRepository(request, auth, parsedOwner, parsedRepositoryNames) { +async function getTokenFromRepository( + request, + auth, + parsedOwner, + parsedRepositoryNames +) { // https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#get-a-repository-installation-for-the-authenticated-app const response = await request("GET /repos/{owner}/{repo}/installation", { owner: parsedOwner, @@ -160,7 +171,7 @@ async function getTokenFromRepository(request, auth, parsedOwner, parsedReposito }); const installationId = response.data.id; - const appSlug = response.data['app_slug']; + const appSlug = response.data["app_slug"]; return { authentication, installationId, appSlug }; } diff --git a/tests/main-token-get-owner-set-to-user-repo-unset.test.js b/tests/main-token-get-owner-set-fail-response.test.js similarity index 78% rename from tests/main-token-get-owner-set-to-user-repo-unset.test.js rename to tests/main-token-get-owner-set-fail-response.test.js index a73c3d7..90408ba 100644 --- a/tests/main-token-get-owner-set-to-user-repo-unset.test.js +++ b/tests/main-token-get-owner-set-fail-response.test.js @@ -1,6 +1,6 @@ import { test } from "./main.js"; -// Verify `main` successfully obtains a token when the `owner` input is set (to a user), but the `repositories` input isn’t set. +// Verify retries work when getting a token for a user or organization fails on the first attempt. await test((mockPool) => { process.env.INPUT_OWNER = "smockle"; delete process.env.INPUT_REPOSITORIES; @@ -10,7 +10,7 @@ await test((mockPool) => { const mockAppSlug = "github-actions"; mockPool .intercept({ - path: `/orgs/${process.env.INPUT_OWNER}/installation`, + path: `/users/${process.env.INPUT_OWNER}/installation`, method: "GET", headers: { accept: "application/vnd.github.v3+json", @@ -18,7 +18,7 @@ await test((mockPool) => { // Intentionally omitting the `authorization` header, since JWT creation is not idempotent. }, }) - .reply(404); + .reply(500, "GitHub API not available"); mockPool .intercept({ path: `/users/${process.env.INPUT_OWNER}/installation`, @@ -31,7 +31,7 @@ await test((mockPool) => { }) .reply( 200, - { id: mockInstallationId, "app_slug": mockAppSlug }, + { id: mockInstallationId, app_slug: mockAppSlug }, { headers: { "content-type": "application/json" } } ); }); diff --git a/tests/main-token-get-owner-set-to-org-repo-unset.test.js b/tests/main-token-get-owner-set-repo-unset.test.js similarity index 74% rename from tests/main-token-get-owner-set-to-org-repo-unset.test.js rename to tests/main-token-get-owner-set-repo-unset.test.js index 87245f7..1c06512 100644 --- a/tests/main-token-get-owner-set-to-org-repo-unset.test.js +++ b/tests/main-token-get-owner-set-repo-unset.test.js @@ -1,16 +1,16 @@ import { test } from "./main.js"; -// Verify `main` successfully obtains a token when the `owner` input is set (to an org), but the `repositories` input isn’t set. +// Verify `main` successfully obtains a token when the `owner` input is set, and the `repositories` input isn’t set. await test((mockPool) => { process.env.INPUT_OWNER = process.env.GITHUB_REPOSITORY_OWNER; delete process.env.INPUT_REPOSITORIES; - // Mock installation id and app slug request + // Mock installation ID and app slug request const mockInstallationId = "123456"; const mockAppSlug = "github-actions"; mockPool .intercept({ - path: `/orgs/${process.env.INPUT_OWNER}/installation`, + path: `/users/${process.env.INPUT_OWNER}/installation`, method: "GET", headers: { accept: "application/vnd.github.v3+json", @@ -20,7 +20,7 @@ await test((mockPool) => { }) .reply( 200, - { id: mockInstallationId, "app_slug": mockAppSlug }, + { id: mockInstallationId, app_slug: mockAppSlug }, { headers: { "content-type": "application/json" } } ); }); diff --git a/tests/main-token-get-owner-set-to-user-fail-response.test.js b/tests/main-token-get-owner-set-to-user-fail-response.test.js deleted file mode 100644 index 318b8dc..0000000 --- a/tests/main-token-get-owner-set-to-user-fail-response.test.js +++ /dev/null @@ -1,37 +0,0 @@ -import { test } from "./main.js"; - -// Verify `main` successfully obtains a token when the `owner` input is set (to a user), but the `repositories` input isn’t set. -await test((mockPool) => { - process.env.INPUT_OWNER = "smockle"; - delete process.env.INPUT_REPOSITORIES; - - // Mock installation ID and app slug request - const mockInstallationId = "123456"; - const mockAppSlug = "github-actions"; - mockPool - .intercept({ - path: `/orgs/${process.env.INPUT_OWNER}/installation`, - method: "GET", - headers: { - accept: "application/vnd.github.v3+json", - "user-agent": "actions/create-github-app-token", - // Intentionally omitting the `authorization` header, since JWT creation is not idempotent. - }, - }) - .reply(500, "GitHub API not available"); - mockPool - .intercept({ - path: `/orgs/${process.env.INPUT_OWNER}/installation`, - method: "GET", - headers: { - accept: "application/vnd.github.v3+json", - "user-agent": "actions/create-github-app-token", - // Intentionally omitting the `authorization` header, since JWT creation is not idempotent. - }, - }) - .reply( - 200, - { id: mockInstallationId, "app_slug": mockAppSlug }, - { headers: { "content-type": "application/json" } } - ); -}); diff --git a/tests/snapshots/index.js.md b/tests/snapshots/index.js.md index 76d4b78..73a4c6a 100644 --- a/tests/snapshots/index.js.md +++ b/tests/snapshots/index.js.md @@ -114,6 +114,26 @@ Generated by [AVA](https://avajs.dev). ::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊ ::save-state name=expiresAt::2016-07-11T22:14:10Z` +## main-token-get-owner-set-fail-response.test.js + +> stderr + + '' + +> stdout + + `repositories not set, creating token for all repositories for given owner "smockle"␊ + Failed to create token for "smockle" (attempt 1): GitHub API not available␊ + ::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊ + ␊ + ::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊ + ␊ + ::set-output name=installation-id::123456␊ + ␊ + ::set-output name=app-slug::github-actions␊ + ::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊ + ::save-state name=expiresAt::2016-07-11T22:14:10Z` + ## main-token-get-owner-set-repo-fail-response.test.js > stderr @@ -191,7 +211,7 @@ Generated by [AVA](https://avajs.dev). ::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊ ::save-state name=expiresAt::2016-07-11T22:14:10Z` -## main-token-get-owner-set-to-org-repo-unset.test.js +## main-token-get-owner-set-repo-unset.test.js > stderr @@ -210,45 +230,6 @@ Generated by [AVA](https://avajs.dev). ::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊ ::save-state name=expiresAt::2016-07-11T22:14:10Z` -## main-token-get-owner-set-to-user-fail-response.test.js - -> stderr - - '' - -> stdout - - `repositories not set, creating token for all repositories for given owner "smockle"␊ - Failed to create token for "smockle" (attempt 1): GitHub API not available␊ - ::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊ - ␊ - ::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊ - ␊ - ::set-output name=installation-id::123456␊ - ␊ - ::set-output name=app-slug::github-actions␊ - ::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊ - ::save-state name=expiresAt::2016-07-11T22:14:10Z` - -## main-token-get-owner-set-to-user-repo-unset.test.js - -> stderr - - '' - -> stdout - - `repositories not set, creating token for all repositories for given owner "smockle"␊ - ::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊ - ␊ - ::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊ - ␊ - ::set-output name=installation-id::123456␊ - ␊ - ::set-output name=app-slug::github-actions␊ - ::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊ - ::save-state name=expiresAt::2016-07-11T22:14:10Z` - ## main-token-get-owner-unset-repo-set.test.js > stderr diff --git a/tests/snapshots/index.js.snap b/tests/snapshots/index.js.snap index 9956084..061ef5b 100644 Binary files a/tests/snapshots/index.js.snap and b/tests/snapshots/index.js.snap differ