slipway-cli 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -0
- package/package.json +29 -0
- package/src/commands/db-create.js +55 -0
- package/src/commands/db-url.js +44 -0
- package/src/commands/deployments.js +72 -0
- package/src/commands/env-list.js +53 -0
- package/src/commands/env-set.js +63 -0
- package/src/commands/env-unset.js +62 -0
- package/src/commands/env.js +2 -0
- package/src/commands/environment-create.js +38 -0
- package/src/commands/environment-update.js +43 -0
- package/src/commands/environments.js +47 -0
- package/src/commands/init.js +94 -0
- package/src/commands/link.js +43 -0
- package/src/commands/login.js +205 -0
- package/src/commands/logout.js +12 -0
- package/src/commands/logs.js +88 -0
- package/src/commands/project-update.js +40 -0
- package/src/commands/projects.js +42 -0
- package/src/commands/run.js +46 -0
- package/src/commands/services.js +68 -0
- package/src/commands/slide.js +310 -0
- package/src/commands/terminal.js +46 -0
- package/src/commands/whoami.js +25 -0
- package/src/index.js +313 -0
- package/src/lib/api.js +136 -0
- package/src/lib/colors.js +30 -0
- package/src/lib/config.js +72 -0
- package/src/lib/prompt.js +102 -0
- package/src/lib/sse.js +102 -0
- package/src/lib/utils.js +122 -0
package/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# slipway
|
|
2
|
+
|
|
3
|
+
> The CLI for Slipway - the Sails-native deployment platform
|
|
4
|
+
|
|
5
|
+
## Zero Dependencies
|
|
6
|
+
|
|
7
|
+
This CLI has **no npm dependencies**. It uses only Node.js 22+ built-ins:
|
|
8
|
+
|
|
9
|
+
- `node:util` → `parseArgs` for argument parsing
|
|
10
|
+
- `node:readline` → interactive prompts
|
|
11
|
+
- `node:fs` → config storage in `~/.slipway/`
|
|
12
|
+
- Native `fetch` → HTTP requests
|
|
13
|
+
- Native `WebSocket` → log streaming, Helm REPL
|
|
14
|
+
|
|
15
|
+
This means instant startup, no supply chain risk, and zero maintenance overhead.
|
|
16
|
+
|
|
17
|
+
## Commands
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Deploy (slide into production!)
|
|
21
|
+
slipway slide # Primary command
|
|
22
|
+
slipway launch # Alias
|
|
23
|
+
slipway deploy # Alias
|
|
24
|
+
|
|
25
|
+
# Apps
|
|
26
|
+
slipway app:create myapp
|
|
27
|
+
slipway app:list
|
|
28
|
+
slipway app:info myapp
|
|
29
|
+
slipway app:destroy myapp
|
|
30
|
+
|
|
31
|
+
# Databases (unified)
|
|
32
|
+
slipway db:create mydb --type=postgres
|
|
33
|
+
slipway db:link mydb myapp # Auto-sets DATABASE_URL
|
|
34
|
+
slipway db:connect mydb # Opens psql/mysql/redis-cli
|
|
35
|
+
slipway db:list
|
|
36
|
+
slipway db:backup mydb
|
|
37
|
+
|
|
38
|
+
# Domains
|
|
39
|
+
slipway domain:add myapp example.com
|
|
40
|
+
slipway domain:list myapp
|
|
41
|
+
slipway domain:remove myapp example.com
|
|
42
|
+
|
|
43
|
+
# Environment
|
|
44
|
+
slipway env:set myapp KEY=value
|
|
45
|
+
slipway env:list myapp
|
|
46
|
+
|
|
47
|
+
# Operations
|
|
48
|
+
slipway helm myapp # Sails REPL (like Tinkerwell)
|
|
49
|
+
slipway logs myapp -t # Tail logs
|
|
50
|
+
slipway dev # Local dev mode
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Installation
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm install -g slipway
|
|
57
|
+
# or
|
|
58
|
+
npx slipway
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Part of the Slipway Suite
|
|
62
|
+
|
|
63
|
+
- **Slipway Dashboard** - The web UI (root of this monorepo)
|
|
64
|
+
- **sails-hook-slipway** - The Sails hook for Bridge, Helm, telemetry
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
*Where your apps slide into production.*
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "slipway-cli",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "CLI for Slipway - Deploy Sails apps with ease",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"slipway": "./src/index.js"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"slipway",
|
|
11
|
+
"sails",
|
|
12
|
+
"deploy",
|
|
13
|
+
"docker",
|
|
14
|
+
"cli"
|
|
15
|
+
],
|
|
16
|
+
"author": "Kelvin Omereshone <kelvin@sailscasts.com>",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/sailscastshq/slipway.git",
|
|
21
|
+
"directory": "packages/cli"
|
|
22
|
+
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=18.0.0"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { c } from '../lib/colors.js'
|
|
2
|
+
import { api } from '../lib/api.js'
|
|
3
|
+
import { isLoggedIn } from '../lib/config.js'
|
|
4
|
+
import { error, requireProject, createSpinner } from '../lib/utils.js'
|
|
5
|
+
|
|
6
|
+
export default async function dbCreate(options, positionals) {
|
|
7
|
+
if (!isLoggedIn()) {
|
|
8
|
+
error('Not logged in. Run `slipway login` first.')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const name = positionals[0]
|
|
12
|
+
if (!name) {
|
|
13
|
+
error('Please provide a database name. Usage: slipway db:create <name>')
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const project = requireProject()
|
|
17
|
+
const environment = options.env || 'production'
|
|
18
|
+
const dbType = options.type || 'postgresql'
|
|
19
|
+
const version = options.version || 'latest'
|
|
20
|
+
|
|
21
|
+
console.log()
|
|
22
|
+
console.log(` ${c.bold(c.highlight('Create Database'))}`)
|
|
23
|
+
console.log()
|
|
24
|
+
|
|
25
|
+
const spin = createSpinner(`Creating ${dbType} database "${name}"...`).start()
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const { service } = await api.services.create(project.project, environment, {
|
|
29
|
+
name,
|
|
30
|
+
type: dbType,
|
|
31
|
+
version
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
spin.succeed('Database created')
|
|
35
|
+
console.log()
|
|
36
|
+
console.log(` ${c.dim('Name:')} ${service.name}`)
|
|
37
|
+
console.log(` ${c.dim('Type:')} ${service.type}`)
|
|
38
|
+
console.log(` ${c.dim('Version:')} ${service.version}`)
|
|
39
|
+
console.log(` ${c.dim('Environment:')} ${environment}`)
|
|
40
|
+
console.log()
|
|
41
|
+
|
|
42
|
+
if (service.connectionUrl) {
|
|
43
|
+
console.log(` ${c.dim('Connection URL:')}`)
|
|
44
|
+
console.log(` ${c.highlight(service.connectionUrl)}`)
|
|
45
|
+
console.log()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
console.log(` ${c.dim('The connection URL has been added to your environment variables.')}`)
|
|
49
|
+
console.log(` ${c.dim('Use')} ${c.highlight(`slipway db:url ${name}`)} ${c.dim('to retrieve it later.')}`)
|
|
50
|
+
console.log()
|
|
51
|
+
} catch (err) {
|
|
52
|
+
spin.fail('Failed to create database')
|
|
53
|
+
error(err.message)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { c } from '../lib/colors.js'
|
|
2
|
+
import { api } from '../lib/api.js'
|
|
3
|
+
import { isLoggedIn } from '../lib/config.js'
|
|
4
|
+
import { error, requireProject, createSpinner } from '../lib/utils.js'
|
|
5
|
+
|
|
6
|
+
export default async function dbUrl(options, positionals) {
|
|
7
|
+
if (!isLoggedIn()) {
|
|
8
|
+
error('Not logged in. Run `slipway login` first.')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const name = positionals[0]
|
|
12
|
+
if (!name) {
|
|
13
|
+
error('Please provide a database name. Usage: slipway db:url <name>')
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const project = requireProject()
|
|
17
|
+
const environment = options.env || 'production'
|
|
18
|
+
|
|
19
|
+
const spin = createSpinner('Fetching database URL...').start()
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const { services } = await api.services.list(project.project, environment)
|
|
23
|
+
|
|
24
|
+
const database = services.find(s => s.name === name)
|
|
25
|
+
|
|
26
|
+
if (!database) {
|
|
27
|
+
spin.fail('Database not found')
|
|
28
|
+
error(`No database named "${name}" found in ${environment} environment.`)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
spin.stop()
|
|
32
|
+
|
|
33
|
+
console.log()
|
|
34
|
+
if (database.connectionUrl) {
|
|
35
|
+
console.log(database.connectionUrl)
|
|
36
|
+
} else {
|
|
37
|
+
console.log(` ${c.dim('No connection URL available for this database.')}`)
|
|
38
|
+
}
|
|
39
|
+
console.log()
|
|
40
|
+
} catch (err) {
|
|
41
|
+
spin.fail('Failed to fetch database URL')
|
|
42
|
+
error(err.message)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { c } from '../lib/colors.js'
|
|
2
|
+
import { api } from '../lib/api.js'
|
|
3
|
+
import { isLoggedIn } from '../lib/config.js'
|
|
4
|
+
import { error, requireProject, table, statusColor, formatDate } from '../lib/utils.js'
|
|
5
|
+
|
|
6
|
+
export default async function deployments(options) {
|
|
7
|
+
if (!isLoggedIn()) {
|
|
8
|
+
error('Not logged in. Run `slipway login` first.')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const project = requireProject()
|
|
12
|
+
|
|
13
|
+
console.log()
|
|
14
|
+
console.log(` ${c.bold(c.highlight('Recent Deployments'))}`)
|
|
15
|
+
console.log()
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
// Get environments first
|
|
19
|
+
const { environments } = await api.environments.list(project.project)
|
|
20
|
+
|
|
21
|
+
// Filter by environment if specified
|
|
22
|
+
const targetEnvs = options.env
|
|
23
|
+
? environments.filter(e => e.slug === options.env)
|
|
24
|
+
: environments
|
|
25
|
+
|
|
26
|
+
if (targetEnvs.length === 0) {
|
|
27
|
+
console.log(` ${c.dim('No environments found.')}`)
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Collect deployments from each environment
|
|
32
|
+
const allDeployments = []
|
|
33
|
+
for (const env of targetEnvs) {
|
|
34
|
+
const { environment } = await api.environments.get(project.project, env.slug)
|
|
35
|
+
if (environment.deployments) {
|
|
36
|
+
environment.deployments.forEach(d => {
|
|
37
|
+
allDeployments.push({
|
|
38
|
+
...d,
|
|
39
|
+
environment: env.slug
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Sort by date and limit
|
|
46
|
+
allDeployments.sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0))
|
|
47
|
+
const limited = allDeployments.slice(0, parseInt(options.limit) || 10)
|
|
48
|
+
|
|
49
|
+
if (limited.length === 0) {
|
|
50
|
+
console.log(` ${c.dim('No deployments yet. Run `slipway deploy` to create one.')}`)
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const rows = limited.map(d => [
|
|
55
|
+
d.id.substring(0, 8),
|
|
56
|
+
statusColor(d.status),
|
|
57
|
+
d.environment,
|
|
58
|
+
d.gitBranch || '-',
|
|
59
|
+
d.gitCommit ? d.gitCommit.substring(0, 7) : '-',
|
|
60
|
+
formatDate(d.createdAt)
|
|
61
|
+
])
|
|
62
|
+
|
|
63
|
+
table(
|
|
64
|
+
['ID', 'Status', 'Env', 'Branch', 'Commit', 'Date'],
|
|
65
|
+
rows
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
console.log()
|
|
69
|
+
} catch (err) {
|
|
70
|
+
error(err.message)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { c } from '../lib/colors.js'
|
|
2
|
+
import { api } from '../lib/api.js'
|
|
3
|
+
import { isLoggedIn } from '../lib/config.js'
|
|
4
|
+
import { error, requireProject } from '../lib/utils.js'
|
|
5
|
+
|
|
6
|
+
export default async function envList(options) {
|
|
7
|
+
if (!isLoggedIn()) {
|
|
8
|
+
error('Not logged in. Run `slipway login` first.')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const project = requireProject()
|
|
12
|
+
const environment = options.env || 'production'
|
|
13
|
+
|
|
14
|
+
console.log()
|
|
15
|
+
console.log(` ${c.bold(c.highlight('Environment Variables'))}`)
|
|
16
|
+
console.log(` ${c.dim(`${project.project} / ${environment}`)}`)
|
|
17
|
+
console.log()
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const { environment: env } = await api.environments.get(project.project, environment)
|
|
21
|
+
|
|
22
|
+
const vars = env.envVars || {}
|
|
23
|
+
const keys = Object.keys(vars).sort()
|
|
24
|
+
|
|
25
|
+
if (keys.length === 0) {
|
|
26
|
+
console.log(` ${c.dim('No environment variables set.')}`)
|
|
27
|
+
console.log(` ${c.dim('Use')} ${c.highlight('slipway env:set KEY=value')} ${c.dim('to add variables.')}`)
|
|
28
|
+
console.log()
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
for (const key of keys) {
|
|
33
|
+
const value = vars[key]
|
|
34
|
+
// Mask sensitive values
|
|
35
|
+
const masked = shouldMask(key) ? '••••••••' : value
|
|
36
|
+
console.log(` ${c.dim(key + '=')}${masked}`)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
console.log()
|
|
40
|
+
} catch (err) {
|
|
41
|
+
error(err.message)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Keys that should have their values masked
|
|
46
|
+
function shouldMask(key) {
|
|
47
|
+
const sensitivePatterns = [
|
|
48
|
+
'PASSWORD', 'SECRET', 'KEY', 'TOKEN', 'PRIVATE',
|
|
49
|
+
'CREDENTIAL', 'AUTH', 'API_KEY', 'APIKEY'
|
|
50
|
+
]
|
|
51
|
+
const upper = key.toUpperCase()
|
|
52
|
+
return sensitivePatterns.some(p => upper.includes(p))
|
|
53
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { c } from '../lib/colors.js'
|
|
2
|
+
import { api } from '../lib/api.js'
|
|
3
|
+
import { isLoggedIn } from '../lib/config.js'
|
|
4
|
+
import { error, requireProject, success, createSpinner } from '../lib/utils.js'
|
|
5
|
+
|
|
6
|
+
export default async function envSet(options, positionals) {
|
|
7
|
+
if (!isLoggedIn()) {
|
|
8
|
+
error('Not logged in. Run `slipway login` first.')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (positionals.length === 0) {
|
|
12
|
+
error('Please provide at least one KEY=value pair. Usage: slipway env:set KEY=value')
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const project = requireProject()
|
|
16
|
+
const environment = options.env || 'production'
|
|
17
|
+
|
|
18
|
+
// Parse KEY=value pairs
|
|
19
|
+
const vars = {}
|
|
20
|
+
for (const pair of positionals) {
|
|
21
|
+
const eqIndex = pair.indexOf('=')
|
|
22
|
+
if (eqIndex === -1) {
|
|
23
|
+
error(`Invalid format: "${pair}". Use KEY=value format.`)
|
|
24
|
+
}
|
|
25
|
+
const key = pair.substring(0, eqIndex)
|
|
26
|
+
const value = pair.substring(eqIndex + 1)
|
|
27
|
+
|
|
28
|
+
if (!key) {
|
|
29
|
+
error(`Invalid key in "${pair}". Key cannot be empty.`)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
vars[key] = value
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const spin = createSpinner('Setting environment variables...').start()
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
// Get current environment
|
|
39
|
+
const { environment: env } = await api.environments.get(project.project, environment)
|
|
40
|
+
|
|
41
|
+
// Merge with existing vars
|
|
42
|
+
const updatedVars = { ...env.envVars, ...vars }
|
|
43
|
+
|
|
44
|
+
// Update environment
|
|
45
|
+
await api.environments.update(project.project, environment, {
|
|
46
|
+
envVars: updatedVars
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
spin.succeed('Environment variables updated')
|
|
50
|
+
console.log()
|
|
51
|
+
|
|
52
|
+
for (const key of Object.keys(vars)) {
|
|
53
|
+
console.log(` ${c.success('+')} ${key}`)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
console.log()
|
|
57
|
+
console.log(` ${c.dim('Run')} ${c.highlight('slipway deploy')} ${c.dim('to apply changes.')}`)
|
|
58
|
+
console.log()
|
|
59
|
+
} catch (err) {
|
|
60
|
+
spin.fail('Failed to set environment variables')
|
|
61
|
+
error(err.message)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { c } from '../lib/colors.js'
|
|
2
|
+
import { api } from '../lib/api.js'
|
|
3
|
+
import { isLoggedIn } from '../lib/config.js'
|
|
4
|
+
import { error, requireProject, createSpinner } from '../lib/utils.js'
|
|
5
|
+
|
|
6
|
+
export default async function envUnset(options, positionals) {
|
|
7
|
+
if (!isLoggedIn()) {
|
|
8
|
+
error('Not logged in. Run `slipway login` first.')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (positionals.length === 0) {
|
|
12
|
+
error('Please provide at least one key to remove. Usage: slipway env:unset KEY1 KEY2')
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const project = requireProject()
|
|
16
|
+
const environment = options.env || 'production'
|
|
17
|
+
|
|
18
|
+
const spin = createSpinner('Removing environment variables...').start()
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
// Get current environment
|
|
22
|
+
const { environment: env } = await api.environments.get(project.project, environment)
|
|
23
|
+
|
|
24
|
+
// Remove specified keys
|
|
25
|
+
const updatedVars = { ...env.envVars }
|
|
26
|
+
const removed = []
|
|
27
|
+
|
|
28
|
+
for (const key of positionals) {
|
|
29
|
+
if (key in updatedVars) {
|
|
30
|
+
delete updatedVars[key]
|
|
31
|
+
removed.push(key)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (removed.length === 0) {
|
|
36
|
+
spin.stop()
|
|
37
|
+
console.log()
|
|
38
|
+
console.log(` ${c.dim('No matching variables found to remove.')}`)
|
|
39
|
+
console.log()
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Update environment
|
|
44
|
+
await api.environments.update(project.project, environment, {
|
|
45
|
+
envVars: updatedVars
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
spin.succeed('Environment variables removed')
|
|
49
|
+
console.log()
|
|
50
|
+
|
|
51
|
+
for (const key of removed) {
|
|
52
|
+
console.log(` ${c.error('-')} ${key}`)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
console.log()
|
|
56
|
+
console.log(` ${c.dim('Run')} ${c.highlight('slipway deploy')} ${c.dim('to apply changes.')}`)
|
|
57
|
+
console.log()
|
|
58
|
+
} catch (err) {
|
|
59
|
+
spin.fail('Failed to remove environment variables')
|
|
60
|
+
error(err.message)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { c } from '../lib/colors.js'
|
|
2
|
+
import { api } from '../lib/api.js'
|
|
3
|
+
import { isLoggedIn } from '../lib/config.js'
|
|
4
|
+
import { error, requireProject } from '../lib/utils.js'
|
|
5
|
+
|
|
6
|
+
export default async function environmentCreate(options, positionals) {
|
|
7
|
+
if (!isLoggedIn()) {
|
|
8
|
+
error('Not logged in. Run `slipway login` first.')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const project = requireProject()
|
|
12
|
+
|
|
13
|
+
const name = positionals[0]
|
|
14
|
+
if (!name) {
|
|
15
|
+
error('Please provide an environment name. Usage: slipway environment:create <name>')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const data = { name }
|
|
20
|
+
if (options.production) data.isProduction = true
|
|
21
|
+
if (options.domain) data.domain = options.domain
|
|
22
|
+
|
|
23
|
+
const { environment } = await api.environments.create(project.project, data)
|
|
24
|
+
|
|
25
|
+
console.log()
|
|
26
|
+
console.log(`${c.success('✓')} Environment created`)
|
|
27
|
+
console.log()
|
|
28
|
+
console.log(` ${c.dim('Name:')} ${environment.name}`)
|
|
29
|
+
console.log(` ${c.dim('Slug:')} ${environment.slug}`)
|
|
30
|
+
console.log(` ${c.dim('Type:')} ${environment.isProduction ? 'production' : 'staging'}`)
|
|
31
|
+
if (environment.domain) {
|
|
32
|
+
console.log(` ${c.dim('Domain:')} ${environment.domain}`)
|
|
33
|
+
}
|
|
34
|
+
console.log()
|
|
35
|
+
} catch (err) {
|
|
36
|
+
error(err.message)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { c } from '../lib/colors.js'
|
|
2
|
+
import { api } from '../lib/api.js'
|
|
3
|
+
import { isLoggedIn } from '../lib/config.js'
|
|
4
|
+
import { error, requireProject } from '../lib/utils.js'
|
|
5
|
+
|
|
6
|
+
export default async function environmentUpdate(options, positionals) {
|
|
7
|
+
if (!isLoggedIn()) {
|
|
8
|
+
error('Not logged in. Run `slipway login` first.')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const project = requireProject()
|
|
12
|
+
|
|
13
|
+
const slug = positionals[0]
|
|
14
|
+
if (!slug) {
|
|
15
|
+
error('Please provide an environment slug. Usage: slipway environment:update <slug> [options]')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const updates = {}
|
|
19
|
+
if (options.name !== undefined) updates.name = options.name
|
|
20
|
+
if (options.domain !== undefined) updates.domain = options.domain
|
|
21
|
+
if (options.production !== undefined) updates.isProduction = options.production
|
|
22
|
+
|
|
23
|
+
if (Object.keys(updates).length === 0) {
|
|
24
|
+
error('No updates provided. Use --name, --domain, or --production.')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const { environment } = await api.environments.update(project.project, slug, updates)
|
|
29
|
+
|
|
30
|
+
console.log()
|
|
31
|
+
console.log(`${c.success('✓')} Environment updated`)
|
|
32
|
+
console.log()
|
|
33
|
+
console.log(` ${c.dim('Name:')} ${environment.name}`)
|
|
34
|
+
console.log(` ${c.dim('Slug:')} ${environment.slug}`)
|
|
35
|
+
console.log(` ${c.dim('Type:')} ${environment.isProduction ? 'production' : 'staging'}`)
|
|
36
|
+
if (environment.domain) {
|
|
37
|
+
console.log(` ${c.dim('Domain:')} ${environment.domain}`)
|
|
38
|
+
}
|
|
39
|
+
console.log()
|
|
40
|
+
} catch (err) {
|
|
41
|
+
error(err.message)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { c } from '../lib/colors.js'
|
|
2
|
+
import { api } from '../lib/api.js'
|
|
3
|
+
import { isLoggedIn } from '../lib/config.js'
|
|
4
|
+
import { error, table, requireProject, formatDate } from '../lib/utils.js'
|
|
5
|
+
|
|
6
|
+
export default async function environments() {
|
|
7
|
+
if (!isLoggedIn()) {
|
|
8
|
+
error('Not logged in. Run `slipway login` first.')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const project = requireProject()
|
|
12
|
+
|
|
13
|
+
console.log()
|
|
14
|
+
console.log(` ${c.bold(c.highlight('Environments'))}`)
|
|
15
|
+
console.log(` ${c.dim(project.project)}`)
|
|
16
|
+
console.log()
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const { environments } = await api.environments.list(project.project)
|
|
20
|
+
|
|
21
|
+
if (environments.length === 0) {
|
|
22
|
+
console.log(` ${c.dim('No environments yet. Run `slipway environment:create` to create one.')}`)
|
|
23
|
+
console.log()
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const rows = environments.map(env => [
|
|
28
|
+
env.name,
|
|
29
|
+
c.dim(env.slug),
|
|
30
|
+
env.isProduction ? c.warn('production') : c.info('staging'),
|
|
31
|
+
env.domain || c.dim('—'),
|
|
32
|
+
env.app ? c.success('running') : c.dim('no app'),
|
|
33
|
+
formatDate(env.createdAt)
|
|
34
|
+
])
|
|
35
|
+
|
|
36
|
+
table(
|
|
37
|
+
['Name', 'Slug', 'Type', 'Domain', 'Status', 'Created'],
|
|
38
|
+
rows
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
console.log()
|
|
42
|
+
console.log(` ${c.dim(`${environments.length} environment${environments.length !== 1 ? 's' : ''}`)}`)
|
|
43
|
+
console.log()
|
|
44
|
+
} catch (err) {
|
|
45
|
+
error(err.message)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { join, basename } from 'node:path'
|
|
2
|
+
import { existsSync, readFileSync, appendFileSync } from 'node:fs'
|
|
3
|
+
import { c } from '../lib/colors.js'
|
|
4
|
+
import { api } from '../lib/api.js'
|
|
5
|
+
import { saveProjectConfig, getProjectConfig, isLoggedIn } from '../lib/config.js'
|
|
6
|
+
import { error, createSpinner, warn } from '../lib/utils.js'
|
|
7
|
+
import { prompt, confirm } from '../lib/prompt.js'
|
|
8
|
+
|
|
9
|
+
export default async function init(options) {
|
|
10
|
+
if (!isLoggedIn()) {
|
|
11
|
+
error('Not logged in. Run `slipway login` first.')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Check if already initialized
|
|
15
|
+
const existing = getProjectConfig()
|
|
16
|
+
if (existing) {
|
|
17
|
+
error(`Already linked to project "${existing.project}". Use \`slipway link\` to switch projects.`)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Check for package.json to determine project name
|
|
21
|
+
const packageJsonPath = join(process.cwd(), 'package.json')
|
|
22
|
+
let suggestedName = basename(process.cwd())
|
|
23
|
+
|
|
24
|
+
if (existsSync(packageJsonPath)) {
|
|
25
|
+
try {
|
|
26
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf8'))
|
|
27
|
+
suggestedName = pkg.name || suggestedName
|
|
28
|
+
} catch {
|
|
29
|
+
// Ignore parsing errors
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
console.log()
|
|
34
|
+
console.log(` ${c.bold(c.highlight('Initialize Slipway Project'))}`)
|
|
35
|
+
console.log()
|
|
36
|
+
|
|
37
|
+
// Get project details
|
|
38
|
+
const projectName = options.name || await prompt(' Project name', suggestedName)
|
|
39
|
+
|
|
40
|
+
// Check for Dockerfile
|
|
41
|
+
const dockerfilePath = join(process.cwd(), 'Dockerfile')
|
|
42
|
+
const hasDockerfile = existsSync(dockerfilePath)
|
|
43
|
+
|
|
44
|
+
if (!hasDockerfile) {
|
|
45
|
+
console.log()
|
|
46
|
+
warn('No Dockerfile found in current directory.')
|
|
47
|
+
console.log(` ${c.dim('Slipway requires a Dockerfile to build and deploy your app.')}`)
|
|
48
|
+
console.log()
|
|
49
|
+
|
|
50
|
+
const proceed = await confirm(' Continue anyway?', true)
|
|
51
|
+
|
|
52
|
+
if (!proceed) {
|
|
53
|
+
console.log(` ${c.dim('Run `slipway init` again after adding a Dockerfile.')}`)
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const spin = createSpinner('Creating project...').start()
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
// Create project via API
|
|
62
|
+
const { project } = await api.projects.create({
|
|
63
|
+
name: projectName,
|
|
64
|
+
dockerfilePath: 'Dockerfile'
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
// Save project config
|
|
68
|
+
saveProjectConfig({
|
|
69
|
+
project: project.slug,
|
|
70
|
+
projectId: project.id
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
spin.succeed('Project created')
|
|
74
|
+
console.log()
|
|
75
|
+
console.log(` ${c.dim('Project:')} ${project.name}`)
|
|
76
|
+
console.log(` ${c.dim('Slug:')} ${project.slug}`)
|
|
77
|
+
console.log(` ${c.dim('Environment:')} production`)
|
|
78
|
+
console.log()
|
|
79
|
+
console.log(` ${c.dim('Run')} ${c.highlight('slipway slide')} ${c.dim('to deploy your app.')}`)
|
|
80
|
+
console.log()
|
|
81
|
+
|
|
82
|
+
// Add .slipway.json to .gitignore if it exists
|
|
83
|
+
const gitignorePath = join(process.cwd(), '.gitignore')
|
|
84
|
+
if (existsSync(gitignorePath)) {
|
|
85
|
+
const gitignore = readFileSync(gitignorePath, 'utf8')
|
|
86
|
+
if (!gitignore.includes('.slipway.json')) {
|
|
87
|
+
appendFileSync(gitignorePath, '\n# Slipway\n.slipway.json\n')
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} catch (err) {
|
|
91
|
+
spin.fail('Failed to create project')
|
|
92
|
+
error(err.message)
|
|
93
|
+
}
|
|
94
|
+
}
|