Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes required for parallel build jobs #24

Merged
merged 5 commits into from
Jun 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions cmd/circleci-logs/fetch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package main

import (
"fmt"
"os"

"github.com/spf13/cobra"
"github.com/spf13/viper"
)

func executeFetchLogs(cmd *cobra.Command, args []string) error {
fetcher := Fetcher{
apiToken: viper.GetString(flagAuthToken),
workflowID: viper.GetString(flagWorkflowID),
projectType: viper.GetString(flagProjectType),
projectUsername: viper.GetString(flagProjectUsername),
projectRepo: viper.GetString(flagProjectRepositoryName),
jobName: viper.GetString(flagJobName),
outputPath: viper.GetString(flagOutputPath),
}

logFile, err := os.Create(fetcher.outputPath)
if err != nil {
return err
}
defer logFile.Close()

workflowJobs, err := fetcher.FetchWorkflowJobs()
if err != nil {
return err
}

var jobNumber int64
for _, workflowJob := range workflowJobs.Items {
if workflowJob.Name == fetcher.jobName {
jobNumber = workflowJob.JobNumber
break
}
}

if jobNumber == 0 {
return fmt.Errorf("a job with the name \"%s\" was not found in workflow \"%s\"",
fetcher.jobName, fetcher.workflowID)
}

job, err := fetcher.FetchJob(jobNumber)
if err != nil {
return err
}

for _, step := range job.Steps {
for _, action := range step.Actions {
stepOutputs, err := fetcher.FetchStepOutputs(jobNumber, action.StepNumber)
if err != nil {
fmt.Fprintf(os.Stderr, "error fetching output for step \"%d\" in job \"%d\"", action.StepNumber, jobNumber)
continue
}
stepStr := fmt.Sprintf(" Step: %s\n", step.Name)
logFile.WriteString(jobStepSeparator)
logFile.WriteString(stepStr)
logFile.WriteString(jobStepSeparator)
for _, stepOutput := range stepOutputs {
timeStr := fmt.Sprintf("--- %s ---\n", stepOutput.Time)
logFile.WriteString(timeStr)
logFile.WriteString(stepOutput.Message)
logFile.WriteString("\n")
}
logFile.Sync()
}
}

return nil
}

func newFetchCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "fetch",
Short: "fetch logs for a given job name",
RunE: executeFetchLogs,
}

cmd.Flags().StringP(flagWorkflowID, "w", "", "id of the workflow that the job belongs to")
cmd.Flags().StringP(flagProjectType, "p", defaultProjectType, "type of the project that the organization exists within (github or bitbucket)")
cmd.Flags().StringP(flagProjectUsername, "u", "", "github or bitbucket username of the project")
cmd.Flags().StringP(flagProjectRepositoryName, "r", "", "name of the repository that the job belongs to")
cmd.Flags().StringP(flagJobName, "j", "", "name of the job to find logs for")
cmd.Flags().StringP(flagOutputPath, "o", defaultOutputPath, "path to a file to store the retrieved logs in")

cmd.MarkFlagRequired(flagAuthToken)
cmd.MarkFlagRequired(flagWorkflowID)
cmd.MarkFlagRequired(flagProjectUsername)
cmd.MarkFlagRequired(flagProjectRepositoryName)
cmd.MarkFlagRequired(flagJobName)

viper.BindEnv(flagAuthToken, "CIRCLE_API_TOKEN")
viper.BindEnv(flagWorkflowID, "CIRCLE_WORKFLOW_ID")
viper.BindEnv(flagProjectType, "CIRCLE_PROJECT_TYPE")
viper.BindEnv(flagProjectUsername, "CIRCLE_PROJECT_USERNAME")
viper.BindEnv(flagProjectRepositoryName, "CIRCLE_PROJECT_REPONAME")
viper.BindEnv(flagJobName, "CIRCLE_JOB")
viper.BindEnv(flagOutputPath, "LOG_OUTPUT_PATH")

viper.BindPFlag(flagAuthToken, cmd.PersistentFlags().Lookup(flagAuthToken))
viper.BindPFlag(flagWorkflowID, cmd.Flags().Lookup(flagWorkflowID))
viper.BindPFlag(flagProjectType, cmd.Flags().Lookup(flagProjectType))
viper.BindPFlag(flagProjectUsername, cmd.Flags().Lookup(flagProjectUsername))
viper.BindPFlag(flagProjectRepositoryName, cmd.Flags().Lookup(flagProjectRepositoryName))
viper.BindPFlag(flagJobName, cmd.Flags().Lookup(flagJobName))
viper.BindPFlag(flagOutputPath, cmd.Flags().Lookup(flagOutputPath))

return cmd
}
45 changes: 45 additions & 0 deletions cmd/circleci-logs/fetch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package main

import (
"reflect"
"testing"

"github.com/spf13/cobra"
)

