hana-cli
Version:
HANA Developer Command Line Interface
136 lines (119 loc) • 5.24 kB
JavaScript
// @ts-check
import * as baseLite from '../utils/base-lite.js'
import * as cf from '../utils/cf.js'
import * as btp from '../utils/btp.js'
import { buildDocEpilogue } from '../utils/doc-linker.js'
export const command = 'version'
export const aliases = 'ver'
export const describe = baseLite.bundle.getText("version")
export const builder = (yargs) => yargs.options(baseLite.getBuilder({}, false)).wrap(160).example('hana-cli version', baseLite.bundle.getText("version")).wrap(160).epilog(buildDocEpilogue('version', 'system-tools', ['systemInfo', 'status']))
export async function handler(argv) {
const base = await import('../utils/base.js')
base.promptHandler(argv, verOutput, {}, false)
}
export async function verOutput() {
const base = await import('../utils/base.js')
base.debug('verOutput')
base.startSpinnerInt()
const [{ highlight }, { default: latestVersion }] =
await Promise.all([
import('cli-highlight'),
import('latest-version')
])
const colors = baseLite.colors
const log = console.log
const info = await getVersion()
base.stopSpinnerInt()
Object.keys(info).forEach(key => log(highlight(`${key}: ${info[key]}`)))
console.log(baseLite.bundle.getText("version.node", [colors.green(process.version)]))
console.log(baseLite.bundle.getText("version.changelog", [colors.blue('https://github.com/SAP-samples/hana-developer-cli-tool-example/blob/main/CHANGELOG.md')]))
const selfVersion = await getLatestHanaCliVersionWithTimeout(latestVersion)
console.log(baseLite.bundle.getText("version.latestAvailable", [colors.green(selfVersion || baseLite.bundle.getText("version.latestUnavailable"))]))
if (selfVersion && info['hana-cli'] < selfVersion) {
console.log(`${colors.red(baseLite.bundle.getText("version.outOfDate"))} ${baseLite.bundle.getText("version.upgradeHint", [colors.green('npm upgrade -g hana-cli')])}`)
}
// No need to call base.end() as there's no DB connection to clean up
// Let the process exit naturally to avoid Windows libuv assertion errors
}
export function version4(pkgPath = '..', info = {}, parentPath) {
baseLite.debug('version4')
try {
const pkj = baseLite.require(pkgPath + '/package.json')
const name = pkj.name || pkgPath
if (info[name]) return // safeguard against circular dependencies
info[name] = pkj.version
// recurse sap packages in dependencies...
for (let dep in pkj.dependencies) if (
dep.startsWith('@sap/') || dep === 'sap-hdb-promisfied' || dep === 'hdb' || dep.startsWith('@cap-js')
) version4(dep, info, pkgPath)
for (let dep in pkj.peerDependencies) if (
dep.startsWith('@sap/') || dep === 'sap-hdb-promisfied' || dep === 'hdb' || dep.startsWith('@cap-js')
) version4(dep, info, pkgPath)
} catch (e) {
if (e.code !== 'MODULE_NOT_FOUND') info[pkgPath] = '-- missing --' // unknown error
else if (parentPath) version4(parentPath + '/node_modules/' + pkgPath, info)
}
return info
}
export async function getVersion() {
const base = await import('../utils/base.js')
base.debug('version')
const [{ URL }, { fileURLToPath }] = await Promise.all([
import('url'),
import('url')
])
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const info = version4()
try {
let cfVer = await cf.getVersion()
cfVer = cfVer.replace(/(\r\n|\n|\r)/gm, "")
info['cf-cli'] = cfVer
} catch (error) {
info['cf-cli'] = baseLite.bundle.getText("version.cfCliMissing")
}
try {
let btpVer = await btp.getVersion()
btpVer = btpVer.replace(/(\r\n|\n|\r)/gm, "")
info['btp-cli'] = btpVer
} catch (error) {
info['btp-cli'] = baseLite.bundle.getText("version.btpCliMissing")
}
Object.defineProperty(info, 'home', { value: __dirname })
info['hana-cli home'] = info.home
Object.defineProperty(info, 'initialHome', { value: base.hanaBin ? base.hanaBin : '' })
if (process.env.DEBUG) info['hana-cli initial home'] = info.initialHome
return info
}
export async function getVersionUI() {
const info = await getVersion()
const { default: latestVersion } = await import('latest-version')
// Add Node.js version
info['Node.js'] = process.version
// Add latest version
const selfVersion = await getLatestHanaCliVersionWithTimeout(latestVersion)
info['latestVersion'] = selfVersion || baseLite.bundle.getText("version.latestUnavailable")
return info
}
/**
* Resolve latest hana-cli version from npm with a bounded timeout.
* Prevents command hangs in slow/offline environments.
* @param {(pkg: string) => Promise<string>} latestVersion
* @param {number} timeoutMs
* @returns {Promise<string|null>}
*/
async function getLatestHanaCliVersionWithTimeout(latestVersion, timeoutMs = 8000) {
try {
/** @type {NodeJS.Timeout | undefined} */
let timeoutHandle
const timeoutPromise = new Promise((_, reject) => {
timeoutHandle = setTimeout(() => reject(new Error('latest-version timeout')), timeoutMs)
})
const latest = await Promise.race([latestVersion('hana-cli'), timeoutPromise])
if (timeoutHandle) {
clearTimeout(timeoutHandle)
}
return typeof latest === 'string' && latest.length > 0 ? latest : null
} catch {
return null
}
}