Skip to content

Add CI/CD for extensions#38

Open
denniskuijs wants to merge 35 commits into
mainfrom
feature/ci-cd
Open

Add CI/CD for extensions#38
denniskuijs wants to merge 35 commits into
mainfrom
feature/ci-cd

Conversation

@denniskuijs
Copy link
Copy Markdown
Member

@denniskuijs denniskuijs commented May 19, 2026

This PR includes the following changes:

  • Moved demo-setup and energy extensions from openremote/openremote to extensions.
  • Create develop image on push to main and publish it to DockerHub with tag openremote/manager:develop including ALL extensions.
  • Create manager image when a new release is created an publish it to DockerHub with tag' openremote/manager:latest and openremote/manager:$version including ALL extensions.
  • Re-enabled ENTSOE-E Agent and Energy tests.
  • Several CI/CD workflow improvements.
  • Add a Gradle task to copy the extension JARs into the deployment folder so they can be included when building a new image.

Tests to be improved:

  • ForecastSolarServiceTest
  • EnergyOptimisationAssetTest
  • ForecastWindServiceTest
  • EntsoeProtocolTest (ENTSO-E integration test supports multiple periods in a single timeseries)

@denniskuijs denniskuijs changed the title Add Energy and Demo Setup extension / update CI/CD to create manager image Add CI/CD for extensions May 20, 2026
@denniskuijs
Copy link
Copy Markdown
Member Author

@codex

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4841e684b9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci_cd.yml Outdated
Comment on lines +58 to +59
if: ${{ !steps.is_main_repo.outputs.value || github.event_name == 'pull_request' }}
timeout-minutes: 20
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Run Gradle build before building Docker context

This condition skips the Gradle build for openremote/extensions push runs, but later steps still try to build Docker from context: deployment/build and include copied extension JARs. On a normal push to main, no prior step creates deployment/build or deployment/build/extensions, so the docker/build-push-action step can fail due to missing context/artifacts and block the publish pipeline.

Useful? React with 👍 / 👎.

DOCKER_BUILD_RECORD_UPLOAD: false

- name: Build manager image
if: ${{ steps.is_main_repo.outputs.value && github.event_name == 'release' }}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Trigger manager image build on an actual release path

This step is gated on github.event_name == 'release', but this workflow is only configured for push, pull_request, and workflow_dispatch events, so the manager image path never executes. As written, neither the tag-push run nor the gh workflow run ci_cd.yml --ref <tag> dispatch from release.yml can satisfy this condition, so versioned/latest manager images are never published.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds CI/CD automation for building/testing/publishing the extensions repository, and introduces deployment support for packaging extension JARs into Docker images, alongside migrating in the energy and demo-setup extensions.

Changes:

  • Adds a CI/CD workflow to build, run tests, publish Maven artifacts, and build/push Docker images (develop + release).
  • Introduces/migrates energy and demo-setup extensions (models, services, demo data, and tests).
  • Adds Gradle tasks to copy built extension JARs into a deployment build context and enables JaCoCo reporting across subprojects.

Reviewed changes

