feat(pipelines): add pipelines feature (services+ui) (wip)
+ 276
- 35
@@ -1,5 +1,5 @@
 {
-  "_generatedAtUnix": 1778671812667,
+  "_generatedAtUnix": 1778680701304,
   "_hashAlgorithm": "sha1",
   "_version": 2,
   "assets": {

...
@@ -121,6 +121,26 @@
       "hash": "9f4015bd7b0c21cf393cf3309f1bd52c9cd2c4d2",
       "pathSource": "./app/views/organization/OrganizationDetailsView.tsx"
     },
+    "PipelineArtefactsView": {
+      "hash": "3dc329458bf81379bdd421f9ecf4d7f5a195c88d",
+      "pathSource": "./app/views/pipelines/PipelineArtefactsView.tsx"
+    },
+    "PipelineDetailsView": {
+      "hash": "3d5cfe3a4cdb93a153f894e4e452ba01708acff5",
+      "pathSource": "./app/views/pipelines/PipelineDetailsView.tsx"
+    },
+    "PipelineStageDetailsView": {
+      "hash": "dfae473d54628e39af86f2e8a53dea541b2e0391",
+      "pathSource": "./app/views/pipelines/PipelineStageDetailsView.tsx"
+    },
+    "PipelineStagesView": {
+      "hash": "89b02e099d9896b1fa67e0dfcbcdafc4ed950f0e",
+      "pathSource": "./app/views/pipelines/PipelineStagesView.tsx"
+    },
+    "PipelinesView": {
+      "hash": "a194e274b2b11c5e9facd86ba669719874399f99",
+      "pathSource": "./app/views/pipelines/PipelinesView.tsx"
+    },
     "RepositoryBrowserView": {
       "hash": "8ad5b2c67b6a65a04c2976cba9079ec2f0da1eee",
       "pathSource": "./app/views/repository/RepositoryBrowserView.tsx"

app/components/DrawerPrimary.tsx
@@ -74,6 +74,11 @@ export const DrawerPrimary = ({
     repoSlug: repoSlug,
   });
 
+  const pathPipelines = buildRouteLink(AppRoute.REPOSITORY_PIPELINES, {
+    orgSlug: orgSlug,
+    repoSlug: repoSlug,
+  });
+
   // const isMobile = useMediaQuery("sm");
   // if (isMobile === false) {
   //   visible = true;

...
@@ -212,14 +217,23 @@ export const DrawerPrimary = ({
             <span>Pull Requests</span>
             <Chip themeScheme={themeScheme}>{counters.pulls || 0}</Chip>
           </StyledDrawerListItem>
+          <StyledDrawerListItem
+            themeScheme={themeScheme}
+            href={pathPipelines}
+            className={
+              commonProps.path! === pathPipelines ||
+              commonProps.path!.startsWith(pathPipelines)
+                ? "active"
+                : undefined
+            }
+          >
+            <span>Pipelines</span>
+            <Chip themeScheme={themeScheme}>{counters.builds || 0}</Chip>
+          </StyledDrawerListItem>
           <StyledDrawerListItem themeScheme={themeScheme} disabled>
             <span>Tests & Coverage</span>
             <Chip themeScheme={themeScheme}>{counters.tests || 0}</Chip>
           </StyledDrawerListItem>
-          <StyledDrawerListItem themeScheme={themeScheme} disabled>
-            <span>Builds</span>
-            <Chip themeScheme={themeScheme}>{counters.builds || 0}</Chip>
-          </StyledDrawerListItem>
           <StyledDrawerListItem themeScheme={themeScheme} disabled>
             <span>Issues</span>
             <Chip themeScheme={themeScheme}>{counters.issues || 0}</Chip>

app/controllers/index.ts
@@ -3,16 +3,9 @@ export { HomeController } from "./home";
 export { OrganizationController } from "./organization";
 export { RepositoryController } from "./repository";
 export { RepositoryPullRequestsController } from "./repositoryPullRequests";
+export { PipelinesController as RepositoryPipelinesController } from "./pipelines";
 export { SettingsController } from "./settings";
 export { SSHAuthController } from "./ssh-auth";
 export { SyntaxHighlightController } from "./syntaxHighlight";
 export { ThemeController } from "./theme";
 export { UserController } from "./user";
-export { getPipelinesView } from "./pipelines/getPipelinesView";
-export { default as getPipelineArtefactsView } from "./pipelines/getPipelineArtefactsView";
-export { default as getPipelineDetailsView } from "./pipelines/getPipelineDetailsView";
-export { default as getPipelineStageDetailsView } from "./pipelines/getPipelineStageDetailsView";
-export { default as getPipelineStagesView } from "./pipelines/getPipelineStagesView";
-export { default as getPipelinesDetailsView } from "./repository/pipelines/getPipelinesView";
-export { default as postPipelineTriggerAction } from "./pipelines/postPipelineTriggerAction";
-export { default as getPipelineArtefactsView } from "./pipelines/getPipelineArtefactsView";

app/controllers/pipelines/getPipelinesView.ts
@@ -9,7 +9,7 @@ import PipelinesView, {
 
 const getPipelinesView: ReqHandler<
   AppRouteParams,
-  AppRoute.REPOSITORY_PIPELINES_LIST
+  AppRoute.REPOSITORY_PIPELINES
 > = async (request, reply) => {
   const { orgSlug, repoSlug } = request.params;
 

app/routes.defs.tsx
@@ -27,8 +27,10 @@ export enum AppRoute {
   REPOSITORY_DETAILS = "repository.details",
   REPOSITORY_DETAILS_WITH_TRAILING_SLASH = "repository.details.with_trailing_slash",
   REPOSITORY_EXPLORE = "repository.explore",
+  REPOSITORY_SHOW_OBJECT = "repository.show_object",
   REPOSITORY_FORK = "repository.fork",
   REPOSITORY_FORK_ACTION = "repository.fork.action",
+  REPOSITORY_PULL_REQUESTS = "repository.pull_requests",
   REPOSITORY_PULL_REQUEST_CLOSE_ACTION = "repository.pull_request.close.action",
   REPOSITORY_PULL_REQUEST_COMMENT_CREATE_ACTION = "repository.pull_request.comment.create.action",
   REPOSITORY_PULL_REQUEST_COMMENT_DELETE_ACTION = "repository.pull_request.comment.delete.action",

...
@@ -40,8 +42,12 @@ export enum AppRoute {
   REPOSITORY_PULL_REQUEST_MERGE_ACTION = "repository.pull_request.merge.action",
   REPOSITORY_PULL_REQUEST_DELETE_SOURCE_BRANCH_ACTION = "repository.pull_request.delete_source_branch.action",
   REPOSITORY_PULL_REQUEST_UPDATE_ACTION = "repository.pull_request.update.action",
-  REPOSITORY_PULL_REQUESTS = "repository.pull_requests",
-  REPOSITORY_SHOW_OBJECT = "repository.show_object",
+  REPOSITORY_PIPELINES = "repository.pipelines",
+  REPOSITORY_PIPELINE_DETAILS = "repository.pipeline_details",
+  REPOSITORY_PIPELINE_STAGES = "repository.pipeline.stages",
+  REPOSITORY_PIPELINE_STAGE_DETAILS = "repository.pipeline.stages.details",
+  REPOSITORY_PIPELINE_ARTEFACTS = "repository.pipeline.artefacts",
+  REPOSITORY_PIPELINE_TRIGGER_ACTION = "repository.pipeline.trigger.action",
   SYNTAX_HIGHLIGHT_HIGHLIGHT_CODE_ACTION = "syntax_highlight.highlight_code.action",
   SETTINGS = "settings.home",
   SETTINGS_KEYS = "settings.keys.list",

...
@@ -74,8 +80,10 @@ export const AppRoutePaths: Record<AppRoute, string> = {
   [AppRoute.REPOSITORY_DETAILS]: "/:orgSlug/:repoSlug",
   [AppRoute.REPOSITORY_DETAILS_WITH_TRAILING_SLASH]: "/:orgSlug/:repoSlug/",
   [AppRoute.REPOSITORY_EXPLORE]: "/repo/explore",
+  [AppRoute.REPOSITORY_SHOW_OBJECT]: "/:orgSlug/:repoSlug/show/:objectId",
   [AppRoute.REPOSITORY_FORK]: "/:orgSlug/:repoSlug/fork",
   [AppRoute.REPOSITORY_FORK_ACTION]: "/:orgSlug/:repoSlug/fork",
+  [AppRoute.REPOSITORY_PULL_REQUESTS]: "/:orgSlug/:repoSlug/pulls",
   [AppRoute.REPOSITORY_PULL_REQUEST_CLOSE_ACTION]:
     "/:orgSlug/:repoSlug/pulls/:pullUid/close",
   [AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_CREATE_ACTION]:

...
@@ -97,8 +105,17 @@ export const AppRoutePaths: Record<AppRoute, string> = {
     "/:orgSlug/:repoSlug/pulls/:pullUid/delete-source-branch",
   [AppRoute.REPOSITORY_PULL_REQUEST_UPDATE_ACTION]:
     "/:orgSlug/:repoSlug/pulls/:pullUid/edit",
-  [AppRoute.REPOSITORY_PULL_REQUESTS]: "/:orgSlug/:repoSlug/pulls",
-  [AppRoute.REPOSITORY_SHOW_OBJECT]: "/:orgSlug/:repoSlug/show/:objectId",
+  [AppRoute.REPOSITORY_PIPELINES]: "/:orgSlug/:repoSlug/pipelines",
+  [AppRoute.REPOSITORY_PIPELINE_DETAILS]:
+    "/:orgSlug/:repoSlug/pipelines/:pipelineId",
+  [AppRoute.REPOSITORY_PIPELINE_STAGES]:
+    "/:orgSlug/:repoSlug/pipelines/:pipelineId/stages",
+  [AppRoute.REPOSITORY_PIPELINE_STAGE_DETAILS]:
+    "/:orgSlug/:repoSlug/pipelines/:pipelineId/stages/:stageId",
+  [AppRoute.REPOSITORY_PIPELINE_ARTEFACTS]:
+    "/:orgSlug/:repoSlug/pipelines/:pipelineId/artefacts",
+  [AppRoute.REPOSITORY_PIPELINE_TRIGGER_ACTION]:
+    "/:orgSlug/:repoSlug/pipelines/:pipelineId/trigger",
   [AppRoute.SETTINGS]: "/@:username/settings",
   [AppRoute.SETTINGS_KEYS]: "/@:username/settings/keys",
   [AppRoute.SETTINGS_KEY_ADD]: "/@:username/settings/keys/add",

...
@@ -220,6 +237,13 @@ export interface AppRouteParams {
     };
   };
   [AppRoute.REPOSITORY_EXPLORE]: undefined;
+  [AppRoute.REPOSITORY_SHOW_OBJECT]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+      objectId: string;
+    };
+  };
   [AppRoute.REPOSITORY_FORK]: {
     params: {
       orgSlug: string;

...
@@ -238,6 +262,12 @@ export interface AppRouteParams {
       target_repo_visibility: ResourceVisibility;
     };
   };
+  [AppRoute.REPOSITORY_PULL_REQUESTS]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+    };
+  };
   [AppRoute.REPOSITORY_PULL_REQUEST_CLOSE_ACTION]: {
     params: {
       orgSlug: string;

...
@@ -341,17 +371,46 @@ export interface AppRouteParams {
     };
     body: {}; // TODO: define this object shape
   };
-  [AppRoute.REPOSITORY_PULL_REQUESTS]: {
+  [AppRoute.REPOSITORY_PIPELINES]: {
     params: {
       orgSlug: string;
       repoSlug: string;
     };
   };
-  [AppRoute.REPOSITORY_SHOW_OBJECT]: {
+  [AppRoute.REPOSITORY_PIPELINE_DETAILS]: {
     params: {
       orgSlug: string;
       repoSlug: string;
-      objectId: string;
+      pipelineId: string;
+    };
+  };
+  [AppRoute.REPOSITORY_PIPELINE_STAGES]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+      pipelineId: string;
+    };
+  };
+  [AppRoute.REPOSITORY_PIPELINE_STAGE_DETAILS]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+      pipelineId: string;
+      stageId: string;
+    };
+  };
+  [AppRoute.REPOSITORY_PIPELINE_ARTEFACTS]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+      pipelineId: string;
+    };
+  };
+  [AppRoute.REPOSITORY_PIPELINE_TRIGGER_ACTION]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+      pipelineId: string;
     };
   };
   [AppRoute.SETTINGS]: undefined;

...
@@ -676,6 +735,24 @@ export const AppRouteSchemas: Record<AppRoute, undefined | FastifySchema> = {
     },
   },
   [AppRoute.REPOSITORY_EXPLORE]: undefined,
