github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/ci/release/avado.go (about)

     1  /*
     2   * Copyright (C) 2021 The "MysteriumNetwork/node" Authors.
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU General Public License as published by
     6   * the Free Software Foundation, either version 3 of the License, or
     7   * (at your option) any later version.
     8   *
     9   * This program is distributed in the hope that it will be useful,
    10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12   * GNU General Public License for more details.
    13   *
    14   * You should have received a copy of the GNU General Public License
    15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16   */
    17  
    18  package release
    19  
    20  import (
    21  	"bufio"
    22  	"context"
    23  	"fmt"
    24  	"strings"
    25  	"time"
    26  
    27  	"github.com/google/go-github/v35/github"
    28  	"github.com/mysteriumnetwork/go-ci/job"
    29  	"github.com/rs/zerolog/log"
    30  	"golang.org/x/oauth2"
    31  
    32  	"github.com/mysteriumnetwork/go-ci/env"
    33  	"github.com/mysteriumnetwork/node/logconfig"
    34  )
    35  
    36  type avadoPR struct {
    37  	client *github.Client
    38  
    39  	version string
    40  
    41  	avadoUser     string
    42  	mysteriumUser string
    43  
    44  	msg    string
    45  	branch string
    46  
    47  	repo string
    48  
    49  	authorName  string
    50  	authorEmail string
    51  }
    52  
    53  func newAvadoPR(ctx context.Context) *avadoPR {
    54  	token := env.Str(env.GithubAPIToken)
    55  	version := env.Str(env.BuildVersion)
    56  	owner := env.Str(env.GithubOwner)
    57  
    58  	ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
    59  	tc := oauth2.NewClient(ctx, ts)
    60  	client := github.NewClient(tc)
    61  
    62  	return &avadoPR{
    63  		version: version,
    64  		client:  client,
    65  
    66  		avadoUser:     "AvadoDServer",
    67  		mysteriumUser: owner,
    68  
    69  		repo: "AVADO-DNP-Mysterium-Server",
    70  
    71  		msg:    "Update Mysterium Node version to " + version,
    72  		branch: "update-mysterium-node-" + version,
    73  
    74  		authorName:  "MysteriumTeam",
    75  		authorEmail: "core-services@mysterium.network",
    76  	}
    77  }
    78  
    79  func (a *avadoPR) getRef(ctx context.Context) (ref *github.Reference, err error) {
    80  	var baseRef *github.Reference
    81  	if baseRef, _, err = a.client.Git.GetRef(ctx, a.avadoUser, a.repo, "refs/heads/master"); err != nil {
    82  		return nil, err
    83  	}
    84  	newRef := &github.Reference{Ref: github.String("refs/heads/" + a.branch), Object: &github.GitObject{SHA: baseRef.Object.SHA}}
    85  	ref, _, err = a.client.Git.CreateRef(ctx, a.mysteriumUser, a.repo, newRef)
    86  	return ref, err
    87  }
    88  
    89  func (a *avadoPR) getTree(ctx context.Context, ref *github.Reference) (tree *github.Tree, err error) {
    90  	entries := []*github.TreeEntry{}
    91  
    92  	for _, fileName := range []string{"dappnode_package.json", "build/Dockerfile"} {
    93  		file, content, err := a.getFileContent(ctx, fileName)
    94  		if err != nil {
    95  			return nil, err
    96  		}
    97  		content = replaceVersion(file, content, a.version)
    98  		entries = append(entries, &github.TreeEntry{Path: github.String(file), Type: github.String("blob"), Content: github.String(content), Mode: github.String("100644")})
    99  	}
   100  
   101  	tree, _, err = a.client.Git.CreateTree(ctx, a.mysteriumUser, a.repo, *ref.Object.SHA, entries)
   102  	return tree, err
   103  }
   104  
   105  func (a *avadoPR) getFileContent(ctx context.Context, fileArg string) (targetName string, s string, err error) {
   106  	f, _, _, err := a.client.Repositories.GetContents(ctx, a.avadoUser, a.repo, fileArg, nil)
   107  	if err != nil {
   108  		return "", "", err
   109  	}
   110  
   111  	c, err := f.GetContent()
   112  	return fileArg, c, err
   113  }
   114  
   115  func (a *avadoPR) pushCommit(ctx context.Context, ref *github.Reference, tree *github.Tree) (err error) {
   116  	parent, _, err := a.client.Repositories.GetCommit(ctx, a.mysteriumUser, a.repo, *ref.Object.SHA)
   117  	if err != nil {
   118  		return err
   119  	}
   120  
   121  	parent.Commit.SHA = parent.SHA
   122  
   123  	date := time.Now()
   124  	author := &github.CommitAuthor{Date: &date, Name: &a.authorName, Email: &a.authorEmail}
   125  	commit := &github.Commit{Author: author, Message: &a.msg, Tree: tree, Parents: []*github.Commit{parent.Commit}}
   126  	newCommit, _, err := a.client.Git.CreateCommit(ctx, a.mysteriumUser, a.repo, commit)
   127  	if err != nil {
   128  		return err
   129  	}
   130  
   131  	ref.Object.SHA = newCommit.SHA
   132  	_, _, err = a.client.Git.UpdateRef(ctx, a.mysteriumUser, a.repo, ref, false)
   133  	return err
   134  }
   135  
   136  func (a *avadoPR) createPR(ctx context.Context) (err error) {
   137  	base := "master"
   138  	head := fmt.Sprintf("%s:%s", a.mysteriumUser, a.branch)
   139  
   140  	newPR := &github.NewPullRequest{
   141  		Title:               &a.msg,
   142  		Head:                &head,
   143  		Base:                &base,
   144  		MaintainerCanModify: github.Bool(true),
   145  	}
   146  
   147  	_, _, err = a.client.PullRequests.Create(ctx, a.avadoUser, a.repo, newPR)
   148  	if err != nil {
   149  		return err
   150  	}
   151  
   152  	return nil
   153  }
   154  
   155  func replaceVersion(file, content, version string) (out string) {
   156  	oldLine := ""
   157  	newLine := ""
   158  	switch file {
   159  	case "dappnode_package.json":
   160  		oldLine = `  "upstream": `
   161  		newLine = fmt.Sprintf(`  "upstream": "%s",`, version)
   162  	case "build/Dockerfile":
   163  		oldLine = "FROM mysteriumnetwork/myst:"
   164  		newLine = fmt.Sprintf(`FROM mysteriumnetwork/myst:%s-alpine`, version)
   165  	}
   166  
   167  	scanner := bufio.NewScanner(strings.NewReader(content))
   168  	for scanner.Scan() {
   169  		line := scanner.Text()
   170  		if strings.Contains(line, oldLine) && !strings.HasPrefix(line, "#") {
   171  			out += newLine + "\n"
   172  		} else {
   173  			out += line + "\n"
   174  		}
   175  	}
   176  
   177  	return out
   178  }
   179  
   180  // CreateAvadoPR create github PR to the Avado repository.
   181  func CreateAvadoPR() error {
   182  	logconfig.Bootstrap()
   183  
   184  	if err := env.EnsureEnvVars(
   185  		env.GithubAPIToken,
   186  		env.GithubOwner,
   187  		env.BuildVersion,
   188  	); err != nil {
   189  		return err
   190  	}
   191  	job.Precondition(func() bool {
   192  		return env.Bool(env.TagBuild) && !env.Bool(env.RCBuild)
   193  	})
   194  
   195  	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
   196  	defer cancel()
   197  
   198  	a := newAvadoPR(ctx)
   199  
   200  	ref, err := a.getRef(ctx)
   201  	if err != nil || ref == nil {
   202  		log.Error().Msgf("Unable to get/create the commit reference: %s\n", err)
   203  		return err
   204  	}
   205  
   206  	tree, err := a.getTree(ctx, ref)
   207  	if err != nil {
   208  		log.Error().Msgf("Unable to create the tree based on the provided files: %s\n", err)
   209  		return err
   210  	}
   211  
   212  	if err := a.pushCommit(ctx, ref, tree); err != nil {
   213  		log.Error().Msgf("Unable to create the commit: %s\n", err)
   214  		return err
   215  	}
   216  
   217  	if err := a.createPR(ctx); err != nil {
   218  		log.Error().Msgf("Error while creating the pull request: %s", err)
   219  		return err
   220  	}
   221  
   222  	return nil
   223  }