1 | 'use strict'
|
2 |
|
3 | const fs = require('fs')
|
4 | const fetch = require('node-fetch')
|
5 | const FormData = require('form-data')
|
6 | const xmlToJson = require('xml-to-json-stream')
|
7 | const Package = require('./package')
|
8 | const log = require('./log')
|
9 | const defaults = require('./defaults')
|
10 |
|
11 | class Pipeline {
|
12 | constructor (opts = {}) {
|
13 | this.lock = false
|
14 | this.queue = []
|
15 | this.checkBeforePush = opts.checkBeforePush || defaults.checkBeforePush
|
16 | this.packmgrPath = opts.packmgrPath || defaults.packmgrPath
|
17 | this.targets = opts.targets || defaults.target
|
18 | this.interval = opts.interval || defaults.interval
|
19 | this.exclude = opts.exclude || defaults.exclude
|
20 | this.onPushEnd = opts.onPushEnd || function () {}
|
21 | }
|
22 |
|
23 | start () {
|
24 | setInterval(async () => {
|
25 | await this._processQueue()
|
26 | }, this.interval)
|
27 | }
|
28 |
|
29 | enqueue (localPath) {
|
30 | log.debug(`Changed: ${localPath}`)
|
31 | this.queue.push(localPath)
|
32 | }
|
33 |
|
34 | async push (pathToPush) {
|
35 | this.enqueue(pathToPush)
|
36 | return this._processQueue()
|
37 | }
|
38 |
|
39 | async _processQueue () {
|
40 |
|
41 |
|
42 |
|
43 | if (this.lock === true || this.queue.length < 1) {
|
44 | return null
|
45 | }
|
46 |
|
47 |
|
48 | this.lock = true
|
49 |
|
50 |
|
51 | const pack = new Package(this.exclude)
|
52 | while (this.queue.length > 0) {
|
53 | const localPath = this.queue.pop()
|
54 | const item = pack.add(localPath)
|
55 | item && log.info(item.exists ? '+' : '-', item.zipPath)
|
56 | }
|
57 |
|
58 |
|
59 | log.group()
|
60 | const archivePath = pack.save()
|
61 | if (archivePath) {
|
62 | for (const target of this.targets) {
|
63 | const result = await this._post(archivePath, target)
|
64 | this.onPushEnd(result.err, result.target, result.log)
|
65 | log.info(log.gray(target + ' >'), log.gray(result.err ? result.err.message : 'OK'))
|
66 | }
|
67 | }
|
68 | log.groupEnd()
|
69 |
|
70 |
|
71 | this.lock = false
|
72 |
|
73 | return pack
|
74 | }
|
75 |
|
76 | async _post (archivePath, target) {
|
77 | const url = target + this.packmgrPath
|
78 | const form = new FormData()
|
79 | form.append('file', fs.createReadStream(archivePath))
|
80 | form.append('force', 'true')
|
81 | form.append('install', 'true')
|
82 |
|
83 |
|
84 | if (this.checkBeforePush && !await this._check(target)) {
|
85 | return { target, err: new Error('AEM not ready') }
|
86 | }
|
87 |
|
88 | const result = { target }
|
89 | try {
|
90 | const res = await fetch(url, { method: 'POST', body: form })
|
91 |
|
92 | if (res.ok) {
|
93 | const text = await res.text()
|
94 | log.debug('Response text:')
|
95 | log.group()
|
96 | log.debug(text)
|
97 | log.groupEnd()
|
98 |
|
99 |
|
100 | try {
|
101 | const obj = await this._parseXml(text)
|
102 | result.log = obj.crx.response.data.log
|
103 | const errorLines = [...new Set(result.log.split('\n').filter(line => line.startsWith('E')))]
|
104 |
|
105 |
|
106 | if (errorLines.length) {
|
107 | result.err = new Error('Error installing nodes:\n' + errorLines.join('\n'))
|
108 |
|
109 | } else if (obj.crx.response.status.code !== '200') {
|
110 | result.err = new Error(obj.crx.response.status.textNode)
|
111 | }
|
112 | } catch (err) {
|
113 |
|
114 | throw new Error('Unexpected response text format')
|
115 | }
|
116 | } else {
|
117 |
|
118 | result.err = new Error(res.statusText)
|
119 | }
|
120 | } catch (err) {
|
121 |
|
122 | result.err = err
|
123 | }
|
124 |
|
125 | return result
|
126 | }
|
127 |
|
128 | async _check (target) {
|
129 | try {
|
130 | const res = await fetch(target)
|
131 | return res.status === 200
|
132 | } catch (err) {
|
133 | log.debug(err.message)
|
134 | return false
|
135 | }
|
136 | }
|
137 |
|
138 | _parseXml (xml) {
|
139 | return new Promise(resolve => {
|
140 | xmlToJson().xmlToJson(xml, (err, json) => err ? resolve({}) : resolve(json))
|
141 | })
|
142 | }
|
143 | }
|
144 |
|
145 | module.exports = Pipeline
|