fix low hanging fruit linter errors

This commit is contained in:
Conor Sloan
2023-11-22 12:41:53 +00:00
parent 4c296f98cf
commit ffca16d01f
8 changed files with 64 additions and 85 deletions
+21 -26
View File
@@ -1,5 +1,5 @@
import { publishOCIArtifact } from '../src/ghcr-client'
import axios, { AxiosRequestConfig } from 'axios'
import axios from 'axios'
import * as fsHelper from '../src/fs-helper'
import * as ociContainer from '../src/oci-container'
@@ -88,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) => {
@@ -109,7 +107,7 @@ describe('publishOCIArtifact', () => {
})
// Simulate successful reading of all the files
fsReadFileSyncMock.mockImplementation(async _ => {
fsReadFileSyncMock.mockImplementation(() => {
return Buffer.from('test')
})
@@ -160,7 +158,7 @@ describe('publishOCIArtifact', () => {
})
// Simulate successful reading of all the files
fsReadFileSyncMock.mockImplementation(async _ => {
fsReadFileSyncMock.mockImplementation(() => {
return Buffer.from('test')
})
@@ -198,7 +196,7 @@ describe('publishOCIArtifact', () => {
}
})
expect(
await expect(
publishOCIArtifact(
token,
registry,
@@ -229,7 +227,7 @@ describe('publishOCIArtifact', () => {
}
})
expect(
await expect(
publishOCIArtifact(
token,
registry,
@@ -261,7 +259,7 @@ describe('publishOCIArtifact', () => {
}
})
expect(
await expect(
publishOCIArtifact(
token,
registry,
@@ -296,7 +294,7 @@ describe('publishOCIArtifact', () => {
})
// Simulate successful reading of all the files
fsReadFileSyncMock.mockImplementation(async path => {
fsReadFileSyncMock.mockImplementation(() => {
return Buffer.from('test')
})
@@ -308,7 +306,7 @@ describe('publishOCIArtifact', () => {
}
})
expect(
await expect(
publishOCIArtifact(
token,
registry,
@@ -343,7 +341,7 @@ describe('publishOCIArtifact', () => {
})
// Simulate successful reading of all the files
fsReadFileSyncMock.mockImplementation(async path => {
fsReadFileSyncMock.mockImplementation(() => {
return Buffer.from('test')
})
@@ -362,7 +360,7 @@ describe('publishOCIArtifact', () => {
}
})
expect(
await expect(
publishOCIArtifact(
token,
registry,
@@ -397,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')
})
@@ -409,7 +407,7 @@ describe('publishOCIArtifact', () => {
}
})
expect(
await expect(
publishOCIArtifact(
token,
registry,
@@ -424,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,
@@ -444,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()
-8
View File
@@ -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
@@ -38,8 +32,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()
+8 -8
View File
@@ -3,21 +3,21 @@ 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-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",
@@ -63,7 +63,7 @@ describe('createActionPackageManigest', () => {
}
}`
let manifest = createActionPackageManifest(
const manifest = createActionPackageManifest(
{
path: 'test.tar.gz',
size: 100,
@@ -79,7 +79,7 @@ describe('createActionPackageManigest', () => {
date
)
let manifestJSON = JSON.stringify(manifest)
const manifestJSON = JSON.stringify(manifest)
expect(manifestJSON).toEqual(expectedJSON.replace(/\s/g, ''))
})
})
+1 -1
View File
@@ -69,7 +69,6 @@
"@actions/core": "^1.10.1",
"@actions/exec": "^1.1.1",
"@actions/github": "^6.0.0",
"@types/axios": "^0.14.0",
"archiver": "^6.0.1",
"axios": "^1.6.2",
"axios-debug-log": "^1.0.0",
@@ -77,6 +76,7 @@
},
"devDependencies": {
"@types/archiver": "^6.0.1",
"@types/axios": "^0.14.0",
"@types/jest": "^29.5.8",
"@types/node": "^20.9.1",
"@types/tar": "^6.1.9",
+13 -16
View File
@@ -1,14 +1,11 @@
import * as core from '@actions/core'
import * as exec from '@actions/exec'
import * as fs from 'fs'
import * as path from 'path'
import * as tar from 'tar'
import * as archiver from 'archiver'
import * as crypto from 'crypto'
import * as os from 'os'
import * as zlib from 'zlib'
export function createTempDir() {
export function createTempDir(): string {
const randomDirName = crypto.randomBytes(4).toString('hex')
const tempDir = path.join(os.tmpdir(), randomDirName)
@@ -19,7 +16,7 @@ export function createTempDir() {
return tempDir
}
export function removeDir(dir: string) {
export function removeDir(dir: string): void {
fs.rmSync(dir, { recursive: true })
}
@@ -60,7 +57,7 @@ export async function createArchives(
archive.finalize()
}),
new Promise<FileMetadata>((resolve, reject) => {
const tarStream = tar
tar
.c(
{
file: tarPath,
@@ -77,20 +74,20 @@ export async function createArchives(
]).then(([zipFile, tarFile]) => ({ zipFile, tarFile }))
}
export function isDirectory(path: string): boolean {
return fs.existsSync(path) && fs.lstatSync(path).isDirectory()
export function isDirectory(dirPath: string): boolean {
return fs.existsSync(dirPath) && fs.lstatSync(dirPath).isDirectory()
}
export function readFileContents(path: string): Buffer {
return fs.readFileSync(path)
export function readFileContents(filePath: string): Buffer {
return fs.readFileSync(filePath)
}
// Converts a file path to a filemetadata object by querying the fs for relevant metadata.
async function fileMetadata(path: string): Promise<FileMetadata> {
const stats = fs.statSync(path)
async function fileMetadata(filePath: string): Promise<FileMetadata> {
const stats = fs.statSync(filePath)
const size = stats.size
const hash = crypto.createHash('sha256')
const fileStream = fs.createReadStream(path)
const fileStream = fs.createReadStream(filePath)
return new Promise((resolve, reject) => {
fileStream.on('data', data => {
hash.update(data)
@@ -98,9 +95,9 @@ async function fileMetadata(path: string): Promise<FileMetadata> {
fileStream.on('end', () => {
const sha256 = hash.digest('hex')
resolve({
path: path,
size: size,
sha256: 'sha256:' + sha256
path: filePath,
size,
sha256: `sha256:${sha256}`
})
})
fileStream.on('error', err => {
+16 -19
View File
@@ -2,9 +2,6 @@ import * as core from '@actions/core'
import { FileMetadata } from './fs-helper'
import * as ociContainer from './oci-container'
import axios from 'axios'
import { fieldEnds } from 'tar'
import * as fs from 'fs'
import { promiseHooks } from 'v8'
import * as fsHelper from './fs-helper'
import axiosDebugLog from 'axios-debug-log'
@@ -18,7 +15,7 @@ export async function publishOCIArtifact(
zipFile: FileMetadata,
tarFile: FileMetadata,
manifest: ociContainer.Manifest,
debugRequests: boolean = false
debugRequests = false
): Promise<URL> {
if (debugRequests) {
configureRequestDebugLogging()
@@ -43,7 +40,7 @@ export async function publishOCIArtifact(
`Creating GHCR package for release with semver:${semver} with path:"${zipFile.path}" and "${tarFile.path}".`
)
let layerUploads: Promise<void>[] = manifest.layers.map(layer => {
const layerUploads: Promise<void>[] = manifest.layers.map(async layer => {
switch (layer.mediaType) {
case 'application/vnd.github.actions.package.layer.v1.tar+gzip':
return uploadLayer(
@@ -98,7 +95,7 @@ async function uploadLayer(
headers: {
Authorization: `Bearer ${b64Token}`
},
validateStatus: function (status: number) {
validateStatus: () => {
return true // Allow non 2xx responses
}
}
@@ -124,12 +121,12 @@ async function uploadLayer(
headers: {
Authorization: `Bearer ${b64Token}`
},
validateStatus: function (status: number) {
validateStatus: () => {
return true // Allow non 2xx responses
}
})
if (initiateUploadResponse.status != 202) {
if (initiateUploadResponse.status !== 202) {
core.error(
`Unexpected response from upload post ${uploadBlobEndpoint}: ${initiateUploadResponse.status}`
)
@@ -139,17 +136,17 @@ async function uploadLayer(
}
const locationResponseHeader = initiateUploadResponse.headers['location']
if (locationResponseHeader == undefined) {
if (locationResponseHeader === undefined) {
throw new Error(
`No location header in response from upload post ${uploadBlobEndpoint} for layer ${layer.digest}`
)
}
let pathname = (locationResponseHeader as string) + '?digest=' + layer.digest
const pathname = `${locationResponseHeader}?digest=${layer.digest}`
const uploadBlobUrl = new URL(pathname, registryURL).toString()
// TODO: must we handle the empty config layer? Maybe we can just skip calling this at all
var data: Buffer
let data: Buffer
if (file.size === 0) {
data = Buffer.alloc(0)
} else {
@@ -163,12 +160,12 @@ async function uploadLayer(
'Accept-Encoding': 'gzip', // TODO: What about for the config layer?
'Content-Length': layer.size.toString()
},
validateStatus: function (status: number) {
validateStatus: () => {
return true // Allow non 2xx responses
}
})
if (putResponse.status != 201) {
if (putResponse.status !== 201) {
throw new Error(
`Unexpected response from PUT upload ${putResponse.status} for layer ${layer.digest}`
)
@@ -187,27 +184,27 @@ async function uploadManifest(
Authorization: `Bearer ${b64Token}`,
'Content-Type': 'application/vnd.oci.image.manifest.v1+json'
},
validateStatus: function (status: number) {
validateStatus: () => {
return true // Allow non 2xx responses
}
})
if (putResponse.status != 201) {
if (putResponse.status !== 201) {
throw new Error(
`Unexpected response from PUT manifest ${putResponse.status}`
)
}
}
function configureRequestDebugLogging() {
function configureRequestDebugLogging(): void {
axiosDebugLog({
request: function (debug, config) {
request: (debug, config) => {
core.debug(`Request with ${config}`)
},
response: function (debug, response) {
response: (debug, response) => {
core.debug(`Response with ${response}`)
},
error: function (debug, error) {
error: (debug, error) => {
core.debug(`Error with ${error}`)
}
})
+3 -4
View File
@@ -4,14 +4,13 @@ import * as fsHelper from './fs-helper'
import * as ociContainer from './oci-container'
import * as ghcr from './ghcr-client'
import semver from 'semver'
import { url } from 'inspector'
/**
* The main function for the action.
* @returns {Promise<void>} Resolves when the action is complete.
*/
export async function run(): Promise<void> {
let tmpDir: string = ''
let tmpDir = ''
try {
// Parse and validate Actions execution context, including the repository name, release name and event type
@@ -29,7 +28,7 @@ export async function run(): Promise<void> {
// 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
let targetVersion = semver.parse(releaseTag.replace(/^v/, ''))
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.`
@@ -62,7 +61,7 @@ export async function run(): Promise<void> {
new Date()
)
let packageURL = await ghcr.publishOCIArtifact(
const packageURL = await ghcr.publishOCIArtifact(
token,
registryURL,
repository,
+2 -3
View File
@@ -1,4 +1,3 @@
import { Tracing } from 'trace_events'
import { FileMetadata } from './fs-helper'
export interface Manifest {
@@ -7,14 +6,14 @@ export interface Manifest {
artifactType: string
config: Layer
layers: Layer[]
annotations: {}
annotations: { [key: string]: string }
}
export interface Layer {
mediaType: string
size: number
digest: string
annotations: {}
annotations: { [key: string]: string }
}
// Given a name and archive metadata, creates a manifest in the format expected by GHCR for an Actions Package.