diff --git a/.github/workflows/README.adoc b/.github/workflows/README.adoc new file mode 100644 index 000000000..36bdf7204 --- /dev/null +++ b/.github/workflows/README.adoc @@ -0,0 +1,56 @@ +== The YAML workflow files vs. the `*.main.kts` files + +The YAML workflow files are generated from the `*.main.kts` files. + +These use the https://github.com/typesafegithub/github-workflows-kt[github-workflows-kt] +Kotlin DSL library to conveniently and type-safely write GitHub Action workflow files. + +As there is no official built-in support in GitHub Actions yet until +https://github.com/orgs/community/discussions/15904 is considered, the YAML files +need to be generated manually. + +There is a safeguard check in all the generated files that this is not forgotten. +Running a workflow where the according `*.main.kts` produces a different output will +fail the execution. Additionally, the workflow that runs for pushes and pull requests +checks the consistency of all the YAML files as not all might be run for pull requests. + + + +== Ways to generate the YAML workflow files + +There are multiple ways to generate the YAML files and all of them are fine, +but be aware of the last one of the caveats below if you are not using the Gradle method: + +* If you are in a `sh` derivate like e.g. `bash` and Kotlin is installed and + available in the `PATH`, you can just call the `*.main.kts` script like any + other shell script: ++ +[source,bash] +---- +$ ./release.main.kts +---- + +* If Kotlin is installed somewhere you can call it with the `*.main.kts` script + as argument: ++ +[source,bash] +---- +$ path/to/kotlin release.main.kts +---- + +* From the IDE you can create a run configuration that executes the `*.main.kts` script. + + + +== Caveat + +There is currently one known caveats with the approach we follow. + +* https://youtrack.jetbrains.com/issue/KTIJ-16532 ++ +If you navigate to a file in the dependencies, only a decompiled file is opened, +even though the source JAR would be available. Also the quick documentation is missing. ++ +This can easily by mitigated by attaching the library to the normal project +dependencies while having the need to navigate the source files or while editing them, +which makes them properly viewable and documentation displayable in the editor. diff --git a/.github/workflows/build.main.kts b/.github/workflows/build.main.kts new file mode 100755 index 000000000..e4e5f9e13 --- /dev/null +++ b/.github/workflows/build.main.kts @@ -0,0 +1,280 @@ +#!/usr/bin/env kotlin + +@file:Repository("https://repo.maven.apache.org/maven2/") +@file:DependsOn("io.github.typesafegithub:github-workflows-kt:2.3.0") + +@file:Repository("https://bindings.krzeminski.it/") +@file:DependsOn("actions:checkout:v4") +@file:DependsOn("actions:setup-java:v4") +@file:DependsOn("DamianReeves:write-file-action:v1.3") +@file:DependsOn("actions:upload-artifact:v4") +@file:DependsOn("actions:upload-artifact__merge:v4") + +import io.github.typesafegithub.workflows.actions.actions.Checkout +import io.github.typesafegithub.workflows.actions.actions.SetupJava +import io.github.typesafegithub.workflows.actions.actions.SetupJava.Distribution.Temurin +import io.github.typesafegithub.workflows.actions.actions.UploadArtifact +import io.github.typesafegithub.workflows.actions.actions.UploadArtifact.BehaviorIfNoFilesFound +import io.github.typesafegithub.workflows.actions.actions.UploadArtifact.BehaviorIfNoFilesFound.Error +import io.github.typesafegithub.workflows.actions.actions.UploadArtifact.BehaviorIfNoFilesFound.Ignore +import io.github.typesafegithub.workflows.actions.actions.UploadArtifact.CompressionLevel.NoCompression +import io.github.typesafegithub.workflows.actions.damianreeves.WriteFileAction +import io.github.typesafegithub.workflows.domain.AbstractResult.Status.Success +import io.github.typesafegithub.workflows.domain.RunnerType +import io.github.typesafegithub.workflows.domain.Shell.Bash +import io.github.typesafegithub.workflows.domain.triggers.PullRequest +import io.github.typesafegithub.workflows.domain.triggers.Push +import io.github.typesafegithub.workflows.dsl.expressions.Contexts.always +import io.github.typesafegithub.workflows.dsl.expressions.Contexts.runner +import io.github.typesafegithub.workflows.dsl.expressions.expr +import io.github.typesafegithub.workflows.dsl.workflow +import io.github.typesafegithub.workflows.yaml.ConsistencyCheckJobConfig.Disabled + +workflow( + name = "Build", + on = listOf( + Push(), + PullRequest() + ), + sourceFile = __FILE__, + //TODO + consistencyCheckJobConfig = Disabled +) { + //TODO + //job( + // id = "check_all_workflow_yaml_consistency", + // name = "Check all Workflow YAML consistency", + // runsOn = UbuntuLatest + //) { + // uses( + // name = "Checkout", + // action = Checkout() + // ) + // run( + // name = "Regenerate all workflow YAMLs and check for modifications", + // command = "find .github/workflows -mindepth 1 -maxdepth 1 -name '*.main.kts' | xargs -ri sh -c '{} && git diff --exit-code'" + // ) + //} + + job( + id = "build", + name = "Build on ${expr("matrix.os")}", + runsOn = RunnerType.Custom(expr("matrix.os")), + _customArguments = mapOf( + "strategy" to mapOf( + "fail-fast" to false, + "matrix" to mapOf( + "os" to listOf( + "ubuntu-latest", + "windows-latest", + "macos-latest" + ) + ) + ) + ) + ) { + run( + name = "Install InnoSetup on Linux", + command = """ + sudo dpkg --add-architecture i386 + sudo wget -nc -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key + sudo wget -NP /etc/apt/sources.list.d/ "https://dl.winehq.org/wine-builds/ubuntu/dists/${'$'}(lsb_release -c | grep -o '\w*${'$'}')/winehq-${'$'}(lsb_release -c | grep -o '\w*${'$'}').sources" + sudo apt update + sudo apt install --yes --no-install-recommends winehq-stable + winecfg /v win10 + wget https://files.jrsoftware.org/is/6/innosetup-6.3.3.exe + Xvfb :0 -screen 0 1024x768x16 & + export DISPLAY=:0.0 + wine innosetup-6.3.3.exe /SP- /VERYSILENT /SUPPRESSMSGBOXES /NORESTART + """.trimIndent(), + condition = "${runner.os} == 'Linux'", + shell = Bash + ) + + run( + name = "Install InnoSetup on macOS", + command = """ + brew install wine-stable + wget https://files.jrsoftware.org/is/6/innosetup-6.3.3.exe + wine innosetup-6.3.3.exe /SP- /VERYSILENT /SUPPRESSMSGBOXES /NORESTART + """.trimIndent(), + condition = "${runner.os} == 'macOS'", + shell = Bash + ) + + run( + name = "Configure Git", + command = "git config --global core.autocrlf input" + ) + + uses( + name = "Checkout", + action = Checkout() + ) + + uses( + name = "Setup Java 11", + action = SetupJava( + javaVersion = "11", + distribution = Temurin + ) + ) + + uses( + name = "Configure Build Properties for Windows", + action = WriteFileAction( + path = "build.properties", + contents = """ + innosetup.compiler.executable = C:/Program Files (x86)/Inno Setup 6/ISCC.exe + """.trimIndent() + ), + condition = "${runner.os} == 'Windows'" + ) + + uses( + name = "Configure Build Properties for Linux", + action = WriteFileAction( + path = "build.properties", + contents = """ + wine.executable = wine + winepath.executable = winepath + innosetup.compiler.executable = /Users/runner/.wine/drive_c/Program Files (x86)/Inno Setup 6/ISCC.exe + innosetup.via.wine = true + """.trimIndent() + ), + condition = "${runner.os} == 'macOS'" + ) + + val build = run( + name = "Build", + command = "ant -keep-going dist" + ) + + val allPossibleArtifacts = listOf( + Artifact( + name = "Manual in A4 Paper size", + path = "dist/jedit*manual-a4.pdf" + ), + Artifact( + name = "Manual in Letter Paper size", + path = "dist/jedit*manual-letter.pdf" + ), + Artifact( + name = "Source Package", + path = "dist/jedit*source.tar.bz2" + ), + Artifact( + name = "Java based Installer", + path = "dist/jedit*install.jar" + ), + Artifact( + name = "Slackware Installer", + path = "dist/jedit-*-noarch-1sao.tgz" + ), + Artifact( + name = "Debian Installer", + path = "dist/jedit_*_all.deb" + ), + Artifact( + name = "Windows Installer", + path = "dist/jedit*install.exe" + ), + Artifact( + name = "macOS Installer", + path = "dist/jedit*install.dmg", + condition = "${runner.os} == 'macOS'" + ), + Artifact( + name = "macOS Intermediate Result", + path = "dist/jedit*-dist-mac-finish.tar.bz2", + condition = "${runner.os} != 'macOS'" + ), + Artifact( + name = "Debian Repository Packages File", + path = "dist/Packages", + ifNoFilesFound = Ignore + ), + Artifact( + name = "Debian Repository Packages File (gz)", + path = "dist/Packages.gz", + ifNoFilesFound = Ignore + ), + Artifact( + name = "Debian Repository Packages File (bz2)", + path = "dist/Packages.bz2", + ifNoFilesFound = Ignore + ), + Artifact( + name = "Debian Repository Release File", + path = "dist/Release", + ifNoFilesFound = Ignore + ), + Artifact( + name = "Debian Repository Release File Signature", + path = "dist/Release.gpg", + ifNoFilesFound = Ignore + ) + ) + + val buildSuccessful = """ + (${always()}) + && (${build.outcome eq Success}) + """.trimIndent() + + // Upload all result files from macOS build that can build all artifacts as one archive + uses( + name = "Upload All Result Files", + action = UploadArtifact( + name = "All Artifacts", + path = allPossibleArtifacts.map { it.path }, + ifNoFilesFound = Error, + compressionLevel = NoCompression + ), + condition = "($buildSuccessful) && (${runner.os} == 'macOS')" + ) + + // upload each artifact as single archive for individual download + // and also to verify it was built, hence not restricted to OS + allPossibleArtifacts.forEach { (name, path, ifNoFilesFound, condition) -> + uses( + name = "Upload $name", + action = UploadArtifact( + name = name, + path = listOf(path), + ifNoFilesFound = ifNoFilesFound, + compressionLevel = NoCompression, + overwrite = true + ), + condition = condition?.let { "($buildSuccessful) && ($it)" } ?: buildSuccessful + ) + } + + val uploadAllUnexpectedResultFiles = uses( + name = "Upload All Unexpected Result Files", + action = UploadArtifact( + name = "Unexpected Artifacts (${expr { runner.os }})", + path = listOf("dist") + allPossibleArtifacts.map { "!${it.path}" }, + ifNoFilesFound = Ignore, + compressionLevel = NoCompression + ), + condition = buildSuccessful + ) + + run( + name = "Verify No Unexpected Result Files", + command = "[ '${expr { uploadAllUnexpectedResultFiles.outputs.artifactId }}' == '' ]", + condition = """ + (${always()}) + && (${uploadAllUnexpectedResultFiles.outcome eq Success}) + """.trimIndent(), + shell = Bash + ) + } +} + +data class Artifact( + val name: String, + val path: String, + val ifNoFilesFound: BehaviorIfNoFilesFound = Error, + val condition: String? = null +) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 000000000..5148506f8 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,302 @@ +# This file was generated using Kotlin DSL (.github/workflows/build.main.kts). +# If you want to modify the workflow, please change the Kotlin file and regenerate this YAML file. +# Generated with https://github.com/typesafegithub/github-workflows-kt + +name: 'Build' +on: + push: {} + pull_request: {} +jobs: + build: + name: 'Build on ${{ matrix.os }}' + runs-on: '${{ matrix.os }}' + strategy: + fail-fast: false + matrix: + os: + - 'ubuntu-latest' + - 'windows-latest' + - 'macos-latest' + steps: + - id: 'step-0' + name: 'Install InnoSetup on Linux' + shell: 'bash' + run: |- + sudo dpkg --add-architecture i386 + sudo wget -nc -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key + sudo wget -NP /etc/apt/sources.list.d/ "https://dl.winehq.org/wine-builds/ubuntu/dists/$(lsb_release -c | grep -o '\w*$')/winehq-$(lsb_release -c | grep -o '\w*$').sources" + sudo apt update + sudo apt install --yes --no-install-recommends winehq-stable + winecfg /v win10 + wget https://files.jrsoftware.org/is/6/innosetup-6.3.3.exe + Xvfb :0 -screen 0 1024x768x16 & + export DISPLAY=:0.0 + wine innosetup-6.3.3.exe /SP- /VERYSILENT /SUPPRESSMSGBOXES /NORESTART + if: 'runner.os == ''Linux''' + - id: 'step-1' + name: 'Install InnoSetup on macOS' + shell: 'bash' + run: |- + brew install wine-stable + wget https://files.jrsoftware.org/is/6/innosetup-6.3.3.exe + wine innosetup-6.3.3.exe /SP- /VERYSILENT /SUPPRESSMSGBOXES /NORESTART + if: 'runner.os == ''macOS''' + - id: 'step-2' + name: 'Configure Git' + run: 'git config --global core.autocrlf input' + - id: 'step-3' + name: 'Checkout' + uses: 'actions/checkout@v4' + - id: 'step-4' + name: 'Setup Java 11' + uses: 'actions/setup-java@v4' + with: + java-version: '11' + distribution: 'temurin' + - id: 'step-5' + name: 'Configure Build Properties for Windows' + uses: 'DamianReeves/write-file-action@v1.3' + with: + path: 'build.properties' + contents: 'innosetup.compiler.executable = C:/Program Files (x86)/Inno Setup 6/ISCC.exe' + if: 'runner.os == ''Windows''' + - id: 'step-6' + name: 'Configure Build Properties for Linux' + uses: 'DamianReeves/write-file-action@v1.3' + with: + path: 'build.properties' + contents: |- + wine.executable = wine + winepath.executable = winepath + innosetup.compiler.executable = /Users/runner/.wine/drive_c/Program Files (x86)/Inno Setup 6/ISCC.exe + innosetup.via.wine = true + if: 'runner.os == ''macOS''' + - id: 'step-7' + name: 'Build' + run: 'ant -keep-going dist' + - id: 'step-8' + name: 'Upload All Result Files' + uses: 'actions/upload-artifact@v4' + with: + name: 'All Artifacts' + path: |- + dist/jedit*manual-a4.pdf + dist/jedit*manual-letter.pdf + dist/jedit*source.tar.bz2 + dist/jedit*install.jar + dist/jedit-*-noarch-1sao.tgz + dist/jedit_*_all.deb + dist/jedit*install.exe + dist/jedit*install.dmg + dist/jedit*-dist-mac-finish.tar.bz2 + dist/Packages + dist/Packages.gz + dist/Packages.bz2 + dist/Release + dist/Release.gpg + if-no-files-found: 'error' + compression-level: '0' + if: |- + ((always()) + && (steps.step-7.outcome == 'success')) && (runner.os == 'macOS') + - id: 'step-9' + name: 'Upload Manual in A4 Paper size' + uses: 'actions/upload-artifact@v4' + with: + name: 'Manual in A4 Paper size' + path: 'dist/jedit*manual-a4.pdf' + if-no-files-found: 'error' + compression-level: '0' + overwrite: 'true' + if: |- + (always()) + && (steps.step-7.outcome == 'success') + - id: 'step-10' + name: 'Upload Manual in Letter Paper size' + uses: 'actions/upload-artifact@v4' + with: + name: 'Manual in Letter Paper size' + path: 'dist/jedit*manual-letter.pdf' + if-no-files-found: 'error' + compression-level: '0' + overwrite: 'true' + if: |- + (always()) + && (steps.step-7.outcome == 'success') + - id: 'step-11' + name: 'Upload Source Package' + uses: 'actions/upload-artifact@v4' + with: + name: 'Source Package' + path: 'dist/jedit*source.tar.bz2' + if-no-files-found: 'error' + compression-level: '0' + overwrite: 'true' + if: |- + (always()) + && (steps.step-7.outcome == 'success') + - id: 'step-12' + name: 'Upload Java based Installer' + uses: 'actions/upload-artifact@v4' + with: + name: 'Java based Installer' + path: 'dist/jedit*install.jar' + if-no-files-found: 'error' + compression-level: '0' + overwrite: 'true' + if: |- + (always()) + && (steps.step-7.outcome == 'success') + - id: 'step-13' + name: 'Upload Slackware Installer' + uses: 'actions/upload-artifact@v4' + with: + name: 'Slackware Installer' + path: 'dist/jedit-*-noarch-1sao.tgz' + if-no-files-found: 'error' + compression-level: '0' + overwrite: 'true' + if: |- + (always()) + && (steps.step-7.outcome == 'success') + - id: 'step-14' + name: 'Upload Debian Installer' + uses: 'actions/upload-artifact@v4' + with: + name: 'Debian Installer' + path: 'dist/jedit_*_all.deb' + if-no-files-found: 'error' + compression-level: '0' + overwrite: 'true' + if: |- + (always()) + && (steps.step-7.outcome == 'success') + - id: 'step-15' + name: 'Upload Windows Installer' + uses: 'actions/upload-artifact@v4' + with: + name: 'Windows Installer' + path: 'dist/jedit*install.exe' + if-no-files-found: 'error' + compression-level: '0' + overwrite: 'true' + if: |- + (always()) + && (steps.step-7.outcome == 'success') + - id: 'step-16' + name: 'Upload macOS Installer' + uses: 'actions/upload-artifact@v4' + with: + name: 'macOS Installer' + path: 'dist/jedit*install.dmg' + if-no-files-found: 'error' + compression-level: '0' + overwrite: 'true' + if: |- + ((always()) + && (steps.step-7.outcome == 'success')) && (runner.os == 'macOS') + - id: 'step-17' + name: 'Upload macOS Intermediate Result' + uses: 'actions/upload-artifact@v4' + with: + name: 'macOS Intermediate Result' + path: 'dist/jedit*-dist-mac-finish.tar.bz2' + if-no-files-found: 'error' + compression-level: '0' + overwrite: 'true' + if: |- + ((always()) + && (steps.step-7.outcome == 'success')) && (runner.os != 'macOS') + - id: 'step-18' + name: 'Upload Debian Repository Packages File' + uses: 'actions/upload-artifact@v4' + with: + name: 'Debian Repository Packages File' + path: 'dist/Packages' + if-no-files-found: 'ignore' + compression-level: '0' + overwrite: 'true' + if: |- + (always()) + && (steps.step-7.outcome == 'success') + - id: 'step-19' + name: 'Upload Debian Repository Packages File (gz)' + uses: 'actions/upload-artifact@v4' + with: + name: 'Debian Repository Packages File (gz)' + path: 'dist/Packages.gz' + if-no-files-found: 'ignore' + compression-level: '0' + overwrite: 'true' + if: |- + (always()) + && (steps.step-7.outcome == 'success') + - id: 'step-20' + name: 'Upload Debian Repository Packages File (bz2)' + uses: 'actions/upload-artifact@v4' + with: + name: 'Debian Repository Packages File (bz2)' + path: 'dist/Packages.bz2' + if-no-files-found: 'ignore' + compression-level: '0' + overwrite: 'true' + if: |- + (always()) + && (steps.step-7.outcome == 'success') + - id: 'step-21' + name: 'Upload Debian Repository Release File' + uses: 'actions/upload-artifact@v4' + with: + name: 'Debian Repository Release File' + path: 'dist/Release' + if-no-files-found: 'ignore' + compression-level: '0' + overwrite: 'true' + if: |- + (always()) + && (steps.step-7.outcome == 'success') + - id: 'step-22' + name: 'Upload Debian Repository Release File Signature' + uses: 'actions/upload-artifact@v4' + with: + name: 'Debian Repository Release File Signature' + path: 'dist/Release.gpg' + if-no-files-found: 'ignore' + compression-level: '0' + overwrite: 'true' + if: |- + (always()) + && (steps.step-7.outcome == 'success') + - id: 'step-23' + name: 'Upload All Unexpected Result Files' + uses: 'actions/upload-artifact@v4' + with: + name: 'Unexpected Artifacts (${{ runner.os }})' + path: |- + dist + !dist/jedit*manual-a4.pdf + !dist/jedit*manual-letter.pdf + !dist/jedit*source.tar.bz2 + !dist/jedit*install.jar + !dist/jedit-*-noarch-1sao.tgz + !dist/jedit_*_all.deb + !dist/jedit*install.exe + !dist/jedit*install.dmg + !dist/jedit*-dist-mac-finish.tar.bz2 + !dist/Packages + !dist/Packages.gz + !dist/Packages.bz2 + !dist/Release + !dist/Release.gpg + if-no-files-found: 'ignore' + compression-level: '0' + if: |- + (always()) + && (steps.step-7.outcome == 'success') + - id: 'step-24' + name: 'Verify No Unexpected Result Files' + shell: 'bash' + run: '[ ''${{ steps.step-23.outputs.artifact-id }}'' == '''' ]' + if: |- + (always()) + && (steps.step-23.outcome == 'success') diff --git a/.github/workflows/mirror-canonical-repository.main.kts b/.github/workflows/mirror-canonical-repository.main.kts index 1eb84e6fc..f7920d4a3 100755 --- a/.github/workflows/mirror-canonical-repository.main.kts +++ b/.github/workflows/mirror-canonical-repository.main.kts @@ -35,6 +35,7 @@ workflow( name = "Clone Canonical Repository", command = "git clone --bare git://git.code.sf.net/p/jedit/jEdit.bak ." ) + val MIRROR_TOKEN by secrets run( name = "Push To Mirror Repository", diff --git a/build.xml b/build.xml index d4ea77222..3294b969e 100644 --- a/build.xml +++ b/build.xml @@ -167,6 +167,7 @@ + (beta != 99 ? "pre" + beta : "." + micro); project.setUserProperty("jedit.version", version); project.setUserProperty("jedit.build.number", build); + project.setUserProperty("jedit.build.number.no.leading.zeros", major + "." + minor + "." + beta + "." + micro); project.setUserProperty("jedit.version.final", Boolean.toString(beta == 99)); + @target.java.version@ - @jedit.build.number@ + @jedit.build.number.no.leading.zeros@ @jedit.version@ jEdit - Programmer's Text Editor Copyright © 1998-@current.year@ Contributors - @jedit.build.number@ + @jedit.build.number.no.leading.zeros@ @jedit.version@ jEdit Contributors