diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 07ec7b1..b57c131 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,154 +1,168 @@ -name: Build +name: Test and prepare release draft + on: push: - branches: [ main ] + branches: [main] pull_request: + workflow_dispatch: concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true + group: release-${{ github.ref }} + cancel-in-progress: false jobs: - build: - name: Build + detect_version: + name: Detect new version + if: github.event_name != 'pull_request' runs-on: ubuntu-latest + permissions: + contents: read + outputs: + is_new_version: ${{ steps.version.outputs.is_new_version }} + version: ${{ steps.version.outputs.version }} steps: - - name: Maximize Build Space - uses: jlumbroso/free-disk-space@v1.3.1 - with: - tool-cache: false - large-packages: false - - - name: Fetch Sources + - name: Fetch sources uses: actions/checkout@v6 - - - name: Setup Java - uses: actions/setup-java@v5 with: - distribution: zulu - java-version: 21 + fetch-depth: 0 - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v6 + - name: Check version + id: version + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail - - name: Build plugin - run: ./gradlew buildPlugin + version=$(sed -nE 's/^version[[:space:]]*=[[:space:]]*(.+)[[:space:]]*$/\1/p' gradle.properties | head -n 1) + if [[ -z "$version" ]]; then + echo "Unable to read version from gradle.properties" >&2 + exit 1 + fi - - name: Upload artifact - uses: actions/upload-artifact@v7 - with: - name: plugin-distribution - path: ./build/distributions/*.zip + echo "version=$version" >> "$GITHUB_OUTPUT" + + if git rev-parse --verify --quiet "refs/tags/$version" >/dev/null || \ + gh release view "$version" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then + echo "Version $version already has a tag or release; nothing to publish." + echo "is_new_version=false" >> "$GITHUB_OUTPUT" + else + echo "Version $version is new and will be released." + echo "is_new_version=true" >> "$GITHUB_OUTPUT" + fi test: - name: Test - needs: [ build ] + name: Unit, integration, and UI component tests runs-on: ubuntu-latest + timeout-minutes: 20 + permissions: + contents: read steps: - - name: Maximize Build Space + - name: Maximize build space uses: jlumbroso/free-disk-space@v1.3.1 with: tool-cache: false large-packages: false - - name: Fetch Sources + - name: Fetch sources uses: actions/checkout@v6 - - name: Setup Java + - name: Set up Java uses: actions/setup-java@v5 with: distribution: zulu java-version: 21 - - name: Setup Gradle + - name: Set up Gradle uses: gradle/actions/setup-gradle@v6 - with: - cache-read-only: true - - name: Run Tests - run: ./gradlew check + - name: Run unit, integration, and UI component tests + run: >- + ./gradlew unitTest integrationTest ideaUiTest + --continue + --no-configuration-cache + --console=plain - - name: Collect Tests Result + - name: Upload test reports on failure if: ${{ failure() }} uses: actions/upload-artifact@v7 with: - name: tests-result - path: ${{ github.workspace }}/build/reports/tests - - verify: - name: Verify plugin - needs: [ build ] + name: test-reports-${{ github.run_id }} + path: build/reports/tests + if-no-files-found: ignore + retention-days: 7 + + release: + name: Build draft ${{ needs.detect_version.outputs.version }} + needs: [detect_version, test] + if: github.event_name != 'pull_request' && needs.detect_version.outputs.is_new_version == 'true' runs-on: ubuntu-latest + permissions: + contents: write steps: - - name: Maximize Build Space + - name: Maximize build space uses: jlumbroso/free-disk-space@v1.3.1 with: tool-cache: false large-packages: false - - name: Fetch Sources + - name: Fetch sources uses: actions/checkout@v6 - - name: Setup Java + - name: Set up Java uses: actions/setup-java@v5 with: distribution: zulu java-version: 21 - - name: Setup Gradle + - name: Set up Gradle uses: gradle/actions/setup-gradle@v6 - with: - cache-read-only: true - - - name: Run Plugin Verification tasks - run: ./gradlew verifyPlugin - - name: Collect Plugin Verifier Result - if: ${{ always() }} - uses: actions/upload-artifact@v7 - with: - name: pluginVerifier-result - path: ${{ github.workspace }}/build/reports/pluginVerifier + - name: Build plugin distribution + run: ./gradlew buildPlugin --console=plain - releaseDraft: - name: Release draft - if: github.event_name != 'pull_request' - needs: [ build, test, verify ] - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Fetch Sources - uses: actions/checkout@v6 - - - name: Setup Java - uses: actions/setup-java@v5 - with: - distribution: zulu - java-version: 21 - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v6 - with: - cache-read-only: true - - - name: Remove Old Release Drafts + - name: Create release notes env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ needs.detect_version.outputs.version }} run: | - gh api repos/{owner}/{repo}/releases \ - --jq '.[] | select(.draft == true) | .id' \ - | xargs -r -I '{}' gh api -X DELETE repos/{owner}/{repo}/releases/{} - - - name: Create Release Draft + mkdir -p build/tmp + ./gradlew getChangelog \ + --project-version "$VERSION" \ + --no-header \ + --no-links \ + --quiet \ + --console=plain \ + --output-file=build/tmp/release-note.md + + - name: Create draft release and upload distribution env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ github.token }} + VERSION: ${{ needs.detect_version.outputs.version }} + shell: bash run: | - VERSION=$(./gradlew properties --property version --quiet --console=plain | tail -n 1 | cut -f2- -d ' ') - RELEASE_NOTE="./build/tmp/release_note.txt" - ./gradlew getChangelog --unreleased --no-header --quiet --console=plain --output-file=$RELEASE_NOTE - - gh release create "$VERSION" \ - --draft \ - --title "$VERSION" \ - --notes-file "$RELEASE_NOTE" + set -euo pipefail + shopt -s nullglob + distributions=(build/distributions/*.zip) + + if (( ${#distributions[@]} == 0 )); then + echo "No plugin distribution found in build/distributions" >&2 + exit 1 + fi + + if gh release view "$VERSION" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then + gh release edit "$VERSION" \ + --repo "$GITHUB_REPOSITORY" \ + --draft \ + --title "$VERSION" \ + --notes-file build/tmp/release-note.md + gh release upload "$VERSION" "${distributions[@]}" \ + --repo "$GITHUB_REPOSITORY" \ + --clobber + else + gh release create "$VERSION" "${distributions[@]}" \ + --repo "$GITHUB_REPOSITORY" \ + --target "$GITHUB_SHA" \ + --draft \ + --title "$VERSION" \ + --notes-file build/tmp/release-note.md + fi diff --git a/.github/workflows/publish-marketplace.yml b/.github/workflows/publish-marketplace.yml new file mode 100644 index 0000000..02afc10 --- /dev/null +++ b/.github/workflows/publish-marketplace.yml @@ -0,0 +1,75 @@ +name: Publish plugin to JetBrains Marketplace + +on: + release: + types: [published] + +concurrency: + group: marketplace-${{ github.event.release.tag_name }} + cancel-in-progress: false + +jobs: + publish: + name: Publish ${{ github.event.release.tag_name }} + runs-on: ubuntu-latest + steps: + - name: Fetch released sources + uses: actions/checkout@v6 + with: + ref: ${{ github.event.release.tag_name }} + + - name: Validate release version + env: + RELEASE_TAG: ${{ github.event.release.tag_name }} + shell: bash + run: | + set -euo pipefail + project_version=$(sed -nE 's/^version[[:space:]]*=[[:space:]]*(.+)[[:space:]]*$/\1/p' gradle.properties | head -n 1) + + if [[ -z "$project_version" ]]; then + echo "Unable to read version from gradle.properties" >&2 + exit 1 + fi + + if [[ "$RELEASE_TAG" != "$project_version" ]]; then + echo "Release tag '$RELEASE_TAG' does not match gradle.properties version '$project_version'." >&2 + exit 1 + fi + + - name: Set up Java + uses: actions/setup-java@v5 + with: + distribution: zulu + java-version: 21 + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@v6 + + - name: Check publishing secrets + env: + PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }} + CERTIFICATE_CHAIN: ${{ secrets.CERTIFICATE_CHAIN }} + PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} + PRIVATE_KEY_PASSWORD: ${{ secrets.PRIVATE_KEY_PASSWORD }} + shell: bash + run: | + set -euo pipefail + missing_secrets=() + for secret_name in PUBLISH_TOKEN CERTIFICATE_CHAIN PRIVATE_KEY PRIVATE_KEY_PASSWORD; do + if [[ -z "${!secret_name}" ]]; then + missing_secrets+=("$secret_name") + fi + done + + if (( ${#missing_secrets[@]} > 0 )); then + echo "Missing required repository secrets: ${missing_secrets[*]}" >&2 + exit 1 + fi + + - name: Publish to JetBrains Marketplace + env: + PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }} + CERTIFICATE_CHAIN: ${{ secrets.CERTIFICATE_CHAIN }} + PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} + PRIVATE_KEY_PASSWORD: ${{ secrets.PRIVATE_KEY_PASSWORD }} + run: ./gradlew publishPlugin --console=plain diff --git a/.github/workflows/run-ui-tests.yml b/.github/workflows/run-ui-tests.yml index 82634cd..0afff1c 100644 --- a/.github/workflows/run-ui-tests.yml +++ b/.github/workflows/run-ui-tests.yml @@ -1,63 +1,44 @@ -# GitHub Actions Workflow for launching UI tests on Linux, Windows, and Mac in the following steps: -# - Prepare and launch IDE with your plugin and robot-server plugin, which is needed to interact with the UI. -# - Wait for IDE to start. -# - Run UI tests with a separate Gradle task. -# -# Please check https://github.com/JetBrains/intellij-ui-test-robot for information about UI tests with IntelliJ Platform. -# -# Workflow is triggered manually. +name: Run UI component tests -name: Run UI Tests on: - workflow_dispatch + workflow_dispatch: -jobs: +concurrency: + group: ui-component-tests-${{ github.ref }} + cancel-in-progress: true - testUI: +jobs: + ui_component_test: + name: UI component tests (${{ matrix.os }}) runs-on: ${{ matrix.os }} + timeout-minutes: 20 strategy: fail-fast: false matrix: - include: - - os: ubuntu-latest - runIde: | - export DISPLAY=:99.0 - Xvfb -ac :99 -screen 0 1920x1080x16 & - gradle runIdeForUiTests & - - os: windows-latest - runIde: start gradlew.bat runIdeForUiTests - - os: macos-latest - runIde: ./gradlew runIdeForUiTests & - + os: [ubuntu-latest, windows-latest, macos-latest] + permissions: + contents: read steps: - - # Check out the current repository - - name: Fetch Sources + - name: Fetch sources uses: actions/checkout@v6 - # Set up Java environment for the next steps - - name: Setup Java + - name: Set up Java uses: actions/setup-java@v5 with: distribution: zulu java-version: 21 - # Setup Gradle - - name: Setup Gradle + - name: Set up Gradle uses: gradle/actions/setup-gradle@v6 - # Run IDEA prepared for UI testing - - name: Run IDE - run: ${{ matrix.runIde }} + - name: Run IntelliJ UI component tests + run: ./gradlew ideaUiTest --no-configuration-cache --console=plain - # Wait for IDEA to be started - - name: Health Check - uses: jtalk/url-health-check-action@v4 + - name: Upload UI test reports on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v7 with: - url: http://127.0.0.1:8082 - max-attempts: 15 - retry-delay: 30s - - # Run tests - - name: Tests - run: ./gradlew test + name: ui-test-reports-${{ matrix.os }}-${{ github.run_id }} + path: build/reports/tests/ideaUiTest + if-no-files-found: ignore + retention-days: 7 diff --git a/CHANGELOG.md b/CHANGELOG.md index 810ddcc..6e5ff49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ ## [Unreleased] +## [2.2.0] - 2026-06-06 +### Added +- 新增基于版本号的 GitHub Release 草稿流程,自动附加 `build/distributions` 中的插件 ZIP。 +- 新增 GitHub Release 正式发布后自动推送 JetBrains Marketplace 的独立工作流。 +- 恢复单元测试、集成测试和 UI 组件测试门禁,并提供手动跨平台 UI 组件测试。 + +### Changed +- 移除耗时且需要下载额外 IDE 的 Plugin Verifier 步骤。 +- 测试任务禁用不兼容的 Gradle Configuration Cache,避免测试通过后因缓存序列化失败。 + ## [2.1.0] - 2026-06-05 ### Changed - 迁移到 IntelliJ Platform Gradle Plugin 2.x 最新模板结构,构建目标调整为 IntelliJ IDEA 2025.3+ / build 253+。 @@ -69,7 +79,8 @@ - Project initialization, covering data analysis, ORM & ROM navigation. - 项目初始化,包含数据分析、ORM&ROM导航。 -[Unreleased]: https://github.com/wl2027/runtime-pivot/compare/2.1.0...HEAD +[Unreleased]: https://github.com/wl2027/data-pivot-plugin/compare/2.2.0...HEAD +[2.2.0]: https://github.com/wl2027/data-pivot-plugin/compare/2.1.0...2.2.0 [2.1.0]: https://github.com/wl2027/runtime-pivot/compare/2.0.0...2.1.0 [2.0.0]: https://github.com/wl2027/runtime-pivot/compare/1.1.2...2.0.0 [1.1.2]: https://github.com/wl2027/runtime-pivot/compare/1.1.1...1.1.2 diff --git a/build.gradle.kts b/build.gradle.kts index 47bd9ec..22df93e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -118,6 +118,9 @@ tasks { val defaultTest = named("test") withType { + // IntelliJ test setup resolves the platform and cleans per-task IDE directories in doFirst. + // Those actions capture Gradle project state and therefore cannot be serialized safely. + notCompatibleWithConfigurationCache("IntelliJ test setup uses runtime project state") useJUnit() jvmArgs( "--add-opens=java.base/java.lang=ALL-UNNAMED", @@ -204,24 +207,3 @@ tasks { dependsOn("unitTest", "integrationTest", "ideaUiTest") } } - -intellijPlatformTesting { - runIde { - register("runIdeForUiTests") { - task { - jvmArgumentProviders += CommandLineArgumentProvider { - listOf( - "-Drobot-server.port=8082", - "-Dide.mac.message.dialogs.as.sheets=false", - "-Djb.privacy.policy.text=", - "-Djb.consents.confirmation.enabled=false", - ) - } - } - - plugins { - robotServerPlugin() - } - } - } -} diff --git a/gradle.properties b/gradle.properties index 2875973..e9a79ad 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ # IntelliJ Platform Artifacts Repositories -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html group = com.data.pivot.plugin -version = 2.1.0 +version = 2.2.0 pluginRepositoryUrl = https://github.com/wl2027/data-pivot-plugin