github.com/freiheit-com/kuberpult@v1.24.2-0.20240328135542-315d5630abe6/services/frontend-service/src/ui/components/CommitInfo/CommitInfo.tsx (about) 1 /*This file is part of kuberpult. 2 3 Kuberpult is free software: you can redistribute it and/or modify 4 it under the terms of the Expat(MIT) License as published by 5 the Free Software Foundation. 6 7 Kuberpult is distributed in the hope that it will be useful, 8 but WITHOUT ANY WARRANTY; without even the implied warranty of 9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 MIT License for more details. 11 12 You should have received a copy of the MIT License 13 along with kuberpult. If not, see <https://directory.fsf.org/wiki/License:Expat>. 14 15 Copyright 2023 freiheit.com*/ 16 17 import { TopAppBar } from '../TopAppBar/TopAppBar'; 18 import { GetCommitInfoResponse, Event, LockPreventedDeploymentEvent_LockType } from '../../../api/api'; 19 20 type CommitInfoProps = { 21 commitInfo: GetCommitInfoResponse | undefined; 22 }; 23 24 export const CommitInfo: React.FC<CommitInfoProps> = (props) => { 25 const commitInfo = props.commitInfo; 26 27 if (commitInfo === undefined) { 28 return ( 29 <div> 30 <TopAppBar showAppFilter={false} showTeamFilter={false} showWarningFilter={false} /> 31 <main className="main-content commit-page">Backend returned empty response</main> 32 </div> 33 ); 34 } 35 // most commit messages and with "/n" but that looks odd when rendered in a table: 36 const msgWithoutTrailingN = commitInfo.commitMessage.trimEnd(); 37 const nextPrevMessage = 38 'Note that kuberpult links to the next commit in the repository that it is aware of.' + 39 'This is not necessarily the next/previous commit that touches the desired microservice.'; 40 const tooltipMsg = ' Limitation: Currently only commits that touch exactly one app are linked.'; 41 const showInfo = !commitInfo.nextCommitHash || !commitInfo.previousCommitHash; 42 return ( 43 <div> 44 <TopAppBar showAppFilter={false} showTeamFilter={false} showWarningFilter={false} /> 45 <main className="main-content commit-page"> 46 <h1> Commit: {commitInfo.commitMessage.split('\n')[0]} </h1> 47 <div> 48 <table className={'metadata'} border={1}> 49 <thead> 50 <tr> 51 <th className={'hash'}>Commit Hash:</th> 52 <th className={'message'}>Commit Message:</th> 53 <th className={'apps'}>Touched apps:</th> 54 </tr> 55 </thead> 56 <tbody> 57 <tr> 58 <td>{commitInfo.commitHash}</td> 59 <td> 60 <div className={'commit-page-message'}> 61 {msgWithoutTrailingN.split('\n').map((msg, index) => ( 62 <div key={index}> 63 <span>{msg}</span> 64 <br /> 65 </div> 66 ))} 67 </div> 68 </td> 69 <td>{commitInfo.touchedApps.join(', ')}</td> 70 </tr> 71 </tbody> 72 </table> 73 <div> 74 {commitInfo.touchedApps.length < 2 && ( 75 <div className="next-prev-buttons"> 76 {commitInfo.previousCommitHash !== '' && ( 77 <div className="history-button-container"> 78 <a 79 href={'/ui/commits/' + commitInfo.previousCommitHash} 80 title={nextPrevMessage}> 81 Previous Commit 82 </a> 83 </div> 84 )} 85 86 {commitInfo.nextCommitHash !== '' && ( 87 <div className="history-button-container"> 88 <a href={'/ui/commits/' + commitInfo.nextCommitHash} title={nextPrevMessage}> 89 Next Commit 90 </a> 91 </div> 92 )} 93 94 {showInfo && <div title={tooltipMsg}> ⓘ </div>} 95 </div> 96 )} 97 </div> 98 </div> 99 <h2>Events</h2> 100 <CommitInfoEvents events={commitInfo.events} /> 101 </main> 102 </div> 103 ); 104 }; 105 106 const CommitInfoEvents: React.FC<{ events: Event[] }> = (props) => ( 107 <table className={'events'} border={1}> 108 <thead> 109 <tr> 110 <th className={'date'}>Date:</th> 111 <th className={'description'}>Event Description:</th> 112 <th className={'environments'}>Environments:</th> 113 </tr> 114 </thead> 115 <tbody> 116 {props.events.map((event, _) => { 117 const createdAt = event.createdAt?.toISOString() || ''; 118 const [description, environments] = eventDescription(event); 119 return ( 120 <tr key={event.uuid}> 121 <td>{createdAt}</td> 122 <td>{description}</td> 123 <td>{environments}</td> 124 </tr> 125 ); 126 })} 127 </tbody> 128 </table> 129 ); 130 131 const eventDescription = (event: Event): [JSX.Element, string] => { 132 const tp = event.eventType; 133 if (tp === undefined) { 134 return [<span>Unspecified event type</span>, '']; 135 } 136 switch (tp.$case) { 137 case 'createReleaseEvent': 138 return [ 139 <span>Kuberpult received data about this commit for the first time</span>, 140 tp.createReleaseEvent.environmentNames.join(', '), 141 ]; 142 case 'deploymentEvent': 143 const de = tp.deploymentEvent; 144 let description: JSX.Element; 145 if (de.releaseTrainSource === undefined) 146 description = ( 147 <span> 148 Manual deployment of application <b>{de.application}</b> to environment{' '} 149 <b>{de.targetEnvironment}</b> 150 </span> 151 ); 152 else { 153 if (de.releaseTrainSource?.targetEnvironmentGroup === undefined) 154 description = ( 155 <span> 156 Release train deployment of application <b>{de.application}</b> from environment{' '} 157 <b>{de.releaseTrainSource.upstreamEnvironment}</b> to environment{' '} 158 <b>{de.targetEnvironment}</b> 159 </span> 160 ); 161 else 162 description = ( 163 <span> 164 Release train deployment of application <b>{de.application}</b> on environment group{' '} 165 <b>{de.releaseTrainSource.targetEnvironmentGroup}</b> from environment{' '} 166 <b>{de.releaseTrainSource?.upstreamEnvironment}</b> to environment{' '} 167 <b>{de.targetEnvironment}</b> 168 </span> 169 ); 170 } 171 return [description, de.targetEnvironment]; 172 case 'lockPreventedDeploymentEvent': 173 const inner = tp.lockPreventedDeploymentEvent; 174 return [ 175 <span> 176 Application <b>{inner.application}</b> was blocked from deploying due to{' '} 177 {lockTypeName(inner.lockType)} with message "{inner.lockMessage}" 178 </span>, 179 inner.environment, 180 ]; 181 case 'replacedByEvent': 182 return [ 183 <span> 184 This commit was replaced by{' '} 185 <a href={'/ui/commits/' + tp.replacedByEvent.replacedByCommitId}> 186 {tp.replacedByEvent.replacedByCommitId.substring(0, 8)} 187 </a>{' '} 188 on <b>{tp.replacedByEvent.environment}</b>. 189 </span>, 190 tp.replacedByEvent.environment, 191 ]; 192 } 193 }; 194 195 const lockTypeName = (tp: LockPreventedDeploymentEvent_LockType): string => { 196 switch (tp) { 197 case LockPreventedDeploymentEvent_LockType.LOCK_TYPE_APP: 198 return 'an application lock'; 199 case LockPreventedDeploymentEvent_LockType.LOCK_TYPE_ENV: 200 return 'an environment lock'; 201 case LockPreventedDeploymentEvent_LockType.LOCK_TYPE_UNKNOWN: 202 case LockPreventedDeploymentEvent_LockType.UNRECOGNIZED: 203 return 'an unknown lock'; 204 } 205 };