Dashboard › publish › Distillation
9ba642ea-44ba-423a-81ba-c02073a06f24["lore_tm_v1_Nq6j_egoy_RsSWfZYTdy9zAzKa6Ep9c0Esh3xwHOTi8"]
🔴 (10:10) Supplied token-creation implementation imports pRetry from "p-retry" and isNetworkError from "is-network-error"; exported main(clientId, privateKey, enterprise, owner, repositories, permissions, core, createAppAuth, request, skipTokenRevoke) orchestrates target resolution, GitHub App authentication, installation-token creation, output publication, and optional post-job revocation state.
🔴 (10:10) main() enforces mutual exclusivity: when enterprise is set alongside owner or a nonempty repositories array, it throws Cannot use 'enterprise' input with 'owner' or 'repositories' inputs.
🔴 (10:10) main() constructs GitHub App authentication with createAppAuth({ appId: clientId, privateKey, request }), calls getTokenFromTarget(request, auth, target, permissions) through pRetry, and derives the retry log description using getTokenRetryDescription(target).
🔴 (10:10) Token-creation retry policy in createTokenRetryOptions(core, targetDescription) retries when error.status >= 500 or isNetworkError(error) is true, sets retries: 3, and logs each failure as Failed to create token for ${targetDescription} (attempt ${context.attemptNumber}): ${context.error.message}.
🔴 (10:10) After token creation, main() masks authentication.token via core.setSecret(authentication.token) and publishes outputs token, installation-id, and app-slug.
🔴 (10:10) When skipTokenRevoke is false, main() persists authentication.token and authentication.expiresAt through core.saveState("token", ...) and core.saveState("expiresAt", ...) so the post function can invalidate the token; when true, it saves neither state value.
🔴 (10:10) resolveInstallationTarget(enterprise, owner, repositories, core) has four target-resolution paths: (1) enterprise returns { type: "enterprise", enterprise }; (2) neither owner nor repositories uses process.env.GITHUB_REPOSITORY, split on /, and returns the current repository as { type: "repository", owner: defaultOwner, repositories: [repo] }; (3) owner with no repositories returns { type: "owner", owner } for all repositories owned by that owner; (4) repository inputs are normalized and returned as a repository target.
🔴 (10:10) resolveInstallationTarget() logs which token scope is being created, including enterprise slug, current repository, all repositories for an owner, or a newline-prefixed list of ${target.owner}/${repo} entries. If no explicit owner is provided, the log states that the default owner is being used.
🔴 (10:10) normalizeRepositoryTarget(owner, repositories) resolves the owner from the explicit owner or String(process.env.GITHUB_REPOSITORY_OWNER), parses each repository with parseRepositoryInput, compares any embedded owner case-insensitively against the resolved owner, and returns repository names without owner prefixes.
🔴 (10:10) normalizeRepositoryTarget() rejects an embedded repository owner mismatch with Repository '${mismatchedRepository.input}' includes owner '${mismatchedRepository.owner}', which does not match the resolved owner '${parsedOwner}'.
🔴 (10:10) parseRepositoryInput(input) accepts exactly either repository, returning { input, owner: "", name: parts[0] }, or owner/repository, returning { input, owner: parts[0], name: parts[1] }; malformed, empty, or extra-segment values throw Invalid repository '${input}'. Expected 'repository' or 'owner/repository'.
🔴 (10:10) getTokenRetryDescription(target) formats enterprise targets as enterprise "${target.enterprise}", repository targets as a quoted comma-joined sequence of ${target.owner}/${repository}, and owner targets as "${target.owner}"; unsupported target types throw Unsupported installation target type: ${target.type}.
🔴 (10:10) getTokenFromTarget(request, auth, target, permissions) dispatches target types to getTokenFromEnterprise, getTokenFromRepository, or getTokenFromOwner; its default branch throws Unsupported installation target type: ${target.type} and is marked with /* c8 ignore next 2 */.
🔴 (10:10) createInstallationAuthResult(auth, installation, permissions, options = {}) requests { type: "installation", installationId: installation.id, permissions, ...options } and returns { authentication, installationId: installation.id, appSlug: installation["app_slug"] }.
🔴 (10:10) getTokenFromOwner(request, auth, parsedOwner, permissions) calls GET /users/{username}/installation with username: parsedOwner and request: { hook: auth.hook }; the endpoint is documented in the code as supporting both users and organizations, and the resulting token covers all repositories in that installation.
🔴 (10:10) getTokenFromRepository(request, auth, parsedOwner, parsedRepositoryNames, permissions) discovers the installation by calling GET /repos/{owner}/{repo}/installation for parsedRepositoryNames[0], then requests an installation token with repositoryNames: parsedRepositoryNames.
🔴 (10:10) getTokenFromEnterprise(request, auth, enterprise, permissions) calls GET /enterprises/{enterprise}/installation with request: { hook: auth.hook }; HTTP 404 is converted to No enterprise installation found matching the enterprise slug "${enterprise}"., while other errors are rethrown unchanged.
🔴 (10:10) Supplied request configuration imports core from "@actions/core" and request from "@octokit/request", obtains baseUrl from core.getInput("github-api-url").replace(/\/$/, ""), and exports request.defaults({ headers: { "user-agent": "actions/create-github-app-token" }, baseUrl }).
🔴 (10:10) Proxy detection checks exactly four environment variables in this order: https_proxy, HTTPS_PROXY, http_proxy, and HTTP_PROXY; proxyEnvironmentConfigured() returns true if any is truthy, while nativeProxySupportEnabled() requires process.env.NODE_USE_ENV_PROXY === "1".
🔴 (10:10) Exported ensureNativeProxySupport() returns without error if no proxy environment variable is configured or native proxy support is enabled; otherwise it throws A proxy environment variable is set, but Node.js native proxy support is not enabled. Set NODE_USE_ENV_PROXY=1 for this action step.
🔴 (10:10) Exported post-job function post(core, request) first reads core.getBooleanInput("skip-token-revoke"); if true, it logs Token revocation was skipped and returns.
🔴 (10:10) post() reads core.getState("token"); if no token exists, it logs Token is not set and returns. It then reads core.getState("expiresAt"); if present and tokenExpiresIn(expiresAt) < 0, it logs Token expired, skipping token revocation and returns.
🔴 (10:10) For a nonexpired saved token, post() calls DELETE /installation/token with header authorization: \token ${token}`and logsToken revokedon success; failures are caught and downgraded tocore.warning(`Token revocation failed: ${error.message}`)` rather than rethrown.
🔴 (10:10) tokenExpiresIn(expiresAt) constructs now and expiresAtDate as Date objects and returns rounded seconds using Math.round((expiresAtDate.getTime() - now.getTime()) / 1000).