File size: 5,193 Bytes
e6c3c17 2417b56 e6c3c17 2417b56 e6c3c17 2417b56 e6c3c17 2417b56 e6c3c17 2417b56 e6c3c17 0fe6cc0 e6c3c17 0fe6cc0 e6c3c17 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
import { Webhooks, createNodeMiddleware } from '@octokit/webhooks'
import { exec as execute, spawn } from 'node:child_process'
import { promisify } from 'node:util'
import morgan from 'morgan'
import express from 'express'
import ini from 'ini'
const exec = promisify(execute)
const {
GIT_URL,
WEBHOOK_SECRET,
PORT = 7860,
} = process.env
const CONFIG_FILE = 'hf.conf' //TODO: mv to env
const REPO_NAME = extractRepoName(GIT_URL)
let childProcess = null
let config = null
let env = {}
const webhooks = new Webhooks({ secret: WEBHOOK_SECRET })
const logApp = createLogger('App')
const logWebhook = createLogger('Webhook')
if (!REPO_NAME) {
logApp('error', 'Please provide $GIT_URL environment variable.')
process.exit(1)
}
if (!WEBHOOK_SECRET) {
logApp('error', 'Please provide $WEBHOOK_SECRET environment variable.')
process.exit(1)
}
function extractRepoName(url) {
if (!url) return null
const name = url.split('/').pop()
return name.endsWith('.git') ? name.slice(0, -4) : name
}
function formatDate(date) {
const options = {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: true
}
return new Date(date).toLocaleString('en-US', options).replace(',', '')
}
function createLogger(context) {
return (level, message) => {
const timestamp = formatDate(new Date())
console.log(`[${timestamp}] [${level.toUpperCase()}] [${context}] ${message}`)
}
}
async function executeCommand(command, cwd = REPO_NAME) {
try {
const stzdCmd = command.replace(new RegExp(GIT_URL, 'g'), '****')
logApp('info', `Executing: ${stzdCmd}`)
const { stdout, stderr } = await exec(command, { cwd })
if (stdout) console.log(stdout)
if (stderr) console.error(stderr)
return true
} catch (error) {
const sanitizedC = command.replace(new RegExp(GIT_URL, 'g'), '****')
logApp('error', `Command failed: ${sanitizedC} - ${error.message}`)
return false
}
}
async function cloneRepository() {
logApp('info', 'Cloning repository...')
return await executeCommand(`git clone ${GIT_URL}`, '.')
}
async function pullLatestChanges() {
logApp('info', 'Pulling latest changes...')
return await executeCommand('git pull')
}
async function runSetupScripts(scripts) {
logApp('info', 'Running setup scripts...')
for (const script of scripts) {
const success = await executeCommand(script)
if (!success) return false
}
return true
}
async function buildApplication() {
logApp('info', 'Building application...')
if (!config) {
logApp('error', 'Configuration not loaded. Please clone the repository before building the application.')
return false
}
if (!(await pullLatestChanges())) return false
if (!(await runSetupScripts(config.script))) return false
return true
}
function validateConfig(config) {
if (!config) throw new Error("No config found in config file.")
if (!config.command) throw new Error("No Command found in config file.")
if (!config.script) throw new Error("No script for setup installation found in config file.")
}
async function loadConfiguration(filename) {
try {
const { stdout } = await exec(`cat ${filename}`, { cwd: REPO_NAME })
const obj = ini.parse(stdout)
validateConfig(obj.config)
config = obj.config
env = obj.env || {}
return true
} catch (error) {
logApp('error', `Failed to load configuration from ${filename}: ${error.message}`)
return false
}
}
async function startApplication(build = false) {
if (childProcess) {
logApp('info', 'Restarting application...')
childProcess.kill()
childProcess = null
} else {
logApp('info', 'Starting application...')
if (!(await cloneRepository())) return
}
if (build) {
if (!(await loadConfiguration(CONFIG_FILE))) return
if (!(await buildApplication())) return
}
const [command, ...args] = config.command.split(' ')
logApp('info', `Executing command: ${config.command}`)
childProcess = spawn(command, args, {
env: { ...process.env, ...env },
cwd: REPO_NAME,
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
})
childProcess.on('message', async (msg) => {
const action = msg.trim()
if (action === 'reset') {
await startApplication()
} else if (action === 'build') {
await startApplication(true)
} else if (action === 'pull') {
await pullLatestChanges()
} else if (action === 'setup') {
await runSetupScripts(config.script)
}
})
}
webhooks.onAny((event) => {
logWebhook('info', `Received event: ${event.name} with ID: ${event.id}`)
if (childProcess && event.name === 'push') {
childProcess.send('push='+JSON.stringify(event))
childProcess.emit('message', 'build')
}
})
function initializeServer() {
const app = express()
const middleware = createNodeMiddleware(webhooks, { path: '/webhook' })
app.use(morgan('combined'))
app.use(middleware)
app.get('/', (req, res) => {
res.json({ now: 'alive', message: "Hello_World" })
})
app.listen(PORT, () => {
logApp('info', `Server listening to port [${PORT}]`)
})
}
initializeServer()
startApplication(true) |