Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(package): allow installation of github-releases #210

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion commands/package/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as install from './install.js'
import * as install from './install/index.js'
import * as list from './list.js'
import * as uninstall from './uninstall.js'

Expand Down
131 changes: 0 additions & 131 deletions commands/package/install.js

This file was deleted.

161 changes: 161 additions & 0 deletions commands/package/install/github.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import { connect } from '@existdb/node-exist'
import { got } from 'got'
import { gt } from 'semver'
// import { basename } from 'node:path'

import { isDBAdmin, getServerUrl } from '../../../utility/connection.js'
import { uploadMethod, removeTemporaryCollection, getInstalledVersion } from '../../../utility/package.js'

async function getRelease (api, owner, repo, release, assetFilter) {
const tag = release === 'latest' ? release : 'tags/' + release
const path = `repos/${owner}/${repo}/releases/${tag}`
const { assets, name } = await got.get(path, { prefixUrl: api }).json()
const f = assets.filter(assetFilter)
if (!f.length) {
throw Error('no matching asset found')
}
if (f.length > 1) {
throw Error('more than one matching asset found')
}
return {
xarName: f[0].name,
packageContents: f[0].browser_download_url,
releaseName: name
}
}

async function install (db, upload, xarName, contents, registry) {
const uploadResult = await upload(contents, xarName)
if (!uploadResult.success) {
throw new Error(uploadResult.error)
}

console.log('✔︎ uploaded')

const installResult = await db.app.install(xarName, registry)

if (!installResult.success) {
throw new Error(installResult.error)
}

const installationMessage = installResult.result.update ? 'updated' : 'installed'
console.log(`✔︎ ${installationMessage}`)

return 0
}

export const command = ['github-release <abbrev>', 'gh']
export const describe = 'Install a XAR package from a github release'
const options = {
rest: {
describe: 'force upload over REST API',
type: 'boolean'
},
xmlrpc: {
alias: 'rpc',
describe: 'force upload over XML-RPC API',
type: 'boolean'
},
f: {
alias: 'force',
describe: 'Force installation, skip version check'
},
release: {
describe: 'Install a specific release',
default: 'latest',
type: 'string'
},
owner: {
describe: 'The owner of the repository',
default: 'eXist-db',
type: 'string'
},
repo: {
describe: 'The name of the repository, if it differs from abbrev',
type: 'string'
},
registry: {
describe: 'Where to resolve dependencies from, if they are not already installed',
default: 'https://exist-db.org/exist/apps/public-repo/',
type: 'string'
},
api: {
describe: 'Connect to a different github server',
default: 'https://api.github.com',
type: 'string'
},
A: {
alias: 'asset',
describe: 'Pattern to match the package file.',
default: '<abbrev>-<version>.xar'
},
T: {
alias: 'tag-prefix',
describe: 'How to read the version from the associated git-tag',
default: 'v',
type: 'string'
},
debug: {
type: 'boolean',
default: false
}
}

export const builder = yargs => {
return yargs.options(options)
.conflicts('xmlrpc', 'rest')
}

export async function handler (argv) {
if (argv.help) {
return 0
}

// main
const {
abbrev, api, force, T, owner, release, registry,
connectionOptions, rest, xmlrpc
} = argv

const repo = argv.repo && argv.repo !== '' ? argv.repo : abbrev
// check permissions (and therefore implicitly the connection)
const db = connect(connectionOptions)
isDBAdmin(db)

const upload = await uploadMethod(db, connectionOptions, xmlrpc, rest)
const tagMatcher = new RegExp(`^${T}(?<version>.+)$`)

// const r = false ? new RegExp(`${asset}`) : new RegExp(`^${abbrev}.*\\.xar$`)
const r = new RegExp(`^${abbrev}.*\\.xar$`)
const assetFilter = a => { return r.test(a.name) }

try {
const installedVersion = await getInstalledVersion(db, abbrev)
const { xarName, packageContents, releaseName } = await getRelease(api, owner, repo, release, assetFilter)
const foundVersion = tagMatcher.exec(releaseName).groups.version
console.log(`Install ${abbrev} on ${getServerUrl(db)}`)
// if (debug) {
// console.debug('released:', valid(foundVersion))
// console.debug('installed:', valid(installedVersion))
// }

// if (valid(foundVersion) === null) {
// throw Error('Package does not have a valid semver "' + foundVersion + '"')
// }

const doUpdate = (installedVersion === null || force || gt(foundVersion, installedVersion))

if (!doUpdate) {
console.log(`Version ${installedVersion} is already installed, nothing to do.`)
return 0
}

const assetDownload = await got.get(packageContents)

await install(db, upload, xarName, assetDownload.rawBody, registry)
} finally {
await removeTemporaryCollection(db)
}

return 0
}
31 changes: 31 additions & 0 deletions commands/package/install/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import * as local from './local.js'
import * as github from './github.js'
// import * as registry from './registry.js'

const commands = [
local,
github
// registry
]

export const command = ['install <command>', 'i']
export const describe = 'Install XAR packages'
export async function handler (argv) {
if (argv.help) {
return 0
}
}

const options = {
registry: {
describe: 'Where to resolve dependencies from, if they are not already installed',
default: 'https://exist-db.org/exist/apps/public-repo/'
}
}

export const builder = function (yargs) {
return yargs
.options(options)
.command(commands)
.recommendCommands()
}
Loading
Loading