Files
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

126 lines
3.3 KiB
TypeScript

import { isString, isTruthy } from '@sindresorhus/is';
import moo from 'moo';
import * as memCache from '../../../util/cache/memory/index.ts';
import { buildCacheKey } from '../../../util/cache/package/key.ts';
import { getEnv } from '../../../util/env.ts';
import { regEx } from '../../../util/regex.ts';
import type { GoproxyItem } from './types.ts';
/**
* Parse `GOPROXY` to the sequence of url + fallback strategy tags.
*
* @example
* parseGoproxy('foo.example.com|bar.example.com,baz.example.com')
* // [
* // { url: 'foo.example.com', fallback: '|' },
* // { url: 'bar.example.com', fallback: ',' },
* // { url: 'baz.example.com', fallback: '|' },
* // ]
*
* @see https://golang.org/ref/mod#goproxy-protocol
*/
export function parseGoproxy(
input: string | undefined = getEnv().GOPROXY,
): GoproxyItem[] {
if (!isString(input)) {
return [];
}
const cacheKey = buildCacheKey('goproxy', input);
const cachedResult = memCache.get<GoproxyItem[]>(cacheKey);
if (cachedResult) {
return cachedResult;
}
const result: GoproxyItem[] = input
.split(regEx(/(?<segment>[^,|]*(?:,|\|))/))
.filter(isTruthy)
.map((s) => s.split(regEx(/(?<separator>,|\|)/)))
// Empty segments (`a||b`, `,a`) carry no url to query, and keeping them
// would apply their separator as the fallback strategy for a bogus request
.filter(([url]) => isTruthy(url))
.map(([url, separator]) => ({
url,
fallback: separator === ',' ? ',' : '|',
}));
memCache.set(cacheKey, result);
return result;
}
// https://golang.org/pkg/path/#Match
/* oxlint-disable renovate/require-regex-util -- moo lexer patterns must be native RegExp: moo recompiles their source with the native engine and rejects RE2 instances (TODO #12870) */
const noproxyLexer = moo.states({
main: {
separator: {
match: /\s*?,\s*?/, // TODO #12870
value: (_: string) => '|',
},
asterisk: {
match: '*',
value: (_: string) => '[^/]*',
},
qmark: {
match: '?',
value: (_: string) => '[^/]',
},
characterRangeOpen: {
match: '[',
push: 'characterRange',
value: (_: string) => '[',
},
trailingSlash: {
match: /\/$/,
value: (_: string) => '',
},
char: {
match: /[^*?\\[\n]/,
value: (s: string) => s.replace(regEx('\\.', 'g'), '\\.'),
},
escapedChar: {
match: /\\./, // TODO #12870
value: (s: string) => s.slice(1),
},
},
characterRange: {
char: /[^\\\]\n]/, // TODO #12870
escapedChar: {
match: /\\./, // TODO #12870
value: (s: string) => s.slice(1),
},
characterRangeEnd: {
match: ']',
pop: 1,
},
},
});
/* oxlint-enable renovate/require-regex-util */
export function parseNoproxy(
input: unknown = (() => {
const env = getEnv();
return env.GONOPROXY ?? env.GOPRIVATE;
})(),
): RegExp | null {
if (!isString(input)) {
return null;
}
const cacheKey = buildCacheKey('noproxy', input);
const cachedResult = memCache.get<RegExp | null>(cacheKey);
if (cachedResult !== undefined) {
return cachedResult;
}
const noproxyPattern = [...noproxyLexer.reset(input)]
.map(({ value }) => value)
.join('');
const result = noproxyPattern
? regEx(`^(?:${noproxyPattern})(?:/.*)?$`)
: null;
memCache.set(cacheKey, result);
return result;
}