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  };