添加cache save缓存go build时的依赖包
All checks were successful
Continuous Integration / GitHub Actions Test (push) Successful in 15s

修改打包命令
This commit is contained in:
soul-walker 2024-03-19 16:17:09 +08:00
parent fb7131260b
commit 40cb2529b5
15 changed files with 325 additions and 22 deletions

View File

@ -36,6 +36,8 @@ extends:
rules:
{
'prefer-const': 'off',
'no-shadow': 'off',
'camelcase': 'off',
'eslint-comments/no-use': 'off',
'eslint-comments/no-unused-disable': 'off',

View File

@ -24,7 +24,11 @@ outputs:
go-version:
description:
'The installed Go version. Useful when given a version range as input.'
cache-hit:
description: 'A boolean value to indicate if a cache was hit'
runs:
using: node20
main: dist/index.js
post: 'dist/cache-save/index.js'
post-if: success()

BIN
dist/cache-save/index.js generated vendored Normal file

Binary file not shown.

BIN
dist/index.js generated vendored

Binary file not shown.

BIN
dist/index.js.map generated vendored

Binary file not shown.

BIN
dist/licenses.txt generated vendored

Binary file not shown.

BIN
dist/sourcemap-register.js generated vendored

Binary file not shown.

38
package-lock.json generated
View File

@ -11,6 +11,7 @@
"dependencies": {
"@actions/cache": "^3.2.4",
"@actions/core": "^1.10.1",
"@actions/glob": "^0.4.0",
"@actions/http-client": "^2.2.1",
"@actions/io": "^1.1.3",
"@actions/tool-cache": "^2.0.1"
@ -63,6 +64,35 @@
"uuid": "^3.3.3"
}
},
"node_modules/@actions/cache/node_modules/@actions/glob": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.1.2.tgz",
"integrity": "sha512-SclLR7Ia5sEqjkJTPs7Sd86maMDw43p769YxBOxvPvEWuPEhpAnBsQfENOpXjFYMmhCqd127bmf+YdvJqVqR4A==",
"dependencies": {
"@actions/core": "^1.2.6",
"minimatch": "^3.0.4"
}
},
"node_modules/@actions/cache/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/@actions/cache/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/@actions/cache/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
@ -98,11 +128,11 @@
}
},
"node_modules/@actions/glob": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.1.2.tgz",
"integrity": "sha512-SclLR7Ia5sEqjkJTPs7Sd86maMDw43p769YxBOxvPvEWuPEhpAnBsQfENOpXjFYMmhCqd127bmf+YdvJqVqR4A==",
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.4.0.tgz",
"integrity": "sha512-+eKIGFhsFa4EBwaf/GMyzCdWrXWymGXfFmZU3FHQvYS8mPcHtTtZONbkcqqUMzw9mJ/pImEBFET1JNifhqGsAQ==",
"dependencies": {
"@actions/core": "^1.2.6",
"@actions/core": "^1.9.1",
"minimatch": "^3.0.4"
}
},

View File

