feat: implement the action in typescript
This commit is contained in:
@@ -1,7 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [ ! -d "$HOME/.kube" ]; then
|
||||
mkdir -p $HOME/.kube
|
||||
fi
|
||||
|
||||
echo "$BASE64_KUBE_CONFIG" | base64 -d > $HOME/.kube/config
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
curl -LO "https://dl.k8s.io/release/$KUBECTL_VERSION/bin/linux/amd64/kubectl"
|
||||
curl -LO "https://dl.k8s.io/$KUBECTL_VERSION/bin/linux/amd64/kubectl.sha256"
|
||||
echo "$(cat kubectl.sha256) kubectl" | sha256sum --check
|
||||
rm -f kubectl.sha256
|
||||
|
||||
mkdir -p $RUNNER_TEMP/bin
|
||||
mv kubectl $RUNNER_TEMP/bin
|
||||
echo "$RUNNER_TEMP/bin" >> $GITHUB_PATH
|
||||
28
src/login.ts
Normal file
28
src/login.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { mkdir, writeFile } from 'node:fs/promises'
|
||||
import { join } from 'node:path'
|
||||
import { env } from 'node:process'
|
||||
|
||||
import { debug, getInput, saveState, setFailed } from '@actions/core'
|
||||
|
||||
export async function setupKubeconfig() {
|
||||
debug('Running kubectl-action setupKubeconfig()')
|
||||
|
||||
if (env.HOME === undefined) {
|
||||
setFailed('$HOME is not defined')
|
||||
return
|
||||
}
|
||||
|
||||
const config = getInput('base64-kube-config', {
|
||||
required: true,
|
||||
trimWhitespace: true
|
||||
})
|
||||
|
||||
const decoded = Buffer.from(config, 'base64')
|
||||
.toString('utf8')
|
||||
|
||||
const path = join(env.HOME, '.kube')
|
||||
saveState('kubeconfig-path', path)
|
||||
|
||||
await mkdir(path, { recursive: true })
|
||||
await writeFile(join(path, 'config'), decoded, 'utf8')
|
||||
}
|
||||
14
src/main.ts
Normal file
14
src/main.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/* eslint-disable unicorn/prefer-top-level-await */
|
||||
import { debug, getState, setFailed } from '@actions/core'
|
||||
import { installKubectl } from 'setup'
|
||||
|
||||
const post = Boolean(getState('isPost'))
|
||||
|
||||
if (!post) {
|
||||
debug('Running kubectl-action setup')
|
||||
installKubectl()
|
||||
.catch(error => {
|
||||
setFailed('Failed to install kubectl (this is a bug in kubectl-action): ')
|
||||
debug(JSON.stringify(error))
|
||||
})
|
||||
}
|
||||
126
src/setup.ts
Normal file
126
src/setup.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
import { createHash, randomUUID } from 'node:crypto'
|
||||
import { createWriteStream } from 'node:fs'
|
||||
import { mkdir } from 'node:fs/promises'
|
||||
import { join } from 'node:path'
|
||||
import { env, stdout } from 'node:process'
|
||||
import { Readable } from 'node:stream'
|
||||
|
||||
import { addPath, debug, getInput, saveState, setFailed, warning } from '@actions/core'
|
||||
import { fetch } from 'undici'
|
||||
|
||||
export async function installKubectl() {
|
||||
debug('Running kubectl-action installKubectl()')
|
||||
|
||||
if (env.RUNNER_TEMP === undefined) {
|
||||
setFailed('$RUNNER_TEMP is not defined')
|
||||
return
|
||||
}
|
||||
|
||||
const input = getInput('kubectl-version', {
|
||||
required: false,
|
||||
trimWhitespace: true
|
||||
})
|
||||
|
||||
const version = input || await fetchLatestVersion()
|
||||
|
||||
if (!version?.startsWith('v')) {
|
||||
setFailed('Unable to determine the `kubectl` version to install')
|
||||
return
|
||||
}
|
||||
|
||||
console.log(`Installing kubectl version ${version}`)
|
||||
|
||||
const kubectl = await downloadKubectl(version)
|
||||
|
||||
if (!kubectl) {
|
||||
return
|
||||
}
|
||||
|
||||
const path = join(env.RUNNER_TEMP, randomUUID())
|
||||
await mkdir(path, { recursive: true })
|
||||
saveState('kubectl-path', path)
|
||||
|
||||
const stream = createWriteStream(join(path, 'kubectl'))
|
||||
|
||||
kubectl.pipe(stream)
|
||||
|
||||
console.log(`Installing kubectl to ${path}`)
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
stream.on('finish', resolve)
|
||||
stream.on('error', reject)
|
||||
})
|
||||
|
||||
addPath(path)
|
||||
}
|
||||
|
||||
// Fetches the latest kubectl version from the Kubernetes release server
|
||||
async function fetchLatestVersion() {
|
||||
const response = await fetch('https://dl.k8s.io/release/stable.txt')
|
||||
if (!response.ok) {
|
||||
setFailed(`Failed to fetch latest kubectl version with status ${response.status}`)
|
||||
return
|
||||
}
|
||||
|
||||
const version = await response.text()
|
||||
return version.trim()
|
||||
}
|
||||
|
||||
// Downloads the kubectl binary from the Kubernetes release server
|
||||
// Also runs a checksum verification on the downloaded binary
|
||||
async function downloadKubectl(version: string) {
|
||||
const url = `https://dl.k8s.io/release/${version}/bin/linux/amd64/kubectl`
|
||||
const hashUrl = `${url}.sha256`
|
||||
|
||||
console.log(`Downloading kubectl (${url})`)
|
||||
|
||||
const hashResponse = await fetch(hashUrl)
|
||||
if (!hashResponse.ok) {
|
||||
warning(`Skipping checksum verification for kubectl ${version}`)
|
||||
}
|
||||
|
||||
const hash = hashResponse.ok ? await hashResponse.text() : ''
|
||||
|
||||
const response = await fetch(url)
|
||||
if (!response.ok || !response.body) {
|
||||
setFailed(`Failed to download kubectl with status ${response.status}`)
|
||||
return
|
||||
}
|
||||
|
||||
const hashStream = createHash('sha256')
|
||||
const body = Readable.fromWeb(response.body)
|
||||
const size = Number(response.headers.get('content-length'))
|
||||
|
||||
return new Promise<Readable | void>((resolve, reject) => {
|
||||
let downloaded = 0
|
||||
let progressed = 0
|
||||
|
||||
body.on('data', (chunk: Buffer) => {
|
||||
hashStream.update(chunk)
|
||||
downloaded += chunk.length
|
||||
|
||||
if (Math.floor((downloaded / size) * 80) > progressed) {
|
||||
stdout.clearLine(0)
|
||||
stdout.cursorTo(0)
|
||||
|
||||
progressed++
|
||||
stdout.write(`[${'='.repeat(progressed)}>${' '.repeat(80 - progressed)}]`)
|
||||
}
|
||||
})
|
||||
|
||||
body.on('end', () => {
|
||||
stdout.clearLine(0)
|
||||
stdout.cursorTo(0)
|
||||
console.log(`[${'='.repeat(80)}]`)
|
||||
|
||||
const hashSum = hashStream.digest('hex')
|
||||
if (hashResponse.ok && hashSum !== hash) {
|
||||
setFailed(`Checksum verification failed for kubectl ${version}`)
|
||||
resolve()
|
||||
}
|
||||
|
||||
resolve(body)
|
||||
})
|
||||
|
||||
body.on('error', reject)
|
||||
})
|
||||
}
|
||||
14
src/teardown.ts
Normal file
14
src/teardown.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { rm } from 'node:fs/promises'
|
||||
|
||||
import { debug, getState } from '@actions/core'
|
||||
|
||||
export async function teardown() {
|
||||
debug('Running kubectl-action teardown()')
|
||||
console.log('Removing kubectl and kubeconfig')
|
||||
|
||||
const path = getState('kubectl-path')
|
||||
await rm(path, { recursive: true, force: true })
|
||||
|
||||
const configPath = getState('kubeconfig-path')
|
||||
await rm(configPath, { recursive: true, force: true })
|
||||
}
|
||||
Reference in New Issue
Block a user