@@ -24,6 +24,7 @@ import (
24
24
"github.com/G-Node/gin-valid/internal/resources/templates"
25
25
gogs "github.com/gogits/go-gogs-client"
26
26
27
+ "github.com/google/uuid"
27
28
"github.com/gorilla/mux"
28
29
)
29
30
@@ -460,6 +461,121 @@ func runValidator(validator, repopath, commit string, gcl *ginclient.Client) {
460
461
}()
461
462
}
462
463
464
+ func runValidatorPub (validator , repopath string , gcl * ginclient.Client ) string {
465
+ uuid := uuid .New ()
466
+ respath := filepath .Join (validator , repopath , uuid .String ())
467
+ go func () {
468
+ log .ShowWrite ("[Info] Running %s validation on repository %q (HEAD)" , validator , repopath )
469
+
470
+ // TODO add check if a repo is currently being validated. Since the cloning
471
+ // can potentially take quite some time prohibit running the same
472
+ // validation at the same time. Could also move this to a mapped go
473
+ // routine and if the same repo is validated twice, the first occurrence is
474
+ // stopped and cleaned up while the second starts anew - to make sure its
475
+ // always the latest state of the repository that is being validated.
476
+
477
+ srvcfg := config .Read ()
478
+ resdir := filepath .Join (srvcfg .Dir .Result , respath )
479
+
480
+ // Create results folder if necessary
481
+ // CHECK: can this lead to a race condition, if a job for the same user/repo combination is started twice in short succession?
482
+ err := os .MkdirAll (resdir , os .ModePerm )
483
+ if err != nil {
484
+ log .ShowWrite ("[Error] creating %q results folder: %s" , resdir , err .Error ())
485
+ return
486
+ }
487
+
488
+ tmpdir , err := ioutil .TempDir (srvcfg .Dir .Temp , validator )
489
+ if err != nil {
490
+ log .ShowWrite ("[Error] Internal error: Couldn't create temporary gin directory: %s" , err .Error ())
491
+ writeValFailure (resdir )
492
+ return
493
+ }
494
+
495
+ repopathparts := strings .SplitN (repopath , "/" , 2 )
496
+ _ , repo := repopathparts [0 ], repopathparts [1 ]
497
+ valroot := filepath .Join (tmpdir , repo )
498
+
499
+ // Enable cleanup once tried and tested
500
+ defer os .RemoveAll (tmpdir )
501
+
502
+ // Add the processing badge and message to display while the validator runs
503
+ procBadge := filepath .Join (resdir , srvcfg .Label .ResultsBadge )
504
+ err = ioutil .WriteFile (procBadge , []byte (resources .ProcessingBadge ), os .ModePerm )
505
+ if err != nil {
506
+ log .ShowWrite ("[Error] writing results badge for %q" , valroot )
507
+ }
508
+
509
+ outFile := filepath .Join (resdir , srvcfg .Label .ResultsFile )
510
+ err = ioutil .WriteFile (outFile , []byte (progressmsg ), os .ModePerm )
511
+ if err != nil {
512
+ log .ShowWrite ("[Error] writing results file for %q" , valroot )
513
+ }
514
+
515
+ // err = makeSessionKey(gcl, commit)
516
+ // if err != nil {
517
+ // log.ShowWrite("[error] failed to create session key: %s", err.Error())
518
+ // writeValFailure(resdir)
519
+ // return
520
+ // }
521
+ // defer deleteSessionKey(gcl, commit)
522
+
523
+ // TODO: if (annexed) content is not available yet, wait and retry. We
524
+ // would have to set a max timeout for this. The issue is that when a user
525
+ // does a 'gin upload' a push happens immediately and the hook is
526
+ // triggered, but annexed content is only transferred after the push and
527
+ // could take a while (hours?). The validation service should try to
528
+ // download content after the transfer is complete, or should keep retrying
529
+ // until it's available, with a timeout. We could also make it more
530
+ // efficient by only downloading the content in the directories which are
531
+ // specified in the validator config (if it exists).
532
+
533
+ glog .Init ()
534
+ clonechan := make (chan git.RepoFileStatus )
535
+ os .Chdir (tmpdir )
536
+ go gcl .CloneRepo (repopath , clonechan )
537
+ for stat := range clonechan {
538
+ if stat .Err != nil {
539
+ log .ShowWrite ("[Error] Failed to fetch repository data for %q: %s" , repopath , stat .Err .Error ())
540
+ writeValFailure (resdir )
541
+ return
542
+ }
543
+ log .ShowWrite ("[Info] %s %s" , stat .State , stat .Progress )
544
+ }
545
+ log .ShowWrite ("[Info] clone complete for '%s'" , repopath )
546
+
547
+ log .ShowWrite ("[Info] Downloading content" )
548
+ getcontentchan := make (chan git.RepoFileStatus )
549
+ // TODO: Get only the content for the files that will be validated
550
+ go gcl .GetContent ([]string {"." }, getcontentchan )
551
+ for stat := range getcontentchan {
552
+ if stat .Err != nil {
553
+ log .ShowWrite ("[Error] failed to get content for %q: %s" , repopath , stat .Err .Error ())
554
+ writeValFailure (resdir )
555
+ return
556
+ }
557
+ log .ShowWrite ("[Info] %s %s %s" , stat .State , stat .FileName , stat .Progress )
558
+ }
559
+ log .ShowWrite ("[Info] get-content complete" )
560
+
561
+ switch validator {
562
+ case "bids" :
563
+ err = validateBIDS (valroot , resdir )
564
+ case "nix" :
565
+ err = validateNIX (valroot , resdir )
566
+ case "odml" :
567
+ err = validateODML (valroot , resdir )
568
+ default :
569
+ err = fmt .Errorf ("[Error] invalid validator name: %s" , validator )
570
+ }
571
+
572
+ if err != nil {
573
+ writeValFailure (resdir )
574
+ }
575
+ }()
576
+ return respath
577
+ }
578
+
463
579
// writeValFailure writes a badge and page content for when a hook payload is
464
580
// valid, but the validator failed to run. This function does not return
465
581
// anything, but logs all errors.
@@ -543,10 +659,8 @@ func PubValidatePost(w http.ResponseWriter, r *http.Request) {
543
659
return
544
660
}
545
661
546
- runValidator (validator , repopath , "HEAD" , gcl )
547
- // TODO redirect to results
548
- w .WriteHeader (http .StatusOK )
549
- w .Write ([]byte ("OK" ))
662
+ respath := runValidatorPub (validator , repopath , gcl )
663
+ http .Redirect (w , r , filepath .Join ("results" , respath ), http .StatusFound )
550
664
}
551
665
552
666
// Validate temporarily clones a provided repository from
0 commit comments