Skip to content

Commit

Permalink
Getting closer.
Browse files Browse the repository at this point in the history
  • Loading branch information
lagergren committed Sep 15, 2024
1 parent 2e91b99 commit 0b9811c
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 8 deletions.
File renamed without changes.
17 changes: 12 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
name: Build, Release, and Publish

#
# Perhaps: The release flow is manually triggered, and will release a snapshot version.
# If a release already exists, it will either fail or delete it. If it's a draft, it will redo it.
# Bump the SNAPSHOT version? Or just release current version. It may be confusing to react specially
# and perform a release action just by changing to a non-SNAPSHOT.
#
on:
workflow_dispatch: # Allows manual triggering from GitHub Actions UIon:
inputs:
Expand All @@ -11,13 +17,13 @@ on:
- 'true'
- 'false'
description: Trigger a manual release
push:
#push:
# TODO let the xvm-build-verify action ensure release tags too. It should already be doing that.
#tags:
# - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10'
branches:
# TODO: MASTER
- simplify-tasks
#branches:
# TODO: MASTER
# - simplify-tasks

env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down Expand Up @@ -117,7 +123,6 @@ jobs:
create-release:
name: Create Release
if: env.CURRENT_RELEASE != ''
runs-on: ubuntu-latest
needs: build-release-artifacts # This job will wait for all builds to complete
steps:
Expand All @@ -131,13 +136,15 @@ jobs:
with:
path: ./artifacts
- name: Create Release
if: env.CURRENT_RELEASE != ''
run: |
gh release create ${{ env.VERSION }} \
--title "XDK ${{ env.VERSION }}" \
--notes "Release notes for XDK ${{ env.VERSION }}" \
--prerelease false \
--draft
- name: Upload Release Assets
if: env.CURRENT_RELEASE != ''
run: |
gh release upload ${{ env.VERSION }} \
./artifacts/xdk-${{ env.VERSION }}-*.${{ env.ARTIFACT_SUFFIX }}
71 changes: 71 additions & 0 deletions bin/git-rename-branch.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/bin/bash

resolve_branch() {
local _branch
if ! _branch=$(git branch --show-current); then
echo "ERROR: Failed to resolve current branch."
exit 1
fi
echo "$_branch"
return 0
}

rename_branch() {
branch=$1
branch_new=$2
echo "Renaming $branch to $branch_new"
echo git branch -m "$branch" "$branch_new"
echo git push origin "$branch_new"
echo git push origin --delete "$branch"
echo git push --set-upstream origin "$branch_new"
}

nargs=$#
if [ "$nargs" -eq 1 ]; then
branch="$(resolve_branch)"
branch_new="$1"
echo "WARNING: No original branch name was given, will assume current branch."
elif [ "$nargs" -eq 2 ]; then
branch="$1"
branch_new="$2"
else
echo "ERROR: Invalid number of arguments. Usage: git-rename-branch.sh [branch] <new_branch_name>"
exit 1
fi

echo "HELLO $branch $branch_new"
if [ -z "$branch" ] || [ -z "$branch_new" ]; then
echo "ERROR: Branch name and new branch name cannot be resolved."
exit 1
fi

# Function to prompt for y/n input with default 'n'
ask_yn() {
local prompt="$1"
local response
read -r -p "$prompt [y/N]: " response
case "$response" in
[yY][eE][sS]|[yY])
return 0 # User input is 'yes'
;;
*)
return 1 # Default or user input is 'no'
;;
esac
}


branch_current=$(resolve_branch)
if [ "$branch_current" != "$branch" ]; then
if ! git checkout "$branch"; then
echo "ERROR: Tried to check out branch $branch, but failed. Make sure to stash or commit any changes in $branch_current."
exit 1
fi
branch_current="$branch"
fi

if ask_yn "Are you sure you want to rename branch: $branch -> $branch_new"; then
rename_branch "$branch" "$branch_new"
fi

echo "Finished. Current branch is now: $"
55 changes: 53 additions & 2 deletions build-logic/common-plugins/src/main/kotlin/GitHubProtocol.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ data class GitHubProtocol(private val project: Project) {
}

