-
Notifications
You must be signed in to change notification settings - Fork 421
Expand file tree
/
Copy pathstate-cache-storage.ts
More file actions
144 lines (131 loc) · 4.13 KB
/
state-cache-storage.ts
File metadata and controls
144 lines (131 loc) · 4.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import {IStateStorage} from '../../interfaces/state/state-storage';
import fs from 'fs';
import path from 'path';
import os from 'os';
import * as core from '@actions/core';
import {context, getOctokit} from '@actions/github';
import {retry as octokitRetry} from '@octokit/plugin-retry';
import * as cache from '@actions/cache';
import {IIssuesProcessorOptions} from '../../interfaces/issues-processor-options';
const CACHE_KEY = '_state';
const STATE_FILE = 'state.txt';
const STALE_DIR = '56acbeaa-1fef-4c79-8f84-7565e560fb03';
const mkTempDir = (): string => {
const tmpDir = path.join(os.tmpdir(), STALE_DIR);
fs.mkdirSync(tmpDir, {recursive: true});
return tmpDir;
};
const unlinkSafely = (filePath: string) => {
try {
fs.unlinkSync(filePath);
} catch (foo) {
/* ignore */
}
};
const getOctokitClient = () => {
const token = core.getInput('repo-token');
return getOctokit(token, undefined, octokitRetry);
};
const checkIfCacheExists = async (cacheKey: string): Promise<boolean> => {
const client = getOctokitClient();
try {
const issueResult = await client.request(
`/repos/${context.repo.owner}/${context.repo.repo}/actions/caches`
);
const caches: Array<{key?: string}> =
issueResult.data['actions_caches'] || [];
return Boolean(caches.find(cache => cache['key'] === cacheKey));
} catch (error) {
core.debug(`Error checking if cache exist: ${error.message}`);
}
return false;
};
const resetCacheWithOctokit = async (cacheKey: string): Promise<void> => {
const client = getOctokitClient();
core.debug(`remove cache "${cacheKey}"`);
try {
// TODO: replace with client.rest.
await client.request(
`DELETE /repos/${context.repo.owner}/${context.repo.repo}/actions/caches?key=${cacheKey}`
);
} catch (error) {
if (error.status) {
core.warning(
`Error delete ${cacheKey}: [${error.status}] ${
error.message || 'Unknown reason'
}`
);
} else {
throw error;
}
}
};
export class StateCacheStorage implements IStateStorage {
/**
* @private don't mutate in the debug mode
*/
private readonly statePrefix: string;
constructor(options: IIssuesProcessorOptions) {
this.statePrefix = options.cachePrefix;
}
async save(serializedState: string): Promise<void> {
const tmpDir = mkTempDir();
const stateFile = `${this.statePrefix}_${STATE_FILE}`;
const filePath = path.join(tmpDir, stateFile);
fs.writeFileSync(filePath, serializedState);
try {
const cacheKey = `${this.statePrefix}${CACHE_KEY}`;
const cacheExists = await checkIfCacheExists(cacheKey);
if (cacheExists) {
await resetCacheWithOctokit(cacheKey);
}
const fileSize = fs.statSync(filePath).size;
if (fileSize === 0) {
core.info(`the state will be removed`);
return;
}
await cache.saveCache([path.dirname(filePath)], cacheKey);
} catch (error) {
core.warning(
`Saving the state was not successful due to "${
error.message || 'unknown reason'
}"`
);
} finally {
unlinkSafely(filePath);
}
}
async restore(): Promise<string> {
const tmpDir = mkTempDir();
const stateFile = `${this.statePrefix}_${STATE_FILE}`;
const filePath = path.join(tmpDir, stateFile);
unlinkSafely(filePath);
try {
const cacheKey = `${this.statePrefix}${CACHE_KEY}`;
const cacheExists = await checkIfCacheExists(cacheKey);
if (!cacheExists) {
core.info(
'The saved state was not found, the process starts from the first issue.'
);
return '';
}
await cache.restoreCache([path.dirname(filePath)], cacheKey);
if (!fs.existsSync(filePath)) {
core.warning(
'Unknown error when unpacking the cache, the process starts from the first issue.'
);
return '';
}
return fs.readFileSync(path.join(tmpDir, stateFile), {
encoding: 'utf8'
});
} catch (error) {
core.warning(
`Restoring the state was not successful due to "${
error.message || 'unknown reason'
}"`
);
return '';
}
}
}