Skip to content

Commit 2c98690

Browse files
authored
fix: convert Windows paths to POSIX format for MSYS2 GPG on Windows
The Git-bundled GPG on Windows (C:\Program Files\Git\usr\bin\gpg.exe) is an MSYS2-based binary that uses POSIX path conventions internally. When Windows-style paths with backslashes and drive letters (D:\a\_temp\...) are passed as arguments, GPG may fail to resolve them correctly, resulting in a fatal error (exit code 2). Fix: add a toGpgPath() helper that converts Windows paths to MSYS2 POSIX format (/d/a/_temp/...) before passing them to any gpg command. On Linux and macOS the helper is a no-op. Applied to all four paths used in verifyPackageSignature: - gpgHome (--homedir argument) - publicKeyFile (--import argument) - signaturePath (--verify signature argument) - archivePath (--verify data argument)
1 parent 8012407 commit 2c98690

4 files changed

Lines changed: 111 additions & 8 deletions

File tree

__tests__/gpg.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,35 @@ describe('gpg tests', () => {
3232
}
3333
});
3434

35+
describe('toGpgPath', () => {
36+
const originalPlatform = process.platform;
37+
38+
afterEach(() => {
39+
Object.defineProperty(process, 'platform', {value: originalPlatform});
40+
});
41+
42+
it('returns path unchanged on non-Windows platforms', () => {
43+
Object.defineProperty(process, 'platform', {value: 'linux'});
44+
expect(gpg.toGpgPath('/tmp/some/path')).toBe('/tmp/some/path');
45+
expect(gpg.toGpgPath('D:\\a\\_temp\\file')).toBe('D:\\a\\_temp\\file');
46+
});
47+
48+
it('converts Windows backslashes and drive letter to POSIX path on Windows', () => {
49+
Object.defineProperty(process, 'platform', {value: 'win32'});
50+
expect(gpg.toGpgPath('D:\\a\\_temp\\gpg-home')).toBe(
51+
'/d/a/_temp/gpg-home'
52+
);
53+
expect(gpg.toGpgPath('C:\\Users\\runner\\AppData\\Local\\Temp\\key.asc')).toBe(
54+
'/c/Users/runner/AppData/Local/Temp/key.asc'
55+
);
56+
});
57+
58+
it('handles uppercase and lowercase drive letters on Windows', () => {
59+
Object.defineProperty(process, 'platform', {value: 'win32'});
60+
expect(gpg.toGpgPath('d:\\a\\_temp\\file')).toBe('/d/a/_temp/file');
61+
});
62+
});
63+
3564
describe('importKey', () => {
3665
it('attempts to import private key and returns null key id on failure', async () => {
3766
const privateKey = 'KEY CONTENTS';

dist/cleanup/index.js

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52313,7 +52313,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
5231352313
});
5231452314
};
5231552315
Object.defineProperty(exports, "__esModule", ({ value: true }));
52316-
exports.verifyPackageSignature = exports.deleteKey = exports.importKey = exports.PRIVATE_KEY_FILE = void 0;
52316+
exports.verifyPackageSignature = exports.deleteKey = exports.importKey = exports.toGpgPath = exports.PRIVATE_KEY_FILE = void 0;
5231752317
const fs = __importStar(__nccwpck_require__(79896));
5231852318
const path = __importStar(__nccwpck_require__(16928));
5231952319
const io = __importStar(__nccwpck_require__(94994));
@@ -52322,6 +52322,18 @@ const tc = __importStar(__nccwpck_require__(33472));
5232252322
const util = __importStar(__nccwpck_require__(54527));
5232352323
exports.PRIVATE_KEY_FILE = path.join(util.getTempDir(), 'private-key.asc');
5232452324
const PRIVATE_KEY_FINGERPRINT_REGEX = /\w{40}/;
52325+
// Convert a Windows path (D:\a\_temp\...) to a POSIX path (/d/a/_temp/...).
52326+
// The Git-bundled GPG on Windows (MSYS2-based) uses POSIX path conventions
52327+
// internally. Passing Windows paths with backslashes can cause fatal GPG errors
52328+
// (exit code 2), so all paths passed to GPG must be in POSIX format on Windows.
52329+
function toGpgPath(p) {
52330+
if (process.platform !== 'win32')
52331+
return p;
52332+
return p
52333+
.replace(/\\/g, '/')
52334+
.replace(/^([A-Za-z]):\//, (_, drive) => `/${drive.toLowerCase()}/`);
52335+
}
52336+
exports.toGpgPath = toGpgPath;
5232552337
function importKey(privateKey) {
5232652338
return __awaiter(this, void 0, void 0, function* () {
5232752339
fs.writeFileSync(exports.PRIVATE_KEY_FILE, privateKey, {
@@ -52378,8 +52390,21 @@ function verifyPackageSignature(archivePath, signatureUrl, publicKeyContent) {
5237852390
const publicKeyFile = path.join(gpgHome, 'public-key.asc');
5237952391
fs.writeFileSync(publicKeyFile, publicKeyContent, { encoding: 'utf-8' });
5238052392
const options = { silent: true };
52381-
yield exec.exec('gpg', ['--homedir', gpgHome, '--batch', '--import', publicKeyFile], options);
52382-
yield exec.exec('gpg', ['--homedir', gpgHome, '--batch', '--verify', signaturePath, archivePath], options);
52393+
yield exec.exec('gpg', [
52394+
'--homedir',
52395+
toGpgPath(gpgHome),
52396+
'--batch',
52397+
'--import',
52398+
toGpgPath(publicKeyFile)
52399+
], options);
52400+
yield exec.exec('gpg', [
52401+
'--homedir',
52402+
toGpgPath(gpgHome),
52403+
'--batch',
52404+
'--verify',
52405+
toGpgPath(signaturePath),
52406+
toGpgPath(archivePath)
52407+
], options);
5238352408
}
5238452409
finally {
5238552410
yield io.rmRF(signaturePath);

dist/setup/index.js

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81093,7 +81093,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8109381093
});
8109481094
};
8109581095
Object.defineProperty(exports, "__esModule", ({ value: true }));
81096-
exports.verifyPackageSignature = exports.deleteKey = exports.importKey = exports.PRIVATE_KEY_FILE = void 0;
81096+
exports.verifyPackageSignature = exports.deleteKey = exports.importKey = exports.toGpgPath = exports.PRIVATE_KEY_FILE = void 0;
8109781097
const fs = __importStar(__nccwpck_require__(79896));
8109881098
const path = __importStar(__nccwpck_require__(16928));
8109981099
const io = __importStar(__nccwpck_require__(94994));
@@ -81102,6 +81102,18 @@ const tc = __importStar(__nccwpck_require__(33472));
8110281102
const util = __importStar(__nccwpck_require__(54527));
8110381103
exports.PRIVATE_KEY_FILE = path.join(util.getTempDir(), 'private-key.asc');
8110481104
const PRIVATE_KEY_FINGERPRINT_REGEX = /\w{40}/;
81105+
// Convert a Windows path (D:\a\_temp\...) to a POSIX path (/d/a/_temp/...).
81106+
// The Git-bundled GPG on Windows (MSYS2-based) uses POSIX path conventions
81107+
// internally. Passing Windows paths with backslashes can cause fatal GPG errors
81108+
// (exit code 2), so all paths passed to GPG must be in POSIX format on Windows.
81109+
function toGpgPath(p) {
81110+
if (process.platform !== 'win32')
81111+
return p;
81112+
return p
81113+
.replace(/\\/g, '/')
81114+
.replace(/^([A-Za-z]):\//, (_, drive) => `/${drive.toLowerCase()}/`);
81115+
}
81116+
exports.toGpgPath = toGpgPath;
8110581117
function importKey(privateKey) {
8110681118
return __awaiter(this, void 0, void 0, function* () {
8110781119
fs.writeFileSync(exports.PRIVATE_KEY_FILE, privateKey, {
@@ -81158,8 +81170,21 @@ function verifyPackageSignature(archivePath, signatureUrl, publicKeyContent) {
8115881170
const publicKeyFile = path.join(gpgHome, 'public-key.asc');
8115981171
fs.writeFileSync(publicKeyFile, publicKeyContent, { encoding: 'utf-8' });
8116081172
const options = { silent: true };
81161-
yield exec.exec('gpg', ['--homedir', gpgHome, '--batch', '--import', publicKeyFile], options);
81162-
yield exec.exec('gpg', ['--homedir', gpgHome, '--batch', '--verify', signaturePath, archivePath], options);
81173+
yield exec.exec('gpg', [
81174+
'--homedir',
81175+
toGpgPath(gpgHome),
81176+
'--batch',
81177+
'--import',
81178+
toGpgPath(publicKeyFile)
81179+
], options);
81180+
yield exec.exec('gpg', [
81181+
'--homedir',
81182+
toGpgPath(gpgHome),
81183+
'--batch',
81184+
'--verify',
81185+
toGpgPath(signaturePath),
81186+
toGpgPath(archivePath)
81187+
], options);
8116381188
}
8116481189
finally {
8116581190
yield io.rmRF(signaturePath);

src/gpg.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@ export const PRIVATE_KEY_FILE = path.join(util.getTempDir(), 'private-key.asc');
1010

1111
const PRIVATE_KEY_FINGERPRINT_REGEX = /\w{40}/;
1212

13+
// Convert a Windows path (D:\a\_temp\...) to a POSIX path (/d/a/_temp/...).
14+
// The Git-bundled GPG on Windows (MSYS2-based) uses POSIX path conventions
15+
// internally. Passing Windows paths with backslashes can cause fatal GPG errors
16+
// (exit code 2), so all paths passed to GPG must be in POSIX format on Windows.
17+
export function toGpgPath(p: string): string {
18+
if (process.platform !== 'win32') return p;
19+
return p
20+
.replace(/\\/g, '/')
21+
.replace(/^([A-Za-z]):\//, (_, drive) => `/${drive.toLowerCase()}/`);
22+
}
23+
1324
export async function importKey(privateKey: string) {
1425
fs.writeFileSync(PRIVATE_KEY_FILE, privateKey, {
1526
encoding: 'utf-8',
@@ -84,12 +95,25 @@ export async function verifyPackageSignature(
8495
const options: ExecOptions = {silent: true};
8596
await exec.exec(
8697
'gpg',
87-
['--homedir', gpgHome, '--batch', '--import', publicKeyFile],
98+
[
99+
'--homedir',
100+
toGpgPath(gpgHome),
101+
'--batch',
102+
'--import',
103+
toGpgPath(publicKeyFile)
104+
],
88105
options
89106
);
90107
await exec.exec(
91108
'gpg',
92-
['--homedir', gpgHome, '--batch', '--verify', signaturePath, archivePath],
109+
[
110+
'--homedir',
111+
toGpgPath(gpgHome),
112+
'--batch',
113+
'--verify',
114+
toGpgPath(signaturePath),
115+
toGpgPath(archivePath)
116+
],
93117
options
94118
);
95119
} finally {

0 commit comments

Comments
 (0)