Skip to content

Commit c139fd5

Browse files
Refactor environment variable handling and path resolution; remove outdated workflows and update dependabot configuration
1 parent 30560b3 commit c139fd5

File tree

9 files changed

+137
-86
lines changed

9 files changed

+137
-86
lines changed

.github/dependabot.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
version: 2
22
updates:
33
- package-ecosystem: "npm"
4-
directory: "/cloudsqlctl"
4+
directory: "/"
55
schedule:
66
interval: "weekly"
77
- package-ecosystem: "github-actions"

.github/workflows/codeql.yml

Lines changed: 0 additions & 38 deletions
This file was deleted.

.github/workflows/dependabot.yml

Lines changed: 0 additions & 11 deletions
This file was deleted.

installer/cloudsqlctl.iss

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,15 @@
55
#define MyAppExeName "cloudsqlctl.exe"
66

77
[Setup]
8-
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
9-
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
108
AppId={{8A4B2C1D-E3F4-5678-9012-3456789ABCDE}
119
AppName={#MyAppName}
1210
AppVersion={#MyAppVersion}
13-
;AppVerName={#MyAppName} {#MyAppVersion}
1411
AppPublisher={#MyAppPublisher}
1512
AppPublisherURL={#MyAppURL}
1613
AppSupportURL={#MyAppURL}
1714
AppUpdatesURL={#MyAppURL}
1815
DefaultDirName={autopf}\{#MyAppName}
1916
DisableProgramGroupPage=yes
20-
; Install for all users (requires admin)
2117
PrivilegesRequired=admin
2218
OutputDir=..\dist
2319
OutputBaseFilename=cloudsqlctl-setup
@@ -38,12 +34,20 @@ Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
3834
; Add to System PATH
3935
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}"; Check: NeedsAddPath(ExpandConstant('{app}'))
4036

41-
; Optional: Set Environment Variables for Machine Scope
42-
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: string; ValueName: "CLOUDSQLCTL_HOME"; ValueData: "{commonappdata}\CloudSQLCTL"; Flags: createvalueifdoesntexist uninsdeletevalue
43-
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: string; ValueName: "CLOUDSQLCTL_LOGS"; ValueData: "{commonappdata}\CloudSQLCTL\logs"; Flags: createvalueifdoesntexist uninsdeletevalue
44-
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: string; ValueName: "CLOUDSQLCTL_PROXY_PATH"; ValueData: "{commonappdata}\CloudSQLCTL\bin\cloud-sql-proxy.exe"; Flags: createvalueifdoesntexist uninsdeletevalue
37+
; Environment Variables (Machine Scope) - Logic handled in Code section for smart reuse
38+
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: string; ValueName: "CLOUDSQLCTL_HOME"; ValueData: "{code:GetHomeDir}"; Flags: uninsdeletevalue
39+
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: string; ValueName: "CLOUDSQLCTL_LOGS"; ValueData: "{code:GetLogsDir}"; Flags: uninsdeletevalue
40+
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: string; ValueName: "CLOUDSQLCTL_PROXY_PATH"; ValueData: "{code:GetProxyPath}"; Flags: uninsdeletevalue
41+
42+
[Dirs]
43+
Name: "{commonappdata}\CloudSQLCTL"; Permissions: users-modify
44+
Name: "{commonappdata}\CloudSQLCTL\logs"; Permissions: users-modify
45+
Name: "{commonappdata}\CloudSQLCTL\bin"; Permissions: users-modify
4546

4647
[Code]
48+
var
49+
ProxyPath: string;
50+
4751
function NeedsAddPath(Param: string): boolean;
4852
var
4953
OrigPath: string;
@@ -55,7 +59,68 @@ begin
5559
Result := True;
5660
exit;
5761
end;
58-
// look for the path with leading and trailing semicolon
59-
// Pos() returns 0 if not found
6062
Result := Pos(';' + Param + ';', ';' + OrigPath + ';') = 0;
6163
end;
64+
65+
function GetHomeDir(Param: string): string;
66+
var
67+
ExistingHome: string;
68+
begin
69+
// Reuse existing HOME if set
70+
if RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', 'CLOUDSQLCTL_HOME', ExistingHome) then
71+
begin
72+
if DirExists(ExistingHome) then
73+
begin
74+
Result := ExistingHome;
75+
exit;
76+
end;
77+
end;
78+
Result := ExpandConstant('{commonappdata}\CloudSQLCTL');
79+
end;
80+
81+
function GetLogsDir(Param: string): string;
82+
begin
83+
Result := ExpandConstant('{commonappdata}\CloudSQLCTL\logs');
84+
end;
85+
86+
function GetProxyPath(Param: string): string;
87+
var
88+
ExistingProxy: string;
89+
CommonBin: string;
90+
UserBin: string;
91+
begin
92+
// 1. Check Registry for existing
93+
if RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', 'CLOUDSQLCTL_PROXY_PATH', ExistingProxy) then
94+
begin
95+
if FileExists(ExistingProxy) then
96+
begin
97+
Result := ExistingProxy;
98+
exit;
99+
end;
100+
end;
101+
102+
CommonBin := ExpandConstant('{commonappdata}\CloudSQLCTL\bin\cloud-sql-proxy.exe');
103+
104+
// 2. Check if exists in Common AppData
105+
if FileExists(CommonBin) then
106+
begin
107+
Result := CommonBin;
108+
exit;
109+
end;
110+
111+
// 3. Check User AppData (Migration scenario)
112+
UserBin := ExpandConstant('{localappdata}\CloudSQLCTL\bin\cloud-sql-proxy.exe');
113+
if FileExists(UserBin) then
114+
begin
115+
// Copy to Common AppData
116+
ForceDirectories(ExpandConstant('{commonappdata}\CloudSQLCTL\bin'));
117+
if FileCopy(UserBin, CommonBin, False) then
118+
begin
119+
Result := CommonBin;
120+
exit;
121+
end;
122+
end;
123+
124+
// 4. Default
125+
Result := CommonBin;
126+
end;

src/commands/check.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ export const checkCommand = new Command('check')
1111
try {
1212
logger.info('Checking system configuration...');
1313

14-
const envOk = await checkEnvironment();
15-
logger.info(`Environment Variables: ${envOk ? 'OK' : 'MISSING/INCORRECT'}`);
14+
const envOk = await checkEnvironment('Machine');
15+
logger.info(`Environment Variables (Machine): ${envOk ? 'OK' : 'MISSING/INCORRECT'}`);
1616

1717
const binaryOk = await fs.pathExists(PATHS.PROXY_EXE);
1818
logger.info(`Proxy Binary: ${binaryOk ? 'OK' : 'MISSING'}`);

src/commands/doctor.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { logger } from '../core/logger.js';
33
import { checkGcloudInstalled, getActiveAccount } from '../core/gcloud.js';
44
import { checkEnvironment } from '../system/env.js';
55
import { isServiceInstalled } from '../system/service.js';
6-
import { SYSTEM_PATHS } from '../system/paths.js';
6+
import { PATHS } from '../system/paths.js';
77
import fs from 'fs-extra';
88
import axios from 'axios';
99

@@ -29,15 +29,15 @@ export const doctorCommand = new Command('doctor')
2929
}
3030

3131
// Check Environment Variables
32-
const envOk = await checkEnvironment();
32+
const envOk = await checkEnvironment('Machine');
3333
if (envOk) {
3434
logger.info('✅ System Environment Variables are correct');
3535
} else {
3636
logger.warn('⚠️ System Environment Variables are missing or incorrect');
3737
}
3838

3939
// Check Proxy Binary
40-
if (await fs.pathExists(SYSTEM_PATHS.PROXY_EXE)) {
40+
if (await fs.pathExists(PATHS.PROXY_EXE)) {
4141
logger.info('✅ Cloud SQL Proxy binary found');
4242
} else {
4343
logger.error('❌ Cloud SQL Proxy binary NOT found');

src/system/paths.ts

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import path from 'path';
22
import os from 'os';
3+
import fs from 'fs-extra';
34

45
const PROGRAM_DATA = process.env.ProgramData || 'C:\\ProgramData';
56
const LOCAL_APP_DATA = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
@@ -32,25 +33,52 @@ export const ENV_VARS = {
3233
};
3334

3435
function resolvePaths() {
35-
const home = process.env[ENV_VARS.HOME] || USER_PATHS.HOME;
36-
const logs = process.env[ENV_VARS.LOGS] || path.join(home, 'logs');
37-
const bin = path.join(home, 'bin');
38-
const proxyExe = process.env[ENV_VARS.PROXY_PATH] || path.join(bin, 'cloud-sql-proxy.exe');
36+
// 1. Priority: Environment Variables
37+
const envProxyPath = process.env[ENV_VARS.PROXY_PATH];
38+
if (envProxyPath) {
39+
const proxyPath = envProxyPath;
40+
const home = process.env[ENV_VARS.HOME] || path.dirname(path.dirname(proxyPath)); // Guess home from proxy
41+
return {
42+
HOME: home,
43+
LOGS: process.env[ENV_VARS.LOGS] || path.join(home, 'logs'),
44+
BIN: path.dirname(proxyPath),
45+
PROXY_EXE: proxyPath,
46+
CONFIG_DIR: home,
47+
CONFIG_FILE: path.join(home, 'config.json'),
48+
TEMP: path.join(home, 'temp'),
49+
GCLOUD_DIR: path.join(home, 'gcloud'),
50+
PID_FILE: path.join(home, 'proxy.pid'),
51+
};
52+
}
3953

54+
// 2. Fallback: Check for existing proxy file (System then User)
55+
if (fs.existsSync(SYSTEM_PATHS.PROXY_EXE)) {
56+
return {
57+
...SYSTEM_PATHS,
58+
CONFIG_DIR: SYSTEM_PATHS.HOME,
59+
CONFIG_FILE: path.join(SYSTEM_PATHS.HOME, 'config.json'),
60+
TEMP: path.join(SYSTEM_PATHS.HOME, 'temp'),
61+
GCLOUD_DIR: path.join(SYSTEM_PATHS.HOME, 'gcloud'),
62+
PID_FILE: path.join(SYSTEM_PATHS.HOME, 'proxy.pid'),
63+
};
64+
}
65+
66+
if (fs.existsSync(USER_PATHS.PROXY_EXE)) {
67+
return USER_PATHS;
68+
}
69+
70+
// 3. Default: System Paths (Target for new installation)
4071
return {
41-
HOME: home,
42-
LOGS: logs,
43-
BIN: bin,
44-
PROXY_EXE: proxyExe,
45-
CONFIG_DIR: home,
46-
CONFIG_FILE: path.join(home, 'config.json'),
47-
TEMP: path.join(home, 'temp'),
48-
GCLOUD_DIR: path.join(home, 'gcloud'),
49-
PID_FILE: path.join(home, 'proxy.pid'),
72+
...SYSTEM_PATHS,
73+
CONFIG_DIR: SYSTEM_PATHS.HOME,
74+
CONFIG_FILE: path.join(SYSTEM_PATHS.HOME, 'config.json'),
75+
TEMP: path.join(SYSTEM_PATHS.HOME, 'temp'),
76+
GCLOUD_DIR: path.join(SYSTEM_PATHS.HOME, 'gcloud'),
77+
PID_FILE: path.join(SYSTEM_PATHS.HOME, 'proxy.pid'),
5078
};
5179
}
5280

53-
// Default to resolved paths (User scope by default, or ENV overrides)
81+
// Default to resolved paths
5482
export const PATHS = resolvePaths();
5583

5684
export const SERVICE_NAME = 'cloudsql-proxy';

src/system/selfheal.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import fs from 'fs-extra';
2-
import { SYSTEM_PATHS } from './paths.js';
2+
import { PATHS } from './paths.js';
33
import { checkEnvironment, setupEnvironment } from './env.js';
44
import { isServiceInstalled, installService } from './service.js';
55
import { getLatestVersion, downloadProxy } from '../core/updater.js';
@@ -11,27 +11,33 @@ export async function selfHeal() {
1111
logger.info('Running self-healing checks...');
1212

1313
const admin = await isAdmin();
14+
const scope = admin ? 'Machine' : 'User';
1415

1516
// 1. Check Environment Variables
16-
if (!await checkEnvironment()) {
17+
if (!await checkEnvironment(scope)) {
1718
if (admin) {
18-
logger.warn('Environment variables missing or incorrect. Fixing...');
19+
logger.warn(`Environment variables (${scope}) missing or incorrect. Fixing...`);
1920
try {
20-
await setupEnvironment();
21+
await setupEnvironment(scope);
2122
} catch (e) {
2223
logger.error('Failed to fix environment variables', e);
2324
}
2425
} else {
25-
logger.warn('Environment variables missing or incorrect. Run as Administrator to fix.');
26+
logger.warn('Environment variables missing or incorrect. Run as Administrator to fix Machine scope, or we will fix User scope now.');
27+
try {
28+
await setupEnvironment(scope);
29+
} catch (e) {
30+
logger.error('Failed to fix environment variables', e);
31+
}
2632
}
2733
}
2834

2935
// 2. Check Proxy Binary
30-
if (!await fs.pathExists(SYSTEM_PATHS.PROXY_EXE)) {
36+
if (!await fs.pathExists(PATHS.PROXY_EXE)) {
3137
logger.warn('Proxy binary missing. Downloading...');
3238
try {
3339
const version = await getLatestVersion();
34-
await downloadProxy(version);
40+
await downloadProxy(version, PATHS.PROXY_EXE);
3541
} catch (e) {
3642
logger.error('Failed to download proxy binary', e);
3743
}

tests/proxy.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { jest } from '@jest/globals';
44
jest.unstable_mockModule('fs-extra', () => ({
55
default: {
66
pathExists: jest.fn().mockReturnValue(Promise.resolve(false)),
7+
existsSync: jest.fn().mockReturnValue(false),
78
readFile: jest.fn(),
89
writeFile: jest.fn(),
910
remove: jest.fn(),

0 commit comments

Comments
 (0)