stack.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. package actions
  2. import (
  3. "context"
  4. "fmt"
  5. "strings"
  6. "github.com/google/go-github/v41/github"
  7. "gopkg.in/yaml.v2"
  8. )
  9. // GithubPRAction is an action to take when opening a PR
  10. type GithubPRAction string
  11. const (
  12. // GithubPRAction_NewAppWorkflow is the action for creating a workflow for a new application
  13. GithubPRAction_NewAppWorkflow GithubPRAction = "new-app-workflow"
  14. // GithubPRAction_DeleteAppWorkflow is the action for deleting an application workflow
  15. GithubPRAction_DeleteAppWorkflow GithubPRAction = "delete-app-workflow"
  16. // GithubPRAction_PreviewAppWorkflow is the action for creating the preview app workflow
  17. GithubPRAction_PreviewAppWorkflow GithubPRAction = "preview-app-workflow"
  18. )
  19. type GithubPROpts struct {
  20. PRAction GithubPRAction
  21. Client *github.Client
  22. GitRepoOwner, GitRepoName string
  23. ApplyWorkflowYAML string
  24. StackName string
  25. ProjectID, ClusterID uint
  26. ServerURL string
  27. DefaultBranch string
  28. SecretName string
  29. PorterYamlPath string
  30. Body string
  31. WorkflowFileName string
  32. PRBranch string
  33. }
  34. type GetStackApplyActionYAMLOpts struct {
  35. ServerURL string
  36. StackName string
  37. ProjectID, ClusterID uint
  38. DefaultBranch string
  39. SecretName string
  40. PorterYamlPath string
  41. Preview bool
  42. }
  43. func OpenGithubPR(opts *GithubPROpts) (*github.PullRequest, error) {
  44. var pr *github.PullRequest
  45. if opts == nil {
  46. return pr, fmt.Errorf("input options cannot be nil")
  47. }
  48. err := createNewBranch(opts.Client,
  49. opts.GitRepoOwner,
  50. opts.GitRepoName,
  51. opts.DefaultBranch,
  52. opts.PRBranch,
  53. )
  54. if err != nil {
  55. return pr, fmt.Errorf("error creating branch: %w", err)
  56. }
  57. err = commitChange(opts.PRBranch, *opts)
  58. if err != nil {
  59. return pr, fmt.Errorf("error committing change: %w", err)
  60. }
  61. prTitle := getPRTitle(opts.PRAction, opts.StackName)
  62. pr, _, err = opts.Client.PullRequests.Create(
  63. context.Background(), opts.GitRepoOwner, opts.GitRepoName, &github.NewPullRequest{
  64. Title: github.String(prTitle),
  65. Base: github.String(opts.DefaultBranch),
  66. Head: github.String(opts.PRBranch),
  67. Body: github.String(opts.Body),
  68. },
  69. )
  70. if err != nil {
  71. return pr, fmt.Errorf(
  72. "error creating PR: %w",
  73. err,
  74. )
  75. }
  76. return pr, nil
  77. }
  78. func getPRTitle(action GithubPRAction, stackName string) string {
  79. switch action {
  80. case GithubPRAction_NewAppWorkflow:
  81. return fmt.Sprintf("Enable Porter Application %s", stackName)
  82. case GithubPRAction_DeleteAppWorkflow:
  83. return fmt.Sprintf("Delete Porter Application %s", stackName)
  84. case GithubPRAction_PreviewAppWorkflow:
  85. return "Enable Preview Environments on Porter"
  86. default:
  87. return ""
  88. }
  89. }
  90. func commitChange(prBranchName string, opts GithubPROpts) error {
  91. switch opts.PRAction {
  92. case GithubPRAction_NewAppWorkflow:
  93. applyWorkflowYAML, err := getStackApplyActionYAML(&GetStackApplyActionYAMLOpts{
  94. ServerURL: opts.ServerURL,
  95. ClusterID: opts.ClusterID,
  96. ProjectID: opts.ProjectID,
  97. StackName: opts.StackName,
  98. DefaultBranch: opts.DefaultBranch,
  99. SecretName: opts.SecretName,
  100. PorterYamlPath: opts.PorterYamlPath,
  101. Preview: false,
  102. })
  103. if err != nil {
  104. return err
  105. }
  106. _, err = commitWorkflowFile(
  107. opts.Client,
  108. fmt.Sprintf("porter_stack_%s.yml", strings.ToLower(opts.StackName)),
  109. applyWorkflowYAML, opts.GitRepoOwner,
  110. opts.GitRepoName, prBranchName, false,
  111. )
  112. if err != nil {
  113. return fmt.Errorf("error committing file: %w", err)
  114. }
  115. return nil
  116. case GithubPRAction_DeleteAppWorkflow:
  117. err := deleteGithubFile(
  118. opts.Client,
  119. opts.WorkflowFileName,
  120. opts.GitRepoOwner,
  121. opts.GitRepoName,
  122. prBranchName,
  123. false,
  124. )
  125. if err != nil {
  126. return fmt.Errorf("error committing deletion: %w", err)
  127. }
  128. return nil
  129. case GithubPRAction_PreviewAppWorkflow:
  130. previewWorkflowYAML, err := getStackApplyActionYAML(&GetStackApplyActionYAMLOpts{
  131. ServerURL: opts.ServerURL,
  132. ClusterID: opts.ClusterID,
  133. ProjectID: opts.ProjectID,
  134. StackName: opts.StackName,
  135. DefaultBranch: opts.DefaultBranch,
  136. SecretName: opts.SecretName,
  137. PorterYamlPath: opts.PorterYamlPath,
  138. Preview: true,
  139. })
  140. if err != nil {
  141. return err
  142. }
  143. _, err = commitWorkflowFile(
  144. opts.Client,
  145. fmt.Sprintf("porter_preview_%s.yml", strings.ToLower(opts.StackName)),
  146. previewWorkflowYAML, opts.GitRepoOwner,
  147. opts.GitRepoName, prBranchName, false,
  148. )
  149. if err != nil {
  150. return fmt.Errorf("error committing file: %w", err)
  151. }
  152. return nil
  153. default:
  154. return fmt.Errorf("invalid PR action: %s", opts.PRAction)
  155. }
  156. }
  157. func getStackApplyActionYAML(opts *GetStackApplyActionYAMLOpts) ([]byte, error) {
  158. gaSteps := []GithubActionYAMLStep{
  159. getCheckoutCodeStep(),
  160. getSetTagStep(),
  161. getSetupPorterStep(),
  162. getDeployStackStep(
  163. opts.ServerURL,
  164. opts.SecretName,
  165. opts.StackName,
  166. "v0.1.0",
  167. opts.PorterYamlPath,
  168. opts.ProjectID,
  169. opts.ClusterID,
  170. opts.Preview,
  171. ),
  172. }
  173. if opts.Preview {
  174. actionYaml := GithubActionYAML{
  175. On: GithubActionYAMLOnPullRequest{
  176. PullRequest: GithubActionYAMLOnPullRequestTypes{
  177. Paths: []string{
  178. "**",
  179. "!./github/workflows/porter_**",
  180. },
  181. Branches: []string{
  182. opts.DefaultBranch,
  183. },
  184. Types: []string{
  185. "opened",
  186. "synchronize",
  187. },
  188. },
  189. },
  190. Name: "Deploy to Preview Environment",
  191. Jobs: map[string]GithubActionYAMLJob{
  192. "porter-deploy": {
  193. RunsOn: "ubuntu-latest",
  194. Steps: gaSteps,
  195. },
  196. },
  197. }
  198. return yaml.Marshal(actionYaml)
  199. }
  200. actionYAML := GithubActionYAML{
  201. On: GithubActionYAMLOnPush{
  202. Push: GithubActionYAMLOnPushBranches{
  203. Branches: []string{
  204. opts.DefaultBranch,
  205. },
  206. },
  207. },
  208. Name: fmt.Sprintf("Deploy to %s", opts.StackName),
  209. Jobs: map[string]GithubActionYAMLJob{
  210. "porter-deploy": {
  211. RunsOn: "ubuntu-latest",
  212. Steps: gaSteps,
  213. },
  214. },
  215. }
  216. return yaml.Marshal(actionYAML)
  217. }