Skip to content
Merged
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
32 changes: 20 additions & 12 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,37 @@ jobs:
matrix:
suite:
- name: applications
file: e2e/tests/applications.test.ts
file: tests/e2e/tests/applications.test.ts
- name: application-deployments
file: tests/e2e/tests/application-deployments.test.ts
- name: application-env-vars
file: tests/e2e/tests/application-env-vars.test.ts
- name: application-lifecycle
file: tests/e2e/tests/application-lifecycle.test.ts
- name: application-settings
file: tests/e2e/tests/application-settings.test.ts
- name: databases
file: e2e/tests/databases.test.ts
file: tests/e2e/tests/databases.test.ts
- name: static-sites
file: e2e/tests/static-sites.test.ts
file: tests/e2e/tests/static-sites.test.ts
- name: load-balancers
file: e2e/tests/load-balancers.test.ts
file: tests/e2e/tests/load-balancers.test.ts
- name: object-storage
file: e2e/tests/object-storage.test.ts
file: tests/e2e/tests/object-storage.test.ts
- name: pipelines
file: e2e/tests/pipelines.test.ts
file: tests/e2e/tests/pipelines.test.ts
- name: projects
file: e2e/tests/projects.test.ts
file: tests/e2e/tests/projects.test.ts
- name: webhooks
file: e2e/tests/webhooks.test.ts
file: tests/e2e/tests/webhooks.test.ts
- name: api-keys
file: e2e/tests/api-keys.test.ts
file: tests/e2e/tests/api-keys.test.ts
- name: docker-registries
file: e2e/tests/docker-registries.test.ts
file: tests/e2e/tests/docker-registries.test.ts
- name: common
file: e2e/tests/common.test.ts
file: tests/e2e/tests/common.test.ts
- name: resources
file: e2e/tests/resources.test.ts
file: tests/e2e/tests/resources.test.ts
name: e2e (${{ matrix.suite.name }})
steps:
- uses: actions/checkout@v4
Expand Down
38 changes: 19 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ sevalla apps cdn-toggle <id>
sevalla apps deployments list --app-id <id>
sevalla apps deployments get <deployment-id> --app-id <id>
sevalla apps deployments trigger <app-id> --branch main
sevalla apps deployments rollback <app-id> --deployment-id <id>
sevalla apps deployments rollback <app-id>
sevalla apps deployments cancel <deployment-id> --app-id <id>
sevalla apps deployments logs <deployment-id> --app-id <id>
```
Expand All @@ -108,7 +108,7 @@ sevalla apps deployments logs <deployment-id> --app-id <id>
```bash
sevalla apps processes list --app-id <id>
sevalla apps processes get <process-id> --app-id <id>
sevalla apps processes create <app-id> --name web --type web
sevalla apps processes create <app-id> --name web --type web --resource-type <id>
sevalla apps processes update <process-id> --app-id <id> --instances 3
sevalla apps processes delete <process-id> --app-id <id>
sevalla apps processes trigger <process-id> --app-id <id>
Expand All @@ -130,7 +130,7 @@ sevalla apps processes metrics instance-count <app-id> <process-id>
sevalla apps domains list --app-id <id>
sevalla apps domains add <app-id> --name example.com
sevalla apps domains get <domain-id> --app-id <id>
sevalla apps domains update <domain-id> --app-id <id>
sevalla apps domains update <domain-id> --app-id <id> --name my-app
sevalla apps domains delete <app-id> <domain-id>
sevalla apps domains set-primary <app-id> <domain-id>
sevalla apps domains toggle <app-id> <domain-id>
Expand Down Expand Up @@ -170,22 +170,22 @@ sevalla apps metrics top-pages <app-id>

```bash
sevalla apps ip-restriction get <app-id>
sevalla apps ip-restriction update <app-id> --type allow --ip-list "1.2.3.4,5.6.7.8"
sevalla apps ip-restriction update <app-id> --type allow --ip-list "1.2.3.4,5.6.7.8" --enabled true
```

### TCP Proxies

```bash
sevalla apps tcp-proxies list --app-id <id>
sevalla apps tcp-proxies create <app-id> --port 3000
sevalla apps tcp-proxies create <app-id> --process-id <id> --port 3000
sevalla apps tcp-proxies delete <app-id> <tcp-proxy-id>
```