Copilot reviewed 74 out of 76 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
gradle.properties Disables Gradle parallelism; bumps OpenRemote version; adds Camel version property.
entsoe/src/test/groovy/org/openremote/extension/entsoe/agent/protocol/EntsoeProtocolTest.groovy Adjusts Spock ignore behavior to re-enable most tests in CI.
entsoe/build.gradle Adds task to copy built extension JAR into the deployment build context.
energy/build.gradle New energy extension Gradle build + publishing + copy-to-deployment task.
energy/src/main/resources/META-INF/services/org.openremote.model.ContainerService Registers energy container services via ServiceLoader.
energy/src/main/resources/META-INF/services/org.openremote.model.AssetModelProvider Registers energy asset model provider.
energy/src/main/java/org/openremote/extension/energy/manager/ForecastWindService.java Adds wind forecast service that updates attributes/predicted datapoints.
energy/src/main/java/org/openremote/extension/energy/model/ElectricityAsset.java Adds base electricity asset model and common attributes.
energy/src/main/java/org/openremote/extension/energy/model/ElectricityBatteryAsset.java Adds battery asset model.
energy/src/main/java/org/openremote/extension/energy/model/ElectricityChargerAsset.java Adds charger asset model.
energy/src/main/java/org/openremote/extension/energy/model/ElectricityConsumerAsset.java Adds consumer asset model.
energy/src/main/java/org/openremote/extension/energy/model/ElectricityProducerAsset.java Adds producer base asset model (forecast attribute).
energy/src/main/java/org/openremote/extension/energy/model/ElectricityProducerSolarAsset.java Adds solar producer asset model.
energy/src/main/java/org/openremote/extension/energy/model/ElectricityProducerWindAsset.java Adds wind producer asset model.
energy/src/main/java/org/openremote/extension/energy/model/ElectricityStorageAsset.java Adds storage asset base model.
energy/src/main/java/org/openremote/extension/energy/model/ElectricitySupplierAsset.java Adds supplier asset model.
energy/src/main/java/org/openremote/extension/energy/model/ElectricVehicleAsset.java Adds EV asset model.
energy/src/main/java/org/openremote/extension/energy/model/ElectricVehicleFleetGroupAsset.java Adds EV fleet group model.
energy/src/main/java/org/openremote/extension/energy/model/EnergyModelProvider.java Enables auto-scan model provider for energy assets.
energy/src/main/java/org/openremote/extension/energy/model/EnergyOptimisationAsset.java Adds energy optimisation asset model.
energy/src/main/java/org/openremote/extension/energy/storage/StorageSimulatorAgent.java Adds a simulated storage agent entity.
energy/src/main/java/org/openremote/extension/energy/storage/StorageSimulatorAgentLink.java Adds agent link for the storage simulator protocol.
energy/src/main/java/org/openremote/extension/energy/storage/StorageSimulatorProtocol.java Adds a protocol that periodically simulates storage behavior.
energy/src/test/resources/META-INF/services/org.openremote.model.setup.SetupTasks Registers energy test setup tasks.
energy/src/test/groovy/org/openremote/extension/energy/TestSetupTasks.groovy Defines test setup tasks for energy integration tests.
energy/src/test/groovy/org/openremote/extension/energy/KeycloakTestSetup.groovy Sets up a Keycloak realm for energy tests.
energy/src/test/groovy/org/openremote/extension/energy/ManagerTestSetup.groovy Creates assets used by energy tests.
energy/src/test/groovy/org/openremote/extension/energy/ForecastSolarServiceTest.groovy Adds a forecast.solar integration-style test (currently ignored).
ems/build.gradle Adds task to copy built extension JAR into the deployment build context.
deployment/build.gradle Adds Gradle logic to populate the Docker build context.
deployment/Dockerfile Adds Dockerfile for building a manager image with extension JARs included.
demo-setup/build.gradle New demo-setup extension build + publishing + copy-to-deployment task.
demo-setup/src/main/resources/META-INF/services/org.openremote.model.setup.SetupTasks Registers demo setup tasks via ServiceLoader.
demo-setup/src/main/resources/META-INF/services/org.openremote.model.AssetModelProvider Registers demo asset model provider.
demo-setup/src/main/resources/demo/rules/smartcity/TotalSolarProduction.flow Adds demo rules flow.
demo-setup/src/main/resources/demo/rules/smartcity/TotalPowerConsumption.flow Adds demo rules flow.
demo-setup/src/main/resources/demo/rules/smartcity/StationCrowded.json Adds demo ruleset.
demo-setup/src/main/resources/demo/rules/smartcity/RotterdamPowerBalance.flow Adds demo rules flow.
demo-setup/src/main/resources/demo/rules/smartcity/RotterdamBatteryUse.json Adds demo ruleset.
demo-setup/src/main/resources/demo/rules/smartcity/ParkingFull.json Adds demo ruleset.
demo-setup/src/main/resources/demo/rules/smartcity/OnsParkDimLightWinds.json Adds demo ruleset.
demo-setup/src/main/resources/demo/rules/smartcity/OnsParkBrightStrongWinds.json Adds demo ruleset.
demo-setup/src/main/resources/demo/rules/smartcity/MarkthalChargersInUse.json Adds demo ruleset.
demo-setup/src/main/resources/demo/rules/smartcity/Markthal.json Adds demo ruleset.
demo-setup/src/main/resources/demo/rules/smartcity/LightGroupOnOff.flow Adds demo rules flow.
demo-setup/src/main/resources/demo/rules/smartcity/Euromast.json Adds demo ruleset.
demo-setup/src/main/resources/demo/rules/smartcity/EnvironmentAlerts.json Adds demo ruleset.
demo-setup/src/main/resources/demo/rules/smartcity/DeKuip.json Adds demo ruleset.
demo-setup/src/main/resources/demo/rules/manufacturer/TotalFlow.flow Adds manufacturer demo rules flow.
demo-setup/src/main/resources/demo/rules/manufacturer/SalinityLessThan3.json Adds manufacturer demo ruleset.
demo-setup/src/main/resources/demo/rules/manufacturer/SalinityGreaterThan25.json Adds manufacturer demo ruleset.
demo-setup/src/main/resources/demo/rules/manufacturer/SalinityBetween20And25.json Adds manufacturer demo ruleset.
demo-setup/src/main/resources/demo/rules/manufacturer/IrrigationTankLow.json Adds manufacturer demo ruleset.
demo-setup/src/main/resources/demo/rules/manufacturer/FlowPerMeter.flow Adds manufacturer demo rules flow.
demo-setup/src/main/resources/demo/dashboards/smartcity/parking.json Adds SmartCity demo dashboard definition.
demo-setup/src/main/resources/demo/dashboards/manufacturer/harvesting.json Adds Manufacturer demo dashboard definition.
demo-setup/src/main/java/org/openremote/extension/demosetup/DemoSetupTasks.java Wires demo setup tasks selection by setup type.
demo-setup/src/main/java/org/openremote/extension/demosetup/KeycloakDemoSetup.java Adds Keycloak demo realm/user setup.
demo-setup/src/main/java/org/openremote/extension/demosetup/ManagerDemoAgentSetup.java Adds optional KNX/Velbus demo agent provisioning.
demo-setup/src/main/java/org/openremote/extension/demosetup/ManagerDemoDashboardSetup.java Imports demo dashboards at startup.
demo-setup/src/main/java/org/openremote/extension/demosetup/RulesDemoSetup.java Imports demo rulesets/flows at startup.
demo-setup/src/main/java/org/openremote/extension/demosetup/model/HarvestRobotAsset.java Adds demo domain asset model.
demo-setup/src/main/java/org/openremote/extension/demosetup/model/IrrigationAsset.java Adds demo domain asset model.
demo-setup/src/main/java/org/openremote/extension/demosetup/model/ManufacturerAssetModelProvider.java Enables auto-scan model provider for demo assets.
demo-setup/src/main/java/org/openremote/extension/demosetup/model/SoilSensorAsset.java Adds demo domain asset model.
build.gradle Enables JaCoCo reporting across Java subprojects.
.github/workflows/release.yml Minor YAML quoting consistency.
.github/workflows/ci_cd.yml Adds release trigger, Docker build/push, Maven publish, artifact archival improvements.
Comments suppressed due to low confidence (2)

