UNPKG

zephyr-agent

Version:
483 lines (482 loc) • 21 kB
"use strict"; var __webpack_require__ = {}; (()=>{ __webpack_require__.d = (exports1, getters, values)=>{ var define = (defs, kind)=>{ for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, { enumerable: true, [kind]: defs[key] }); }; define(getters, "get"); define(values, "value"); }; })(); (()=>{ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop); })(); (()=>{ __webpack_require__.r = (exports1)=>{ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, { value: 'Module' }); Object.defineProperty(exports1, '__esModule', { value: true }); }; })(); var __webpack_exports__ = {}; __webpack_require__.r(__webpack_exports__); __webpack_require__.d(__webpack_exports__, { getGitInfo: ()=>getGitInfo }); const external_ci_info_namespaceObject = require("ci-info"); const external_node_child_process_namespaceObject = require("node:child_process"); const external_node_path_namespaceObject = require("node:path"); const external_node_util_namespaceObject = require("node:util"); const external_zephyr_edge_contract_namespaceObject = require("zephyr-edge-contract"); const auth_flags_js_namespaceObject = require("../auth/auth-flags.js"); const login_js_namespaceObject = require("../auth/login.js"); const index_js_namespaceObject = require("../errors/index.js"); const http_request_js_namespaceObject = require("../http/http-request.js"); const external_logging_index_js_namespaceObject = require("../logging/index.js"); const ze_log_event_js_namespaceObject = require("../logging/ze-log-event.js"); const secret_token_js_namespaceObject = require("../node-persist/secret-token.js"); const token_js_namespaceObject = require("../node-persist/token.js"); const external_ci_branch_detection_js_namespaceObject = require("./ci-branch-detection.js"); const external_git_provider_utils_js_namespaceObject = require("./git-provider-utils.js"); const external_detect_monorepo_js_namespaceObject = require("./detect-monorepo.js"); const external_ze_util_read_package_json_js_namespaceObject = require("./ze-util-read-package-json.js"); const external_zephyr_config_js_namespaceObject = require("./zephyr-config.js"); const execFile = (0, external_node_util_namespaceObject.promisify)(external_node_child_process_namespaceObject.execFile); async function runGit(args, context) { try { const { stdout } = await execFile('git', args, { cwd: context }); const trimmed = stdout.trim(); return '' === trimmed ? null : trimmed; } catch { return null; } } function generateBranchName(context, userId) { const timestamp = new Date().toISOString().replace(/\D/g, ''); const userSuffix = userId ? `-${userId.slice(0, 8)}` : ''; return `${context}${userSuffix}-${timestamp}`; } async function getGitInfo(context, zephyrConfig = (0, external_zephyr_config_js_namespaceObject.getZephyrConfig)(context)) { try { return await gatherGitInfo((0, external_zephyr_config_js_namespaceObject.resolveZephyrContextDirectory)(context), zephyrConfig); } catch (error) { throw index_js_namespaceObject.ZephyrError.withContext(error, 'resolve-git-metadata'); } } async function gatherGitInfo(context, zephyrConfig) { const hasToken = (0, secret_token_js_namespaceObject.hasSecretToken)(); try { const { name, email, remoteOrigin, branch, commit, tags, stdout } = await loadGitInfo(hasToken, context); if (!hasToken && (!name || !email)) throw new index_js_namespaceObject.ZephyrError(index_js_namespaceObject.ZeErrors.ERR_NO_GIT_USERNAME_EMAIL, { data: { stdout } }); const app = resolveApplicationIdentity(remoteOrigin, stdout, zephyrConfig); const gitInfo = { git: { name, email, branch, commit, tags }, app }; external_logging_index_js_namespaceObject.ze_log.git('Loaded: git info', gitInfo); return gitInfo; } catch (error) { if (external_ci_info_namespaceObject.isCI) throw new index_js_namespaceObject.ZephyrError(index_js_namespaceObject.ZeErrors.ERR_NO_GIT_INFO, { message: 'Stable Git branch and commit information is required in CI environments', cause: error }); external_logging_index_js_namespaceObject.ze_log.git('Git repository not found, falling back to global git config'); try { const globalGitInfo = await loadGlobalGitInfo(context, zephyrConfig); return globalGitInfo; } catch { (0, ze_log_event_js_namespaceObject.logFn)('warn', 'Global git config not found, checking authenticated user metadata'); external_logging_index_js_namespaceObject.ze_log.git('Global git config not found, checking authenticated user metadata'); const fallbackInfo = await getFallbackGitInfo(context, zephyrConfig); return fallbackInfo; } } } const NO_GIT_COMMIT = 'no-git-commit'; async function loadGitInfo(hasSecretToken, context) { const automated = external_ci_info_namespaceObject.isCI || hasSecretToken; const [name, email, remoteOrigin, gitBranch, rawCommit, tags] = await Promise.all([ automated ? runGit([ 'log', '-1', '--pretty=format:%an' ], context) : runGit([ 'config', 'user.name' ], context), automated ? runGit([ 'log', '-1', '--pretty=format:%ae' ], context) : runGit([ 'config', 'user.email' ], context), runGit([ 'config', '--get', 'remote.origin.url' ], context), loadGitBranch(context), runGit([ 'rev-parse', 'HEAD' ], context), loadGitTags(context) ]); if (null === name && null === email && null === gitBranch && null === rawCommit) throw new index_js_namespaceObject.ZephyrError(index_js_namespaceObject.ZeErrors.ERR_NO_GIT_INFO, { message: 'Not a git repository', data: { context } }); const commit = rawCommit ?? NO_GIT_COMMIT; const stdout = [ name, email, remoteOrigin, gitBranch, commit ].join('\n'); let branch = gitBranch; if (external_ci_info_namespaceObject.isCI && ('HEAD' === gitBranch || !gitBranch)) { const ciInfo = (0, external_ci_branch_detection_js_namespaceObject.detectCIBranch)(); if (ciInfo.branch) { branch = ciInfo.branch; external_logging_index_js_namespaceObject.ze_log.git(`Detected branch from ${ciInfo.platform} environment variables: ${branch}`, { platform: ciInfo.platform, branch, isPR: ciInfo.isPR, gitBranch }); } else external_logging_index_js_namespaceObject.ze_log.git(`Branch detection in CI failed. Git returned: "${gitBranch}", no CI env vars available`, { platform: ciInfo.platform, gitBranch }); } if (external_ci_info_namespaceObject.isCI && (!branch || 'HEAD' === branch)) throw new index_js_namespaceObject.ZephyrError(index_js_namespaceObject.ZeErrors.ERR_NO_GIT_INFO, { message: 'Git branch is required in CI environments. Configure the CI provider branch variables or check out a named branch.' }); if (external_ci_info_namespaceObject.isCI && (!commit || commit === NO_GIT_COMMIT)) throw new index_js_namespaceObject.ZephyrError(index_js_namespaceObject.ZeErrors.ERR_NO_GIT_INFO, { message: 'Git commit hash is required in CI environments. Ensure this repository has commit history.' }); return { name: name ?? '', email: email ?? '', remoteOrigin: remoteOrigin ?? '', branch: branch ?? '', commit, tags, stdout }; } async function loadGitBranch(context) { const symbolic = await runGit([ 'symbolic-ref', '--short', 'HEAD' ], context); if (symbolic) return symbolic; return runGit([ 'rev-parse', '--abbrev-ref', 'HEAD' ], context); } async function loadGitTags(context) { const stdout = await runGit([ 'tag', '--points-at', 'HEAD' ], context); return stdout ? stdout.split('\n').filter(Boolean) : []; } function hasConfiguredApp(config) { return Boolean(config.org && config.project); } function resolveApplicationIdentity(remoteOrigin, stdout, config) { if (hasConfiguredApp(config)) return { org: config.org, project: config.project }; const inferred = parseGitUrl(remoteOrigin, stdout); return { org: config.org ?? inferred.org, project: config.project ?? inferred.project }; } function applyConfiguredApp(app, config) { return { org: config.org ?? app.org, project: config.project ?? app.project }; } function parseGitUrl(remoteOrigin, stdout) { if (!remoteOrigin) throw new index_js_namespaceObject.ZephyrError(index_js_namespaceObject.ZeErrors.ERR_GIT_REMOTE_ORIGIN, { data: { stdout } }); try { const gitInfo = (0, external_git_provider_utils_js_namespaceObject.getGitProviderInfo)(remoteOrigin); external_logging_index_js_namespaceObject.ze_log.git(`Git provider detected: ${gitInfo.provider}`, { provider: gitInfo.provider, owner: gitInfo.owner, project: gitInfo.project, isEnterprise: gitInfo.isEnterprise }); return { org: gitInfo.owner, project: gitInfo.project }; } catch (cause) { throw new index_js_namespaceObject.ZephyrError(index_js_namespaceObject.ZeErrors.ERR_NO_GIT_INFO, { message: stdout, cause, data: { stdout } }); } } async function loadGlobalGitInfo(context, zephyrConfig) { try { const [globalName, globalEmail] = await Promise.all([ runGit([ 'config', '--global', 'user.name' ], context), runGit([ 'config', '--global', 'user.email' ], context) ]); const name = globalName ?? ''; const email = globalEmail ?? ''; if (!name && !email) { (0, ze_log_event_js_namespaceObject.logFn)('warn', 'Global git config incomplete: both name and email are missing. Falling back to API user info.'); throw new index_js_namespaceObject.ZephyrError(index_js_namespaceObject.ZeErrors.ERR_NO_GIT_INFO, { message: 'Global git config incomplete: both name and email are missing' }); } if (!name) { (0, ze_log_event_js_namespaceObject.logFn)('warn', 'Global git config incomplete: user name is missing. Run: git config --global user.name "Your Name". Falling back to API user info.'); throw new index_js_namespaceObject.ZephyrError(index_js_namespaceObject.ZeErrors.ERR_NO_GIT_INFO, { message: 'Global git config incomplete: user name is missing' }); } if (!email) { (0, ze_log_event_js_namespaceObject.logFn)('warn', 'Global git config incomplete: user email is missing. Run: git config --global user.email "your@email.com". Falling back to API user info.'); throw new index_js_namespaceObject.ZephyrError(index_js_namespaceObject.ZeErrors.ERR_NO_GIT_INFO, { message: 'Global git config incomplete: user email is missing' }); } const app = hasConfiguredApp(zephyrConfig) ? { org: zephyrConfig.org, project: zephyrConfig.project } : applyConfiguredApp(await getAppNamingFromPackageJson(name, context, zephyrConfig), zephyrConfig); const gitInfo = { git: { name, email, branch: generateBranchName('global-git'), commit: NO_GIT_COMMIT, tags: [] }, app }; external_logging_index_js_namespaceObject.ze_log.git('Using global git config (no local repository)', gitInfo); if (!hasConfiguredApp(zephyrConfig)) { (0, ze_log_event_js_namespaceObject.logFn)('warn', 'Configuration accepted for THIS BUILD ONLY.'); (0, ze_log_event_js_namespaceObject.logFn)('warn', 'This manual configuration will NOT work for production deployments.'); (0, ze_log_event_js_namespaceObject.logFn)('warn', 'Zephyr REQUIRES a proper git repository with remote origin to function correctly.'); (0, ze_log_event_js_namespaceObject.logFn)('warn', 'Please set up git before your next deployment: https://docs.zephyr-cloud.io'); } return gitInfo; } catch (error) { throw new index_js_namespaceObject.ZephyrError(index_js_namespaceObject.ZeErrors.ERR_NO_GIT_INFO, { message: 'Failed to load global git config', cause: error }); } } async function getUserInfoFromAPI() { try { let authenticationAttempted = false; const authenticate = async ()=>{ authenticationAttempted = true; await (0, login_js_namespaceObject.checkAuth)(); }; while(true){ const token = await (0, token_js_namespaceObject.getToken)(); if (!token || !(0, login_js_namespaceObject.isTokenStillValid)(token, auth_flags_js_namespaceObject.TOKEN_EXPIRY.SHORT_VALIDITY_CHECK_SEC)) { if (authenticationAttempted) throw new index_js_namespaceObject.ZephyrError(index_js_namespaceObject.ZeErrors.ERR_AUTH_ERROR, { message: 'No valid authentication token was available after login.' }); await authenticate(); continue; } const [ok, cause, response] = await (0, http_request_js_namespaceObject.makeRequest)({ path: external_zephyr_edge_contract_namespaceObject.ze_api_gateway.user_info, base: (0, external_zephyr_edge_contract_namespaceObject.ZE_API_ENDPOINT)(), query: {} }, { headers: { Authorization: `Bearer ${token}` }, credentialToken: token }); if (!ok) { if (!authenticationAttempted && !(0, secret_token_js_namespaceObject.hasSecretToken)() && index_js_namespaceObject.ZephyrError.is(cause, index_js_namespaceObject.ZeErrors.ERR_AUTH_ERROR)) { await authenticate(); continue; } throw cause; } const userData = response.value; external_logging_index_js_namespaceObject.ze_log.git('Retrieved user info from API:', { name: userData.name, email: userData.email }); return userData; } } catch (error) { throw index_js_namespaceObject.ZephyrError.withContext(error, 'get-user-info'); } } async function getFallbackGitInfo(context, zephyrConfig) { let userInfo; try { userInfo = await getUserInfoFromAPI(); } catch (error) { external_logging_index_js_namespaceObject.ze_log.git('Failed to get user info from API:', error); throw error; } const gitName = userInfo.name; const gitEmail = userInfo.email; if (external_ci_info_namespaceObject.isCI && !hasConfiguredApp(zephyrConfig)) throw new index_js_namespaceObject.ZephyrError(index_js_namespaceObject.ZeErrors.ERR_NO_GIT_INFO, { message: 'Git repository information is required in CI environments. Please ensure your CI workflow includes git repository with proper remote origin.' }); const app = hasConfiguredApp(zephyrConfig) ? { org: zephyrConfig.org, project: zephyrConfig.project } : applyConfiguredApp(await getAppNamingFromPackageJson(userInfo.name, context, zephyrConfig), zephyrConfig); const gitInfo = { git: { name: gitName, email: gitEmail, branch: generateBranchName('no-git', userInfo.id), commit: `fallback-deployment-${Date.now()}`, tags: [ `deployed-by:${userInfo.id}` ] }, app }; external_logging_index_js_namespaceObject.ze_log.git('Using fallback git info (git not available)', gitInfo); if (!hasConfiguredApp(zephyrConfig)) { (0, ze_log_event_js_namespaceObject.logFn)('warn', `Using organization "${app.org}" from authenticated user.`); (0, ze_log_event_js_namespaceObject.logFn)('warn', 'Configuration accepted for THIS BUILD ONLY.'); (0, ze_log_event_js_namespaceObject.logFn)('warn', 'This manual configuration will NOT work for production deployments.'); (0, ze_log_event_js_namespaceObject.logFn)('warn', 'Zephyr REQUIRES a proper git repository with remote origin to function correctly.'); (0, ze_log_event_js_namespaceObject.logFn)('warn', 'Please set up git before your next deployment: https://docs.zephyr-cloud.io'); } return gitInfo; } function sanitizeName(name) { return name.replace(/[^a-zA-Z0-9-]/g, '-').toLowerCase(); } function getCurrentDirectoryName(context) { const dirName = context.split(external_node_path_namespaceObject.sep).pop() || 'untitled-project'; return sanitizeName(dirName); } async function getAppNamingFromPackageJson(tokenUserName, context, zephyrConfig) { try { const packageJson = await (0, external_ze_util_read_package_json_js_namespaceObject.getPackageJson)(context, zephyrConfig); const packageName = packageJson.name; if (!tokenUserName) throw new index_js_namespaceObject.ZephyrError(index_js_namespaceObject.ZeErrors.ERR_NO_GIT_INFO, { message: 'Unable to determine organization: no authenticated user found. Please ensure you are logged in with a valid token.' }); const org = sanitizeName(tokenUserName); if (packageName.includes('@')) { const [scope, name] = packageName.split('/'); return { org, project: scope.replace('@', ''), app: name }; } const monorepoInfo = await (0, external_detect_monorepo_js_namespaceObject.detectMonorepo)(context); if ('none' !== monorepoInfo.type) { const rootPackageJson = await (0, external_detect_monorepo_js_namespaceObject.getMonorepoRootPackageJson)(monorepoInfo.root); if (rootPackageJson && rootPackageJson['name']) { const rootName = rootPackageJson['name']; if (rootName.includes('@')) { const [scope] = rootName.split('/'); return { org, project: scope.replace('@', ''), app: packageName }; } return { org, project: sanitizeName(rootName), app: packageName }; } { const rootDirName = monorepoInfo.root.split(external_node_path_namespaceObject.sep).pop() || 'monorepo'; return { org, project: sanitizeName(rootDirName), app: packageName }; } } return { org, project: packageName, app: packageName }; } catch { const dirName = getCurrentDirectoryName(context); if (!tokenUserName) throw new index_js_namespaceObject.ZephyrError(index_js_namespaceObject.ZeErrors.ERR_NO_GIT_INFO, { message: 'Unable to determine organization: no authenticated user found and no package.json available. Please ensure you are logged in with a valid token.' }); return { org: sanitizeName(tokenUserName), project: dirName, app: dirName }; } } exports.getGitInfo = __webpack_exports__.getGitInfo; for(var __rspack_i in __webpack_exports__)if (-1 === [ "getGitInfo" ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i]; Object.defineProperty(exports, '__esModule', { value: true }); //# sourceMappingURL=ze-util-get-git-info.js.map