工具来由
需要把指定帐号下的所有仓库批量下载下来,一个个复制仓库地址进行克隆太忙,所以做了这个自动化工具,如果你有类似需求,可供参考。
操作准备
本地需要安装 git,生成私钥后添加到指定帐号的 SSH 公钥中。
代码
核心代码参考:
const puppeteer = require('puppeteer');
const account = '帐号';
const password = '密码';
const localPath = '本地存放代码的目录';
const { exec, execSync } = require('child_process');
(async () => {
const browser = await puppeteer.launch({
headless: true,
args: [
'--no-sandBox',
'--disable-setuid-sandBox',
'--disable-blink-features=AutomationControlled',
],
dumpio: false,
});
const page = await browser.newPage();
// webdriver
await page.evaluateOnNewDocument(() => {
const newProto = navigator.__proto__;
delete newProto.webdriver; //删除 navigator.webdriver字段
navigator.__proto__ = newProto;
});
// 添加 window.chrome字段,向内部填充一些值
await page.evaluateOnNewDocument(() => {
window.chrome = {};
window.chrome.app = {
InstallState: 'hehe',
RunningState: 'haha',
getDetails: 'xixi',
getIsInstalled: 'ohno',
};
window.chrome.csi = function() {};
window.chrome.loadTimes = function() {};
window.chrome.runtime = function() {};
});
// userAgent设置
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, 'userAgent', {
//userAgent在无头模式下有headless字样,所以需覆盖
get: () =>
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36',
});
});
// plugins设置
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, 'plugins', {
//伪装真实的插件信息
get: () => [
{
0: {
type: 'application/x-google-chrome-pdf',
suffixes: 'pdf',
description: 'Portable Document Format',
enabledplugin: Plugin,
},
description: 'Portable Document Format',
filename: 'internal-pdf-viewer',
length: 1,
name: 'Chrome PDF Plugin',
},
{
0: {
type: 'application/pdf',
suffixes: 'pdf',
description: '',
enabledplugin: Plugin,
},
description: '',
filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai',
length: 1,
name: 'Chrome PDF Viewer',
},
{
0: {
type: 'application/x-nacl',
suffixes: '',
description: 'Native Client Executable',
enabledplugin: Plugin,
},
1: {
type: 'application/x-pnacl',
suffixes: '',
description: 'Portable Native Client Executable',
enabledplugin: Plugin,
},
description: '',
filename: 'internal-nacl-plugin',
length: 2,
name: 'Native Client',
},
],
});
});
// languages设置
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, 'languages', {
//添加语言
get: () => ['zh-CN', 'zh', 'en'],
});
});
// permissions设置
await page.evaluateOnNewDocument(() => {
const originalQuery = window.navigator.permissions.query; //notification伪装
window.navigator.permissions.query = (parameters) =>
parameters.name === 'notifications'
? Promise.resolve({ state: Notification.permission })
: originalQuery(parameters);
});
// WebGL设置
await page.evaluateOnNewDocument(() => {
const getParameter = WebglrenderingContext.getParameter;
WebglrenderingContext.prototype.getParameter = function(parameter) {
// UNMASKED_vendOR_WEBGL
if (parameter === 37445) {
return 'Intel Inc.';
}
// UNMASKED_RENDERER_WEBGL
if (parameter === 37446) {
return 'Intel(R) Iris(TM) Graphics 6100';
}
return getParameter(parameter);
};
});
await page.goto('https://xxx.coding.net/login', {
waitUntil: 'networkidle0',
});
await page.type('input[name="account"]', account);
await page.type('input[name="password"]', password);
console.log('登录...');
await page.click('.cuk2-button-type-confirm');
await page.waitForNavigation({
waitUntil: 'networkidle0',
});
page.on('response', async (response) => {
const request = response.request();
if (request.url().includes('xxx/depot-group/depots')) {
console.log('获取所有仓库地址');
const json = await response.text();
let repos = JSON.parse(json).data;
// 下载到本地
repos.forEach((r) => {
console.log('下载:' + r.gitSshUrl);
execSync(`cd ${localPath} && git clone ${r.gitSshUrl}`);
});
// await browser.close();
}
});
console.log('跳转仓库列表页');
await page.goto('https://jnpfsoft.coding.net/p/xxx/repos', {
waitUntil: 'networkidle2',
});
})();