// Once update set has been tested, we must send the result to Gemnasium, // in order to update statitics. func pushUpdateSetResult(rs *UpdateSetResult) error { fmt.Printf("Pushing result (status='%s'): ", rs.State) if rs.UpdateSetID == 0 || rs.State == "" { return errors.New("Missing updateSet ID and/or State args") } revision, err := getRevision() if err != nil { return err } opts := &gemnasium.APIRequestOptions{ Method: "PATCH", URI: fmt.Sprintf("/projects/%s/revisions/%s/auto_update_steps/%d", rs.ProjectSlug, revision, rs.UpdateSetID), Body: rs, } err = gemnasium.APIRequest(opts) if err != nil { return err } fmt.Printf("done\n") return nil }
// Update project details // http://docs.gemnasium.apiary.io/#patch-%2Fprojects%2F%7Bslug%7D func (p *Project) Update(name, desc *string, monitored *bool) error { if name == nil && desc == nil && monitored == nil { return errors.New("Please specify at least one thing to update (name, desc, or monitored") } update := make(map[string]interface{}) if name != nil { update["name"] = *name } if desc != nil { update["desc"] = *desc } if monitored != nil { update["monitored"] = *monitored } opts := &gemnasium.APIRequestOptions{ Method: "PATCH", URI: fmt.Sprintf("/projects/%s", p.Slug), Body: update, } err := gemnasium.APIRequest(opts) if err != nil { return err } color.Printf("@gProject %s updated succesfully\n", p.Slug) return nil }
func (p *Project) Fetch() error { opts := &gemnasium.APIRequestOptions{ Method: "GET", URI: fmt.Sprintf("/projects/%s", p.Slug), Result: p, } return gemnasium.APIRequest(opts) }
// Fetch and return the dependency files ([]DependecyFile) for the current project func (p *Project) DependencyFiles() (dfiles []DependencyFile, err error) { opts := &gemnasium.APIRequestOptions{ Method: "GET", URI: fmt.Sprintf("/projects/%s/dependency_files", p.Slug), Result: &dfiles, } err = gemnasium.APIRequest(opts) return dfiles, err }
// Start project synchronization // http://docs.gemnasium.apiary.io/#post-%2Fprojects%2F%7Bslug%7D%2Fsync func (p *Project) Sync() error { opts := &gemnasium.APIRequestOptions{ Method: "POST", URI: fmt.Sprintf("/projects/%s/sync", p.Slug), } err := gemnasium.APIRequest(opts) if err != nil { return err } color.Printf("@gSynchronization started for project %s\n", p.Slug) return nil }
// Fetch the best dependency files that have been found so far func fetchDependencyFiles(projectSlug string) (dfiles []models.DependencyFile, err error) { revision, err := getRevision() if err != nil { return nil, err } opts := &gemnasium.APIRequestOptions{ Method: "GET", URI: fmt.Sprintf("/projects/%s/revisions/%s/auto_update_steps/best", projectSlug, revision), Result: &dfiles, } err = gemnasium.APIRequest(opts) return dfiles, err }
func fetchUpdateSet(projectSlug string) (*UpdateSet, error) { revision, err := getRevision() if err != nil { return nil, err } var updateSet *UpdateSet opts := &gemnasium.APIRequestOptions{ Method: "POST", URI: fmt.Sprintf("/projects/%s/revisions/%s/auto_update_steps/next", projectSlug, revision), Result: &updateSet, } err = gemnasium.APIRequest(opts) return updateSet, err }
// Push project dependencies // The current path will be scanned for supported dependency files (SUPPORTED_DEPENDENCY_FILES) func PushDependencyFiles(projectSlug string, files []string) error { dfiles, err := LookupDependencyFiles(files) if err != nil { return err } fmt.Printf("Sending files to Gemnasium: ") var jsonResp map[string][]DependencyFile opts := &gemnasium.APIRequestOptions{ Method: "POST", URI: fmt.Sprintf("/projects/%s/dependency_files", projectSlug), Body: dfiles, Result: &jsonResp, } err = gemnasium.APIRequest(opts) if err != nil { return err } added := []string{} for _, df := range jsonResp["added"] { added = append(added, df.Path) } updated := []string{} for _, df := range jsonResp["updated"] { updated = append(updated, df.Path) } unchanged := []string{} for _, df := range jsonResp["unchanged"] { unchanged = append(unchanged, df.Path) } unsupported := []string{} for _, df := range jsonResp["unsupported"] { unsupported = append(unsupported, df.Path) } fmt.Printf("done.\n\n") fmt.Printf("Added: %s\n", strings.Join(added, ", ")) fmt.Printf("Updated: %s\n", strings.Join(updated, ", ")) fmt.Printf("Unchanged: %s\n", strings.Join(unchanged, ", ")) fmt.Printf("Unsupported: %s\n", strings.Join(unsupported, ", ")) return nil }
func ListDependencyAlerts(project *Project) error { var alerts []Alert opts := &gemnasium.APIRequestOptions{ Method: "GET", URI: fmt.Sprintf("/projects/%s/alerts", project.Slug), Result: &alerts, } err := gemnasium.APIRequest(opts) if err != nil { return err } table := tablewriter.NewWriter(os.Stdout) table.SetHeader([]string{"Advisory", "Date", "Status"}) table.SetAlignment(tablewriter.ALIGN_LEFT) // table is lost when ID have 2 or 3 digits... for _, alert := range alerts { table.Append([]string{strconv.Itoa(alert.Advisory.ID), alert.OpenAt.Format(time.RFC822), alert.Status}) } table.Render() // Send output return nil }
// List projects on gemnasium // TODO: Add a flag to display unmonitored projects too func ListProjects(privateProjectsOnly bool) error { var projects map[string][]Project opts := &gemnasium.APIRequestOptions{ Method: "GET", URI: LIST_PROJECTS_PATH, Result: &projects, } err := gemnasium.APIRequest(opts) if err != nil { return err } for owner, _ := range projects { MonitoredProjectsCount := 0 if owner != "owned" { fmt.Printf("\nShared by: %s\n\n", owner) } table := tablewriter.NewWriter(os.Stdout) table.SetHeader([]string{"Name", "Slug", "Private"}) for _, project := range projects[owner] { if !project.Monitored || (!project.Private && privateProjectsOnly) { continue } var private string if project.Private { private = "private" } else { private = "" } table.Append([]string{project.Name, project.Slug, private}) MonitoredProjectsCount += 1 } table.Render() color.Printf("@{g!}Found %d projects (%d unmonitored are hidden)\n\n", MonitoredProjectsCount, len(projects[owner])-MonitoredProjectsCount) } return nil }
// Live evaluation of dependency files Several files can be sent, not only from // the same language (ie: package.json + Gemfile + Gemfile.lock) LiveEvaluation // will return 2 stases (color for Runtime / Dev.) and the list of deps with // their color. func LiveEvaluation(files []string) error { dfiles, err := models.LookupDependencyFiles(files) if err != nil { return err } requestDeps := map[string][]*models.DependencyFile{"dependency_files": dfiles} var jsonResp map[string]interface{} opts := &gemnasium.APIRequestOptions{ Method: "POST", URI: LIVE_EVAL_PATH, Body: requestDeps, Result: &jsonResp, } err = gemnasium.APIRequest(opts) if err != nil { return err } // Wait until job is done url := fmt.Sprintf("%s%s/%s", config.APIEndpoint, LIVE_EVAL_PATH, jsonResp["job_id"]) req, err := http.NewRequest("GET", url, nil) if err != nil { return err } req.SetBasicAuth("x", config.APIKey) req.Header.Add("Content-Type", "application/json") var response struct { Status string `json:"status"` Result struct { RuntimeStatus string `json:"runtime_status"` DevelopmentStatus string `json:"development_status"` Dependencies []models.Dependency `json:"dependencies"` } `json:"result"` } var iter int // used to display the little dots for each loop bellow client := &http.Client{} for { // use the same request again and again resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return err } if resp.StatusCode != http.StatusOK { response.Status = "error" } if err = json.Unmarshal(body, &response); err != nil { return err } if !config.RawFormat { // don't display status if RawFormat iter += 1 fmt.Printf("\rJob Status: %s%s", response.Status, strings.Repeat(".", iter)) } if response.Status != "working" && response.Status != "queued" { // Job has completed or failed or whatever if config.RawFormat { fmt.Printf("%s\n", body) return nil } break } // Wait 1s before trying again time.Sleep(time.Second * 1) } color.Println(fmt.Sprintf("\n\n%-12.12s %s", "Run. Status", utils.StatusDots(response.Result.RuntimeStatus))) color.Println(fmt.Sprintf("%-12.12s %s\n\n", "Dev. Status", utils.StatusDots(response.Result.DevelopmentStatus))) // Display deps in an ascii table models.RenderDepsAsTable(response.Result.Dependencies, os.Stdout) if response.Result.RuntimeStatus == "red" { return fmt.Errorf("There are important updates available.\n") } return nil }