private val semanticVersion = project.property("semanticVersion") as SemanticVersion
private val releaseVersion = project.releaseVersion()
private val artifactBaseVersion = semanticVersion.artifactVersion.removeSuffix("-SNAPSHOT")
private val tagPrefix = if (semanticVersion.isSnapshot()) "snapshot/" else "release/"
private val localTag = "${tagPrefix}v$artifactBaseVersion"
Expand Down Expand Up @@ -84,7 +85,7 @@ data class GitHubProtocol(private val project: Project) {
/**
* Make sure any local tags out of sync with remote are clobbered and/or updated.
*/
private fun fetch(): GitTagInfo = project.run {
private fun fetchTags(): GitTagInfo = project.run {
git("fetch", "--tags", "--prune-tags", "--force", "--verbose", logger = logger)
return resolveTags()
}
Expand Down Expand Up @@ -114,7 +115,7 @@ data class GitHubProtocol(private val project: Project) {
*/
fun ensureTags(snapshotOnly: Boolean = false): Pair<String, String> = project.run {
// Fetch and prune remote tags, making sure that we have all remote tags locally, and no local tags that aren't on the remote.
val tags = fetch()
val tags = fetchTags()
assert(tags.verifySync())
val isSnapshot = semanticVersion.isSnapshot()
val isRelease = !isSnapshot
Expand All @@ -135,10 +136,15 @@ data class GitHubProtocol(private val project: Project) {

if (isSnapshot) {
git("tag", "-d", localTag, throwOnError = false, logger = logger)
logger.info("$prefix Deleted tag: $localTag (locally), if it existed.")
git("push", "--delete", "origin", localTag, throwOnError = false, logger = logger)
logger.info("$prefix Deleted tag: $localTag (remotely).")
}
git("tag", localTag, logger = logger)
logger.info("$prefix Created $localTag (locally).")

git("push", "origin", localTag, logger = logger)
logger.info("$prefix Pushed $localTag to remote.")

return localTag to tags.localLastCommit
}
Expand All @@ -162,6 +168,51 @@ data class GitHubProtocol(private val project: Project) {
return false
}

fun isReleased(): Boolean = project.run {
// gh release view <version> return if release already exists.
//currentVersion = senamticVersion.ar
var currentVersion = semanticVersion.artifactVersion
val releaseVersion = project.releaseVersion()
val desc = if (releaseVersion != currentVersion) "'$releaseVersion'" else "the same."
logger.lifecycle("$prefix Current project version is '$currentVersion', release version would be $desc")
val result = gh("release", "view", releaseVersion, throwOnError = false)
if (result.isSuccessful()) {
logger.error("$prefix Release '$releaseVersion' already exists. Aborting release.")
return true
}
logger.lifecycle("$prefix Release '$releaseVersion' has not been released.")
return false
}

fun triggerRelease(): Boolean = project.run {
// TODO: Will trigger a remote workflow on GitHub so we can build all our platforms regardless of what machine we are on.
val info = fetchTags()
logger.lifecycle("$prefix Triggering Release GitHub Actions workflow for $semanticVersion.")
val inputs = mapOf(
"semanticVersion" to semanticVersion,
"releaseVersion" to releaseVersion()
)
// Command to trigger GitHub Actions workflow using gh CLI
val workflowId = "release.yml"
val branch = info.localBranchName
val cmdLineArgs = buildList {
add("gh")
add("workflow")
add("run")
add(workflowId)
add("--ref")
add(info.localBranchName)
inputs.forEach { (key, value) ->
add("--field")
add("$key=$value")
}
}
print(cmdLineArgs)
gh(*cmdLineArgs.toTypedArray(), logger = logger)
logger.info("Triggered GitHub workflow $workflowId on branch $branch.")
return true
}

/**
* Delete all versions of a package with a certain name, and or certain versions.
* If no arguments are given, everything is deleted.
Expand Down
3 changes: 3 additions & 0 deletions build-logic/common-plugins/src/main/kotlin/Processes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ data class ProcessResult(val execResult: Pair<Int, String>, val failure: Throwab
val output: String = execResult.second
fun lines(): List<String> = output.lines()
fun isSuccessful(): Boolean = exitValue == 0
fun isFailure(): Boolean = !isSuccessful()
fun rethrowFailure() {
if (failure != null) {
throw failure
Expand Down Expand Up @@ -52,11 +53,13 @@ fun spawn(command: String, vararg args: String, throwOnError: Boolean = true, lo
val commandLine = listOf(commandPath, *args)
val builder = ProcessBuilder(commandLine).redirectErrorStream(true)

System.err.println("[processes] Spawning process: '${commandLine.joinToString(" ")}'")
logger?.info("[processes] Spawning process: '${commandLine.joinToString(" ")}'")

val processResult = try {
val process = builder.start() // can throw IOException
val exitValue = process.waitFor()
System.err.println("Spawn finished: exitValue=$exitValue")
val result = process.inputStream.readTextAndClose()
var failure: IllegalStateException? = null
if (exitValue != 0) {
Expand Down
4 changes: 4 additions & 0 deletions build-logic/common-plugins/src/main/kotlin/XdkBuildLogic.kt
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ fun Task.considerAlwaysUpToDate() {
*/
fun Project.isDryRun() = project.findProperty("dryRun")?.toString()?.toBoolean() ?: false

fun Project.releaseVersion(): String {
return project.version.toString().replace("-SNAPSHOT", "")
}

fun Project.isSnapshot(): Boolean {
return project.version.toString().endsWith("-SNAPSHOT")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ tasks.withType<PublishToMavenRepository>().configureEach {
allowPublication()
}
if (!allowPublication()) {
logger.warn("$prefix Skipping publication task, snapshotOnly=${snapshotOnly()} for version: '$semanticVersion'")
logger.warn("$prefix Skipping publication task, snapshotOnly=${snapshotOnly()} for version: '$semanticVersion')")
}
}

Expand Down
41 changes: 41 additions & 0 deletions xdk/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import XdkBuildLogic.Companion.XDK_ARTIFACT_NAME_DISTRIBUTION_ARCHIVE
import XdkDistribution.Companion.DISTRIBUTION_TASK_GROUP
import XdkDistribution.Companion.JAVATOOLS_INSTALLATION_NAME
import XdkDistribution.Companion.JAVATOOLS_PREFIX_PATTERN
import org.gradle.api.attributes.Category.CATEGORY_ATTRIBUTE
Expand Down Expand Up @@ -202,6 +203,46 @@ val ensureTags by tasks.registering {
}
}

// If we have a snapshot version, create the release for the verison with the snapshot stripped.
//
// If the release already exists, this will fail. If the release exists but is draft, or with an override, we
// may allow overwriting it.
//
// We trigger a GitHub workflow that uploads release artifacts for all our platforms, and then collect them
// and call GH release from here. The default is that the release is in draft mode.
// We can also have a mode where we create a separate pull request after the release, that contains
// e.g. a new entry about the release in some REAADME.md or something, as well as comitting a new VERSION
// file with the next snapshot.
//
// TODO: Figure out the best place to do the tagging. It may be enough and simplest just to do publishRemote
// when the release has been created, and let the existing ensureTagging logic just solve it with 100% existing
// code. Should be safe enough given enough state checks like the checkReleased tasks and so on before we start
// weaving the actual artifacts and release specificaiton.
//
// TODO: Figure out where to hook up publishing the plugin and the XDK artifact to gradlePluinPortal and
// mavenCentral. Verify our mavenCentral credentials again, by publishing a removable normal Java-Maven artifact
// so that we confirm that the XDK artifact is equally legal, even if we don't look 100% like a Java artifact
// For mavenCentral this may still involve faking a jar file, or shipping XDK as a jar file with all other
// stuff as reasources. It should not be a big problem and can be wrapped in abstraction.
val relaseXvm by tasks.registering {
description = "Trigger a GitHub workflow that builds and releases the current branch at the last commit."
doLast {
xdkBuildLogic.gitHubProtocol().triggerRelease()
}
}

val checkUnreleased by tasks.registering {
group = DISTRIBUTION_TASK_GROUP
description = "Fail if we already have a release with the current release version."
doLast {
val releaseVersion = releaseVersion()
if (xdkBuildLogic.gitHubProtocol().isReleased()) {
throw buildException("Release already exists for version: '$releaseVersion'")
}
logger.info("$prefix Successfully resolved new release version: $releaseVersion")
}
}

/**
* Creates distribution contents based on a distribution name, version and classifier.
* This logic is used for the nain distribution artifact (named "xdk"), and the contents
Expand Down

0 comments on commit 0b9811c

Please sign in to comment.