UNPKG

@lcap/cli

Version:

utils for lcap

239 lines 9.98 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.release = void 0; const path_1 = __importDefault(require("path")); const recursive_readdir_1 = __importDefault(require("recursive-readdir")); const lodash_1 = require("lodash"); const promises_1 = require("fs/promises"); const undici_1 = require("undici"); const rest_1 = require("@gitbeaker/rest"); const utils_1 = require("../utils"); const constant_1 = require("./constant"); const HOST = 'https://g.hz.netease.com'; async function getFilesDiff(gitlab, remoteFiles, filesInDisk) { /** 文件编码格式 */ const encoding = 'base64'; /** 只在远端的文件 */ const filesOnlyInRemote = remoteFiles.filter((file) => !filesInDisk.find((remote) => remote.remotePath === file)); /** 只在硬盘里的文件 */ const filesOnlyInDisk = filesInDisk.filter(({ remotePath: remote }) => !remoteFiles.includes(remote)); /** 同时存在两端的文件 */ const filesBothIn = filesInDisk.filter(({ remotePath: remote }) => remoteFiles.includes(remote)); /** 更新操作数据 */ const actions = []; /** 并发控制器 */ const agent = new undici_1.Agent({ connections: 1000, pipelining: 2, autoSelectFamily: true, connectTimeout: 120000, autoSelectFamilyAttemptTimeout: 120000, headersTimeout: 120000, bodyTimeout: 120000, keepAliveTimeout: 120000, }); async function getFileMeta(file) { const { headers } = await agent.request({ origin: HOST, path: `/api/v4/projects/${encodeURIComponent(constant_1.AssetsReleaseId)}/repository/files/${encodeURIComponent(file)}?ref=master`, method: 'HEAD', headers: { 'User-Agent': 'Undici/1.0', 'PRIVATE-TOKEN': gitlab.headers['PRIVATE-TOKEN'], //'ze4vCDGm7LkvBMfn2CsV', }, }).catch((err) => { return { headers: {}, }; }); return { blobId: headers['x-gitlab-blob-id'], contentSha256: headers['x-gitlab-content-sha256'], encoding: headers['x-gitlab-encoding'], ref: headers['x-gitlab-ref'], size: Number(headers['x-gitlab-size']), commitId: headers['x-gitlab-commit-id'], lastCommitId: headers['x-gitlab-last-commit-id'], fileName: headers['x-gitlab-file-name'], filePath: headers['x-gitlab-file-path'] ?? file, }; } // 只存在于远端的文件要删除 for (const file of filesOnlyInRemote) { actions.push({ action: 'delete', filePath: file, }); } // 只存在于硬盘的文件要创建 for (const file of filesOnlyInDisk) { actions.push({ action: 'create', filePath: file.remotePath, content: file.content.toString('base64'), encoding, }); } // 同时存在于这两者的需要更新 for (const files of (0, lodash_1.chunk)(filesBothIn, 200)) { await Promise.all(files.map(async (file) => { const meta = await getFileMeta(file.remotePath); const add = () => { actions.push({ action: 'update', encoding, content: file.content.toString('base64'), filePath: file.remotePath, }); }; if (meta.size !== file.size) { add(); return; } const hashInDisk = (0, utils_1.createSHA256)(file.content); if (meta.contentSha256 !== hashInDisk) { add(); return; } })); } await agent.close(); await agent.destroy(); return (0, lodash_1.chunk)(actions, 12); } /** 关闭匹配前缀的分支和合并请求 */ async function deleteBranchAndMr(gitlab, prefix) { const restMr = await gitlab.MergeRequests.all({ AssetsReleaseId: constant_1.AssetsReleaseId, search: prefix, state: 'opened', }); if (restMr.length > 0) { utils_1.logger.info(`发现 ${restMr.length} 个重复的旧合并请求,开始关闭...`); for (const mrs of (0, lodash_1.chunk)(restMr, 5)) { await Promise.all(mrs.map(({ iid }) => gitlab.MergeRequests.edit(constant_1.AssetsReleaseId, iid, { stateEvent: 'close', }))); } } else { utils_1.logger.info(`没有发现重复的旧合并请求!`); } const restAutoBranch = await gitlab.Branches.all(constant_1.AssetsReleaseId, { search: prefix, }); if (restAutoBranch.length > 0) { utils_1.logger.info(`发现 ${restAutoBranch.length} 个重复的旧分支,开始删除...`); for (const branches of (0, lodash_1.chunk)(restAutoBranch, 5)) { await Promise.all(branches.map(({ name }) => gitlab.Branches.remove(constant_1.AssetsReleaseId, name))); } } else { utils_1.logger.info(`没有发现重复的旧分支!`); } } async function autoMr(root, gitlab, data) { const version = await (0, utils_1.getVersion)(root); utils_1.logger.info(`当前物料 ${utils_1.color.yellow(data.kind.toUpperCase())},版本 ${utils_1.color.yellow(version)}`); const basePathInRemote = constant_1.AssetPath.ide(version); utils_1.logger.info(`检索远程仓库代码...`); const filesInRemote = await gitlab.Repositories.allRepositoryTrees(constant_1.AssetsReleaseId, { path: basePathInRemote, recursive: true, ref: constant_1.AssetRef, }); const fileNamesInRemote = filesInRemote .filter((item) => item.type === 'blob') .map((item) => item.path); utils_1.logger.info(`读取本地文件...`); const filesInDisk = await (0, recursive_readdir_1.default)(data.distDir); const filesData = await Promise.all((filesInDisk).map(async (file) => { const diskPath = path_1.default.join(root, file); const remotePath = path_1.default.join(basePathInRemote, path_1.default.relative(data.distDir, file)); const fileBuffer = await (0, promises_1.readFile)(diskPath); const size = fileBuffer.byteLength; return { diskPath, remotePath, size, content: fileBuffer, }; })); if (filesData.length === 0) { utils_1.logger.info(`未发现本地数据,进程退出。`); if (data.output) { utils_1.logger.info(`MR 结果写入上下文...`); await (0, utils_1.writeContext)(root, data.output, { mr_url: 'NONE' }); } return; } utils_1.logger.info(`正在准备 Diff 数据...`); const filesDiff = await getFilesDiff(gitlab, fileNamesInRemote, filesData); if (filesDiff.length === 0) { utils_1.logger.info(`文件全部相同,不需要提交!`); return; } const searchFix = `${data.kind}@${version}`; const newBranchPrefix = `auto-release/${searchFix}`; utils_1.logger.info(`开始删除旧的分支和合并请求...`); // 发生错误不影响后面的流程 await deleteBranchAndMr(gitlab, searchFix).catch((e) => { utils_1.logger.error(`删除旧分支和合并请求时出错,跳过此流程!`); utils_1.logger.error(e); }); utils_1.logger.info(`开始创建分支...`); const newBranchName = `${newBranchPrefix}/${Date.now()}`; const branchData = await gitlab.Branches.create(constant_1.AssetsReleaseId, newBranchName, constant_1.AssetRef); utils_1.logger.info(`分支创建完成,新分支名为 ${utils_1.color.yellow(newBranchName)},分支链接为:${branchData.webUrl}`); utils_1.logger.info(`开始创建 MR...`); const params = { version, time: Date.now(), kind: data.kind, }; const mergeRequestData = await gitlab.MergeRequests.create(constant_1.AssetsReleaseId, newBranchName, constant_1.AssetRef, (0, utils_1.formatMessageByParams)(data.mrTitle, params), { description: (0, utils_1.formatMessageByParams)(data.mrMessage, params), squash: true, removeSourceBranch: true, labels: 'auto-release', }); utils_1.logger.info(`MR 创建成功,MR 链接为:${utils_1.color.blackBright(mergeRequestData.webUrl)}`); utils_1.logger.info(`开始提交代码...`); utils_1.spinner.start(); for (let i = 0; i < filesDiff.length; i++) { const group = filesDiff[i]; utils_1.spinner.text = `提交代码:${i + 1} / ${filesDiff.length}...`; await gitlab.Commits.create(constant_1.AssetsReleaseId, newBranchName, `auto-release: 🔧 自动提交 ${version} 版本 ${data.kind.toUpperCase()} 物料`, group, { authorName: data.authorName, authorEmail: data.authorEmail, force: false, }); } utils_1.spinner.stop(); utils_1.spinner.clear(); utils_1.logger.info(`代码提交成功!`); if (data.output) { utils_1.logger.info(`MR 结果写入上下文...`); await (0, utils_1.writeContext)(root, data.output, { mr_url: mergeRequestData.webUrl, }); } utils_1.logger.info(`代码自动合并结束!`); } async function release(root, opt) { const tokenData = await (0, utils_1.getSecretByOption)(root, opt); const gitlab = new rest_1.Gitlab({ token: tokenData, host: HOST, camelize: true, queryTimeout: 30 * 60 * 1000, }); // 存一下 token gitlab.headers['PRIVATE-TOKEN'] = 'ze4vCDGm7LkvBMfn2CsV'; return autoMr(root, gitlab, opt); } exports.release = release; //# sourceMappingURL=api.js.map