github.com/nuvolaris/nuv@v0.0.0-20240511174247-a74e3a52bfd8/prepare.go (about)

     1  // Licensed to the Apache Software Foundation (ASF) under one
     2  // or more contributor license agreements.  See the NOTICE file
     3  // distributed with this work for additional information
     4  // regarding copyright ownership.  The ASF licenses this file
     5  // to you under the Apache License, Version 2.0 (the
     6  // "License"); you may not use this file except in compliance
     7  // with the License.  You may obtain a copy of the License at
     8  //
     9  //	http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing,
    12  // software distributed under the License is distributed on an
    13  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    14  // KIND, either express or implied.  See the License for the
    15  // specific language governing permissions and limitations
    16  // under the License.
    17  package main
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"os/exec"
    23  	"path/filepath"
    24  	"strings"
    25  
    26  	"github.com/Masterminds/semver"
    27  	git "github.com/go-git/go-git/v5"
    28  	"github.com/go-git/go-git/v5/plumbing"
    29  	"github.com/mitchellh/go-homedir"
    30  )
    31  
    32  func downloadTasksFromGitHub(force bool, silent bool) (string, error) {
    33  	debug("Download tasks from github")
    34  	repoURL := getNuvRepo()
    35  	branch := getNuvBranch()
    36  	nuvDir, err := homedir.Expand("~/.nuv")
    37  	if err != nil {
    38  		return "", err
    39  	}
    40  	if err := os.MkdirAll(nuvDir, 0755); err != nil {
    41  		return "", err
    42  	}
    43  
    44  	nuvBranchDir := joinpath(nuvDir, branch)
    45  	localDir, err := homedir.Expand(joinpath(nuvBranchDir, "olaris"))
    46  	if err != nil {
    47  		return "", err
    48  	}
    49  	debug("localDir", localDir)
    50  
    51  	// Updating existing tools
    52  	if exists(nuvBranchDir, "olaris") {
    53  		trace("Updating olaris in", nuvBranchDir)
    54  		fmt.Println("Updating tasks...")
    55  		r, err := git.PlainOpen(localDir)
    56  		if err != nil {
    57  			return "", err
    58  		}
    59  		// Get the working directory for the repository
    60  		w, err := r.Worktree()
    61  		if err != nil {
    62  			return "", err
    63  		}
    64  
    65  		// Pull the latest changes from the origin remote and merge into the current branch
    66  		// Clone the repo if not existing
    67  		ref := plumbing.NewBranchReferenceName(branch)
    68  		err = w.Pull(&git.PullOptions{
    69  			RemoteName:    "origin",
    70  			ReferenceName: ref,
    71  			SingleBranch:  true,
    72  		})
    73  		if err != nil {
    74  			if err.Error() == "already up-to-date" {
    75  				fmt.Println("Tasks are already up to date!")
    76  				return localDir, nil
    77  			}
    78  			return "", err
    79  		}
    80  
    81  		fmt.Println("Nuvfiles updated successfully")
    82  		touchLatestCheckFile(joinpath(nuvBranchDir, LATESTCHECK))
    83  		return localDir, nil
    84  	}
    85  
    86  	// Clone the repo if not existing
    87  	ref := plumbing.NewBranchReferenceName(branch)
    88  	cloneOpts := &git.CloneOptions{
    89  		URL:           repoURL,
    90  		Progress:      os.Stderr,
    91  		ReferenceName: ref, // Specify the branch to clone
    92  	}
    93  
    94  	fmt.Println("Cloning tasks...")
    95  	_, err = git.PlainClone(localDir, false, cloneOpts)
    96  	if err != nil {
    97  		os.RemoveAll(nuvBranchDir)
    98  		warn(fmt.Sprintf("failed to clone olaris on branch '%s'", branch))
    99  		return "", err
   100  	}
   101  
   102  	fmt.Println("Nuvfiles downloaded successfully")
   103  
   104  	createLatestCheckFile(nuvBranchDir)
   105  
   106  	// clone
   107  	return localDir, nil
   108  }
   109  
   110  func pullTasks(force, silent bool) (string, error) {
   111  	// download from github
   112  	localDir, err := downloadTasksFromGitHub(force, silent)
   113  	debug("localDir", localDir)
   114  	if err != nil {
   115  		return "", err
   116  	}
   117  
   118  	// validate NuvVersion semver against nuvroot.json
   119  	nuvRoot, err := readNuvRootFile(localDir)
   120  	if err != nil {
   121  		return "", err
   122  	}
   123  
   124  	// check if the version is up to date
   125  	nuvVersion, err := semver.NewVersion(NuvVersion)
   126  	if err != nil {
   127  		// in development mode, we don't have a valid semver version
   128  		warn("Unable to validate nuv version", NuvVersion, ":", err)
   129  		return localDir, nil
   130  	}
   131  
   132  	nuvRootVersion, err := semver.NewVersion(nuvRoot.Version)
   133  	if err != nil {
   134  		warn("Unable to validate nuvroot.json version", nuvRoot.Version, ":", err)
   135  		return localDir, nil
   136  	}
   137  
   138  	// check if the version is up to date, if not warn the user
   139  	if nuvVersion.LessThan(nuvRootVersion) {
   140  		fmt.Println()
   141  		fmt.Printf("Your nuv version (%v) is older than the required version in nuvroot.json (%v).\n", nuvVersion, nuvRootVersion)
   142  		fmt.Println("Attempting to update nuv...")
   143  		if err := autoCLIUpdate(); err != nil {
   144  			return "", err
   145  		}
   146  	}
   147  
   148  	err = checkOperatorVersion(nuvRoot.Config)
   149  	if err == nil {
   150  		fmt.Println()
   151  		fmt.Println("New operator version detected!")
   152  		fmt.Println("Current deployed operator can be updated with: nuv update operator")
   153  	}
   154  
   155  	return localDir, nil
   156  }
   157  
   158  // locateNuvRoot locate the folder where starts execution
   159  // it can be a parent folder of the current folder or it can be downloaded
   160  // from github - it should contain a file nuvfile.yml and a file nuvtools.yml in the root
   161  func locateNuvRoot(cur string) (string, error) {
   162  	cur, err := filepath.Abs(cur)
   163  	if err != nil {
   164  		return "", err
   165  	}
   166  
   167  	// search the root from here
   168  	search := locateNuvRootSearch(cur)
   169  	if search != "" {
   170  		trace("found searching up:", search)
   171  		return search, nil
   172  	}
   173  
   174  	// is there  olaris folder?
   175  	olaris := joinpath(cur, "olaris")
   176  	if exists(cur, "olaris") && exists(olaris, NUVFILE) && exists(olaris, NUVROOT) {
   177  		trace("found sub olaris:", olaris)
   178  		return olaris, nil
   179  	}
   180  
   181  	// is there an olaris folder in ~/.nuv ?
   182  	nuvOlarisDir := fmt.Sprintf("~/.nuv/%s/olaris", getNuvBranch())
   183  	olaris, err = homedir.Expand(nuvOlarisDir)
   184  	if err == nil && exists(olaris, NUVFILE) && exists(olaris, NUVROOT) {
   185  		trace("found sub", nuvOlarisDir, ":", olaris)
   186  		return olaris, nil
   187  	}
   188  
   189  	// is there an olaris folder in NUV_BIN?
   190  	nuvBin := os.Getenv("NUV_BIN")
   191  	if nuvBin != "" {
   192  		olaris = joinpath(nuvBin, "olaris")
   193  		if exists(olaris, NUVFILE) && exists(olaris, NUVROOT) {
   194  			trace("found sub NUV_BIN olaris:", olaris)
   195  			return olaris, nil
   196  		}
   197  	}
   198  
   199  	return "", fmt.Errorf("we cannot find nuvfiles, download them with nuv -update")
   200  }
   201  
   202  // locateNuvRootSearch search for `nuvfiles.yml`
   203  // and goes up looking for a folder with also `nuvroot.json`
   204  func locateNuvRootSearch(cur string) string {
   205  	debug("locateNuvRootSearch:", cur)
   206  	// exits nuvfile.yml? if not, go up until you find it
   207  	if !exists(cur, NUVFILE) {
   208  		return ""
   209  	}
   210  	if exists(cur, NUVROOT) {
   211  		return cur
   212  	}
   213  	parent := parent(cur)
   214  	if parent == "" {
   215  		return ""
   216  	}
   217  	return locateNuvRootSearch(parent)
   218  }
   219  
   220  func autoCLIUpdate() error {
   221  	trace("autoCLIUpdate")
   222  	cmd := exec.Command("nuv", "update", "cli")
   223  	cmd.Stdout = os.Stdout
   224  	cmd.Stderr = os.Stderr
   225  	return cmd.Run()
   226  }
   227  
   228  func checkOperatorVersion(nuvRootConfig map[string]interface{}) error {
   229  	trace("checkOperatorVersion")
   230  	images := nuvRootConfig["images"].(map[string]interface{})
   231  	operator := images["operator"].(string)
   232  	opVer := strings.Split(operator, ":")[1]
   233  
   234  	cmd := exec.Command("nuv", "util", "check-operator-version", opVer)
   235  	return cmd.Run()
   236  }
   237  
   238  func setNuvOlarisHash(olarisDir string) error {
   239  	trace("setNuvOlarisHash", olarisDir)
   240  	r, err := git.PlainOpen(olarisDir)
   241  	if err != nil {
   242  		return err
   243  	}
   244  	h, err := r.Head()
   245  	if err != nil {
   246  		return err
   247  	}
   248  	debug("olaris hash", h.Hash().String())
   249  	os.Setenv("NUV_OLARIS", h.Hash().String())
   250  	trace("NUV_OLARIS", os.Getenv("NUV_OLARIS"))
   251  	return nil
   252  }