Skip to content

Commit c7c67c3

Browse files
committed
cosign install
Signed-off-by: CrazyMax <[email protected]>
1 parent 9ada6fd commit c7c67c3

File tree

5 files changed

+513
-0
lines changed

5 files changed

+513
-0
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* Copyright 2025 actions-toolkit authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import {describe, expect, it, test} from '@jest/globals';
18+
import * as fs from 'fs';
19+
20+
import {Install} from '../../src/cosign/install';
21+
22+
const maybe = !process.env.GITHUB_ACTIONS || (process.env.GITHUB_ACTIONS === 'true' && process.env.ImageOS && process.env.ImageOS.startsWith('ubuntu')) ? describe : describe.skip;
23+
24+
describe('download', () => {
25+
// prettier-ignore
26+
test.each(['latest'])(
27+
'install cosign %s', async (version) => {
28+
await expect((async () => {
29+
const install = new Install();
30+
const toolPath = await install.download(version);
31+
if (!fs.existsSync(toolPath)) {
32+
throw new Error('toolPath does not exist');
33+
}
34+
const binPath = await install.install(toolPath);
35+
if (!fs.existsSync(binPath)) {
36+
throw new Error('binPath does not exist');
37+
}
38+
})()).resolves.not.toThrow();
39+
}, 60000);
40+
});
41+
42+
maybe('build', () => {
43+
it.skip('builds refs/pull/4492/head', async () => {
44+
const install = new Install();
45+
const toolPath = await install.build('https://github.com/sigstore/cosign.git#refs/pull/4492/head');
46+
expect(fs.existsSync(toolPath)).toBe(true);
47+
const buildxBin = await install.install(toolPath);
48+
expect(fs.existsSync(buildxBin)).toBe(true);
49+
}, 500000);
50+
});

