mirror of
https://codeberg.org/PGPainless/sop-java.git
synced 2025-09-09 02:09:42 +02:00
Compare commits
70 commits
69dd846667
...
7ee856f9b9
Author | SHA1 | Date | |
---|---|---|---|
7ee856f9b9 | |||
b42b966866 | |||
0b68d9f04e | |||
db315e9ed2 | |||
966ea3c3a6 | |||
f6d7af2dab | |||
3367450f94 | |||
54782d2a22 | |||
83eafd8817 | |||
6c7fcfccce | |||
04f7a5d30d | |||
88d3301daa | |||
6fc849bb6a | |||
5bebdb779a | |||
3e6e47ed50 | |||
023b4ea45b | |||
dd07d10350 | |||
859bb5bdde | |||
edb405d79e | |||
57e2f8391b | |||
51ba24ddbe | |||
d1893c5ea0 | |||
c145f8bb37 | |||
924cfaa140 | |||
f2602bb413 | |||
97e91f50ab | |||
690ba6dc16 | |||
9ec3cc911b | |||
f92a73a5ad | |||
2b6015f59a | |||
84e381fe8e | |||
b1e1a2283f | |||
b3b8da4e35 | |||
ca65cbe668 | |||
4eb6d1fdcb | |||
594b9029b2 | |||
471947ef9c | |||
1fd3161851 | |||
a8a753536a | |||
eadea08d3c | |||
547acdb740 | |||
bb026bcbeb | |||
e7778cb0d2 | |||
ac00b68694 | |||
e6c9d6f43d | |||
c136d40fa7 | |||
f35fd6c1ae | |||
375dd65789 | |||
42a16a4f6d | |||
b3f446fe8d | |||
1958614fac | |||
a09f10fe85 | |||
a90f9be0e4 | |||
63d8045224 | |||
7014dbcfb7 | |||
354ef8841a | |||
261ac212b8 | |||
f7530e3263 | |||
8d7e89098f | |||
a523270395 | |||
d25a424adc | |||
2d4bc24c64 | |||
65945e0094 | |||
4388f00dc0 | |||
1df5747549 | |||
ae2389cabf | |||
34a05e96a1 | |||
7b04275625 | |||
a0e7356757 | |||
173bc55eb9 |
44 changed files with 177 additions and 38 deletions
|
@ -16,4 +16,6 @@ steps:
|
|||
- gradle check javadocAll
|
||||
# Code has coverage
|
||||
- gradle jacocoRootReport coveralls
|
||||
secrets: [coveralls_repo_token]
|
||||
environment:
|
||||
coveralls_repo_token:
|
||||
from_secret: coveralls_repo_token
|
||||
|
|
40
CHANGELOG.md
40
CHANGELOG.md
|
@ -6,10 +6,37 @@ SPDX-License-Identifier: Apache-2.0
|
|||
|
||||
# Changelog
|
||||
|
||||
## 10.0.1-SNAPSHOT
|
||||
## 10.1.1-SNAPSHOT
|
||||
- Prepare jar files for use in native images, e.g. using GraalVM by generating and including
|
||||
configuration files for reflection, resources and dynamic proxies.
|
||||
|
||||
## 10.1.0
|
||||
- `sop-java`:
|
||||
- Remove `label()` option from `armor()` subcommand
|
||||
- Move test-fixtures artifact built with the `testFixtures` plugin into
|
||||
its own module `sop-java-testfixtures`, which can be consumed by maven builds.
|
||||
- `sop-java-picocli`:
|
||||
- Properly map `MissingParameterException` to `MissingArg` exit code
|
||||
- As a workaround for native builds using graalvm:
|
||||
- Do not re-set message bundles dynamically (fails in native builds)
|
||||
- Prevent an unmatched argument error
|
||||
|
||||
## 10.0.3
|
||||
- CLI `change-key-password`: Fix indirect parameter passing for new and old passwords (thanks to @dkg for the report)
|
||||
- Backport: `revoke-key`: Allow for multiple password options
|
||||
|
||||
## 10.0.2
|
||||
- Downgrade `logback-core` to `1.2.13`
|
||||
|
||||
## 10.0.1
|
||||
- Remove `label()` option from `Armor` operation
|
||||
- Fix exit code for 'Missing required option/parameter' error
|
||||
- Fix `revoke-key`: Allow for multiple invocations of `--with-key-password` option
|
||||
- Fix `EncryptExternal` use of `--sign-with` parameter
|
||||
- Fix `NullPointerException` in `DecryptExternal` when reading lines
|
||||
- Fix `DecryptExternal` use of `verifications-out`
|
||||
- Test suite: Ignore tests if `UnsupportedOption` is thrown
|
||||
- Bump `logback-core` to `1.4.14`
|
||||
|
||||
## 10.0.0
|
||||
- Update implementation to [SOP Specification revision 10](https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-10.html).
|
||||
|
@ -17,6 +44,10 @@ SPDX-License-Identifier: Apache-2.0
|
|||
- Introduce `sopv` interface subset with revision `1.0`
|
||||
- Add `sop version --sopv`
|
||||
|
||||
## 8.0.2
|
||||
- CLI `change-key-password`: Fix indirect parameter passing for new and old passwords (thanks to @dkg for the report)
|
||||
- Backport: `revoke-key`: Allow for multiple password options
|
||||
|
||||
## 8.0.1
|
||||
- `decrypt`: Do not throw `NoSignature` exception (exit code 3) if `--verify-with` is provided, but `VERIFICATIONS` is empty.
|
||||
|
||||
|
@ -35,6 +66,13 @@ SPDX-License-Identifier: Apache-2.0
|
|||
- Change `EncryptAs` values into lowercase
|
||||
- Change `SignAs` values into lowercase
|
||||
|
||||
## 7.0.2
|
||||
- CLI `change-key-password`: Fix indirect parameter passing for new and old passwords (thanks to @dkg for the report)
|
||||
- Backport: revoke-key command: Allow for multiple '--with-key-password' options
|
||||
|
||||
## 7.0.1
|
||||
- `decrypt`: Do not throw `NoSignature` exception (exit code 3) if `--verify-with` is provided, but `VERIFICATIONS` is empty.
|
||||
|
||||
## 7.0.0
|
||||
- Update implementation to [SOP Specification revision 07](https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-07.html).
|
||||
- Add support for new `revoke-key` subcommand
|
||||
|
|
12
README.md
12
README.md
|
@ -27,6 +27,8 @@ The repository contains the following modules:
|
|||
compatible with the SOP-CLI specification.
|
||||
* [external-sop](/external-sop) contains an API implementation that can be used to forward API calls to a SOP executable,
|
||||
allowing to delegate the implementation logic to an arbitrary SOP CLI implementation.
|
||||
* [sop-java-testfixtures](/sop-java-testfixtures) contains a test suite that can be shared by downstream implementations
|
||||
of `sop-java`.
|
||||
|
||||
## Known Implementations
|
||||
(Please expand!)
|
||||
|
@ -35,9 +37,11 @@ allowing to delegate the implementation logic to an arbitrary SOP CLI implementa
|
|||
|-------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|
|
||||
| [pgpainless-sop](https://github.com/pgpainless/pgpainless/tree/main/pgpainless-sop) | Implementation of `sop-java` using PGPainless |
|
||||
| [external-sop](https://github.com/pgpainless/sop-java/tree/main/external-sop) | Implementation of `sop-java` that allows binding to external SOP binaries such as `sqop` |
|
||||
| [bcsop](https://codeberg.org/PGPainless/bc-sop) | Implementation of `sop-java` using vanilla Bouncy Castle |
|
||||
|
||||
### Implementations in other languages
|
||||
| Project | Language |
|
||||
|-------------------------------------------------|----------|
|
||||
| [sop-rs](https://sequoia-pgp.gitlab.io/sop-rs/) | Rust |
|
||||
| [SOP for python](https://pypi.org/project/sop/) | Python |
|
||||
| Project | Language |
|
||||
|---------------------------------------------------|----------|
|
||||
| [sop-rs](https://sequoia-pgp.gitlab.io/sop-rs/) | Rust |
|
||||
| [SOP for python](https://pypi.org/project/sop/) | Python |
|
||||
| [rpgpie-sop](https://crates.io/crates/rpgpie-sop) | Rust |
|
||||
|
|
|
@ -19,7 +19,7 @@ buildscript {
|
|||
|
||||
plugins {
|
||||
id 'ru.vyarus.animalsniffer' version '1.5.3'
|
||||
id 'org.jetbrains.kotlin.jvm' version "1.8.10"
|
||||
id 'org.jetbrains.kotlin.jvm' version "1.9.21"
|
||||
id 'com.diffplug.spotless' version '6.22.0' apply false
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,7 @@ allprojects {
|
|||
apply plugin: 'jacoco'
|
||||
apply plugin: 'checkstyle'
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'com.diffplug.spotless'
|
||||
|
||||
// For non-cli modules enable android api compatibility check
|
||||
|
|
|
@ -27,7 +27,7 @@ dependencies {
|
|||
// The ExternalTestSubjectFactory reads json config file to find configured SOP binaries...
|
||||
testImplementation "com.google.code.gson:gson:$gsonVersion"
|
||||
// ...and extends TestSubjectFactory
|
||||
testImplementation(testFixtures(project(":sop-java")))
|
||||
testImplementation(project(":sop-java-testfixtures"))
|
||||
}
|
||||
|
||||
test {
|
||||
|
|
|
@ -78,7 +78,7 @@ class DecryptExternal(
|
|||
val verifyOut = File(tempDir, "verifications-out")
|
||||
verifyOut.delete()
|
||||
if (requireVerification) {
|
||||
commandList.add("--verify-out=${verifyOut.absolutePath}")
|
||||
commandList.add("--verifications-out=${verifyOut.absolutePath}")
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
|
@ -37,7 +37,7 @@ class EncryptExternal(
|
|||
|
||||
override fun signWith(key: InputStream): Encrypt = apply {
|
||||
commandList.add("--sign-with=@ENV:SIGN_WITH_$argCounter")
|
||||
envList.add("SIGN_WITH_$argCounter=${ExternalSOP.readString(key)}")
|
||||
envList.add("SIGN_WITH_$argCounter=${readString(key)}")
|
||||
argCounter += 1
|
||||
}
|
||||
|
||||
|
|
|
@ -6,5 +6,6 @@ rootProject.name = 'SOP-Java'
|
|||
|
||||
include 'sop-java',
|
||||
'sop-java-picocli',
|
||||
'sop-java-testfixtures',
|
||||
'external-sop'
|
||||
|
||||
|
|
|
@ -17,11 +17,11 @@ dependencies {
|
|||
|
||||
// SOP
|
||||
implementation(project(":sop-java"))
|
||||
testImplementation(testFixtures(project(":sop-java")))
|
||||
testImplementation(project(":sop-java-testfixtures"))
|
||||
|
||||
// CLI
|
||||
implementation "info.picocli:picocli:$picocliVersion"
|
||||
annotationProcessor "info.picocli:picocli-codegen:$picocliVersion"
|
||||
kapt "info.picocli:picocli-codegen:$picocliVersion"
|
||||
|
||||
// @Nonnull, @Nullable...
|
||||
implementation "com.google.code.findbugs:jsr305:$jsrVersion"
|
||||
|
@ -33,6 +33,10 @@ application {
|
|||
mainClass = mainClassName
|
||||
}
|
||||
|
||||
compileJava {
|
||||
options.compilerArgs += ["-Aproject=${project.group}/${project.name}"]
|
||||
}
|
||||
|
||||
jar {
|
||||
dependsOn(":sop-java:jar")
|
||||
duplicatesStrategy(DuplicatesStrategy.EXCLUDE)
|
||||
|
|
|
@ -64,7 +64,7 @@ class SopCLI {
|
|||
@JvmField var EXECUTABLE_NAME = "sop"
|
||||
|
||||
@JvmField
|
||||
@Option(names = ["--stacktrace"], scope = CommandLine.ScopeType.INHERIT)
|
||||
@Option(names = ["--stacktrace"], scope = ScopeType.INHERIT)
|
||||
var stacktrace = false
|
||||
|
||||
@JvmStatic
|
||||
|
@ -78,33 +78,25 @@ class SopCLI {
|
|||
@JvmStatic
|
||||
fun execute(vararg args: String): Int {
|
||||
// Set locale
|
||||
CommandLine(InitLocale()).parseArgs(*args)
|
||||
CommandLine(InitLocale()).setUnmatchedArgumentsAllowed(true).parseArgs(*args)
|
||||
|
||||
// Re-set bundle with updated locale
|
||||
cliMsg = ResourceBundle.getBundle("msg_sop")
|
||||
|
||||
val cmd =
|
||||
CommandLine(SopCLI::class.java).apply {
|
||||
// explicitly set help command resource bundle
|
||||
subcommands["help"]?.setResourceBundle(ResourceBundle.getBundle("msg_help"))
|
||||
return CommandLine(SopCLI::class.java)
|
||||
.apply {
|
||||
// Hide generate-completion command
|
||||
subcommands["generate-completion"]?.commandSpec?.usageMessage()?.hidden(true)
|
||||
// render Input/Output sections in help command
|
||||
subcommands.values.filter { (it.getCommand() as Any) is AbstractSopCmd } // Only for AbstractSopCmd objects
|
||||
.forEach { (it.getCommand() as AbstractSopCmd).installIORenderer(it) }
|
||||
// overwrite executable name
|
||||
commandName = EXECUTABLE_NAME
|
||||
// setup exception handling
|
||||
executionExceptionHandler = SOPExecutionExceptionHandler()
|
||||
exitCodeExceptionMapper = SOPExceptionExitCodeMapper()
|
||||
isCaseInsensitiveEnumValuesAllowed = true
|
||||
}
|
||||
|
||||
// render Input/Output sections in help command
|
||||
cmd.subcommands.values
|
||||
.filter {
|
||||
(it.getCommand() as Any) is AbstractSopCmd
|
||||
} // Only for AbstractSopCmd objects
|
||||
.forEach { (it.getCommand() as AbstractSopCmd).installIORenderer(it) }
|
||||
|
||||
return cmd.execute(*args)
|
||||
}.execute(*args)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,9 +33,15 @@ class ChangeKeyPasswordCmd : AbstractSopCmd() {
|
|||
changeKeyPassword.noArmor()
|
||||
}
|
||||
|
||||
oldKeyPasswords.forEach { changeKeyPassword.oldKeyPassphrase(it) }
|
||||
oldKeyPasswords.forEach {
|
||||
val password = stringFromInputStream(getInput(it))
|
||||
changeKeyPassword.oldKeyPassphrase(password)
|
||||
}
|
||||
|
||||
newKeyPassword?.let { changeKeyPassword.newKeyPassphrase(it) }
|
||||
newKeyPassword?.let {
|
||||
val password = stringFromInputStream(getInput(it))
|
||||
changeKeyPassword.newKeyPassphrase(password)
|
||||
}
|
||||
|
||||
try {
|
||||
changeKeyPassword.keys(System.`in`).writeTo(System.out)
|
||||
|
|
|
@ -29,7 +29,7 @@ class DecryptCmd : AbstractSopCmd() {
|
|||
@Option(names = [OPT_WITH_PASSWORD], paramLabel = "PASSWORD")
|
||||
var withPassword: List<String> = listOf()
|
||||
|
||||
@Option(names = [OPT_VERIFICATIONS_OUT], paramLabel = "VERIFICATIONS")
|
||||
@Option(names = [OPT_VERIFICATIONS_OUT, "--verify-out"], paramLabel = "VERIFICATIONS")
|
||||
var verifyOut: String? = null
|
||||
|
||||
@Option(names = [OPT_VERIFY_WITH], paramLabel = "CERT") var certs: List<String> = listOf()
|
||||
|
|
24
sop-java-testfixtures/build.gradle
Normal file
24
sop-java-testfixtures/build.gradle
Normal file
|
@ -0,0 +1,24 @@
|
|||
// SPDX-FileCopyrightText: 2024 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
plugins {
|
||||
id 'java-library'
|
||||
}
|
||||
|
||||
group 'org.pgpainless'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":sop-java"))
|
||||
implementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
|
||||
implementation "org.junit.jupiter:junit-jupiter-params:$junitVersion"
|
||||
runtimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
||||
|
||||
// @Nullable, @Nonnull annotations
|
||||
implementation "com.google.code.findbugs:jsr305:3.0.2"
|
||||
|
||||
}
|
|
@ -5,8 +5,11 @@
|
|||
package sop.testsuite.operation;
|
||||
|
||||
import org.junit.jupiter.api.Named;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import sop.SOP;
|
||||
import sop.testsuite.AbortOnUnsupportedOption;
|
||||
import sop.testsuite.AbortOnUnsupportedOptionExtension;
|
||||
import sop.testsuite.SOPInstanceFactory;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
@ -15,6 +18,8 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@ExtendWith(AbortOnUnsupportedOptionExtension.class)
|
||||
@AbortOnUnsupportedOption
|
||||
public abstract class AbstractSOPTest {
|
||||
|
||||
private static final List<Arguments> backends = new ArrayList<>();
|
|
@ -0,0 +1,12 @@
|
|||
// SPDX-FileCopyrightText: 2024 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.testsuite
|
||||
|
||||
import java.lang.annotation.Inherited
|
||||
|
||||
@Target(AnnotationTarget.TYPE)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Inherited
|
||||
annotation class AbortOnUnsupportedOption
|
|
@ -0,0 +1,21 @@
|
|||
// SPDX-FileCopyrightText: 2024 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.testsuite
|
||||
|
||||
import org.junit.jupiter.api.Assumptions
|
||||
import org.junit.jupiter.api.extension.ExtensionContext
|
||||
import org.junit.jupiter.api.extension.TestExecutionExceptionHandler
|
||||
import sop.exception.SOPGPException
|
||||
|
||||
class AbortOnUnsupportedOptionExtension : TestExecutionExceptionHandler {
|
||||
override fun handleTestExecutionException(context: ExtensionContext, throwable: Throwable) {
|
||||
val testClass = context.requiredTestClass
|
||||
val annotation = testClass.getAnnotation(AbortOnUnsupportedOption::class.java)
|
||||
if (annotation != null && SOPGPException.UnsupportedOption::class.isInstance(throwable)) {
|
||||
Assumptions.assumeTrue(false, "Test aborted due to: " + throwable.message)
|
||||
}
|
||||
throw throwable
|
||||
}
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
|
||||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import org.apache.tools.ant.filters.*
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'java-test-fixtures'
|
||||
}
|
||||
|
||||
group 'org.pgpainless'
|
||||
|
@ -17,14 +19,19 @@ dependencies {
|
|||
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion"
|
||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
||||
testFixturesImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
|
||||
testFixturesImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion"
|
||||
testImplementation(project(":sop-java-testfixtures"))
|
||||
|
||||
// @Nullable, @Nonnull annotations
|
||||
implementation "com.google.code.findbugs:jsr305:3.0.2"
|
||||
|
||||
}
|
||||
|
||||
processResources {
|
||||
filter ReplaceTokens, tokens: [
|
||||
"project.version": project.version.toString()
|
||||
]
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
|
|
@ -9,8 +9,9 @@ package sop
|
|||
*
|
||||
* @param micAlg string identifying the digest mechanism used to create the signed message. This is
|
||||
* useful for setting the `micalg=` parameter for the multipart/signed content-type of a PGP/MIME
|
||||
* object as described in section 5 of [RFC3156]. If more than one signature was generated and
|
||||
* different digest mechanisms were used, the value of the micalg object is an empty string.
|
||||
* object as described in section 5 of [RFC3156](https://www.rfc-editor.org/rfc/rfc3156#section-5).
|
||||
* If more than one signature was generated and different digest mechanisms were used, the value
|
||||
* of the micalg object is an empty string.
|
||||
*/
|
||||
data class SigningResult(val micAlg: MicAlg) {
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ interface GenerateKey {
|
|||
fun signingOnly(): GenerateKey
|
||||
|
||||
/**
|
||||
* Generate the OpenPGP key and return it encoded as an [InputStream].
|
||||
* Generate the OpenPGP key and return it encoded as an [java.io.InputStream].
|
||||
*
|
||||
* @return key
|
||||
* @throws MissingArg if no user-id was provided
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
|
||||
package sop.operation
|
||||
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.util.*
|
||||
import kotlin.jvm.Throws
|
||||
import sop.exception.SOPGPException
|
||||
|
||||
|
@ -107,4 +110,17 @@ interface Version {
|
|||
* this method throws an [SOPGPException.UnsupportedOption] instead.
|
||||
*/
|
||||
@Throws(SOPGPException.UnsupportedOption::class) fun getSopVVersion(): String
|
||||
|
||||
/** Return the current version of the SOP-Java library. */
|
||||
fun getSopJavaVersion(): String? {
|
||||
return try {
|
||||
val resourceIn: InputStream =
|
||||
javaClass.getResourceAsStream("/sop-java-version.properties")
|
||||
?: throw IOException("File sop-java-version.properties not found.")
|
||||
val properties = Properties().apply { load(resourceIn) }
|
||||
properties.getProperty("sop-java-version")
|
||||
} catch (e: IOException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import java.io.OutputStream
|
|||
* target output stream is not yet known.
|
||||
*/
|
||||
@Deprecated("Marked for removal.")
|
||||
// TODO: Remove in 11.X
|
||||
class ProxyOutputStream : OutputStream() {
|
||||
private val buffer = ByteArrayOutputStream()
|
||||
private var swapped: OutputStream? = null
|
||||
|
|
4
sop-java/src/main/resources/sop-java-version.properties
Normal file
4
sop-java/src/main/resources/sop-java-version.properties
Normal file
|
@ -0,0 +1,4 @@
|
|||
# SPDX-FileCopyrightText: 2025 Paul Schaub <info@pgpainless.org>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
sop-java-version=@project.version@
|
|
@ -12,7 +12,7 @@ allprojects {
|
|||
jsrVersion = '3.0.2'
|
||||
junitVersion = '5.8.2'
|
||||
junitSysExitVersion = '1.1.2'
|
||||
logbackVersion = '1.2.13'
|
||||
logbackVersion = '1.2.13' // 1.4+ cause CLI spam
|
||||
mockitoVersion = '4.5.1'
|
||||
picocliVersion = '4.6.3'
|
||||
slf4jVersion = '1.7.36'
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue