@rharkor/caching-for-turbo 2.0.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/script/release DELETED
@@ -1,59 +0,0 @@
1
- #!/bin/bash
2
-
3
- # About:
4
- #
5
- # This is a helper script to tag and push a new release. GitHub Actions use
6
- # release tags to allow users to select a specific version of the action to use.
7
- #
8
- # See: https://github.com/actions/typescript-action#publishing-a-new-release
9
- #
10
- # This script will do the following:
11
- #
12
- # 1. Get the latest release tag
13
- # 2. Prompt the user for a new release tag
14
- # 3. Tag the new release
15
- # 4. Push the new tag to the remote
16
- #
17
- # Usage:
18
- #
19
- # script/release
20
-
21
- # Terminal colors
22
- OFF='\033[0m'
23
- RED='\033[0;31m'
24
- GREEN='\033[0;32m'
25
- BLUE='\033[0;34m'
26
-
27
- # Get the latest release tag
28
- latest_tag=$(git describe --tags "$(git rev-list --tags --max-count=1)")
29
-
30
- if [[ -z "$latest_tag" ]]; then
31
- # There are no existing release tags
32
- echo -e "No tags found (yet) - Continue to create and push your first tag"
33
- latest_tag="[unknown]"
34
- fi
35
-
36
- # Display the latest release tag
37
- echo -e "The latest release tag is: ${BLUE}${latest_tag}${OFF}"
38
-
39
- # Prompt the user for the new release tag
40
- read -r -p 'Enter a new release tag (vX.X.X format): ' new_tag
41
-
42
- # Validate the new release tag
43
- tag_regex='v[0-9]+\.[0-9]+\.[0-9]+$'
44
- if echo "$new_tag" | grep -q -E "$tag_regex"; then
45
- echo -e "Tag: ${BLUE}$new_tag${OFF} is valid"
46
- else
47
- # Release tag is not `vX.X.X` format
48
- echo -e "Tag: ${BLUE}$new_tag${OFF} is ${RED}not valid${OFF} (must be in vX.X.X format)"
49
- exit 1
50
- fi
51
-
52
- # Tag the new release
53
- git tag -a "$new_tag" -m "$new_tag Release"
54
- echo -e "${GREEN}Tagged: $new_tag${OFF}"
55
-
56
- # Push the new tag to the remote
57
- git push --tags
58
- echo -e "${GREEN}Release tag pushed to remote${OFF}"
59
- echo -e "${GREEN}Done!${OFF}"
package/src/cli.ts DELETED
@@ -1,94 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { config } from 'dotenv'
4
- import { server } from './lib/server'
5
- import { killServer, launchServer } from './lib/server/utils'
6
- import { logger } from '@rharkor/logger'
7
- import { ping } from './lib/ping'
8
- import { serverLogFile } from './lib/constants'
9
- import { writeFile } from 'fs/promises'
10
- import { version } from '../package.json'
11
-
12
- // Load environment variables from .env file if it exists
13
- config()
14
-
15
- const main = async (): Promise<void> => {
16
- await logger.init()
17
-
18
- const args = process.argv.slice(2)
19
-
20
- if (args.includes('--help') || args.includes('-h')) {
21
- console.log(`
22
- Turborepo GitHub Actions Cache Server
23
-
24
- Usage:
25
- turbogha [options]
26
-
27
- Options:
28
- --server Run the server in foreground mode
29
- --help, -h Show this help message
30
- --version, -v Show version
31
- --ping Ping the server
32
- --kill Kill the server
33
-
34
- Environment Variables:
35
- The following environment variables are supported for S3 configuration:
36
- - S3_ACCESS_KEY_ID: AWS S3 access key ID
37
- - S3_SECRET_ACCESS_KEY: AWS S3 secret access key
38
- - S3_BUCKET: AWS S3 bucket name
39
- - S3_REGION: AWS S3 region
40
- - S3_ENDPOINT: S3 endpoint (default: https://s3.amazonaws.com)
41
- - S3_PREFIX: Prefix for S3 objects (default: turbogha/)
42
- - PROVIDER: Cache provider (github or s3)
43
-
44
- Examples:
45
- # Run server in background and export environment variables
46
- turbogha
47
-
48
- # Run server in foreground mode
49
- turbogha --server
50
-
51
- # Ping the server
52
- turbogha --ping
53
-
54
- # With S3 configuration
55
- S3_ACCESS_KEY_ID=your-key S3_SECRET_ACCESS_KEY=your-secret S3_BUCKET=your-bucket S3_REGION=us-east-1 turbogha
56
- `)
57
- process.exit(0)
58
- }
59
-
60
- if (args.includes('--version') || args.includes('-v')) {
61
- console.log(version)
62
- process.exit(0)
63
- }
64
-
65
- try {
66
- if (args.includes('--ping')) {
67
- await ping()
68
- } else if (args.includes('--kill')) {
69
- await killServer()
70
- } else if (args.includes('--server')) {
71
- // Empty log file
72
- await writeFile(serverLogFile, '', { flag: 'w' })
73
- // Run server in foreground mode
74
- console.log('Starting Turborepo cache server in foreground mode...')
75
- await server()
76
- } else {
77
- // Run server in background mode and export environment variables
78
- console.log('Starting Turborepo cache server...')
79
- // Empty log file
80
- await writeFile(serverLogFile, '', { flag: 'w' })
81
- await launchServer()
82
- console.log(
83
- '\nServer is running! You can now use Turbo with remote caching.'
84
- )
85
- console.log('\nTo stop the server, run:')
86
- console.log('curl -X DELETE http://localhost:41230/shutdown')
87
- }
88
- } catch (error) {
89
- console.error('Error:', error)
90
- process.exit(1)
91
- }
92
- }
93
-
94
- main()
@@ -1,14 +0,0 @@
1
- import { config } from 'dotenv'
2
- config()
3
-
4
- import { cleanup } from 'src/lib/server/cleanup'
5
-
6
- const main = async () => {
7
- await cleanup({
8
- log: {
9
- info: console.log
10
- }
11
- })
12
- }
13
-
14
- main()
package/src/dev-run.ts DELETED
@@ -1,16 +0,0 @@
1
- // Run the server in foreground and kill it after the test
2
-
3
- import { config } from 'dotenv'
4
- config()
5
-
6
- import { server } from './lib/server'
7
- import { launchServer } from './lib/server/utils'
8
-
9
- const main = async (): Promise<void> => {
10
- //* Run server
11
- server()
12
- //* Run launch server
13
- await launchServer(true)
14
- }
15
-
16
- main()
package/src/index.ts DELETED
@@ -1,6 +0,0 @@
1
- /**
2
- * The entrypoint for the action.
3
- */
4
- import { run } from './main'
5
-
6
- run()
@@ -1,29 +0,0 @@
1
- import { join } from 'path'
2
- import { env } from './env'
3
- import { core } from './core'
4
-
5
- // Helper function to get input value, prioritizing environment variables for local development
6
- const getInput = (name: string, envName?: string): string | undefined => {
7
- // In GitHub Actions context, try core.getInput first
8
- if (process.env.CI === 'true') {
9
- const coreInput = core.getInput(name)
10
- if (coreInput) return coreInput
11
- }
12
-
13
- // Fall back to environment variable
14
- const envVar = envName || name.toUpperCase().replace(/-/g, '_')
15
- return process.env[envVar]
16
- }
17
-
18
- export const serverPort = 41230
19
- export const cachePath = 'turbogha_'
20
- export const cachePrefix = getInput('cache-prefix', 'CACHE_PREFIX') || cachePath
21
- export const getCacheKey = (hash: string, tag?: string): string =>
22
- `${cachePrefix}${hash}${tag ? `#${tag}` : ''}`
23
- export const serverLogFile = env.RUNNER_TEMP
24
- ? join(env.RUNNER_TEMP, 'turbogha.log')
25
- : '/tmp/turbogha.log'
26
- export const getFsCachePath = (hash: string): string =>
27
- join(env.RUNNER_TEMP || '/tmp', `${hash}.tg.bin`)
28
- export const getTempCachePath = (key: string): string =>
29
- join(env.RUNNER_TEMP || '/tmp', `cache-${key}.tg.bin`)
package/src/lib/core.ts DELETED
@@ -1,62 +0,0 @@
1
- import * as coreLib from '@actions/core'
2
- import { logger as loggerLib } from '@rharkor/logger'
3
-
4
- const isCI = process.env.CI === 'true'
5
-
6
- export const core = {
7
- isCI,
8
- setFailed: (message: string) => {
9
- if (isCI) {
10
- coreLib.setFailed(message)
11
- } else {
12
- loggerLib.error(message)
13
- }
14
- },
15
- getInput: (name: string) => {
16
- if (isCI) {
17
- return coreLib.getInput(name)
18
- }
19
- return undefined
20
- },
21
- exportVariable: (name: string, value: string) => {
22
- if (isCI) {
23
- coreLib.exportVariable(name, value)
24
- }
25
- },
26
- //* Logger
27
- info: (message: string) => {
28
- if (isCI) {
29
- coreLib.info(message)
30
- } else {
31
- loggerLib.info(message)
32
- }
33
- },
34
- error: (message: string) => {
35
- if (isCI) {
36
- coreLib.error(message)
37
- } else {
38
- loggerLib.error(message)
39
- }
40
- },
41
- debug: (message: string) => {
42
- if (isCI) {
43
- coreLib.debug(message)
44
- } else {
45
- loggerLib.debug(message)
46
- }
47
- },
48
- log: (message: string) => {
49
- if (isCI) {
50
- coreLib.info(message)
51
- } else {
52
- loggerLib.log(message)
53
- }
54
- },
55
- success: (message: string) => {
56
- if (isCI) {
57
- coreLib.info(message)
58
- } else {
59
- loggerLib.success(message)
60
- }
61
- }
62
- }
@@ -1,22 +0,0 @@
1
- const envObject = {
2
- ACTIONS_RUNTIME_TOKEN: process.env.ACTIONS_RUNTIME_TOKEN,
3
- ACTIONS_CACHE_URL: process.env.ACTIONS_CACHE_URL,
4
- RUNNER_TEMP: process.env.RUNNER_TEMP
5
- }
6
-
7
- type TInvalidEnv = {
8
- valid: false
9
- } & typeof envObject
10
-
11
- type TValidEnv = {
12
- valid: true
13
- } & {
14
- [K in keyof typeof envObject]: NonNullable<(typeof envObject)[K]>
15
- }
16
-
17
- type TEnv = TInvalidEnv | TValidEnv
18
-
19
- export const env = {
20
- valid: Object.values(envObject).every(value => value !== undefined),
21
- ...envObject
22
- } as TEnv
package/src/lib/ping.ts DELETED
@@ -1,7 +0,0 @@
1
- import { core } from './core'
2
- import { waitForServer } from './server/utils'
3
-
4
- export const ping = async () => {
5
- await waitForServer()
6
- core.success('Server is up and running')
7
- }
@@ -1,87 +0,0 @@
1
- import { Readable } from 'node:stream'
2
- import { env } from '../../env'
3
- import { pipeline } from 'node:stream/promises'
4
- import {
5
- createReadStream,
6
- createWriteStream,
7
- existsSync,
8
- statSync
9
- } from 'node:fs'
10
- import { getCacheKey, getFsCachePath, getTempCachePath } from '../../constants'
11
- import { RequestContext } from '../../server'
12
- import { TListFile } from '../../server/cleanup'
13
- import { getCacheClient } from './utils'
14
- import { TProvider } from '../../providers'
15
- import { core } from 'src/lib/core'
16
-
17
- //* Cache API
18
- export async function saveCache(
19
- ctx: RequestContext,
20
- hash: string,
21
- tag: string,
22
- stream: Readable
23
- ): Promise<void> {
24
- if (!env.valid) {
25
- ctx.log.info(
26
- `Using filesystem cache because cache API env vars are not set`
27
- )
28
- await pipeline(stream, createWriteStream(getFsCachePath(hash)))
29
- return
30
- }
31
- const client = getCacheClient()
32
- const key = getCacheKey(hash, tag)
33
- await client.save(key, stream)
34
- ctx.log.info(`Saved cache ${key} for ${hash}`)
35
- }
36
-
37
- export async function getCache(
38
- ctx: RequestContext,
39
- hash: string
40
- ): Promise<
41
- [number | undefined, Readable | ReadableStream, string | undefined] | null
42
- > {
43
- //* Get cache from filesystem if cache API env vars are not set
44
- if (!env.valid) {
45
- const path = getFsCachePath(hash)
46
- if (!existsSync(path)) return null
47
- const size = statSync(path).size
48
- return [size, createReadStream(path), undefined]
49
- }
50
- //* Get cache from cache API
51
- const client = getCacheClient()
52
- const cacheKey = getCacheKey(hash)
53
- const fileRestorationPath = getTempCachePath(cacheKey)
54
- const foundKey = await client.restore(fileRestorationPath, cacheKey)
55
- ctx.log.info(`Cache lookup for ${cacheKey}`)
56
- if (!foundKey) {
57
- ctx.log.info(`Cache lookup did not return data`)
58
- return null
59
- }
60
- const [foundCacheKey, artifactTag] = String(foundKey).split('#')
61
- if (foundCacheKey !== cacheKey) {
62
- ctx.log.info(`Cache key mismatch: ${foundCacheKey} !== ${cacheKey}`)
63
- return null
64
- }
65
- const size = statSync(fileRestorationPath).size
66
- const readableStream = createReadStream(fileRestorationPath)
67
- return [size, readableStream, artifactTag]
68
- }
69
-
70
- export async function deleteCache(): Promise<void> {
71
- core.error(`Cannot delete github cache automatically.`)
72
- throw new Error(`Cannot delete github cache automatically.`)
73
- }
74
-
75
- export async function listCache(): Promise<TListFile[]> {
76
- core.error(`Cannot list github cache automatically.`)
77
- throw new Error(`Cannot list github cache automatically.`)
78
- }
79
-
80
- export const getGithubProvider = (): TProvider => {
81
- return {
82
- save: saveCache,
83
- get: getCache,
84
- delete: deleteCache,
85
- list: listCache
86
- }
87
- }
@@ -1,70 +0,0 @@
1
- import { Readable } from 'node:stream'
2
- import { env } from '../../env'
3
- import streamToPromise from 'stream-to-promise'
4
- import { createWriteStream } from 'node:fs'
5
- import { unlink } from 'node:fs/promises'
6
- import { getTempCachePath } from '../../constants'
7
- import { restoreCache, saveCache } from '@actions/cache'
8
- import { core } from 'src/lib/core'
9
- class HandledError extends Error {
10
- status: number
11
- statusText: string
12
- data: unknown
13
- constructor(status: number, statusText: string, data: unknown) {
14
- super(`${status}: ${statusText}`)
15
- this.status = status
16
- this.statusText = statusText
17
- this.data = data
18
- }
19
- }
20
-
21
- function handleFetchError(message: string) {
22
- return (error: unknown) => {
23
- if (error instanceof HandledError) {
24
- core.error(`${message}: ${error.status} ${error.statusText}`)
25
- core.error(JSON.stringify(error.data))
26
- throw error
27
- }
28
- core.error(`${message}: ${error}`)
29
- throw error
30
- }
31
- }
32
-
33
- export function getCacheClient() {
34
- if (!env.valid) {
35
- throw new Error('Cache API env vars are not set')
36
- }
37
-
38
- const save = async (key: string, stream: Readable): Promise<void> => {
39
- try {
40
- //* Create a temporary file to store the cache
41
- const tempFile = getTempCachePath(key)
42
- const writeStream = createWriteStream(tempFile)
43
- await streamToPromise(stream.pipe(writeStream))
44
- core.info(`Saved cache to ${tempFile}`)
45
-
46
- core.info(`Saving cache for key: ${key}, path: ${tempFile}`)
47
- await saveCache([tempFile], key)
48
- core.info(`Saved cache ${key}`)
49
-
50
- //* Remove the temporary file
51
- await unlink(tempFile)
52
- } catch (error) {
53
- handleFetchError('Unable to upload cache')(error)
54
- }
55
- }
56
-
57
- const restore = async (
58
- path: string,
59
- key: string
60
- ): Promise<string | undefined> => {
61
- core.info(`Querying cache for key: ${key}, path: ${path}`)
62
-
63
- return restoreCache([path], key, [])
64
- }
65
-
66
- return {
67
- save,
68
- restore
69
- }
70
- }