// ==UserScript==
// @name Van Deploy
// @namespace http://tampermonkey.net/
// @version 0.4.4
// @description Auto Deploy for Van!
// @author Alexander
// @match https://van.huolala.work/projects/835/*
// @match https://van.huolala.work/projects/833/*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// console.log(' van deploy is running ')
let monitor
const startTime = new Date().getTime()
const sleep = (time = 1000) => new Promise(resolve => setTimeout(resolve, time));
class DeployTask {
constructor(config) {
this.id = config.id // building id
this.branch = config.branch // branch
this.startTime = new Date().getTime()
// this.config = config
this.status = 'pending' // pending, success, failed, deploying , deployed, undeployed
this.queryId = undefined
this.deployInterval = undefined
this.start()
}
getDom() {
const taskDom = Array.from(document.getElementsByTagName('strong')).find(dom => dom.innerText === this.id)
return taskDom ? taskDom.parentNode.parentNode : null
}
async start () {
try {
console.log('task ', this.id, ' start')
this.status = 'pending'
await this.query()
await this.queueForDeply()
console.log('task is deploying ', this.id)
this.status = 'deploying'
await this.deploy()
this.stop()
} catch (error) {
console.log('deploy error ', error)
this.stop()
}
}
async query() {
return new Promise((resolve, reject) => {
this.queryId = setInterval(() => {
console.log(this.id, ' building status query ')
const dom = this.getDom()
if (!dom) {
return
}
console.log('task is pending', this.id)
const status = dom.getElementsByClassName('anticon')[0].className.split(' ')
if (status.some(classItem => ['anticon-check-circle'].includes(classItem))) {
clearInterval(this.queryId)
this.status = 'success'
resolve()
}
if (status.some(classItem => ['anticon-close-circle'].includes(classItem))) {
clearInterval(this.queryId)
this.status = 'failed'
reject()
}
}, 1000)
})
}
async queueForDeply () {
return new Promise((resolve, reject) => {
console.log('queue for deploy ', this.id)
this.deployInterval = setInterval(() => {
if (monitor.canDeploy()) {
clearInterval(this.deployInterval)
resolve()
}
}, 500)
})
}
async deploy() {
const dom = this.getDom()
let deployed = false
if (!dom) {
return
}
dom.click()
await sleep(3000)
if (!document.getElementsByClassName('ant-dropdown')[0] || getComputedStyle( document.getElementsByClassName('ant-dropdown')[0]).display === 'none') {
document.querySelector('.publish-btn-check button').click()
}
await sleep(1000)
Array.from(document.getElementsByClassName('fast-publish-btn-menu')[0].getElementsByClassName('ant-menu-item-group')).forEach(envDom => {
const envName = envDom.getElementsByClassName('ant-menu-item-group-title')[0].innerText.toLocaleLowerCase().trim()
if (this.branch.includes(envName) ) {
deployed = true
envDom.getElementsByClassName('ant-space-item')[0].click()
}
// env.getElementsByClassName('ant-space-item')
})
await sleep(300)
document.querySelector('.publish-btn-check button').click()
if (deployed) {
this.status = 'deployed'
return Promise.resolve()
}
this.status = 'undeployed'
return Promise.reject()
}
stop() {
// deployed, failed
console.log('monitor ', this.queryId, ' stop')
clearInterval(this.queryId)
clearInterval(this.deployInterval)
}
}
class DeloyMonitor {
constructor() {
this.tasksQueue = []
this.monitorId = undefined
this.hibernateId = undefined
}
async getTaskNodes () {
const parentNode = document.getElementsByClassName('task-list-sider__list')[0]
let nodes
if (!parentNode) {
await sleep(1000)
nodes = await this.getTaskNodes()
return nodes
}
nodes = parentNode.getElementsByClassName('task-card')
if (!nodes || nodes.length === 0) {
await sleep(1000)
nodes = await this.getTaskNodes()
return nodes
}
if (nodes) {
return nodes
} else {
throw new Error('query node error')
}
}
parseNode(node) {
const id = node.getElementsByClassName('first-line')[0].getElementsByTagName('strong')[0].innerText
const branch = node.getElementsByClassName('branch')[0].innerText.trim()
const user = node.getElementsByClassName('second-line')[0].getElementsByTagName('strong')[0].innerText.split(' ')[1];
// const status = node.className.split(' ')[1];
// pending : anticon-sync anticon-spin
// success : anticon-check-circle
// failed : anticon-close-circle
// deployed : anticon-flag
const status = node.getElementsByClassName('anticon')[0].className.split(' ')
// console.log('id', id, 'status', status)
return { id, branch, user, status }
}
start () {
if (this.monitorId) {
return console.error('DeloyMonitor has already runned')
}
this.monitorId = setInterval(async () => {
console.log('deloyMonitor is running')
if (!navigator.onLine) {
this.hibernate()
}
try {
const taskNodes = await this.getTaskNodes();
Array.from(taskNodes).forEach(node => {
const { id, branch, user, status } = this.parseNode(node)
// add new task
const task = this.tasksQueue.find(item => item.id === id)
const isStgPre = branch.includes('stg') || branch.includes('pre')
if (isStgPre && !task && status.some(statusClass => ['anticon-sync', 'anticon-spin'].includes(statusClass))) {
const task = new DeployTask({ id, branch, user, status })
console.log('add new task ', id , task)
this.tasksQueue.push(task)
}
// remove when failed , deployed
if (status.some(statusClass => ['anticon-close-circle', 'anticon-flag'].includes(statusClass))) {
const taskIndex = this.tasksQueue.findIndex(item => item.id === id)
taskIndex !== -1 && console.log('will remove task ', id , task, 'taskIndex', taskIndex)
if (taskIndex !== -1) {
task.stop()
this.tasksQueue.splice(taskIndex, 1)
}
}
// remove when success but undeployed
if (status.some(statusClass => ['anticon-check-circle'].includes(statusClass))) {
const taskIndex = this.tasksQueue.findIndex(item => item.id === id)
const successTask = this.tasksQueue[taskIndex]
if (['undeployed', 'pending'].includes(successTask?.status)) {
successTask.stop()
this.tasksQueue.splice(taskIndex, 1)
}
}
})
console.clear()
const timespan = parseInt((new Date().getTime() - startTime) / 1000)
console.log('at time', timespan , 'tasks are ',[...this.tasksQueue])
timespan > 3600 && this.reload()
} catch (err) {
this.reload()
}
}, 5000)
}
stop () {
console.log('monitor stop')
this.tasksQueue.forEach(task => task.stop())
clearInterval(this.monitorId)
this.monitorId = undefined
}
canDeploy (){
return !this.tasksQueue.some(task => task.status === 'deploying')
}
reload() {
window.location.reload()
}
hibernate () {
this.stop()
this.tasksQueue = []
this.hibernateId = setInterval(() => {
if (navigator.onLine) {
this.start()
clearInterval(this.hibernateId)
this.hibernateId = undefined
}
}, 60 * 1000)
}
}
monitor = new DeloyMonitor()
monitor.start();
window.onbeforeunload = function(e) {
monitor.stop();
};
window.addEventListener('online', () => monitor.start());
window.addEventListener('offline', () => monitor.hibernate());
})();