Skip to content

Commit aa1a8c8

Browse files
Add interactive setup wizard and authentication management; enhance release workflow with version checks and checksum generation
1 parent a040660 commit aa1a8c8

File tree

15 files changed

+459
-61
lines changed

15 files changed

+459
-61
lines changed

.github/workflows/release.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,25 @@ jobs:
2323
- name: Install dependencies
2424
run: npm ci
2525

26+
- name: Check Version
27+
run: |
28+
$tag = $env:GITHUB_REF -replace 'refs/tags/v', ''
29+
$pkg = Get-Content package.json | ConvertFrom-Json
30+
if ($pkg.version -ne $tag) {
31+
Write-Error "Version mismatch: Tag $tag vs Package $($pkg.version)"
32+
exit 1
33+
}
34+
2635
- name: Build
2736
run: npm run build
2837

2938
- name: Package
3039
run: npm run package
3140

41+
- name: Generate Checksums
42+
run: |
43+
Get-FileHash dist\*, installer\Output\* -Algorithm SHA256 | Select-Object Hash, Path | ForEach-Object { "$($_.Hash) $(Split-Path $_.Path -Leaf)" } | Out-File SHA256SUMS.txt
44+
3245
- name: Install Inno Setup
3346
run: choco install innosetup
3447

CHANGELOG_DRAFT.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
# Changelog
22

3+
## [0.3.0] - 2025-12-21
4+
5+
### Added
6+
7+
- **Setup Wizard**: New `setup` command for interactive initialization.
8+
- **Authentication**: New `auth` command suite (`login`, `adc`, `status`, `set-service-account`).
9+
- **Service Account Support**: Securely manage service account keys with ACL hardening.
10+
- **Windows Service**: Enhanced `service` command with `install` and `configure` options for instance/port.
11+
- **Installer**: Improved Inno Setup script with smart binary reuse and permission management.
12+
- **Diagnostics**: Expanded `doctor` command to check ADC, network connectivity, and service credentials.
13+
14+
### Changed
15+
16+
- **Proxy**: `startProxy` now respects `GOOGLE_APPLICATION_CREDENTIALS` from environment or config.
17+
- **Logging**: Credential paths are now masked in logs.
18+
- **CLI**: Version is now dynamically read from `package.json`.
19+
- **Release**: Added SHA256 checksum generation to release workflow.
20+
321
## [0.2.0] - 2025-12-21
422

523
### Added

README.md

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,26 @@
44

55
## Features
66

7+
- **Setup Wizard**: Interactive `setup` command to get everything running in minutes.
8+
- **Authentication Management**: Built-in `auth` command for gcloud login, ADC setup, and Service Account management.
79
- **Automated Installation**: Downloads and verifies the official Cloud SQL Proxy binary.
810
- **Instance Management**: Lists and selects Cloud SQL instances using your active `gcloud` configuration.
9-
- **Process Management**: Starts, stops, and restarts the proxy as a background process with PID tracking.
10-
- **Structured Logging**: JSON logging with automatic masking of sensitive tokens, stored in `%LOCALAPPDATA%`.
11-
- **Diagnostics**: Built-in `doctor` command to check environment health (gcloud, ADC, network).
11+
- **Process Management**: Starts, stops, and restarts the proxy as a background process or Windows Service.
12+
- **Structured Logging**: JSON logging with automatic masking of sensitive tokens.
13+
- **Diagnostics**: Built-in `doctor` command to check environment health (gcloud, ADC, network, service).
1214
- **Self-Update**: Easily update the proxy binary to the latest version.
1315

1416
## Installation
1517