energy/build.gradle:62

  • The POM scm connection/developerConnection/url still reference openremote/openremote. Since the extension was moved here, these SCM fields should point to openremote/extensions (consistent with ems/build.gradle) so Maven consumers get correct source links.
    demo-setup/build.gradle:56
  • The POM scm connection/developerConnection/url still reference openremote/openremote. Since the code lives in openremote/extensions, update these SCM values so published metadata links to the correct repository (see ems/build.gradle for the convention).
                scm {
                    connection = 'scm:git:git://github.com/openremote/openremote.git'
                    developerConnection = 'scm:git:ssh://github.com:openremote/openremote.git'
                    url = 'https://github.com/openremote/openremote/tree/master'
                }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/workflows/ci_cd.yml Outdated
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0

- name: Build develop image
if: ${{ steps.is_main_repo.outputs.value && github.event_name == 'push' }}
Comment on lines +79 to +83
- name: Install QEMU
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
with:
platforms: linux/amd64,linux/aarch64

Comment thread .github/workflows/ci_cd.yml Outdated
load: false
push: true
tags: |
openremote/manager:${{ inputs.tag }}
Comment thread deployment/build.gradle
Comment on lines +1 to +4
copy {
into layout.buildDirectory
from "Dockerfile"
} No newline at end of file
Comment on lines +74 to +80
electricityWindAsset.setWindSpeedMax(18d)
electricityWindAsset.setWindSpeedMin(2d)
electricityWindAsset.setWindSpeedReference(12d)
electricityWindAsset.setPowerExportMax(9000d)
electricityWindAsset.setEfficiencyExport(100)
electricityWindAsset.setPowerExportMax(2.5)
electricityWindAsset.setLocation(new GeoJSONPoint(9.195285, 48.787418))
Comment on lines +62 to +66
}

