Tying up loose ends (#54)
* various qol updates to publish action * review comments and run bundle
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import {
|
||||
getRepositoryMetadata,
|
||||
getContainerRegistryURL
|
||||
} from '../src/api-client'
|
||||
|
||||
let fetchMock: jest.SpyInstance
|
||||
|
||||
beforeEach(() => {
|
||||
fetchMock = jest.spyOn(global, 'fetch')
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
fetchMock.mockRestore()
|
||||
})
|
||||
|
||||
describe('getRepositoryMetadata', () => {
|
||||
it('returns repository metadata when the fetch response is ok', async () => {
|
||||
fetchMock.mockResolvedValueOnce(
|
||||
new Response(JSON.stringify({ id: '123', owner: { id: '456' } }))
|
||||
)
|
||||
const result = await getRepositoryMetadata('repository', 'token')
|
||||
expect(result).toEqual({ repoId: '123', ownerId: '456' })
|
||||
})
|
||||
|
||||
it('throws an error when the fetch errors', async () => {
|
||||
fetchMock.mockRejectedValueOnce(new Error('API is down'))
|
||||
await expect(getRepositoryMetadata('repository', 'token')).rejects.toThrow(
|
||||
'API is down'
|
||||
)
|
||||
})
|
||||
|
||||
it('throws an error when the response status is not ok', async () => {
|
||||
fetchMock.mockResolvedValueOnce(new Response(null, { status: 500 }))
|
||||
await expect(getRepositoryMetadata('repository', 'token')).rejects.toThrow(
|
||||
'Failed to fetch repository metadata due to bad status code: 500'
|
||||
)
|
||||
})
|
||||
|
||||
it('throws an error when the response data is in the wrong format', async () => {
|
||||
fetchMock.mockResolvedValueOnce(
|
||||
new Response(JSON.stringify({ wrong: 'format' }))
|
||||
)
|
||||
await expect(getRepositoryMetadata('repository', 'token')).rejects.toThrow(
|
||||
'Failed to fetch repository metadata: unexpected response format'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getContainerRegistryURL', () => {
|
||||
it('returns container registry URL when the fetch response is ok', async () => {
|
||||
fetchMock.mockResolvedValueOnce(
|
||||
new Response(JSON.stringify({ url: 'https://registry.example.com' }))
|
||||
)
|
||||
const result = await getContainerRegistryURL()
|
||||
expect(result).toEqual(new URL('https://registry.example.com'))
|
||||
})
|
||||
|
||||
it('throws an error when the fetch errors', async () => {
|
||||
fetchMock.mockRejectedValueOnce(new Error('API is down'))
|
||||
await expect(getContainerRegistryURL()).rejects.toThrow('API is down')
|
||||
})
|
||||
|
||||
it('throws an error when the response status is not ok', async () => {
|
||||
fetchMock.mockResolvedValueOnce(new Response(null, { status: 500 }))
|
||||
await expect(getContainerRegistryURL()).rejects.toThrow(
|
||||
'Failed to fetch container registry url due to bad status code: 500'
|
||||
)
|
||||
})
|
||||
|
||||
it('throws an error when the response data is in the wrong format', async () => {
|
||||
fetchMock.mockResolvedValueOnce(
|
||||
new Response(JSON.stringify({ wrong: 'format' }))
|
||||
)
|
||||
await expect(getContainerRegistryURL()).rejects.toThrow(
|
||||
'Failed to fetch repository metadata: unexpected response format'
|
||||
)
|
||||
})
|
||||
})
|
||||
+44
-121
@@ -6,115 +6,65 @@ import { execSync } from 'child_process'
|
||||
|
||||
const fileContent = 'This is the content of the file'
|
||||
|
||||
describe('getConsolidatedDirectory', () => {
|
||||
describe('stageActionFiles', () => {
|
||||
let sourceDir: string
|
||||
let stagingDir: string
|
||||
|
||||
beforeAll(() => {
|
||||
sourceDir = `.` // fsHelper.createTempDir()
|
||||
fs.mkdirSync(`${sourceDir}/folder1`)
|
||||
fs.mkdirSync(`${sourceDir}/folder2`)
|
||||
fs.mkdirSync(`${sourceDir}/folder2/folder3`)
|
||||
fs.writeFileSync(`${sourceDir}/file0.txt`, fileContent)
|
||||
fs.writeFileSync(`${sourceDir}/folder1/file1.txt`, fileContent)
|
||||
fs.writeFileSync(`${sourceDir}/folder2/file2.txt`, fileContent)
|
||||
fs.writeFileSync(`${sourceDir}/folder2/folder3/file3.txt`, fileContent)
|
||||
beforeEach(() => {
|
||||
sourceDir = fsHelper.createTempDir()
|
||||
fs.mkdirSync(`${sourceDir}/src`)
|
||||
fs.writeFileSync(`${sourceDir}/src/main.js`, fileContent)
|
||||
fs.writeFileSync(`${sourceDir}/src/other.js`, fileContent)
|
||||
|
||||
stagingDir = fsHelper.createTempDir()
|
||||
})
|
||||
|
||||
beforeEach(() => {})
|
||||
|
||||
afterEach(() => {})
|
||||
|
||||
afterAll(() => {
|
||||
fs.rmSync(`file0.txt`)
|
||||
fs.rmSync(`folder1`, { recursive: true })
|
||||
fs.rmSync(`folder2`, { recursive: true })
|
||||
afterEach(() => {
|
||||
fs.rmSync(sourceDir, { recursive: true })
|
||||
fs.rmSync(stagingDir, { recursive: true })
|
||||
})
|
||||
|
||||
it('returns the directory itself if it is a single directory, and instructed to not clean it up', () => {
|
||||
// TODO: In these tests, we're not really distinguishing between the `publish-action-package` directory and the consumer repo directory, i.e., they share the same space.
|
||||
// In real life, when the consumer workflow runs, its own javascript is in ., but
|
||||
// the publish-action-package's code is in ${{github.action_path}}.
|
||||
// So.... I guess to emulate this, we should create a temp directory (representing the consumer repo)
|
||||
// and cd there before the test starts?
|
||||
const { consolidatedPath, needToCleanUpDir } =
|
||||
fsHelper.getConsolidatedDirectory('.')
|
||||
|
||||
expect(needToCleanUpDir).toBe(false)
|
||||
expect(consolidatedPath).toBe('.')
|
||||
expect(fsHelper.readFileContents(`file0.txt`).toString()).toEqual(
|
||||
fileContent
|
||||
it('returns an error if no action.yml file is present', () => {
|
||||
expect(() => fsHelper.stageActionFiles(sourceDir, stagingDir)).toThrow(
|
||||
/^No action.yml or action.yaml file found in source repository/
|
||||
)
|
||||
expect(fsHelper.readFileContents(`folder1/file1.txt`).toString()).toEqual(
|
||||
fileContent
|
||||
)
|
||||
expect(fsHelper.readFileContents(`folder2/file2.txt`).toString()).toEqual(
|
||||
fileContent
|
||||
)
|
||||
expect(
|
||||
fsHelper.readFileContents(`folder2/folder3/file3.txt`).toString()
|
||||
).toEqual(fileContent)
|
||||
})
|
||||
it('returns a new directory containing copies of the multiple paths if they are legally specified, and instruct to clean it up', () => {
|
||||
const { consolidatedPath, needToCleanUpDir } =
|
||||
fsHelper.getConsolidatedDirectory('file0.txt folder1')
|
||||
|
||||
expect(needToCleanUpDir).toBe(true)
|
||||
expect(consolidatedPath).not.toBe('.')
|
||||
expect(
|
||||
fsHelper
|
||||
.readFileContents(path.join(consolidatedPath, `file0.txt`))
|
||||
.toString()
|
||||
).toEqual(fileContent)
|
||||
expect(
|
||||
fsHelper
|
||||
.readFileContents(path.join(consolidatedPath, `folder1/file1.txt`))
|
||||
.toString()
|
||||
).toEqual(fileContent)
|
||||
expect(
|
||||
fs.existsSync(path.join(consolidatedPath, `folder2/file2.txt`))
|
||||
).toEqual(false)
|
||||
expect(
|
||||
fs.existsSync(path.join(consolidatedPath, `folder2/folder3/file3.txt`))
|
||||
).toEqual(false)
|
||||
})
|
||||
|
||||
it('what happens here?', () => {
|
||||
const { consolidatedPath, needToCleanUpDir } =
|
||||
fsHelper.getConsolidatedDirectory('folder1 folder2/folder3')
|
||||
it('copies all non-hidden files to the staging directory', () => {
|
||||
fs.writeFileSync(`${sourceDir}/action.yml`, fileContent)
|
||||
|
||||
expect(needToCleanUpDir).toBe(true)
|
||||
expect(consolidatedPath).not.toBe('.')
|
||||
expect(fs.existsSync(path.join(consolidatedPath, `file0.txt`))).toEqual(
|
||||
false
|
||||
)
|
||||
expect(
|
||||
fsHelper
|
||||
.readFileContents(path.join(consolidatedPath, `folder1/file1.txt`))
|
||||
.toString()
|
||||
).toEqual(fileContent)
|
||||
expect(
|
||||
fs.existsSync(path.join(consolidatedPath, `folder2/file2.txt`))
|
||||
).toEqual(false)
|
||||
expect(
|
||||
fsHelper
|
||||
.readFileContents(path.join(consolidatedPath, `folder3/file3.txt`))
|
||||
.toString()
|
||||
).toEqual(fileContent) // <--- TODO: This is what I'm unsure of
|
||||
fs.mkdirSync(`${sourceDir}/.git`)
|
||||
fs.writeFileSync(`${sourceDir}/.git/HEAD`, fileContent)
|
||||
|
||||
fs.mkdirSync(`${sourceDir}/.github/workflows`, { recursive: true })
|
||||
fs.writeFileSync(`${sourceDir}/.github/workflows/workflow.yml`, fileContent)
|
||||
|
||||
fsHelper.stageActionFiles(sourceDir, stagingDir)
|
||||
expect(fs.existsSync(`${stagingDir}/action.yml`)).toBe(true)
|
||||
expect(fs.existsSync(`${stagingDir}/src/main.js`)).toBe(true)
|
||||
expect(fs.existsSync(`${stagingDir}/src/other.js`)).toBe(true)
|
||||
|
||||
// Hidden files should not be copied
|
||||
expect(fs.existsSync(`${stagingDir}/.git`)).toBe(false)
|
||||
expect(fs.existsSync(`${stagingDir}/.github`)).toBe(false)
|
||||
})
|
||||
|
||||
it('throws an error for illegal path spec - single', () => {
|
||||
expect(() => {
|
||||
fsHelper.getConsolidatedDirectory('folder4')
|
||||
}).toThrow('filePath folder4 does not exist')
|
||||
it('copies all non-hidden files to the staging directory, even if action.yml is in a subdirectory', () => {
|
||||
fs.mkdirSync(`${sourceDir}/my-sub-action`, { recursive: true })
|
||||
fs.writeFileSync(`${sourceDir}/my-sub-action/action.yml`, fileContent)
|
||||
|
||||
fsHelper.stageActionFiles(sourceDir, stagingDir)
|
||||
expect(fs.existsSync(`${stagingDir}/src/main.js`)).toBe(true)
|
||||
expect(fs.existsSync(`${stagingDir}/src/other.js`)).toBe(true)
|
||||
expect(fs.existsSync(`${stagingDir}/my-sub-action/action.yml`)).toBe(true)
|
||||
})
|
||||
|
||||
it('throws an error for illegal path spec - multiple', () => {
|
||||
expect(() => {
|
||||
fsHelper.getConsolidatedDirectory('folder1 folder4')
|
||||
}).toThrow('filePath folder4 does not exist')
|
||||
})
|
||||
it('accepts action.yaml as a valid action file as well as action.yml', () => {
|
||||
fs.writeFileSync(`${sourceDir}/action.yaml`, fileContent)
|
||||
|
||||
// TODO: consider doing the thing Michael suggested where we exclude directories starting with .
|
||||
fsHelper.stageActionFiles(sourceDir, stagingDir)
|
||||
expect(fs.existsSync(`${stagingDir}/action.yaml`)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('createArchives', () => {
|
||||
@@ -243,33 +193,6 @@ describe('isDirectory', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('isActionRepo', () => {
|
||||
let stagingDir: string
|
||||
|
||||
beforeEach(() => {
|
||||
stagingDir = fsHelper.createTempDir()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(stagingDir, { recursive: true })
|
||||
})
|
||||
|
||||
it('returns true if action.yml exists at the root', () => {
|
||||
fs.writeFileSync(path.join(stagingDir, `action.yml`), fileContent)
|
||||
expect(fsHelper.isActionRepo(stagingDir)).toEqual(true)
|
||||
})
|
||||
|
||||
it('returns true if action.yaml exists at the root', () => {
|
||||
fs.writeFileSync(path.join(stagingDir, `action.yaml`), fileContent)
|
||||
expect(fsHelper.isActionRepo(stagingDir)).toEqual(true)
|
||||
})
|
||||
|
||||
it("returns false if action.y(a)ml doesn't exist at the root", () => {
|
||||
fs.writeFileSync(path.join(stagingDir, `action.yaaml`), fileContent)
|
||||
expect(fsHelper.isActionRepo(stagingDir)).toEqual(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('readFileContents', () => {
|
||||
let dir: string
|
||||
|
||||
|
||||
@@ -115,6 +115,14 @@ describe('publishOCIArtifact', () => {
|
||||
// Simulate successful upload of all blobs & then the manifest
|
||||
axiosPutMock.mockImplementation(async (url, data, config) => {
|
||||
validateRequestConfig(201, url, config)
|
||||
|
||||
if ((url as string).includes('manifest')) {
|
||||
return {
|
||||
status: 201,
|
||||
headers: { 'Docker-Content-Digest': '1234567678' }
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
status: 201
|
||||
}
|
||||
@@ -124,7 +132,6 @@ describe('publishOCIArtifact', () => {
|
||||
token,
|
||||
registry,
|
||||
repository,
|
||||
releaseId,
|
||||
semver,
|
||||
zipFile,
|
||||
tarFile,
|
||||
@@ -164,6 +171,14 @@ describe('publishOCIArtifact', () => {
|
||||
// Simulate successful upload of all blobs & then the manifest
|
||||
axiosPutMock.mockImplementation(async (url, data, config) => {
|
||||
validateRequestConfig(201, url, config)
|
||||
|
||||
if ((url as string).includes('manifest')) {
|
||||
return {
|
||||
status: 201,
|
||||
headers: { 'Docker-Content-Digest': '1234567678' }
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
status: 201
|
||||
}
|
||||
@@ -173,7 +188,6 @@ describe('publishOCIArtifact', () => {
|
||||
token,
|
||||
registry,
|
||||
repository,
|
||||
releaseId,
|
||||
semver,
|
||||
zipFile,
|
||||
tarFile,
|
||||
@@ -226,6 +240,14 @@ describe('publishOCIArtifact', () => {
|
||||
// Simulate successful upload of all blobs & then the manifest
|
||||
axiosPutMock.mockImplementation(async (url, data, config) => {
|
||||
validateRequestConfig(201, url, config)
|
||||
|
||||
if ((url as string).includes('manifest')) {
|
||||
return {
|
||||
status: 201,
|
||||
headers: { 'Docker-Content-Digest': '1234567678' }
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
status: 201
|
||||
}
|
||||
@@ -235,7 +257,6 @@ describe('publishOCIArtifact', () => {
|
||||
token,
|
||||
registry,
|
||||
repository,
|
||||
releaseId,
|
||||
semver,
|
||||
zipFile,
|
||||
tarFile,
|
||||
@@ -262,7 +283,6 @@ describe('publishOCIArtifact', () => {
|
||||
token,
|
||||
registry,
|
||||
repository,
|
||||
releaseId,
|
||||
semver,
|
||||
zipFile,
|
||||
tarFile,
|
||||
@@ -293,7 +313,6 @@ describe('publishOCIArtifact', () => {
|
||||
token,
|
||||
registry,
|
||||
repository,
|
||||
releaseId,
|
||||
semver,
|
||||
zipFile,
|
||||
tarFile,
|
||||
@@ -325,7 +344,6 @@ describe('publishOCIArtifact', () => {
|
||||
token,
|
||||
registry,
|
||||
repository,
|
||||
releaseId,
|
||||
semver,
|
||||
zipFile,
|
||||
tarFile,
|
||||
@@ -372,7 +390,6 @@ describe('publishOCIArtifact', () => {
|
||||
token,
|
||||
registry,
|
||||
repository,
|
||||
releaseId,
|
||||
semver,
|
||||
zipFile,
|
||||
tarFile,
|
||||
@@ -426,7 +443,6 @@ describe('publishOCIArtifact', () => {
|
||||
token,
|
||||
registry,
|
||||
repository,
|
||||
releaseId,
|
||||
semver,
|
||||
zipFile,
|
||||
tarFile,
|
||||
@@ -473,7 +489,6 @@ describe('publishOCIArtifact', () => {
|
||||
token,
|
||||
registry,
|
||||
repository,
|
||||
releaseId,
|
||||
semver,
|
||||
zipFile,
|
||||
tarFile,
|
||||
@@ -497,7 +512,6 @@ describe('publishOCIArtifact', () => {
|
||||
token,
|
||||
registry,
|
||||
repository,
|
||||
releaseId,
|
||||
semver,
|
||||
zipFile,
|
||||
tarFile,
|
||||
|
||||
+263
-137
@@ -12,6 +12,7 @@ import * as github from '@actions/github'
|
||||
|
||||
import * as fsHelper from '../src/fs-helper'
|
||||
import * as ghcr from '../src/ghcr-client'
|
||||
import * as api from '../src/api-client'
|
||||
|
||||
// Mock the GitHub Actions core library
|
||||
let getInputMock: jest.SpyInstance
|
||||
@@ -22,13 +23,16 @@ let setOutputMock: jest.SpyInstance
|
||||
let createTempDirMock: jest.SpyInstance
|
||||
let createArchivesMock: jest.SpyInstance
|
||||
let removeDirMock: jest.SpyInstance
|
||||
let getConsolidatedDirectoryMock: jest.SpyInstance
|
||||
let isActionRepoMock: jest.SpyInstance
|
||||
let stageActionFilesMock: jest.SpyInstance
|
||||
|
||||
// Mock the GHCR Client
|
||||
let publishOCIArtifactMock: jest.SpyInstance
|
||||
|
||||
describe('action', () => {
|
||||
// Mock the API Client
|
||||
let getContainerRegistryURLMock: jest.SpyInstance
|
||||
let getRepositoryMetadataMock: jest.SpyInstance
|
||||
|
||||
describe('run', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
|
||||
@@ -45,15 +49,23 @@ describe('action', () => {
|
||||
.spyOn(fsHelper, 'createArchives')
|
||||
.mockImplementation()
|
||||
removeDirMock = jest.spyOn(fsHelper, 'removeDir').mockImplementation()
|
||||
getConsolidatedDirectoryMock = jest
|
||||
.spyOn(fsHelper, 'getConsolidatedDirectory')
|
||||
stageActionFilesMock = jest
|
||||
.spyOn(fsHelper, 'stageActionFiles')
|
||||
.mockImplementation()
|
||||
isActionRepoMock = jest.spyOn(fsHelper, 'isActionRepo').mockImplementation()
|
||||
|
||||
// GHCR Client mocks
|
||||
publishOCIArtifactMock = jest
|
||||
.spyOn(ghcr, 'publishOCIArtifact')
|
||||
.mockImplementation()
|
||||
|
||||
// API Client mocks
|
||||
getContainerRegistryURLMock = jest
|
||||
.spyOn(api, 'getContainerRegistryURL')
|
||||
.mockImplementation()
|
||||
|
||||
getRepositoryMetadataMock = jest
|
||||
.spyOn(api, 'getRepositoryMetadata')
|
||||
.mockImplementation()
|
||||
})
|
||||
|
||||
it('fails if no repository found', async () => {
|
||||
@@ -67,202 +79,316 @@ describe('action', () => {
|
||||
expect(setFailedMock).toHaveBeenCalledWith('Could not find Repository.')
|
||||
})
|
||||
|
||||
it('fails if event is not a release', async () => {
|
||||
it('fails if no token found', async () => {
|
||||
// Mock the environment
|
||||
process.env.GITHUB_REPOSITORY = 'test-org/test-repo'
|
||||
github.context.eventName = 'push'
|
||||
process.env.TOKEN = ''
|
||||
|
||||
// Run the action
|
||||
await main.run('directory1 directory2')
|
||||
|
||||
// Check the results
|
||||
expect(setFailedMock).toHaveBeenCalledWith(
|
||||
'Please ensure you have the workflow trigger as release.'
|
||||
)
|
||||
expect(setFailedMock).toHaveBeenCalledWith('Could not find GITHUB_TOKEN.')
|
||||
})
|
||||
|
||||
it('fails if release tag is not a valid semantic version', async () => {
|
||||
it('fails if no source commit found', async () => {
|
||||
// Mock the environment
|
||||
process.env.GITHUB_REPOSITORY = 'test-org/test-repo'
|
||||
github.context.eventName = 'release'
|
||||
github.context.payload = {
|
||||
release: {
|
||||
id: '123',
|
||||
tag_name: 'invalid-tag'
|
||||
}
|
||||
process.env.TOKEN = 'test'
|
||||
process.env.GITHUB_SHA = ''
|
||||
|
||||
// Run the action
|
||||
await main.run('')
|
||||
|
||||
// Check the results
|
||||
expect(setFailedMock).toHaveBeenCalledWith('Could not find source commit.')
|
||||
})
|
||||
|
||||
it('fails if trigger is not release or tag push', async () => {
|
||||
process.env.GITHUB_REPOSITORY = 'test-org/test-repo'
|
||||
process.env.GITHUB_SHA = 'test-sha'
|
||||
process.env.TOKEN = 'token'
|
||||
|
||||
// TODO: If we want we can add all of these: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows
|
||||
const invalidEvents = ['workflow_dispatch, pull_request, schedule']
|
||||
for (const event of invalidEvents) {
|
||||
github.context.eventName = event
|
||||
await main.run('')
|
||||
expect(setFailedMock).toHaveBeenCalledWith(
|
||||
'This action can only be triggered by release events or tag push events.'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
// Run the action
|
||||
await main.run('directory1 directory2')
|
||||
it('fails if the trigger is a push, but not a tag push', async () => {
|
||||
process.env.GITHUB_REPOSITORY = 'test-org/test-repo'
|
||||
process.env.GITHUB_SHA = 'test-sha'
|
||||
process.env.TOKEN = 'token'
|
||||
github.context.eventName = 'push'
|
||||
github.context.ref = 'refs/heads/main' // This is a branch, not a tag
|
||||
|
||||
await main.run('')
|
||||
|
||||
// Check the results
|
||||
expect(setFailedMock).toHaveBeenCalledWith(
|
||||
'invalid-tag is not a valid semantic version, and so cannot be uploaded as an Immutable Action.'
|
||||
'This action can only be triggered by release events or tag push events.'
|
||||
)
|
||||
})
|
||||
|
||||
it('fails if multiple paths are provided and staging files fails', async () => {
|
||||
it('fails if the value of the tag input is not a valid semver', async () => {
|
||||
process.env.GITHUB_REPOSITORY = 'test-org/test-repo'
|
||||
process.env.GITHUB_SHA = 'test-sha'
|
||||
process.env.TOKEN = 'token'
|
||||
github.context.eventName = 'release'
|
||||
|
||||
const tags = ['test', 'v1.0', 'chicken', '111111']
|
||||
|
||||
for (const tag of tags) {
|
||||
github.context.payload = {
|
||||
release: {
|
||||
id: '123',
|
||||
tag_name: tag
|
||||
}
|
||||
}
|
||||
|
||||
await main.run('')
|
||||
expect(setFailedMock).toHaveBeenCalledWith(
|
||||
`${tag} is not a valid semantic version, and so cannot be uploaded as an Immutable Action.`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('fails if staging files fails', async () => {
|
||||
// Mock the environment
|
||||
process.env.GITHUB_REPOSITORY = 'test-org/test-repo'
|
||||
github.context.eventName = 'release'
|
||||
process.env.GITHUB_SHA = 'test-sha'
|
||||
process.env.TOKEN = 'token'
|
||||
github.context.payload = {
|
||||
release: {
|
||||
id: '123',
|
||||
tag_name: 'v1.2.3'
|
||||
}
|
||||
}
|
||||
getInputMock.mockImplementation((name: string) => {
|
||||
if (name === 'path') {
|
||||
return 'directory1 directory2'
|
||||
} else if (name === 'registry') {
|
||||
return 'https://ghcr.io'
|
||||
}
|
||||
return ''
|
||||
})
|
||||
|
||||
getConsolidatedDirectoryMock.mockImplementation(() => {
|
||||
stageActionFilesMock.mockImplementation(() => {
|
||||
throw new Error('Something went wrong')
|
||||
})
|
||||
|
||||
// Run the action
|
||||
await main.run('directory1 directory2')
|
||||
await main.run('')
|
||||
|
||||
// Check the results
|
||||
expect(setFailedMock).toHaveBeenCalledWith('Something went wrong')
|
||||
})
|
||||
|
||||
it('fails if an error is thrown from dependent code', async () => {
|
||||
it('fails if creating temp directory fails', async () => {
|
||||
// Mock the environment
|
||||
process.env.GITHUB_REPOSITORY = 'test-org/test-repo'
|
||||
github.context.eventName = 'release'
|
||||
process.env.GITHUB_SHA = 'test-sha'
|
||||
process.env.TOKEN = 'token'
|
||||
github.context.payload = {
|
||||
release: {
|
||||
id: '123',
|
||||
tag_name: 'v1.2.3'
|
||||
}
|
||||
}
|
||||
getInputMock.mockImplementation((name: string) => {
|
||||
if (name === 'path') {
|
||||
return 'directory'
|
||||
} else if (name === 'registry') {
|
||||
return 'https://ghcr.io'
|
||||
|
||||
createTempDirMock.mockImplementation(() => {
|
||||
throw new Error('Something went wrong')
|
||||
})
|
||||
|
||||
// Run the action
|
||||
await main.run('')
|
||||
|
||||
// Check the results
|
||||
expect(setFailedMock).toHaveBeenCalledWith('Something went wrong')
|
||||
})
|
||||
|
||||
it('fails if creating archives fails', async () => {
|
||||
// Mock the environment
|
||||
process.env.GITHUB_REPOSITORY = 'test-org/test-repo'
|
||||
github.context.eventName = 'release'
|
||||
process.env.GITHUB_SHA = 'test-sha'
|
||||
process.env.TOKEN = 'token'
|
||||
github.context.payload = {
|
||||
release: {
|
||||
id: '123',
|
||||
tag_name: 'v1.2.3'
|
||||
}
|
||||
return ''
|
||||
})
|
||||
|
||||
getConsolidatedDirectoryMock.mockImplementation(() => {
|
||||
return { consolidatedDirectory: '/tmp/test', needToCleanUpDir: false }
|
||||
})
|
||||
isActionRepoMock.mockImplementation(() => true)
|
||||
|
||||
createTempDirMock.mockImplementation(() => '/tmp/test')
|
||||
}
|
||||
|
||||
createArchivesMock.mockImplementation(() => {
|
||||
throw new Error('Something went wrong')
|
||||
})
|
||||
|
||||
// Run the action
|
||||
await main.run('directory')
|
||||
await main.run('')
|
||||
|
||||
// Check the results
|
||||
expect(getConsolidatedDirectoryMock).toHaveBeenCalledTimes(1)
|
||||
expect(setFailedMock).toHaveBeenCalledWith('Something went wrong')
|
||||
|
||||
// Expect the files to be cleaned up
|
||||
expect(removeDirMock).toHaveBeenCalledWith('/tmp/test')
|
||||
})
|
||||
|
||||
it('successfully uploads if the release tag is a semver without v prefix', async () => {
|
||||
await testHappyPath('1.2.3', 'test')
|
||||
})
|
||||
|
||||
it('successfully uploads if the release tag is a semver with v prefix', async () => {
|
||||
await testHappyPath('v1.2.3', 'test')
|
||||
})
|
||||
|
||||
it('successfully uploads if multiple paths are provided', async () => {
|
||||
await testHappyPath('v1.2.3', 'test test2')
|
||||
})
|
||||
})
|
||||
|
||||
// Test that main successfully uploads and returns the manifest & package URL
|
||||
async function testHappyPath(version: string, path: string): Promise<void> {
|
||||
// Mock the environment
|
||||
process.env.GITHUB_REPOSITORY = 'test-org/test-repo'
|
||||
github.context.eventName = 'release'
|
||||
github.context.payload = {
|
||||
release: {
|
||||
id: '123',
|
||||
tag_name: version
|
||||
}
|
||||
}
|
||||
getInputMock.mockImplementation((name: string) => {
|
||||
if (name === 'path') {
|
||||
return path
|
||||
} else if (name === 'registry') {
|
||||
return 'https://ghcr.io'
|
||||
}
|
||||
return ''
|
||||
})
|
||||
|
||||
isActionRepoMock.mockImplementation(() => true)
|
||||
|
||||
getConsolidatedDirectoryMock.mockImplementation(() => {
|
||||
return { consolidatedDirectory: '/tmp/test', needToCleanUpDir: false } // TODO: I don't understand why I have to name the variables here but not in the implementation code
|
||||
})
|
||||
|
||||
createTempDirMock.mockImplementation(() => '/tmp/test')
|
||||
|
||||
createArchivesMock.mockImplementation(() => {
|
||||
return {
|
||||
zipFile: {
|
||||
path: 'test',
|
||||
size: 5,
|
||||
sha256: '123'
|
||||
},
|
||||
tarFile: {
|
||||
path: 'test2',
|
||||
size: 52,
|
||||
sha256: '1234'
|
||||
it('fails if getting container registry URL fails', async () => {
|
||||
process.env.GITHUB_REPOSITORY = 'test-org/test-repo'
|
||||
github.context.eventName = 'release'
|
||||
process.env.GITHUB_SHA = 'test-sha'
|
||||
process.env.TOKEN = 'token'
|
||||
github.context.payload = {
|
||||
release: {
|
||||
id: '123',
|
||||
tag_name: 'v1.2.3'
|
||||
}
|
||||
}
|
||||
|
||||
createArchivesMock.mockImplementation(() => {
|
||||
return {
|
||||
zipFile: {
|
||||
path: 'test',
|
||||
size: 5,
|
||||
sha256: '123'
|
||||
},
|
||||
tarFile: {
|
||||
path: 'test2',
|
||||
size: 52,
|
||||
sha256: '1234'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
getRepositoryMetadataMock.mockImplementation(() => {
|
||||
return { repoId: 'test', ownerId: 'test' }
|
||||
})
|
||||
|
||||
getContainerRegistryURLMock.mockImplementation(() => {
|
||||
throw new Error('Something went wrong')
|
||||
})
|
||||
|
||||
// Run the action
|
||||
await main.run('')
|
||||
|
||||
// Check the results
|
||||
expect(setFailedMock).toHaveBeenCalledWith('Something went wrong')
|
||||
})
|
||||
|
||||
publishOCIArtifactMock.mockImplementation(() => {
|
||||
return new URL('https://ghcr.io/v2/test-org/test-repo:1.2.3')
|
||||
it('fails if publishing OCI artifact fails', async () => {
|
||||
process.env.GITHUB_REPOSITORY = 'test-org/test-repo'
|
||||
github.context.eventName = 'release'
|
||||
process.env.GITHUB_SHA = 'test-sha'
|
||||
process.env.TOKEN = 'token'
|
||||
github.context.payload = {
|
||||
release: {
|
||||
id: '123',
|
||||
tag_name: 'v1.2.3'
|
||||
}
|
||||
}
|
||||
|
||||
createArchivesMock.mockImplementation(() => {
|
||||
return {
|
||||
zipFile: {
|
||||
path: 'test',
|
||||
size: 5,
|
||||
sha256: '123'
|
||||
},
|
||||
tarFile: {
|
||||
path: 'test2',
|
||||
size: 52,
|
||||
sha256: '1234'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
getRepositoryMetadataMock.mockImplementation(() => {
|
||||
return { repoId: 'test', ownerId: 'test' }
|
||||
})
|
||||
|
||||
getContainerRegistryURLMock.mockImplementation(() => {
|
||||
return new URL('https://ghcr.io')
|
||||
})
|
||||
|
||||
publishOCIArtifactMock.mockImplementation(() => {
|
||||
throw new Error('Something went wrong')
|
||||
})
|
||||
|
||||
// Run the action
|
||||
await main.run('')
|
||||
|
||||
// Check the results
|
||||
expect(setFailedMock).toHaveBeenCalledWith('Something went wrong')
|
||||
})
|
||||
|
||||
// Run the action
|
||||
await main.run(path)
|
||||
it('uploads the artifact, returns package metadata from GHCR, and cleans up tmp dirs', async () => {
|
||||
process.env.GITHUB_REPOSITORY = 'test-org/test-repo'
|
||||
github.context.eventName = 'release'
|
||||
process.env.GITHUB_SHA = 'test-sha'
|
||||
process.env.TOKEN = 'token'
|
||||
github.context.payload = {
|
||||
release: {
|
||||
id: '123',
|
||||
tag_name: 'v1.2.3'
|
||||
}
|
||||
}
|
||||
|
||||
expect(publishOCIArtifactMock).toHaveBeenCalledTimes(1)
|
||||
createTempDirMock.mockImplementation(() => '/tmp/test')
|
||||
|
||||
// Check manifest is in output
|
||||
expect(setOutputMock).toHaveBeenCalledWith(
|
||||
'package-url',
|
||||
'https://ghcr.io/v2/test-org/test-repo:1.2.3'
|
||||
)
|
||||
expect(setOutputMock).toHaveBeenCalledWith(
|
||||
'package-manifest',
|
||||
expect.any(String)
|
||||
)
|
||||
createArchivesMock.mockImplementation(() => {
|
||||
return {
|
||||
zipFile: {
|
||||
path: 'test',
|
||||
size: 5,
|
||||
sha256: '123'
|
||||
},
|
||||
tarFile: {
|
||||
path: 'test2',
|
||||
size: 52,
|
||||
sha256: '1234'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Validate the manifest
|
||||
const manifest = JSON.parse(setOutputMock.mock.calls[1][1])
|
||||
expect(manifest.mediaType).toEqual(
|
||||
'application/vnd.oci.image.manifest.v1+json'
|
||||
)
|
||||
expect(manifest.config.mediaType).toEqual(
|
||||
'application/vnd.github.actions.package.config.v1+json'
|
||||
)
|
||||
expect(manifest.layers.length).toEqual(3)
|
||||
expect(manifest.annotations['com.github.package.type']).toEqual(
|
||||
'actions_oci_pkg'
|
||||
)
|
||||
getRepositoryMetadataMock.mockImplementation(() => {
|
||||
return { repoId: 'test', ownerId: 'test' }
|
||||
})
|
||||
|
||||
// Expect all the temp files to be cleaned up
|
||||
expect(removeDirMock).toHaveBeenCalledWith('/tmp/test')
|
||||
expect(removeDirMock).toHaveBeenCalledTimes(
|
||||
createTempDirMock.mock.calls.length
|
||||
)
|
||||
}
|
||||
getContainerRegistryURLMock.mockImplementation(() => {
|
||||
return new URL('https://ghcr.io')
|
||||
})
|
||||
|
||||
publishOCIArtifactMock.mockImplementation(() => {
|
||||
return {
|
||||
packageURL: 'https://ghcr.io/v2/test-org/test-repo:1.2.3',
|
||||
manifestDigest: 'my-test-digest'
|
||||
}
|
||||
})
|
||||
|
||||
// Run the action
|
||||
await main.run('')
|
||||
|
||||
// Check the results
|
||||
expect(publishOCIArtifactMock).toHaveBeenCalledTimes(1)
|
||||
|
||||
// Check outputs
|
||||
expect(setOutputMock).toHaveBeenCalledTimes(3)
|
||||
|
||||
expect(setOutputMock).toHaveBeenCalledWith(
|
||||
'package-url',
|
||||
'https://ghcr.io/v2/test-org/test-repo:1.2.3'
|
||||
)
|
||||
|
||||
expect(setOutputMock).toHaveBeenCalledWith(
|
||||
'package-manifest',
|
||||
expect.any(String)
|
||||
)
|
||||
|
||||
expect(setOutputMock).toHaveBeenCalledWith(
|
||||
'package-manifest-sha',
|
||||
'sha256:my-test-digest'
|
||||
)
|
||||
|
||||
// Expect all the temp files to be cleaned up
|
||||
expect(removeDirMock).toHaveBeenCalledWith('/tmp/test')
|
||||
expect(removeDirMock).toHaveBeenCalledTimes(
|
||||
createTempDirMock.mock.calls.length
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,6 +7,9 @@ describe('createActionPackageManifest', () => {
|
||||
const repo = 'test-org/test-repo'
|
||||
const sanitizedRepo = 'test-org-test-repo'
|
||||
const version = '1.2.3'
|
||||
const repoId = '123'
|
||||
const ownerId = '456'
|
||||
const sourceCommit = 'abc'
|
||||
const tarFile: FileMetadata = {
|
||||
path: '/test/test/test.tar.gz',
|
||||
sha256: 'tarSha',
|
||||
@@ -21,7 +24,7 @@ describe('createActionPackageManifest', () => {
|
||||
const expectedJSON = `{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"artifactType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"artifactType": "application/vnd.github.actions.package.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.github.actions.package.config.v1+json",
|
||||
"size": 0,
|
||||
@@ -60,7 +63,11 @@ describe('createActionPackageManifest', () => {
|
||||
"org.opencontainers.image.created":"${date.toISOString()}",
|
||||
"action.tar.gz.digest":"${tarFile.sha256}",
|
||||
"action.zip.digest":"${zipFile.sha256}",
|
||||
"com.github.package.type":"actions_oci_pkg"
|
||||
"com.github.package.type":"actions_oci_pkg",
|
||||
"com.github.package.version":"1.2.3",
|
||||
"com.github.source.repo.id":"123",
|
||||
"com.github.source.repo.owner.id":"456",
|
||||
"com.github.source.commit":"abc"
|
||||
}
|
||||
}`
|
||||
|
||||
@@ -76,6 +83,9 @@ describe('createActionPackageManifest', () => {
|
||||
sha256: zipFile.sha256
|
||||
},
|
||||
repo,
|
||||
repoId,
|
||||
ownerId,
|
||||
sourceCommit,
|
||||
version,
|
||||
date
|
||||
)
|
||||
|
||||
@@ -37,6 +37,8 @@ runs:
|
||||
env:
|
||||
TOKEN: ${{ github.token }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
GITHUB_REF: ${{ github.ref }}
|
||||
GITHUB_SHA: ${{ github.sha }}
|
||||
- name: Output variables
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getContainerRegistryURL = exports.getRepositoryMetadata = void 0;
|
||||
async function getRepositoryMetadata(repository, token) {
|
||||
const response = await fetch(`${process.env.GITHUB_API_URL}/repos/${repository}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch repository metadata: ${response.statusText}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
// Check that the response contains the expected data
|
||||
if (!data.id || !data.owner.id) {
|
||||
throw new Error(`Failed to fetch repository metadata: ${JSON.stringify(data)}`);
|
||||
}
|
||||
return { repoId: data.id, ownerId: data.owner.id };
|
||||
}
|
||||
exports.getRepositoryMetadata = getRepositoryMetadata;
|
||||
async function getContainerRegistryURL() {
|
||||
const response = await fetch(`${process.env.GITHUB_API_URL}/packages/container-registry-url`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch status page: ${response.statusText}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
const registryURL = new URL(data.url);
|
||||
return registryURL;
|
||||
}
|
||||
exports.getContainerRegistryURL = getContainerRegistryURL;
|
||||
//# sourceMappingURL=api-client.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":";;;AAGO,KAAK,UAAU,qBAAqB,CAAC,UAAkB,EAAE,KAAa;IACzE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAC1B,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,UAAU,UAAU,EAAE,CACpD,CAAA;IAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,wCAAwC,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;IAChF,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;IAElC,qDAAqD;IACrD,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,wCAAwC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACjF,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAA;AACpD,CAAC;AAjBH,sDAiBG;AAEM,KAAK,UAAU,uBAAuB;IAC3C,MAAM,QAAQ,GAAG,MAAM,KAAK,CAC1B,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,kCAAkC,CAChE,CAAA;IACD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,gCAAgC,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;IACxE,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;IAClC,MAAM,WAAW,GAAQ,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC1C,OAAO,WAAW,CAAA;AACpB,CAAC;AAVD,0DAUC"}
|
||||
+18
-16
@@ -26,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.bundleFilesintoDirectory = exports.readFileContents = exports.isDirectory = exports.createArchives = exports.removeDir = exports.createTempDir = void 0;
|
||||
exports.stageActionFiles = exports.readFileContents = exports.isDirectory = exports.createArchives = exports.removeDir = exports.createTempDir = void 0;
|
||||
const fs = __importStar(require("fs"));
|
||||
const fs_extra_1 = __importDefault(require("fs-extra"));
|
||||
const path = __importStar(require("path"));
|
||||
@@ -67,7 +67,7 @@ async function createArchives(distPath, archiveTargetPath = createTempDir()) {
|
||||
resolve(fileMetadata(zipPath));
|
||||
});
|
||||
archive.pipe(output);
|
||||
archive.directory(distPath, false);
|
||||
archive.directory(distPath, false); // TODO: make sure this doesn't include dirs that start with ., same with below
|
||||
archive.finalize();
|
||||
});
|
||||
const createTarPromise = new Promise((resolve, reject) => {
|
||||
@@ -101,23 +101,25 @@ function readFileContents(filePath) {
|
||||
return fs.readFileSync(filePath);
|
||||
}
|
||||
exports.readFileContents = readFileContents;
|
||||
function bundleFilesintoDirectory(files, targetDir = createTempDir()) {
|
||||
for (const file of files) {
|
||||
if (!fs.existsSync(file)) {
|
||||
throw new Error(`File ${file} does not exist`);
|
||||
}
|
||||
if (isDirectory(file)) {
|
||||
const targetFolder = path.join(targetDir, path.basename(file));
|
||||
fs_extra_1.default.copySync(file, targetFolder);
|
||||
}
|
||||
else {
|
||||
const targetFile = path.join(targetDir, path.basename(file));
|
||||
fs.copyFileSync(file, targetFile);
|
||||
// Copy actions files from sourceDir to targetDir, excluding files and folders not relevant to the action
|
||||
// Errors if the repo appears to not contain any action files, such as an action.yml file
|
||||
function stageActionFiles(actionDir, targetDir) {
|
||||
var actionYmlFound = false;
|
||||
fs_extra_1.default.copySync(actionDir, targetDir, {
|
||||
filter: (src, dest) => {
|
||||
const basename = path.basename(src);
|
||||
if (basename === 'action.yml' || basename === 'action.yaml') {
|
||||
actionYmlFound = true;
|
||||
}
|
||||
// Filter out hidden folers like .git and .github
|
||||
return !basename.startsWith('.');
|
||||
}
|
||||
});
|
||||
if (!actionYmlFound) {
|
||||
throw new Error(`No action.yml or action.yaml file found in source repository`);
|
||||
}
|
||||
return targetDir;
|
||||
}
|
||||
exports.bundleFilesintoDirectory = bundleFilesintoDirectory;
|
||||
exports.stageActionFiles = stageActionFiles;
|
||||
// Converts a file path to a filemetadata object by querying the fs for relevant metadata.
|
||||
async function fileMetadata(filePath) {
|
||||
const stats = fs.statSync(filePath);
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"fs-helper.js","sourceRoot":"","sources":["../src/fs-helper.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"fs-helper.js","sourceRoot":"","sources":["../src/fs-helper.ts"],"names":[],"mappings":"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"}
|
||||
+9
-3
@@ -32,7 +32,7 @@ const axios_1 = __importDefault(require("axios"));
|
||||
const fsHelper = __importStar(require("./fs-helper"));
|
||||
const axios_debug_log_1 = __importDefault(require("axios-debug-log"));
|
||||
// Publish the OCI artifact and return the URL where it can be downloaded
|
||||
async function publishOCIArtifact(token, registry, repository, releaseId, semver, zipFile, tarFile, manifest, debugRequests = false) {
|
||||
async function publishOCIArtifact(token, registry, repository, semver, zipFile, tarFile, manifest, debugRequests = false) {
|
||||
if (debugRequests) {
|
||||
configureRequestDebugLogging();
|
||||
}
|
||||
@@ -54,8 +54,8 @@ async function publishOCIArtifact(token, registry, repository, releaseId, semver
|
||||
}
|
||||
});
|
||||
await Promise.all(layerUploads);
|
||||
await uploadManifest(JSON.stringify(manifest), manifestEndpoint, b64Token);
|
||||
return new URL(`${repository}:${semver}`, registry);
|
||||
const digest = await uploadManifest(JSON.stringify(manifest), manifestEndpoint, b64Token);
|
||||
return { packageURL: new URL(`${repository}:${semver}`, registry), manifestDigest: digest };
|
||||
}
|
||||
exports.publishOCIArtifact = publishOCIArtifact;
|
||||
async function uploadLayer(layer, file, registryURL, checkBlobEndpoint, uploadBlobEndpoint, b64Token) {
|
||||
@@ -117,6 +117,7 @@ async function uploadLayer(layer, file, registryURL, checkBlobEndpoint, uploadBl
|
||||
throw new Error(`Unexpected response from PUT upload ${putResponse.status} for layer ${layer.digest}`);
|
||||
}
|
||||
}
|
||||
// Uploads the manifest and returns the digest returned by GHCR
|
||||
async function uploadManifest(manifestJSON, manifestEndpoint, b64Token) {
|
||||
core.info(`Uploading manifest to ${manifestEndpoint}.`);
|
||||
const putResponse = await axios_1.default.put(manifestEndpoint, manifestJSON, {
|
||||
@@ -131,6 +132,11 @@ async function uploadManifest(manifestJSON, manifestEndpoint, b64Token) {
|
||||
if (putResponse.status !== 201) {
|
||||
throw new Error(`Unexpected response from PUT manifest ${putResponse.status}`);
|
||||
}
|
||||
const digestResponseHeader = putResponse.headers['Docker-Content-Digest'];
|
||||
if (digestResponseHeader === undefined) {
|
||||
throw new Error(`No digest header in response from PUT manifest ${manifestEndpoint}`);
|
||||
}
|
||||
return digestResponseHeader;
|
||||
}
|
||||
function configureRequestDebugLogging() {
|
||||
(0, axios_debug_log_1.default)({
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"ghcr-client.js","sourceRoot":"","sources":["../src/ghcr-client.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"ghcr-client.js","sourceRoot":"","sources":["../src/ghcr-client.ts"],"names":[],"mappings":"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"}
|
||||
+115
-80
@@ -74659,6 +74659,43 @@ ZipStream.prototype.finalize = function() {
|
||||
*/
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 95707:
|
||||
/***/ ((__unused_webpack_module, exports) => {
|
||||
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.getContainerRegistryURL = exports.getRepositoryMetadata = void 0;
|
||||
async function getRepositoryMetadata(repository, token) {
|
||||
const response = await fetch(`${process.env.GITHUB_API_URL}/repos/${repository}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch repository metadata due to bad status code: ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
// Check that the response contains the expected data
|
||||
if (!data.id || !data.owner.id) {
|
||||
throw new Error(`Failed to fetch repository metadata: unexpected response format`);
|
||||
}
|
||||
return { repoId: data.id, ownerId: data.owner.id };
|
||||
}
|
||||
exports.getRepositoryMetadata = getRepositoryMetadata;
|
||||
async function getContainerRegistryURL() {
|
||||
const response = await fetch(`${process.env.GITHUB_API_URL}/packages/container-registry-url`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch container registry url due to bad status code: ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
if (!data.url) {
|
||||
throw new Error(`Failed to fetch repository metadata: unexpected response format`);
|
||||
}
|
||||
const registryURL = new URL(data.url);
|
||||
return registryURL;
|
||||
}
|
||||
exports.getContainerRegistryURL = getContainerRegistryURL;
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 76642:
|
||||
@@ -74693,7 +74730,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.readFileContents = exports.isActionRepo = exports.isDirectory = exports.createArchives = exports.getConsolidatedDirectory = exports.removeDir = exports.createTempDir = void 0;
|
||||
exports.stageActionFiles = exports.readFileContents = exports.isDirectory = exports.createArchives = exports.removeDir = exports.createTempDir = void 0;
|
||||
const fs = __importStar(__nccwpck_require__(57147));
|
||||
const fs_extra_1 = __importDefault(__nccwpck_require__(5630));
|
||||
const path = __importStar(__nccwpck_require__(71017));
|
||||
@@ -74716,24 +74753,6 @@ function removeDir(dir) {
|
||||
}
|
||||
}
|
||||
exports.removeDir = removeDir;
|
||||
// TODO: rename this function, it is not state-preserving, so it shouldn't just be called "get'"
|
||||
function getConsolidatedDirectory(filePathSpec) {
|
||||
const paths = filePathSpec.split(' '); // TODO: handle files with spaces
|
||||
// TODO: do check on paths to make sure they're valid and not reaching outside the space
|
||||
let consolidatedPath = '';
|
||||
let needToCleanUpDir = false;
|
||||
if (paths.length === 1 && isDirectory(paths[0])) {
|
||||
// If the path is a single directory, we can skip the bundling step
|
||||
consolidatedPath = paths[0];
|
||||
}
|
||||
else {
|
||||
// Otherwise, we need to bundle the files & folders into a temporary directory
|
||||
consolidatedPath = bundleFilesintoDirectory(paths);
|
||||
needToCleanUpDir = true;
|
||||
}
|
||||
return { consolidatedPath, needToCleanUpDir };
|
||||
}
|
||||
exports.getConsolidatedDirectory = getConsolidatedDirectory;
|
||||
// Creates both a tar.gz and zip archive of the given directory and returns the paths to both archives (stored in the provided target directory)
|
||||
// as well as the size/sha256 hash of each file.
|
||||
async function createArchives(distPath, archiveTargetPath = createTempDir()) {
|
||||
@@ -74782,32 +74801,29 @@ function isDirectory(dirPath) {
|
||||
return fs.existsSync(dirPath) && fs.lstatSync(dirPath).isDirectory();
|
||||
}
|
||||
exports.isDirectory = isDirectory;
|
||||
function isActionRepo(stagingDir) {
|
||||
return (fs.existsSync(path.join(stagingDir, 'action.yml')) ||
|
||||
fs.existsSync(path.join(stagingDir, 'action.yaml')));
|
||||
}
|
||||
exports.isActionRepo = isActionRepo;
|
||||
function readFileContents(filePath) {
|
||||
return fs.readFileSync(filePath);
|
||||
}
|
||||
exports.readFileContents = readFileContents;
|
||||
function bundleFilesintoDirectory(filePaths) {
|
||||
const targetDir = createTempDir();
|
||||
for (const filePath of filePaths) {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
throw new Error(`filePath ${filePath} does not exist`);
|
||||
}
|
||||
if (isDirectory(filePath)) {
|
||||
const targetFolder = path.join(targetDir, path.basename(filePath)); // TODO: basename is probably not what we actually want here. Or is it? Maybe conflicts between dir1/dir2 and dir1/dir3/dir2 are just user error or ??
|
||||
fs_extra_1.default.copySync(filePath, targetFolder); // TODO: ignore files preceded by .
|
||||
}
|
||||
else {
|
||||
const targetFile = path.join(targetDir, path.basename(filePath));
|
||||
fs.copyFileSync(filePath, targetFile);
|
||||
// Copy actions files from sourceDir to targetDir, excluding files and folders not relevant to the action
|
||||
// Errors if the repo appears to not contain any action files, such as an action.yml file
|
||||
function stageActionFiles(actionDir, targetDir) {
|
||||
var actionYmlFound = false;
|
||||
fs_extra_1.default.copySync(actionDir, targetDir, {
|
||||
filter: (src, dest) => {
|
||||
const basename = path.basename(src);
|
||||
if (basename === 'action.yml' || basename === 'action.yaml') {
|
||||
actionYmlFound = true;
|
||||
}
|
||||
// Filter out hidden folers like .git and .github
|
||||
return !basename.startsWith('.');
|
||||
}
|
||||
});
|
||||
if (!actionYmlFound) {
|
||||
throw new Error(`No action.yml or action.yaml file found in source repository`);
|
||||
}
|
||||
return targetDir;
|
||||
}
|
||||
exports.stageActionFiles = stageActionFiles;
|
||||
// Converts a file path to a filemetadata object by querying the fs for relevant metadata.
|
||||
async function fileMetadata(filePath) {
|
||||
const stats = fs.statSync(filePath);
|
||||
@@ -74873,7 +74889,7 @@ const axios_1 = __importDefault(__nccwpck_require__(88757));
|
||||
const fsHelper = __importStar(__nccwpck_require__(76642));
|
||||
const axios_debug_log_1 = __importDefault(__nccwpck_require__(79301));
|
||||
// Publish the OCI artifact and return the URL where it can be downloaded
|
||||
async function publishOCIArtifact(token, registry, repository, releaseId, semver, zipFile, tarFile, manifest, debugRequests = false) {
|
||||
async function publishOCIArtifact(token, registry, repository, semver, zipFile, tarFile, manifest, debugRequests = false) {
|
||||
if (debugRequests) {
|
||||
configureRequestDebugLogging();
|
||||
}
|
||||
@@ -74895,8 +74911,11 @@ async function publishOCIArtifact(token, registry, repository, releaseId, semver
|
||||
}
|
||||
});
|
||||
await Promise.all(layerUploads);
|
||||
await uploadManifest(JSON.stringify(manifest), manifestEndpoint, b64Token);
|
||||
return new URL(`${repository}:${semver}`, registry);
|
||||
const digest = await uploadManifest(JSON.stringify(manifest), manifestEndpoint, b64Token);
|
||||
return {
|
||||
packageURL: new URL(`${repository}:${semver}`, registry),
|
||||
manifestDigest: digest
|
||||
};
|
||||
}
|
||||
exports.publishOCIArtifact = publishOCIArtifact;
|
||||
async function uploadLayer(layer, file, registryURL, checkBlobEndpoint, uploadBlobEndpoint, b64Token) {
|
||||
@@ -74958,6 +74977,7 @@ async function uploadLayer(layer, file, registryURL, checkBlobEndpoint, uploadBl
|
||||
throw new Error(`Unexpected response from PUT upload ${putResponse.status} for layer ${layer.digest}`);
|
||||
}
|
||||
}
|
||||
// Uploads the manifest and returns the digest returned by GHCR
|
||||
async function uploadManifest(manifestJSON, manifestEndpoint, b64Token) {
|
||||
core.info(`Uploading manifest to ${manifestEndpoint}.`);
|
||||
const putResponse = await axios_1.default.put(manifestEndpoint, manifestJSON, {
|
||||
@@ -74972,6 +74992,11 @@ async function uploadManifest(manifestJSON, manifestEndpoint, b64Token) {
|
||||
if (putResponse.status !== 201) {
|
||||
throw new Error(`Unexpected response from PUT manifest ${putResponse.status}`);
|
||||
}
|
||||
const digestResponseHeader = putResponse.headers['Docker-Content-Digest'];
|
||||
if (digestResponseHeader === undefined) {
|
||||
throw new Error(`No digest header in response from PUT manifest ${manifestEndpoint}`);
|
||||
}
|
||||
return digestResponseHeader;
|
||||
}
|
||||
function configureRequestDebugLogging() {
|
||||
(0, axios_debug_log_1.default)({
|
||||
@@ -75049,8 +75074,8 @@ const github = __importStar(__nccwpck_require__(95438));
|
||||
const fsHelper = __importStar(__nccwpck_require__(76642));
|
||||
const ociContainer = __importStar(__nccwpck_require__(33207));
|
||||
const ghcr = __importStar(__nccwpck_require__(62894));
|
||||
const api = __importStar(__nccwpck_require__(95707));
|
||||
const semver_1 = __importDefault(__nccwpck_require__(11383));
|
||||
const crypto_1 = __importDefault(__nccwpck_require__(6113));
|
||||
/**
|
||||
* The main function for the action.
|
||||
* @returns {Promise<void>} Resolves when the action is complete.
|
||||
@@ -75058,55 +75083,38 @@ const crypto_1 = __importDefault(__nccwpck_require__(6113));
|
||||
async function run(pathInput) {
|
||||
const tmpDirs = [];
|
||||
try {
|
||||
// Parse and validate Actions execution context, including the repository name, release name and event type
|
||||
const repository = process.env.GITHUB_REPOSITORY || '';
|
||||
if (repository === '') {
|
||||
core.setFailed(`Could not find Repository.`);
|
||||
return;
|
||||
}
|
||||
if (github.context.eventName !== 'release') {
|
||||
core.setFailed('Please ensure you have the workflow trigger as release.');
|
||||
const token = process.env.TOKEN || '';
|
||||
const sourceCommit = process.env.GITHUB_SHA || '';
|
||||
if (token === '') {
|
||||
core.setFailed(`Could not find GITHUB_TOKEN.`);
|
||||
return;
|
||||
}
|
||||
const releaseId = github.context.payload.release.id;
|
||||
const releaseTag = github.context.payload.release.tag_name;
|
||||
// Strip any leading 'v' from the tag in case the release format is e.g. 'v1.0.0' as recommended by GitHub docs
|
||||
// https://docs.github.com/en/actions/creating-actions/releasing-and-maintaining-actions
|
||||
const targetVersion = semver_1.default.parse(releaseTag.replace(/^v/, ''));
|
||||
if (!targetVersion) {
|
||||
core.setFailed(`${releaseTag} is not a valid semantic version, and so cannot be uploaded as an Immutable Action.`);
|
||||
return;
|
||||
}
|
||||
const token = process.env.TOKEN;
|
||||
const { consolidatedPath, needToCleanUpDir } = fsHelper.getConsolidatedDirectory(pathInput);
|
||||
if (needToCleanUpDir) {
|
||||
tmpDirs.push(consolidatedPath);
|
||||
}
|
||||
if (!fsHelper.isActionRepo(consolidatedPath)) {
|
||||
core.setFailed('action.y(a)ml not found. Action packages can be created only for action repositories.');
|
||||
if (sourceCommit === '') {
|
||||
core.setFailed(`Could not find source commit.`);
|
||||
return;
|
||||
}
|
||||
const semanticVersion = parseSourceSemanticVersion();
|
||||
// Create a temporary directory to stage files for packaging in archives
|
||||
const stagedActionFilesDir = fsHelper.createTempDir();
|
||||
tmpDirs.push(stagedActionFilesDir);
|
||||
fsHelper.stageActionFiles('.', stagedActionFilesDir);
|
||||
// Create a temporary directory to store the archives
|
||||
const archiveDir = fsHelper.createTempDir();
|
||||
tmpDirs.push(archiveDir);
|
||||
const archives = await fsHelper.createArchives(consolidatedPath, archiveDir);
|
||||
const manifest = ociContainer.createActionPackageManifest(archives.tarFile, archives.zipFile, repository, targetVersion.raw, new Date());
|
||||
// Generate SHA-256 hash of the manifest
|
||||
const manifestSHA = crypto_1.default.createHash('sha256');
|
||||
const manifestHash = manifestSHA
|
||||
.update(JSON.stringify(manifest))
|
||||
.digest('hex');
|
||||
const response = await fetch(`${process.env.GITHUB_API_URL}/packages/container-registry-url`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch status page: ${response.statusText}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
const registryURL = new URL(data.url);
|
||||
console.log(`Container registry URL: ${registryURL}`);
|
||||
const packageURL = await ghcr.publishOCIArtifact(token, registryURL, repository, releaseId.toString(), targetVersion.raw, archives.zipFile, archives.tarFile, manifest, true);
|
||||
const archives = await fsHelper.createArchives(stagedActionFilesDir, archiveDir);
|
||||
const { repoId, ownerId } = await api.getRepositoryMetadata(repository, token);
|
||||
const manifest = ociContainer.createActionPackageManifest(archives.tarFile, archives.zipFile, repository, repoId, ownerId, sourceCommit, semanticVersion.raw, new Date());
|
||||
const containerRegistryURL = await api.getContainerRegistryURL();
|
||||
console.log(`Container registry URL: ${containerRegistryURL}`);
|
||||
const { packageURL, manifestDigest } = await ghcr.publishOCIArtifact(token, containerRegistryURL, repository, semanticVersion.raw, archives.zipFile, archives.tarFile, manifest, true);
|
||||
core.setOutput('package-url', packageURL.toString());
|
||||
core.setOutput('package-manifest', JSON.stringify(manifest));
|
||||
core.setOutput('package-manifest-sha', `sha256:${manifestHash}`);
|
||||
core.setOutput('package-manifest-sha', `sha256:${manifestDigest}`);
|
||||
}
|
||||
catch (error) {
|
||||
// Fail the workflow run if an error occurs
|
||||
@@ -75123,6 +75131,29 @@ async function run(pathInput) {
|
||||
}
|
||||
}
|
||||
exports.run = run;
|
||||
// This action can be triggered by release events or tag push events.
|
||||
// In each case, the source event should produce a Semantic Version compliant tag representing the code to be packaged.
|
||||
function parseSourceSemanticVersion() {
|
||||
const event = github.context.eventName;
|
||||
var semverTag = '';
|
||||
// Grab the raw tag
|
||||
if (event === 'release')
|
||||
semverTag = github.context.payload.release.tag_name;
|
||||
else if (event === 'push' && github.context.ref.startsWith('refs/tags/')) {
|
||||
semverTag = github.context.ref.replace(/^refs\/tags\//, '');
|
||||
}
|
||||
else {
|
||||
throw new Error(`This action can only be triggered by release events or tag push events.`);
|
||||
}
|
||||
if (semverTag === '') {
|
||||
throw new Error(`Could not find a Semantic Version tag in the event payload.`);
|
||||
}
|
||||
const semanticVersion = semver_1.default.parse(semverTag.replace(/^v/, ''));
|
||||
if (!semanticVersion) {
|
||||
throw new Error(`${semverTag} is not a valid semantic version, and so cannot be uploaded as an Immutable Action.`);
|
||||
}
|
||||
return semanticVersion;
|
||||
}
|
||||
|
||||
|
||||
/***/ }),
|
||||
@@ -75135,7 +75166,7 @@ exports.run = run;
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.createActionPackageManifest = void 0;
|
||||
// Given a name and archive metadata, creates a manifest in the format expected by GHCR for an Actions Package.
|
||||
function createActionPackageManifest(tarFile, zipFile, repository, version, created) {
|
||||
function createActionPackageManifest(tarFile, zipFile, repository, repoId, ownerId, sourceCommit, version, created) {
|
||||
const configLayer = createConfigLayer();
|
||||
const sanitizedRepo = sanitizeRepository(repository);
|
||||
const tarLayer = createTarLayer(tarFile, sanitizedRepo, version);
|
||||
@@ -75143,14 +75174,18 @@ function createActionPackageManifest(tarFile, zipFile, repository, version, crea
|
||||
const manifest = {
|
||||
schemaVersion: 2,
|
||||
mediaType: 'application/vnd.oci.image.manifest.v1+json',
|
||||
artifactType: 'application/vnd.oci.image.manifest.v1+json',
|
||||
artifactType: 'application/vnd.github.actions.package.v1+json',
|
||||
config: configLayer,
|
||||
layers: [configLayer, tarLayer, zipLayer],
|
||||
annotations: {
|
||||
'org.opencontainers.image.created': created.toISOString(),
|
||||
'action.tar.gz.digest': tarFile.sha256,
|
||||
'action.zip.digest': zipFile.sha256,
|
||||
'com.github.package.type': 'actions_oci_pkg'
|
||||
'com.github.package.type': 'actions_oci_pkg',
|
||||
'com.github.package.version': version,
|
||||
'com.github.source.repo.id': repoId,
|
||||
'com.github.source.repo.owner.id': ownerId,
|
||||
'com.github.source.commit': sourceCommit
|
||||
}
|
||||
};
|
||||
return manifest;
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAAA;;GAEG;AACH,iCAA4B;AAE5B,mEAAmE;AACnE,IAAA,UAAG,GAAE,CAAA"}
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;AAAA;;GAEG;AACH,iCAA4B;AAC5B,wDAA+B;AAE/B,MAAM,IAAI,GAAG,IAAA,kBAAQ,EAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAA;AACxD,mEAAmE;AACnE,IAAA,UAAG,EAAC,IAAI,CAAC,CAAA"}
|
||||
+43
-36
@@ -32,63 +32,47 @@ const github = __importStar(require("@actions/github"));
|
||||
const fsHelper = __importStar(require("./fs-helper"));
|
||||
const ociContainer = __importStar(require("./oci-container"));
|
||||
const ghcr = __importStar(require("./ghcr-client"));
|
||||
const api = __importStar(require("./api-client"));
|
||||
const semver_1 = __importDefault(require("semver"));
|
||||
/**
|
||||
* The main function for the action.
|
||||
* @returns {Promise<void>} Resolves when the action is complete.
|
||||
*/
|
||||
async function run() {
|
||||
async function run(pathInput) {
|
||||
const tmpDirs = [];
|
||||
try {
|
||||
// Parse and validate Actions execution context, including the repository name, release name and event type
|
||||
const repository = process.env.GITHUB_REPOSITORY || '';
|
||||
if (repository === '') {
|
||||
core.setFailed(`Could not find Repository.`);
|
||||
return;
|
||||
}
|
||||
if (github.context.eventName !== 'release') {
|
||||
core.setFailed('Please ensure you have the workflow trigger as release.');
|
||||
const token = process.env.TOKEN || '';
|
||||
const sourceCommit = process.env.GITHUB_SHA || '';
|
||||
if (token === '') {
|
||||
core.setFailed(`Could not find source commit.`);
|
||||
return;
|
||||
}
|
||||
const releaseId = github.context.payload.release.id;
|
||||
const releaseTag = github.context.payload.release.tag_name;
|
||||
// Strip any leading 'v' from the tag in case the release format is e.g. 'v1.0.0' as recommended by GitHub docs
|
||||
// https://docs.github.com/en/actions/creating-actions/releasing-and-maintaining-actions
|
||||
const targetVersion = semver_1.default.parse(releaseTag.replace(/^v/, ''));
|
||||
if (!targetVersion) {
|
||||
// TODO: We may want to limit semvers to only x.x.x, without the pre-release tags, but for now we'll allow them.
|
||||
core.setFailed(`${releaseTag} is not a valid semantic version, and so cannot be uploaded as an Immutable Action.`);
|
||||
if (sourceCommit === '') {
|
||||
core.setFailed(`Could not find source commit.`);
|
||||
return;
|
||||
}
|
||||
// Gather & validate user inputs
|
||||
const token = core.getInput('token');
|
||||
const registryURL = new URL('https://ghcr.io/'); // TODO: Should this be dynamic? Maybe an API endpoint to grab the registry for GHES/proxima purposes.
|
||||
console.log(core.getInput('registry'));
|
||||
console.log(`registryURL: ${registryURL}`);
|
||||
// Paths to be included in the OCI image
|
||||
const paths = core.getInput('path').split(' ');
|
||||
let path = '';
|
||||
if (paths.length === 1 && fsHelper.isDirectory(paths[0])) {
|
||||
// If the path is a single directory, we can skip the bundling step
|
||||
path = paths[0];
|
||||
}
|
||||
else {
|
||||
// Otherwise, we need to bundle the files & folders into a temporary directory
|
||||
const bundleDir = fsHelper.createTempDir();
|
||||
tmpDirs.push(bundleDir);
|
||||
path = fsHelper.bundleFilesintoDirectory(paths, bundleDir);
|
||||
}
|
||||
const semanticVersion = parseSourceSemanticVersion();
|
||||
// Create a temporary directory to stage files for packaging in archives
|
||||
const stagedActionFilesDir = fsHelper.createTempDir();
|
||||
tmpDirs.push(stagedActionFilesDir);
|
||||
fsHelper.stageActionFiles(".", stagedActionFilesDir);
|
||||
// Create a temporary directory to store the archives
|
||||
const archiveDir = fsHelper.createTempDir();
|
||||
tmpDirs.push(archiveDir);
|
||||
const archives = await fsHelper.createArchives(path, archiveDir);
|
||||
const manifest = ociContainer.createActionPackageManifest(archives.tarFile, archives.zipFile, repository, targetVersion.raw, new Date());
|
||||
const packageURL = await ghcr.publishOCIArtifact(token, registryURL, repository, releaseId.toString(), targetVersion.raw, archives.zipFile, archives.tarFile, manifest, true);
|
||||
const archives = await fsHelper.createArchives(stagedActionFilesDir, archiveDir);
|
||||
const { repoId, ownerId } = await api.getRepositoryMetadata(repository, token);
|
||||
const manifest = ociContainer.createActionPackageManifest(archives.tarFile, archives.zipFile, repository, repoId, ownerId, sourceCommit, semanticVersion.raw, new Date());
|
||||
const containerRegistryURL = await api.getContainerRegistryURL();
|
||||
console.log(`Container registry URL: ${containerRegistryURL}`);
|
||||
const { packageURL, manifestDigest } = await ghcr.publishOCIArtifact(token, containerRegistryURL, repository, semanticVersion.raw, archives.zipFile, archives.tarFile, manifest, true);
|
||||
core.setOutput('package-url', packageURL.toString());
|
||||
// TODO: We might need to do some attestation stuff here, but unsure how to integrate it yet.
|
||||
// We might need to return the manifest JSON from the Action and link it to another action,
|
||||
// or we might be able to make an API call here. It's unclear at this point.
|
||||
core.setOutput('package-manifest', JSON.stringify(manifest));
|
||||
core.setOutput('package-manifest-sha', `sha256:${manifestDigest}`);
|
||||
}
|
||||
catch (error) {
|
||||
// Fail the workflow run if an error occurs
|
||||
@@ -105,4 +89,27 @@ async function run() {
|
||||
}
|
||||
}
|
||||
exports.run = run;
|
||||
// This action can be triggered by release events or tag push events.
|
||||
// In each case, the source event should produce a Semantic Version compliant tag representing the code to be packaged.
|
||||
function parseSourceSemanticVersion() {
|
||||
const event = github.context.eventName;
|
||||
var semverTag = '';
|
||||
// Grab the raw tag
|
||||
if (event === 'release')
|
||||
semverTag = github.context.payload.release.tag_name;
|
||||
else if (event === 'push' && github.context.ref.startsWith('refs/tags/')) {
|
||||
semverTag = github.context.ref.replace(/^refs\/tags\//, '');
|
||||
}
|
||||
else {
|
||||
throw new Error(`This action can only be triggered by release events or tag push events.`);
|
||||
}
|
||||
if (semverTag === '') {
|
||||
throw new Error(`Could not find a Semantic Version tag in the event payload.`);
|
||||
}
|
||||
const semanticVersion = semver_1.default.parse(semverTag.replace(/^v/, ''));
|
||||
if (!semanticVersion) {
|
||||
throw new Error(`${semverTag} is not a valid semantic version, and so cannot be uploaded as an Immutable Action.`);
|
||||
}
|
||||
return semanticVersion;
|
||||
}
|
||||
//# sourceMappingURL=main.js.map
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"}
|
||||
+7
-3
@@ -2,7 +2,7 @@
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createActionPackageManifest = void 0;
|
||||
// Given a name and archive metadata, creates a manifest in the format expected by GHCR for an Actions Package.
|
||||
function createActionPackageManifest(tarFile, zipFile, repository, version, created) {
|
||||
function createActionPackageManifest(tarFile, zipFile, repository, repoId, ownerId, sourceCommit, version, created) {
|
||||
const configLayer = createConfigLayer();
|
||||
const sanitizedRepo = sanitizeRepository(repository);
|
||||
const tarLayer = createTarLayer(tarFile, sanitizedRepo, version);
|
||||
@@ -10,14 +10,18 @@ function createActionPackageManifest(tarFile, zipFile, repository, version, crea
|
||||
const manifest = {
|
||||
schemaVersion: 2,
|
||||
mediaType: 'application/vnd.oci.image.manifest.v1+json',
|
||||
artifactType: 'application/vnd.oci.image.manifest.v1+json',
|
||||
artifactType: 'application/vnd.github.actions.package.v1+json',
|
||||
config: configLayer,
|
||||
layers: [configLayer, tarLayer, zipLayer],
|
||||
annotations: {
|
||||
'org.opencontainers.image.created': created.toISOString(),
|
||||
'action.tar.gz.digest': tarFile.sha256,
|
||||
'action.zip.digest': zipFile.sha256,
|
||||
'com.github.package.type': 'actions_oci_pkg'
|
||||
'com.github.package.type': 'actions_oci_pkg',
|
||||
'com.github.package.version': version,
|
||||
'com.github.source.repo.id': repoId,
|
||||
'com.github.source.repo.owner.id': ownerId,
|
||||
'com.github.source.commit': sourceCommit,
|
||||
}
|
||||
};
|
||||
return manifest;
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"oci-container.js","sourceRoot":"","sources":["../src/oci-container.ts"],"names":[],"mappings":";;;AAkBA,+GAA+G;AAC/G,SAAgB,2BAA2B,CACzC,OAAqB,EACrB,OAAqB,EACrB,UAAkB,EAClB,OAAe,EACf,OAAa;IAEb,MAAM,WAAW,GAAG,iBAAiB,EAAE,CAAA;IACvC,MAAM,aAAa,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAA;IACpD,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,CAAC,CAAA;IAChE,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,CAAC,CAAA;IAEhE,MAAM,QAAQ,GAAa;QACzB,aAAa,EAAE,CAAC;QAChB,SAAS,EAAE,4CAA4C;QACvD,YAAY,EAAE,4CAA4C;QAC1D,MAAM,EAAE,WAAW;QACnB,MAAM,EAAE,CAAC,WAAW,EAAE,QAAQ,EAAE,QAAQ,CAAC;QACzC,WAAW,EAAE;YACX,kCAAkC,EAAE,OAAO,CAAC,WAAW,EAAE;YACzD,sBAAsB,EAAE,OAAO,CAAC,MAAM;YACtC,mBAAmB,EAAE,OAAO,CAAC,MAAM;YACnC,yBAAyB,EAAE,iBAAiB;SAC7C;KACF,CAAA;IAED,OAAO,QAAQ,CAAA;AACjB,CAAC;AA3BD,kEA2BC;AAED,8BAA8B;AAC9B,SAAS,iBAAiB;IACxB,MAAM,WAAW,GAAU;QACzB,SAAS,EAAE,uDAAuD;QAClE,IAAI,EAAE,CAAC;QACP,MAAM,EACJ,yEAAyE;QAC3E,WAAW,EAAE;YACX,gCAAgC,EAAE,aAAa;SAChD;KACF,CAAA;IAED,OAAO,WAAW,CAAA;AACpB,CAAC;AAED,SAAS,cAAc,CACrB,OAAqB,EACrB,UAAkB,EAClB,OAAe;IAEf,MAAM,QAAQ,GAAU;QACtB,SAAS,EAAE,qDAAqD;QAChE,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,WAAW,EAAE;YACX,gCAAgC,EAAE,GAAG,UAAU,IAAI,OAAO,MAAM;SACjE;KACF,CAAA;IAED,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,SAAS,cAAc,CACrB,OAAqB,EACrB,UAAkB,EAClB,OAAe;IAEf,MAAM,QAAQ,GAAU;QACtB,SAAS,EAAE,0DAA0D;QACrE,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,WAAW,EAAE;YACX,gCAAgC,EAAE,GAAG,UAAU,IAAI,OAAO,SAAS;SACpE;KACF,CAAA;IAED,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,4DAA4D;AAC5D,qEAAqE;AACrE,SAAS,kBAAkB,CAAC,UAAkB;IAC5C,OAAO,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;AACrC,CAAC"}
|
||||
{"version":3,"file":"oci-container.js","sourceRoot":"","sources":["../src/oci-container.ts"],"names":[],"mappings":";;;AAkBA,+GAA+G;AAC/G,SAAgB,2BAA2B,CACzC,OAAqB,EACrB,OAAqB,EACrB,UAAkB,EAClB,MAAc,EACd,OAAe,EACf,YAAoB,EACpB,OAAe,EACf,OAAa;IAEb,MAAM,WAAW,GAAG,iBAAiB,EAAE,CAAA;IACvC,MAAM,aAAa,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAA;IACpD,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,CAAC,CAAA;IAChE,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,CAAC,CAAA;IAEhE,MAAM,QAAQ,GAAa;QACzB,aAAa,EAAE,CAAC;QAChB,SAAS,EAAE,4CAA4C;QACvD,YAAY,EAAE,gDAAgD;QAC9D,MAAM,EAAE,WAAW;QACnB,MAAM,EAAE,CAAC,WAAW,EAAE,QAAQ,EAAE,QAAQ,CAAC;QACzC,WAAW,EAAE;YACX,kCAAkC,EAAE,OAAO,CAAC,WAAW,EAAE;YACzD,sBAAsB,EAAE,OAAO,CAAC,MAAM;YACtC,mBAAmB,EAAE,OAAO,CAAC,MAAM;YACnC,yBAAyB,EAAE,iBAAiB;YAC5C,4BAA4B,EAAE,OAAO;YACrC,2BAA2B,EAAE,MAAM;YACnC,iCAAiC,EAAE,OAAO;YAC1C,0BAA0B,EAAE,YAAY;SACzC;KACF,CAAA;IAED,OAAO,QAAQ,CAAA;AACjB,CAAC;AAlCD,kEAkCC;AAED,8BAA8B;AAC9B,SAAS,iBAAiB;IACxB,MAAM,WAAW,GAAU;QACzB,SAAS,EAAE,uDAAuD;QAClE,IAAI,EAAE,CAAC;QACP,MAAM,EACJ,yEAAyE;QAC3E,WAAW,EAAE;YACX,gCAAgC,EAAE,aAAa;SAChD;KACF,CAAA;IAED,OAAO,WAAW,CAAA;AACpB,CAAC;AAED,SAAS,cAAc,CACrB,OAAqB,EACrB,UAAkB,EAClB,OAAe;IAEf,MAAM,QAAQ,GAAU;QACtB,SAAS,EAAE,qDAAqD;QAChE,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,WAAW,EAAE;YACX,gCAAgC,EAAE,GAAG,UAAU,IAAI,OAAO,MAAM;SACjE;KACF,CAAA;IAED,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,SAAS,cAAc,CACrB,OAAqB,EACrB,UAAkB,EAClB,OAAe;IAEf,MAAM,QAAQ,GAAU;QACtB,SAAS,EAAE,0DAA0D;QACrE,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,WAAW,EAAE;YACX,gCAAgC,EAAE,GAAG,UAAU,IAAI,OAAO,SAAS;SACpE;KACF,CAAA;IAED,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,4DAA4D;AAC5D,qEAAqE;AACrE,SAAS,kBAAkB,CAAC,UAAkB;IAC5C,OAAO,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;AACrC,CAAC"}
|
||||
@@ -0,0 +1,49 @@
|
||||
import * as core from '@actions/core'
|
||||
import * as github from '@actions/github'
|
||||
|
||||
export async function getRepositoryMetadata(
|
||||
repository: string,
|
||||
token: string
|
||||
): Promise<{ repoId: string; ownerId: string }> {
|
||||
const response = await fetch(
|
||||
`${process.env.GITHUB_API_URL}/repos/${repository}`
|
||||
)
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Failed to fetch repository metadata due to bad status code: ${response.status}`
|
||||
)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
// Check that the response contains the expected data
|
||||
if (!data.id || !data.owner.id) {
|
||||
throw new Error(
|
||||
`Failed to fetch repository metadata: unexpected response format`
|
||||
)
|
||||
}
|
||||
|
||||
return { repoId: data.id, ownerId: data.owner.id }
|
||||
}
|
||||
|
||||
export async function getContainerRegistryURL(): Promise<URL> {
|
||||
const response = await fetch(
|
||||
`${process.env.GITHUB_API_URL}/packages/container-registry-url`
|
||||
)
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Failed to fetch container registry url due to bad status code: ${response.status}`
|
||||
)
|
||||
}
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.url) {
|
||||
throw new Error(
|
||||
`Failed to fetch repository metadata: unexpected response format`
|
||||
)
|
||||
}
|
||||
|
||||
const registryURL: URL = new URL(data.url)
|
||||
return registryURL
|
||||
}
|
||||
+20
-42
@@ -29,27 +29,6 @@ export interface FileMetadata {
|
||||
sha256: string
|
||||
}
|
||||
|
||||
// TODO: rename this function, it is not state-preserving, so it shouldn't just be called "get'"
|
||||
export function getConsolidatedDirectory(filePathSpec: string): {
|
||||
consolidatedPath: string
|
||||
needToCleanUpDir: boolean
|
||||
} {
|
||||
const paths: string[] = filePathSpec.split(' ') // TODO: handle files with spaces
|
||||
// TODO: do check on paths to make sure they're valid and not reaching outside the space
|
||||
let consolidatedPath = ''
|
||||
let needToCleanUpDir = false
|
||||
if (paths.length === 1 && isDirectory(paths[0])) {
|
||||
// If the path is a single directory, we can skip the bundling step
|
||||
consolidatedPath = paths[0]
|
||||
} else {
|
||||
// Otherwise, we need to bundle the files & folders into a temporary directory
|
||||
consolidatedPath = bundleFilesintoDirectory(paths)
|
||||
needToCleanUpDir = true
|
||||
}
|
||||
|
||||
return { consolidatedPath, needToCleanUpDir }
|
||||
}
|
||||
|
||||
// Creates both a tar.gz and zip archive of the given directory and returns the paths to both archives (stored in the provided target directory)
|
||||
// as well as the size/sha256 hash of each file.
|
||||
export async function createArchives(
|
||||
@@ -112,34 +91,33 @@ export function isDirectory(dirPath: string): boolean {
|
||||
return fs.existsSync(dirPath) && fs.lstatSync(dirPath).isDirectory()
|
||||
}
|
||||
|
||||
export function isActionRepo(stagingDir: string): boolean {
|
||||
return (
|
||||
fs.existsSync(path.join(stagingDir, 'action.yml')) ||
|
||||
fs.existsSync(path.join(stagingDir, 'action.yaml'))
|
||||
)
|
||||
}
|
||||
|
||||
export function readFileContents(filePath: string): Buffer {
|
||||
return fs.readFileSync(filePath)
|
||||
}
|
||||
|
||||
function bundleFilesintoDirectory(filePaths: string[]): string {
|
||||
const targetDir: string = createTempDir()
|
||||
for (const filePath of filePaths) {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
throw new Error(`filePath ${filePath} does not exist`)
|
||||
}
|
||||
// Copy actions files from sourceDir to targetDir, excluding files and folders not relevant to the action
|
||||
// Errors if the repo appears to not contain any action files, such as an action.yml file
|
||||
export function stageActionFiles(actionDir: string, targetDir: string) {
|
||||
var actionYmlFound = false
|
||||
|
||||
if (isDirectory(filePath)) {
|
||||
const targetFolder = path.join(targetDir, path.basename(filePath)) // TODO: basename is probably not what we actually want here. Or is it? Maybe conflicts between dir1/dir2 and dir1/dir3/dir2 are just user error or ??
|
||||
fsExtra.copySync(filePath, targetFolder) // TODO: ignore files preceded by .
|
||||
} else {
|
||||
const targetFile = path.join(targetDir, path.basename(filePath))
|
||||
fs.copyFileSync(filePath, targetFile)
|
||||
fsExtra.copySync(actionDir, targetDir, {
|
||||
filter: (src: string, dest: string) => {
|
||||
const basename = path.basename(src)
|
||||
|
||||
if (basename === 'action.yml' || basename === 'action.yaml') {
|
||||
actionYmlFound = true
|
||||
}
|
||||
|
||||
// Filter out hidden folers like .git and .github
|
||||
return !basename.startsWith('.')
|
||||
}
|
||||
})
|
||||
|
||||
if (!actionYmlFound) {
|
||||
throw new Error(
|
||||
`No action.yml or action.yaml file found in source repository`
|
||||
)
|
||||
}
|
||||
|
||||
return targetDir
|
||||
}
|
||||
|
||||
// Converts a file path to a filemetadata object by querying the fs for relevant metadata.
|
||||
|
||||
+21
-5
@@ -10,13 +10,12 @@ export async function publishOCIArtifact(
|
||||
token: string,
|
||||
registry: URL,
|
||||
repository: string,
|
||||
releaseId: string,
|
||||
semver: string,
|
||||
zipFile: FileMetadata,
|
||||
tarFile: FileMetadata,
|
||||
manifest: ociContainer.Manifest,
|
||||
debugRequests = false
|
||||
): Promise<URL> {
|
||||
): Promise<{ packageURL: URL; manifestDigest: string }> {
|
||||
if (debugRequests) {
|
||||
configureRequestDebugLogging()
|
||||
}
|
||||
@@ -76,9 +75,16 @@ export async function publishOCIArtifact(
|
||||
|
||||
await Promise.all(layerUploads)
|
||||
|
||||
await uploadManifest(JSON.stringify(manifest), manifestEndpoint, b64Token)
|
||||
const digest = await uploadManifest(
|
||||
JSON.stringify(manifest),
|
||||
manifestEndpoint,
|
||||
b64Token
|
||||
)
|
||||
|
||||
return new URL(`${repository}:${semver}`, registry)
|
||||
return {
|
||||
packageURL: new URL(`${repository}:${semver}`, registry),
|
||||
manifestDigest: digest
|
||||
}
|
||||
}
|
||||
|
||||
async function uploadLayer(
|
||||
@@ -172,11 +178,12 @@ async function uploadLayer(
|
||||
}
|
||||
}
|
||||
|
||||
// Uploads the manifest and returns the digest returned by GHCR
|
||||
async function uploadManifest(
|
||||
manifestJSON: string,
|
||||
manifestEndpoint: string,
|
||||
b64Token: string
|
||||
): Promise<void> {
|
||||
): Promise<string> {
|
||||
core.info(`Uploading manifest to ${manifestEndpoint}.`)
|
||||
|
||||
const putResponse = await axios.put(manifestEndpoint, manifestJSON, {
|
||||
@@ -194,6 +201,15 @@ async function uploadManifest(
|
||||
`Unexpected response from PUT manifest ${putResponse.status}`
|
||||
)
|
||||
}
|
||||
|
||||
const digestResponseHeader = putResponse.headers['Docker-Content-Digest']
|
||||
if (digestResponseHeader === undefined) {
|
||||
throw new Error(
|
||||
`No digest header in response from PUT manifest ${manifestEndpoint}`
|
||||
)
|
||||
}
|
||||
|
||||
return digestResponseHeader
|
||||
}
|
||||
|
||||
function configureRequestDebugLogging(): void {
|
||||
|
||||
+64
-52
@@ -3,8 +3,8 @@ import * as github from '@actions/github'
|
||||
import * as fsHelper from './fs-helper'
|
||||
import * as ociContainer from './oci-container'
|
||||
import * as ghcr from './ghcr-client'
|
||||
import * as api from './api-client'
|
||||
import semver from 'semver'
|
||||
import crypto from 'crypto'
|
||||
|
||||
/**
|
||||
* The main function for the action.
|
||||
@@ -14,82 +14,62 @@ export async function run(pathInput: string): Promise<void> {
|
||||
const tmpDirs: string[] = []
|
||||
|
||||
try {
|
||||
// Parse and validate Actions execution context, including the repository name, release name and event type
|
||||
const repository: string = process.env.GITHUB_REPOSITORY || ''
|
||||
|
||||
if (repository === '') {
|
||||
core.setFailed(`Could not find Repository.`)
|
||||
return
|
||||
}
|
||||
|
||||
if (github.context.eventName !== 'release') {
|
||||
core.setFailed('Please ensure you have the workflow trigger as release.')
|
||||
const token: string = process.env.TOKEN || ''
|
||||
const sourceCommit: string = process.env.GITHUB_SHA || ''
|
||||
if (token === '') {
|
||||
core.setFailed(`Could not find GITHUB_TOKEN.`)
|
||||
return
|
||||
}
|
||||
if (sourceCommit === '') {
|
||||
core.setFailed(`Could not find source commit.`)
|
||||
return
|
||||
}
|
||||
|
||||
const releaseId: string = github.context.payload.release.id
|
||||
const releaseTag: string = github.context.payload.release.tag_name
|
||||
// Strip any leading 'v' from the tag in case the release format is e.g. 'v1.0.0' as recommended by GitHub docs
|
||||
// https://docs.github.com/en/actions/creating-actions/releasing-and-maintaining-actions
|
||||
const targetVersion = semver.parse(releaseTag.replace(/^v/, ''))
|
||||
if (!targetVersion) {
|
||||
core.setFailed(
|
||||
`${releaseTag} is not a valid semantic version, and so cannot be uploaded as an Immutable Action.`
|
||||
)
|
||||
return
|
||||
}
|
||||
const semanticVersion = parseSourceSemanticVersion()
|
||||
|
||||
const token: string = process.env.TOKEN!
|
||||
|
||||
const { consolidatedPath, needToCleanUpDir } =
|
||||
fsHelper.getConsolidatedDirectory(pathInput)
|
||||
if (needToCleanUpDir) {
|
||||
tmpDirs.push(consolidatedPath)
|
||||
}
|
||||
|
||||
if (!fsHelper.isActionRepo(consolidatedPath)) {
|
||||
core.setFailed(
|
||||
'action.y(a)ml not found. Action packages can be created only for action repositories.'
|
||||
)
|
||||
return
|
||||
}
|
||||
// Create a temporary directory to stage files for packaging in archives
|
||||
const stagedActionFilesDir = fsHelper.createTempDir()
|
||||
tmpDirs.push(stagedActionFilesDir)
|
||||
fsHelper.stageActionFiles('.', stagedActionFilesDir)
|
||||
|
||||
// Create a temporary directory to store the archives
|
||||
const archiveDir = fsHelper.createTempDir()
|
||||
tmpDirs.push(archiveDir)
|
||||
const archives = await fsHelper.createArchives(
|
||||
stagedActionFilesDir,
|
||||
archiveDir
|
||||
)
|
||||
|
||||
const archives = await fsHelper.createArchives(consolidatedPath, archiveDir)
|
||||
const { repoId, ownerId } = await api.getRepositoryMetadata(
|
||||
repository,
|
||||
token
|
||||
)
|
||||
|
||||
const manifest = ociContainer.createActionPackageManifest(
|
||||
archives.tarFile,
|
||||
archives.zipFile,
|
||||
repository,
|
||||
targetVersion.raw,
|
||||
repoId,
|
||||
ownerId,
|
||||
sourceCommit,
|
||||
semanticVersion.raw,
|
||||
new Date()
|
||||
)
|
||||
|
||||
// Generate SHA-256 hash of the manifest
|
||||
const manifestSHA = crypto.createHash('sha256')
|
||||
const manifestHash = manifestSHA
|
||||
.update(JSON.stringify(manifest))
|
||||
.digest('hex')
|
||||
const containerRegistryURL = await api.getContainerRegistryURL()
|
||||
console.log(`Container registry URL: ${containerRegistryURL}`)
|
||||
|
||||
const response = await fetch(
|
||||
`${process.env.GITHUB_API_URL}/packages/container-registry-url`
|
||||
)
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch status page: ${response.statusText}`)
|
||||
}
|
||||
const data = await response.json()
|
||||
const registryURL: URL = new URL(data.url)
|
||||
console.log(`Container registry URL: ${registryURL}`)
|
||||
|
||||
const packageURL = await ghcr.publishOCIArtifact(
|
||||
const { packageURL, manifestDigest } = await ghcr.publishOCIArtifact(
|
||||
token,
|
||||
registryURL,
|
||||
containerRegistryURL,
|
||||
repository,
|
||||
releaseId.toString(),
|
||||
targetVersion.raw,
|
||||
semanticVersion.raw,
|
||||
archives.zipFile,
|
||||
archives.tarFile,
|
||||
manifest,
|
||||
@@ -98,7 +78,7 @@ export async function run(pathInput: string): Promise<void> {
|
||||
|
||||
core.setOutput('package-url', packageURL.toString())
|
||||
core.setOutput('package-manifest', JSON.stringify(manifest))
|
||||
core.setOutput('package-manifest-sha', `sha256:${manifestHash}`)
|
||||
core.setOutput('package-manifest-sha', `sha256:${manifestDigest}`)
|
||||
} catch (error) {
|
||||
// Fail the workflow run if an error occurs
|
||||
if (error instanceof Error) core.setFailed(error.message)
|
||||
@@ -111,3 +91,35 @@ export async function run(pathInput: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This action can be triggered by release events or tag push events.
|
||||
// In each case, the source event should produce a Semantic Version compliant tag representing the code to be packaged.
|
||||
function parseSourceSemanticVersion(): semver.SemVer {
|
||||
const event = github.context.eventName
|
||||
var semverTag = ''
|
||||
|
||||
// Grab the raw tag
|
||||
if (event === 'release') semverTag = github.context.payload.release.tag_name
|
||||
else if (event === 'push' && github.context.ref.startsWith('refs/tags/')) {
|
||||
semverTag = github.context.ref.replace(/^refs\/tags\//, '')
|
||||
} else {
|
||||
throw new Error(
|
||||
`This action can only be triggered by release events or tag push events.`
|
||||
)
|
||||
}
|
||||
|
||||
if (semverTag === '') {
|
||||
throw new Error(
|
||||
`Could not find a Semantic Version tag in the event payload.`
|
||||
)
|
||||
}
|
||||
|
||||
const semanticVersion = semver.parse(semverTag.replace(/^v/, ''))
|
||||
if (!semanticVersion) {
|
||||
throw new Error(
|
||||
`${semverTag} is not a valid semantic version, and so cannot be uploaded as an Immutable Action.`
|
||||
)
|
||||
}
|
||||
|
||||
return semanticVersion
|
||||
}
|
||||
|
||||
@@ -21,6 +21,9 @@ export function createActionPackageManifest(
|
||||
tarFile: FileMetadata,
|
||||
zipFile: FileMetadata,
|
||||
repository: string,
|
||||
repoId: string,
|
||||
ownerId: string,
|
||||
sourceCommit: string,
|
||||
version: string,
|
||||
created: Date
|
||||
): Manifest {
|
||||
@@ -32,14 +35,18 @@ export function createActionPackageManifest(
|
||||
const manifest: Manifest = {
|
||||
schemaVersion: 2,
|
||||
mediaType: 'application/vnd.oci.image.manifest.v1+json',
|
||||
artifactType: 'application/vnd.oci.image.manifest.v1+json',
|
||||
artifactType: 'application/vnd.github.actions.package.v1+json',
|
||||
config: configLayer,
|
||||
layers: [configLayer, tarLayer, zipLayer],
|
||||
annotations: {
|
||||
'org.opencontainers.image.created': created.toISOString(),
|
||||
'action.tar.gz.digest': tarFile.sha256,
|
||||
'action.zip.digest': zipFile.sha256,
|
||||
'com.github.package.type': 'actions_oci_pkg'
|
||||
'com.github.package.type': 'actions_oci_pkg',
|
||||
'com.github.package.version': version,
|
||||
'com.github.source.repo.id': repoId,
|
||||
'com.github.source.repo.owner.id': ownerId,
|
||||
'com.github.source.commit': sourceCommit
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user