Skip to content

Commit 2d9305f

Browse files
PhilipAbedviceice
andauthoredApr 11, 2024··
feat(cache): RENOVATE_X_REPO_CACHE_FORCE_LOCAL (#28291)
Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
1 parent 56fd8fb commit 2d9305f

File tree

5 files changed

+48
-8
lines changed

5 files changed

+48
-8
lines changed
 

‎docs/usage/self-hosted-experimental.md

+4
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ If set, Renovate will rewrite GitHub Enterprise Server's pagination responses to
160160
!!! note
161161
For the GitHub Enterprise Server platform only.
162162

163+
## `RENOVATE_X_REPO_CACHE_FORCE_LOCAL`
164+
165+
If set, Renovate will persist repository cache locally after uploading to S3.
166+
163167
## `RENOVATE_X_S3_ENDPOINT`
164168

165169
If set, Renovate will use this string as the `endpoint` when instantiating the AWS S3 client.

‎lib/util/cache/repository/common.ts

+11
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,13 @@
11
// Increment this whenever there could be incompatibilities between old and new cache structure
2+
import upath from 'upath';
3+
24
export const CACHE_REVISION = 13;
5+
6+
export function getLocalCacheFileName(
7+
platform: string,
8+
repository: string,
9+
): string {
10+
const repoCachePath = 'renovate/repository/';
11+
const fileName = `${repository}.json`;
12+
return upath.join(repoCachePath, platform, fileName);
13+
}

‎lib/util/cache/repository/impl/local.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import upath from 'upath';
21
import { logger } from '../../../../logger';
32
import { cachePathExists, outputCacheFile, readCacheFile } from '../../../fs';
3+
import { getLocalCacheFileName } from '../common';
44
import type { RepoCacheRecord } from '../schema';
55
import { RepoCacheBase } from './base';
66

@@ -29,9 +29,6 @@ export class RepoCacheLocal extends RepoCacheBase {
2929
}
3030

3131
private getCacheFileName(): string {
32-
const repoCachePath = 'renovate/repository/';
33-
const platform = this.platform;
34-
const fileName = `${this.repository}.json`;
35-
return upath.join(repoCachePath, platform, fileName);
32+
return getLocalCacheFileName(this.platform, this.repository);
3633
}
3734
}

‎lib/util/cache/repository/impl/s3.spec.ts

+19-2
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@ import {
88
S3Client,
99
} from '@aws-sdk/client-s3';
1010
import { mockClient } from 'aws-sdk-client-mock';
11-
import { partial } from '../../../../../test/util';
11+
import { fs, partial } from '../../../../../test/util';
1212
import { GlobalConfig } from '../../../../config/global';
1313
import { logger } from '../../../../logger';
1414
import { parseS3Url } from '../../../s3';
1515
import type { RepoCacheRecord } from '../schema';
1616
import { CacheFactory } from './cache-factory';
1717
import { RepoCacheS3 } from './s3';
1818

19+
jest.mock('../../../fs');
20+
1921
function createGetObjectCommandInput(
2022
repository: string,
2123
url: string,
@@ -57,7 +59,7 @@ describe('util/cache/repository/impl/s3', () => {
5759
let s3Cache: RepoCacheS3;
5860

5961
beforeEach(() => {
60-
GlobalConfig.set({ platform: 'github' });
62+
GlobalConfig.set({ cacheDir: '/tmp/cache', platform: 'github' });
6163
s3Mock.reset();
6264
s3Cache = new RepoCacheS3(repository, '0123456789abcdef', url);
6365
getObjectCommandInput = createGetObjectCommandInput(repository, url);
@@ -196,4 +198,19 @@ describe('util/cache/repository/impl/s3', () => {
196198
const cache = CacheFactory.get(repository, '0123456789abcdef', url);
197199
expect(cache instanceof RepoCacheS3).toBeTrue();
198200
});
201+
202+
it('should persists data locally after uploading to s3', async () => {
203+
process.env.RENOVATE_X_REPO_CACHE_FORCE_LOCAL = 'true';
204+
const putObjectCommandOutput: PutObjectCommandOutput = {
205+
$metadata: { attempts: 1, httpStatusCode: 200, totalRetryDelay: 0 },
206+
};
207+
s3Mock
208+
.on(PutObjectCommand, putObjectCommandInput)
209+
.resolvesOnce(putObjectCommandOutput);
210+
await s3Cache.write(repoCache);
211+
expect(fs.outputCacheFile).toHaveBeenCalledWith(
212+
'renovate/repository/github/org/repo.json',
213+
JSON.stringify(repoCache),
214+
);
215+
});
199216
});

‎lib/util/cache/repository/impl/s3.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ import {
55
PutObjectCommand,
66
PutObjectCommandInput,
77
} from '@aws-sdk/client-s3';
8+
import is from '@sindresorhus/is';
89
import { logger } from '../../../../logger';
10+
import { outputCacheFile } from '../../../fs';
911
import { getS3Client, parseS3Url } from '../../../s3';
1012
import { streamToString } from '../../../streams';
13+
import { getLocalCacheFileName } from '../common';
1114
import type { RepoCacheRecord } from '../schema';
1215
import { RepoCacheBase } from './base';
1316

@@ -54,14 +57,22 @@ export class RepoCacheS3 extends RepoCacheBase {
5457

5558
async write(data: RepoCacheRecord): Promise<void> {
5659
const cacheFileName = this.getCacheFileName();
60+
const stringifiedCache = JSON.stringify(data);
5761
const s3Params: PutObjectCommandInput = {
5862
Bucket: this.bucket,
5963
Key: cacheFileName,
60-
Body: JSON.stringify(data),
64+
Body: stringifiedCache,
6165
ContentType: 'application/json',
6266
};
6367
try {
6468
await this.s3Client.send(new PutObjectCommand(s3Params));
69+
if (is.nonEmptyString(process.env.RENOVATE_X_REPO_CACHE_FORCE_LOCAL)) {
70+
const cacheLocalFileName = getLocalCacheFileName(
71+
this.platform,
72+
this.repository,
73+
);
74+
await outputCacheFile(cacheLocalFileName, stringifiedCache);
75+
}
6576
} catch (err) {
6677
logger.warn({ err }, 'RepoCacheS3.write() - failure');
6778
}

0 commit comments

Comments
 (0)
Please sign in to comment.