Skip to content

Provide plugin publishing workflow - #29

Open
Vladyslav-Kuksiuk wants to merge 3 commits into
masterfrom
publish
Open

Provide plugin publishing workflow#29
Vladyslav-Kuksiuk wants to merge 3 commits into
masterfrom
publish

Conversation

@Vladyslav-Kuksiuk

@Vladyslav-Kuksiuk Vladyslav-Kuksiuk commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

This PR provides the plugin publishing workflow and initial publishing.

Also, provides version incrementation guard.

Resolves #3
Resolves #4

@Vladyslav-Kuksiuk Vladyslav-Kuksiuk self-assigned this Jul 28, 2026
@Vladyslav-Kuksiuk
Vladyslav-Kuksiuk marked this pull request as ready for review July 28, 2026 15:16
@Vladyslav-Kuksiuk
Vladyslav-Kuksiuk marked this pull request as draft July 28, 2026 15:16
@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.35%. Comparing base (6fe6627) to head (f37c500).

Additional details and impacted files
@@            Coverage Diff            @@
##             master      #29   +/-   ##
=========================================
  Coverage     94.35%   94.35%           
  Complexity       84       84           
=========================================
  Files             9        9           
  Lines           744      744           
  Branches         99       99           
=========================================
  Hits            702      702           
  Misses           20       20           
  Partials         22       22           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Vladyslav-Kuksiuk
Vladyslav-Kuksiuk marked this pull request as ready for review July 28, 2026 16:18
--pinentry-mode loopback \
--passphrase-fd 0 \
--output "$HOME/.gradle/gradle.properties" \
--decrypt .github/keys/gradle-plugin-portal.secret.properties.gpg

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Must fix — .gitattributes has no rule for *.gpg, so Git normalizes line endings inside the credential ciphertext.

git check-attr -a .github/keys/gradle-plugin-portal.secret.properties.gpg returns nothing, so the blob falls back to Git's text auto-detection. Checking the committed bytes: the file is 179 bytes of GPG symmetrically encrypted data (AES256 cipher) and contains no NUL byte, so Git's heuristic classifies it as text. It does contain a lone LF at offset 171 (and a lone CR at offset 1).

On a checkout with core.autocrlf=true — the default on GitHub's windows-latest runners, which check.yml already uses, and on many Windows workstations — Git rewrites that lone LF to CRLF. The working-tree file becomes 180 bytes, so the OpenPGP symmetric-encrypted-data packet no longer matches its declared payload and this gpg --decrypt cannot succeed. (I verified the byte layout and the missing attribute directly; gpg is not installed here, so the decrypt failure itself is inferred from the byte change rather than observed.)

PROJECT.md states the cross-platform invariant this breaks: "Preserve cross-platform paths, permissions, line endings, and process behavior."

.gitattributes already declares *.jar binary. Add the same rule for the key:

*.gpg binary

Then confirm with git check-attr -a .github/keys/gradle-plugin-portal.secret.properties.gpg that it resolves to text: unset.


- name: Check Build
shell: bash
run: ./gradlew :buildSrc:check check

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should fix — this check runs without the Java 17 toolchain that CI is required to provision.

buildSrc/src/main/kotlin/jvm-module.gradle.kts:90 pins the test launcher to BuildSettings.bytecodeVersion (17), and check.yml handles that by installing Java 17 first, exporting EMBED_CODE_JAVA_17_HOME, and passing -Dorg.gradle.java.installations.paths="$EMBED_CODE_JAVA_17_HOME". This job installs only Java 25, so test and functionalTest have no local JDK 17 and Gradle has to resolve one through api.foojay.io and download it on every release run.

PROJECT.md states the rule directly: "CI provisions its required JDKs before invoking Gradle. This build-time provisioning is outside the plugin's SHA-256 verification of Embed Code release assets." Routing the release build through an unpinned third-party toolchain download is exactly what that line exists to prevent, and it makes a foojay/CDN outage a release-blocking failure.

This step has also never actually executed in CI — the trial Publish run on this branch (run 30372596781) went straight from Set Up Gradle to Decrypt Gradle Plugin Portal Credentials, so the gap is untested.

Mirror check.yml: add the Set Up Java 17 for Compatibility Tests and Save Java 17 Toolchain steps before Set Up Java 25, and pass -Dorg.gradle.java.installations.paths="$EMBED_CODE_JAVA_17_HOME" on this invocation.

--passphrase-fd 0 \
--output "$HOME/.gradle/gradle.properties" \
--decrypt .github/keys/gradle-plugin-portal.secret.properties.gpg
chmod 600 "$HOME/.gradle/gradle.properties"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should fix — the decrypted credentials are world-readable until this chmod runs.

gpg --output creates the plaintext file with 0666 & ~umask, i.e. mode 0644 under the runner's default umask 022. The chmod 600 only closes that window afterwards, so the Portal key and secret exist at 0644 for the duration of the decryption.

PROJECT.md lists this as a trust boundary: "restrict the decrypted Gradle properties file to its owner". The current sequence satisfies the end state but not the invariant.

Create the file restricted rather than fixing it up — either run umask 077 immediately before the gpg invocation (keeping this chmod is harmless), or pre-create the target with install -m 600 /dev/null "$HOME/.gradle/gradle.properties" and let gpg --yes write into it.

}
}

rootProject.tasks.named<CheckVersionIncrement>("checkVersionIncrement") {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should fix — pluginId has no value whenever :gradle-plugin is not configured.

checkVersionIncrement is registered in the root build.gradle.kts with pluginVersion and portalBaseUrl set but no value or convention for pluginId; this cross-project reach-back is the only assignment. Any invocation that configures the root project without this one leaves the guard's required input unset, so it fails on validation instead of querying the Portal. Reproduced on this branch:

$ ./gradlew --configure-on-demand :checkVersionIncrement

A problem was found with the configuration of task ':checkVersionIncrement' (type 'CheckVersionIncrement').
Value not set
  Type 'io.spine.embedcode.gradle.publish.CheckVersionIncrement' property 'pluginId' doesn't have a configured value

./gradlew checkVersionIncrement passes today only because nothing enables configure-on-demand — the guard fails closed, but for the wrong reason and with a message that says nothing about publication. rootProject.tasks.named from a subproject is also cross-project state access, which Isolated Projects rejects, so this blocks that mode later.

Smallest fix: give the property a value where the task is registered — pluginId.convention("io.spine.embed-code") in the root script — and delete this block. If keeping the id declared exactly once matters more, register checkVersionIncrement in this module next to embedCodePlugin instead; both workflows invoke it unqualified, so ./gradlew checkVersionIncrement keeps working and nothing crosses a project boundary.

}
}

gradlePlugin {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit — nothing in the build ties publishPlugins to the version guard.

checkVersionIncrement protects publication only because publish.yml happens to invoke it in an earlier step. A local ./gradlew :gradle-plugin:publishPlugins, or a later workflow edit that drops or reorders that step, republishes with no check at all.

If the task moves into this module as suggested on the rootProject.tasks.named block above, tasks.named("publishPlugins") { dependsOn(checkVersionIncrement) } would make the guard part of the publication contract rather than of CI step ordering. Non-blocking — the workflow does enforce it today.

@Oleg-Melnik Oleg-Melnik left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Vladyslav-Kuksiuk LGTM with comments to address.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add publish workflow Add version increment guard

2 participants