diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 79297fd0f..107fdd3c4 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -8,5 +8,5 @@ jobs: name: Validation runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: gradle/actions/wrapper-validation@v4 + - uses: actions/checkout@v5 + - uses: gradle/actions/wrapper-validation@v5 diff --git a/.github/workflows/pr_check_target_branch.yml b/.github/workflows/pr_check_target_branch.yml index 8a8b8741f..a1d0a32a3 100644 --- a/.github/workflows/pr_check_target_branch.yml +++ b/.github/workflows/pr_check_target_branch.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: # Do not check out the repository here. See https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ - - uses: Vankka/pr-target-branch-action@v2.1 + - uses: Vankka/pr-target-branch-action@v3 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -20,4 +20,3 @@ jobs: comment: | New PRs should target the `dev` branch. The base branch of this PR has been automatically changed to `dev`. Please check that there are no merge conflicts. - diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index de6789258..98193607b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -10,43 +10,52 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: submodules: true - name: Fetch latest submodule updates run: git submodule update --remote - name: Setup Java - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 17 + java-version: 21 - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 with: cache-read-only: true - - name: Setup publish token + - name: Setup tokens env: JETBRAINS_TOKEN: ${{ secrets.JETBRAINS_TOKEN }} + SENTRY_TOKEN: ${{ secrets.SENTRY_TOKEN }} shell: bash run: | mkdir -p ~/.gradle/ echo "GRADLE_USER_HOME=$HOME/.gradle" >> "$GITHUB_ENV" echo "mcdev.deploy.token=${JETBRAINS_TOKEN}" > ~/.gradle/gradle.properties - - shell: bash - run: | - mkdir -p ~/.gradle/ + echo -e '\nmcdev.sentry.token=${SENTRY_TOKEN}\n' >> ~/.gradle/gradle.properties echo -e '\norg.gradle.jvmargs=-Xmx4G\n' >> ~/.gradle/gradle.properties - - name: Publish plugin - run: ./gradlew clean :publishPlugin --stacktrace - name: Get tag name id: get_tag shell: bash # Tag name is reversed from release name due to how IntelliJ parses plugin version numbers run: | tag_name="$(echo $GITHUB_REF | cut -d / -f 3)" - echo ::set-output name=tag::$tag_name + echo tag=$tag_name >> $GITHUB_OUTPUT version_array=(${tag_name//-/ }) - echo ::set-output name=release::${version_array[1]}-${version_array[0]} + echo release=${version_array[1]}-${version_array[0]} >> $GITHUB_OUTPUT + - name: Create Sentry release + uses: getsentry/action-release@v3 + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_TOKEN }} + SENTRY_ORG: mcdev + SENTRY_PROJECT: mcdev + SENTRY_URL: https://sentry.mcdev.io + with: + environment: production + version: ${{ steps.get_tag.outputs.release }} + - name: Publish plugin + run: ./gradlew clean :sentryUploadSourceBundleJava :publishPlugin --stacktrace - name: Create GitHub Release uses: softprops/action-gh-release@v2 env: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index da67b6983..c76f9a9cc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,19 +17,19 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Checkout sources - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Java - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 17 + java-version: 21 - uses: actions-ecosystem/action-regex-match@d50fd2e7a37d0e617aea3d7ada663bd56862b9cc id: branch-match with: text: ${{ github.ref }} regex: '^refs\/heads\/\d{4}\.\d$' - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 with: cache-read-only: ${{ github.ref != 'refs/heads/dev' && steps.branch-match.outputs.match == '' }} - shell: bash @@ -37,7 +37,9 @@ jobs: mkdir -p ~/.gradle/ echo -e '\norg.gradle.jvmargs=-Xmx4G\n' >> ~/.gradle/gradle.properties - name: Build - run: ./gradlew build --stacktrace + run: ./gradlew :build --stacktrace + env: + NO_SENTRY: "true" - name: Upload test reports if: always() uses: actions/upload-artifact@v4 diff --git a/build.gradle.kts b/build.gradle.kts index 7a668d8e9..6a4591b7c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -18,7 +18,8 @@ * along with this program. If not, see . */ -import org.gradle.internal.jvm.Jvm +import io.sentry.android.gradle.extensions.SentryPluginExtension +import org.gradle.kotlin.dsl.configure import org.jetbrains.changelog.Changelog import org.jetbrains.gradle.ext.settings import org.jetbrains.gradle.ext.taskTriggers @@ -32,6 +33,7 @@ plugins { `mcdev-core` `mcdev-parsing` `mcdev-publishing` + alias(libs.plugins.sentry) apply (System.getenv("CI") == "true" && System.getenv("NO_SENTRY") != "true") } val coreVersion: String by project @@ -43,32 +45,32 @@ val testLibs: Configuration by configurations.creating { group = "com.demonwav.mcdev" -val gradleToolingExtensionSourceSet: SourceSet = sourceSets.create("gradle-tooling-extension") { +val gradleToolingExtensionSourceSet: SourceSet = sourceSets.create("gradle-tooling-extension", Action { configurations.named(compileOnlyConfigurationName) { extendsFrom(gradleToolingExtension) } -} +}) val gradleToolingExtensionJar = tasks.register(gradleToolingExtensionSourceSet.jarTaskName) { from(gradleToolingExtensionSourceSet.output) archiveClassifier.set("gradle-tooling-extension") exclude("META-INF/plugin.xml") } -val templatesSourceSet: SourceSet = sourceSets.create("templates") { +val templatesSourceSet: SourceSet = sourceSets.create("templates", Action { resources { srcDir("templates") compileClasspath += sourceSets.main.get().output } -} +}) val templateSourceSets: List = (file("templates").listFiles() ?: emptyArray()).mapNotNull { file -> if (file.isDirectory() && (file.listFiles() ?: emptyArray()).any { it.name.endsWith(".mcdev.template.json") }) { - sourceSets.create("templates-${file.name}") { + sourceSets.create("templates-${file.name}", Action { resources { srcDir(file) compileClasspath += sourceSets.main.get().output } - } + }) } else { null } @@ -81,9 +83,6 @@ val externalAnnotationsJar = tasks.register("externalAnnotationsJar") { } dependencies { - // Add tools.jar for the JDI API - implementation(files(Jvm.current().toolsJar)) - implementation(files(gradleToolingExtensionJar)) implementation(libs.mixinExtras.expressions) { @@ -96,6 +95,9 @@ dependencies { implementation(libs.bundles.asm) implementation(libs.bundles.fuel) + implementation(libs.sentry) { + exclude(group = "org.slf4j") + } intellijPlatform { intellijIdeaCommunity(libs.versions.intellij.ide, useInstaller = false) @@ -167,20 +169,17 @@ tasks.patchPluginXml { changeNotes = changelog.render(Changelog.OutputType.HTML) } -// Compile classes to be loaded into the Gradle VM to Java 5 to match Groovy +// Compile classes to be loaded into the Gradle VM to Java 8 // This is for maximum compatibility, these classes will be loaded into every Gradle import on all // projects (not just Minecraft), so we don't want to break that with an incompatible class version. tasks.named(gradleToolingExtensionSourceSet.compileJavaTaskName, JavaCompile::class) { - val java7Compiler = javaToolchains.compilerFor { languageVersion.set(JavaLanguageVersion.of(11)) } - javaCompiler.set(java7Compiler) - options.release.set(6) - options.bootstrapClasspath = files(java7Compiler.map { it.metadata.installationPath.file("jre/lib/rt.jar") }) + options.release = 8 options.compilerArgs = listOf("-Xlint:-options") } tasks.withType().configureEach { options.compilerArgs = listOf("-proc:none") - sourceCompatibility = "1.5" - targetCompatibility = "1.5" + sourceCompatibility = "1.8" + targetCompatibility = "1.8" } tasks.processResources { @@ -222,6 +221,7 @@ idea { license { val endings = listOf("java", "kt", "kts", "groovy", "gradle.kts", "xml", "properties", "html", "flex", "bnf") exclude("META-INF/plugin.xml") // https://youtrack.jetbrains.com/issue/IDEA-345026 + exclude("sentry-debug-meta.properties", "sentry-external-modules.txt") include(endings.map { "**/*.$it" }) val projectDir = layout.projectDirectory.asFile @@ -338,3 +338,29 @@ tasks.runIde { // systemProperty("user.language", "fr") // systemProperty("user.country", "FR") } + +if (System.getenv("CI") == "true" && System.getenv("NO_SENTRY") != "true") { + configure { + includeSourceContext = true + includeDependenciesReport = true + autoInstallation { + enabled = false + } + + url = "https://sentry.mcdev.io/" + org = "mcdev" + projectName = "mcdev" + authToken = providers.gradleProperty("mcdev.sentry.token") + } + + // Wire together some tasks to make Gradle happy + tasks.named("generateSentryBundleIdJava") { + dependsOn(generate) + } + tasks.named("sentryCollectSourcesJava") { + dependsOn(generate) + } + tasks.checkLicenseMain { + dependsOn(tasks.named("generateSentryDebugMetaPropertiesjava"), tasks.named("collectExternalDependenciesForSentry")) + } +} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 4a69ac6ce..fb3f1247e 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -35,6 +35,11 @@ tasks.withType().configureEach { repositories { mavenCentral() gradlePluginPortal() + maven("https://maven.neoforged.net/releases/") { + content { + includeGroupAndSubgroups("net.neoforged") + } + } } dependencies { diff --git a/buildSrc/src/main/kotlin/mcdev-core.gradle.kts b/buildSrc/src/main/kotlin/mcdev-core.gradle.kts index f506142cf..07883d518 100644 --- a/buildSrc/src/main/kotlin/mcdev-core.gradle.kts +++ b/buildSrc/src/main/kotlin/mcdev-core.gradle.kts @@ -20,16 +20,17 @@ import org.cadixdev.gradle.licenser.header.HeaderStyle import org.gradle.accessors.dm.LibrariesForLibs +import org.jetbrains.kotlin.gradle.dsl.JvmDefaultMode import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinVersion -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.gradle.tasks.CompileUsingKotlinDaemon plugins { java idea id("org.jetbrains.kotlin.jvm") id("org.jetbrains.intellij.platform") - id("org.cadixdev.licenser") + id("net.neoforged.licenser") } val ideaVersionName: String by project @@ -40,7 +41,7 @@ version = "$ideaVersionName-$coreVersion" // Build numbers are used for nightlies if (buildNumber != null) { - version = "$version-$buildNumber" + version = "$version-nightly+$buildNumber" } java { @@ -59,27 +60,29 @@ kotlin { jvmToolchain { languageVersion.set(JavaLanguageVersion.of(21)) } -} - -tasks.withType().configureEach { compilerOptions { jvmTarget = JvmTarget.JVM_21 - languageVersion = KotlinVersion.KOTLIN_2_0 - freeCompilerArgs = listOf("-Xjvm-default=all", "-Xjdk-release=21") + languageVersion = KotlinVersion.KOTLIN_2_2 + jvmDefault = JvmDefaultMode.NO_COMPATIBILITY + freeCompilerArgs = listOf("-Xjdk-release=21") optIn.add("kotlin.contracts.ExperimentalContracts") } +} +tasks.withType().configureEach { kotlinDaemonJvmArguments.add("-Xmx2G") } repositories { - maven("https://repo.denwav.dev/repository/maven-public/") + intellijPlatform { + defaultRepositories() + } + maven("https://maven.fabricmc.net/") { content { includeModule("net.fabricmc", "mapping-io") includeModule("net.fabricmc", "fabric-loader") } } - mavenCentral() maven("https://repo.spongepowered.org/maven/") { content { includeGroup("org.spongepowered") @@ -96,9 +99,8 @@ repositories { } } - intellijPlatform { - defaultRepositories() - } + mavenCentral() + maven("https://repo.denwav.dev/repository/maven-public/") } val libs = the() diff --git a/gradle.properties b/gradle.properties index b2e24e2a8..24657aceb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,7 +23,7 @@ org.gradle.jvmargs=-Xmx1g ideaVersionName = 2024.3 -coreVersion = 1.8.6 +coreVersion = 1.8.7 # Silences a build-time warning because we are bundling our own kotlin library kotlin.stdlib.default.dependency = false diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 17a042d46..2e2c20058 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,11 +1,11 @@ [versions] -kotlin = "2.0.20" +kotlin = "2.2.20" coroutines = "1.9.0-RC.2" junit = "5.10.2" junit-platform = "1.10.2" asm = "9.6" fuel = "2.3.1" -licenser = "0.6.1" +licenser = "0.7.5" changelog = "2.2.0" intellij-plugin = "2.5.0" intellij-plugin-repository-rest-client = "2.0.46" @@ -17,8 +17,9 @@ psiPlugin = "243.7768" kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } intellij-platform = { id = "org.jetbrains.intellij.platform", version.ref = "intellij-plugin" } idea-ext = { id = "org.jetbrains.gradle.plugin.idea-ext", version.ref = "idea-ext" } -licenser = { id = "org.cadixdev.licenser", version.ref = "licenser" } +licenser = { id = "net.neoforged.licenser", version.ref = "licenser" } changelog = { id = "org.jetbrains.changelog", version.ref = "changelog" } +sentry = "io.sentry.jvm.gradle:5.8.0" [libraries] intellij-plugin-repository-rest-client = { module = "org.jetbrains.intellij:plugin-repository-rest-client", version.ref = "intellij-plugin-repository-rest-client" } @@ -28,7 +29,7 @@ kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.re kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } intellij-plugin = { module = "org.jetbrains.intellij.platform:org.jetbrains.intellij.platform.gradle.plugin", version.ref = "intellij-plugin" } -licenser-plugin = { module = "org.cadixdev.licenser:org.cadixdev.licenser.gradle.plugin", version.ref = "licenser" } +licenser-plugin = { module = "net.neoforged.licenser:net.neoforged.licenser.gradle.plugin", version.ref = "licenser" } changelog-plugin = { module = "org.jetbrains.changelog:org.jetbrains.changelog.gradle.plugin", version.ref = "changelog" } coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "coroutines" } @@ -57,6 +58,8 @@ gson = "com.google.code.gson:gson:2.10.1" fuel = { module = "com.github.kittinunf.fuel:fuel", version.ref = "fuel" } fuel-coroutines = { module = "com.github.kittinunf.fuel:fuel-coroutines", version.ref = "fuel" } +sentry = "io.sentry:sentry:8.22.0" + # Testing test-mixin = "org.spongepowered:mixin:0.8.5" test-spigotapi = "org.spigotmc:spigot-api:1.21-R0.1-SNAPSHOT" diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 9bbc975c7..8bdaf60c7 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 37f853b1c..2e1113280 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index faf93008b..adff685a0 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -172,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -212,8 +210,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 9d21a2183..c4bdd3ab8 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,10 @@ goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/mixin-test-data/build.gradle.kts b/mixin-test-data/build.gradle.kts index 7fa38495a..10dfb3b4a 100644 --- a/mixin-test-data/build.gradle.kts +++ b/mixin-test-data/build.gradle.kts @@ -24,7 +24,7 @@ plugins { java { toolchain { - languageVersion.set(JavaLanguageVersion.of(11)) + languageVersion.set(JavaLanguageVersion.of(21)) } } diff --git a/obfuscation-explorer/src/main/kotlin/mappings/MappingSet.kt b/obfuscation-explorer/src/main/kotlin/mappings/MappingSet.kt index b8e0c326d..ac193a459 100644 --- a/obfuscation-explorer/src/main/kotlin/mappings/MappingSet.kt +++ b/obfuscation-explorer/src/main/kotlin/mappings/MappingSet.kt @@ -34,6 +34,7 @@ import io.mcdev.obfex.ref.VoidTypeDef import io.mcdev.obfex.ref.asClass import io.mcdev.obfex.ref.asPackage +// TODO this sucks, start over from scratch @Suppress("MemberVisibilityCanBePrivate") class MappingSet(namespaceNames: Iterable = emptyList()) { @@ -84,9 +85,9 @@ class MappingSet(namespaceNames: Iterable = emptyList()) { fun namespaceIndex(ns: MappingNamespace): Int = findNamespace(ns).index fun mapType(fromNs: MappingNamespace, toNs: MappingNamespace, typeDef: T): T { - when (typeDef) { - is VoidTypeDef, is PrimitiveTypeDef -> return typeDef - } +// when (typeDef) { +// is VoidTypeDef, is PrimitiveTypeDef -> return typeDef +// } val fromNamespace = findNamespace(fromNs) val toNamespace = findNamespace(toNs) diff --git a/obfuscation-explorer/src/test/kotlin/formats/jam/JamMappingsFormatParserTest.kt b/obfuscation-explorer/src/test/kotlin/formats/jam/JamMappingsFormatParserTest.kt index 2740d5239..56d009243 100644 --- a/obfuscation-explorer/src/test/kotlin/formats/jam/JamMappingsFormatParserTest.kt +++ b/obfuscation-explorer/src/test/kotlin/formats/jam/JamMappingsFormatParserTest.kt @@ -97,6 +97,7 @@ class JamMappingsFormatParserTest : ParserFixture() { @Test fun testParam() { + /* TODO val ref = "net/minecraft/Test".asClass() .method("isEven".asMethodRef("(I)Z".asMethodDesc())) @@ -108,5 +109,6 @@ class JamMappingsFormatParserTest : ParserFixture() { assertNull(fromName) assertEquals("num", toName) + */ } } diff --git a/obfuscation-explorer/src/test/kotlin/formats/proguard/ProGuardMappingsFormatParserTest.kt b/obfuscation-explorer/src/test/kotlin/formats/proguard/ProGuardMappingsFormatParserTest.kt index 172c3342a..6c48cce37 100644 --- a/obfuscation-explorer/src/test/kotlin/formats/proguard/ProGuardMappingsFormatParserTest.kt +++ b/obfuscation-explorer/src/test/kotlin/formats/proguard/ProGuardMappingsFormatParserTest.kt @@ -97,9 +97,11 @@ class ProGuardMappingsFormatParserTest : ParserFixture() { @Test fun testInitSkipped() { + /* TODO val clazz = def.mappings.clazz("com.mojang.math.Constants".asClass())!! val mapping = clazz.method("".asMethodRef("()V")) assertNull(mapping) + */ } } diff --git a/obfuscation-explorer/src/test/kotlin/formats/tsrg/TSrgMappingsFormatParserTest.kt b/obfuscation-explorer/src/test/kotlin/formats/tsrg/TSrgMappingsFormatParserTest.kt index 526ecb001..b216fb54f 100644 --- a/obfuscation-explorer/src/test/kotlin/formats/tsrg/TSrgMappingsFormatParserTest.kt +++ b/obfuscation-explorer/src/test/kotlin/formats/tsrg/TSrgMappingsFormatParserTest.kt @@ -92,11 +92,13 @@ class TSrgMappingsFormatParserTest : ParserFixture() { @Test fun testByMappedType() { + /* TODO val ref = "net/minecraft/util/text/TextFormatting".asClass() .method("func_211165_a".asMethodRef("(C)Lnet/minecraft/util/text/TextFormatting;".asMethodDesc())) val mapping = def.mappings.methodMapping(ref) assertNotNull(mapping) + */ } @Test diff --git a/readme.md b/readme.md index eb1fc82cb..a780adfff 100644 --- a/readme.md +++ b/readme.md @@ -48,6 +48,14 @@ Because of this, you can install the plugin through IntelliJ's internal plugin b `File -> Settings -> Plugins` and click the `Browse Repositories...` button at the bottom of the window. In the search box, simply search for `Minecraft`. You can install it from there and restart IntelliJ to activate the plugin. +Dependencies +------------ + +Gradle will download all necessary dependencies to build the project, but you do need JDK 21 installed and available to +Gradle in order to build. + +You can easily install JDK 21 from [Adoptium](https://adoptium.net/temurin/releases). + Building -------- @@ -106,21 +114,6 @@ IntelliJ: If you're curious about that task, it is implemented in `buildSrc`. -Developers ----------- - -- Project Owner - [**@DenWav** - Kyle Wood](https://github.com/DenWav) -- [**@Minecrell**](https://github.com/Minecrell) -- [**@PaleoCrafter** - Marvin Rösch](https://github.com/PaleoCrafter) -- [**@RedNesto**](https://github.com/RedNesto) -- [**@Earthcomputer** - Joseph Burton](https://github.com/Earthcomputer) - -#### **Significant Contributors** - -- [**@gabizou** - Gabriel Harris-Rouquette](https://github.com/gabizou) -- [**@kashike** - Riley Park](https://github.com/kashike) -- [**@jamierocks** - Jamie Mansfield](https://github.com/jamierocks) - License ------- diff --git a/settings.gradle.kts b/settings.gradle.kts index e9eb209ef..e29303768 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,8 +18,15 @@ * along with this program. If not, see . */ -plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version("0.8.0") +pluginManagement { + repositories { + gradlePluginPortal() + maven("https://maven.neoforged.net/releases/") { + content { + includeGroupAndSubgroups("net.neoforged") + } + } + } } rootProject.name = "MinecraftDev" diff --git a/src/main/kotlin/errorreporter/AnonymousFeedback.kt b/src/main/kotlin/errorreporter/AnonymousFeedback.kt deleted file mode 100644 index 7f878c354..000000000 --- a/src/main/kotlin/errorreporter/AnonymousFeedback.kt +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Minecraft Development for IntelliJ - * - * https://mcdev.io/ - * - * Copyright (C) 2025 minecraft-dev - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, version 3.0 only. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.demonwav.mcdev.errorreporter - -import com.demonwav.mcdev.asset.MCDevBundle -import com.demonwav.mcdev.update.PluginUtil -import com.demonwav.mcdev.util.HttpConnectionFactory -import com.demonwav.mcdev.util.fromJson -import com.google.gson.Gson -import com.intellij.ide.plugins.PluginManagerCore -import com.intellij.openapi.diagnostic.Attachment -import com.intellij.openapi.util.text.StringUtil -import java.net.HttpURLConnection -import java.nio.ByteBuffer -import java.nio.charset.CodingErrorAction -import org.apache.commons.io.IOUtils -import org.apache.http.HttpHeaders -import org.apache.http.HttpStatus -import org.apache.http.entity.ContentType - -object AnonymousFeedback { - - data class FeedbackData(val url: String, val token: Int, val isDuplicate: Boolean) - - private const val AUTHED_URL = "https://www.denwav.dev/errorReport" - private const val BASE_URL = "https://api.github.com/repos/minecraft-dev/mcdev-error-report/issues" - - fun sendFeedback( - factory: HttpConnectionFactory, - envDetails: LinkedHashMap, - attachments: List, - ): FeedbackData { - val duplicateId = findDuplicateIssue(envDetails, factory) - if (duplicateId != null) { - // This is a duplicate - val issueContent = convertToGitHubIssueFormat(envDetails, attachments) - val commentUrl = sendCommentOnDuplicateIssue(duplicateId, factory, issueContent) - return FeedbackData(commentUrl, duplicateId, true) - } - - val (htmlUrl, token) = sendFeedback(factory, convertToGitHubIssueFormat(envDetails, attachments)) - return FeedbackData(htmlUrl, token, false) - } - - private fun convertToGitHubIssueFormat( - envDetails: LinkedHashMap, - attachments: List, - ): ByteArray { - val result = LinkedHashMap(5) - result["title"] = "[auto-generated] Exception in plugin" - result["body"] = generateGitHubIssueBody(envDetails, attachments) - return Gson().toJson(result).toByteArray() - } - - private fun generateGitHubIssueBody(body: LinkedHashMap, attachments: List): String { - val errorDescription = body.remove("error.description") ?: "" - - var errorMessage = body.remove("error.message") - if (errorMessage.isNullOrBlank()) { - errorMessage = "no error" - } - - val rawStackTrace = body.remove("error.raw_stacktrace")?.takeIf { it.isNotBlank() } ?: "no stacktrace" - val stackTrace = body.remove("error.stacktrace")?.takeIf { it.isNotBlank() } ?: "no stacktrace" - - val sb = StringBuilder() - - if (errorDescription.isNotEmpty()) { - sb.append(errorDescription).append("\n\n") - } - - sb.append("
\n") - for ((i, entry) in body.entries.withIndex()) { - if (i == 6) { - sb.append("
\n") - } - val (key, value) = entry - sb.append("\n") - } - sb.append("
") - .append(key) - .append("") - .append(value) - .append("
\n") - - sb.append("\n
").append(stackTrace).append("
\n") - - sb.append("\n
Original stack trace\n\n```\n") - .append(rawStackTrace) - .append("\n```\n
\n") - - sb.append("\n```\n").append(errorMessage).append("\n```\n") - - if (attachments.isNotEmpty()) { - for (attachment in attachments) { - sb.append("\n---\n\n```\n").append(attachment.name).append("\n```\n") - sb.append("```\n") - - try { - // No clue what the data format of the attachment is - // but if we try to decode it as UTF-8 and it succeeds, chances are likely that's what it is - val charBuf = Charsets.UTF_8.newDecoder() - .onMalformedInput(CodingErrorAction.REPORT) - .onUnmappableCharacter(CodingErrorAction.REPORT) - .decode(ByteBuffer.wrap(attachment.bytes)) - - val text = charBuf.toString() - if (text != attachment.displayText) { - sb.append(attachment.displayText).append("\n```\n") - sb.append("```\n") - } - sb.append(text) - } catch (e: Exception) { - // Guess it's not text... - sb.append(attachment.displayText).append("\n```\n") - sb.append("```\n") - sb.append(attachment.encodedBytes) - } - - sb.append("\n```\n") - } - } - - return sb.toString() - } - - private fun sendFeedback(factory: HttpConnectionFactory, payload: ByteArray): Pair { - val connection = getConnection(factory, AUTHED_URL) - connection.connect() - val json = executeCall(connection, payload) - return json["html_url"] as String to (json["number"] as Double).toInt() - } - - private fun connect(factory: HttpConnectionFactory, url: String): HttpURLConnection { - val connection = factory.openHttpConnection(url) - connection.connectTimeout = 5000 - connection.readTimeout = 5000 - return connection - } - - private const val openIssueUrl = "$BASE_URL?state=open&creator=minecraft-dev-autoreporter&per_page=100" - private const val closedIssueUrl = "$BASE_URL?state=closed&creator=minecraft-dev-autoreporter&per_page=100" - - private const val packagePrefix = "\tat com.demonwav.mcdev" - - private fun findDuplicateIssue(envDetails: LinkedHashMap, factory: HttpConnectionFactory): Int? { - val numberRegex = Regex("\\d+") - val newLineRegex = Regex("[\r\n]+") - - val stack = envDetails["error.raw_stacktrace"]?.replace(numberRegex, "") ?: return null - - val ourMcdevParts = stack.lineSequence() - .filter { line -> line.startsWith(packagePrefix) } - .map { it.trim() } - .toList() - - if (ourMcdevParts.isEmpty()) { - return null - } - - val predicate = fun(map: Map<*, *>): Boolean { - val body = (map["body"] as? String ?: return false) - .replace(numberRegex, "") - .replace(newLineRegex, "\n") - - // We can't comment on locked issues - if (map["locked"] as Boolean) { - return false - } - - val first = body.indexOf("\n```\n", startIndex = 0) + 5 - val second = body.indexOf("\n```\n", startIndex = first) - if (first == 4 || second == -1) { - return false - } - - val stackText = body.substring(first, second) - - val theirMcdevParts = stackText.lineSequence() - .filter { line -> line.startsWith(packagePrefix) } - .map { it.trim() } - .toList() - - return ourMcdevParts == theirMcdevParts - } - - // Look first for an open issue, then for a closed issue if one isn't found - val block = getAllIssues(openIssueUrl, factory)?.firstOrNull(predicate) - ?: getAllIssues(closedIssueUrl, factory, limit = 300)?.firstOrNull(predicate) - ?: return null - return (block["number"] as Double).toInt() - } - - private fun getMcdevStackElementLines(stack: String, numberRegex: Regex, linkRegex: Regex): List { - return stack.lineSequence() - .mapNotNull { line -> linkRegex.matchEntire(line)?.groups?.get("content")?.value } - .map { line -> StringUtil.unescapeXmlEntities(line) } - .map { line -> line.replace(numberRegex, "") } - .toList() - } - - private fun getAllIssues(url: String, factory: HttpConnectionFactory, limit: Int = -1): List>? { - var useAuthed = false - - var next: String? = url - val list = mutableListOf>() - - while (next != null) { - val connection: HttpURLConnection = connect(factory, next) - try { - connection.requestMethod = "GET" - connection.setRequestProperty("User-Agent", userAgent) - - connection.connect() - - if (connection.responseCode == HttpStatus.SC_FORBIDDEN && !useAuthed) { - useAuthed = true - next = replaceWithAuth(next) - continue - } - - if (connection.responseCode != HttpStatus.SC_OK) { - return null - } - - val charset = connection.getHeaderField(HttpHeaders.CONTENT_TYPE)?.let { - ContentType.parse(it).charset - } ?: Charsets.UTF_8 - - val data = connection.inputStream.reader(charset).readText() - - val response = Gson().fromJson>>(data) - list.addAll(response) - - if (limit > 0 && list.size >= limit) { - return list - } - - val link = connection.getHeaderField("Link") - - next = getNextLink(link, useAuthed) - } finally { - connection.disconnect() - } - } - - return list - } - - private fun getNextLink(linkHeader: String?, useAuthed: Boolean): String? { - if (linkHeader == null) { - return null - } - val links = linkHeader.split(",") - for (link in links) { - if (!link.contains("rel=\"next\"")) { - continue - } - - val parts = link.split(";") - if (parts.isEmpty()) { - continue - } - val nextUrl = parts[0].trim().removePrefix("<").removeSuffix(">") - if (!useAuthed) { - return nextUrl - } - - return replaceWithAuth(nextUrl) - } - - return null - } - - private fun replaceWithAuth(url: String): String? { - // non-authed-API requests are rate limited at 60 / hour / IP - // authed requests have a rate limit of 5000 / hour / account - // We don't want to use the authed URL by default since all users would use the same rate limit - // but it's a good fallback when the non-authed API stops working. - val index = url.indexOf('?') - if (index == -1) { - return null - } - return AUTHED_URL + url.substring(index) - } - - private fun sendCommentOnDuplicateIssue(id: Int, factory: HttpConnectionFactory, payload: ByteArray): String { - val commentUrl = "$AUTHED_URL/$id/comments" - val connection = getConnection(factory, commentUrl) - val json = executeCall(connection, payload) - return json["html_url"] as String - } - - private fun executeCall(connection: HttpURLConnection, payload: ByteArray): Map<*, *> { - connection.outputStream.use { - it.write(payload) - } - - val responseCode = connection.responseCode - if (responseCode != HttpStatus.SC_CREATED) { - throw RuntimeException(MCDevBundle("error_reporter.submit.failure", responseCode)) - } - - val contentEncoding = connection.contentEncoding ?: "UTF-8" - val body = connection.inputStream.use { - IOUtils.toString(it, contentEncoding) - } - connection.disconnect() - - return Gson().fromJson(body) - } - - private fun getConnection(factory: HttpConnectionFactory, url: String): HttpURLConnection { - val connection = connect(factory, url) - connection.doOutput = true - connection.requestMethod = "POST" - connection.setRequestProperty("User-Agent", userAgent) - connection.setRequestProperty("Content-Type", "application/json") - - return connection - } - - private val userAgent by lazy { - var agent = "Minecraft Development IntelliJ IDEA plugin" - - val pluginDescription = PluginManagerCore.getPlugin(PluginUtil.PLUGIN_ID) - if (pluginDescription != null) { - val name = pluginDescription.name - val version = pluginDescription.version - agent = "$name ($version)" - } - agent - } -} diff --git a/src/main/kotlin/errorreporter/AnonymousFeedbackTask.kt b/src/main/kotlin/errorreporter/AnonymousFeedbackTask.kt deleted file mode 100644 index 2c4a3415a..000000000 --- a/src/main/kotlin/errorreporter/AnonymousFeedbackTask.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Minecraft Development for IntelliJ - * - * https://mcdev.io/ - * - * Copyright (C) 2025 minecraft-dev - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, version 3.0 only. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.demonwav.mcdev.errorreporter - -import com.demonwav.mcdev.util.ProxyHttpConnectionFactory -import com.intellij.openapi.diagnostic.Attachment -import com.intellij.openapi.progress.ProgressIndicator -import com.intellij.openapi.progress.Task -import com.intellij.openapi.project.Project - -class AnonymousFeedbackTask( - project: Project?, - title: String, - canBeCancelled: Boolean, - private val params: LinkedHashMap, - private val attachments: List, - private val callback: (String, Int, Boolean) -> Unit, - private val errorCallback: (Exception) -> Unit, -) : Task.Backgroundable(project, title, canBeCancelled) { - - override fun run(indicator: ProgressIndicator) { - indicator.isIndeterminate = true - - try { - val factory = ProxyHttpConnectionFactory - val (url, token, isDuplicate) = AnonymousFeedback.sendFeedback(factory, params, attachments) - - callback(url, token, isDuplicate) - } catch (e: Exception) { - errorCallback(e) - } - } -} diff --git a/src/main/kotlin/errorreporter/ErrorData.kt b/src/main/kotlin/errorreporter/ErrorData.kt deleted file mode 100644 index a42e6be6c..000000000 --- a/src/main/kotlin/errorreporter/ErrorData.kt +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Minecraft Development for IntelliJ - * - * https://mcdev.io/ - * - * Copyright (C) 2025 minecraft-dev - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, version 3.0 only. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.demonwav.mcdev.errorreporter - -import com.intellij.openapi.application.ApplicationNamesInfo -import com.intellij.openapi.application.ex.ApplicationInfoEx -import com.intellij.openapi.diagnostic.Attachment -import com.intellij.openapi.util.SystemInfo -import com.intellij.openapi.util.text.StringUtil - -// It's easier to just re-use the code that we already were using, rather than changing to a map like -// Jetbrains said to do in the deprecation message -class ErrorData(var throwable: Throwable?, private val lastAction: String?) { - - var message: String? = null - get() = field ?: throwable?.message - - var description: String? = null - var pluginName: String? = null - var pluginVersion: String? = null - var attachments = emptyList() - - private val versionRegex by lazy(LazyThreadSafetyMode.NONE) { - Regex("""(?\d{4}\.\d)-(?\d+\.\d+\.\d+)""") - } - - fun formatErrorData(): Pair, List> { - val appInfo = ApplicationInfoEx.getInstanceEx() - val namesInfo = ApplicationNamesInfo.getInstance() - - val params = LinkedHashMap(21) - - params["error.description"] = description - - params["Plugin Name"] = pluginName - params["Plugin Version"] = pluginVersion - - params["OS Name"] = SystemInfo.OS_NAME - params["Java Version"] = SystemInfo.JAVA_VERSION - params["Java VM Vendor"] = SystemInfo.JAVA_VENDOR - - params["App Name"] = namesInfo.productName - params["App Full Name"] = namesInfo.fullProductName - params["App Version Name"] = appInfo.versionName - params["Is EAP"] = appInfo.isEAP.toString() - params["App Build"] = appInfo.build.asString() - params["App Version"] = appInfo.fullVersion - - if (lastAction.isNullOrBlank()) { - params["Last Action"] = "None" - } else { - params["Last Action"] = lastAction - } - - params["error.message"] = message - params["error.raw_stacktrace"] = throwable?.stackTraceToString() - params["error.stacktrace"] = formatStackTrace() - - return params to attachments - } - - private fun formatStackTrace(): String? { - val t = throwable ?: return null - val stackText = escape(t.stackTraceToString()) - - val version = pluginVersion ?: return stackText - val match = versionRegex.matchEntire(version) ?: return stackText - - val intellijVersion = match.groups["intellijVersion"]?.value ?: return stackText - val pluginVersion = match.groups["pluginVersion"]?.value ?: return stackText - - val tag = "$pluginVersion-$intellijVersion" - val baseTagUrl = "https://github.com/minecraft-dev/MinecraftDev/blob/$tag/src/main/kotlin/" - - val links = mutableListOf() - - val mcdevPackage = "com.demonwav.mcdev" - - for (element in t.stackTrace) { - if (!element.className.startsWith(mcdevPackage)) { - continue - } - - val path = element.className.substring(mcdevPackage.length + 1).substringBeforeLast('.').replace('.', '/') - val file = element.fileName - val line = element.lineNumber - - val escapedElement = escape(element) - val linkText = "$escapedElement" - links += LinkedStackTraceElement(escapedElement, linkText) - } - - if (links.isEmpty()) { - return stackText - } - - var currentStackText = stackText - for (link in links) { - currentStackText = link.apply(currentStackText) - } - - return currentStackText - } - - private fun escape(text: Any) = StringUtil.escapeXmlEntities(text.toString()) -} diff --git a/src/main/kotlin/errorreporter/ErrorReporter.kt b/src/main/kotlin/errorreporter/ErrorReporter.kt index 031e49ee6..62421d7dd 100644 --- a/src/main/kotlin/errorreporter/ErrorReporter.kt +++ b/src/main/kotlin/errorreporter/ErrorReporter.kt @@ -26,10 +26,11 @@ import com.intellij.diagnostic.LogMessage import com.intellij.ide.DataManager import com.intellij.ide.plugins.PluginManagerCore import com.intellij.idea.IdeaLogger -import com.intellij.notification.BrowseNotificationAction import com.intellij.notification.NotificationGroupManager import com.intellij.notification.NotificationType import com.intellij.openapi.actionSystem.CommonDataKeys +import com.intellij.openapi.application.ApplicationNamesInfo +import com.intellij.openapi.application.ex.ApplicationInfoEx import com.intellij.openapi.diagnostic.ErrorReportSubmitter import com.intellij.openapi.diagnostic.IdeaLoggingEvent import com.intellij.openapi.diagnostic.SubmittedReportInfo @@ -37,7 +38,15 @@ import com.intellij.openapi.progress.EmptyProgressIndicator import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.Task +import com.intellij.openapi.util.SystemInfo import com.intellij.util.Consumer +import io.sentry.Attachment +import io.sentry.Hint +import io.sentry.SentryClient +import io.sentry.SentryEvent +import io.sentry.SentryLevel +import io.sentry.SentryOptions +import io.sentry.protocol.Message import java.awt.Component class ErrorReporter : ErrorReportSubmitter() { @@ -74,71 +83,76 @@ class ErrorReporter : ErrorReportSubmitter() { return true } - val errorData = ErrorData(event.throwable, IdeaLogger.ourLastActionId) + val sentryEvent = SentryEvent() + sentryEvent.serverName = "" + sentryEvent.level = SentryLevel.ERROR + additionalInfo?.let { sentryEvent.setExtra("additional_info", it) } - errorData.description = additionalInfo - errorData.message = event.message + event.message?.let { msg -> sentryEvent.message = Message().apply { message = msg} } + + val data = event.data + if (data is LogMessage) { + sentryEvent.throwable = data.throwable + } PluginManagerCore.getPlugin(PluginUtil.PLUGIN_ID)?.let { plugin -> - errorData.pluginName = plugin.name - errorData.pluginVersion = plugin.version + val (ideaVersion, coreVersion) = plugin.version.split('-', limit = 2) + sentryEvent.release = plugin.version + sentryEvent.setTag("idea_version", ideaVersion) + sentryEvent.setTag("core_version", coreVersion) } - val data = event.data + val appInfo = ApplicationInfoEx.getInstanceEx() + val namesInfo = ApplicationNamesInfo.getInstance() - if (data is LogMessage) { - errorData.throwable = data.throwable - errorData.attachments = data.includedAttachments - } + sentryEvent.platform = "java" - val (reportValues, attachments) = errorData.formatErrorData() - - val task = AnonymousFeedbackTask( - project, - "Submitting error report", - true, - reportValues, - attachments, - { htmlUrl, token, isDuplicate -> - val type = if (isDuplicate) { - SubmittedReportInfo.SubmissionStatus.DUPLICATE - } else { - SubmittedReportInfo.SubmissionStatus.NEW_ISSUE - } + // Environment + sentryEvent.setExtra("os", SystemInfo.OS_NAME) + sentryEvent.setExtra("java_version", SystemInfo.JAVA_VERSION) + sentryEvent.setExtra("java_vendor", SystemInfo.JAVA_VENDOR) + + // IDE + sentryEvent.setExtra("ide", namesInfo.productName) + sentryEvent.setExtra("ide_version", namesInfo.productName) + sentryEvent.setExtra("ide_is_eap", appInfo.isEAP.toString()) + sentryEvent.setExtra("ide_build", appInfo.build.asString()) + sentryEvent.setExtra("ide_version", appInfo.fullVersion) - val message = if (!isDuplicate) { - "${MCDevBundle("error_reporter.report.created", token)}" + sentryEvent.setExtra("last_action", IdeaLogger.ourLastActionId) + + val task = object : Task.Backgroundable(project, "Submitting error report", true) { + override fun run(indicator: ProgressIndicator) { + val opts = SentryOptions().apply { dsn = "https://e4e537f6f896904a3ec0586bce8c9a57@sentry.mcdev.io/1" } + // Marked internal, but I have no idea what the intended way to do this, everything I can find online + // only ever shows the global static API + val client = SentryClient(opts) + + val hint = if (data is LogMessage && data.allAttachments.isNotEmpty()) { + Hint.withAttachments(data.allAttachments.map { Attachment(it.bytes, it.name) }) } else { - "${MCDevBundle("error_reporter.report.commented", token)}" + null } - val actionText = if (!isDuplicate) { - MCDevBundle("error_reporter.report.created.action") - } else { - MCDevBundle("error_reporter.report.commented.action") + + val id = try { + client.captureEvent(sentryEvent, hint) + } finally { + client.close() } + // It would be nice to get the sentry issue associated with this event, but I don't see a direct way to + // do that right now + val message = "${MCDevBundle("error_reporter.report.created", id.toString())}" NotificationGroupManager.getInstance().getNotificationGroup("Error Report").createNotification( MCDevBundle("error_reporter.report.title"), message, NotificationType.INFORMATION, - ).addAction(BrowseNotificationAction(actionText, htmlUrl)).setImportant(false).notify(project) + ).setImportant(false).notify(project) - val reportInfo = SubmittedReportInfo(htmlUrl, "Issue #$token", type) + val reportInfo = SubmittedReportInfo(null, "Event $id", SubmittedReportInfo.SubmissionStatus.NEW_ISSUE) consumer.consume(reportInfo) - }, - { e -> - val message = "${MCDevBundle("error_reporter.report.error", e.message)}" - val actionText = MCDevBundle("error_reporter.report.error.action") - val userUrl = "https://github.com/minecraft-dev/MinecraftDev/issues" - NotificationGroupManager.getInstance().getNotificationGroup("Error Report").createNotification( - MCDevBundle("error_reporter.report.title"), - message, - NotificationType.ERROR, - ).addAction(BrowseNotificationAction(actionText, userUrl)).setImportant(false).notify(project) - - consumer.consume(SubmittedReportInfo(null, null, SubmittedReportInfo.SubmissionStatus.FAILED)) - }, - ) + } + } if (project == null) { task.run(EmptyProgressIndicator()) diff --git a/src/main/kotlin/errorreporter/LinkedStackTraceElement.kt b/src/main/kotlin/errorreporter/LinkedStackTraceElement.kt deleted file mode 100644 index 088aace92..000000000 --- a/src/main/kotlin/errorreporter/LinkedStackTraceElement.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Minecraft Development for IntelliJ - * - * https://mcdev.io/ - * - * Copyright (C) 2025 minecraft-dev - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, version 3.0 only. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.demonwav.mcdev.errorreporter - -data class LinkedStackTraceElement(val stackElementText: String, val httpLinkText: String) { - fun apply(text: String): String { - return text.replace(stackElementText, httpLinkText) - } -} diff --git a/src/main/kotlin/errorreporter/package-info.kt b/src/main/kotlin/errorreporter/package-info.kt deleted file mode 100644 index 011162375..000000000 --- a/src/main/kotlin/errorreporter/package-info.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Minecraft Development for IntelliJ - * - * https://mcdev.io/ - * - * Copyright (C) 2025 minecraft-dev - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, version 3.0 only. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -/** - * This package was taken from https://github.com/go-lang-plugin-org/go-lang-idea-plugin/pull/909 - */ -package com.demonwav.mcdev.errorreporter diff --git a/src/main/resources/messages/MinecraftDevelopment.properties b/src/main/resources/messages/MinecraftDevelopment.properties index bf9f6e65c..6425bc59e 100644 --- a/src/main/resources/messages/MinecraftDevelopment.properties +++ b/src/main/resources/messages/MinecraftDevelopment.properties @@ -149,12 +149,11 @@ creator.validation.invalid_option=Selection is not a valid option creator.validation.jdk_preferred=Java {0} is recommended for {1} creator.validation.jdk_preferred_default_reason=these settings -error_reporter.submit.action=Report to Minecraft Dev GitHub Issue Tracker -error_reporter.submit.failure=Expected HTTP_CREATED (201), obtained {0} instead. +error_reporter.submit.action=Report to Minecraft Dev error_reporter.submit.ignored=Ignored error error_reporter.report.title=Error report -error_reporter.report.created=Created Issue #{0} successfully. +error_reporter.report.created=Submitted event {0}. error_reporter.report.created.action=View issue error_reporter.report.commented=Commented on existing Issue #{0} successfully. error_reporter.report.commented.action=View comment diff --git a/src/main/resources/messages/MinecraftDevelopment_zh.properties b/src/main/resources/messages/MinecraftDevelopment_zh.properties index f7f845c37..70d6259e5 100644 --- a/src/main/resources/messages/MinecraftDevelopment_zh.properties +++ b/src/main/resources/messages/MinecraftDevelopment_zh.properties @@ -78,12 +78,12 @@ creator.validation.semantic_version=版本必须是有效的语义版本 creator.validation.jdk_preferred=Java {0} 推荐用于 {1} creator.validation.jdk_preferred_default_reason=这些设置 -error_reporter.submit.action=向 Minecraft Dev 的 GitHub Issue Tracker报告 -error_reporter.submit.failure=预期为 HTTP_CREATED (201),结果为 {0}。 +error_reporter.submit.action=向 Minecraft Dev 的 报告 error_reporter.submit.ignored=忽略错误 error_reporter.report.title=错误报告 -error_reporter.report.created=成功创建 Issue #{0}。 + +error_reporter.report.created=成功创建事件 {0}。 error_reporter.report.created.action=查看 issue error_reporter.report.commented=已成功 comment 现有 Issue #{0}。 error_reporter.report.commented.action=查看 comment