mirror of
https://github.com/game-ci/unity-builder.git
synced 2026-06-13 09:23:52 -07:00
fix(providers): add polling timeouts, fix credential parsing, validate dependencies
- GitHub Actions: max 4-hour polling with clear timeout error including run URL - GitLab CI: max 4-hour polling with clear timeout error including pipeline URL - Remote PowerShell: fix credential split to preserve passwords with colons (split on first colon only instead of all colons) - Remote PowerShell: throw clear error when credential format is invalid - Ansible: validate ansible-playbook binary exists in setupWorkflow (separate from ansible --version check) - All timeout errors use core.error() for GitHub Actions annotation visibility Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -148,11 +148,17 @@ class RemotePowershellProvider implements ProviderInterface {
|
||||
}
|
||||
|
||||
// WinRM (default)
|
||||
const credentialPart = this.credential
|
||||
? `-Credential (New-Object PSCredential('${this.credential.split(':')[0]}', (ConvertTo-SecureString '${
|
||||
this.credential.split(':')[1]
|
||||
}' -AsPlainText -Force)))`
|
||||
: '';
|
||||
// Split on the FIRST colon only — passwords may contain colons
|
||||
let credentialPart = '';
|
||||
if (this.credential) {
|
||||
const colonIndex = this.credential.indexOf(':');
|
||||
if (colonIndex === -1) {
|
||||
throw new Error('remotePowershellCredential must be in "username:password" format (no colon found)');
|
||||
}
|
||||
const user = this.credential.substring(0, colonIndex);
|
||||
const pass = this.credential.substring(colonIndex + 1);
|
||||
credentialPart = `-Credential (New-Object PSCredential('${user}', (ConvertTo-SecureString '${pass}' -AsPlainText -Force)))`;
|
||||
}
|
||||
|
||||
return `pwsh -NoProfile -NonInteractive -Command "Invoke-Command -ComputerName '${this.host}' ${credentialPart} -ScriptBlock { ${escapedScript} }"`;
|
||||
}
|
||||
|
||||
+29
-6
@@ -140,9 +140,7 @@ describe('RemotePowershellProvider', () => {
|
||||
it('includes secrets in the remote script block', async () => {
|
||||
mockRun.mockResolvedValueOnce('output');
|
||||
|
||||
const secrets = [
|
||||
{ ParameterKey: 'key1', EnvironmentVariable: 'SECRET_KEY', ParameterValue: 'secret-val-123' },
|
||||
];
|
||||
const secrets = [{ ParameterKey: 'key1', EnvironmentVariable: 'SECRET_KEY', ParameterValue: 'secret-val-123' }];
|
||||
|
||||
await provider.runTaskInWorkflow('guid-sec', 'img', 'build-cmd', '/m', '/w', [], secrets as any);
|
||||
|
||||
@@ -178,13 +176,38 @@ describe('RemotePowershellProvider', () => {
|
||||
const execError = new Error('Remote execution failed: access denied');
|
||||
mockRun.mockRejectedValueOnce(execError);
|
||||
|
||||
await expect(
|
||||
provider.runTaskInWorkflow('guid-fail', 'img', 'cmd', '/m', '/w', [], []),
|
||||
).rejects.toThrow('Remote execution failed');
|
||||
await expect(provider.runTaskInWorkflow('guid-fail', 'img', 'cmd', '/m', '/w', [], [])).rejects.toThrow(
|
||||
'Remote execution failed',
|
||||
);
|
||||
|
||||
expect(mockLogWarning).toHaveBeenCalledWith(expect.stringContaining('Task failed'));
|
||||
});
|
||||
|
||||
it('preserves passwords containing colons when splitting credentials', async () => {
|
||||
const params = createBuildParameters({
|
||||
remotePowershellCredential: 'admin:P@ss:w0rd:with:colons!',
|
||||
});
|
||||
provider = new RemotePowershellProvider(params);
|
||||
mockRun.mockResolvedValueOnce('output');
|
||||
|
||||
await provider.runTaskInWorkflow('guid-colon', 'img', 'cmd', '/m', '/w', [], []);
|
||||
|
||||
const command = mockRun.mock.calls[0][0];
|
||||
expect(command).toContain("PSCredential('admin'");
|
||||
expect(command).toContain("ConvertTo-SecureString 'P@ss:w0rd:with:colons!'");
|
||||
});
|
||||
|
||||
it('throws when credential has no colon separator', async () => {
|
||||
const params = createBuildParameters({
|
||||
remotePowershellCredential: 'nocolonhere',
|
||||
});
|
||||
provider = new RemotePowershellProvider(params);
|
||||
|
||||
await expect(provider.runTaskInWorkflow('guid-badcred', 'img', 'cmd', '/m', '/w', [], [])).rejects.toThrow(
|
||||
'username:password',
|
||||
);
|
||||
});
|
||||
|
||||
it('sets working directory in the remote script', async () => {
|
||||
mockRun.mockResolvedValueOnce('output');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user