Fix alpine failures by switching default back to only warn on verification failures. To prevent build failures due to missing GPG or rotated vendor keys. (#1262)

Also allow multiple GPG keys to be provided.

Co-authored-by: John <1615532+johnoliver@users.noreply.github.com>
This commit is contained in:
Bruno Borges
2026-09-03 13:27:24 -04:00
committed by GitHub
parent 4889c4aff5
commit 0781fc6af3
27 changed files with 575 additions and 136 deletions
+12 -7
View File
@@ -71,8 +71,11 @@ jest.unstable_mockModule('@actions/tool-cache', () => ({
}));
jest.unstable_mockModule('../../src/jdk-cache.js', () => ({
getJdkVerificationIdentity: jest.fn((verified: boolean, key?: string) =>
verified ? (key ? 'verified:custom' : 'verified:bundled') : 'unverified'
getJdkVerificationIdentity: jest.fn(
(verified: boolean, enforced: boolean, key?: string) =>
verified
? `${enforced ? 'enforced' : 'check-and-warn'}:${key ? 'custom' : 'bundled'}`
: 'disabled'
),
registerJdk: jest.fn(),
restoreJdk: jest.fn()
@@ -395,8 +398,10 @@ describe('setupJava', () => {
beforeEach(() => {
(jdkCache.getJdkVerificationIdentity as jest.Mock).mockImplementation(
(verified: boolean, key?: string) =>
verified ? (key ? 'verified:custom' : 'verified:bundled') : 'unverified'
(verified: boolean, enforced: boolean, key?: string) =>
verified
? `${enforced ? 'enforced' : 'check-and-warn'}:${key ? 'custom' : 'bundled'}`
: 'disabled'
);
spyGetToolcachePath = util.getToolcachePath as jest.Mock;
spyGetToolcachePath.mockImplementation(
@@ -826,7 +831,7 @@ describe('setupJava', () => {
expect(jdkCache.registerJdk).toHaveBeenCalledWith(
expect.objectContaining({
version: actualJavaVersion,
verification: 'unverified'
verification: 'disabled'
})
);
});
@@ -886,7 +891,7 @@ describe('setupJava', () => {
architecture: 'x86',
version: actualJavaVersion,
source: `some/random_url/java/${actualJavaVersion}`,
verification: 'unverified',
verification: 'disabled',
path: path.join(toolCachePath, 'Java_Empty_jdk', actualJavaVersion)
});
expect(downloadTool).not.toHaveBeenCalled();
@@ -919,7 +924,7 @@ describe('setupJava', () => {
architecture: 'x86',
version: actualJavaVersion,
source: `some/random_url/java/${actualJavaVersion}`,
verification: 'unverified',
verification: 'disabled',
path: path.join(toolCachePath, 'Java_Empty_jdk', actualJavaVersion)
};
expect(jdkCache.restoreJdk).toHaveBeenCalledWith(expectedIdentity);
@@ -58,7 +58,7 @@ jest.unstable_mockModule('@actions/tool-cache', () => ({
}));
jest.unstable_mockModule('../../src/jdk-cache.js', () => ({
getJdkVerificationIdentity: jest.fn(() => 'unverified'),
getJdkVerificationIdentity: jest.fn(() => 'disabled'),
registerJdk: jest.fn(),
restoreJdk: jest.fn()
}));
@@ -106,7 +106,7 @@ describe('setupJava', () => {
beforeEach(() => {
(jdkCache.getJdkVerificationIdentity as jest.Mock).mockReturnValue(
'unverified'
'disabled'
);
spyGetToolcachePath = util.getToolcachePath as jest.Mock;
spyGetToolcachePath.mockImplementation(
@@ -283,7 +283,7 @@ describe('setupJava', () => {
expect.objectContaining({
distribution: 'jdkfile',
version: actualJavaVersion,
verification: 'unverified'
verification: 'disabled'
})
);
} finally {
@@ -444,6 +444,64 @@ describe('downloadTool', () => {
);
});
it('warns with key rotation recovery guidance when default verification fails', async () => {
spyVerifySignature.mockRejectedValue(new Error('bad signature'));
const signedDistribution = new MicrosoftDistributions({
version: '17',
architecture: 'x64',
packageType: 'jdk',
checkLatest: false
});
await signedDistribution['downloadTool']({
version: '17.0.14+7',
url: 'https://example.com/jdk.tar.gz',
signatureUrl: 'https://example.com/jdk.tar.gz.sig'
});
expect(core.warning).toHaveBeenCalledWith(
expect.stringMatching(
/bad signature.*https:\/\/github\.com\/actions\/setup-java#download-integrity-and-signatures/
)
);
expect(spyExtractJdkFile).toHaveBeenCalled();
});
it('fails with recovery guidance when verification is explicitly enabled', async () => {
spyVerifySignature.mockRejectedValue(new Error('bad signature'));
const signedDistribution = new MicrosoftDistributions({
version: '17',
architecture: 'x64',
packageType: 'jdk',
checkLatest: false,
verifySignature: true
});
await expect(
signedDistribution['downloadTool']({
version: '17.0.14+7',
url: 'https://example.com/jdk.tar.gz',
signatureUrl: 'https://example.com/jdk.tar.gz.sig'
})
).rejects.toThrow(
/bad signature.*https:\/\/github\.com\/actions\/setup-java#download-integrity-and-signatures/
);
expect(spyExtractJdkFile).not.toHaveBeenCalled();
});
it('warns when the signature is missing during default verification', async () => {
await distribution['downloadTool']({
version: '17.0.14+7',
url: 'https://example.com/jdk.tar.gz'
});
expect(core.warning).toHaveBeenCalledWith(
"Input 'verify-signature' is enabled, but no signature URL was found for Microsoft Build of OpenJDK version 17.0.14+7."
);
expect(spyVerifySignature).not.toHaveBeenCalled();
expect(spyExtractJdkFile).toHaveBeenCalled();
});
it('fails when signature is missing and verification is enabled', async () => {
const signedDistribution = new MicrosoftDistributions({
version: '17',
@@ -73,6 +73,7 @@ jest.unstable_mockModule('../../src/util.js', () => ({
jest.unstable_mockModule('../../src/gpg.js', () => ({
importKey: jest.fn(),
removeGpgHome: jest.fn(),
isGpgAvailable: jest.fn(),
verifyPackageSignature: jest.fn()
}));
@@ -437,6 +438,7 @@ describe('downloadTool', () => {
beforeEach(() => {
spyDownloadTool = tc.downloadTool as jest.Mock;
spyDownloadTool.mockResolvedValue('/tmp/jdk.tar.gz');
(gpg.isGpgAvailable as jest.Mock).mockResolvedValue(true);
spyVerifySignature = gpg.verifyPackageSignature as jest.Mock;
spyVerifySignature.mockResolvedValue(undefined);
spyExtractJdkFile = util.extractJdkFile as jest.Mock;
@@ -502,6 +504,133 @@ describe('downloadTool', () => {
expect(spyVerifySignature).not.toHaveBeenCalled();
});
it('skips implicit signature verification when gpg is unavailable', async () => {
(gpg.isGpgAvailable as jest.Mock).mockResolvedValue(false);
const distribution = new TemurinDistribution(
{
version: '17',
architecture: 'x64',
packageType: 'jdk',
checkLatest: false
},
TemurinImplementation.Hotspot
);
await expect(
distribution['downloadTool']({
version: '17.0.14+7',
url: 'https://example.com/jdk.tar.gz',
signatureUrl: 'https://example.com/jdk.tar.gz.sig'
})
).resolves.toEqual({version: '17.0.14+7', path: '/tmp/toolcache'});
expect(spyVerifySignature).not.toHaveBeenCalled();
expect(core.warning).toHaveBeenCalledWith(
"Input 'verify-signature' is enabled, but gpg is not available."
);
});
it('fails when signature verification is explicitly enabled without gpg', async () => {
(gpg.isGpgAvailable as jest.Mock).mockResolvedValue(false);
const distribution = new TemurinDistribution(
{
version: '17',
architecture: 'x64',
packageType: 'jdk',
checkLatest: false,
verifySignature: true
},
TemurinImplementation.Hotspot
);
await expect(
distribution['downloadTool']({
version: '17.0.14+7',
url: 'https://example.com/jdk.tar.gz',
signatureUrl: 'https://example.com/jdk.tar.gz.sig'
})
).rejects.toThrow(
"Input 'verify-signature' is enabled, but gpg is not available."
);
expect(spyVerifySignature).not.toHaveBeenCalled();
});
it('warns when implicit signature verification fails', async () => {
spyVerifySignature.mockRejectedValue(new Error('bad signature'));
const distribution = new TemurinDistribution(
{
version: '17',
architecture: 'x64',
packageType: 'jdk',
checkLatest: false
},
TemurinImplementation.Hotspot
);
await expect(
distribution['downloadTool']({
version: '17.0.14+7',
url: 'https://example.com/jdk.tar.gz',
signatureUrl: 'https://example.com/jdk.tar.gz.sig'
})
).resolves.toEqual({version: '17.0.14+7', path: '/tmp/toolcache'});
expect(core.warning).toHaveBeenCalledWith(
expect.stringContaining(
'https://github.com/actions/setup-java#download-integrity-and-signatures'
)
);
});
it('fails when explicitly requested signature verification fails', async () => {
spyVerifySignature.mockRejectedValue(new Error('bad signature'));
const distribution = new TemurinDistribution(
{
version: '17',
architecture: 'x64',
packageType: 'jdk',
checkLatest: false,
verifySignature: true
},
TemurinImplementation.Hotspot
);
await expect(
distribution['downloadTool']({
version: '17.0.14+7',
url: 'https://example.com/jdk.tar.gz',
signatureUrl: 'https://example.com/jdk.tar.gz.sig'
})
).rejects.toThrow(
/Failed to verify signature for Temurin version 17\.0\.14\+7.*bad signature.*https:\/\/github\.com\/actions\/setup-java#download-integrity-and-signatures/
);
});
it('warns when a signature is missing and verification is implicit', async () => {
const distribution = new TemurinDistribution(
{
version: '17',
architecture: 'x64',
packageType: 'jdk',
checkLatest: false
},
TemurinImplementation.Hotspot
);
await expect(
distribution['downloadTool']({
version: '17.0.14+7',
url: 'https://example.com/jdk.tar.gz'
})
).resolves.toEqual({version: '17.0.14+7', path: '/tmp/toolcache'});
expect(core.warning).toHaveBeenCalledWith(
"Input 'verify-signature' is enabled, but no signature URL was found for Temurin version 17.0.14+7."
);
expect(spyVerifySignature).not.toHaveBeenCalled();
});
it('downloads and adds matching JMODs to the JDK', async () => {
spyDownloadTool
.mockResolvedValueOnce('/tmp/jdk.tar.gz')