update deps, linting, test cases, etc.
This commit is contained in:
@@ -41,8 +41,8 @@ describe('createArchives', () => {
|
||||
expect(tarFile.sha256.startsWith('sha256:')).toEqual(true)
|
||||
|
||||
// Validate the hashes by comparing to the output of the system's hashing utility
|
||||
let zipSHA = zipFile.sha256.substring(7) // remove "sha256:" prefix
|
||||
let tarSHA = tarFile.sha256.substring(7) // remove "sha256:" prefix
|
||||
const zipSHA = zipFile.sha256.substring(7) // remove "sha256:" prefix
|
||||
const tarSHA = tarFile.sha256.substring(7) // remove "sha256:" prefix
|
||||
|
||||
// sha256 hash is 64 characters long
|
||||
expect(zipSHA).toHaveLength(64)
|
||||
@@ -86,13 +86,13 @@ describe('createTempDir', () => {
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
dirs.forEach(dir => {
|
||||
for (const dir of dirs) {
|
||||
fs.rmSync(dir, { recursive: true })
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
it('creates a temporary directory in the OS temporary dir', () => {
|
||||
let tmpDir = fsHelper.createTempDir()
|
||||
const tmpDir = fsHelper.createTempDir()
|
||||
dirs.push(tmpDir)
|
||||
|
||||
expect(fs.existsSync(tmpDir)).toEqual(true)
|
||||
@@ -101,10 +101,10 @@ describe('createTempDir', () => {
|
||||
})
|
||||
|
||||
it('creates a unique temporary directory', () => {
|
||||
let dir1 = fsHelper.createTempDir()
|
||||
const dir1 = fsHelper.createTempDir()
|
||||
dirs.push(dir1)
|
||||
|
||||
let dir2 = fsHelper.createTempDir()
|
||||
const dir2 = fsHelper.createTempDir()
|
||||
dirs.push(dir2)
|
||||
|
||||
expect(dir1).not.toEqual(dir2)
|
||||
@@ -170,3 +170,47 @@ describe('removeDir', () => {
|
||||
expect(fs.existsSync(dir)).toEqual(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('bundleFilesintoDirectory', () => {
|
||||
let sourceDir: string
|
||||
let targetDir: string
|
||||
|
||||
beforeEach(() => {
|
||||
sourceDir = fsHelper.createTempDir()
|
||||
targetDir = fsHelper.createTempDir()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(sourceDir, { recursive: true })
|
||||
fs.rmSync(targetDir, { recursive: true })
|
||||
})
|
||||
|
||||
it('bundles files and folders into a directory', () => {
|
||||
// Create some test files and folders in the sourceDir
|
||||
const file1 = `${sourceDir}/file1.txt`
|
||||
const folder1 = `${sourceDir}/folder1`
|
||||
const file2 = `${folder1}/file3.txt`
|
||||
|
||||
fs.mkdirSync(folder1)
|
||||
fs.writeFileSync(file1, fileContent)
|
||||
fs.writeFileSync(file2, fileContent)
|
||||
|
||||
// Bundle the files and folders into the targetDir
|
||||
fsHelper.bundleFilesintoDirectory([file1, folder1], targetDir)
|
||||
|
||||
// Check that the files and folders were copied
|
||||
expect(fs.existsSync(file1)).toEqual(true)
|
||||
expect(fsHelper.readFileContents(file1).toString()).toEqual(fileContent)
|
||||
|
||||
expect(fs.existsSync(`${targetDir}/folder1`)).toEqual(true)
|
||||
|
||||
expect(fs.existsSync(file2)).toEqual(true)
|
||||
expect(fsHelper.readFileContents(file2).toString()).toEqual(fileContent)
|
||||
})
|
||||
|
||||
it('throws an error if a file or directory does not exist', () => {
|
||||
expect(() => {
|
||||
fsHelper.bundleFilesintoDirectory(['/does/not/exist'], targetDir)
|
||||
}).toThrow('File /does/not/exist does not exist')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { publishOCIArtifact } from '../src/ghcr-client'
|
||||
import axios, { AxiosRequestConfig } from 'axios'
|
||||
import * as fs from 'fs'
|
||||
import axios from 'axios'
|
||||
import * as fsHelper from '../src/fs-helper'
|
||||
import * as ociContainer from '../src/oci-container'
|
||||
|
||||
@@ -89,14 +88,12 @@ describe('publishOCIArtifact', () => {
|
||||
|
||||
it('publishes layer blobs & then a manifest to the provided registry', async () => {
|
||||
// Simulate none of the blobs existing currently
|
||||
axiosHeadMock.mockImplementation(
|
||||
async (url: string, config: AxiosRequestConfig) => {
|
||||
validateRequestConfig(404, url, config)
|
||||
return {
|
||||
status: 404
|
||||
}
|
||||
axiosHeadMock.mockImplementation(async (url, config) => {
|
||||
validateRequestConfig(404, url, config)
|
||||
return {
|
||||
status: 404
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
// Simulate successful initiation of uploads for all blobs & return location
|
||||
axiosPostMock.mockImplementation(async (url, data, config) => {
|
||||
@@ -110,7 +107,7 @@ describe('publishOCIArtifact', () => {
|
||||
})
|
||||
|
||||
// Simulate successful reading of all the files
|
||||
fsReadFileSyncMock.mockImplementation(async path => {
|
||||
fsReadFileSyncMock.mockImplementation(() => {
|
||||
return Buffer.from('test')
|
||||
})
|
||||
|
||||
@@ -161,7 +158,7 @@ describe('publishOCIArtifact', () => {
|
||||
})
|
||||
|
||||
// Simulate successful reading of all the files
|
||||
fsReadFileSyncMock.mockImplementation(async path => {
|
||||
fsReadFileSyncMock.mockImplementation(() => {
|
||||
return Buffer.from('test')
|
||||
})
|
||||
|
||||
@@ -199,7 +196,7 @@ describe('publishOCIArtifact', () => {
|
||||
}
|
||||
})
|
||||
|
||||
expect(
|
||||
await expect(
|
||||
publishOCIArtifact(
|
||||
token,
|
||||
registry,
|
||||
@@ -230,7 +227,7 @@ describe('publishOCIArtifact', () => {
|
||||
}
|
||||
})
|
||||
|
||||
expect(
|
||||
await expect(
|
||||
publishOCIArtifact(
|
||||
token,
|
||||
registry,
|
||||
@@ -262,7 +259,7 @@ describe('publishOCIArtifact', () => {
|
||||
}
|
||||
})
|
||||
|
||||
expect(
|
||||
await expect(
|
||||
publishOCIArtifact(
|
||||
token,
|
||||
registry,
|
||||
@@ -297,7 +294,7 @@ describe('publishOCIArtifact', () => {
|
||||
})
|
||||
|
||||
// Simulate successful reading of all the files
|
||||
fsReadFileSyncMock.mockImplementation(async path => {
|
||||
fsReadFileSyncMock.mockImplementation(() => {
|
||||
return Buffer.from('test')
|
||||
})
|
||||
|
||||
@@ -309,7 +306,7 @@ describe('publishOCIArtifact', () => {
|
||||
}
|
||||
})
|
||||
|
||||
expect(
|
||||
await expect(
|
||||
publishOCIArtifact(
|
||||
token,
|
||||
registry,
|
||||
@@ -344,7 +341,7 @@ describe('publishOCIArtifact', () => {
|
||||
})
|
||||
|
||||
// Simulate successful reading of all the files
|
||||
fsReadFileSyncMock.mockImplementation(async path => {
|
||||
fsReadFileSyncMock.mockImplementation(() => {
|
||||
return Buffer.from('test')
|
||||
})
|
||||
|
||||
@@ -363,7 +360,7 @@ describe('publishOCIArtifact', () => {
|
||||
}
|
||||
})
|
||||
|
||||
expect(
|
||||
await expect(
|
||||
publishOCIArtifact(
|
||||
token,
|
||||
registry,
|
||||
@@ -398,7 +395,7 @@ describe('publishOCIArtifact', () => {
|
||||
})
|
||||
|
||||
// Simulate successful reading of all the files
|
||||
fsReadFileSyncMock.mockImplementation(path => {
|
||||
fsReadFileSyncMock.mockImplementation(() => {
|
||||
throw new Error('failed to read a file: test')
|
||||
})
|
||||
|
||||
@@ -410,7 +407,7 @@ describe('publishOCIArtifact', () => {
|
||||
}
|
||||
})
|
||||
|
||||
expect(
|
||||
await expect(
|
||||
publishOCIArtifact(
|
||||
token,
|
||||
registry,
|
||||
@@ -425,10 +422,10 @@ describe('publishOCIArtifact', () => {
|
||||
})
|
||||
|
||||
it('throws an error if one of the layers has the wrong media type', async () => {
|
||||
let modifiedTestManifest = testManifest
|
||||
const modifiedTestManifest = testManifest
|
||||
modifiedTestManifest.layers[0].mediaType = 'application/json'
|
||||
|
||||
expect(
|
||||
await expect(
|
||||
publishOCIArtifact(
|
||||
token,
|
||||
registry,
|
||||
@@ -445,11 +442,8 @@ describe('publishOCIArtifact', () => {
|
||||
|
||||
// We expect all axios calls to have auth headers set and to not intercept any status codes so we can handle them.
|
||||
// This function verifies that given an axios request config.
|
||||
function validateRequestConfig(
|
||||
status: number,
|
||||
url: string,
|
||||
config: AxiosRequestConfig
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function validateRequestConfig(status: number, url: string, config: any): void {
|
||||
// Basic URL checks
|
||||
expect(url).toBeDefined()
|
||||
|
||||
|
||||
+102
-85
@@ -11,15 +11,9 @@ import * as main from '../src/main'
|
||||
import * as github from '@actions/github'
|
||||
|
||||
import * as fsHelper from '../src/fs-helper'
|
||||
import * as ociContainer from '../src/oci-container'
|
||||
import * as ghcr from '../src/ghcr-client'
|
||||
|
||||
// Mock the action's main function
|
||||
const runMock = jest.spyOn(main, 'run')
|
||||
|
||||
// Mock the GitHub Actions core library
|
||||
let debugMock: jest.SpyInstance
|
||||
let errorMock: jest.SpyInstance
|
||||
let getInputMock: jest.SpyInstance
|
||||
let setFailedMock: jest.SpyInstance
|
||||
let setOutputMock: jest.SpyInstance
|
||||
@@ -29,6 +23,7 @@ let createTempDirMock: jest.SpyInstance
|
||||
let isDirectoryMock: jest.SpyInstance
|
||||
let createArchivesMock: jest.SpyInstance
|
||||
let removeDirMock: jest.SpyInstance
|
||||
let bundleFilesintoDirectoryMock: jest.SpyInstance
|
||||
|
||||
// Mock the GHCR Client
|
||||
let publishOCIArtifactMock: jest.SpyInstance
|
||||
@@ -38,8 +33,6 @@ describe('action', () => {
|
||||
jest.clearAllMocks()
|
||||
|
||||
// Core mocks
|
||||
debugMock = jest.spyOn(core, 'debug').mockImplementation()
|
||||
errorMock = jest.spyOn(core, 'error').mockImplementation()
|
||||
getInputMock = jest.spyOn(core, 'getInput').mockImplementation()
|
||||
setFailedMock = jest.spyOn(core, 'setFailed').mockImplementation()
|
||||
setOutputMock = jest.spyOn(core, 'setOutput').mockImplementation()
|
||||
@@ -53,6 +46,9 @@ describe('action', () => {
|
||||
.spyOn(fsHelper, 'createArchives')
|
||||
.mockImplementation()
|
||||
removeDirMock = jest.spyOn(fsHelper, 'removeDir').mockImplementation()
|
||||
bundleFilesintoDirectoryMock = jest
|
||||
.spyOn(fsHelper, 'bundleFilesintoDirectory')
|
||||
.mockImplementation()
|
||||
|
||||
// GHCR Client mocks
|
||||
publishOCIArtifactMock = jest
|
||||
@@ -105,7 +101,7 @@ describe('action', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('fails if path is not a directory', async () => {
|
||||
it('fails if multiple paths are provided and staging files fails', async () => {
|
||||
// Mock the environment
|
||||
process.env.GITHUB_REPOSITORY = 'test/test'
|
||||
github.context.eventName = 'release'
|
||||
@@ -117,23 +113,24 @@ describe('action', () => {
|
||||
}
|
||||
getInputMock.mockImplementation((name: string) => {
|
||||
if (name === 'path') {
|
||||
return 'not-a-directory'
|
||||
return 'directory1 directory2'
|
||||
} else if (name === 'registry') {
|
||||
return 'https://ghcr.io'
|
||||
}
|
||||
return ''
|
||||
})
|
||||
|
||||
isDirectoryMock.mockImplementation(() => false)
|
||||
isDirectoryMock.mockImplementation(() => true)
|
||||
|
||||
bundleFilesintoDirectoryMock.mockImplementation(() => {
|
||||
throw new Error('Something went wrong')
|
||||
})
|
||||
|
||||
// Run the action
|
||||
await main.run()
|
||||
|
||||
// Check the results
|
||||
expect(isDirectoryMock).toHaveBeenCalledWith('not-a-directory')
|
||||
expect(setFailedMock).toHaveBeenCalledWith(
|
||||
'The path not-a-directory is not a directory. Please provide a path to a valid directory.'
|
||||
)
|
||||
expect(setFailedMock).toHaveBeenCalledWith('Something went wrong')
|
||||
})
|
||||
|
||||
it('fails if an error is thrown from dependent code', async () => {
|
||||
@@ -174,77 +171,97 @@ describe('action', () => {
|
||||
expect(removeDirMock).toHaveBeenCalledWith('/tmp/test')
|
||||
})
|
||||
|
||||
it('uploads and returns the manifest & package URL if all succeeds', async () => {
|
||||
// Mock the environment
|
||||
process.env.GITHUB_REPOSITORY = 'test/test'
|
||||
github.context.eventName = 'release'
|
||||
github.context.payload = {
|
||||
release: {
|
||||
id: '123',
|
||||
tag_name: 'v1.0.0'
|
||||
}
|
||||
}
|
||||
getInputMock.mockImplementation((name: string) => {
|
||||
if (name === 'path') {
|
||||
return 'test'
|
||||
} else if (name === 'registry') {
|
||||
return 'https://ghcr.io'
|
||||
}
|
||||
return ''
|
||||
})
|
||||
it('successfully uploads if the release tag is a semver without v prefix', async () => {
|
||||
await testHappyPath('1.0.0', 'test')
|
||||
})
|
||||
|
||||
isDirectoryMock.mockImplementation(() => true)
|
||||
it('successfully uploads if the release tag is a semver with v prefix', async () => {
|
||||
await testHappyPath('v1.0.0', 'test')
|
||||
})
|
||||
|
||||
createTempDirMock.mockImplementation(() => '/tmp/test')
|
||||
|
||||
createArchivesMock.mockImplementation(() => {
|
||||
return {
|
||||
zipFile: {
|
||||
path: 'test',
|
||||
size: 5,
|
||||
sha256: '123'
|
||||
},
|
||||
tarFile: {
|
||||
path: 'test2',
|
||||
size: 52,
|
||||
sha256: '1234'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
publishOCIArtifactMock.mockImplementation(() => {
|
||||
return new URL('https://ghcr.io/v2/test/test:1.0.0')
|
||||
})
|
||||
|
||||
// Run the action
|
||||
await main.run()
|
||||
|
||||
expect(publishOCIArtifactMock).toHaveBeenCalledTimes(1)
|
||||
|
||||
// Check manifest is in output
|
||||
expect(setOutputMock).toHaveBeenCalledWith(
|
||||
'package-url',
|
||||
'https://ghcr.io/v2/test/test:1.0.0'
|
||||
)
|
||||
expect(setOutputMock).toHaveBeenCalledWith(
|
||||
'package-manifest',
|
||||
expect.any(String)
|
||||
)
|
||||
|
||||
// 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'
|
||||
)
|
||||
|
||||
// Expect the files to be cleaned up
|
||||
expect(removeDirMock).toHaveBeenCalledWith('/tmp/test')
|
||||
it('successfully uploads if multiple paths are provided', async () => {
|
||||
await testHappyPath('v1.0.0', '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/test'
|
||||
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 ''
|
||||
})
|
||||
|
||||
isDirectoryMock.mockImplementation(() => true)
|
||||
|
||||
bundleFilesintoDirectoryMock.mockImplementation(() => {
|
||||
return '/tmp/test'
|
||||
})
|
||||
|
||||
createTempDirMock.mockImplementation(() => '/tmp/test')
|
||||
|
||||
createArchivesMock.mockImplementation(() => {
|
||||
return {
|
||||
zipFile: {
|
||||
path: 'test',
|
||||
size: 5,
|
||||
sha256: '123'
|
||||
},
|
||||
tarFile: {
|
||||
path: 'test2',
|
||||
size: 52,
|
||||
sha256: '1234'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
publishOCIArtifactMock.mockImplementation(() => {
|
||||
return new URL('https://ghcr.io/v2/test/test:1.0.0')
|
||||
})
|
||||
|
||||
// Run the action
|
||||
await main.run()
|
||||
|
||||
expect(publishOCIArtifactMock).toHaveBeenCalledTimes(1)
|
||||
|
||||
// Check manifest is in output
|
||||
expect(setOutputMock).toHaveBeenCalledWith(
|
||||
'package-url',
|
||||
'https://ghcr.io/v2/test/test:1.0.0'
|
||||
)
|
||||
expect(setOutputMock).toHaveBeenCalledWith(
|
||||
'package-manifest',
|
||||
expect.any(String)
|
||||
)
|
||||
|
||||
// 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'
|
||||
)
|
||||
|
||||
// Expect all the temp files to be cleaned up
|
||||
expect(removeDirMock).toHaveBeenCalledWith('/tmp/test')
|
||||
expect(removeDirMock).toHaveBeenCalledTimes(
|
||||
createTempDirMock.mock.calls.length
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,21 +3,22 @@ import { FileMetadata } from '../src/fs-helper'
|
||||
|
||||
describe('createActionPackageManigest', () => {
|
||||
it('creates a manifest containing the provided information', () => {
|
||||
let date = new Date()
|
||||
let repo = 'test-repo'
|
||||
let version = '1.0.0'
|
||||
let tarFile: FileMetadata = {
|
||||
const date = new Date()
|
||||
const repo = 'test-org/test-repo'
|
||||
const sanitizedRepo = 'test-org-test-repo'
|
||||
const version = '1.0.0'
|
||||
const tarFile: FileMetadata = {
|
||||
path: '/test/test/test',
|
||||
sha256: '1234567890',
|
||||
size: 100
|
||||
}
|
||||
let zipFile: FileMetadata = {
|
||||
const zipFile: FileMetadata = {
|
||||
path: '/test/test/test',
|
||||
sha256: '1234567890',
|
||||
size: 100
|
||||
}
|
||||
|
||||
let expectedJSON: String = `{
|
||||
const expectedJSON = `{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"artifactType": "application/vnd.oci.image.manifest.v1+json",
|
||||
@@ -43,7 +44,7 @@ describe('createActionPackageManigest', () => {
|
||||
"size":${tarFile.size},
|
||||
"digest":"${tarFile.sha256}",
|
||||
"annotations":{
|
||||
"org.opencontainers.image.title":"${repo}-${version}.tar.gz"
|
||||
"org.opencontainers.image.title":"${sanitizedRepo}_${version}.tar.gz"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -51,7 +52,7 @@ describe('createActionPackageManigest', () => {
|
||||
"size":${tarFile.size},
|
||||
"digest":"${tarFile.sha256}",
|
||||
"annotations":{
|
||||
"org.opencontainers.image.title":"${repo}-${version}.zip"
|
||||
"org.opencontainers.image.title":"${sanitizedRepo}_${version}.zip"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -63,7 +64,7 @@ describe('createActionPackageManigest', () => {
|
||||
}
|
||||
}`
|
||||
|
||||
let manifest = createActionPackageManifest(
|
||||
const manifest = createActionPackageManifest(
|
||||
{
|
||||
path: 'test.tar.gz',
|
||||
size: 100,
|
||||
@@ -74,12 +75,12 @@ describe('createActionPackageManigest', () => {
|
||||
size: 100,
|
||||
sha256: '1234567890'
|
||||
},
|
||||
'test-repo',
|
||||
'1.0.0',
|
||||
repo,
|
||||
version,
|
||||
date
|
||||
)
|
||||
|
||||
let manifestJSON = JSON.stringify(manifest)
|
||||
const manifestJSON = JSON.stringify(manifest)
|
||||
expect(manifestJSON).toEqual(expectedJSON.replace(/\s/g, ''))
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user