-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Enable JDK-EA builds again #13036
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
Enable JDK-EA builds again #13036
Conversation
…project build directory
|
|
Updated bytebuddy to latest version to support JDK25: https://github.com/raphw/byte-buddy/releases/tag/byte-buddy-1.17.5 |
|
jmod module path on mac is wrong, uses/evaluates to --module-path /Users/runner/hostedtoolcache/Java_Temurin-Hotspot_jdk/21.0.7-6.0/arm64/Contents/Home/jmods/:build/jlinkbase/jlinkjars \ |
|
Windows: |
|
Windows: |
.github/workflows/deployment.yml
Outdated
| cd jabgui | ||
| eval "$(mise hook-env)" | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems like the env.JAVA_HOME var is not set correctly
jlink failed with: Error: jlink version 25.0 does not match target java.base version 21.0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
Too much effort; we will "just" use JDK25 as soon as it is released. |
|
The build for this PR is no longer available. Please visit https://builds.jabref.org/main/ for the latest build. |
…ge-update-to-JDK25
…JabRef/jabref into fix-jpackage-post-image-update-to-JDK25
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| include: | ||
| # if you change the os version rename all other occurrences | ||
| - os: ubuntu-22.04 | ||
| displayName: linux | ||
| archivePortable: tar -c -C jabgui/build/distribution JabRef | pigz --rsyncable > jabgui/build/distribution/JabRef-portable_linux.tar.gz && rm -R jabgui/build/distribution/JabRef | ||
| archivePortableJabKit: tar -c -C jabkit/build/distribution jabkit | pigz --rsyncable > jabkit/build/distribution/jabkit-portable_linux.tar.gz && rm -R jabkit/build/distribution/jabkit | ||
| - os: windows-latest | ||
| displayName: windows | ||
| archivePortable: 7z a -r jabgui/build/distribution/JabRef-portable_windows.zip ./jabgui/build/distribution/JabRef && rm -R jabgui/build/distribution/JabRef | ||
| archivePortableJabKit: 7z a -r jabkit/build/distribution/jabkit-portable_windows.zip ./jabkit/build/distribution/jabkit && rm -R jabkit/build/distribution/jabkit | ||
| - os: macos-13 # intel image | ||
| displayName: macOS | ||
| suffix: '' | ||
| - os: macos-14 | ||
| displayName: macOS (ARM64) | ||
| suffix: '_arm64' | ||
| runs-on: ${{ matrix.os }} | ||
| outputs: | ||
| major: ${{ steps.gitversion.outputs.Major }} | ||
| minor: ${{ steps.gitversion.outputs.Minor }} | ||
| branchname: ${{ steps.gitversion.outputs.branchName }} | ||
| name: ${{ matrix.displayName }} installer and portable version | ||
| steps: | ||
| - name: Check secrets presence | ||
| id: checksecrets | ||
| shell: bash | ||
| run: | | ||
| if [ "$BUILDJABREFPRIVATEKEY" == "" ]; then | ||
| echo "secretspresent=NO" >> $GITHUB_OUTPUT | ||
| echo "❌ Secret BUILDJABREFPRIVATEKEY not present" | ||
| else | ||
| echo "secretspresent=YES" >> $GITHUB_OUTPUT | ||
| echo "✔️ Secret BUILDJABREFPRIVATEKEY present" | ||
| fi | ||
| env: | ||
| BUILDJABREFPRIVATEKEY: ${{ secrets.buildJabRefPrivateKey }} | ||
| - name: Fetch all history for all tags and branches | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
| submodules: 'true' | ||
| show-progress: 'false' | ||
| - name: Install pigz and cache (linux) | ||
| if: (matrix.os == 'ubuntu-22.04') | ||
| uses: awalsh128/cache-apt-pkgs-action@latest | ||
| with: | ||
| packages: pigz | ||
| version: 1.0 | ||
| - name: Install GitVersion | ||
| uses: gittools/actions/gitversion/[email protected] | ||
| with: | ||
| versionSpec: "5.x" | ||
| - name: Run GitVersion | ||
| id: gitversion | ||
| uses: gittools/actions/gitversion/[email protected] | ||
|
|
||
| - name: Tell gradle to use JDK25 | ||
| run: sed -i "s/JavaLanguageVersion.of(25)/JavaLanguageVersion.of(24)/" build-logic/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts | ||
|
|
||
| # region setup-JDK | ||
| - name: Setup JDK 25 for "java toolchain" of Gradle | ||
| uses: jdx/mise-action@v2 | ||
| with: | ||
| mise_toml: | | ||
| [tools] | ||
| java = { version = "openjdk-25.0.0-ea+21", release_type = "ea" } | ||
| - name: Debug | ||
| run: | | ||
| set -x | ||
| set -e | ||
| echo $JAVA_HOME | ||
| java --version | ||
| - name: Make JDK known to gradle (Linux, macOS) | ||
| if: (matrix.os != 'windows-latest') | ||
| shell: bash | ||
| # Hint by https://github.com/gradle/gradle/issues/29355#issuecomment-2598556970 | ||
| run: ln -s ~/.local/share/mise ~/.asdf | ||
| - name: Make JDK known to gradle (Windows) | ||
| if: (matrix.os == 'windows-latest') | ||
| shell: bash | ||
| run: mv ~/AppData/Local/mise ~/.asdf | ||
| - name: Setup JDK for gradle itself | ||
| uses: actions/setup-java@v4 | ||
| with: | ||
| java-version: '21' | ||
| distribution: 'temurin' | ||
| # endregion | ||
|
|
||
| - name: Setup Gradle | ||
| uses: gradle/actions/setup-gradle@v4 | ||
| - name: Prepare merged jars and modules dir (macOS) | ||
| # prepareModulesDir is executing a build, which should run through even if no upload to builds.jabref.org is made | ||
| if: (matrix.os == 'macos-13') || (matrix.os == 'macos-14') || (steps.checksecrets.outputs.secretspresent == 'NO') | ||
| run: ./gradlew -i -PprojVersion="${{ steps.gitversion.outputs.AssemblySemVer }}" -PprojVersionInfo="${{ steps.gitversion.outputs.InformationalVersion }}" :jabgui:prepareModulesDir | ||
| - name: Setup macOS key chain | ||
| if: ((matrix.os == 'macos-13') || (matrix.os == 'macos-14')) && (steps.checksecrets.outputs.secretspresent == 'YES') | ||
| uses: slidoapp/import-codesign-certs@1923310662e8682dd05b76b612b53301f431cd5d | ||
| with: | ||
| p12-file-base64: ${{ secrets.OSX_SIGNING_CERT }} | ||
| p12-password: ${{ secrets.OSX_CERT_PWD }} | ||
| keychain-password: jabref | ||
| - name: Setup macOS key chain for app id cert | ||
| if: ((matrix.os == 'macos-13') || (matrix.os == 'macos-14')) && (steps.checksecrets.outputs.secretspresent == 'YES') | ||
| uses: slidoapp/import-codesign-certs@1923310662e8682dd05b76b612b53301f431cd5d | ||
| with: | ||
| p12-file-base64: ${{ secrets.OSX_SIGNING_CERT_APPLICATION }} | ||
| p12-password: ${{ secrets.OSX_CERT_PWD }} | ||
| create-keychain: false | ||
| keychain-password: jabref | ||
| - name: Debug | ||
| run: | | ||
| echo $JAVA_HOME | ||
| cat mise.toml | ||
| mise hook-env -f | ||
| mise hook-env -f | grep 'export JAVA_HOME' | ||
| eval $(mise hook-env -f | grep 'export JAVA_HOME') | ||
| echo $JAVA_HOME | ||
| eval $(mise hook-env -f | grep 'export PATH') | ||
| echo $JAVA_HOME | ||
| - name: Build dmg and pkg (macOS) | ||
| if: ((matrix.os == 'macos-13') || (matrix.os == 'macos-14')) && (steps.checksecrets.outputs.secretspresent == 'YES') | ||
| shell: bash | ||
| run: | | ||
| set -e | ||
| cd jabgui | ||
| # see https://github.com/jdx/mise/discussions/4973 | ||
| eval $(mise hook-env -f | grep 'export JAVA_HOME') | ||
| eval $(mise hook-env -f | grep 'export PATH') | ||
| echo $JAVA_HOME | ||
| # Use Java's packaging tool to create "JabRef.app" | ||
| jpackage \ | ||
| --module org.jabref/org.jabref.Launcher \ | ||
| --module-path $JAVA_HOME/jmods/:build/jlinkbase/jlinkjars \ | ||
| --add-modules org.jabref,org.jabref.merged.module \ | ||
| --add-modules jdk.incubator.vector \ | ||
| --dest build/distribution \ | ||
| --name JabRef \ | ||
| --app-version ${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }} \ | ||
| --verbose \ | ||
| --mac-sign \ | ||
| --vendor "JabRef e.V." \ | ||
| --mac-package-identifier JabRef \ | ||
| --mac-package-name JabRef \ | ||
| --type app-image \ | ||
| --mac-signing-key-user-name "JabRef e.V. (6792V39SK3)" \ | ||
| --mac-package-signing-prefix org.jabref \ | ||
| --mac-entitlements buildres/mac/jabref.entitlements \ | ||
| --icon src/main/resources/icons/jabref.icns \ | ||
| --resource-dir buildres/mac \ | ||
| --file-associations buildres/mac/bibtexAssociations.properties \ | ||
| --jlink-options --bind-services \ | ||
| --java-options --add-exports=javafx.base/com.sun.javafx.event=org.jabref.merged.module \ | ||
| --java-options --add-exports=javafx.controls/com.sun.javafx.scene.control=org.jabref.merged.module \ | ||
| --java-options --add-opens=javafx.graphics/javafx.scene=org.jabref.merged.module \ | ||
| --java-options --add-opens=javafx.controls/javafx.scene.control=org.jabref.merged.module \ | ||
| --java-options --add-opens=javafx.controls/javafx.scene.control.skin=org.jabref.merged.module \ | ||
| --java-options --add-opens=javafx.controls/com.sun.javafx.scene.control=org.jabref.merged.module \ | ||
| --java-options --add-opens=javafx.controls/javafx.scene.control=org.jabref \ | ||
| --java-options --add-exports=javafx.base/com.sun.javafx.event=org.jabref \ | ||
| --java-options --add-exports=javafx.controls/com.sun.javafx.scene.control=org.jabref \ | ||
| --java-options --add-opens=javafx.graphics/javafx.scene=org.jabref \ | ||
| --java-options --add-opens=javafx.controls/javafx.scene.control=org.jabref \ | ||
| --java-options --add-opens=javafx.controls/com.sun.javafx.scene.control=org.jabref \ | ||
| --java-options --add-opens=javafx.base/javafx.collections=org.jabref \ | ||
| --java-options --add-opens=javafx.base/javafx.collections.transformation=org.jabref \ | ||
| --java-options --add-modules=jdk.incubator.vector | ||
| # Add additional files / directories | ||
| cp buildres/mac/jabrefHost.py build/distribution/JabRef.app/Contents/ | ||
| cp -R buildres/mac/native-messaging-host build/distribution/JabRef.app/Contents/ | ||
| # Sign the final artifact | ||
| codesign --force --deep --sign "Developer ID Application: JabRef e.V. (6792V39SK3)" \ | ||
| --entitlements buildres/mac/jabref.entitlements \ | ||
| --options runtime --timestamp build/distribution/JabRef.app | ||
| # pack dmg and pkg | ||
| hdiutil create -volname "JabRef" -srcfolder build/distribution/JabRef.app -ov -format UDZO build/distribution/JabRef${{ matrix.suffix }}.dmg | ||
| productbuild --component build/distribution/JabRef.app /Applications --sign "Developer ID Installer: JabRef e.V. (6792V39SK3)" build/distribution/JabRef${{ matrix.suffix }}.pkg | ||
| # Cleanup unpacked file | ||
| rm -rf build/distribution/JabRef.app | ||
| - name: Build runtime image and installer (linux, Windows) | ||
| if: (matrix.os != 'macos-13') && (matrix.os != 'macos-14') && (steps.checksecrets.outputs.secretspresent == 'YES') | ||
| shell: bash | ||
| run: ./gradlew -i -PprojVersion="${{ steps.gitversion.outputs.AssemblySemVer }}" -PprojVersionInfo="${{ steps.gitversion.outputs.InformationalVersion }}" :jabgui:jpackage | ||
| - name: Build JabKit | ||
| if: (steps.checksecrets.outputs.secretspresent == 'YES') | ||
| shell: bash | ||
| run: ./gradlew -i -PprojVersion="${{ steps.gitversion.outputs.AssemblySemVer }}" -PprojVersionInfo="${{ steps.gitversion.outputs.InformationalVersion }}" :jabkit:jpackage | ||
| - name: Remove JabKit build for macOS | ||
| if: (matrix.os == 'macos-13') || (matrix.os == 'macos-14') | ||
| run: rm -rf jabkit/build/distribution/jabkit.app | ||
| - name: Package application image (linux, Windows) | ||
| if: (matrix.os != 'macos-13') && (matrix.os != 'macos-14') && (steps.checksecrets.outputs.secretspresent == 'YES') | ||
| shell: bash | ||
| run: | | ||
| set -e | ||
| ${{ matrix.archivePortable }} | ||
| ${{ matrix.archivePortableJabKit }} | ||
| - name: Rename files | ||
| if: (matrix.os != 'macos-13') && (matrix.os != 'macos-14') && (steps.checksecrets.outputs.secretspresent == 'YES') | ||
| shell: pwsh | ||
| run: | | ||
| get-childitem -Path jabgui/build/distribution/* | rename-item -NewName {$_.name -replace "${{ steps.gitversion.outputs.AssemblySemVer }}","${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}"} | ||
| get-childitem -Path jabgui/build/distribution/* | rename-item -NewName {$_.name -replace "portable","${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}-portable"} | ||
| - name: Repack deb file for Debian | ||
| if: (matrix.os == 'ubuntu-22.04') && (steps.checksecrets.outputs.secretspresent == 'YES') | ||
| shell: bash | ||
| run: | | ||
| cd jabgui/build/distribution | ||
| ar x jabref_${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}_amd64.deb | ||
| zstd -d < control.tar.zst | xz > control.tar.xz | ||
| zstd -d < data.tar.zst | xz > data.tar.xz | ||
| ar -m -c -a sdsd jabref_${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}_amd64_repackaged.deb debian-binary control.tar.xz data.tar.xz | ||
| rm debian-binary control.tar.* data.tar.* | ||
| mv -f jabref_${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}_amd64_repackaged.deb jabref_${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}_amd64.deb | ||
| - name: Setup rsync (macOS) | ||
| if: ${{ (!startsWith(github.ref, 'refs/heads/gh-readonly-queue')) && (steps.checksecrets.outputs.secretspresent == 'YES') && (((matrix.os == 'macos-13') || (matrix.os == 'macos-14')) && !((startsWith(github.ref, 'refs/tags/') || inputs.notarization == true))) }} | ||
| run: brew install rsync | ||
| - name: Setup rsync (Windows) | ||
| if: (matrix.os == 'windows-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') && (!startsWith(github.ref, 'refs/heads/gh-readonly-queue')) | ||
| # We want to have rsync available at this place to avoid uploading and downloading from GitHub artifact store (taking > 5 minutes in total) | ||
| # We cannot use "action-rsyncer", because that requires Docker which is unavailable on Windows | ||
| # We cannot use "setup-rsync", because that does not work on Windows | ||
| # We do not use egor-tensin/setup-cygwin@v4, because it replaces the default shell | ||
| run: choco install --no-progress rsync | ||
| - name: Setup SSH key | ||
| if: ${{ (steps.checksecrets.outputs.secretspresent == 'YES') && (!startsWith(github.ref, 'refs/heads/gh-readonly-queue')) && (!((startsWith(github.ref, 'refs/tags/') || (inputs.notarization == true)))) }} | ||
| run: | | ||
| echo "${{ secrets.buildJabRefPrivateKey }}" > sshkey | ||
| chmod 600 sshkey | ||
| - name: Upload to builds.jabref.org (Windows) | ||
| if: (matrix.os == 'windows-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') && (!startsWith(github.ref, 'refs/heads/gh-readonly-queue')) | ||
| shell: cmd | ||
| # for rsync installed by chocolatey, we need the ssh.exe delivered with that installation | ||
| run: | | ||
| rsync -rt --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r --itemize-changes --stats --rsync-path="mkdir -p /var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }} && rsync" -e 'C:\ProgramData\chocolatey\lib\rsync\tools\bin\ssh.exe -p 9922 -i sshkey -o StrictHostKeyChecking=no' jabgui/build/distribution/ [email protected]:/var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }}/ || true | ||
| rsync -rt --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r --itemize-changes --stats --rsync-path="mkdir -p /var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }} && rsync" -e 'C:\ProgramData\chocolatey\lib\rsync\tools\bin\ssh.exe -p 9922 -i sshkey -o StrictHostKeyChecking=no' jabkit/build/distribution/ [email protected]:/var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }}/ || true | ||
| - name: Upload to builds.jabref.org (linux, macOS) | ||
| # macOS: Negated condition of "Upload to GitHub workflow artifacts store (macOS)" | ||
| # Reason: We either upload the non-notarized files - or notarize the files later (and upload these later) | ||
| # needs to be on one line; multi line does not work | ||
| if: ${{ (!startsWith(github.ref, 'refs/heads/gh-readonly-queue')) && (steps.checksecrets.outputs.secretspresent == 'YES') && ((matrix.os == 'ubuntu-22.04') || (matrix.os == 'macos-13') || (matrix.os == 'macos-14')) && !((startsWith(github.ref, 'refs/tags/') || inputs.notarization == true)) }} | ||
| shell: bash | ||
| run: | | ||
| rsync -rt --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r --itemize-changes --stats --rsync-path="mkdir -p /var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }} && rsync" -e 'ssh -p 9922 -i sshkey -o StrictHostKeyChecking=no' jabgui/build/distribution/ [email protected]:/var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }}/ || true | ||
| rsync -rt --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r --itemize-changes --stats --rsync-path="mkdir -p /var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }} && rsync" -e 'ssh -p 9922 -i sshkey -o StrictHostKeyChecking=no' jabkit/build/distribution/ [email protected]:/var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }}/ || true | ||
| - name: Upload to GitHub workflow artifacts store (macOS) | ||
| if: ((matrix.os == 'macos-13') || (matrix.os == 'macos-14')) && (steps.checksecrets.outputs.secretspresent == 'YES') && (startsWith(github.ref, 'refs/tags/') || inputs.notarization == true) | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| # tbn = to-be-notarized | ||
| name: JabRef-macOS-tbn-${{ matrix.os }} | ||
| path: build/distribution | ||
| compression-level: 0 # no compression | ||
| - name: Upload to GitHub workflow artifacts store | ||
| if: (steps.checksecrets.outputs.secretspresent != 'YES') | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: JabRef-${{ matrix.os }} | ||
| path: build/distribution | ||
| compression-level: 0 # no compression | ||
| announce: |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 6 months ago
To fix the issue, we will add a permissions block at the root level of the workflow to define the minimal permissions required for the GITHUB_TOKEN. Based on the workflow's operations, the contents: read permission is sufficient for most steps, as it allows the workflow to read repository contents without granting unnecessary write access. If specific jobs or steps require additional permissions, they can be defined at the job level.
-
Copy modified lines R3-R5
| @@ -2,2 +2,5 @@ | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| on: |
| name: Comment on pull request | ||
| runs-on: ubuntu-22.04 | ||
| needs: [build] | ||
| if: ${{ github.event_name == 'pull_request' }} | ||
| steps: | ||
| - name: Check secrets presence | ||
| id: checksecrets | ||
| shell: bash | ||
| run: | | ||
| if [ "$BUILDJABREFPRIVATEKEY" == "" ]; then | ||
| echo "secretspresent=NO" >> $GITHUB_OUTPUT | ||
| echo "❌ Secret BUILDJABREFPRIVATEKEY not present" | ||
| else | ||
| echo "secretspresent=YES" >> $GITHUB_OUTPUT | ||
| echo "✔️ Secret BUILDJABREFPRIVATEKEY present" | ||
| fi | ||
| env: | ||
| BUILDJABREFPRIVATEKEY: ${{ secrets.buildJabRefPrivateKey }} | ||
| - name: Comment PR | ||
| if: (steps.checksecrets.outputs.secretspresent == 'YES') | ||
| uses: thollander/actions-comment-pull-request@v3 | ||
| with: | ||
| message: | | ||
| The build of this PR is available at <https://builds.jabref.org/pull/${{ github.event.pull_request.number }}/merge>. | ||
| comment-tag: download-link | ||
| mode: recreate | ||
| notarize: # outsourced in a separate job to be able to rerun if this fails for timeouts |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 6 months ago
To fix the issue, we will add a permissions block to the announce job to explicitly define the minimal permissions required. Since the job interacts with pull requests by adding comments, it needs pull-requests: write. All other permissions will be omitted to restrict access.
-
Copy modified lines R311-R312
| @@ -310,2 +310,4 @@ | ||
| if: ${{ github.event_name == 'pull_request' }} | ||
| permissions: | ||
| pull-requests: write | ||
| steps: |
|
@trag-bot didn't find any issues in the code! ✅✨ |
|
Closing, because too much back and forth - will start a clean PR - which "just" enables -ea builds. |
Experiments based on #13033 - not pushing to #13033, do avoid noice for the contributor.
With this PR, we are working towards having a single
mise.tomlconfiguring the JDK. Works for all GitHub workflows; need to update IDE instructions (there is a mise plugin) - and look at the dev container etc.Howto for windows:
mise install(in JabRef dir)%APPDATA%\..\Local\mise\installs\*toc:\user\{login}\.asdf\installs\*. - see Add support formiselocations. gradle/foojay-toolchains#110Kind of blocker: 134130/intellij-mise#234
Mandatory checks
CHANGELOG.mddescribed in a way that is understandable for the average user (if change is visible to the user)