Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SOLR-17602: Per-Module Dependency Locking #2925

Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a27855a
Introduce platform module
malliaridis Dec 16, 2024
6bce484
Cleanup version catalog
malliaridis Dec 16, 2024
2d195bd
Fix dependency conflicts when errorprone enabled
malliaridis Dec 16, 2024
5cdbd20
Add comment about platform module
malliaridis Dec 16, 2024
1fc5df7
Merge remote-tracking branch 'apache/main' into feature/optimize-depe…
malliaridis Dec 23, 2024
41ca82c
Enable per-module dependency lockfiles
malliaridis Dec 23, 2024
721f0aa
Fix renderJavaDoc task dependencies for platform
malliaridis Jan 3, 2025
9cf9d80
Add lockfile for llm module
malliaridis Jan 3, 2025
7128d69
Merge remote-tracking branch 'apache/main' into feature/SOLR-17602/pe…
malliaridis Jan 3, 2025
b577da6
Generate lock files with errorprone enabled
malliaridis Jan 3, 2025
984c0c7
Support the common usage of write-locks
malliaridis Jan 4, 2025
0783610
Fix testAnnotationProcessor missing for errorprone
malliaridis Jan 11, 2025
69240ad
Merge remote-tracking branch 'apache/main' into feature/SOLR-17602/pe…
malliaridis Jan 11, 2025
ac799e3
Update locks
malliaridis Jan 11, 2025
039734b
Update renovate config to use new task
malliaridis Jan 11, 2025
e87dbbe
Update documentation
malliaridis Jan 11, 2025
1e806d7
Replace references to versions.props, versions.lock and writeLocks
malliaridis Jan 11, 2025
31537e5
Add platform module to include constraints
malliaridis Jan 11, 2025
52c092f
Update locks
malliaridis Jan 11, 2025
ff15ff2
Fix missing flag in renovate config
malliaridis Jan 20, 2025
bc6ae63
Remove unused dependency of "dependencies" task
malliaridis Jan 20, 2025
ebaeb44
Merge branch 'main' into feature/SOLR-17602/per-module-dependency-loc…
malliaridis Jan 20, 2025
ff8b204
Update lockfiles
malliaridis Jan 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Ignore all differences in line endings for the lock file.
# Ignore all differences in line endings for lock files.
versions.lock text eol=lf
**/*.lockfile text eol=lf

# Gradle files are always in LF.
*.gradle text eol=lf
9 changes: 5 additions & 4 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
dependencies:
- changed-files:
- any-glob-to-any-file:
- gradle/libs.versions.toml # Solr 10+
- versions.props # Solr < v10
- versions.lock
- solr/licenses/**
- "gradle/libs.versions.toml" # Solr 10+
- "**/*.lockfile"
- "versions.props" # Solr < v10
- "versions.lock"
- "solr/licenses/**"

