get visibility when grabbing repo information
This commit is contained in:
@@ -18,10 +18,20 @@ afterEach(() => {
|
||||
describe('getRepositoryMetadata', () => {
|
||||
it('returns repository metadata when the fetch response is ok', async () => {
|
||||
fetchMock.mockResolvedValueOnce(
|
||||
new Response(JSON.stringify({ id: '123', owner: { id: '456' } }))
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
id: '123',
|
||||
owner: { id: '456' },
|
||||
visibility: 'public'
|
||||
})
|
||||
)
|
||||
)
|
||||
const result = await getRepositoryMetadata(url, 'repository', 'token')
|
||||
expect(result).toEqual({ repoId: '123', ownerId: '456' })
|
||||
expect(result).toEqual({
|
||||
repoId: '123',
|
||||
ownerId: '456',
|
||||
visibility: 'public'
|
||||
})
|
||||
})
|
||||
|
||||
it('throws an error when the fetch errors', async () => {
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as cfg from '../src/config'
|
||||
import * as apiClient from '../src/api-client'
|
||||
|
||||
let getContainerRegistryURLMock: jest.SpyInstance
|
||||
let getRepositoryMetadataMock: jest.SpyInstance
|
||||
let getInputMock: jest.SpyInstance
|
||||
|
||||
const ghcrUrl = new URL('https://ghcr.io')
|
||||
@@ -14,6 +15,10 @@ describe('config.resolvePublishActionOptions', () => {
|
||||
.spyOn(apiClient, 'getContainerRegistryURL')
|
||||
.mockImplementation()
|
||||
|
||||
getRepositoryMetadataMock = jest
|
||||
.spyOn(apiClient, 'getRepositoryMetadata')
|
||||
.mockImplementation()
|
||||
|
||||
getInputMock = jest.spyOn(core, 'getInput').mockImplementation()
|
||||
|
||||
configureEventContext()
|
||||
@@ -133,6 +138,30 @@ describe('config.resolvePublishActionOptions', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('throws an error when getting the repository metadata fails', async () => {
|
||||
getInputMock.mockReturnValueOnce('token')
|
||||
getContainerRegistryURLMock.mockResolvedValue(ghcrUrl)
|
||||
getRepositoryMetadataMock.mockRejectedValue(
|
||||
new Error('Failed to get repository metadata')
|
||||
)
|
||||
|
||||
await expect(cfg.resolvePublishActionOptions()).rejects.toThrow(
|
||||
'Failed to get repository metadata'
|
||||
)
|
||||
})
|
||||
|
||||
it('throws an error when returned repository visibility is empty', async () => {
|
||||
getInputMock.mockReturnValueOnce('token')
|
||||
getContainerRegistryURLMock.mockResolvedValue(ghcrUrl)
|
||||
getRepositoryMetadataMock.mockResolvedValue({
|
||||
visibility: ''
|
||||
})
|
||||
|
||||
await expect(cfg.resolvePublishActionOptions()).rejects.toThrow(
|
||||
'Could not find repository visibility.'
|
||||
)
|
||||
})
|
||||
|
||||
it('returns options when all values are present', async () => {
|
||||
getInputMock.mockImplementation((name: string) => {
|
||||
expect(name).toBe('github-token')
|
||||
@@ -140,6 +169,10 @@ describe('config.resolvePublishActionOptions', () => {
|
||||
})
|
||||
getContainerRegistryURLMock.mockResolvedValue(ghcrUrl)
|
||||
|
||||
getRepositoryMetadataMock.mockResolvedValue({
|
||||
visibility: 'public'
|
||||
})
|
||||
|
||||
const options = await cfg.resolvePublishActionOptions()
|
||||
|
||||
expect(options).toEqual({
|
||||
@@ -150,6 +183,7 @@ describe('config.resolvePublishActionOptions', () => {
|
||||
apiBaseUrl: 'apiBaseUrl',
|
||||
runnerTempDir: 'runnerTempDir',
|
||||
sha: 'sha',
|
||||
repositoryVisibility: 'public',
|
||||
repositoryId: 'repositoryId',
|
||||
repositoryOwnerId: 'repositoryOwnerId',
|
||||
isEnterprise: false,
|
||||
@@ -164,6 +198,9 @@ describe('config.resolvePublishActionOptions', () => {
|
||||
return 'token'
|
||||
})
|
||||
getContainerRegistryURLMock.mockResolvedValue(ghcrUrl)
|
||||
getRepositoryMetadataMock.mockResolvedValue({
|
||||
visibility: 'public'
|
||||
})
|
||||
|
||||
process.env.GITHUB_SERVER_URL = 'https://github-enterprise.com'
|
||||
|
||||
@@ -181,7 +218,8 @@ describe('config.resolvePublishActionOptions', () => {
|
||||
repositoryOwnerId: 'repositoryOwnerId',
|
||||
isEnterprise: true,
|
||||
containerRegistryUrl: ghcrUrl,
|
||||
token: 'token'
|
||||
token: 'token',
|
||||
repositoryVisibility: 'public'
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -200,7 +238,8 @@ describe('config.serializeOptions', () => {
|
||||
repositoryOwnerId: 'repositoryOwnerId',
|
||||
isEnterprise: false,
|
||||
containerRegistryUrl: ghcrUrl,
|
||||
token: 'token'
|
||||
token: 'token',
|
||||
repositoryVisibility: 'public'
|
||||
}
|
||||
|
||||
const serialized = cfg.serializeOptions(options)
|
||||
|
||||
@@ -389,6 +389,7 @@ function baseOptions(): cfg.PublishActionOptions {
|
||||
isEnterprise: false,
|
||||
containerRegistryUrl: ghcrUrl,
|
||||
token: 'token',
|
||||
ref: 'refs/tags/v1.2.3'
|
||||
ref: 'refs/tags/v1.2.3',
|
||||
repositoryVisibility: 'public'
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="116" height="20" role="img" aria-label="Coverage: 96.52%"><title>Coverage: 96.52%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="116" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="63" height="20" fill="#555"/><rect x="63" width="53" height="20" fill="#4c1"/><rect width="116" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">Coverage</text><text x="325" y="140" transform="scale(.1)" fill="#fff" textLength="530">Coverage</text><text aria-hidden="true" x="885" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">96.52%</text><text x="885" y="140" transform="scale(.1)" fill="#fff" textLength="430">96.52%</text></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="110" height="20" role="img" aria-label="Coverage: 93.8%"><title>Coverage: 93.8%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="110" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="63" height="20" fill="#555"/><rect x="63" width="47" height="20" fill="#4c1"/><rect width="110" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">Coverage</text><text x="325" y="140" transform="scale(.1)" fill="#fff" textLength="530">Coverage</text><text aria-hidden="true" x="855" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="370">93.8%</text><text x="855" y="140" transform="scale(.1)" fill="#fff" textLength="370">93.8%</text></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
+26
-3
@@ -99284,7 +99284,7 @@ ZipStream.prototype.finalize = function() {
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.getContainerRegistryURL = exports.getRepositoryMetadata = void 0;
|
||||
exports.getRepositoryVisibility = exports.getContainerRegistryURL = exports.getRepositoryMetadata = void 0;
|
||||
async function getRepositoryMetadata(githubAPIURL, repository, token) {
|
||||
const response = await fetch(`${githubAPIURL}/repos/${repository}`, {
|
||||
method: 'GET',
|
||||
@@ -99301,7 +99301,11 @@ async function getRepositoryMetadata(githubAPIURL, repository, token) {
|
||||
if (!data.id || !data.owner.id) {
|
||||
throw new Error(`Failed to fetch repository metadata: unexpected response format`);
|
||||
}
|
||||
return { repoId: String(data.id), ownerId: String(data.owner.id) };
|
||||
return {
|
||||
repoId: String(data.id),
|
||||
ownerId: String(data.owner.id),
|
||||
visibility: String(data.visibility)
|
||||
};
|
||||
}
|
||||
exports.getRepositoryMetadata = getRepositoryMetadata;
|
||||
async function getContainerRegistryURL(githubAPIURL) {
|
||||
@@ -99317,6 +99321,18 @@ async function getContainerRegistryURL(githubAPIURL) {
|
||||
return registryURL;
|
||||
}
|
||||
exports.getContainerRegistryURL = getContainerRegistryURL;
|
||||
async function getRepositoryVisibility(githubAPIURL) {
|
||||
const response = await fetch(`${githubAPIURL}/`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch repository metadata due to bad status code: ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
if (!data.full_name) {
|
||||
throw new Error(`Failed to fetch repository metadata: unexpected response format`);
|
||||
}
|
||||
return data.full_name;
|
||||
}
|
||||
exports.getRepositoryVisibility = getRepositoryVisibility;
|
||||
|
||||
|
||||
/***/ }),
|
||||
@@ -99406,6 +99422,11 @@ async function resolvePublishActionOptions() {
|
||||
const containerRegistryUrl = await apiClient.getContainerRegistryURL(apiBaseUrl);
|
||||
const isEnterprise = !githubServerUrl.includes('https://github.com') &&
|
||||
!githubServerUrl.endsWith('.ghe.com');
|
||||
const repoMetadata = await apiClient.getRepositoryMetadata(apiBaseUrl, nameWithOwner, token);
|
||||
if (repoMetadata.visibility === '') {
|
||||
throw new Error(`Could not find repository visibility.`);
|
||||
}
|
||||
const repositoryVisibility = repoMetadata.visibility;
|
||||
return {
|
||||
event,
|
||||
ref,
|
||||
@@ -99417,6 +99438,7 @@ async function resolvePublishActionOptions() {
|
||||
sha,
|
||||
containerRegistryUrl,
|
||||
isEnterprise,
|
||||
repositoryVisibility,
|
||||
repositoryId,
|
||||
repositoryOwnerId
|
||||
};
|
||||
@@ -99825,12 +99847,13 @@ function parseSemverTagFromRef(ref) {
|
||||
// Generate an attestation using the actions toolkit
|
||||
// Subject name will contain the repo/package name and the tag name
|
||||
async function generateAttestation(manifestDigest, semverTag, options) {
|
||||
const subjectName = `${options.nameWithOwner}_${semverTag}`;
|
||||
const subjectName = `${options.nameWithOwner}@${semverTag}`;
|
||||
const subjectDigest = removePrefix(manifestDigest, 'sha256:');
|
||||
return await attest.attestProvenance({
|
||||
subjectName,
|
||||
subjectDigest: { sha256: subjectDigest },
|
||||
token: options.token,
|
||||
sigstore: 'github',
|
||||
skipWrite: false // TODO: Attestation storage is only supported for public repositories or repositories which belong to a GitHub Enterprise Cloud account
|
||||
});
|
||||
}
|
||||
|
||||
+26
-2
@@ -2,7 +2,7 @@ export async function getRepositoryMetadata(
|
||||
githubAPIURL: string,
|
||||
repository: string,
|
||||
token: string
|
||||
): Promise<{ repoId: string; ownerId: string }> {
|
||||
): Promise<{ repoId: string; ownerId: string; visibility: string }> {
|
||||
const response = await fetch(`${githubAPIURL}/repos/${repository}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
@@ -26,7 +26,11 @@ export async function getRepositoryMetadata(
|
||||
)
|
||||
}
|
||||
|
||||
return { repoId: String(data.id), ownerId: String(data.owner.id) }
|
||||
return {
|
||||
repoId: String(data.id),
|
||||
ownerId: String(data.owner.id),
|
||||
visibility: String(data.visibility)
|
||||
}
|
||||
}
|
||||
|
||||
export async function getContainerRegistryURL(
|
||||
@@ -51,3 +55,23 @@ export async function getContainerRegistryURL(
|
||||
const registryURL: URL = new URL(data.url)
|
||||
return registryURL
|
||||
}
|
||||
|
||||
export async function getRepositoryVisibility(
|
||||
githubAPIURL: string
|
||||
): Promise<string> {
|
||||
const response = await fetch(`${githubAPIURL}/`)
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Failed to fetch repository metadata due to bad status code: ${response.status}`
|
||||
)
|
||||
}
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.full_name) {
|
||||
throw new Error(
|
||||
`Failed to fetch repository metadata: unexpected response format`
|
||||
)
|
||||
}
|
||||
|
||||
return data.full_name
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ export interface PublishActionOptions {
|
||||
runnerTempDir: string
|
||||
// Whether this action is running in enterprise, determined from the github URL
|
||||
isEnterprise: boolean
|
||||
// The visibility of the action repository ("public", "internal" or "private")
|
||||
repositoryVisibility: string
|
||||
// The repository ID of the action repository
|
||||
repositoryId: string
|
||||
// The owner ID of the action repository
|
||||
@@ -97,6 +99,18 @@ export async function resolvePublishActionOptions(): Promise<PublishActionOption
|
||||
!githubServerUrl.includes('https://github.com') &&
|
||||
!githubServerUrl.endsWith('.ghe.com')
|
||||
|
||||
const repoMetadata = await apiClient.getRepositoryMetadata(
|
||||
apiBaseUrl,
|
||||
nameWithOwner,
|
||||
token
|
||||
)
|
||||
|
||||
if (repoMetadata.visibility === '') {
|
||||
throw new Error(`Could not find repository visibility.`)
|
||||
}
|
||||
|
||||
const repositoryVisibility = repoMetadata.visibility
|
||||
|
||||
return {
|
||||
event,
|
||||
ref,
|
||||
@@ -108,6 +122,7 @@ export async function resolvePublishActionOptions(): Promise<PublishActionOption
|
||||
sha,
|
||||
containerRegistryUrl,
|
||||
isEnterprise,
|
||||
repositoryVisibility,
|
||||
repositoryId,
|
||||
repositoryOwnerId
|
||||
}
|
||||
|
||||
+2
-1
@@ -98,13 +98,14 @@ async function generateAttestation(
|
||||
semverTag: string,
|
||||
options: cfg.PublishActionOptions
|
||||
): Promise<attest.Attestation> {
|
||||
const subjectName = `${options.nameWithOwner}_${semverTag}`
|
||||
const subjectName = `${options.nameWithOwner}@${semverTag}`
|
||||
const subjectDigest = removePrefix(manifestDigest, 'sha256:')
|
||||
|
||||
return await attest.attestProvenance({
|
||||
subjectName,
|
||||
subjectDigest: { sha256: subjectDigest },
|
||||
token: options.token,
|
||||
sigstore: 'github',
|
||||
skipWrite: false // TODO: Attestation storage is only supported for public repositories or repositories which belong to a GitHub Enterprise Cloud account
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user