@@ -2,13 +2,9 @@ package sourcemaps
22
33import (
44 "bytes"
5- "encoding/json"
65 "fmt"
7- "io"
8- "mime/multipart"
9- "net/http"
106 "os"
11- "path/filepath "
7+ "os/exec "
128 "strings"
139
1410 "github.com/spf13/cobra"
@@ -30,17 +26,7 @@ const (
3026 defaultPath = "."
3127 defaultBackendUrl = "https://app.launchdarkly.com"
3228
33- verifyApiKeyQuery = `
34- query ApiKeyToOrgID($api_key: String!) {
35- api_key_to_org_id(api_key: $api_key)
36- }
37- `
38-
39- getSourceMapUrlsQuery = `
40- query GetSourceMapUploadUrls($api_key: String!, $paths: [String!]!) {
41- get_source_map_upload_urls(api_key: $api_key, paths: $paths)
42- }
43- `
29+ npmPackage = "@highlight-run/sourcemap-uploader"
4430)
4531
4632func NewUploadCmd (client resources.Client ) * cobra.Command {
@@ -106,261 +92,59 @@ func runE(client resources.Client) func(cmd *cobra.Command, args []string) error
10692 return fmt .Errorf ("api key cannot be empty" )
10793 }
10894
109- organizationId , err := verifyApiKey (apiKey , backendUrl )
110- if err != nil {
111- return err
95+ if err := checkNodeInstalled (); err != nil {
96+ return fmt .Errorf ("Node.js is required to upload sourcemaps: %v" , err )
11297 }
11398
114- fmt . Printf ( "Starting to upload source maps from %s \n " , path )
99+ args := [] string { npmPackage , " upload" , "--apiKey" , apiKey }
115100
116- fileList , err := getAllSourceMapFiles (path )
117- if err != nil {
118- return err
101+ if appVersion != "" {
102+ args = append (args , "--appVersion" , appVersion )
119103 }
120104
121- if len ( fileList ) == 0 {
122- return fmt . Errorf ( "no source maps found in %s, is this the correct path? " , path )
105+ if path != defaultPath {
106+ args = append ( args , "-- path" , path )
123107 }
124108
125- s3Keys := make ([]string , len (fileList ))
126- for i , file := range fileList {
127- s3Keys [i ] = getS3Key (organizationId , appVersion , basePath , file .Name )
109+ if basePath != "" {
110+ args = append (args , "--basePath" , basePath )
128111 }
129112
130- uploadUrls , err := getSourceMapUploadUrls (apiKey , s3Keys , backendUrl )
131- if err != nil {
132- return err
133- }
134-
135- for i , file := range fileList {
136- err = uploadFile (file .Path , uploadUrls [i ], file .Name )
137- if err != nil {
138- return err
139- }
113+ if backendUrl != defaultBackendUrl {
114+ args = append (args , "--backendUrl" , backendUrl )
140115 }
141116
142- fmt .Println ("Successfully uploaded all sourcemaps" )
143- return nil
144- }
145- }
146-
147- func verifyApiKey (apiKey , backendUrl string ) (string , error ) {
148- variables := map [string ]interface {}{
149- "api_key" : apiKey ,
150- }
151-
152- body , err := json .Marshal (map [string ]interface {}{
153- "query" : verifyApiKeyQuery ,
154- "variables" : variables ,
155- })
156- if err != nil {
157- return "" , err
158- }
159-
160- req , err := http .NewRequest ("POST" , backendUrl , bytes .NewBuffer (body ))
161- if err != nil {
162- return "" , err
163- }
164-
165- req .Header .Set ("Content-Type" , "application/json" )
166- req .Header .Set ("ApiKey" , apiKey )
167-
168- client := & http.Client {}
169- resp , err := client .Do (req )
170- if err != nil {
171- return "" , err
172- }
173- defer resp .Body .Close ()
174-
175- respBody , err := io .ReadAll (resp .Body )
176- if err != nil {
177- return "" , err
178- }
179-
180- var result struct {
181- Data struct {
182- ApiKeyToOrgID string `json:"api_key_to_org_id"`
183- } `json:"data"`
184- }
117+ fmt .Printf ("Starting to upload source maps from %s using %s\n " , path , npmPackage )
185118
186- err = json .Unmarshal (respBody , & result )
187- if err != nil {
188- return "" , err
189- }
190-
191- if result .Data .ApiKeyToOrgID == "" || result .Data .ApiKeyToOrgID == "0" {
192- return "" , fmt .Errorf ("invalid api key" )
193- }
119+ cmd := exec .Command ("npx" , args ... )
120+ var stdout , stderr bytes.Buffer
121+ cmd .Stdout = & stdout
122+ cmd .Stderr = & stderr
123+ cmd .Env = os .Environ ()
194124
195- return result .Data .ApiKeyToOrgID , nil
196- }
197-
198- func getSourceMapUploadUrls (apiKey string , paths []string , backendUrl string ) ([]string , error ) {
199- variables := map [string ]interface {}{
200- "api_key" : apiKey ,
201- "paths" : paths ,
202- }
203-
204- body , err := json .Marshal (map [string ]interface {}{
205- "query" : getSourceMapUrlsQuery ,
206- "variables" : variables ,
207- })
208- if err != nil {
209- return nil , err
210- }
211-
212- req , err := http .NewRequest ("POST" , backendUrl , bytes .NewBuffer (body ))
213- if err != nil {
214- return nil , err
215- }
216-
217- req .Header .Set ("Content-Type" , "application/json" )
218-
219- client := & http.Client {}
220- resp , err := client .Do (req )
221- if err != nil {
222- return nil , err
223- }
224- defer resp .Body .Close ()
225-
226- respBody , err := io .ReadAll (resp .Body )
227- if err != nil {
228- return nil , err
229- }
230-
231- var result struct {
232- Data struct {
233- GetSourceMapUploadUrls []string `json:"get_source_map_upload_urls"`
234- } `json:"data"`
235- }
236-
237- err = json .Unmarshal (respBody , & result )
238- if err != nil {
239- return nil , err
240- }
241-
242- if len (result .Data .GetSourceMapUploadUrls ) == 0 {
243- return nil , fmt .Errorf ("unable to generate source map upload urls" )
244- }
245-
246- return result .Data .GetSourceMapUploadUrls , nil
247- }
248-
249- type SourceMapFile struct {
250- Path string
251- Name string
252- }
253-
254- func getAllSourceMapFiles (path string ) ([]SourceMapFile , error ) {
255- var fileList []SourceMapFile
256-
257- absPath , err := filepath .Abs (path )
258- if err != nil {
259- return nil , err
260- }
261-
262- fileInfo , err := os .Stat (absPath )
263- if err != nil {
264- return nil , err
265- }
266-
267- if fileInfo .IsDir () {
268- err = filepath .Walk (absPath , func (path string , info os.FileInfo , err error ) error {
269- if err != nil {
270- return err
271- }
272-
273- if info .IsDir () {
274- if info .Name () == "node_modules" {
275- return filepath .SkipDir
276- }
277- return nil
278- }
279-
280- if strings .HasSuffix (info .Name (), ".js.map" ) {
281- relPath , err := filepath .Rel (absPath , path )
282- if err != nil {
283- return err
284- }
285-
286- fileList = append (fileList , SourceMapFile {
287- Path : path ,
288- Name : relPath ,
289- })
290- }
291-
292- return nil
293- })
125+ err := cmd .Run ()
126+ fmt .Print (stdout .String ())
294127
295128 if err != nil {
296- return nil , err
297- }
298- } else {
299- if strings .HasSuffix (fileInfo .Name (), ".js.map" ) {
300- fileList = append (fileList , SourceMapFile {
301- Path : absPath ,
302- Name : fileInfo .Name (),
303- })
129+ fmt .Print (stderr .String ())
130+ return fmt .Errorf ("failed to upload sourcemaps: %v" , err )
304131 }
305- }
306-
307- return fileList , nil
308- }
309132
310- func getS3Key (organizationId , version , basePath , fileName string ) string {
311- if version == "" {
312- version = "unversioned"
313- }
314-
315- if basePath != "" && ! strings .HasSuffix (basePath , "/" ) {
316- basePath = basePath + "/"
133+ fmt .Println ("Successfully uploaded all sourcemaps" )
134+ return nil
317135 }
318-
319- return fmt .Sprintf ("%s/%s/%s%s" , organizationId , version , basePath , fileName )
320136}
321137
322- func uploadFile (filePath , uploadUrl , name string ) error {
323- fileContent , err := os .ReadFile (filePath )
324- if err != nil {
325- return err
326- }
327-
328- var requestBody bytes.Buffer
329- writer := multipart .NewWriter (& requestBody )
330-
331- part , err := writer .CreateFormFile ("file" , filepath .Base (filePath ))
138+ func checkNodeInstalled () error {
139+ _ , err := exec .LookPath ("node" )
332140 if err != nil {
333- return err
141+ return fmt . Errorf ( "Node.js is not installed or not in PATH: %v" , err )
334142 }
335143
336- _ , err = part . Write ( fileContent )
144+ _ , err = exec . LookPath ( "npx" )
337145 if err != nil {
338- return err
339- }
340-
341- err = writer .Close ()
342- if err != nil {
343- return err
344- }
345-
346- req , err := http .NewRequest ("PUT" , uploadUrl , bytes .NewReader (fileContent ))
347- if err != nil {
348- return err
349- }
350-
351- req .Header .Set ("Content-Type" , "application/octet-stream" )
352-
353- client := & http.Client {}
354- resp , err := client .Do (req )
355- if err != nil {
356- return err
357- }
358- defer resp .Body .Close ()
359-
360- if resp .StatusCode >= 400 {
361- return fmt .Errorf ("failed to upload %s: %s" , name , resp .Status )
146+ return fmt .Errorf ("npx is not installed or not in PATH: %v" , err )
362147 }
363148
364- fmt .Printf ("Uploaded %s\n " , name )
365149 return nil
366150}
0 commit comments