@ -30,7 +30,7 @@
"format:write": "npx prettier --write .",
"format:check": "npx prettier --check .",
"lint": "npx eslint . -c ./.github/linters/.eslintrc.yml",
"package": "npx ncc build src/index.ts -o dist --source-map --license licenses.txt",
"package": "npx ncc build src/index.ts -o dist && npx ncc build -o dist/cache-save src/cache-save.ts",
"package:watch": "npm run package -- --watch",
"test": "npx jest",
"all": "npm run format:write && npm run lint && npm run package",
@ -69,6 +69,7 @@
"dependencies": {
"@actions/cache": "^3.2.4",
"@actions/core": "^1.10.1",
"@actions/glob": "^0.4.0",
"@actions/http-client": "^2.2.1",
"@actions/io": "^1.1.3",
"@actions/tool-cache": "^2.0.1"

65
src/cache-restore.ts Normal file
View File

@ -0,0 +1,65 @@
import * as cache from '@actions/cache'
import * as core from '@actions/core'
import * as glob from '@actions/glob'
import path from 'path'
import fs from 'fs'
import { State, Outputs } from './constants'
import { PackageManagerInfo } from './package-managers'
import { getCacheDirectoryPath, getPackageManagerInfo } from './cache-utils'
export const restoreCache = async (
versionSpec: string,
packageManager: string,
cacheDependencyPath?: string
): Promise<void> => {
const packageManagerInfo = await getPackageManagerInfo(packageManager)
const platform = process.env.RUNNER_OS
const cachePaths = await getCacheDirectoryPath(packageManagerInfo)
const dependencyFilePath = cacheDependencyPath
? cacheDependencyPath
: findDependencyFile(packageManagerInfo)
const fileHash = await glob.hashFiles(dependencyFilePath)
if (!fileHash) {
throw new Error(
'Some specified paths were not resolved, unable to cache dependencies.'
)
}
const linuxVersion =
process.env.RUNNER_OS === 'Linux' ? `${process.env.ImageOS}-` : ''
const primaryKey = `setup-go-${platform}-${linuxVersion}go-${versionSpec}-${fileHash}`
core.info(`primary key is ${primaryKey}`)
core.saveState(State.CachePrimaryKey, primaryKey)
const cacheKey = await cache.restoreCache(cachePaths, primaryKey)
core.setOutput(Outputs.CacheHit, Boolean(cacheKey))
if (!cacheKey) {
core.info(`Cache is not found`)
core.setOutput(Outputs.CacheHit, false)
return
}
core.saveState(State.CacheMatchedKey, cacheKey)
core.info(`Cache restored from key: ${cacheKey}`)
}
const findDependencyFile = (packageManager: PackageManagerInfo): string => {
const dependencyFile = packageManager.dependencyFilePattern
const workspace = process.env.GITHUB_WORKSPACE!
const rootContent = fs.readdirSync(workspace)
const goSumFileExists = rootContent.includes(dependencyFile)
if (!goSumFileExists) {
throw new Error(
`Dependencies file is not found in ${workspace}. Supported file pattern: ${dependencyFile}`
)
}
return path.join(workspace, dependencyFile)
}

93
src/cache-save.ts Normal file
View File

@ -0,0 +1,93 @@
import * as core from '@actions/core'
import * as cache from '@actions/cache'
import fs from 'fs'
import { State } from './constants'
import { getCacheDirectoryPath, getPackageManagerInfo } from './cache-utils'
// Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in
// @actions/toolkit when a failed upload closes the file descriptor causing any in-process reads to
// throw an uncaught exception. Instead of failing this action, just warn.
process.on('uncaughtException', e => {
const warningPrefix = '[warning]'
core.info(`${warningPrefix}${e.message}`)
})
// Added early exit to resolve issue with slow post action step:
// - https://github.com/actions/setup-node/issues/878
// https://github.com/actions/cache/pull/1217
export async function run(earlyExit?: boolean): Promise<void> {
try {
const cacheInput = core.getBooleanInput('cache')
if (cacheInput) {
await cachePackages()
if (earlyExit) {
process.exit(0)
}
}
} catch (error) {
let message = 'Unknown error!'
if (error instanceof Error) {
message = error.message
}
if (typeof error === 'string') {
message = error
}
core.warning(message)
}
}
const cachePackages = async (): Promise<void> => {
const packageManager = 'default'
const state = core.getState(State.CacheMatchedKey)
const primaryKey = core.getState(State.CachePrimaryKey)
const packageManagerInfo = await getPackageManagerInfo(packageManager)
const cachePaths = await getCacheDirectoryPath(packageManagerInfo)
const nonExistingPaths = cachePaths.filter(
cachePath => !fs.existsSync(cachePath)
)
if (nonExistingPaths.length === cachePaths.length) {
core.warning('There are no cache folders on the disk')
return
}
if (nonExistingPaths.length) {
logWarning(
`Cache folder path is retrieved but doesn't exist on disk: ${nonExistingPaths.join(
', '
)}`
)
}
if (!primaryKey) {
core.info(
'Primary key was not generated. Please check the log messages above for more errors or information'
)
return
}
if (primaryKey === state) {
core.info(
`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`
)
return
}
const cacheId = await cache.saveCache(cachePaths, primaryKey)
if (cacheId === -1) {
return
}
core.info(`Cache saved with the key: ${primaryKey}`)
}
function logWarning(message: string): void {
const warningPrefix = '[warning]'
core.info(`${warningPrefix}${message}`)
}
run(true)

92
src/cache-utils.ts Normal file
View File

@ -0,0 +1,92 @@
import * as cache from '@actions/cache'
import * as core from '@actions/core'
import * as exec from '@actions/exec'
import {
supportedPackageManagers,
PackageManagerInfo
} from './package-managers'
export const getCommandOutput = async (
toolCommand: string
): Promise<string> => {
let { stdout, stderr, exitCode } = await exec.getExecOutput(
toolCommand,
undefined,
{ ignoreReturnCode: true }
)
if (exitCode) {
stderr = !stderr.trim()
? `The '${toolCommand}' command failed with exit code: ${exitCode}`
: stderr
throw new Error(stderr)
}
return stdout.trim()
}
export const getPackageManagerInfo = async (
packageManager: string
): Promise<PackageManagerInfo> => {
if (!supportedPackageManagers[packageManager]) {
throw new Error(
`It's not possible to use ${packageManager}, please, check correctness of the package manager name spelling.`
)
}
const obtainedPackageManager = supportedPackageManagers[packageManager]
return obtainedPackageManager
}
export const getCacheDirectoryPath = async (
packageManagerInfo: PackageManagerInfo
): Promise<string[]> => {
const pathOutputs = await Promise.allSettled(
packageManagerInfo.cacheFolderCommandList.map(async command =>
getCommandOutput(command)
)
)
const results = pathOutputs.map(item => {
if (item.status === 'fulfilled') {
return item.value
} else {
core.info(`[warning]getting cache directory path failed: ${item.reason}`)
}
return ''
})
const cachePaths = results.filter(item => item)
if (!cachePaths.length) {
throw new Error(`Could not get cache folder paths.`)
}
return cachePaths
}
export function isGhes(): boolean {
const ghUrl = new URL(
process.env['GITHUB_SERVER_URL'] || 'https://github.com'
)
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'
}
export function isCacheFeatureAvailable(): boolean {
if (cache.isFeatureAvailable()) {
return true
}
if (isGhes()) {
core.warning(
'Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not.'
)
return false
}
core.warning(
'The runner was not able to contact the cache service. Caching will be skipped'
)
return false
}

8
src/constants.ts Normal file
View File

@ -0,0 +1,8 @@
export enum State {
CachePrimaryKey = 'CACHE_KEY',
CacheMatchedKey = 'CACHE_RESULT'
}
export enum Outputs {
CacheHit = 'cache-hit'
}

View File

@ -2,10 +2,10 @@ import * as core from '@actions/core'
import * as io from '@actions/io'
import * as cache from '@actions/cache'
import os from 'os'
import fs from 'fs'
import path from 'path'
import cp from 'child_process'
import { getGoFileName, getGoRootPath, getInstalledGoPath } from './install'
import { restoreCache } from './cache-restore'
/**
* The main function for the action.
@ -41,13 +41,21 @@ export async function run(): Promise<void> {
// 将go的bin目录添加到PATH
const binPath = path.join(installedPath, 'bin')
getFiles(binPath)
core.addPath(binPath)
// 测试输出go的版本
const goPath = await io.which('go')
const goVersion = (cp.execSync(`${goPath} version`) || '').toString()
core.info(`go version cmd result is: ${goVersion}`)
// 尝试从缓存中恢复
const packageManager = 'default'
const cacheDependencyPath = core.getInput('cache-dependency-path')
try {
await restoreCache(version, packageManager, cacheDependencyPath)
} catch (error) {
core.warning(`Restore cache failed: ${(error as Error).message}`)
}
// 设置输出参数
core.setOutput('go-version', goVersion)
} catch (error) {
@ -56,21 +64,6 @@ export async function run(): Promise<void> {
}
}
function getFiles(dir: string): void {
const stat = fs.statSync(dir)
if (stat.isDirectory()) {
// 判断是不是目录
const dirs = fs.readdirSync(dir)
for (const value of dirs) {
// 递归调用,处理子目录
getFiles(path.join(dir, value))
}
} else if (stat.isFile()) {
// 判断是不是文件
core.info(`文件: ${dir}`)
}
}
function resolveVersionInput(): string {
const version = core.getInput('go-version')

15
src/package-managers.ts Normal file
View File

@ -0,0 +1,15 @@
type SupportedPackageManagers = {
[prop: string]: PackageManagerInfo
}
export interface PackageManagerInfo {
dependencyFilePattern: string
cacheFolderCommandList: string[]
}
export const supportedPackageManagers: SupportedPackageManagers = {
default: {
dependencyFilePattern: 'go.sum',
cacheFolderCommandList: ['go env GOMODCACHE', 'go env GOCACHE']
}
}