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
21 changes: 21 additions & 0 deletions components/Application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,21 @@ export function assertApplicationConfig(
}
}

/**
* Returns true when npm/git stderr indicates an SSH authentication failure —
* git exits 128 with the standard "could not read" message, or the SSH layer
* reports a missing uid (no SSH daemon user), explicit publickey denial, or
* an unverified host key.
*/
export function isSSHAuthFailure(stderr: string): boolean {
return (
stderr.includes('Could not read from remote repository') ||
stderr.includes('Permission denied (publickey)') ||
stderr.includes('No user exists for uid') ||
stderr.includes('Host key verification failed')
);
}

/**
* Extract an application given payload (content of the application) or package (npm-compatible identifier to the application).
*
Expand Down Expand Up @@ -189,6 +204,12 @@ export async function extractApplication(application: Application) {
parentDirPath
);
if (code !== 0) {
if (isSSHAuthFailure(stderr)) {
throw new Error(
`Failed to deploy private repository ${application.packageIdentifier}: SSH access failed. Verify the repository URL, configure an SSH key on this Harper instance, ensure the key has access to the target repository, and confirm the host is present in the ssh/known_hosts file.`,
{ cause: new Error(stderr) }
);
}
throw new Error(`Failed to download package ${application.packageIdentifier}: ${stderr}`);
}

Expand Down
67 changes: 67 additions & 0 deletions unitTests/components/Application.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
'use strict';

const assert = require('node:assert');

const testUtils = require('../testUtils.js');
testUtils.preTestPrep();

const { isSSHAuthFailure } = require('#src/components/Application');

describe('isSSHAuthFailure', () => {
it('returns true for "Could not read from remote repository"', () => {
const stderr = `
npm error code 128
npm error An unknown git error occurred
npm error command git --no-replace-objects ls-remote git@github.com:Org/repo.git
npm error fatal: Could not read from remote repository.
npm error Please make sure you have the correct access rights
npm error and the repository exists.
`;
assert.strictEqual(isSSHAuthFailure(stderr), true);
});

it('returns true for "Permission denied (publickey)"', () => {
const stderr = `
npm error code 128
npm error An unknown git error occurred
npm error git@github.com: Permission denied (publickey).
npm error fatal: Could not read from remote repository.
`;
assert.strictEqual(isSSHAuthFailure(stderr), true);
});

it('returns true for "No user exists for uid"', () => {
const stderr = `
npm error code 128
npm error An unknown git error occurred
npm error No user exists for uid 42932
npm error fatal: Could not read from remote repository.
npm error Please make sure you have the correct access rights
npm error and the repository exists.
`;
assert.strictEqual(isSSHAuthFailure(stderr), true);
});

it('returns true for "Host key verification failed"', () => {
const stderr = `
npm error code 128
npm error An unknown git error occurred
npm error Host key verification failed.
npm error fatal: Could not read from remote repository.
`;
assert.strictEqual(isSSHAuthFailure(stderr), true);
});

it('returns false for unrelated npm errors', () => {
const stderr = `
npm error code E404
npm error 404 Not Found - GET https://registry.npmjs.org/nonexistent-pkg
npm error 404 '@scope/nonexistent-pkg@latest' is not in this registry.
`;
assert.strictEqual(isSSHAuthFailure(stderr), false);
});

it('returns false for empty stderr', () => {
assert.strictEqual(isSSHAuthFailure(''), false);
});
});
Loading