zephyr-agent
Version:
Zephyr plugin agent
483 lines (482 loc) • 21 kB
JavaScript
"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