From decd36b67e9db8073c9d079529d970f7b173be99 Mon Sep 17 00:00:00 2001 From: see Date: Wed, 1 Feb 2023 14:46:18 +0000 Subject: [PATCH 001/119] Initial commit --- .gitignore | 23 +++++++++++++++++++++++ LICENSE | 9 +++++++++ README.md | 2 ++ 3 files changed, 34 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..adf8f72 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# ---> Go +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2071b23 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e8c70ca --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# terraformDummyRepo + From 0bd416a63f5327f0bb10990af05e293496e132cb Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 8 Feb 2023 10:33:39 +0100 Subject: [PATCH 002/119] WIP prepare build.py --- build.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 build.py diff --git a/build.py b/build.py new file mode 100644 index 0000000..9bc2066 --- /dev/null +++ b/build.py @@ -0,0 +1,21 @@ +from pybuilder.core import task, init +from ddadevops import * + +@init +def initialize(project): + pass + +@task +def release(project): + build = get_devops_build(project) + build.init() + build.prepareRelease() # mit config file + # build + build.publish + build.releaseInGit + + +@task +def build(project): + build = get_devops_build(project) + # ToDo: Implement for project From 68397ed0e4ca94ed1509f375362477ee863c185e Mon Sep 17 00:00:00 2001 From: bom Date: Wed, 8 Feb 2023 10:57:40 +0100 Subject: [PATCH 003/119] Add skeleton for testing Version --- .gitignore | 1 + build.py | 25 ++++++++++++++++++------- devops_test.py | 34 ++++++++++++++++++++++++++++++++++ package.json | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 devops_test.py create mode 100644 package.json diff --git a/.gitignore b/.gitignore index adf8f72..a9c5fbc 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ # Go workspace file go.work +__pycache__ \ No newline at end of file diff --git a/build.py b/build.py index 9bc2066..af9430f 100644 --- a/build.py +++ b/build.py @@ -1,21 +1,32 @@ from pybuilder.core import task, init from ddadevops import * +from devops_test import * + + +def main(): + init_project() @init def initialize(project): - pass + project.build_depends_on('ddadevops>=3.1.2') + # build = get_devops_build() + # build.init() + init_project() @task def release(project): - build = get_devops_build(project) - build.init() - build.prepareRelease() # mit config file - # build - build.publish - build.releaseInGit + # build = get_devops_build(project) + # build.prepare_release() # mit config file + prepare_release() + # build() + # build.publish() + # build.release_in_git() + # release_in_git() @task def build(project): build = get_devops_build(project) # ToDo: Implement for project + +main() \ No newline at end of file diff --git a/devops_test.py b/devops_test.py new file mode 100644 index 0000000..8d87964 --- /dev/null +++ b/devops_test.py @@ -0,0 +1,34 @@ +import json + +def init_project(): + # validate_values() + version = Version('package.json') + version.parse() + +def prepare_release(): + pass + +def release_in_git(): + pass + +class Version(): + + def __init__(self, config_file_path): + self.version = "0.0.0" + self.config_file_path = config_file_path + print('init project') + + def parse(self): + if self.config_file_path.split('.').last() == 'json': + self.__parse_json() + + def __parse_json(self): + with open(self.config_file_path, 'r') as json_file: + json_data = json.load(json_file) + print(json_data['version']) + + def increment(self, level): + pass + + def get(): + pass \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..03f0a3e --- /dev/null +++ b/package.json @@ -0,0 +1,33 @@ +{ + "name": "dummy", + "description": "Generate c4k yaml for a jitsi deployment.", + "author": "meissa GmbH", + "version": "1.3.2", + "homepage": "https://gitlab.com/domaindrivenarchitecture/c4k-jitsi#readme", + "repository": "https://www.npmjs.com/package/c4k-jitsi", + "license": "APACHE2", + "main": "c4k-jitsi.js", + "bin": { + "c4k-jitsi": "./c4k-jitsi.js" + }, + "keywords": [ + "cljs", + "jitsi", + "k8s", + "c4k", + "deployment", + "yaml", + "convention4kubernetes" + ], + "bugs": { + "url": "https://gitlab.com/domaindrivenarchitecture/c4k-jitsi/issues" + }, + "dependencies": { + "js-base64": "^3.6.1", + "js-yaml": "^4.0.0" + }, + "devDependencies": { + "shadow-cljs": "^2.11.18", + "source-map-support": "^0.5.19" + } + } \ No newline at end of file From fbcd9ee3981534308ca958074073ce3a46239ffa Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 8 Feb 2023 11:22:55 +0100 Subject: [PATCH 004/119] WIP Implement ReleaseLevel --- build.py | 3 +-- devops_test.py | 24 ++++++++++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/build.py b/build.py index af9430f..3efe26d 100644 --- a/build.py +++ b/build.py @@ -21,8 +21,7 @@ def release(project): # build() # build.publish() # build.release_in_git() - # release_in_git() - + # release_in_git() @task def build(project): diff --git a/devops_test.py b/devops_test.py index 8d87964..59b1e71 100644 --- a/devops_test.py +++ b/devops_test.py @@ -1,4 +1,5 @@ import json +from enum import Enum def init_project(): # validate_values() @@ -13,22 +14,37 @@ def release_in_git(): class Version(): + class ReleaseLevel(Enum): + SNAPSHOT = 0 + PATCH = 1 + MINOR = 2 + MAJOR = 3 + def __init__(self, config_file_path): self.version = "0.0.0" self.config_file_path = config_file_path print('init project') def parse(self): - if self.config_file_path.split('.').last() == 'json': + if self.config_file_path.split('.')[-1] == 'json': self.__parse_json() def __parse_json(self): with open(self.config_file_path, 'r') as json_file: json_data = json.load(json_file) - print(json_data['version']) + self.version = json_data['version'] - def increment(self, level): - pass + def increment(self, level: ReleaseLevel): + match level: + case ReleaseLevel.SNAPSHOT: + if "-SNAPSHOT" not in self.version + self.version = self.version + "-SNAPSHOT" + case ReleaseLevel.PATCH: + pass + case ReleaseLevel.MINOR: + pass + case ReleaseLevel.MAJOR: + pass def get(): pass \ No newline at end of file From 691b17dc5664cfc8325be71827103e343fd0ac4b Mon Sep 17 00:00:00 2001 From: bom Date: Wed, 8 Feb 2023 11:37:41 +0100 Subject: [PATCH 005/119] Move some functions around for testing --- devops_test.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/devops_test.py b/devops_test.py index 59b1e71..1695307 100644 --- a/devops_test.py +++ b/devops_test.py @@ -5,6 +5,8 @@ def init_project(): # validate_values() version = Version('package.json') version.parse() + version.increment(ReleaseLevel.SNAPSHOT) + print(version.get()) def prepare_release(): pass @@ -12,13 +14,13 @@ def prepare_release(): def release_in_git(): pass -class Version(): +class ReleaseLevel(Enum): + SNAPSHOT = 0 + PATCH = 1 + MINOR = 2 + MAJOR = 3 - class ReleaseLevel(Enum): - SNAPSHOT = 0 - PATCH = 1 - MINOR = 2 - MAJOR = 3 +class Version(): def __init__(self, config_file_path): self.version = "0.0.0" @@ -46,5 +48,5 @@ class Version(): case ReleaseLevel.MAJOR: pass - def get(): - pass \ No newline at end of file + def get(self) -> str: + return self.version \ No newline at end of file From 09d08d6e431ccddac202a2c860238672b1a36b3f Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 8 Feb 2023 11:42:46 +0100 Subject: [PATCH 006/119] WIP --- devops_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devops_test.py b/devops_test.py index 1695307..9ac7180 100644 --- a/devops_test.py +++ b/devops_test.py @@ -39,7 +39,7 @@ class Version(): def increment(self, level: ReleaseLevel): match level: case ReleaseLevel.SNAPSHOT: - if "-SNAPSHOT" not in self.version + if "-SNAPSHOT" not in self.version: self.version = self.version + "-SNAPSHOT" case ReleaseLevel.PATCH: pass From c1928b8c440671ec02c02e459b7529c879113eb8 Mon Sep 17 00:00:00 2001 From: bom Date: Wed, 8 Feb 2023 11:52:15 +0100 Subject: [PATCH 007/119] WIP implement patch --- devops_test.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/devops_test.py b/devops_test.py index 9ac7180..a0aa37f 100644 --- a/devops_test.py +++ b/devops_test.py @@ -5,7 +5,7 @@ def init_project(): # validate_values() version = Version('package.json') version.parse() - version.increment(ReleaseLevel.SNAPSHOT) + version.increment(ReleaseLevel.PATCH) print(version.get()) def prepare_release(): @@ -15,17 +15,17 @@ def release_in_git(): pass class ReleaseLevel(Enum): - SNAPSHOT = 0 - PATCH = 1 - MINOR = 2 - MAJOR = 3 + MAJOR = 0 + MINOR = 1 + PATCH = 2 + SNAPSHOT = 3 + class Version(): def __init__(self, config_file_path): self.version = "0.0.0" self.config_file_path = config_file_path - print('init project') def parse(self): if self.config_file_path.split('.')[-1] == 'json': @@ -42,7 +42,10 @@ class Version(): if "-SNAPSHOT" not in self.version: self.version = self.version + "-SNAPSHOT" case ReleaseLevel.PATCH: - pass + split_version = self.version.split('.') + patch_version = int(split_version[ReleaseLevel.PATCH.value]) + self.version = split_version[:ReleaseLevel.PATCH.value] + str(patch_version + 1) + print(self.version) case ReleaseLevel.MINOR: pass case ReleaseLevel.MAJOR: From 1fe186cc4a243ac32831c97188cf91e55c33673c Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 8 Feb 2023 12:14:53 +0100 Subject: [PATCH 008/119] WIP conversion issues --- devops_test.py | 39 ++++++++++++++++++++++++++++++--------- package.json | 30 +++++++++++++++--------------- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/devops_test.py b/devops_test.py index a0aa37f..2958295 100644 --- a/devops_test.py +++ b/devops_test.py @@ -5,7 +5,7 @@ def init_project(): # validate_values() version = Version('package.json') version.parse() - version.increment(ReleaseLevel.PATCH) + version.increment(ReleaseLevel.MAJOR) print(version.get()) def prepare_release(): @@ -37,19 +37,40 @@ class Version(): self.version = json_data['version'] def increment(self, level: ReleaseLevel): - match level: - case ReleaseLevel.SNAPSHOT: - if "-SNAPSHOT" not in self.version: + + if level is ReleaseLevel.SNAPSHOT: + if "-SNAPSHOT" not in self.version: self.version = self.version + "-SNAPSHOT" - case ReleaseLevel.PATCH: + else: + # convert array to int + # e.g. patch index +1 + # convert back to str + # join + split_versiont = split_version. + + + match level: + + + case ReleaseLevel.PATCH: + self.version = self.version.replace("-SNAPSHOT", "") split_version = self.version.split('.') patch_version = int(split_version[ReleaseLevel.PATCH.value]) - self.version = split_version[:ReleaseLevel.PATCH.value] + str(patch_version + 1) - print(self.version) + self.version = ".".join(split_version[:ReleaseLevel.PATCH.value]) + "." + str(patch_version + 1) + case ReleaseLevel.MINOR: - pass + self.version = self.version.replace("-SNAPSHOT", "") + split_version = self.version.split('.') + minor_version = int(split_version[ReleaseLevel.MINOR.value]) + self.version = ".".join(split_version[:ReleaseLevel.MINOR.value]) + "." + str(minor_version + 1) + ".0" + case ReleaseLevel.MAJOR: - pass + self.version = self.version.replace("-SNAPSHOT", "") + split_version = self.version.split('.') + major_version = int(split_version[ReleaseLevel.MAJOR.value]) + self.version = "".join(split_version[:ReleaseLevel.MAJOR.value]) + str(major_version + 1) + ".0" + ".0" + + def get(self) -> str: return self.version \ No newline at end of file diff --git a/package.json b/package.json index 03f0a3e..c582f21 100644 --- a/package.json +++ b/package.json @@ -2,32 +2,32 @@ "name": "dummy", "description": "Generate c4k yaml for a jitsi deployment.", "author": "meissa GmbH", - "version": "1.3.2", + "version": "1.3.2-SNAPSHOT", "homepage": "https://gitlab.com/domaindrivenarchitecture/c4k-jitsi#readme", "repository": "https://www.npmjs.com/package/c4k-jitsi", "license": "APACHE2", "main": "c4k-jitsi.js", "bin": { - "c4k-jitsi": "./c4k-jitsi.js" + "c4k-jitsi": "./c4k-jitsi.js" }, "keywords": [ - "cljs", - "jitsi", - "k8s", - "c4k", - "deployment", - "yaml", - "convention4kubernetes" + "cljs", + "jitsi", + "k8s", + "c4k", + "deployment", + "yaml", + "convention4kubernetes" ], "bugs": { - "url": "https://gitlab.com/domaindrivenarchitecture/c4k-jitsi/issues" + "url": "https://gitlab.com/domaindrivenarchitecture/c4k-jitsi/issues" }, "dependencies": { - "js-base64": "^3.6.1", - "js-yaml": "^4.0.0" + "js-base64": "^3.6.1", + "js-yaml": "^4.0.0" }, "devDependencies": { - "shadow-cljs": "^2.11.18", - "source-map-support": "^0.5.19" + "shadow-cljs": "^2.11.18", + "source-map-support": "^0.5.19" } - } \ No newline at end of file +} \ No newline at end of file From eb6f5eb5684d8829e39af19ba882f567b88a19b4 Mon Sep 17 00:00:00 2001 From: bom Date: Wed, 8 Feb 2023 12:37:00 +0100 Subject: [PATCH 009/119] Add write_json logic --- devops_test.py | 75 +++++++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/devops_test.py b/devops_test.py index 2958295..4224191 100644 --- a/devops_test.py +++ b/devops_test.py @@ -24,53 +24,52 @@ class ReleaseLevel(Enum): class Version(): def __init__(self, config_file_path): - self.version = "0.0.0" + self.version = [] + self.is_snapshot = False self.config_file_path = config_file_path + self.config_file_type = config_file_path.split('.')[-1] def parse(self): - if self.config_file_path.split('.')[-1] == 'json': - self.__parse_json() + match self.config_file_type: + case 'json': + self.__parse_json() def __parse_json(self): with open(self.config_file_path, 'r') as json_file: - json_data = json.load(json_file) - self.version = json_data['version'] + json_version = json.load(json_file)['version'] + if '-SNAPSHOT' in json_version: + self.is_snapshot = True + json_version = json_version.replace('-SNAPSHOT', '') + self.version = [int(x) for x in json_version.split('.')] def increment(self, level: ReleaseLevel): - - if level is ReleaseLevel.SNAPSHOT: - if "-SNAPSHOT" not in self.version: - self.version = self.version + "-SNAPSHOT" - else: - # convert array to int - # e.g. patch index +1 - # convert back to str - # join - split_versiont = split_version. - - - match level: - - - case ReleaseLevel.PATCH: - self.version = self.version.replace("-SNAPSHOT", "") - split_version = self.version.split('.') - patch_version = int(split_version[ReleaseLevel.PATCH.value]) - self.version = ".".join(split_version[:ReleaseLevel.PATCH.value]) + "." + str(patch_version + 1) - + self.is_snapshot = False + match level: + case ReleaseLevel.SNAPSHOT: + self.is_snapshot = True + case ReleaseLevel.PATCH: + self.version[ReleaseLevel.PATCH.value] += 1 case ReleaseLevel.MINOR: - self.version = self.version.replace("-SNAPSHOT", "") - split_version = self.version.split('.') - minor_version = int(split_version[ReleaseLevel.MINOR.value]) - self.version = ".".join(split_version[:ReleaseLevel.MINOR.value]) + "." + str(minor_version + 1) + ".0" - + self.version[ReleaseLevel.PATCH.value] = 0 + self.version[ReleaseLevel.MINOR.value] += 1 case ReleaseLevel.MAJOR: - self.version = self.version.replace("-SNAPSHOT", "") - split_version = self.version.split('.') - major_version = int(split_version[ReleaseLevel.MAJOR.value]) - self.version = "".join(split_version[:ReleaseLevel.MAJOR.value]) + str(major_version + 1) + ".0" + ".0" - - + self.version[ReleaseLevel.PATCH.value] = 0 + self.version[ReleaseLevel.MINOR.value] = 0 + self.version[ReleaseLevel.MAJOR.value] += 1 + + def write(self): + match self.config_file_type: + case 'json': + self.__write_json() + + def __write_json(self): + with open(self.config_file_path, 'wr') as json_file: + json_data = json.load(json_file) + json_data['version'] = self.get() + json.dump(json_data, json_file) def get(self) -> str: - return self.version \ No newline at end of file + version_string = ".".join([str(x) for x in self.version]) + if self.is_snapshot: + version_string += "-SNAPSHOT" + return version_string \ No newline at end of file From 8a3e77aadf113bc3077adb495ecd8d782430b163 Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 8 Feb 2023 12:57:15 +0100 Subject: [PATCH 010/119] Implement json Versioning POC --- devops_test.py | 8 +++++--- package.json | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/devops_test.py b/devops_test.py index 4224191..190ea5d 100644 --- a/devops_test.py +++ b/devops_test.py @@ -6,6 +6,7 @@ def init_project(): version = Version('package.json') version.parse() version.increment(ReleaseLevel.MAJOR) + version.write() print(version.get()) def prepare_release(): @@ -19,7 +20,6 @@ class ReleaseLevel(Enum): MINOR = 1 PATCH = 2 SNAPSHOT = 3 - class Version(): @@ -63,10 +63,12 @@ class Version(): self.__write_json() def __write_json(self): - with open(self.config_file_path, 'wr') as json_file: + with open(self.config_file_path, 'r+') as json_file: json_data = json.load(json_file) json_data['version'] = self.get() - json.dump(json_data, json_file) + json_file.seek(0) + json.dump(json_data, json_file, indent=4) + json_file.truncate() def get(self) -> str: version_string = ".".join([str(x) for x in self.version]) diff --git a/package.json b/package.json index c582f21..bbf2883 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "dummy", "description": "Generate c4k yaml for a jitsi deployment.", "author": "meissa GmbH", - "version": "1.3.2-SNAPSHOT", + "version": "3.0.0", "homepage": "https://gitlab.com/domaindrivenarchitecture/c4k-jitsi#readme", "repository": "https://www.npmjs.com/package/c4k-jitsi", "license": "APACHE2", From 1995a7b200088c3fb238d9fcb56500c1931e77cf Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 9 Feb 2023 11:43:10 +0100 Subject: [PATCH 011/119] Add support for gradle files --- build.gradle | 6352 ++++++++++++++++++++++++++++++++++++++++++++++++ devops_test.py | 39 +- 2 files changed, 6388 insertions(+), 3 deletions(-) create mode 100644 build.gradle diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..6a8577e --- /dev/null +++ b/build.gradle @@ -0,0 +1,6352 @@ +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1" + +repositories { + mavenCentral() +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" +repositories { + mavenCentral() +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" +repositories { + mavenCentral() +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" + mavenCentral() +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" +repositories { + mavenCentral() +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" + mavenCentral() +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" + mavenCentral() +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" +repositories { + mavenCentral() +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" + mavenCentral() +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" + mavenCentral() +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" + mavenCentral() +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" +repositories { + mavenCentral() +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" + mavenCentral() +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" + mavenCentral() +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" + mavenCentral() +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" + mavenCentral() +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" +} + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" + + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} +buildscript { + ext.kotlin_version = "1.7.0" + ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID + + repositories { mavenCentral() } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: "org.jetbrains.kotlin.jvm" +apply plugin: "java-library" +apply plugin: "java-test-fixtures" +apply plugin: "maven-publish" +apply plugin: "kotlinx-serialization" + + +group = "org.domaindrivenarchitecture.provs" +version = "0.17.1-SNAPSHOT" + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + + +test { + // set properties for the tests + def propertiesForTests = ["testdockerwithoutsudo"] + for (def prop : propertiesForTests) { + def value = System.getProperty(prop) + if (value != null) { + systemProperty prop, value + } + } + + useJUnitPlatform { + def excludedTags = System.getProperty("excludeTags") + if (System.getProperty("excludeTags") != null) { + excludeTags(excludedTags.split(",")) + } + if (System.getenv("CI_JOB_TOKEN") != null) { + excludeTags("containernonci") + } + } +} + +compileJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" +compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" + +// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc +java { + withSourcesJar() + withJavadocJar() +} + + +dependencies { + + api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") + api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") + + api('com.charleskorn.kaml:kaml:0.43.0') + + api("org.slf4j:slf4j-api:1.7.36") + api('ch.qos.logback:logback-classic:1.2.11') + api('ch.qos.logback:logback-core:1.2.11') + + implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation("com.hierynomus:sshj:0.32.0") + + implementation("aws.sdk.kotlin:s3:0.17.1-beta") + + testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesApi('io.mockk:mockk:1.12.3') + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + + +task uberjarDesktop(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" + } + archiveFileName = "provs-desktop.jar" +} + + +task uberjarServer(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" + } + archiveFileName = "provs-server.jar" +} + + +task uberjarSyspec(type: Jar) { + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } + } { + duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" + } + + manifest { + attributes "Implementation-Title": "Uberjar of provs", + "Implementation-Version": project.version, + "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" + } + archiveFileName = "provs-syspec.jar" +} +def projectRoot = rootProject.projectDir + + +// copy jar to /usr/local/bin and make it executable +// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) +task installlocally { + dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) + doLast { + exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } + exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } + } +} + +task sourceJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + archiveClassifier.set("sources") +} + + +publishing { + publications { + library(MavenPublication) { + from components.java + } + } + repositories { + if (System.getenv("CI_JOB_TOKEN") != null) { + // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html + maven { + url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" + name "GitLab" + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + mavenLocal() + } + } +} diff --git a/devops_test.py b/devops_test.py index 190ea5d..0ba4358 100644 --- a/devops_test.py +++ b/devops_test.py @@ -1,13 +1,13 @@ import json from enum import Enum +import re def init_project(): # validate_values() - version = Version('package.json') + version = Version('build.gradle') version.parse() - version.increment(ReleaseLevel.MAJOR) + version.increment(ReleaseLevel.SNAPSHOT) version.write() - print(version.get()) def prepare_release(): pass @@ -33,6 +33,8 @@ class Version(): match self.config_file_type: case 'json': self.__parse_json() + case 'gradle': + self.__parse_gradle() def __parse_json(self): with open(self.config_file_path, 'r') as json_file: @@ -42,6 +44,25 @@ class Version(): json_version = json_version.replace('-SNAPSHOT', '') self.version = [int(x) for x in json_version.split('.')] + def __parse_gradle(self): + with open(self.config_file_path, 'r') as gradle_file: + contents = gradle_file.read() + version_line = re.search("\nversion = .*\n", contents) + if version_line is None: + raise Exception("Version not found in gradle file") + version_line = version_line.group() + version_string = re.search('[0-9]*\.[0-9]*\.[0-9]*(-SNAPSHOT)?', version_line) + if version_string is None: + raise Exception("Version not found in gradle file") + + version_string = version_string.group() + if '-SNAPSHOT' in version_string: + self.is_snapshot = True + version_string = version_string.replace('-SNAPSHOT', '') + + self.version = [int(x) for x in version_string.split('.')] + + def increment(self, level: ReleaseLevel): self.is_snapshot = False match level: @@ -61,6 +82,10 @@ class Version(): match self.config_file_type: case 'json': self.__write_json() + case 'gradle': + self.__write_gradle() + case _: + raise Exception(f'The file type "{self.config_file_type}" is not implemented') def __write_json(self): with open(self.config_file_path, 'r+') as json_file: @@ -70,6 +95,14 @@ class Version(): json.dump(json_data, json_file, indent=4) json_file.truncate() + def __write_gradle(self): + with open(self.config_file_path, 'r+') as gradle_file: + gradle_contents = gradle_file.read() + version_substitute = re.sub("\nversion = .*\n", f'\nversion = "{self.get()}"\n', gradle_contents) + gradle_file.seek(0) + gradle_file.write(version_substitute) + gradle_file.truncate() + def get(self) -> str: version_string = ".".join([str(x) for x in self.version]) if self.is_snapshot: From 4a34763fb2bc14ef586eb4900b615c18d6304e0a Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 9 Feb 2023 12:02:31 +0100 Subject: [PATCH 012/119] Add basic tests for version increment --- devops_test.py | 23 +++++++++++++++-------- test/test_version.py | 25 +++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 8 deletions(-) create mode 100644 test/test_version.py diff --git a/devops_test.py b/devops_test.py index 0ba4358..8adcb5f 100644 --- a/devops_test.py +++ b/devops_test.py @@ -4,8 +4,7 @@ import re def init_project(): # validate_values() - version = Version('build.gradle') - version.parse() + version = Version.from_file('build.gradle') version.increment(ReleaseLevel.SNAPSHOT) version.write() @@ -23,11 +22,17 @@ class ReleaseLevel(Enum): class Version(): - def __init__(self, config_file_path): - self.version = [] - self.is_snapshot = False - self.config_file_path = config_file_path - self.config_file_type = config_file_path.split('.')[-1] + def __init__(self, version, is_snapshot): + self.version = version + self.is_snapshot = is_snapshot + + @classmethod + def from_file(cls, config_file_path): + ret_cls = cls(None, False) + ret_cls.config_file_path = config_file_path + ret_cls.config_file_type = config_file_path.split('.')[-1] + ret_cls.parse() + return ret_cls def parse(self): match self.config_file_type: @@ -35,6 +40,8 @@ class Version(): self.__parse_json() case 'gradle': self.__parse_gradle() + case _: + raise Exception(f'The file type "{self.config_file_type}" is not implemented') def __parse_json(self): with open(self.config_file_path, 'r') as json_file: @@ -51,7 +58,7 @@ class Version(): if version_line is None: raise Exception("Version not found in gradle file") version_line = version_line.group() - version_string = re.search('[0-9]*\.[0-9]*\.[0-9]*(-SNAPSHOT)?', version_line) + version_string = re.search('[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', version_line) if version_string is None: raise Exception("Version not found in gradle file") diff --git a/test/test_version.py b/test/test_version.py new file mode 100644 index 0000000..d72416b --- /dev/null +++ b/test/test_version.py @@ -0,0 +1,25 @@ +from devops_test import Version, ReleaseLevel + +def test_version(): + version = Version([1, 2, 3], False) + + version.increment(ReleaseLevel.SNAPSHOT) + assert version.get() == "1.2.3-SNAPSHOT" + assert version.version == [1, 2, 3] + assert version.is_snapshot + + version.increment(ReleaseLevel.PATCH) + assert version.get() == "1.2.4" + assert version.version == [1, 2, 4] + assert not version.is_snapshot + + version.increment(ReleaseLevel.SNAPSHOT) + assert version.get() == "1.2.4-SNAPSHOT" + version.increment(ReleaseLevel.SNAPSHOT) + assert version.get() == "1.2.4-SNAPSHOT" + + version.increment(ReleaseLevel.MINOR) + assert version.get() == "1.3.0" + + version.increment(ReleaseLevel.MAJOR) + assert version.get() == "2.0.0" \ No newline at end of file From d50b8bd95b1feef96e375e800564e80dcc1c6e7b Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 9 Feb 2023 12:38:04 +0100 Subject: [PATCH 013/119] Move parse and write code to file handler --- devops_test.py | 71 ++---------------------------------------- file_handlers.py | 81 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 68 deletions(-) create mode 100644 file_handlers.py diff --git a/devops_test.py b/devops_test.py index 8adcb5f..1a2806b 100644 --- a/devops_test.py +++ b/devops_test.py @@ -1,6 +1,5 @@ -import json from enum import Enum -import re +from file_handlers import FileHandler def init_project(): # validate_values() @@ -28,47 +27,8 @@ class Version(): @classmethod def from_file(cls, config_file_path): - ret_cls = cls(None, False) - ret_cls.config_file_path = config_file_path - ret_cls.config_file_type = config_file_path.split('.')[-1] - ret_cls.parse() - return ret_cls - - def parse(self): - match self.config_file_type: - case 'json': - self.__parse_json() - case 'gradle': - self.__parse_gradle() - case _: - raise Exception(f'The file type "{self.config_file_type}" is not implemented') - - def __parse_json(self): - with open(self.config_file_path, 'r') as json_file: - json_version = json.load(json_file)['version'] - if '-SNAPSHOT' in json_version: - self.is_snapshot = True - json_version = json_version.replace('-SNAPSHOT', '') - self.version = [int(x) for x in json_version.split('.')] - - def __parse_gradle(self): - with open(self.config_file_path, 'r') as gradle_file: - contents = gradle_file.read() - version_line = re.search("\nversion = .*\n", contents) - if version_line is None: - raise Exception("Version not found in gradle file") - version_line = version_line.group() - version_string = re.search('[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', version_line) - if version_string is None: - raise Exception("Version not found in gradle file") - - version_string = version_string.group() - if '-SNAPSHOT' in version_string: - self.is_snapshot = True - version_string = version_string.replace('-SNAPSHOT', '') - - self.version = [int(x) for x in version_string.split('.')] - + file_handler = FileHandler.from_file_path(config_file_path) + return cls(file_handler.parse()) def increment(self, level: ReleaseLevel): self.is_snapshot = False @@ -85,31 +45,6 @@ class Version(): self.version[ReleaseLevel.MINOR.value] = 0 self.version[ReleaseLevel.MAJOR.value] += 1 - def write(self): - match self.config_file_type: - case 'json': - self.__write_json() - case 'gradle': - self.__write_gradle() - case _: - raise Exception(f'The file type "{self.config_file_type}" is not implemented') - - def __write_json(self): - with open(self.config_file_path, 'r+') as json_file: - json_data = json.load(json_file) - json_data['version'] = self.get() - json_file.seek(0) - json.dump(json_data, json_file, indent=4) - json_file.truncate() - - def __write_gradle(self): - with open(self.config_file_path, 'r+') as gradle_file: - gradle_contents = gradle_file.read() - version_substitute = re.sub("\nversion = .*\n", f'\nversion = "{self.get()}"\n', gradle_contents) - gradle_file.seek(0) - gradle_file.write(version_substitute) - gradle_file.truncate() - def get(self) -> str: version_string = ".".join([str(x) for x in self.version]) if self.is_snapshot: diff --git a/file_handlers.py b/file_handlers.py new file mode 100644 index 0000000..c7fec26 --- /dev/null +++ b/file_handlers.py @@ -0,0 +1,81 @@ +from abc import ABC, abstractmethod +import json +import re + +class FileHandler(ABC): + + @classmethod + def from_file_path(cls, file_path): + config_file_type = file_path.split('.')[-1] + match config_file_type: + case 'json': + file_handler = JsonFileHandler() + case 'gradle': + file_handler = GradleFileHandler() + case _: + raise Exception(f'The file type "{config_file_type}" is not implemented') + + file_handler.config_file_path = file_path + file_handler.config_file_type = config_file_type + return file_handler + + @abstractmethod + def parse(self) -> tuple[list[int], bool]: + pass + + @abstractmethod + def write(self): + pass + +class JsonFileHandler(FileHandler): + + def parse(self) -> tuple[list[int], bool]: + with open(self.config_file_path, 'r') as json_file: + json_version = json.load(json_file)['version'] + is_snapshot = False + if '-SNAPSHOT' in json_version: + is_snapshot = True + json_version = json_version.replace('-SNAPSHOT', '') + version = [int(x) for x in json_version.split('.')] + return version, is_snapshot + + def write(self): + with open(self.config_file_path, 'r+') as json_file: + json_data = json.load(json_file) + json_data['version'] = self.get() + json_file.seek(0) + json.dump(json_data, json_file, indent=4) + json_file.truncate() + + +class GradleFileHandler(FileHandler): + + def parse(self) -> tuple[list[int], bool]: + with open(self.config_file_path, 'r') as gradle_file: + contents = gradle_file.read() + version_line = re.search("\nversion = .*\n", contents) + if version_line is None: + raise Exception("Version not found in gradle file") + + version_line = version_line.group() + version_string = re.search('[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', version_line) + if version_string is None: + raise Exception("Version not found in gradle file") + + version_string = version_string.group() + if '-SNAPSHOT' in version_string: + self.is_snapshot = True + version_string = version_string.replace('-SNAPSHOT', '') + + self.version = [int(x) for x in version_string.split('.')] + + def write(self): + with open(self.config_file_path, 'r+') as gradle_file: + gradle_contents = gradle_file.read() + version_substitute = re.sub("\nversion = .*\n", f'\nversion = "{self.get()}"\n', gradle_contents) + gradle_file.seek(0) + gradle_file.write(version_substitute) + gradle_file.truncate() + +a = FileHandler.from_file_path('build.gradle') +print(type(a)) \ No newline at end of file From fd84063edc73aede317019dead5549aef2ba6b2e Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 9 Feb 2023 12:52:58 +0100 Subject: [PATCH 014/119] Fix file handling and version creation --- devops_test.py | 15 ++++++++++++--- file_handlers.py | 10 +++++----- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/devops_test.py b/devops_test.py index 1a2806b..ec0b2ab 100644 --- a/devops_test.py +++ b/devops_test.py @@ -4,8 +4,12 @@ from file_handlers import FileHandler def init_project(): # validate_values() version = Version.from_file('build.gradle') - version.increment(ReleaseLevel.SNAPSHOT) - version.write() + v2 = Version.from_file('package.json') + print(type(version.file_handler)) + print(type(v2.file_handler)) + # version.increment(ReleaseLevel.SNAPSHOT) + # version.write() + print(version.get()) def prepare_release(): pass @@ -24,11 +28,16 @@ class Version(): def __init__(self, version, is_snapshot): self.version = version self.is_snapshot = is_snapshot + self.file_handler = None @classmethod def from_file(cls, config_file_path): file_handler = FileHandler.from_file_path(config_file_path) - return cls(file_handler.parse()) + version, is_snapshot = file_handler.parse() + inst = cls(version, is_snapshot) + inst.file_handler = file_handler + + return inst def increment(self, level: ReleaseLevel): self.is_snapshot = False diff --git a/file_handlers.py b/file_handlers.py index c7fec26..3543833 100644 --- a/file_handlers.py +++ b/file_handlers.py @@ -63,11 +63,14 @@ class GradleFileHandler(FileHandler): raise Exception("Version not found in gradle file") version_string = version_string.group() + is_snapshot = False if '-SNAPSHOT' in version_string: - self.is_snapshot = True + is_snapshot = True version_string = version_string.replace('-SNAPSHOT', '') - self.version = [int(x) for x in version_string.split('.')] + version = [int(x) for x in version_string.split('.')] + + return version, is_snapshot def write(self): with open(self.config_file_path, 'r+') as gradle_file: @@ -76,6 +79,3 @@ class GradleFileHandler(FileHandler): gradle_file.seek(0) gradle_file.write(version_substitute) gradle_file.truncate() - -a = FileHandler.from_file_path('build.gradle') -print(type(a)) \ No newline at end of file From ca3db5efe9e01ee66d5b16c76139eb01d965af24 Mon Sep 17 00:00:00 2001 From: erik Date: Thu, 9 Feb 2023 13:20:42 +0100 Subject: [PATCH 015/119] WIP Add tests for gradle and json --- build.gradle | 2 +- devops_test.py | 15 +++++++++------ file_handlers.py | 10 +++++----- test/config.gradle | 2 ++ test/config.json | 3 +++ test/{test_version.py => test_version_class.py} | 13 ++++++++++++- 6 files changed, 32 insertions(+), 13 deletions(-) create mode 100644 test/config.gradle create mode 100644 test/config.json rename test/{test_version.py => test_version_class.py} (76%) diff --git a/build.gradle b/build.gradle index 6a8577e..6fa64a8 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,7 @@ apply plugin: "kotlinx-serialization" group = "org.domaindrivenarchitecture.provs" -version = "0.17.1" +version = "0.17.1-SNAPSHOT" repositories { mavenCentral() diff --git a/devops_test.py b/devops_test.py index ec0b2ab..17b9220 100644 --- a/devops_test.py +++ b/devops_test.py @@ -3,12 +3,9 @@ from file_handlers import FileHandler def init_project(): # validate_values() - version = Version.from_file('build.gradle') - v2 = Version.from_file('package.json') - print(type(version.file_handler)) - print(type(v2.file_handler)) - # version.increment(ReleaseLevel.SNAPSHOT) - # version.write() + version = Version.from_file('build.gradle') + version.increment(ReleaseLevel.SNAPSHOT) + version.to_file() print(version.get()) def prepare_release(): @@ -39,6 +36,12 @@ class Version(): return inst + def to_file(self): + if self.file_handler is None: + raise Exception('Version was not created by from_file method.') + else: + self.file_handler.write(self.get()) + def increment(self, level: ReleaseLevel): self.is_snapshot = False match level: diff --git a/file_handlers.py b/file_handlers.py index 3543833..fe6495f 100644 --- a/file_handlers.py +++ b/file_handlers.py @@ -24,7 +24,7 @@ class FileHandler(ABC): pass @abstractmethod - def write(self): + def write(self, version_string): pass class JsonFileHandler(FileHandler): @@ -39,10 +39,10 @@ class JsonFileHandler(FileHandler): version = [int(x) for x in json_version.split('.')] return version, is_snapshot - def write(self): + def write(self, version_string): with open(self.config_file_path, 'r+') as json_file: json_data = json.load(json_file) - json_data['version'] = self.get() + json_data['version'] = version_string json_file.seek(0) json.dump(json_data, json_file, indent=4) json_file.truncate() @@ -72,10 +72,10 @@ class GradleFileHandler(FileHandler): return version, is_snapshot - def write(self): + def write(self, version_string): with open(self.config_file_path, 'r+') as gradle_file: gradle_contents = gradle_file.read() - version_substitute = re.sub("\nversion = .*\n", f'\nversion = "{self.get()}"\n', gradle_contents) + version_substitute = re.sub("\nversion = .*\n", f'\nversion = "{version_string}"\n', gradle_contents) gradle_file.seek(0) gradle_file.write(version_substitute) gradle_file.truncate() diff --git a/test/config.gradle b/test/config.gradle new file mode 100644 index 0000000..9123ba2 --- /dev/null +++ b/test/config.gradle @@ -0,0 +1,2 @@ + +version = "12.4.678" \ No newline at end of file diff --git a/test/config.json b/test/config.json new file mode 100644 index 0000000..f407ec1 --- /dev/null +++ b/test/config.json @@ -0,0 +1,3 @@ +{ + "version": "123.123.456" +} \ No newline at end of file diff --git a/test/test_version.py b/test/test_version_class.py similarity index 76% rename from test/test_version.py rename to test/test_version_class.py index d72416b..f90446c 100644 --- a/test/test_version.py +++ b/test/test_version_class.py @@ -22,4 +22,15 @@ def test_version(): assert version.get() == "1.3.0" version.increment(ReleaseLevel.MAJOR) - assert version.get() == "2.0.0" \ No newline at end of file + assert version.get() == "2.0.0" + + +def test_gradle(): + # validate_values() + version = Version.from_file('build.gradle') + + + + version.increment(ReleaseLevel.SNAPSHOT) + version.to_file() + print(version.get()) \ No newline at end of file From 86c66dc7f7818ace98014dd098d4081ed5ecef58 Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 9 Feb 2023 13:33:56 +0100 Subject: [PATCH 016/119] Implement tests for file handling --- file_handlers.py | 6 +++--- test/config.gradle | 2 +- test/test_version_class.py | 35 ++++++++++++++++++++++++++++++----- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/file_handlers.py b/file_handlers.py index fe6495f..8248e8a 100644 --- a/file_handlers.py +++ b/file_handlers.py @@ -6,11 +6,11 @@ class FileHandler(ABC): @classmethod def from_file_path(cls, file_path): - config_file_type = file_path.split('.')[-1] + config_file_type = file_path.suffix match config_file_type: - case 'json': + case '.json': file_handler = JsonFileHandler() - case 'gradle': + case '.gradle': file_handler = GradleFileHandler() case _: raise Exception(f'The file type "{config_file_type}" is not implemented') diff --git a/test/config.gradle b/test/config.gradle index 9123ba2..5c6a467 100644 --- a/test/config.gradle +++ b/test/config.gradle @@ -1,2 +1,2 @@ -version = "12.4.678" \ No newline at end of file +version = "12.4.678" diff --git a/test/test_version_class.py b/test/test_version_class.py index f90446c..55b6df2 100644 --- a/test/test_version_class.py +++ b/test/test_version_class.py @@ -1,4 +1,5 @@ from devops_test import Version, ReleaseLevel +from pathlib import Path def test_version(): version = Version([1, 2, 3], False) @@ -25,12 +26,36 @@ def test_version(): assert version.get() == "2.0.0" -def test_gradle(): - # validate_values() - version = Version.from_file('build.gradle') - +def test_gradle(tmp_path): + # init + file_name = 'config.gradle' + with open(f'test/{file_name}', 'r') as gradle_file: + contents = gradle_file.read() + f = tmp_path / file_name + f.write_text(contents) + # test + version = Version.from_file(f) version.increment(ReleaseLevel.SNAPSHOT) version.to_file() - print(version.get()) \ No newline at end of file + + # check + assert 'version = "12.4.678-SNAPSHOT"' in f.read_text() + +def test_json(tmp_path): + # init + file_name = 'config.json' + with open(f'test/{file_name}', 'r') as gradle_file: + contents = gradle_file.read() + + f = tmp_path / file_name + f.write_text(contents) + + # test + version = Version.from_file(f) + version.increment(ReleaseLevel.SNAPSHOT) + version.to_file() + + # check + assert '"version": "123.123.456-SNAPSHOT"' in f.read_text() \ No newline at end of file From a4c8f8934a53bb38687c311c0fc323a4a632c398 Mon Sep 17 00:00:00 2001 From: erik Date: Thu, 9 Feb 2023 13:56:45 +0100 Subject: [PATCH 017/119] Implement clj file handler --- file_handlers.py | 42 ++++++++++++++++++++++++++++++++-- project.clj | 46 ++++++++++++++++++++++++++++++++++++++ test/config.clj | 5 +++++ test/test_version_class.py | 19 +++++++++++++++- 4 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 project.clj create mode 100644 test/config.clj diff --git a/file_handlers.py b/file_handlers.py index 8248e8a..6c0813a 100644 --- a/file_handlers.py +++ b/file_handlers.py @@ -12,6 +12,8 @@ class FileHandler(ABC): file_handler = JsonFileHandler() case '.gradle': file_handler = GradleFileHandler() + case '.clj': + file_handler = CljFileHandler() case _: raise Exception(f'The file type "{config_file_type}" is not implemented') @@ -54,13 +56,14 @@ class GradleFileHandler(FileHandler): with open(self.config_file_path, 'r') as gradle_file: contents = gradle_file.read() version_line = re.search("\nversion = .*\n", contents) + exception = Exception("Version not found in gradle file") if version_line is None: - raise Exception("Version not found in gradle file") + raise exception version_line = version_line.group() version_string = re.search('[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', version_line) if version_string is None: - raise Exception("Version not found in gradle file") + raise exception version_string = version_string.group() is_snapshot = False @@ -79,3 +82,38 @@ class GradleFileHandler(FileHandler): gradle_file.seek(0) gradle_file.write(version_substitute) gradle_file.truncate() + +class CljFileHandler(FileHandler): + + def parse(self) -> tuple[list[int], bool]: + with open(self.config_file_path, 'r') as clj_file: + contents = clj_file.read() + version_line = re.search("^\\(defproject .*\n", contents) + exception = Exception("Version not found in clj file") + if version_line is None: + raise exception + + version_line = version_line.group() + version_string = re.search('[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', version_line) + if version_string is None: + raise exception + + version_string = version_string.group() + is_snapshot = False + if '-SNAPSHOT' in version_string: + is_snapshot = True + version_string = version_string.replace('-SNAPSHOT', '') + + version = [int(x) for x in version_string.split('.')] + + return version, is_snapshot + + def write(self, version_string): + with open(self.config_file_path, 'r+') as clj_file: + clj_first = clj_file.readline() + clj_rest = clj_file.read() + version_substitute = re.sub('[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', f'"{version_string}"\n', clj_first) + clj_file.seek(0) + clj_file.write(version_substitute) + clj_file.write(clj_rest) + clj_file.truncate() \ No newline at end of file diff --git a/project.clj b/project.clj new file mode 100644 index 0000000..6e14c1f --- /dev/null +++ b/project.clj @@ -0,0 +1,46 @@ +(defproject org.domaindrivenarchitecture/c4k-website "1.1.3-SNAPSHOT" + :description "website c4k-installation package" + :url "https://domaindrivenarchitecture.org" + :license {:name "Apache License, Version 2.0" + :url "https://www.apache.org/licenses/LICENSE-2.0.html"} + :dependencies [[org.clojure/clojure "1.11.1"] + [org.clojure/tools.reader "1.3.6"] + [org.domaindrivenarchitecture/c4k-common-clj "5.0.1"] + [hickory "0.7.1"]] + :target-path "target/%s/" + :source-paths ["src/main/cljc" + "src/main/clj"] + :resource-paths ["src/main/resources"] + :repositories [["snapshots" :clojars] + ["releases" :clojars]] + :deploy-repositories [["snapshots" {:sign-releases false :url "https://clojars.org/repo"}] + ["releases" {:sign-releases false :url "https://clojars.org/repo"}]] + :profiles {:test {:test-paths ["src/test/cljc"] + :resource-paths ["src/test/resources"] + :dependencies [[dda/data-test "0.1.1"]]} + :dev {:plugins [[lein-shell "0.5.0"]]} + :uberjar {:aot :all + :main dda.c4k-website.uberjar + :uberjar-name "c4k-website-standalone.jar" + :dependencies [[org.clojure/tools.cli "1.0.214"] + [ch.qos.logback/logback-classic "1.4.5" + :exclusions [com.sun.mail/javax.mail]] + [org.slf4j/jcl-over-slf4j "2.0.6"]]}} + :release-tasks [["test"] + ["vcs" "assert-committed"] + ["change" "version" "leiningen.release/bump-version" "release"] + ["vcs" "commit"] + ["vcs" "tag" "v" "--no-sign"] + ["change" "version" "leiningen.release/bump-version"]] + :aliases {"native" ["shell" + "native-image" + "--report-unsupported-elements-at-runtime" + "--initialize-at-build-time" + "-jar" "target/uberjar/c4k-website-standalone.jar" + "-H:ResourceConfigurationFiles=graalvm-resource-config.json" + "-H:Log=registerResource" + "-H:Name=target/graalvm/${:name}"] + "inst" ["shell" + "sh" + "-c" + "lein uberjar && sudo install -m=755 target/uberjar/c4k-website-standalone.jar /usr/local/bin/c4k-website-standalone.jar"]}) diff --git a/test/config.clj b/test/config.clj new file mode 100644 index 0000000..c329c34 --- /dev/null +++ b/test/config.clj @@ -0,0 +1,5 @@ +(defproject org.domaindrivenarchitecture/c4k-website "1.1.3" + :description "website c4k-installation package" + :url "https://domaindrivenarchitecture.org" + :license {:name "Apache License, Version 2.0" + :url "https://www.apache.org/licenses/LICENSE-2.0.html"}) \ No newline at end of file diff --git a/test/test_version_class.py b/test/test_version_class.py index 55b6df2..9811e09 100644 --- a/test/test_version_class.py +++ b/test/test_version_class.py @@ -58,4 +58,21 @@ def test_json(tmp_path): version.to_file() # check - assert '"version": "123.123.456-SNAPSHOT"' in f.read_text() \ No newline at end of file + assert '"version": "123.123.456-SNAPSHOT"' in f.read_text() + +def test_clj(tmp_path): + # init + file_name = 'config.clj' + with open(f'test/{file_name}', 'r') as gradle_file: + contents = gradle_file.read() + + f = tmp_path / file_name + f.write_text(contents) + + # test + version = Version.from_file(f) + version.increment(ReleaseLevel.SNAPSHOT) + version.to_file() + + # check + assert '1.1.3-SNAPSHOT' in f.read_text() \ No newline at end of file From 9d1726051dfa212974cf46d6bbf8d1d35d9c0ef2 Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 9 Feb 2023 14:32:55 +0100 Subject: [PATCH 018/119] Implement python file handler --- file_handlers.py | 46 +++++++++++++++++++--- test/config.py | 78 ++++++++++++++++++++++++++++++++++++++ test/test_version_class.py | 21 +++++++++- 3 files changed, 138 insertions(+), 7 deletions(-) create mode 100644 test/config.py diff --git a/file_handlers.py b/file_handlers.py index 6c0813a..e3f627d 100644 --- a/file_handlers.py +++ b/file_handlers.py @@ -13,7 +13,9 @@ class FileHandler(ABC): case '.gradle': file_handler = GradleFileHandler() case '.clj': - file_handler = CljFileHandler() + file_handler = ClojureFileHandler() + case '.py': + file_handler = PythonFileHandler() case _: raise Exception(f'The file type "{config_file_type}" is not implemented') @@ -55,7 +57,7 @@ class GradleFileHandler(FileHandler): def parse(self) -> tuple[list[int], bool]: with open(self.config_file_path, 'r') as gradle_file: contents = gradle_file.read() - version_line = re.search("\nversion = .*\n", contents) + version_line = re.search("\nversion = .*", contents) exception = Exception("Version not found in gradle file") if version_line is None: raise exception @@ -77,13 +79,47 @@ class GradleFileHandler(FileHandler): def write(self, version_string): with open(self.config_file_path, 'r+') as gradle_file: - gradle_contents = gradle_file.read() - version_substitute = re.sub("\nversion = .*\n", f'\nversion = "{version_string}"\n', gradle_contents) + contents = gradle_file.read() + version_substitute = re.sub('\nversion = "[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?"', f'\nversion = "{version_string}"', contents) gradle_file.seek(0) gradle_file.write(version_substitute) gradle_file.truncate() -class CljFileHandler(FileHandler): + +class PythonFileHandler(FileHandler): + + def parse(self) -> tuple[list[int], bool]: + with open(self.config_file_path, 'r') as python_file: + contents = python_file.read() + version_line = re.search("\nversion = .*\n", contents) + exception = Exception("Version not found in gradle file") + if version_line is None: + raise exception + + version_line = version_line.group() + version_string = re.search('[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', version_line) + if version_string is None: + raise exception + + version_string = version_string.group() + is_snapshot = False + if '-SNAPSHOT' in version_string: + is_snapshot = True + version_string = version_string.replace('-SNAPSHOT', '') + + version = [int(x) for x in version_string.split('.')] + + return version, is_snapshot + + def write(self, version_string): + with open(self.config_file_path, 'r+') as python_file: + contents = python_file.read() + version_substitute = re.sub('\nversion = "[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?"', f'\nversion = "{version_string}"', contents) + python_file.seek(0) + python_file.write(version_substitute) + python_file.truncate() + +class ClojureFileHandler(FileHandler): def parse(self) -> tuple[list[int], bool]: with open(self.config_file_path, 'r') as clj_file: diff --git a/test/config.py b/test/config.py new file mode 100644 index 0000000..bb47bcd --- /dev/null +++ b/test/config.py @@ -0,0 +1,78 @@ +# dda_devops_build +# Copyright 2019 meissa GmbH. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from pybuilder.core import init, use_plugin, Author + +use_plugin("python.core") +use_plugin("copy_resources") +use_plugin("filter_resources") +#use_plugin("python.unittest") +#use_plugin("python.coverage") +use_plugin("python.distutils") + +#use_plugin("python.install_dependencies") + +default_task = "publish" + +name = "ddadevops" +version = "3.1.3" +summary = "tools to support builds combining gopass, terraform, dda-pallet, aws & hetzner-cloud" +description = __doc__ +authors = [Author("meissa GmbH", "buero@meissa-gmbh.de")] +url = "https://github.com/DomainDrivenArchitecture/dda-devops-build" +requires_python = ">=2.7,!=3.0,!=3.1,!=3.2,!=3.3,!=3.4" # CHECK IF NEW VERSION EXISTS +license = "Apache Software License" + +@init +def initialize(project): + #project.build_depends_on('mockito') + #project.build_depends_on('unittest-xml-reporting') + + project.set_property("verbose", True) + project.get_property("filter_resources_glob").append("main/python/ddadevops/__init__.py") + #project.set_property("dir_source_unittest_python", "src/unittest/python") + + project.set_property("copy_resources_target", "$dir_dist/ddadevops") + project.get_property("copy_resources_glob").append("LICENSE") + project.get_property("copy_resources_glob").append("src/main/resources/terraform/*") + project.get_property("copy_resources_glob").append("src/main/resources/docker/image/resources/*") + project.include_file("ddadevops", "LICENSE") + project.include_file("ddadevops", "src/main/resources/terraform/*") + project.include_file("ddadevops", "src/main/resources/docker/image/resources/*") + + #project.set_property('distutils_upload_sign', True) + #project.set_property('distutils_upload_sign_identity', '') + project.set_property("distutils_readme_description", True) + project.set_property("distutils_description_overwrite", True) + project.set_property("distutils_classifiers", [ + 'License :: OSI Approved :: Apache Software License', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Operating System :: POSIX :: Linux', + 'Operating System :: OS Independent', + 'Development Status :: 5 - Production/Stable', + 'Environment :: Console', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: Apache Software License', + 'Topic :: Software Development :: Build Tools', + 'Topic :: Software Development :: Quality Assurance', + 'Topic :: Software Development :: Testing' + ]) diff --git a/test/test_version_class.py b/test/test_version_class.py index 9811e09..ddf38c3 100644 --- a/test/test_version_class.py +++ b/test/test_version_class.py @@ -60,7 +60,7 @@ def test_json(tmp_path): # check assert '"version": "123.123.456-SNAPSHOT"' in f.read_text() -def test_clj(tmp_path): +def test_clojure(tmp_path): # init file_name = 'config.clj' with open(f'test/{file_name}', 'r') as gradle_file: @@ -75,4 +75,21 @@ def test_clj(tmp_path): version.to_file() # check - assert '1.1.3-SNAPSHOT' in f.read_text() \ No newline at end of file + assert '1.1.3-SNAPSHOT' in f.read_text() + +def test_python(tmp_path): + # init + file_name = 'config.py' + with open(f'test/{file_name}', 'r') as gradle_file: + contents = gradle_file.read() + + f = tmp_path / file_name + f.write_text(contents) + + # test + version = Version.from_file(f) + version.increment(ReleaseLevel.SNAPSHOT) + version.to_file() + + # check + assert '3.1.3-SNAPSHOT' in f.read_text() \ No newline at end of file From 3659b888f8590c5487ff6877c639dc7f33cba8bd Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 9 Feb 2023 17:09:42 +0100 Subject: [PATCH 019/119] Move test resources to distinct folder --- test/{ => resources}/config.clj | 0 test/{ => resources}/config.gradle | 0 test/{ => resources}/config.json | 0 test/{ => resources}/config.py | 0 test/test_version_class.py | 8 ++++---- 5 files changed, 4 insertions(+), 4 deletions(-) rename test/{ => resources}/config.clj (100%) rename test/{ => resources}/config.gradle (100%) rename test/{ => resources}/config.json (100%) rename test/{ => resources}/config.py (100%) diff --git a/test/config.clj b/test/resources/config.clj similarity index 100% rename from test/config.clj rename to test/resources/config.clj diff --git a/test/config.gradle b/test/resources/config.gradle similarity index 100% rename from test/config.gradle rename to test/resources/config.gradle diff --git a/test/config.json b/test/resources/config.json similarity index 100% rename from test/config.json rename to test/resources/config.json diff --git a/test/config.py b/test/resources/config.py similarity index 100% rename from test/config.py rename to test/resources/config.py diff --git a/test/test_version_class.py b/test/test_version_class.py index ddf38c3..5c7b49a 100644 --- a/test/test_version_class.py +++ b/test/test_version_class.py @@ -29,7 +29,7 @@ def test_version(): def test_gradle(tmp_path): # init file_name = 'config.gradle' - with open(f'test/{file_name}', 'r') as gradle_file: + with open(f'test/resources/{file_name}', 'r') as gradle_file: contents = gradle_file.read() f = tmp_path / file_name @@ -46,7 +46,7 @@ def test_gradle(tmp_path): def test_json(tmp_path): # init file_name = 'config.json' - with open(f'test/{file_name}', 'r') as gradle_file: + with open(f'test/resources/{file_name}', 'r') as gradle_file: contents = gradle_file.read() f = tmp_path / file_name @@ -63,7 +63,7 @@ def test_json(tmp_path): def test_clojure(tmp_path): # init file_name = 'config.clj' - with open(f'test/{file_name}', 'r') as gradle_file: + with open(f'test/resources/{file_name}', 'r') as gradle_file: contents = gradle_file.read() f = tmp_path / file_name @@ -80,7 +80,7 @@ def test_clojure(tmp_path): def test_python(tmp_path): # init file_name = 'config.py' - with open(f'test/{file_name}', 'r') as gradle_file: + with open(f'test/resources/{file_name}', 'r') as gradle_file: contents = gradle_file.read() f = tmp_path / file_name From 2f0bd5c211d59bc7d67d6d70671924130c3e2f7c Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 9 Feb 2023 17:20:58 +0100 Subject: [PATCH 020/119] Remove old config files --- build.gradle | 6352 -------------------------------------------------- package.json | 33 - project.clj | 46 - 3 files changed, 6431 deletions(-) delete mode 100644 build.gradle delete mode 100644 package.json delete mode 100644 project.clj diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 6fa64a8..0000000 --- a/build.gradle +++ /dev/null @@ -1,6352 +0,0 @@ -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" - -repositories { - mavenCentral() -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" -repositories { - mavenCentral() -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" -repositories { - mavenCentral() -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" - mavenCentral() -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" -repositories { - mavenCentral() -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" - mavenCentral() -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" - mavenCentral() -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" -repositories { - mavenCentral() -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" - mavenCentral() -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" - mavenCentral() -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" - mavenCentral() -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" -repositories { - mavenCentral() -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" - mavenCentral() -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" - mavenCentral() -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" - mavenCentral() -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" - mavenCentral() -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" -} - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" - - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} -buildscript { - ext.kotlin_version = "1.7.0" - ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID - - repositories { mavenCentral() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "java-library" -apply plugin: "java-test-fixtures" -apply plugin: "maven-publish" -apply plugin: "kotlinx-serialization" - - -group = "org.domaindrivenarchitecture.provs" -version = "0.17.1-SNAPSHOT" - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } -} - - -test { - // set properties for the tests - def propertiesForTests = ["testdockerwithoutsudo"] - for (def prop : propertiesForTests) { - def value = System.getProperty(prop) - if (value != null) { - systemProperty prop, value - } - } - - useJUnitPlatform { - def excludedTags = System.getProperty("excludeTags") - if (System.getProperty("excludeTags") != null) { - excludeTags(excludedTags.split(",")) - } - if (System.getenv("CI_JOB_TOKEN") != null) { - excludeTags("containernonci") - } - } -} - -compileJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestFixturesJava.options.debugOptions.debugLevel = "source,lines,vars" -compileTestJava.options.debugOptions.debugLevel = "source,lines,vars" - -// https://stackoverflow.com/questions/21904269/configure-gradle-to-publish-sources-and-javadoc -java { - withSourcesJar() - withJavadocJar() -} - - -dependencies { - - api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2") - api("org.jetbrains.kotlinx:kotlinx-cli:0.3.4") - - api('com.charleskorn.kaml:kaml:0.43.0') - - api("org.slf4j:slf4j-api:1.7.36") - api('ch.qos.logback:logback-classic:1.2.11') - api('ch.qos.logback:logback-core:1.2.11') - - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("com.hierynomus:sshj:0.32.0") - - implementation("aws.sdk.kotlin:s3:0.17.1-beta") - - testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.2") - testFixturesApi('io.mockk:mockk:1.12.3') - - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") -} - - -task uberjarDesktop(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" - } - archiveFileName = "provs-desktop.jar" -} - - -task uberjarServer(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.server.application.ApplicationKt" - } - archiveFileName = "provs-server.jar" -} - - -task uberjarSyspec(type: Jar) { - - from sourceSets.main.output - - dependsOn configurations.runtimeClasspath - from { - configurations.runtimeClasspath.findAll { it.name.endsWith("jar") }.collect { zipTree(it) } - } { - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) - exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" - } - - manifest { - attributes "Implementation-Title": "Uberjar of provs", - "Implementation-Version": project.version, - "Main-Class": "org.domaindrivenarchitecture.provs.syspec.application.ApplicationKt" - } - archiveFileName = "provs-syspec.jar" -} -def projectRoot = rootProject.projectDir - - -// copy jar to /usr/local/bin and make it executable -// Remark: to be able to use it you must have jarwrapper installed (sudo apt install jarwrapper) -task installlocally { - dependsOn(uberjarServer, uberjarDesktop, uberjarSyspec) - doLast { - exec { commandLine("sh", "-c", "sudo apt-get update & sudo apt-get install jarwrapper") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-server.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-desktop.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo cp $projectRoot/build/libs/provs-syspec.jar /usr/local/bin/") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-server.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-desktop.jar") } - exec { commandLine("sh", "-c", "sudo chmod 755 /usr/local/bin/provs-syspec.jar") } - } -} - -task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - archiveClassifier.set("sources") -} - - -publishing { - publications { - library(MavenPublication) { - from components.java - } - } - repositories { - if (System.getenv("CI_JOB_TOKEN") != null) { - // see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html - maven { - url "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/maven" - name "GitLab" - credentials(HttpHeaderCredentials) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - mavenLocal() - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index bbf2883..0000000 --- a/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "dummy", - "description": "Generate c4k yaml for a jitsi deployment.", - "author": "meissa GmbH", - "version": "3.0.0", - "homepage": "https://gitlab.com/domaindrivenarchitecture/c4k-jitsi#readme", - "repository": "https://www.npmjs.com/package/c4k-jitsi", - "license": "APACHE2", - "main": "c4k-jitsi.js", - "bin": { - "c4k-jitsi": "./c4k-jitsi.js" - }, - "keywords": [ - "cljs", - "jitsi", - "k8s", - "c4k", - "deployment", - "yaml", - "convention4kubernetes" - ], - "bugs": { - "url": "https://gitlab.com/domaindrivenarchitecture/c4k-jitsi/issues" - }, - "dependencies": { - "js-base64": "^3.6.1", - "js-yaml": "^4.0.0" - }, - "devDependencies": { - "shadow-cljs": "^2.11.18", - "source-map-support": "^0.5.19" - } -} \ No newline at end of file diff --git a/project.clj b/project.clj deleted file mode 100644 index 6e14c1f..0000000 --- a/project.clj +++ /dev/null @@ -1,46 +0,0 @@ -(defproject org.domaindrivenarchitecture/c4k-website "1.1.3-SNAPSHOT" - :description "website c4k-installation package" - :url "https://domaindrivenarchitecture.org" - :license {:name "Apache License, Version 2.0" - :url "https://www.apache.org/licenses/LICENSE-2.0.html"} - :dependencies [[org.clojure/clojure "1.11.1"] - [org.clojure/tools.reader "1.3.6"] - [org.domaindrivenarchitecture/c4k-common-clj "5.0.1"] - [hickory "0.7.1"]] - :target-path "target/%s/" - :source-paths ["src/main/cljc" - "src/main/clj"] - :resource-paths ["src/main/resources"] - :repositories [["snapshots" :clojars] - ["releases" :clojars]] - :deploy-repositories [["snapshots" {:sign-releases false :url "https://clojars.org/repo"}] - ["releases" {:sign-releases false :url "https://clojars.org/repo"}]] - :profiles {:test {:test-paths ["src/test/cljc"] - :resource-paths ["src/test/resources"] - :dependencies [[dda/data-test "0.1.1"]]} - :dev {:plugins [[lein-shell "0.5.0"]]} - :uberjar {:aot :all - :main dda.c4k-website.uberjar - :uberjar-name "c4k-website-standalone.jar" - :dependencies [[org.clojure/tools.cli "1.0.214"] - [ch.qos.logback/logback-classic "1.4.5" - :exclusions [com.sun.mail/javax.mail]] - [org.slf4j/jcl-over-slf4j "2.0.6"]]}} - :release-tasks [["test"] - ["vcs" "assert-committed"] - ["change" "version" "leiningen.release/bump-version" "release"] - ["vcs" "commit"] - ["vcs" "tag" "v" "--no-sign"] - ["change" "version" "leiningen.release/bump-version"]] - :aliases {"native" ["shell" - "native-image" - "--report-unsupported-elements-at-runtime" - "--initialize-at-build-time" - "-jar" "target/uberjar/c4k-website-standalone.jar" - "-H:ResourceConfigurationFiles=graalvm-resource-config.json" - "-H:Log=registerResource" - "-H:Name=target/graalvm/${:name}"] - "inst" ["shell" - "sh" - "-c" - "lein uberjar && sudo install -m=755 target/uberjar/c4k-website-standalone.jar /usr/local/bin/c4k-website-standalone.jar"]}) From 3661e47bfff794ca4b6de063775da9cf67dbc00d Mon Sep 17 00:00:00 2001 From: erik Date: Thu, 16 Feb 2023 14:21:27 +0100 Subject: [PATCH 021/119] WIP create ReleaseMixin --- .gitignore | 5 ++- build.py | 2 +- release_mixin.py | 45 +++++++++++++++++++ ...core.classpath.extract-native-dependencies | 1 + devops_test.py => version.py | 0 5 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 release_mixin.py create mode 100644 target/default+test/stale/leiningen.core.classpath.extract-native-dependencies rename devops_test.py => version.py (100%) diff --git a/.gitignore b/.gitignore index a9c5fbc..7f3a111 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,7 @@ # Go workspace file go.work -__pycache__ \ No newline at end of file +__pycache__ + +.clj-kondo/ +.lsp/ \ No newline at end of file diff --git a/build.py b/build.py index 3efe26d..183e28e 100644 --- a/build.py +++ b/build.py @@ -1,6 +1,6 @@ from pybuilder.core import task, init from ddadevops import * -from devops_test import * +from version import * def main(): diff --git a/release_mixin.py b/release_mixin.py new file mode 100644 index 0000000..6d709f7 --- /dev/null +++ b/release_mixin.py @@ -0,0 +1,45 @@ + +from ddadevops import DevopsBuild +from ddadevops import execute +from ddadevops import gopass_field_from_path, gopass_password_from_path +from version import Version + + +def add_release_mixin_config(config, release_type, commit, file): + config.update({'ReleaseMixin': + {'release_type': release_type, + 'commit': commit, + 'file': file}}) + return config + + +class ReleaseMixin(DevopsBuild): + + def __init__(self, project, config): + super().__init__(project, config) + release_mixin_config = config['ReleaseMixin'] + self.release_type = release_mixin_config['release_type4'] + self.commit = release_mixin_config['commit'] + self.file = release_mixin_config['file'] + + def read_commit_message(self): + pass + + def calculate_release_type(self): + pass + + def get_version(self): + + return Version.from_file(self.file) + + def create_release_version(self): + pass + + def create_bump_version(self): + pass + + + + + + diff --git a/target/default+test/stale/leiningen.core.classpath.extract-native-dependencies b/target/default+test/stale/leiningen.core.classpath.extract-native-dependencies new file mode 100644 index 0000000..03ba243 --- /dev/null +++ b/target/default+test/stale/leiningen.core.classpath.extract-native-dependencies @@ -0,0 +1 @@ +[{:dependencies {args4j {:vsn "2.0.26", :native-prefix nil}, org.clojure/data.json {:vsn "0.2.6", :native-prefix nil}, org.clojure/clojure {:vsn "1.11.1", :native-prefix nil}, org.clojure/core.specs.alpha {:vsn "0.2.62", :native-prefix nil}, org.clojure/spec.alpha {:vsn "0.3.218", :native-prefix nil}, dda/data-test {:vsn "0.1.1", :native-prefix nil}, quoin {:vsn "0.1.2", :native-prefix nil}, org.domaindrivenarchitecture/c4k-common-clj {:vsn "5.0.1", :native-prefix nil}, hickory {:vsn "0.7.1", :native-prefix nil}, org.clojure/google-closure-library {:vsn "0.0-20160609-f42b4a24", :native-prefix nil}, org.clojure/clojurescript {:vsn "1.9.293", :native-prefix nil}, aero {:vsn "1.1.6", :native-prefix nil}, orchestra {:vsn "2021.01.01-1", :native-prefix nil}, org.flatland/ordered {:vsn "1.5.9", :native-prefix nil}, com.google.jsinterop/jsinterop-annotations {:vsn "1.0.0", :native-prefix nil}, org.yaml/snakeyaml {:vsn "1.33", :native-prefix nil}, org.mozilla/rhino {:vsn "1.7R5", :native-prefix nil}, org.clojure/google-closure-library-third-party {:vsn "0.0-20160609-f42b4a24", :native-prefix nil}, pjstadig/humane-test-output {:vsn "0.11.0", :native-prefix nil}, com.google.javascript/closure-compiler-externs {:vsn "v20160911", :native-prefix nil}, clojure-complete {:vsn "0.2.5", :native-prefix nil}, com.google.guava/guava {:vsn "19.0", :native-prefix nil}, viebel/codox-klipse-theme {:vsn "0.0.1", :native-prefix nil}, clj-commons/clj-yaml {:vsn "1.0.26", :native-prefix nil}, prismatic/schema {:vsn "1.1.10", :native-prefix nil}, org.clojure/tools.reader {:vsn "1.3.6", :native-prefix nil}, nrepl {:vsn "0.6.0", :native-prefix nil}, org.jsoup/jsoup {:vsn "1.9.2", :native-prefix nil}, expound {:vsn "0.9.0", :native-prefix nil}, com.google.javascript/closure-compiler-unshaded {:vsn "v20160911", :native-prefix nil}, com.google.protobuf/protobuf-java {:vsn "2.5.0", :native-prefix nil}, com.google.code.findbugs/jsr305 {:vsn "1.3.9", :native-prefix nil}, com.google.code.gson/gson {:vsn "2.2.4", :native-prefix nil}}, :native-path "target/default+test/native"} {:native-path "target/default+test/native", :dependencies {args4j {:vsn "2.0.26", :native-prefix nil, :native? false}, org.clojure/data.json {:vsn "0.2.6", :native-prefix nil, :native? false}, org.clojure/clojure {:vsn "1.11.1", :native-prefix nil, :native? false}, org.clojure/core.specs.alpha {:vsn "0.2.62", :native-prefix nil, :native? false}, org.clojure/spec.alpha {:vsn "0.3.218", :native-prefix nil, :native? false}, dda/data-test {:vsn "0.1.1", :native-prefix nil, :native? false}, quoin {:vsn "0.1.2", :native-prefix nil, :native? false}, org.domaindrivenarchitecture/c4k-common-clj {:vsn "5.0.1", :native-prefix nil, :native? false}, hickory {:vsn "0.7.1", :native-prefix nil, :native? false}, org.clojure/google-closure-library {:vsn "0.0-20160609-f42b4a24", :native-prefix nil, :native? false}, org.clojure/clojurescript {:vsn "1.9.293", :native-prefix nil, :native? false}, aero {:vsn "1.1.6", :native-prefix nil, :native? false}, orchestra {:vsn "2021.01.01-1", :native-prefix nil, :native? false}, org.flatland/ordered {:vsn "1.5.9", :native-prefix nil, :native? false}, com.google.jsinterop/jsinterop-annotations {:vsn "1.0.0", :native-prefix nil, :native? false}, org.yaml/snakeyaml {:vsn "1.33", :native-prefix nil, :native? false}, org.mozilla/rhino {:vsn "1.7R5", :native-prefix nil, :native? false}, org.clojure/google-closure-library-third-party {:vsn "0.0-20160609-f42b4a24", :native-prefix nil, :native? false}, pjstadig/humane-test-output {:vsn "0.11.0", :native-prefix nil, :native? false}, com.google.javascript/closure-compiler-externs {:vsn "v20160911", :native-prefix nil, :native? false}, clojure-complete {:vsn "0.2.5", :native-prefix nil, :native? false}, com.google.guava/guava {:vsn "19.0", :native-prefix nil, :native? false}, viebel/codox-klipse-theme {:vsn "0.0.1", :native-prefix nil, :native? false}, clj-commons/clj-yaml {:vsn "1.0.26", :native-prefix nil, :native? false}, prismatic/schema {:vsn "1.1.10", :native-prefix nil, :native? false}, org.clojure/tools.reader {:vsn "1.3.6", :native-prefix nil, :native? false}, nrepl {:vsn "0.6.0", :native-prefix nil, :native? false}, org.jsoup/jsoup {:vsn "1.9.2", :native-prefix nil, :native? false}, expound {:vsn "0.9.0", :native-prefix nil, :native? false}, com.google.javascript/closure-compiler-unshaded {:vsn "v20160911", :native-prefix nil, :native? false}, com.google.protobuf/protobuf-java {:vsn "2.5.0", :native-prefix nil, :native? false}, com.google.code.findbugs/jsr305 {:vsn "1.3.9", :native-prefix nil, :native? false}, com.google.code.gson/gson {:vsn "2.2.4", :native-prefix nil, :native? false}}}] \ No newline at end of file diff --git a/devops_test.py b/version.py similarity index 100% rename from devops_test.py rename to version.py From e68ae468040a19b99423cd8a2ed57e2be3579f57 Mon Sep 17 00:00:00 2001 From: erik Date: Thu, 16 Feb 2023 15:59:38 +0100 Subject: [PATCH 022/119] Prepare build.py --- build.py | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/build.py b/build.py index 183e28e..91d74dc 100644 --- a/build.py +++ b/build.py @@ -1,7 +1,13 @@ from pybuilder.core import task, init from ddadevops import * from version import * +from release_mixin import * +CONFIG_FILE = '' +COMMIT_ID = '' + +class MyBuild(ReleaseMixin): + pass def main(): init_project() @@ -9,23 +15,24 @@ def main(): @init def initialize(project): project.build_depends_on('ddadevops>=3.1.2') - # build = get_devops_build() - # build.init() - init_project() + + if COMMIT_ID == '': + COMMIT_ID = 'HEAD' + + config = create_release_mixin_config(CONFIG_FILE, COMMIT_ID) + + build = MyBuild(project, config) + + build.init() + @task -def release(project): - # build = get_devops_build(project) - # build.prepare_release() # mit config file - prepare_release() - # build() - # build.publish() - # build.release_in_git() - # release_in_git() - -@task -def build(project): +def prepare(project): build = get_devops_build(project) - # ToDo: Implement for project + build.prepare_release() -main() \ No newline at end of file +@task +def tag_and_push(project): + build = get_devops_build(project) + build.tag_and_push() + From 22c5f6777e7750a62bfea7cde14f7f3d143733b7 Mon Sep 17 00:00:00 2001 From: erik Date: Thu, 16 Feb 2023 16:22:11 +0100 Subject: [PATCH 023/119] Prepare release_mixin.py --- release_mixin.py | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/release_mixin.py b/release_mixin.py index 6d709f7..f83a49f 100644 --- a/release_mixin.py +++ b/release_mixin.py @@ -5,10 +5,9 @@ from ddadevops import gopass_field_from_path, gopass_password_from_path from version import Version -def add_release_mixin_config(config, release_type, commit, file): +def create_release_mixin_config(config, release_type, commit, file): config.update({'ReleaseMixin': - {'release_type': release_type, - 'commit': commit, + {'commit_id': commit, 'file': file}}) return config @@ -17,26 +16,18 @@ class ReleaseMixin(DevopsBuild): def __init__(self, project, config): super().__init__(project, config) - release_mixin_config = config['ReleaseMixin'] - self.release_type = release_mixin_config['release_type4'] - self.commit = release_mixin_config['commit'] + release_mixin_config = config['ReleaseMixin'] + self.commit_id = release_mixin_config['commit_id'] self.file = release_mixin_config['file'] - def read_commit_message(self): - pass + def init(self): + release_and_bump_version = InitReleaseService(self.commit_id, self.file).get_version() + return release_and_bump_version - def calculate_release_type(self): - pass - def get_version(self): - return Version.from_file(self.file) - def create_release_version(self): - pass - - def create_bump_version(self): - pass + From 38c7aa97ca20db42f586c1f6142ef75d35f8fb9e Mon Sep 17 00:00:00 2001 From: erik Date: Thu, 16 Feb 2023 16:55:01 +0100 Subject: [PATCH 024/119] Prepare services.py --- services.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 services.py diff --git a/services.py b/services.py new file mode 100644 index 0000000..49f9a62 --- /dev/null +++ b/services.py @@ -0,0 +1,39 @@ +class InitReleaseService(): + + def __init__(self, commit_id, file): + self.commit_id = commit_id + self.file = file + + def __read_commit_message(self): + pass + + def __calculate_release_type(self): + pass + + def get_version(self): + + current_version = VersionRepository.get_current_version(self.file) + commit_message = self.read_commit_message(self.commit_id) + release_type = self.calculate_release_type(commit_message) + + release_version = create_release_version(current_version,release_type) + bump_version = create_bump_version(current_version,release_type) + + release_and_bump_version = tuple(release_version, bump_version) + + return release_and_bump_version + + def create_release_version(self): + pass + + def create_bump_version(self): + pass + + + +class PrepareReleaseService(): + pass + +class TagAndPushReleaseService(): + pass + From 7eb7a2647a59d782899e2ba2dcf02bb943a83ab0 Mon Sep 17 00:00:00 2001 From: erik Date: Thu, 16 Feb 2023 16:55:23 +0100 Subject: [PATCH 025/119] WIP Prepare version_repository.py --- version_repository.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 version_repository.py diff --git a/version_repository.py b/version_repository.py new file mode 100644 index 0000000..57c362d --- /dev/null +++ b/version_repository.py @@ -0,0 +1,17 @@ +from file_handlers import FileHandler + +class VersionRepository(): + + def __init__(self, file): + self.file = file + + @classmethod + def load_file(cls, file): + file_handler = FileHandler.from_file_path(file) + version, is_snapshot = file_handler.parse() + inst = cls(version, is_snapshot) + inst.file_handler = file_handler + + return inst + + \ No newline at end of file From dac901769d9e8ccdf15df4d0e88ebb8467e9f578 Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 20 Feb 2023 13:33:17 +0100 Subject: [PATCH 026/119] Add architecture img --- architecture.png | Bin 0 -> 66829 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 architecture.png diff --git a/architecture.png b/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..f169118d4b9211ccb8028a0de39f1da71f2fd8a5 GIT binary patch literal 66829 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 Ge*AC7Pe-Nz literal 0 HcmV?d00001 From 88c3acb73cba34aee23ad087aad45fcde902f8a3 Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 20 Feb 2023 13:33:28 +0100 Subject: [PATCH 027/119] WIP Create version repo in services with classmethod The goal is to create a single version_repo object for each file loaded. In that way we may avoid loading the file for loading and writing. --- services.py | 7 ++++--- version.py | 18 +----------------- version_repository.py | 37 +++++++++++++++++++++++++++++-------- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/services.py b/services.py index 49f9a62..6074e4b 100644 --- a/services.py +++ b/services.py @@ -3,6 +3,7 @@ class InitReleaseService(): def __init__(self, commit_id, file): self.commit_id = commit_id self.file = file + self.version_repo = none def __read_commit_message(self): pass @@ -12,12 +13,12 @@ class InitReleaseService(): def get_version(self): - current_version = VersionRepository.get_current_version(self.file) + version_repo = VersionRepository.get_current_version(self.file) commit_message = self.read_commit_message(self.commit_id) release_type = self.calculate_release_type(commit_message) - release_version = create_release_version(current_version,release_type) - bump_version = create_bump_version(current_version,release_type) + release_version = create_release_version(version_repo.version ,release_type) + bump_version = create_bump_version(version_repo.version,release_type) release_and_bump_version = tuple(release_version, bump_version) diff --git a/version.py b/version.py index 17b9220..a90a281 100644 --- a/version.py +++ b/version.py @@ -24,23 +24,7 @@ class Version(): def __init__(self, version, is_snapshot): self.version = version - self.is_snapshot = is_snapshot - self.file_handler = None - - @classmethod - def from_file(cls, config_file_path): - file_handler = FileHandler.from_file_path(config_file_path) - version, is_snapshot = file_handler.parse() - inst = cls(version, is_snapshot) - inst.file_handler = file_handler - - return inst - - def to_file(self): - if self.file_handler is None: - raise Exception('Version was not created by from_file method.') - else: - self.file_handler.write(self.get()) + self.is_snapshot = is_snapshot def increment(self, level: ReleaseLevel): self.is_snapshot = False diff --git a/version_repository.py b/version_repository.py index 57c362d..a018e65 100644 --- a/version_repository.py +++ b/version_repository.py @@ -4,14 +4,35 @@ class VersionRepository(): def __init__(self, file): self.file = file + self.file_handler = None + self.version = None + self.is_snapshot = None + + def load_file(self): + self.file_handler = FileHandler.from_file_path(self.file) + return file_handler + + def write_file(self): + if self.file_handler is None: + raise Exception('Version was not created by load_file method.') + if self.version is None or self.is_snapshot is None: + raise Exception('Version or is_snapshot attribute not set.') + else: + self.file_handler.write(self.version) + + def parse_file(self, file_handler): + version, is_snapshot = file_handler.parse() + return version, is_snapshot @classmethod - def load_file(cls, file): - file_handler = FileHandler.from_file_path(file) - version, is_snapshot = file_handler.parse() - inst = cls(version, is_snapshot) - inst.file_handler = file_handler - - return inst + def get_current_version(cls, file): + inst = cls(file) - \ No newline at end of file + file_handler= inst.load_file(file) + version, is_snapshot = inst.parse_file(file_handler) + + inst.version = version + inst.is_snapshot = is_snapshot + inst.file_handler = file_handler + + return inst From ecdea88a6be013f234753be2cb27ca437eb740e9 Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 20 Feb 2023 13:47:14 +0100 Subject: [PATCH 028/119] Refactor version variable Avoid ambiguous variable names. We are handling a version list and a version string. --- services.py | 6 +++--- version.py | 8 ++++---- version_repository.py | 18 +++++++++--------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/services.py b/services.py index 6074e4b..74cfa99 100644 --- a/services.py +++ b/services.py @@ -13,12 +13,12 @@ class InitReleaseService(): def get_version(self): - version_repo = VersionRepository.get_current_version(self.file) + version_repo = VersionRepository.get(self.file) commit_message = self.read_commit_message(self.commit_id) release_type = self.calculate_release_type(commit_message) - release_version = create_release_version(version_repo.version ,release_type) - bump_version = create_bump_version(version_repo.version,release_type) + release_version = create_release_version(version_repo.version_list, release_type) + bump_version = create_bump_version(version_repo.version_list, release_type) release_and_bump_version = tuple(release_version, bump_version) diff --git a/version.py b/version.py index a90a281..c1e4c59 100644 --- a/version.py +++ b/version.py @@ -22,8 +22,8 @@ class ReleaseLevel(Enum): class Version(): - def __init__(self, version, is_snapshot): - self.version = version + def __init__(self, version_list, is_snapshot): + self.version_list = version_list self.is_snapshot = is_snapshot def increment(self, level: ReleaseLevel): @@ -41,8 +41,8 @@ class Version(): self.version[ReleaseLevel.MINOR.value] = 0 self.version[ReleaseLevel.MAJOR.value] += 1 - def get(self) -> str: - version_string = ".".join([str(x) for x in self.version]) + def get_version_string(self) -> str: + version_string = ".".join([str(x) for x in self.version_list]) if self.is_snapshot: version_string += "-SNAPSHOT" return version_string \ No newline at end of file diff --git a/version_repository.py b/version_repository.py index a018e65..28d46ce 100644 --- a/version_repository.py +++ b/version_repository.py @@ -5,33 +5,33 @@ class VersionRepository(): def __init__(self, file): self.file = file self.file_handler = None - self.version = None + self.version_list = None self.is_snapshot = None def load_file(self): self.file_handler = FileHandler.from_file_path(self.file) return file_handler - def write_file(self): + def write_file(self, version_string): if self.file_handler is None: raise Exception('Version was not created by load_file method.') - if self.version is None or self.is_snapshot is None: + if self.version_list is None or self.is_snapshot is None: raise Exception('Version or is_snapshot attribute not set.') else: - self.file_handler.write(self.version) + self.file_handler.write(version_string) def parse_file(self, file_handler): - version, is_snapshot = file_handler.parse() - return version, is_snapshot + version_list, is_snapshot = file_handler.parse() + return version_list, is_snapshot @classmethod - def get_current_version(cls, file): + def get(cls, file) -> VersionRepository: inst = cls(file) file_handler= inst.load_file(file) - version, is_snapshot = inst.parse_file(file_handler) + version_list, is_snapshot = inst.parse_file(file_handler) - inst.version = version + inst.version_list = version inst.is_snapshot = is_snapshot inst.file_handler = file_handler From 61629e9d97380a10afdb91893a93aba45816982f Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 20 Feb 2023 14:07:58 +0100 Subject: [PATCH 029/119] Fix undefined var --- version_repository.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version_repository.py b/version_repository.py index 28d46ce..741aaa4 100644 --- a/version_repository.py +++ b/version_repository.py @@ -31,7 +31,7 @@ class VersionRepository(): file_handler= inst.load_file(file) version_list, is_snapshot = inst.parse_file(file_handler) - inst.version_list = version + inst.version_list = version_list inst.is_snapshot = is_snapshot inst.file_handler = file_handler From 91f3d7d9721b6ef009c7f57946ff8dbb47441526 Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 20 Feb 2023 14:24:55 +0100 Subject: [PATCH 030/119] Refactor version class --- version.py | 52 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/version.py b/version.py index c1e4c59..8d1ea87 100644 --- a/version.py +++ b/version.py @@ -4,7 +4,7 @@ from file_handlers import FileHandler def init_project(): # validate_values() version = Version.from_file('build.gradle') - version.increment(ReleaseLevel.SNAPSHOT) + version.increment(ReleaseType.SNAPSHOT) version.to_file() print(version.get()) @@ -14,7 +14,7 @@ def prepare_release(): def release_in_git(): pass -class ReleaseLevel(Enum): +class ReleaseType(Enum): MAJOR = 0 MINOR = 1 PATCH = 2 @@ -22,27 +22,39 @@ class ReleaseLevel(Enum): class Version(): - def __init__(self, version_list, is_snapshot): - self.version_list = version_list - self.is_snapshot = is_snapshot + def __init__(self, version_list, release_type): + self.version_list = None + self.is_snapshot = None + self.version_string = None - def increment(self, level: ReleaseLevel): + def increment(self, release_type: ReleaseType): self.is_snapshot = False - match level: - case ReleaseLevel.SNAPSHOT: + match release_type: + case ReleaseType.SNAPSHOT: self.is_snapshot = True - case ReleaseLevel.PATCH: - self.version[ReleaseLevel.PATCH.value] += 1 - case ReleaseLevel.MINOR: - self.version[ReleaseLevel.PATCH.value] = 0 - self.version[ReleaseLevel.MINOR.value] += 1 - case ReleaseLevel.MAJOR: - self.version[ReleaseLevel.PATCH.value] = 0 - self.version[ReleaseLevel.MINOR.value] = 0 - self.version[ReleaseLevel.MAJOR.value] += 1 + case ReleaseType.PATCH: + self.version_list[ReleaseType.PATCH.value] += 1 + case ReleaseType.MINOR: + self.version_list[ReleaseType.PATCH.value] = 0 + self.version_list[ReleaseType.MINOR.value] += 1 + case ReleaseType.MAJOR: + self.version_list[ReleaseType.PATCH.value] = 0 + self.version_list[ReleaseType.MINOR.value] = 0 + self.version_list[ReleaseType.MAJOR.value] += 1 def get_version_string(self) -> str: - version_string = ".".join([str(x) for x in self.version_list]) + self.version_string = ".".join([str(x) for x in self.version_list]) if self.is_snapshot: - version_string += "-SNAPSHOT" - return version_string \ No newline at end of file + self.version_string += "-SNAPSHOT" + + @classmethod + def create_release_version(cls, version_list, release_type): + inst = cls(version_list, release_type) + inst.increment(release_type) + + return inst + + + @classmethod + def create_bump_version(cls, version_list, release_type): + pass \ No newline at end of file From eb998cfdbbb0ab85c91c605405fe2b0fd80a993d Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 20 Feb 2023 14:45:15 +0100 Subject: [PATCH 031/119] Implement creation of release version --- services.py | 27 ++++++++++++++------------- version.py | 2 +- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/services.py b/services.py index 74cfa99..b4ba72f 100644 --- a/services.py +++ b/services.py @@ -3,7 +3,7 @@ class InitReleaseService(): def __init__(self, commit_id, file): self.commit_id = commit_id self.file = file - self.version_repo = none + self.version_repo = None def __read_commit_message(self): pass @@ -11,23 +11,24 @@ class InitReleaseService(): def __calculate_release_type(self): pass - def get_version(self): + def get_version_repo(self): + self.version_repo = VersionRepository.get(self.file) - version_repo = VersionRepository.get(self.file) - commit_message = self.read_commit_message(self.commit_id) - release_type = self.calculate_release_type(commit_message) - - release_version = create_release_version(version_repo.version_list, release_type) - bump_version = create_bump_version(version_repo.version_list, release_type) - - release_and_bump_version = tuple(release_version, bump_version) - - return release_and_bump_version + def get_version_list(self): + self.get_version_repo() + return self.version_repo.version_list def create_release_version(self): - pass + commit_message = self.read_commit_message(self.commit_id) + release_type = self.calculate_release_type(commit_message) + version_list = self.get_version_list() + + release_version = Version.create_release_version(version_list, release_type) + self.version_repo.write_file(release_version.get_version_string()) + def create_bump_version(self): + bump_version = create_bump_version(version_repo.version_list, release_type) pass diff --git a/version.py b/version.py index 8d1ea87..162e28c 100644 --- a/version.py +++ b/version.py @@ -24,8 +24,8 @@ class Version(): def __init__(self, version_list, release_type): self.version_list = None - self.is_snapshot = None self.version_string = None + self.is_snapshot = None def increment(self, release_type: ReleaseType): self.is_snapshot = False From fa7e10d3ce0b8573772f9e47cbaa2aac6132f680 Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 20 Feb 2023 15:50:58 +0100 Subject: [PATCH 032/119] Implement creation of bump version --- services.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/services.py b/services.py index b4ba72f..c7240aa 100644 --- a/services.py +++ b/services.py @@ -26,10 +26,14 @@ class InitReleaseService(): release_version = Version.create_release_version(version_list, release_type) self.version_repo.write_file(release_version.get_version_string()) - def create_bump_version(self): - bump_version = create_bump_version(version_repo.version_list, release_type) - pass + if self.version_repo == None: + raise Exception('VersionRepo was not created. Did you run create_lease_version()?') + version_list = self.get_version_list() + + bump_version = Version.create_bump_version(version_list, ReleaseType.SNAPSHOT) + self.version_repo.write_file(bump_version.get_version_string()) + From c13734493296543ff07709a94e5068fc9103b875 Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 20 Feb 2023 16:21:02 +0100 Subject: [PATCH 033/119] Implement bump release --- services.py | 2 +- version.py | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/services.py b/services.py index c7240aa..952dc2d 100644 --- a/services.py +++ b/services.py @@ -31,7 +31,7 @@ class InitReleaseService(): raise Exception('VersionRepo was not created. Did you run create_lease_version()?') version_list = self.get_version_list() - bump_version = Version.create_bump_version(version_list, ReleaseType.SNAPSHOT) + bump_version = Version.create_bump_version(version_list) self.version_repo.write_file(bump_version.get_version_string()) diff --git a/version.py b/version.py index 162e28c..863f82e 100644 --- a/version.py +++ b/version.py @@ -3,7 +3,7 @@ from file_handlers import FileHandler def init_project(): # validate_values() - version = Version.from_file('build.gradle') + version = Version.from_file('build.gradle') version.increment(ReleaseType.SNAPSHOT) version.to_file() print(version.get()) @@ -19,6 +19,7 @@ class ReleaseType(Enum): MINOR = 1 PATCH = 2 SNAPSHOT = 3 + BUMP = None class Version(): @@ -30,6 +31,9 @@ class Version(): def increment(self, release_type: ReleaseType): self.is_snapshot = False match release_type: + case ReleaseType.BUMP: + self.is_snapshot = True + self.version_list[ReleaseType.PATCH.value] += 1 case ReleaseType.SNAPSHOT: self.is_snapshot = True case ReleaseType.PATCH: @@ -45,16 +49,19 @@ class Version(): def get_version_string(self) -> str: self.version_string = ".".join([str(x) for x in self.version_list]) if self.is_snapshot: - self.version_string += "-SNAPSHOT" + self.version_string += "-SNAPSHOT" @classmethod def create_release_version(cls, version_list, release_type): inst = cls(version_list, release_type) - inst.increment(release_type) - + if release_type == ReleaseType.PATCH: + inst.is_snapshot = False + else: + inst.increment(release_type) return inst - @classmethod - def create_bump_version(cls, version_list, release_type): - pass \ No newline at end of file + def create_bump_version(cls, version_list): + inst = cls(version_list, ReleaseType.BUMP) + inst.increment(release_type) + return inst From 54c3917ec23acc1fc977fcd1252bbf34a02e6cce Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 20 Feb 2023 16:37:01 +0100 Subject: [PATCH 034/119] Clean up --- version.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/version.py b/version.py index 863f82e..0a08d99 100644 --- a/version.py +++ b/version.py @@ -1,26 +1,6 @@ from enum import Enum from file_handlers import FileHandler -def init_project(): - # validate_values() - version = Version.from_file('build.gradle') - version.increment(ReleaseType.SNAPSHOT) - version.to_file() - print(version.get()) - -def prepare_release(): - pass - -def release_in_git(): - pass - -class ReleaseType(Enum): - MAJOR = 0 - MINOR = 1 - PATCH = 2 - SNAPSHOT = 3 - BUMP = None - class Version(): def __init__(self, version_list, release_type): From 3d3b26b70e1e452c859021cc53ee42079b243508 Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 20 Feb 2023 16:37:30 +0100 Subject: [PATCH 035/119] Refactor release type --- release_type.py | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 release_type.py diff --git a/release_type.py b/release_type.py new file mode 100644 index 0000000..e8cccbe --- /dev/null +++ b/release_type.py @@ -0,0 +1,6 @@ +class ReleaseType(Enum): + MAJOR = 0 + MINOR = 1 + PATCH = 2 + SNAPSHOT = 3 + BUMP = None \ No newline at end of file From e790bc6eeea9d696d32e4e0619764ccf65b17f09 Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 20 Feb 2023 16:37:49 +0100 Subject: [PATCH 036/119] WIP Tests --- test/test_version_class.py | 78 +++++++++----------------------------- 1 file changed, 17 insertions(+), 61 deletions(-) diff --git a/test/test_version_class.py b/test/test_version_class.py index 5c7b49a..a6782b4 100644 --- a/test/test_version_class.py +++ b/test/test_version_class.py @@ -1,29 +1,36 @@ -from devops_test import Version, ReleaseLevel +from version import Version +from version_repository import VersionRepository +from release_type import ReleaseType from pathlib import Path def test_version(): version = Version([1, 2, 3], False) version.increment(ReleaseLevel.SNAPSHOT) - assert version.get() == "1.2.3-SNAPSHOT" + assert version.get_version_string() == "1.2.3-SNAPSHOT" assert version.version == [1, 2, 3] assert version.is_snapshot + version.increment(ReleaseLevel.BUMP) + assert version.get_version_string() == "1.2.4-SNAPSHOT" + assert version.version == [1, 2, 4] + assert not version.is_snapshot + version.increment(ReleaseLevel.PATCH) - assert version.get() == "1.2.4" + assert version.get_version_string() == "1.2.5" assert version.version == [1, 2, 4] assert not version.is_snapshot version.increment(ReleaseLevel.SNAPSHOT) - assert version.get() == "1.2.4-SNAPSHOT" + assert version.get_version_string() == "1.2.5-SNAPSHOT" version.increment(ReleaseLevel.SNAPSHOT) - assert version.get() == "1.2.4-SNAPSHOT" + assert version.get_version_string() == "1.2.5-SNAPSHOT" version.increment(ReleaseLevel.MINOR) - assert version.get() == "1.3.0" + assert version.get_version_string() == "1.3.0" version.increment(ReleaseLevel.MAJOR) - assert version.get() == "2.0.0" + assert version.get_version_string() == "2.0.0" def test_gradle(tmp_path): @@ -36,60 +43,9 @@ def test_gradle(tmp_path): f.write_text(contents) # test - version = Version.from_file(f) - version.increment(ReleaseLevel.SNAPSHOT) - version.to_file() + version_repo = VersionRepository.get(f) + version = Version.create_release_version(ReleaseLevel.SNAPSHOT) + version_repo.write_file(version.get_version_string) # check assert 'version = "12.4.678-SNAPSHOT"' in f.read_text() - -def test_json(tmp_path): - # init - file_name = 'config.json' - with open(f'test/resources/{file_name}', 'r') as gradle_file: - contents = gradle_file.read() - - f = tmp_path / file_name - f.write_text(contents) - - # test - version = Version.from_file(f) - version.increment(ReleaseLevel.SNAPSHOT) - version.to_file() - - # check - assert '"version": "123.123.456-SNAPSHOT"' in f.read_text() - -def test_clojure(tmp_path): - # init - file_name = 'config.clj' - with open(f'test/resources/{file_name}', 'r') as gradle_file: - contents = gradle_file.read() - - f = tmp_path / file_name - f.write_text(contents) - - # test - version = Version.from_file(f) - version.increment(ReleaseLevel.SNAPSHOT) - version.to_file() - - # check - assert '1.1.3-SNAPSHOT' in f.read_text() - -def test_python(tmp_path): - # init - file_name = 'config.py' - with open(f'test/resources/{file_name}', 'r') as gradle_file: - contents = gradle_file.read() - - f = tmp_path / file_name - f.write_text(contents) - - # test - version = Version.from_file(f) - version.increment(ReleaseLevel.SNAPSHOT) - version.to_file() - - # check - assert '3.1.3-SNAPSHOT' in f.read_text() \ No newline at end of file From 80c0799d1a522817246a3a8a6c74627285895d4a Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 20 Feb 2023 16:45:36 +0100 Subject: [PATCH 037/119] Add proper imports --- test/test_version_class.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/test/test_version_class.py b/test/test_version_class.py index a6782b4..906ddb5 100644 --- a/test/test_version_class.py +++ b/test/test_version_class.py @@ -1,7 +1,25 @@ +from pathlib import Path +import sys +import os + +# getting the name of the directory +# where the this file is present. +current = os.path.dirname(os.path.realpath(__file__)) + +# Getting the parent directory name +# where the current directory is present. +parent = os.path.dirname(current) + +# adding the parent directory to +# the sys.path. +sys.path.append(parent) + +# now we can import the module in the parent +# directory. + from version import Version from version_repository import VersionRepository -from release_type import ReleaseType -from pathlib import Path +from release_type import ReleaseType def test_version(): version = Version([1, 2, 3], False) From 1743dce46d8adb2f222865963e138dad8f6caff6 Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 20 Feb 2023 16:50:12 +0100 Subject: [PATCH 038/119] Add missing imports and cleanup --- release_type.py | 1 + version.py | 2 +- version_repository.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/release_type.py b/release_type.py index e8cccbe..e4217a5 100644 --- a/release_type.py +++ b/release_type.py @@ -1,3 +1,4 @@ +from enum import Enum class ReleaseType(Enum): MAJOR = 0 MINOR = 1 diff --git a/version.py b/version.py index 0a08d99..0a5dd1f 100644 --- a/version.py +++ b/version.py @@ -1,4 +1,4 @@ -from enum import Enum +from release_type import ReleaseType from file_handlers import FileHandler class Version(): diff --git a/version_repository.py b/version_repository.py index 741aaa4..516a6f1 100644 --- a/version_repository.py +++ b/version_repository.py @@ -25,7 +25,7 @@ class VersionRepository(): return version_list, is_snapshot @classmethod - def get(cls, file) -> VersionRepository: + def get(cls, file): inst = cls(file) file_handler= inst.load_file(file) From d539c7331864273d0a6e149f3f8c3a009a06e6b6 Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 20 Feb 2023 16:53:53 +0100 Subject: [PATCH 039/119] Fix test method and attribute calls --- test/test_version_class.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/test/test_version_class.py b/test/test_version_class.py index 906ddb5..abc7b35 100644 --- a/test/test_version_class.py +++ b/test/test_version_class.py @@ -24,30 +24,30 @@ from release_type import ReleaseType def test_version(): version = Version([1, 2, 3], False) - version.increment(ReleaseLevel.SNAPSHOT) + version.increment(ReleaseType.SNAPSHOT) assert version.get_version_string() == "1.2.3-SNAPSHOT" - assert version.version == [1, 2, 3] + assert version.version_list == [1, 2, 3] assert version.is_snapshot - version.increment(ReleaseLevel.BUMP) + version.increment(ReleaseType.BUMP) assert version.get_version_string() == "1.2.4-SNAPSHOT" - assert version.version == [1, 2, 4] + assert version.version_list == [1, 2, 4] assert not version.is_snapshot - version.increment(ReleaseLevel.PATCH) + version.increment(ReleaseType.PATCH) assert version.get_version_string() == "1.2.5" - assert version.version == [1, 2, 4] + assert version.version_list == [1, 2, 5] assert not version.is_snapshot - version.increment(ReleaseLevel.SNAPSHOT) + version.increment(ReleaseType.SNAPSHOT) assert version.get_version_string() == "1.2.5-SNAPSHOT" - version.increment(ReleaseLevel.SNAPSHOT) + version.increment(ReleaseType.SNAPSHOT) assert version.get_version_string() == "1.2.5-SNAPSHOT" - version.increment(ReleaseLevel.MINOR) + version.increment(ReleaseType.MINOR) assert version.get_version_string() == "1.3.0" - version.increment(ReleaseLevel.MAJOR) + version.increment(ReleaseType.MAJOR) assert version.get_version_string() == "2.0.0" @@ -62,7 +62,7 @@ def test_gradle(tmp_path): # test version_repo = VersionRepository.get(f) - version = Version.create_release_version(ReleaseLevel.SNAPSHOT) + version = Version.create_release_version(ReleaseType.SNAPSHOT) version_repo.write_file(version.get_version_string) # check From b06728c55322ea3273a30d34546012b7d3ee4028 Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 20 Feb 2023 17:04:50 +0100 Subject: [PATCH 040/119] Fix return val and hardcoded None --- version.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/version.py b/version.py index 0a5dd1f..64b854d 100644 --- a/version.py +++ b/version.py @@ -3,8 +3,8 @@ from file_handlers import FileHandler class Version(): - def __init__(self, version_list, release_type): - self.version_list = None + def __init__(self, version_list: list): + self.version_list = version_list self.version_string = None self.is_snapshot = None @@ -30,6 +30,7 @@ class Version(): self.version_string = ".".join([str(x) for x in self.version_list]) if self.is_snapshot: self.version_string += "-SNAPSHOT" + return self.version_string @classmethod def create_release_version(cls, version_list, release_type): From f96733f8b5712b36294a94b424f039cb831d7c41 Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 20 Feb 2023 17:05:30 +0100 Subject: [PATCH 041/119] Fix version instance --- test/test_version_class.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_version_class.py b/test/test_version_class.py index abc7b35..aaad5af 100644 --- a/test/test_version_class.py +++ b/test/test_version_class.py @@ -22,7 +22,7 @@ from version_repository import VersionRepository from release_type import ReleaseType def test_version(): - version = Version([1, 2, 3], False) + version = Version([1, 2, 3]) version.increment(ReleaseType.SNAPSHOT) assert version.get_version_string() == "1.2.3-SNAPSHOT" From 156d33631404b6b8e1e3c5bf00c1b03cc3c58f6d Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 20 Feb 2023 17:07:14 +0100 Subject: [PATCH 042/119] Fix logical error --- test/test_version_class.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_version_class.py b/test/test_version_class.py index aaad5af..9607ab4 100644 --- a/test/test_version_class.py +++ b/test/test_version_class.py @@ -32,7 +32,7 @@ def test_version(): version.increment(ReleaseType.BUMP) assert version.get_version_string() == "1.2.4-SNAPSHOT" assert version.version_list == [1, 2, 4] - assert not version.is_snapshot + assert version.is_snapshot version.increment(ReleaseType.PATCH) assert version.get_version_string() == "1.2.5" From 1dc5a1e9e8861e4ca5ce4b9cbe255c07ff728e79 Mon Sep 17 00:00:00 2001 From: erik Date: Tue, 21 Feb 2023 09:54:18 +0100 Subject: [PATCH 043/119] WIP Fix returns and function calls --- version_repository.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version_repository.py b/version_repository.py index 516a6f1..725884b 100644 --- a/version_repository.py +++ b/version_repository.py @@ -10,7 +10,7 @@ class VersionRepository(): def load_file(self): self.file_handler = FileHandler.from_file_path(self.file) - return file_handler + return self.file_handler def write_file(self, version_string): if self.file_handler is None: @@ -28,7 +28,7 @@ class VersionRepository(): def get(cls, file): inst = cls(file) - file_handler= inst.load_file(file) + file_handler = inst.load_file() version_list, is_snapshot = inst.parse_file(file_handler) inst.version_list = version_list From ea45671bea8bdfba26c3d4c2516d7def76d184cc Mon Sep 17 00:00:00 2001 From: erik Date: Tue, 21 Feb 2023 10:07:30 +0100 Subject: [PATCH 044/119] Fix version and gradle test --- test/test_version_class.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/test_version_class.py b/test/test_version_class.py index 9607ab4..881ea7f 100644 --- a/test/test_version_class.py +++ b/test/test_version_class.py @@ -22,7 +22,7 @@ from version_repository import VersionRepository from release_type import ReleaseType def test_version(): - version = Version([1, 2, 3]) + version = Version([1, 2, 3], ReleaseType.SNAPSHOT) version.increment(ReleaseType.SNAPSHOT) assert version.get_version_string() == "1.2.3-SNAPSHOT" @@ -62,8 +62,9 @@ def test_gradle(tmp_path): # test version_repo = VersionRepository.get(f) - version = Version.create_release_version(ReleaseType.SNAPSHOT) - version_repo.write_file(version.get_version_string) + version_list = version_repo.version_list + version = Version.create_release_version(version_list, ReleaseType.SNAPSHOT) + version_repo.write_file(version.get_version_string()) # check assert 'version = "12.4.678-SNAPSHOT"' in f.read_text() From bb170175837e9c75f9b3662d9fa00e1f509bd7a2 Mon Sep 17 00:00:00 2001 From: erik Date: Tue, 21 Feb 2023 10:08:04 +0100 Subject: [PATCH 045/119] readd release type argument --- version.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/version.py b/version.py index 64b854d..492411d 100644 --- a/version.py +++ b/version.py @@ -3,8 +3,9 @@ from file_handlers import FileHandler class Version(): - def __init__(self, version_list: list): + def __init__(self, version_list: list, release_type: ReleaseType): self.version_list = version_list + self.release_type = release_type self.version_string = None self.is_snapshot = None From e9301dedd17fede7fb52ddc742e203f58ffb8c1e Mon Sep 17 00:00:00 2001 From: erik Date: Tue, 21 Feb 2023 10:17:16 +0100 Subject: [PATCH 046/119] Readd tests for python, clojure and json --- test/test_version_class.py | 54 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/test/test_version_class.py b/test/test_version_class.py index 881ea7f..eb3404d 100644 --- a/test/test_version_class.py +++ b/test/test_version_class.py @@ -68,3 +68,57 @@ def test_gradle(tmp_path): # check assert 'version = "12.4.678-SNAPSHOT"' in f.read_text() + +def test_json(tmp_path): + # init + file_name = 'config.json' + with open(f'test/resources/{file_name}', 'r') as gradle_file: + contents = gradle_file.read() + + f = tmp_path / file_name + f.write_text(contents) + + # test + version_repo = VersionRepository.get(f) + version_list = version_repo.version_list + version = Version.create_release_version(version_list, ReleaseType.SNAPSHOT) + version_repo.write_file(version.get_version_string()) + + # check + assert '"version": "123.123.456-SNAPSHOT"' in f.read_text() + +def test_clojure(tmp_path): + # init + file_name = 'config.clj' + with open(f'test/resources/{file_name}', 'r') as gradle_file: + contents = gradle_file.read() + + f = tmp_path / file_name + f.write_text(contents) + + # test + version_repo = VersionRepository.get(f) + version_list = version_repo.version_list + version = Version.create_release_version(version_list, ReleaseType.SNAPSHOT) + version_repo.write_file(version.get_version_string()) + + # check + assert '1.1.3-SNAPSHOT' in f.read_text() + +def test_python(tmp_path): + # init + file_name = 'config.py' + with open(f'test/resources/{file_name}', 'r') as gradle_file: + contents = gradle_file.read() + + f = tmp_path / file_name + f.write_text(contents) + + # test + version_repo = VersionRepository.get(f) + version_list = version_repo.version_list + version = Version.create_release_version(version_list, ReleaseType.SNAPSHOT) + version_repo.write_file(version.get_version_string()) + + # check + assert '3.1.3-SNAPSHOT' in f.read_text() \ No newline at end of file From fce565d2d508a9bf9fccd383c0a0038e0107bb4e Mon Sep 17 00:00:00 2001 From: erik Date: Tue, 21 Feb 2023 15:08:39 +0100 Subject: [PATCH 047/119] Implement git handler --- git_handler.py | 77 ++++++++++++++++++++++++++++++++++++++++ test/test_git_handler.py | 30 ++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 git_handler.py create mode 100644 test/test_git_handler.py diff --git a/git_handler.py b/git_handler.py new file mode 100644 index 0000000..3f30551 --- /dev/null +++ b/git_handler.py @@ -0,0 +1,77 @@ +import os +import subprocess as sub + +# define semantics for release types by commit messages +## snapshot - snapshot release +## fix/patch - patch release +## bump - version bump release +## feature/feat/minor - minor release +## major/breaking - major release + +GIT = 'git' +LOG = 'log' +FORMAT = '"%h %s"' +FORMAT_DEC = "%d" +PRETTY_OPTION = '--pretty=' +DECORATE_OPTION = '--decorate=full' + +class GitRepo(): + + def __init__(self): + self.commits = None + self.tags = None + + def __clean_commit_string(self, commit_string): + return commit_string.replace('\"', "").replace('\n', "").split() + + def __clean_tag_string(self, tag_string): + return tag_string.replace(" ", "").replace('\n', "") + + def get_tags(self): + stream = sub.Popen([GIT, + LOG, + PRETTY_OPTION + FORMAT_DEC, + DECORATE_OPTION], + stdout=sub.PIPE, + stderr=sub.PIPE, + text=True, + encoding="UTF-8") + stdout = stream.stdout.readlines() + stderr = stream.stderr.readlines() + + if len(stderr) > 0: + raise Exception(f"Git command failed with: {stderr}") + + return stdout + + def get_commits(self): + stream = sub.Popen([GIT, + LOG, + PRETTY_OPTION + FORMAT], + stdout=sub.PIPE, + stderr=sub.PIPE, + text=True, + encoding="UTF-8") + stdout = stream.stdout.readlines() + stderr = stream.stderr.readlines() + + if len(stderr) > 0: + raise Exception(f"Git command failed with: {stderr}") + + self.tags = self.get_tags() + self.commits = {} + + if len(self.tags) != len(stdout): + raise Exception("Tags list did not match commits list") + + for i, elem in enumerate(stdout): + commit_string = self.__clean_commit_string(elem) + + commit_id = commit_string[0] + commit_message = " ".join(commit_string[1:len(commit_string)]) + commit_tag = self.__clean_tag_string(self.tags[i]) + + self.commits[commit_id] = [commit_tag, commit_message] + + return self.commits + diff --git a/test/test_git_handler.py b/test/test_git_handler.py new file mode 100644 index 0000000..77d95c9 --- /dev/null +++ b/test/test_git_handler.py @@ -0,0 +1,30 @@ +from pathlib import Path +import sys +import os + +# getting the name of the directory +# where the this file is present. +current = os.path.dirname(os.path.realpath(__file__)) + +# Getting the parent directory name +# where the current directory is present. +parent = os.path.dirname(current) + +# adding the parent directory to +# the sys.path. +sys.path.append(parent) + +# now we can import the module in the parent +# directory. + +from git_handler import * + +def test_git_handler(): + + # init + repo = GitRepo() + repo.get_commits() + + #test + assert type(repo.commits) == dict + assert repo.commits["decd36b"] == ["(tag:refs/tags/TEST)", "Initial commit"] From 6883435248052a7d37724780f365c1a26e66302b Mon Sep 17 00:00:00 2001 From: erik Date: Tue, 21 Feb 2023 15:52:09 +0100 Subject: [PATCH 048/119] Refactor for code consistency --- services.py | 25 +++++++++++-------------- version.py | 30 +++++++++++++----------------- version_repository.py | 23 ++++++++--------------- 3 files changed, 32 insertions(+), 46 deletions(-) diff --git a/services.py b/services.py index 952dc2d..3792d41 100644 --- a/services.py +++ b/services.py @@ -1,3 +1,5 @@ +import version_repository +import release_type class InitReleaseService(): def __init__(self, commit_id, file): @@ -11,28 +13,23 @@ class InitReleaseService(): def __calculate_release_type(self): pass - def get_version_repo(self): - self.version_repo = VersionRepository.get(self.file) - - def get_version_list(self): - self.get_version_repo() - return self.version_repo.version_list + def get_version(self, release_type): + self.version_repo = VersionRepository(self.file) + return repo.get_version(release_type) def create_release_version(self): commit_message = self.read_commit_message(self.commit_id) release_type = self.calculate_release_type(commit_message) - version_list = self.get_version_list() - - release_version = Version.create_release_version(version_list, release_type) - self.version_repo.write_file(release_version.get_version_string()) + version = self.get_version(release_type).create_release_version() + + self.version_repo.write_file(version.get_version_string()) def create_bump_version(self): if self.version_repo == None: raise Exception('VersionRepo was not created. Did you run create_lease_version()?') - version_list = self.get_version_list() - - bump_version = Version.create_bump_version(version_list) - self.version_repo.write_file(bump_version.get_version_string()) + version = self.get_version(ReleaseType.BUMP).create_bump_version() + + self.version_repo.write_file(version.get_version_string()) diff --git a/version.py b/version.py index 492411d..59a6c5c 100644 --- a/version.py +++ b/version.py @@ -7,11 +7,11 @@ class Version(): self.version_list = version_list self.release_type = release_type self.version_string = None - self.is_snapshot = None + self.is_snapshot = is_snapshot - def increment(self, release_type: ReleaseType): + def increment(self): self.is_snapshot = False - match release_type: + match self.release_type: case ReleaseType.BUMP: self.is_snapshot = True self.version_list[ReleaseType.PATCH.value] += 1 @@ -26,24 +26,20 @@ class Version(): self.version_list[ReleaseType.PATCH.value] = 0 self.version_list[ReleaseType.MINOR.value] = 0 self.version_list[ReleaseType.MAJOR.value] += 1 + case None + raise Exception("Release Type was not set!") def get_version_string(self) -> str: self.version_string = ".".join([str(x) for x in self.version_list]) if self.is_snapshot: self.version_string += "-SNAPSHOT" return self.version_string - - @classmethod - def create_release_version(cls, version_list, release_type): - inst = cls(version_list, release_type) - if release_type == ReleaseType.PATCH: - inst.is_snapshot = False + + def create_release_version(self): + if self.release_type == ReleaseType.PATCH: + self.is_snapshot = False else: - inst.increment(release_type) - return inst - - @classmethod - def create_bump_version(cls, version_list): - inst = cls(version_list, ReleaseType.BUMP) - inst.increment(release_type) - return inst + self.increment(release_type) + + def create_bump_version(self): + self.increment(ReleaseType.BUMP) diff --git a/version_repository.py b/version_repository.py index 725884b..a5bf519 100644 --- a/version_repository.py +++ b/version_repository.py @@ -1,16 +1,15 @@ from file_handlers import FileHandler +from version import Version class VersionRepository(): def __init__(self, file): self.file = file - self.file_handler = None - self.version_list = None - self.is_snapshot = None + self.file_handler = None def load_file(self): self.file_handler = FileHandler.from_file_path(self.file) - return self.file_handler + return file_handler def write_file(self, version_string): if self.file_handler is None: @@ -23,16 +22,10 @@ class VersionRepository(): def parse_file(self, file_handler): version_list, is_snapshot = file_handler.parse() return version_list, is_snapshot + + def get_version(self, release_type): - @classmethod - def get(cls, file): - inst = cls(file) + self.file_handler = self.load_file(self.file) + version_list, is_snapshot = self.parse_file(file_handler) - file_handler = inst.load_file() - version_list, is_snapshot = inst.parse_file(file_handler) - - inst.version_list = version_list - inst.is_snapshot = is_snapshot - inst.file_handler = file_handler - - return inst + return Version(version_list, release_type) From 94b993ccf5a7ecea53c85a9e3a738576c08fcf31 Mon Sep 17 00:00:00 2001 From: erik Date: Tue, 21 Feb 2023 16:14:47 +0100 Subject: [PATCH 049/119] Fix bugs and tests --- test/test_version_class.py | 61 ++++++++++++++++++++------------------ version.py | 6 ++-- version_repository.py | 19 ++++++------ 3 files changed, 45 insertions(+), 41 deletions(-) diff --git a/test/test_version_class.py b/test/test_version_class.py index eb3404d..8880488 100644 --- a/test/test_version_class.py +++ b/test/test_version_class.py @@ -19,36 +19,39 @@ sys.path.append(parent) from version import Version from version_repository import VersionRepository -from release_type import ReleaseType +from release_type import ReleaseType def test_version(): version = Version([1, 2, 3], ReleaseType.SNAPSHOT) - version.increment(ReleaseType.SNAPSHOT) + version.increment() assert version.get_version_string() == "1.2.3-SNAPSHOT" assert version.version_list == [1, 2, 3] assert version.is_snapshot - version.increment(ReleaseType.BUMP) + version = Version([1, 2, 3], ReleaseType.BUMP) + version.increment() assert version.get_version_string() == "1.2.4-SNAPSHOT" assert version.version_list == [1, 2, 4] assert version.is_snapshot - version.increment(ReleaseType.PATCH) - assert version.get_version_string() == "1.2.5" - assert version.version_list == [1, 2, 5] + version = Version([1, 2, 3], ReleaseType.PATCH) + version.increment() + assert version.get_version_string() == "1.2.4" + assert version.version_list == [1, 2, 4] assert not version.is_snapshot - version.increment(ReleaseType.SNAPSHOT) - assert version.get_version_string() == "1.2.5-SNAPSHOT" - version.increment(ReleaseType.SNAPSHOT) - assert version.get_version_string() == "1.2.5-SNAPSHOT" - - version.increment(ReleaseType.MINOR) + version = Version([1, 2, 3], ReleaseType.MINOR) + version.increment() assert version.get_version_string() == "1.3.0" + assert version.version_list == [1, 3, 0] + assert not version.is_snapshot - version.increment(ReleaseType.MAJOR) + version = Version([1, 2, 3], ReleaseType.MAJOR) + version.increment() assert version.get_version_string() == "2.0.0" + assert version.version_list == [2, 0, 0] + assert not version.is_snapshot def test_gradle(tmp_path): @@ -61,10 +64,10 @@ def test_gradle(tmp_path): f.write_text(contents) # test - version_repo = VersionRepository.get(f) - version_list = version_repo.version_list - version = Version.create_release_version(version_list, ReleaseType.SNAPSHOT) - version_repo.write_file(version.get_version_string()) + repo = VersionRepository(f) + version = repo.get_version(ReleaseType.SNAPSHOT) + version.create_release_version() + repo.write_file(version.get_version_string()) # check assert 'version = "12.4.678-SNAPSHOT"' in f.read_text() @@ -79,10 +82,10 @@ def test_json(tmp_path): f.write_text(contents) # test - version_repo = VersionRepository.get(f) - version_list = version_repo.version_list - version = Version.create_release_version(version_list, ReleaseType.SNAPSHOT) - version_repo.write_file(version.get_version_string()) + repo = VersionRepository(f) + version = repo.get_version(ReleaseType.SNAPSHOT) + version.create_release_version() + repo.write_file(version.get_version_string()) # check assert '"version": "123.123.456-SNAPSHOT"' in f.read_text() @@ -97,10 +100,10 @@ def test_clojure(tmp_path): f.write_text(contents) # test - version_repo = VersionRepository.get(f) - version_list = version_repo.version_list - version = Version.create_release_version(version_list, ReleaseType.SNAPSHOT) - version_repo.write_file(version.get_version_string()) + repo = VersionRepository(f) + version = repo.get_version(ReleaseType.SNAPSHOT) + version.create_release_version() + repo.write_file(version.get_version_string()) # check assert '1.1.3-SNAPSHOT' in f.read_text() @@ -115,10 +118,10 @@ def test_python(tmp_path): f.write_text(contents) # test - version_repo = VersionRepository.get(f) - version_list = version_repo.version_list - version = Version.create_release_version(version_list, ReleaseType.SNAPSHOT) - version_repo.write_file(version.get_version_string()) + repo = VersionRepository(f) + version = repo.get_version(ReleaseType.SNAPSHOT) + version.create_release_version() + repo.write_file(version.get_version_string()) # check assert '3.1.3-SNAPSHOT' in f.read_text() \ No newline at end of file diff --git a/version.py b/version.py index 59a6c5c..6439195 100644 --- a/version.py +++ b/version.py @@ -7,7 +7,7 @@ class Version(): self.version_list = version_list self.release_type = release_type self.version_string = None - self.is_snapshot = is_snapshot + self.is_snapshot = None def increment(self): self.is_snapshot = False @@ -26,7 +26,7 @@ class Version(): self.version_list[ReleaseType.PATCH.value] = 0 self.version_list[ReleaseType.MINOR.value] = 0 self.version_list[ReleaseType.MAJOR.value] += 1 - case None + case None: raise Exception("Release Type was not set!") def get_version_string(self) -> str: @@ -39,7 +39,7 @@ class Version(): if self.release_type == ReleaseType.PATCH: self.is_snapshot = False else: - self.increment(release_type) + self.increment() def create_bump_version(self): self.increment(ReleaseType.BUMP) diff --git a/version_repository.py b/version_repository.py index a5bf519..b135b79 100644 --- a/version_repository.py +++ b/version_repository.py @@ -9,23 +9,24 @@ class VersionRepository(): def load_file(self): self.file_handler = FileHandler.from_file_path(self.file) - return file_handler + return self.file_handler def write_file(self, version_string): if self.file_handler is None: - raise Exception('Version was not created by load_file method.') - if self.version_list is None or self.is_snapshot is None: - raise Exception('Version or is_snapshot attribute not set.') + raise Exception('Version was not created by load_file method.') else: self.file_handler.write(version_string) - def parse_file(self, file_handler): - version_list, is_snapshot = file_handler.parse() + def parse_file(self): + version_list, is_snapshot = self.file_handler.parse() return version_list, is_snapshot def get_version(self, release_type): - self.file_handler = self.load_file(self.file) - version_list, is_snapshot = self.parse_file(file_handler) + self.file_handler = self.load_file() + version_list, is_snapshot = self.parse_file() + version = Version(version_list, release_type) + version.is_snapshot = is_snapshot + + return version - return Version(version_list, release_type) From 72e9add5aff01c9c2f7b15382c7d4d420cc52b5f Mon Sep 17 00:00:00 2001 From: bom Date: Wed, 22 Feb 2023 09:58:57 +0100 Subject: [PATCH 050/119] Add system repository for command line handling --- system_repository.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 system_repository.py diff --git a/system_repository.py b/system_repository.py new file mode 100644 index 0000000..18f5893 --- /dev/null +++ b/system_repository.py @@ -0,0 +1,22 @@ +import subprocess as sub + +class SystemRepository(): + + def __init__(self): + self.stdout = [""] + self.stderr = [""] + + def run(self, *args): + stream = sub.Popen(args, + stdout=sub.PIPE, + stderr=sub.PIPE, + text=True, + encoding="UTF-8") + self.stdout = stream.stdout.readlines() + self.stderr = stream.stderr.readlines() + + def run_checked(self, *args): + self.run(args) + + if len(self.stderr) > 0: + raise Exception(f"Command failed with: {self.stderr}") From 0f4899bf39782ca857403ec0142ec2e8db6eed7b Mon Sep 17 00:00:00 2001 From: bom Date: Wed, 22 Feb 2023 09:59:16 +0100 Subject: [PATCH 051/119] Fix typos in services.py --- services.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/services.py b/services.py index 3792d41..542a2ba 100644 --- a/services.py +++ b/services.py @@ -1,5 +1,6 @@ -import version_repository -import release_type +from version_repository import VersionRepository +from release_type import ReleaseType + class InitReleaseService(): def __init__(self, commit_id, file): @@ -15,7 +16,7 @@ class InitReleaseService(): def get_version(self, release_type): self.version_repo = VersionRepository(self.file) - return repo.get_version(release_type) + return self.version_repo.get_version(release_type) def create_release_version(self): commit_message = self.read_commit_message(self.commit_id) From 0b3daf595a36346bd25e6f9d80defb634be9babe Mon Sep 17 00:00:00 2001 From: bom Date: Wed, 22 Feb 2023 09:59:35 +0100 Subject: [PATCH 052/119] WIP rework git handler --- git_handler.py | 69 ++++++++++---------------------------------------- 1 file changed, 13 insertions(+), 56 deletions(-) diff --git a/git_handler.py b/git_handler.py index 3f30551..95d2915 100644 --- a/git_handler.py +++ b/git_handler.py @@ -1,5 +1,7 @@ import os import subprocess as sub +from system_repository import SystemRepository +from release_type import ReleaseType # define semantics for release types by commit messages ## snapshot - snapshot release @@ -14,64 +16,19 @@ FORMAT = '"%h %s"' FORMAT_DEC = "%d" PRETTY_OPTION = '--pretty=' DECORATE_OPTION = '--decorate=full' - + + +# git log --oneline --format="%s %b" origin/master...HEAD + class GitRepo(): def __init__(self): - self.commits = None - self.tags = None + self.latest_commit = None + self.system_repository = SystemRepository() - def __clean_commit_string(self, commit_string): - return commit_string.replace('\"', "").replace('\n', "").split() + def get_latest_commit(self): + self.latest_commit = self.system_repository.run_checked('git', 'log', '--oneline', '--format="%s %b"', '-n' + '1') - def __clean_tag_string(self, tag_string): - return tag_string.replace(" ", "").replace('\n', "") - - def get_tags(self): - stream = sub.Popen([GIT, - LOG, - PRETTY_OPTION + FORMAT_DEC, - DECORATE_OPTION], - stdout=sub.PIPE, - stderr=sub.PIPE, - text=True, - encoding="UTF-8") - stdout = stream.stdout.readlines() - stderr = stream.stderr.readlines() - - if len(stderr) > 0: - raise Exception(f"Git command failed with: {stderr}") - - return stdout - - def get_commits(self): - stream = sub.Popen([GIT, - LOG, - PRETTY_OPTION + FORMAT], - stdout=sub.PIPE, - stderr=sub.PIPE, - text=True, - encoding="UTF-8") - stdout = stream.stdout.readlines() - stderr = stream.stderr.readlines() - - if len(stderr) > 0: - raise Exception(f"Git command failed with: {stderr}") - - self.tags = self.get_tags() - self.commits = {} - - if len(self.tags) != len(stdout): - raise Exception("Tags list did not match commits list") - - for i, elem in enumerate(stdout): - commit_string = self.__clean_commit_string(elem) - - commit_id = commit_string[0] - commit_message = " ".join(commit_string[1:len(commit_string)]) - commit_tag = self.__clean_tag_string(self.tags[i]) - - self.commits[commit_id] = [commit_tag, commit_message] - - return self.commits - + def get_release_type_from_latest_commit(self): + if self.latest_commit is None: + self.get_latest_commit() From ac564ade7ecc55012b28ce9d9ece7166ef2032be Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 22 Feb 2023 10:25:21 +0100 Subject: [PATCH 053/119] Update tests for git handler --- test/test_git_handler.py | 46 ++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/test/test_git_handler.py b/test/test_git_handler.py index 77d95c9..5623bc1 100644 --- a/test/test_git_handler.py +++ b/test/test_git_handler.py @@ -17,14 +17,48 @@ sys.path.append(parent) # now we can import the module in the parent # directory. -from git_handler import * +from git_repository import * +from release_type import ReleaseType -def test_git_handler(): +def test_git_repository(): # init - repo = GitRepo() - repo.get_commits() + commit_string = "Major bla" + repo = GitRepository.create_from_commit_string(commit_string) + release_type = repo.get_release_type_from_latest_commit() + + #test + assert release_type == ReleaseType.MAJOR + + + # init + commit_string = "MINOR bla" + repo = GitRepository.create_from_commit_string(commit_string) + release_type = repo.get_release_type_from_latest_commit() #test - assert type(repo.commits) == dict - assert repo.commits["decd36b"] == ["(tag:refs/tags/TEST)", "Initial commit"] + assert release_type == ReleaseType.MINOR + + # init + commit_string = "PATCH bla" + repo = GitRepository.create_from_commit_string(commit_string) + release_type = repo.get_release_type_from_latest_commit() + + # test + assert release_type == ReleaseType.PATCH + + # init + commit_string = "SNAPSHOT bla" + repo = GitRepository.create_from_commit_string(commit_string) + release_type = repo.get_release_type_from_latest_commit() + + #test + assert release_type == ReleaseType.SNAPSHOT + + # init + commit_string = "bla" + repo = GitRepository.create_from_commit_string(commit_string) + release_type = repo.get_release_type_from_latest_commit() + + #test + assert release_type == None From ff4cb702669b8aed14e2c840bcc0376bb463a114 Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 22 Feb 2023 10:26:25 +0100 Subject: [PATCH 054/119] Rename for consitency --- test/{test_git_handler.py => test_git_repository.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/{test_git_handler.py => test_git_repository.py} (100%) diff --git a/test/test_git_handler.py b/test/test_git_repository.py similarity index 100% rename from test/test_git_handler.py rename to test/test_git_repository.py From 33fdf3073a236e477f9a752b6b7f215749dddcaf Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 22 Feb 2023 10:26:49 +0100 Subject: [PATCH 055/119] Rename for consitency --- git_handler.py | 34 ---------------------------------- git_repository.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 34 deletions(-) delete mode 100644 git_handler.py create mode 100644 git_repository.py diff --git a/git_handler.py b/git_handler.py deleted file mode 100644 index 95d2915..0000000 --- a/git_handler.py +++ /dev/null @@ -1,34 +0,0 @@ -import os -import subprocess as sub -from system_repository import SystemRepository -from release_type import ReleaseType - -# define semantics for release types by commit messages -## snapshot - snapshot release -## fix/patch - patch release -## bump - version bump release -## feature/feat/minor - minor release -## major/breaking - major release - -GIT = 'git' -LOG = 'log' -FORMAT = '"%h %s"' -FORMAT_DEC = "%d" -PRETTY_OPTION = '--pretty=' -DECORATE_OPTION = '--decorate=full' - - -# git log --oneline --format="%s %b" origin/master...HEAD - -class GitRepo(): - - def __init__(self): - self.latest_commit = None - self.system_repository = SystemRepository() - - def get_latest_commit(self): - self.latest_commit = self.system_repository.run_checked('git', 'log', '--oneline', '--format="%s %b"', '-n' + '1') - - def get_release_type_from_latest_commit(self): - if self.latest_commit is None: - self.get_latest_commit() diff --git a/git_repository.py b/git_repository.py new file mode 100644 index 0000000..83c9ea5 --- /dev/null +++ b/git_repository.py @@ -0,0 +1,35 @@ +import os +import subprocess as sub +from system_repository import SystemRepository +from release_type import ReleaseType + +class GitRepository(): + + def __init__(self): + self.latest_commit = None + self.system_repository = SystemRepository() + + @classmethod + def create_from_commit_string(cls, commit_string): + inst = cls() + inst.latest_commit = commit_string + return inst + + def get_latest_commit(self): + self.latest_commit = self.system_repository.run_checked('git', 'log', '--oneline', '--format="%s %b"', '-n' + '1') + + def get_release_type_from_latest_commit(self): + if self.latest_commit is None: + self.get_latest_commit() + + if ReleaseType.MAJOR.name in self.latest_commit.upper(): + return ReleaseType.MAJOR + elif ReleaseType.MINOR.name in self.latest_commit.upper(): + return ReleaseType.MINOR + elif ReleaseType.PATCH.name in self.latest_commit.upper(): + return ReleaseType.PATCH + elif ReleaseType.SNAPSHOT.name in self.latest_commit.upper(): + return ReleaseType.SNAPSHOT + else: + return None + From f94d1879dc321a2d1c864901862c92b57ea74722 Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 22 Feb 2023 10:27:07 +0100 Subject: [PATCH 056/119] Implement release type calculation --- services.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/services.py b/services.py index 542a2ba..8da851d 100644 --- a/services.py +++ b/services.py @@ -1,26 +1,24 @@ from version_repository import VersionRepository from release_type import ReleaseType +from git_repository import GitRepository class InitReleaseService(): def __init__(self, commit_id, file): self.commit_id = commit_id self.file = file - self.version_repo = None - - def __read_commit_message(self): - pass + self.version_repo = None def __calculate_release_type(self): - pass - + return GitRepository().get_release_type_from_latest_commit() + def get_version(self, release_type): self.version_repo = VersionRepository(self.file) return self.version_repo.get_version(release_type) def create_release_version(self): commit_message = self.read_commit_message(self.commit_id) - release_type = self.calculate_release_type(commit_message) + release_type = self.__calculate_release_type(commit_message) version = self.get_version(release_type).create_release_version() self.version_repo.write_file(version.get_version_string()) From e578878b82019af7f4ac7811c379a3db116fb8dc Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 22 Feb 2023 10:27:29 +0100 Subject: [PATCH 057/119] WIP test the services --- test/test_services.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/test_services.py diff --git a/test/test_services.py b/test/test_services.py new file mode 100644 index 0000000..e69de29 From 167eec4620fb673dda94e6236625991da3158825 Mon Sep 17 00:00:00 2001 From: bom Date: Wed, 22 Feb 2023 10:57:24 +0100 Subject: [PATCH 058/119] Make services testable --- services.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/services.py b/services.py index 8da851d..37445b2 100644 --- a/services.py +++ b/services.py @@ -4,28 +4,29 @@ from git_repository import GitRepository class InitReleaseService(): - def __init__(self, commit_id, file): - self.commit_id = commit_id + def __init__(self, file): self.file = file self.version_repo = None - def __calculate_release_type(self): - return GitRepository().get_release_type_from_latest_commit() + def __calculate_release_type(self, commit_string = None): + if commit_string is None: + return GitRepository().get_release_type_from_latest_commit() + else: + return GitRepository.create_from_commit_string(commit_string).get_release_type_from_latest_commit() def get_version(self, release_type): self.version_repo = VersionRepository(self.file) return self.version_repo.get_version(release_type) - def create_release_version(self): - commit_message = self.read_commit_message(self.commit_id) - release_type = self.__calculate_release_type(commit_message) + def create_release_version(self, commit_string = None): + release_type = self.__calculate_release_type(commit_string) version = self.get_version(release_type).create_release_version() self.version_repo.write_file(version.get_version_string()) def create_bump_version(self): if self.version_repo == None: - raise Exception('VersionRepo was not created. Did you run create_lease_version()?') + raise Exception('VersionRepo was not created. Did you run create_release_version()?') version = self.get_version(ReleaseType.BUMP).create_bump_version() self.version_repo.write_file(version.get_version_string()) From fea572016106e079a8c548cf2be99fbc6c36cdb2 Mon Sep 17 00:00:00 2001 From: bom Date: Wed, 22 Feb 2023 10:57:42 +0100 Subject: [PATCH 059/119] Return version objects instead of mutating --- version.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/version.py b/version.py index 6439195..61d92f5 100644 --- a/version.py +++ b/version.py @@ -36,10 +36,14 @@ class Version(): return self.version_string def create_release_version(self): - if self.release_type == ReleaseType.PATCH: - self.is_snapshot = False - else: - self.increment() + release_version = Version(self.version_list, self.release_type) + release_version.is_snapshot = self.is_snapshot + release_version.increment() + return release_version def create_bump_version(self): - self.increment(ReleaseType.BUMP) + bump_version = Version(self.version_list, self.release_type) + bump_version.is_snapshot = self.is_snapshot + bump_version.release_type = ReleaseType.BUMP + bump_version.increment() + return bump_version From e2f562aa8c16cd90a86603f06dc34c96f5fc2e75 Mon Sep 17 00:00:00 2001 From: bom Date: Wed, 22 Feb 2023 10:57:55 +0100 Subject: [PATCH 060/119] Remove wildcard import --- test/test_git_repository.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_git_repository.py b/test/test_git_repository.py index 5623bc1..a1e9db8 100644 --- a/test/test_git_repository.py +++ b/test/test_git_repository.py @@ -17,7 +17,7 @@ sys.path.append(parent) # now we can import the module in the parent # directory. -from git_repository import * +from git_repository import GitRepository from release_type import ReleaseType def test_git_repository(): From 9250d366b97655f7477778c95a1bd7153ddef356 Mon Sep 17 00:00:00 2001 From: bom Date: Wed, 22 Feb 2023 10:58:11 +0100 Subject: [PATCH 061/119] Use newly created version object --- test/test_version_class.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_version_class.py b/test/test_version_class.py index 8880488..486a2a7 100644 --- a/test/test_version_class.py +++ b/test/test_version_class.py @@ -66,7 +66,7 @@ def test_gradle(tmp_path): # test repo = VersionRepository(f) version = repo.get_version(ReleaseType.SNAPSHOT) - version.create_release_version() + version = version.create_release_version() repo.write_file(version.get_version_string()) # check @@ -84,7 +84,7 @@ def test_json(tmp_path): # test repo = VersionRepository(f) version = repo.get_version(ReleaseType.SNAPSHOT) - version.create_release_version() + version = version.create_release_version() repo.write_file(version.get_version_string()) # check @@ -102,7 +102,7 @@ def test_clojure(tmp_path): # test repo = VersionRepository(f) version = repo.get_version(ReleaseType.SNAPSHOT) - version.create_release_version() + version = version.create_release_version() repo.write_file(version.get_version_string()) # check @@ -120,7 +120,7 @@ def test_python(tmp_path): # test repo = VersionRepository(f) version = repo.get_version(ReleaseType.SNAPSHOT) - version.create_release_version() + version = version.create_release_version() repo.write_file(version.get_version_string()) # check From 3bd8497522e90901898a68dc092d0e3d527bd4dc Mon Sep 17 00:00:00 2001 From: bom Date: Wed, 22 Feb 2023 10:58:27 +0100 Subject: [PATCH 062/119] WIP Create test_services --- test/test_services.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test/test_services.py b/test/test_services.py index e69de29..085cf90 100644 --- a/test/test_services.py +++ b/test/test_services.py @@ -0,0 +1,39 @@ +from pathlib import Path +import sys +import os + +# getting the name of the directory +# where the this file is present. +current = os.path.dirname(os.path.realpath(__file__)) + +# Getting the parent directory name +# where the current directory is present. +parent = os.path.dirname(current) + +# adding the parent directory to +# the sys.path. +sys.path.append(parent) + +# now we can import the module in the parent +# directory. + +from services import InitReleaseService +from release_type import ReleaseType + +def test_init_release_service(tmp_path): + # init + file_name = 'config.json' + with open(f'test/resources/{file_name}', 'r') as gradle_file: + contents = gradle_file.read() + + f = tmp_path / file_name + f.write_text(contents) + + release_service = InitReleaseService(f) + release_service.create_release_version(commit_string='Release MINOR') + + assert '"version": "123.124.1"' in f.read_text() + + release_service.create_bump_version() + + assert '"version": "123.124.2-SNAPSHOT"' in f.read_text() \ No newline at end of file From 1d2cb7882eac42134e93b56dc2dbcd0ba6d9931e Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 22 Feb 2023 10:59:35 +0100 Subject: [PATCH 063/119] Fix test --- test/test_services.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_services.py b/test/test_services.py index 085cf90..45367f9 100644 --- a/test/test_services.py +++ b/test/test_services.py @@ -32,8 +32,8 @@ def test_init_release_service(tmp_path): release_service = InitReleaseService(f) release_service.create_release_version(commit_string='Release MINOR') - assert '"version": "123.124.1"' in f.read_text() + assert '"version": "123.124.0"' in f.read_text() release_service.create_bump_version() - assert '"version": "123.124.2-SNAPSHOT"' in f.read_text() \ No newline at end of file + assert '"version": "123.124.1-SNAPSHOT"' in f.read_text() \ No newline at end of file From 821e06f82a4d1c38a98c2f4b8d6ec0a9a9273da0 Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 22 Feb 2023 11:33:44 +0100 Subject: [PATCH 064/119] Pass VersionRepo to InitReleaseService Update Tests. --- services.py | 48 +++++++++++++++++++++++++++++-------------- test/test_services.py | 12 ++++++----- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/services.py b/services.py index 37445b2..35573bb 100644 --- a/services.py +++ b/services.py @@ -1,12 +1,14 @@ from version_repository import VersionRepository from release_type import ReleaseType from git_repository import GitRepository +from version import Version class InitReleaseService(): - def __init__(self, file): - self.file = file - self.version_repo = None + def __init__(self, version_repo: VersionRepository): + if version_repo is None: + raise Exception('VersionRepo was not created. Did you run create_release_version()?') + self.version_repo = version_repo def __calculate_release_type(self, commit_string = None): if commit_string is None: @@ -14,27 +16,43 @@ class InitReleaseService(): else: return GitRepository.create_from_commit_string(commit_string).get_release_type_from_latest_commit() - def get_version(self, release_type): - self.version_repo = VersionRepository(self.file) + def get_version(self, release_type): return self.version_repo.get_version(release_type) def create_release_version(self, commit_string = None): release_type = self.__calculate_release_type(commit_string) - version = self.get_version(release_type).create_release_version() - - self.version_repo.write_file(version.get_version_string()) + version = self.get_version(release_type).create_release_version() + return version - def create_bump_version(self): - if self.version_repo == None: - raise Exception('VersionRepo was not created. Did you run create_release_version()?') + def create_bump_version(self): version = self.get_version(ReleaseType.BUMP).create_bump_version() + return version + - self.version_repo.write_file(version.get_version_string()) - - - class PrepareReleaseService(): + + def __init__(self): + self.version_repo + pass + + def run_tests(self): # maybe auto? + pass + + def prepare_release(self, version: Version): + # self.version_repo.write_file(version.get_version_string()) # side effect + pass + + def prepare_bump(self, version: Version): + # self.version_repo.write_file(version.get_version_string()) # side effect + pass + + + # write + + + # add + # commit pass class TagAndPushReleaseService(): diff --git a/test/test_services.py b/test/test_services.py index 45367f9..a5deaa1 100644 --- a/test/test_services.py +++ b/test/test_services.py @@ -19,6 +19,7 @@ sys.path.append(parent) from services import InitReleaseService from release_type import ReleaseType +from version_repository import VersionRepository def test_init_release_service(tmp_path): # init @@ -29,11 +30,12 @@ def test_init_release_service(tmp_path): f = tmp_path / file_name f.write_text(contents) - release_service = InitReleaseService(f) - release_service.create_release_version(commit_string='Release MINOR') + repo = VersionRepository(f) + release_service = InitReleaseService(repo) + version = release_service.create_release_version(commit_string='Release MINOR') - assert '"version": "123.124.0"' in f.read_text() + assert "123.124.0" in version.get_version_string() - release_service.create_bump_version() + version = release_service.create_bump_version() - assert '"version": "123.124.1-SNAPSHOT"' in f.read_text() \ No newline at end of file + assert "123.123.457-SNAPSHOT" in version.get_version_string() \ No newline at end of file From 1112b1ad4902f098feefb8c90221276ced8efacb Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 22 Feb 2023 11:34:27 +0100 Subject: [PATCH 065/119] WIP Implement prepare and tagandpush services --- build.py | 9 +++++++-- release_mixin.py | 18 ++++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/build.py b/build.py index 91d74dc..9e4d198 100644 --- a/build.py +++ b/build.py @@ -21,9 +21,14 @@ def initialize(project): config = create_release_mixin_config(CONFIG_FILE, COMMIT_ID) - build = MyBuild(project, config) - build.init() + build = MyBuild(project, config) + release_version, bump_version = build.init() + build.prepare() + build.tag_and_push() + + + @task diff --git a/release_mixin.py b/release_mixin.py index f83a49f..9b8a5d2 100644 --- a/release_mixin.py +++ b/release_mixin.py @@ -3,6 +3,7 @@ from ddadevops import DevopsBuild from ddadevops import execute from ddadevops import gopass_field_from_path, gopass_password_from_path from version import Version +from version_repository import VersionRepository def create_release_mixin_config(config, release_type, commit, file): @@ -16,14 +17,23 @@ class ReleaseMixin(DevopsBuild): def __init__(self, project, config): super().__init__(project, config) - release_mixin_config = config['ReleaseMixin'] - self.commit_id = release_mixin_config['commit_id'] + release_mixin_config = config['ReleaseMixin'] self.file = release_mixin_config['file'] + self.version_repo = VersionRepository(self.file) - def init(self): - release_and_bump_version = InitReleaseService(self.commit_id, self.file).get_version() + def init(self): # returns versions + release_and_bump_version = InitReleaseService(self.version_repo).get_version() return release_and_bump_version + def prepare(self, version: Version): # writes into files, add. commit + pass + + def tag_and_push(): # correct tag and do push + pass + + + + From ee8b49f078eb200f5b4127193b9f321de3fd2705 Mon Sep 17 00:00:00 2001 From: bom Date: Wed, 22 Feb 2023 12:04:46 +0100 Subject: [PATCH 066/119] Add some useful git functions --- git_repository.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/git_repository.py b/git_repository.py index 83c9ea5..f891646 100644 --- a/git_repository.py +++ b/git_repository.py @@ -1,5 +1,6 @@ import os import subprocess as sub +from pathlib import Path from system_repository import SystemRepository from release_type import ReleaseType @@ -33,3 +34,18 @@ class GitRepository(): else: return None + def get_current_branch(self): + self.system_repository.run_checked('git', 'branch', '--show-current') + + def add_file(self, file_path: Path): + self.system_repository.run_checked('git', 'add', file_path) + + def commit(self, commit_message: str): + self.system_repository.run_checked('git', 'commit', '-m', commit_message) + + def push(self): + self.system_repository.run_checked('git', 'push') + + def checkout(self, branch: str): + self.system_repository.run_checked('git', 'checkout', branch) + From 2d8ca4ecddbac8732c042089ca7c00e3fc60361e Mon Sep 17 00:00:00 2001 From: bom Date: Wed, 22 Feb 2023 12:05:02 +0100 Subject: [PATCH 067/119] Fix test_services --- test/test_services.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_services.py b/test/test_services.py index a5deaa1..f7ec74c 100644 --- a/test/test_services.py +++ b/test/test_services.py @@ -36,6 +36,6 @@ def test_init_release_service(tmp_path): assert "123.124.0" in version.get_version_string() - version = release_service.create_bump_version() + version = version.create_bump_version() - assert "123.123.457-SNAPSHOT" in version.get_version_string() \ No newline at end of file + assert "123.124.1-SNAPSHOT" in version.get_version_string() \ No newline at end of file From 238b0304b66336ce7f052d7163f08551b8eacf4c Mon Sep 17 00:00:00 2001 From: bom Date: Wed, 22 Feb 2023 12:05:34 +0100 Subject: [PATCH 068/119] Implement prepare for ReleaseMixin --- release_mixin.py | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/release_mixin.py b/release_mixin.py index 9b8a5d2..c78ec1f 100644 --- a/release_mixin.py +++ b/release_mixin.py @@ -1,15 +1,15 @@ - from ddadevops import DevopsBuild from ddadevops import execute from ddadevops import gopass_field_from_path, gopass_password_from_path from version import Version from version_repository import VersionRepository +from services import InitReleaseService, PrepareReleaseService, TagAndPushReleaseService +from git_repository import GitRepository - -def create_release_mixin_config(config, release_type, commit, file): - config.update({'ReleaseMixin': - {'commit_id': commit, - 'file': file}}) +def create_release_mixin_config(config, release_type, config_file, main_branch): + config.update({'ReleaseMixin': + {'main_branch': main_branch, + 'file': config_file}}) return config @@ -19,14 +19,28 @@ class ReleaseMixin(DevopsBuild): super().__init__(project, config) release_mixin_config = config['ReleaseMixin'] self.file = release_mixin_config['file'] + self.main_branch = release_mixin_config['main_branch'] self.version_repo = VersionRepository(self.file) def init(self): # returns versions - release_and_bump_version = InitReleaseService(self.version_repo).get_version() - return release_and_bump_version + init_service = InitReleaseService(self.version_repo) + release_version = init_service.create_release_version() + bump_version = release_version.create_bump_version() + return release_version, bump_version - def prepare(self, version: Version): # writes into files, add. commit - pass + def prepare(self, release_version: Version, bump_version: Version): # writes into files, add. commit + git_repository = GitRepository() + if self.main_branch not in git_repository.get_current_branch(): + raise Exception('Trying to release while not on main branch') + + self.version_repo.write_file(release_version.get_version_string()) + git_repository.add_file(self.file) + git_repository.commit(f'Release {release_version.get_version_string()}') + + self.version_repo.write_file(bump_version.get_version_string()) + git_repository.add_file(self.file) + git_repository.commit(f'Version bump {bump_version.get_version_string()}') + def tag_and_push(): # correct tag and do push pass From 03f8507968499c7ea19e4f86a4e9fbe1a9115230 Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 22 Feb 2023 14:10:19 +0100 Subject: [PATCH 069/119] Setup build.py --- build.py | 54 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/build.py b/build.py index 9e4d198..af78598 100644 --- a/build.py +++ b/build.py @@ -4,40 +4,42 @@ from version import * from release_mixin import * CONFIG_FILE = '' -COMMIT_ID = '' +MAIN_BRANCH = 'main' class MyBuild(ReleaseMixin): pass -def main(): - init_project() - @init def initialize(project): project.build_depends_on('ddadevops>=3.1.2') - - if COMMIT_ID == '': - COMMIT_ID = 'HEAD' - - config = create_release_mixin_config(CONFIG_FILE, COMMIT_ID) - - - build = MyBuild(project, config) - release_version, bump_version = build.init() - build.prepare() - build.tag_and_push() - - - - + config = create_release_mixin_config(CONFIG_FILE, MAIN_BRANCH) + build = MyBuild(project, config) + build.init() @task -def prepare(project): +def release(project): build = get_devops_build(project) - build.prepare_release() -@task -def tag_and_push(project): - build = get_devops_build(project) - build.tag_and_push() - + prepare_release(build) + tag_and_push_release(build) + + prepare_version_bump(build) + tag_and_push_version_bump(build) + +def prepare_release(build): + release_version = build.release_version + build.prepare(release_version) + +def tag_and_push_release(build): + release_version = build.release_version + build.tag_and_push(release_version) + +def prepare_version_bump(build): + bump_version = build.bump_version + build.prepare(bump_version) + +def tag_and_push_version_bump(build): + bump_version = build.bump_version + build.tag_and_push(bump_version) + + From a07c23434e506e20634e9723a5ceb3f8cb049b35 Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 22 Feb 2023 14:10:53 +0100 Subject: [PATCH 070/119] Implement tag_annotated and bugfix --- git_repository.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/git_repository.py b/git_repository.py index f891646..0622e9f 100644 --- a/git_repository.py +++ b/git_repository.py @@ -17,7 +17,8 @@ class GitRepository(): return inst def get_latest_commit(self): - self.latest_commit = self.system_repository.run_checked('git', 'log', '--oneline', '--format="%s %b"', '-n' + '1') + self.system_repository.run_checked('git', 'log', '--oneline', '--format="%s %b"', '-n' + '1') + self.latest_commit = self.system_repository.stdout def get_release_type_from_latest_commit(self): if self.latest_commit is None: @@ -34,6 +35,9 @@ class GitRepository(): else: return None + def tag_annotated(self, annotation: str, message: str): + self.system_repository.run_checked('git', 'tag', '-a', annotation, '-m', message) + def get_current_branch(self): self.system_repository.run_checked('git', 'branch', '--show-current') @@ -48,4 +52,3 @@ class GitRepository(): def checkout(self, branch: str): self.system_repository.run_checked('git', 'checkout', branch) - From 7d7347653e1ed1e9aa70f9784cfb9217bf833203 Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 22 Feb 2023 14:11:39 +0100 Subject: [PATCH 071/119] Update release_mixin methods --- release_mixin.py | 73 ++++++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/release_mixin.py b/release_mixin.py index c78ec1f..b1a6d8f 100644 --- a/release_mixin.py +++ b/release_mixin.py @@ -6,12 +6,19 @@ from version_repository import VersionRepository from services import InitReleaseService, PrepareReleaseService, TagAndPushReleaseService from git_repository import GitRepository -def create_release_mixin_config(config, release_type, config_file, main_branch): - config.update({'ReleaseMixin': - {'main_branch': main_branch, - 'file': config_file}}) +def create_release_mixin_config(config_file, main_branch) -> dict: + config = {} + config.update( + {'ReleaseMixin': + {'main_branch': main_branch, + 'file': config_file}}) return config +def add_versions(config, release_version, bump_version) -> dict: + config['ReleaseMixin'].update( + {'release_version': release_version, + 'bump_version': bump_version}) + return config class ReleaseMixin(DevopsBuild): @@ -21,40 +28,40 @@ class ReleaseMixin(DevopsBuild): self.file = release_mixin_config['file'] self.main_branch = release_mixin_config['main_branch'] self.version_repo = VersionRepository(self.file) + self.release_version = None + self.bump_version = None - def init(self): # returns versions + def init(self): init_service = InitReleaseService(self.version_repo) - release_version = init_service.create_release_version() - bump_version = release_version.create_bump_version() - return release_version, bump_version + self.release_version = init_service.create_release_version() + self.bump_version = self.release_version.create_bump_version() + return self.release_version, self.bump_version - def prepare(self, release_version: Version, bump_version: Version): # writes into files, add. commit + def prepare(self, version): git_repository = GitRepository() if self.main_branch not in git_repository.get_current_branch(): raise Exception('Trying to release while not on main branch') - - self.version_repo.write_file(release_version.get_version_string()) - git_repository.add_file(self.file) - git_repository.commit(f'Release {release_version.get_version_string()}') - - self.version_repo.write_file(bump_version.get_version_string()) - git_repository.add_file(self.file) - git_repository.commit(f'Version bump {bump_version.get_version_string()}') + self.version_repo.write_file(version.get_version_string()) + git_repository.add_file(self.file) + match version.release_type: + case None: + raise Exception('Release type not set but trying to commit.') + case ReleaseType.BUMP: + git_repository.commit(f'Version bump') + case _: + git_repository.commit(f'Release {version.get_version_string()}') - def tag_and_push(): # correct tag and do push - pass - - - - - - - - - - - - - - + def tag_and_push(self, version): + git_repository = GitRepository() + match version.release_type: + case None: + raise Exception('Release type not set but trying to tag and push.') + case ReleaseType.BUMP: + annotation = 'v' + version.get_version_string() + message = 'Version bump' + case _: + annotation = 'v' + self.release_version.get_version_string() + message = 'Release' + annotation + git_repository.tag_annotated(annotation, message) + git_repository.push() From 0d0bde4b9e05dd83b58f1eaa2032dcf707c608f3 Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 22 Feb 2023 14:12:03 +0100 Subject: [PATCH 072/119] Remove whitespaces --- system_repository.py | 2 +- test/test_version_class.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/system_repository.py b/system_repository.py index 18f5893..31c9f78 100644 --- a/system_repository.py +++ b/system_repository.py @@ -5,7 +5,7 @@ class SystemRepository(): def __init__(self): self.stdout = [""] self.stderr = [""] - + def run(self, *args): stream = sub.Popen(args, stdout=sub.PIPE, diff --git a/test/test_version_class.py b/test/test_version_class.py index 486a2a7..24c9d33 100644 --- a/test/test_version_class.py +++ b/test/test_version_class.py @@ -5,15 +5,15 @@ import os # getting the name of the directory # where the this file is present. current = os.path.dirname(os.path.realpath(__file__)) - + # Getting the parent directory name # where the current directory is present. parent = os.path.dirname(current) - + # adding the parent directory to # the sys.path. sys.path.append(parent) - + # now we can import the module in the parent # directory. From 1e2593e35853f87db4bc4af0ffaae5a6e4448ced Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 22 Feb 2023 14:12:54 +0100 Subject: [PATCH 073/119] No settings in repo --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7f3a111..6b02ccb 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,7 @@ go.work __pycache__ .clj-kondo/ -.lsp/ \ No newline at end of file +.lsp/ + +# vs code settings +.vscode \ No newline at end of file From d22dbbf050a4dac6e525c7562bc3d60d374f4b00 Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 22 Feb 2023 15:14:48 +0100 Subject: [PATCH 074/119] Add import, remove return --- release_mixin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release_mixin.py b/release_mixin.py index b1a6d8f..85c63c7 100644 --- a/release_mixin.py +++ b/release_mixin.py @@ -5,6 +5,7 @@ from version import Version from version_repository import VersionRepository from services import InitReleaseService, PrepareReleaseService, TagAndPushReleaseService from git_repository import GitRepository +from release_type import ReleaseType def create_release_mixin_config(config_file, main_branch) -> dict: config = {} @@ -35,7 +36,6 @@ class ReleaseMixin(DevopsBuild): init_service = InitReleaseService(self.version_repo) self.release_version = init_service.create_release_version() self.bump_version = self.release_version.create_bump_version() - return self.release_version, self.bump_version def prepare(self, version): git_repository = GitRepository() From 294d664bb8d24a250daf3060cd94b18148002a8d Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 22 Feb 2023 15:15:23 +0100 Subject: [PATCH 075/119] WIP Add test for release_mixin --- test/test_release_mixin.py | 56 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 test/test_release_mixin.py diff --git a/test/test_release_mixin.py b/test/test_release_mixin.py new file mode 100644 index 0000000..601f3fd --- /dev/null +++ b/test/test_release_mixin.py @@ -0,0 +1,56 @@ +import sys +import os +from pathlib import Path +from ddadevops import * + +# getting the name of the directory +# where the this file is present. +current = os.path.dirname(os.path.realpath(__file__)) + +# Getting the parent directory name +# where the current directory is present. +parent = os.path.dirname(current) + +# adding the parent directory to +# the sys.path. +sys.path.append(parent) + +# now we can import the module in the parent +# directory. + +from pybuilder.core import task, init, Project +from version import * +from release_mixin import ReleaseMixin, create_release_mixin_config + +MAIN_BRANCH = 'main' + + +class MyBuild(ReleaseMixin): + pass + +def initialize(project, CONFIG_FILE): + project.build_depends_on('ddadevops>=3.1.2') + config = create_release_mixin_config(CONFIG_FILE, MAIN_BRANCH) + build = MyBuild(project, config) + return build + +def test_release_mixin(tmp_path): + + with open(f'test/resources/config.json', 'r') as json_file: + contents = json_file.read() + + CONFIG_FILE = tmp_path / "config.json" + CONFIG_FILE.write_text(contents) + + base_dir = "." + project = Project(base_dir) + + # init + build = initialize(project, CONFIG_FILE) + build.init() + release_version = build.release_version + bump_version = build.bump_version + + # test + assert "123.124.0" in release_version.get_version_string() + assert "123.124.1-SNAPSHOT" in bump_version.get_version_string() \ No newline at end of file From 99b8884d1120fb349845b2f85059f3c473e6708a Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 22 Feb 2023 15:15:56 +0100 Subject: [PATCH 076/119] WIP implement prepare release service --- services.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services.py b/services.py index 35573bb..36c2ff1 100644 --- a/services.py +++ b/services.py @@ -32,9 +32,9 @@ class InitReleaseService(): class PrepareReleaseService(): - def __init__(self): - self.version_repo - pass + def __init__(self, version_repo: VersionRepository): + self.version_repo = version_repo + def run_tests(self): # maybe auto? pass From d759036989ea180aac4572bfc6a64b2e85bfb255 Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 22 Feb 2023 16:19:36 +0100 Subject: [PATCH 077/119] Create realistic test conditions --- test/test_release_mixin.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/test/test_release_mixin.py b/test/test_release_mixin.py index 601f3fd..e83ad85 100644 --- a/test/test_release_mixin.py +++ b/test/test_release_mixin.py @@ -23,7 +23,10 @@ from version import * from release_mixin import ReleaseMixin, create_release_mixin_config MAIN_BRANCH = 'main' - +STAGE = 'test' +PROJECT_ROOT_PATH = '.' +MODULE = 'test' +BUILD_DIR_NAME = "build_dir" class MyBuild(ReleaseMixin): pass @@ -31,11 +34,16 @@ class MyBuild(ReleaseMixin): def initialize(project, CONFIG_FILE): project.build_depends_on('ddadevops>=3.1.2') config = create_release_mixin_config(CONFIG_FILE, MAIN_BRANCH) + config.update({'stage': STAGE}) + config.update({'module': MODULE}) + config.update({'project_root_path': PROJECT_ROOT_PATH}) + config.update({'build_dir_name': BUILD_DIR_NAME}) build = MyBuild(project, config) return build def test_release_mixin(tmp_path): + #init with open(f'test/resources/config.json', 'r') as json_file: contents = json_file.read() @@ -46,11 +54,16 @@ def test_release_mixin(tmp_path): project = Project(base_dir) # init - build = initialize(project, CONFIG_FILE) + build = initialize(project, CONFIG_FILE) + build.commit_string = "MAJOR bla" build.init() release_version = build.release_version + + # test + assert "124.0.0" in release_version.get_version_string() + + # init bump_version = build.bump_version # test - assert "123.124.0" in release_version.get_version_string() - assert "123.124.1-SNAPSHOT" in bump_version.get_version_string() \ No newline at end of file + assert "124.0.1-SNAPSHOT" in bump_version.get_version_string() \ No newline at end of file From c91bbe787b365d0d7fb9813d7b8c062458442f03 Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 22 Feb 2023 16:20:46 +0100 Subject: [PATCH 078/119] Create string from return value stdout returns a list of strings. This needed to be handled. --- git_repository.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git_repository.py b/git_repository.py index 0622e9f..4fdfc64 100644 --- a/git_repository.py +++ b/git_repository.py @@ -17,8 +17,8 @@ class GitRepository(): return inst def get_latest_commit(self): - self.system_repository.run_checked('git', 'log', '--oneline', '--format="%s %b"', '-n' + '1') - self.latest_commit = self.system_repository.stdout + self.system_repository.run_checked('git', 'log', '--oneline', '--format="%s %b"', '-n 1') + self.latest_commit = " ".join(self.system_repository.stdout) # returns a list of strings otherwise def get_release_type_from_latest_commit(self): if self.latest_commit is None: From fa73c52fe2033bc96115e087f48b7c6f782f7611 Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 22 Feb 2023 16:23:03 +0100 Subject: [PATCH 079/119] Remove asterisk on destructured args Otherwise we would create a tuple containing a tuple of arguments and an empty tuple. --- system_repository.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system_repository.py b/system_repository.py index 31c9f78..914276f 100644 --- a/system_repository.py +++ b/system_repository.py @@ -6,7 +6,7 @@ class SystemRepository(): self.stdout = [""] self.stderr = [""] - def run(self, *args): + def run(self, args): stream = sub.Popen(args, stdout=sub.PIPE, stderr=sub.PIPE, From 2fe05b0e06342cea2af501e1c0755bde0d2f2506 Mon Sep 17 00:00:00 2001 From: erik Date: Wed, 22 Feb 2023 16:25:18 +0100 Subject: [PATCH 080/119] Use deep copy of release_version object Release version is modified again, when calling create_bump_version on it. --- release_mixin.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/release_mixin.py b/release_mixin.py index 85c63c7..48afeaf 100644 --- a/release_mixin.py +++ b/release_mixin.py @@ -1,3 +1,4 @@ +import copy from ddadevops import DevopsBuild from ddadevops import execute from ddadevops import gopass_field_from_path, gopass_password_from_path @@ -31,11 +32,13 @@ class ReleaseMixin(DevopsBuild): self.version_repo = VersionRepository(self.file) self.release_version = None self.bump_version = None + self.commit_string = None def init(self): init_service = InitReleaseService(self.version_repo) - self.release_version = init_service.create_release_version() - self.bump_version = self.release_version.create_bump_version() + self.release_version = init_service.create_release_version(self.commit_string) + release_version_copy = copy.deepcopy(self.release_version) # otherwise we'll modify the release_version again + self.bump_version = release_version_copy.create_bump_version() def prepare(self, version): git_repository = GitRepository() From 0df4db54d818c65cc496174a07d308be71b8fef6 Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 23 Feb 2023 14:16:17 +0100 Subject: [PATCH 081/119] Copy the version list when creating new Version --- release_mixin.py | 3 +-- version.py | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/release_mixin.py b/release_mixin.py index 48afeaf..fbf5756 100644 --- a/release_mixin.py +++ b/release_mixin.py @@ -37,8 +37,7 @@ class ReleaseMixin(DevopsBuild): def init(self): init_service = InitReleaseService(self.version_repo) self.release_version = init_service.create_release_version(self.commit_string) - release_version_copy = copy.deepcopy(self.release_version) # otherwise we'll modify the release_version again - self.bump_version = release_version_copy.create_bump_version() + self.bump_version = self.release_version.create_bump_version() def prepare(self, version): git_repository = GitRepository() diff --git a/version.py b/version.py index 61d92f5..7dea4f7 100644 --- a/version.py +++ b/version.py @@ -36,14 +36,13 @@ class Version(): return self.version_string def create_release_version(self): - release_version = Version(self.version_list, self.release_type) + release_version = Version(self.version_list.copy(), self.release_type) release_version.is_snapshot = self.is_snapshot release_version.increment() return release_version def create_bump_version(self): - bump_version = Version(self.version_list, self.release_type) + bump_version = Version(self.version_list.copy(), ReleaseType.BUMP) bump_version.is_snapshot = self.is_snapshot - bump_version.release_type = ReleaseType.BUMP bump_version.increment() return bump_version From 992c72bda3308eb2be9761e31a17a1ae3f81d616 Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 23 Feb 2023 14:23:32 +0100 Subject: [PATCH 082/119] Give 'file' a more descriptive name --- release_mixin.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/release_mixin.py b/release_mixin.py index fbf5756..4b8dc38 100644 --- a/release_mixin.py +++ b/release_mixin.py @@ -13,7 +13,7 @@ def create_release_mixin_config(config_file, main_branch) -> dict: config.update( {'ReleaseMixin': {'main_branch': main_branch, - 'file': config_file}}) + 'config_file': config_file}}) return config def add_versions(config, release_version, bump_version) -> dict: @@ -27,9 +27,9 @@ class ReleaseMixin(DevopsBuild): def __init__(self, project, config): super().__init__(project, config) release_mixin_config = config['ReleaseMixin'] - self.file = release_mixin_config['file'] + self.config_file = release_mixin_config['config_file'] self.main_branch = release_mixin_config['main_branch'] - self.version_repo = VersionRepository(self.file) + self.version_repo = VersionRepository(self.config_file) self.release_version = None self.bump_version = None self.commit_string = None @@ -45,7 +45,7 @@ class ReleaseMixin(DevopsBuild): raise Exception('Trying to release while not on main branch') self.version_repo.write_file(version.get_version_string()) - git_repository.add_file(self.file) + git_repository.add_file(self.config_file) match version.release_type: case None: raise Exception('Release type not set but trying to commit.') From 1ce4bd9c55c2d888665dc9c05fc9df931f3ded80 Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 23 Feb 2023 14:38:54 +0100 Subject: [PATCH 083/119] Remove release_type as requirement for Version Require it in functions that actually use it instead --- services.py | 15 +++++++-------- test/test_version_class.py | 36 ++++++++++++++++++------------------ version.py | 17 ++++++++--------- version_repository.py | 4 ++-- 4 files changed, 35 insertions(+), 37 deletions(-) diff --git a/services.py b/services.py index 36c2ff1..212f016 100644 --- a/services.py +++ b/services.py @@ -5,7 +5,7 @@ from version import Version class InitReleaseService(): - def __init__(self, version_repo: VersionRepository): + def __init__(self, version_repo: VersionRepository): if version_repo is None: raise Exception('VersionRepo was not created. Did you run create_release_version()?') self.version_repo = version_repo @@ -16,25 +16,24 @@ class InitReleaseService(): else: return GitRepository.create_from_commit_string(commit_string).get_release_type_from_latest_commit() - def get_version(self, release_type): - return self.version_repo.get_version(release_type) + def get_version(self): + return self.version_repo.get_version() def create_release_version(self, commit_string = None): release_type = self.__calculate_release_type(commit_string) - version = self.get_version(release_type).create_release_version() + version = self.get_version().create_release_version(release_type) return version - def create_bump_version(self): - version = self.get_version(ReleaseType.BUMP).create_bump_version() + def create_bump_version(self): + version = self.get_version().create_bump_version() return version - class PrepareReleaseService(): def __init__(self, version_repo: VersionRepository): self.version_repo = version_repo - + def run_tests(self): # maybe auto? pass diff --git a/test/test_version_class.py b/test/test_version_class.py index 24c9d33..4f1ac6b 100644 --- a/test/test_version_class.py +++ b/test/test_version_class.py @@ -22,33 +22,33 @@ from version_repository import VersionRepository from release_type import ReleaseType def test_version(): - version = Version([1, 2, 3], ReleaseType.SNAPSHOT) + version = Version([1, 2, 3]) - version.increment() + version.increment(ReleaseType.SNAPSHOT) assert version.get_version_string() == "1.2.3-SNAPSHOT" assert version.version_list == [1, 2, 3] assert version.is_snapshot - version = Version([1, 2, 3], ReleaseType.BUMP) - version.increment() + version = Version([1, 2, 3]) + version.increment(ReleaseType.BUMP) assert version.get_version_string() == "1.2.4-SNAPSHOT" assert version.version_list == [1, 2, 4] assert version.is_snapshot - version = Version([1, 2, 3], ReleaseType.PATCH) - version.increment() + version = Version([1, 2, 3]) + version.increment(ReleaseType.PATCH) assert version.get_version_string() == "1.2.4" assert version.version_list == [1, 2, 4] assert not version.is_snapshot - version = Version([1, 2, 3], ReleaseType.MINOR) - version.increment() + version = Version([1, 2, 3]) + version.increment(ReleaseType.MINOR) assert version.get_version_string() == "1.3.0" assert version.version_list == [1, 3, 0] assert not version.is_snapshot - version = Version([1, 2, 3], ReleaseType.MAJOR) - version.increment() + version = Version([1, 2, 3]) + version.increment(ReleaseType.MAJOR) assert version.get_version_string() == "2.0.0" assert version.version_list == [2, 0, 0] assert not version.is_snapshot @@ -65,8 +65,8 @@ def test_gradle(tmp_path): # test repo = VersionRepository(f) - version = repo.get_version(ReleaseType.SNAPSHOT) - version = version.create_release_version() + version = repo.get_version() + version = version.create_release_version(ReleaseType.SNAPSHOT) repo.write_file(version.get_version_string()) # check @@ -83,8 +83,8 @@ def test_json(tmp_path): # test repo = VersionRepository(f) - version = repo.get_version(ReleaseType.SNAPSHOT) - version = version.create_release_version() + version = repo.get_version() + version = version.create_release_version(ReleaseType.SNAPSHOT) repo.write_file(version.get_version_string()) # check @@ -101,8 +101,8 @@ def test_clojure(tmp_path): # test repo = VersionRepository(f) - version = repo.get_version(ReleaseType.SNAPSHOT) - version = version.create_release_version() + version = repo.get_version() + version = version.create_release_version(ReleaseType.SNAPSHOT) repo.write_file(version.get_version_string()) # check @@ -119,8 +119,8 @@ def test_python(tmp_path): # test repo = VersionRepository(f) - version = repo.get_version(ReleaseType.SNAPSHOT) - version = version.create_release_version() + version = repo.get_version() + version = version.create_release_version(ReleaseType.SNAPSHOT) repo.write_file(version.get_version_string()) # check diff --git a/version.py b/version.py index 7dea4f7..3cfca62 100644 --- a/version.py +++ b/version.py @@ -3,15 +3,14 @@ from file_handlers import FileHandler class Version(): - def __init__(self, version_list: list, release_type: ReleaseType): + def __init__(self, version_list: list): self.version_list = version_list - self.release_type = release_type self.version_string = None self.is_snapshot = None - def increment(self): + def increment(self, release_type: ReleaseType): self.is_snapshot = False - match self.release_type: + match release_type: case ReleaseType.BUMP: self.is_snapshot = True self.version_list[ReleaseType.PATCH.value] += 1 @@ -35,14 +34,14 @@ class Version(): self.version_string += "-SNAPSHOT" return self.version_string - def create_release_version(self): - release_version = Version(self.version_list.copy(), self.release_type) + def create_release_version(self, release_type: ReleaseType): + release_version = Version(self.version_list.copy()) release_version.is_snapshot = self.is_snapshot - release_version.increment() + release_version.increment(release_type) return release_version def create_bump_version(self): - bump_version = Version(self.version_list.copy(), ReleaseType.BUMP) + bump_version = Version(self.version_list.copy()) bump_version.is_snapshot = self.is_snapshot - bump_version.increment() + bump_version.increment(ReleaseType.BUMP) return bump_version diff --git a/version_repository.py b/version_repository.py index b135b79..2c3ec63 100644 --- a/version_repository.py +++ b/version_repository.py @@ -21,11 +21,11 @@ class VersionRepository(): version_list, is_snapshot = self.file_handler.parse() return version_list, is_snapshot - def get_version(self, release_type): + def get_version(self): self.file_handler = self.load_file() version_list, is_snapshot = self.parse_file() - version = Version(version_list, release_type) + version = Version(version_list) version.is_snapshot = is_snapshot return version From c58ddf70dac4b5d66db000b8298f54bb5815c0cf Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 23 Feb 2023 14:40:10 +0100 Subject: [PATCH 084/119] Compact updating config into one call --- test/test_release_mixin.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_release_mixin.py b/test/test_release_mixin.py index e83ad85..4b129c6 100644 --- a/test/test_release_mixin.py +++ b/test/test_release_mixin.py @@ -34,10 +34,10 @@ class MyBuild(ReleaseMixin): def initialize(project, CONFIG_FILE): project.build_depends_on('ddadevops>=3.1.2') config = create_release_mixin_config(CONFIG_FILE, MAIN_BRANCH) - config.update({'stage': STAGE}) - config.update({'module': MODULE}) - config.update({'project_root_path': PROJECT_ROOT_PATH}) - config.update({'build_dir_name': BUILD_DIR_NAME}) + config.update({'stage': STAGE, + 'module': MODULE, + 'project_root_path': PROJECT_ROOT_PATH, + 'build_dir_name': BUILD_DIR_NAME}) build = MyBuild(project, config) return build From fa6beebc4e2e838b9681f6b37d67281c9d9b9458 Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 23 Feb 2023 15:07:22 +0100 Subject: [PATCH 085/119] Move commit logic to prepare_release_service --- release_mixin.py | 23 +++++++++-------------- services.py | 35 ++++++++++++++++++----------------- test/test_release_mixin.py | 2 +- 3 files changed, 28 insertions(+), 32 deletions(-) diff --git a/release_mixin.py b/release_mixin.py index 4b8dc38..b31e838 100644 --- a/release_mixin.py +++ b/release_mixin.py @@ -30,29 +30,24 @@ class ReleaseMixin(DevopsBuild): self.config_file = release_mixin_config['config_file'] self.main_branch = release_mixin_config['main_branch'] self.version_repo = VersionRepository(self.config_file) + self.git_repo = GitRepository() self.release_version = None self.bump_version = None self.commit_string = None - def init(self): + def init_release(self): init_service = InitReleaseService(self.version_repo) self.release_version = init_service.create_release_version(self.commit_string) self.bump_version = self.release_version.create_bump_version() - def prepare(self, version): - git_repository = GitRepository() - if self.main_branch not in git_repository.get_current_branch(): - raise Exception('Trying to release while not on main branch') + def prepare_release(self): + prepare_release_service = PrepareReleaseService(self.version_repo, self.config_file, self.main_branch) + if self.release_version is None or self.bump_version is None: + raise Exception('prepare_release was called before init_release') - self.version_repo.write_file(version.get_version_string()) - git_repository.add_file(self.config_file) - match version.release_type: - case None: - raise Exception('Release type not set but trying to commit.') - case ReleaseType.BUMP: - git_repository.commit(f'Version bump') - case _: - git_repository.commit(f'Release {version.get_version_string()}') + # prepare_release_service.run_tests() # not implemented + prepare_release_service.write_and_commit_release(self.release_version) + prepare_release_service.write_and_commit_bump(self.bump_version) def tag_and_push(self, version): git_repository = GitRepository() diff --git a/services.py b/services.py index 212f016..04eac74 100644 --- a/services.py +++ b/services.py @@ -1,3 +1,4 @@ +from pathlib import Path from version_repository import VersionRepository from release_type import ReleaseType from git_repository import GitRepository @@ -31,28 +32,28 @@ class InitReleaseService(): class PrepareReleaseService(): - def __init__(self, version_repo: VersionRepository): - self.version_repo = version_repo - + def __init__(self, version_repository: VersionRepository, git_repository: GitRepository, config_file: Path, main_branch: str): + self.version_repository = version_repository + self.git_repository = git_repository + self.main_branch = main_branch + self.config_file = config_file def run_tests(self): # maybe auto? - pass + raise NotImplementedError - def prepare_release(self, version: Version): - # self.version_repo.write_file(version.get_version_string()) # side effect - pass + def __write_and_commit_version(self, version: Version, commit_message: str): + if self.main_branch != self.git_repository.get_current_branch(): + raise Exception('Trying to release while not on main branch') + + self.version_repository.write_file(version.get_version_string()) + self.git_repository.add_file(self.config_file) + self.git_repository.commit(commit_message) - def prepare_bump(self, version: Version): - # self.version_repo.write_file(version.get_version_string()) # side effect - pass + def write_and_commit_release(self, release_version: Version): + self.__write_and_commit_version(release_version, commit_message=f'Release {release_version.get_version_string()}') - - # write - - - # add - # commit - pass + def write_and_commit_bump(self, bump_version: Version): + self.__write_and_commit_version(bump_version, commit_message=f'Version bump') class TagAndPushReleaseService(): pass diff --git a/test/test_release_mixin.py b/test/test_release_mixin.py index 4b129c6..67b25a9 100644 --- a/test/test_release_mixin.py +++ b/test/test_release_mixin.py @@ -56,7 +56,7 @@ def test_release_mixin(tmp_path): # init build = initialize(project, CONFIG_FILE) build.commit_string = "MAJOR bla" - build.init() + build.init_release() release_version = build.release_version # test From e7152301dddc91c7667f544d57664f2b01b28e43 Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 23 Feb 2023 15:33:00 +0100 Subject: [PATCH 086/119] Add utility functions to git_repository --- git_repository.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/git_repository.py b/git_repository.py index 4fdfc64..a448847 100644 --- a/git_repository.py +++ b/git_repository.py @@ -16,9 +16,14 @@ class GitRepository(): inst.latest_commit = commit_string return inst + def get_latest_n_commits(self, n: int): + self.system_repository.run_checked('git', 'log', '--oneline', '--format="%s %b"', f'-n {n}') + return self.system_repository.stdout + def get_latest_commit(self): - self.system_repository.run_checked('git', 'log', '--oneline', '--format="%s %b"', '-n 1') - self.latest_commit = " ".join(self.system_repository.stdout) # returns a list of strings otherwise + output = self.get_latest_n_commits(1) + self.latest_commit = " ".join(output) # returns a list of strings otherwise + return self.latest_commit def get_release_type_from_latest_commit(self): if self.latest_commit is None: @@ -38,6 +43,9 @@ class GitRepository(): def tag_annotated(self, annotation: str, message: str): self.system_repository.run_checked('git', 'tag', '-a', annotation, '-m', message) + def tag_annotated(self, annotation: str, message: str, count: int): + self.system_repository.run_checked('git', 'tag', '-a', annotation, '-m', message, f'HEAD~{count}') + def get_current_branch(self): self.system_repository.run_checked('git', 'branch', '--show-current') From 4a23b0b800c61ec764f1fa9492e5b5b9879a0bef Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 23 Feb 2023 15:33:34 +0100 Subject: [PATCH 087/119] Implement and use TagAndPushReleaseService --- release_mixin.py | 16 +++------------- services.py | 11 ++++++++++- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/release_mixin.py b/release_mixin.py index b31e838..44adc92 100644 --- a/release_mixin.py +++ b/release_mixin.py @@ -49,16 +49,6 @@ class ReleaseMixin(DevopsBuild): prepare_release_service.write_and_commit_release(self.release_version) prepare_release_service.write_and_commit_bump(self.bump_version) - def tag_and_push(self, version): - git_repository = GitRepository() - match version.release_type: - case None: - raise Exception('Release type not set but trying to tag and push.') - case ReleaseType.BUMP: - annotation = 'v' + version.get_version_string() - message = 'Version bump' - case _: - annotation = 'v' + self.release_version.get_version_string() - message = 'Release' + annotation - git_repository.tag_annotated(annotation, message) - git_repository.push() + def tag_and_push(self): + tag_and_push_release_service = TagAndPushReleaseService(self.git_repo) + tag_and_push_release_service.tag_and_push_release(self.release_version) diff --git a/services.py b/services.py index 04eac74..0751046 100644 --- a/services.py +++ b/services.py @@ -56,5 +56,14 @@ class PrepareReleaseService(): self.__write_and_commit_version(bump_version, commit_message=f'Version bump') class TagAndPushReleaseService(): - pass + + def __init__(self, git_repository: GitRepository): + self.git_repository = git_repository + + def tag_and_push_release(self, release_version: Version): + annotation = 'v' + release_version.get_version_string() + message = 'Release ' + annotation + self.git_repository.tag_annotated(annotation, message, 1) + self.git_repository.push() + From a109759c68957cf701c701191a898908b4489491 Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 23 Feb 2023 16:30:43 +0100 Subject: [PATCH 088/119] Return stdout from git_repository functions --- git_repository.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/git_repository.py b/git_repository.py index a448847..e835384 100644 --- a/git_repository.py +++ b/git_repository.py @@ -42,21 +42,28 @@ class GitRepository(): def tag_annotated(self, annotation: str, message: str): self.system_repository.run_checked('git', 'tag', '-a', annotation, '-m', message) + return self.system_repository.stdout def tag_annotated(self, annotation: str, message: str, count: int): self.system_repository.run_checked('git', 'tag', '-a', annotation, '-m', message, f'HEAD~{count}') + return self.system_repository.stdout def get_current_branch(self): self.system_repository.run_checked('git', 'branch', '--show-current') + return ''.join(self.system_repository.stdout).rstrip() def add_file(self, file_path: Path): self.system_repository.run_checked('git', 'add', file_path) + return self.system_repository.stdout def commit(self, commit_message: str): self.system_repository.run_checked('git', 'commit', '-m', commit_message) + return self.system_repository.stdout def push(self): self.system_repository.run_checked('git', 'push') + return self.system_repository.stdout def checkout(self, branch: str): self.system_repository.run_checked('git', 'checkout', branch) + return self.system_repository.stdout From b93772d3a1d7df1b9ee660ee2f7f6629f39f30e4 Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 23 Feb 2023 16:31:33 +0100 Subject: [PATCH 089/119] Fix naming and arguments in release_mixin --- release_mixin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release_mixin.py b/release_mixin.py index 44adc92..13b174a 100644 --- a/release_mixin.py +++ b/release_mixin.py @@ -41,7 +41,7 @@ class ReleaseMixin(DevopsBuild): self.bump_version = self.release_version.create_bump_version() def prepare_release(self): - prepare_release_service = PrepareReleaseService(self.version_repo, self.config_file, self.main_branch) + prepare_release_service = PrepareReleaseService(self.version_repo, self.git_repo, self.config_file, self.main_branch) if self.release_version is None or self.bump_version is None: raise Exception('prepare_release was called before init_release') @@ -49,6 +49,6 @@ class ReleaseMixin(DevopsBuild): prepare_release_service.write_and_commit_release(self.release_version) prepare_release_service.write_and_commit_bump(self.bump_version) - def tag_and_push(self): + def tag_and_push_release(self): tag_and_push_release_service = TagAndPushReleaseService(self.git_repo) tag_and_push_release_service.tag_and_push_release(self.release_version) From 0cdf4b4c9c8e19264509b01c93f9de4b175cf3a0 Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 23 Feb 2023 16:34:22 +0100 Subject: [PATCH 090/119] Update files for in project release testing 1. commit anything and include one release type 2. run `pyb release` in project root 3. check that release commit and version bump commit were added 4. reset with `git reset origin/main` --- build.py | 57 ++++++++++++++++++++++++++++------------------------- config.json | 3 +++ services.py | 2 +- 3 files changed, 34 insertions(+), 28 deletions(-) create mode 100644 config.json diff --git a/build.py b/build.py index af78598..4e5ea5f 100644 --- a/build.py +++ b/build.py @@ -1,10 +1,29 @@ +import sys +import os +from pathlib import Path +from ddadevops import * + +# getting the name of the directory +# where the this file is present. +current = os.path.dirname(os.path.realpath(__file__)) + +# adding the current directory to +# the sys.path. +sys.path.append(current) + +# now we can import the module in the current +# directory. + from pybuilder.core import task, init from ddadevops import * -from version import * -from release_mixin import * +from release_mixin import ReleaseMixin, create_release_mixin_config -CONFIG_FILE = '' +CONFIG_FILE = Path('config.json') MAIN_BRANCH = 'main' +STAGE = 'test' +PROJECT_ROOT_PATH = '.' +MODULE = 'test' +BUILD_DIR_NAME = "build_dir" class MyBuild(ReleaseMixin): pass @@ -13,33 +32,17 @@ class MyBuild(ReleaseMixin): def initialize(project): project.build_depends_on('ddadevops>=3.1.2') config = create_release_mixin_config(CONFIG_FILE, MAIN_BRANCH) + config.update({'stage': STAGE, + 'module': MODULE, + 'project_root_path': PROJECT_ROOT_PATH, + 'build_dir_name': BUILD_DIR_NAME}) build = MyBuild(project, config) - build.init() + build.initialize_build_dir() @task def release(project): build = get_devops_build(project) - prepare_release(build) - tag_and_push_release(build) - - prepare_version_bump(build) - tag_and_push_version_bump(build) - -def prepare_release(build): - release_version = build.release_version - build.prepare(release_version) - -def tag_and_push_release(build): - release_version = build.release_version - build.tag_and_push(release_version) - -def prepare_version_bump(build): - bump_version = build.bump_version - build.prepare(bump_version) - -def tag_and_push_version_bump(build): - bump_version = build.bump_version - build.tag_and_push(bump_version) - - + build.init_release() + build.prepare_release() + build.tag_and_push_release() diff --git a/config.json b/config.json new file mode 100644 index 0000000..6068f8d --- /dev/null +++ b/config.json @@ -0,0 +1,3 @@ +{ + "version": "123.125.1-SNAPSHOT" +} \ No newline at end of file diff --git a/services.py b/services.py index 0751046..3ff28de 100644 --- a/services.py +++ b/services.py @@ -64,6 +64,6 @@ class TagAndPushReleaseService(): annotation = 'v' + release_version.get_version_string() message = 'Release ' + annotation self.git_repository.tag_annotated(annotation, message, 1) - self.git_repository.push() + # self.git_repository.push() From 4f499bd1f47aa40a8ba54d448a9e1c039d30432e Mon Sep 17 00:00:00 2001 From: jerger Date: Fri, 24 Feb 2023 10:00:11 +0100 Subject: [PATCH 091/119] ignore target --- .gitignore | 1 + .../stale/leiningen.core.classpath.extract-native-dependencies | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 target/default+test/stale/leiningen.core.classpath.extract-native-dependencies diff --git a/.gitignore b/.gitignore index 6b02ccb..58f5cd3 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ __pycache__ .clj-kondo/ .lsp/ +target/ # vs code settings .vscode \ No newline at end of file diff --git a/target/default+test/stale/leiningen.core.classpath.extract-native-dependencies b/target/default+test/stale/leiningen.core.classpath.extract-native-dependencies deleted file mode 100644 index 03ba243..0000000 --- a/target/default+test/stale/leiningen.core.classpath.extract-native-dependencies +++ /dev/null @@ -1 +0,0 @@ -[{:dependencies {args4j {:vsn "2.0.26", :native-prefix nil}, org.clojure/data.json {:vsn "0.2.6", :native-prefix nil}, org.clojure/clojure {:vsn "1.11.1", :native-prefix nil}, org.clojure/core.specs.alpha {:vsn "0.2.62", :native-prefix nil}, org.clojure/spec.alpha {:vsn "0.3.218", :native-prefix nil}, dda/data-test {:vsn "0.1.1", :native-prefix nil}, quoin {:vsn "0.1.2", :native-prefix nil}, org.domaindrivenarchitecture/c4k-common-clj {:vsn "5.0.1", :native-prefix nil}, hickory {:vsn "0.7.1", :native-prefix nil}, org.clojure/google-closure-library {:vsn "0.0-20160609-f42b4a24", :native-prefix nil}, org.clojure/clojurescript {:vsn "1.9.293", :native-prefix nil}, aero {:vsn "1.1.6", :native-prefix nil}, orchestra {:vsn "2021.01.01-1", :native-prefix nil}, org.flatland/ordered {:vsn "1.5.9", :native-prefix nil}, com.google.jsinterop/jsinterop-annotations {:vsn "1.0.0", :native-prefix nil}, org.yaml/snakeyaml {:vsn "1.33", :native-prefix nil}, org.mozilla/rhino {:vsn "1.7R5", :native-prefix nil}, org.clojure/google-closure-library-third-party {:vsn "0.0-20160609-f42b4a24", :native-prefix nil}, pjstadig/humane-test-output {:vsn "0.11.0", :native-prefix nil}, com.google.javascript/closure-compiler-externs {:vsn "v20160911", :native-prefix nil}, clojure-complete {:vsn "0.2.5", :native-prefix nil}, com.google.guava/guava {:vsn "19.0", :native-prefix nil}, viebel/codox-klipse-theme {:vsn "0.0.1", :native-prefix nil}, clj-commons/clj-yaml {:vsn "1.0.26", :native-prefix nil}, prismatic/schema {:vsn "1.1.10", :native-prefix nil}, org.clojure/tools.reader {:vsn "1.3.6", :native-prefix nil}, nrepl {:vsn "0.6.0", :native-prefix nil}, org.jsoup/jsoup {:vsn "1.9.2", :native-prefix nil}, expound {:vsn "0.9.0", :native-prefix nil}, com.google.javascript/closure-compiler-unshaded {:vsn "v20160911", :native-prefix nil}, com.google.protobuf/protobuf-java {:vsn "2.5.0", :native-prefix nil}, com.google.code.findbugs/jsr305 {:vsn "1.3.9", :native-prefix nil}, com.google.code.gson/gson {:vsn "2.2.4", :native-prefix nil}}, :native-path "target/default+test/native"} {:native-path "target/default+test/native", :dependencies {args4j {:vsn "2.0.26", :native-prefix nil, :native? false}, org.clojure/data.json {:vsn "0.2.6", :native-prefix nil, :native? false}, org.clojure/clojure {:vsn "1.11.1", :native-prefix nil, :native? false}, org.clojure/core.specs.alpha {:vsn "0.2.62", :native-prefix nil, :native? false}, org.clojure/spec.alpha {:vsn "0.3.218", :native-prefix nil, :native? false}, dda/data-test {:vsn "0.1.1", :native-prefix nil, :native? false}, quoin {:vsn "0.1.2", :native-prefix nil, :native? false}, org.domaindrivenarchitecture/c4k-common-clj {:vsn "5.0.1", :native-prefix nil, :native? false}, hickory {:vsn "0.7.1", :native-prefix nil, :native? false}, org.clojure/google-closure-library {:vsn "0.0-20160609-f42b4a24", :native-prefix nil, :native? false}, org.clojure/clojurescript {:vsn "1.9.293", :native-prefix nil, :native? false}, aero {:vsn "1.1.6", :native-prefix nil, :native? false}, orchestra {:vsn "2021.01.01-1", :native-prefix nil, :native? false}, org.flatland/ordered {:vsn "1.5.9", :native-prefix nil, :native? false}, com.google.jsinterop/jsinterop-annotations {:vsn "1.0.0", :native-prefix nil, :native? false}, org.yaml/snakeyaml {:vsn "1.33", :native-prefix nil, :native? false}, org.mozilla/rhino {:vsn "1.7R5", :native-prefix nil, :native? false}, org.clojure/google-closure-library-third-party {:vsn "0.0-20160609-f42b4a24", :native-prefix nil, :native? false}, pjstadig/humane-test-output {:vsn "0.11.0", :native-prefix nil, :native? false}, com.google.javascript/closure-compiler-externs {:vsn "v20160911", :native-prefix nil, :native? false}, clojure-complete {:vsn "0.2.5", :native-prefix nil, :native? false}, com.google.guava/guava {:vsn "19.0", :native-prefix nil, :native? false}, viebel/codox-klipse-theme {:vsn "0.0.1", :native-prefix nil, :native? false}, clj-commons/clj-yaml {:vsn "1.0.26", :native-prefix nil, :native? false}, prismatic/schema {:vsn "1.1.10", :native-prefix nil, :native? false}, org.clojure/tools.reader {:vsn "1.3.6", :native-prefix nil, :native? false}, nrepl {:vsn "0.6.0", :native-prefix nil, :native? false}, org.jsoup/jsoup {:vsn "1.9.2", :native-prefix nil, :native? false}, expound {:vsn "0.9.0", :native-prefix nil, :native? false}, com.google.javascript/closure-compiler-unshaded {:vsn "v20160911", :native-prefix nil, :native? false}, com.google.protobuf/protobuf-java {:vsn "2.5.0", :native-prefix nil, :native? false}, com.google.code.findbugs/jsr305 {:vsn "1.3.9", :native-prefix nil, :native? false}, com.google.code.gson/gson {:vsn "2.2.4", :native-prefix nil, :native? false}}}] \ No newline at end of file From 74925a90b737c54664288cb0380754401950d68e Mon Sep 17 00:00:00 2001 From: jerger Date: Fri, 24 Feb 2023 10:14:26 +0100 Subject: [PATCH 092/119] mob --- version.py => domain.py | 29 +++++++++++++++++++++-------- git_repository.py | 2 +- infrastructure.py | 8 ++++++++ release_mixin.py | 3 +-- release_type.py | 7 ------- services.py | 2 +- version_repository.py | 2 +- 7 files changed, 33 insertions(+), 20 deletions(-) rename version.py => domain.py (75%) create mode 100644 infrastructure.py delete mode 100644 release_type.py diff --git a/version.py b/domain.py similarity index 75% rename from version.py rename to domain.py index 3cfca62..c5193a6 100644 --- a/version.py +++ b/domain.py @@ -1,5 +1,12 @@ -from release_type import ReleaseType -from file_handlers import FileHandler +from enum import Enum + +class ReleaseType(Enum): + MAJOR = 0 + MINOR = 1 + PATCH = 2 + SNAPSHOT = 3 + BUMP = None + class Version(): @@ -19,11 +26,11 @@ class Version(): case ReleaseType.PATCH: self.version_list[ReleaseType.PATCH.value] += 1 case ReleaseType.MINOR: - self.version_list[ReleaseType.PATCH.value] = 0 + self.version_list[ReleaseType.PATCH.value] = 0 self.version_list[ReleaseType.MINOR.value] += 1 case ReleaseType.MAJOR: - self.version_list[ReleaseType.PATCH.value] = 0 - self.version_list[ReleaseType.MINOR.value] = 0 + self.version_list[ReleaseType.PATCH.value] = 0 + self.version_list[ReleaseType.MINOR.value] = 0 self.version_list[ReleaseType.MAJOR.value] += 1 case None: raise Exception("Release Type was not set!") @@ -33,15 +40,21 @@ class Version(): if self.is_snapshot: self.version_string += "-SNAPSHOT" return self.version_string - + def create_release_version(self, release_type: ReleaseType): release_version = Version(self.version_list.copy()) release_version.is_snapshot = self.is_snapshot release_version.increment(release_type) - return release_version - + return release_version + def create_bump_version(self): bump_version = Version(self.version_list.copy()) bump_version.is_snapshot = self.is_snapshot bump_version.increment(ReleaseType.BUMP) return bump_version + + +class Release(): + def __init__(self, release_type: ReleaseType, version: Version): + self.release_type = release_type + self.version = version diff --git a/git_repository.py b/git_repository.py index e835384..5761048 100644 --- a/git_repository.py +++ b/git_repository.py @@ -2,7 +2,7 @@ import os import subprocess as sub from pathlib import Path from system_repository import SystemRepository -from release_type import ReleaseType +from domain import ReleaseType class GitRepository(): diff --git a/infrastructure.py b/infrastructure.py new file mode 100644 index 0000000..baa9e74 --- /dev/null +++ b/infrastructure.py @@ -0,0 +1,8 @@ +from domain import Release + +class ReleaseRepository(): + def __init__(self): + pass + + def get_current_release(self) -> Release: + pass \ No newline at end of file diff --git a/release_mixin.py b/release_mixin.py index 13b174a..7e02598 100644 --- a/release_mixin.py +++ b/release_mixin.py @@ -2,11 +2,10 @@ import copy from ddadevops import DevopsBuild from ddadevops import execute from ddadevops import gopass_field_from_path, gopass_password_from_path -from version import Version from version_repository import VersionRepository from services import InitReleaseService, PrepareReleaseService, TagAndPushReleaseService from git_repository import GitRepository -from release_type import ReleaseType +from domain import ReleaseType, Version def create_release_mixin_config(config_file, main_branch) -> dict: config = {} diff --git a/release_type.py b/release_type.py deleted file mode 100644 index e4217a5..0000000 --- a/release_type.py +++ /dev/null @@ -1,7 +0,0 @@ -from enum import Enum -class ReleaseType(Enum): - MAJOR = 0 - MINOR = 1 - PATCH = 2 - SNAPSHOT = 3 - BUMP = None \ No newline at end of file diff --git a/services.py b/services.py index 3ff28de..5453bee 100644 --- a/services.py +++ b/services.py @@ -2,7 +2,7 @@ from pathlib import Path from version_repository import VersionRepository from release_type import ReleaseType from git_repository import GitRepository -from version import Version +from domain import Version class InitReleaseService(): diff --git a/version_repository.py b/version_repository.py index 2c3ec63..16e706f 100644 --- a/version_repository.py +++ b/version_repository.py @@ -1,5 +1,5 @@ from file_handlers import FileHandler -from version import Version +from domain import Version class VersionRepository(): From 0551fe6bfe7bcdb3bb3da28b1372d82549a7fc9c Mon Sep 17 00:00:00 2001 From: bom Date: Fri, 24 Feb 2023 10:31:04 +0100 Subject: [PATCH 093/119] mob --- git_repository.py | 69 ------------ infrastructure.py | 105 +++++++++++++++++- file_handlers.py => infrastructure_api.py | 24 +++- release_mixin.py | 3 +- services.py | 6 +- system_repository.py | 22 ---- test/test_domain.py | 53 +++++++++ test/test_git_repository.py | 64 ----------- ...ersion_class.py => test_infrastructure.py} | 90 +++++++++------ test/test_release_mixin.py | 1 - test/test_services.py | 4 +- version_repository.py | 32 ------ 12 files changed, 240 insertions(+), 233 deletions(-) delete mode 100644 git_repository.py rename file_handlers.py => infrastructure_api.py (90%) delete mode 100644 system_repository.py create mode 100644 test/test_domain.py delete mode 100644 test/test_git_repository.py rename test/{test_version_class.py => test_infrastructure.py} (57%) delete mode 100644 version_repository.py diff --git a/git_repository.py b/git_repository.py deleted file mode 100644 index 5761048..0000000 --- a/git_repository.py +++ /dev/null @@ -1,69 +0,0 @@ -import os -import subprocess as sub -from pathlib import Path -from system_repository import SystemRepository -from domain import ReleaseType - -class GitRepository(): - - def __init__(self): - self.latest_commit = None - self.system_repository = SystemRepository() - - @classmethod - def create_from_commit_string(cls, commit_string): - inst = cls() - inst.latest_commit = commit_string - return inst - - def get_latest_n_commits(self, n: int): - self.system_repository.run_checked('git', 'log', '--oneline', '--format="%s %b"', f'-n {n}') - return self.system_repository.stdout - - def get_latest_commit(self): - output = self.get_latest_n_commits(1) - self.latest_commit = " ".join(output) # returns a list of strings otherwise - return self.latest_commit - - def get_release_type_from_latest_commit(self): - if self.latest_commit is None: - self.get_latest_commit() - - if ReleaseType.MAJOR.name in self.latest_commit.upper(): - return ReleaseType.MAJOR - elif ReleaseType.MINOR.name in self.latest_commit.upper(): - return ReleaseType.MINOR - elif ReleaseType.PATCH.name in self.latest_commit.upper(): - return ReleaseType.PATCH - elif ReleaseType.SNAPSHOT.name in self.latest_commit.upper(): - return ReleaseType.SNAPSHOT - else: - return None - - def tag_annotated(self, annotation: str, message: str): - self.system_repository.run_checked('git', 'tag', '-a', annotation, '-m', message) - return self.system_repository.stdout - - def tag_annotated(self, annotation: str, message: str, count: int): - self.system_repository.run_checked('git', 'tag', '-a', annotation, '-m', message, f'HEAD~{count}') - return self.system_repository.stdout - - def get_current_branch(self): - self.system_repository.run_checked('git', 'branch', '--show-current') - return ''.join(self.system_repository.stdout).rstrip() - - def add_file(self, file_path: Path): - self.system_repository.run_checked('git', 'add', file_path) - return self.system_repository.stdout - - def commit(self, commit_message: str): - self.system_repository.run_checked('git', 'commit', '-m', commit_message) - return self.system_repository.stdout - - def push(self): - self.system_repository.run_checked('git', 'push') - return self.system_repository.stdout - - def checkout(self, branch: str): - self.system_repository.run_checked('git', 'checkout', branch) - return self.system_repository.stdout diff --git a/infrastructure.py b/infrastructure.py index baa9e74..528194e 100644 --- a/infrastructure.py +++ b/infrastructure.py @@ -1,8 +1,107 @@ -from domain import Release +from domain import Release, Version, ReleaseType +from infrastructure_api import FileHandler, SystemAPI +from pathlib import Path + +class VersionRepository(): + + def __init__(self, file): + self.file = file + self.file_handler = None + + def load_file(self): + self.file_handler = FileHandler.from_file_path(self.file) + return self.file_handler + + def write_file(self, version_string): + if self.file_handler is None: + raise Exception('Version was not created by load_file method.') + else: + self.file_handler.write(version_string) + + def parse_file(self): + version_list, is_snapshot = self.file_handler.parse() + return version_list, is_snapshot + + def get_version(self) -> Version: + + self.file_handler = self.load_file() + version_list, is_snapshot = self.parse_file() + version = Version(version_list) + version.is_snapshot = is_snapshot + + return version class ReleaseRepository(): + def __init__(self, version_repository: VersionRepository): + self.version_repository = version_repository + + def get_current_release(self) -> Release: + pass + +class ReleaseTypeRepository(): def __init__(self): pass - def get_current_release(self) -> Release: - pass \ No newline at end of file +class GitRepository(): + + def __init__(self): + self.latest_commit = None + self.system_repository = SystemAPI() + + @classmethod + def create_from_commit_string(cls, commit_string): + inst = cls() + inst.latest_commit = commit_string + return inst + + def get_latest_n_commits(self, n: int): + self.system_repository.run_checked('git', 'log', '--oneline', '--format="%s %b"', f'-n {n}') + return self.system_repository.stdout + + def get_latest_commit(self): + output = self.get_latest_n_commits(1) + self.latest_commit = " ".join(output) # returns a list of strings otherwise + return self.latest_commit + + def get_release_type_from_latest_commit(self): + if self.latest_commit is None: + self.get_latest_commit() + + if ReleaseType.MAJOR.name in self.latest_commit.upper(): + return ReleaseType.MAJOR + elif ReleaseType.MINOR.name in self.latest_commit.upper(): + return ReleaseType.MINOR + elif ReleaseType.PATCH.name in self.latest_commit.upper(): + return ReleaseType.PATCH + elif ReleaseType.SNAPSHOT.name in self.latest_commit.upper(): + return ReleaseType.SNAPSHOT + else: + return None + + def tag_annotated(self, annotation: str, message: str): + self.system_repository.run_checked('git', 'tag', '-a', annotation, '-m', message) + return self.system_repository.stdout + + def tag_annotated(self, annotation: str, message: str, count: int): + self.system_repository.run_checked('git', 'tag', '-a', annotation, '-m', message, f'HEAD~{count}') + return self.system_repository.stdout + + def get_current_branch(self): + self.system_repository.run_checked('git', 'branch', '--show-current') + return ''.join(self.system_repository.stdout).rstrip() + + def add_file(self, file_path: Path): + self.system_repository.run_checked('git', 'add', file_path) + return self.system_repository.stdout + + def commit(self, commit_message: str): + self.system_repository.run_checked('git', 'commit', '-m', commit_message) + return self.system_repository.stdout + + def push(self): + self.system_repository.run_checked('git', 'push') + return self.system_repository.stdout + + def checkout(self, branch: str): + self.system_repository.run_checked('git', 'checkout', branch) + return self.system_repository.stdout \ No newline at end of file diff --git a/file_handlers.py b/infrastructure_api.py similarity index 90% rename from file_handlers.py rename to infrastructure_api.py index e3f627d..aacaa08 100644 --- a/file_handlers.py +++ b/infrastructure_api.py @@ -1,6 +1,7 @@ from abc import ABC, abstractmethod import json import re +import subprocess as sub class FileHandler(ABC): @@ -152,4 +153,25 @@ class ClojureFileHandler(FileHandler): clj_file.seek(0) clj_file.write(version_substitute) clj_file.write(clj_rest) - clj_file.truncate() \ No newline at end of file + clj_file.truncate() + +class SystemAPI(): + + def __init__(self): + self.stdout = [""] + self.stderr = [""] + + def run(self, args): + stream = sub.Popen(args, + stdout=sub.PIPE, + stderr=sub.PIPE, + text=True, + encoding="UTF-8") + self.stdout = stream.stdout.readlines() + self.stderr = stream.stderr.readlines() + + def run_checked(self, *args): + self.run(args) + + if len(self.stderr) > 0: + raise Exception(f"Command failed with: {self.stderr}") diff --git a/release_mixin.py b/release_mixin.py index 7e02598..3d6c992 100644 --- a/release_mixin.py +++ b/release_mixin.py @@ -2,9 +2,8 @@ import copy from ddadevops import DevopsBuild from ddadevops import execute from ddadevops import gopass_field_from_path, gopass_password_from_path -from version_repository import VersionRepository +from infrastructure import VersionRepository, GitRepository from services import InitReleaseService, PrepareReleaseService, TagAndPushReleaseService -from git_repository import GitRepository from domain import ReleaseType, Version def create_release_mixin_config(config_file, main_branch) -> dict: diff --git a/services.py b/services.py index 5453bee..6b46c12 100644 --- a/services.py +++ b/services.py @@ -1,8 +1,6 @@ from pathlib import Path -from version_repository import VersionRepository -from release_type import ReleaseType -from git_repository import GitRepository -from domain import Version +from infrastructure import VersionRepository, GitRepository +from domain import Version, ReleaseType class InitReleaseService(): diff --git a/system_repository.py b/system_repository.py deleted file mode 100644 index 914276f..0000000 --- a/system_repository.py +++ /dev/null @@ -1,22 +0,0 @@ -import subprocess as sub - -class SystemRepository(): - - def __init__(self): - self.stdout = [""] - self.stderr = [""] - - def run(self, args): - stream = sub.Popen(args, - stdout=sub.PIPE, - stderr=sub.PIPE, - text=True, - encoding="UTF-8") - self.stdout = stream.stdout.readlines() - self.stderr = stream.stderr.readlines() - - def run_checked(self, *args): - self.run(args) - - if len(self.stderr) > 0: - raise Exception(f"Command failed with: {self.stderr}") diff --git a/test/test_domain.py b/test/test_domain.py new file mode 100644 index 0000000..68fd7bf --- /dev/null +++ b/test/test_domain.py @@ -0,0 +1,53 @@ +from pathlib import Path +import sys +import os + +# getting the name of the directory +# where the this file is present. +current = os.path.dirname(os.path.realpath(__file__)) + +# Getting the parent directory name +# where the current directory is present. +parent = os.path.dirname(current) + +# adding the parent directory to +# the sys.path. +sys.path.append(parent) + +# now we can import the module in the parent +# directory. + +from domain import Version, ReleaseType +from infrastructure import VersionRepository + +def test_version(): + version = Version([1, 2, 3]) + + version.increment(ReleaseType.SNAPSHOT) + assert version.get_version_string() == "1.2.3-SNAPSHOT" + assert version.version_list == [1, 2, 3] + assert version.is_snapshot + + version = Version([1, 2, 3]) + version.increment(ReleaseType.BUMP) + assert version.get_version_string() == "1.2.4-SNAPSHOT" + assert version.version_list == [1, 2, 4] + assert version.is_snapshot + + version = Version([1, 2, 3]) + version.increment(ReleaseType.PATCH) + assert version.get_version_string() == "1.2.4" + assert version.version_list == [1, 2, 4] + assert not version.is_snapshot + + version = Version([1, 2, 3]) + version.increment(ReleaseType.MINOR) + assert version.get_version_string() == "1.3.0" + assert version.version_list == [1, 3, 0] + assert not version.is_snapshot + + version = Version([1, 2, 3]) + version.increment(ReleaseType.MAJOR) + assert version.get_version_string() == "2.0.0" + assert version.version_list == [2, 0, 0] + assert not version.is_snapshot diff --git a/test/test_git_repository.py b/test/test_git_repository.py deleted file mode 100644 index a1e9db8..0000000 --- a/test/test_git_repository.py +++ /dev/null @@ -1,64 +0,0 @@ -from pathlib import Path -import sys -import os - -# getting the name of the directory -# where the this file is present. -current = os.path.dirname(os.path.realpath(__file__)) - -# Getting the parent directory name -# where the current directory is present. -parent = os.path.dirname(current) - -# adding the parent directory to -# the sys.path. -sys.path.append(parent) - -# now we can import the module in the parent -# directory. - -from git_repository import GitRepository -from release_type import ReleaseType - -def test_git_repository(): - - # init - commit_string = "Major bla" - repo = GitRepository.create_from_commit_string(commit_string) - release_type = repo.get_release_type_from_latest_commit() - - #test - assert release_type == ReleaseType.MAJOR - - - # init - commit_string = "MINOR bla" - repo = GitRepository.create_from_commit_string(commit_string) - release_type = repo.get_release_type_from_latest_commit() - - #test - assert release_type == ReleaseType.MINOR - - # init - commit_string = "PATCH bla" - repo = GitRepository.create_from_commit_string(commit_string) - release_type = repo.get_release_type_from_latest_commit() - - # test - assert release_type == ReleaseType.PATCH - - # init - commit_string = "SNAPSHOT bla" - repo = GitRepository.create_from_commit_string(commit_string) - release_type = repo.get_release_type_from_latest_commit() - - #test - assert release_type == ReleaseType.SNAPSHOT - - # init - commit_string = "bla" - repo = GitRepository.create_from_commit_string(commit_string) - release_type = repo.get_release_type_from_latest_commit() - - #test - assert release_type == None diff --git a/test/test_version_class.py b/test/test_infrastructure.py similarity index 57% rename from test/test_version_class.py rename to test/test_infrastructure.py index 4f1ac6b..6abf6ed 100644 --- a/test/test_version_class.py +++ b/test/test_infrastructure.py @@ -5,54 +5,63 @@ import os # getting the name of the directory # where the this file is present. current = os.path.dirname(os.path.realpath(__file__)) - + # Getting the parent directory name # where the current directory is present. parent = os.path.dirname(current) - + # adding the parent directory to # the sys.path. sys.path.append(parent) - + # now we can import the module in the parent # directory. -from version import Version -from version_repository import VersionRepository -from release_type import ReleaseType +from infrastructure import GitRepository, VersionRepository, ReleaseRepository +from domain import ReleaseType, Release -def test_version(): - version = Version([1, 2, 3]) +def test_git_repository(): - version.increment(ReleaseType.SNAPSHOT) - assert version.get_version_string() == "1.2.3-SNAPSHOT" - assert version.version_list == [1, 2, 3] - assert version.is_snapshot + # init + commit_string = "Major bla" + repo = GitRepository.create_from_commit_string(commit_string) + release_type = repo.get_release_type_from_latest_commit() - version = Version([1, 2, 3]) - version.increment(ReleaseType.BUMP) - assert version.get_version_string() == "1.2.4-SNAPSHOT" - assert version.version_list == [1, 2, 4] - assert version.is_snapshot + #test + assert release_type == ReleaseType.MAJOR - version = Version([1, 2, 3]) - version.increment(ReleaseType.PATCH) - assert version.get_version_string() == "1.2.4" - assert version.version_list == [1, 2, 4] - assert not version.is_snapshot - version = Version([1, 2, 3]) - version.increment(ReleaseType.MINOR) - assert version.get_version_string() == "1.3.0" - assert version.version_list == [1, 3, 0] - assert not version.is_snapshot + # init + commit_string = "MINOR bla" + repo = GitRepository.create_from_commit_string(commit_string) + release_type = repo.get_release_type_from_latest_commit() - version = Version([1, 2, 3]) - version.increment(ReleaseType.MAJOR) - assert version.get_version_string() == "2.0.0" - assert version.version_list == [2, 0, 0] - assert not version.is_snapshot + #test + assert release_type == ReleaseType.MINOR + # init + commit_string = "PATCH bla" + repo = GitRepository.create_from_commit_string(commit_string) + release_type = repo.get_release_type_from_latest_commit() + + # test + assert release_type == ReleaseType.PATCH + + # init + commit_string = "SNAPSHOT bla" + repo = GitRepository.create_from_commit_string(commit_string) + release_type = repo.get_release_type_from_latest_commit() + + #test + assert release_type == ReleaseType.SNAPSHOT + + # init + commit_string = "bla" + repo = GitRepository.create_from_commit_string(commit_string) + release_type = repo.get_release_type_from_latest_commit() + + #test + assert release_type == None def test_gradle(tmp_path): # init @@ -124,4 +133,19 @@ def test_python(tmp_path): repo.write_file(version.get_version_string()) # check - assert '3.1.3-SNAPSHOT' in f.read_text() \ No newline at end of file + assert '3.1.3-SNAPSHOT' in f.read_text() + +def test_release_repository(tmp_path): + # init + file_name = 'config.json' + with open(f'test/resources/{file_name}', 'r') as gradle_file: + contents = gradle_file.read() + + f = tmp_path / file_name + f.write_text(contents) + + # test + sut = ReleaseRepository(VersionRepository(f)) + current_release = sut.get_current_release() + + assert current_release.version is not None \ No newline at end of file diff --git a/test/test_release_mixin.py b/test/test_release_mixin.py index 67b25a9..d9d387a 100644 --- a/test/test_release_mixin.py +++ b/test/test_release_mixin.py @@ -19,7 +19,6 @@ sys.path.append(parent) # directory. from pybuilder.core import task, init, Project -from version import * from release_mixin import ReleaseMixin, create_release_mixin_config MAIN_BRANCH = 'main' diff --git a/test/test_services.py b/test/test_services.py index f7ec74c..1eef213 100644 --- a/test/test_services.py +++ b/test/test_services.py @@ -18,8 +18,8 @@ sys.path.append(parent) # directory. from services import InitReleaseService -from release_type import ReleaseType -from version_repository import VersionRepository +from domain import ReleaseType +from infrastructure import VersionRepository def test_init_release_service(tmp_path): # init diff --git a/version_repository.py b/version_repository.py deleted file mode 100644 index 16e706f..0000000 --- a/version_repository.py +++ /dev/null @@ -1,32 +0,0 @@ -from file_handlers import FileHandler -from domain import Version - -class VersionRepository(): - - def __init__(self, file): - self.file = file - self.file_handler = None - - def load_file(self): - self.file_handler = FileHandler.from_file_path(self.file) - return self.file_handler - - def write_file(self, version_string): - if self.file_handler is None: - raise Exception('Version was not created by load_file method.') - else: - self.file_handler.write(version_string) - - def parse_file(self): - version_list, is_snapshot = self.file_handler.parse() - return version_list, is_snapshot - - def get_version(self): - - self.file_handler = self.load_file() - version_list, is_snapshot = self.parse_file() - version = Version(version_list) - version.is_snapshot = is_snapshot - - return version - From a09eaa7521823619f49e0720bbfdaf4748e39992 Mon Sep 17 00:00:00 2001 From: jerger Date: Fri, 24 Feb 2023 10:46:43 +0100 Subject: [PATCH 094/119] mob --- infrastructure.py | 68 ++++---------------------- infrastructure_api.py | 96 +++++++++++++++++++++++++++++-------- test/test_infrastructure.py | 51 ++++++++++++-------- 3 files changed, 118 insertions(+), 97 deletions(-) diff --git a/infrastructure.py b/infrastructure.py index 528194e..8234a98 100644 --- a/infrastructure.py +++ b/infrastructure.py @@ -1,5 +1,5 @@ from domain import Release, Version, ReleaseType -from infrastructure_api import FileHandler, SystemAPI +from infrastructure_api import FileHandler, SystemAPI, GitApi from pathlib import Path class VersionRepository(): @@ -39,69 +39,19 @@ class ReleaseRepository(): pass class ReleaseTypeRepository(): - def __init__(self): - pass + def __init__(self, git_api, environment_api=None): + self.git_api = git_api -class GitRepository(): + def get_release_type(self): + latest_commit = self.git_api.get_latest_commit() - def __init__(self): - self.latest_commit = None - self.system_repository = SystemAPI() - - @classmethod - def create_from_commit_string(cls, commit_string): - inst = cls() - inst.latest_commit = commit_string - return inst - - def get_latest_n_commits(self, n: int): - self.system_repository.run_checked('git', 'log', '--oneline', '--format="%s %b"', f'-n {n}') - return self.system_repository.stdout - - def get_latest_commit(self): - output = self.get_latest_n_commits(1) - self.latest_commit = " ".join(output) # returns a list of strings otherwise - return self.latest_commit - - def get_release_type_from_latest_commit(self): - if self.latest_commit is None: - self.get_latest_commit() - - if ReleaseType.MAJOR.name in self.latest_commit.upper(): + if ReleaseType.MAJOR.name in latest_commit.upper(): return ReleaseType.MAJOR - elif ReleaseType.MINOR.name in self.latest_commit.upper(): + elif ReleaseType.MINOR.name in latest_commit.upper(): return ReleaseType.MINOR - elif ReleaseType.PATCH.name in self.latest_commit.upper(): + elif ReleaseType.PATCH.name in latest_commit.upper(): return ReleaseType.PATCH - elif ReleaseType.SNAPSHOT.name in self.latest_commit.upper(): + elif ReleaseType.SNAPSHOT.name in latest_commit.upper(): return ReleaseType.SNAPSHOT else: return None - - def tag_annotated(self, annotation: str, message: str): - self.system_repository.run_checked('git', 'tag', '-a', annotation, '-m', message) - return self.system_repository.stdout - - def tag_annotated(self, annotation: str, message: str, count: int): - self.system_repository.run_checked('git', 'tag', '-a', annotation, '-m', message, f'HEAD~{count}') - return self.system_repository.stdout - - def get_current_branch(self): - self.system_repository.run_checked('git', 'branch', '--show-current') - return ''.join(self.system_repository.stdout).rstrip() - - def add_file(self, file_path: Path): - self.system_repository.run_checked('git', 'add', file_path) - return self.system_repository.stdout - - def commit(self, commit_message: str): - self.system_repository.run_checked('git', 'commit', '-m', commit_message) - return self.system_repository.stdout - - def push(self): - self.system_repository.run_checked('git', 'push') - return self.system_repository.stdout - - def checkout(self, branch: str): - self.system_repository.run_checked('git', 'checkout', branch) - return self.system_repository.stdout \ No newline at end of file diff --git a/infrastructure_api.py b/infrastructure_api.py index aacaa08..06e2d17 100644 --- a/infrastructure_api.py +++ b/infrastructure_api.py @@ -1,8 +1,10 @@ -from abc import ABC, abstractmethod +from abc import ABC, abstractmethod +from pathlib import Path import json import re import subprocess as sub + class FileHandler(ABC): @classmethod @@ -18,7 +20,8 @@ class FileHandler(ABC): case '.py': file_handler = PythonFileHandler() case _: - raise Exception(f'The file type "{config_file_type}" is not implemented') + raise Exception( + f'The file type "{config_file_type}" is not implemented') file_handler.config_file_path = file_path file_handler.config_file_type = config_file_type @@ -27,11 +30,12 @@ class FileHandler(ABC): @abstractmethod def parse(self) -> tuple[list[int], bool]: pass - + @abstractmethod def write(self, version_string): pass + class JsonFileHandler(FileHandler): def parse(self) -> tuple[list[int], bool]: @@ -48,7 +52,7 @@ class JsonFileHandler(FileHandler): with open(self.config_file_path, 'r+') as json_file: json_data = json.load(json_file) json_data['version'] = version_string - json_file.seek(0) + json_file.seek(0) json.dump(json_data, json_file, indent=4) json_file.truncate() @@ -59,15 +63,16 @@ class GradleFileHandler(FileHandler): with open(self.config_file_path, 'r') as gradle_file: contents = gradle_file.read() version_line = re.search("\nversion = .*", contents) - exception = Exception("Version not found in gradle file") + exception = Exception("Version not found in gradle file") if version_line is None: raise exception - + version_line = version_line.group() - version_string = re.search('[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', version_line) + version_string = re.search( + '[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', version_line) if version_string is None: raise exception - + version_string = version_string.group() is_snapshot = False if '-SNAPSHOT' in version_string: @@ -81,7 +86,8 @@ class GradleFileHandler(FileHandler): def write(self, version_string): with open(self.config_file_path, 'r+') as gradle_file: contents = gradle_file.read() - version_substitute = re.sub('\nversion = "[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?"', f'\nversion = "{version_string}"', contents) + version_substitute = re.sub( + '\nversion = "[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?"', f'\nversion = "{version_string}"', contents) gradle_file.seek(0) gradle_file.write(version_substitute) gradle_file.truncate() @@ -93,15 +99,16 @@ class PythonFileHandler(FileHandler): with open(self.config_file_path, 'r') as python_file: contents = python_file.read() version_line = re.search("\nversion = .*\n", contents) - exception = Exception("Version not found in gradle file") + exception = Exception("Version not found in gradle file") if version_line is None: raise exception - + version_line = version_line.group() - version_string = re.search('[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', version_line) + version_string = re.search( + '[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', version_line) if version_string is None: raise exception - + version_string = version_string.group() is_snapshot = False if '-SNAPSHOT' in version_string: @@ -115,26 +122,29 @@ class PythonFileHandler(FileHandler): def write(self, version_string): with open(self.config_file_path, 'r+') as python_file: contents = python_file.read() - version_substitute = re.sub('\nversion = "[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?"', f'\nversion = "{version_string}"', contents) + version_substitute = re.sub( + '\nversion = "[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?"', f'\nversion = "{version_string}"', contents) python_file.seek(0) python_file.write(version_substitute) python_file.truncate() + class ClojureFileHandler(FileHandler): def parse(self) -> tuple[list[int], bool]: with open(self.config_file_path, 'r') as clj_file: contents = clj_file.read() version_line = re.search("^\\(defproject .*\n", contents) - exception = Exception("Version not found in clj file") + exception = Exception("Version not found in clj file") if version_line is None: raise exception - + version_line = version_line.group() - version_string = re.search('[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', version_line) + version_string = re.search( + '[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', version_line) if version_string is None: raise exception - + version_string = version_string.group() is_snapshot = False if '-SNAPSHOT' in version_string: @@ -149,12 +159,14 @@ class ClojureFileHandler(FileHandler): with open(self.config_file_path, 'r+') as clj_file: clj_first = clj_file.readline() clj_rest = clj_file.read() - version_substitute = re.sub('[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', f'"{version_string}"\n', clj_first) + version_substitute = re.sub( + '[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', f'"{version_string}"\n', clj_first) clj_file.seek(0) clj_file.write(version_substitute) clj_file.write(clj_rest) clj_file.truncate() + class SystemAPI(): def __init__(self): @@ -175,3 +187,49 @@ class SystemAPI(): if len(self.stderr) > 0: raise Exception(f"Command failed with: {self.stderr}") + + +class GitApi(): + + def __init__(self): + self.system_repository = SystemAPI() + + def get_latest_n_commits(self, n: int): + self.system_repository.run_checked( + 'git', 'log', '--oneline', '--format="%s %b"', f'-n {n}') + return self.system_repository.stdout + + def get_latest_commit(self): + output = self.get_latest_n_commits(1) + return " ".join(output) + + def tag_annotated(self, annotation: str, message: str): + self.system_repository.run_checked( + 'git', 'tag', '-a', annotation, '-m', message) + return self.system_repository.stdout + + def tag_annotated(self, annotation: str, message: str, count: int): + self.system_repository.run_checked( + 'git', 'tag', '-a', annotation, '-m', message, f'HEAD~{count}') + return self.system_repository.stdout + + def get_current_branch(self): + self.system_repository.run_checked('git', 'branch', '--show-current') + return ''.join(self.system_repository.stdout).rstrip() + + def add_file(self, file_path: Path): + self.system_repository.run_checked('git', 'add', file_path) + return self.system_repository.stdout + + def commit(self, commit_message: str): + self.system_repository.run_checked( + 'git', 'commit', '-m', commit_message) + return self.system_repository.stdout + + def push(self): + self.system_repository.run_checked('git', 'push') + return self.system_repository.stdout + + def checkout(self, branch: str): + self.system_repository.run_checked('git', 'checkout', branch) + return self.system_repository.stdout diff --git a/test/test_infrastructure.py b/test/test_infrastructure.py index 6abf6ed..d7d28fd 100644 --- a/test/test_infrastructure.py +++ b/test/test_infrastructure.py @@ -1,3 +1,6 @@ +from domain import ReleaseType, Release +from infrastructure import GitRepository, VersionRepository, ReleaseRepository +from infrastructure_api import GitApi from pathlib import Path import sys import os @@ -5,20 +8,26 @@ import os # getting the name of the directory # where the this file is present. current = os.path.dirname(os.path.realpath(__file__)) - + # Getting the parent directory name # where the current directory is present. parent = os.path.dirname(current) - + # adding the parent directory to # the sys.path. sys.path.append(parent) - + # now we can import the module in the parent # directory. -from infrastructure import GitRepository, VersionRepository, ReleaseRepository -from domain import ReleaseType, Release + +def TestGitApi(GitApi): + def __init__(self, commit_string): + self.commit_string = commit_string + + def get_latest_commit(self): + return self.commit_string + def test_git_repository(): @@ -27,16 +36,15 @@ def test_git_repository(): repo = GitRepository.create_from_commit_string(commit_string) release_type = repo.get_release_type_from_latest_commit() - #test + # test assert release_type == ReleaseType.MAJOR - # init commit_string = "MINOR bla" repo = GitRepository.create_from_commit_string(commit_string) release_type = repo.get_release_type_from_latest_commit() - #test + # test assert release_type == ReleaseType.MINOR # init @@ -52,7 +60,7 @@ def test_git_repository(): repo = GitRepository.create_from_commit_string(commit_string) release_type = repo.get_release_type_from_latest_commit() - #test + # test assert release_type == ReleaseType.SNAPSHOT # init @@ -60,9 +68,10 @@ def test_git_repository(): repo = GitRepository.create_from_commit_string(commit_string) release_type = repo.get_release_type_from_latest_commit() - #test + # test assert release_type == None + def test_gradle(tmp_path): # init file_name = 'config.gradle' @@ -75,12 +84,13 @@ def test_gradle(tmp_path): # test repo = VersionRepository(f) version = repo.get_version() - version = version.create_release_version(ReleaseType.SNAPSHOT) + version = version.create_release_version(ReleaseType.SNAPSHOT) repo.write_file(version.get_version_string()) - + # check assert 'version = "12.4.678-SNAPSHOT"' in f.read_text() + def test_json(tmp_path): # init file_name = 'config.json' @@ -89,16 +99,17 @@ def test_json(tmp_path): f = tmp_path / file_name f.write_text(contents) - + # test repo = VersionRepository(f) version = repo.get_version() - version = version.create_release_version(ReleaseType.SNAPSHOT) + version = version.create_release_version(ReleaseType.SNAPSHOT) repo.write_file(version.get_version_string()) - + # check assert '"version": "123.123.456-SNAPSHOT"' in f.read_text() + def test_clojure(tmp_path): # init file_name = 'config.clj' @@ -113,10 +124,11 @@ def test_clojure(tmp_path): version = repo.get_version() version = version.create_release_version(ReleaseType.SNAPSHOT) repo.write_file(version.get_version_string()) - + # check assert '1.1.3-SNAPSHOT' in f.read_text() + def test_python(tmp_path): # init file_name = 'config.py' @@ -129,12 +141,13 @@ def test_python(tmp_path): # test repo = VersionRepository(f) version = repo.get_version() - version = version.create_release_version(ReleaseType.SNAPSHOT) + version = version.create_release_version(ReleaseType.SNAPSHOT) repo.write_file(version.get_version_string()) - + # check assert '3.1.3-SNAPSHOT' in f.read_text() + def test_release_repository(tmp_path): # init file_name = 'config.json' @@ -148,4 +161,4 @@ def test_release_repository(tmp_path): sut = ReleaseRepository(VersionRepository(f)) current_release = sut.get_current_release() - assert current_release.version is not None \ No newline at end of file + assert current_release.version is not None From c6ef13ca7cf9804f8d181ce84d2b98e085106dcd Mon Sep 17 00:00:00 2001 From: bom Date: Fri, 24 Feb 2023 11:02:11 +0100 Subject: [PATCH 095/119] mob --- infrastructure.py | 15 ++++++++------- test/test_infrastructure.py | 20 +++++++++++++------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/infrastructure.py b/infrastructure.py index 8234a98..d1d844a 100644 --- a/infrastructure.py +++ b/infrastructure.py @@ -31,13 +31,6 @@ class VersionRepository(): return version -class ReleaseRepository(): - def __init__(self, version_repository: VersionRepository): - self.version_repository = version_repository - - def get_current_release(self) -> Release: - pass - class ReleaseTypeRepository(): def __init__(self, git_api, environment_api=None): self.git_api = git_api @@ -55,3 +48,11 @@ class ReleaseTypeRepository(): return ReleaseType.SNAPSHOT else: return None + +class ReleaseRepository(): + def __init__(self, version_repository: VersionRepository, release_type_repository: ReleaseTypeRepository): + self.version_repository = version_repository + self.release_type_repository = release_type_repository + + def get_current_release(self) -> Release: + return Release(self.release_type_repository.get_release_type(), self.version_repository.get_version()) diff --git a/test/test_infrastructure.py b/test/test_infrastructure.py index d7d28fd..a767ef5 100644 --- a/test/test_infrastructure.py +++ b/test/test_infrastructure.py @@ -1,6 +1,3 @@ -from domain import ReleaseType, Release -from infrastructure import GitRepository, VersionRepository, ReleaseRepository -from infrastructure_api import GitApi from pathlib import Path import sys import os @@ -20,8 +17,11 @@ sys.path.append(parent) # now we can import the module in the parent # directory. +from domain import ReleaseType, Release +from infrastructure import ReleaseTypeRepository, VersionRepository, ReleaseRepository +from infrastructure_api import GitApi -def TestGitApi(GitApi): +class TestGitApi(GitApi): def __init__(self, commit_string): self.commit_string = commit_string @@ -158,7 +158,13 @@ def test_release_repository(tmp_path): f.write_text(contents) # test - sut = ReleaseRepository(VersionRepository(f)) - current_release = sut.get_current_release() + sut = ReleaseRepository(VersionRepository(f), ReleaseTypeRepository(TestGitApi('MINOR test'))) + release = sut.get_current_release() - assert current_release.version is not None + assert release.version is not None + +def test_release_type_repository(): + sut = ReleaseTypeRepository(TestGitApi('MINOR test')) + release_type = sut.get_release_type() + + assert release_type is ReleaseType.MINOR From 975de636ed258c8b7cd852fae650f1be1bdaa550 Mon Sep 17 00:00:00 2001 From: jerger Date: Fri, 24 Feb 2023 11:39:38 +0100 Subject: [PATCH 096/119] mob --- domain.py | 27 ++++++++++++- infrastructure.py | 2 +- services.py | 39 +++++-------------- test/test_infrastructure.py | 69 +++++++++++---------------------- test/test_infrastructure_api.py | 21 ++++++++++ 5 files changed, 79 insertions(+), 79 deletions(-) create mode 100644 test/test_infrastructure_api.py diff --git a/domain.py b/domain.py index c5193a6..7fa79bd 100644 --- a/domain.py +++ b/domain.py @@ -1,5 +1,11 @@ from enum import Enum + +class Config(): + def __init__(self, main_branch): + pass + + class ReleaseType(Enum): MAJOR = 0 MINOR = 1 @@ -10,7 +16,8 @@ class ReleaseType(Enum): class Version(): - def __init__(self, version_list: list): + def __init__(self, id: Path, version_list: list): + self.id = id self.version_list = version_list self.version_string = None self.is_snapshot = None @@ -55,6 +62,22 @@ class Version(): class Release(): - def __init__(self, release_type: ReleaseType, version: Version): + def __init__(self, release_type: ReleaseType, version: Version, current_branch: str): self.release_type = release_type self.version = version + self.current_branch = current_branch + + def release_version(self): + return self.version.create_release_version(self.release_type) + + def bump_version(self): + return self.version.create_bump_version() + + def validate(self, main_branch): + result = [] + if self.release_type is not None and main_branch != self.current_branch: + result.append(f"Releases are allowed only on {main_branch}") + return result + + def is_valid(self, main_branch): + return self.validate(main_branch).count < 1 diff --git a/infrastructure.py b/infrastructure.py index d1d844a..4bf963a 100644 --- a/infrastructure.py +++ b/infrastructure.py @@ -54,5 +54,5 @@ class ReleaseRepository(): self.version_repository = version_repository self.release_type_repository = release_type_repository - def get_current_release(self) -> Release: + def get_release(self) -> Release: return Release(self.release_type_repository.get_release_type(), self.version_repository.get_version()) diff --git a/services.py b/services.py index 6b46c12..e3e87cc 100644 --- a/services.py +++ b/services.py @@ -1,43 +1,22 @@ from pathlib import Path -from infrastructure import VersionRepository, GitRepository +from infrastructure import ReleaseRepository from domain import Version, ReleaseType + +# Todo: can be removed class InitReleaseService(): - def __init__(self, version_repo: VersionRepository): - if version_repo is None: - raise Exception('VersionRepo was not created. Did you run create_release_version()?') - self.version_repo = version_repo - - def __calculate_release_type(self, commit_string = None): - if commit_string is None: - return GitRepository().get_release_type_from_latest_commit() - else: - return GitRepository.create_from_commit_string(commit_string).get_release_type_from_latest_commit() - + def __init__(self, release_repo: ReleaseRepository): + self.release_repo = release_repo + def get_version(self): - return self.version_repo.get_version() - - def create_release_version(self, commit_string = None): - release_type = self.__calculate_release_type(commit_string) - version = self.get_version().create_release_version(release_type) - return version - - def create_bump_version(self): - version = self.get_version().create_bump_version() - return version + return self.release_repo.get_release().version class PrepareReleaseService(): - def __init__(self, version_repository: VersionRepository, git_repository: GitRepository, config_file: Path, main_branch: str): - self.version_repository = version_repository - self.git_repository = git_repository - self.main_branch = main_branch - self.config_file = config_file - - def run_tests(self): # maybe auto? - raise NotImplementedError + def __init__(self, release_repo: ReleaseRepository): + self.release_repo = release_repo def __write_and_commit_version(self, version: Version, commit_message: str): if self.main_branch != self.git_repository.get_current_branch(): diff --git a/test/test_infrastructure.py b/test/test_infrastructure.py index a767ef5..1e0ff48 100644 --- a/test/test_infrastructure.py +++ b/test/test_infrastructure.py @@ -29,49 +29,6 @@ class TestGitApi(GitApi): return self.commit_string -def test_git_repository(): - - # init - commit_string = "Major bla" - repo = GitRepository.create_from_commit_string(commit_string) - release_type = repo.get_release_type_from_latest_commit() - - # test - assert release_type == ReleaseType.MAJOR - - # init - commit_string = "MINOR bla" - repo = GitRepository.create_from_commit_string(commit_string) - release_type = repo.get_release_type_from_latest_commit() - - # test - assert release_type == ReleaseType.MINOR - - # init - commit_string = "PATCH bla" - repo = GitRepository.create_from_commit_string(commit_string) - release_type = repo.get_release_type_from_latest_commit() - - # test - assert release_type == ReleaseType.PATCH - - # init - commit_string = "SNAPSHOT bla" - repo = GitRepository.create_from_commit_string(commit_string) - release_type = repo.get_release_type_from_latest_commit() - - # test - assert release_type == ReleaseType.SNAPSHOT - - # init - commit_string = "bla" - repo = GitRepository.create_from_commit_string(commit_string) - release_type = repo.get_release_type_from_latest_commit() - - # test - assert release_type == None - - def test_gradle(tmp_path): # init file_name = 'config.gradle' @@ -159,12 +116,32 @@ def test_release_repository(tmp_path): # test sut = ReleaseRepository(VersionRepository(f), ReleaseTypeRepository(TestGitApi('MINOR test'))) - release = sut.get_current_release() + release = sut.get_release() + + assert release is not None - assert release.version is not None def test_release_type_repository(): sut = ReleaseTypeRepository(TestGitApi('MINOR test')) release_type = sut.get_release_type() - assert release_type is ReleaseType.MINOR + + sut = ReleaseTypeRepository(TestGitApi('MINOR bla')) + release_type = sut.get_release_type() + assert release_type is ReleaseType.MINOR + + sut = ReleaseTypeRepository(TestGitApi('Major bla')) + release_type = sut.get_release_type() + assert release_type == ReleaseType.MAJOR + + sut = ReleaseTypeRepository(TestGitApi('PATCH bla')) + release_type = sut.get_release_type() + assert release_type == ReleaseType.PATCH + + sut = ReleaseTypeRepository(TestGitApi('SNAPSHOT bla')) + release_type = sut.get_release_type() + assert release_type == ReleaseType.SNAPSHOT + + sut = ReleaseTypeRepository(TestGitApi('bla')) + release_type = sut.get_release_type() + assert release_type == None diff --git a/test/test_infrastructure_api.py b/test/test_infrastructure_api.py new file mode 100644 index 0000000..6d681da --- /dev/null +++ b/test/test_infrastructure_api.py @@ -0,0 +1,21 @@ +from pathlib import Path +import sys +import os + +# getting the name of the directory +# where the this file is present. +current = os.path.dirname(os.path.realpath(__file__)) + +# Getting the parent directory name +# where the current directory is present. +parent = os.path.dirname(current) + +# adding the parent directory to +# the sys.path. +sys.path.append(parent) + +# now we can import the module in the parent +# directory. + +from infrastructure_api import GitApi + From ef51db14032c663cdf7b505b776b52692d9bc54e Mon Sep 17 00:00:00 2001 From: bom Date: Tue, 28 Feb 2023 13:25:47 +0100 Subject: [PATCH 097/119] Fix some of the tests --- domain.py | 6 +++--- infrastructure.py | 7 ++++--- release_mixin.py | 8 ++++---- services.py | 8 ++++---- test/test_domain.py | 12 ++++++------ test/test_infrastructure.py | 2 +- test/test_services.py | 7 ++++--- 7 files changed, 26 insertions(+), 24 deletions(-) diff --git a/domain.py b/domain.py index 7fa79bd..bcfedeb 100644 --- a/domain.py +++ b/domain.py @@ -1,5 +1,5 @@ from enum import Enum - +from pathlib import Path class Config(): def __init__(self, main_branch): @@ -49,13 +49,13 @@ class Version(): return self.version_string def create_release_version(self, release_type: ReleaseType): - release_version = Version(self.version_list.copy()) + release_version = Version(self.id, self.version_list.copy()) release_version.is_snapshot = self.is_snapshot release_version.increment(release_type) return release_version def create_bump_version(self): - bump_version = Version(self.version_list.copy()) + bump_version = Version(self.id, self.version_list.copy()) bump_version.is_snapshot = self.is_snapshot bump_version.increment(ReleaseType.BUMP) return bump_version diff --git a/infrastructure.py b/infrastructure.py index 4bf963a..3989ed6 100644 --- a/infrastructure.py +++ b/infrastructure.py @@ -26,7 +26,7 @@ class VersionRepository(): self.file_handler = self.load_file() version_list, is_snapshot = self.parse_file() - version = Version(version_list) + version = Version(self.file, version_list) version.is_snapshot = is_snapshot return version @@ -50,9 +50,10 @@ class ReleaseTypeRepository(): return None class ReleaseRepository(): - def __init__(self, version_repository: VersionRepository, release_type_repository: ReleaseTypeRepository): + def __init__(self, version_repository: VersionRepository, release_type_repository: ReleaseTypeRepository, main_branch: str): self.version_repository = version_repository self.release_type_repository = release_type_repository + self.main_branch = main_branch def get_release(self) -> Release: - return Release(self.release_type_repository.get_release_type(), self.version_repository.get_version()) + return Release(self.release_type_repository.get_release_type(), self.version_repository.get_version(), self.main_branch) diff --git a/release_mixin.py b/release_mixin.py index 3d6c992..648c32a 100644 --- a/release_mixin.py +++ b/release_mixin.py @@ -2,7 +2,7 @@ import copy from ddadevops import DevopsBuild from ddadevops import execute from ddadevops import gopass_field_from_path, gopass_password_from_path -from infrastructure import VersionRepository, GitRepository +from infrastructure import VersionRepository, GitApi from services import InitReleaseService, PrepareReleaseService, TagAndPushReleaseService from domain import ReleaseType, Version @@ -28,7 +28,7 @@ class ReleaseMixin(DevopsBuild): self.config_file = release_mixin_config['config_file'] self.main_branch = release_mixin_config['main_branch'] self.version_repo = VersionRepository(self.config_file) - self.git_repo = GitRepository() + self.git_api = GitApi() self.release_version = None self.bump_version = None self.commit_string = None @@ -39,7 +39,7 @@ class ReleaseMixin(DevopsBuild): self.bump_version = self.release_version.create_bump_version() def prepare_release(self): - prepare_release_service = PrepareReleaseService(self.version_repo, self.git_repo, self.config_file, self.main_branch) + prepare_release_service = PrepareReleaseService(self.version_repo, self.git_api, self.config_file, self.main_branch) if self.release_version is None or self.bump_version is None: raise Exception('prepare_release was called before init_release') @@ -48,5 +48,5 @@ class ReleaseMixin(DevopsBuild): prepare_release_service.write_and_commit_bump(self.bump_version) def tag_and_push_release(self): - tag_and_push_release_service = TagAndPushReleaseService(self.git_repo) + tag_and_push_release_service = TagAndPushReleaseService(self.git_api) tag_and_push_release_service.tag_and_push_release(self.release_version) diff --git a/services.py b/services.py index e3e87cc..068ee3b 100644 --- a/services.py +++ b/services.py @@ -1,5 +1,5 @@ from pathlib import Path -from infrastructure import ReleaseRepository +from infrastructure import ReleaseRepository, GitApi from domain import Version, ReleaseType @@ -34,13 +34,13 @@ class PrepareReleaseService(): class TagAndPushReleaseService(): - def __init__(self, git_repository: GitRepository): - self.git_repository = git_repository + def __init__(self, git_api: GitApi): + self.git_api = git_api def tag_and_push_release(self, release_version: Version): annotation = 'v' + release_version.get_version_string() message = 'Release ' + annotation - self.git_repository.tag_annotated(annotation, message, 1) + self.git_api.tag_annotated(annotation, message, 1) # self.git_repository.push() diff --git a/test/test_domain.py b/test/test_domain.py index 68fd7bf..412b409 100644 --- a/test/test_domain.py +++ b/test/test_domain.py @@ -20,33 +20,33 @@ sys.path.append(parent) from domain import Version, ReleaseType from infrastructure import VersionRepository -def test_version(): - version = Version([1, 2, 3]) +def test_version(tmp_path): + version = Version(tmp_path, [1, 2, 3]) version.increment(ReleaseType.SNAPSHOT) assert version.get_version_string() == "1.2.3-SNAPSHOT" assert version.version_list == [1, 2, 3] assert version.is_snapshot - version = Version([1, 2, 3]) + version = Version(tmp_path, [1, 2, 3]) version.increment(ReleaseType.BUMP) assert version.get_version_string() == "1.2.4-SNAPSHOT" assert version.version_list == [1, 2, 4] assert version.is_snapshot - version = Version([1, 2, 3]) + version = Version(tmp_path, [1, 2, 3]) version.increment(ReleaseType.PATCH) assert version.get_version_string() == "1.2.4" assert version.version_list == [1, 2, 4] assert not version.is_snapshot - version = Version([1, 2, 3]) + version = Version(tmp_path, [1, 2, 3]) version.increment(ReleaseType.MINOR) assert version.get_version_string() == "1.3.0" assert version.version_list == [1, 3, 0] assert not version.is_snapshot - version = Version([1, 2, 3]) + version = Version(tmp_path, [1, 2, 3]) version.increment(ReleaseType.MAJOR) assert version.get_version_string() == "2.0.0" assert version.version_list == [2, 0, 0] diff --git a/test/test_infrastructure.py b/test/test_infrastructure.py index 1e0ff48..5ae86ec 100644 --- a/test/test_infrastructure.py +++ b/test/test_infrastructure.py @@ -115,7 +115,7 @@ def test_release_repository(tmp_path): f.write_text(contents) # test - sut = ReleaseRepository(VersionRepository(f), ReleaseTypeRepository(TestGitApi('MINOR test'))) + sut = ReleaseRepository(VersionRepository(f), ReleaseTypeRepository(TestGitApi('MINOR test')), 'main') release = sut.get_release() assert release is not None diff --git a/test/test_services.py b/test/test_services.py index 1eef213..4815e2c 100644 --- a/test/test_services.py +++ b/test/test_services.py @@ -19,7 +19,8 @@ sys.path.append(parent) from services import InitReleaseService from domain import ReleaseType -from infrastructure import VersionRepository +from infrastructure import VersionRepository, ReleaseRepository, ReleaseTypeRepository +from infrastructure_api import GitApi def test_init_release_service(tmp_path): # init @@ -30,9 +31,9 @@ def test_init_release_service(tmp_path): f = tmp_path / file_name f.write_text(contents) - repo = VersionRepository(f) + repo = ReleaseRepository(VersionRepository(f), ReleaseTypeRepository(GitApi()), 'main') release_service = InitReleaseService(repo) - version = release_service.create_release_version(commit_string='Release MINOR') + version = release_service.get_version().create_release_version(ReleaseType.MINOR) assert "123.124.0" in version.get_version_string() From 802dd7d57bdd95f695cc37b3cdcbe7e082fd28bb Mon Sep 17 00:00:00 2001 From: erik Date: Tue, 28 Feb 2023 14:55:41 +0100 Subject: [PATCH 098/119] WIP Remove prepare release service --- build.py | 1 - 1 file changed, 1 deletion(-) diff --git a/build.py b/build.py index 4e5ea5f..cfedec4 100644 --- a/build.py +++ b/build.py @@ -43,6 +43,5 @@ def initialize(project): def release(project): build = get_devops_build(project) - build.init_release() build.prepare_release() build.tag_and_push_release() From 39d60f6854ce2225dcdb3f13d01a29275bd8c612 Mon Sep 17 00:00:00 2001 From: erik Date: Tue, 28 Feb 2023 14:55:51 +0100 Subject: [PATCH 099/119] WIP Remove prepare release service --- release_mixin.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/release_mixin.py b/release_mixin.py index 648c32a..6064a96 100644 --- a/release_mixin.py +++ b/release_mixin.py @@ -2,8 +2,8 @@ import copy from ddadevops import DevopsBuild from ddadevops import execute from ddadevops import gopass_field_from_path, gopass_password_from_path -from infrastructure import VersionRepository, GitApi -from services import InitReleaseService, PrepareReleaseService, TagAndPushReleaseService +from infrastructure import GitApi, ReleaseRepository, ReleaseTypeRepository, VersionRepository +from services import PrepareReleaseService, TagAndPushReleaseService from domain import ReleaseType, Version def create_release_mixin_config(config_file, main_branch) -> dict: @@ -27,25 +27,23 @@ class ReleaseMixin(DevopsBuild): release_mixin_config = config['ReleaseMixin'] self.config_file = release_mixin_config['config_file'] self.main_branch = release_mixin_config['main_branch'] - self.version_repo = VersionRepository(self.config_file) self.git_api = GitApi() + self.release_type_repo = ReleaseTypeRepository(self.git_api) + self.version_repo = VersionRepository(self.config_file) + self.release_repo = ReleaseRepository(self.version_repo, self.release_type_repo, self.main_branch) self.release_version = None self.bump_version = None self.commit_string = None - def init_release(self): - init_service = InitReleaseService(self.version_repo) - self.release_version = init_service.create_release_version(self.commit_string) - self.bump_version = self.release_version.create_bump_version() - def prepare_release(self): - prepare_release_service = PrepareReleaseService(self.version_repo, self.git_api, self.config_file, self.main_branch) + prepare_release_service = PrepareReleaseService(self.release_repo) + if self.release_version is None or self.bump_version is None: raise Exception('prepare_release was called before init_release') # prepare_release_service.run_tests() # not implemented - prepare_release_service.write_and_commit_release(self.release_version) - prepare_release_service.write_and_commit_bump(self.bump_version) + prepare_release_service.write_and_commit_release() + prepare_release_service.write_and_commit_bump() def tag_and_push_release(self): tag_and_push_release_service = TagAndPushReleaseService(self.git_api) From 9bde301ee404fbfeabac61fcd691064b2dcb06af Mon Sep 17 00:00:00 2001 From: erik Date: Tue, 28 Feb 2023 14:56:07 +0100 Subject: [PATCH 100/119] Use release class Create it out of the given release_repo. Thus simplifying the call tree as we only access the release_repo in the service. --- services.py | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/services.py b/services.py index 068ee3b..da3c6a5 100644 --- a/services.py +++ b/services.py @@ -1,36 +1,29 @@ from pathlib import Path -from infrastructure import ReleaseRepository, GitApi +from infrastructure import VersionRepository, ReleaseRepository, GitApi from domain import Version, ReleaseType - -# Todo: can be removed -class InitReleaseService(): - - def __init__(self, release_repo: ReleaseRepository): - self.release_repo = release_repo - - def get_version(self): - return self.release_repo.get_release().version - - class PrepareReleaseService(): def __init__(self, release_repo: ReleaseRepository): self.release_repo = release_repo + self.release = release_repo.get_release() + self.git_api = GitApi() + self.main_branch = None def __write_and_commit_version(self, version: Version, commit_message: str): - if self.main_branch != self.git_repository.get_current_branch(): + if self.main_branch != self.git_api.get_current_branch(): raise Exception('Trying to release while not on main branch') - self.version_repository.write_file(version.get_version_string()) - self.git_repository.add_file(self.config_file) - self.git_repository.commit(commit_message) + self.release_repo.version_repository.write_file(version.get_version_string()) + self.git_api.add_file(self.release_repo.version_repository.file) + self.git_api.commit(commit_message) - def write_and_commit_release(self, release_version: Version): - self.__write_and_commit_version(release_version, commit_message=f'Release {release_version.get_version_string()}') + def write_and_commit_release(self): + self.__write_and_commit_version(self.release.release_version(), commit_message=f'Release {self.release.release_version().get_version_string()}') - def write_and_commit_bump(self, bump_version: Version): - self.__write_and_commit_version(bump_version, commit_message=f'Version bump') + def write_and_commit_bump(self): + self.__write_and_commit_version(self.release.bump_version(), commit_message=f'Version bump') + class TagAndPushReleaseService(): @@ -41,6 +34,6 @@ class TagAndPushReleaseService(): annotation = 'v' + release_version.get_version_string() message = 'Release ' + annotation self.git_api.tag_annotated(annotation, message, 1) - # self.git_repository.push() + # self.git_api.push() From 338cafc211e7a6c2f7317f725f8a979ca4e93368 Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 2 Mar 2023 14:59:06 +0100 Subject: [PATCH 101/119] Validate correct branch in Release object Avoids throwing exceptions in service and delegates validation to the domain level --- services.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/services.py b/services.py index da3c6a5..741e3e1 100644 --- a/services.py +++ b/services.py @@ -8,12 +8,10 @@ class PrepareReleaseService(): self.release_repo = release_repo self.release = release_repo.get_release() self.git_api = GitApi() - self.main_branch = None def __write_and_commit_version(self, version: Version, commit_message: str): - if self.main_branch != self.git_api.get_current_branch(): - raise Exception('Trying to release while not on main branch') - + self.release.validate(self.release_repo.main_branch) + self.release_repo.version_repository.write_file(version.get_version_string()) self.git_api.add_file(self.release_repo.version_repository.file) self.git_api.commit(commit_message) From 28344667dbf31ac673dccdb980821771c5121cee Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 2 Mar 2023 15:03:11 +0100 Subject: [PATCH 102/119] Remove unused fields from ReleaseMixin These are handled through repos and services instead --- release_mixin.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/release_mixin.py b/release_mixin.py index 6064a96..39b9ab7 100644 --- a/release_mixin.py +++ b/release_mixin.py @@ -31,16 +31,9 @@ class ReleaseMixin(DevopsBuild): self.release_type_repo = ReleaseTypeRepository(self.git_api) self.version_repo = VersionRepository(self.config_file) self.release_repo = ReleaseRepository(self.version_repo, self.release_type_repo, self.main_branch) - self.release_version = None - self.bump_version = None - self.commit_string = None def prepare_release(self): prepare_release_service = PrepareReleaseService(self.release_repo) - - if self.release_version is None or self.bump_version is None: - raise Exception('prepare_release was called before init_release') - # prepare_release_service.run_tests() # not implemented prepare_release_service.write_and_commit_release() prepare_release_service.write_and_commit_bump() From 9c9676b5b84303a0809faa6a77735c4a0d040205 Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 2 Mar 2023 15:04:30 +0100 Subject: [PATCH 103/119] Pass Release instead of Version object --- release_mixin.py | 2 +- services.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/release_mixin.py b/release_mixin.py index 39b9ab7..05f97da 100644 --- a/release_mixin.py +++ b/release_mixin.py @@ -40,4 +40,4 @@ class ReleaseMixin(DevopsBuild): def tag_and_push_release(self): tag_and_push_release_service = TagAndPushReleaseService(self.git_api) - tag_and_push_release_service.tag_and_push_release(self.release_version) + tag_and_push_release_service.tag_and_push_release(self.release_repo.get_release()) diff --git a/services.py b/services.py index 741e3e1..296bf98 100644 --- a/services.py +++ b/services.py @@ -1,6 +1,6 @@ from pathlib import Path from infrastructure import VersionRepository, ReleaseRepository, GitApi -from domain import Version, ReleaseType +from domain import Version, ReleaseType, Release class PrepareReleaseService(): @@ -28,8 +28,8 @@ class TagAndPushReleaseService(): def __init__(self, git_api: GitApi): self.git_api = git_api - def tag_and_push_release(self, release_version: Version): - annotation = 'v' + release_version.get_version_string() + def tag_and_push_release(self, release: Release): + annotation = 'v' + release.version.get_version_string() message = 'Release ' + annotation self.git_api.tag_annotated(annotation, message, 1) # self.git_api.push() From 24e8f65aafa8daad5256f339a06a4e544b38a4f7 Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 2 Mar 2023 15:07:31 +0100 Subject: [PATCH 104/119] Remove run_test line Will not be implemented, as it is already run in CI beforehand --- release_mixin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/release_mixin.py b/release_mixin.py index 05f97da..08c3a34 100644 --- a/release_mixin.py +++ b/release_mixin.py @@ -34,7 +34,6 @@ class ReleaseMixin(DevopsBuild): def prepare_release(self): prepare_release_service = PrepareReleaseService(self.release_repo) - # prepare_release_service.run_tests() # not implemented prepare_release_service.write_and_commit_release() prepare_release_service.write_and_commit_bump() From 8855a2c6b605095adbe1a93a76256bdb0aef72ec Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 2 Mar 2023 15:08:41 +0100 Subject: [PATCH 105/119] Remove unused imports --- infrastructure.py | 3 +-- release_mixin.py | 1 - services.py | 5 ++--- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/infrastructure.py b/infrastructure.py index 3989ed6..b367c74 100644 --- a/infrastructure.py +++ b/infrastructure.py @@ -1,6 +1,5 @@ from domain import Release, Version, ReleaseType -from infrastructure_api import FileHandler, SystemAPI, GitApi -from pathlib import Path +from infrastructure_api import FileHandler class VersionRepository(): diff --git a/release_mixin.py b/release_mixin.py index 08c3a34..f35725c 100644 --- a/release_mixin.py +++ b/release_mixin.py @@ -4,7 +4,6 @@ from ddadevops import execute from ddadevops import gopass_field_from_path, gopass_password_from_path from infrastructure import GitApi, ReleaseRepository, ReleaseTypeRepository, VersionRepository from services import PrepareReleaseService, TagAndPushReleaseService -from domain import ReleaseType, Version def create_release_mixin_config(config_file, main_branch) -> dict: config = {} diff --git a/services.py b/services.py index 296bf98..b3b5771 100644 --- a/services.py +++ b/services.py @@ -1,6 +1,5 @@ -from pathlib import Path -from infrastructure import VersionRepository, ReleaseRepository, GitApi -from domain import Version, ReleaseType, Release +from infrastructure import ReleaseRepository, GitApi +from domain import Version, Release class PrepareReleaseService(): From 5700b42098286b40f2ca190040891e1b897a15f8 Mon Sep 17 00:00:00 2001 From: erik Date: Thu, 2 Mar 2023 12:47:27 +0100 Subject: [PATCH 106/119] Remove redundant method --- infrastructure_api.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/infrastructure_api.py b/infrastructure_api.py index 06e2d17..2490686 100644 --- a/infrastructure_api.py +++ b/infrastructure_api.py @@ -203,11 +203,6 @@ class GitApi(): output = self.get_latest_n_commits(1) return " ".join(output) - def tag_annotated(self, annotation: str, message: str): - self.system_repository.run_checked( - 'git', 'tag', '-a', annotation, '-m', message) - return self.system_repository.stdout - def tag_annotated(self, annotation: str, message: str, count: int): self.system_repository.run_checked( 'git', 'tag', '-a', annotation, '-m', message, f'HEAD~{count}') From 455585f78cf7031a95429a4a22eedb4511663c1d Mon Sep 17 00:00:00 2001 From: erik Date: Thu, 2 Mar 2023 13:12:02 +0100 Subject: [PATCH 107/119] Remove Config class If the config class was to replace the config dict, further refactorings upstream would be needed. This is out of scope. --- domain.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/domain.py b/domain.py index bcfedeb..fe0805e 100644 --- a/domain.py +++ b/domain.py @@ -1,11 +1,6 @@ from enum import Enum from pathlib import Path -class Config(): - def __init__(self, main_branch): - pass - - class ReleaseType(Enum): MAJOR = 0 MINOR = 1 @@ -13,7 +8,6 @@ class ReleaseType(Enum): SNAPSHOT = 3 BUMP = None - class Version(): def __init__(self, id: Path, version_list: list): @@ -60,7 +54,6 @@ class Version(): bump_version.increment(ReleaseType.BUMP) return bump_version - class Release(): def __init__(self, release_type: ReleaseType, version: Version, current_branch: str): self.release_type = release_type From 9dc32ac92fd67cf18630633e6f045e33d4026970 Mon Sep 17 00:00:00 2001 From: erik Date: Thu, 2 Mar 2023 13:17:26 +0100 Subject: [PATCH 108/119] Refactor Class Name --- test/test_infrastructure.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/test_infrastructure.py b/test/test_infrastructure.py index 5ae86ec..3aa05c0 100644 --- a/test/test_infrastructure.py +++ b/test/test_infrastructure.py @@ -21,7 +21,7 @@ from domain import ReleaseType, Release from infrastructure import ReleaseTypeRepository, VersionRepository, ReleaseRepository from infrastructure_api import GitApi -class TestGitApi(GitApi): +class MyGitApi(GitApi): def __init__(self, commit_string): self.commit_string = commit_string @@ -115,33 +115,33 @@ def test_release_repository(tmp_path): f.write_text(contents) # test - sut = ReleaseRepository(VersionRepository(f), ReleaseTypeRepository(TestGitApi('MINOR test')), 'main') + sut = ReleaseRepository(VersionRepository(f), ReleaseTypeRepository(MyGitApi('MINOR test')), 'main') release = sut.get_release() assert release is not None def test_release_type_repository(): - sut = ReleaseTypeRepository(TestGitApi('MINOR test')) + sut = ReleaseTypeRepository(MyGitApi('MINOR test')) release_type = sut.get_release_type() assert release_type is ReleaseType.MINOR - sut = ReleaseTypeRepository(TestGitApi('MINOR bla')) + sut = ReleaseTypeRepository(MyGitApi('MINOR bla')) release_type = sut.get_release_type() assert release_type is ReleaseType.MINOR - sut = ReleaseTypeRepository(TestGitApi('Major bla')) + sut = ReleaseTypeRepository(MyGitApi('Major bla')) release_type = sut.get_release_type() assert release_type == ReleaseType.MAJOR - sut = ReleaseTypeRepository(TestGitApi('PATCH bla')) + sut = ReleaseTypeRepository(MyGitApi('PATCH bla')) release_type = sut.get_release_type() assert release_type == ReleaseType.PATCH - sut = ReleaseTypeRepository(TestGitApi('SNAPSHOT bla')) + sut = ReleaseTypeRepository(MyGitApi('SNAPSHOT bla')) release_type = sut.get_release_type() assert release_type == ReleaseType.SNAPSHOT - sut = ReleaseTypeRepository(TestGitApi('bla')) + sut = ReleaseTypeRepository(MyGitApi('bla')) release_type = sut.get_release_type() assert release_type == None From 0d36a8a66a8712fdb25cfb808a72798f6ea0b3db Mon Sep 17 00:00:00 2001 From: erik Date: Thu, 2 Mar 2023 14:26:34 +0100 Subject: [PATCH 109/119] Fix service test and cleanup services We now create a temporary git repository that creates a release commit. Remove whitespaces, unused imports. --- services.py | 13 +++++-------- test/test_services.py | 39 ++++++++++++++++++++++++++------------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/services.py b/services.py index b3b5771..dd55cec 100644 --- a/services.py +++ b/services.py @@ -13,17 +13,16 @@ class PrepareReleaseService(): self.release_repo.version_repository.write_file(version.get_version_string()) self.git_api.add_file(self.release_repo.version_repository.file) - self.git_api.commit(commit_message) + self.git_api.commit(commit_message) def write_and_commit_release(self): - self.__write_and_commit_version(self.release.release_version(), commit_message=f'Release {self.release.release_version().get_version_string()}') + self.__write_and_commit_version(self.release.release_version(), commit_message=f'Release v{self.release.release_version().get_version_string()}') - def write_and_commit_bump(self): - self.__write_and_commit_version(self.release.bump_version(), commit_message=f'Version bump') - + def write_and_commit_bump(self): + self.__write_and_commit_version(self.release.bump_version(), commit_message='Version bump') class TagAndPushReleaseService(): - + def __init__(self, git_api: GitApi): self.git_api = git_api @@ -32,5 +31,3 @@ class TagAndPushReleaseService(): message = 'Release ' + annotation self.git_api.tag_annotated(annotation, message, 1) # self.git_api.push() - - diff --git a/test/test_services.py b/test/test_services.py index 4815e2c..f82ca9b 100644 --- a/test/test_services.py +++ b/test/test_services.py @@ -1,3 +1,4 @@ +import pytest as pt from pathlib import Path import sys import os @@ -5,38 +6,50 @@ import os # getting the name of the directory # where the this file is present. current = os.path.dirname(os.path.realpath(__file__)) - + # Getting the parent directory name # where the current directory is present. parent = os.path.dirname(current) - + # adding the parent directory to # the sys.path. sys.path.append(parent) - + # now we can import the module in the parent # directory. -from services import InitReleaseService +from services import PrepareReleaseService from domain import ReleaseType from infrastructure import VersionRepository, ReleaseRepository, ReleaseTypeRepository from infrastructure_api import GitApi -def test_init_release_service(tmp_path): +def change_test_dir( tmp_path: Path, monkeypatch: pt.MonkeyPatch): + monkeypatch.chdir(tmp_path) + +def test_prepare_release_service(tmp_path: Path, monkeypatch: pt.MonkeyPatch): # init file_name = 'config.json' - with open(f'test/resources/{file_name}', 'r') as gradle_file: - contents = gradle_file.read() - + with open(f'test/resources/{file_name}', 'r') as json_file: + contents = json_file.read() f = tmp_path / file_name f.write_text(contents) + change_test_dir(tmp_path, monkeypatch) # change the context of the script execution to tmp_path + + git_api = GitApi() + git_api.init() + git_api.add_file(file_name) + git_api.commit("MINOR release") repo = ReleaseRepository(VersionRepository(f), ReleaseTypeRepository(GitApi()), 'main') - release_service = InitReleaseService(repo) - version = release_service.get_version().create_release_version(ReleaseType.MINOR) + prepare_release_service = PrepareReleaseService(repo) + prepare_release_service.main_branch = "main" + prepare_release_service.write_and_commit_release() - assert "123.124.0" in version.get_version_string() + latest_commit = git_api.get_latest_commit() - version = version.create_bump_version() + assert '"Release v123.124.0 "\n' in latest_commit - assert "123.124.1-SNAPSHOT" in version.get_version_string() \ No newline at end of file + prepare_release_service.write_and_commit_bump() + latest_commit = git_api.get_latest_commit() + + assert '"Version bump "\n' in latest_commit From 99877b3fbf1220d1601c6be8e0d719ac3d279e50 Mon Sep 17 00:00:00 2001 From: erik Date: Thu, 2 Mar 2023 14:31:15 +0100 Subject: [PATCH 110/119] Implement git init Allows creation of new git repo, may be of use in tests. --- infrastructure_api.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/infrastructure_api.py b/infrastructure_api.py index 2490686..125fb57 100644 --- a/infrastructure_api.py +++ b/infrastructure_api.py @@ -212,6 +212,9 @@ class GitApi(): self.system_repository.run_checked('git', 'branch', '--show-current') return ''.join(self.system_repository.stdout).rstrip() + def init(self): + self.system_repository.run_checked('git', 'init') + def add_file(self, file_path: Path): self.system_repository.run_checked('git', 'add', file_path) return self.system_repository.stdout From 8eb510d0346bb237aa8f78233c9f3f3842949651 Mon Sep 17 00:00:00 2001 From: erik Date: Thu, 2 Mar 2023 16:06:53 +0100 Subject: [PATCH 111/119] Base bump version on release version --- domain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain.py b/domain.py index fe0805e..a95e847 100644 --- a/domain.py +++ b/domain.py @@ -64,7 +64,7 @@ class Release(): return self.version.create_release_version(self.release_type) def bump_version(self): - return self.version.create_bump_version() + return self.release_version().create_bump_version() def validate(self, main_branch): result = [] From 63d3823d87a7626ec0542b031c66efd15dc86b8f Mon Sep 17 00:00:00 2001 From: erik Date: Thu, 2 Mar 2023 16:07:13 +0100 Subject: [PATCH 112/119] Implement release class test --- test/test_domain.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/test/test_domain.py b/test/test_domain.py index 412b409..abef6d1 100644 --- a/test/test_domain.py +++ b/test/test_domain.py @@ -17,12 +17,10 @@ sys.path.append(parent) # now we can import the module in the parent # directory. -from domain import Version, ReleaseType -from infrastructure import VersionRepository +from domain import Version, ReleaseType, Release -def test_version(tmp_path): +def test_version(tmp_path: Path): version = Version(tmp_path, [1, 2, 3]) - version.increment(ReleaseType.SNAPSHOT) assert version.get_version_string() == "1.2.3-SNAPSHOT" assert version.version_list == [1, 2, 3] @@ -51,3 +49,13 @@ def test_version(tmp_path): assert version.get_version_string() == "2.0.0" assert version.version_list == [2, 0, 0] assert not version.is_snapshot + +def test_release(tmp_path): + version = Version(tmp_path, [1, 2, 3]) + release = Release(ReleaseType.MINOR, version, "main") + + release_version = release.release_version() + assert release_version.get_version_string() in '1.3.0' + + bump_version = release.bump_version() + assert bump_version.get_version_string() in "1.3.1-SNAPSHOT" From 874106e1d44df8c5031df2b7c75a615b38167fb5 Mon Sep 17 00:00:00 2001 From: erik Date: Thu, 2 Mar 2023 16:08:18 +0100 Subject: [PATCH 113/119] Disable test_release_mixin --- test/test_release_mixin.py | 53 +++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/test/test_release_mixin.py b/test/test_release_mixin.py index d9d387a..e85360b 100644 --- a/test/test_release_mixin.py +++ b/test/test_release_mixin.py @@ -1,5 +1,6 @@ import sys import os +import pytest as pt from pathlib import Path from ddadevops import * @@ -40,29 +41,29 @@ def initialize(project, CONFIG_FILE): build = MyBuild(project, config) return build -def test_release_mixin(tmp_path): - - #init - with open(f'test/resources/config.json', 'r') as json_file: - contents = json_file.read() - - CONFIG_FILE = tmp_path / "config.json" - CONFIG_FILE.write_text(contents) - - base_dir = "." - project = Project(base_dir) - - # init - build = initialize(project, CONFIG_FILE) - build.commit_string = "MAJOR bla" - build.init_release() - release_version = build.release_version - - # test - assert "124.0.0" in release_version.get_version_string() - - # init - bump_version = build.bump_version - - # test - assert "124.0.1-SNAPSHOT" in bump_version.get_version_string() \ No newline at end of file +#def test_release_mixin(tmp_path): +# +# #init +# with open(f'test/resources/config.json', 'r') as json_file: +# contents = json_file.read() +# +# CONFIG_FILE = tmp_path / "config.json" +# CONFIG_FILE.write_text(contents) +# +# base_dir = "." +# project = Project(base_dir) +# +# # init +# build = initialize(project, CONFIG_FILE) +# build.commit_string = "MAJOR bla" +# build.init_release() +# release_version = build.release_version +# +# # test +# assert "124.0.0" in release_version.get_version_string() +# +# # init +# bump_version = build.bump_version +# +# # test +# assert "124.0.1-SNAPSHOT" in bump_version.get_version_string() \ No newline at end of file From 21e941cce001eaf975a9419f63b77e781d2ee815 Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 2 Mar 2023 16:26:52 +0100 Subject: [PATCH 114/119] Import GitApi from correct module --- release_mixin.py | 3 ++- services.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/release_mixin.py b/release_mixin.py index f35725c..c488f51 100644 --- a/release_mixin.py +++ b/release_mixin.py @@ -2,7 +2,8 @@ import copy from ddadevops import DevopsBuild from ddadevops import execute from ddadevops import gopass_field_from_path, gopass_password_from_path -from infrastructure import GitApi, ReleaseRepository, ReleaseTypeRepository, VersionRepository +from infrastructure import ReleaseRepository, ReleaseTypeRepository, VersionRepository +from infrastructure_api import GitApi from services import PrepareReleaseService, TagAndPushReleaseService def create_release_mixin_config(config_file, main_branch) -> dict: diff --git a/services.py b/services.py index dd55cec..76125a7 100644 --- a/services.py +++ b/services.py @@ -1,4 +1,5 @@ -from infrastructure import ReleaseRepository, GitApi +from infrastructure import ReleaseRepository +from infrastructure_api import GitApi from domain import Version, Release class PrepareReleaseService(): From 58e6473747fcb72198aa6965f8f5e7f5d6d65a62 Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 2 Mar 2023 16:37:42 +0100 Subject: [PATCH 115/119] Split tag_and_push_release into two --- release_mixin.py | 3 ++- services.py | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/release_mixin.py b/release_mixin.py index c488f51..06648e7 100644 --- a/release_mixin.py +++ b/release_mixin.py @@ -39,4 +39,5 @@ class ReleaseMixin(DevopsBuild): def tag_and_push_release(self): tag_and_push_release_service = TagAndPushReleaseService(self.git_api) - tag_and_push_release_service.tag_and_push_release(self.release_repo.get_release()) + tag_and_push_release_service.tag_release(self.release_repo.get_release()) + # tag_and_push_release_service.push_release() diff --git a/services.py b/services.py index 76125a7..42b8543 100644 --- a/services.py +++ b/services.py @@ -27,8 +27,10 @@ class TagAndPushReleaseService(): def __init__(self, git_api: GitApi): self.git_api = git_api - def tag_and_push_release(self, release: Release): + def tag_release(self, release: Release): annotation = 'v' + release.version.get_version_string() message = 'Release ' + annotation self.git_api.tag_annotated(annotation, message, 1) - # self.git_api.push() + + def push_release(self): + self.git_api.push() From 950d76c883d52622ecbf2f5ae4310bd0a753b1e5 Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 2 Mar 2023 16:55:49 +0100 Subject: [PATCH 116/119] Add get_latest_tag to GitApi also rename system_repo to system_api --- infrastructure_api.py | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/infrastructure_api.py b/infrastructure_api.py index 125fb57..bd86de5 100644 --- a/infrastructure_api.py +++ b/infrastructure_api.py @@ -192,42 +192,46 @@ class SystemAPI(): class GitApi(): def __init__(self): - self.system_repository = SystemAPI() + self.system_api = SystemAPI() def get_latest_n_commits(self, n: int): - self.system_repository.run_checked( + self.system_api.run_checked( 'git', 'log', '--oneline', '--format="%s %b"', f'-n {n}') - return self.system_repository.stdout + return self.system_api.stdout def get_latest_commit(self): output = self.get_latest_n_commits(1) return " ".join(output) def tag_annotated(self, annotation: str, message: str, count: int): - self.system_repository.run_checked( + self.system_api.run_checked( 'git', 'tag', '-a', annotation, '-m', message, f'HEAD~{count}') - return self.system_repository.stdout + return self.system_api.stdout + + def get_latest_tag(self): + self.system_api.run_checked('git', 'describe', '--tags', '--abbrev=0') + return self.system_api.stdout def get_current_branch(self): - self.system_repository.run_checked('git', 'branch', '--show-current') - return ''.join(self.system_repository.stdout).rstrip() + self.system_api.run_checked('git', 'branch', '--show-current') + return ''.join(self.system_api.stdout).rstrip() def init(self): - self.system_repository.run_checked('git', 'init') + self.system_api.run_checked('git', 'init') def add_file(self, file_path: Path): - self.system_repository.run_checked('git', 'add', file_path) - return self.system_repository.stdout + self.system_api.run_checked('git', 'add', file_path) + return self.system_api.stdout def commit(self, commit_message: str): - self.system_repository.run_checked( + self.system_api.run_checked( 'git', 'commit', '-m', commit_message) - return self.system_repository.stdout + return self.system_api.stdout def push(self): - self.system_repository.run_checked('git', 'push') - return self.system_repository.stdout + self.system_api.run_checked('git', 'push') + return self.system_api.stdout def checkout(self, branch: str): - self.system_repository.run_checked('git', 'checkout', branch) - return self.system_repository.stdout + self.system_api.run_checked('git', 'checkout', branch) + return self.system_api.stdout From d5d8b8e7e1cc82bcef335ccc838de7ee1ece3318 Mon Sep 17 00:00:00 2001 From: bom Date: Thu, 2 Mar 2023 16:56:09 +0100 Subject: [PATCH 117/119] Add test for TagAndPushReleaseService --- test/test_services.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/test/test_services.py b/test/test_services.py index f82ca9b..3bcf1b0 100644 --- a/test/test_services.py +++ b/test/test_services.py @@ -18,8 +18,7 @@ sys.path.append(parent) # now we can import the module in the parent # directory. -from services import PrepareReleaseService -from domain import ReleaseType +from services import PrepareReleaseService, TagAndPushReleaseService from infrastructure import VersionRepository, ReleaseRepository, ReleaseTypeRepository from infrastructure_api import GitApi @@ -53,3 +52,28 @@ def test_prepare_release_service(tmp_path: Path, monkeypatch: pt.MonkeyPatch): latest_commit = git_api.get_latest_commit() assert '"Version bump "\n' in latest_commit + +def test_tag_and_push_release_service(tmp_path: Path, monkeypatch: pt.MonkeyPatch): + # init + file_name = 'config.json' + with open(f'test/resources/{file_name}', 'r') as json_file: + contents = json_file.read() + f = tmp_path / file_name + f.write_text(contents) + change_test_dir(tmp_path, monkeypatch) # change the context of the script execution to tmp_path + + git_api = GitApi() + git_api.init() + git_api.add_file(file_name) + git_api.commit("MINOR release") + + repo = ReleaseRepository(VersionRepository(f), ReleaseTypeRepository(GitApi()), 'main') + + prepare_release_service = PrepareReleaseService(repo) + prepare_release_service.main_branch = "main" + prepare_release_service.write_and_commit_release() + + tag_and_push_release_service = TagAndPushReleaseService(git_api) + tag_and_push_release_service.tag_release(repo.get_release()) + + assert 'v123.124.0\n' in git_api.get_latest_tag() From 81458c6acc6fa058cea79d8ca7dc9538d730153c Mon Sep 17 00:00:00 2001 From: erik Date: Thu, 2 Mar 2023 18:16:03 +0100 Subject: [PATCH 118/119] dd todos from review --- release_mixin.py | 4 ++-- test/test_infrastructure_api.py | 3 +-- test/test_services.py | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/release_mixin.py b/release_mixin.py index 06648e7..03f3c99 100644 --- a/release_mixin.py +++ b/release_mixin.py @@ -22,13 +22,13 @@ def add_versions(config, release_version, bump_version) -> dict: class ReleaseMixin(DevopsBuild): - def __init__(self, project, config): + def __init__(self, project, config): # todo: create services in init, dont expose repos etc in api super().__init__(project, config) release_mixin_config = config['ReleaseMixin'] self.config_file = release_mixin_config['config_file'] self.main_branch = release_mixin_config['main_branch'] self.git_api = GitApi() - self.release_type_repo = ReleaseTypeRepository(self.git_api) + self.release_type_repo = ReleaseTypeRepository(self.git_api) # maybe get from env? self.version_repo = VersionRepository(self.config_file) self.release_repo = ReleaseRepository(self.version_repo, self.release_type_repo, self.main_branch) diff --git a/test/test_infrastructure_api.py b/test/test_infrastructure_api.py index 6d681da..d914d32 100644 --- a/test/test_infrastructure_api.py +++ b/test/test_infrastructure_api.py @@ -17,5 +17,4 @@ sys.path.append(parent) # now we can import the module in the parent # directory. -from infrastructure_api import GitApi - +from infrastructure_api import GitApi # todo: implement from services example diff --git a/test/test_services.py b/test/test_services.py index 3bcf1b0..4b3926c 100644 --- a/test/test_services.py +++ b/test/test_services.py @@ -25,7 +25,7 @@ from infrastructure_api import GitApi def change_test_dir( tmp_path: Path, monkeypatch: pt.MonkeyPatch): monkeypatch.chdir(tmp_path) -def test_prepare_release_service(tmp_path: Path, monkeypatch: pt.MonkeyPatch): +def test_prepare_release_service(tmp_path: Path, monkeypatch: pt.MonkeyPatch): # todo: maybe use mocks for service api tests # init file_name = 'config.json' with open(f'test/resources/{file_name}', 'r') as json_file: From 4fbbe26714660bdfb5f67b8ccb08c78b965f5364 Mon Sep 17 00:00:00 2001 From: erik Date: Thu, 2 Mar 2023 18:19:18 +0100 Subject: [PATCH 119/119] Update arch documentation --- architecture.png | Bin 66829 -> 0 bytes doc/arch.md | 1 + doc/architecture.png.png | Bin 0 -> 112256 bytes 3 files changed, 1 insertion(+) delete mode 100644 architecture.png create mode 100644 doc/arch.md create mode 100644 doc/architecture.png.png diff --git a/architecture.png b/architecture.png deleted file mode 100644 index f169118d4b9211ccb8028a0de39f1da71f2fd8a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66829 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 Ge*AC7Pe-Nz diff --git a/doc/arch.md b/doc/arch.md new file mode 100644 index 0000000..201223d --- /dev/null +++ b/doc/arch.md @@ -0,0 +1 @@ +[![](https://mermaid.ink/img/pako:eNrtV99vmzAQ_lcsPzUSrUjIj8JDpUqb9jSpaqs9TJGQg6_UGxhmTNes6v8-E0MCGIem6-MiBYnz3Xd3vu8ulxccZRRwgAv4VQKP4BMjsSApUp81r54CIolEvDmbup6D6sdEn21KllB0fnWFbiEBUsBX9sx4gMKQcSbD8CwX2Q9l76Ao4w8sDh9YArUtiSR7IhI6pge3HWnl4QuT1zlrYU8sirXgfpvDLeRZwWQmti07DVRb50RIFrGccNk2tEB_A1GwjA_Cmhm2sWvL4yEP4ho-neEMHZQSxsONIDx6tGenD4BTo7oO8tV3NVLaXIBChVBoaVOFPc7MrfixcNDCtRXoRkPU8jsQTyyCVsbGbfQZMwigdQaPbHccg-zn0WflQb2TzEF8jHIt_FCqM5uTrl3HUfeo0wgVeqJQChlGWZoyacBrTS2kMCi2u2mdBOjQly2c8eh7kAPtUyXxpMVG-Ia6PjfENuwkI3TXjw3ymy0VhVTJ3mza4m5l46A6ozBhhZwY92bJ6yi1zPaort1psNSALYUALrv9v29zs2p972Pn4wgfMAJ-CyYhJJzWjO5352h3B6jpNxupOmOwfunWMhKgFEMD6FgPjIVnHXlGxo27OpzJO8baiS2oQ9iRveFtIQXj8ajvZhIRSs_erNydVeYP0QeyZ1Om-QnUqdSHyn06d2xI_4nzYcRpXeWRdWBPr4GFpyLaym3xzLbySBLvLjovi8fDRDqyqt6T-JrTG6Vu3XE6S-g-E5vhu3wNh61hrI7GIU9BakqnQ-GZVEnSf4zh5HSaICrDAfbYbG0du7v6HqmqJ3ZwCkLt4FT9m3qpJGssHyGFNVadhSkRP9d4zV-VHilldrflEQ4eSFKAg8ucKg_1X6-e9DOt2m0vVLv89yxTSlKU-hUHL_gZB-fT6cWlt_Td5dzz1ACdL30Hb5Xcny4uZjN_7k2ni5k39y5fHfxnBzG7cD135fnzxdJfrObu6vUveFPSvA?type=png)](https://mermaid.live/edit#pako:eNrtV99vmzAQ_lcsPzUSrUjIj8JDpUqb9jSpaqs9TJGQg6_UGxhmTNes6v8-E0MCGIem6-MiBYnz3Xd3vu8ulxccZRRwgAv4VQKP4BMjsSApUp81r54CIolEvDmbup6D6sdEn21KllB0fnWFbiEBUsBX9sx4gMKQcSbD8CwX2Q9l76Ao4w8sDh9YArUtiSR7IhI6pge3HWnl4QuT1zlrYU8sirXgfpvDLeRZwWQmti07DVRb50RIFrGccNk2tEB_A1GwjA_Cmhm2sWvL4yEP4ho-neEMHZQSxsONIDx6tGenD4BTo7oO8tV3NVLaXIBChVBoaVOFPc7MrfixcNDCtRXoRkPU8jsQTyyCVsbGbfQZMwigdQaPbHccg-zn0WflQb2TzEF8jHIt_FCqM5uTrl3HUfeo0wgVeqJQChlGWZoyacBrTS2kMCi2u2mdBOjQly2c8eh7kAPtUyXxpMVG-Ia6PjfENuwkI3TXjw3ymy0VhVTJ3mza4m5l46A6ozBhhZwY92bJ6yi1zPaort1psNSALYUALrv9v29zs2p972Pn4wgfMAJ-CyYhJJzWjO5352h3B6jpNxupOmOwfunWMhKgFEMD6FgPjIVnHXlGxo27OpzJO8baiS2oQ9iRveFtIQXj8ajvZhIRSs_erNydVeYP0QeyZ1Om-QnUqdSHyn06d2xI_4nzYcRpXeWRdWBPr4GFpyLaym3xzLbySBLvLjovi8fDRDqyqt6T-JrTG6Vu3XE6S-g-E5vhu3wNh61hrI7GIU9BakqnQ-GZVEnSf4zh5HSaICrDAfbYbG0du7v6HqmqJ3ZwCkLt4FT9m3qpJGssHyGFNVadhSkRP9d4zV-VHilldrflEQ4eSFKAg8ucKg_1X6-e9DOt2m0vVLv89yxTSlKU-hUHL_gZB-fT6cWlt_Td5dzz1ACdL30Hb5Xcny4uZjN_7k2ni5k39y5fHfxnBzG7cD135fnzxdJfrObu6vUveFPSvA) \ No newline at end of file diff --git a/doc/architecture.png.png b/doc/architecture.png.png new file mode 100644 index 0000000000000000000000000000000000000000..cef9e94942292c52776003e4850d7b62092a9797 GIT binary patch literal 112256 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 rM;NnK>^x-Z|BY_{zx|~$b$p`!`Oo2kUe