Add getApp command and gitUrl helper

This commit is contained in:
Chris Wanstrath 2026-03-01 14:57:39 -08:00
parent c0b48c03da
commit baa3712fa2
5 changed files with 42 additions and 3 deletions

View File

@ -2,6 +2,7 @@ export { cronList, cronLog, cronRun, cronStatus } from './cron'
export { envList, envRm, envSet } from './env' export { envList, envRm, envSet } from './env'
export { logApp } from './logs' export { logApp } from './logs'
export { export {
getApp,
infoApp, infoApp,
listApps, listApps,
newApp, newApp,

View File

@ -4,7 +4,7 @@ import color from 'kleur'
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs' import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'
import { basename, join } from 'path' import { basename, join } from 'path'
import { buildAppUrl } from '@urls' import { buildAppUrl } from '@urls'
import { del, get, getManifest, HOST, post } from '../http' import { del, get, getManifest, gitUrl, HOST, post } from '../http'
import { confirm, prompt } from '../prompts' import { confirm, prompt } from '../prompts'
import { resolveAppName } from '../name' import { resolveAppName } from '../name'
@ -179,7 +179,7 @@ export async function newApp(name: string | undefined, options: NewAppOptions) {
await run(['git', 'init']) await run(['git', 'init'])
await run(['git', 'add', '.']) await run(['git', 'add', '.'])
await run(['git', 'commit', '-m', 'init']) await run(['git', 'commit', '-m', 'init'])
await run(['git', 'remote', 'add', 'toes', `${HOST}/tool/git/${appName}`]) await run(['git', 'remote', 'add', 'toes', gitUrl(appName)])
await run(['git', 'push', 'toes', 'main']) await run(['git', 'push', 'toes', 'main'])
console.log(color.green(`✓ Created ${appName}`)) console.log(color.green(`✓ Created ${appName}`))
@ -188,6 +188,29 @@ export async function newApp(name: string | undefined, options: NewAppOptions) {
} }
} }
export async function getApp(name: string, directory?: string) {
const target = directory ?? name
if (existsSync(target)) {
console.error(`Directory already exists: ${target}`)
return
}
const url = gitUrl(name)
const args = ['git', 'clone', url]
if (directory) args.push(directory)
const proc = Bun.spawn(args, { stdout: 'inherit', stderr: 'inherit' })
const exitCode = await proc.exited
if (exitCode !== 0) {
console.error(color.red(`Failed to clone ${name}`))
return
}
console.log(color.green(`✓ Cloned ${name}`))
console.log(`\n cd ${target}\n bun install`)
}
export async function openApp(arg?: string) { export async function openApp(arg?: string) {
const name = resolveAppName(arg) const name = resolveAppName(arg)
if (!name) return if (!name) return

View File

@ -1,4 +1,5 @@
import type { Manifest } from '@types' import type { Manifest } from '@types'
import { buildAppUrl } from '@urls'
import { AsyncLocalStorage } from 'node:async_hooks' import { AsyncLocalStorage } from 'node:async_hooks'
const DEFAULT_HOST = process.env.DEV ? 'http://localhost:3000' : 'http://toes.local' const DEFAULT_HOST = process.env.DEV ? 'http://localhost:3000' : 'http://toes.local'
@ -20,6 +21,8 @@ export const HOST = process.env.TOES_URL
? normalizeUrl(process.env.TOES_URL) ? normalizeUrl(process.env.TOES_URL)
: DEFAULT_HOST : DEFAULT_HOST
export const gitUrl = (name: string) => `${buildAppUrl('git', HOST)}/${name}`
export const getSignal = () => signalStore.getStore() export const getSignal = () => signalStore.getStore()
export function withSignal<T>(signal: AbortSignal, fn: () => T): T { export function withSignal<T>(signal: AbortSignal, fn: () => T): T {

View File

@ -11,6 +11,7 @@ import {
envList, envList,
envRm, envRm,
envSet, envSet,
getApp,
infoApp, infoApp,
listApps, listApps,
logApp, logApp,
@ -75,6 +76,14 @@ program
.option('--spa', 'single-page app with client-side rendering') .option('--spa', 'single-page app with client-side rendering')
.action(newApp) .action(newApp)
program
.command('get')
.helpGroup('Apps:')
.description('Clone an app from the server')
.argument('<name>', 'app name')
.argument('[directory]', 'target directory (defaults to app name)')
.action(getApp)
program program
.command('open') .command('open')
.helpGroup('Apps:') .helpGroup('Apps:')

View File

@ -1,4 +1,5 @@
import { APPS_DIR, TOES_DIR, TOES_URL, allApps, appendLog, getLogDates, onChange, readLogs, registerApp, renameApp, restartApp, startApp, stopApp, updateAppIcon } from '$apps' import { APPS_DIR, TOES_DIR, TOES_URL, allApps, appendLog, getLogDates, onChange, readLogs, registerApp, renameApp, restartApp, startApp, stopApp, updateAppIcon } from '$apps'
import { buildAppUrl } from '@urls'
import { isTunnelsAvailable, shareApp, unshareApp } from '../tunnels' import { isTunnelsAvailable, shareApp, unshareApp } from '../tunnels'
import type { App as BackendApp } from '$apps' import type { App as BackendApp } from '$apps'
import type { App as SharedApp } from '@types' import type { App as SharedApp } from '@types'
@ -7,6 +8,8 @@ import { Hype } from '@because/hype'
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'fs' import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'fs'
import { dirname, join } from 'path' import { dirname, join } from 'path'
const gitUrl = (name: string) => `${buildAppUrl('git', TOES_URL)}/${name}`
const router = Hype.router() const router = Hype.router()
// BackendApp -> SharedApp // BackendApp -> SharedApp
@ -143,7 +146,7 @@ router.post('/', async c => {
await run(['git', 'init']) await run(['git', 'init'])
await run(['git', 'add', '.']) await run(['git', 'add', '.'])
await run(['git', 'commit', '-m', 'init']) await run(['git', 'commit', '-m', 'init'])
await run(['git', 'remote', 'add', 'toes', `${TOES_URL}/tool/git/${name}`]) await run(['git', 'remote', 'add', 'toes', gitUrl(name)])
const exitCode = await run(['git', 'push', 'toes', 'main']) const exitCode = await run(['git', 'push', 'toes', 'main'])
if (exitCode !== 0) { if (exitCode !== 0) {