mirror of
https://github.com/renovatebot/renovate.git
synced 2025-01-11 22:46:27 +00:00
Compare commits
21 commits
08ab2e642b
...
a769c88f33
Author | SHA1 | Date | |
---|---|---|---|
|
a769c88f33 | ||
|
f97189c600 | ||
|
59455c0512 | ||
|
147b620187 | ||
|
39fb207a83 | ||
|
db31a1634c | ||
|
5282f7c080 | ||
|
974a8a8b51 | ||
|
2bfc754634 | ||
|
0fae10626b | ||
|
50e53440f4 | ||
|
c043653c4b | ||
|
351d9ef3e8 | ||
|
c04c64f5e7 | ||
|
2c75a8d4f7 | ||
|
ae54b7069f | ||
|
8708557f44 | ||
|
0edddf4a28 | ||
|
2411a6f713 | ||
|
d2b04461aa | ||
|
3f65694a00 |
44 changed files with 1198 additions and 753 deletions
|
@ -9,6 +9,7 @@ nav:
|
|||
- 'Self-hosted': 'self-hosted-configuration.md'
|
||||
- 'Presets': 'config-presets.md'
|
||||
- 'Validation': 'config-validation.md'
|
||||
- 'Switching bot identity': 'switching-bot-identity.md'
|
||||
- ... | mend-hosted
|
||||
- ... | key-concepts
|
||||
- ... | modules
|
||||
|
|
BIN
docs/usage/assets/images/portal-sign-in.png
Normal file
BIN
docs/usage/assets/images/portal-sign-in.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
|
@ -145,6 +145,19 @@ archive_override(
|
|||
Renovate ignores [`multiple_version_override`](https://bazel.build/rules/lib/globals/module#multiple_version_override).
|
||||
`multiple_version_override` does not affect the processing of version updates for a module.
|
||||
|
||||
### `git_repository`
|
||||
|
||||
If Renovate finds a [`git_repository`](https://bazel.build/rules/lib/repo/git#git_repository), it evaluates the `commit` value at the specified `remote`.
|
||||
`remote` is limited to github repos: `https://github.com/<owner>/<repo>.git`
|
||||
|
||||
```python
|
||||
git_repository(
|
||||
name = "rules_foo",
|
||||
remote = "https://github.com/fooexample/rules_foo.git",
|
||||
commit = "8c94e11c2b05b6f25ced5f23cd07d0cfd36edc1a",
|
||||
)
|
||||
```
|
||||
|
||||
## Legacy `WORKSPACE` files
|
||||
|
||||
Renovate extracts dependencies from the following repository rules:
|
||||
|
@ -160,7 +173,7 @@ Renovate extracts dependencies from the following repository rules:
|
|||
It also recognizes when these repository rule names are prefixed with an underscore.
|
||||
For example, `_http_archive` is treated the same as `http_archive`.
|
||||
|
||||
### `git_repository`
|
||||
### `git_repository` (legacy)
|
||||
|
||||
Renovate updates any `git_repository` declaration that has the following:
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ name: renovate-otel-demo
|
|||
services:
|
||||
# Jaeger for storing traces
|
||||
jaeger:
|
||||
image: jaegertracing/jaeger:2.1.0
|
||||
image: jaegertracing/jaeger:2.2.0
|
||||
ports:
|
||||
- '16686:16686' # Web UI
|
||||
- '4317' # OTLP gRPC
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
title: Mend-hosted Apps
|
||||
nav:
|
||||
- 'Renovate Plans': 'renovate-plans.md'
|
||||
- 'Configuration': 'hosted-apps-config.md'
|
||||
- 'Credentials': 'credentials.md'
|
||||
- 'Migrating Secrets': 'migrating-secrets.md'
|
||||
|
|
53
docs/usage/mend-hosted/renovate-plans.md
Normal file
53
docs/usage/mend-hosted/renovate-plans.md
Normal file
|
@ -0,0 +1,53 @@
|
|||
# Renovate Plans on Mend-Hosted Apps
|
||||
|
||||
Mend provides cloud hosting services for running Renovate on repositories hosted on the following cloud platforms:
|
||||
|
||||
- GitHub
|
||||
- Bitbucket Cloud
|
||||
- Azure DevOps
|
||||
|
||||
Mend Cloud will regularly schedule Renovate jobs against all installed repositories.
|
||||
It also listens to webhooks and enqueues a Renovate job when relevant changes occur in a repo, or when actions are triggered from the Renovate PRs or Dashboard issue.
|
||||
There is a web UI with functionality to view and interact with installed repositories, their jobs and job logs.
|
||||
|
||||
## Accessing Mend Cloud via the Web UI
|
||||
|
||||
Users can access the cloud-hosted Renovate service via the Developer Portal at [https://developer.mend.io/](https://developer.mend.io/).
|
||||
Developers can log in with OAuth credentials from their cloud-based Git repository.
|
||||
|
||||
![Developer Portal sign-in screen](../assets/images/portal-sign-in.png)
|
||||
|
||||
Features of the Developer Portal include:
|
||||
|
||||
- Ability to install, uninstall and view installed repositories
|
||||
- Trigger Renovate jobs to run on demand
|
||||
- View logs for all Renovate jobs
|
||||
- Configure settings that apply at the Org-level or Repo-level
|
||||
|
||||
## Resources and Scheduling
|
||||
|
||||
The plan assigned to each Org determines the resources, scheduling and concurrency of Renoate jobs.
|
||||
Mend Cloud has free and paid Renovate plans. Details of the plans are shown in the table below.
|
||||
|
||||
| | Community (Free) | Pioneer (Free) | OSS Select (Free) | Enterprise |
|
||||
| ----------------------------- | ---------------- | -------------- | ----------------- | ------------ |
|
||||
| Concurrent jobs per Org | 1 | 8 | 2 | 16 |
|
||||
| Job scheduling (active repos) | Every 4 hours | Every 4 hours | Hourly | Hourly (\*1) |
|
||||
| Job runner CPUs | 1 CPU | 1 CPU | 1 CPU | 2 CPU |
|
||||
| Job runner Memory | 2Gb | 3.5Gb | 6Gb | 8Gb |
|
||||
| Job runner Disk space | 15Gb | 15Gb | 25Gb | 40Gb |
|
||||
| Job timeout | 30 minutes | 30 minutes | 60 minutes | 60 minutes |
|
||||
| Merge Confidence Workflows | Not included | Not included | Included | Included |
|
||||
| Mend.io Helpdesk Support | Not included | Not included | Not Included | Included |
|
||||
|
||||
(1) Bitbucket repositories on the Renovate Enterprise plan are scheduled to run every 4 hours, to avoid hitting rate limits on GitHub APIs.
|
||||
|
||||
### Plan descriptions
|
||||
|
||||
**Community (Free)** - This plan is available for free for all repositories.
|
||||
|
||||
**Pioneer (Free)** - This plan is available for a limited time for Orgs that were installed on Renovate Cloud before 2025. Users on this plan will be transitioned to other plans over time.
|
||||
|
||||
**OSS Select (Free)** - This is a premium plan granted for free to selected OSS Orgs. If you would like your Org to be considered for the free OSS Select plan, create a “[Suggest an Idea](https://github.com/renovatebot/renovate/discussions/categories/suggest-an-idea)” item on the Renovate discussions board on GitHub. Acceptance is at the discretion of Mend.io.
|
||||
|
||||
**Enterprise** - A supported, paid plan available for purchase through Mend.io. Contact Mend at [sales@mend.io](mailto:sales@mend.io) for purchase details.
|
52
docs/usage/switching-bot-identity.md
Normal file
52
docs/usage/switching-bot-identity.md
Normal file
|
@ -0,0 +1,52 @@
|
|||
# Switching bot identity
|
||||
|
||||
Renovate uses its bot identity to know:
|
||||
|
||||
- if a PR is authored by Renovate bot
|
||||
- if you, or some other bot, pushed commits into the PR branch
|
||||
- Editor note: list other things Renovate uses the bot id for here
|
||||
|
||||
## Reasons to switch bot identity
|
||||
|
||||
Common reasons to switch bot identity are:
|
||||
|
||||
- Migrating between Mend-hosted and self-hosted
|
||||
- Renaming the bot (self-hosted)
|
||||
- Editor note: Maybe there are other reasons too, please comment
|
||||
|
||||
### Migrating from Mend-hosted to self-hosted
|
||||
|
||||
Most users start with the Mend-hosted bot, and are happy with this.
|
||||
After a while, some users want to switch to self-hosting, for more control.
|
||||
|
||||
When you migrate you must tell your new self-hosted Renovate bot to take over from the old Mend-hosted bot.
|
||||
|
||||
### Renaming the bot (self-hosted)
|
||||
|
||||
Maybe you started self-hosting Renovate, and called your bot `@old-bot-name`.
|
||||
But the name no longer fits, so you want to use `@new-bot-name`.
|
||||
|
||||
Follow these steps:
|
||||
|
||||
1. Start of ordered list with steps
|
||||
1. Second step
|
||||
1. Third step, and so on
|
||||
|
||||
### Other situations
|
||||
|
||||
Editor note: Please comment about other cases where you need to switch bot identity.
|
||||
|
||||
## How to switch bot identity
|
||||
|
||||
Looks like the steps are:
|
||||
|
||||
1. Put old bot name in the `gitIgnoredAuthors` config option
|
||||
1. Set `ignorePrAuthor` to `true`
|
||||
1. Let the "new" bot take over from the "old bot"
|
||||
|
||||
## Questions from the editor
|
||||
|
||||
- Is the `gitAuthor` field the bot identity?
|
||||
- What's `gitIgnoredAuthors` for? It looks like you can ignore commits from the old bot with it?
|
||||
- We also have `ignorePrAuthor` which if set to `true` fetches the _whole_ list of PRs, instead of just fetching Renovate PRs. The docs say this is the one to use to ignore old bot names. But the description of `ignorePrAuthor` only mentions the full list fetching, nothing about the name/id of the bot.
|
||||
- It's easy to confuse `gitIgnoredAuthors` and `ignorePrAuthor` they have similar names, but do different things.
|
|
@ -505,7 +505,10 @@
|
|||
"shiki": "https://github.com/shikijs/shiki",
|
||||
"shopify-app-bridge": "https://github.com/Shopify/app-bridge",
|
||||
"sitecore-jss": "https://github.com/Sitecore/jss",
|
||||
"skiasharp": "https://github.com/mono/SkiaSharp",
|
||||
"skiasharp": [
|
||||
"https://github.com/mono/SkiaSharp",
|
||||
"https://go.microsoft.com/fwlink/?linkid=868515"
|
||||
],
|
||||
"slack-net": "https://github.com/soxtoby/SlackNet",
|
||||
"slf4j": "https://github.com/qos-ch/slf4j",
|
||||
"slim-message-bus": "https://github.com/zarusz/SlimMessageBus",
|
||||
|
|
|
@ -392,5 +392,31 @@ describe('modules/manager/bazel-module/extract', () => {
|
|||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns git_repository dependencies', async () => {
|
||||
const input = codeBlock`
|
||||
git_repository(
|
||||
name = "rules_foo",
|
||||
commit = "850cb49c8649e463b80ef7984e7c744279746170",
|
||||
remote = "https://github.com/example/rules_foo.git",
|
||||
)
|
||||
`;
|
||||
const result = await extractPackageFile(input, 'MODULE.bazel');
|
||||
if (!result) {
|
||||
throw new Error('Expected a result.');
|
||||
}
|
||||
expect(result.deps).toHaveLength(1);
|
||||
expect(result.deps).toEqual(
|
||||
expect.arrayContaining([
|
||||
{
|
||||
datasource: GithubTagsDatasource.id,
|
||||
depType: 'git_repository',
|
||||
depName: 'rules_foo',
|
||||
currentDigest: '850cb49c8649e463b80ef7984e7c744279746170',
|
||||
packageName: 'example/rules_foo',
|
||||
},
|
||||
]),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,7 +8,10 @@ import type { RecordFragment } from './fragments';
|
|||
import { parse } from './parser';
|
||||
import { RuleToMavenPackageDep, fillRegistryUrls } from './parser/maven';
|
||||
import { RuleToDockerPackageDep } from './parser/oci';
|
||||
import { RuleToBazelModulePackageDep } from './rules';
|
||||
import {
|
||||
GitRepositoryToPackageDep,
|
||||
RuleToBazelModulePackageDep,
|
||||
} from './rules';
|
||||
import * as rules from './rules';
|
||||
|
||||
export async function extractPackageFile(
|
||||
|
@ -18,9 +21,14 @@ export async function extractPackageFile(
|
|||
try {
|
||||
const records = parse(content);
|
||||
const pfc = await extractBazelPfc(records, packageFile);
|
||||
const gitRepositoryDeps = extractGitRepositoryDeps(records);
|
||||
const mavenDeps = extractMavenDeps(records);
|
||||
const dockerDeps = LooseArray(RuleToDockerPackageDep).parse(records);
|
||||
|
||||
if (gitRepositoryDeps.length) {
|
||||
pfc.deps.push(...gitRepositoryDeps);
|
||||
}
|
||||
|
||||
if (mavenDeps.length) {
|
||||
pfc.deps.push(...mavenDeps);
|
||||
}
|
||||
|
@ -57,6 +65,12 @@ async function extractBazelPfc(
|
|||
return pfc;
|
||||
}
|
||||
|
||||
function extractGitRepositoryDeps(
|
||||
records: RecordFragment[],
|
||||
): PackageDependency[] {
|
||||
return LooseArray(GitRepositoryToPackageDep).parse(records);
|
||||
}
|
||||
|
||||
function extractMavenDeps(records: RecordFragment[]): PackageDependency[] {
|
||||
return LooseArray(RuleToMavenPackageDep)
|
||||
.transform(fillRegistryUrls)
|
||||
|
|
|
@ -315,5 +315,37 @@ describe('modules/manager/bazel-module/parser/index', () => {
|
|||
),
|
||||
]);
|
||||
});
|
||||
|
||||
it('finds the git_repository', () => {
|
||||
const input = codeBlock`
|
||||
git_repository(
|
||||
name = "rules_foo",
|
||||
remote = "https://github.com/example/rules_foo.git",
|
||||
commit = "6a2c2e22849b3e6b33d5ea9aa72222d4803a986a",
|
||||
patches = ["//:rules_foo.patch"],
|
||||
patch_strip = 1,
|
||||
)
|
||||
`;
|
||||
const res = parse(input);
|
||||
expect(res).toEqual([
|
||||
fragments.record(
|
||||
{
|
||||
rule: fragments.string('git_repository'),
|
||||
name: fragments.string('rules_foo'),
|
||||
patches: fragments.array(
|
||||
[fragments.string('//:rules_foo.patch')],
|
||||
true,
|
||||
),
|
||||
commit: fragments.string(
|
||||
'6a2c2e22849b3e6b33d5ea9aa72222d4803a986a',
|
||||
),
|
||||
remote: fragments.string(
|
||||
'https://github.com/example/rules_foo.git',
|
||||
),
|
||||
},
|
||||
true,
|
||||
),
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,6 +9,7 @@ const supportedRules = [
|
|||
'git_override',
|
||||
'local_path_override',
|
||||
'single_version_override',
|
||||
'git_repository',
|
||||
];
|
||||
const supportedRulesRegex = regEx(`^${supportedRules.join('|')}$`);
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import type {
|
|||
OverridePackageDep,
|
||||
} from './rules';
|
||||
import {
|
||||
GitRepositoryToPackageDep,
|
||||
RuleToBazelModulePackageDep,
|
||||
bazelModulePackageDepToPackageDependency,
|
||||
processModulePkgDeps,
|
||||
|
@ -72,6 +73,19 @@ const singleVersionOverrideWithoutVersionAndRegistryPkgDep: BasePackageDep = {
|
|||
depName: 'rules_foo',
|
||||
skipReason: 'ignored',
|
||||
};
|
||||
const gitRepositoryForGithubPkgDep: BasePackageDep = {
|
||||
datasource: GithubTagsDatasource.id,
|
||||
depType: 'git_repository',
|
||||
depName: 'rules_foo',
|
||||
packageName: 'example/rules_foo',
|
||||
currentDigest: '850cb49c8649e463b80ef7984e7c744279746170',
|
||||
};
|
||||
const gitRepositoryForUnsupportedPkgDep: BasePackageDep = {
|
||||
depType: 'git_repository',
|
||||
depName: 'rules_foo',
|
||||
currentDigest: '850cb49c8649e463b80ef7984e7c744279746170',
|
||||
skipReason: 'unsupported-datasource',
|
||||
};
|
||||
|
||||
describe('modules/manager/bazel-module/rules', () => {
|
||||
describe('RuleToBazelModulePackageDep', () => {
|
||||
|
@ -129,6 +143,30 @@ describe('modules/manager/bazel-module/rules', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('GitRepositoryToPackageDep', () => {
|
||||
const gitRepositoryWithGihubHost = fragments.record({
|
||||
rule: fragments.string('git_repository'),
|
||||
name: fragments.string('rules_foo'),
|
||||
remote: fragments.string('https://github.com/example/rules_foo.git'),
|
||||
commit: fragments.string('850cb49c8649e463b80ef7984e7c744279746170'),
|
||||
});
|
||||
const gitRepositoryWithUnsupportedHost = fragments.record({
|
||||
rule: fragments.string('git_repository'),
|
||||
name: fragments.string('rules_foo'),
|
||||
remote: fragments.string('https://nobuenos.com/example/rules_foo.git'),
|
||||
commit: fragments.string('850cb49c8649e463b80ef7984e7c744279746170'),
|
||||
});
|
||||
|
||||
it.each`
|
||||
msg | a | exp
|
||||
${'git_repository, GitHub host'} | ${gitRepositoryWithGihubHost} | ${gitRepositoryForGithubPkgDep}
|
||||
${'git_repository, unsupported host'} | ${gitRepositoryWithUnsupportedHost} | ${gitRepositoryForUnsupportedPkgDep}
|
||||
`('.parse() with $msg', ({ a, exp }) => {
|
||||
const pkgDep = GitRepositoryToPackageDep.parse(a);
|
||||
expect(pkgDep).toEqual(exp);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.toPackageDependencies()', () => {
|
||||
const expectedBazelDepNoOverrides: PackageDependency[] = [bazelDepPkgDep];
|
||||
const expectedBazelDepAndGitOverride: PackageDependency[] = [
|
||||
|
|
|
@ -242,3 +242,28 @@ export function toPackageDependencies(
|
|||
): PackageDependency[] {
|
||||
return collectByModule(packageDeps).map(processModulePkgDeps).flat();
|
||||
}
|
||||
|
||||
export const GitRepositoryToPackageDep = RecordFragmentSchema.extend({
|
||||
children: z.object({
|
||||
rule: StringFragmentSchema.extend({
|
||||
value: z.literal('git_repository'),
|
||||
}),
|
||||
name: StringFragmentSchema,
|
||||
remote: StringFragmentSchema,
|
||||
commit: StringFragmentSchema,
|
||||
}),
|
||||
}).transform(({ children: { rule, name, remote, commit } }): BasePackageDep => {
|
||||
const gitRepo: BasePackageDep = {
|
||||
depType: rule.value,
|
||||
depName: name.value,
|
||||
currentDigest: commit.value,
|
||||
};
|
||||
const ghPackageName = githubPackageName(remote.value);
|
||||
if (is.nonEmptyString(ghPackageName)) {
|
||||
gitRepo.datasource = GithubTagsDatasource.id;
|
||||
gitRepo.packageName = ghPackageName;
|
||||
} else {
|
||||
gitRepo.skipReason = 'unsupported-datasource';
|
||||
}
|
||||
return gitRepo;
|
||||
});
|
||||
|
|
|
@ -438,8 +438,8 @@ describe('modules/manager/bundler/artifacts', () => {
|
|||
bundlerHostRules.findAllAuthenticatable.mockReturnValue([
|
||||
{
|
||||
hostType: 'bundler',
|
||||
matchHost: 'gems.private.com',
|
||||
resolvedHost: 'gems.private.com',
|
||||
matchHost: 'gems-private.com',
|
||||
resolvedHost: 'gems-private.com',
|
||||
username: 'some-user',
|
||||
password: 'some-password',
|
||||
},
|
||||
|
@ -470,7 +470,7 @@ describe('modules/manager/bundler/artifacts', () => {
|
|||
'docker run --rm --name=renovate_sidecar --label=renovate_child ' +
|
||||
'-v "/tmp/github/some/repo":"/tmp/github/some/repo" ' +
|
||||
'-v "/tmp/cache":"/tmp/cache" ' +
|
||||
'-e BUNDLE_GEMS__PRIVATE__COM ' +
|
||||
'-e BUNDLE_GEMS___PRIVATE__COM ' +
|
||||
'-e GEM_HOME ' +
|
||||
'-e CONTAINERBASE_CACHE_DIR ' +
|
||||
'-w "/tmp/github/some/repo" ' +
|
||||
|
@ -487,218 +487,6 @@ describe('modules/manager/bundler/artifacts', () => {
|
|||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('injects bundler host configuration as command with bundler < 2', async () => {
|
||||
GlobalConfig.set({ ...adminConfig, binarySource: 'docker' });
|
||||
fs.readLocalFile.mockResolvedValueOnce('Current Gemfile.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce('1.2.0');
|
||||
// ruby
|
||||
datasource.getPkgReleases.mockResolvedValueOnce({
|
||||
releases: [
|
||||
{ version: '1.0.0' },
|
||||
{ version: '1.2.0' },
|
||||
{ version: '1.3.0' },
|
||||
],
|
||||
});
|
||||
bundlerHostRules.findAllAuthenticatable.mockReturnValue([
|
||||
{
|
||||
hostType: 'bundler',
|
||||
matchHost: 'gems-private.com',
|
||||
resolvedHost: 'gems-private.com',
|
||||
username: 'some-user',
|
||||
password: 'some-password',
|
||||
},
|
||||
]);
|
||||
bundlerHostRules.getAuthenticationHeaderValue.mockReturnValue(
|
||||
'some-user:some-password',
|
||||
);
|
||||
const execSnapshots = mockExecAll();
|
||||
git.getRepoStatus.mockResolvedValueOnce(
|
||||
partial<StatusResult>({
|
||||
modified: ['Gemfile.lock'],
|
||||
}),
|
||||
);
|
||||
fs.readLocalFile.mockResolvedValueOnce('Updated Gemfile.lock');
|
||||
expect(
|
||||
await updateArtifacts({
|
||||
packageFileName: 'Gemfile',
|
||||
updatedDeps: [{ depName: 'foo' }, { depName: 'bar' }],
|
||||
newPackageFileContent: 'Updated Gemfile content',
|
||||
config: {
|
||||
...config,
|
||||
constraints: {
|
||||
bundler: '1.2',
|
||||
},
|
||||
},
|
||||
}),
|
||||
).toEqual([updatedGemfileLock]);
|
||||
expect(execSnapshots).toMatchObject([
|
||||
{ cmd: 'docker pull ghcr.io/containerbase/sidecar' },
|
||||
{ cmd: 'docker ps --filter name=renovate_sidecar -aq' },
|
||||
{
|
||||
cmd:
|
||||
'docker run --rm --name=renovate_sidecar --label=renovate_child ' +
|
||||
'-v "/tmp/github/some/repo":"/tmp/github/some/repo" ' +
|
||||
'-v "/tmp/cache":"/tmp/cache" ' +
|
||||
'-e GEM_HOME ' +
|
||||
'-e CONTAINERBASE_CACHE_DIR ' +
|
||||
'-w "/tmp/github/some/repo" ' +
|
||||
'ghcr.io/containerbase/sidecar' +
|
||||
' bash -l -c "' +
|
||||
'install-tool ruby 1.2.0' +
|
||||
' && ' +
|
||||
'install-tool bundler 1.2' +
|
||||
' && ' +
|
||||
'ruby --version' +
|
||||
' && ' +
|
||||
'bundler config --local gems-private.com some-user:some-password' +
|
||||
' && ' +
|
||||
'bundler lock --update foo bar' +
|
||||
'"',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('injects bundler host configuration as command with bundler >= 2', async () => {
|
||||
GlobalConfig.set({ ...adminConfig, binarySource: 'docker' });
|
||||
fs.readLocalFile.mockResolvedValueOnce('Current Gemfile.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce('1.2.0');
|
||||
// ruby
|
||||
datasource.getPkgReleases.mockResolvedValueOnce({
|
||||
releases: [
|
||||
{ version: '1.0.0' },
|
||||
{ version: '1.2.0' },
|
||||
{ version: '1.3.0' },
|
||||
],
|
||||
});
|
||||
bundlerHostRules.findAllAuthenticatable.mockReturnValue([
|
||||
{
|
||||
hostType: 'bundler',
|
||||
matchHost: 'gems-private.com',
|
||||
resolvedHost: 'gems-private.com',
|
||||
username: 'some-user',
|
||||
password: 'some-password',
|
||||
},
|
||||
]);
|
||||
bundlerHostRules.getAuthenticationHeaderValue.mockReturnValue(
|
||||
'some-user:some-password',
|
||||
);
|
||||
const execSnapshots = mockExecAll();
|
||||
git.getRepoStatus.mockResolvedValueOnce(
|
||||
partial<StatusResult>({
|
||||
modified: ['Gemfile.lock'],
|
||||
}),
|
||||
);
|
||||
fs.readLocalFile.mockResolvedValueOnce('Updated Gemfile.lock');
|
||||
expect(
|
||||
await updateArtifacts({
|
||||
packageFileName: 'Gemfile',
|
||||
updatedDeps: [{ depName: 'foo' }, { depName: 'bar' }],
|
||||
newPackageFileContent: 'Updated Gemfile content',
|
||||
config: {
|
||||
...config,
|
||||
constraints: {
|
||||
bundler: '2.1',
|
||||
},
|
||||
},
|
||||
}),
|
||||
).toEqual([updatedGemfileLock]);
|
||||
expect(execSnapshots).toMatchObject([
|
||||
{ cmd: 'docker pull ghcr.io/containerbase/sidecar' },
|
||||
{ cmd: 'docker ps --filter name=renovate_sidecar -aq' },
|
||||
{
|
||||
cmd:
|
||||
'docker run --rm --name=renovate_sidecar --label=renovate_child ' +
|
||||
'-v "/tmp/github/some/repo":"/tmp/github/some/repo" ' +
|
||||
'-v "/tmp/cache":"/tmp/cache" ' +
|
||||
'-e GEM_HOME ' +
|
||||
'-e CONTAINERBASE_CACHE_DIR ' +
|
||||
'-w "/tmp/github/some/repo" ' +
|
||||
'ghcr.io/containerbase/sidecar' +
|
||||
' bash -l -c "' +
|
||||
'install-tool ruby 1.2.0' +
|
||||
' && ' +
|
||||
'install-tool bundler 2.1' +
|
||||
' && ' +
|
||||
'ruby --version' +
|
||||
' && ' +
|
||||
'bundler config set --local gems-private.com some-user:some-password' +
|
||||
' && ' +
|
||||
'bundler lock --update foo bar' +
|
||||
'"',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('injects bundler host configuration as command with bundler == latest', async () => {
|
||||
GlobalConfig.set({ ...adminConfig, binarySource: 'docker' });
|
||||
fs.readLocalFile.mockResolvedValueOnce('Current Gemfile.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce('1.2.0');
|
||||
// ruby
|
||||
datasource.getPkgReleases.mockResolvedValueOnce({
|
||||
releases: [
|
||||
{ version: '1.0.0' },
|
||||
{ version: '1.2.0' },
|
||||
{ version: '1.3.0' },
|
||||
],
|
||||
});
|
||||
// bundler
|
||||
datasource.getPkgReleases.mockResolvedValueOnce({
|
||||
releases: [{ version: '1.17.2' }, { version: '2.3.5' }],
|
||||
});
|
||||
bundlerHostRules.findAllAuthenticatable.mockReturnValue([
|
||||
{
|
||||
hostType: 'bundler',
|
||||
matchHost: 'gems-private.com',
|
||||
resolvedHost: 'gems-private.com',
|
||||
username: 'some-user',
|
||||
password: 'some-password',
|
||||
},
|
||||
]);
|
||||
bundlerHostRules.getAuthenticationHeaderValue.mockReturnValue(
|
||||
'some-user:some-password',
|
||||
);
|
||||
const execSnapshots = mockExecAll();
|
||||
git.getRepoStatus.mockResolvedValueOnce(
|
||||
partial<StatusResult>({
|
||||
modified: ['Gemfile.lock'],
|
||||
}),
|
||||
);
|
||||
fs.readLocalFile.mockResolvedValueOnce('Updated Gemfile.lock');
|
||||
expect(
|
||||
await updateArtifacts({
|
||||
packageFileName: 'Gemfile',
|
||||
updatedDeps: [{ depName: 'foo' }, { depName: 'bar' }],
|
||||
newPackageFileContent: 'Updated Gemfile content',
|
||||
config,
|
||||
}),
|
||||
).toEqual([updatedGemfileLock]);
|
||||
expect(execSnapshots).toMatchObject([
|
||||
{ cmd: 'docker pull ghcr.io/containerbase/sidecar' },
|
||||
{ cmd: 'docker ps --filter name=renovate_sidecar -aq' },
|
||||
{
|
||||
cmd:
|
||||
'docker run --rm --name=renovate_sidecar --label=renovate_child ' +
|
||||
'-v "/tmp/github/some/repo":"/tmp/github/some/repo" ' +
|
||||
'-v "/tmp/cache":"/tmp/cache" ' +
|
||||
'-e GEM_HOME ' +
|
||||
'-e CONTAINERBASE_CACHE_DIR ' +
|
||||
'-w "/tmp/github/some/repo" ' +
|
||||
'ghcr.io/containerbase/sidecar' +
|
||||
' bash -l -c "' +
|
||||
'install-tool ruby 1.2.0' +
|
||||
' && ' +
|
||||
'install-tool bundler 1.3.0' +
|
||||
' && ' +
|
||||
'ruby --version' +
|
||||
' && ' +
|
||||
'bundler config set --local gems-private.com some-user:some-password' +
|
||||
' && ' +
|
||||
'bundler lock --update foo bar' +
|
||||
'"',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns error when failing in lockFileMaintenance true', async () => {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { lt } from '@renovatebot/ruby-semver';
|
||||
import is from '@sindresorhus/is';
|
||||
import { quote } from 'shlex';
|
||||
import {
|
||||
|
@ -17,7 +16,6 @@ import {
|
|||
} from '../../../util/fs';
|
||||
import { getRepoStatus } from '../../../util/git';
|
||||
import { newlineRegex, regEx } from '../../../util/regex';
|
||||
import { isValid } from '../../versioning/ruby';
|
||||
import type { UpdateArtifact, UpdateArtifactsResult } from '../types';
|
||||
import {
|
||||
getBundlerConstraint,
|
||||
|
@ -32,14 +30,17 @@ import {
|
|||
const hostConfigVariablePrefix = 'BUNDLE_';
|
||||
|
||||
function buildBundleHostVariable(hostRule: HostRule): Record<string, string> {
|
||||
if (!hostRule.resolvedHost || hostRule.resolvedHost.includes('-')) {
|
||||
// istanbul ignore if: doesn't happen in practice
|
||||
if (!hostRule.resolvedHost) {
|
||||
return {};
|
||||
}
|
||||
const varName = hostConfigVariablePrefix.concat(
|
||||
hostRule.resolvedHost
|
||||
.toUpperCase()
|
||||
.split('.')
|
||||
.map((term) => term.toUpperCase())
|
||||
.join('__'),
|
||||
.join('__')
|
||||
.split('-')
|
||||
.join('___'),
|
||||
);
|
||||
return {
|
||||
[varName]: `${getAuthenticationHeaderValue(hostRule)}`,
|
||||
|
@ -149,47 +150,12 @@ export async function updateArtifacts(
|
|||
{} as Record<string, string>,
|
||||
);
|
||||
|
||||
// Detect hosts with a hyphen '-' in the url.
|
||||
// Those cannot be added with environment variables but need to be added
|
||||
// with the bundler config
|
||||
const bundlerHostRulesAuthCommands: string[] = bundlerHostRules.reduce(
|
||||
(authCommands: string[], hostRule) => {
|
||||
if (hostRule.resolvedHost?.includes('-')) {
|
||||
// TODO: fix me, hostrules can missing all auth
|
||||
const creds = getAuthenticationHeaderValue(hostRule);
|
||||
authCommands.push(`${quote(hostRule.resolvedHost)} ${quote(creds)}`);
|
||||
}
|
||||
return authCommands;
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const bundler = getBundlerConstraint(
|
||||
updateArtifact,
|
||||
existingLockFileContent,
|
||||
);
|
||||
const preCommands = ['ruby --version'];
|
||||
|
||||
// Bundler < 2 has a different config option syntax than >= 2
|
||||
if (
|
||||
bundlerHostRulesAuthCommands &&
|
||||
bundler &&
|
||||
isValid(bundler) &&
|
||||
lt(bundler, '2')
|
||||
) {
|
||||
preCommands.push(
|
||||
...bundlerHostRulesAuthCommands.map(
|
||||
(authCommand) => `bundler config --local ${authCommand}`,
|
||||
),
|
||||
);
|
||||
} else if (bundlerHostRulesAuthCommands) {
|
||||
preCommands.push(
|
||||
...bundlerHostRulesAuthCommands.map(
|
||||
(authCommand) => `bundler config set --local ${authCommand}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const execOptions: ExecOptions = {
|
||||
cwdFile: lockFileName,
|
||||
userConfiguredEnv: config.env,
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<Folder Include="wwwroot\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Version="1.2.3" />
|
||||
<PackageReference Include="Autofac" Version="4.5.0" />
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="1.1.2" />
|
||||
|
|
|
@ -14,6 +14,43 @@ exports[`modules/manager/nuget/extract extractPackageFile() extracts all depende
|
|||
"depName": "Microsoft.VisualStudio.Web.CodeGeneration.Tools",
|
||||
"depType": "nuget",
|
||||
},
|
||||
{
|
||||
"currentValue": "undefined",
|
||||
"datasource": "nuget",
|
||||
"depName": "NotUpdatable3",
|
||||
"depType": "nuget",
|
||||
"skipReason": "invalid-version",
|
||||
},
|
||||
{
|
||||
"datasource": "nuget",
|
||||
"depName": "NotUpdatable3",
|
||||
"depType": "nuget",
|
||||
"skipReason": "invalid-version",
|
||||
},
|
||||
{
|
||||
"datasource": "nuget",
|
||||
"depName": "NotUpdatable3",
|
||||
"depType": "nuget",
|
||||
"skipReason": "invalid-version",
|
||||
},
|
||||
{
|
||||
"datasource": "nuget",
|
||||
"depName": "NotUpdatable3",
|
||||
"depType": "nuget",
|
||||
"skipReason": "invalid-version",
|
||||
},
|
||||
{
|
||||
"datasource": "nuget",
|
||||
"depName": "NotUpdatable2",
|
||||
"depType": "nuget",
|
||||
"skipReason": "invalid-version",
|
||||
},
|
||||
{
|
||||
"datasource": "nuget",
|
||||
"depName": "NotUpdatable1",
|
||||
"depType": "nuget",
|
||||
"skipReason": "invalid-version",
|
||||
},
|
||||
{
|
||||
"currentValue": "1.2.3",
|
||||
"datasource": "nuget",
|
||||
|
@ -115,6 +152,36 @@ exports[`modules/manager/nuget/extract extractPackageFile() extracts all depende
|
|||
"depName": "Microsoft.VisualStudio.Web.CodeGeneration.Tools",
|
||||
"depType": "nuget",
|
||||
},
|
||||
{
|
||||
"datasource": "nuget",
|
||||
"depName": "NotUpdatable3",
|
||||
"depType": "nuget",
|
||||
"skipReason": "invalid-version",
|
||||
},
|
||||
{
|
||||
"datasource": "nuget",
|
||||
"depName": "NotUpdatable3",
|
||||
"depType": "nuget",
|
||||
"skipReason": "invalid-version",
|
||||
},
|
||||
{
|
||||
"datasource": "nuget",
|
||||
"depName": "NotUpdatable3",
|
||||
"depType": "nuget",
|
||||
"skipReason": "invalid-version",
|
||||
},
|
||||
{
|
||||
"datasource": "nuget",
|
||||
"depName": "NotUpdatable2",
|
||||
"depType": "nuget",
|
||||
"skipReason": "invalid-version",
|
||||
},
|
||||
{
|
||||
"datasource": "nuget",
|
||||
"depName": "NotUpdatable1",
|
||||
"depType": "nuget",
|
||||
"skipReason": "invalid-version",
|
||||
},
|
||||
{
|
||||
"currentValue": "1.2.3",
|
||||
"datasource": "nuget",
|
||||
|
|
|
@ -57,7 +57,7 @@ describe('modules/manager/nuget/extract', () => {
|
|||
const sample = Fixtures.get(packageFile);
|
||||
const res = await extractPackageFile(sample, packageFile, config);
|
||||
expect(res?.deps).toMatchSnapshot();
|
||||
expect(res?.deps).toHaveLength(17);
|
||||
expect(res?.deps).toHaveLength(23);
|
||||
});
|
||||
|
||||
it('extracts msbuild sdk from the Sdk attr of Project element', async () => {
|
||||
|
@ -157,7 +157,7 @@ describe('modules/manager/nuget/extract', () => {
|
|||
const sample = Fixtures.get(packageFile);
|
||||
const res = await extractPackageFile(sample, packageFile, config);
|
||||
expect(res?.deps).toMatchSnapshot();
|
||||
expect(res?.deps).toHaveLength(17);
|
||||
expect(res?.deps).toHaveLength(22);
|
||||
});
|
||||
|
||||
it('extracts ContainerBaseImage', async () => {
|
||||
|
|
|
@ -28,7 +28,7 @@ import { applyRegistries, findVersion, getConfiguredRegistries } from './util';
|
|||
* so we don't include it in the extracting regexp
|
||||
*/
|
||||
const checkVersion = regEx(
|
||||
`^\\s*(?:[[])?(?:(?<currentValue>[^"(,[\\]]+)\\s*(?:,\\s*[)\\]]|])?)\\s*$`,
|
||||
/^\s*(?:[[])?(?:(?<currentValue>[^"(,[\]]+)\s*(?:,\s*[)\]]|])?)\s*$/,
|
||||
);
|
||||
const elemNames = new Set([
|
||||
'PackageReference',
|
||||
|
@ -58,23 +58,38 @@ function extractDepsFromXml(xmlNode: XmlDocument): NugetPackageDependency[] {
|
|||
|
||||
if (elemNames.has(name)) {
|
||||
const depName = attr?.Include || attr?.Update;
|
||||
const version =
|
||||
if (!depName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const dep: NugetPackageDependency = {
|
||||
datasource: NugetDatasource.id,
|
||||
depType: 'nuget',
|
||||
depName,
|
||||
};
|
||||
|
||||
let currentValue: string | undefined =
|
||||
attr?.Version ??
|
||||
attr?.version ??
|
||||
child.valueWithPath('Version') ??
|
||||
attr?.VersionOverride ??
|
||||
child.valueWithPath('VersionOverride');
|
||||
const currentValue = is.nonEmptyStringAndNotWhitespace(version)
|
||||
? checkVersion.exec(version)?.groups?.currentValue?.trim()
|
||||
: undefined;
|
||||
if (depName && currentValue) {
|
||||
results.push({
|
||||
datasource: NugetDatasource.id,
|
||||
depType: 'nuget',
|
||||
depName,
|
||||
currentValue,
|
||||
});
|
||||
|
||||
if (!is.nonEmptyStringAndNotWhitespace(currentValue)) {
|
||||
dep.skipReason = 'invalid-version';
|
||||
}
|
||||
|
||||
currentValue = checkVersion
|
||||
.exec(currentValue)
|
||||
?.groups?.currentValue?.trim();
|
||||
|
||||
if (currentValue) {
|
||||
dep.currentValue = currentValue;
|
||||
} else {
|
||||
dep.skipReason = 'invalid-version';
|
||||
}
|
||||
|
||||
results.push(dep);
|
||||
} else if (name === 'Sdk') {
|
||||
const depName = attr?.Name;
|
||||
const version = attr?.Version;
|
||||
|
|
|
@ -16,6 +16,10 @@ url = "last.url"
|
|||
[[tool.poetry.source]]
|
||||
name = "five"
|
||||
|
||||
[[tool.poetry.source]]
|
||||
name = "invalid-url"
|
||||
url = "invalid-url"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry_core>=1.0", "wheel"]
|
||||
build-backend = "poetry.masonry.api"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { codeBlock } from 'common-tags';
|
||||
import { GoogleAuth as _googleAuth } from 'google-auth-library';
|
||||
import { mockDeep } from 'jest-mock-extended';
|
||||
import { join } from 'upath';
|
||||
import { envMock, mockExecAll } from '../../../../test/exec-util';
|
||||
|
@ -15,16 +16,26 @@ import { updateArtifacts } from '.';
|
|||
|
||||
const pyproject1toml = Fixtures.get('pyproject.1.toml');
|
||||
const pyproject10toml = Fixtures.get('pyproject.10.toml');
|
||||
const pyproject13toml = `[[tool.poetry.source]]
|
||||
name = "some-gar-repo"
|
||||
url = "https://someregion-python.pkg.dev/some-project/some-repo/simple/"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry_core>=1.0", "wheel"]
|
||||
build-backend = "poetry.masonry.api"
|
||||
`;
|
||||
|
||||
jest.mock('../../../util/exec/env');
|
||||
jest.mock('../../../util/fs');
|
||||
jest.mock('../../datasource', () => mockDeep());
|
||||
jest.mock('../../../util/host-rules', () => mockDeep());
|
||||
jest.mock('google-auth-library');
|
||||
|
||||
process.env.CONTAINERBASE = 'true';
|
||||
|
||||
const datasource = mocked(_datasource);
|
||||
const hostRules = mocked(_hostRules);
|
||||
const googleAuth = mocked(_googleAuth);
|
||||
|
||||
const adminConfig: RepoGlobalConfig = {
|
||||
localDir: join('/tmp/github/some/repo'),
|
||||
|
@ -198,7 +209,99 @@ describe('modules/manager/poetry/artifacts', () => {
|
|||
},
|
||||
},
|
||||
]);
|
||||
expect(hostRules.find.mock.calls).toHaveLength(5);
|
||||
expect(hostRules.find.mock.calls).toHaveLength(7);
|
||||
expect(execSnapshots).toMatchObject([
|
||||
{
|
||||
cmd: 'poetry update --lock --no-interaction dep1',
|
||||
options: {
|
||||
env: {
|
||||
POETRY_HTTP_BASIC_ONE_PASSWORD: 'passwordOne',
|
||||
POETRY_HTTP_BASIC_ONE_USERNAME: 'usernameOne',
|
||||
POETRY_HTTP_BASIC_TWO_USERNAME: 'usernameTwo',
|
||||
POETRY_HTTP_BASIC_FOUR_OH_FOUR_PASSWORD: 'passwordFour',
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('passes Google Artifact Registry credentials environment vars', async () => {
|
||||
// poetry.lock
|
||||
fs.getSiblingFileName.mockReturnValueOnce('poetry.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce(null);
|
||||
// pyproject.lock
|
||||
fs.getSiblingFileName.mockReturnValueOnce('pyproject.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce('[metadata]\n');
|
||||
const execSnapshots = mockExecAll();
|
||||
fs.readLocalFile.mockResolvedValueOnce('New poetry.lock');
|
||||
googleAuth.mockImplementationOnce(
|
||||
jest.fn().mockImplementationOnce(() => ({
|
||||
getAccessToken: jest.fn().mockResolvedValue('some-token'),
|
||||
})),
|
||||
);
|
||||
const updatedDeps = [{ depName: 'dep1' }];
|
||||
expect(
|
||||
await updateArtifacts({
|
||||
packageFileName: 'pyproject.toml',
|
||||
updatedDeps,
|
||||
newPackageFileContent: pyproject13toml,
|
||||
config,
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
file: {
|
||||
type: 'addition',
|
||||
path: 'pyproject.lock',
|
||||
contents: 'New poetry.lock',
|
||||
},
|
||||
},
|
||||
]);
|
||||
expect(hostRules.find.mock.calls).toHaveLength(3);
|
||||
expect(execSnapshots).toMatchObject([
|
||||
{
|
||||
cmd: 'poetry update --lock --no-interaction dep1',
|
||||
options: {
|
||||
env: {
|
||||
POETRY_HTTP_BASIC_SOME_GAR_REPO_USERNAME: 'oauth2accesstoken',
|
||||
POETRY_HTTP_BASIC_SOME_GAR_REPO_PASSWORD: 'some-token',
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('continues if Google auth is not configured', async () => {
|
||||
// poetry.lock
|
||||
fs.getSiblingFileName.mockReturnValueOnce('poetry.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce(null);
|
||||
// pyproject.lock
|
||||
fs.getSiblingFileName.mockReturnValueOnce('pyproject.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce('[metadata]\n');
|
||||
const execSnapshots = mockExecAll();
|
||||
fs.readLocalFile.mockResolvedValueOnce('New poetry.lock');
|
||||
googleAuth.mockImplementation(
|
||||
jest.fn().mockImplementation(() => ({
|
||||
getAccessToken: jest.fn().mockResolvedValue(undefined),
|
||||
})),
|
||||
);
|
||||
const updatedDeps = [{ depName: 'dep1' }];
|
||||
expect(
|
||||
await updateArtifacts({
|
||||
packageFileName: 'pyproject.toml',
|
||||
updatedDeps,
|
||||
newPackageFileContent: pyproject13toml,
|
||||
config,
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
file: {
|
||||
type: 'addition',
|
||||
path: 'pyproject.lock',
|
||||
contents: 'New poetry.lock',
|
||||
},
|
||||
},
|
||||
]);
|
||||
expect(hostRules.find.mock.calls).toHaveLength(3);
|
||||
expect(execSnapshots).toMatchObject([
|
||||
{ cmd: 'poetry update --lock --no-interaction dep1' },
|
||||
]);
|
||||
|
|
|
@ -17,7 +17,9 @@ import { find } from '../../../util/host-rules';
|
|||
import { regEx } from '../../../util/regex';
|
||||
import { Result } from '../../../util/result';
|
||||
import { parse as parseToml } from '../../../util/toml';
|
||||
import { parseUrl } from '../../../util/url';
|
||||
import { PypiDatasource } from '../../datasource/pypi';
|
||||
import { getGoogleAuthTokenRaw } from '../../datasource/util';
|
||||
import type { UpdateArtifact, UpdateArtifactsResult } from '../types';
|
||||
import { Lockfile, PoetrySchemaToml } from './schema';
|
||||
import type { PoetryFile, PoetrySource } from './types';
|
||||
|
@ -101,7 +103,7 @@ function getPoetrySources(content: string, fileName: string): PoetrySource[] {
|
|||
return [];
|
||||
}
|
||||
if (!pyprojectFile.tool?.poetry) {
|
||||
logger.debug(`{$fileName} contains no poetry section`);
|
||||
logger.debug(`${fileName} contains no poetry section`);
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -115,20 +117,42 @@ function getPoetrySources(content: string, fileName: string): PoetrySource[] {
|
|||
return sourceArray;
|
||||
}
|
||||
|
||||
function getMatchingHostRule(url: string | undefined): HostRule {
|
||||
async function getMatchingHostRule(url: string | undefined): Promise<HostRule> {
|
||||
const scopedMatch = find({ hostType: PypiDatasource.id, url });
|
||||
return is.nonEmptyObject(scopedMatch) ? scopedMatch : find({ url });
|
||||
const hostRule = is.nonEmptyObject(scopedMatch) ? scopedMatch : find({ url });
|
||||
if (hostRule) {
|
||||
return hostRule;
|
||||
}
|
||||
|
||||
function getSourceCredentialVars(
|
||||
const parsedUrl = parseUrl(url);
|
||||
if (!parsedUrl) {
|
||||
logger.once.debug(`Failed to parse URL ${url}`);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (parsedUrl.hostname.endsWith('.pkg.dev')) {
|
||||
const accessToken = await getGoogleAuthTokenRaw();
|
||||
if (accessToken) {
|
||||
return {
|
||||
username: 'oauth2accesstoken',
|
||||
password: accessToken,
|
||||
};
|
||||
}
|
||||
logger.once.debug(`Could not get Google access token (url=${url})`);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
async function getSourceCredentialVars(
|
||||
pyprojectContent: string,
|
||||
packageFileName: string,
|
||||
): NodeJS.ProcessEnv {
|
||||
): Promise<NodeJS.ProcessEnv> {
|
||||
const poetrySources = getPoetrySources(pyprojectContent, packageFileName);
|
||||
const envVars: NodeJS.ProcessEnv = {};
|
||||
|
||||
for (const source of poetrySources) {
|
||||
const matchingHostRule = getMatchingHostRule(source.url);
|
||||
const matchingHostRule = await getMatchingHostRule(source.url);
|
||||
const formattedSourceName = source.name
|
||||
.replace(regEx(/(\.|-)+/g), '_')
|
||||
.toUpperCase();
|
||||
|
@ -192,7 +216,10 @@ export async function updateArtifacts({
|
|||
config.constraints?.poetry ??
|
||||
getPoetryRequirement(newPackageFileContent, existingLockFileContent);
|
||||
const extraEnv = {
|
||||
...getSourceCredentialVars(newPackageFileContent, packageFileName),
|
||||
...(await getSourceCredentialVars(
|
||||
newPackageFileContent,
|
||||
packageFileName,
|
||||
)),
|
||||
...getGitEnvironmentVariables(['poetry']),
|
||||
PIP_CACHE_DIR: await ensureCacheDir('pip'),
|
||||
};
|
||||
|
|
|
@ -13,11 +13,18 @@ repos:
|
|||
rev: 19.3b0
|
||||
hooks:
|
||||
- id: black
|
||||
language: python
|
||||
additional_dependencies:
|
||||
- "request==1.1.1"
|
||||
- "" # broken pypi package
|
||||
- repo: https://gitlab.com/psf/black
|
||||
# should also detect gitlab
|
||||
rev: 19.3b0
|
||||
hooks:
|
||||
- id: black
|
||||
# missing language, not extracted
|
||||
additional_dependencies:
|
||||
- "urllib==24.9.0"
|
||||
- repo: http://gitlab.com/psf/black
|
||||
# should also detect http
|
||||
rev: 19.3b0
|
||||
|
@ -48,3 +55,7 @@ repos:
|
|||
- repo: some_invalid_url
|
||||
# case with invlalid url.
|
||||
rev: v1.0.0
|
||||
|
||||
# pre-commit meta hooks
|
||||
- repo: meta
|
||||
hooks: []
|
||||
|
|
|
@ -10,6 +10,14 @@ exports[`modules/manager/pre-commit/extract extractPackageFile() extracts from c
|
|||
"depType": "repository",
|
||||
"packageName": "pre-commit/pre-commit-hooks",
|
||||
},
|
||||
{
|
||||
"currentValue": "==1.1.1",
|
||||
"currentVersion": "1.1.1",
|
||||
"datasource": "pypi",
|
||||
"depName": "request",
|
||||
"depType": "pre-commit-python",
|
||||
"packageName": "request",
|
||||
},
|
||||
{
|
||||
"currentValue": "19.3b0",
|
||||
"datasource": "github-tags",
|
||||
|
|
|
@ -2,6 +2,7 @@ import { mockDeep } from 'jest-mock-extended';
|
|||
import { Fixtures } from '../../../../test/fixtures';
|
||||
import { mocked } from '../../../../test/util';
|
||||
import * as _hostRules from '../../../util/host-rules';
|
||||
import { PypiDatasource } from '../../datasource/pypi';
|
||||
import { extractPackageFile } from '.';
|
||||
|
||||
jest.mock('../../../util/host-rules', () => mockDeep());
|
||||
|
@ -81,6 +82,14 @@ describe('modules/manager/pre-commit/extract', () => {
|
|||
expect(result).toMatchSnapshot({
|
||||
deps: [
|
||||
{ depName: 'pre-commit/pre-commit-hooks', currentValue: 'v3.3.0' },
|
||||
{
|
||||
currentValue: '==1.1.1',
|
||||
currentVersion: '1.1.1',
|
||||
datasource: PypiDatasource.id,
|
||||
depName: 'request',
|
||||
depType: 'pre-commit-python',
|
||||
packageName: 'request',
|
||||
},
|
||||
{ depName: 'psf/black', currentValue: '19.3b0' },
|
||||
{ depName: 'psf/black', currentValue: '19.3b0' },
|
||||
{ depName: 'psf/black', currentValue: '19.3b0' },
|
||||
|
|
|
@ -7,6 +7,7 @@ import { regEx } from '../../../util/regex';
|
|||
import { parseSingleYaml } from '../../../util/yaml';
|
||||
import { GithubTagsDatasource } from '../../datasource/github-tags';
|
||||
import { GitlabTagsDatasource } from '../../datasource/gitlab-tags';
|
||||
import { pep508ToPackageDependency } from '../pep621/utils';
|
||||
import type { PackageDependency, PackageFileContent } from '../types';
|
||||
import {
|
||||
matchesPrecommitConfigHeuristic,
|
||||
|
@ -137,6 +138,23 @@ function findDependencies(precommitFile: PreCommitConfig): PackageDependency[] {
|
|||
}
|
||||
const packageDependencies: PackageDependency[] = [];
|
||||
precommitFile.repos.forEach((item) => {
|
||||
// meta hooks is defined from pre-commit and doesn't support `additional_dependencies`
|
||||
if (item.repo !== 'meta') {
|
||||
item.hooks?.forEach((hook) => {
|
||||
// normally language are not defined in yaml
|
||||
// only support it when it's explicitly defined.
|
||||
// this avoid to parse hooks from pre-commit-hooks.yaml from git repo
|
||||
if (hook.language === 'python') {
|
||||
hook.additional_dependencies?.map((req) => {
|
||||
const dep = pep508ToPackageDependency('pre-commit-python', req);
|
||||
if (dep) {
|
||||
packageDependencies.push(dep);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (matchesPrecommitDependencyHeuristic(item)) {
|
||||
logger.trace(item, 'Matched pre-commit dependency spec');
|
||||
const repository = String(item.repo);
|
||||
|
|
|
@ -26,3 +26,33 @@ To enable the `pre-commit` manager, add the following config:
|
|||
```
|
||||
|
||||
Alternatively, add `:enablePreCommit` to your `extends` array.
|
||||
|
||||
### Additional Dependencies
|
||||
|
||||
renovate has partial support for `additional_dependencies`, currently python only.
|
||||
|
||||
for python hooks, you will need to **explicitly add language** to your hooks with `additional_dependencies`
|
||||
to let renovatebot know what kind of dependencies they are.
|
||||
|
||||
For example, this work for `request`:
|
||||
|
||||
```yaml
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 19.3b0
|
||||
hooks:
|
||||
- id: black
|
||||
language: python
|
||||
additional_dependencies:
|
||||
- 'request==1.1.1'
|
||||
```
|
||||
|
||||
this won't work:
|
||||
|
||||
```yaml
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 19.3b0
|
||||
hooks:
|
||||
- id: black
|
||||
additional_dependencies:
|
||||
- 'request==1.1.1'
|
||||
```
|
||||
|
|
|
@ -2,7 +2,13 @@ export interface PreCommitConfig {
|
|||
repos: PreCommitDependency[];
|
||||
}
|
||||
|
||||
export interface PreCommitHook {
|
||||
language?: string;
|
||||
additional_dependencies?: Array<string>;
|
||||
}
|
||||
|
||||
export interface PreCommitDependency {
|
||||
repo: string;
|
||||
hooks?: Array<PreCommitHook>;
|
||||
rev: string;
|
||||
}
|
||||
|
|
|
@ -1531,6 +1531,57 @@ describe('modules/platform/github/index', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('getIssue()', () => {
|
||||
it('returns null if issues disabled', async () => {
|
||||
const scope = httpMock.scope(githubApiHost);
|
||||
initRepoMock(scope, 'some/repo', { hasIssuesEnabled: false });
|
||||
await github.initRepo({ repository: 'some/repo' });
|
||||
const res = await github.getIssue(1);
|
||||
expect(res).toBeNull();
|
||||
});
|
||||
|
||||
it('returns issue', async () => {
|
||||
const scope = httpMock.scope(githubApiHost);
|
||||
initRepoMock(scope, 'some/repo');
|
||||
const issue = {
|
||||
number: 1,
|
||||
state: 'open',
|
||||
title: 'title-1',
|
||||
body: 'body-1',
|
||||
};
|
||||
scope
|
||||
.get('/repos/some/repo/issues/1')
|
||||
.reply(200, { ...issue, updated_at: '2022-01-01T00:00:00Z' });
|
||||
await github.initRepo({ repository: 'some/repo' });
|
||||
const res = await github.getIssue(1);
|
||||
expect(res).toMatchObject({
|
||||
...issue,
|
||||
lastModified: '2022-01-01T00:00:00Z',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns null if issue not found', async () => {
|
||||
const scope = httpMock.scope(githubApiHost);
|
||||
initRepoMock(scope, 'some/repo');
|
||||
scope.get('/repos/some/repo/issues/1').reply(404);
|
||||
await github.initRepo({ repository: 'some/repo' });
|
||||
const res = await github.getIssue(1);
|
||||
expect(res).toBeNull();
|
||||
});
|
||||
|
||||
it('logs debug message if issue deleted', async () => {
|
||||
const scope = httpMock.scope(githubApiHost);
|
||||
initRepoMock(scope, 'some/repo');
|
||||
scope.get('/repos/some/repo/issues/1').reply(410);
|
||||
await github.initRepo({ repository: 'some/repo' });
|
||||
const res = await github.getIssue(1);
|
||||
expect(res).toBeNull();
|
||||
expect(logger.logger.debug).toHaveBeenCalledWith(
|
||||
'Issue #1 has been deleted',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('findIssue()', () => {
|
||||
it('returns null if no issue', async () => {
|
||||
httpMock
|
||||
|
|
|
@ -1231,7 +1231,6 @@ export async function getIssueList(): Promise<Issue[]> {
|
|||
}
|
||||
|
||||
export async function getIssue(number: number): Promise<Issue | null> {
|
||||
// istanbul ignore if
|
||||
if (config.hasIssuesEnabled === false) {
|
||||
return null;
|
||||
}
|
||||
|
@ -1246,8 +1245,12 @@ export async function getIssue(number: number): Promise<Issue | null> {
|
|||
);
|
||||
GithubIssueCache.updateIssue(issue);
|
||||
return issue;
|
||||
} catch (err) /* istanbul ignore next */ {
|
||||
} catch (err) {
|
||||
logger.debug({ err, number }, 'Error getting issue');
|
||||
if (err.response?.statusCode === 410) {
|
||||
logger.debug(`Issue #${number} has been deleted`);
|
||||
GithubIssueCache.deleteIssue(number);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -159,6 +159,32 @@ describe('modules/platform/github/issue', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('removes particular issue from the cache', () => {
|
||||
cache.platform = {
|
||||
github: {
|
||||
issuesCache: {
|
||||
'1': {
|
||||
number: 1,
|
||||
body: 'body-1',
|
||||
state: 'open',
|
||||
title: 'title-1',
|
||||
lastModified: '2020-01-01T00:00:00.000Z',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
GithubIssueCache.deleteIssue(1);
|
||||
|
||||
expect(cache).toEqual({
|
||||
platform: {
|
||||
github: {
|
||||
issuesCache: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('reconciles cache', () => {
|
||||
cache.platform = {
|
||||
github: {
|
||||
|
|
|
@ -85,6 +85,13 @@ export class GithubIssueCache {
|
|||
}
|
||||
}
|
||||
|
||||
static deleteIssue(number: number): void {
|
||||
const cacheData = this.data;
|
||||
if (cacheData) {
|
||||
delete cacheData[number];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* At the moment of repo initialization, repository cache is not available.
|
||||
* What we can do is to store issues for later reconciliation.
|
||||
|
|
|
@ -1361,9 +1361,12 @@ export async function ensureComment({
|
|||
if (topic) {
|
||||
logger.debug(`Ensuring comment "${massagedTopic!}" in #${number}`);
|
||||
body = `### ${topic}\n\n${sanitizedContent}`;
|
||||
body = body
|
||||
body = smartTruncate(
|
||||
body
|
||||
.replace(regEx(/Pull Request/g), 'Merge Request')
|
||||
.replace(regEx(/PR/g), 'MR');
|
||||
.replace(regEx(/PR/g), 'MR'),
|
||||
maxBodyLength(),
|
||||
);
|
||||
comments.forEach((comment: { body: string; id: number }) => {
|
||||
if (comment.body.startsWith(`### ${massagedTopic!}\n\n`)) {
|
||||
commentId = comment.id;
|
||||
|
@ -1372,7 +1375,7 @@ export async function ensureComment({
|
|||
});
|
||||
} else {
|
||||
logger.debug(`Ensuring content-only comment in #${number}`);
|
||||
body = `${sanitizedContent}`;
|
||||
body = smartTruncate(`${sanitizedContent}`, maxBodyLength());
|
||||
comments.forEach((comment: { body: string; id: number }) => {
|
||||
if (comment.body === body) {
|
||||
commentId = comment.id;
|
||||
|
|
|
@ -4,7 +4,7 @@ import { platform } from '../../../modules/platform';
|
|||
import * as repositoryCache from '../../../util/cache/repository';
|
||||
import { clearRenovateRefs } from '../../../util/git';
|
||||
import { PackageFiles } from '../package-files';
|
||||
import { validateReconfigureBranch } from '../reconfigure';
|
||||
import { checkReconfigureBranch } from '../reconfigure';
|
||||
import { pruneStaleBranches } from './prune';
|
||||
import {
|
||||
runBranchSummary,
|
||||
|
@ -16,7 +16,7 @@ export async function finalizeRepo(
|
|||
config: RenovateConfig,
|
||||
branchList: string[],
|
||||
): Promise<void> {
|
||||
await validateReconfigureBranch(config);
|
||||
await checkReconfigureBranch(config);
|
||||
await repositoryCache.saveCache();
|
||||
await pruneStaleBranches(config, branchList);
|
||||
await ensureIssuesClosing();
|
||||
|
|
|
@ -9,7 +9,7 @@ import { scm } from '../../../modules/platform/scm';
|
|||
import { getBranchList, setUserRepoConfig } from '../../../util/git';
|
||||
import { escapeRegExp, regEx } from '../../../util/regex';
|
||||
import { uniqueStrings } from '../../../util/string';
|
||||
import { getReconfigureBranchName } from '../reconfigure';
|
||||
import { getReconfigureBranchName } from '../reconfigure/utils';
|
||||
|
||||
async function cleanUpBranches(
|
||||
config: RenovateConfig,
|
||||
|
|
|
@ -1,242 +1,42 @@
|
|||
import { mock } from 'jest-mock-extended';
|
||||
import type { RenovateConfig } from '../../../../test/util';
|
||||
import { fs, git, mocked, partial, platform, scm } from '../../../../test/util';
|
||||
import { logger, mocked, scm } from '../../../../test/util';
|
||||
import { GlobalConfig } from '../../../config/global';
|
||||
import { logger } from '../../../logger';
|
||||
import type { Pr } from '../../../modules/platform/types';
|
||||
import * as _cache from '../../../util/cache/repository';
|
||||
import type { LongCommitSha } from '../../../util/git/types';
|
||||
import * as _merge from '../init/merge';
|
||||
import { validateReconfigureBranch } from '.';
|
||||
import * as _validate from './validate';
|
||||
import { checkReconfigureBranch } from '.';
|
||||
|
||||
jest.mock('../../../util/cache/repository');
|
||||
jest.mock('../../../util/fs');
|
||||
jest.mock('../../../util/git');
|
||||
jest.mock('../init/merge');
|
||||
jest.mock('./validate');
|
||||
|
||||
const cache = mocked(_cache);
|
||||
const merge = mocked(_merge);
|
||||
const validate = mocked(_validate);
|
||||
|
||||
describe('workers/repository/reconfigure/index', () => {
|
||||
const config: RenovateConfig = {
|
||||
branchPrefix: 'prefix/',
|
||||
baseBranch: 'base',
|
||||
statusCheckNames: partial<RenovateConfig['statusCheckNames']>({
|
||||
configValidation: 'renovate/config-validation',
|
||||
}),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
config.repository = 'some/repo';
|
||||
merge.detectConfigFile.mockResolvedValue('renovate.json');
|
||||
scm.branchExists.mockResolvedValue(true);
|
||||
cache.getCache.mockReturnValue({});
|
||||
git.getBranchCommit.mockReturnValue('sha' as LongCommitSha);
|
||||
fs.readLocalFile.mockResolvedValue(null);
|
||||
platform.getBranchStatusCheck.mockResolvedValue(null);
|
||||
GlobalConfig.reset();
|
||||
scm.branchExists.mockResolvedValue(true);
|
||||
validate.validateReconfigureBranch.mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
it('no effect when running with platform=local', async () => {
|
||||
GlobalConfig.set({ platform: 'local' });
|
||||
await validateReconfigureBranch(config);
|
||||
expect(logger.debug).toHaveBeenCalledWith(
|
||||
await checkReconfigureBranch(config);
|
||||
expect(logger.logger.debug).toHaveBeenCalledWith(
|
||||
'Not attempting to reconfigure when running with local platform',
|
||||
);
|
||||
});
|
||||
|
||||
it('no effect on repo with no reconfigure branch', async () => {
|
||||
scm.branchExists.mockResolvedValueOnce(false);
|
||||
await validateReconfigureBranch(config);
|
||||
expect(logger.debug).toHaveBeenCalledWith('No reconfigure branch found');
|
||||
});
|
||||
|
||||
it('logs error if config file search fails', async () => {
|
||||
const err = new Error();
|
||||
merge.detectConfigFile.mockRejectedValueOnce(err as never);
|
||||
await validateReconfigureBranch(config);
|
||||
expect(logger.error).toHaveBeenCalledWith(
|
||||
{ err },
|
||||
'Error while searching for config file in reconfigure branch',
|
||||
await checkReconfigureBranch(config);
|
||||
expect(logger.logger.debug).toHaveBeenCalledWith(
|
||||
'No reconfigure branch found',
|
||||
);
|
||||
});
|
||||
|
||||
it('throws error if config file not found in reconfigure branch', async () => {
|
||||
merge.detectConfigFile.mockResolvedValue(null);
|
||||
await validateReconfigureBranch(config);
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
'No config file found in reconfigure branch',
|
||||
);
|
||||
});
|
||||
|
||||
it('logs error if config file is unreadable', async () => {
|
||||
const err = new Error();
|
||||
fs.readLocalFile.mockRejectedValueOnce(err as never);
|
||||
await validateReconfigureBranch(config);
|
||||
expect(logger.error).toHaveBeenCalledWith(
|
||||
{ err },
|
||||
'Error while reading config file',
|
||||
);
|
||||
});
|
||||
|
||||
it('throws error if config file is empty', async () => {
|
||||
await validateReconfigureBranch(config);
|
||||
expect(logger.warn).toHaveBeenCalledWith('Empty or invalid config file');
|
||||
});
|
||||
|
||||
it('throws error if config file content is invalid', async () => {
|
||||
fs.readLocalFile.mockResolvedValueOnce(`
|
||||
{
|
||||
"name":
|
||||
}
|
||||
`);
|
||||
await validateReconfigureBranch(config);
|
||||
expect(logger.error).toHaveBeenCalledWith(
|
||||
{ err: expect.any(Object) },
|
||||
'Error while parsing config file',
|
||||
);
|
||||
expect(platform.setBranchStatus).toHaveBeenCalledWith({
|
||||
branchName: 'prefix/reconfigure',
|
||||
context: 'renovate/config-validation',
|
||||
description: 'Validation Failed - Unparsable config file',
|
||||
state: 'red',
|
||||
});
|
||||
});
|
||||
|
||||
it('handles failed validation', async () => {
|
||||
fs.readLocalFile.mockResolvedValueOnce(`
|
||||
{
|
||||
"enabledManagers": ["docker"]
|
||||
}
|
||||
`);
|
||||
await validateReconfigureBranch(config);
|
||||
expect(logger.debug).toHaveBeenCalledWith(
|
||||
{ errors: expect.any(String) },
|
||||
'Validation Errors',
|
||||
);
|
||||
expect(platform.setBranchStatus).toHaveBeenCalledWith({
|
||||
branchName: 'prefix/reconfigure',
|
||||
context: 'renovate/config-validation',
|
||||
description: 'Validation Failed',
|
||||
state: 'red',
|
||||
});
|
||||
});
|
||||
|
||||
it('adds comment if reconfigure PR exists', async () => {
|
||||
fs.readLocalFile.mockResolvedValueOnce(`
|
||||
{
|
||||
"enabledManagers": ["docker"]
|
||||
}
|
||||
`);
|
||||
platform.findPr.mockResolvedValueOnce(mock<Pr>({ number: 1 }));
|
||||
await validateReconfigureBranch(config);
|
||||
expect(logger.debug).toHaveBeenCalledWith(
|
||||
{ errors: expect.any(String) },
|
||||
'Validation Errors',
|
||||
);
|
||||
expect(platform.setBranchStatus).toHaveBeenCalled();
|
||||
expect(platform.ensureComment).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('handles successful validation', async () => {
|
||||
const pJson = `
|
||||
{
|
||||
"renovate": {
|
||||
"enabledManagers": ["npm"]
|
||||
}
|
||||
}
|
||||
`;
|
||||
merge.detectConfigFile.mockResolvedValue('package.json');
|
||||
fs.readLocalFile.mockResolvedValueOnce(pJson).mockResolvedValueOnce(pJson);
|
||||
await validateReconfigureBranch(config);
|
||||
expect(platform.setBranchStatus).toHaveBeenCalledWith({
|
||||
branchName: 'prefix/reconfigure',
|
||||
context: 'renovate/config-validation',
|
||||
description: 'Validation Successful',
|
||||
state: 'green',
|
||||
});
|
||||
});
|
||||
|
||||
it('skips adding status check if statusCheckNames.configValidation is null', async () => {
|
||||
cache.getCache.mockReturnValueOnce({
|
||||
reconfigureBranchCache: {
|
||||
reconfigureBranchSha: 'new-sha',
|
||||
isConfigValid: false,
|
||||
},
|
||||
});
|
||||
|
||||
await validateReconfigureBranch({
|
||||
...config,
|
||||
statusCheckNames: partial<RenovateConfig['statusCheckNames']>({
|
||||
configValidation: null,
|
||||
}),
|
||||
});
|
||||
expect(logger.debug).toHaveBeenCalledWith(
|
||||
'Status check is null or an empty string, skipping status check addition.',
|
||||
);
|
||||
expect(platform.setBranchStatus).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('skips adding status check if statusCheckNames.configValidation is empty string', async () => {
|
||||
cache.getCache.mockReturnValueOnce({
|
||||
reconfigureBranchCache: {
|
||||
reconfigureBranchSha: 'new-sha',
|
||||
isConfigValid: false,
|
||||
},
|
||||
});
|
||||
|
||||
await validateReconfigureBranch({
|
||||
...config,
|
||||
statusCheckNames: partial<RenovateConfig['statusCheckNames']>({
|
||||
configValidation: '',
|
||||
}),
|
||||
});
|
||||
expect(logger.debug).toHaveBeenCalledWith(
|
||||
'Status check is null or an empty string, skipping status check addition.',
|
||||
);
|
||||
expect(platform.setBranchStatus).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('skips validation if cache is valid', async () => {
|
||||
cache.getCache.mockReturnValueOnce({
|
||||
reconfigureBranchCache: {
|
||||
reconfigureBranchSha: 'sha',
|
||||
isConfigValid: false,
|
||||
},
|
||||
});
|
||||
await validateReconfigureBranch(config);
|
||||
expect(logger.debug).toHaveBeenCalledWith(
|
||||
'Skipping validation check as branch sha is unchanged',
|
||||
);
|
||||
});
|
||||
|
||||
it('skips validation if status check present', async () => {
|
||||
cache.getCache.mockReturnValueOnce({
|
||||
reconfigureBranchCache: {
|
||||
reconfigureBranchSha: 'new_sha',
|
||||
isConfigValid: false,
|
||||
},
|
||||
});
|
||||
platform.getBranchStatusCheck.mockResolvedValueOnce('green');
|
||||
await validateReconfigureBranch(config);
|
||||
expect(logger.debug).toHaveBeenCalledWith(
|
||||
'Skipping validation check because status check already exists.',
|
||||
);
|
||||
});
|
||||
|
||||
it('handles non-default config file', async () => {
|
||||
merge.detectConfigFile.mockResolvedValue('.renovaterc');
|
||||
fs.readLocalFile.mockResolvedValueOnce(`
|
||||
{
|
||||
"enabledManagers": ["npm",]
|
||||
}
|
||||
`);
|
||||
await validateReconfigureBranch(config);
|
||||
expect(platform.setBranchStatus).toHaveBeenCalledWith({
|
||||
branchName: 'prefix/reconfigure',
|
||||
context: 'renovate/config-validation',
|
||||
description: 'Validation Successful',
|
||||
state: 'green',
|
||||
});
|
||||
it('validates reconfigure branch', async () => {
|
||||
await expect(checkReconfigureBranch(config)).toResolve();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,49 +1,15 @@
|
|||
import is from '@sindresorhus/is';
|
||||
import JSON5 from 'json5';
|
||||
import { GlobalConfig } from '../../../config/global';
|
||||
import type { RenovateConfig } from '../../../config/types';
|
||||
import { validateConfig } from '../../../config/validation';
|
||||
import { logger } from '../../../logger';
|
||||
import { platform } from '../../../modules/platform';
|
||||
import { ensureComment } from '../../../modules/platform/comment';
|
||||
import { scm } from '../../../modules/platform/scm';
|
||||
import type { BranchStatus } from '../../../types';
|
||||
import { getCache } from '../../../util/cache/repository';
|
||||
import { readLocalFile } from '../../../util/fs';
|
||||
import { getBranchCommit } from '../../../util/git';
|
||||
import { regEx } from '../../../util/regex';
|
||||
import { detectConfigFile } from '../init/merge';
|
||||
import {
|
||||
deleteReconfigureBranchCache,
|
||||
setReconfigureBranchCache,
|
||||
} from './reconfigure-cache';
|
||||
import { deleteReconfigureBranchCache } from './reconfigure-cache';
|
||||
import { getReconfigureBranchName } from './utils';
|
||||
import { validateReconfigureBranch } from './validate';
|
||||
|
||||
async function setBranchStatus(
|
||||
branchName: string,
|
||||
description: string,
|
||||
state: BranchStatus,
|
||||
context?: string | null,
|
||||
): Promise<void> {
|
||||
if (!is.nonEmptyString(context)) {
|
||||
// already logged this case when validating the status check
|
||||
return;
|
||||
}
|
||||
|
||||
await platform.setBranchStatus({
|
||||
branchName,
|
||||
context,
|
||||
description,
|
||||
state,
|
||||
});
|
||||
}
|
||||
|
||||
export function getReconfigureBranchName(prefix: string): string {
|
||||
return `${prefix}reconfigure`;
|
||||
}
|
||||
export async function validateReconfigureBranch(
|
||||
export async function checkReconfigureBranch(
|
||||
config: RenovateConfig,
|
||||
): Promise<void> {
|
||||
logger.debug('validateReconfigureBranch()');
|
||||
logger.debug('checkReconfigureBranch()');
|
||||
if (GlobalConfig.get('platform') === 'local') {
|
||||
logger.debug(
|
||||
'Not attempting to reconfigure when running with local platform',
|
||||
|
@ -51,10 +17,8 @@ export async function validateReconfigureBranch(
|
|||
return;
|
||||
}
|
||||
|
||||
const context = config.statusCheckNames?.configValidation;
|
||||
|
||||
const branchName = getReconfigureBranchName(config.branchPrefix!);
|
||||
const branchExists = await scm.branchExists(branchName);
|
||||
const reconfigureBranch = getReconfigureBranchName(config.branchPrefix!);
|
||||
const branchExists = await scm.branchExists(reconfigureBranch);
|
||||
|
||||
// this is something the user initiates, so skip if no branch exists
|
||||
if (!branchExists) {
|
||||
|
@ -63,141 +27,5 @@ export async function validateReconfigureBranch(
|
|||
return;
|
||||
}
|
||||
|
||||
// look for config file
|
||||
// 1. check reconfigure branch cache and use the configFileName if it exists
|
||||
// 2. checkout reconfigure branch and look for the config file, don't assume default configFileName
|
||||
const branchSha = getBranchCommit(branchName)!;
|
||||
const cache = getCache();
|
||||
let configFileName: string | null = null;
|
||||
const reconfigureCache = cache.reconfigureBranchCache;
|
||||
// only use valid cached information
|
||||
if (reconfigureCache?.reconfigureBranchSha === branchSha) {
|
||||
logger.debug('Skipping validation check as branch sha is unchanged');
|
||||
return;
|
||||
}
|
||||
|
||||
if (context) {
|
||||
const validationStatus = await platform.getBranchStatusCheck(
|
||||
branchName,
|
||||
context,
|
||||
);
|
||||
|
||||
// if old status check is present skip validation
|
||||
if (is.nonEmptyString(validationStatus)) {
|
||||
logger.debug(
|
||||
'Skipping validation check because status check already exists.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
logger.debug(
|
||||
'Status check is null or an empty string, skipping status check addition.',
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
await scm.checkoutBranch(branchName);
|
||||
configFileName = await detectConfigFile();
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
{ err },
|
||||
'Error while searching for config file in reconfigure branch',
|
||||
);
|
||||
}
|
||||
|
||||
if (!is.nonEmptyString(configFileName)) {
|
||||
logger.warn('No config file found in reconfigure branch');
|
||||
await setBranchStatus(
|
||||
branchName,
|
||||
'Validation Failed - No config file found',
|
||||
'red',
|
||||
context,
|
||||
);
|
||||
setReconfigureBranchCache(branchSha, false);
|
||||
await scm.checkoutBranch(config.defaultBranch!);
|
||||
return;
|
||||
}
|
||||
|
||||
let configFileRaw: string | null = null;
|
||||
try {
|
||||
configFileRaw = await readLocalFile(configFileName, 'utf8');
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Error while reading config file');
|
||||
}
|
||||
|
||||
if (!is.nonEmptyString(configFileRaw)) {
|
||||
logger.warn('Empty or invalid config file');
|
||||
await setBranchStatus(
|
||||
branchName,
|
||||
'Validation Failed - Empty/Invalid config file',
|
||||
'red',
|
||||
context,
|
||||
);
|
||||
setReconfigureBranchCache(branchSha, false);
|
||||
await scm.checkoutBranch(config.baseBranch!);
|
||||
return;
|
||||
}
|
||||
|
||||
let configFileParsed: any;
|
||||
try {
|
||||
configFileParsed = JSON5.parse(configFileRaw);
|
||||
// no need to confirm renovate field in package.json we already do it in `detectConfigFile()`
|
||||
if (configFileName === 'package.json') {
|
||||
configFileParsed = configFileParsed.renovate;
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Error while parsing config file');
|
||||
await setBranchStatus(
|
||||
branchName,
|
||||
'Validation Failed - Unparsable config file',
|
||||
'red',
|
||||
context,
|
||||
);
|
||||
setReconfigureBranchCache(branchSha, false);
|
||||
await scm.checkoutBranch(config.baseBranch!);
|
||||
return;
|
||||
}
|
||||
|
||||
// perform validation and provide a passing or failing check run based on result
|
||||
const validationResult = await validateConfig('repo', configFileParsed);
|
||||
|
||||
// failing check
|
||||
if (validationResult.errors.length > 0) {
|
||||
logger.debug(
|
||||
{ errors: validationResult.errors.map((err) => err.message).join(', ') },
|
||||
'Validation Errors',
|
||||
);
|
||||
|
||||
// add comment to reconfigure PR if it exists
|
||||
const branchPr = await platform.findPr({
|
||||
branchName,
|
||||
state: 'open',
|
||||
includeOtherAuthors: true,
|
||||
});
|
||||
if (branchPr) {
|
||||
let body = `There is an error with this repository's Renovate configuration that needs to be fixed.\n\n`;
|
||||
body += `Location: \`${configFileName}\`\n`;
|
||||
body += `Message: \`${validationResult.errors
|
||||
.map((e) => e.message)
|
||||
.join(', ')
|
||||
.replace(regEx(/`/g), "'")}\`\n`;
|
||||
|
||||
await ensureComment({
|
||||
number: branchPr.number,
|
||||
topic: 'Action Required: Fix Renovate Configuration',
|
||||
content: body,
|
||||
});
|
||||
}
|
||||
|
||||
await setBranchStatus(branchName, 'Validation Failed', 'red', context);
|
||||
setReconfigureBranchCache(branchSha, false);
|
||||
await scm.checkoutBranch(config.baseBranch!);
|
||||
return;
|
||||
}
|
||||
|
||||
// passing check
|
||||
await setBranchStatus(branchName, 'Validation Successful', 'green', context);
|
||||
|
||||
setReconfigureBranchCache(branchSha, true);
|
||||
await scm.checkoutBranch(config.baseBranch!);
|
||||
await validateReconfigureBranch(config);
|
||||
}
|
||||
|
|
3
lib/workers/repository/reconfigure/utils.ts
Normal file
3
lib/workers/repository/reconfigure/utils.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export function getReconfigureBranchName(prefix: string): string {
|
||||
return `${prefix}reconfigure`;
|
||||
}
|
228
lib/workers/repository/reconfigure/validate.spec.ts
Normal file
228
lib/workers/repository/reconfigure/validate.spec.ts
Normal file
|
@ -0,0 +1,228 @@
|
|||
import { mock } from 'jest-mock-extended';
|
||||
import type { RenovateConfig } from '../../../../test/util';
|
||||
import { fs, git, mocked, partial, platform, scm } from '../../../../test/util';
|
||||
import { GlobalConfig } from '../../../config/global';
|
||||
import { logger } from '../../../logger';
|
||||
import type { Pr } from '../../../modules/platform/types';
|
||||
import * as _cache from '../../../util/cache/repository';
|
||||
import type { LongCommitSha } from '../../../util/git/types';
|
||||
import * as _merge from '../init/merge';
|
||||
import { validateReconfigureBranch } from './validate';
|
||||
|
||||
jest.mock('../../../util/cache/repository');
|
||||
jest.mock('../../../util/fs');
|
||||
jest.mock('../../../util/git');
|
||||
jest.mock('../init/merge');
|
||||
|
||||
const cache = mocked(_cache);
|
||||
const merge = mocked(_merge);
|
||||
|
||||
describe('workers/repository/reconfigure/validate', () => {
|
||||
const config: RenovateConfig = {
|
||||
branchPrefix: 'prefix/',
|
||||
baseBranch: 'base',
|
||||
statusCheckNames: partial<RenovateConfig['statusCheckNames']>({
|
||||
configValidation: 'renovate/config-validation',
|
||||
}),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
config.repository = 'some/repo';
|
||||
merge.detectConfigFile.mockResolvedValue('renovate.json');
|
||||
scm.branchExists.mockResolvedValue(true);
|
||||
cache.getCache.mockReturnValue({});
|
||||
git.getBranchCommit.mockReturnValue('sha' as LongCommitSha);
|
||||
fs.readLocalFile.mockResolvedValue(null);
|
||||
platform.getBranchStatusCheck.mockResolvedValue(null);
|
||||
GlobalConfig.reset();
|
||||
});
|
||||
|
||||
it('logs error if config file search fails', async () => {
|
||||
const err = new Error();
|
||||
merge.detectConfigFile.mockRejectedValueOnce(err as never);
|
||||
await validateReconfigureBranch(config);
|
||||
expect(logger.error).toHaveBeenCalledWith(
|
||||
{ err },
|
||||
'Error while searching for config file in reconfigure branch',
|
||||
);
|
||||
});
|
||||
|
||||
it('throws error if config file not found in reconfigure branch', async () => {
|
||||
merge.detectConfigFile.mockResolvedValue(null);
|
||||
await validateReconfigureBranch(config);
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
'No config file found in reconfigure branch',
|
||||
);
|
||||
});
|
||||
|
||||
it('logs error if config file is unreadable', async () => {
|
||||
const err = new Error();
|
||||
fs.readLocalFile.mockRejectedValueOnce(err as never);
|
||||
await validateReconfigureBranch(config);
|
||||
expect(logger.error).toHaveBeenCalledWith(
|
||||
{ err },
|
||||
'Error while reading config file',
|
||||
);
|
||||
});
|
||||
|
||||
it('throws error if config file is empty', async () => {
|
||||
await validateReconfigureBranch(config);
|
||||
expect(logger.warn).toHaveBeenCalledWith('Empty or invalid config file');
|
||||
});
|
||||
|
||||
it('throws error if config file content is invalid', async () => {
|
||||
fs.readLocalFile.mockResolvedValueOnce(`
|
||||
{
|
||||
"name":
|
||||
}
|
||||
`);
|
||||
await validateReconfigureBranch(config);
|
||||
expect(logger.error).toHaveBeenCalledWith(
|
||||
{ err: expect.any(Object) },
|
||||
'Error while parsing config file',
|
||||
);
|
||||
expect(platform.setBranchStatus).toHaveBeenCalledWith({
|
||||
branchName: 'prefix/reconfigure',
|
||||
context: 'renovate/config-validation',
|
||||
description: 'Validation Failed - Unparsable config file',
|
||||
state: 'red',
|
||||
});
|
||||
});
|
||||
|
||||
it('handles failed validation', async () => {
|
||||
fs.readLocalFile.mockResolvedValueOnce(`
|
||||
{
|
||||
"enabledManagers": ["docker"]
|
||||
}
|
||||
`);
|
||||
await validateReconfigureBranch(config);
|
||||
expect(logger.debug).toHaveBeenCalledWith(
|
||||
{ errors: expect.any(String) },
|
||||
'Validation Errors',
|
||||
);
|
||||
expect(platform.setBranchStatus).toHaveBeenCalledWith({
|
||||
branchName: 'prefix/reconfigure',
|
||||
context: 'renovate/config-validation',
|
||||
description: 'Validation Failed',
|
||||
state: 'red',
|
||||
});
|
||||
});
|
||||
|
||||
it('adds comment if reconfigure PR exists', async () => {
|
||||
fs.readLocalFile.mockResolvedValueOnce(`
|
||||
{
|
||||
"enabledManagers": ["docker"]
|
||||
}
|
||||
`);
|
||||
platform.findPr.mockResolvedValueOnce(mock<Pr>({ number: 1 }));
|
||||
await validateReconfigureBranch(config);
|
||||
expect(logger.debug).toHaveBeenCalledWith(
|
||||
{ errors: expect.any(String) },
|
||||
'Validation Errors',
|
||||
);
|
||||
expect(platform.setBranchStatus).toHaveBeenCalled();
|
||||
expect(platform.ensureComment).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('handles successful validation', async () => {
|
||||
const pJson = `
|
||||
{
|
||||
"renovate": {
|
||||
"enabledManagers": ["npm"]
|
||||
}
|
||||
}
|
||||
`;
|
||||
merge.detectConfigFile.mockResolvedValue('package.json');
|
||||
fs.readLocalFile.mockResolvedValueOnce(pJson).mockResolvedValueOnce(pJson);
|
||||
await validateReconfigureBranch(config);
|
||||
expect(platform.setBranchStatus).toHaveBeenCalledWith({
|
||||
branchName: 'prefix/reconfigure',
|
||||
context: 'renovate/config-validation',
|
||||
description: 'Validation Successful',
|
||||
state: 'green',
|
||||
});
|
||||
});
|
||||
|
||||
it('skips adding status check if statusCheckNames.configValidation is null', async () => {
|
||||
cache.getCache.mockReturnValueOnce({
|
||||
reconfigureBranchCache: {
|
||||
reconfigureBranchSha: 'new-sha',
|
||||
isConfigValid: false,
|
||||
},
|
||||
});
|
||||
|
||||
await validateReconfigureBranch({
|
||||
...config,
|
||||
statusCheckNames: partial<RenovateConfig['statusCheckNames']>({
|
||||
configValidation: null,
|
||||
}),
|
||||
});
|
||||
expect(logger.debug).toHaveBeenCalledWith(
|
||||
'Status check is null or an empty string, skipping status check addition.',
|
||||
);
|
||||
expect(platform.setBranchStatus).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('skips adding status check if statusCheckNames.configValidation is empty string', async () => {
|
||||
cache.getCache.mockReturnValueOnce({
|
||||
reconfigureBranchCache: {
|
||||
reconfigureBranchSha: 'new-sha',
|
||||
isConfigValid: false,
|
||||
},
|
||||
});
|
||||
|
||||
await validateReconfigureBranch({
|
||||
...config,
|
||||
statusCheckNames: partial<RenovateConfig['statusCheckNames']>({
|
||||
configValidation: '',
|
||||
}),
|
||||
});
|
||||
expect(logger.debug).toHaveBeenCalledWith(
|
||||
'Status check is null or an empty string, skipping status check addition.',
|
||||
);
|
||||
expect(platform.setBranchStatus).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('skips validation if cache is valid', async () => {
|
||||
cache.getCache.mockReturnValueOnce({
|
||||
reconfigureBranchCache: {
|
||||
reconfigureBranchSha: 'sha',
|
||||
isConfigValid: false,
|
||||
},
|
||||
});
|
||||
await validateReconfigureBranch(config);
|
||||
expect(logger.debug).toHaveBeenCalledWith(
|
||||
'Skipping validation check as branch sha is unchanged',
|
||||
);
|
||||
});
|
||||
|
||||
it('skips validation if status check present', async () => {
|
||||
cache.getCache.mockReturnValueOnce({
|
||||
reconfigureBranchCache: {
|
||||
reconfigureBranchSha: 'new_sha',
|
||||
isConfigValid: false,
|
||||
},
|
||||
});
|
||||
platform.getBranchStatusCheck.mockResolvedValueOnce('green');
|
||||
await validateReconfigureBranch(config);
|
||||
expect(logger.debug).toHaveBeenCalledWith(
|
||||
'Skipping validation check because status check already exists.',
|
||||
);
|
||||
});
|
||||
|
||||
it('handles non-default config file', async () => {
|
||||
merge.detectConfigFile.mockResolvedValue('.renovaterc');
|
||||
fs.readLocalFile.mockResolvedValueOnce(`
|
||||
{
|
||||
"enabledManagers": ["npm",]
|
||||
}
|
||||
`);
|
||||
await validateReconfigureBranch(config);
|
||||
expect(platform.setBranchStatus).toHaveBeenCalledWith({
|
||||
branchName: 'prefix/reconfigure',
|
||||
context: 'renovate/config-validation',
|
||||
description: 'Validation Successful',
|
||||
state: 'green',
|
||||
});
|
||||
});
|
||||
});
|
184
lib/workers/repository/reconfigure/validate.ts
Normal file
184
lib/workers/repository/reconfigure/validate.ts
Normal file
|
@ -0,0 +1,184 @@
|
|||
import is from '@sindresorhus/is';
|
||||
import JSON5 from 'json5';
|
||||
import type { RenovateConfig } from '../../../config/types';
|
||||
import { validateConfig } from '../../../config/validation';
|
||||
import { logger } from '../../../logger';
|
||||
import { platform } from '../../../modules/platform';
|
||||
import { ensureComment } from '../../../modules/platform/comment';
|
||||
import { scm } from '../../../modules/platform/scm';
|
||||
import type { BranchStatus } from '../../../types';
|
||||
import { getCache } from '../../../util/cache/repository';
|
||||
import { readLocalFile } from '../../../util/fs';
|
||||
import { getBranchCommit } from '../../../util/git';
|
||||
import { regEx } from '../../../util/regex';
|
||||
import { detectConfigFile } from '../init/merge';
|
||||
import { setReconfigureBranchCache } from './reconfigure-cache';
|
||||
import { getReconfigureBranchName } from './utils';
|
||||
|
||||
async function setBranchStatus(
|
||||
branchName: string,
|
||||
description: string,
|
||||
state: BranchStatus,
|
||||
context?: string | null,
|
||||
): Promise<void> {
|
||||
if (!is.nonEmptyString(context)) {
|
||||
// already logged this case when validating the status check
|
||||
return;
|
||||
}
|
||||
|
||||
await platform.setBranchStatus({
|
||||
branchName,
|
||||
context,
|
||||
description,
|
||||
state,
|
||||
});
|
||||
}
|
||||
|
||||
export async function validateReconfigureBranch(
|
||||
config: RenovateConfig,
|
||||
): Promise<void> {
|
||||
logger.debug('validateReconfigureBranch()');
|
||||
|
||||
const context = config.statusCheckNames?.configValidation;
|
||||
const branchName = getReconfigureBranchName(config.branchPrefix!);
|
||||
|
||||
// look for config file
|
||||
// 1. check reconfigure branch cache and use the configFileName if it exists
|
||||
// 2. checkout reconfigure branch and look for the config file, don't assume default configFileName
|
||||
const branchSha = getBranchCommit(branchName)!;
|
||||
const cache = getCache();
|
||||
let configFileName: string | null = null;
|
||||
const reconfigureCache = cache.reconfigureBranchCache;
|
||||
// only use valid cached information
|
||||
if (reconfigureCache?.reconfigureBranchSha === branchSha) {
|
||||
logger.debug('Skipping validation check as branch sha is unchanged');
|
||||
return;
|
||||
}
|
||||
|
||||
if (context) {
|
||||
const validationStatus = await platform.getBranchStatusCheck(
|
||||
branchName,
|
||||
context,
|
||||
);
|
||||
|
||||
// if old status check is present skip validation
|
||||
if (is.nonEmptyString(validationStatus)) {
|
||||
logger.debug(
|
||||
'Skipping validation check because status check already exists.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
logger.debug(
|
||||
'Status check is null or an empty string, skipping status check addition.',
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
await scm.checkoutBranch(branchName);
|
||||
configFileName = await detectConfigFile();
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
{ err },
|
||||
'Error while searching for config file in reconfigure branch',
|
||||
);
|
||||
}
|
||||
|
||||
if (!is.nonEmptyString(configFileName)) {
|
||||
logger.warn('No config file found in reconfigure branch');
|
||||
await setBranchStatus(
|
||||
branchName,
|
||||
'Validation Failed - No config file found',
|
||||
'red',
|
||||
context,
|
||||
);
|
||||
setReconfigureBranchCache(branchSha, false);
|
||||
await scm.checkoutBranch(config.defaultBranch!);
|
||||
return;
|
||||
}
|
||||
|
||||
let configFileRaw: string | null = null;
|
||||
try {
|
||||
configFileRaw = await readLocalFile(configFileName, 'utf8');
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Error while reading config file');
|
||||
}
|
||||
|
||||
if (!is.nonEmptyString(configFileRaw)) {
|
||||
logger.warn('Empty or invalid config file');
|
||||
await setBranchStatus(
|
||||
branchName,
|
||||
'Validation Failed - Empty/Invalid config file',
|
||||
'red',
|
||||
context,
|
||||
);
|
||||
setReconfigureBranchCache(branchSha, false);
|
||||
await scm.checkoutBranch(config.baseBranch!);
|
||||
return;
|
||||
}
|
||||
|
||||
let configFileParsed: any;
|
||||
try {
|
||||
configFileParsed = JSON5.parse(configFileRaw);
|
||||
// no need to confirm renovate field in package.json we already do it in `detectConfigFile()`
|
||||
if (configFileName === 'package.json') {
|
||||
configFileParsed = configFileParsed.renovate;
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Error while parsing config file');
|
||||
await setBranchStatus(
|
||||
branchName,
|
||||
'Validation Failed - Unparsable config file',
|
||||
'red',
|
||||
context,
|
||||
);
|
||||
setReconfigureBranchCache(branchSha, false);
|
||||
await scm.checkoutBranch(config.baseBranch!);
|
||||
return;
|
||||
}
|
||||
|
||||
// perform validation and provide a passing or failing check based on result
|
||||
const validationResult = await validateConfig('repo', configFileParsed);
|
||||
|
||||
// failing check
|
||||
if (validationResult.errors.length > 0) {
|
||||
logger.debug(
|
||||
{ errors: validationResult.errors.map((err) => err.message).join(', ') },
|
||||
'Validation Errors',
|
||||
);
|
||||
|
||||
const reconfigurePr = await platform.findPr({
|
||||
branchName,
|
||||
state: 'open',
|
||||
includeOtherAuthors: true,
|
||||
});
|
||||
|
||||
// add comment to reconfigure PR if it exists
|
||||
if (reconfigurePr) {
|
||||
let body = `There is an error with this repository's Renovate configuration that needs to be fixed.\n\n`;
|
||||
body += `Location: \`${configFileName}\`\n`;
|
||||
body += `Message: \`${validationResult.errors
|
||||
.map((e) => e.message)
|
||||
.join(', ')
|
||||
.replace(regEx(/`/g), "'")}\`\n`;
|
||||
|
||||
await ensureComment({
|
||||
number: reconfigurePr.number,
|
||||
topic: 'Action Required: Fix Renovate Configuration',
|
||||
content: body,
|
||||
});
|
||||
}
|
||||
|
||||
await setBranchStatus(branchName, 'Validation Failed', 'red', context);
|
||||
setReconfigureBranchCache(branchSha, false);
|
||||
await scm.checkoutBranch(config.baseBranch!);
|
||||
return;
|
||||
}
|
||||
|
||||
// passing check
|
||||
await setBranchStatus(branchName, 'Validation Successful', 'green', context);
|
||||
|
||||
setReconfigureBranchCache(branchSha, true);
|
||||
await scm.checkoutBranch(config.baseBranch!);
|
||||
return;
|
||||
}
|
|
@ -299,7 +299,7 @@
|
|||
"@types/mdast": "3.0.15",
|
||||
"@types/moo": "0.5.9",
|
||||
"@types/ms": "0.7.34",
|
||||
"@types/node": "20.17.10",
|
||||
"@types/node": "20.17.11",
|
||||
"@types/parse-link-header": "2.0.3",
|
||||
"@types/punycode": "2.1.4",
|
||||
"@types/semver": "7.5.8",
|
||||
|
|
130
pnpm-lock.yaml
130
pnpm-lock.yaml
|
@ -470,8 +470,8 @@ importers:
|
|||
specifier: 0.7.34
|
||||
version: 0.7.34
|
||||
'@types/node':
|
||||
specifier: 20.17.10
|
||||
version: 20.17.10
|
||||
specifier: 20.17.11
|
||||
version: 20.17.11
|
||||
'@types/parse-link-header':
|
||||
specifier: 2.0.3
|
||||
version: 2.0.3
|
||||
|
@ -540,7 +540,7 @@ importers:
|
|||
version: 2.31.0(@typescript-eslint/parser@8.19.0(eslint@8.57.1)(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1)
|
||||
eslint-plugin-jest:
|
||||
specifier: 28.10.0
|
||||
version: 28.10.0(@typescript-eslint/eslint-plugin@8.19.0(@typescript-eslint/parser@8.19.0(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1)(jest@29.7.0(@types/node@20.17.10)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2)))(typescript@5.7.2)
|
||||
version: 28.10.0(@typescript-eslint/eslint-plugin@8.19.0(@typescript-eslint/parser@8.19.0(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1)(jest@29.7.0(@types/node@20.17.11)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2)))(typescript@5.7.2)
|
||||
eslint-plugin-jest-formatting:
|
||||
specifier: 3.1.0
|
||||
version: 3.1.0(eslint@8.57.1)
|
||||
|
@ -564,16 +564,16 @@ importers:
|
|||
version: 9.1.7
|
||||
jest:
|
||||
specifier: 29.7.0
|
||||
version: 29.7.0(@types/node@20.17.10)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2))
|
||||
version: 29.7.0(@types/node@20.17.11)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2))
|
||||
jest-extended:
|
||||
specifier: 4.0.2
|
||||
version: 4.0.2(jest@29.7.0(@types/node@20.17.10)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2)))
|
||||
version: 4.0.2(jest@29.7.0(@types/node@20.17.11)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2)))
|
||||
jest-mock:
|
||||
specifier: 29.7.0
|
||||
version: 29.7.0
|
||||
jest-mock-extended:
|
||||
specifier: 3.0.7
|
||||
version: 3.0.7(jest@29.7.0(@types/node@20.17.10)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2)))(typescript@5.7.2)
|
||||
version: 3.0.7(jest@29.7.0(@types/node@20.17.11)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2)))(typescript@5.7.2)
|
||||
jest-snapshot:
|
||||
specifier: 29.7.0
|
||||
version: 29.7.0
|
||||
|
@ -609,10 +609,10 @@ importers:
|
|||
version: 3.0.3
|
||||
ts-jest:
|
||||
specifier: 29.2.5
|
||||
version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.10)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2)))(typescript@5.7.2)
|
||||
version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.11)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2)))(typescript@5.7.2)
|
||||
ts-node:
|
||||
specifier: 10.9.2
|
||||
version: 10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2)
|
||||
version: 10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2)
|
||||
type-fest:
|
||||
specifier: 4.31.0
|
||||
version: 4.31.0
|
||||
|
@ -2121,8 +2121,8 @@ packages:
|
|||
'@types/ms@0.7.34':
|
||||
resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
|
||||
|
||||
'@types/node@20.17.10':
|
||||
resolution: {integrity: sha512-/jrvh5h6NXhEauFFexRin69nA0uHJ5gwk4iDivp/DeoEua3uwCUto6PC86IpRITBOs4+6i2I56K5x5b6WYGXHA==}
|
||||
'@types/node@20.17.11':
|
||||
resolution: {integrity: sha512-Ept5glCK35R8yeyIeYlRIZtX6SLRyqMhOFTgj5SOkMpLTdw3SEHI9fHx60xaUZ+V1aJxQJODE+7/j5ocZydYTg==}
|
||||
|
||||
'@types/normalize-package-data@2.4.4':
|
||||
resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
|
||||
|
@ -7496,27 +7496,27 @@ snapshots:
|
|||
'@jest/console@29.7.0':
|
||||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
chalk: 4.1.2
|
||||
jest-message-util: 29.7.0
|
||||
jest-util: 29.7.0
|
||||
slash: 3.0.0
|
||||
|
||||
'@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2))':
|
||||
'@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2))':
|
||||
dependencies:
|
||||
'@jest/console': 29.7.0
|
||||
'@jest/reporters': 29.7.0
|
||||
'@jest/test-result': 29.7.0
|
||||
'@jest/transform': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
ansi-escapes: 4.3.2
|
||||
chalk: 4.1.2
|
||||
ci-info: 3.9.0
|
||||
exit: 0.1.2
|
||||
graceful-fs: 4.2.11
|
||||
jest-changed-files: 29.7.0
|
||||
jest-config: 29.7.0(@types/node@20.17.10)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2))
|
||||
jest-config: 29.7.0(@types/node@20.17.11)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2))
|
||||
jest-haste-map: 29.7.0
|
||||
jest-message-util: 29.7.0
|
||||
jest-regex-util: 29.6.3
|
||||
|
@ -7541,7 +7541,7 @@ snapshots:
|
|||
dependencies:
|
||||
'@jest/fake-timers': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
jest-mock: 29.7.0
|
||||
|
||||
'@jest/expect-utils@29.4.1':
|
||||
|
@ -7563,7 +7563,7 @@ snapshots:
|
|||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
'@sinonjs/fake-timers': 10.3.0
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
jest-message-util: 29.7.0
|
||||
jest-mock: 29.7.0
|
||||
jest-util: 29.7.0
|
||||
|
@ -7585,7 +7585,7 @@ snapshots:
|
|||
'@jest/transform': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
chalk: 4.1.2
|
||||
collect-v8-coverage: 1.0.2
|
||||
exit: 0.1.2
|
||||
|
@ -7655,7 +7655,7 @@ snapshots:
|
|||
'@jest/schemas': 29.6.3
|
||||
'@types/istanbul-lib-coverage': 2.0.6
|
||||
'@types/istanbul-reports': 3.0.4
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
'@types/yargs': 17.0.33
|
||||
chalk: 4.1.2
|
||||
|
||||
|
@ -8703,7 +8703,7 @@ snapshots:
|
|||
|
||||
'@types/aws4@1.11.6':
|
||||
dependencies:
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
|
||||
'@types/babel__core@7.20.5':
|
||||
dependencies:
|
||||
|
@ -8728,27 +8728,27 @@ snapshots:
|
|||
|
||||
'@types/better-sqlite3@7.6.12':
|
||||
dependencies:
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
|
||||
'@types/breejs__later@4.1.5': {}
|
||||
|
||||
'@types/bunyan@1.8.11':
|
||||
dependencies:
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
|
||||
'@types/bunyan@1.8.9':
|
||||
dependencies:
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
|
||||
'@types/cacache@17.0.2':
|
||||
dependencies:
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
|
||||
'@types/cacheable-request@6.0.3':
|
||||
dependencies:
|
||||
'@types/http-cache-semantics': 4.0.4
|
||||
'@types/keyv': 3.1.4
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
'@types/responselike': 1.0.3
|
||||
|
||||
'@types/callsite@1.0.34': {}
|
||||
|
@ -8779,7 +8779,7 @@ snapshots:
|
|||
'@types/fs-extra@11.0.4':
|
||||
dependencies:
|
||||
'@types/jsonfile': 6.1.4
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
|
||||
'@types/git-url-parse@9.0.3': {}
|
||||
|
||||
|
@ -8789,7 +8789,7 @@ snapshots:
|
|||
|
||||
'@types/graceful-fs@4.1.9':
|
||||
dependencies:
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
|
||||
'@types/http-cache-semantics@4.0.4': {}
|
||||
|
||||
|
@ -8815,13 +8815,13 @@ snapshots:
|
|||
|
||||
'@types/jsonfile@6.1.4':
|
||||
dependencies:
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
|
||||
'@types/katex@0.16.7': {}
|
||||
|
||||
'@types/keyv@3.1.4':
|
||||
dependencies:
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
|
||||
'@types/linkify-it@5.0.0': {}
|
||||
|
||||
|
@ -8840,7 +8840,7 @@ snapshots:
|
|||
|
||||
'@types/marshal@0.5.3':
|
||||
dependencies:
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
|
||||
'@types/mdast@3.0.15':
|
||||
dependencies:
|
||||
|
@ -8856,7 +8856,7 @@ snapshots:
|
|||
|
||||
'@types/ms@0.7.34': {}
|
||||
|
||||
'@types/node@20.17.10':
|
||||
'@types/node@20.17.11':
|
||||
dependencies:
|
||||
undici-types: 6.19.8
|
||||
|
||||
|
@ -8870,7 +8870,7 @@ snapshots:
|
|||
|
||||
'@types/responselike@1.0.3':
|
||||
dependencies:
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
|
||||
'@types/semver-stable@3.0.2': {}
|
||||
|
||||
|
@ -8890,7 +8890,7 @@ snapshots:
|
|||
|
||||
'@types/tar@6.1.13':
|
||||
dependencies:
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
minipass: 4.2.8
|
||||
|
||||
'@types/tmp@0.2.6': {}
|
||||
|
@ -8915,7 +8915,7 @@ snapshots:
|
|||
|
||||
'@types/yauzl@2.10.3':
|
||||
dependencies:
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
optional: true
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.19.0(@typescript-eslint/parser@8.19.0(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1)(typescript@5.7.2)':
|
||||
|
@ -9680,13 +9680,13 @@ snapshots:
|
|||
optionalDependencies:
|
||||
typescript: 5.7.2
|
||||
|
||||
create-jest@29.7.0(@types/node@20.17.10)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2)):
|
||||
create-jest@29.7.0(@types/node@20.17.11)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2)):
|
||||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
chalk: 4.1.2
|
||||
exit: 0.1.2
|
||||
graceful-fs: 4.2.11
|
||||
jest-config: 29.7.0(@types/node@20.17.10)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2))
|
||||
jest-config: 29.7.0(@types/node@20.17.11)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2))
|
||||
jest-util: 29.7.0
|
||||
prompts: 2.4.2
|
||||
transitivePeerDependencies:
|
||||
|
@ -10109,13 +10109,13 @@ snapshots:
|
|||
dependencies:
|
||||
eslint: 8.57.1
|
||||
|
||||
eslint-plugin-jest@28.10.0(@typescript-eslint/eslint-plugin@8.19.0(@typescript-eslint/parser@8.19.0(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1)(jest@29.7.0(@types/node@20.17.10)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2)))(typescript@5.7.2):
|
||||
eslint-plugin-jest@28.10.0(@typescript-eslint/eslint-plugin@8.19.0(@typescript-eslint/parser@8.19.0(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1)(jest@29.7.0(@types/node@20.17.11)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2)))(typescript@5.7.2):
|
||||
dependencies:
|
||||
'@typescript-eslint/utils': 8.19.0(eslint@8.57.1)(typescript@5.7.2)
|
||||
eslint: 8.57.1
|
||||
optionalDependencies:
|
||||
'@typescript-eslint/eslint-plugin': 8.19.0(@typescript-eslint/parser@8.19.0(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1)(typescript@5.7.2)
|
||||
jest: 29.7.0(@types/node@20.17.10)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2))
|
||||
jest: 29.7.0(@types/node@20.17.11)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2))
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
@ -11146,7 +11146,7 @@ snapshots:
|
|||
'@jest/expect': 29.7.0
|
||||
'@jest/test-result': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
chalk: 4.1.2
|
||||
co: 4.6.0
|
||||
dedent: 1.5.3
|
||||
|
@ -11166,16 +11166,16 @@ snapshots:
|
|||
- babel-plugin-macros
|
||||
- supports-color
|
||||
|
||||
jest-cli@29.7.0(@types/node@20.17.10)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2)):
|
||||
jest-cli@29.7.0(@types/node@20.17.11)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2)):
|
||||
dependencies:
|
||||
'@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2))
|
||||
'@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2))
|
||||
'@jest/test-result': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
chalk: 4.1.2
|
||||
create-jest: 29.7.0(@types/node@20.17.10)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2))
|
||||
create-jest: 29.7.0(@types/node@20.17.11)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2))
|
||||
exit: 0.1.2
|
||||
import-local: 3.2.0
|
||||
jest-config: 29.7.0(@types/node@20.17.10)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2))
|
||||
jest-config: 29.7.0(@types/node@20.17.11)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2))
|
||||
jest-util: 29.7.0
|
||||
jest-validate: 29.7.0
|
||||
yargs: 17.7.2
|
||||
|
@ -11185,7 +11185,7 @@ snapshots:
|
|||
- supports-color
|
||||
- ts-node
|
||||
|
||||
jest-config@29.7.0(@types/node@20.17.10)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2)):
|
||||
jest-config@29.7.0(@types/node@20.17.11)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2)):
|
||||
dependencies:
|
||||
'@babel/core': 7.26.0
|
||||
'@jest/test-sequencer': 29.7.0
|
||||
|
@ -11210,8 +11210,8 @@ snapshots:
|
|||
slash: 3.0.0
|
||||
strip-json-comments: 3.1.1
|
||||
optionalDependencies:
|
||||
'@types/node': 20.17.10
|
||||
ts-node: 10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2)
|
||||
'@types/node': 20.17.11
|
||||
ts-node: 10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2)
|
||||
transitivePeerDependencies:
|
||||
- babel-plugin-macros
|
||||
- supports-color
|
||||
|
@ -11240,16 +11240,16 @@ snapshots:
|
|||
'@jest/environment': 29.7.0
|
||||
'@jest/fake-timers': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
jest-mock: 29.7.0
|
||||
jest-util: 29.7.0
|
||||
|
||||
jest-extended@4.0.2(jest@29.7.0(@types/node@20.17.10)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2))):
|
||||
jest-extended@4.0.2(jest@29.7.0(@types/node@20.17.11)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2))):
|
||||
dependencies:
|
||||
jest-diff: 29.7.0
|
||||
jest-get-type: 29.6.3
|
||||
optionalDependencies:
|
||||
jest: 29.7.0(@types/node@20.17.10)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2))
|
||||
jest: 29.7.0(@types/node@20.17.11)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2))
|
||||
|
||||
jest-get-type@29.6.3: {}
|
||||
|
||||
|
@ -11257,7 +11257,7 @@ snapshots:
|
|||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
'@types/graceful-fs': 4.1.9
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
anymatch: 3.1.3
|
||||
fb-watchman: 2.0.2
|
||||
graceful-fs: 4.2.11
|
||||
|
@ -11300,16 +11300,16 @@ snapshots:
|
|||
slash: 3.0.0
|
||||
stack-utils: 2.0.6
|
||||
|
||||
jest-mock-extended@3.0.7(jest@29.7.0(@types/node@20.17.10)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2)))(typescript@5.7.2):
|
||||
jest-mock-extended@3.0.7(jest@29.7.0(@types/node@20.17.11)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2)))(typescript@5.7.2):
|
||||
dependencies:
|
||||
jest: 29.7.0(@types/node@20.17.10)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2))
|
||||
jest: 29.7.0(@types/node@20.17.11)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2))
|
||||
ts-essentials: 10.0.4(typescript@5.7.2)
|
||||
typescript: 5.7.2
|
||||
|
||||
jest-mock@29.7.0:
|
||||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
jest-util: 29.7.0
|
||||
|
||||
jest-pnp-resolver@1.2.3(jest-resolve@29.7.0):
|
||||
|
@ -11344,7 +11344,7 @@ snapshots:
|
|||
'@jest/test-result': 29.7.0
|
||||
'@jest/transform': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
chalk: 4.1.2
|
||||
emittery: 0.13.1
|
||||
graceful-fs: 4.2.11
|
||||
|
@ -11372,7 +11372,7 @@ snapshots:
|
|||
'@jest/test-result': 29.7.0
|
||||
'@jest/transform': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
chalk: 4.1.2
|
||||
cjs-module-lexer: 1.4.1
|
||||
collect-v8-coverage: 1.0.2
|
||||
|
@ -11418,7 +11418,7 @@ snapshots:
|
|||
jest-util@29.7.0:
|
||||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
chalk: 4.1.2
|
||||
ci-info: 3.9.0
|
||||
graceful-fs: 4.2.11
|
||||
|
@ -11437,7 +11437,7 @@ snapshots:
|
|||
dependencies:
|
||||
'@jest/test-result': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
ansi-escapes: 4.3.2
|
||||
chalk: 4.1.2
|
||||
emittery: 0.13.1
|
||||
|
@ -11446,17 +11446,17 @@ snapshots:
|
|||
|
||||
jest-worker@29.7.0:
|
||||
dependencies:
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
jest-util: 29.7.0
|
||||
merge-stream: 2.0.0
|
||||
supports-color: 8.1.1
|
||||
|
||||
jest@29.7.0(@types/node@20.17.10)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2)):
|
||||
jest@29.7.0(@types/node@20.17.11)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2)):
|
||||
dependencies:
|
||||
'@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2))
|
||||
'@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2))
|
||||
'@jest/types': 29.6.3
|
||||
import-local: 3.2.0
|
||||
jest-cli: 29.7.0(@types/node@20.17.10)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2))
|
||||
jest-cli: 29.7.0(@types/node@20.17.11)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2))
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- babel-plugin-macros
|
||||
|
@ -12619,7 +12619,7 @@ snapshots:
|
|||
'@protobufjs/path': 1.1.2
|
||||
'@protobufjs/pool': 1.1.0
|
||||
'@protobufjs/utf8': 1.1.0
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
long: 5.2.3
|
||||
|
||||
protocols@2.0.1: {}
|
||||
|
@ -13382,12 +13382,12 @@ snapshots:
|
|||
optionalDependencies:
|
||||
typescript: 5.7.2
|
||||
|
||||
ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.10)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2)))(typescript@5.7.2):
|
||||
ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.11)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2)))(typescript@5.7.2):
|
||||
dependencies:
|
||||
bs-logger: 0.2.6
|
||||
ejs: 3.1.10
|
||||
fast-json-stable-stringify: 2.1.0
|
||||
jest: 29.7.0(@types/node@20.17.10)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2))
|
||||
jest: 29.7.0(@types/node@20.17.11)(ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2))
|
||||
jest-util: 29.7.0
|
||||
json5: 2.2.3
|
||||
lodash.memoize: 4.1.2
|
||||
|
@ -13401,14 +13401,14 @@ snapshots:
|
|||
'@jest/types': 29.6.3
|
||||
babel-jest: 29.7.0(@babel/core@7.26.0)
|
||||
|
||||
ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.10)(typescript@5.7.2):
|
||||
ts-node@10.9.2(@swc/core@1.10.4)(@types/node@20.17.11)(typescript@5.7.2):
|
||||
dependencies:
|
||||
'@cspotcode/source-map-support': 0.8.1
|
||||
'@tsconfig/node10': 1.0.11
|
||||
'@tsconfig/node12': 1.0.11
|
||||
'@tsconfig/node14': 1.0.3
|
||||
'@tsconfig/node16': 1.0.4
|
||||
'@types/node': 20.17.10
|
||||
'@types/node': 20.17.11
|
||||
acorn: 8.14.0
|
||||
acorn-walk: 8.3.4
|
||||
arg: 4.1.3
|
||||
|
|
|
@ -5,19 +5,19 @@ ARG BASE_IMAGE_TYPE=slim
|
|||
# --------------------------------------
|
||||
# slim image
|
||||
# --------------------------------------
|
||||
FROM ghcr.io/renovatebot/base-image:9.28.1@sha256:d012a79a5f3dc6e6067c46016405064b30fbaaac954597318a7a2122ef807444 AS slim-base
|
||||
FROM ghcr.io/renovatebot/base-image:9.29.0@sha256:10e27273241a0ba63d3a298a7b1e178dbb75b84da6bc2ea7a71db7c9d1a4971c AS slim-base
|
||||
|
||||
# --------------------------------------
|
||||
# full image
|
||||
# --------------------------------------
|
||||
FROM ghcr.io/renovatebot/base-image:9.28.1-full@sha256:422a843cbf6c1a3730fab9e89877bf04c49d329501a5b998488078cc6153fc03 AS full-base
|
||||
FROM ghcr.io/renovatebot/base-image:9.29.0-full@sha256:7b2353855c0f59b9efdb93ce9356aff5dad7d5102f8947c4ebc906855be9177c AS full-base
|
||||
|
||||
ENV RENOVATE_BINARY_SOURCE=global
|
||||
|
||||
# --------------------------------------
|
||||
# build image
|
||||
# --------------------------------------
|
||||
FROM --platform=$BUILDPLATFORM ghcr.io/renovatebot/base-image:9.28.1@sha256:d012a79a5f3dc6e6067c46016405064b30fbaaac954597318a7a2122ef807444 AS build
|
||||
FROM --platform=$BUILDPLATFORM ghcr.io/renovatebot/base-image:9.29.0@sha256:10e27273241a0ba63d3a298a7b1e178dbb75b84da6bc2ea7a71db7c9d1a4971c AS build
|
||||
|
||||
# We want a specific node version here
|
||||
# renovate: datasource=node-version
|
||||
|
|
Loading…
Reference in a new issue