16-
Download the latest `cloudsqlctl.exe` from the [Releases](https://github.com/your-org/cloudsqlctl/releases) page and add it to your PATH.
18+
Download the latest installer (`cloudsqlctl-setup.exe`) from the [Releases](https://github.com/Kinin-Code-Offical/cloudsqlctl/releases) page.
19+
20+
## Quick Start
21+
22+
Run the setup wizard to configure gcloud, authentication, and the proxy:
23+
24+
```powershell
25+
cloudsqlctl setup
26+
```
1727

1828
## Usage
1929

@@ -23,29 +33,53 @@ cloudsqlctl [command] [options]
2333

2434
### Commands
2535

26-
| Command | Description |
27-
| :-------- | :------------------------------------------------------------ |
28-
| `install` | Download and install the Cloud SQL Proxy binary (User scope). |
29-
| `update` | Update the Cloud SQL Proxy binary to the latest version. |
30-
| `list` | List available Cloud SQL instances. |
31-
| `select` | Interactively select a Cloud SQL instance to proxy. |
32-
| `connect` | Connect to a specific instance directly. |
33-
| `start` | Start the proxy for the selected instance. |
34-
| `stop` | Stop the running proxy process. |
35-
| `service` | Manage Windows Service (Admin required). |
36-
| `env` | Manage environment variables (User/Machine scope). |
37-
| `gcloud` | Manage Google Cloud CLI (install portable version). |
38-
| `restart` | Restart the proxy process. |
39-
| `status` | Check if the proxy is running and view details. |
40-
| `logs` | View the tail of the proxy logs. |
41-
| `doctor` | Run diagnostics to verify environment setup. |
42-
| `reset` | Reset configuration and remove local files. |
43-
44-
### Example
36+
| Command | Description |
37+
| :-------- | :------------------------------------------------------- |
38+
| `setup` | Interactive setup wizard (Recommended for first run). |
39+
| `auth` | Manage authentication (Login, ADC, Service Accounts). |
40+
| `install` | Download and install the Cloud SQL Proxy binary. |
41+
| `update` | Update the Cloud SQL Proxy binary to the latest version. |
42+
| `list` | List available Cloud SQL instances. |
43+
| `select` | Interactively select a Cloud SQL instance to proxy. |
44+
| `connect` | Connect to a specific instance directly. |
45+
| `start` | Start the proxy for the selected instance. |
46+
| `stop` | Stop the running proxy process. |
47+
| `service` | Manage Windows Service (Admin required). |
48+
| `env` | Manage environment variables (User/Machine scope). |
49+
| `gcloud` | Manage Google Cloud CLI (install portable version). |
50+
| `status` | Check if the proxy is running and view details. |
51+
| `logs` | View the tail of the proxy logs. |
52+
| `doctor` | Run diagnostics to verify environment setup. |
53+
| `reset` | Reset configuration and remove local files. |
54+
55+
### Authentication Modes
56+
57+
**1. Developer Mode (Interactive)**
58+
Uses your personal Google Cloud credentials via `gcloud`.
4559

4660
```powershell
47-
# 1. Install the proxy
48-
cloudsqlctl install
61+
cloudsqlctl auth login
62+
cloudsqlctl auth adc
63+
```
64+
65+
**2. Machine/Service Mode (Service Account)**
66+
Uses a Service Account JSON key. Ideal for automated environments or Windows Services.
67+
68+
```powershell
69+
# Securely install service account key (Machine scope requires Admin)
70+
cloudsqlctl auth set-service-account --file "C:\path\to\key.json" --scope Machine
71+
```
72+
73+
### Windows Service
74+
75+
Run the proxy as a Windows Service for background persistence.
76+
77+
```powershell
78+
# Install service (Admin required)
79+
cloudsqlctl service install --instance "my-project:region:instance" --port 5432
80+
81+
# Start service
82+
cloudsqlctl service start
4983
5084
# 2. Select your database instance
5185
cloudsqlctl select

installer/cloudsqlctl.iss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environmen
4343
Name: "{commonappdata}\CloudSQLCTL"; Permissions: users-modify
4444
Name: "{commonappdata}\CloudSQLCTL\logs"; Permissions: users-modify
4545
Name: "{commonappdata}\CloudSQLCTL\bin"; Permissions: users-modify
46+
Name: "{commonappdata}\CloudSQLCTL\secrets"; Permissions: admins-full system-full
4647

4748
[Code]
4849
var

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": "0.2.0",
3+
"version": "0.3.0",
44
"description": "Windows-native CLI tool that installs, updates, and manages Google Cloud SQL Auth Proxy via gcloud CLI.",
55
"main": "dist/cli.cjs",
66
"type": "module",

src/cli.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,20 @@ import { ps1Command } from './commands/ps1.js';
1717
import { repairCommand } from './commands/repair.js';
1818
import { checkCommand } from './commands/check.js';
1919
import { gcloudCommand } from './commands/gcloud.js';
20+
import { authCommand } from './commands/auth.js';
21+
import { setupCommand } from './commands/setup.js';
2022
import { logger } from './core/logger.js';
23+
import { createRequire } from 'module';
24+
25+
const require = createRequire(import.meta.url);
26+
const pkg = require('../package.json');
2127

2228
const program = new Command();
2329

2430
program
2531
.name('cloudsqlctl')
2632
.description('CLI for managing Google Cloud SQL Auth Proxy')
27-
.version('1.0.0');
33+
.version(pkg.version);
2834

2935
program.addCommand(installCommand);
3036
program.addCommand(updateCommand);
@@ -43,6 +49,8 @@ program.addCommand(ps1Command);
4349
program.addCommand(repairCommand);
4450
program.addCommand(checkCommand);
4551
program.addCommand(gcloudCommand);
52+
program.addCommand(authCommand);
53+
program.addCommand(setupCommand);
4654

4755
program.parseAsync(process.argv).catch(err => {
4856
logger.error('Unhandled error', err);

src/commands/auth.ts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { Command } from 'commander';
2+
import { logger } from '../core/logger.js';
3+
import { getActiveAccount, getProject, checkAdc, login, adcLogin, setProject } from '../core/gcloud.js';
4+
import { setEnv } from '../system/env.js';
5+
import { SYSTEM_PATHS, USER_PATHS, ENV_VARS } from '../system/paths.js';
6+
import { runPs } from '../system/powershell.js';
7+
import fs from 'fs-extra';
8+
import path from 'path';
9+
10+
export const authCommand = new Command('auth')
11+
.description('Manage authentication and credentials');
12+
13+
authCommand.command('status')
14+
.description('Check authentication status')
15+
.action(async () => {
16+
const account = await getActiveAccount();
17+
const project = await getProject();
18+
const adc = await checkAdc();
19+
const credsEnv = process.env[ENV_VARS.GOOGLE_CREDS];
20+
21+
logger.info('Authentication Status:');
22+
logger.info(` Active Account: ${account || 'Not logged in'}`);
23+
logger.info(` Active Project: ${project || 'Not set'}`);
24+
logger.info(` ADC Status: ${adc ? '✅ Configured' : '❌ Not configured'}`);
25+
if (credsEnv) {
26+
logger.info(` Credentials: ${credsEnv}`);
27+
}
28+
});
29+
30+
authCommand.command('login')
31+
.description('Login via gcloud')
32+
.action(async () => {
33+
try {
34+
await login();
35+
logger.info('Successfully logged in.');
36+
} catch (error) {
37+
logger.error('Login failed', error);
38+
process.exit(1);
39+
}
40+
});
41+
42+
authCommand.command('adc')
43+
.description('Setup Application Default Credentials')
44+
.action(async () => {
45+
try {
46+
await adcLogin();
47+
logger.info('ADC configured successfully.');
48+
} catch (error) {
49+
logger.error('ADC setup failed', error);
50+
process.exit(1);
51+
}
52+
});
53+
54+
authCommand.command('project')
55+
.description('Set active project')
56+
.argument('<projectId>', 'Project ID')
57+
.action(async (projectId) => {
58+
try {
59+
await setProject(projectId);
60+
logger.info(`Project set to ${projectId}`);
61+
} catch (error) {
62+
logger.error('Failed to set project', error);
63+
process.exit(1);
64+
}
65+
});
66+
67+
authCommand.command('set-service-account')
68+
.description('Configure service account credentials')
69+
.requiredOption('-f, --file <path>', 'Path to service account JSON key')
70+
.option('-s, --scope <scope>', 'Scope (Machine or User)', 'User')
71+
.action(async (options) => {
72+
const { file, scope } = options;
73+
if (!['Machine', 'User'].includes(scope)) {
74+
logger.error('Scope must be either Machine or User');
75+
process.exit(1);
76+
}
77+
78+
try {
79+
if (!await fs.pathExists(file)) {
80+
logger.error(`File not found: ${file}`);
81+
process.exit(1);
82+
}
83+
84+
const paths = scope === 'Machine' ? SYSTEM_PATHS : USER_PATHS;
85+
await fs.ensureDir(paths.SECRETS);
86+
87+
const fileName = path.basename(file);
88+
const dest = path.join(paths.SECRETS, fileName);
89+
90+
await fs.copy(file, dest, { overwrite: true });
91+
logger.info(`Copied credentials to ${dest}`);
92+
93+
if (scope === 'Machine') {
94+
// Hardening ACLs
95+
// Remove inheritance, grant Administrators and SYSTEM full access
96+
await runPs(`icacls "${dest}" /inheritance:r /grant:r "Administrators:(R)" "SYSTEM:(R)"`);
97+
logger.info('Applied security ACLs to credentials file.');
98+
}
99+
100+
await setEnv(ENV_VARS.GOOGLE_CREDS, dest, scope);
101+
logger.info(`Set ${ENV_VARS.GOOGLE_CREDS} environment variable.`);
102+
103+
} catch (error) {
104+
logger.error('Failed to set service account', error);
105+
process.exit(1);
106+
}
107+
});

src/commands/doctor.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { Command } from 'commander';
22
import { logger } from '../core/logger.js';
3-
import { checkGcloudInstalled, getActiveAccount } from '../core/gcloud.js';
3+
import { checkGcloudInstalled, getActiveAccount, checkAdc, listInstances } from '../core/gcloud.js';
44
import { checkEnvironment } from '../system/env.js';
55
import { isServiceInstalled } from '../system/service.js';
6-
import { PATHS } from '../system/paths.js';
6+
import { getEnvVar } from '../system/powershell.js';
7+
import { PATHS, ENV_VARS } from '../system/paths.js';
78
import fs from 'fs-extra';
89
import axios from 'axios';
910

@@ -28,6 +29,22 @@ export const doctorCommand = new Command('doctor')
2829
logger.warn('⚠️ No active gcloud account found. Run "gcloud auth login"');
2930
}
3031

32+
// Check ADC
33+
const adc = await checkAdc();
34+
if (adc) {
35+
logger.info('✅ Application Default Credentials configured');
36+
} else {
37+
logger.warn('⚠️ Application Default Credentials NOT configured');
38+
}
39+
40+
// Check Network/Permissions
41+
try {
42+
await listInstances();
43+
logger.info('✅ Network/Permissions OK (Can list instances)');
44+
} catch {
45+
logger.error('❌ Network/Permissions Check Failed (Cannot list instances)');
46+
}
47+
3148
// Check Environment Variables
3249
const envOk = await checkEnvironment('Machine');
3350
if (envOk) {
@@ -46,6 +63,12 @@ export const doctorCommand = new Command('doctor')
4663
// Check Service
4764
if (await isServiceInstalled()) {
4865
logger.info('✅ Windows Service is installed');
66+
const serviceCreds = await getEnvVar(ENV_VARS.GOOGLE_CREDS, 'Machine');
67+
if (serviceCreds) {
68+
logger.info('✅ Service Credentials configured');
69+
} else {
70+
logger.warn('⚠️ Service Credentials NOT configured (Service might fail)');
71+
}
4972
} else {
5073
logger.info('ℹ️ Windows Service is NOT installed (Optional)');
5174
}

0 commit comments

Comments
 (0)