diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3511138 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,111 @@ +name: CI +on: + push: + branches: [main] + pull_request: + +# A second push to a PR makes the first run's answer worthless — the verifier job downloads +# IDEs, so letting both finish is minutes of runner time spent on a stale commit. +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + name: Build and test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 21 + - uses: gradle/actions/setup-gradle@v4 + + # :core holds the parser and its tests; :plugin has none by design (its test task is + # disabled in build.gradle.kts), so `gradlew test` alone reports success either way. + # Naming :core:test means a removed test suite shows up as a task that no longer exists. + - id: coretest + run: ./gradlew :core:test + + - name: Test report + if: always() + run: | + set -euo pipefail + shopt -s nullglob + files=(core/build/test-results/test/TEST-*.xml) + if [ ${#files[@]} -eq 0 ]; then + # No results after a *failed* test step says nothing new — the job is already red + # for the real reason. It is only a finding when the step claimed success. + if [ "${{ steps.coretest.outcome }}" != "success" ]; then + echo "::notice::no test results, but the test step failed above — see it for the cause" + exit 0 + fi + echo "::error:::core:test succeeded and produced no results — did the suite disappear?" + exit 1 + fi + awk -F'"' '/> "$GITHUB_STEP_SUMMARY" + + - run: ./gradlew build verifyPluginProjectConfiguration verifyPluginStructure + + - name: Build the distributable + run: ./gradlew buildPlugin + + - uses: actions/upload-artifact@v4 + with: + name: plugin-zip + path: plugin/build/distributions/*.zip + if-no-files-found: error + + verify: + name: Plugin Verifier + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 21 + - uses: gradle/actions/setup-gradle@v4 + + # Each IDE under test is a ~1GB download. Caching them separately from the Gradle cache + # keeps a dependency change from evicting them. + - uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches/modules-2/files-2.1/com.jetbrains.intellij.idea + ~/.pluginVerifier + key: ide-verifier-${{ hashFiles('plugin/build.gradle.kts') }} + restore-keys: ide-verifier- + + - run: ./gradlew verifyPlugin + + # The task passes when an IDE reports problems below the failure level, and it passes + # just as quietly when the ides {} block resolves to nothing. Assert on the reports: + # one directory per verified IDE, and the count has to match what is declared. + - name: Every declared IDE was actually verified + run: | + set -euo pipefail + shopt -s nullglob + reports=plugin/build/reports/pluginVerifier + declared=$(grep -c '^ *ide("' plugin/build.gradle.kts) + dirs=("$reports"/*/) + echo "declared=$declared verified=${#dirs[@]}" + if [ "${#dirs[@]}" -ne "$declared" ]; then + echo "::error::$declared IDEs declared in pluginVerification, ${#dirs[@]} verified" + for d in "${dirs[@]}"; do echo " $(basename "$d")"; done + exit 1 + fi + for d in "${dirs[@]}"; do + echo "- verified against $(basename "$d")" >> "$GITHUB_STEP_SUMMARY" + done + + - uses: actions/upload-artifact@v4 + if: always() + with: + name: plugin-verifier-reports + path: plugin/build/reports/pluginVerifier + if-no-files-found: warn diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 79750d8..26b8923 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -21,6 +21,8 @@ dependencies { // installed IDE (no ~1GB SDK download); the default downloads Community for CI/others. val localIde = providers.gradleProperty("localIdePath").orNull if (localIde != null) local(localIde) else intellijIdeaCommunity("2024.3.5") + + pluginVerifier() // the CLI the verifyPlugin task shells out to } } @@ -35,6 +37,20 @@ intellijPlatform { untilBuild = "261.*" } } + + // sinceBuild above is a claim; this is what makes it true. 2024.2.5 is the oldest release + // in the 242 line, so it is where an API the plugin uses might not exist yet — the failure + // the compile target cannot show. untilBuild stays unverified on purpose: 261 has no + // release to check against, so it remains a forward-looking promise. + // + // Use the string notation. The ide(IntelliJPlatformType, String) overload accepts these + // versions and then schedules nothing, leaving a green run that verified one IDE. + pluginVerification { + ides { + ide("IC-2024.2.5") // the floor sinceBuild = 242 claims + ide("IC-2024.3.5") // what the plugin compiles against + } + } } // No test sources live here (the parser is tested in :core). Disable the platform test