diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 000000000..abc7f4b02 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,36 @@ +name-template: 'v$RESOLVED_VERSION' +tag-template: 'v$RESOLVED_VERSION' +categories: + - title: '🚀 Features' + labels: + - 'feature' + - 'enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - title: '🧰 Maintenance' + labels: + - 'chore' + - 'dependency' + - 'dependencies' +exclude-labels: + - 'skip-changelog' +change-template: '- $TITLE @$AUTHOR (#$NUMBER)' +change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. +version-resolver: + major: + labels: + - 'major' + minor: + labels: + - 'minor' + patch: + labels: + - 'patch' + default: patch +template: | + ## Changes + + $CHANGES \ No newline at end of file diff --git a/.github/workflows/baker.XanitizerConfig b/.github/workflows/baker.XanitizerConfig deleted file mode 100644 index efa70e99c..000000000 --- a/.github/workflows/baker.XanitizerConfig +++ /dev/null @@ -1,774 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void sample.package.path.ExampleClass.dummyMethod() - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - lib/jfxrt.jar - lib/jfr.jar - - - - - - - - diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..76218f0a4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,117 @@ +name: CI +on: + push: + branches: ["*"] + tags: ["v*"] + +permissions: + contents: read + +jobs: + ci: + name: Build and test + permissions: + contents: write + pull-requests: write + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + cache: sbt + - name: Run tests and coverage + run: |- + cp .jvmopts-ci .jvmopts + sbt coverage test coverageReport && bash <(curl -s https://codecov.io/bash) + - name: Prepare draft release notes + # Run this step only for pushes to the master branch or for tags starting with 'v' + if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v')) }} + continue-on-error: true + uses: release-drafter/release-drafter@v6 + with: + config-name: release-drafter.yml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + publish-sonatype: + permissions: + contents: write + pull-requests: write + name: Publish to Sonatype + # Publish to Sonatype only on tags starting with 'v' (stable releases) and not from forked repositories + if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') && ! github.event.repository.fork }} + needs: [ci] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + cache: sbt + - name: Publish to Sonatype + run: sbt ci-release + env: + PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} + PGP_SECRET: ${{ secrets.PGP_SECRET }} + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} + publish-azure: + permissions: + contents: write + pull-requests: write + name: Publish to Azure + # Publish to Azure only for non-tag references (e.g., all the pushes to default and other branches) and not from forked repositories + if: ${{ ! startsWith(github.ref, 'refs/tags/') && ! github.event.repository.fork }} + needs: [ci] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + cache: sbt + - name: Publish to Azure + run: sbt "clean; +aetherDeploy; project interaction-example-make-payment-and-ship-items; +aetherDeploy; project interaction-example-reserve-items; +aetherDeploy; project bakery-client-example; +aetherDeploy; project bakery-kafka-listener-example; +aetherDeploy" + env: + AZURE_FEEDUSER: ${{ secrets.AZURE_FEEDUSER }} + AZURE_FEEDPASSWORD: ${{ secrets.AZURE_FEEDPASSWORD }} + AZURE_FEEDURL: ${{ secrets.AZURE_FEEDURL }} + dependency-submission: + name: Submit dependency graph + continue-on-error: true + needs: [ci] + # run on 1) master branch + # do not run on pull requests + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + cache: sbt + - name: Submit dependency graph + uses: scalacenter/sbt-dependency-submission@v2 + \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..66006caa5 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,41 @@ +name: Release +on: + workflow_dispatch: + inputs: + release_tag: + description: 'Please specify the release version. i.e. v1.0.0 (must start with "v") with an optional suffix like -RC1' + required: true + type: string + +permissions: + contents: read + +jobs: + release: + name: Release + permissions: + contents: write + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.PAT_GIT_TAG_PUSH }} + + - name: Check release tag format + run: | + if [[ ! "${{ inputs.release_tag }}" =~ ^v[0-9]+(\.[0-9]+)*(-[A-Za-z0-9]+)?$ ]]; then + echo "Error: release_tag must start with 'v' followed by a version number i.e. v4.1.0, optionally with a suffix like -RC1." + exit 1 + fi + + - name: Set Git user name and email + run: | + git config --global user.name "${{ github.actor }}" + git config --global user.email "${{ github.actor }}@users.noreply.github.com" + + - name: Create release tag and push (triggers the CI run) + run: |- + git tag ${{ inputs.release_tag }} -m "${{ inputs.release_tag }}" + git push origin ${{ inputs.release_tag }} diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml deleted file mode 100644 index 8982011a9..000000000 --- a/.github/workflows/security.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Security Scan - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up JDK 17 - uses: actions/setup-java@v2 - with: - distribution: adopt - java-version: 17 - - - name: Dependency check - run: sbt test:compile updateClassifiers - -# Dependency check is broken now -# - name: Dependency check -# run: sbt dependencyCheck test:compile updateClassifiers -# -# - name: Xanitizer Security Analysis -# uses: RIGS-IT/xanitizer-action@v1 -# with: -# license: ${{ secrets.XANITIZER_LICENSE }} -# configFile: .github/workflows/baker.XanitizerConfig - - - uses: actions/upload-artifact@v2 - with: - name: Xanitizer-Reports - path: | - *-Findings-List.pdf - *-Findings-List.sarif diff --git a/.jvmopts-ci b/.jvmopts-ci new file mode 100644 index 000000000..1eefd1807 --- /dev/null +++ b/.jvmopts-ci @@ -0,0 +1,13 @@ +# This is used to configure the sbt instance in CI + +-XX:+UseG1GC +-Xms3G +-Xmx3G +-Xss2M +-XX:+AlwaysActAsServerClassMachine +-XX:ReservedCodeCacheSize=256m +-XX:MaxGCPauseMillis=750 +-XX:+UseCompressedOops +-XX:MetaspaceSize=512M +-XX:-ClassUnloadingWithConcurrentMark +-Djava.security.egd=file:/dev/./urandom \ No newline at end of file diff --git a/.scala-steward.conf b/.scala-steward.conf index ed190bb11..c43bd8825 100644 --- a/.scala-steward.conf +++ b/.scala-steward.conf @@ -1,5 +1,7 @@ # PRs are created only again after 7 days since the last PR has passed pullRequests.frequency = "7 days" +# Add custom labels to PRs to be used by the automated release process +pullRequests.customLabels = [ "dependency" ] # group all patches, and typelevel updates pullRequests.grouping = [ { name = "patches", "title" = "Patch updates", "filter" = [{"version" = "patch"}] }, @@ -9,14 +11,13 @@ pullRequests.grouping = [ updates.pin = [ { groupId = "org.typelevel", artifactId = "cats-effect", version = "2." }, { groupId = "co.fs2", version = "2." }, + { groupId = "org.http4s", version = "0.22." }, { groupId = "com.github.fd4s", artifactId = "fs2-kafka", version = "1."}, { groupId = "com.github.cb372", artifactId = "cats-retry", version = "2."}, { groupId = "org.scala-graph", version = "1."} ] -# ignore kotlin compiler version updates and http4s updates for cats-effect 3.x compatible versions +# ignore kotlin compiler version updates updates.ignore = [ - { groupId = "org.http4s", version = "0.23." }, - { groupId = "org.http4s", version = "1." }, { groupId = "org.jetbrains.kotlin" } ] \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 148f30de8..000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,358 +0,0 @@ -# Changelog - -# 3.1.0 - -- Upgraded to akka 2.6.8 mainly to support [Lightbend's split brain resolver](https://doc.akka.io/docs/akka/2.6.8/split-brain-resolver.html) -- Removed unused configuration from `com.ing.baker.runtime.akka.AkkaBakerConfig`, namely `readConfig`. Deleting this line from your config does not impact functionality. - -# 3.0.7 - -[Baker 3 release notes](https://ing-bank.github.io/baker/sections/versions/baker-3-release-notes/) - -# 2.0.4 - -- Fixed an issue that Map types would be translated to List types in the Java and Scala dsl - -# 2.0.3 - -- New feature: Dealing with blocked interactions. - -An interaction can become blocked. Either directly or after retry was exhausted. - -It is now possible to continue a process instance manually from that state. - -Either force another retry: - -``` java -baker.retryInteraction(recipeInstanceId, interactionName); -``` - -Or manually specify the interaction output. - -``` java -baker.resolveInteraction(recipeInstanceId, interactionName, event); -``` - -Note, this *ONLY* works if the interaction has become blocked. - -- New feature: Stop the retry of an interaction. - -Sometimes, perhaps because of a bug in the code, a interaction is failing with the same exception each retry. - -You can now stop this retry from continuing unnecessarily. - -``` java -baker.stopRetryingInteraction(recipeInstanceId, interactionName); -``` - -More documentation these features [here](https://ing-bank.github.io/baker/documentation/process-execution/#incident-resolving). - -## 2.0.2 -- New feature: Split Brain Resolver is added. See the [documentation](https://ing-bank.github.io/baker/documentation/split-brain-resolver/) of this feature for more information. - -## 2.0.1 -- Bugfix: Fixed the broken Event Receive Period in the 2.0.0 release. - -## 2.0.0 - -### Bugfixes - -- [#112](https://github.com/ing-bank/baker/issues/112) User specified timeout in processEvent calls were ignored. -- [#103](https://github.com/ing-bank/baker/issues/103) NullPointer exception when providing implementations directly to `JBaker` -- [#50](https://github.com/ing-bank/baker/issues/50) Updated protobuf to 3.5.1 to fix a security related issue (CVE-2015-5237) - -### New features - -- [#110](https://github.com/ing-bank/baker/issues/110) Sensory events are now displayed with a different style in the visual recipe. -- [#111](https://github.com/ing-bank/baker/issues/111) Executed interactions are now displayed with a green color in the visual recipe. -- [#108](https://github.com/ing-bank/baker/issues/108) Deprecated the sieve concept, only 'normal' interactions remain. -- [#99](https://github.com/ing-bank/baker/issues/99) Added the option to get an index of all process instances. -``` java -// ProcessMetaData contains 'recipeId', 'recipeInstanceId' and 'createdTime' fields. -java.util.Set index = baker.getIndex(); -``` - -- Added a list of event names to the process state that can be retreived from memory: -``` java -java.util.List eventNames = - baker.getProcessState(recipeInstanceId).getEventNames(); -``` - -- Added an optional correlation id to events to achieve idempotent event delivery -``` java -String correlationId = ... // -SensoryEventStatus status = - baker.processEvent(recipeInstanceId, event, correlationId); - -switch (status) { - case Received: - // the event was received normally (first time) - case AlreadyReceived: - // an event with this correlation id was already received -} -``` -- New feature: Optionally allow `@javax.inject.Named` to be used instead of `@RequiresIngredient`. -``` java -import javax.inject.Named; -import com.example.CustomerData; - -interface ExampleInteraction { - - class Output { } - - @FiresEvent(oneOf = { Output.class } ) - Output apply(@Named("customer") CustomerData customer); -} -``` -- Recipe information can be retrieved at runtime: -``` java -import com.ing.baker.runtime.common.RecipeInformation; - -RecipeInformation info = baker.getRecipe(recipeId); - -// any errors (missing interaction implementations) -info.getErrors(); - -// the time the recipe was added / created. -info.getRecipeCreatedTime(); -``` -- Allow listeners to subscribe to internal baker events, see documentation [here](https://ing-bank.github.io/baker/documentation/event-listener). - -## 1.3.5 - -- Fixed [#142](https://github.com/ing-bank/baker/issues/142): Exception in receiveRecover when having called bake for non existing process id - -## 1.3.4 - -- Implemented workaround for [#133](https://github.com/ing-bank/baker/issues/133): Exception in receiveRecover of ProcessIndexActor - -## 1.3.3 - -- Fixed [#68](https://github.com/ing-bank/baker/issues/68): catch and handle exceptions thrown by exception strategy handlers -- Bugfix in the RetryWithIncrementalBackoff (integer overflow) - -## 1.3.2 - -- Fixed [#91](https://github.com/ing-bank/baker/issues/91): allow java.util.Set as Ingredient types - -## 1.3.1 - -- Bugfix in protobuf serialization - -## 1.3.0 - -- Use protobuf for all persisted messages, including compiled recipes -- Enable cross builds for scala 2.11 and 2.12 -- Introduced a specific ProcessDeletedException for when process instances where removed after specified retetion period - -## 1.2.1 - -- Bugfix, do not allow null values for ingredients from interactions -- Bugfix, accidental recursion (self-call) in JBaker.getVisualRecipe - -## 1.2.0 -- Baker now has it's own type system that does not depend on java classes. This means that: - - The baker runtime does not have any knowledge/dependency of java classes of ingredients - - CompiledRecipes are completely serializable and can be stored & recovered later. - - Process history/state can be recovered even if ingredient classes change or disappear. -- Baker now supports multiple recipes - this is done by adding recipes to Baker, this will return a recipeId. - This Id can in turn be used to Bake instances of that recipe. -- CompilesRecipes are now also persisted if using Persistence Actors - This ensures a process always uses the recipe it was Baked for. - If a Recipe is added with the same name we see this a new recipe. - Meaning the already baked instances will use the original Recipe they are baked with for the execution. -- It is possible to add interaction implementations at runtime. -- Scala/Java DSL alignment: renamed `handleEvent -> processEvent` and `handleEventAsync -> processEventAsync` -- Exhausted Retry events are now always set with a default name. Its not possible to give the event anymore. - Now its just a configuration with a boolean on the RetryWithIncrementalBackoff retry strategy - -## 1.1.17 -- Fixed [#72](https://github.com/ing-bank/baker/issues/72): do not join to Akka cluster when there are persistence problems. `akka.cluster.seed-nodes` configuration should be renamed to `baker.cluster.seed-nodes` to support this "late cluster join" feature. - -## 1.1.16 -- Fixed [#55](https://github.com/ing-bank/baker/issues/55): Improved readability of duration of scheduled retry log entries -- DSL syntactic sugar: Variable name is inferred as 'name' for the Ingredients, Events and Recipes, if not defined explicitly. - Example: - ```scala - val myIngredient = Ingredient[String] - myIngredient.name shouldBe "myIngredient" - ``` -- Scala/Java dsl alignment: JBaker.processEventAsync now supports a timeout parameter - -## 1.1.15 -- Fixed [#62](https://github.com/ing-bank/baker/issues/62): internal IdleStop message for the ProcessInstance actor is now configured to be serialized by Kryo -- Fixed [#59](https://github.com/ing-bank/baker/issues/59): disabled the usage of Akka Distributed Data until the growing memory issue in the shared process metadata feature is solved. -- Fixed [#57](https://github.com/ing-bank/baker/issues/57): configured the default actor idle-timeout as 5 minutes (baker.actor.idle-timeout config can be overridden in the application.conf) -- Fixed [#56](https://github.com/ing-bank/baker/issues/56): fixed one unhandled message warning for ProcessInstanceEvent -- deprecated the methods that return the generated SVG -- cleaned up unnecessary Passivate pattern from ProcessInstance -- removed unnecessary debug logging of Akka Distributed Data updates -- log when scheduling a retry on actor startup - -## 1.1.14 -- Fixed [#53](https://github.com/ing-bank/baker/issues/53): EventListeners are now notified of retry-exhausted events. -- Fixed [#49](https://github.com/ing-bank/baker/issues/49): improved error message when receiving invalid sensory event -- Added a method to CompiledRecipe to obtain an SVG String: ```getVisualRecipeAsSVG``` -- Updated to Akka 2.5.6 - -## 1.1.13 -- Fixed [#47](https://github.com/ing-bank/baker/issues/47): added writeVisualrecipeToSVGFile to write away the CompiledRecipe to a file. -- Fixed [#46](https://github.com/ing-bank/baker/issues/46): it's now allowed to require an event on the name only, however it's still possible to require on event class in the JavaDSL. -- Fixed [#45](https://github.com/ing-bank/baker/issues/45): validate that ingredients are not of primitive type after compilation. - -## 1.1.12 -- Fixed [#43](https://github.com/ing-bank/baker/issues/43): interaction is not compiled into petrinet when requires a renamed optional ingredient provided through a renamed event - -## 1.1.11 -- bugfix: predefining a higher order type other then option/optional crashed the recipe validations when compiling a recipe - -## 1.1.10 -- bugfix: consuming a token when handling a sensory event with maxFireLimit was not calculated correctly - -## 1.1.9 - -- Added objenesis dependency runtime module since it is required by kryo at runtime -- Added InteractionFailureStrategy.FireEvent(SomeEvent.class) option to javadsl (feature from 1.1.8) - -## 1.1.8 - -- Added an option to fire an event immediately on technical failures in interactions. -- Improved logging/error messages in various places - -## 1.1.7 - -- Implemented a better way to provide interaction implementations for the scala dsl. -- Improved the WebShop example (using the scala dsl) - -## 1.1.6 - -- Fixed a NullPointer exception involving recipes without a retention period. - -## 1.1.5 - -- Bug fixes involving ingredients with generic types - -## 1.1.4 - -- Added an option to set a retention period on recipes. When set, after the retention period has passed, the process -instance will be stopped and all persisted messages (history) will be deleted. - -- Fixed a NullPointer exception involving interactions with Void/Unit return types. - -## 1.1.3 - -- We now preserve the full generic type for ingredients. (e.g. Option) - -## 1.1.2 - -- Bugfix involving predefined ingredients for optional values. - -## 1.1.1 - -- The compiler now gives a validation error for empty recipes. - -## 1.1.0 - -..... - - -## 1.0.9 - -- Changed serialization mechanism to allow custom Akka serializers for ingredients where before only kryo was used. - - You might see these messages for ingredient types without bindings: - - Ingredient 'foo' of type 'com.example.Foo' cannot be serialized - Please add a binding in your application.conf like this: - akka.actor.serialization-bindings { - "com.example.Foo" = kryo - } -- Added validations and better error logging if null is provided by a Interaction or Event - -## 1.0.8 -- Added the functionality that if an Ingredient of Java Optional or Scala Option is needed but not provided its provided as empty. -- Side note (no impact for baker users): kagera library is merged into baker, therefore Baker has two new artifacts now: petrinet-api and petrinet-akka. -- slf4j MDC field 'kageraEvent' is renamed to 'petrinetEvent' due to new Petri-net modules in baker. -- If io.kagera packages are used/imported in your application (maybe in logback files), you need to change them as com.ing.baker.petrinet -- baker.conf file disables java serialization, you don't need to have 'akka.actor.allow-java-serialization = off' setting anymore in your application.conf files - -## 1.0.7 -- In your application.conf files, it is mandatory to include also baker.conf file which will set some sensible defaults for baker. -``` -include "baker.conf" -``` - -## 1.0.6 -- Using a better consistent hash function for place/transition identifiers in the petri net -- IMPORTANT: This change is not backwards compatible, on going processing cannot be resumed after a restart - -## 1.0.4 -- Migrated to Akka 2.5.x -- A local event bus is implemented so that a listener can be registered to act on baker events. Ex: -```scala - baker.registerEventListener(new EventListener { - /** - * Called when an event occurred. - * - * @param recipeInstanceId The process id for which the event occurred. - * @param event The event. - */ - override def processEvent(recipeInstanceId: String, event: RuntimeEvent): Unit = ??? - }) -``` - -## 1.0.1 -- Fixed a bug in the runtime that it could not bind an ingredient multiple times as parameter for an interaction -- Removed the JCompiledRecipe and moved the functionality to the CompiledRecipe -- Added a validation to the java Recipe that @sFiresEvent and @ProvidesIngredient are not used at the same time. - -## 1.0.0 -- baker modules are reorganized to support mode modularity and loose coupling -- created separate modules: compiler, intermediate-language, recipe-dsl, runtime -- design time and runtime parts of baker are now separate and ideally could be updated independently - -## 0.2.19 -- enabled kryo serialization for all baker events that extend from com.ing.baker.api.Event interface -- disabled default Java serialization -- IMPORTANT: It is mandatory to extend/implement com.ing.baker.api.Event interface in your event classes. Kryo serialization understands plain pojo classes, so you may continue using lombok annotations -- IMPORTANT: It is mandatory to extend/implement com.ing.baker.api.Ingredient interface in your custom Ingredient data types. Kryo serialization understands all standard Java/Scala data types + guava + jodatime types. - -## 0.2.16 -- clients of baker can use protocol buffers serialized events with a proper protobuf configuration -- bug fix in the passivation logic - -## 0.2.15 -- bug fix for the actor passivation logic - -## 0.2.14 -- bug fixes related to Akka sharding - -## 0.2.13 -- Baker can now persist encrypted ingredients if enabled. This feature is disabled by default. - - To enable encryption you need to have the following two settings in your application.conf file. - ``` - baker.encryption.enabled = on - baker.encryption.secret = someStrongSecretText - ``` - -## 0.2.6 -- Now allow the adding of Events that have no ingredients nor have any preconditions. - For these events a dummy ingredient is created to allow it to be added to the petri net. - This dummy ingredient is filtered out during visualisation of the recipe. - -## 0.2.5 -- Fixed a bug in visualisation of a recipe that had max interaction count. - -## 0.2.4 -- JBaker now expects a List\ instead of List\ -- Sieves do not need to have a implementation added anymore -- Added a check to the recipe compilation to check if a Sieves have a default constructor - -## 0.2.3 -- Allowed changing the Interaction name in the JInteractionDescriptor -- By changing a interaction name now multiple of the same interaction are allowed in a recipe - -## 0.2.2 -- Baker and JBaker now expect a List\ instead of a List\ for the implementations diff --git a/README.md b/README.md index 47fa1cde6..e1583505e 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@
Baker Logo -[![Build Status](https://dev.azure.com/BakeryOSS/BakeryOSS/_apis/build/status/baker-oss-pr?branchName=master)](https://dev.azure.com/BakeryOSS/BakeryOSS/_build?definitionId=9&_a=summary) -[![Maven Central](https://img.shields.io/maven-central/v/com.ing.baker/baker-runtime_2.12.svg?label=Maven%20Central&logo=apachemaven)](https://search.maven.org/artifact/com.ing.baker/baker-runtime_2.12) -[![codecov.io](http://codecov.io/github/ing-bank/baker/coverage.svg?branch=master)](https://codecov.io/gh/ing-bank/baker?branch=master) +[![Build Status](https://github.com/ing-bank/baker/actions/workflows/ci.yml/badge.svg)](https://github.com/ing-bank/baker/actions/workflows/ci.yml) +[![Maven Central](https://img.shields.io/maven-central/v/com.ing.baker/baker-runtime_2.13.svg?label=Maven%20Central&logo=apachemaven)](https://search.maven.org/artifact/com.ing.baker/baker-runtime_2.13) +[![Code Coverage Status](https://codecov.io/gh/ing-bank/baker/graph/badge.svg)](https://codecov.io/gh/ing-bank/baker)
# Baker @@ -152,36 +152,36 @@ modules. com.ing.baker baker-recipe-dsl_2.13 - 3.6.3 + 4.1.0 com.ing.baker baker-compiler_2.13 - 3.6.3 + 4.1.0 com.ing.baker baker-runtime_2.13 - 3.6.3 + 4.1.0 ``` ### Gradle ```groovy -implementation 'com.ing.baker:baker-recipe-dsl_2.13:3.6.3' -implementation 'com.ing.baker:baker-compiler_2.13:3.6.3' -implementation 'com.ing.baker:baker-runtime_2.13:3.6.3' +implementation 'com.ing.baker:baker-recipe-dsl_2.13:4.1.0' +implementation 'com.ing.baker:baker-compiler_2.13:4.1.0' +implementation 'com.ing.baker:baker-runtime_2.13:4.1.0' ``` ### Scala SBT -Baker gets cross compiled and released for both Scala 2.12 and 2.13. +Baker gets compiled and released for both 2.13. ```scala -libraryDependencies += "com.ing.baker" % "baker-recipe-dsl_2.13" % "3.6.3" -libraryDependencies += "com.ing.baker" % "baker-compiler_2.13" % "3.6.3" -libraryDependencies += "com.ing.baker" % "baker-runtime_2.13" % "3.6.3" +libraryDependencies += "com.ing.baker" % "baker-recipe-dsl_2.13" % "4.1.0" +libraryDependencies += "com.ing.baker" % "baker-compiler_2.13" % "4.1.0" +libraryDependencies += "com.ing.baker" % "baker-runtime_2.13" % "4.1.0" ``` ## Contributing diff --git a/azure-build.yaml b/azure-build.yaml deleted file mode 100644 index dabfcf3a7..000000000 --- a/azure-build.yaml +++ /dev/null @@ -1,116 +0,0 @@ -pr: - branches: - include: - - master - - ing-bank:master - -parameters: - - name: ForcePublish - default: false - type: boolean - displayName: Force publish (otherwise nothing is published from PR/branch builds) - -pool: - vmImage: 'ubuntu-latest' - -variables: - - group: BakeryOSSFeedCredentials - - name: CACHE - value: $(HOME)/.cache - - name: IVY_HOME - value: $(Pipeline.Workspace)/.ivy2 - - name: CACHE_RUN_ID - value: "20211019" - -steps: - - task: Bash@3 - displayName: Scan sources - inputs: - targetType: 'inline' - script: | - echo $(Build.Repository.Name) - failOnStderr: true - - - task: DownloadSecureFile@1 - displayName: Get source scan script - name: sourceScan - inputs: - secureFile: sourceScan.sh - - - task: Bash@3 - displayName: Scan sources - condition: ne(variables['System.PullRequest.IsFork'], true) - inputs: - targetType: 'inline' - script: | - bash $(sourceScan.secureFilePath) - failOnStderr: true - - - task: CacheBeta@1 - displayName: Package resolver cache - inputs: - key: cache_$(CACHE_RUN_ID) - path: '$(CACHE)' - - - task: CacheBeta@1 - displayName: Ivy resolver cache - inputs: - key: ivy_home_$(CACHE_RUN_ID) - path: '$(IVY_HOME)' - - - task: Bash@3 - displayName: Generate artifacts' version - inputs: - targetType: 'inline' - script: bash ./set-version.sh - - - task: JavaToolInstaller@0 - inputs: - versionSpec: '17' - jdkArchitectureOption: 'x64' - jdkSourceOption: 'PreInstalled' - cleanDestinationDirectory: false - - - task: Bash@3 - displayName: 'Building and testing main Baker' - inputs: - targetType: 'inline' - script: | - sbt -Divy.home=${IVY_HOME} -Dsbt.ivy.home=${IVY_HOME} coverage test coverageReport && bash <(curl -s https://codecov.io/bash) - failOnStderr: false - - - ${{ if or(eq(parameters['ForcePublish'],true),eq(variables['Build.SourceBranch'], 'refs/heads/master')) }}: - - task: Bash@3 - displayName: 'Prepare publishing credentials' - inputs: - targetType: 'inline' - script: printf "realm=pkgs.dev.azure.com\nhost=pkgs.dev.azure.com\nuser=${FEEDUSER}\npassword=${FEEDPASSWORD}\n" > ~/.credentials - env: - FEEDPASSWORD: $(feedPassword) # secrets extracted explicitly, other vars - from the group - - - script: sbt -Divy.home=${IVY_HOME} -Dsbt.ivy.home=${IVY_HOME} "clean; +aetherDeploy; project interaction-example-make-payment-and-ship-items; +aetherDeploy; project interaction-example-reserve-items; +aetherDeploy; project bakery-client-example; +aetherDeploy; project bakery-kafka-listener-example; +aetherDeploy" - displayName: 'Building and publishing Baker with examples' - - - task: Docker@2 - displayName: Login to docker - inputs: - command: login - containerRegistry: INGBakeryAtDockerHub - - - task: Bash@3 - displayName: 'Prepare docker build environments' - inputs: - targetType: 'inline' - script: | - sbt bakery-state/Docker/stage - VERSION=$(./get-version.sh) - echo "##vso[task.setvariable variable=BAKER_VERSION]${VERSION}" - - - task: Docker@2 - displayName: 'Build and push baker image' - inputs: - workingDirectory: $(Build.SourcesDirectory)/bakery/state/target/docker - command: buildAndPush - repository: ingbakery/baker - dockerfile: $(Build.SourcesDirectory)/bakery/state/target/docker/stage/Dockerfile - tags: latest,$(BAKER_VERSION) diff --git a/azure-release.yaml b/azure-release.yaml deleted file mode 100644 index 1fa582740..000000000 --- a/azure-release.yaml +++ /dev/null @@ -1,66 +0,0 @@ -trigger: - batch: true - branches: - include: - - master - -pool: - vmImage: 'ubuntu-latest' - -variables: - - group: SonaTypeCredentials - - group: gpg key - - group: github_token - -steps: - - task: DownloadSecureFile@1 - name: pubring - inputs: - secureFile: pubring.asc - - - task: DownloadSecureFile@1 - name: secring - inputs: - secureFile: secring.asc - - - task: Bash@3 - displayName: 'Build, sign and publish' - inputs: - targetType: 'inline' - script: | - export SourceBranchName=`echo "$(Build.SourceBranch)" | cut -b 12-` # cuts the first 12 characters, namely "refs/heads/" - echo "Will build on branch $SourceBranchName." - echo $(tty) - ls -la $(pubring.secureFilePath) - ls -la $(secring.secureFilePath) - mkdir ~/.gnupg - chmod -R 700 ~/.gnupg - echo "pinentry-mode loopback" >> ~/.gnupg/gpg.conf - - gpg --import $(pubring.secureFilePath) - gpg --batch --import $(secring.secureFilePath) - gpg --version - gpg --list-keys - gpg --list-secret-keys - - git checkout "$SourceBranchName" - git config --global user.email "bakery-release@github.com" - git config --global user.name "baker release pipeline" - git remote set-url origin "https://${GIT_AUTH_USER}:${GIT_AUTH_TOKEN}@github.com/ing-bank/baker" - - sbt -Divy.home=${IVY_HOME} -Dsbt.ivy.home=${IVY_HOME} "release cross with-defaults" - - rm -rf $(pubring.secureFilePath) - rm -rf $(secring.secureFilePath) - rm -rf ~/.gnupg - - env: - # secrets must be extracted explicitly - USERNAME: $(username) - PASSWORD: $(password) - # passphrase will be passed to sbt plugin from here - PGP_PASSPHRASE: $(gpg key) - # neeeded for gpg to work on headless azure agents - GPG_TTY: /dev/pts/0 - GIT_AUTH_USER: $(github_user) - GIT_AUTH_TOKEN: $(github_token) diff --git a/build.sbt b/build.sbt index bf04b18cc..6d1db8ad7 100644 --- a/build.sbt +++ b/build.sbt @@ -116,9 +116,7 @@ lazy val crossBuildSettings: Seq[Setting[_]] = Seq( crossScalaVersions := supportedScalaVersions ) -lazy val defaultModuleSettings212: Seq[Setting[_]] = commonSettings ++ dependencyOverrideSettings - -lazy val defaultModuleSettings: Seq[Setting[_]] = crossBuildSettings ++ defaultModuleSettings212 +lazy val defaultModuleSettings: Seq[Setting[_]] = crossBuildSettings ++ commonSettings ++ dependencyOverrideSettings lazy val yPartialUnificationSetting: Seq[Setting[_]] = Seq( Compile / scalacOptions ++= { @@ -217,7 +215,9 @@ lazy val `baker-interface-kotlin`: Project = project.in(file("core/baker-interfa junitInterface, slf4jApi ) - ).dependsOn(`baker-interface`) + ).dependsOn( + `baker-interface`, + `baker-recipe-dsl`) lazy val `baker-akka-runtime`: Project = project.in(file("core/akka-runtime")) .settings(defaultModuleSettings) @@ -649,7 +649,7 @@ lazy val `bakery-integration-tests`: Project = project.in(file("bakery/integrati scalaCheck ) ) - .dependsOn( + .dependsOn( `baker-http-client`, `bakery-client-example`, `interaction-example-make-payment-and-ship-items`, diff --git a/core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/FunctionInteractionInstance.kt b/core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/FunctionInteractionInstance.kt index 318d886dd..acb7ef2a0 100644 --- a/core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/FunctionInteractionInstance.kt +++ b/core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/FunctionInteractionInstance.kt @@ -1,130 +1,96 @@ -@file:OptIn(ExperimentalStdlibApi::class, ExperimentalReflectionOnLambdas::class) - package com.ing.baker.runtime.kotlindsl +import com.ing.baker.recipe.kotlindsl.Recipe import com.ing.baker.runtime.javadsl.EventInstance import com.ing.baker.runtime.javadsl.IngredientInstance import com.ing.baker.runtime.javadsl.InteractionInstance import com.ing.baker.runtime.javadsl.InteractionInstanceInput import com.ing.baker.types.Converters +import scala.collection.Seq import scala.collection.immutable.Map import java.util.* import java.util.concurrent.CompletableFuture -import kotlin.reflect.jvm.ExperimentalReflectionOnLambdas import kotlin.reflect.jvm.javaType import kotlin.reflect.jvm.reflect import kotlin.reflect.typeOf -inline fun functionInteractionInstance( - name: String, - noinline function: (T1) -> R -): InteractionInstance { - - val types = listOf( - javaTypeOf() - ) - - val params = function.reflect()?.parameters ?: error("Cannot read parameters") +class FunctionInteractionInstance(private val types: List, val function: Function, private val name: String): InteractionInstance() { + private val params = function.reflect()?.parameters ?: error("Cannot read parameters") - return object : InteractionInstance() { + override fun execute( + input: MutableList, metadata: Map + ): CompletableFuture> { + return run(input) + } - override fun execute( - input: MutableList, metadata: Map - ): CompletableFuture> { - return run(input) - } + override fun execute(input: Any, metaData: Map): CompletableFuture> { + TODO("Not yet implemented") + } - override fun execute(input: Any, metaData: Map): CompletableFuture> { - TODO("Not yet implemented") - } + override fun input(): List = + types + .zip(params) + .map { (type, param) -> + InteractionInstanceInput( + Optional.ofNullable(param.name), + Converters.readJavaType(type) + ) + } - override fun input(): List = - types - .zip(params) - .map { (type, param) -> - InteractionInstanceInput( - Optional.ofNullable(param.name), - Converters.readJavaType(type) - ) - } - - override fun name(): String { - return "\$SieveInteraction\$${name}" ?: error("Cannot read class name") - } + override fun name(): String { + return "\$SieveInteraction\$${name}" ?: error("Cannot read class name") + } - override fun run(input: MutableList): CompletableFuture> { - try { - val args = types - .zip(input.toList()) { type, param -> param.value.`as`(type) } - .toTypedArray() - val res = function.invoke( - args[0] as T1 - ) - val event = EventInstance.from(mapOf(name to res)) - val eventInstance = EventInstance("\$SieveEvent\$$name", event.providedIngredients) - return CompletableFuture.completedFuture(Optional.ofNullable(eventInstance)) - } catch (e: Exception) { - return CompletableFuture.failedFuture(e) + override fun run(input: MutableList): CompletableFuture> { + try { + val args = types + .zip(input.toList()) { type, param -> param.value.`as`(type) } + .toTypedArray() + val res = when (args.size) { + 1 -> (function as Function1).invoke(args[0]) + 2 -> (function as Function2).invoke(args[0], args[1]) + 3 -> (function as Function3).invoke(args[0], args[1], args[2]) + else -> throw IllegalStateException("More than 3 parameters not supported") } + val event = EventInstance.from(mapOf(name to res)) + val eventInstance = EventInstance("\$SieveEvent\$$name", event.providedIngredients) + return CompletableFuture.completedFuture(Optional.ofNullable(eventInstance)) + } catch (e: Exception) { + return CompletableFuture.failedFuture(e) } } } -inline fun functionInteractionInstance( +fun Recipe.sieveInstances(): List = sieves().asJava.map { sieve -> + FunctionInteractionInstance(sieve.javaTypes().asJava, sieve.function() as Function<*>, sieve.name()) +} + subRecipes().asJava.filterIsInstance().flatMap(Recipe::sieveInstances) + +val scala.collection.Seq.asJava get() = scala.jdk.CollectionConverters.SeqHasAsJava(this).asJava() +val scala.collection.Set.asJava get() = scala.jdk.CollectionConverters.SetHasAsJava(this).asJava() + +inline fun functionInteractionInstance( name: String, - noinline function: (T1, T2) -> R + noinline function: (T1) -> R ): InteractionInstance { val types = listOf( javaTypeOf(), - javaTypeOf() ) - val params = function.reflect()?.parameters ?: error("Cannot read parameters") - - return object : InteractionInstance() { - - override fun execute( - input: MutableList, metadata: Map - ): CompletableFuture> { - return run(input) - } + return FunctionInteractionInstance(types, function, name) +} - override fun execute(input: Any, metaData: Map): CompletableFuture> { - TODO("Not yet implemented") - } +inline fun functionInteractionInstance( + name: String, + noinline function: (T1, T2) -> R +): InteractionInstance { - override fun input(): List = - types - .zip(params) - .map { (type, param) -> - InteractionInstanceInput( - Optional.ofNullable(param.name), - Converters.readJavaType(type) - ) - } - - override fun name(): String { - return "\$SieveInteraction\$${name}" ?: error("Cannot read class name") - } + val types = listOf( + javaTypeOf(), + javaTypeOf(), + ) - override fun run(input: MutableList): CompletableFuture> { - try { - val args = types - .zip(input.toList()) { type, param -> param.value.`as`(type) } - .toTypedArray() - val res = function.invoke( - args[0] as T1, - args[1] as T2 - ) - val event = EventInstance.from(mapOf(name to res)) - val eventInstance = EventInstance("\$SieveEvent\$$name", event.providedIngredients) - return CompletableFuture.completedFuture(Optional.ofNullable(eventInstance)) - } catch (e: Exception) { - return CompletableFuture.failedFuture(e) - } - } - } + return FunctionInteractionInstance(types, function, name) } inline fun functionInteractionInstance( @@ -135,55 +101,10 @@ inline fun functionInteractionInstance( val types = listOf( javaTypeOf(), javaTypeOf(), - javaTypeOf() + javaTypeOf(), ) - val params = function.reflect()?.parameters ?: error("Cannot read parameters") - - return object : InteractionInstance() { - - override fun execute( - input: MutableList, metadata: Map - ): CompletableFuture> { - return run(input) - } - - override fun execute(input: Any, metaData: Map): CompletableFuture> { - TODO("Not yet implemented") - } - - override fun input(): List = - types - .zip(params) - .map { (type, param) -> - InteractionInstanceInput( - Optional.ofNullable(param.name), - Converters.readJavaType(type) - ) - } - - override fun name(): String { - return "\$SieveInteraction\$${name}" ?: error("Cannot read class name") - } - - override fun run(input: MutableList): CompletableFuture> { - try { - val args = types - .zip(input.toList()) { type, param -> param.value.`as`(type) } - .toTypedArray() - val res = function.invoke( - args[0] as T1, - args[1] as T2, - args[2] as T3, - ) - val event = EventInstance.from(mapOf(name to res)) - val eventInstance = EventInstance("\$SieveEvent\$$name", event.providedIngredients) - return CompletableFuture.completedFuture(Optional.ofNullable(eventInstance)) - } catch (e: Exception) { - return CompletableFuture.failedFuture(e) - } - } - } + return FunctionInteractionInstance(types, function, name) } inline fun javaTypeOf() = typeOf().javaType \ No newline at end of file diff --git a/core/baker-interface-kotlin/src/test/kotlin/FunctionInteractionInstanceTest.kt b/core/baker-interface-kotlin/src/test/kotlin/FunctionInteractionInstanceTest.kt index 3b2dffedb..af6de2714 100644 --- a/core/baker-interface-kotlin/src/test/kotlin/FunctionInteractionInstanceTest.kt +++ b/core/baker-interface-kotlin/src/test/kotlin/FunctionInteractionInstanceTest.kt @@ -1,31 +1,78 @@ +import com.ing.baker.runtime.javadsl.IngredientInstance +import com.ing.baker.runtime.javadsl.InteractionInstance import com.ing.baker.runtime.kotlindsl.functionInteractionInstance +import com.ing.baker.types.Converters +import com.ing.baker.types.Value import org.junit.Assert.assertEquals import org.junit.Test import java.util.* class FunctionInteractionInstanceTest { - @Test fun `should handle function interaction with optional`() { - val func = { test: Optional -> "" } + val func = { test: Optional -> "Empty: ${test.isEmpty}" } val interaction = functionInteractionInstance("test", func) - assertEquals(interaction.name(), "\$SieveInteraction\$test") + assertEquals("\$SieveInteraction\$test", interaction.name()) + assertEquals("Empty: false", callInteraction(mapOf("test" to Optional.of("test")), interaction)) + + } + + @Test + fun `should handle function interaction with empty optional`() { + val func = { test: Optional -> "Empty: ${test.isEmpty}" } + val interaction = functionInteractionInstance("test", func) + + assertEquals("\$SieveInteraction\$test", interaction.name()) + assertEquals("Empty: true", callInteraction(mapOf("test" to Optional.empty()), interaction)) + } @Test fun `should handle function interaction`() { - val func = { test: String -> "" } + val func = { test: String -> "Output: $test" } + val interaction = functionInteractionInstance("test", func) + + assertEquals("\$SieveInteraction\$test", interaction.name()) + assertEquals("Output: value", callInteraction(mapOf("test" to "value"), interaction)) + } + + @Test + fun `should handle function interaction with multiple ingredients`() { + val func = { test1: String, test2: Int, test3: Boolean -> "$test1: ${if (test3) 2*test2 else test2}" } val interaction = functionInteractionInstance("test", func) - assertEquals(interaction.name(), "\$SieveInteraction\$test") + assertEquals("\$SieveInteraction\$test", interaction.name()) + assertEquals("calculation: 10", callInteraction(mapOf("test1" to "calculation", "test2" to 5, "test3" to true), interaction)) } + @Test fun `should handle function interaction list`() { - val func = { test: String -> listOf("") } + val func = { test: String -> listOf(test, test) } val interaction = functionInteractionInstance("test", func) - assertEquals(interaction.name(), "\$SieveInteraction\$test") + assertEquals("\$SieveInteraction\$test", interaction.name(), ) + assertEquals(listOf("input", "input"), callInteractionList(mapOf("test" to "input"), interaction)) + } + + private val emptyMetaMap = scala.collection.immutable.Map.from( + scala.jdk.CollectionConverters.MapHasAsScala( + emptyMap() + ).asScala()) + + private inline fun callInteraction(ingredients: Map, interaction: InteractionInstance): T? { + return executeInteraction(interaction, ingredients).`as`(T::class.java) + } + + private inline fun callInteractionList(ingredients: Map, interaction: InteractionInstance): List? { + return executeInteraction(interaction, ingredients).asList(T::class.java) + } + + private fun executeInteraction(interaction: InteractionInstance, ingredients: Map): Value = + interaction.execute( + ingredients.map { IngredientInstance(it.key, Converters.toValue(it.value)) }, emptyMetaMap + ).get().get().providedIngredients.values.first() + } diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/InteractionInstance.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/InteractionInstance.scala index 61409e936..2bd8a081d 100644 --- a/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/InteractionInstance.scala +++ b/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/InteractionInstance.scala @@ -35,8 +35,9 @@ abstract class InteractionInstance[F[_]] extends common.InteractionInstance[F] w def shaBase64: String = { val nameBytes: Array[Byte] = name.getBytes("UTF-8") - val interfaceBytes: Array[Byte] = input.toArray.map(_.hashCode().toByte) - val sha: Array[Byte] = MessageDigest.getInstance("SHA-256").digest(nameBytes ++ interfaceBytes) + val inputBytes: Array[Byte] = input.toArray.map(_.hashCode().toByte) + val outputBytes: Array[Byte] = output.toArray.map(_.hashCode().toByte) + val sha: Array[Byte] = MessageDigest.getInstance("SHA-256").digest(nameBytes ++ inputBytes ++ outputBytes) val base64: Array[Byte] = Base64.getEncoder.encode(sha) new String(base64) } diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala index 4e7aba2a3..c211dfa39 100644 --- a/core/baker-interface/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala +++ b/core/baker-interface/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala @@ -149,7 +149,7 @@ class InMemoryMemoryCleanupSpec extends AnyFlatSpec with Matchers with Retries { result.unsafeRunSync() } - it should "not delete a process if the idle timeout is reset due to activity" in { + it should "not delete a process if the idle timeout is reset due to activity" taggedAs(Retryable) in { val recipe = Recipe("tempRecipe3") .withInteractions( interactionOne diff --git a/core/recipe-dsl-kotlin/src/main/kotlin/com/ing/baker/recipe/kotlindsl/KotlinDsl.kt b/core/recipe-dsl-kotlin/src/main/kotlin/com/ing/baker/recipe/kotlindsl/KotlinDsl.kt index 40421c1c2..cb648d74d 100644 --- a/core/recipe-dsl-kotlin/src/main/kotlin/com/ing/baker/recipe/kotlindsl/KotlinDsl.kt +++ b/core/recipe-dsl-kotlin/src/main/kotlin/com/ing/baker/recipe/kotlindsl/KotlinDsl.kt @@ -100,14 +100,15 @@ class RecipeBuilder(private val name: String) { /** - * Registers a sieve [T1, T2, R] to the recipe. + * Registers a sieve [T1, R] to the recipe. */ inline fun ingredient(name: String, noinline function: (T1) -> R) { val parameters = function.reflect()?.parameters ?: error("Cannot read parameters") - val ingredients = listOf(javaTypeOf()) + val javaTypes = listOf(javaTypeOf()) + val ingredients = javaTypes .zip(parameters) .map { (clazz, param) -> Ingredient(param.name, clazz) } - addSieve(name, ingredients, javaTypeOf(), function) + addSieve(name, ingredients, javaTypeOf(), function, javaTypes) } /** @@ -115,27 +116,29 @@ class RecipeBuilder(private val name: String) { */ inline fun ingredient(name: String, noinline function: (T1, T2) -> R) { val parameters = function.reflect()?.parameters ?: error("Cannot read parameters") - val ingredients = listOf(javaTypeOf(), javaTypeOf()) + val javaTypes = listOf(javaTypeOf(), javaTypeOf()) + val ingredients = javaTypes .zip(parameters) .map { (clazz, param) -> Ingredient(param.name, clazz) } - addSieve(name, ingredients, javaTypeOf(), function) + addSieve(name, ingredients, javaTypeOf(), function, javaTypes) } /** - * Registers a sieve [T1, T2, R] to the recipe. + * Registers a sieve [T1, T2, T3, R] to the recipe. */ inline fun ingredient( name: String, noinline function: (T1, T2, T3) -> R ) { val parameters = function.reflect()?.parameters ?: error("Cannot read parameters") - val ingredients = listOf(javaTypeOf(), javaTypeOf(), javaTypeOf()) + val javaTypes = listOf(javaTypeOf(), javaTypeOf(), javaTypeOf()) + val ingredients = javaTypes .zip(parameters) .map { (clazz, param) -> Ingredient(param.name, clazz) } - addSieve(name, ingredients, javaTypeOf(), function) + addSieve(name, ingredients, javaTypeOf(), function, javaTypes) } - fun addSieve(name: String, ingredients: List, returnType: Type, function: Any) { + fun addSieve(name: String, ingredients: List, returnType: Type, function: Any, javaTypes: List) { sieves.add( Sieve( name, @@ -152,7 +155,8 @@ class RecipeBuilder(private val name: String) { Optional.empty() ) ), - function + function, + javaTypes ) ) } diff --git a/core/recipe-dsl-kotlin/src/test/kotlin/com/ing/baker/recipe/kotlindsl/KotlinDslTest.kt b/core/recipe-dsl-kotlin/src/test/kotlin/com/ing/baker/recipe/kotlindsl/KotlinDslTest.kt index 0cfe90f50..aebfe5fdf 100644 --- a/core/recipe-dsl-kotlin/src/test/kotlin/com/ing/baker/recipe/kotlindsl/KotlinDslTest.kt +++ b/core/recipe-dsl-kotlin/src/test/kotlin/com/ing/baker/recipe/kotlindsl/KotlinDslTest.kt @@ -186,17 +186,17 @@ class KotlinDslTest { assertEquals(true, (predefinedIngredients().get("bar").get() as com.ing.baker.types.PrimitiveValue).value()) assertEquals(2, requiredEvents().size()) - assertEquals("PaymentSuccessful", requiredEvents().get(0)) + assertEquals("PaymentSuccessful", requiredEvents()[0]) assertEquals("myEvent", requiredEvents().get(1)) assertEquals(1, requiredOneOfEvents().size()) - assertEquals(2, requiredOneOfEvents().get(0).size()) - assertEquals("PaymentInformation", requiredOneOfEvents().get(0).get(0)) - assertEquals("SomeEvent", requiredOneOfEvents().get(0).get(1)) + assertEquals(2, requiredOneOfEvents()[0].size()) + assertEquals("PaymentInformation", requiredOneOfEvents()[0][0]) + assertEquals("SomeEvent", requiredOneOfEvents()[0].get(1)) assertEquals(2, eventOutputTransformers().size()) - assertEquals(Event("Char", listOf(), Optional.empty()), eventOutputTransformers().toList().get(0)._1) - assertEquals(EventOutputTransformer("foo", emptyMap()), eventOutputTransformers().toList().get(0)._2) + assertEquals(Event("Char", listOf(), Optional.empty()), eventOutputTransformers().toList()[0]._1) + assertEquals(EventOutputTransformer("foo", emptyMap()), eventOutputTransformers().toList()[0]._2) assertEquals(Event("Boolean", listOf(), Optional.empty()), eventOutputTransformers().toList().get(1)._1) assertEquals( EventOutputTransformer("AnotherBoolean", mapOf("foo" to "bar", "this" to "that")), @@ -260,26 +260,26 @@ class KotlinDslTest { assertEquals("recipe with sensory events", name()) assertEquals(3, sensoryEvents().size()) - with(sensoryEvents().get(0)) { + with(sensoryEvents()[0]) { assertEquals("One", name()) assertEquals(Option.empty(), maxFiringLimit()) assertEquals(1, providedIngredients().size()) - assertEquals("flag", providedIngredients().get(0).name()) - assertEquals("Bool", providedIngredients().get(0).ingredientType().toString()) + assertEquals("flag", providedIngredients()[0].name()) + assertEquals("Bool", providedIngredients()[0].ingredientType().toString()) } - with(sensoryEvents().get(1)) { + with(sensoryEvents()[1]) { assertEquals("Two", name()) assertEquals(Some(5), maxFiringLimit()) assertEquals(1, providedIngredients().size()) - assertEquals("text", providedIngredients().get(0).name()) - assertEquals("CharArray", providedIngredients().get(0).ingredientType().toString()) + assertEquals("text", providedIngredients()[0].name()) + assertEquals("CharArray", providedIngredients()[0].ingredientType().toString()) } with(sensoryEvents().get(2)) { assertEquals("Three", name()) assertEquals(Some(1), maxFiringLimit()) assertEquals(1, providedIngredients().size()) - assertEquals("number", providedIngredients().get(0).name()) - assertEquals("Int32", providedIngredients().get(0).ingredientType().toString()) + assertEquals("number", providedIngredients()[0].name()) + assertEquals("Int32", providedIngredients()[0].ingredientType().toString()) } } } @@ -354,14 +354,14 @@ class KotlinDslTest { assertEquals(Option.empty(), eventReceivePeriod()) assertEquals(BlockInteraction::class.java, defaultFailureStrategy().javaClass) assertEquals(1, checkpointEvents().size()) - assertEquals("Success", checkpointEvents().get(0).name()) - assertEquals(2, checkpointEvents().get(0).requiredEvents().size()) - assertEquals("PaymentSuccessful", checkpointEvents().get(0).requiredEvents().get(0)) - assertEquals("myEvent", checkpointEvents().get(0).requiredEvents().get(1)) - assertEquals(1, checkpointEvents().get(0).requiredOneOfEvents().size()) - assertEquals(2, checkpointEvents().get(0).requiredOneOfEvents().get(0).size()) - assertEquals("PaymentInformation", checkpointEvents().get(0).requiredOneOfEvents().get(0).get(0)) - assertEquals("SomeEvent", checkpointEvents().get(0).requiredOneOfEvents().get(0).get(1)) + assertEquals("Success", checkpointEvents()[0].name()) + assertEquals(2, checkpointEvents()[0].requiredEvents().size()) + assertEquals("PaymentSuccessful", checkpointEvents()[0].requiredEvents()[0]) + assertEquals("myEvent", checkpointEvents()[0].requiredEvents()[1]) + assertEquals(1, checkpointEvents()[0].requiredOneOfEvents().size()) + assertEquals(2, checkpointEvents()[0].requiredOneOfEvents()[0].size()) + assertEquals("PaymentInformation", checkpointEvents()[0].requiredOneOfEvents()[0][0]) + assertEquals("SomeEvent", checkpointEvents()[0].requiredOneOfEvents()[0][1]) assertTrue(sensoryEvents().isEmpty) assertTrue(interactions().isEmpty) } @@ -386,14 +386,16 @@ class KotlinDslTest { assertEquals(Option.empty(), eventReceivePeriod()) assertEquals(BlockInteraction::class.java, defaultFailureStrategy().javaClass) assertEquals(2, interactions().size()) - assertEquals("MakePayment", interactions().get(0).name()) - assertEquals("ReserveItems", interactions().get(1).name()) + assertEquals("MakePayment", interactions()[0].name()) + assertEquals("ReserveItems", interactions()[1].name()) assertEquals(1, sieves().size()) - assertEquals("extractDate", sieves().get(0).name()) - assertEquals(1, sieves().get(0).inputIngredients().size()) - assertEquals("reservedItems", sieves().get(0).inputIngredients().get(0).name()) - assertEquals(1, sieves().get(0).output().size()) - assertEquals("\$SieveEvent\$extractDate", sieves().get(0).output().get(0).name()) + assertEquals("extractDate", sieves()[0].name()) + assertEquals(1, sieves()[0].inputIngredients().size()) + assertEquals("reservedItems", sieves()[0].inputIngredients()[0].name()) + assertEquals(1, sieves()[0].output().size()) + assertEquals("\$SieveEvent\$extractDate", sieves()[0].output()[0].name()) + assertEquals("1981-01-07", (sieves()[0].function() as Function1).invoke( + Ingredients.ReservedItems(emptyList(), "1981-01-07"))) } } @@ -439,7 +441,7 @@ class KotlinDslTest { assertEquals(Option.empty(), eventReceivePeriod()) assertEquals(BlockInteraction::class.java, defaultFailureStrategy().javaClass) assertEquals(1, subRecipes().size()) - assertEquals("Fulfillment", subRecipes().get(0).name()) + assertEquals("Fulfillment", subRecipes()[0].name()) } } @@ -455,10 +457,10 @@ class KotlinDslTest { with(recipe.interactions().apply(0)) { assertEquals(1, eventOutputTransformers().size()) - assertEquals("ItemsReserved", eventOutputTransformers().toList().get(0)._1.name()) + assertEquals("ItemsReserved", eventOutputTransformers().toList()[0]._1.name()) assertEquals( EventOutputTransformer("ItemsReservedNew", mapOf("reservedItems" to "reservedItemsNew")), - eventOutputTransformers().toList().get(0)._2 + eventOutputTransformers().toList()[0]._2 ) } } @@ -597,6 +599,7 @@ class KotlinDslTest { ): Outcome } - private fun Seq.get(index: Int): T = apply(index) - private fun Set.get(index: Int): T = toList().apply(index) + private operator fun Seq.get(index: Int): T = apply(index) + // These sets are ordered, so in the test we use them indexed + private operator fun Set.get(index: Int): T = toList().apply(index) } \ No newline at end of file diff --git a/core/recipe-dsl/src/main/scala/com/ing/baker/recipe/common/Sieve.scala b/core/recipe-dsl/src/main/scala/com/ing/baker/recipe/common/Sieve.scala index ed48d72dc..9ead809f5 100644 --- a/core/recipe-dsl/src/main/scala/com/ing/baker/recipe/common/Sieve.scala +++ b/core/recipe-dsl/src/main/scala/com/ing/baker/recipe/common/Sieve.scala @@ -24,4 +24,8 @@ trait Sieve { */ val function: Any + /** + * javaTypes Java types of the input parameters of the function + */ + val javaTypes: Seq[java.lang.reflect.Type] } diff --git a/core/recipe-dsl/src/main/scala/com/ing/baker/recipe/kotlindsl/Recipe.scala b/core/recipe-dsl/src/main/scala/com/ing/baker/recipe/kotlindsl/Recipe.scala index e64f930a8..5c61d20fc 100644 --- a/core/recipe-dsl/src/main/scala/com/ing/baker/recipe/kotlindsl/Recipe.scala +++ b/core/recipe-dsl/src/main/scala/com/ing/baker/recipe/kotlindsl/Recipe.scala @@ -5,12 +5,7 @@ import com.ing.baker.recipe.common import java.util.concurrent.TimeUnit import scala.collection.compat.immutable.ArraySeq import scala.concurrent.duration.FiniteDuration - import scala.jdk.CollectionConverters._ -import scala.collection.immutable.Seq -import scala.collection.immutable.List -import scala.collection.immutable.Map -import scala.collection.immutable.Set class Recipe( nameInput: String, diff --git a/core/recipe-dsl/src/main/scala/com/ing/baker/recipe/kotlindsl/Sieve.scala b/core/recipe-dsl/src/main/scala/com/ing/baker/recipe/kotlindsl/Sieve.scala index 7e66e18b5..7d57a0cf4 100644 --- a/core/recipe-dsl/src/main/scala/com/ing/baker/recipe/kotlindsl/Sieve.scala +++ b/core/recipe-dsl/src/main/scala/com/ing/baker/recipe/kotlindsl/Sieve.scala @@ -2,16 +2,18 @@ package com.ing.baker.recipe.kotlindsl import com.ing.baker.recipe.common +import java.lang.reflect.Type import scala.jdk.CollectionConverters._ case class Sieve( nameInput: String = "", inputIngredientsInput: java.util.List[Ingredient], outputInput: java.util.List[Event], - functionInput: Any -) extends common.Sieve { + functionInput: Any, + javaTypesInput: java.util.List[Type]) extends common.Sieve { val name: String = nameInput override val inputIngredients: Seq[common.Ingredient] = inputIngredientsInput.asScala.toSeq override val output: Seq[common.Event] = outputInput.asScala.toSeq override val function: Any = functionInput + override val javaTypes: Seq[Type] = javaTypesInput.asScala.toSeq } diff --git a/docs.md b/docs.md index b07c7a0b9..67a793487 100644 --- a/docs.md +++ b/docs.md @@ -4,17 +4,15 @@ This documentation is for developers of Baker. ## Creating a release -To create a release run the following: +CI and release processes are fully automated using the following Github Action workflows: -1. Make sure tests are OK: +* [CI workflow](https://github.com/ing-bank/baker/blob/master/.github/workflows/ci.yml): Runs for each commit to any branch and also for release tags starting with 'v'. This workflow runs all tests, updates the draft release notes, uploads dependency graph to github (only for master branch) and publishes to Sonatype/Maven or Azure depending on following conditions. + + * Sonatype/Maven: Only for tags starting with 'v' prefix. - `sbt ";+clean;+test"` + * Azure: All commits to all branches except the tagged commits, so we have stable releases in Sonatype and non-stable more frequent releases in Azure for ING internal use and tests. -2. Create the release: - - `sbt -mem 2048 "release cross skip-tests"` - - When prompted, enter the PGP password for signing the files. +* [Release workflow](https://github.com/ing-bank/baker/blob/master/.github/workflows/release.yml): Manually triggered by one of the contributors when a stable release to Sonatype Maven Central is needed. This workflow just creates and pushes a tag. Then CI workflow gets triggered by the tag (i.e v1.0.0) and artifacts published to Sonatype Maven Central with the version in the tag. ## Documentation site at github.io diff --git a/get-version.sh b/get-version.sh deleted file mode 100755 index 95b46e941..000000000 --- a/get-version.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -DIR=$(dirname $0) -cat $DIR/version.sbt | sed 's/ThisBuild \/ version := \"\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\1.\2.\3/' diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 4760a461d..1c22d9c75 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -8,13 +8,13 @@ object Dependencies { val akkaPersistenceCassandraVersion = "1.2.1" val akkaHttpVersion = "10.6.3" val http4sVersion = "0.22.15" - val circeVersion = "0.14.9" + val circeVersion = "0.14.10" val fs2Version = "2.5.12" val mockitoScalaVersion = "1.17.37" val catsEffectVersion = "2.5.5" val catsCoreVersion = "2.12.0" val scalapbVersion = scalapb.compiler.Version.scalapbVersion - val springVersion = "6.1.12" + val springVersion = "6.1.13" val springBootVersion = "2.6.1" val akkaInmemoryJournal = ("com.github.dnvriend" %% "akka-persistence-inmemory" % "2.5.15.2") @@ -122,11 +122,11 @@ object Dependencies { val typeSafeConfig = "com.typesafe" % "config" % "1.4.3" val objenisis = "org.objenesis" % "objenesis" % "3.4" - val jodaTime = "joda-time" % "joda-time" % "2.12.7" + val jodaTime = "joda-time" % "joda-time" % "2.13.0" val slf4jApi = "org.slf4j" % "slf4j-api" % "2.0.16" - val logback = "ch.qos.logback" % "logback-classic" % "1.5.7" + val logback = "ch.qos.logback" % "logback-classic" % "1.5.8" val logstash = "net.logstash.logback" % "logstash-logback-encoder" % "7.3" - val scalaCheck = "org.scalacheck" %% "scalacheck" % "1.18.0" + val scalaCheck = "org.scalacheck" %% "scalacheck" % "1.18.1" val scalaCheckPlus = "org.scalatestplus" %% "scalacheck-1-17" % "3.2.18.0" val scalaCheckPlusMockito = "org.scalatestplus" %% "mockito-3-12" % "3.2.10.0" val scalaLogging = "com.typesafe.scala-logging" %% "scala-logging" % "3.9.5" @@ -136,12 +136,12 @@ object Dependencies { val springCore = "org.springframework" % "spring-core" % springVersion val springBootStarter = "org.springframework.boot" % "spring-boot-starter" % springBootVersion - val snakeYaml = "org.yaml" % "snakeyaml" % "2.2" + val snakeYaml = "org.yaml" % "snakeyaml" % "2.3" val jacksonDatabind = "com.fasterxml.jackson.core" % "jackson-databind" % "2.17.2" val jacksonCore = "com.fasterxml.jackson.core" % "jackson-core" % "2.17.2" val jawnParser = "org.typelevel" %% "jawn-parser" % "1.6.0" - val nettyHandler = "io.netty" % "netty-handler" % "4.1.112.Final" + val nettyHandler = "io.netty" % "netty-handler" % "4.1.113.Final" private val bouncycastleVersion = "1.78.1" diff --git a/project/Publish.scala b/project/Publish.scala index f913c349f..19f039112 100644 --- a/project/Publish.scala +++ b/project/Publish.scala @@ -1,14 +1,13 @@ import sbt._ import Keys._ -import sbtrelease.ReleasePlugin.autoImport._ -import sbtrelease.ReleaseStateTransformations._ import xerial.sbt.Sonatype.SonatypeKeys._ +import sbtdynver.DynVerPlugin.autoImport._ object Publish { - lazy val settings = - if (sys.env.contains("FEEDURL")) StableToAzureFeed - else if ( (sys.env.contains("USERNAME"))) ReleaseToSonatype + lazy val settings = + if (sys.env.contains("AZURE_FEEDURL")) PublishToAzure + else if ( (sys.env.contains("SONATYPE_USERNAME"))) PublishToSonatype else SuppressJavaDocs import aether.AetherKeys._ @@ -19,41 +18,30 @@ object Publish { packageSrc / publishArtifact := true ) - val StableToAzureFeed = Seq( - credentials += Credentials(Path.userHome / ".credentials"), - publishTo := Some("pkgs.dev.azure.com" at sys.env.getOrElse("FEEDURL", "")), + // `sbt aetherDeploy` will publish artifacts to the Azure Artifacts repository + val PublishToAzure = inThisBuild(List( + dynverSeparator := "-", + version := version.value.replace("-SNAPSHOT", ""), + )) ++ List( + credentials += Credentials( + "Azure Artifacts", + "pkgs.dev.azure.com", + sys.env.getOrElse("AZURE_FEEDUSER", ""), + sys.env.getOrElse("AZURE_FEEDPASSWORD", "") + ), + publishTo := Some("Azure Artifacts" at sys.env.getOrElse("AZURE_FEEDURL", "")), publishMavenStyle := true, - aetherDeploy / logLevel := Level.Info + sonatypeCredentialHost := "" ) - protected def isSnapshot(s: String) = s.trim endsWith "SNAPSHOT" - - protected val nexus = "https://oss.sonatype.org/" - protected val ossSnapshots = "Sonatype OSS Snapshots" at nexus + "content/repositories/snapshots/" - protected val ossStaging = "Sonatype OSS Staging" at nexus + "service/local/staging/deploy/maven2/" - - val ReleaseToSonatype = Seq( - credentials ++= Seq( - Credentials( - "Sonatype Nexus Repository Manager", - "oss.sonatype.org", - sys.env.getOrElse("USERNAME", ""), - sys.env.getOrElse("PASSWORD", "") - ), - Credentials( - "GnuPG Key ID", - "gpg", - "303489A85EBB77F6E93E2A254CCF1479F92AE2B7", // key identifier - "ignored" // this field is ignored; passwords are supplied by pinentry - ) - ), - releaseIgnoreUntrackedFiles := true, - sonatypeProfileName := "com.ing", - licenses := Seq("MIT" -> url("https://opensource.org/licenses/MIT")), + // `sbt ci-release` will publish artifacts to the Sonatype repository + val PublishToSonatype = inThisBuild(List( homepage := Some(url("https://github.com/ing-bank/baker")), - scmInfo := Some(ScmInfo( - browseUrl = url("https://github.com/ing-bank/baker"), - connection = "scm:git@github.com:ing-bank/baker.git")), + licenses := List(License.MIT), + dynverSeparator := "-", + version := version.value.replace("-SNAPSHOT", ""), + )) ++ List( + sonatypeProfileName := "com.ing", pomExtra := ( @@ -61,28 +49,6 @@ object Publish { Squad Apollo - ), - publishMavenStyle := true, - publishTo := version((v: String) => Some(if (isSnapshot(v)) ossSnapshots else ossStaging)).value, - Test / publishArtifact := false, - packageDoc / publishArtifact := true, - packageSrc / publishArtifact := true, - pomIncludeRepository := (_ => false), - releaseCrossBuild := true, - releaseProcess := Seq[ReleaseStep]( - releaseStepCommand("sonatypeDropAll"), - checkSnapshotDependencies, - inquireVersions, - runClean, - runTest, - setReleaseVersion, - commitReleaseVersion, - tagRelease, - releaseStepCommandAndRemaining("+publishSigned"), - setNextVersion, - commitNextVersion, - releaseStepCommand("sonatypeReleaseAll"), - pushChanges ) ) } diff --git a/project/build.properties b/project/build.properties index ee4c672cd..0b699c305 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.10.1 +sbt.version=1.10.2 diff --git a/project/plugins.sbt b/project/plugins.sbt index cb4f25c66..dfdc71f9b 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,17 +1,11 @@ -addSbtPlugin("com.github.sbt" % "sbt-git" % "2.0.1") - addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.2.0") -addSbtPlugin("com.github.sbt" % "sbt-release" % "1.4.0") - -addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.1.1") +addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.6.1") -addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.11.3") +addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.2.0") addSbtPlugin("net.vonbuchholtz" % "sbt-dependency-check" % "5.1.0") -addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.2.1") - addSbtPlugin("com.github.sbt" % "sbt-multi-jvm" % "0.6.0") addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.10.4") diff --git a/set-version.sh b/set-version.sh deleted file mode 100755 index c13c21bb8..000000000 --- a/set-version.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -DIR=$(dirname $0) -# bump this manually whenever breaking change occurs -SEM_VERSION=$($DIR/get-version.sh) - -DATE=$(date +"%y%m%d%H%M%S") -COMMIT=$( git log --pretty=format:'%h' -n 1) -VERSION="${SEM_VERSION}-${DATE}-${COMMIT}" - -echo "ThisBuild / version := \"${VERSION}\"" > ./version.sbt diff --git a/version.sbt b/version.sbt deleted file mode 100644 index 895ae6915..000000000 --- a/version.sbt +++ /dev/null @@ -1 +0,0 @@ -ThisBuild / version := "4.1.0-SNAPSHOT"