diff --git a/src/components/docs/versions/builds/build-failure-details.tsx b/src/components/docs/versions/builds/build-failure-details.tsx
new file mode 100644
index 00000000..860e7a78
--- /dev/null
+++ b/src/components/docs/versions/builds/build-failure-details.tsx
@@ -0,0 +1,93 @@
+import React, { useReducer } from 'react';
+import CodeBlock from '@theme/CodeBlock';
+
+interface Props {
+ ciJob;
+ ciBuild;
+ repoVersionInfo;
+ editorVersionInfo;
+ style;
+}
+
+const BuildFailureDetails = ({
+ ciJob,
+ repoVersionInfo,
+ editorVersionInfo,
+ ciBuild,
+ ...rest
+}: Props) => {
+ const { editorVersion, baseOs, targetPlatform } = ciBuild.buildInfo;
+ const { major, minor, patch } = repoVersionInfo;
+
+ const reducer = (state, action) => {
+ const { tag, value } = action;
+ return { ...state, [tag]: value };
+ };
+
+ const [tags /* , dispatch */] = useReducer(reducer, {
+ [`${baseOs}-${editorVersion}-${targetPlatform}-${major}`]: '❓',
+ [`${baseOs}-${editorVersion}-${targetPlatform}-${major}.${minor}`]: '❓',
+ [`${baseOs}-${editorVersion}-${targetPlatform}-${major}.${minor}.${patch}`]: '❓',
+ [`${editorVersion}-${targetPlatform}-${major}`]: '❓',
+ [`${editorVersion}-${targetPlatform}-${major}.${minor}`]: '❓',
+ [`${editorVersion}-${targetPlatform}-${major}.${minor}.${patch}`]: '❓',
+ });
+
+ // Todo - fetch docker info from dockerhub for all tags, or do it on the server
+ // useEffect(() => {
+ // (async () => {
+ // const repo = 'unityci/editor';
+ // for (const tag of Object.keys(tags)) {
+ // const requestUrl = `https://index.docker.io/v1/repositories/${repo}/tags/${tag}`;
+ // try {
+ // const response = await fetch(requestUrl);
+ // dispatch({ tag, value: response.status === 0 });
+ // } catch (error) {
+ // dispatch({ tag, value: false });
+ // }
+ // }
+ // })();
+ // }, []);
+
+ const { changeSet } = editorVersionInfo;
+ const buildCommand = `#!/usr/bin/env bash
+git clone git@github.com:game-ci/docker.git
+cd docker
+
+editor_version=${editorVersion}
+change_set=${changeSet}
+base_os=${baseOs}
+target_platform=${targetPlatform}
+image_name=unityci-editor:$editor_version-$target_platform
+
+docker build . \\
+ --file ./images/$base_os/editor/Dockerfile \\
+ -t $image_name \\
+ --build-arg=version=$editor_version \\
+ --build-arg=changeSet=$change_set \\
+ --build-arg=module=$target_platform
+`;
+
+ const pullCommand = `docker pull unityci/editor:${baseOs}-${editorVersion}-${targetPlatform}-${major}.${minor}.${patch}`;
+
+ return (
+
+
CI Job identification
+
{JSON.stringify(ciJob, null, 2)}
+
+
Commands
+
Build the docker image locally for debugging:
+
{buildCommand}
+
Pull this docker image:
+
{pullCommand}
+
+
Associated tags on docker hub
+
{JSON.stringify(tags, null, 2)}
+
+
CI Build details
+
{JSON.stringify(ciBuild, null, 2)}
+
+ );
+};
+
+export default BuildFailureDetails;
diff --git a/src/components/docs/versions/builds/build-row.tsx b/src/components/docs/versions/builds/build-row.tsx
new file mode 100644
index 00000000..79bd02a2
--- /dev/null
+++ b/src/components/docs/versions/builds/build-row.tsx
@@ -0,0 +1,83 @@
+import React, { useState } from 'react';
+import DockerImageLinkOrRetryButton, {
+ type Record,
+} from '@site/src/components/docs/versions/docker-image-link-or-retry-button';
+import Spinner from '@site/src/components/molecules/spinner';
+import Tooltip from '@site/src/components/molecules/tooltip/tooltip';
+import styles from './builds.module.scss';
+
+const mapBuildStatusToIcon = {
+ started: ,
+ failed: '⚠',
+ published: '✅',
+};
+
+type Props = {
+ children: React.JSX.Element | React.JSX.Element[];
+ build: Record;
+};
+
+const CopyToClipboard = (copyString: string) => {
+ navigator.clipboard.writeText(copyString);
+};
+
+export default function BuildRow({ children, build }: Props) {
+ const [expanded, setExpanded] = useState(false);
+ const [toolbarContent, setToolbarContent] = useState('Click to copy');
+
+ const MapBuildStatusToElement = (status: string) => {
+ const icon = mapBuildStatusToIcon[status];
+
+ switch (status) {
+ case 'started':
+ return ;
+ case 'failed':
+ return {icon};
+ case 'published':
+ return icon;
+ default:
+ return status;
+ }
+ };
+
+ return (
+ <>
+
+ setExpanded(!expanded)}
+ className="text-center select-none cursor-pointer text-2xl"
+ >
+ {expanded ? '-' : '+'}
+ |
+ {MapBuildStatusToElement(build.status)} |
+
+
+
+
+
+
+
+ |
+ {build.imageType} |
+ {build.buildInfo.baseOs} |
+ {build.buildInfo.targetPlatform} |
+
+ {expanded && (
+
+ {children} |
+
+ )}
+ >
+ );
+}
diff --git a/src/components/docs/versions/builds/builds.module.scss b/src/components/docs/versions/builds/builds.module.scss
new file mode 100644
index 00000000..9256dab7
--- /dev/null
+++ b/src/components/docs/versions/builds/builds.module.scss
@@ -0,0 +1,22 @@
+.stickyRow:global(.ant-table-row.ant-table-row-level-0) > :global(td) {
+ position: sticky;
+ top: 99px; /* height of the collapse-item-header + the parent tr > td sticky */
+ background-color: rgb(255, 255, 255);
+ z-index: 2;
+ box-shadow: 0 0 25px rgba(255,255,255,1), 0 0 15px rgba(255,255,255,1), 0 0 20px rgba(255,255,255,1);
+ clip-path: inset(-25px 0 0 0);
+}
+
+.stickyRow:global(.ant-table-row.ant-table-row-level-0) > :global(td:not(:last-child)) {
+ clip-path: inset(-25px -1px 0 0); /* fix for space from responsive vs fixed width on cells */
+}
+
+.expandedContentRow {
+ height: 0;
+ overflow: hidden;
+}
+
+
+.tableRow {
+ width: 100%;
+}
diff --git a/src/components/docs/versions/builds/builds.tsx b/src/components/docs/versions/builds/builds.tsx
new file mode 100644
index 00000000..64e2f9ef
--- /dev/null
+++ b/src/components/docs/versions/builds/builds.tsx
@@ -0,0 +1,62 @@
+import React from 'react';
+import { useFirestore, useFirestoreCollectionData } from 'reactfire';
+import BuildFailureDetails from '@site/src/components/docs/versions/builds/build-failure-details';
+import styles from './builds.module.scss';
+import BuildRow from './build-row';
+import { Record } from '../docker-image-link-or-retry-button';
+
+interface RepoVersionInfo {
+ version: string;
+ major: number;
+ minor: number;
+ patch: number;
+}
+
+interface Props {
+ ciJobId: string;
+ repoVersionInfo: RepoVersionInfo;
+ editorVersionInfo;
+}
+
+const Builds = ({ ciJobId, repoVersionInfo, editorVersionInfo }: Props) => {
+ const loading = Fetching builds...
;
+
+ const ciBuilds = useFirestore().collection('ciBuilds').where('relatedJobId', '==', ciJobId);
+
+ const { status, data } = useFirestoreCollectionData<{ [key: string]: any }>(ciBuilds);
+ const isLoading = status === 'loading';
+
+ if (isLoading) {
+ return loading;
+ }
+
+ const expandable = {
+ expandedRowRender: (record) => (
+
+ ),
+ };
+
+ return (
+
+
+ |
+ Status |
+ Build ID |
+ Image type |
+ OS |
+ Target Platform |
+
+ {data.map((build: Record) => (
+ {expandable.expandedRowRender(build)}
+ ))}
+
+ );
+};
+
+export default Builds;
diff --git a/src/components/docs/versions/date-time.tsx b/src/components/docs/versions/date-time.tsx
index 58e243a4..212cafb7 100644
--- a/src/components/docs/versions/date-time.tsx
+++ b/src/components/docs/versions/date-time.tsx
@@ -1,4 +1,5 @@
import React from 'react';
+import Tooltip from '../../molecules/tooltip/tooltip';
interface Props {
utcSeconds: number;
@@ -10,8 +11,8 @@ const options: Intl.DateTimeFormatOptions = {
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
- second: 'numeric',
timeZoneName: 'short',
+ hour12: true,
};
const DateTime = ({ utcSeconds }: Props) => {
@@ -19,7 +20,13 @@ const DateTime = ({ utcSeconds }: Props) => {
date.setUTCSeconds(utcSeconds);
- return {`${date.toLocaleString('en-GB', options)}`};
+ return (
+
+
+ ( ... )
+
+
+ );
};
export default DateTime;
diff --git a/src/components/docs/versions/docker-image-link-or-retry-button.tsx b/src/components/docs/versions/docker-image-link-or-retry-button.tsx
index bbd02291..544747a1 100644
--- a/src/components/docs/versions/docker-image-link-or-retry-button.tsx
+++ b/src/components/docs/versions/docker-image-link-or-retry-button.tsx
@@ -1,4 +1,3 @@
-import { Tooltip } from 'antd';
import React, { useState } from 'react';
import { HiOutlineRefresh } from 'react-icons/hi';
import { SimpleAuthCheck } from '@site/src/components/auth/safe-auth-check';
@@ -6,25 +5,32 @@ import DockerImageLink from '@site/src/components/docs/versions/docker-image-lin
import { useAuthenticatedEndpoint } from '@site/src/core/hooks/use-authenticated-endpoint';
import { useNotification } from '@site/src/core/hooks/use-notification';
import Spinner from '@site/src/components/molecules/spinner';
+import Tooltip from '@site/src/components/molecules/tooltip/tooltip';
-interface Props {
- record: {
- buildId: string;
- relatedJobId: string;
- buildInfo: {
- baseOs: string;
- editorVersion: string;
- targetPlatform: string;
- repoVersion: string;
- };
- dockerInfo: {
- imageRepo: string;
- imageName: string;
- };
- status: string;
+type Record = {
+ buildId: string;
+ relatedJobId: string;
+ buildInfo: {
+ baseOs: string;
+ editorVersion: string;
+ targetPlatform: string;
+ repoVersion: string;
+ };
+ dockerInfo: {
+ digest: string;
+ imageRepo: string;
+ imageName: string;
};
+ status: string;
+ [key: string]: any;
+};
+
+interface Props {
+ record: Record;
}
+export { Record };
+
const DockerImageLinkOrRetryButton = ({ record }: Props) => {
const { buildInfo, dockerInfo, buildId, relatedJobId, status } = record;
const { baseOs, editorVersion, targetPlatform, repoVersion } = buildInfo;
@@ -55,7 +61,7 @@ const DockerImageLinkOrRetryButton = ({ record }: Props) => {
return (
} requiredClaims={{ admin: true }}>
-
+