@pushto/cli 0.0.1
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/.turbo/turbo-build.log +4 -0
- package/bin/pushto.js +2 -0
- package/dist/commands/deploy.d.ts +2 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +46 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +50 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +87 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logs.d.ts +2 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +21 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +48 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api.d.ts +2 -0
- package/dist/lib/api.d.ts.map +1 -0
- package/dist/lib/api.js +17 -0
- package/dist/lib/api.js.map +1 -0
- package/dist/lib/config.d.ts +14 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +19 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/resolve-slug.d.ts +7 -0
- package/dist/lib/resolve-slug.d.ts.map +1 -0
- package/dist/lib/resolve-slug.js +24 -0
- package/dist/lib/resolve-slug.js.map +1 -0
- package/package.json +28 -0
- package/src/commands/deploy.ts +125 -0
- package/src/commands/doctor.ts +160 -0
- package/src/commands/domain.ts +54 -0
- package/src/commands/env.ts +128 -0
- package/src/commands/init.ts +65 -0
- package/src/commands/login.ts +97 -0
- package/src/commands/logs.ts +24 -0
- package/src/commands/open.ts +27 -0
- package/src/commands/rollback.ts +43 -0
- package/src/commands/status.ts +58 -0
- package/src/index.ts +88 -0
- package/src/lib/api.ts +20 -0
- package/src/lib/config.ts +30 -0
- package/src/lib/resolve-slug.ts +23 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { getToken } from '../lib/config';
|
|
4
|
+
import { resolveSlug } from '../lib/resolve-slug';
|
|
5
|
+
import { api } from '../lib/api';
|
|
6
|
+
|
|
7
|
+
export const rollback = async (version?: string): Promise<void> => {
|
|
8
|
+
const token = getToken();
|
|
9
|
+
if (!token) {
|
|
10
|
+
console.log(chalk.red('> not logged in.'));
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const slug = resolveSlug();
|
|
15
|
+
if (!slug) {
|
|
16
|
+
console.log(chalk.red('> no project here. run pushto init first.'));
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const spinner = ora('rolling back...').start();
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
// TODO: when build engine is live, this will call the rollback API
|
|
24
|
+
// which restores the previous Fly Machine image
|
|
25
|
+
spinner.info(chalk.yellow('> rollback is ready to wire up.'));
|
|
26
|
+
console.log();
|
|
27
|
+
console.log(chalk.dim(' when the build engine is live, this command will:'));
|
|
28
|
+
console.log(chalk.dim(' 1. find the last working deployment'));
|
|
29
|
+
console.log(chalk.dim(' 2. restore that container image'));
|
|
30
|
+
console.log(chalk.dim(' 3. update DNS to point to it'));
|
|
31
|
+
console.log(chalk.dim(' 4. mark the current deploy as rolled back'));
|
|
32
|
+
console.log();
|
|
33
|
+
if (version) {
|
|
34
|
+
console.log(chalk.dim(` target version: v${version}`));
|
|
35
|
+
} else {
|
|
36
|
+
console.log(chalk.dim(' target: previous working deploy'));
|
|
37
|
+
}
|
|
38
|
+
console.log(chalk.dim(" you're early. this will work soon."));
|
|
39
|
+
} catch {
|
|
40
|
+
spinner.fail(chalk.red('> rollback failed.'));
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { getToken } from '../lib/config';
|
|
3
|
+
import { api } from '../lib/api';
|
|
4
|
+
import { resolveSlug } from '../lib/resolve-slug';
|
|
5
|
+
|
|
6
|
+
const STATUS_COLORS: Record<string, (s: string) => string> = {
|
|
7
|
+
CREATED: chalk.dim,
|
|
8
|
+
BUILDING: chalk.yellow,
|
|
9
|
+
DEPLOYING: chalk.yellow,
|
|
10
|
+
LIVE: chalk.green,
|
|
11
|
+
SLEEPING: chalk.cyan,
|
|
12
|
+
FAILED: chalk.red,
|
|
13
|
+
SUSPENDED: chalk.red,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const status = async (): Promise<void> => {
|
|
17
|
+
const token = getToken();
|
|
18
|
+
if (!token) {
|
|
19
|
+
console.log(chalk.red('> not logged in.'));
|
|
20
|
+
console.log(chalk.dim(' run ') + chalk.cyan('pushto login') + chalk.dim(' first.'));
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const slug = resolveSlug();
|
|
25
|
+
if (!slug) {
|
|
26
|
+
console.log(chalk.red('> no project here.'));
|
|
27
|
+
console.log(chalk.dim(' run ') + chalk.cyan('pushto init <name>') + chalk.dim(' first.'));
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const res = await api(`/cli/projects/${slug}`);
|
|
33
|
+
|
|
34
|
+
if (!res.ok) {
|
|
35
|
+
const data = (await res.json()) as { error?: string };
|
|
36
|
+
console.log(chalk.red(`> ${data.error ?? 'something went wrong.'}`));
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const project = (await res.json()) as {
|
|
41
|
+
name: string;
|
|
42
|
+
status: string;
|
|
43
|
+
subdomain: string;
|
|
44
|
+
customDomain?: string | null;
|
|
45
|
+
};
|
|
46
|
+
const colorFn = STATUS_COLORS[project.status] ?? chalk.dim;
|
|
47
|
+
|
|
48
|
+
console.log(chalk.green(`> ${project.name}`));
|
|
49
|
+
console.log(chalk.dim(' status: ') + colorFn(project.status.toLowerCase()));
|
|
50
|
+
console.log(chalk.dim(' url: ') + chalk.cyan(`${project.subdomain}.pushto.host`));
|
|
51
|
+
if (project.customDomain) {
|
|
52
|
+
console.log(chalk.dim(' domain: ') + chalk.cyan(project.customDomain));
|
|
53
|
+
}
|
|
54
|
+
} catch {
|
|
55
|
+
console.log(chalk.red('> could not reach pushto.host. check your internet.'));
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { init } from './commands/init';
|
|
5
|
+
import { deploy } from './commands/deploy';
|
|
6
|
+
import { login } from './commands/login';
|
|
7
|
+
import { logs } from './commands/logs';
|
|
8
|
+
import { status } from './commands/status';
|
|
9
|
+
import { doctor } from './commands/doctor';
|
|
10
|
+
import { env } from './commands/env';
|
|
11
|
+
import { open } from './commands/open';
|
|
12
|
+
import { rollback } from './commands/rollback';
|
|
13
|
+
import { domain } from './commands/domain';
|
|
14
|
+
|
|
15
|
+
const program = new Command();
|
|
16
|
+
|
|
17
|
+
program
|
|
18
|
+
.name('pushto')
|
|
19
|
+
.description(chalk.green('pushto') + chalk.dim(' — deploy from your terminal. no yaml. no drama.'))
|
|
20
|
+
.version('0.0.1')
|
|
21
|
+
.action(async () => {
|
|
22
|
+
await deploy();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
program
|
|
26
|
+
.command('init <name>')
|
|
27
|
+
.description('create a new project')
|
|
28
|
+
.action(async (name: string) => {
|
|
29
|
+
await init(name);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
program
|
|
33
|
+
.command('login')
|
|
34
|
+
.description('authenticate with pushto.host')
|
|
35
|
+
.action(async () => {
|
|
36
|
+
await login();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
program
|
|
40
|
+
.command('logs')
|
|
41
|
+
.description('stream logs from your project')
|
|
42
|
+
.action(async () => {
|
|
43
|
+
await logs();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
program
|
|
47
|
+
.command('status')
|
|
48
|
+
.description('check your project status')
|
|
49
|
+
.action(async () => {
|
|
50
|
+
await status();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
program
|
|
54
|
+
.command('doctor')
|
|
55
|
+
.description('check if your project is ready to deploy')
|
|
56
|
+
.action(async () => {
|
|
57
|
+
await doctor();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
program
|
|
61
|
+
.command('env <action> [args...]')
|
|
62
|
+
.description('manage environment variables (set, list, pull, rm)')
|
|
63
|
+
.action(async (action: string, args: string[]) => {
|
|
64
|
+
await env(action, args);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
program
|
|
68
|
+
.command('open')
|
|
69
|
+
.description('open your live site in the browser')
|
|
70
|
+
.action(async () => {
|
|
71
|
+
await open();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
program
|
|
75
|
+
.command('rollback [version]')
|
|
76
|
+
.description('rollback to a previous deployment')
|
|
77
|
+
.action(async (version?: string) => {
|
|
78
|
+
await rollback(version);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
program
|
|
82
|
+
.command('domain <action> [domain]')
|
|
83
|
+
.description('manage custom domains (add, remove)')
|
|
84
|
+
.action(async (action: string, domainName?: string) => {
|
|
85
|
+
await domain(action, domainName);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
program.parse();
|
package/src/lib/api.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { config, getToken } from './config';
|
|
2
|
+
|
|
3
|
+
export const api = async (path: string, options: RequestInit = {}): Promise<Response> => {
|
|
4
|
+
const baseUrl = config.get('apiUrl');
|
|
5
|
+
const token = getToken();
|
|
6
|
+
|
|
7
|
+
const headers: Record<string, string> = {
|
|
8
|
+
'Content-Type': 'application/json',
|
|
9
|
+
...((options.headers as Record<string, string>) ?? {}),
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
if (token) {
|
|
13
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return fetch(`${baseUrl}/api${path}`, {
|
|
17
|
+
...options,
|
|
18
|
+
headers,
|
|
19
|
+
});
|
|
20
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import Conf from 'conf';
|
|
2
|
+
|
|
3
|
+
interface PushtoConfig {
|
|
4
|
+
token?: string;
|
|
5
|
+
apiUrl: string;
|
|
6
|
+
projectSlug?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const config = new Conf<PushtoConfig>({
|
|
10
|
+
projectName: 'pushto',
|
|
11
|
+
defaults: {
|
|
12
|
+
apiUrl: 'https://pushto.host',
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const getToken = (): string | undefined => config.get('token');
|
|
17
|
+
|
|
18
|
+
export const setToken = (token: string): void => {
|
|
19
|
+
config.set('token', token);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const clearToken = (): void => {
|
|
23
|
+
config.delete('token');
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const getProjectSlug = (): string | undefined => config.get('projectSlug');
|
|
27
|
+
|
|
28
|
+
export const setProjectSlug = (slug: string): void => {
|
|
29
|
+
config.set('projectSlug', slug);
|
|
30
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { getProjectSlug } from './config';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Resolve the project slug from:
|
|
7
|
+
* 1. .pushto file in the current directory
|
|
8
|
+
* 2. Global config (last `pushto init`)
|
|
9
|
+
*/
|
|
10
|
+
export const resolveSlug = (): string | undefined => {
|
|
11
|
+
// Check for .pushto file in cwd
|
|
12
|
+
const pushtoFile = path.join(process.cwd(), '.pushto');
|
|
13
|
+
try {
|
|
14
|
+
const content = fs.readFileSync(pushtoFile, 'utf-8');
|
|
15
|
+
const parsed = JSON.parse(content);
|
|
16
|
+
if (parsed.slug) return parsed.slug;
|
|
17
|
+
} catch {
|
|
18
|
+
// No .pushto file or invalid JSON — fall through
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Fall back to global config
|
|
22
|
+
return getProjectSlug();
|
|
23
|
+
};
|