+  [AppRoute.REPOSITORY_SHOW_OBJECT]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug", "objectId"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+        objectId: {
+          type: "string",
+        },
+      },
+    },
+  },
   [AppRoute.REPOSITORY_FORK]: {
     params: {
       type: "object",

...
@@ -731,6 +808,21 @@ export const AppRouteSchemas: Record<AppRoute, undefined | FastifySchema> = {
       },
     },
   },
+  [AppRoute.REPOSITORY_PULL_REQUESTS]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+      },
+    },
+  },
   [AppRoute.REPOSITORY_PULL_REQUEST_CLOSE_ACTION]: {
     params: {
       type: "object",

...
@@ -996,7 +1088,7 @@ export const AppRouteSchemas: Record<AppRoute, undefined | FastifySchema> = {
       },
     },
   },
-  [AppRoute.REPOSITORY_PULL_REQUESTS]: {
+  [AppRoute.REPOSITORY_PIPELINES]: {
     params: {
       type: "object",
       required: ["orgSlug", "repoSlug"],

...
@@ -1011,10 +1103,10 @@ export const AppRouteSchemas: Record<AppRoute, undefined | FastifySchema> = {
       },
     },
   },
-  [AppRoute.REPOSITORY_SHOW_OBJECT]: {
+  [AppRoute.REPOSITORY_PIPELINE_DETAILS]: {
     params: {
       type: "object",
-      required: ["orgSlug", "repoSlug", "objectId"],
+      required: ["orgSlug", "repoSlug", "pipelineId"],
       additionalProperties: false,
       properties: {
         orgSlug: {

...
@@ -1023,7 +1115,85 @@ export const AppRouteSchemas: Record<AppRoute, undefined | FastifySchema> = {
         repoSlug: {
           type: "string",
         },
-        objectId: {
+        pipelineId: {
+          type: "string",
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_PIPELINE_STAGES]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug", "pipelineId"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+        pipelineId: {
+          type: "string",
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_PIPELINE_STAGE_DETAILS]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug", "pipelineId", "stageId"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+        pipelineId: {
+          type: "string",
+        },
+        stageId: {
+          type: "string",
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_PIPELINE_ARTEFACTS]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug", "pipelineId", "stageId"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+        pipelineId: {
+          type: "string",
+        },
+        stageId: {
+          type: "string",
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_PIPELINE_TRIGGER_ACTION]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug", "pipelineId"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+        pipelineId: {
           type: "string",
         },
       },

@@ -25,6 +25,7 @@ import {
   OrganizationController,
   RepositoryController,
   RepositoryPullRequestsController,
+  RepositoryPipelinesController,
   SSHAuthController,
   SettingsController,
   SyntaxHighlightController,

...
@@ -244,6 +245,13 @@ const RootAppRouter: AppRouter<AppRouteParams> = () => {
           path={AppRoutePaths[AppRoute.REPOSITORY_EXPLORE]}
           handler={RepositoryController.getRepositoryExploreView}
         />
+        <Route
+          name={AppRoute.REPOSITORY_SHOW_OBJECT}
+          method={"GET"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_SHOW_OBJECT]}
+          schema={AppRouteSchemas[AppRoute.REPOSITORY_SHOW_OBJECT]}
+          handler={RepositoryController.getRepositoryShowObjectView}
+        />
         <Route
           name={AppRoute.REPOSITORY_FORK}
           method={"GET"}

...
@@ -258,6 +266,15 @@ const RootAppRouter: AppRouter<AppRouteParams> = () => {
           preHandler={loggedOrLoginRedirect}
           handler={RepositoryController.postRepositoryForkAction}
         />
+        <Route
+          name={AppRoute.REPOSITORY_PULL_REQUESTS}
+          method={"GET"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_PULL_REQUESTS]}
+          schema={AppRouteSchemas[AppRoute.REPOSITORY_PULL_REQUESTS]}
+          handler={
+            RepositoryPullRequestsController.getRepositoryPullRequestsView
+          }
+        />
         <Route
           name={AppRoute.REPOSITORY_PULL_REQUEST_CLOSE_ACTION}
           method={"POST"}

...
@@ -408,21 +425,48 @@ const RootAppRouter: AppRouter<AppRouteParams> = () => {
             RepositoryPullRequestsController.getRepositoryPullRequestUpdateAction
           }
         />
+        {/* --- */}
         <Route
-          name={AppRoute.REPOSITORY_PULL_REQUESTS}
+          name={AppRoute.REPOSITORY_PIPELINES}
           method={"GET"}
-          path={AppRoutePaths[AppRoute.REPOSITORY_PULL_REQUESTS]}
-          schema={AppRouteSchemas[AppRoute.REPOSITORY_PULL_REQUESTS]}
-          handler={
-            RepositoryPullRequestsController.getRepositoryPullRequestsView
-          }
+          path={AppRoutePaths[AppRoute.REPOSITORY_PIPELINES]}
+          schema={AppRouteSchemas[AppRoute.REPOSITORY_PIPELINES]}
+          handler={RepositoryPipelinesController.getPipelinesView}
         />
         <Route
-          name={AppRoute.REPOSITORY_SHOW_OBJECT}
+          name={AppRoute.REPOSITORY_PIPELINE_DETAILS}
           method={"GET"}
-          path={AppRoutePaths[AppRoute.REPOSITORY_SHOW_OBJECT]}
-          schema={AppRouteSchemas[AppRoute.REPOSITORY_SHOW_OBJECT]}
-          handler={RepositoryController.getRepositoryShowObjectView}
+          path={AppRoutePaths[AppRoute.REPOSITORY_PIPELINE_DETAILS]}
+          schema={AppRouteSchemas[AppRoute.REPOSITORY_PIPELINE_DETAILS]}
+          handler={RepositoryPipelinesController.getPipelineDetailsView}
+        />
+        <Route
+          name={AppRoute.REPOSITORY_PIPELINE_STAGES}
+          method={"GET"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_PIPELINE_STAGES]}
+          schema={AppRouteSchemas[AppRoute.REPOSITORY_PIPELINE_STAGES]}
+          handler={RepositoryPipelinesController.getPipelineStagesView}
+        />
+        <Route
+          name={AppRoute.REPOSITORY_PIPELINE_STAGE_DETAILS}
+          method={"GET"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_PIPELINE_STAGE_DETAILS]}
+          schema={AppRouteSchemas[AppRoute.REPOSITORY_PIPELINE_STAGE_DETAILS]}
+          handler={RepositoryPipelinesController.getPipelineStageDetailsView}
+        />
+        <Route
+          name={AppRoute.REPOSITORY_PIPELINE_ARTEFACTS}
+          method={"GET"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_PIPELINE_ARTEFACTS]}
+          schema={AppRouteSchemas[AppRoute.REPOSITORY_PIPELINE_ARTEFACTS]}
+          handler={RepositoryPipelinesController.getPipelineArtefactsView}
+        />
+        <Route
+          name={AppRoute.REPOSITORY_PIPELINE_TRIGGER_ACTION}
+          method={"POST"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_PIPELINE_TRIGGER_ACTION]}
+          schema={AppRouteSchemas[AppRoute.REPOSITORY_PIPELINE_TRIGGER_ACTION]}
+          handler={RepositoryPipelinesController.postPipelineTriggerAction}
         />
         {/* --- */}
         <Route