| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- package actions
- import (
- "context"
- "encoding/base64"
- "fmt"
- "github.com/google/go-github/v33/github"
- "github.com/porter-dev/porter/internal/models"
- "github.com/porter-dev/porter/internal/repository"
- "golang.org/x/crypto/nacl/box"
- "golang.org/x/oauth2"
- "strings"
- "gopkg.in/yaml.v2"
- )
- type GithubActions struct {
- GitIntegration *models.GitRepo
- GitRepoName string
- GitRepoOwner string
- Repo repository.Repository
- GithubConf *oauth2.Config
- WebhookToken string
- PorterToken string
- ProjectID uint
- ReleaseName string
- DockerFilePath string
- ImageRepoURL string
- defaultBranch string
- }
- func (g *GithubActions) Setup() (string, error) {
- client, err := g.getClient()
- if err != nil {
- return "", err
- }
- // get the repository to find the default branch
- repo, _, err := client.Repositories.Get(
- context.TODO(),
- g.GitRepoOwner,
- g.GitRepoName,
- )
- if err != nil {
- return "", err
- }
- g.defaultBranch = repo.GetDefaultBranch()
- // create a new secret with a webhook token
- err = g.createGithubSecret(client, g.getWebhookSecretName(), g.WebhookToken)
- if err != nil {
- return "", err
- }
- // create a new secret with a porter token
- err = g.createGithubSecret(client, g.getPorterTokenSecretName(), g.PorterToken)
- if err != nil {
- return "", err
- }
- fileBytes, err := g.GetGithubActionYAML()
- if err != nil {
- return "", err
- }
- return g.commitGithubFile(client, fileBytes)
- }
- type GithubActionYAMLStep struct {
- Name string `yaml:"name"`
- ID string `yaml:"id"`
- Run string `yaml:"run"`
- }
- type GithubActionYAMLOnPushBranches struct {
- Branches []string `yaml:"branches"`
- }
- type GithubActionYAMLOnPush struct {
- Push GithubActionYAMLOnPushBranches `yaml:"push"`
- }
- type GithubActionYAMLJob struct {
- RunsOn string `yaml:"runs-on"`
- Steps []GithubActionYAMLStep `yaml:"steps"`
- }
- type GithubActionYAML struct {
- On GithubActionYAMLOnPush `yaml:"on"`
- Name string `yaml:"name"`
- Jobs map[string]GithubActionYAMLJob `yaml:"jobs"`
- }
- func (g *GithubActions) GetGithubActionYAML() ([]byte, error) {
- actionYAML := &GithubActionYAML{
- On: GithubActionYAMLOnPush{
- Push: GithubActionYAMLOnPushBranches{
- Branches: []string{
- g.defaultBranch,
- },
- },
- },
- Name: "Deploy to Porter",
- Jobs: map[string]GithubActionYAMLJob{
- "porter-deploy": GithubActionYAMLJob{
- RunsOn: "ubuntu-latest",
- Steps: []GithubActionYAMLStep{
- getDownloadPorterStep(),
- getConfigurePorterStep(g.getPorterTokenSecretName()),
- getDockerBuildPushStep(g.DockerFilePath, g.ImageRepoURL),
- deployPorterWebhookStep(g.getWebhookSecretName(), g.ImageRepoURL),
- },
- },
- },
- }
- return yaml.Marshal(actionYAML)
- }
- func (g *GithubActions) getClient() (*github.Client, error) {
- // get the oauth integration
- oauthInt, err := g.Repo.OAuthIntegration.ReadOAuthIntegration(g.GitIntegration.OAuthIntegrationID)
- if err != nil {
- return nil, err
- }
- tok := &oauth2.Token{
- AccessToken: string(oauthInt.AccessToken),
- RefreshToken: string(oauthInt.RefreshToken),
- TokenType: "Bearer",
- }
- client := github.NewClient(g.GithubConf.Client(oauth2.NoContext, tok))
- return client, nil
- }
- func (g *GithubActions) createGithubSecret(
- client *github.Client,
- secretName,
- secretValue string,
- ) error {
- // get the public key for the repo
- key, _, err := client.Actions.GetRepoPublicKey(context.TODO(), g.GitRepoOwner, g.GitRepoName)
- if err != nil {
- return err
- }
- // encrypt the secret with the public key
- keyBytes := [32]byte{}
- keyDecoded, err := base64.StdEncoding.DecodeString(*key.Key)
- if err != nil {
- return err
- }
- copy(keyBytes[:], keyDecoded[:])
- secretEncoded, err := box.SealAnonymous(nil, []byte(secretValue), &keyBytes, nil)
- if err != nil {
- return err
- }
- encrypted := base64.StdEncoding.EncodeToString(secretEncoded)
- encryptedSecret := &github.EncryptedSecret{
- Name: secretName,
- KeyID: *key.KeyID,
- EncryptedValue: encrypted,
- }
- // write the secret to the repo
- _, err = client.Actions.CreateOrUpdateRepoSecret(context.TODO(), g.GitRepoOwner, g.GitRepoName, encryptedSecret)
- return nil
- }
- func (g *GithubActions) getWebhookSecretName() string {
- return fmt.Sprintf("WEBHOOK_%s", strings.Replace(
- strings.ToUpper(g.ReleaseName), "-", "_", -1),
- )
- }
- func (g *GithubActions) getPorterTokenSecretName() string {
- return fmt.Sprintf("PORTER_TOKEN_%d", g.ProjectID)
- }
- func (g *GithubActions) commitGithubFile(
- client *github.Client,
- contents []byte,
- ) (string, error) {
- fmt.Println("GITHUB ACTION CONTENTS ARE", string(contents))
- opts := &github.RepositoryContentFileOptions{
- Message: github.String("Create porter.yml file"),
- Content: contents,
- Branch: github.String(g.defaultBranch),
- Committer: &github.CommitAuthor{
- Name: github.String("Porter Bot"),
- Email: github.String("contact@getporter.dev"),
- },
- }
- resp, _, err := client.Repositories.CreateFile(
- context.TODO(),
- g.GitRepoOwner,
- g.GitRepoName,
- ".github/workflows/porter.yml",
- opts,
- )
- if err != nil {
- return "", err
- }
- return *resp.Commit.SHA, nil
- }
|