/**
* pgh deploy commands
*
* Subcommands: setup, status, trigger, log, disable, registry
*/
import { table, jsonOut, log, error } from '../output.js'
export async function deployCommand (client, sub, args, flags) {
switch (sub) {
case 'setup':
return deploySetup(client, args[0], flags)
case 'status':
return deployStatus(client, args[0], flags)
case 'trigger':
return deployTrigger(client, args[0], flags)
case 'log':
return deployLog(client, args[0], flags)
case 'disable':
return deployDisable(client, args[0], flags)
case 'registry':
return deployRegistry(client, flags)
default:
deployHelp()
}
}
async function deploySetup (client, repoName, flags) {
if (!repoName) error('Usage: pgh deploy setup <repo> --channel <name>')
const channel = flags.channel || repoName
const mode = flags.mode || 'git'
const autoRelease = flags['auto-release'] !== 'false' && flags.autoRelease !== 'false'
const autoSeed = flags['auto-seed'] !== 'false' && flags.autoSeed !== 'false'
const autoDeploy = flags['auto-deploy'] !== 'false' && flags.autoDeploy !== 'false'
const data = await client.post(`/api/deploy/config/${encodeURIComponent(repoName)}`, {
channel,
mode,
autoRelease,
autoSeed,
autoDeploy,
enabled: true
})
if (flags.json) {
jsonOut(data)
return
}
if (data.error) {
error(data.error)
return
}
log(`Deploy configured for ${repoName}`)
log(` Channel: ${channel}`)
log(` Mode: ${mode}`)
log(` Auto-release: ${autoRelease}`)
log(` Auto-seed: ${autoSeed}`)
log(` Auto-deploy: ${autoDeploy}`)
}
async function deployStatus (client, repoName, flags) {
if (!repoName) {
// List all repos with deploy config
const repos = await client.get('/api/repos')
const deployRepos = repos.filter(r => r.deploy && r.deploy.enabled)
if (flags.json) {
jsonOut(deployRepos.map(r => ({ name: r.name, ...r.deploy })))
return
}
if (deployRepos.length === 0) {
log('No repos configured for deploy.')
log('Run: pgh deploy setup <repo> --channel <name>')
return
}
table(deployRepos.map(r => ({
name: r.name,
channel: r.deploy.channel,
mode: r.deploy.mode || 'git',
version: r.deploy.lastCommitHash
? r.deploy.lastCommitHash.slice(0, 8)
: (r.deploy.lastVersion || '-'),
lastDeploy: r.deploy.lastDeploy ? new Date(r.deploy.lastDeploy).toLocaleString() : 'Never'
})), [
{ key: 'name', label: 'REPO', width: 25 },
{ key: 'channel', label: 'CHANNEL', width: 20 },
{ key: 'mode', label: 'MODE', width: 8 },
{ key: 'version', label: 'VERSION' },
{ key: 'lastDeploy', label: 'LAST DEPLOY', width: 25 }
])
return
}
const data = await client.get(`/api/deploy/status/${encodeURIComponent(repoName)}`)
if (flags.json) {
jsonOut(data)
return
}
if (!data.configured) {
log(`Deploy not configured for ${repoName}`)
log(`Run: pgh deploy setup ${repoName} --channel <name>`)
return
}
log(`Deploy Status: ${repoName}`)
log(` Channel: ${data.channel}`)
log(` Mode: ${data.mode || 'git'}`)
if (data.mode === 'git' || !data.mode || data.mode !== 'legacy') {
log(` Commit: ${data.lastCommitHash ? data.lastCommitHash.slice(0, 12) : '-'}`)
log(` Branch: ${data.lastBranch || 'release'}`)
log(` Last Deploy: ${data.lastDeploy ? new Date(data.lastDeploy).toLocaleString() : 'Never'}`)
if (data.registryApp) {
log(` Registry:`)
log(` Deployed by: ${data.registryApp.deployedBy ? data.registryApp.deployedBy.slice(0, 12) + '...' : '-'}`)
log(` Deployed at: ${data.registryApp.deployedAt ? new Date(data.registryApp.deployedAt).toLocaleString() : '-'}`)
}
} else {
log(` Version: ${data.lastVersion || '-'}`)
log(` Key: ${data.lastKey ? data.lastKey.slice(0, 16) + '...' : '-'}`)
log(` Seeding: ${data.seeding ? 'Yes' : 'No'}`)
log(` Last Deploy: ${data.lastDeploy ? new Date(data.lastDeploy).toLocaleString() : 'Never'}`)
if (data.info) {
log(` Release: ${data.info.release || '-'}`)
log(` Length: ${data.info.length || '-'}`)
}
}
}
async function deployTrigger (client, repoName, flags) {
if (!repoName) error('Usage: pgh deploy trigger <repo>')
const legacy = flags.legacy === true || flags.legacy === 'true'
log(`Deploying ${repoName}${legacy ? ' (legacy mode)' : ''}...`)
const data = await client.post(`/api/deploy/trigger/${encodeURIComponent(repoName)}`, {
legacy
})
if (flags.json) {
jsonOut(data)
return
}
if (data.error) {
error(data.error)
return
}
log(`\u2713 Deployed ${repoName}`)
if (data.commitHash) {
log(` Commit: ${data.commitHash.slice(0, 12)}`)
log(` Branch: ${data.branch || 'release'}`)
} else {
log(` Version: ${data.version || '-'}`)
}
log(` Channel: ${data.channel}`)
log(` Mode: ${data.mode || 'git'}`)
log(` Duration: ${data.duration}ms`)
if (data.key) log(` Key: ${data.key.slice(0, 16)}...`)
}
async function deployLog (client, repoName, flags) {
if (!repoName) error('Usage: pgh deploy log <repo>')
const entries = await client.get(`/api/deploy/log/${encodeURIComponent(repoName)}`)
if (flags.json) {
jsonOut(entries)
return
}
if (entries.length === 0) {
log(`No deploy history for ${repoName}`)
return
}
table(entries.map(e => ({
time: new Date(e.timestamp).toLocaleString(),
version: e.commitHash
? e.commitHash.slice(0, 8)
: (e.version || '-'),
channel: e.channel,
mode: e.mode || (e.commitHash ? 'git' : 'legacy'),
duration: (e.duration || 0) + 'ms'
})), [
{ key: 'time', label: 'TIME', width: 25 },
{ key: 'version', label: 'VERSION' },
{ key: 'channel', label: 'CHANNEL', width: 20 },
{ key: 'mode', label: 'MODE', width: 8 },
{ key: 'duration', label: 'DURATION' }
])
}
async function deployDisable (client, repoName, flags) {
if (!repoName) error('Usage: pgh deploy disable <repo>')
const data = await client.post(`/api/deploy/config/${encodeURIComponent(repoName)}`, {
channel: '_disabled',
enabled: false
})
if (flags.json) {
jsonOut(data)
return
}
if (data.error) {
error(data.error)
return
}
log(`Deploy disabled for ${repoName}`)
}
async function deployRegistry (client, flags) {
const data = await client.get('/api/deploy/registry')
if (flags.json) {
jsonOut(data)
return
}
if (!data.available) {
log('Deploy registry not available')
if (data.reason) log(` Reason: ${data.reason}`)
return
}
log('Deploy Registry')
log(` Bootstrap Key: ${data.bootstrapKey ? data.bootstrapKey.slice(0, 16) + '...' : '-'}`)
log(` Writable: ${data.writable ? 'Yes' : 'No'}`)
log(` Servers: ${data.servers ? data.servers.length : 0}`)
log(` Apps: ${data.appCount || 0}`)
if (data.servers && data.servers.length > 0) {
log('')
log('Registered Servers:')
for (const s of data.servers) {
log(` ${s.serverKey ? s.serverKey.slice(0, 16) + '...' : '?'} — ${s.hostname || s.instanceUrl || 'unknown'} (${s.status})`)
}
}
}
export function deployHelp () {
log(`Usage: pgh deploy <subcommand> [args] [flags]
Subcommands:
setup <repo> Configure deployment for a repo
status [repo] Show deploy status (all repos if no name)
trigger <repo> Execute deploy now
log <repo> Show deploy history
disable <repo> Remove deploy config
registry Show SharedDeployRegistry status
Setup Flags:
--channel NAME Pear channel name (default: repo name)
--mode git|legacy Deploy mode (default: git)
--auto-release false Disable auto-release after staging (legacy)
--auto-seed false Disable auto-seed after release (legacy)
--auto-deploy false Disable auto-deploy on git push
Trigger Flags:
--legacy Force legacy Hyperdrive deploy
Other Flags:
--json Output as JSON
Examples:
pgh deploy setup hyper-note --channel hyper-note
pgh deploy setup wirecore --channel wirecore --mode legacy
pgh deploy status
pgh deploy trigger hyper-note
pgh deploy log hyper-note
pgh deploy registry`)
}