useGithubWorkflow.ts 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import { useQueries } from "@tanstack/react-query";
  2. import axios from "axios";
  3. import { PorterAppRecord } from "main/home/app-dashboard/app-view/AppView";
  4. import { useCallback, useContext, useEffect, useMemo, useState } from "react";
  5. import { Context } from "shared/Context";
  6. import api from "shared/api";
  7. import { z } from "zod";
  8. export const useGithubWorkflow = (
  9. porterApp: PorterAppRecord,
  10. previouslyBuilt: boolean
  11. ) => {
  12. const { currentProject, currentCluster } = useContext(Context);
  13. const [githubWorkflowFilename, setGithubWorkflowName] = useState<string>("");
  14. const [userHasGithubAccess, setUserHasGithubAccess] = useState<boolean>(true);
  15. const gitMetadata = useMemo(() => {
  16. const repoNameParts = z
  17. .tuple([z.string(), z.string()])
  18. .safeParse(porterApp.repo_name?.split("/"));
  19. if (
  20. !repoNameParts.success ||
  21. !porterApp.git_repo_id ||
  22. !porterApp.git_branch
  23. ) {
  24. return {
  25. repo_id: 0,
  26. owner: "",
  27. name: "",
  28. branch: "",
  29. };
  30. }
  31. return {
  32. repo_id: porterApp.git_repo_id,
  33. owner: repoNameParts.data[0],
  34. name: repoNameParts.data[1],
  35. branch: porterApp.git_branch,
  36. };
  37. }, [porterApp.git_repo_id, porterApp.repo_name, porterApp.git_branch]);
  38. const fetchGithubWorkflow = useCallback(
  39. async (fileName: string) => {
  40. try {
  41. if (githubWorkflowFilename !== "") {
  42. return githubWorkflowFilename;
  43. }
  44. if (currentProject == null || currentCluster == null) {
  45. return "";
  46. }
  47. const res = await api.getBranchContents(
  48. "<token>",
  49. {
  50. dir: `./.github/workflows/${fileName}`,
  51. },
  52. {
  53. project_id: currentProject.id,
  54. git_repo_id: gitMetadata.repo_id,
  55. kind: "github",
  56. owner: gitMetadata.owner,
  57. name: gitMetadata.name,
  58. branch: gitMetadata.branch,
  59. }
  60. );
  61. if (res.data) {
  62. return fileName;
  63. }
  64. return "";
  65. } catch (err) {
  66. return "";
  67. }
  68. },
  69. [currentProject, currentCluster, gitMetadata, githubWorkflowFilename]
  70. );
  71. const enabled =
  72. !previouslyBuilt &&
  73. !!currentProject &&
  74. !!currentCluster &&
  75. githubWorkflowFilename === "";
  76. const [
  77. {
  78. data: applicationWorkflowCheck,
  79. isLoading: isLoadingApplicationWorkflow,
  80. },
  81. { data: defaultWorkflowCheck, isLoading: isLoadingDefaultWorkflow },
  82. ] = useQueries({
  83. queries: [
  84. {
  85. queryKey: [
  86. `checkForApplicationWorkflow_porter_stack_${porterApp.name}`,
  87. currentProject?.id,
  88. currentCluster?.id,
  89. githubWorkflowFilename,
  90. previouslyBuilt,
  91. ],
  92. queryFn: () =>
  93. fetchGithubWorkflow(`porter_stack_${porterApp.name}.yml`),
  94. enabled,
  95. refetchInterval: 5000,
  96. retry: (_failureCount: number, error: unknown) => {
  97. if (axios.isAxiosError(error) && error.response?.status === 403) {
  98. setUserHasGithubAccess(false);
  99. return false;
  100. }
  101. return true;
  102. },
  103. refetchOnWindowFocus: false,
  104. },
  105. {
  106. queryKey: [
  107. `checkForApplicationWorkflow_porter`,
  108. currentProject?.id,
  109. currentCluster?.id,
  110. githubWorkflowFilename,
  111. previouslyBuilt,
  112. ],
  113. queryFn: () => fetchGithubWorkflow("porter.yml"),
  114. enabled,
  115. refetchInterval: 5000,
  116. retry: (_failureCount: number, error: unknown) => {
  117. if (axios.isAxiosError(error) && error.response?.status === 403) {
  118. setUserHasGithubAccess(false);
  119. return false;
  120. }
  121. return true;
  122. },
  123. refetchOnWindowFocus: false,
  124. },
  125. ],
  126. });
  127. useEffect(() => {
  128. if (!!applicationWorkflowCheck) {
  129. setGithubWorkflowName(applicationWorkflowCheck);
  130. } else if (!!defaultWorkflowCheck) {
  131. setGithubWorkflowName(defaultWorkflowCheck);
  132. }
  133. }, [applicationWorkflowCheck, defaultWorkflowCheck]);
  134. return {
  135. githubWorkflowFilename,
  136. isLoading: isLoadingApplicationWorkflow || isLoadingDefaultWorkflow,
  137. userHasGithubAccess,
  138. };
  139. };