public Optional<Double> getPowerForecast() {
return getAttributes().getOrCreate(POWER_FORECAST).getValue();
}
Comment on lines +71 to +73
@JsonProperty("temp")
protected double tempature;

Comment on lines +29 to +30
@Ignore
class ForecastSolarServiceTest extends Specification implements ManagerContainerTrait {
Comment thread energy/build.gradle
Comment on lines +39 to +43
pom {
name = 'OpenRemote energy extension'
description = 'Adds the energy domain extension'
url = 'https://github.com/openremote/openremote'
licenses {
Comment thread demo-setup/build.gradle
Comment on lines +33 to +37
pom {
name = 'OpenRemote Demo setup'
description = 'Adds the OpenRemote Demo setup'
url = 'https://github.com/openremote/openremote'
licenses {
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 74 out of 76 changed files in this pull request and generated 8 comments.

Comment thread deployment/build.gradle
Comment thread .github/workflows/ci_cd.yml Outdated
Comment on lines +19 to +21
# Release
release:

@denniskuijs denniskuijs marked this pull request as ready for review May 20, 2026 14:48
@denniskuijs denniskuijs requested a review from wborn May 20, 2026 14:48
@denniskuijs denniskuijs assigned wborn and unassigned wborn May 20, 2026
@wborn wborn requested a review from Copilot May 29, 2026 08:19
@wborn
Copy link
Copy Markdown
Member

wborn commented May 29, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3c9e7838d5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

-PsigningKey=$MAVEN_SIGNING_KEY -PsigningPassword=$MAVEN_SIGNING_PASSWORD -PpublishUsername=$MAVEN_USERNAME -PpublishPassword=$MAVEN_PASSWORD \
-PkeystoreKeyAlias=$KEYSTORE_KEY_ALIAS -PkeystoreKeyPassword=$KEYSTORE_KEY_PASSWORD -PkeystoreFile=$PWD/keystore -PkeystorePassword=$KEYSTORE_PASSWORD
- name: Build manager image
if: ${{ steps.is_main_repo.outputs.value && github.event_name == 'release' }}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Trigger release publishing from tag pushes

This workflow is only registered for push, pull_request, and workflow_dispatch, so github.event_name is never release; release tags configured at the top of this file arrive as push events. Because release.yml was deleted in this commit, the manager image release step here and the Maven Sonatype publish step below are now skipped for every tagged release.

Useful? React with 👍 / 👎.

context: deployment/build
platforms: linux/amd64,linux/arm64
load: false
push: false
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Push the tagged Docker images

The job logs in to DockerHub and applies registry tags, but the build-push-action invocation sets push: false while also using load: false; per docker/build-push-action semantics, that means the build result is not exported to the registry. On main pushes this leaves openremote/manager:develop unchanged, and the release image step has the same issue for version/latest tags.

Useful? React with 👍 / 👎.

Comment on lines +190 to +191
if ((attributeName.equals(ElectricityChargerAsset.VEHICLE_CONNECTED.getName()) || attributeName.equals(ElectricVehicleAsset.CHARGER_CONNECTED.getName()))
&& (Boolean)attributeEvent.getValue().orElse(false)) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Clear force charging on disconnect, not reconnect

This branch runs only when vehicleConnected/chargerConnected is true, but storageAssetConnected() treats true as connected and the log says the flag should be cleared after disconnection. In the scenario where a force-charged EV/charger reconnects or reports connected again, this removes it from forceChargeAssetIds while FORCE_CHARGE can remain RUNNING, so the next optimisation cycle can overwrite the forced charge setpoint; the false disconnect event is the one that should clear it.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 73 out of 76 changed files in this pull request and generated 9 comments.

Comment on lines +100 to +103
- name: Build manager image
if: ${{ steps.is_main_repo.outputs.value && github.event_name == 'release' }}
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
timeout-minutes: 20
Comment on lines +105 to +109
context: deployment/build
platforms: linux/amd64,linux/arm64
load: false
push: false
tags: |
Comment on lines +118 to +122
- name: Publish to Maven Sonatype
if: ${{ steps.is_main_repo.outputs.value && github.event_name == 'release' }}
timeout-minutes: 20
run: ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository -PsigningKey=$MAVEN_SIGNING_KEY -PsigningPassword=$MAVEN_SIGNING_PASSWORD -PpublishUsername=$MAVEN_USERNAME -PpublishPassword=$MAVEN_PASSWORD
env:
Comment on lines +77 to +79
electricityWindAsset.setPowerExportMax(9000d)
electricityWindAsset.setEfficiencyExport(100)
electricityWindAsset.setPowerExportMax(2.5)
Comment on lines +89 to +93
context: deployment/build
platforms: linux/amd64,linux/arm64
load: false
push: false
tags: openremote/manager:develop
Comment on lines +278 to +282
assetProcessingService.sendAttributeEvent(new AttributeEvent(electricityProducerWindAsset.getId(), ElectricityProducerAsset.POWER_FORECAST.getName(), -currentPower), getClass().getSimpleName());

if (electricityProducerWindAsset.isSetActualWindValueWithForecast().orElse(false)) {
assetProcessingService.sendAttributeEvent(new AttributeEvent(electricityProducerWindAsset.getId(), ElectricityProducerAsset.POWER.getName(), -currentPower), getClass().getSimpleName());
}
Comment on lines +287 to +289
LocalDateTime timestamp = Instant.ofEpochMilli(weatherForecastModel.getTimestamp()).atZone(ZoneId.systemDefault()).toLocalDateTime();
assetPredictedDatapointService.updateValue(electricityProducerWindAsset.getId(), ElectricityProducerAsset.POWER_FORECAST.getName(), -powerForecast, timestamp);
assetPredictedDatapointService.updateValue(electricityProducerWindAsset.getId(), ElectricityProducerAsset.POWER.getName(), -powerForecast, timestamp);
Comment on lines +292 to +295
timestamp = timestamp.plusMinutes(15);
assetPredictedDatapointService.updateValue(electricityProducerWindAsset.getId(), ElectricityProducerAsset.POWER_FORECAST.getName(), -powerForecast, timestamp);
assetPredictedDatapointService.updateValue(electricityProducerWindAsset.getId(), ElectricityProducerAsset.POWER.getName(), -powerForecast, timestamp);
}
Comment on lines +168 to +170
double deltaHours = seconds / 3600d;
double efficiency = power > 0 ? ((double) storageAsset.getEfficiencyImport().orElse(100)) / 100d : (1d / (((double) storageAsset.getEfficiencyExport().orElse(100)) / 100d)); // Export efficiency < 1 means more energy is consumed to produce requested power
double energyDelta = power * deltaHours * efficiency;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants