Skip to content

Commit

Permalink
Merge pull request #14 from RoboVault/feat/cli-qol-fixes
Browse files Browse the repository at this point in the history
Feat/cli qol fixes
  • Loading branch information
hazelnutcloud authored Jul 11, 2023
2 parents 98d9d68 + 875ccf6 commit 97a6d98
Show file tree
Hide file tree
Showing 23 changed files with 639 additions and 868 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
# v0.4.16
- feat: enhance `arkiver list` command, see `arkiver list --help`
- feat: added 3 new commands to the CLI:
- `arkiver keygen` to generate a new API key
- `arkiver keyrm` to remove an API key
- `arkiver keyls` to list all API keys
- feat: adjusted `arkiver init` command
- feat: Add --gql-only option to arkiver start command to start only the graphQL server
- feat: Change EventHandler and BlockHandler return type to be Promise<void> | void
- fix: bug where deploying while logged out causes issues when trying to login again in the same command

# v0.4.15
- feat: populate relational entities on graphql endpoint
- feat: add overload to `manifest.addChain` and `chain.addContract` functions to pass in a callback function that takes in a DataSourceBuilder and ContractBuilder instance respectively
Expand Down
29 changes: 26 additions & 3 deletions cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import {
Command,
deploy,
init,
keygen,
keyls,
keyrm,
list,
login,
logout,
Expand All @@ -14,7 +17,7 @@ import {
} from './cli/mod.ts'
import 'https://deno.land/[email protected]/dotenv/load.ts'

export const version = 'v0.4.15'
export const version = 'v0.4.16'

const command = new Command()
.name('arkiver')
Expand Down Expand Up @@ -88,6 +91,7 @@ command
.option('--log-level <logLevel:string>', 'Log level', {
default: 'INFO',
})
.option('--gql-only', 'Only start GraphQL server')
.action(async (opts, ...args) => {
util.logHeader(version)
await checkVersion(version)
Expand All @@ -111,11 +115,30 @@ command
// list
command
.command('list', 'List all your arkives')
.action(async () => {
.option('-A, --all', 'List all arkives')
.option('-s, --status <status>', 'Filter by status')
.action(async (opts) => {
await checkVersion(version)
await list.action()
await list.action(opts)
})

// keygen
command
.command('keygen', 'Generate a new API key')
.action(keygen.action)

// keyrm
command
.command('keyrm', 'Delete an API key')
.arguments('<key:string>')
.action(async (_, key) => {
await keyrm.action(key)
})

command
.command('keyls', 'List all API keys')
.action(keyls.action)

if (import.meta.main) {
await command.parse(Deno.args)
}
3 changes: 3 additions & 0 deletions cli/deploy/cleanup.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { spinner } from '../spinner.ts'

export const cleanup = async (tempPath: string) => {
spinner().text = 'Cleaning up...'
await Deno.remove(tempPath, { recursive: true })
}
41 changes: 31 additions & 10 deletions cli/deploy/mod.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { parseArkiveManifest } from '../../mod.ts'
import { join, wait } from '../deps.ts'
import { ArkiveManifest, parseArkiveManifest } from '../../mod.ts'
import { join } from '../deps.ts'
import { spinner } from '../spinner.ts'
import { craftEndpoint, getSupabaseClient } from '../utils.ts'
import { cleanup } from './cleanup.ts'
import { pkg } from './pkg.ts'
import { upload } from './upload.ts'
Expand All @@ -12,9 +14,8 @@ export const action = async (

if (dev) return deployDev(options, directory)

const spinner = wait('Packaging...').start()

try {
spinner('Deploying...')
// package directory
const { fileName, tempPath } = await pkg(directory)

Expand All @@ -25,7 +26,8 @@ export const action = async (
} catch (error) {
throw new Error(`Error importing manifest.ts: ${error.message}`)
}
const manifest = manifestImport.default ?? manifestImport.manifest
const manifest: ArkiveManifest = manifestImport.default ??
manifestImport.manifest
if (!manifest) {
throw new Error(
`Manifest file must export a default or manifest object.`,
Expand All @@ -41,18 +43,37 @@ export const action = async (
throw new Error(`Invalid manifest: ${problems}`)
}

spinner.text = 'Uploading package...'
// upload package
await upload(fileName, tempPath, manifest, options)

spinner.text = 'Cleaning up...'
// cleanup
await cleanup(tempPath)

spinner.succeed('Deployed successfully!')
spinner().succeed(`Deployed successfully!`)

const client = getSupabaseClient()
const { data: userData, error: userError } = await client.auth.getUser()

if (userError) {
console.error(userError)
} else {
const { data, error } = await client.from('user_profile').select(
'username',
).eq('id', userData.user.id).single()

if (error) {
console.error(error)
} else {
const endpoint = craftEndpoint({
arkiveName: manifest.name,
environment: options.env ?? 'staging',
username: data.username,
})
console.log(`🚀 Arkive GraphQL endpoint ready at: ${endpoint}`)
}
}
} catch (error) {
spinner.fail('Deployment failed: ' + error.message)
console.error(error)
spinner().fail('Deployment failed: ' + error.message)
}

Deno.exit()
Expand Down
31 changes: 12 additions & 19 deletions cli/deploy/pkg.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
import { join } from 'https://deno.land/[email protected]/path/mod.ts'
import { $ } from '../deps.ts'
import { spinner } from '../spinner.ts'

export const pkg = async (dir: string) => {
const tempPath = await Deno.makeTempDir()
const fileName = crypto.randomUUID() + '.tar.gz'
const out = join(tempPath, fileName)
spinner().text = 'Packaging...'
try {
const tempPath = await Deno.makeTempDir()
const fileName = crypto.randomUUID() + '.tar.gz'
const out = join(tempPath, fileName)

const process = Deno.run({
cmd: ['tar', '-zcvf', out, '-C', dir, '.'],
stdout: 'piped',
stderr: 'piped',
})
await $`tar -zcvf ${out} -C ${dir} .`

const [status, err] = await Promise.all([
process.status(),
process.stderrOutput(),
])
if (status.code !== 0) {
const errMsg = `Failed to build package: ${new TextDecoder().decode(err)}`
throw new Error(errMsg)
return { fileName, tempPath }
} catch (error) {
spinner().fail('Packaging failed: ' + error)
Deno.exit(1)
}

process.close()

return { fileName, tempPath }
}
7 changes: 5 additions & 2 deletions cli/deploy/upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@ import { getSupabaseClient } from '../utils.ts'
import { login } from '../login/mod.ts'
import { SUPABASE_FUNCTIONS_URL } from '../constants.ts'
import { ArkiveManifest, JSONBigIntReplacer } from '../../mod.ts'
import { spinner } from '../spinner.ts'

export const upload = async (
pkgName: string,
tempPath: string,
manifest: ArkiveManifest,
options: { public?: true; major?: true; env?: string },
) => {
spinner().text = 'Uploading...'
const supabase = getSupabaseClient()
const sessionRes = await supabase.auth.getSession()

if (!sessionRes.data.session) {
spinner().info('Not logged in, logging in now...')
await login({}, supabase)
}

Expand Down Expand Up @@ -57,7 +60,7 @@ export const upload = async (
},
)
if (!res.ok) {
throw new Error(await res.text())
spinner().fail(`Upload failed: ${await res.text()}`)
Deno.exit(1)
}
console.log('Deployed successfully: ', await res.json())
}
6 changes: 3 additions & 3 deletions cli/deps.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
export { z } from 'https://esm.sh/[email protected]'
export { Command } from 'https://deno.land/x/cliffy@v0.25.7/command/mod.ts'
export { Command } from 'https://deno.land/x/cliffy@v1.0.0-rc.1/command/mod.ts'
export {
Input,
prompt,
Secret,
Select,
Toggle,
} from 'https://deno.land/x/cliffy@v0.25.7/prompt/mod.ts'
} from 'https://deno.land/x/cliffy@v1.0.0-rc.1/prompt/mod.ts'
export {
createClient,
SupabaseClient,
} from 'https://esm.sh/@supabase/[email protected]'
export { wait } from 'https://deno.land/x/[email protected].12/mod.ts'
export { Spinner, wait } from 'https://deno.land/x/[email protected].13/mod.ts'
export { default as $ } from 'https://deno.land/x/[email protected]/mod.ts'
export { delay } from 'https://deno.land/[email protected]/async/mod.ts'
export { join } from 'https://deno.land/[email protected]/path/mod.ts'
Expand Down
49 changes: 23 additions & 26 deletions cli/init/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ import { $, Input, join, prompt, Select, Toggle } from '../deps.ts'
export const action = async () => {
let pb = $.progress('Fetching templates...')

let templatesRes: Response

await pb.with(async () => {
templatesRes = await fetch(
const templatesRes = await pb.with(() =>
fetch(
'https://api.github.com/repos/RoboVault/robo-arkiver/contents/examples',
)
})
)

if (!templatesRes!.ok) {
console.log('Error fetching templates: ', templatesRes!.statusText)
Expand Down Expand Up @@ -52,8 +50,8 @@ export const action = async () => {
default: templateNames[0].value,
},
{
name: 'vscode',
message: 'Are you using VSCode?',
name: 'git',
message: 'Initialize git repo?',
type: Toggle,
default: true,
},
Expand All @@ -74,8 +72,8 @@ export const action = async () => {
)

await $`git remote add origin https://github.com/RoboVault/robo-arkiver && git pull origin main && rm -rf .git`
.quiet('both')
.cwd(newDir)
.quiet('stdout')

// traverse the template directory and move all files to the root
for await (
Expand All @@ -88,38 +86,37 @@ export const action = async () => {

await Deno.remove(join(newDir, 'examples'), { recursive: true })

if (arkive.vscode) {
const dir = arkive.dir ?? defaultPath
await Deno.mkdir(join(Deno.cwd(), dir, '.vscode'))
const dir = arkive.dir ?? defaultPath
await Deno.mkdir(join(Deno.cwd(), dir, '.vscode'))

const vscode = `{
const vscode = `{
"deno.enable": true,
"deno.unstable": true
}`
await Deno.writeTextFile(
join(Deno.cwd(), dir, '.vscode', 'settings.json'),
vscode,
)
await Deno.writeTextFile(
join(Deno.cwd(), dir, '.vscode', 'settings.json'),
vscode,
)

const gitignore = `/.vscode
const gitignore = `/.vscode
/.vscode/*
/.vscode/**/*
`
await Deno.writeTextFile(
join(Deno.cwd(), dir, '.gitignore'),
gitignore,
)
}
await Deno.writeTextFile(
join(Deno.cwd(), dir, '.gitignore'),
gitignore,
)

await $`git init && git add . && git commit -m "Initial commit"`
.cwd(newDir)
.quiet('stdout')
if (arkive.git) {
await $`git init && git add . && git commit -m "Initial commit"`
.cwd(newDir)
.quiet('stdout')
}
} catch (e) {
$.logError(`Error initializing arkive: ${e}`)
return
}

// spinner.succeed('Initialized arkive')
pb.finish()
$.logStep('Initialized arkive')
}
22 changes: 22 additions & 0 deletions cli/key/delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { spinner } from '../spinner.ts'
import { getSupabaseClient } from '../utils.ts'

export const action = async (key: string) => {
spinner(`Deleting key ${key}`)

const client = getSupabaseClient()

const { error } = await client.functions.invoke('api-key', {
method: 'DELETE',
body: {
apiKey: key,
},
})

if (error) {
spinner().fail(`Failed to delete key: ${error.message}`)
return
}

spinner().succeed(`Successfully deleted key: ${key}`)
}
29 changes: 29 additions & 0 deletions cli/key/list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { spinner } from '../spinner.ts'
import { getSupabaseClient } from '../utils.ts'

export const action = async () => {
spinner('Fetching API keys')

const client = getSupabaseClient()

const { data, error } = await client.functions.invoke<{ api_key: string }[]>(
'api-key',
{
method: 'GET',
},
)

if (error) {
spinner().fail(`Failed to fetch keys: ${error.message}`)
return
}

if (!data) {
spinner().fail(`Failed to fetch keys: no data returned`)
return
}

spinner().succeed(`Successfully fetched keys:`)

console.table(data.map((d) => ({ key: d.api_key })))
}
Loading

0 comments on commit 97a6d98

Please sign in to comment.