### Private Ports

```bash
sevalla apps private-ports list --app-id <id>
sevalla apps private-ports create <app-id> --port 8080 --protocol TCP
sevalla apps private-ports create <app-id> --process-id <id> --port 8080
sevalla apps private-ports delete <app-id> <private-port-id>
```

Expand All @@ -209,14 +209,14 @@ sevalla databases update <id> --display-name "My DB"
sevalla databases delete <id>
sevalla databases activate <id>
sevalla databases suspend <id>
sevalla databases reset-password <id>
sevalla databases reset-password <id> --new-password <password>
```

### Database Backups

```bash
sevalla databases backups list --db-id <id>
sevalla databases backups create <db-id>
sevalla databases backups create <db-id> --name nightly
sevalla databases backups delete <db-id> <backup-id>
sevalla databases backups restore <db-id> <backup-id>
```
Expand All @@ -225,7 +225,7 @@ sevalla databases backups restore <db-id> <backup-id>

```bash
sevalla databases connections internal-list --db-id <id>
sevalla databases connections internal-create <db-id> --application-id <id>
sevalla databases connections internal-create <db-id> --target-id <app-id>
sevalla databases connections internal-delete <db-id> <connection-id>
sevalla databases connections external-toggle <db-id>
```
Expand All @@ -247,7 +247,7 @@ sevalla databases metrics used-storage <id>

```bash
sevalla databases ip-restriction get <db-id>
sevalla databases ip-restriction update <db-id>
sevalla databases ip-restriction update <db-id> --type allow --ip-list "1.2.3.4" --enabled true
```

### Static Sites
Expand Down Expand Up @@ -277,7 +277,7 @@ sevalla static-sites deployments logs <deployment-id> --site-id <id>
sevalla static-sites domains list --site-id <id>
sevalla static-sites domains get <domain-id> --site-id <id>
sevalla static-sites domains add <site-id> --name example.com
sevalla static-sites domains update <domain-id> --site-id <id>
sevalla static-sites domains update <domain-id> --site-id <id> --name my-site
sevalla static-sites domains delete <site-id> <domain-id>
sevalla static-sites domains set-primary <site-id> <domain-id>
sevalla static-sites domains toggle <site-id> <domain-id>
Expand Down Expand Up @@ -326,7 +326,7 @@ sevalla load-balancers delete <id>

```bash
sevalla load-balancers destinations list --lb-id <id>
sevalla load-balancers destinations create <lb-id> --application-id <id>
sevalla load-balancers destinations create <lb-id> --service-type APP --service-id <app-id>
sevalla load-balancers destinations toggle <lb-id> <destination-id>
sevalla load-balancers destinations delete <lb-id> <destination-id>
```
Expand All @@ -337,7 +337,7 @@ sevalla load-balancers destinations delete <lb-id> <destination-id>
sevalla load-balancers domains list --lb-id <id>
sevalla load-balancers domains get <domain-id> --lb-id <id>
sevalla load-balancers domains add <lb-id> --name example.com
sevalla load-balancers domains update <domain-id> --lb-id <id>
sevalla load-balancers domains update <domain-id> --lb-id <id> --name my-lb
sevalla load-balancers domains delete <lb-id> <domain-id>
sevalla load-balancers domains set-primary <lb-id> <domain-id>
sevalla load-balancers domains toggle <lb-id> <domain-id>
Expand Down Expand Up @@ -365,16 +365,16 @@ sevalla object-storage cdn-domain disable <bucket-id>

```bash
sevalla object-storage cors-policies list --bucket-id <id>
sevalla object-storage cors-policies create <bucket-id>
sevalla object-storage cors-policies update <policy-id> --bucket-id <id>
sevalla object-storage cors-policies create <bucket-id> --origins "*" --methods GET,HEAD
sevalla object-storage cors-policies update <policy-id> --bucket-id <id> --origins "*" --methods GET
sevalla object-storage cors-policies delete <bucket-id> <policy-id>
```

### Object Storage Objects

```bash
sevalla object-storage objects list --bucket-id <id>
sevalla object-storage objects delete <bucket-id>
sevalla object-storage objects delete <bucket-id> --keys a.txt,b.txt --confirm
```

### Pipelines
Expand All @@ -385,7 +385,7 @@ sevalla pipelines get <id>
sevalla pipelines create --name mypipeline --type trunk
sevalla pipelines update <id> --name "My Pipeline"
sevalla pipelines delete <id>
sevalla pipelines promote <id>
sevalla pipelines promote <id> --source-app-id <id> --target-app-ids <id>,<id>
```

