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'
|
- 'Self-hosted': 'self-hosted-configuration.md'
|
||||||
- 'Presets': 'config-presets.md'
|
- 'Presets': 'config-presets.md'
|
||||||
- 'Validation': 'config-validation.md'
|
- 'Validation': 'config-validation.md'
|
||||||
|
- 'Switching bot identity': 'switching-bot-identity.md'
|
||||||
- ... | mend-hosted
|
- ... | mend-hosted
|
||||||
- ... | key-concepts
|
- ... | key-concepts
|
||||||
- ... | modules
|
- ... | 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).
|
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.
|
`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
|
## Legacy `WORKSPACE` files
|
||||||
|
|
||||||
Renovate extracts dependencies from the following repository rules:
|
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.
|
It also recognizes when these repository rule names are prefixed with an underscore.
|
||||||
For example, `_http_archive` is treated the same as `http_archive`.
|
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:
|
Renovate updates any `git_repository` declaration that has the following:
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ name: renovate-otel-demo
|
||||||
services:
|
services:
|
||||||
# Jaeger for storing traces
|
# Jaeger for storing traces
|
||||||
jaeger:
|
jaeger:
|
||||||
image: jaegertracing/jaeger:2.1.0
|
image: jaegertracing/jaeger:2.2.0
|
||||||
ports:
|
ports:
|
||||||
- '16686:16686' # Web UI
|
- '16686:16686' # Web UI
|
||||||
- '4317' # OTLP gRPC
|
- '4317' # OTLP gRPC
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
title: Mend-hosted Apps
|
title: Mend-hosted Apps
|
||||||
nav:
|
nav:
|
||||||
|
- 'Renovate Plans': 'renovate-plans.md'
|
||||||
- 'Configuration': 'hosted-apps-config.md'
|
- 'Configuration': 'hosted-apps-config.md'
|
||||||
- 'Credentials': 'credentials.md'
|
- 'Credentials': 'credentials.md'
|
||||||
- 'Migrating Secrets': 'migrating-secrets.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",
|
"shiki": "https://github.com/shikijs/shiki",
|
||||||
"shopify-app-bridge": "https://github.com/Shopify/app-bridge",
|
"shopify-app-bridge": "https://github.com/Shopify/app-bridge",
|
||||||
"sitecore-jss": "https://github.com/Sitecore/jss",
|
"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",
|
"slack-net": "https://github.com/soxtoby/SlackNet",
|
||||||
"slf4j": "https://github.com/qos-ch/slf4j",
|
"slf4j": "https://github.com/qos-ch/slf4j",
|
||||||
"slim-message-bus": "https://github.com/zarusz/SlimMessageBus",
|
"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 { parse } from './parser';
|
||||||
import { RuleToMavenPackageDep, fillRegistryUrls } from './parser/maven';
|
import { RuleToMavenPackageDep, fillRegistryUrls } from './parser/maven';
|
||||||
import { RuleToDockerPackageDep } from './parser/oci';
|
import { RuleToDockerPackageDep } from './parser/oci';
|
||||||
import { RuleToBazelModulePackageDep } from './rules';
|
import {
|
||||||
|
GitRepositoryToPackageDep,
|
||||||
|
RuleToBazelModulePackageDep,
|
||||||
|
} from './rules';
|
||||||
import * as rules from './rules';
|
import * as rules from './rules';
|
||||||
|
|
||||||
export async function extractPackageFile(
|
export async function extractPackageFile(
|
||||||
|
@ -18,9 +21,14 @@ export async function extractPackageFile(
|
||||||
try {
|
try {
|
||||||
const records = parse(content);
|
const records = parse(content);
|
||||||
const pfc = await extractBazelPfc(records, packageFile);
|
const pfc = await extractBazelPfc(records, packageFile);
|
||||||
|
const gitRepositoryDeps = extractGitRepositoryDeps(records);
|
||||||
const mavenDeps = extractMavenDeps(records);
|
const mavenDeps = extractMavenDeps(records);
|
||||||
const dockerDeps = LooseArray(RuleToDockerPackageDep).parse(records);
|
const dockerDeps = LooseArray(RuleToDockerPackageDep).parse(records);
|
||||||
|
|
||||||
|
if (gitRepositoryDeps.length) {
|
||||||
|
pfc.deps.push(...gitRepositoryDeps);
|
||||||
|
}
|
||||||
|
|
||||||
if (mavenDeps.length) {
|
if (mavenDeps.length) {
|
||||||
pfc.deps.push(...mavenDeps);
|
pfc.deps.push(...mavenDeps);
|
||||||
}
|
}
|
||||||
|
@ -57,6 +65,12 @@ async function extractBazelPfc(
|
||||||
return pfc;
|
return pfc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extractGitRepositoryDeps(
|
||||||
|
records: RecordFragment[],
|
||||||
|
): PackageDependency[] {
|
||||||
|
return LooseArray(GitRepositoryToPackageDep).parse(records);
|
||||||
|
}
|
||||||
|
|
||||||
function extractMavenDeps(records: RecordFragment[]): PackageDependency[] {
|
function extractMavenDeps(records: RecordFragment[]): PackageDependency[] {
|
||||||
return LooseArray(RuleToMavenPackageDep)
|
return LooseArray(RuleToMavenPackageDep)
|
||||||
.transform(fillRegistryUrls)
|
.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',
|
'git_override',
|
||||||
'local_path_override',
|
'local_path_override',
|
||||||
'single_version_override',
|
'single_version_override',
|
||||||
|
'git_repository',
|
||||||
];
|
];
|
||||||
const supportedRulesRegex = regEx(`^${supportedRules.join('|')}$`);
|
const supportedRulesRegex = regEx(`^${supportedRules.join('|')}$`);
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import type {
|
||||||
OverridePackageDep,
|
OverridePackageDep,
|
||||||
} from './rules';
|
} from './rules';
|
||||||
import {
|
import {
|
||||||
|
GitRepositoryToPackageDep,
|
||||||
RuleToBazelModulePackageDep,
|
RuleToBazelModulePackageDep,
|
||||||
bazelModulePackageDepToPackageDependency,
|
bazelModulePackageDepToPackageDependency,
|
||||||
processModulePkgDeps,
|
processModulePkgDeps,
|
||||||
|
@ -72,6 +73,19 @@ const singleVersionOverrideWithoutVersionAndRegistryPkgDep: BasePackageDep = {
|
||||||
depName: 'rules_foo',
|
depName: 'rules_foo',
|
||||||
skipReason: 'ignored',
|
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('modules/manager/bazel-module/rules', () => {
|
||||||
describe('RuleToBazelModulePackageDep', () => {
|
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()', () => {
|
describe('.toPackageDependencies()', () => {
|
||||||
const expectedBazelDepNoOverrides: PackageDependency[] = [bazelDepPkgDep];
|
const expectedBazelDepNoOverrides: PackageDependency[] = [bazelDepPkgDep];
|
||||||
const expectedBazelDepAndGitOverride: PackageDependency[] = [
|
const expectedBazelDepAndGitOverride: PackageDependency[] = [
|
||||||
|
|
|
@ -242,3 +242,28 @@ export function toPackageDependencies(
|
||||||
): PackageDependency[] {
|
): PackageDependency[] {
|
||||||
return collectByModule(packageDeps).map(processModulePkgDeps).flat();
|
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([
|
bundlerHostRules.findAllAuthenticatable.mockReturnValue([
|
||||||
{
|
{
|
||||||
hostType: 'bundler',
|
hostType: 'bundler',
|
||||||
matchHost: 'gems.private.com',
|
matchHost: 'gems-private.com',
|
||||||
resolvedHost: 'gems.private.com',
|
resolvedHost: 'gems-private.com',
|
||||||
username: 'some-user',
|
username: 'some-user',
|
||||||
password: 'some-password',
|
password: 'some-password',
|
||||||
},
|
},
|
||||||
|
@ -470,7 +470,7 @@ describe('modules/manager/bundler/artifacts', () => {
|
||||||
'docker run --rm --name=renovate_sidecar --label=renovate_child ' +
|
'docker run --rm --name=renovate_sidecar --label=renovate_child ' +
|
||||||
'-v "/tmp/github/some/repo":"/tmp/github/some/repo" ' +
|
'-v "/tmp/github/some/repo":"/tmp/github/some/repo" ' +
|
||||||
'-v "/tmp/cache":"/tmp/cache" ' +
|
'-v "/tmp/cache":"/tmp/cache" ' +
|
||||||
'-e BUNDLE_GEMS__PRIVATE__COM ' +
|
'-e BUNDLE_GEMS___PRIVATE__COM ' +
|
||||||
'-e GEM_HOME ' +
|
'-e GEM_HOME ' +
|
||||||
'-e CONTAINERBASE_CACHE_DIR ' +
|
'-e CONTAINERBASE_CACHE_DIR ' +
|
||||||
'-w "/tmp/github/some/repo" ' +
|
'-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 () => {
|
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 is from '@sindresorhus/is';
|
||||||
import { quote } from 'shlex';
|
import { quote } from 'shlex';
|
||||||
import {
|
import {
|
||||||
|
@ -17,7 +16,6 @@ import {
|
||||||
} from '../../../util/fs';
|
} from '../../../util/fs';
|
||||||
import { getRepoStatus } from '../../../util/git';
|
import { getRepoStatus } from '../../../util/git';
|
||||||
import { newlineRegex, regEx } from '../../../util/regex';
|
import { newlineRegex, regEx } from '../../../util/regex';
|
||||||
import { isValid } from '../../versioning/ruby';
|
|
||||||
import type { UpdateArtifact, UpdateArtifactsResult } from '../types';
|
import type { UpdateArtifact, UpdateArtifactsResult } from '../types';
|
||||||
import {
|
import {
|
||||||
getBundlerConstraint,
|
getBundlerConstraint,
|
||||||
|
@ -32,14 +30,17 @@ import {
|
||||||
const hostConfigVariablePrefix = 'BUNDLE_';
|
const hostConfigVariablePrefix = 'BUNDLE_';
|
||||||
|
|
||||||
function buildBundleHostVariable(hostRule: HostRule): Record<string, string> {
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
const varName = hostConfigVariablePrefix.concat(
|
const varName = hostConfigVariablePrefix.concat(
|
||||||
hostRule.resolvedHost
|
hostRule.resolvedHost
|
||||||
|
.toUpperCase()
|
||||||
.split('.')
|
.split('.')
|
||||||
.map((term) => term.toUpperCase())
|
.join('__')
|
||||||
.join('__'),
|
.split('-')
|
||||||
|
.join('___'),
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
[varName]: `${getAuthenticationHeaderValue(hostRule)}`,
|
[varName]: `${getAuthenticationHeaderValue(hostRule)}`,
|
||||||
|
@ -149,47 +150,12 @@ export async function updateArtifacts(
|
||||||
{} as Record<string, string>,
|
{} 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(
|
const bundler = getBundlerConstraint(
|
||||||
updateArtifact,
|
updateArtifact,
|
||||||
existingLockFileContent,
|
existingLockFileContent,
|
||||||
);
|
);
|
||||||
const preCommands = ['ruby --version'];
|
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 = {
|
const execOptions: ExecOptions = {
|
||||||
cwdFile: lockFileName,
|
cwdFile: lockFileName,
|
||||||
userConfiguredEnv: config.env,
|
userConfiguredEnv: config.env,
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
<Folder Include="wwwroot\" />
|
<Folder Include="wwwroot\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Version="1.2.3" />
|
||||||
<PackageReference Include="Autofac" Version="4.5.0" />
|
<PackageReference Include="Autofac" Version="4.5.0" />
|
||||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.1.0" />
|
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.1.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="1.1.2" />
|
<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",
|
"depName": "Microsoft.VisualStudio.Web.CodeGeneration.Tools",
|
||||||
"depType": "nuget",
|
"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",
|
"currentValue": "1.2.3",
|
||||||
"datasource": "nuget",
|
"datasource": "nuget",
|
||||||
|
@ -115,6 +152,36 @@ exports[`modules/manager/nuget/extract extractPackageFile() extracts all depende
|
||||||
"depName": "Microsoft.VisualStudio.Web.CodeGeneration.Tools",
|
"depName": "Microsoft.VisualStudio.Web.CodeGeneration.Tools",
|
||||||
"depType": "nuget",
|
"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",
|
"currentValue": "1.2.3",
|
||||||
"datasource": "nuget",
|
"datasource": "nuget",
|
||||||
|
|
|
@ -57,7 +57,7 @@ describe('modules/manager/nuget/extract', () => {
|
||||||
const sample = Fixtures.get(packageFile);
|
const sample = Fixtures.get(packageFile);
|
||||||
const res = await extractPackageFile(sample, packageFile, config);
|
const res = await extractPackageFile(sample, packageFile, config);
|
||||||
expect(res?.deps).toMatchSnapshot();
|
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 () => {
|
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 sample = Fixtures.get(packageFile);
|
||||||
const res = await extractPackageFile(sample, packageFile, config);
|
const res = await extractPackageFile(sample, packageFile, config);
|
||||||
expect(res?.deps).toMatchSnapshot();
|
expect(res?.deps).toMatchSnapshot();
|
||||||
expect(res?.deps).toHaveLength(17);
|
expect(res?.deps).toHaveLength(22);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('extracts ContainerBaseImage', async () => {
|
it('extracts ContainerBaseImage', async () => {
|
||||||
|
|
|
@ -28,7 +28,7 @@ import { applyRegistries, findVersion, getConfiguredRegistries } from './util';
|
||||||
* so we don't include it in the extracting regexp
|
* so we don't include it in the extracting regexp
|
||||||
*/
|
*/
|
||||||
const checkVersion = regEx(
|
const checkVersion = regEx(
|
||||||
`^\\s*(?:[[])?(?:(?<currentValue>[^"(,[\\]]+)\\s*(?:,\\s*[)\\]]|])?)\\s*$`,
|
/^\s*(?:[[])?(?:(?<currentValue>[^"(,[\]]+)\s*(?:,\s*[)\]]|])?)\s*$/,
|
||||||
);
|
);
|
||||||
const elemNames = new Set([
|
const elemNames = new Set([
|
||||||
'PackageReference',
|
'PackageReference',
|
||||||
|
@ -58,23 +58,38 @@ function extractDepsFromXml(xmlNode: XmlDocument): NugetPackageDependency[] {
|
||||||
|
|
||||||
if (elemNames.has(name)) {
|
if (elemNames.has(name)) {
|
||||||
const depName = attr?.Include || attr?.Update;
|
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 ??
|
||||||
attr?.version ??
|
attr?.version ??
|
||||||
child.valueWithPath('Version') ??
|
child.valueWithPath('Version') ??
|
||||||
attr?.VersionOverride ??
|
attr?.VersionOverride ??
|
||||||
child.valueWithPath('VersionOverride');
|
child.valueWithPath('VersionOverride');
|
||||||
const currentValue = is.nonEmptyStringAndNotWhitespace(version)
|
|
||||||
? checkVersion.exec(version)?.groups?.currentValue?.trim()
|
if (!is.nonEmptyStringAndNotWhitespace(currentValue)) {
|
||||||
: undefined;
|
dep.skipReason = 'invalid-version';
|
||||||
if (depName && currentValue) {
|
|
||||||
results.push({
|
|
||||||
datasource: NugetDatasource.id,
|
|
||||||
depType: 'nuget',
|
|
||||||
depName,
|
|
||||||
currentValue,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
currentValue = checkVersion
|
||||||
|
.exec(currentValue)
|
||||||
|
?.groups?.currentValue?.trim();
|
||||||
|
|
||||||
|
if (currentValue) {
|
||||||
|
dep.currentValue = currentValue;
|
||||||
|
} else {
|
||||||
|
dep.skipReason = 'invalid-version';
|
||||||
|
}
|
||||||
|
|
||||||
|
results.push(dep);
|
||||||
} else if (name === 'Sdk') {
|
} else if (name === 'Sdk') {
|
||||||
const depName = attr?.Name;
|
const depName = attr?.Name;
|
||||||
const version = attr?.Version;
|
const version = attr?.Version;
|
||||||
|
|
|
@ -16,6 +16,10 @@ url = "last.url"
|
||||||
[[tool.poetry.source]]
|
[[tool.poetry.source]]
|
||||||
name = "five"
|
name = "five"
|
||||||
|
|
||||||
|
[[tool.poetry.source]]
|
||||||
|
name = "invalid-url"
|
||||||
|
url = "invalid-url"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry_core>=1.0", "wheel"]
|
requires = ["poetry_core>=1.0", "wheel"]
|
||||||
build-backend = "poetry.masonry.api"
|
build-backend = "poetry.masonry.api"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { codeBlock } from 'common-tags';
|
import { codeBlock } from 'common-tags';
|
||||||
|
import { GoogleAuth as _googleAuth } from 'google-auth-library';
|
||||||
import { mockDeep } from 'jest-mock-extended';
|
import { mockDeep } from 'jest-mock-extended';
|
||||||
import { join } from 'upath';
|
import { join } from 'upath';
|
||||||
import { envMock, mockExecAll } from '../../../../test/exec-util';
|
import { envMock, mockExecAll } from '../../../../test/exec-util';
|
||||||
|
@ -15,16 +16,26 @@ import { updateArtifacts } from '.';
|
||||||
|
|
||||||
const pyproject1toml = Fixtures.get('pyproject.1.toml');
|
const pyproject1toml = Fixtures.get('pyproject.1.toml');
|
||||||
const pyproject10toml = Fixtures.get('pyproject.10.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/exec/env');
|
||||||
jest.mock('../../../util/fs');
|
jest.mock('../../../util/fs');
|
||||||
jest.mock('../../datasource', () => mockDeep());
|
jest.mock('../../datasource', () => mockDeep());
|
||||||
jest.mock('../../../util/host-rules', () => mockDeep());
|
jest.mock('../../../util/host-rules', () => mockDeep());
|
||||||
|
jest.mock('google-auth-library');
|
||||||
|
|
||||||
process.env.CONTAINERBASE = 'true';
|
process.env.CONTAINERBASE = 'true';
|
||||||
|
|
||||||
const datasource = mocked(_datasource);
|
const datasource = mocked(_datasource);
|
||||||
const hostRules = mocked(_hostRules);
|
const hostRules = mocked(_hostRules);
|
||||||
|
const googleAuth = mocked(_googleAuth);
|
||||||
|
|
||||||
const adminConfig: RepoGlobalConfig = {
|
const adminConfig: RepoGlobalConfig = {
|
||||||
localDir: join('/tmp/github/some/repo'),
|
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([
|
expect(execSnapshots).toMatchObject([
|
||||||
{ cmd: 'poetry update --lock --no-interaction dep1' },
|
{ cmd: 'poetry update --lock --no-interaction dep1' },
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -17,7 +17,9 @@ import { find } from '../../../util/host-rules';
|
||||||
import { regEx } from '../../../util/regex';
|
import { regEx } from '../../../util/regex';
|
||||||
import { Result } from '../../../util/result';
|
import { Result } from '../../../util/result';
|
||||||
import { parse as parseToml } from '../../../util/toml';
|
import { parse as parseToml } from '../../../util/toml';
|
||||||
|
import { parseUrl } from '../../../util/url';
|
||||||
import { PypiDatasource } from '../../datasource/pypi';
|
import { PypiDatasource } from '../../datasource/pypi';
|
||||||
|
import { getGoogleAuthTokenRaw } from '../../datasource/util';
|
||||||
import type { UpdateArtifact, UpdateArtifactsResult } from '../types';
|
import type { UpdateArtifact, UpdateArtifactsResult } from '../types';
|
||||||
import { Lockfile, PoetrySchemaToml } from './schema';
|
import { Lockfile, PoetrySchemaToml } from './schema';
|
||||||
import type { PoetryFile, PoetrySource } from './types';
|
import type { PoetryFile, PoetrySource } from './types';
|
||||||
|
@ -101,7 +103,7 @@ function getPoetrySources(content: string, fileName: string): PoetrySource[] {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
if (!pyprojectFile.tool?.poetry) {
|
if (!pyprojectFile.tool?.poetry) {
|
||||||
logger.debug(`{$fileName} contains no poetry section`);
|
logger.debug(`${fileName} contains no poetry section`);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,20 +117,42 @@ function getPoetrySources(content: string, fileName: string): PoetrySource[] {
|
||||||
return sourceArray;
|
return sourceArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMatchingHostRule(url: string | undefined): HostRule {
|
async function getMatchingHostRule(url: string | undefined): Promise<HostRule> {
|
||||||
const scopedMatch = find({ hostType: PypiDatasource.id, url });
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSourceCredentialVars(
|
async function getSourceCredentialVars(
|
||||||
pyprojectContent: string,
|
pyprojectContent: string,
|
||||||
packageFileName: string,
|
packageFileName: string,
|
||||||
): NodeJS.ProcessEnv {
|
): Promise<NodeJS.ProcessEnv> {
|
||||||
const poetrySources = getPoetrySources(pyprojectContent, packageFileName);
|
const poetrySources = getPoetrySources(pyprojectContent, packageFileName);
|
||||||
const envVars: NodeJS.ProcessEnv = {};
|
const envVars: NodeJS.ProcessEnv = {};
|
||||||
|
|
||||||
for (const source of poetrySources) {
|
for (const source of poetrySources) {
|
||||||
const matchingHostRule = getMatchingHostRule(source.url);
|
const matchingHostRule = await getMatchingHostRule(source.url);
|
||||||
const formattedSourceName = source.name
|
const formattedSourceName = source.name
|
||||||
.replace(regEx(/(\.|-)+/g), '_')
|
.replace(regEx(/(\.|-)+/g), '_')
|
||||||
.toUpperCase();
|
.toUpperCase();
|
||||||
|
@ -192,7 +216,10 @@ export async function updateArtifacts({
|
||||||
config.constraints?.poetry ??
|
config.constraints?.poetry ??
|
||||||
getPoetryRequirement(newPackageFileContent, existingLockFileContent);
|
getPoetryRequirement(newPackageFileContent, existingLockFileContent);
|
||||||
const extraEnv = {
|
const extraEnv = {
|
||||||
...getSourceCredentialVars(newPackageFileContent, packageFileName),
|
...(await getSourceCredentialVars(
|
||||||
|
newPackageFileContent,
|
||||||
|
packageFileName,
|
||||||
|
)),
|
||||||
...getGitEnvironmentVariables(['poetry']),
|
...getGitEnvironmentVariables(['poetry']),
|
||||||
PIP_CACHE_DIR: await ensureCacheDir('pip'),
|
PIP_CACHE_DIR: await ensureCacheDir('pip'),
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,11 +13,18 @@ repos:
|
||||||
rev: 19.3b0
|
rev: 19.3b0
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
|
language: python
|
||||||
|
additional_dependencies:
|
||||||
|
- "request==1.1.1"
|
||||||
|
- "" # broken pypi package
|
||||||
- repo: https://gitlab.com/psf/black
|
- repo: https://gitlab.com/psf/black
|
||||||
# should also detect gitlab
|
# should also detect gitlab
|
||||||
rev: 19.3b0
|
rev: 19.3b0
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
|
# missing language, not extracted
|
||||||
|
additional_dependencies:
|
||||||
|
- "urllib==24.9.0"
|
||||||
- repo: http://gitlab.com/psf/black
|
- repo: http://gitlab.com/psf/black
|
||||||
# should also detect http
|
# should also detect http
|
||||||
rev: 19.3b0
|
rev: 19.3b0
|
||||||
|
@ -48,3 +55,7 @@ repos:
|
||||||
- repo: some_invalid_url
|
- repo: some_invalid_url
|
||||||
# case with invlalid url.
|
# case with invlalid url.
|
||||||
rev: v1.0.0
|
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",
|
"depType": "repository",
|
||||||
"packageName": "pre-commit/pre-commit-hooks",
|
"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",
|
"currentValue": "19.3b0",
|
||||||
"datasource": "github-tags",
|
"datasource": "github-tags",
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { mockDeep } from 'jest-mock-extended';
|
||||||
import { Fixtures } from '../../../../test/fixtures';
|
import { Fixtures } from '../../../../test/fixtures';
|
||||||
import { mocked } from '../../../../test/util';
|
import { mocked } from '../../../../test/util';
|
||||||
import * as _hostRules from '../../../util/host-rules';
|
import * as _hostRules from '../../../util/host-rules';
|
||||||
|
import { PypiDatasource } from '../../datasource/pypi';
|
||||||
import { extractPackageFile } from '.';
|
import { extractPackageFile } from '.';
|
||||||
|
|
||||||
jest.mock('../../../util/host-rules', () => mockDeep());
|
jest.mock('../../../util/host-rules', () => mockDeep());
|
||||||
|
@ -81,6 +82,14 @@ describe('modules/manager/pre-commit/extract', () => {
|
||||||
expect(result).toMatchSnapshot({
|
expect(result).toMatchSnapshot({
|
||||||
deps: [
|
deps: [
|
||||||
{ depName: 'pre-commit/pre-commit-hooks', currentValue: 'v3.3.0' },
|
{ 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' },
|
{ 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 { parseSingleYaml } from '../../../util/yaml';
|
||||||
import { GithubTagsDatasource } from '../../datasource/github-tags';
|
import { GithubTagsDatasource } from '../../datasource/github-tags';
|
||||||
import { GitlabTagsDatasource } from '../../datasource/gitlab-tags';
|
import { GitlabTagsDatasource } from '../../datasource/gitlab-tags';
|
||||||
|
import { pep508ToPackageDependency } from '../pep621/utils';
|
||||||
import type { PackageDependency, PackageFileContent } from '../types';
|
import type { PackageDependency, PackageFileContent } from '../types';
|
||||||
import {
|
import {
|
||||||
matchesPrecommitConfigHeuristic,
|
matchesPrecommitConfigHeuristic,
|
||||||
|
@ -137,6 +138,23 @@ function findDependencies(precommitFile: PreCommitConfig): PackageDependency[] {
|
||||||
}
|
}
|
||||||
const packageDependencies: PackageDependency[] = [];
|
const packageDependencies: PackageDependency[] = [];
|
||||||
precommitFile.repos.forEach((item) => {
|
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)) {
|
if (matchesPrecommitDependencyHeuristic(item)) {
|
||||||
logger.trace(item, 'Matched pre-commit dependency spec');
|
logger.trace(item, 'Matched pre-commit dependency spec');
|
||||||
const repository = String(item.repo);
|
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.
|
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[];
|
repos: PreCommitDependency[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PreCommitHook {
|
||||||
|
language?: string;
|
||||||
|
additional_dependencies?: Array<string>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface PreCommitDependency {
|
export interface PreCommitDependency {
|
||||||
repo: string;
|
repo: string;
|
||||||
|
hooks?: Array<PreCommitHook>;
|
||||||
rev: string;
|
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()', () => {
|
describe('findIssue()', () => {
|
||||||
it('returns null if no issue', async () => {
|
it('returns null if no issue', async () => {
|
||||||
httpMock
|
httpMock
|
||||||
|
|
|
@ -1231,7 +1231,6 @@ export async function getIssueList(): Promise<Issue[]> {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getIssue(number: number): Promise<Issue | null> {
|
export async function getIssue(number: number): Promise<Issue | null> {
|
||||||
// istanbul ignore if
|
|
||||||
if (config.hasIssuesEnabled === false) {
|
if (config.hasIssuesEnabled === false) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1246,8 +1245,12 @@ export async function getIssue(number: number): Promise<Issue | null> {
|
||||||
);
|
);
|
||||||
GithubIssueCache.updateIssue(issue);
|
GithubIssueCache.updateIssue(issue);
|
||||||
return issue;
|
return issue;
|
||||||
} catch (err) /* istanbul ignore next */ {
|
} catch (err) {
|
||||||
logger.debug({ err, number }, 'Error getting issue');
|
logger.debug({ err, number }, 'Error getting issue');
|
||||||
|
if (err.response?.statusCode === 410) {
|
||||||
|
logger.debug(`Issue #${number} has been deleted`);
|
||||||
|
GithubIssueCache.deleteIssue(number);
|
||||||
|
}
|
||||||
return null;
|
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', () => {
|
it('reconciles cache', () => {
|
||||||
cache.platform = {
|
cache.platform = {
|
||||||
github: {
|
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.
|
* At the moment of repo initialization, repository cache is not available.
|
||||||
* What we can do is to store issues for later reconciliation.
|
* What we can do is to store issues for later reconciliation.
|
||||||
|
|
|
@ -1361,9 +1361,12 @@ export async function ensureComment({
|
||||||
if (topic) {
|
if (topic) {
|
||||||
logger.debug(`Ensuring comment "${massagedTopic!}" in #${number}`);
|
logger.debug(`Ensuring comment "${massagedTopic!}" in #${number}`);
|
||||||
body = `### ${topic}\n\n${sanitizedContent}`;
|
body = `### ${topic}\n\n${sanitizedContent}`;
|
||||||
body = body
|
body = smartTruncate(
|
||||||
.replace(regEx(/Pull Request/g), 'Merge Request')
|
body
|
||||||
.replace(regEx(/PR/g), 'MR');
|
.replace(regEx(/Pull Request/g), 'Merge Request')
|
||||||
|
.replace(regEx(/PR/g), 'MR'),
|
||||||
|
maxBodyLength(),
|
||||||
|
);
|
||||||
comments.forEach((comment: { body: string; id: number }) => {
|
comments.forEach((comment: { body: string; id: number }) => {
|
||||||
if (comment.body.startsWith(`### ${massagedTopic!}\n\n`)) {
|
if (comment.body.startsWith(`### ${massagedTopic!}\n\n`)) {
|
||||||
commentId = comment.id;
|
commentId = comment.id;
|
||||||
|
@ -1372,7 +1375,7 @@ export async function ensureComment({
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
logger.debug(`Ensuring content-only comment in #${number}`);
|
logger.debug(`Ensuring content-only comment in #${number}`);
|
||||||
body = `${sanitizedContent}`;
|
body = smartTruncate(`${sanitizedContent}`, maxBodyLength());
|
||||||
comments.forEach((comment: { body: string; id: number }) => {
|
comments.forEach((comment: { body: string; id: number }) => {
|
||||||
if (comment.body === body) {
|
if (comment.body === body) {
|
||||||
commentId = comment.id;
|
commentId = comment.id;
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { platform } from '../../../modules/platform';
|
||||||
import * as repositoryCache from '../../../util/cache/repository';
|
import * as repositoryCache from '../../../util/cache/repository';
|
||||||
import { clearRenovateRefs } from '../../../util/git';
|
import { clearRenovateRefs } from '../../../util/git';
|
||||||
import { PackageFiles } from '../package-files';
|
import { PackageFiles } from '../package-files';
|
||||||
import { validateReconfigureBranch } from '../reconfigure';
|
import { checkReconfigureBranch } from '../reconfigure';
|
||||||
import { pruneStaleBranches } from './prune';
|
import { pruneStaleBranches } from './prune';
|
||||||
import {
|
import {
|
||||||
runBranchSummary,
|
runBranchSummary,
|
||||||
|
@ -16,7 +16,7 @@ export async function finalizeRepo(
|
||||||
config: RenovateConfig,
|
config: RenovateConfig,
|
||||||
branchList: string[],
|
branchList: string[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await validateReconfigureBranch(config);
|
await checkReconfigureBranch(config);
|
||||||
await repositoryCache.saveCache();
|
await repositoryCache.saveCache();
|
||||||
await pruneStaleBranches(config, branchList);
|
await pruneStaleBranches(config, branchList);
|
||||||
await ensureIssuesClosing();
|
await ensureIssuesClosing();
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { scm } from '../../../modules/platform/scm';
|
||||||
import { getBranchList, setUserRepoConfig } from '../../../util/git';
|
import { getBranchList, setUserRepoConfig } from '../../../util/git';
|
||||||
import { escapeRegExp, regEx } from '../../../util/regex';
|
import { escapeRegExp, regEx } from '../../../util/regex';
|
||||||
import { uniqueStrings } from '../../../util/string';
|
import { uniqueStrings } from '../../../util/string';
|
||||||
import { getReconfigureBranchName } from '../reconfigure';
|
import { getReconfigureBranchName } from '../reconfigure/utils';
|
||||||
|
|
||||||
async function cleanUpBranches(
|
async function cleanUpBranches(
|
||||||
config: RenovateConfig,
|
config: RenovateConfig,
|
||||||
|
|
|
@ -1,242 +1,42 @@
|
||||||
import { mock } from 'jest-mock-extended';
|
|
||||||
import type { RenovateConfig } from '../../../../test/util';
|
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 { GlobalConfig } from '../../../config/global';
|
||||||
import { logger } from '../../../logger';
|
import * as _validate from './validate';
|
||||||
import type { Pr } from '../../../modules/platform/types';
|
import { checkReconfigureBranch } from '.';
|
||||||
import * as _cache from '../../../util/cache/repository';
|
|
||||||
import type { LongCommitSha } from '../../../util/git/types';
|
|
||||||
import * as _merge from '../init/merge';
|
|
||||||
import { validateReconfigureBranch } from '.';
|
|
||||||
|
|
||||||
jest.mock('../../../util/cache/repository');
|
jest.mock('./validate');
|
||||||
jest.mock('../../../util/fs');
|
|
||||||
jest.mock('../../../util/git');
|
|
||||||
jest.mock('../init/merge');
|
|
||||||
|
|
||||||
const cache = mocked(_cache);
|
const validate = mocked(_validate);
|
||||||
const merge = mocked(_merge);
|
|
||||||
|
|
||||||
describe('workers/repository/reconfigure/index', () => {
|
describe('workers/repository/reconfigure/index', () => {
|
||||||
const config: RenovateConfig = {
|
const config: RenovateConfig = {
|
||||||
branchPrefix: 'prefix/',
|
branchPrefix: 'prefix/',
|
||||||
baseBranch: 'base',
|
baseBranch: 'base',
|
||||||
statusCheckNames: partial<RenovateConfig['statusCheckNames']>({
|
|
||||||
configValidation: 'renovate/config-validation',
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
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();
|
GlobalConfig.reset();
|
||||||
|
scm.branchExists.mockResolvedValue(true);
|
||||||
|
validate.validateReconfigureBranch.mockResolvedValue(undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('no effect when running with platform=local', async () => {
|
it('no effect when running with platform=local', async () => {
|
||||||
GlobalConfig.set({ platform: 'local' });
|
GlobalConfig.set({ platform: 'local' });
|
||||||
await validateReconfigureBranch(config);
|
await checkReconfigureBranch(config);
|
||||||
expect(logger.debug).toHaveBeenCalledWith(
|
expect(logger.logger.debug).toHaveBeenCalledWith(
|
||||||
'Not attempting to reconfigure when running with local platform',
|
'Not attempting to reconfigure when running with local platform',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('no effect on repo with no reconfigure branch', async () => {
|
it('no effect on repo with no reconfigure branch', async () => {
|
||||||
scm.branchExists.mockResolvedValueOnce(false);
|
scm.branchExists.mockResolvedValueOnce(false);
|
||||||
await validateReconfigureBranch(config);
|
await checkReconfigureBranch(config);
|
||||||
expect(logger.debug).toHaveBeenCalledWith('No reconfigure branch found');
|
expect(logger.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',
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws error if config file not found in reconfigure branch', async () => {
|
it('validates reconfigure branch', async () => {
|
||||||
merge.detectConfigFile.mockResolvedValue(null);
|
await expect(checkReconfigureBranch(config)).toResolve();
|
||||||
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',
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,49 +1,15 @@
|
||||||
import is from '@sindresorhus/is';
|
|
||||||
import JSON5 from 'json5';
|
|
||||||
import { GlobalConfig } from '../../../config/global';
|
import { GlobalConfig } from '../../../config/global';
|
||||||
import type { RenovateConfig } from '../../../config/types';
|
import type { RenovateConfig } from '../../../config/types';
|
||||||
import { validateConfig } from '../../../config/validation';
|
|
||||||
import { logger } from '../../../logger';
|
import { logger } from '../../../logger';
|
||||||
import { platform } from '../../../modules/platform';
|
|
||||||
import { ensureComment } from '../../../modules/platform/comment';
|
|
||||||
import { scm } from '../../../modules/platform/scm';
|
import { scm } from '../../../modules/platform/scm';
|
||||||
import type { BranchStatus } from '../../../types';
|
import { deleteReconfigureBranchCache } from './reconfigure-cache';
|
||||||
import { getCache } from '../../../util/cache/repository';
|
import { getReconfigureBranchName } from './utils';
|
||||||
import { readLocalFile } from '../../../util/fs';
|
import { validateReconfigureBranch } from './validate';
|
||||||
import { getBranchCommit } from '../../../util/git';
|
|
||||||
import { regEx } from '../../../util/regex';
|
|
||||||
import { detectConfigFile } from '../init/merge';
|
|
||||||
import {
|
|
||||||
deleteReconfigureBranchCache,
|
|
||||||
setReconfigureBranchCache,
|
|
||||||
} from './reconfigure-cache';
|
|
||||||
|
|
||||||
async function setBranchStatus(
|
export async function checkReconfigureBranch(
|
||||||
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(
|
|
||||||
config: RenovateConfig,
|
config: RenovateConfig,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
logger.debug('validateReconfigureBranch()');
|
logger.debug('checkReconfigureBranch()');
|
||||||
if (GlobalConfig.get('platform') === 'local') {
|
if (GlobalConfig.get('platform') === 'local') {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
'Not attempting to reconfigure when running with local platform',
|
'Not attempting to reconfigure when running with local platform',
|
||||||
|
@ -51,10 +17,8 @@ export async function validateReconfigureBranch(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const context = config.statusCheckNames?.configValidation;
|
const reconfigureBranch = getReconfigureBranchName(config.branchPrefix!);
|
||||||
|
const branchExists = await scm.branchExists(reconfigureBranch);
|
||||||
const branchName = getReconfigureBranchName(config.branchPrefix!);
|
|
||||||
const branchExists = await scm.branchExists(branchName);
|
|
||||||
|
|
||||||
// this is something the user initiates, so skip if no branch exists
|
// this is something the user initiates, so skip if no branch exists
|
||||||
if (!branchExists) {
|
if (!branchExists) {
|
||||||
|
@ -63,141 +27,5 @@ export async function validateReconfigureBranch(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// look for config file
|
await validateReconfigureBranch(config);
|
||||||
// 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!);
|
|
||||||
}
|
}
|
||||||
|
|
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/mdast": "3.0.15",
|
||||||
"@types/moo": "0.5.9",
|
"@types/moo": "0.5.9",
|
||||||
"@types/ms": "0.7.34",
|
"@types/ms": "0.7.34",
|
||||||
"@types/node": "20.17.10",
|
"@types/node": "20.17.11",
|
||||||
"@types/parse-link-header": "2.0.3",
|
"@types/parse-link-header": "2.0.3",
|
||||||
"@types/punycode": "2.1.4",
|
"@types/punycode": "2.1.4",
|
||||||
"@types/semver": "7.5.8",
|
"@types/semver": "7.5.8",
|
||||||
|
|
130
pnpm-lock.yaml
130
pnpm-lock.yaml
|
@ -470,8 +470,8 @@ importers:
|
||||||
specifier: 0.7.34
|
specifier: 0.7.34
|
||||||
version: 0.7.34
|
version: 0.7.34
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: 20.17.10
|
specifier: 20.17.11
|
||||||
version: 20.17.10
|
version: 20.17.11
|
||||||
'@types/parse-link-header':
|
'@types/parse-link-header':
|
||||||
specifier: 2.0.3
|
specifier: 2.0.3
|
||||||
version: 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)
|
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:
|
eslint-plugin-jest:
|
||||||
specifier: 28.10.0
|
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:
|
eslint-plugin-jest-formatting:
|
||||||
specifier: 3.1.0
|
specifier: 3.1.0
|
||||||
version: 3.1.0(eslint@8.57.1)
|
version: 3.1.0(eslint@8.57.1)
|
||||||
|
@ -564,16 +564,16 @@ importers:
|
||||||
version: 9.1.7
|
version: 9.1.7
|
||||||
jest:
|
jest:
|
||||||
specifier: 29.7.0
|
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:
|
jest-extended:
|
||||||
specifier: 4.0.2
|
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:
|
jest-mock:
|
||||||
specifier: 29.7.0
|
specifier: 29.7.0
|
||||||
version: 29.7.0
|
version: 29.7.0
|
||||||
jest-mock-extended:
|
jest-mock-extended:
|
||||||
specifier: 3.0.7
|
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:
|
jest-snapshot:
|
||||||
specifier: 29.7.0
|
specifier: 29.7.0
|
||||||
version: 29.7.0
|
version: 29.7.0
|
||||||
|
@ -609,10 +609,10 @@ importers:
|
||||||
version: 3.0.3
|
version: 3.0.3
|
||||||
ts-jest:
|
ts-jest:
|
||||||
specifier: 29.2.5
|
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:
|
ts-node:
|
||||||
specifier: 10.9.2
|
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:
|
type-fest:
|
||||||
specifier: 4.31.0
|
specifier: 4.31.0
|
||||||
version: 4.31.0
|
version: 4.31.0
|
||||||
|
@ -2121,8 +2121,8 @@ packages:
|
||||||
'@types/ms@0.7.34':
|
'@types/ms@0.7.34':
|
||||||
resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
|
resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
|
||||||
|
|
||||||
'@types/node@20.17.10':
|
'@types/node@20.17.11':
|
||||||
resolution: {integrity: sha512-/jrvh5h6NXhEauFFexRin69nA0uHJ5gwk4iDivp/DeoEua3uwCUto6PC86IpRITBOs4+6i2I56K5x5b6WYGXHA==}
|
resolution: {integrity: sha512-Ept5glCK35R8yeyIeYlRIZtX6SLRyqMhOFTgj5SOkMpLTdw3SEHI9fHx60xaUZ+V1aJxQJODE+7/j5ocZydYTg==}
|
||||||
|
|
||||||
'@types/normalize-package-data@2.4.4':
|
'@types/normalize-package-data@2.4.4':
|
||||||
resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
|
resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
|
||||||
|
@ -7496,27 +7496,27 @@ snapshots:
|
||||||
'@jest/console@29.7.0':
|
'@jest/console@29.7.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
jest-message-util: 29.7.0
|
jest-message-util: 29.7.0
|
||||||
jest-util: 29.7.0
|
jest-util: 29.7.0
|
||||||
slash: 3.0.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:
|
dependencies:
|
||||||
'@jest/console': 29.7.0
|
'@jest/console': 29.7.0
|
||||||
'@jest/reporters': 29.7.0
|
'@jest/reporters': 29.7.0
|
||||||
'@jest/test-result': 29.7.0
|
'@jest/test-result': 29.7.0
|
||||||
'@jest/transform': 29.7.0
|
'@jest/transform': 29.7.0
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
ansi-escapes: 4.3.2
|
ansi-escapes: 4.3.2
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
ci-info: 3.9.0
|
ci-info: 3.9.0
|
||||||
exit: 0.1.2
|
exit: 0.1.2
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
jest-changed-files: 29.7.0
|
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-haste-map: 29.7.0
|
||||||
jest-message-util: 29.7.0
|
jest-message-util: 29.7.0
|
||||||
jest-regex-util: 29.6.3
|
jest-regex-util: 29.6.3
|
||||||
|
@ -7541,7 +7541,7 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jest/fake-timers': 29.7.0
|
'@jest/fake-timers': 29.7.0
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
jest-mock: 29.7.0
|
jest-mock: 29.7.0
|
||||||
|
|
||||||
'@jest/expect-utils@29.4.1':
|
'@jest/expect-utils@29.4.1':
|
||||||
|
@ -7563,7 +7563,7 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
'@sinonjs/fake-timers': 10.3.0
|
'@sinonjs/fake-timers': 10.3.0
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
jest-message-util: 29.7.0
|
jest-message-util: 29.7.0
|
||||||
jest-mock: 29.7.0
|
jest-mock: 29.7.0
|
||||||
jest-util: 29.7.0
|
jest-util: 29.7.0
|
||||||
|
@ -7585,7 +7585,7 @@ snapshots:
|
||||||
'@jest/transform': 29.7.0
|
'@jest/transform': 29.7.0
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
'@jridgewell/trace-mapping': 0.3.25
|
'@jridgewell/trace-mapping': 0.3.25
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
collect-v8-coverage: 1.0.2
|
collect-v8-coverage: 1.0.2
|
||||||
exit: 0.1.2
|
exit: 0.1.2
|
||||||
|
@ -7655,7 +7655,7 @@ snapshots:
|
||||||
'@jest/schemas': 29.6.3
|
'@jest/schemas': 29.6.3
|
||||||
'@types/istanbul-lib-coverage': 2.0.6
|
'@types/istanbul-lib-coverage': 2.0.6
|
||||||
'@types/istanbul-reports': 3.0.4
|
'@types/istanbul-reports': 3.0.4
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
'@types/yargs': 17.0.33
|
'@types/yargs': 17.0.33
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
|
|
||||||
|
@ -8703,7 +8703,7 @@ snapshots:
|
||||||
|
|
||||||
'@types/aws4@1.11.6':
|
'@types/aws4@1.11.6':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
|
|
||||||
'@types/babel__core@7.20.5':
|
'@types/babel__core@7.20.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -8728,27 +8728,27 @@ snapshots:
|
||||||
|
|
||||||
'@types/better-sqlite3@7.6.12':
|
'@types/better-sqlite3@7.6.12':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
|
|
||||||
'@types/breejs__later@4.1.5': {}
|
'@types/breejs__later@4.1.5': {}
|
||||||
|
|
||||||
'@types/bunyan@1.8.11':
|
'@types/bunyan@1.8.11':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
|
|
||||||
'@types/bunyan@1.8.9':
|
'@types/bunyan@1.8.9':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
|
|
||||||
'@types/cacache@17.0.2':
|
'@types/cacache@17.0.2':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
|
|
||||||
'@types/cacheable-request@6.0.3':
|
'@types/cacheable-request@6.0.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/http-cache-semantics': 4.0.4
|
'@types/http-cache-semantics': 4.0.4
|
||||||
'@types/keyv': 3.1.4
|
'@types/keyv': 3.1.4
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
'@types/responselike': 1.0.3
|
'@types/responselike': 1.0.3
|
||||||
|
|
||||||
'@types/callsite@1.0.34': {}
|
'@types/callsite@1.0.34': {}
|
||||||
|
@ -8779,7 +8779,7 @@ snapshots:
|
||||||
'@types/fs-extra@11.0.4':
|
'@types/fs-extra@11.0.4':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/jsonfile': 6.1.4
|
'@types/jsonfile': 6.1.4
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
|
|
||||||
'@types/git-url-parse@9.0.3': {}
|
'@types/git-url-parse@9.0.3': {}
|
||||||
|
|
||||||
|
@ -8789,7 +8789,7 @@ snapshots:
|
||||||
|
|
||||||
'@types/graceful-fs@4.1.9':
|
'@types/graceful-fs@4.1.9':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
|
|
||||||
'@types/http-cache-semantics@4.0.4': {}
|
'@types/http-cache-semantics@4.0.4': {}
|
||||||
|
|
||||||
|
@ -8815,13 +8815,13 @@ snapshots:
|
||||||
|
|
||||||
'@types/jsonfile@6.1.4':
|
'@types/jsonfile@6.1.4':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
|
|
||||||
'@types/katex@0.16.7': {}
|
'@types/katex@0.16.7': {}
|
||||||
|
|
||||||
'@types/keyv@3.1.4':
|
'@types/keyv@3.1.4':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
|
|
||||||
'@types/linkify-it@5.0.0': {}
|
'@types/linkify-it@5.0.0': {}
|
||||||
|
|
||||||
|
@ -8840,7 +8840,7 @@ snapshots:
|
||||||
|
|
||||||
'@types/marshal@0.5.3':
|
'@types/marshal@0.5.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
|
|
||||||
'@types/mdast@3.0.15':
|
'@types/mdast@3.0.15':
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -8856,7 +8856,7 @@ snapshots:
|
||||||
|
|
||||||
'@types/ms@0.7.34': {}
|
'@types/ms@0.7.34': {}
|
||||||
|
|
||||||
'@types/node@20.17.10':
|
'@types/node@20.17.11':
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 6.19.8
|
undici-types: 6.19.8
|
||||||
|
|
||||||
|
@ -8870,7 +8870,7 @@ snapshots:
|
||||||
|
|
||||||
'@types/responselike@1.0.3':
|
'@types/responselike@1.0.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
|
|
||||||
'@types/semver-stable@3.0.2': {}
|
'@types/semver-stable@3.0.2': {}
|
||||||
|
|
||||||
|
@ -8890,7 +8890,7 @@ snapshots:
|
||||||
|
|
||||||
'@types/tar@6.1.13':
|
'@types/tar@6.1.13':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
minipass: 4.2.8
|
minipass: 4.2.8
|
||||||
|
|
||||||
'@types/tmp@0.2.6': {}
|
'@types/tmp@0.2.6': {}
|
||||||
|
@ -8915,7 +8915,7 @@ snapshots:
|
||||||
|
|
||||||
'@types/yauzl@2.10.3':
|
'@types/yauzl@2.10.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
optional: true
|
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)':
|
'@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:
|
optionalDependencies:
|
||||||
typescript: 5.7.2
|
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:
|
dependencies:
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
exit: 0.1.2
|
exit: 0.1.2
|
||||||
graceful-fs: 4.2.11
|
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
|
jest-util: 29.7.0
|
||||||
prompts: 2.4.2
|
prompts: 2.4.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
|
@ -10109,13 +10109,13 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 8.57.1
|
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:
|
dependencies:
|
||||||
'@typescript-eslint/utils': 8.19.0(eslint@8.57.1)(typescript@5.7.2)
|
'@typescript-eslint/utils': 8.19.0(eslint@8.57.1)(typescript@5.7.2)
|
||||||
eslint: 8.57.1
|
eslint: 8.57.1
|
||||||
optionalDependencies:
|
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)
|
'@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:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
- typescript
|
- typescript
|
||||||
|
@ -11146,7 +11146,7 @@ snapshots:
|
||||||
'@jest/expect': 29.7.0
|
'@jest/expect': 29.7.0
|
||||||
'@jest/test-result': 29.7.0
|
'@jest/test-result': 29.7.0
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
co: 4.6.0
|
co: 4.6.0
|
||||||
dedent: 1.5.3
|
dedent: 1.5.3
|
||||||
|
@ -11166,16 +11166,16 @@ snapshots:
|
||||||
- babel-plugin-macros
|
- babel-plugin-macros
|
||||||
- supports-color
|
- 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:
|
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/test-result': 29.7.0
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
chalk: 4.1.2
|
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
|
exit: 0.1.2
|
||||||
import-local: 3.2.0
|
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-util: 29.7.0
|
||||||
jest-validate: 29.7.0
|
jest-validate: 29.7.0
|
||||||
yargs: 17.7.2
|
yargs: 17.7.2
|
||||||
|
@ -11185,7 +11185,7 @@ snapshots:
|
||||||
- supports-color
|
- supports-color
|
||||||
- ts-node
|
- 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:
|
dependencies:
|
||||||
'@babel/core': 7.26.0
|
'@babel/core': 7.26.0
|
||||||
'@jest/test-sequencer': 29.7.0
|
'@jest/test-sequencer': 29.7.0
|
||||||
|
@ -11210,8 +11210,8 @@ snapshots:
|
||||||
slash: 3.0.0
|
slash: 3.0.0
|
||||||
strip-json-comments: 3.1.1
|
strip-json-comments: 3.1.1
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
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)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- babel-plugin-macros
|
- babel-plugin-macros
|
||||||
- supports-color
|
- supports-color
|
||||||
|
@ -11240,16 +11240,16 @@ snapshots:
|
||||||
'@jest/environment': 29.7.0
|
'@jest/environment': 29.7.0
|
||||||
'@jest/fake-timers': 29.7.0
|
'@jest/fake-timers': 29.7.0
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
jest-mock: 29.7.0
|
jest-mock: 29.7.0
|
||||||
jest-util: 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:
|
dependencies:
|
||||||
jest-diff: 29.7.0
|
jest-diff: 29.7.0
|
||||||
jest-get-type: 29.6.3
|
jest-get-type: 29.6.3
|
||||||
optionalDependencies:
|
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: {}
|
jest-get-type@29.6.3: {}
|
||||||
|
|
||||||
|
@ -11257,7 +11257,7 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
'@types/graceful-fs': 4.1.9
|
'@types/graceful-fs': 4.1.9
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
anymatch: 3.1.3
|
anymatch: 3.1.3
|
||||||
fb-watchman: 2.0.2
|
fb-watchman: 2.0.2
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
|
@ -11300,16 +11300,16 @@ snapshots:
|
||||||
slash: 3.0.0
|
slash: 3.0.0
|
||||||
stack-utils: 2.0.6
|
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:
|
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)
|
ts-essentials: 10.0.4(typescript@5.7.2)
|
||||||
typescript: 5.7.2
|
typescript: 5.7.2
|
||||||
|
|
||||||
jest-mock@29.7.0:
|
jest-mock@29.7.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
jest-util: 29.7.0
|
jest-util: 29.7.0
|
||||||
|
|
||||||
jest-pnp-resolver@1.2.3(jest-resolve@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/test-result': 29.7.0
|
||||||
'@jest/transform': 29.7.0
|
'@jest/transform': 29.7.0
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
emittery: 0.13.1
|
emittery: 0.13.1
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
|
@ -11372,7 +11372,7 @@ snapshots:
|
||||||
'@jest/test-result': 29.7.0
|
'@jest/test-result': 29.7.0
|
||||||
'@jest/transform': 29.7.0
|
'@jest/transform': 29.7.0
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
cjs-module-lexer: 1.4.1
|
cjs-module-lexer: 1.4.1
|
||||||
collect-v8-coverage: 1.0.2
|
collect-v8-coverage: 1.0.2
|
||||||
|
@ -11418,7 +11418,7 @@ snapshots:
|
||||||
jest-util@29.7.0:
|
jest-util@29.7.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
ci-info: 3.9.0
|
ci-info: 3.9.0
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
|
@ -11437,7 +11437,7 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jest/test-result': 29.7.0
|
'@jest/test-result': 29.7.0
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
ansi-escapes: 4.3.2
|
ansi-escapes: 4.3.2
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
emittery: 0.13.1
|
emittery: 0.13.1
|
||||||
|
@ -11446,17 +11446,17 @@ snapshots:
|
||||||
|
|
||||||
jest-worker@29.7.0:
|
jest-worker@29.7.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
jest-util: 29.7.0
|
jest-util: 29.7.0
|
||||||
merge-stream: 2.0.0
|
merge-stream: 2.0.0
|
||||||
supports-color: 8.1.1
|
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:
|
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
|
'@jest/types': 29.6.3
|
||||||
import-local: 3.2.0
|
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:
|
transitivePeerDependencies:
|
||||||
- '@types/node'
|
- '@types/node'
|
||||||
- babel-plugin-macros
|
- babel-plugin-macros
|
||||||
|
@ -12619,7 +12619,7 @@ snapshots:
|
||||||
'@protobufjs/path': 1.1.2
|
'@protobufjs/path': 1.1.2
|
||||||
'@protobufjs/pool': 1.1.0
|
'@protobufjs/pool': 1.1.0
|
||||||
'@protobufjs/utf8': 1.1.0
|
'@protobufjs/utf8': 1.1.0
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
long: 5.2.3
|
long: 5.2.3
|
||||||
|
|
||||||
protocols@2.0.1: {}
|
protocols@2.0.1: {}
|
||||||
|
@ -13382,12 +13382,12 @@ snapshots:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
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.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:
|
dependencies:
|
||||||
bs-logger: 0.2.6
|
bs-logger: 0.2.6
|
||||||
ejs: 3.1.10
|
ejs: 3.1.10
|
||||||
fast-json-stable-stringify: 2.1.0
|
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
|
jest-util: 29.7.0
|
||||||
json5: 2.2.3
|
json5: 2.2.3
|
||||||
lodash.memoize: 4.1.2
|
lodash.memoize: 4.1.2
|
||||||
|
@ -13401,14 +13401,14 @@ snapshots:
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
babel-jest: 29.7.0(@babel/core@7.26.0)
|
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:
|
dependencies:
|
||||||
'@cspotcode/source-map-support': 0.8.1
|
'@cspotcode/source-map-support': 0.8.1
|
||||||
'@tsconfig/node10': 1.0.11
|
'@tsconfig/node10': 1.0.11
|
||||||
'@tsconfig/node12': 1.0.11
|
'@tsconfig/node12': 1.0.11
|
||||||
'@tsconfig/node14': 1.0.3
|
'@tsconfig/node14': 1.0.3
|
||||||
'@tsconfig/node16': 1.0.4
|
'@tsconfig/node16': 1.0.4
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.11
|
||||||
acorn: 8.14.0
|
acorn: 8.14.0
|
||||||
acorn-walk: 8.3.4
|
acorn-walk: 8.3.4
|
||||||
arg: 4.1.3
|
arg: 4.1.3
|
||||||
|
|
|
@ -5,19 +5,19 @@ ARG BASE_IMAGE_TYPE=slim
|
||||||
# --------------------------------------
|
# --------------------------------------
|
||||||
# slim image
|
# 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
|
# 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
|
ENV RENOVATE_BINARY_SOURCE=global
|
||||||
|
|
||||||
# --------------------------------------
|
# --------------------------------------
|
||||||
# build image
|
# 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
|
# We want a specific node version here
|
||||||
# renovate: datasource=node-version
|
# renovate: datasource=node-version
|
||||||
|
|
Loading…
Reference in a new issue