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 }