Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 3 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ jobs:
fail-fast: false
matrix:
version:
- { jdk: 21, idea: 2025.1 }
- { jdk: 21, idea: 2025.2 }
- { jdk: 21, idea: 2025.3 }
- { jdk: 21, idea: 2026.1 }
- { jdk: 25, idea: 2026.2 }
- { jdk: 25, idea: 2026.3 }
- { jdk: 25, idea: LATEST-EAP-SNAPSHOT }
name: 'IDEA ${{ matrix.version.idea }}'
env:
Expand All @@ -32,7 +31,7 @@ jobs:
- name: 'Generate coverage report'
run: ./gradlew --warning-mode=all jacocoTestReport
- name: 'Upload coverage to Codecov'
if: github.repository == 'mapstruct/mapstruct-idea' && matrix.version.idea == '2025.1'
if: github.repository == 'mapstruct/mapstruct-idea' && matrix.version.idea == '2026.1'
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with:
fail_ci_if_error: true
Expand Down
131 changes: 131 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
name: Release
run-name: "Release ${{ inputs.version || 'from current snapshot version' }}"

on:
workflow_dispatch:
inputs:
version:
description: 'Release version (e.g. 1.9.2). Defaults to pluginVersion in gradle.properties without -SNAPSHOT'
required: false
next:
description: 'Next development version without -SNAPSHOT (e.g. 1.9.3). Defaults to the release version with the patch incremented'
required: false

permissions:
contents: read

concurrency:
group: release
cancel-in-progress: false

jobs:
release:
name: 'Release'
if: github.repository == 'mapstruct/mapstruct-idea'
runs-on: ubuntu-latest
environment: release
permissions:
contents: write
id-token: write
attestations: write
steps:
- name: 'Checkout'
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: main
- name: 'Set up JDK'
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
with:
distribution: 'zulu'
java-version: 21
- name: Setup Gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
with:
cache-read-only: true
- name: 'Determine versions'
env:
RELEASE_VERSION: ${{ inputs.version }}
NEXT_BASE_VERSION: ${{ inputs.next }}
run: |
semver='^[0-9]+\.[0-9]+\.[0-9]+$'
if [ -z "$RELEASE_VERSION" ]; then
current=$(sed -n 's/^pluginVersion = //p' gradle.properties)
RELEASE_VERSION="${current%-SNAPSHOT}"
echo "Deduced release version $RELEASE_VERSION from pluginVersion $current"
fi
[[ "$RELEASE_VERSION" =~ $semver ]] || { echo "Invalid release version: $RELEASE_VERSION"; exit 1; }
if [ -z "$NEXT_BASE_VERSION" ]; then
IFS=. read -r major minor patch <<< "$RELEASE_VERSION"
NEXT_BASE_VERSION="$major.$minor.$((patch + 1))"
echo "Deduced next development version $NEXT_BASE_VERSION-SNAPSHOT"
fi
[[ "$NEXT_BASE_VERSION" =~ $semver ]] || { echo "Invalid next version: $NEXT_BASE_VERSION"; exit 1; }
if [ -n "$(git ls-remote --tags origin "refs/tags/$RELEASE_VERSION")" ]; then
echo "Tag $RELEASE_VERSION already exists"
exit 1
fi
echo "RELEASE_VERSION=$RELEASE_VERSION" >> "$GITHUB_ENV"
echo "NEXT_BASE_VERSION=$NEXT_BASE_VERSION" >> "$GITHUB_ENV"
echo "### Releasing $RELEASE_VERSION (next: $NEXT_BASE_VERSION-SNAPSHOT)" >> "$GITHUB_STEP_SUMMARY"
- name: 'Prepare release notes'
run: |
# Drop the sections of NEXT_RELEASE_CHANGELOG.md that have no entries
awk '
/^### / { heading = $0; next }
NF {
if (heading != "") { if (printed) print ""; print heading; print ""; heading = ""; printed = 1 }
print
}
' NEXT_RELEASE_CHANGELOG.md > "$RUNNER_TEMP/release-notes.md"
if ! grep -q '^\* ' "$RUNNER_TEMP/release-notes.md"; then
echo "NEXT_RELEASE_CHANGELOG.md has no entries, add the release notes before releasing"
exit 1
fi
cat "$RUNNER_TEMP/release-notes.md" >> "$GITHUB_STEP_SUMMARY"
- name: 'Configure git'
run: |
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
- name: 'Set release version'
run: |
sed -i "s/^pluginVersion = .*/pluginVersion = $RELEASE_VERSION/" gradle.properties
git commit -am "Release $RELEASE_VERSION"
git tag -a "$RELEASE_VERSION" -m "$RELEASE_VERSION"
- name: 'Build'
# Tests run in CI, only build the plugin distribution here so no unverified dependencies run in this job
run: ./gradlew --warning-mode=all buildPlugin
- name: 'Sign plugin'
env:
CERTIFICATE_CHAIN: ${{ secrets.CERTIFICATE_CHAIN }}
PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
PRIVATE_KEY_PASSWORD: ${{ secrets.PRIVATE_KEY_PASSWORD }}
run: |
if [ -z "$CERTIFICATE_CHAIN" ] || [ -z "$PRIVATE_KEY" ]; then
echo "CERTIFICATE_CHAIN and PRIVATE_KEY secrets are required to sign the plugin"
exit 1
fi
./gradlew --warning-mode=all signPlugin
./gradlew --warning-mode=all verifyPluginSignature
echo "PLUGIN_ZIP=$(ls build/distributions/*-signed.zip)" >> "$GITHUB_ENV"
- name: 'Attest build provenance'
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-path: ${{ env.PLUGIN_ZIP }}
- name: 'Publish to JetBrains Marketplace'
env:
JETBRAINS_MARKETPLACE_TOKEN: ${{ secrets.JETBRAINS_MARKETPLACE_TOKEN }}
CERTIFICATE_CHAIN: ${{ secrets.CERTIFICATE_CHAIN }}
PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
PRIVATE_KEY_PASSWORD: ${{ secrets.PRIVATE_KEY_PASSWORD }}
run: ./gradlew --warning-mode=all publishPlugin
- name: 'Set next development version'
run: |
sed -i "s/^pluginVersion = .*/pluginVersion = $NEXT_BASE_VERSION-SNAPSHOT/" gradle.properties
printf '### Features\n\n### Enhancements\n\n### Bugs\n\n### Documentation\n\n### Build\n' > NEXT_RELEASE_CHANGELOG.md
git commit -am "Prepare next development version"
- name: 'Push changes'
run: git push --atomic origin main "refs/tags/$RELEASE_VERSION"
- name: 'Create GitHub release'
env:
GH_TOKEN: ${{ github.token }}
run: gh release create "$RELEASE_VERSION" "$PLUGIN_ZIP" --title "$RELEASE_VERSION" --verify-tag --notes-file "$RUNNER_TEMP/release-notes.md"
26 changes: 26 additions & 0 deletions NEXT_RELEASE_CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
### Features

