diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..6db3a41 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,34 @@ +# Dependabot configuration: +# https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + # Maintain dependencies for Gradle dependencies + - package-ecosystem: "gradle" + directory: "/" + target-branch: "main" + schedule: + interval: "weekly" + time: "06:00" + timezone: "America/Chicago" + ignore: + # these depend on the toolbox API and should be updated manually + - dependency-name: "org.jetbrains.kotlin.jvm" + - dependency-name: "org.jetbrains.kotlin.plugin.serialization" + - dependency-name: "com.google.devtools.ksp" + # these can have breaking changes + - dependency-name: "com.jetbrains.toolbox:core-api" + - dependency-name: "com.jetbrains.toolbox:ui-api" + - dependency-name: "com.jetbrains.toolbox:remote-dev-api" + commit-message: + prefix: "chore" + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + target-branch: "main" + schedule: + interval: "weekly" + time: "06:00" + timezone: "America/Chicago" + commit-message: + prefix: "chore" \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..70187f5 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,132 @@ +# GitHub Actions workflow for testing and preparing the plugin release. +# GitHub Actions reference: https://help.github.com/en/actions + +name: Coder Toolbox Plugin Build + +on: + push: + branches: + - main + pull_request: + +jobs: + # Run plugin tests on every supported platform. + test: + strategy: + matrix: + platform: + - ubuntu-latest + - macos-latest + - windows-latest + runs-on: ${{ matrix.platform }} + steps: + - uses: actions/checkout@v4.2.2 + + - uses: actions/setup-java@v4 + with: + distribution: zulu + java-version: 21 + cache: gradle + + - uses: gradle/wrapper-validation-action@v3.5.0 + + # Run tests + - run: ./gradlew test --info + + # Collect Tests Result of failed tests + - if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: tests-result + path: ${{ github.workspace }}/build/reports/tests + + build: + name: Build + needs: test + runs-on: ubuntu-latest + outputs: + version: ${{ steps.properties.outputs.version }} + changelog: ${{ steps.properties.outputs.changelog }} + steps: + # Check out current repository + - name: Fetch Sources + uses: actions/checkout@v4.2.2 + + # Setup Java 21 environment for the next steps + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: zulu + java-version: 21 + cache: gradle + + # Set environment variables + - name: Export Properties + id: properties + shell: bash + run: | + PROPERTIES="$(./gradlew properties --console=plain -q)" + VERSION="$(echo "$PROPERTIES" | grep "^version:" | cut -f2- -d ' ')" + NAME="$(echo "$PROPERTIES" | grep "^group:" | cut -f2- -d ' ')" + CHANGELOG="$(./gradlew getChangelog --unreleased --no-header --console=plain -q)" + CHANGELOG="${CHANGELOG//'%'/'%25'}" + CHANGELOG="${CHANGELOG//$'\n'/'%0A'}" + CHANGELOG="${CHANGELOG//$'\r'/'%0D'}" + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "name=$NAME" >> $GITHUB_OUTPUT + echo "changelog=$CHANGELOG" >> $GITHUB_OUTPUT + + # Run plugin build + - name: Run Build + run: ./gradlew clean pluginZip --info + + # Prepare plugin archive content for creating artifact + - name: Prepare Plugin Artifact + id: artifact + shell: bash + run: | + cd ${{ github.workspace }}/build/distributions + FILENAME=$(ls *.zip) + unzip "$FILENAME" -d content + echo "filename=${FILENAME:0:-4}" >> $GITHUB_OUTPUT + + # Store already-built plugin as an artifact for downloading + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ steps.artifact.outputs.filename }} + path: ./build/distributions/content/*/* + + # Prepare a draft release for GitHub Releases page for the manual verification + # If accepted and published, release workflow would be triggered + releaseDraft: + name: Release Draft + if: github.event_name != 'pull_request' + needs: build + runs-on: ubuntu-latest + steps: + + # Check out current repository + - name: Fetch Sources + uses: actions/checkout@v4.2.2 + + # Remove old release drafts by using GitHub CLI + - name: Remove Old Release Drafts + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh api repos/${{ github.repository }}/releases \ + --jq '.[] | select(.draft == true) | .id' \ + | xargs -I '{}' gh api -X DELETE repos/${{ github.repository }}/releases/{} + + # Create new release draft - which is not publicly visible and requires manual acceptance + - name: Create Release Draft + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + echo "${{ needs.build.outputs.changelog }}" > RELEASE_NOTES.md + gh release create v${{ needs.build.outputs.version }} \ + --draft \ + --target ${GITHUB_REF_NAME} \ + --title "v${{ needs.build.outputs.version }}" \ + --notes-file RELEASE_NOTES.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..7eccb36 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,91 @@ +# GitHub Actions Workflow created for handling the release process based on the draft release prepared with the Build workflow. + +name: Release +on: + release: + types: [ prereleased, released ] + +jobs: + + # Prepare and publish the plugin to the Marketplace repository + release: + name: Publish Plugin + runs-on: ubuntu-latest + steps: + + # Check out current repository + - name: Fetch Sources + uses: actions/checkout@v4.2.2 + with: + ref: ${{ github.event.release.tag_name }} + + # Setup Java 21 environment for the next steps + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: zulu + java-version: 21 + cache: gradle + + # Set environment variables + - name: Export Properties + id: properties + shell: bash + run: | + CHANGELOG="$(cat << 'EOM' | sed -e 's/^[[:space:]]*$//g' -e '/./,$!d' + ${{ github.event.release.body }} + EOM + )" + + CHANGELOG="${CHANGELOG//'%'/'%25'}" + CHANGELOG="${CHANGELOG//$'\n'/'%0A'}" + CHANGELOG="${CHANGELOG//$'\r'/'%0D'}" + + echo "changelog=$CHANGELOG" >> $GITHUB_OUTPUT + + # Update Unreleased section with the current release note + - name: Patch Changelog + if: ${{ steps.properties.outputs.changelog != '' }} + env: + CHANGELOG: ${{ steps.properties.outputs.changelog }} + run: | + ./gradlew patchChangelog --release-note="$CHANGELOG" + + # Publish the plugin to the Marketplace + # TODO - enable this step (by removing the `if` block) when JetBrains is clear about release procedures + - name: Publish Plugin + if: false + 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 --info + + # Upload artifact as a release asset + - name: Upload Release Asset + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: gh release upload ${{ github.event.release.tag_name }} ./build/distributions/* + + # Create pull request + - name: Create Pull Request + if: ${{ steps.properties.outputs.changelog != '' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + VERSION="${{ github.event.release.tag_name }}" + BRANCH="changelog-update-$VERSION" + + git config user.email "action@github.com" + git config user.name "GitHub Action" + + git checkout -b $BRANCH + git commit -am "Changelog update - $VERSION" + git push --set-upstream origin $BRANCH + + gh pr create \ + --title "Changelog update - \`$VERSION\`" \ + --body "Current pull request contains patched \`CHANGELOG.md\` file for the \`$VERSION\` version." \ + --base main \ + --head $BRANCH \ No newline at end of file diff --git a/.gitignore b/.gitignore index ba33143..01b3214 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,7 @@ build jvm/ # IntelliJ IDEA -.idea \ No newline at end of file +.idea + +# hidden macOS metadata files +.DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..6867b6f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +# Coder Toolbox Plugin Changelog + +## Unreleased + +### Added + +- initial support for JetBrains Toolbox 2.6.0.38311 with the possibility to manage the workspaces - i.e. start, stop, + update and delete actions and also quick shortcuts to templates, web terminal and dashboard. \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 4c89011..a541a15 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,6 +13,7 @@ plugins { alias(libs.plugins.dependency.license.report) alias(libs.plugins.ksp) alias(libs.plugins.gradle.wrapper) + alias(libs.plugins.changelog) } buildscript { @@ -50,6 +51,16 @@ dependencies { testImplementation(kotlin("test")) } +val pluginId = properties("group") +val pluginName = properties("name") +val pluginVersion = properties("version") + +changelog { + version.set(pluginVersion) + groups.set(emptyList()) + title.set("Coder Toolbox Plugin Changelog") +} + licenseReport { renderers = arrayOf(JsonReportRenderer("dependencies.json")) filters = arrayOf(ExcludeTransitiveDependenciesFilter()) @@ -65,9 +76,6 @@ tasks.test { useJUnitPlatform() } -val pluginId = "com.coder.toolbox" -val pluginVersion = "0.0.1" - val assemblePlugin by tasks.registering(Jar::class) { archiveBaseName.set(pluginId) from(sourceSets.main.get().output) @@ -75,7 +83,12 @@ val assemblePlugin by tasks.registering(Jar::class) { val copyPlugin by tasks.creating(Sync::class.java) { dependsOn(assemblePlugin) + fromCompileDependencies() + + into(getPluginInstallDir()) +} +fun CopySpec.fromCompileDependencies() { from(assemblePlugin.get().outputs.files) from("src/main/resources") { include("extension.json") @@ -97,8 +110,14 @@ val copyPlugin by tasks.creating(Sync::class.java) { } }, ) +} - into(getPluginInstallDir()) +val pluginZip by tasks.creating(Zip::class) { + dependsOn(assemblePlugin) + + fromCompileDependencies() + into(pluginId) + archiveBaseName.set(pluginName) } tasks.register("cleanAll", Delete::class.java) { @@ -126,26 +145,14 @@ private fun getPluginInstallDir(): Path { return pluginsDir / pluginId } -val pluginZip by tasks.creating(Zip::class) { - dependsOn(assemblePlugin) - - from(assemblePlugin.get().outputs.files) - from("src/main/resources") { - include("extension.json") - include("dependencies.json") - } - from("src/main/resources") { - include("icon.svg") - rename("icon.svg", "pluginIcon.svg") - } - archiveBaseName.set("$pluginId-$pluginVersion") -} - -val uploadPlugin by tasks.creating { +val publishPlugin by tasks.creating { dependsOn(pluginZip) doLast { - val instance = PluginRepositoryFactory.create("https://plugins.jetbrains.com", project.property("pluginMarketplaceToken").toString()) + val instance = PluginRepositoryFactory.create( + "https://plugins.jetbrains.com", + project.property("PUBLISH_TOKEN").toString() + ) // first upload // instance.uploader.uploadNewPlugin(pluginZip.outputs.files.singleFile, listOf("toolbox", "gateway"), LicenseUrl.APACHE_2_0, ProductFamily.TOOLBOX) @@ -163,3 +170,5 @@ tasks.register("classpath") { ) } } + +fun properties(key: String) = project.findProperty(key).toString() \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..77308ba --- /dev/null +++ b/gradle.properties @@ -0,0 +1,3 @@ +version=0.1.0 +group=com.coder.toolbox +name=coder-toolbox diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fc96a97..00bb2c7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,6 +12,7 @@ exec = "1.12" moshi = "1.15.1" ksp = "2.1.0-1.0.29" retrofit = "2.8.2" +changelog = "2.2.1" [libraries] toolbox-core-api = { module = "com.jetbrains.toolbox:core-api", version.ref = "toolbox-plugin-api" } @@ -40,4 +41,5 @@ kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } dependency-license-report = { id = "com.github.jk1.dependency-license-report", version.ref = "dependency-license-report" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp"} -gradle-wrapper = { id = "me.filippov.gradle.jvm.wrapper", version.ref = "gradle-wrapper" } \ No newline at end of file +gradle-wrapper = { id = "me.filippov.gradle.jvm.wrapper", version.ref = "gradle-wrapper" } +changelog = { id = "org.jetbrains.changelog", version.ref = "changelog" } \ No newline at end of file