Skip to content

Commit 37169ea

Browse files
committed
Merge branch 'develop' into release/1.8.0
2 parents c198374 + 7708704 commit 37169ea

6 files changed

Lines changed: 95 additions & 51 deletions

File tree

.github/workflows/build.yml

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ jobs:
1515
id-token: write # Required for the attestations step
1616
attestations: write # Required for the attestations step
1717
steps:
18-
- uses: actions/checkout@v5
19-
- uses: actions/setup-java@v5
18+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
19+
- uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
2020
with:
2121
distribution: 'temurin'
2222
java-version: ${{ env.JAVA_VERSION }}
@@ -28,12 +28,12 @@ jobs:
2828
run: mvn -B verify --no-transfer-progress
2929
- name: Attest
3030
if: startsWith(github.ref, 'refs/tags/')
31-
uses: actions/attest-build-provenance@v3
31+
uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # v3.2.0
3232
with:
3333
subject-path: |
3434
target/*.jar
3535
target/*.pom
36-
- uses: actions/upload-artifact@v5
36+
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
3737
with:
3838
name: artifacts
3939
path: target/*.jar
@@ -45,20 +45,18 @@ jobs:
4545
needs: [build]
4646
if: github.repository_owner == 'cryptomator' && (startsWith(github.ref, 'refs/tags/') || contains(github.event.head_commit.message, '[deploy]'))
4747
steps:
48-
- uses: actions/checkout@v5
49-
- uses: actions/setup-java@v5
48+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
49+
- uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
5050
with:
5151
distribution: 'temurin'
5252
java-version: ${{ env.JAVA_VERSION }}
5353
cache: 'maven'
5454
server-id: central
5555
server-username: MAVEN_CENTRAL_USERNAME
5656
server-password: MAVEN_CENTRAL_PASSWORD
57-
- name: Verify project version matches tag
57+
- name: Ensure to use tagged version
5858
if: startsWith(github.ref, 'refs/tags/')
59-
run: |
60-
PROJECT_VERSION=$(mvn help:evaluate "-Dexpression=project.version" -q -DforceStdout)
61-
test "$PROJECT_VERSION" = "${GITHUB_REF##*/}"
59+
run: mvn versions:set --file ./pom.xml -DnewVersion=${GITHUB_REF##*/}
6260
- name: Verify project version is -SNAPSHOT
6361
if: startsWith(github.ref, 'refs/tags/') == false
6462
run: |
@@ -81,17 +79,15 @@ jobs:
8179
needs: [build]
8280
if: github.repository_owner == 'cryptomator' && (startsWith(github.ref, 'refs/tags/') || contains(github.event.head_commit.message, '[deploy]'))
8381
steps:
84-
- uses: actions/checkout@v5
85-
- uses: actions/setup-java@v5
82+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
83+
- uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
8684
with:
8785
java-version: ${{ env.JAVA_VERSION }}
8886
distribution: 'temurin'
8987
cache: 'maven'
90-
- name: Verify project version matches tag
88+
- name: Ensure to use tagged version
9189
if: startsWith(github.ref, 'refs/tags/')
92-
run: |
93-
PROJECT_VERSION=$(mvn help:evaluate "-Dexpression=project.version" -q -DforceStdout)
94-
test "$PROJECT_VERSION" = "${GITHUB_REF##*/}"
90+
run: mvn versions:set --file ./pom.xml -DnewVersion=${GITHUB_REF##*/}
9591
- name: Verify project version is -SNAPSHOT
9692
if: startsWith(github.ref, 'refs/tags/') == false
9793
run: |
@@ -114,7 +110,7 @@ jobs:
114110
if: startsWith(github.ref, 'refs/tags/')
115111
steps:
116112
- name: Create Release
117-
uses: softprops/action-gh-release@v2
113+
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1
118114
with:
119115
prerelease: true
120116
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}

.github/workflows/codeql-analysis.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,19 @@ jobs:
1616
# dependeabot has on push events only read-only access, but codeql requires write access
1717
if: ${{ !(github.actor == 'dependabot[bot]' && contains(fromJSON('["push"]'), github.event_name)) }}
1818
steps:
19-
- uses: actions/checkout@v5
19+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
2020
with:
2121
fetch-depth: 2
22-
- uses: actions/setup-java@v5
22+
- uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
2323
with:
2424
distribution: 'temurin'
2525
java-version: 25
2626
cache: 'maven'
2727
- name: Initialize CodeQL
28-
uses: github/codeql-action/init@v4
28+
uses: github/codeql-action/init@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6
2929
with:
3030
languages: java
3131
- name: Build
3232
run: mvn -B compile
3333
- name: Perform CodeQL Analysis
34-
uses: github/codeql-action/analyze@v4
34+
uses: github/codeql-action/analyze@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6

CHANGELOG.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
77
The changelog starts with version 1.7.0.
88
Changes to prior versions can be found on the [Github release page](https://github.com/cryptomator/integrations-api/releases).
99

10+
1011
## [Unreleased](https://github.com/cryptomator/integrations-api/compare/1.7.0...HEAD)
1112

1213
### Added
13-
1414
* Experimental [Update API](https://github.com/cryptomator/integrations-api/blob/a522f36cf45884127e2431dd18222391669d5992/src/main/java/org/cryptomator/integrations/update/UpdateMechanism.java) (#72)
1515

16-
## [1.7.0](https://github.com/cryptomator/integrations-api/releases/tag/1.7.0) - 2025-09-17
16+
### Fixed
17+
* Fixed plugin loading/discovery active even if pluginDir property is not set ([#86](https://github.com/cryptomator/integrations-api/pull/86))
1718

1819
### Changed
20+
* **[BREAKING]** Updated required build JDK to 25 ([#73](https://github.com/cryptomator/integrations-api/pull/73))
21+
* Pin version of GitHub actions in CI ([#87](https://github.com/cryptomator/integrations-api/pull/87))
22+
1923

24+
## [1.7.0](https://github.com/cryptomator/integrations-api/releases/tag/1.7.0) - 2025-09-17
25+
26+
### Changed
2027
* **[BREAKING]** Updated required build JDK to 21 (a88fa29d9cc05a0e39ab15420517d0f25cff6f35)

pom.xml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,20 @@
3030
<jdk.version>25</jdk.version>
3131

3232
<slf4j.version>2.0.17</slf4j.version>
33-
<jackson.version>2.20.0</jackson.version>
34-
<jetbrains-annotation.version>26.0.2-1</jetbrains-annotation.version>
33+
<jackson.version>2.21.1</jackson.version>
34+
<jetbrains-annotation.version>26.1.0</jetbrains-annotation.version>
3535

3636
<!-- Test dependencies -->
37-
<junit.version>6.0.0</junit.version>
38-
<mockito.version>5.20.0</mockito.version>
37+
<junit.version>6.0.1</junit.version>
38+
<mockito.version>5.21.0</mockito.version>
3939

4040
<!-- Build dependencies -->
41-
<mvn-compiler.version>3.14.1</mvn-compiler.version>
42-
<mvn-source.version>3.3.1</mvn-source.version>
43-
<mvn-surefire.version>3.5.4</mvn-surefire.version>
41+
<mvn-compiler.version>3.15.0</mvn-compiler.version>
42+
<mvn-source.version>3.4.0</mvn-source.version>
43+
<mvn-surefire.version>3.5.5</mvn-surefire.version>
4444
<mvn-javadoc.version>3.12.0</mvn-javadoc.version>
4545
<mvn-gpg.version>3.2.8</mvn-gpg.version>
46-
<central-publishing.version>0.9.0</central-publishing.version>
46+
<central-publishing.version>0.10.0</central-publishing.version>
4747
</properties>
4848

4949
<licenses>

src/main/java/org/cryptomator/integrations/common/ClassLoaderFactory.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
class ClassLoaderFactory {
1919

2020
private static final Logger LOG = LoggerFactory.getLogger(ClassLoaderFactory.class);
21-
private static final String USER_HOME = System.getProperty("user.home");
2221
private static final String PLUGIN_DIR_KEY = "cryptomator.pluginDir";
2322
private static final String JAR_SUFFIX = ".jar";
2423

@@ -30,21 +29,27 @@ class ClassLoaderFactory {
3029
*/
3130
@Contract(value = "-> new", pure = true)
3231
public static URLClassLoader forPluginDir() {
33-
String val = System.getProperty(PLUGIN_DIR_KEY, "");
34-
final Path p;
35-
if (val.startsWith("~/")) {
36-
p = Path.of(USER_HOME).resolve(val.substring(2));
37-
} else {
38-
p = Path.of(val);
32+
String val = System.getProperty(PLUGIN_DIR_KEY);
33+
if (val == null) {
34+
return URLClassLoader.newInstance(new URL[0]);
35+
}
36+
37+
try {
38+
if (val.isBlank()) {
39+
throw new IllegalArgumentException("Plugin dir path is blank");
40+
}
41+
return forPluginDirWithPath(Path.of(val)); //Path.of() might throw InvalidPathException
42+
} catch (IllegalArgumentException e) {
43+
LOG.debug("{} contains illegal value. Skipping plugin directory.", PLUGIN_DIR_KEY, e);
44+
return URLClassLoader.newInstance(new URL[0]);
3945
}
40-
return forPluginDirWithPath(p);
4146
}
4247

4348
@VisibleForTesting
4449
@Contract(value = "_ -> new", pure = true)
4550
static URLClassLoader forPluginDirWithPath(Path path) throws UncheckedIOException {
4651
var jars = findJars(path);
47-
if (LOG.isDebugEnabled()) {
52+
if (LOG.isDebugEnabled() && jars.length != 0) {
4853
String jarList = Arrays.stream(jars).map(URL::getPath).collect(Collectors.joining(", "));
4954
LOG.debug("Found jars in cryptomator.pluginDir: {}", jarList);
5055
}
@@ -56,7 +61,7 @@ static URL[] findJars(Path path) {
5661
try (var stream = Files.walk(path)) {
5762
return stream.filter(ClassLoaderFactory::isJarFile).map(ClassLoaderFactory::toUrl).toArray(URL[]::new);
5863
} catch (IOException | UncheckedIOException e) {
59-
// unable to locate any jars // TODO: log a warning?
64+
LOG.debug("Failed to read plugin dir {}", path, e);
6065
return new URL[0];
6166
}
6267
}

src/test/java/org/cryptomator/integrations/common/ClassLoaderFactoryTest.java

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
package org.cryptomator.integrations.common;
22

3+
import org.junit.jupiter.api.AfterEach;
34
import org.junit.jupiter.api.Assertions;
45
import org.junit.jupiter.api.BeforeEach;
56
import org.junit.jupiter.api.DisplayName;
67
import org.junit.jupiter.api.Nested;
78
import org.junit.jupiter.api.Test;
89
import org.junit.jupiter.api.io.TempDir;
10+
import org.junit.jupiter.params.ParameterizedTest;
11+
import org.junit.jupiter.params.provider.EmptySource;
12+
import org.junit.jupiter.params.provider.ValueSource;
13+
import org.mockito.MockedStatic;
914
import org.mockito.Mockito;
1015

1116
import java.io.ByteArrayInputStream;
@@ -17,6 +22,9 @@
1722
import java.util.Arrays;
1823
import java.util.Comparator;
1924

25+
import static org.mockito.ArgumentMatchers.any;
26+
import static org.mockito.Mockito.never;
27+
2028
public class ClassLoaderFactoryTest {
2129

2230
@Nested
@@ -91,21 +99,49 @@ public void testReadPluginDirFromSysProp() {
9199
}
92100
}
93101

94-
@Test
95-
@DisplayName("read path from cryptomator.pluginDir and replace ~/ with user.home")
96-
public void testReadPluginDirFromSysPropAndReplaceHome() {
97-
var ucl = Mockito.mock(URLClassLoader.class, "ucl");
98-
var relPath = "~/there/will/be/plugins";
99-
var absPath = Path.of(System.getProperty("user.home")).resolve("there/will/be/plugins");
100-
try (var mockedClass = Mockito.mockStatic(ClassLoaderFactory.class)) {
102+
@Nested
103+
@DisplayName("when the system property contains invalid values")
104+
public class InvalidSystemProperty {
105+
106+
MockedStatic<ClassLoaderFactory> mockedClass;
107+
108+
@BeforeEach
109+
public void beforeEach() {
110+
mockedClass = Mockito.mockStatic(ClassLoaderFactory.class);
101111
mockedClass.when(() -> ClassLoaderFactory.forPluginDir()).thenCallRealMethod();
102-
mockedClass.when(() -> ClassLoaderFactory.forPluginDirWithPath(absPath)).thenReturn(ucl);
112+
mockedClass.when(() -> ClassLoaderFactory.forPluginDirWithPath(any())).thenReturn(null);
113+
}
103114

104-
System.setProperty("cryptomator.pluginDir", relPath);
115+
@AfterEach
116+
public void afterEach() {
117+
mockedClass.close();
118+
}
119+
120+
121+
@Test
122+
@DisplayName("Undefined cryptomator.pluginDir returns empty URLClassLoader")
123+
public void testUndefinedSysProp() {
124+
System.clearProperty("cryptomator.pluginDir");
105125
var result = ClassLoaderFactory.forPluginDir();
106126

107-
Assertions.assertSame(ucl, result);
127+
mockedClass.verify(() -> ClassLoaderFactory.forPluginDirWithPath(any()), never());
128+
Assertions.assertNotNull(result);
129+
Assertions.assertEquals(0, result.getURLs().length);
108130
}
131+
132+
@ParameterizedTest
133+
@DisplayName("Property cryptomator.pluginDir filled with blanks returns empty URLClassLoader")
134+
@EmptySource
135+
@ValueSource(strings = {"\t\t", " "})
136+
public void testBlankSysProp(String propValue) {
137+
System.setProperty("cryptomator.pluginDir", propValue);
138+
var result = ClassLoaderFactory.forPluginDir();
139+
140+
mockedClass.verify(() -> ClassLoaderFactory.forPluginDirWithPath(any()), never());
141+
Assertions.assertNotNull(result);
142+
Assertions.assertEquals(0, result.getURLs().length);
143+
}
144+
109145
}
110146

111147
@Test

0 commit comments

Comments
 (0)