Files
renovate/tools/lint/rules/require-cacheable.ts
Michael Kriese f64ec3975a fix(cache): build all cache keys with a shared helper (#46811)
* refactor(datasource): separate registry URLs in cache keys with a colon

Co-Authored-By: Claude Opus 5.5 <michael.kriese+claude-code@mend.io>
Co-Authored-By: Claude Sonnet 5.5 <michael.kriese+claude-code@mend.io>

* fix(datasource/go): separate the remaining cache key parts with a colon

Co-Authored-By: Claude Opus 5.5 <michael.kriese+claude-code@mend.io>

* refactor(datasource): build cache keys with a shared colon-joining helper

Co-Authored-By: Claude Opus 5.5 <michael.kriese+claude-code@mend.io>

* refactor: build all cache keys with buildCacheKey

Co-Authored-By: Claude Opus 5.5 <michael.kriese+claude-code@mend.io>

* refactor(http): keep GET package cache keys as the bare URL

Co-Authored-By: Claude Opus 5.5 <michael.kriese+claude-code@mend.io>

* refactor(cache): extend cache key helper to memory cache keys

Co-Authored-By: Claude Opus 5.5 <michael.kriese+claude-code@mend.io>

* refactor(datasource/go): prefix the goproxy cache keys with their purpose

Co-Authored-By: Claude Opus 5.5 <michael.kriese+claude-code@mend.io>

---------

Co-authored-by: Claude Opus 5.5 <michael.kriese+claude-code@mend.io>
2026-10-10 10:46:09 +00:00

57 lines
1.9 KiB
TypeScript

import { defineRule } from '@oxlint/plugins';
import { getPropertyName } from '../utils/property-name.ts';
/**
* Requires every `withCache()` call to say whether its result may be cached.
*
* `cacheable` defaults to `true`, so a call which leaves it out writes whatever it fetched to the package cache - including data which came from a registry only this user can reach. That cache is shared between repositories, and on the hosted app between tenants, so the decision should be made deliberately at each call site rather than inherited from the default.
*
* Where the data can never be private - a datasource with a single, well-known, public registry - `cacheable: true` states that, and is what the reviewer needs to see.
*
* Calls which build their options elsewhere, or spread them in, are left alone: the property may well be set, and this rule cannot see it.
*/
export default defineRule({
meta: {
type: 'problem',
messages: {
requireCacheable:
"Pass 'cacheable' to withCache() to say whether this result may be written to a shared cache. Use a check for the public registry, or `cacheable: true` where the data can never be private.",
},
},
createOnce(context) {
return {
CallExpression(node) {
if (
node.callee.type !== 'Identifier' ||
node.callee.name !== 'withCache'
) {
return;
}
const [options] = node.arguments;
if (options?.type !== 'ObjectExpression') {
return;
}
let isKnown = true;
for (const property of options.properties) {
if (property.type === 'SpreadElement') {
return;
}
const name = getPropertyName(property);
if (name === 'cacheable') {
return;
}
isKnown &&= name !== undefined;
}
if (isKnown) {
context.report({ node: options, messageId: 'requireCacheable' });
}
},
};
},
});