func Test_executeFetchLogs(t *testing.T) {
type args struct {
cmd *cobra.Command
args []string
}
tests := []struct {
name string
args args
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := executeFetchLogs(tt.args.cmd, tt.args.args); (err != nil) != tt.wantErr {
t.Errorf("executeFetchLogs() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

func Test_newFetchCmd(t *testing.T) {
tests := []struct {
name string
want *cobra.Command
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := newFetchCmd(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("newFetchCmd() = %v, want %v", got, tt.want)
}
})
}
}
102 changes: 102 additions & 0 deletions cmd/circleci-logs/fetchall.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package main

import (
"fmt"
"os"
"path/filepath"

"github.com/spf13/cobra"
"github.com/spf13/viper"
)

func executeFetchAllLogs(cmd *cobra.Command, args []string) error {
fetcher := Fetcher{
apiToken: viper.GetString(flagAuthToken),
workflowID: viper.GetString(flagWorkflowID),
projectType: viper.GetString(flagProjectType),
projectUsername: viper.GetString(flagProjectUsername),
projectRepo: viper.GetString(flagProjectRepositoryName),
outputDir: viper.GetString(flagOutputDir),
}

workflowJobs, err := fetcher.FetchWorkflowJobs()
if err != nil {
return err
}

var jobNumber int64
for _, workflowJob := range workflowJobs.Items {
jobNumber = workflowJob.JobNumber

logName := fmt.Sprintf("%s.txt", workflowJob.Name)
logPath := filepath.Join(fetcher.outputDir, logName)
logFile, err := os.Create(logPath)
if err != nil {
return err
}
defer logFile.Close()

job, err := fetcher.FetchJob(jobNumber)
if err != nil {
return err
}

for _, step := range job.Steps {
for _, action := range step.Actions {
stepOutputs, err := fetcher.FetchStepOutputs(jobNumber, action.StepNumber)
if err != nil {
fmt.Fprintf(os.Stderr, "error fetching output for step \"%d\" in job \"%d\"", action.StepNumber, jobNumber)
continue
}
stepStr := fmt.Sprintf(" Step: %s\n", step.Name)
logFile.WriteString(jobStepSeparator)
logFile.WriteString(stepStr)
logFile.WriteString(jobStepSeparator)
for _, stepOutput := range stepOutputs {
timeStr := fmt.Sprintf("--- %s ---\n", stepOutput.Time)
logFile.WriteString(timeStr)
logFile.WriteString(stepOutput.Message)
logFile.WriteString("\n")
}
logFile.Sync()
}
}
}

return nil
}

func newFetchAllCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "fetch-all",
Short: "fetch logs for all jobs for a given workflow",
RunE: executeFetchAllLogs,
}

cmd.Flags().StringP(flagWorkflowID, "w", "", "id of the workflow that the job belongs to")
cmd.Flags().StringP(flagProjectType, "p", defaultProjectType, "type of the project that the organization exists within (github or bitbucket)")
cmd.Flags().StringP(flagProjectUsername, "u", "", "github or bitbucket username of the project")
cmd.Flags().StringP(flagProjectRepositoryName, "r", "", "name of the repository that the job belongs to")
cmd.Flags().StringP(flagOutputDir, "d", "", "path to a directory to store the retrieved logs in")

cmd.MarkFlagRequired(flagAuthToken)
cmd.MarkFlagRequired(flagWorkflowID)
cmd.MarkFlagRequired(flagProjectUsername)
cmd.MarkFlagRequired(flagProjectRepositoryName)

viper.BindEnv(flagAuthToken, "CIRCLE_API_TOKEN")
viper.BindEnv(flagWorkflowID, "CIRCLE_WORKFLOW_ID")
viper.BindEnv(flagProjectType, "CIRCLE_PROJECT_TYPE")
viper.BindEnv(flagProjectUsername, "CIRCLE_PROJECT_USERNAME")
viper.BindEnv(flagProjectRepositoryName, "CIRCLE_PROJECT_REPONAME")
viper.BindEnv(flagOutputPath, "LOG_OUTPUT_DIR")

viper.BindPFlag(flagAuthToken, cmd.PersistentFlags().Lookup(flagAuthToken))
viper.BindPFlag(flagWorkflowID, cmd.Flags().Lookup(flagWorkflowID))
viper.BindPFlag(flagProjectType, cmd.Flags().Lookup(flagProjectType))
viper.BindPFlag(flagProjectUsername, cmd.Flags().Lookup(flagProjectUsername))
viper.BindPFlag(flagProjectRepositoryName, cmd.Flags().Lookup(flagProjectRepositoryName))
viper.BindPFlag(flagOutputDir, cmd.Flags().Lookup(flagOutputDir))

return cmd
}
45 changes: 45 additions & 0 deletions cmd/circleci-logs/fetchall_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package main

import (
"reflect"
"testing"

"github.com/spf13/cobra"
)

func Test_executeFetchAllLogs(t *testing.T) {
type args struct {
cmd *cobra.Command
args []string
}
tests := []struct {
name string
args args
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := executeFetchAllLogs(tt.args.cmd, tt.args.args); (err != nil) != tt.wantErr {
t.Errorf("executeFetchAllLogs() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

func Test_newFetchAllCmd(t *testing.T) {
tests := []struct {
name string
want *cobra.Command
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := newFetchAllCmd(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("newFetchAllCmd() = %v, want %v", got, tt.want)
}
})
}
}
Loading