Skip to content

Commit

Permalink
feat: Add tags (#282)
Browse files Browse the repository at this point in the history
Co-authored-by: Stefan Nemeth <[email protected]>
  • Loading branch information
thielpa and StefanNemeth authored Feb 3, 2025
1 parent 1de56d9 commit 70e8dc9
Show file tree
Hide file tree
Showing 55 changed files with 2,044 additions and 248 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -186,4 +186,7 @@ CMakeUserPresets.json
.DS_STORE

# Ignore pem files (GitHub App)
*.pem
*.pem

# Ignore test files
test/*
3 changes: 3 additions & 0 deletions client/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,6 @@ Thumbs.db

# Sentry Config File
.sentryclirc

# Test files
html/*
12 changes: 12 additions & 0 deletions client/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ export const routes: Routes = [
{
path: 'release',
loadComponent: () => import('./pages/release/release.component').then(m => m.ReleaseComponent),
children: [
{
path: '',
redirectTo: 'list',
pathMatch: 'full',
},
{ path: 'list', loadComponent: () => import('./pages/tag-list/tag-list.component').then(m => m.TagListComponent) },
{
path: ':name',
loadComponent: () => import('./pages/tag-details/tag-details.component').then(m => m.TagDetailsComponent),
},
],
},
{
path: 'ci-cd',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<p-dialog [(visible)]="isVisible" [modal]="true" [style]="{ width: '25rem' }">
<ng-template #header>
<div class="font-bold text-xl">New Tag</div>
</ng-template>
@if (newCommitListQuery.data(); as commitListData) {
<div class="flex flex-col gap-4">
<div class="flex gap-1 items-center mb-2">
<i-tabler name="tag" class="!size-5 text-primary-500" />
<input pInputText [(ngModel)]="tagName" placeholder="Tag Name" pSize="small" />
</div>
<div class="flex gap-2">
<p-tag class="text-xs">
<i-tabler name="git-branch" class="!size-4" />
{{ branchName() }}
</p-tag>
@if (headCommit()) {
<p-tag class="text-xs">
<i-tabler name="git-commit" class="!size-4" />
{{ headCommit().sha | slice: 0 : 7 }}
</p-tag>
} @else {
<span class="font-semibold">The commit sha could not be loaded.</span>
}
</div>
<div class="flex">
@if (commitListData.commitsLength > 0) {
<div class="font-semibold">
<p-button link pTooltip="Commit info can be seen soon!" (onClick)="isCommitListVisible.set(!isCommitListVisible())">
{{ commitListData.commitsLength }} new commit(s)
</p-button>
will be included in this tag
</div>
} @else if (commitListData.commitsLength == -1) {
<span class="font-semibold">There is no previous tag yet, this will be the first tag created on this branch.</span>
} @else {
<span class="font-semibold">There is no new commit since the last tag, please create one first before creating another tag!</span>
}
</div>
</div>
}
<ng-template #footer>
<p-button label="Cancel" [text]="true" (click)="onClose()" />
<p-button label="Save" severity="primary" [disabled]="newCommitListQuery.data()?.commitsLength === 0 || tagName() === ''" (click)="createTag()" />
</ng-template>
</p-dialog>
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { SlicePipe } from '@angular/common';
import { Component, inject, input, model, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { CommitInfoDto } from '@app/core/modules/openapi';
import { createTagMutation, getBranchByRepositoryIdAndNameQueryKey, getCommitsSinceLastTagOptions } from '@app/core/modules/openapi/@tanstack/angular-query-experimental.gen';
import { KeycloakService } from '@app/core/services/keycloak/keycloak.service';
import { injectMutation, injectQuery, QueryClient } from '@tanstack/angular-query-experimental';
import { IconsModule } from 'icons.module';
import { MessageService } from 'primeng/api';
import { ButtonModule } from 'primeng/button';
import { DialogModule } from 'primeng/dialog';
import { InputTextModule } from 'primeng/inputtext';
import { TagModule } from 'primeng/tag';
import { TooltipModule } from 'primeng/tooltip';

@Component({
selector: 'app-tag-create',
imports: [ButtonModule, DialogModule, IconsModule, FormsModule, InputTextModule, TagModule, SlicePipe, TooltipModule],
templateUrl: './tag-create.component.html',
})
export class TagCreateComponent {
private messageService = inject(MessageService);
private keycloakService = inject(KeycloakService);
private queryClient = inject(QueryClient);

isVisible = model.required<boolean>();
branchName = input.required<string>();
headCommit = input.required<CommitInfoDto>();
repositoryId = input.required<number>();

isCommitListVisible = signal(false);
tagName = signal('');

newCommitListQuery = injectQuery(() => ({
...getCommitsSinceLastTagOptions({ query: { branch: this.branchName() } }),
enabled: !!this.isVisible() && !!this.branchName(),
}));
newCommitListMutation = injectMutation(() => ({
...createTagMutation(),
onSuccess: () => {
this.messageService.add({ severity: 'success', summary: 'Tag created', detail: 'Tag has been created successfully' });
this.queryClient.invalidateQueries({
queryKey: getBranchByRepositoryIdAndNameQueryKey({ path: { repoId: this.repositoryId() }, query: { name: this.branchName() } }),
});
this.onClose();
},
}));

onClose = () => {
this.isVisible.update(() => false);
};

createTag = () => {
this.newCommitListMutation.mutate({
body: { name: this.tagName(), commitSha: this.headCommit().sha, branchName: this.branchName() },
});
};
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
<div class="flex items-center gap-2 w-full">
<p-button type="button" outlined (onClick)="toggle($event)" size="small">
@if (searchTableService().hasActiveFilter()) {
<i-tabler name="filter" />
} @else {
<i-tabler name="filter-plus" />
}
</p-button>
@if (searchTableService().filterOptions.length > 0) {
<p-button type="button" outlined (onClick)="toggle($event)" size="small">
@if (searchTableService().hasActiveFilter()) {
<i-tabler name="filter" />
} @else {
<i-tabler name="filter-plus" />
}
</p-button>
}
<input
pInputText
[(ngModel)]="searchTableService().searchValue"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
@if (environmentQuery.isPending() || environmentQuery.isError()) {
<p-table [value]="[1, 2, 3, 4, 5]">
<ng-template pTemplate="header">
<tr>
<th>Name</th>
<th>Status</th>
<th>Deploy</th>
</tr>
</ng-template>
<ng-template pTemplate="body">
<tr>
<td>
<p-skeleton class="w-96" />
</td>
<td class="w-[0.1%]"><p-skeleton class="w-96" /></td>
<td class="w-[0.1%]">
<p-button disabled pTooltip="Deployment will be available soon here!">Deploy</p-button>
</td>
</tr>
</ng-template>
</p-table>
} @else {
<p-table #table [rowHover]="true" [value]="environmentQuery.data() || []" [paginator]="true" [rows]="20">
<ng-template #header>
<tr>
<th>Name</th>
<th>Status</th>
<th>Deploy</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-environment>
<tr>
<td>
{{ environment.name }}
</td>
<td>
@switch (deploymentStatus(environment)) {
@case ('SUCCESS') {
<p-tag value="success" class="text-xs" severity="success" />
}
@case ('REPLACED') {
<div class="flex items-center gap-1">
<p-tag value="replaced" class="text-xs" severity="contrast" />
now at
@if (environment.latestDeployment.tagName) {
<p-tag class="text-xs">
<i-tabler name="git-branch" class="!size-4" />
{{ environment.latestDeployment.tagName }}
</p-tag>
} @else {
<p-tag class="text-xs">
<i-tabler name="git-branch" class="!size-4" />
{{ environment.latestDeployment.ref }}
</p-tag>
}
</div>
}
@case ('FAILURE') {
<p-tag value="failed" class="text-xs" severity="danger" />
}
@case ('NEVER_DEPLOYED') {
<div class="flex items-center gap-1">
@if (environment.latestDeployment.tagName) {
now at
<p-tag class="text-xs">
<i-tabler name="git-branch" class="!size-4" />
{{ environment.latestDeployment.tagName }}
</p-tag>
} @else if (environment.latestDeployment.ref) {
now at
<p-tag class="text-xs">
<i-tabler name="git-branch" class="!size-4" />
{{ environment.latestDeployment.ref }}
</p-tag>
} @else {
no tag deployed yet
}
</div>
}
@default {
<p-tag value="unknown" class="text-xs" />
now at {{ environment.latestDeployment.tagName }}
}
}
</td>
<td>
<p-button disabled pTooltip="Deployment will be available soon here!">Deploy</p-button>
</td>
</tr>
</ng-template>
<ng-template #emptymessage>
<tr>
<td colspan="4">
<div class="flex flex-col gap-2 p-20 justify-center items-center">
<i-tabler name="server-cog" class="!h-20 !w-20 text-red-500" />
<span class="font-semibold text-xl">There were no environments found.</span>
<span>Try creating a new environment in GitHub first.</span>
</div>
</td>
</tr>
</ng-template>
</p-table>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Component, input } from '@angular/core';
import { EnvironmentDto, TagDetailsDto } from '@app/core/modules/openapi';
import { getAllEnabledEnvironmentsOptions } from '@app/core/modules/openapi/@tanstack/angular-query-experimental.gen';
import { injectQuery } from '@tanstack/angular-query-experimental';
import { IconsModule } from 'icons.module';
import { AvatarModule } from 'primeng/avatar';
import { ButtonModule } from 'primeng/button';
import { OverlayBadgeModule } from 'primeng/overlaybadge';
import { BadgeModule } from 'primeng/badge';
import { SkeletonModule } from 'primeng/skeleton';
import { TableModule } from 'primeng/table';
import { TagModule } from 'primeng/tag';
import { TooltipModule } from 'primeng/tooltip';

@Component({
selector: 'app-tag-deployment-table',
imports: [TableModule, ButtonModule, IconsModule, SkeletonModule, TagModule, AvatarModule, OverlayBadgeModule, BadgeModule, TooltipModule],
templateUrl: './tag-deployment-table.component.html',
})
export class TagDeploymentTableComponent {
tag = input.required<TagDetailsDto>();

environmentQuery = injectQuery(() => getAllEnabledEnvironmentsOptions());

deploymentStatus = (environment: EnvironmentDto) => {
let deployments = this.tag().deployments.filter(deployment => deployment.environment.id === environment.id);

// If there are no deployments related to this tag anymore, this tag was not deployed yet
if (deployments.length === 0) {
return 'NEVER_DEPLOYED';
}

// If the latest deployment is not part of the deployments list, but there were deployments to this environment, the tag was replaced
if (deployments.every(deployment => environment.latestDeployment?.id !== deployment.id)) {
return 'REPLACED';
}

// For the following cases, we are only interested in the deployment that matches the latest deployment of the environment
const deployment = deployments.find(deployment => environment.latestDeployment?.id === deployment.id);

// If there is no deployment that matches the latest deployment, the tag was replaced
if (deployment === undefined) {
return 'REPLACED';
}

if (deployment.state === 'SUCCESS') {
return 'SUCCESS';
}

if (deployment.state === 'FAILURE') {
return 'FAILURE';
}
return 'UNKNOWN';
};
}
74 changes: 74 additions & 0 deletions client/src/app/components/tag-table/tag-table.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
@if (tagsQuery.isPending() || tagsQuery.isError()) {
<p-table [value]="[1, 2, 3, 4, 5]" styleClass="p-datatable-lg">
<ng-template pTemplate="header">
<tr>
<th>
<div class="flex items-center gap-2">
<p-button type="button" outlined disabled size="small">
<i-tabler name="plus" />
</p-button>
<input pInputText type="text" pSize="small" placeholder="Search" class="w-full" disabled />
</div>
</th>
<th>Branch</th>
<th>Commit</th>
</tr>
</ng-template>
<ng-template pTemplate="body">
<tr>
<td><p-skeleton /></td>
<td><p-skeleton /></td>
<td><p-skeleton /></td>
</tr>
</ng-template>
</p-table>
} @else {
<p-table #table [rowHover]="true" [value]="filteredTags()" [paginator]="true" [rows]="20" [globalFilterFields]="['name']">
<ng-template #header>
<tr>
<th>
<div class="flex gap-2 items-center">
<p-button type="button" outlined size="small" routerLink="../../ci-cd/branch">
<i-tabler name="plus" />
</p-button>
<app-table-filter [table]="table" [searchTableService]="searchTableService" />
</div>
</th>
<th>Branch</th>
<th>Commit</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-tag>
<tr class="cursor-pointer" [routerLink]="'../' + tag.name">
<td>
<p-tag [value]="tag.name" [rounded]="true" class="border font-medium text-xs py-0.5 whitespace-nowrap" />
</td>
<td>
<p-tag>
<i-tabler name="git-branch" class="!size-5" />
{{ tag.branchName }}
</p-tag>
</td>

<td>
<p-tag>
<i-tabler name="git-commit" class="!size-5" />
{{ tag.commitSha | slice: 0 : 6 }}
</p-tag>
</td>
</tr>
</ng-template>
<ng-template #emptymessage>
<tr>
<td colspan="4">
<div class="flex flex-col gap-2 p-20 justify-center items-center">
<i-tabler name="tag" class="!h-20 !w-20 text-red-500" />
<span class="font-semibold text-xl">There were no tags found matching your filter criteria.</span>
<span>Try clearing the filter, creating tags or reloading the page.</span>
<p-button (click)="searchTableService.clearFilter(table)">Clear Filter</p-button>
</div>
</td>
</tr>
</ng-template>
</p-table>
}
Loading

0 comments on commit 70e8dc9

Please sign in to comment.