# Add 'documentation' label to any changes within ref-guide or dev-docs
documentation:
Expand Down
2 changes: 1 addition & 1 deletion .github/renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"enabledManagers": ["gradle", "github-actions"],
"includePaths": ["gradle/libs.versions.toml", "versions.*", "build.gradle", ".github/workflows/*"],
"postUpgradeTasks": {
"commands": ["./gradlew writeLocks", "./gradlew updateLicenses"],
"commands": ["./gradlew resolveAndLockAll", "./gradlew updateLicenses"],
"fileFilters": ["solr/licenses/*.sha1"],
"executionMode": "branch"
},
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/bin-solr-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
with:
path: |
~/.gradle/caches
key: ${{ runner.os }}-gradle-binsolr-${{ hashFiles('versions.lock') }}
key: ${{ runner.os }}-gradle-binsolr-${{ hashFiles('**/*.lockfile') }}
restore-keys: |
${{ runner.os }}-gradle-binsolr-
${{ runner.os }}-gradle-
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docker-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
with:
path: |
~/.gradle/caches
key: ${{ runner.os }}-gradle-docker-${{ hashFiles('versions.lock') }}
key: ${{ runner.os }}-gradle-docker-${{ hashFiles('**/*.lockfile') }}
restore-keys: |
${{ runner.os }}-gradle-docker-
${{ runner.os }}-gradle-
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/gradle-precommit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
with:
path: |
~/.gradle/caches
key: ${{ runner.os }}-gradle-precommit-${{ hashFiles('versions.lock') }}
key: ${{ runner.os }}-gradle-precommit-${{ hashFiles('**/*.lockfile') }}
restore-keys: |
${{ runner.os }}-gradle-precommit-
${{ runner.os }}-gradle-
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/solrj-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
with:
path: |
~/.gradle/caches
key: ${{ runner.os }}-gradle-solrj-${{ hashFiles('versions.lock') }}
key: ${{ runner.os }}-gradle-solrj-${{ hashFiles('**/*.lockfile') }}
restore-keys: |
${{ runner.os }}-gradle-solrj-
${{ runner.os }}-gradle-
Expand Down
1 change: 0 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ plugins {
id 'base'
id 'solr.build-infra'

alias(libs.plugins.carrotsearch.dependencychecks)
alias(libs.plugins.owasp.dependencycheck)
alias(libs.plugins.cutterslade.analyze)
alias(libs.plugins.benmanes.versions)
Expand Down
51 changes: 26 additions & 25 deletions dev-docs/dependency-upgrades.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -30,43 +30,40 @@ In order to upgrade a dependency, you need to run through a number of steps:

1. Identify the available versions from e.g. https://search.maven.org[Maven Central]
2. Update the version in `gradle/libs.versions.toml` file
3. Run `./gradlew writeLocks` to re-generate `versions.lock`. Note that this may cause a cascading effect where
3. Run `./gradlew resolveAndLockAll` to re-generate lockfiles. Note that this may cause a cascading effect where
the locked version of other dependencies also change.
4. In case of a conflict, resolve the conflict according to `help/dependencies.txt`
5. Check if there are any constraints that are obsolete after the dependency update
6. Update the license and notice files of the changed dependencies. See `help/dependencies.txt` for
details.
7. Run `./gradlew updateLicenses` to re-generate SHA1 checksums of the new jar files.
8. Once in a while, a new version of a dependency will transitively bring in brand-new dependencies.
5. Update the license and notice files of the changed dependencies. See `help/dependencies.txt` for details.
6. Run `./gradlew updateLicenses` to re-generate SHA1 checksums of the new jar files.
7. Once in a while, a new version of a dependency will transitively bring in brand-new dependencies.
You'll need to decide whether to keep or exclude them. See `help/dependencies.txt` for details.

=== Reviewing Constraints
=== Constraints and Version Alignment

The constraints are defined in gradle/validation/dependencies.gradle. There, if the updated dependency is listed,
the constraint can be reviewed, updated or removed.
To sync the version of direct and transitive dependencies across the project, we iterate in the `:platform` module
over the libraries defined in `gradle/libs.version.toml` and add them as constraints. Then, we use the module in
main modules like `:solr:api` and `:solr:core` and transitively pass down to all other modules the constraints.

The constraints fall into two "groups". In the first group there are dependency constraints from dependencies
that our project directly includes and require version alignment to sync the versions across all transitive
dependencies. In the second group are dependencies that are only present as transitive dependencies.
There, we try to follow the convention to provide additional information with "which dependencies use what version",
so that the next person reviewing the constraint does not have to look it up. However, this is quite time-consuming
to analyze the dependencies and therefore subject to change.
If a new module does not depend on another module that already includes `:platform` as a platform dependency, it should
explicitly add it to sync the versions with the rest of the project. `:solr:server` is one case where this is necessary.

In order to review a constraint, you have to check if the updated dependency is mentioned in any of the constraints,
either as a reason for another dependency constraint or as the constraint's dependency. Removing temporarily
a constraint, the task writeLocks will fail if the constraint is still required.
=== Addressing Security Vulnerabilities

This process and the constraints of dependencies.gradle are not optimal, as it is quite time-consuming and not obvious
by just looking at it. We just haven't found yet a more efficient way to maintain these constraints.
When it comes to security vulnerabilities that are found in direct or transitive dependencies, the recommended way to
address them is to update the specific library if there is a new release that solves this issue. For both direct and
transitive dependencies, we simply have to update the version as described above.

== Renovate bot Pull Requests
In case it is a transitive dependency that is not directly used, you can simply add it to `libs.versions.toml` as you
would with any other dependency. The dependency resolution approach defined in `:platform` will handle the rest.
Don't forget to add a `# @keep` note with a reference to the vulnerable version and CVE that is fixed with the explicit
definition of the library and new version. This way it is easier to keep track of unreferenced dependencies in our
libraries toml file, and we can clean them up once the libraries using the modules are updated.
Comment on lines +56 to +60
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooooh, now I see what this is for.


The renovate bot may be replaced in the future with dependabot and this section may only be relevant for older
versions (<10.0). See https://lists.apache.org/thread/1sb9ttv3lp57z2yod1htx1fykp5sj73z for updates.
== Renovate bot Pull Requests

A member of the Solr community operates a Github bot running https://github.com/renovatebot/renovate[Renovate], which
files Pull Requests to Solr with dependency upgrade proposals. The PRs are labeled `dependencies` and do include
changes resulting from `./gradlew writeLocks` and `updateLicenses`.
changes resulting from the gradle tasks `resolveAndLockAll` and `updateLicenses`.

Community members and committers can then review, and if manual changes are needed, help bring the PR to completion.
For many dependencies, a changelog is included in the PR text, which may help guide the upgrade decision.
Expand All @@ -78,9 +75,13 @@ that will get its own separate Pull Request, so you can choose.
If an upgrade is decided, simply merge (and backport) the PR. To skip an upgrade, close the PR. If a PR is left open,
it will be re-used and auto updated whenever a newer patch- or minor version gets available. Thus, one can reduce
churn from frequently-updated dependencies by delaying merge until a few weeks before a new release. One can also
choose to change to a less frequent schedule or disable the bot, by editing `renovate.json`
choose to change to a less frequent schedule or disable the bot, by editing `renovate.json`.

Please note that Solr version prior to 10.X use a versions resolution plugin that uses `versions.lock` instead of
`libs.version.toml`. Therefore, changes cannot be backported via cherry-pick.

=== Configuring renovate.json

While the bot runs on a https://github.com/solrbot/renovate-github-action[GitHub repo external to the project],
the bot behavior can be tailored by editing `.github/renovate.json` in this project.
See https://docs.renovatebot.com[Renovatebot docs] for available options.
4 changes: 2 additions & 2 deletions dev-docs/lucene-upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ Create a new branch locally e.g. `git checkout -b lucene940 -t origin/main` for
+ org.apache.lucene:*=9.4.0
```

### `versions.lock` update
### lockfiles update

```
gradlew :writeLocks
gradlew :resolveAndLockAll
```

### `solr/licenses` update
Expand Down
2 changes: 1 addition & 1 deletion dev-tools/scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ of the other tools in this folder.

optional arguments:
-h, --help show this help message and exit
-l LUCENE_VERSION Optional lucene version. By default will read versions.props
-l LUCENE_VERSION Optional lucene version. By default will read gradle/libs.versions.toml

### releasedJirasRegex.py

Expand Down
4 changes: 2 additions & 2 deletions dev-tools/scripts/addVersion.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import argparse
import re
from configparser import ConfigParser, ExtendedInterpolation
from textwrap import dedent


def update_changes(filename, new_version, init_changes, headers):
print(' adding new section to %s...' % filename, end='', flush=True)
Expand Down Expand Up @@ -115,7 +115,7 @@ def check_lucene_match_version_tests():
def read_config(current_version, current_lucene_version):
parser = argparse.ArgumentParser(description='Add a new version to CHANGES, to Version.java, build.gradle and solrconfig.xml files')
parser.add_argument('version', type=Version.parse, help='New Solr version')
parser.add_argument('-l', dest='lucene_version', type=Version.parse, help='Optional lucene version. By default will read versions.props')
parser.add_argument('-l', dest='lucene_version', type=Version.parse, help='Optional lucene version. By default will read gradle/libs.versions.toml')
newconf = parser.parse_args()
if not newconf.lucene_version:
newconf.lucene_version = current_lucene_version
Expand Down
13 changes: 8 additions & 5 deletions dev-tools/scripts/scriptutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,16 @@
# limitations under the License.

import argparse
import os
import re
import subprocess
import sys
import os
from enum import Enum
import time
import urllib.request, urllib.error, urllib.parse
import urllib.error
import urllib.parse
import urllib.request
from enum import Enum


class Version(object):
def __init__(self, major, minor, bugfix, prerelease):
Expand Down Expand Up @@ -179,7 +182,7 @@ def attemptDownload(urlString, fileName):

version_prop_re = re.compile(r'baseVersion\s*=\s*([\'"])(.*)\1')

lucene_version_prop_re = re.compile(r'org\.apache\.lucene:\*=(.*?)\n')
lucene_version_prop_re = re.compile(r'^apache-lucene\s*=\s*"([a-zA-Z0-9\.\-]+)"')

def find_current_version():
script_path = os.path.dirname(os.path.realpath(__file__))
Expand All @@ -190,7 +193,7 @@ def find_current_version():
def find_current_lucene_version():
script_path = os.path.dirname(os.path.realpath(__file__))
top_level_dir = os.path.join(os.path.abspath("%s/" % script_path), os.path.pardir, os.path.pardir)
versions_file = open('%s/versions.props' % top_level_dir).read()
versions_file = open('%s/gradle/libs.versions.toml' % top_level_dir).read()
return lucene_version_prop_re.search(versions_file).group(1).strip()


Expand Down
5 changes: 3 additions & 2 deletions dev-tools/scripts/smokeTestRelease.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import hashlib
import http.client
import os
import platform
import re
import shutil
import subprocess
Expand All @@ -35,6 +34,8 @@
import xml.etree.ElementTree as ET
import zipfile
from collections import namedtuple

import platform
import scriptutil

# This tool expects to find /solr off the base URL. You
Expand Down Expand Up @@ -626,7 +627,7 @@ def verifyUnpacked(java, artifact, unpackPath, gitRevision, version, testArgs):

if isSrc:
expected_src_root_folders = ['buildSrc', 'dev-docs', 'dev-tools', 'gradle', 'help', 'solr']
expected_src_root_files = ['build.gradle', 'gradlew', 'gradlew.bat', 'settings.gradle', 'versions.lock', 'versions.props']
expected_src_root_files = ['build.gradle', 'gradlew', 'gradlew.bat', 'settings.gradle', 'settings.gradle.lockfile']
expected_src_solr_files = ['build.gradle']
expected_src_solr_folders = ['benchmark', 'bin', 'modules', 'api', 'core', 'cross-dc-manager', 'docker', 'documentation', 'example', 'licenses', 'packaging', 'distribution', 'prometheus-exporter', 'server', 'solr-ref-guide', 'solrj', 'solrj-streaming', 'solrj-zookeeper', 'test-framework', 'webapp', '.gitignore', '.gitattributes']
is_in_list(in_root_folder, expected_src_root_folders)
Expand Down
16 changes: 16 additions & 0 deletions gradle.lockfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
commons-cli:commons-cli:1.5.0=ratDeps
commons-io:commons-io:2.11.0=ratDeps
junit:junit:3.8.1=javacc
net.java.dev.javacc:javacc:7.0.12=javacc
org.apache.commons:commons-collections4:4.4=ratDeps
org.apache.commons:commons-compress:1.21=ratDeps
org.apache.commons:commons-lang3:3.12.0=ratDeps
org.apache.rat:apache-rat-api:0.15=ratDeps
org.apache.rat:apache-rat-core:0.15=ratDeps
org.apache.rat:apache-rat-tasks:0.15=ratDeps
org.apache.rat:apache-rat:0.15=ratDeps
org.eclipse.jdt:ecj:3.39.0=ecjDeps
empty=
5 changes: 5 additions & 0 deletions gradle/documentation/render-javadoc.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ configure(project(":solr:test-framework")) {
// Disable Javadoc rendering for these projects.
configure(subprojects.findAll {
it.path in [
':platform',
':solr:solr-ref-guide',
':solr:server',
':solr:webapp']
Expand All @@ -193,6 +194,10 @@ configure(subprojects) {
task.project.configurations.implementation.allDependencies.withType(ProjectDependency).collect { dep ->
def otherProject = dep.dependencyProject
return otherProject.tasks.findByName(task.name)
}.findAll {
// Do not depend on disabled tasks or tasks that do not exist
// :platform module does not have a renderJavaDoc and therefore task is null
it?.enabled == true
}
}
}
Expand Down
9 changes: 1 addition & 8 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,13 @@ bouncycastle = "1.78.1"
# @keep Browserify version used in ref-guide
browserify = "17.0.0"
carrot2-core = "4.5.1"
carrotsearch-dependencychecks = "0.0.9"
carrotsearch-hppc = "0.10.0"
carrotsearch-randomizedtesting = "2.8.1"
# @keep for version alignment
checkerframework = "3.44.0"
codehaus-woodstox = "4.2.2"
commons-cli = "1.9.0"
commons-codec = "1.17.1"
commons-collections = "3.2.2"
commons-io = "2.15.1"
cutterslade-analyze = "1.10.0"
cybozulabs-langdetect = "1.1-20120112"
Expand All @@ -101,8 +99,6 @@ google-cloud-nio = "0.127.3"
google-cloud-storage = "2.27.0"
google-errorprone = "2.31.0"
# @keep for version alignment
google-failureaccess = "1.0.2"
# @keep for version alignment
google-gson = "2.11.0"
google-guava = "33.1.0-jre"
# @keep for version alignment
Expand All @@ -111,7 +107,6 @@ google-j2objc = "3.0.0"
google-javaformat = "1.18.1"
# @keep for version alignment
google-protobuf = "3.25.3"
google-re2j = "1.7"
# @keep Gradle version to run the build
gradle = "8.10"
grpc = "1.65.1"
Expand Down Expand Up @@ -187,7 +182,6 @@ xerial-snappy = "1.1.10.5"

[plugins]
benmanes-versions = { id = "com.github.ben-manes.versions", version.ref = "benmanes-versions" }
carrotsearch-dependencychecks = { id = "com.carrotsearch.gradle.dependencychecks", version.ref = "carrotsearch-dependencychecks" }
cutterslade-analyze = { id = "ca.cutterslade.analyze", version.ref = "cutterslade-analyze" }
diffplug-spotless = { id = "com.diffplug.spotless", version.ref = "diffplug-spotless" }
littlerobots-versioncatalogupdate = { id = "nl.littlerobots.version-catalog-update", version.ref = "littlerobots-versioncatalogupdate" }
Expand Down Expand Up @@ -294,7 +288,6 @@ checkerframework-qual = { module = "org.checkerframework:checker-qual", version.
codehaus-woodstox-stax2api = { module = "org.codehaus.woodstox:stax2-api", version.ref = "codehaus-woodstox" }
commonscli-commonscli = { module = "commons-cli:commons-cli", version.ref = "commons-cli" }
commonscodec-commonscodec = { module = "commons-codec:commons-codec", version.ref = "commons-codec" }
commonscollections-commonscollections = { module = "commons-collections:commons-collections", version.ref = "commons-collections" }
commonsio-commonsio = { module = "commons-io:commons-io", version.ref = "commons-io" }
cybozulabs-langdetect = { module = "com.cybozu.labs:langdetect", version.ref = "cybozulabs-langdetect" }
dropwizard-metrics-core = { module = "io.dropwizard.metrics:metrics-core", version.ref = "dropwizard-metrics" }
Expand Down Expand Up @@ -362,7 +355,6 @@ google-j2objc-annotations = { module = "com.google.j2objc:j2objc-annotations", v
# @keep transitive dependency for version alignment
google-protobuf-java = { module = "com.google.protobuf:protobuf-java", version.ref = "google-protobuf" }
google-protobuf-javautils = { module = "com.google.protobuf:protobuf-java-util", version.ref = "google-protobuf" }
google-re2j = { module = "com.google.re2j:re2j", version.ref = "google-re2j" }
# @keep transitive dependency for version alignment
grpc-api = { module = "io.grpc:grpc-api", version.ref = "grpc" }
# @keep transitive dependency for version alignment
Expand Down Expand Up @@ -420,6 +412,7 @@ netty-tcnative-boringssl = { module = "io.netty:netty-tcnative-boringssl-static"
netty-tcnative-classes = { module = "io.netty:netty-tcnative-classes", version.ref = "netty-tcnative" }
# @keep transitive dependency for version alignment
netty-transport-classes-epoll = { module = "io.netty:netty-transport-classes-epoll", version.ref = "netty" }
# @keep transitive dependency for version alignment
netty-transport-native-epoll = { module = "io.netty:netty-transport-native-epoll", version.ref = "netty" }
nimbusds-josejwt = { module = "com.nimbusds:nimbus-jose-jwt", version.ref = "nimbusds-josejwt" }
openjdk-jmh-core = { module = "org.openjdk.jmh:jmh-core", version.ref = "openjdk-jmh" }
Expand Down
Loading
Loading