mirror of
				https://github.com/actions/cache.git
				synced 2025-10-23 08:56:26 +08:00 
			
		
		
		
	proxy support (#166)
* Replace typed rest client with new http-client * Send Content-Type: application/json and fix up some types * Lint * Consume @actions/http-client:1.0.5 * Consume @actions/http-client:1.0.6 * Dont send headers manually, http-client automatically will
This commit is contained in:
		
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -94,3 +94,6 @@ typings/ | ||||
|  | ||||
| # DynamoDB Local files | ||||
| .dynamodb/ | ||||
|  | ||||
| # Text editor files | ||||
| .vscode/ | ||||
|   | ||||
							
								
								
									
										28
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										28
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -14,6 +14,14 @@ | ||||
|       "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.1.tgz", | ||||
|       "integrity": "sha512-nvFkxwiicvpzNiCBF4wFBDfnBvi7xp/as7LE1hBxBxKG2L29+gkIPBiLKMVORL+Hg3JNf07AKRfl0V5djoypjQ==" | ||||
|     }, | ||||
|     "@actions/http-client": { | ||||
|       "version": "1.0.6", | ||||
|       "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.6.tgz", | ||||
|       "integrity": "sha512-LGmio4w98UyGX33b/W6V6Nx/sQHRXZ859YlMkn36wPsXPB82u8xTVlA/Dq2DXrm6lEq9RVmisRJa1c+HETAIJA==", | ||||
|       "requires": { | ||||
|         "tunnel": "0.0.6" | ||||
|       } | ||||
|     }, | ||||
|     "@actions/io": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.1.tgz", | ||||
| @@ -5933,9 +5941,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "tunnel": { | ||||
|       "version": "0.0.4", | ||||
|       "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.4.tgz", | ||||
|       "integrity": "sha1-LTeFoVjBdMmhbcLARuxfxfF0IhM=" | ||||
|       "version": "0.0.6", | ||||
|       "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", | ||||
|       "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" | ||||
|     }, | ||||
|     "tunnel-agent": { | ||||
|       "version": "0.6.0", | ||||
| @@ -5973,15 +5981,6 @@ | ||||
|       "integrity": "sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "typed-rest-client": { | ||||
|       "version": "1.5.0", | ||||
|       "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.5.0.tgz", | ||||
|       "integrity": "sha512-DVZRlmsfnTjp6ZJaatcdyvvwYwbWvR4YDNFDqb+qdTxpvaVP99YCpBkA8rxsLtAPjBVoDe4fNsnMIdZTiPuKWg==", | ||||
|       "requires": { | ||||
|         "tunnel": "0.0.4", | ||||
|         "underscore": "1.8.3" | ||||
|       } | ||||
|     }, | ||||
|     "typescript": { | ||||
|       "version": "3.7.3", | ||||
|       "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.3.tgz", | ||||
| @@ -5999,11 +5998,6 @@ | ||||
|         "source-map": "~0.6.1" | ||||
|       } | ||||
|     }, | ||||
|     "underscore": { | ||||
|       "version": "1.8.3", | ||||
|       "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", | ||||
|       "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" | ||||
|     }, | ||||
|     "union-value": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", | ||||
|   | ||||
| @@ -26,8 +26,8 @@ | ||||
|   "dependencies": { | ||||
|     "@actions/core": "^1.2.0", | ||||
|     "@actions/exec": "^1.0.1", | ||||
|     "@actions/http-client": "^1.0.6", | ||||
|     "@actions/io": "^1.0.1", | ||||
|     "typed-rest-client": "^1.5.0", | ||||
|     "uuid": "^3.3.3" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|   | ||||
| @@ -1,13 +1,12 @@ | ||||
| import * as core from "@actions/core"; | ||||
| import * as fs from "fs"; | ||||
| import { BearerCredentialHandler } from "typed-rest-client/Handlers"; | ||||
| import { HttpClient, HttpCodes } from "typed-rest-client/HttpClient"; | ||||
| import { IHttpClientResponse } from "typed-rest-client/Interfaces"; | ||||
| import { BearerCredentialHandler } from "@actions/http-client/auth"; | ||||
| import { HttpClient, HttpCodes } from "@actions/http-client"; | ||||
| import { | ||||
|     IHttpClientResponse, | ||||
|     IRequestOptions, | ||||
|     RestClient, | ||||
|     IRestResponse | ||||
| } from "typed-rest-client/RestClient"; | ||||
|     ITypedResponse | ||||
| } from "@actions/http-client/interfaces"; | ||||
| import { | ||||
|     ArtifactCacheEntry, | ||||
|     CommitCacheRequest, | ||||
| @@ -16,11 +15,17 @@ import { | ||||
| } from "./contracts"; | ||||
| import * as utils from "./utils/actionUtils"; | ||||
|  | ||||
| function isSuccessStatusCode(statusCode: number): boolean { | ||||
| function isSuccessStatusCode(statusCode?: number): boolean { | ||||
|     if (!statusCode) { | ||||
|         return false; | ||||
|     } | ||||
|     return statusCode >= 200 && statusCode < 300; | ||||
| } | ||||
|  | ||||
| function isRetryableStatusCode(statusCode: number): boolean { | ||||
| function isRetryableStatusCode(statusCode?: number): boolean { | ||||
|     if (!statusCode) { | ||||
|         return false; | ||||
|     } | ||||
|     const retryableStatusCodes = [ | ||||
|         HttpCodes.BadGateway, | ||||
|         HttpCodes.ServiceUnavailable, | ||||
| @@ -29,7 +34,7 @@ function isRetryableStatusCode(statusCode: number): boolean { | ||||
|     return retryableStatusCodes.includes(statusCode); | ||||
| } | ||||
|  | ||||
| function getCacheApiUrl(): string { | ||||
| function getCacheApiUrl(resource: string): string { | ||||
|     // Ideally we just use ACTIONS_CACHE_URL | ||||
|     const baseUrl: string = ( | ||||
|         process.env["ACTIONS_CACHE_URL"] || | ||||
| @@ -42,8 +47,9 @@ function getCacheApiUrl(): string { | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     core.debug(`Cache Url: ${baseUrl}`); | ||||
|     return `${baseUrl}_apis/artifactcache/`; | ||||
|     const url = `${baseUrl}_apis/artifactcache/${resource}`; | ||||
|     core.debug(`Resource Url: ${url}`); | ||||
|     return url; | ||||
| } | ||||
|  | ||||
| function createAcceptHeader(type: string, apiVersion: string): string { | ||||
| @@ -52,30 +58,33 @@ function createAcceptHeader(type: string, apiVersion: string): string { | ||||
|  | ||||
| function getRequestOptions(): IRequestOptions { | ||||
|     const requestOptions: IRequestOptions = { | ||||
|         acceptHeader: createAcceptHeader("application/json", "6.0-preview.1") | ||||
|         headers: { | ||||
|             Accept: createAcceptHeader("application/json", "6.0-preview.1") | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     return requestOptions; | ||||
| } | ||||
|  | ||||
| function createRestClient(): RestClient { | ||||
| function createHttpClient(): HttpClient { | ||||
|     const token = process.env["ACTIONS_RUNTIME_TOKEN"] || ""; | ||||
|     const bearerCredentialHandler = new BearerCredentialHandler(token); | ||||
|  | ||||
|     return new RestClient("actions/cache", getCacheApiUrl(), [ | ||||
|         bearerCredentialHandler | ||||
|     ]); | ||||
|     return new HttpClient( | ||||
|         "actions/cache", | ||||
|         [bearerCredentialHandler], | ||||
|         getRequestOptions() | ||||
|     ); | ||||
| } | ||||
|  | ||||
| export async function getCacheEntry( | ||||
|     keys: string[] | ||||
| ): Promise<ArtifactCacheEntry | null> { | ||||
|     const restClient = createRestClient(); | ||||
|     const httpClient = createHttpClient(); | ||||
|     const resource = `cache?keys=${encodeURIComponent(keys.join(","))}`; | ||||
|  | ||||
|     const response = await restClient.get<ArtifactCacheEntry>( | ||||
|         resource, | ||||
|         getRequestOptions() | ||||
|     const response = await httpClient.getJson<ArtifactCacheEntry>( | ||||
|         getCacheApiUrl(resource) | ||||
|     ); | ||||
|     if (response.statusCode === 204) { | ||||
|         return null; | ||||
| @@ -83,6 +92,7 @@ export async function getCacheEntry( | ||||
|     if (!isSuccessStatusCode(response.statusCode)) { | ||||
|         throw new Error(`Cache service responded with ${response.statusCode}`); | ||||
|     } | ||||
|  | ||||
|     const cacheResult = response.result; | ||||
|     const cacheDownloadUrl = cacheResult?.archiveLocation; | ||||
|     if (!cacheDownloadUrl) { | ||||
| @@ -118,17 +128,15 @@ export async function downloadCache( | ||||
|  | ||||
| // Reserve Cache | ||||
| export async function reserveCache(key: string): Promise<number> { | ||||
|     const restClient = createRestClient(); | ||||
|     const httpClient = createHttpClient(); | ||||
|  | ||||
|     const reserveCacheRequest: ReserveCacheRequest = { | ||||
|         key | ||||
|     }; | ||||
|     const response = await restClient.create<ReserveCacheResponse>( | ||||
|         "caches", | ||||
|         reserveCacheRequest, | ||||
|         getRequestOptions() | ||||
|     const response = await httpClient.postJson<ReserveCacheResponse>( | ||||
|         getCacheApiUrl("caches"), | ||||
|         reserveCacheRequest | ||||
|     ); | ||||
|  | ||||
|     return response?.result?.cacheId ?? -1; | ||||
| } | ||||
|  | ||||
| @@ -142,7 +150,7 @@ function getContentRange(start: number, end: number): string { | ||||
| } | ||||
|  | ||||
| async function uploadChunk( | ||||
|     restClient: RestClient, | ||||
|     httpClient: HttpClient, | ||||
|     resourceUrl: string, | ||||
|     data: NodeJS.ReadableStream, | ||||
|     start: number, | ||||
| @@ -156,38 +164,37 @@ async function uploadChunk( | ||||
|             end | ||||
|         )}` | ||||
|     ); | ||||
|     const requestOptions = getRequestOptions(); | ||||
|     requestOptions.additionalHeaders = { | ||||
|     const additionalHeaders = { | ||||
|         "Content-Type": "application/octet-stream", | ||||
|         "Content-Range": getContentRange(start, end) | ||||
|     }; | ||||
|  | ||||
|     const uploadChunkRequest = async (): Promise<IRestResponse<void>> => { | ||||
|         return await restClient.uploadStream<void>( | ||||
|     const uploadChunkRequest = async (): Promise<IHttpClientResponse> => { | ||||
|         return await httpClient.sendStream( | ||||
|             "PATCH", | ||||
|             resourceUrl, | ||||
|             data, | ||||
|             requestOptions | ||||
|             additionalHeaders | ||||
|         ); | ||||
|     }; | ||||
|  | ||||
|     const response = await uploadChunkRequest(); | ||||
|     if (isSuccessStatusCode(response.statusCode)) { | ||||
|     if (isSuccessStatusCode(response.message.statusCode)) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (isRetryableStatusCode(response.statusCode)) { | ||||
|     if (isRetryableStatusCode(response.message.statusCode)) { | ||||
|         core.debug( | ||||
|             `Received ${response.statusCode}, retrying chunk at offset ${start}.` | ||||
|             `Received ${response.message.statusCode}, retrying chunk at offset ${start}.` | ||||
|         ); | ||||
|         const retryResponse = await uploadChunkRequest(); | ||||
|         if (isSuccessStatusCode(retryResponse.statusCode)) { | ||||
|         if (isSuccessStatusCode(retryResponse.message.statusCode)) { | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     throw new Error( | ||||
|         `Cache service responded with ${response.statusCode} during chunk upload.` | ||||
|         `Cache service responded with ${response.message.statusCode} during chunk upload.` | ||||
|     ); | ||||
| } | ||||
|  | ||||
| @@ -200,13 +207,13 @@ function parseEnvNumber(key: string): number | undefined { | ||||
| } | ||||
|  | ||||
| async function uploadFile( | ||||
|     restClient: RestClient, | ||||
|     httpClient: HttpClient, | ||||
|     cacheId: number, | ||||
|     archivePath: string | ||||
| ): Promise<void> { | ||||
|     // Upload Chunks | ||||
|     const fileSize = fs.statSync(archivePath).size; | ||||
|     const resourceUrl = getCacheApiUrl() + "caches/" + cacheId.toString(); | ||||
|     const resourceUrl = getCacheApiUrl(`caches/${cacheId.toString()}`); | ||||
|     const fd = fs.openSync(archivePath, "r"); | ||||
|  | ||||
|     const concurrency = parseEnvNumber("CACHE_UPLOAD_CONCURRENCY") ?? 4; // # of HTTP requests in parallel | ||||
| @@ -237,7 +244,7 @@ async function uploadFile( | ||||
|                     }); | ||||
|  | ||||
|                     await uploadChunk( | ||||
|                         restClient, | ||||
|                         httpClient, | ||||
|                         resourceUrl, | ||||
|                         chunk, | ||||
|                         start, | ||||
| @@ -253,16 +260,14 @@ async function uploadFile( | ||||
| } | ||||
|  | ||||
| async function commitCache( | ||||
|     restClient: RestClient, | ||||
|     httpClient: HttpClient, | ||||
|     cacheId: number, | ||||
|     filesize: number | ||||
| ): Promise<IRestResponse<void>> { | ||||
|     const requestOptions = getRequestOptions(); | ||||
| ): Promise<ITypedResponse<null>> { | ||||
|     const commitCacheRequest: CommitCacheRequest = { size: filesize }; | ||||
|     return await restClient.create( | ||||
|         `caches/${cacheId.toString()}`, | ||||
|         commitCacheRequest, | ||||
|         requestOptions | ||||
|     return await httpClient.postJson<null>( | ||||
|         getCacheApiUrl(`caches/${cacheId.toString()}`), | ||||
|         commitCacheRequest | ||||
|     ); | ||||
| } | ||||
|  | ||||
| @@ -270,16 +275,16 @@ export async function saveCache( | ||||
|     cacheId: number, | ||||
|     archivePath: string | ||||
| ): Promise<void> { | ||||
|     const restClient = createRestClient(); | ||||
|     const httpClient = createHttpClient(); | ||||
|  | ||||
|     core.debug("Upload cache"); | ||||
|     await uploadFile(restClient, cacheId, archivePath); | ||||
|     await uploadFile(httpClient, cacheId, archivePath); | ||||
|  | ||||
|     // Commit Cache | ||||
|     core.debug("Commiting cache"); | ||||
|     const cacheSize = utils.getArchiveFileSize(archivePath); | ||||
|     const commitCacheResponse = await commitCache( | ||||
|         restClient, | ||||
|         httpClient, | ||||
|         cacheId, | ||||
|         cacheSize | ||||
|     ); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user