Skip to content

Commit

Permalink
Merge branch 'echang49/main' into with-fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
autoantwort committed Oct 7, 2024
2 parents f691222 + 52ba5a3 commit 05396ae
Show file tree
Hide file tree
Showing 10 changed files with 62 additions and 23 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ This can be achieved with the following [configuration in the action](https://do

```yaml
permissions:
actions: write # for caching state
contents: write # only for delete-branch option
issues: write
pull-requests: write
Expand Down Expand Up @@ -97,6 +98,7 @@ Every argument is optional.
| [ignore-issue-updates](#ignore-issue-updates) | Override [ignore-updates](#ignore-updates) for issues only | |
| [ignore-pr-updates](#ignore-pr-updates) | Override [ignore-updates](#ignore-updates) for PRs only | |
| [include-only-assigned](#include-only-assigned) | Process only assigned issues | `false` |
| [cache-prefix](#cache-prefix) | Add a prefix to the stored cache | |

### List of output options

Expand Down Expand Up @@ -547,6 +549,11 @@ If set to `true`, only the issues or the pull requests with an assignee will be

Default value: `false`

#### cache-prefix

Beneficial so the action has a more unique cache key. Useful when calling this action multiple times, independent of each other.
An example for usage would be closing all PRs with `x` label after 7 days in one action, and closing all PRs except for `x` label after 10 days in another.

### Usage

See also [action.yml](./action.yml) for a comprehensive list of all the options.
Expand Down
3 changes: 2 additions & 1 deletion __tests__/constants/default-processor-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,6 @@ export const DefaultProcessorOptions: IIssuesProcessorOptions = Object.freeze({
ignorePrUpdates: undefined,
exemptDraftPr: false,
closeIssueReason: 'not_planned',
includeOnlyAssigned: false
includeOnlyAssigned: false,
cachePrefix: ''
});
4 changes: 4 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ inputs:
description: 'Only the issues or the pull requests with an assignee will be marked as stale automatically.'
default: 'false'
required: false
cache-prefix:
description: 'Add a prefix to the stored cache. Useful when using this action multiple times with different params.'
default: ''
required: false
outputs:
closed-issues-prs:
description: 'List of all closed issues and pull requests.'
Expand Down
29 changes: 19 additions & 10 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1644,22 +1644,27 @@ const resetCacheWithOctokit = (cacheKey) => __awaiter(void 0, void 0, void 0, fu
}
});
class StateCacheStorage {
constructor(options) {
this.statePrefix = options.cachePrefix;
}
save(serializedState) {
return __awaiter(this, void 0, void 0, function* () {
const tmpDir = mkTempDir();
const filePath = path_1.default.join(tmpDir, STATE_FILE);
const stateFile = `${this.statePrefix}_${STATE_FILE}`;
const filePath = path_1.default.join(tmpDir, stateFile);
fs_1.default.writeFileSync(filePath, serializedState);
try {
const cacheExists = yield checkIfCacheExists(CACHE_KEY);
const cacheKey = `${this.statePrefix}${CACHE_KEY}`;
const cacheExists = yield checkIfCacheExists(cacheKey);
if (cacheExists) {
yield resetCacheWithOctokit(CACHE_KEY);
yield resetCacheWithOctokit(cacheKey);
}
const fileSize = fs_1.default.statSync(filePath).size;
if (fileSize === 0) {
core.info(`the state will be removed`);
return;
}
yield cache.saveCache([path_1.default.dirname(filePath)], CACHE_KEY);
yield cache.saveCache([path_1.default.dirname(filePath)], cacheKey);
}
catch (error) {
core.warning(`Saving the state was not successful due to "${error.message || 'unknown reason'}"`);
Expand All @@ -1672,20 +1677,22 @@ class StateCacheStorage {
restore() {
return __awaiter(this, void 0, void 0, function* () {
const tmpDir = mkTempDir();
const filePath = path_1.default.join(tmpDir, STATE_FILE);
const stateFile = `${this.statePrefix}_${STATE_FILE}`;
const filePath = path_1.default.join(tmpDir, stateFile);
unlinkSafely(filePath);
try {
const cacheExists = yield checkIfCacheExists(CACHE_KEY);
const cacheKey = `${this.statePrefix}${CACHE_KEY}`;
const cacheExists = yield checkIfCacheExists(cacheKey);
if (!cacheExists) {
core.info('The saved state was not found, the process starts from the first issue.');
return '';
}
yield cache.restoreCache([path_1.default.dirname(filePath)], CACHE_KEY);
yield cache.restoreCache([path_1.default.dirname(filePath)], cacheKey);
if (!fs_1.default.existsSync(filePath)) {
core.warning('Unknown error when unpacking the cache, the process starts from the first issue.');
return '';
}
return fs_1.default.readFileSync(path_1.default.join(tmpDir, STATE_FILE), {
return fs_1.default.readFileSync(path_1.default.join(tmpDir, stateFile), {
encoding: 'utf8'
});
}
Expand Down Expand Up @@ -2222,6 +2229,7 @@ var Option;
Option["IgnorePrUpdates"] = "ignore-pr-updates";
Option["ExemptDraftPr"] = "exempt-draft-pr";
Option["CloseIssueReason"] = "close-issue-reason";
Option["CachePrefix"] = "cache-prefix";
})(Option || (exports.Option = Option = {}));


Expand Down Expand Up @@ -2567,7 +2575,8 @@ function _getAndValidateArgs() {
ignorePrUpdates: _toOptionalBoolean('ignore-pr-updates'),
exemptDraftPr: core.getInput('exempt-draft-pr') === 'true',
closeIssueReason: core.getInput('close-issue-reason'),
includeOnlyAssigned: core.getInput('include-only-assigned') === 'true'
includeOnlyAssigned: core.getInput('include-only-assigned') === 'true',
cachePrefix: core.getInput('cache-prefix')
};
for (const numberInput of ['days-before-stale']) {
if (isNaN(parseFloat(core.getInput(numberInput)))) {
Expand Down Expand Up @@ -2694,7 +2703,7 @@ exports.getStateInstance = void 0;
const state_1 = __nccwpck_require__(5186);
const state_cache_storage_1 = __nccwpck_require__(3709);
const getStateInstance = (options) => {
const storage = new state_cache_storage_1.StateCacheStorage();
const storage = new state_cache_storage_1.StateCacheStorage(options);
return new state_1.State(storage, options);
};
exports.getStateInstance = getStateInstance;
Expand Down
3 changes: 2 additions & 1 deletion src/classes/issue.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ describe('Issue', (): void => {
ignorePrUpdates: undefined,
exemptDraftPr: false,
closeIssueReason: '',
includeOnlyAssigned: false
includeOnlyAssigned: false,
cachePrefix: ''
};
issueInterface = {
title: 'dummy-title',
Expand Down
30 changes: 22 additions & 8 deletions src/classes/state/state-cache-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ 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';
Expand Down Expand Up @@ -65,15 +66,26 @@ const resetCacheWithOctokit = async (cacheKey: string): Promise<void> => {
}
};
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 filePath = path.join(tmpDir, STATE_FILE);
const stateFile = `${this.statePrefix}_${STATE_FILE}`;
const filePath = path.join(tmpDir, stateFile);
fs.writeFileSync(filePath, serializedState);

try {
const cacheExists = await checkIfCacheExists(CACHE_KEY);
const cacheKey = `${this.statePrefix}${CACHE_KEY}`;
const cacheExists = await checkIfCacheExists(cacheKey);
if (cacheExists) {
await resetCacheWithOctokit(CACHE_KEY);
await resetCacheWithOctokit(cacheKey);
}
const fileSize = fs.statSync(filePath).size;

Expand All @@ -82,7 +94,7 @@ export class StateCacheStorage implements IStateStorage {
return;
}

await cache.saveCache([path.dirname(filePath)], CACHE_KEY);
await cache.saveCache([path.dirname(filePath)], cacheKey);
} catch (error) {
core.warning(
`Saving the state was not successful due to "${
Expand All @@ -96,26 +108,28 @@ export class StateCacheStorage implements IStateStorage {

async restore(): Promise<string> {
const tmpDir = mkTempDir();
const filePath = path.join(tmpDir, STATE_FILE);
const stateFile = `${this.statePrefix}_${STATE_FILE}`;
const filePath = path.join(tmpDir, stateFile);
unlinkSafely(filePath);
try {
const cacheExists = await checkIfCacheExists(CACHE_KEY);
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)], CACHE_KEY);
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, STATE_FILE), {
return fs.readFileSync(path.join(tmpDir, stateFile), {
encoding: 'utf8'
});
} catch (error) {
Expand Down
3 changes: 2 additions & 1 deletion src/enums/option.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,6 @@ export enum Option {
IgnoreIssueUpdates = 'ignore-issue-updates',
IgnorePrUpdates = 'ignore-pr-updates',
ExemptDraftPr = 'exempt-draft-pr',
CloseIssueReason = 'close-issue-reason'
CloseIssueReason = 'close-issue-reason',
CachePrefix = 'cache-prefix'
}
1 change: 1 addition & 0 deletions src/interfaces/issues-processor-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,5 @@ export interface IIssuesProcessorOptions {
exemptDraftPr: boolean;
closeIssueReason: string;
includeOnlyAssigned: boolean;
cachePrefix: string;
}
3 changes: 2 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ function _getAndValidateArgs(): IIssuesProcessorOptions {
ignorePrUpdates: _toOptionalBoolean('ignore-pr-updates'),
exemptDraftPr: core.getInput('exempt-draft-pr') === 'true',
closeIssueReason: core.getInput('close-issue-reason'),
includeOnlyAssigned: core.getInput('include-only-assigned') === 'true'
includeOnlyAssigned: core.getInput('include-only-assigned') === 'true',
cachePrefix: core.getInput('cache-prefix')
};

for (const numberInput of ['days-before-stale']) {
Expand Down
2 changes: 1 addition & 1 deletion src/services/state.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
import {StateCacheStorage} from '../classes/state/state-cache-storage';

export const getStateInstance = (options: IIssuesProcessorOptions): IState => {
const storage = new StateCacheStorage();
const storage = new StateCacheStorage(options);
return new State(storage, options);
};

0 comments on commit 05396ae

Please sign in to comment.