github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/scripts/ci/s3-deployer/main.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"net/http"
     8  	"os"
     9  	"path"
    10  	"path/filepath"
    11  	"runtime"
    12  	"strings"
    13  
    14  	"github.com/aws/aws-sdk-go/aws"
    15  	"github.com/aws/aws-sdk-go/aws/session"
    16  	"github.com/aws/aws-sdk-go/service/s3"
    17  
    18  	"github.com/ActiveState/cli/internal/condition"
    19  )
    20  
    21  var sourcePath, awsRegionName, awsBucketName, awsBucketPrefix string
    22  
    23  var sess *session.Session
    24  
    25  func main() {
    26  	if !condition.InUnitTest() {
    27  		if len(os.Args) != 5 {
    28  			log.Fatalf("Usage: %s <source> <region-name> <bucket-name> <bucket-prefix>", os.Args[0])
    29  		}
    30  
    31  		sourcePath = os.Args[1]
    32  		awsRegionName = os.Args[2]
    33  		awsBucketName = os.Args[3]
    34  		awsBucketPrefix = os.Args[4]
    35  
    36  		run()
    37  	}
    38  }
    39  
    40  func run() {
    41  	fmt.Printf("Uploading files from %s\n", sourcePath)
    42  
    43  	createSession()
    44  	fileList := getFileList()
    45  
    46  	// Upload the files
    47  	fmt.Printf("Uploading %d files\n", len(fileList))
    48  	for _, path := range fileList {
    49  		params := prepareFile(path)
    50  		uploadFile(params)
    51  	}
    52  }
    53  
    54  func createSession() {
    55  	// Specify profile to load for the session's config
    56  	var err error
    57  	var verboseErr = true
    58  	var logLevel = aws.LogDebug
    59  	_ = logLevel
    60  	opts := session.Options{
    61  		Config: aws.Config{
    62  			CredentialsChainVerboseErrors: &verboseErr,
    63  			Region:                        aws.String(awsRegionName),
    64  			/*Logger:                        &logger{},*/
    65  			/*LogLevel:                      &logLevel,*/
    66  		},
    67  	}
    68  	if runtime.GOOS == "windows" && !condition.OnCI() {
    69  		opts.Profile = "mfa" // For some reason on windows workstations this is necessary
    70  	}
    71  	sess, err = session.NewSessionWithOptions(opts)
    72  	if err != nil {
    73  		log.Fatalf("failed to create session, %s", err.Error())
    74  		os.Exit(1)
    75  	}
    76  }
    77  
    78  func getFileList() []string {
    79  	// Get list of files to upload
    80  	fmt.Printf("Getting list of files\n")
    81  	fileList := []string{}
    82  
    83  	err := os.MkdirAll(sourcePath, os.ModePerm)
    84  	if err != nil {
    85  		fmt.Println("Failed to create directory", sourcePath, err)
    86  		os.Exit(1)
    87  	}
    88  
    89  	if err = filepath.Walk(sourcePath, func(p string, f os.FileInfo, err error) error {
    90  		if isDirectory(p) {
    91  			return nil
    92  		}
    93  		fileList = append(fileList, p)
    94  		return nil
    95  	}); err != nil {
    96  		fmt.Println("Failed to walk directory", sourcePath, err)
    97  		os.Exit(1)
    98  	}
    99  
   100  	return fileList
   101  }
   102  
   103  func prepareFile(p string) *s3.PutObjectInput {
   104  	fmt.Printf("Uploading %s\n", p)
   105  
   106  	file, err := os.Open(p)
   107  	if err != nil {
   108  		fmt.Println("Failed to open file", file, err)
   109  		os.Exit(1)
   110  	}
   111  
   112  	// We just created our file, so no need to err check .Stat()
   113  	fileInfo, _ := file.Stat()
   114  	size := fileInfo.Size()
   115  	buffer := make([]byte, size)
   116  
   117  	_, err = file.Read(buffer)
   118  	if err != nil {
   119  		fmt.Println("Failed to read file", file, err)
   120  		os.Exit(1)
   121  	}
   122  
   123  	defer file.Close()
   124  	var key string
   125  	key = normalizePath(awsBucketPrefix + p)
   126  	key = strings.Replace(key, normalizePath(sourcePath), "", 1)
   127  	key = strings.Replace(key, normalizePath(path.Join(getRootPath(), "public")), "", 1)
   128  	fmt.Printf(" \\- Destination: %s\n", key)
   129  
   130  	params := &s3.PutObjectInput{
   131  		Bucket:             aws.String(awsBucketName),
   132  		Key:                aws.String(key),
   133  		Body:               bytes.NewReader(buffer),
   134  		ContentLength:      aws.Int64(size),
   135  		ContentType:        aws.String(http.DetectContentType(buffer)),
   136  		ContentDisposition: aws.String("attachment"),
   137  		ACL:                aws.String("public-read"),
   138  	}
   139  
   140  	return params
   141  }
   142  
   143  func uploadFile(params *s3.PutObjectInput) {
   144  	s3Svc := s3.New(sess)
   145  	_, err := s3Svc.PutObject(params)
   146  	if err != nil {
   147  		fmt.Printf("Failed to upload data to %s/%s, %s\n",
   148  			awsBucketName, *params.Key, err.Error())
   149  		os.Exit(1)
   150  	}
   151  }
   152  
   153  func normalizePath(p string) string {
   154  	return path.Join(strings.Split(p, "\\")...)
   155  }
   156  
   157  func getRootPath() string {
   158  	pathsep := string(os.PathSeparator)
   159  
   160  	_, file, _, ok := runtime.Caller(0)
   161  	if !ok {
   162  		panic("Could not call Caller(0)")
   163  	}
   164  
   165  	abs := path.Dir(file)
   166  
   167  	// When tests are ran with coverage the location of this file is changed to a temp file, and we have to
   168  	// adjust accordingly
   169  	if strings.HasSuffix(abs, "_obj_test") {
   170  		abs = ""
   171  	}
   172  
   173  	var err error
   174  	abs, err = filepath.Abs(path.Join(abs, "..", ".."))
   175  
   176  	if err != nil {
   177  		return ""
   178  	}
   179  
   180  	return abs + pathsep
   181  }
   182  
   183  func isDirectory(p string) bool {
   184  	fd, err := os.Stat(p)
   185  	if err != nil {
   186  		fmt.Println(err)
   187  		os.Exit(2)
   188  	}
   189  	switch mode := fd.Mode(); {
   190  	case mode.IsDir():
   191  		return true
   192  	case mode.IsRegular():
   193  		return false
   194  	}
   195  	return false
   196  }