diff --git a/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.ts b/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.ts
index 5197ea46..0c1476d1 100644
--- a/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.ts
+++ b/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.ts
@@ -15,18 +15,22 @@ export class DeploymentStepperComponent implements OnInit, OnDestroy {
// Define the four steps. (Note: estimatedTimes are defined only for the first three.)
steps: ('WAITING' | 'PENDING' | 'IN_PROGRESS' | 'SUCCESS')[] = ['WAITING', 'PENDING', 'IN_PROGRESS', 'SUCCESS'];
stepDescriptions: {
- [key in 'WAITING' | 'PENDING' | 'IN_PROGRESS' | 'SUCCESS' | 'ERROR' | 'FAILURE']: string;
+ [key in 'QUEUED' | 'WAITING' | 'PENDING' | 'IN_PROGRESS' | 'SUCCESS' | 'ERROR' | 'FAILURE' | 'UNKNOWN' | 'INACTIVE']: string;
} = {
+ QUEUED: 'Deployment Queued',
WAITING: 'Waiting for approval',
PENDING: 'Pending deployment',
IN_PROGRESS: 'Deployment in progress',
SUCCESS: 'Deployment successful',
ERROR: 'Deployment error',
FAILURE: 'Deployment failed',
+ UNKNOWN: 'State unknown',
+ INACTIVE: 'Inactive deployment',
};
// Estimated times in minutes for each non-terminal step.
estimatedTimes = {
+ QUEUED: 5,
WAITING: 1,
PENDING: 1,
IN_PROGRESS: 5,
@@ -48,28 +52,6 @@ export class DeploymentStepperComponent implements OnInit, OnDestroy {
}
}
- /**
- * Calculates a virtual step index based solely on elapsed time.
- * - If elapsed time is less than WAITING's estimated time, returns 0.
- * - If elapsed time is between WAITING and WAITING+PENDING, returns 1.
- * - If between WAITING+PENDING and WAITING+PENDING+IN_PROGRESS, returns 2.
- * - Otherwise, returns 3 (i.e. SUCCESS).
- */
- get virtualStepIndex(): number {
- if (!this.deployment || !this.deployment.createdAt) return 0;
- const start = new Date(this.deployment.createdAt).getTime();
- const elapsedMinutes = (this.time - start) / 60000;
- if (elapsedMinutes >= this.estimatedTimes.WAITING + this.estimatedTimes.PENDING + this.estimatedTimes.IN_PROGRESS) {
- return 3;
- } else if (elapsedMinutes >= this.estimatedTimes.WAITING + this.estimatedTimes.PENDING) {
- return 2;
- } else if (elapsedMinutes >= this.estimatedTimes.WAITING) {
- return 1;
- } else {
- return 0;
- }
- }
-
/**
* Determines the effective step index.
* If the deployment state is terminal (SUCCESS, ERROR, FAILURE), use that state.
@@ -84,10 +66,14 @@ export class DeploymentStepperComponent implements OnInit, OnDestroy {
// return this.virtualStepIndex;
}
- get isErrorState(): boolean {
+ isErrorState(): boolean {
return ['ERROR', 'FAILURE'].includes(this.deployment?.state || '');
}
+ isUnknownState(): boolean {
+ return ['UNKNOWN', 'INACTIVE'].includes(this.deployment?.state || '');
+ }
+
/**
* Returns a status string for a given step index.
* - "completed" if the step index is less than the current effective step.
@@ -96,8 +82,9 @@ export class DeploymentStepperComponent implements OnInit, OnDestroy {
*/
getStepStatus(index: number): string {
const effectiveStep = this.currentEffectiveStepIndex;
- if (index < effectiveStep) return 'completed';
- if (index === effectiveStep) return this.isErrorState ? 'error' : 'active';
+ if (this.isUnknownState()) return 'unknown'; // all steps should be unkown
+ if (index < effectiveStep) return 'completed'; //it's already done
+ if (index === effectiveStep) return this.isErrorState() ? 'error' : this.steps[index] == 'SUCCESS' ? 'completed' : 'active';
return 'upcoming';
}
@@ -111,42 +98,31 @@ export class DeploymentStepperComponent implements OnInit, OnDestroy {
get dynamicProgress(): number {
if (!this.deployment || !this.deployment.createdAt) return 0;
// If the server indicates a terminal state, show full progress.
- if (['SUCCESS', 'ERROR', 'FAILURE'].includes(this.deployment.state || '')) {
+ if (['UNKNOWN', 'INACTIVE'].includes(this.deployment.state || '')) {
+ return 0;
+ } else if (['SUCCESS', 'ERROR', 'FAILURE'].includes(this.deployment.state || '')) {
return 100;
}
- const start = new Date(this.deployment.createdAt).getTime();
- const elapsedMs = this.time - start;
+ const start = new Date(this.deployment.createdAt).getTime(); // start time is when the deployment was created
+ const elapsedMs = this.time - start > 0 ? this.time - start : 0; // elapsed time
// If the virtual step is 3, show 100%.
if (this.currentEffectiveStepIndex === 3) return 100;
let progress = 0;
let cumulativeEstimateMs = 0;
// There are three segments: 0 (0–25%), 1 (25–50%), 2 (50–75%).
- for (let i = 0; i < 3; i++) {
- const segStart = i * 25;
- const segEnd = (i + 1) * 25;
- const estimatedMs = this.estimatedTimes[this.steps[i] as keyof typeof this.estimatedTimes] * 60000;
- // If this step is fully complete, assign the full segment.
- if (this.currentEffectiveStepIndex > i) {
- progress = segEnd;
- cumulativeEstimateMs += estimatedMs;
- }
- // If we're in the middle of this segment, compute the ratio.
- else if (this.currentEffectiveStepIndex === i) {
- const elapsedForSegmentMs = elapsedMs - cumulativeEstimateMs;
- let ratio = elapsedForSegmentMs / estimatedMs;
- console.log('elapsed for segment ', elapsedForSegmentMs);
- console.log('estimated ms ', estimatedMs);
- if (ratio > 1) {
- ratio = 1;
- }
- progress = segStart + ratio * 25;
- console.log('progress ', progress);
- break;
- } else {
- break;
- }
+ const segStart = this.currentEffectiveStepIndex * 33;
+ const estimatedMs = this.estimatedTimes[this.steps[this.currentEffectiveStepIndex] as keyof typeof this.estimatedTimes] * 60000;
+
+ const elapsedForSegmentMs = elapsedMs - cumulativeEstimateMs > 0 ? elapsedMs - cumulativeEstimateMs : 0;
+ let ratio = elapsedForSegmentMs / estimatedMs;
+ console.log('elapsed for segment ', elapsedForSegmentMs);
+ console.log('estimated ms ', estimatedMs);
+ if (ratio > 1) {
+ ratio = 1;
}
+ progress = segStart + ratio * 33;
+ console.log('progress ', progress);
return Math.floor(progress);
}
@@ -161,7 +137,9 @@ export class DeploymentStepperComponent implements OnInit, OnDestroy {
let cumulative = 0;
for (let i = 0; i < 3; i++) {
const est = this.estimatedTimes[this.steps[i] as keyof typeof this.estimatedTimes];
- cumulative += est;
+ if (this.getStepStatus(i) !== 'completed') {
+ cumulative += est;
+ }
if (i === index) {
if (this.getStepStatus(i) === 'completed') {
return '0m 0s';
From f7958a0a87ea7b5a894b51f1f41a92c3ab4c8c47 Mon Sep 17 00:00:00 2001
From: Turker Koc <39654393+TurkerKoc@users.noreply.github.com>
Date: Sun, 16 Feb 2025 18:33:41 +0100
Subject: [PATCH 04/14] always send helios deployment createdAt
---
.../cit/aet/helios/deployment/LatestDeploymentUnion.java | 6 ++++++
.../tum/cit/aet/helios/environment/EnvironmentService.java | 2 +-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/server/application-server/src/main/java/de/tum/cit/aet/helios/deployment/LatestDeploymentUnion.java b/server/application-server/src/main/java/de/tum/cit/aet/helios/deployment/LatestDeploymentUnion.java
index f87a9924..3f46bd88 100644
--- a/server/application-server/src/main/java/de/tum/cit/aet/helios/deployment/LatestDeploymentUnion.java
+++ b/server/application-server/src/main/java/de/tum/cit/aet/helios/deployment/LatestDeploymentUnion.java
@@ -15,6 +15,12 @@ private LatestDeploymentUnion(Deployment realDeployment, HeliosDeployment helios
this.heliosDeployment = heliosDeployment;
}
+ public static LatestDeploymentUnion realDeployment(
+ Deployment dep, OffsetDateTime heliosDeploymentCreatedAt) {
+ dep.setCreatedAt(heliosDeploymentCreatedAt);
+ return new LatestDeploymentUnion(dep, null);
+ }
+
public static LatestDeploymentUnion realDeployment(Deployment dep) {
return new LatestDeploymentUnion(dep, null);
}
diff --git a/server/application-server/src/main/java/de/tum/cit/aet/helios/environment/EnvironmentService.java b/server/application-server/src/main/java/de/tum/cit/aet/helios/environment/EnvironmentService.java
index 4ee11f22..16361b57 100644
--- a/server/application-server/src/main/java/de/tum/cit/aet/helios/environment/EnvironmentService.java
+++ b/server/application-server/src/main/java/de/tum/cit/aet/helios/environment/EnvironmentService.java
@@ -126,7 +126,7 @@ public LatestDeploymentUnion findLatestDeployment(Environment env) {
// Compare updatedAt timestamps to determine the latest
if (latestDeployment.getCreatedAt().isAfter(latestHelios.getCreatedAt())) {
- return LatestDeploymentUnion.realDeployment(latestDeployment);
+ return LatestDeploymentUnion.realDeployment(latestDeployment, latestHelios.getCreatedAt());
} else {
return LatestDeploymentUnion.heliosDeployment(latestHelios);
}
From 0840d062950cdbf757dff948171043295cbaf1b9 Mon Sep 17 00:00:00 2001
From: Turker Koc <39654393+TurkerKoc@users.noreply.github.com>
Date: Mon, 17 Feb 2025 02:20:39 +0100
Subject: [PATCH 05/14] stepper harmonize
---
.../deployment-stepper.component.html | 168 +++++++++++-------
.../deployment-stepper.component.ts | 47 ++++-
.../environment-list-view.component.html | 73 +++++---
.../environment-list-view.component.ts | 18 ++
.../environment/EnvironmentService.java | 2 +-
.../heliosdeployment/HeliosDeployment.java | 9 +-
.../github/GitHubWorkflowRunSyncService.java | 36 ++--
7 files changed, 236 insertions(+), 117 deletions(-)
diff --git a/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.html b/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.html
index 427fc375..2f23f7ed 100644
--- a/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.html
+++ b/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.html
@@ -1,79 +1,117 @@
-
-
+
+
Latest Deployment Progress
+
+
+
+ @if (deployment && deployment.state === 'SUCCESS') {
+
+
+
+
+ {{ getDeploymentDuration() }}
+
+
+
(Deployment completed in)
+
+ } @else if (deployment && isErrorState()) {
+
+
+
+
+ {{ getDeploymentDuration() }}
+
+
+
(Deployment failed in)
+
+ } @else if (deployment && isUnknownState()) {
+
+
+
+ 0m 0s
+
+
(Unknown deployment duration)
+
+ } @else {
+
+
+
+
+ {{ getTimeEstimate(2) }}
+
+
+
(Estimated time remaining)
+
+ }
+
+
+
@for (step of steps; track step; let i = $index) {
-
-
-
- @switch (getStepStatus(i)) {
- @case ('completed') {
-
- }
- @case ('active') {
-
- }
- @case ('error') {
-
- }
- @case ('unknown') {
-
- }
- @case ('inactive') {
-
- }
- @case ('upcoming') {
-
- }
+
+
+ @switch (getStepStatus(i)) {
+ @case ('completed') {
+
}
-
-
-
-
- @if (getStepStatus(i) === 'error') {
-
{{ 'ERROR' }}
-
- {{ stepDescriptions['ERROR'] }}
-
- } @else if (isUnknownState()) {
-
{{ 'UNKNOWN' }}
-
- {{ stepDescriptions['UNKNOWN'] }}
-
- } @else {
-
{{ step }}
-
- {{ stepDescriptions[step] }}
-
-
- @if (getTimeEstimate(i)) {
- {{ getTimeEstimate(i) }} remaining
- }
-
+ @case ('active') {
+
+ }
+ @case ('error') {
+
}
-
+ @case ('unknown') {
+
+ }
+ @case ('inactive') {
+
+ }
+ @case ('upcoming') {
+
+ }
+ }
+
+
+
+
+ @if (getStepStatus(i) === 'error') {
+
{{ 'ERROR' }}
+
+ {{ stepDescriptions['ERROR'] }}
+
+ } @else if (isUnknownState()) {
+
{{ 'UNKNOWN' }}
+
+ {{ stepDescriptions['UNKNOWN'] }}
+
+ } @else {
+
+ {{ getStepDisplayName(step) }}
+
+
+ {{ stepDescriptions[step] }}
+
+
+ @if (getTimeEstimate(i)) {
+ {{ getTimeEstimate(i) }} remaining
+ }
+
+ }
}
-
+
* All time estimates are approximate and based on historical data
diff --git a/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.ts b/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.ts
index 0c1476d1..f09d06fb 100644
--- a/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.ts
+++ b/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.ts
@@ -3,10 +3,11 @@ import { CommonModule } from '@angular/common';
import { IconsModule } from 'icons.module';
import { ProgressBarModule } from 'primeng/progressbar';
import { EnvironmentDeployment } from '@app/core/modules/openapi';
+import { TooltipModule } from 'primeng/tooltip';
@Component({
selector: 'app-deployment-stepper',
- imports: [CommonModule, IconsModule, ProgressBarModule],
+ imports: [CommonModule, IconsModule, ProgressBarModule, TooltipModule],
templateUrl: './deployment-stepper.component.html',
})
export class DeploymentStepperComponent implements OnInit, OnDestroy {
@@ -18,8 +19,8 @@ export class DeploymentStepperComponent implements OnInit, OnDestroy {
[key in 'QUEUED' | 'WAITING' | 'PENDING' | 'IN_PROGRESS' | 'SUCCESS' | 'ERROR' | 'FAILURE' | 'UNKNOWN' | 'INACTIVE']: string;
} = {
QUEUED: 'Deployment Queued',
- WAITING: 'Waiting for approval',
- PENDING: 'Pending deployment',
+ WAITING: 'Request sent to Github',
+ PENDING: 'Preparing deployment',
IN_PROGRESS: 'Deployment in progress',
SUCCESS: 'Deployment successful',
ERROR: 'Deployment error',
@@ -142,14 +143,50 @@ export class DeploymentStepperComponent implements OnInit, OnDestroy {
}
if (i === index) {
if (this.getStepStatus(i) === 'completed') {
- return '0m 0s';
+ return '';
}
const remaining = cumulative - elapsedMinutes;
const minutes = Math.floor(remaining);
const seconds = Math.floor((remaining - minutes) * 60);
- return remaining > 0 ? `${minutes}m ${seconds}s` : '0m 0s';
+ return remaining > 0 ? `${minutes}m ${seconds}s` : '';
}
}
return '';
}
+
+ getStepDisplayName(step: string): string {
+ return (
+ {
+ WAITING: 'REQUESTED',
+ PENDING: 'PRE-DEPLOYMENT',
+ IN_PROGRESS: 'DEPLOYING',
+ SUCCESS: 'SUCCESS',
+ }[step] || step
+ );
+ }
+
+ /**
+ * Computes the deployment duration by subtracting the deployment creation time
+ * from the finish time (or current time if finishedAt isn’t available).
+ */
+ getDeploymentDuration(): string {
+ if (!this.deployment || !this.deployment.createdAt) return '';
+
+ // For terminal states, use finishedAt if available; otherwise use current time.
+ let endTime: number;
+ if (['SUCCESS', 'ERROR', 'FAILURE'].includes(this.deployment.state || '')) {
+ endTime = this.deployment.updatedAt ? new Date(this.deployment.updatedAt).getTime() : this.time;
+ } else {
+ // For ongoing deployments, we use the current time.
+ endTime = this.time;
+ }
+
+ const startTime = new Date(this.deployment.createdAt).getTime();
+ const elapsedMs = endTime - startTime;
+ if (elapsedMs < 0) return '0m 0s';
+
+ const minutes = Math.floor(elapsedMs / 60000);
+ const seconds = Math.floor((elapsedMs % 60000) / 1000);
+ return `${minutes}m ${seconds}s`;
+ }
}
diff --git a/client/src/app/components/environments/environment-list/environment-list-view.component.html b/client/src/app/components/environments/environment-list/environment-list-view.component.html
index af6f80a2..71694e0c 100644
--- a/client/src/app/components/environments/environment-list/environment-list-view.component.html
+++ b/client/src/app/components/environments/environment-list/environment-list-view.component.html
@@ -96,38 +96,53 @@
-
-
- @if (environment.latestDeployment; as deployment) {
-
- }
+
+
+
-
-
- @if (environment.latestStatus; as status) {
-
- }
-
-
- @if (environment.latestDeployment; as deployment) {
-
+
+
+ @if (showLatestDeployment) {
+
+ } @else {
+
+
+ @if (environment.latestDeployment; as deployment) {
+
+ }
+
+
+
+ @if (environment.latestStatus; as status) {
+
+ }
+
+
}
-
-
-
-
-
- @for (installedApp of environment.installedApps; track installedApp) {
-
{{ installedApp }}
- }
+
+
+
+
+
+ @for (installedApp of environment.installedApps; track installedApp) {
+
{{ installedApp }}
+ }
+
diff --git a/client/src/app/components/environments/environment-list/environment-list-view.component.ts b/client/src/app/components/environments/environment-list/environment-list-view.component.ts
index b88e5168..46c7100e 100644
--- a/client/src/app/components/environments/environment-list/environment-list-view.component.ts
+++ b/client/src/app/components/environments/environment-list/environment-list-view.component.ts
@@ -32,6 +32,10 @@ import { UserAvatarComponent } from '@app/components/user-avatar/user-avatar.com
import { EnvironmentStatusInfoComponent } from '../environment-status-info/environment-status-info.component';
import { EnvironmentStatusTagComponent } from '../environment-status-tag/environment-status-tag.component';
import { DeploymentStepperComponent } from '../deployment-stepper/deployment-stepper.component';
+import { ToggleButtonModule } from 'primeng/togglebutton';
+import { FormsModule } from '@angular/forms';
+import { ToggleSwitchModule } from 'primeng/toggleswitch';
+
@Component({
selector: 'app-environment-list-view',
imports: [
@@ -54,6 +58,9 @@ import { DeploymentStepperComponent } from '../deployment-stepper/deployment-ste
CommonModule,
TimeAgoPipe,
UserAvatarComponent,
+ ToggleButtonModule,
+ FormsModule,
+ ToggleSwitchModule,
],
providers: [DatePipe],
templateUrl: './environment-list-view.component.html',
@@ -67,6 +74,8 @@ export class EnvironmentListViewComponent implements OnDestroy {
private currentTime = signal(Date.now());
private intervalId: number | undefined;
+ showLatestDeployment: boolean = true;
+
editable = input
();
deployable = input();
hideLinkToList = input();
@@ -226,4 +235,13 @@ export class EnvironmentListViewComponent implements OnDestroy {
return timeLeftMinutes > 1 ? `You can unlock this environment in ${timeLeftMinutes} minutes` : 'You can unlock this environment in 1 minute';
}
}
+
+ isDeploymentOngoing(environment: EnvironmentDto) {
+ if (!environment.latestDeployment) {
+ return false;
+ } else if (environment.latestDeployment.state && ['SUCCESS', 'FAILURE', 'ERROR', 'INACTIVE', 'UNKNOWN'].includes(environment.latestDeployment.state)) {
+ return false;
+ }
+ return true;
+ }
}
diff --git a/server/application-server/src/main/java/de/tum/cit/aet/helios/environment/EnvironmentService.java b/server/application-server/src/main/java/de/tum/cit/aet/helios/environment/EnvironmentService.java
index 16361b57..abf93ad0 100644
--- a/server/application-server/src/main/java/de/tum/cit/aet/helios/environment/EnvironmentService.java
+++ b/server/application-server/src/main/java/de/tum/cit/aet/helios/environment/EnvironmentService.java
@@ -123,7 +123,7 @@ public LatestDeploymentUnion findLatestDeployment(Environment env) {
if (latestHeliosOpt.isPresent() && latestDeploymentOpt.isPresent()) {
HeliosDeployment latestHelios = latestHeliosOpt.get();
Deployment latestDeployment = latestDeploymentOpt.get();
-
+ // TODO: add logs and check what's returned in ehre
// Compare updatedAt timestamps to determine the latest
if (latestDeployment.getCreatedAt().isAfter(latestHelios.getCreatedAt())) {
return LatestDeploymentUnion.realDeployment(latestDeployment, latestHelios.getCreatedAt());
diff --git a/server/application-server/src/main/java/de/tum/cit/aet/helios/heliosdeployment/HeliosDeployment.java b/server/application-server/src/main/java/de/tum/cit/aet/helios/heliosdeployment/HeliosDeployment.java
index beb2f4ed..4afdbb3a 100644
--- a/server/application-server/src/main/java/de/tum/cit/aet/helios/heliosdeployment/HeliosDeployment.java
+++ b/server/application-server/src/main/java/de/tum/cit/aet/helios/heliosdeployment/HeliosDeployment.java
@@ -14,7 +14,6 @@
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.PrePersist;
-import jakarta.persistence.PreUpdate;
import jakarta.persistence.Table;
import java.time.OffsetDateTime;
import java.util.Map;
@@ -86,10 +85,10 @@ protected void onCreate() {
updatedAt = OffsetDateTime.now();
}
- @PreUpdate
- protected void onUpdate() {
- updatedAt = OffsetDateTime.now();
- }
+ // @PreUpdate
+ // protected void onUpdate() {
+ // updatedAt = OffsetDateTime.now();
+ // }
// Enum to represent deployment status
public enum Status {
diff --git a/server/application-server/src/main/java/de/tum/cit/aet/helios/workflow/github/GitHubWorkflowRunSyncService.java b/server/application-server/src/main/java/de/tum/cit/aet/helios/workflow/github/GitHubWorkflowRunSyncService.java
index 8a3c2c65..a60d4580 100644
--- a/server/application-server/src/main/java/de/tum/cit/aet/helios/workflow/github/GitHubWorkflowRunSyncService.java
+++ b/server/application-server/src/main/java/de/tum/cit/aet/helios/workflow/github/GitHubWorkflowRunSyncService.java
@@ -16,6 +16,7 @@
import jakarta.transaction.Transactional;
import java.io.IOException;
import java.time.OffsetDateTime;
+import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
@@ -207,24 +208,35 @@ private void processRunForHeliosDeployment(GHWorkflowRun workflowRun) throws IOE
// triggered the workflow run
// Then we can check whether it's triggered via Helios-App or a Github User via Github UI.
// We only need to update heliosDeployment if it's triggered via Helios-App
-
heliosDeploymentRepository
.findTopByBranchNameAndCreatedAtLessThanEqualOrderByCreatedAtDesc(
workflowRun.getHeadBranch(),
DateUtil.convertToOffsetDateTime(workflowRun.getRunStartedAt()))
.ifPresent(
heliosDeployment -> {
- HeliosDeployment.Status mappedStatus =
- mapWorkflowRunStatus(workflowRun.getStatus(), workflowRun.getConclusion());
- log.debug("Mapped status {} to {}", workflowRun.getStatus(), mappedStatus);
-
- // Update the deployment status
- heliosDeployment.setStatus(mappedStatus);
- log.info(
- "Updated HeliosDeployment {} to status {}",
- heliosDeployment.getId(),
- mappedStatus);
- heliosDeploymentRepository.save(heliosDeployment);
+ try {
+ if (workflowRun
+ .getUpdatedAt()
+ .toInstant()
+ .isAfter(heliosDeployment.getUpdatedAt().toInstant())) {
+ heliosDeployment.setUpdatedAt(
+ OffsetDateTime.ofInstant(
+ workflowRun.getUpdatedAt().toInstant(), ZoneId.systemDefault()));
+ HeliosDeployment.Status mappedStatus =
+ mapWorkflowRunStatus(workflowRun.getStatus(), workflowRun.getConclusion());
+ log.debug("Mapped status {} to {}", workflowRun.getStatus(), mappedStatus);
+
+ // Update the deployment status
+ heliosDeployment.setStatus(mappedStatus);
+ log.info(
+ "Updated HeliosDeployment {} to status {}",
+ heliosDeployment.getId(),
+ mappedStatus);
+ heliosDeploymentRepository.save(heliosDeployment);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
});
}
}
From 2a599a4c644553e2453713be803140d61ba8ba50 Mon Sep 17 00:00:00 2001
From: Turker Koc <39654393+TurkerKoc@users.noreply.github.com>
Date: Mon, 17 Feb 2025 02:54:10 +0100
Subject: [PATCH 06/14] helios udpate logic changed
---
.../github/GitHubDeploymentSyncService.java | 26 +++++++++++--------
.../github/GitHubWorkflowRunSyncService.java | 4 +--
2 files changed, 16 insertions(+), 14 deletions(-)
diff --git a/server/application-server/src/main/java/de/tum/cit/aet/helios/deployment/github/GitHubDeploymentSyncService.java b/server/application-server/src/main/java/de/tum/cit/aet/helios/deployment/github/GitHubDeploymentSyncService.java
index 94976ee4..8058256c 100644
--- a/server/application-server/src/main/java/de/tum/cit/aet/helios/deployment/github/GitHubDeploymentSyncService.java
+++ b/server/application-server/src/main/java/de/tum/cit/aet/helios/deployment/github/GitHubDeploymentSyncService.java
@@ -62,11 +62,10 @@ public GitHubDeploymentSyncService(
* Synchronizes deployments for all repositories.
*
* @param repositories the list of GitHub repositories to sync deployments from
- * @param since an optional timestamp to fetch deployments since
+ * @param since an optional timestamp to fetch deployments since
*/
public void syncDeploymentsOfAllRepositories(
- @NotNull List repositories,
- Optional since) {
+ @NotNull List repositories, Optional since) {
repositories.forEach(ghRepository -> syncDeploymentsOfRepository(ghRepository, since));
}
@@ -74,11 +73,10 @@ public void syncDeploymentsOfAllRepositories(
* Synchronizes deployments for a specific repository.
*
* @param ghRepository the GitHub repository to sync deployments from
- * @param since an optional timestamp to fetch deployments since
+ * @param since an optional timestamp to fetch deployments since
*/
public void syncDeploymentsOfRepository(
- @NotNull GHRepository ghRepository,
- Optional since) {
+ @NotNull GHRepository ghRepository, Optional since) {
try {
// Fetch the GitRepository entity
String fullName = ghRepository.getFullName();
@@ -106,8 +104,8 @@ public void syncDeploymentsOfRepository(
* Synchronizes deployments for a specific environment.
*
* @param ghRepository the GitHub repository
- * @param environment the environment entity
- * @param since an optional timestamp to fetch deployments since
+ * @param environment the environment entity
+ * @param since an optional timestamp to fetch deployments since
*/
public void syncDeploymentsOfEnvironment(
@NotNull GHRepository ghRepository,
@@ -171,9 +169,9 @@ public void syncDeploymentsOfEnvironment(
* repository.
*
* @param deploymentSource the source (GHDeployment or GitHubDeploymentDto) wrapped as a
- * DeploymentSource
- * @param gitRepository the associated GitRepository entity
- * @param environment the associated environment entity
+ * DeploymentSource
+ * @param gitRepository the associated GitRepository entity
+ * @param environment the associated environment entity
*/
@Transactional
void processDeployment(
@@ -227,6 +225,12 @@ private void updateHeliosDeployment(Deployment deployment, Environment environme
heliosDeployment.setDeploymentId(deployment.getId());
heliosDeployment.setStatus(
HeliosDeployment.mapDeploymentStateToHeliosStatus(deployment.getState()));
+ if (deployment
+ .getUpdatedAt()
+ .toInstant()
+ .isAfter(heliosDeployment.getUpdatedAt().toInstant())) {
+ heliosDeployment.setUpdatedAt(deployment.getUpdatedAt());
+ }
heliosDeploymentRepository.save(heliosDeployment);
log.info("Helios Deployment updated");
}
diff --git a/server/application-server/src/main/java/de/tum/cit/aet/helios/workflow/github/GitHubWorkflowRunSyncService.java b/server/application-server/src/main/java/de/tum/cit/aet/helios/workflow/github/GitHubWorkflowRunSyncService.java
index a60d4580..a726e88f 100644
--- a/server/application-server/src/main/java/de/tum/cit/aet/helios/workflow/github/GitHubWorkflowRunSyncService.java
+++ b/server/application-server/src/main/java/de/tum/cit/aet/helios/workflow/github/GitHubWorkflowRunSyncService.java
@@ -16,7 +16,6 @@
import jakarta.transaction.Transactional;
import java.io.IOException;
import java.time.OffsetDateTime;
-import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
@@ -220,8 +219,7 @@ private void processRunForHeliosDeployment(GHWorkflowRun workflowRun) throws IOE
.toInstant()
.isAfter(heliosDeployment.getUpdatedAt().toInstant())) {
heliosDeployment.setUpdatedAt(
- OffsetDateTime.ofInstant(
- workflowRun.getUpdatedAt().toInstant(), ZoneId.systemDefault()));
+ DateUtil.convertToOffsetDateTime(workflowRun.getUpdatedAt()));
HeliosDeployment.Status mappedStatus =
mapWorkflowRunStatus(workflowRun.getStatus(), workflowRun.getConclusion());
log.debug("Mapped status {} to {}", workflowRun.getStatus(), mappedStatus);
From 229a7df9263a5b21aaf5541cc25cc5604e70ed6c Mon Sep 17 00:00:00 2001
From: Turker Koc <39654393+TurkerKoc@users.noreply.github.com>
Date: Mon, 17 Feb 2025 03:03:33 +0100
Subject: [PATCH 07/14] latest deployment check logic udpate
---
.../de/tum/cit/aet/helios/environment/EnvironmentService.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/server/application-server/src/main/java/de/tum/cit/aet/helios/environment/EnvironmentService.java b/server/application-server/src/main/java/de/tum/cit/aet/helios/environment/EnvironmentService.java
index abf93ad0..bc662bb1 100644
--- a/server/application-server/src/main/java/de/tum/cit/aet/helios/environment/EnvironmentService.java
+++ b/server/application-server/src/main/java/de/tum/cit/aet/helios/environment/EnvironmentService.java
@@ -125,7 +125,8 @@ public LatestDeploymentUnion findLatestDeployment(Environment env) {
Deployment latestDeployment = latestDeploymentOpt.get();
// TODO: add logs and check what's returned in ehre
// Compare updatedAt timestamps to determine the latest
- if (latestDeployment.getCreatedAt().isAfter(latestHelios.getCreatedAt())) {
+ if (latestDeployment.getCreatedAt().isAfter(latestHelios.getCreatedAt())
+ || latestDeployment.getCreatedAt().isEqual(latestHelios.getCreatedAt())) {
return LatestDeploymentUnion.realDeployment(latestDeployment, latestHelios.getCreatedAt());
} else {
return LatestDeploymentUnion.heliosDeployment(latestHelios);
From ce41e49a3aa15e10b074bd954a525ff49157bb46 Mon Sep 17 00:00:00 2001
From: Turker Koc <39654393+TurkerKoc@users.noreply.github.com>
Date: Mon, 17 Feb 2025 17:37:06 +0100
Subject: [PATCH 08/14] progress bar size change
---
.../deployment-stepper/deployment-stepper.component.html | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.html b/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.html
index 2f23f7ed..bc9d56ce 100644
--- a/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.html
+++ b/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.html
@@ -44,8 +44,10 @@
}
-
-
+
From b94ab550ad51ac5bf11f27c9fa3a1720ee3d8978 Mon Sep 17 00:00:00 2001
From: Turker Koc <39654393+TurkerKoc@users.noreply.github.com>
Date: Mon, 17 Feb 2025 18:40:26 +0100
Subject: [PATCH 09/14] different time estimation in pr and branch
---
.../deployment-stepper.component.ts | 9 +++++----
client/src/app/core/modules/openapi/schemas.gen.ts | 3 +++
client/src/app/core/modules/openapi/types.gen.ts | 1 +
server/application-server/openapi.yaml | 2 ++
.../aet/helios/deployment/DeploymentService.java | 13 ++++++++++++-
.../helios/deployment/LatestDeploymentUnion.java | 14 ++++++++++++++
.../cit/aet/helios/environment/EnvironmentDto.java | 2 ++
.../helios/heliosdeployment/HeliosDeployment.java | 5 +++++
.../migration/V8__add_pr_to_helios_deployment.sql | 8 ++++++++
9 files changed, 52 insertions(+), 5 deletions(-)
create mode 100644 server/application-server/src/main/resources/db/migration/V8__add_pr_to_helios_deployment.sql
diff --git a/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.ts b/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.ts
index f09d06fb..a2583f83 100644
--- a/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.ts
+++ b/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.ts
@@ -33,7 +33,7 @@ export class DeploymentStepperComponent implements OnInit, OnDestroy {
estimatedTimes = {
QUEUED: 5,
WAITING: 1,
- PENDING: 1,
+ PENDING: 5,
IN_PROGRESS: 5,
};
@@ -45,6 +45,10 @@ export class DeploymentStepperComponent implements OnInit, OnDestroy {
this.intervalId = window.setInterval(() => {
this.time = Date.now();
}, 1000);
+
+ if (this.deployment?.prName !== null && this.deployment?.prName !== undefined) {
+ this.estimatedTimes.PENDING = 1;
+ }
}
ngOnDestroy(): void {
@@ -117,13 +121,10 @@ export class DeploymentStepperComponent implements OnInit, OnDestroy {
const elapsedForSegmentMs = elapsedMs - cumulativeEstimateMs > 0 ? elapsedMs - cumulativeEstimateMs : 0;
let ratio = elapsedForSegmentMs / estimatedMs;
- console.log('elapsed for segment ', elapsedForSegmentMs);
- console.log('estimated ms ', estimatedMs);
if (ratio > 1) {
ratio = 1;
}
progress = segStart + ratio * 33;
- console.log('progress ', progress);
return Math.floor(progress);
}
diff --git a/client/src/app/core/modules/openapi/schemas.gen.ts b/client/src/app/core/modules/openapi/schemas.gen.ts
index c07f50f0..7289e4ed 100644
--- a/client/src/app/core/modules/openapi/schemas.gen.ts
+++ b/client/src/app/core/modules/openapi/schemas.gen.ts
@@ -86,6 +86,9 @@ export const EnvironmentDeploymentSchema = {
releaseCandidateName: {
type: 'string',
},
+ prName: {
+ type: 'string',
+ },
user: {
$ref: '#/components/schemas/UserInfoDto',
},
diff --git a/client/src/app/core/modules/openapi/types.gen.ts b/client/src/app/core/modules/openapi/types.gen.ts
index e103b166..fdcc7848 100644
--- a/client/src/app/core/modules/openapi/types.gen.ts
+++ b/client/src/app/core/modules/openapi/types.gen.ts
@@ -27,6 +27,7 @@ export type EnvironmentDeployment = {
ref?: string;
task?: string;
releaseCandidateName?: string;
+ prName?: string;
user?: UserInfoDto;
createdAt?: string;
updatedAt?: string;
diff --git a/server/application-server/openapi.yaml b/server/application-server/openapi.yaml
index a201584d..130b3e5e 100644
--- a/server/application-server/openapi.yaml
+++ b/server/application-server/openapi.yaml
@@ -905,6 +905,8 @@ components:
type: string
releaseCandidateName:
type: string
+ prName:
+ type: string
user:
$ref: "#/components/schemas/UserInfoDto"
createdAt:
diff --git a/server/application-server/src/main/java/de/tum/cit/aet/helios/deployment/DeploymentService.java b/server/application-server/src/main/java/de/tum/cit/aet/helios/deployment/DeploymentService.java
index c038642f..bb253956 100644
--- a/server/application-server/src/main/java/de/tum/cit/aet/helios/deployment/DeploymentService.java
+++ b/server/application-server/src/main/java/de/tum/cit/aet/helios/deployment/DeploymentService.java
@@ -11,6 +11,8 @@
import de.tum.cit.aet.helios.github.GitHubService;
import de.tum.cit.aet.helios.heliosdeployment.HeliosDeployment;
import de.tum.cit.aet.helios.heliosdeployment.HeliosDeploymentRepository;
+import de.tum.cit.aet.helios.pullrequest.PullRequest;
+import de.tum.cit.aet.helios.pullrequest.PullRequestRepository;
import de.tum.cit.aet.helios.user.User;
import de.tum.cit.aet.helios.workflow.Workflow;
import de.tum.cit.aet.helios.workflow.WorkflowService;
@@ -41,6 +43,7 @@ public class DeploymentService {
private final EnvironmentLockHistoryRepository lockHistoryRepository;
private final EnvironmentRepository environmentRepository;
private final BranchService branchService;
+ private final PullRequestRepository pullRequestRepository;
public DeploymentService(
DeploymentRepository deploymentRepository,
@@ -51,7 +54,8 @@ public DeploymentService(
HeliosDeploymentRepository heliosDeploymentRepository,
BranchService branchService,
EnvironmentLockHistoryRepository lockHistoryRepository,
- EnvironmentRepository environmentRepository) {
+ EnvironmentRepository environmentRepository,
+ PullRequestRepository pullRequestRepository) {
this.deploymentRepository = deploymentRepository;
this.gitHubService = gitHubService;
this.environmentService = environmentService;
@@ -61,6 +65,7 @@ public DeploymentService(
this.lockHistoryRepository = lockHistoryRepository;
this.environmentRepository = environmentRepository;
this.branchService = branchService;
+ this.pullRequestRepository = pullRequestRepository;
}
public Optional
getDeploymentById(Long id) {
@@ -125,6 +130,11 @@ public void deployToEnvironment(DeployRequest deployRequest) {
throw new DeploymentException("Deployment is still in progress, please wait.");
}
+ // Set the PR associated with the deployment
+ Optional optionalPullRequest =
+ pullRequestRepository.findByRepositoryRepositoryIdAndHeadRefNameOrHeadSha(
+ RepositoryContext.getRepositoryId(), deployRequest.branchName(), branchCommitSha);
+
User githubUser = this.authService.getUserFromGithubId();
// Create a new HeliosDeployment record
HeliosDeployment heliosDeployment = new HeliosDeployment();
@@ -134,6 +144,7 @@ public void deployToEnvironment(DeployRequest deployRequest) {
heliosDeployment.setBranchName(deployRequest.branchName());
heliosDeployment.setSha(branchCommitSha);
heliosDeployment.setCreator(githubUser);
+ heliosDeployment.setPullRequest(optionalPullRequest.orElse(null));
heliosDeployment = heliosDeploymentRepository.saveAndFlush(heliosDeployment);
// Build parameters for the workflow
diff --git a/server/application-server/src/main/java/de/tum/cit/aet/helios/deployment/LatestDeploymentUnion.java b/server/application-server/src/main/java/de/tum/cit/aet/helios/deployment/LatestDeploymentUnion.java
index 3f46bd88..ccb2c871 100644
--- a/server/application-server/src/main/java/de/tum/cit/aet/helios/deployment/LatestDeploymentUnion.java
+++ b/server/application-server/src/main/java/de/tum/cit/aet/helios/deployment/LatestDeploymentUnion.java
@@ -157,6 +157,20 @@ public OffsetDateTime getUpdatedAt() {
}
}
+ public String getPullRequestName() {
+ if (isRealDeployment()) {
+ return realDeployment.getPullRequest() != null
+ ? realDeployment.getPullRequest().getTitle()
+ : null;
+ } else if (isHeliosDeployment()) {
+ return heliosDeployment.getPullRequest() != null
+ ? heliosDeployment.getPullRequest().getTitle()
+ : null;
+ } else {
+ return null;
+ }
+ }
+
public boolean isNone() {
return !isRealDeployment() && !isHeliosDeployment();
}
diff --git a/server/application-server/src/main/java/de/tum/cit/aet/helios/environment/EnvironmentDto.java b/server/application-server/src/main/java/de/tum/cit/aet/helios/environment/EnvironmentDto.java
index ae521a7c..3ddf4dba 100644
--- a/server/application-server/src/main/java/de/tum/cit/aet/helios/environment/EnvironmentDto.java
+++ b/server/application-server/src/main/java/de/tum/cit/aet/helios/environment/EnvironmentDto.java
@@ -67,6 +67,7 @@ public static record EnvironmentDeployment(
String ref,
String task,
String releaseCandidateName,
+ String prName,
UserInfoDto user,
OffsetDateTime createdAt,
OffsetDateTime updatedAt) {
@@ -85,6 +86,7 @@ public static EnvironmentDeployment fromUnion(
.findByRepositoryRepositoryIdAndCommitSha(union.getRepository().id(), union.getSha())
.map(ReleaseCandidate::getName)
.orElse(null),
+ union.getPullRequestName(),
UserInfoDto.fromUser(union.getCreator()),
union.getCreatedAt(),
union.getUpdatedAt());
diff --git a/server/application-server/src/main/java/de/tum/cit/aet/helios/heliosdeployment/HeliosDeployment.java b/server/application-server/src/main/java/de/tum/cit/aet/helios/heliosdeployment/HeliosDeployment.java
index 4afdbb3a..afb7b821 100644
--- a/server/application-server/src/main/java/de/tum/cit/aet/helios/heliosdeployment/HeliosDeployment.java
+++ b/server/application-server/src/main/java/de/tum/cit/aet/helios/heliosdeployment/HeliosDeployment.java
@@ -2,6 +2,7 @@
import de.tum.cit.aet.helios.deployment.Deployment;
import de.tum.cit.aet.helios.environment.Environment;
+import de.tum.cit.aet.helios.pullrequest.PullRequest;
import de.tum.cit.aet.helios.user.User;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@@ -85,6 +86,10 @@ protected void onCreate() {
updatedAt = OffsetDateTime.now();
}
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "pull_request_id")
+ private PullRequest pullRequest;
+
// @PreUpdate
// protected void onUpdate() {
// updatedAt = OffsetDateTime.now();
diff --git a/server/application-server/src/main/resources/db/migration/V8__add_pr_to_helios_deployment.sql b/server/application-server/src/main/resources/db/migration/V8__add_pr_to_helios_deployment.sql
new file mode 100644
index 00000000..a261d537
--- /dev/null
+++ b/server/application-server/src/main/resources/db/migration/V8__add_pr_to_helios_deployment.sql
@@ -0,0 +1,8 @@
+-- Add pull_request_id column to the helios_deployment table
+ALTER TABLE helios_deployment
+ ADD COLUMN pull_request_id BIGINT;
+
+-- Add foreign key constraint to ensure referential integrity
+ALTER TABLE helios_deployment
+ ADD CONSTRAINT fk_helios_deployment_pull_request
+ FOREIGN KEY (pull_request_id) REFERENCES public.issue(id);
From 8ddd99ebcd1c8935e04716b8a52badedf8deabbf Mon Sep 17 00:00:00 2001
From: Turker Koc <39654393+TurkerKoc@users.noreply.github.com>
Date: Mon, 17 Feb 2025 21:32:17 +0100
Subject: [PATCH 10/14] migrations script name change
---
...helios_deployment.sql => V13__add_pr_to_helios_deployment.sql} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename server/application-server/src/main/resources/db/migration/{V12__add_pr_to_helios_deployment.sql => V13__add_pr_to_helios_deployment.sql} (100%)
diff --git a/server/application-server/src/main/resources/db/migration/V12__add_pr_to_helios_deployment.sql b/server/application-server/src/main/resources/db/migration/V13__add_pr_to_helios_deployment.sql
similarity index 100%
rename from server/application-server/src/main/resources/db/migration/V12__add_pr_to_helios_deployment.sql
rename to server/application-server/src/main/resources/db/migration/V13__add_pr_to_helios_deployment.sql
From 355ab4b5ecf746b99648085fa78aa38c008ac00b Mon Sep 17 00:00:00 2001
From: Turker Koc <39654393+TurkerKoc@users.noreply.github.com>
Date: Mon, 17 Feb 2025 21:39:30 +0100
Subject: [PATCH 11/14] comment deleted
---
.../cit/aet/helios/heliosdeployment/HeliosDeployment.java | 5 -----
1 file changed, 5 deletions(-)
diff --git a/server/application-server/src/main/java/de/tum/cit/aet/helios/heliosdeployment/HeliosDeployment.java b/server/application-server/src/main/java/de/tum/cit/aet/helios/heliosdeployment/HeliosDeployment.java
index 5dc3d3de..ac6fd2c4 100644
--- a/server/application-server/src/main/java/de/tum/cit/aet/helios/heliosdeployment/HeliosDeployment.java
+++ b/server/application-server/src/main/java/de/tum/cit/aet/helios/heliosdeployment/HeliosDeployment.java
@@ -93,11 +93,6 @@ protected void onCreate() {
@JoinColumn(name = "pull_request_id")
private PullRequest pullRequest;
- // @PreUpdate
- // protected void onUpdate() {
- // updatedAt = OffsetDateTime.now();
- // }
-
// Enum to represent deployment status
public enum Status {
/** Deployment called and waiting GitHub webhook listener. */
From 8987bab8b45cba76e5f4a88dc4e049e9fc904d65 Mon Sep 17 00:00:00 2001
From: Turker Koc <39654393+TurkerKoc@users.noreply.github.com>
Date: Mon, 17 Feb 2025 23:39:50 +0100
Subject: [PATCH 12/14] waiting state changed to requested state
---
.../deployment-state-tag.component.ts | 8 +-
.../deployment-stepper.component.ts | 90 +++++++++++--------
.../app/core/modules/openapi/schemas.gen.ts | 2 +-
.../src/app/core/modules/openapi/types.gen.ts | 2 +-
server/application-server/openapi.yaml | 1 +
.../deployment/LatestDeploymentUnion.java | 49 ++++++++--
.../helios/environment/EnvironmentDto.java | 3 +-
7 files changed, 106 insertions(+), 49 deletions(-)
diff --git a/client/src/app/components/environments/deployment-state-tag/deployment-state-tag.component.ts b/client/src/app/components/environments/deployment-state-tag/deployment-state-tag.component.ts
index 30b95a90..8f47255d 100644
--- a/client/src/app/components/environments/deployment-state-tag/deployment-state-tag.component.ts
+++ b/client/src/app/components/environments/deployment-state-tag/deployment-state-tag.component.ts
@@ -2,9 +2,9 @@ import { Component, computed, input } from '@angular/core';
import { TagModule } from 'primeng/tag';
import { IconsModule } from 'icons.module';
import { TooltipModule } from 'primeng/tooltip';
-import { DeploymentDto } from '@app/core/modules/openapi';
+import { EnvironmentDeployment } from '@app/core/modules/openapi';
-type BaseDeploymentState = NonNullable;
+type BaseDeploymentState = NonNullable;
type ExtendedDeploymentState = BaseDeploymentState | 'NEVER_DEPLOYED' | 'REPLACED';
@Component({
@@ -34,6 +34,7 @@ export class DeploymentStateTagComponent {
UNKNOWN: 'secondary',
NEVER_DEPLOYED: 'secondary',
REPLACED: 'contrast',
+ REQUESTED: 'warn',
};
return severityMap[this.internalState()];
});
@@ -51,6 +52,7 @@ export class DeploymentStateTagComponent {
UNKNOWN: 'question-mark',
NEVER_DEPLOYED: 'question-mark',
REPLACED: 'repeat',
+ REQUESTED: 'progress',
};
return iconMap[this.internalState()];
});
@@ -73,6 +75,7 @@ export class DeploymentStateTagComponent {
UNKNOWN: 'unknown',
NEVER_DEPLOYED: 'never deployed',
REPLACED: 'replaced',
+ REQUESTED: 'requested',
};
return valueMap[this.internalState()];
});
@@ -90,6 +93,7 @@ export class DeploymentStateTagComponent {
UNKNOWN: 'Deployment state unknown',
NEVER_DEPLOYED: 'Never deployed',
REPLACED: 'Deployment was replaced',
+ REQUESTED: 'Deployment requested',
};
return tooltipMap[this.internalState()];
});
diff --git a/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.ts b/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.ts
index a2583f83..e4c3fb9d 100644
--- a/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.ts
+++ b/client/src/app/components/environments/deployment-stepper/deployment-stepper.component.ts
@@ -1,25 +1,42 @@
-import { Component, Input, OnInit, OnDestroy } from '@angular/core';
+import { Component, Input, OnInit, OnDestroy, computed, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IconsModule } from 'icons.module';
import { ProgressBarModule } from 'primeng/progressbar';
import { EnvironmentDeployment } from '@app/core/modules/openapi';
import { TooltipModule } from 'primeng/tooltip';
+interface EstimatedTimes {
+ REQUESTED: number;
+ PENDING: number;
+ IN_PROGRESS: number;
+}
+
@Component({
selector: 'app-deployment-stepper',
imports: [CommonModule, IconsModule, ProgressBarModule, TooltipModule],
templateUrl: './deployment-stepper.component.html',
})
export class DeploymentStepperComponent implements OnInit, OnDestroy {
- @Input() deployment: EnvironmentDeployment | undefined;
+ // Create a private signal to hold the deployment value.
+ private _deployment = signal(undefined);
+
+ @Input()
+ set deployment(value: EnvironmentDeployment | undefined) {
+ this._deployment.set(value);
+ }
+ get deployment(): EnvironmentDeployment | undefined {
+ return this._deployment();
+ }
// Define the four steps. (Note: estimatedTimes are defined only for the first three.)
- steps: ('WAITING' | 'PENDING' | 'IN_PROGRESS' | 'SUCCESS')[] = ['WAITING', 'PENDING', 'IN_PROGRESS', 'SUCCESS'];
+ steps: ('REQUESTED' | 'PENDING' | 'IN_PROGRESS' | 'SUCCESS')[] = ['REQUESTED', 'PENDING', 'IN_PROGRESS', 'SUCCESS'];
+
+ // Mapping of state keys to their display descriptions.
stepDescriptions: {
- [key in 'QUEUED' | 'WAITING' | 'PENDING' | 'IN_PROGRESS' | 'SUCCESS' | 'ERROR' | 'FAILURE' | 'UNKNOWN' | 'INACTIVE']: string;
+ [key in 'QUEUED' | 'REQUESTED' | 'PENDING' | 'IN_PROGRESS' | 'SUCCESS' | 'ERROR' | 'FAILURE' | 'UNKNOWN' | 'INACTIVE']: string;
} = {
QUEUED: 'Deployment Queued',
- WAITING: 'Request sent to Github',
+ REQUESTED: 'Request sent to Github',
PENDING: 'Preparing deployment',
IN_PROGRESS: 'Deployment in progress',
SUCCESS: 'Deployment successful',
@@ -30,12 +47,15 @@ export class DeploymentStepperComponent implements OnInit, OnDestroy {
};
// Estimated times in minutes for each non-terminal step.
- estimatedTimes = {
- QUEUED: 5,
- WAITING: 1,
- PENDING: 5,
- IN_PROGRESS: 5,
- };
+ // Define estimatedTimes as a computed signal that depends on the reactive deployment signal.
+ estimatedTimes = computed(() => {
+ const deployment = this._deployment();
+ return {
+ REQUESTED: 1,
+ PENDING: deployment?.prName != null ? 1 : 10, // if deployment started from PR then no build time it's 1 minute, if there is a build via branch then it's 4-7 minute in avg
+ IN_PROGRESS: 4, // deployment state takes around 1-3 mintues
+ };
+ });
// A timer updated every second to drive the dynamic progress.
time: number = Date.now();
@@ -45,10 +65,6 @@ export class DeploymentStepperComponent implements OnInit, OnDestroy {
this.intervalId = window.setInterval(() => {
this.time = Date.now();
}, 1000);
-
- if (this.deployment?.prName !== null && this.deployment?.prName !== undefined) {
- this.estimatedTimes.PENDING = 1;
- }
}
ngOnDestroy(): void {
@@ -64,17 +80,20 @@ export class DeploymentStepperComponent implements OnInit, OnDestroy {
*/
get currentEffectiveStepIndex(): number {
if (!this.deployment || !this.deployment.createdAt) return 0;
- // if (['SUCCESS', 'ERROR', 'FAILURE'].includes(this.deployment.state || '')) {
- const index = this.steps.indexOf(this.deployment.state as 'WAITING' | 'PENDING' | 'IN_PROGRESS' | 'SUCCESS');
+ const index = this.steps.indexOf(this.deployment.state as 'REQUESTED' | 'PENDING' | 'IN_PROGRESS' | 'SUCCESS');
return index !== -1 ? index : 3;
- // }
- // return this.virtualStepIndex;
}
+ /**
+ * Checks if the deployment is in an error state.
+ */
isErrorState(): boolean {
return ['ERROR', 'FAILURE'].includes(this.deployment?.state || '');
}
+ /**
+ * Checks if the deployment is in an unknown state.
+ */
isUnknownState(): boolean {
return ['UNKNOWN', 'INACTIVE'].includes(this.deployment?.state || '');
}
@@ -87,44 +106,38 @@ export class DeploymentStepperComponent implements OnInit, OnDestroy {
*/
getStepStatus(index: number): string {
const effectiveStep = this.currentEffectiveStepIndex;
- if (this.isUnknownState()) return 'unknown'; // all steps should be unkown
- if (index < effectiveStep) return 'completed'; //it's already done
- if (index === effectiveStep) return this.isErrorState() ? 'error' : this.steps[index] == 'SUCCESS' ? 'completed' : 'active';
+ if (this.isUnknownState()) return 'unknown';
+ if (index < effectiveStep) return 'completed';
+ if (index === effectiveStep) return this.isErrorState() ? 'error' : this.steps[index] === 'SUCCESS' ? 'completed' : 'active';
return 'upcoming';
}
/**
* Computes overall progress in a piecewise manner.
- * Each segment (WAITING, PENDING, IN_PROGRESS) is allotted 25% of the bar.
+ * Each segment (REQUESTED, PENDING, IN_PROGRESS) is allotted 25% of the bar.
* For the current segment, progress is calculated as a ratio of its elapsed time;
* if a segment is already completed (i.e. virtualStepIndex > segment index), that segment is full.
* When the virtual step reaches 3, the progress returns 100%.
*/
get dynamicProgress(): number {
if (!this.deployment || !this.deployment.createdAt) return 0;
- // If the server indicates a terminal state, show full progress.
if (['UNKNOWN', 'INACTIVE'].includes(this.deployment.state || '')) {
return 0;
} else if (['SUCCESS', 'ERROR', 'FAILURE'].includes(this.deployment.state || '')) {
return 100;
}
- const start = new Date(this.deployment.createdAt).getTime(); // start time is when the deployment was created
- const elapsedMs = this.time - start > 0 ? this.time - start : 0; // elapsed time
- // If the virtual step is 3, show 100%.
+ const start = new Date(this.deployment.createdAt).getTime();
+ const elapsedMs = this.time - start > 0 ? this.time - start : 0;
if (this.currentEffectiveStepIndex === 3) return 100;
- let progress = 0;
- let cumulativeEstimateMs = 0;
- // There are three segments: 0 (0–25%), 1 (25–50%), 2 (50–75%).
const segStart = this.currentEffectiveStepIndex * 33;
- const estimatedMs = this.estimatedTimes[this.steps[this.currentEffectiveStepIndex] as keyof typeof this.estimatedTimes] * 60000;
-
- const elapsedForSegmentMs = elapsedMs - cumulativeEstimateMs > 0 ? elapsedMs - cumulativeEstimateMs : 0;
+ const estimatedMs = this.estimatedTimes()[this.steps[this.currentEffectiveStepIndex] as keyof EstimatedTimes] * 60000;
+ const elapsedForSegmentMs = elapsedMs > 0 ? elapsedMs : 0;
let ratio = elapsedForSegmentMs / estimatedMs;
if (ratio > 1) {
ratio = 1;
}
- progress = segStart + ratio * 33;
+ const progress = segStart + ratio * 33;
return Math.floor(progress);
}
@@ -138,7 +151,7 @@ export class DeploymentStepperComponent implements OnInit, OnDestroy {
const elapsedMinutes = (this.time - start) / 60000;
let cumulative = 0;
for (let i = 0; i < 3; i++) {
- const est = this.estimatedTimes[this.steps[i] as keyof typeof this.estimatedTimes];
+ const est = this.estimatedTimes()[this.steps[i] as keyof EstimatedTimes];
if (this.getStepStatus(i) !== 'completed') {
cumulative += est;
}
@@ -155,10 +168,13 @@ export class DeploymentStepperComponent implements OnInit, OnDestroy {
return '';
}
+ /**
+ * Returns a display name for a given step.
+ */
getStepDisplayName(step: string): string {
return (
{
- WAITING: 'REQUESTED',
+ REQUESTED: 'REQUESTED',
PENDING: 'PRE-DEPLOYMENT',
IN_PROGRESS: 'DEPLOYING',
SUCCESS: 'SUCCESS',
@@ -173,12 +189,10 @@ export class DeploymentStepperComponent implements OnInit, OnDestroy {
getDeploymentDuration(): string {
if (!this.deployment || !this.deployment.createdAt) return '';
- // For terminal states, use finishedAt if available; otherwise use current time.
let endTime: number;
if (['SUCCESS', 'ERROR', 'FAILURE'].includes(this.deployment.state || '')) {
endTime = this.deployment.updatedAt ? new Date(this.deployment.updatedAt).getTime() : this.time;
} else {
- // For ongoing deployments, we use the current time.
endTime = this.time;
}
diff --git a/client/src/app/core/modules/openapi/schemas.gen.ts b/client/src/app/core/modules/openapi/schemas.gen.ts
index 1d8e46d0..c4133d2f 100644
--- a/client/src/app/core/modules/openapi/schemas.gen.ts
+++ b/client/src/app/core/modules/openapi/schemas.gen.ts
@@ -69,7 +69,7 @@ export const EnvironmentDeploymentSchema = {
},
state: {
type: 'string',
- enum: ['PENDING', 'WAITING', 'SUCCESS', 'ERROR', 'FAILURE', 'IN_PROGRESS', 'QUEUED', 'INACTIVE', 'UNKNOWN'],
+ enum: ['REQUESTED', 'PENDING', 'WAITING', 'SUCCESS', 'ERROR', 'FAILURE', 'IN_PROGRESS', 'QUEUED', 'INACTIVE', 'UNKNOWN'],
},
statusesUrl: {
type: 'string',
diff --git a/client/src/app/core/modules/openapi/types.gen.ts b/client/src/app/core/modules/openapi/types.gen.ts
index a87446e5..11f09a95 100644
--- a/client/src/app/core/modules/openapi/types.gen.ts
+++ b/client/src/app/core/modules/openapi/types.gen.ts
@@ -21,7 +21,7 @@ export type WorkflowMembershipDto = {
export type EnvironmentDeployment = {
id: number;
url?: string;
- state?: 'PENDING' | 'WAITING' | 'SUCCESS' | 'ERROR' | 'FAILURE' | 'IN_PROGRESS' | 'QUEUED' | 'INACTIVE' | 'UNKNOWN';
+ state?: 'REQUESTED' | 'PENDING' | 'WAITING' | 'SUCCESS' | 'ERROR' | 'FAILURE' | 'IN_PROGRESS' | 'QUEUED' | 'INACTIVE' | 'UNKNOWN';
statusesUrl?: string;
sha?: string;
ref?: string;
diff --git a/server/application-server/openapi.yaml b/server/application-server/openapi.yaml
index 721ca636..7de2be72 100644
--- a/server/application-server/openapi.yaml
+++ b/server/application-server/openapi.yaml
@@ -917,6 +917,7 @@ components:
state:
type: string
enum:
+ - REQUESTED
- PENDING
- WAITING
- SUCCESS
diff --git a/server/application-server/src/main/java/de/tum/cit/aet/helios/deployment/LatestDeploymentUnion.java b/server/application-server/src/main/java/de/tum/cit/aet/helios/deployment/LatestDeploymentUnion.java
index 6da926f7..072e031c 100644
--- a/server/application-server/src/main/java/de/tum/cit/aet/helios/deployment/LatestDeploymentUnion.java
+++ b/server/application-server/src/main/java/de/tum/cit/aet/helios/deployment/LatestDeploymentUnion.java
@@ -111,18 +111,57 @@ public Environment getEnvironment() {
}
}
- public Deployment.State getState() {
+ public State getState() {
if (isRealDeployment()) {
- return realDeployment.getState();
+ return State.fromDeploymentState(realDeployment.getState());
} else if (isHeliosDeployment()) {
- Deployment.State state =
- HeliosDeployment.mapHeliosStatusToDeploymentState(heliosDeployment.getStatus());
- return state;
+ return State.fromHeliosStatus(heliosDeployment.getStatus());
} else {
return null;
}
}
+ public static enum State {
+ REQUESTED,
+
+ // Deployment.State
+ PENDING,
+ WAITING,
+ SUCCESS,
+ ERROR,
+ FAILURE,
+ IN_PROGRESS,
+ QUEUED,
+ INACTIVE,
+ UNKNOWN;
+
+ public static State fromDeploymentState(Deployment.State state) {
+ return switch (state) {
+ case PENDING -> PENDING;
+ case WAITING -> WAITING;
+ case SUCCESS -> SUCCESS;
+ case ERROR -> ERROR;
+ case FAILURE -> FAILURE;
+ case IN_PROGRESS -> IN_PROGRESS;
+ case QUEUED -> QUEUED;
+ case INACTIVE -> INACTIVE;
+ case UNKNOWN -> UNKNOWN;
+ default -> throw new IllegalArgumentException("Invalid state: " + state);
+ };
+ }
+
+ public static State fromHeliosStatus(HeliosDeployment.Status status) {
+ return switch (status) {
+ case WAITING -> REQUESTED;
+ case QUEUED -> PENDING;
+ case IN_PROGRESS -> IN_PROGRESS;
+ case DEPLOYMENT_SUCCESS -> SUCCESS;
+ case FAILED -> FAILURE;
+ case IO_ERROR, UNKNOWN -> UNKNOWN;
+ };
+ }
+ }
+
public String getStatusesUrl() {
if (isRealDeployment()) {
return realDeployment.getStatusesUrl();
diff --git a/server/application-server/src/main/java/de/tum/cit/aet/helios/environment/EnvironmentDto.java b/server/application-server/src/main/java/de/tum/cit/aet/helios/environment/EnvironmentDto.java
index 4122e228..38283c28 100644
--- a/server/application-server/src/main/java/de/tum/cit/aet/helios/environment/EnvironmentDto.java
+++ b/server/application-server/src/main/java/de/tum/cit/aet/helios/environment/EnvironmentDto.java
@@ -1,6 +1,5 @@
package de.tum.cit.aet.helios.environment;
-import de.tum.cit.aet.helios.deployment.Deployment;
import de.tum.cit.aet.helios.deployment.LatestDeploymentUnion;
import de.tum.cit.aet.helios.deployment.LatestDeploymentUnion.DeploymentType;
import de.tum.cit.aet.helios.environment.status.EnvironmentStatus;
@@ -63,7 +62,7 @@ public static EnvironmentStatusDto fromEnvironmentStatus(EnvironmentStatus envir
public static record EnvironmentDeployment(
@NonNull Long id,
String url,
- Deployment.State state,
+ LatestDeploymentUnion.State state,
String statusesUrl,
String sha,
String ref,
From 9c3048b19490ec5bdeed5f32b82b8183398b0ce6 Mon Sep 17 00:00:00 2001
From: Turker Koc <39654393+TurkerKoc@users.noreply.github.com>
Date: Mon, 17 Feb 2025 23:57:12 +0100
Subject: [PATCH 13/14] migration for cascade
---
.../migration/V14__add_missing_on_delete_cascade.sql | 10 ++++++++++
1 file changed, 10 insertions(+)
create mode 100644 server/application-server/src/main/resources/db/migration/V14__add_missing_on_delete_cascade.sql
diff --git a/server/application-server/src/main/resources/db/migration/V14__add_missing_on_delete_cascade.sql b/server/application-server/src/main/resources/db/migration/V14__add_missing_on_delete_cascade.sql
new file mode 100644
index 00000000..92bf1a58
--- /dev/null
+++ b/server/application-server/src/main/resources/db/migration/V14__add_missing_on_delete_cascade.sql
@@ -0,0 +1,10 @@
+-------------------------------------------------------------------------------
+-- workflow_group_membership -> workflow
+-------------------------------------------------------------------------------
+-- Removing a workflow_group_membership row automatically removes any workflow rows that reference that release_candidate.
+ALTER TABLE public.workflow_group_membership DROP CONSTRAINT fkixdlgaqu4hykyehs17gbvyvfj;
+ALTER TABLE public.workflow_group_membership
+ ADD CONSTRAINT fkixdlgaqu4hykyehs17gbvyvfj
+ FOREIGN KEY (repository_id)
+ REFERENCES public.workflow(repository_id)
+ ON DELETE CASCADE;
\ No newline at end of file
From f690c104e15154e2b5f9fd1356222a7c8ba52377 Mon Sep 17 00:00:00 2001
From: Turker Koc <39654393+TurkerKoc@users.noreply.github.com>
Date: Tue, 18 Feb 2025 00:16:42 +0100
Subject: [PATCH 14/14] migration cascade fix
---
.../db/migration/V14__add_missing_on_delete_cascade.sql | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/server/application-server/src/main/resources/db/migration/V14__add_missing_on_delete_cascade.sql b/server/application-server/src/main/resources/db/migration/V14__add_missing_on_delete_cascade.sql
index 92bf1a58..64e26e6d 100644
--- a/server/application-server/src/main/resources/db/migration/V14__add_missing_on_delete_cascade.sql
+++ b/server/application-server/src/main/resources/db/migration/V14__add_missing_on_delete_cascade.sql
@@ -5,6 +5,6 @@
ALTER TABLE public.workflow_group_membership DROP CONSTRAINT fkixdlgaqu4hykyehs17gbvyvfj;
ALTER TABLE public.workflow_group_membership
ADD CONSTRAINT fkixdlgaqu4hykyehs17gbvyvfj
- FOREIGN KEY (repository_id)
- REFERENCES public.workflow(repository_id)
+ FOREIGN KEY (workflow_id)
+ REFERENCES public.workflow(id)
ON DELETE CASCADE;
\ No newline at end of file