### Pipeline Stages
Expand All @@ -402,7 +402,7 @@ sevalla pipelines stages apps remove <pipeline-id> <stage-id> <app-id>
```bash
sevalla pipelines preview enable <pipeline-id>
sevalla pipelines preview disable <pipeline-id>
sevalla pipelines preview update <pipeline-id>
sevalla pipelines preview update <pipeline-id> --auto-create-app true
```

### Projects
Expand All @@ -413,7 +413,7 @@ sevalla projects get <id>
sevalla projects create --name myproject
sevalla projects update <id> --name "My Project"
sevalla projects delete <id>
sevalla projects services add <project-id>
sevalla projects services add <project-id> --service-id <id>
sevalla projects services remove <project-id> <service-id>
```

Expand Down
4 changes: 3 additions & 1 deletion src/client/api-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ export class ApiClient {
);
}

return (await response.json()) as T;
// Some endpoints answer 202 with no body (e.g. object storage delete)
const text = await response.text();
return (text ? JSON.parse(text) : undefined) as T;
}

async get<T>(path: string, query?: QueryParams): Promise<T> {
Expand Down
2 changes: 1 addition & 1 deletion src/client/endpoints/applications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { QueryParams } from '../types.ts';
// Path prefix
// ---------------------------------------------------------------------------

const BASE = '/v3/applications';
const BASE = '/applications';

// ---------------------------------------------------------------------------
// Query helpers
Expand Down
9 changes: 6 additions & 3 deletions src/commands/apps/clone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ export const appsCloneCommand = makeActionCommand({
name: 'clone',
description: 'Clone an application',
options: [
{ flags: '--display-name <name>', description: 'Display name for the cloned application' },
{
flags: '--display-name <name>',
description: 'Display name for the cloned application',
required: true,
},
{ flags: '--cluster <id>', description: 'Cluster ID for the cloned application' },
],
apiCall: (client, id, opts) => {
const body: Record<string, unknown> = {};
if (opts['displayName']) body['display_name'] = opts['displayName'];
const body: Record<string, unknown> = { display_name: opts['displayName'] };
if (opts['cluster']) body['cluster_id'] = opts['cluster'];
return client.post(`/applications/${id}/clone`, body);
},
Expand Down
10 changes: 2 additions & 8 deletions src/commands/apps/deployments/rollback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,9 @@ import { makeActionCommand } from '../../../helpers/command-factory.ts';

export const deploymentsRollbackCommand = makeActionCommand({
name: 'rollback',
description: 'Rollback to a previous deployment',
description: 'Rollback to the most recent successful deployment',
idArg: 'app-id',
options: [
{ flags: '--deployment-id <id>', description: 'Deployment ID to rollback to', required: true },
],
apiCall: (client, id, opts) =>
client.post(`/applications/${id}/deployments/rollback`, {
deployment_id: opts['deploymentId'],
}),
apiCall: (client, id) => client.post(`/applications/${id}/deployments/rollback`),
successMessage: 'Rollback initiated.',
spinnerText: 'Rolling back...',
});
6 changes: 1 addition & 5 deletions src/commands/apps/deployments/trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,10 @@ export const deploymentsTriggerCommand = makeActionCommand({
name: 'trigger',
description: 'Trigger a new deployment',
idArg: 'app-id',
options: [
{ flags: '--branch <branch>', description: 'Branch to deploy' },
{ flags: '--tag <tag>', description: 'Tag to deploy' },
],
options: [{ flags: '--branch <branch>', description: 'Branch to deploy' }],
apiCall: (client, id, opts) => {
const body: Record<string, unknown> = {};
if (opts['branch']) body['branch'] = opts['branch'];
if (opts['tag']) body['tag'] = opts['tag'];
return client.post(`/applications/${id}/deployments`, body);
},
successMessage: 'Deployment triggered.',
Expand Down
3 changes: 2 additions & 1 deletion src/commands/apps/domains/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const domainsAddCommand = makeActionCommand({
description: 'Add a domain to an application',
idArg: 'app-id',
options: [{ flags: '--name <name>', description: 'Domain name', required: true }],
apiCall: (client, id, opts) => client.post(`/applications/${id}/domains`, { name: opts['name'] }),
apiCall: (client, id, opts) =>
client.post(`/applications/${id}/domains`, { domain_name: opts['name'] }),
successMessage: 'Domain added.',
});
5 changes: 4 additions & 1 deletion src/commands/apps/domains/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ export const domainsUpdateCommand = new Command('update')
.description('Update application system domain')
.requiredOption('--app-id <id>', 'Application ID')
.argument('<domain-id>', 'Domain ID')
.requiredOption('--name <name>', 'New subdomain name for the system domain')
.addOption(jsonOption())
.action(async (domainId: string, opts: Record<string, unknown>) => {
const client = createClient({ apiUrl: opts['apiUrl'] as string | undefined });
const json = resolveJsonMode(opts);
try {
if (!json) startSpinner('Updating...');
const result = await client.patch(`/applications/${opts['appId']}/domains/${domainId}`);
const result = await client.patch(`/applications/${opts['appId']}/domains/${domainId}`, {
name: opts['name'],
});
stopSpinner();
if (json) {
printJson(result);
Expand Down
21 changes: 13 additions & 8 deletions src/commands/apps/ip-restriction/update.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from 'commander';
import { Command, Option } from 'commander';
import { createClient } from '../../../client/api-client.ts';
import { handleError } from '../../../errors/handler.ts';
import { jsonOption, resolveJsonMode } from '../../../helpers/flags.ts';
Expand All @@ -8,18 +8,23 @@ import { startSpinner, stopSpinner } from '../../../output/spinner.ts';
export const ipRestrictionUpdateCommand = new Command('update')
.description('Update IP restriction rules')
.argument('<app-id>', 'Application ID')
.option('--type <type>', 'Restriction type: allow or deny')
.option('--ip-list <ips>', 'Comma-separated IP addresses/CIDRs')
.option('--enabled <bool>', 'Enable or disable')
.requiredOption('--type <type>', 'Restriction type: allow or deny')
.requiredOption('--ip-list <ips>', 'Comma-separated IP addresses/CIDRs')
.addOption(
new Option('--enabled <bool>', 'Enable or disable')
.choices(['true', 'false'])
.makeOptionMandatory(),
)
.addOption(jsonOption())
.action(async (appId: string, opts: Record<string, unknown>) => {
const client = createClient({ apiUrl: opts['apiUrl'] as string | undefined });
const json = resolveJsonMode(opts);
try {
const body: Record<string, unknown> = {};
if (opts['type']) body['type'] = opts['type'];
if (opts['ipList']) body['ip_list'] = (opts['ipList'] as string).split(',');
if (opts['enabled'] !== undefined) body['is_enabled'] = opts['enabled'] === 'true';
const body = {
type: opts['type'],
ip_list: (opts['ipList'] as string).split(',').filter(Boolean),
is_enabled: opts['enabled'] === 'true',
};
if (!json) startSpinner('Updating...');
const result = await client.put(`/applications/${appId}/ip-restriction`, body);
stopSpinner();
Expand Down
12 changes: 6 additions & 6 deletions src/commands/apps/private-ports/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ export const privatePortsCreateCommand = makeActionCommand({
description: 'Create a private port',
idArg: 'app-id',
options: [
{ flags: '--process-id <id>', description: 'Process ID', required: true },
{ flags: '--port <port>', description: 'Port number', required: true },
{ flags: '--protocol <protocol>', description: 'Protocol (TCP/UDP)' },
],
apiCall: (client, id, opts) => {
const body: Record<string, unknown> = { port: Number(opts['port']) };
if (opts['protocol']) body['protocol'] = opts['protocol'];
return client.post(`/applications/${id}/private-ports`, body);
},
apiCall: (client, id, opts) =>
client.post(`/applications/${id}/private-ports`, {
process_id: opts['processId'],
port: Number(opts['port']),
}),
successMessage: 'Private port created.',
});
Loading
Loading