mirror of
				https://github.com/actions/cache.git
				synced 2025-10-31 11:48:38 +08:00 
			
		
		
		
	Provide better errors for unsupported event types (#68)
* Validate event type during restore * PR Feedback * Format * Linting
This commit is contained in:
		
							parent
							
								
									50a2fdee6f
								
							
						
					
					
						commit
						b7d83b4095
					
				|  | @ -3,7 +3,7 @@ import * as exec from "@actions/exec"; | ||||||
| import * as io from "@actions/io"; | import * as io from "@actions/io"; | ||||||
| import * as path from "path"; | import * as path from "path"; | ||||||
| import * as cacheHttpClient from "../src/cacheHttpClient"; | import * as cacheHttpClient from "../src/cacheHttpClient"; | ||||||
| import { Inputs } from "../src/constants"; | import { Events, Inputs } from "../src/constants"; | ||||||
| import { ArtifactCacheEntry } from "../src/contracts"; | import { ArtifactCacheEntry } from "../src/contracts"; | ||||||
| import run from "../src/restore"; | import run from "../src/restore"; | ||||||
| import * as actionUtils from "../src/utils/actionUtils"; | import * as actionUtils from "../src/utils/actionUtils"; | ||||||
|  | @ -26,12 +26,38 @@ beforeAll(() => { | ||||||
|         } |         } | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|  |     jest.spyOn(actionUtils, "isValidEvent").mockImplementation(() => { | ||||||
|  |         const actualUtils = jest.requireActual("../src/utils/actionUtils"); | ||||||
|  |         return actualUtils.isValidEvent(); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     jest.spyOn(actionUtils, "getSupportedEvents").mockImplementation(() => { | ||||||
|  |         const actualUtils = jest.requireActual("../src/utils/actionUtils"); | ||||||
|  |         return actualUtils.getSupportedEvents(); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     jest.spyOn(io, "which").mockImplementation(tool => { |     jest.spyOn(io, "which").mockImplementation(tool => { | ||||||
|         return Promise.resolve(tool); |         return Promise.resolve(tool); | ||||||
|     }); |     }); | ||||||
| }); | }); | ||||||
|  | 
 | ||||||
|  | beforeEach(() => { | ||||||
|  |     process.env[Events.Key] = Events.Push; | ||||||
|  | }); | ||||||
|  | 
 | ||||||
| afterEach(() => { | afterEach(() => { | ||||||
|     testUtils.clearInputs(); |     testUtils.clearInputs(); | ||||||
|  |     delete process.env[Events.Key]; | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test("restore with invalid event", async () => { | ||||||
|  |     const failedMock = jest.spyOn(core, "setFailed"); | ||||||
|  |     const invalidEvent = "commit_comment"; | ||||||
|  |     process.env[Events.Key] = invalidEvent; | ||||||
|  |     await run(); | ||||||
|  |     expect(failedMock).toHaveBeenCalledWith( | ||||||
|  |         `Event Validation Error: The event type ${invalidEvent} is not supported. Only push, pull_request events are supported at this time.` | ||||||
|  |     ); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| test("restore with no path should fail", async () => { | test("restore with no path should fail", async () => { | ||||||
|  | @ -255,6 +281,82 @@ test("restore with cache found", async () => { | ||||||
|     expect(failedMock).toHaveBeenCalledTimes(0); |     expect(failedMock).toHaveBeenCalledTimes(0); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|  | test("restore with a pull request event and cache found", async () => { | ||||||
|  |     const key = "node-test"; | ||||||
|  |     const cachePath = path.resolve("node_modules"); | ||||||
|  |     testUtils.setInputs({ | ||||||
|  |         path: "node_modules", | ||||||
|  |         key | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     process.env[Events.Key] = Events.PullRequest; | ||||||
|  | 
 | ||||||
|  |     const infoMock = jest.spyOn(core, "info"); | ||||||
|  |     const warningMock = jest.spyOn(core, "warning"); | ||||||
|  |     const failedMock = jest.spyOn(core, "setFailed"); | ||||||
|  |     const stateMock = jest.spyOn(core, "saveState"); | ||||||
|  | 
 | ||||||
|  |     const cacheEntry: ArtifactCacheEntry = { | ||||||
|  |         cacheKey: key, | ||||||
|  |         scope: "refs/heads/master", | ||||||
|  |         archiveLocation: "https://www.example.com/download" | ||||||
|  |     }; | ||||||
|  |     const getCacheMock = jest.spyOn(cacheHttpClient, "getCacheEntry"); | ||||||
|  |     getCacheMock.mockImplementation(() => { | ||||||
|  |         return Promise.resolve(cacheEntry); | ||||||
|  |     }); | ||||||
|  |     const tempPath = "/foo/bar"; | ||||||
|  | 
 | ||||||
|  |     const createTempDirectoryMock = jest.spyOn( | ||||||
|  |         actionUtils, | ||||||
|  |         "createTempDirectory" | ||||||
|  |     ); | ||||||
|  |     createTempDirectoryMock.mockImplementation(() => { | ||||||
|  |         return Promise.resolve(tempPath); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     const archivePath = path.join(tempPath, "cache.tgz"); | ||||||
|  |     const setCacheStateMock = jest.spyOn(actionUtils, "setCacheState"); | ||||||
|  |     const downloadCacheMock = jest.spyOn(cacheHttpClient, "downloadCache"); | ||||||
|  | 
 | ||||||
|  |     const fileSize = 142; | ||||||
|  |     const getArchiveFileSizeMock = jest | ||||||
|  |         .spyOn(actionUtils, "getArchiveFileSize") | ||||||
|  |         .mockReturnValue(fileSize); | ||||||
|  | 
 | ||||||
|  |     const mkdirMock = jest.spyOn(io, "mkdirP"); | ||||||
|  |     const execMock = jest.spyOn(exec, "exec"); | ||||||
|  |     const setCacheHitOutputMock = jest.spyOn(actionUtils, "setCacheHitOutput"); | ||||||
|  | 
 | ||||||
|  |     await run(); | ||||||
|  | 
 | ||||||
|  |     expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key); | ||||||
|  |     expect(getCacheMock).toHaveBeenCalledWith([key]); | ||||||
|  |     expect(setCacheStateMock).toHaveBeenCalledWith(cacheEntry); | ||||||
|  |     expect(createTempDirectoryMock).toHaveBeenCalledTimes(1); | ||||||
|  |     expect(downloadCacheMock).toHaveBeenCalledWith(cacheEntry, archivePath); | ||||||
|  |     expect(getArchiveFileSizeMock).toHaveBeenCalledWith(archivePath); | ||||||
|  |     expect(mkdirMock).toHaveBeenCalledWith(cachePath); | ||||||
|  | 
 | ||||||
|  |     const IS_WINDOWS = process.platform === "win32"; | ||||||
|  |     const tarArchivePath = IS_WINDOWS | ||||||
|  |         ? archivePath.replace(/\\/g, "/") | ||||||
|  |         : archivePath; | ||||||
|  |     const tarCachePath = IS_WINDOWS ? cachePath.replace(/\\/g, "/") : cachePath; | ||||||
|  |     const args = IS_WINDOWS ? ["-xz", "--force-local"] : ["-xz"]; | ||||||
|  |     args.push(...["-f", tarArchivePath, "-C", tarCachePath]); | ||||||
|  | 
 | ||||||
|  |     expect(execMock).toHaveBeenCalledTimes(1); | ||||||
|  |     expect(execMock).toHaveBeenCalledWith(`"tar"`, args); | ||||||
|  | 
 | ||||||
|  |     expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1); | ||||||
|  |     expect(setCacheHitOutputMock).toHaveBeenCalledWith(true); | ||||||
|  | 
 | ||||||
|  |     expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`); | ||||||
|  |     expect(warningMock).toHaveBeenCalledTimes(0); | ||||||
|  |     expect(failedMock).toHaveBeenCalledTimes(0); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
| test("restore with cache found for restore key", async () => { | test("restore with cache found for restore key", async () => { | ||||||
|     const key = "node-test"; |     const key = "node-test"; | ||||||
|     const restoreKey = "node-"; |     const restoreKey = "node-"; | ||||||
|  |  | ||||||
|  | @ -12,3 +12,9 @@ export enum State { | ||||||
|     CacheKey = "CACHE_KEY", |     CacheKey = "CACHE_KEY", | ||||||
|     CacheResult = "CACHE_RESULT" |     CacheResult = "CACHE_RESULT" | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | export enum Events { | ||||||
|  |     Key = "GITHUB_EVENT_NAME", | ||||||
|  |     Push = "push", | ||||||
|  |     PullRequest = "pull_request" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -3,12 +3,22 @@ import { exec } from "@actions/exec"; | ||||||
| import * as io from "@actions/io"; | import * as io from "@actions/io"; | ||||||
| import * as path from "path"; | import * as path from "path"; | ||||||
| import * as cacheHttpClient from "./cacheHttpClient"; | import * as cacheHttpClient from "./cacheHttpClient"; | ||||||
| import { Inputs, State } from "./constants"; | import { Events, Inputs, State } from "./constants"; | ||||||
| import * as utils from "./utils/actionUtils"; | import * as utils from "./utils/actionUtils"; | ||||||
| 
 | 
 | ||||||
| async function run(): Promise<void> { | async function run(): Promise<void> { | ||||||
|     try { |     try { | ||||||
|         // Validate inputs, this can cause task failure
 |         // Validate inputs, this can cause task failure
 | ||||||
|  |         if (!utils.isValidEvent()) { | ||||||
|  |             core.setFailed( | ||||||
|  |                 `Event Validation Error: The event type ${ | ||||||
|  |                     process.env[Events.Key] | ||||||
|  |                 } is not supported. Only ${utils | ||||||
|  |                     .getSupportedEvents() | ||||||
|  |                     .join(", ")} events are supported at this time.` | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         let cachePath = utils.resolvePath( |         let cachePath = utils.resolvePath( | ||||||
|             core.getInput(Inputs.Path, { required: true }) |             core.getInput(Inputs.Path, { required: true }) | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|  | @ -4,7 +4,8 @@ import * as fs from "fs"; | ||||||
| import * as os from "os"; | import * as os from "os"; | ||||||
| import * as path from "path"; | import * as path from "path"; | ||||||
| import * as uuidV4 from "uuid/v4"; | import * as uuidV4 from "uuid/v4"; | ||||||
| import { Outputs, State } from "../constants"; | 
 | ||||||
|  | import { Events, Outputs, State } from "../constants"; | ||||||
| import { ArtifactCacheEntry } from "../contracts"; | import { ArtifactCacheEntry } from "../contracts"; | ||||||
| 
 | 
 | ||||||
| // From https://github.com/actions/toolkit/blob/master/packages/tool-cache/src/tool-cache.ts#L23
 | // From https://github.com/actions/toolkit/blob/master/packages/tool-cache/src/tool-cache.ts#L23
 | ||||||
|  | @ -83,3 +84,15 @@ export function resolvePath(filePath: string): string { | ||||||
| 
 | 
 | ||||||
|     return path.resolve(filePath); |     return path.resolve(filePath); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | export function getSupportedEvents(): string[] { | ||||||
|  |     return [Events.Push, Events.PullRequest]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Currently the cache token is only authorized for push and pull_request events
 | ||||||
|  | // All other events will fail when reading and saving the cache
 | ||||||
|  | // See GitHub Context https://help.github.com/actions/automating-your-workflow-with-github-actions/contexts-and-expression-syntax-for-github-actions#github-context
 | ||||||
|  | export function isValidEvent(): boolean { | ||||||
|  |     const githubEvent = process.env[Events.Key] || ""; | ||||||
|  |     return getSupportedEvents().includes(githubEvent); | ||||||
|  | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Josh Gross
						Josh Gross