Skip to content

Commit 81ba311

Browse files
Add 'list' and 'connect' commands to CLI, update README, and adjust versioning
1 parent 258d3c1 commit 81ba311

File tree

6 files changed

+101
-2
lines changed

6 files changed

+101
-2
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ cloudsqlctl [command] [options]
2727
| :-------- | :------------------------------------------------------- |
2828
| `install` | Download and install the Cloud SQL Proxy binary. |
2929
| `update` | Update the Cloud SQL Proxy binary to the latest version. |
30+
| `list` | List available Cloud SQL instances. |
3031
| `select` | Interactively select a Cloud SQL instance to proxy. |
32+
| `connect` | Connect to a specific instance directly. |
3133
| `start` | Start the proxy for the selected instance. |
3234
| `stop` | Stop the running proxy process. |
3335
| `restart` | Restart the proxy process. |
@@ -45,7 +47,11 @@ cloudsqlctl install
4547
# 2. Select your database instance
4648
cloudsqlctl select
4749
48-
# 3. Start the proxy
50+
# Or list and connect directly
51+
cloudsqlctl list
52+
cloudsqlctl connect project:region:instance
53+
54+
# 3. Start the proxy (if using select)
4955
cloudsqlctl start
5056
5157
# 4. Check status

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cloudsqlctl",
3-
"version": "1.0.0",
3+
"version": "0.2.0",
44
"description": "Windows-native CLI tool that installs, updates, and manages Google Cloud SQL Auth Proxy via gcloud CLI.",
55
"main": "dist/cli.js",
66
"type": "module",

src/cli.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { Command } from 'commander';
33
import { installCommand } from './commands/install.js';
44
import { updateCommand } from './commands/update.js';
55
import { selectCommand } from './commands/select.js';
6+
import { listCommand } from './commands/list.js';
7+
import { connectCommand } from './commands/connect.js';
68
import { startCommand } from './commands/start.js';
79
import { stopCommand } from './commands/stop.js';
810
import { statusCommand } from './commands/status.js';
@@ -21,6 +23,8 @@ program
2123
program.addCommand(installCommand);
2224
program.addCommand(updateCommand);
2325
program.addCommand(selectCommand);
26+
program.addCommand(listCommand);
27+
program.addCommand(connectCommand);
2428
program.addCommand(startCommand);
2529
program.addCommand(stopCommand);
2630
program.addCommand(statusCommand);

src/commands/connect.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Command } from 'commander';
2+
import { startProxy, isRunning } from '../core/proxy.js';
3+
import { logger } from '../core/logger.js';
4+
5+
export const connectCommand = new Command('connect')
6+
.description('Connect to a specific Cloud SQL instance')
7+
.argument('<instance>', 'Instance connection name (e.g., project:region:instance)')
8+
.option('-p, --port <port>', 'Port to listen on', parseInt, 5432)
9+
.action(async (instance, options) => {
10+
try {
11+
if (await isRunning()) {
12+
logger.warn('Proxy is already running. Please stop it first using "cloudsqlctl stop".');
13+
return;
14+
}
15+
16+
logger.info(`Starting proxy for ${instance} on port ${options.port}...`);
17+
const pid = await startProxy(instance, options.port);
18+
logger.info(`Proxy started with PID ${pid}`);
19+
} catch (error) {
20+
logger.error('Failed to start proxy', error);
21+
process.exit(1);
22+
}
23+
});

src/commands/list.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { Command } from 'commander';
2+
import { listInstances } from '../core/gcloud.js';
3+
import { logger } from '../core/logger.js';
4+
5+
export const listCommand = new Command('list')
6+
.description('List available Cloud SQL instances')
7+
.option('--json', 'Output as JSON')
8+
.action(async (options) => {
9+
try {
10+
const instances = await listInstances();
11+
12+
if (options.json) {
13+
console.log(JSON.stringify(instances, null, 2));
14+
return;
15+
}
16+
17+
if (instances.length === 0) {
18+
logger.info('No instances found.');
19+
return;
20+
}
21+
22+
console.table(instances.map(i => ({
23+
Name: i.name,
24+
'Connection Name': i.connectionName,
25+
Region: i.region,
26+
State: i.state,
27+
Version: i.databaseVersion
28+
})));
29+
30+
} catch (error) {
31+
logger.error('Failed to list instances', error);
32+
process.exit(1);
33+
}
34+
});

tests/gcloud.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { listInstances } from '../src/core/gcloud.js';
2+
import { execa } from 'execa';
3+
4+
jest.mock('execa');
5+
6+
describe('Gcloud Module', () => {
7+
it('should list instances', async () => {
8+
const mockInstances = [
9+
{
10+
connectionName: 'project:region:instance',
11+
name: 'instance',
12+
region: 'region',
13+
project: 'project',
14+
databaseVersion: 'POSTGRES_13',
15+
state: 'RUNNABLE'
16+
}
17+
];
18+
19+
(execa as any).mockResolvedValue({
20+
stdout: JSON.stringify(mockInstances)
21+
});
22+
23+
const instances = await listInstances();
24+
expect(instances).toEqual(mockInstances);
25+
expect(execa).toHaveBeenCalledWith('gcloud', ['sql', 'instances', 'list', '--format=json']);
26+
});
27+
28+
it('should throw error if gcloud fails', async () => {
29+
(execa as any).mockRejectedValue(new Error('gcloud failed'));
30+
await expect(listInstances()).rejects.toThrow('gcloud failed');
31+
});
32+
});

0 commit comments

Comments
 (0)