### Enhancements

* Support generics in `@Mapping` source and target references (#83)
* Do not report unknown source properties when a `Map` is used as the source parameter (#243)

### Bugs

* Completion in `java(...)` expressions does not work for record targets (#245)
* Kotlin references fail with `NoClassDefFoundError` in IntelliJ IDEA 2026.3 (#281)
* `componentModel` completion shows unrelated suggestions (#275)

### Documentation

### Build

* Release the plugin through a GitHub Actions workflow
* Sign the plugin and attest the build provenance of the released plugin
* Verify the dependencies of the plugin distribution and the Gradle plugins
* Test against IntelliJ IDEA 2026.1, 2026.2 and 2026.3
* Update Gradle to 9.8.2

### Minimum Supported IntelliJ

* 2026.1
29 changes: 27 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ java {
}

repositories {
mavenLocal()
mavenCentral()
intellijPlatform {
defaultRepositories()
Expand All @@ -40,10 +39,18 @@ intellijPlatform {
projectName = 'MapStruct-Intellij-Plugin'
pluginConfiguration {
ideaVersion {
sinceBuild = "251"
sinceBuild = "261"
untilBuild = provider { null } as Provider<? extends String>
}
}
signing {
certificateChain = providers.environmentVariable('CERTIFICATE_CHAIN')
privateKey = providers.environmentVariable('PRIVATE_KEY')
password = providers.environmentVariable('PRIVATE_KEY_PASSWORD')
}
publishing {
token = providers.environmentVariable('JETBRAINS_MARKETPLACE_TOKEN')
}
}

// Simple function to load change-notes.html and description.html into valid text for plugin.xml
Expand Down Expand Up @@ -105,6 +112,8 @@ dependencies {
bundledPlugin( 'com.intellij.java' )
bundledPlugin( 'org.jetbrains.kotlin' )

zipSigner( '0.1.45' )

testFramework( TestFrameworkType.Platform.INSTANCE )
testFramework( TestFrameworkType.Plugin.Java.INSTANCE )
}
Expand Down Expand Up @@ -152,3 +161,19 @@ test {
exceptionFormat = 'full'
}
}

// Dependency verification (gradle/verification-metadata.xml) only covers what ends up in the plugin distribution
// and what runs while signing and publishing the plugin.
// The Gradle plugins are always verified. Everything else (tests, checkstyle, jacoco) only runs in CI,
// and depends on the IDEA version that is being tested against.
def verifiedConfigurations = [
'runtimeClasspath',
'intellijPlatformRuntimeClasspath',
'intellijPlatformSandboxRuntimeClasspath',
'marketplaceZipSigner',
]
configurations.configureEach {
if (!verifiedConfigurations.contains(name)) {
resolutionStrategy.disableDependencyVerification()
}
}
9 changes: 9 additions & 0 deletions change-notes.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
<html lang="en">
<h2>1.10.0</h2>
<ul>
<li>Support for IntelliJ IDEA 2026.1, 2026.2 and 2026.3. The minimum supported version is now 2026.1</li>
<li>Support generics in <code>@Mapping</code> source and target references</li>
<li>Do not report unknown source properties when a <code>Map</code> is used as the source parameter</li>
<li>Fix Java expression injection for record targets</li>
<li>Fix Kotlin references in <code>@Mapping</code> annotations not working in IntelliJ IDEA 2026.3</li>
<li>Fix <code>componentModel</code> completion showing unrelated suggestions</li>
</ul>
<h2>1.9.1</h2>
<ul>
<li>Improve error messages for reference inspection</li>
Expand Down
9 changes: 2 additions & 7 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,11 @@
# https://www.jetbrains.com/intellij-repository/releases
# https://www.jetbrains.com/intellij-repository/snapshots

ideaVersion = 2025.1
ideaVersion = 2026.1
sources = true
runGenerators = true
org.gradle.jvmargs=-Xmx1024m

pluginGroup = org.mapstruct
pluginName = mapstruct-intellij
pluginVersion = 1.9.2-SNAPSHOT

publishPluginId =
publishUsername =
publishPassword =
publishChannel =
pluginVersion = 1.10.0-SNAPSHOT
Loading
Loading