__tests__/cosign/install.test.ts

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/**
2+
* Copyright 2025 actions-toolkit authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import {describe, expect, it, jest, test, afterEach} from '@jest/globals';
18+
import fs from 'fs';
19+
import os from 'os';
20+
import path from 'path';
21+
import * as rimraf from 'rimraf';
22+
import osm = require('os');
23+
24+
import {Install} from '../../src/cosign/install';
25+
26+
const tmpDir = fs.mkdtempSync(path.join(process.env.TEMP || os.tmpdir(), 'cosign-install-'));
27+
28+
afterEach(function () {
29+
rimraf.sync(tmpDir);
30+
});
31+
32+
describe('download', () => {
33+
// prettier-ignore
34+
test.each([
35+
['v2.6.1'],
36+
['v3.0.1'],
37+
['latest']
38+
])(
39+
'acquires %p of cosign', async (version) => {
40+
const install = new Install();
41+
const toolPath = await install.download(version);
42+
expect(fs.existsSync(toolPath)).toBe(true);
43+
const cosignBin = await install.install(toolPath, tmpDir);
44+
expect(fs.existsSync(cosignBin)).toBe(true);
45+
}, 100000);
46+
47+
// prettier-ignore
48+
test.each([
49+
// following versions are already cached to htc from previous test cases
50+
['v2.6.1'],
51+
['v3.0.1'],
52+
])(
53+
'acquires %p of cosign with cache', async (version) => {
54+
const install = new Install();
55+
const toolPath = await install.download(version);
56+
expect(fs.existsSync(toolPath)).toBe(true);
57+
}, 100000);
58+
59+
// prettier-ignore
60+
test.each([
61+
['v2.5.3'],
62+
['v2.6.0'],
63+
])(
64+
'acquires %p of cosign without cache', async (version) => {
65+
const install = new Install();
66+
const toolPath = await install.download(version, true);
67+
expect(fs.existsSync(toolPath)).toBe(true);
68+
}, 100000);
69+
70+
// TODO: add tests for arm
71+
// prettier-ignore
72+
test.each([
73+
['win32', 'x64'],
74+
['darwin', 'x64'],
75+
['darwin', 'arm64'],
76+
['linux', 'x64'],
77+
['linux', 'arm64']
78+
])(
79+
'acquires undock for %s/%s', async (os, arch) => {
80+
jest.spyOn(osm, 'platform').mockImplementation(() => os as NodeJS.Platform);
81+
jest.spyOn(osm, 'arch').mockImplementation(() => arch);
82+
const install = new Install();
83+
const cosignBin = await install.download('latest');
84+
expect(fs.existsSync(cosignBin)).toBe(true);
85+
}, 100000);
86+
});
87+
88+
describe('getDownloadVersion', () => {
89+
it('returns latest download version', async () => {
90+
const version = await Install.getDownloadVersion('latest');
91+
expect(version.version).toEqual('latest');
92+
expect(version.downloadURL).toEqual('https://github.com/sigstore/cosign/releases/download/v%s/%s');
93+
expect(version.contentOpts).toEqual({
94+
owner: 'docker',
95+
repo: 'actions-toolkit',
96+
ref: 'main',
97+
path: '.github/cosign-releases.json'
98+
});
99+
});
100+
it('returns v3.0.2 download version', async () => {
101+
const version = await Install.getDownloadVersion('v3.0.2');
102+
expect(version.version).toEqual('v3.0.2');
103+
expect(version.downloadURL).toEqual('https://github.com/sigstore/cosign/releases/download/v%s/%s');
104+
expect(version.contentOpts).toEqual({
105+
owner: 'docker',
106+
repo: 'actions-toolkit',
107+
ref: 'main',
108+
path: '.github/cosign-releases.json'
109+
});
110+
});
111+
});
112+
113+
describe('getRelease', () => {
114+
it('returns latest GitHub release', async () => {
115+
const version = await Install.getDownloadVersion('latest');
116+
const release = await Install.getRelease(version);
117+
expect(release).not.toBeNull();
118+
expect(release?.tag_name).not.toEqual('');
119+
});
120+
it('returns v3.0.2 GitHub release', async () => {
121+
const version = await Install.getDownloadVersion('v3.0.2');
122+
const release = await Install.getRelease(version);
123+
expect(release).not.toBeNull();
124+
expect(release?.id).toEqual(253720294);
125+
expect(release?.tag_name).toEqual('v3.0.2');
126+
expect(release?.html_url).toEqual('https://github.com/sigstore/cosign/releases/tag/v3.0.2');
127+
});
128+
it('unknown release', async () => {
129+
const version = await Install.getDownloadVersion('foo');
130+
await expect(Install.getRelease(version)).rejects.toThrow(new Error('Cannot find Cosign release foo in releases JSON'));
131+
});
132+
});

src/cosign/dockerfile.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* Copyright 2025 actions-toolkit authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
export const dockerfileContent = `
18+
# syntax=docker/dockerfile:1
19+
20+
ARG GO_VERSION="1.25"
21+
ARG ALPINE_VERSION="3.22"
22+
23+
FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.7.0 AS xx
24+
25+
FROM --platform=$BUILDPLATFORM golang:\${GO_VERSION}-alpine\${ALPINE_VERSION} AS builder-base
26+
COPY --from=xx / /
27+
RUN apk add --no-cache git
28+
ENV CGO_ENABLED=0
29+
WORKDIR /src
30+
RUN --mount=type=cache,target=/go/pkg/mod \\
31+
--mount=type=bind,source=go.mod,target=go.mod \\
32+
--mount=type=bind,source=go.sum,target=go.sum \\
33+
go mod download
34+
35+
FROM builder-base AS version
36+
RUN --mount=type=bind,target=. <<'EOT'
37+
git rev-parse HEAD 2>/dev/null || {
38+
echo >&2 "Failed to get git revision, make sure --build-arg BUILDKIT_CONTEXT_KEEP_GIT_DIR=1 is set when building from Git directly"
39+
exit 1
40+
}
41+
set -ex
42+
export PKG=sigs.k8s.io BUILDDATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") TREESTATE=$(if ! git diff --no-ext-diff --quiet --exit-code; then echo dirty; else echo clean; fi) VERSION=$(git describe --match 'v[0-9]*' --dirty='.m' --always --tags) COMMIT=$(git rev-parse HEAD)$(if ! git diff --no-ext-diff --quiet --exit-code; then echo .m; fi);
43+
echo "-X \${PKG}/release-utils/version.gitVersion=\${VERSION} -X \${PKG}/release-utils/version.gitCommit=\${COMMIT} -X \${PKG}/release-utils/version.gitTreeState=\${TREESTATE} -X \${PKG}/release-utils/version.buildDate=\${BUILDDATE}" > /tmp/.ldflags;
44+
echo -n "\${VERSION}" > /tmp/.version;
45+
EOT
46+
47+
FROM builder-base AS builder
48+
ARG TARGETPLATFORM
49+
RUN --mount=type=bind,target=. \\
50+
--mount=type=cache,target=/root/.cache,id=cosign-$TARGETPLATFORM \\
51+
--mount=source=/tmp/.ldflags,target=/tmp/.ldflags,from=version \\
52+
--mount=type=cache,target=/go/pkg/mod <<EOT
53+
set -ex
54+
xx-go build -trimpath -ldflags "-s -w $(cat /tmp/.ldflags)" -o /out/cosign ./cmd/cosign
55+
xx-verify --static /out/cosign
56+
EOT
57+
58+
FROM scratch
59+
COPY --from=builder /out /
60+
`;

0 commit comments

Comments
 (0)