Greasy Fork 支持简体中文。

Van Deploy

Auto Deploy for Van!

目前為 2022-06-06 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Van Deploy
// @namespace    http://tampermonkey.net/
// @version      0.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.monitorId = 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.monitorId = 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.monitorId)
            this.status = 'success'
            resolve()
          }
          if (status.some(classItem => ['anticon-close-circle'].includes(classItem))) {
            clearInterval(this.monitorId)
            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)
      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.monitorId, ' stop')
      clearInterval(this.monitorId)
      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)
              taskIndex !== -1 && 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]
              successTask?.status === 'undeployed' && 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());


})();