diff --git a/cmd/circleci-logs/fetch.go b/cmd/circleci-logs/fetch.go new file mode 100644 index 0000000..be35533 --- /dev/null +++ b/cmd/circleci-logs/fetch.go @@ -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 +} diff --git a/cmd/circleci-logs/fetch_test.go b/cmd/circleci-logs/fetch_test.go new file mode 100644 index 0000000..0786bce --- /dev/null +++ b/cmd/circleci-logs/fetch_test.go @@ -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) + } + }) + } +} diff --git a/cmd/circleci-logs/fetchall.go b/cmd/circleci-logs/fetchall.go new file mode 100644 index 0000000..9efb876 --- /dev/null +++ b/cmd/circleci-logs/fetchall.go @@ -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 +} diff --git a/cmd/circleci-logs/fetchall_test.go b/cmd/circleci-logs/fetchall_test.go new file mode 100644 index 0000000..5fa8426 --- /dev/null +++ b/cmd/circleci-logs/fetchall_test.go @@ -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) + } + }) + } +} diff --git a/cmd/circleci-logs/main.go b/cmd/circleci-logs/main.go index 0f1808d..92d9024 100644 --- a/cmd/circleci-logs/main.go +++ b/cmd/circleci-logs/main.go @@ -51,6 +51,7 @@ type Fetcher struct { projectRepo string jobName string outputPath string + outputDir string } const ( @@ -61,6 +62,7 @@ const ( flagProjectRepositoryName = "project-reponame" flagJobName = "job-name" flagOutputPath = "output-path" + flagOutputDir = "output-dir" defaultProjectType = "github" defaultOutputPath = "log.txt" @@ -72,15 +74,6 @@ var ( rootCmd = newRootCmd() ) -func er(msg interface{}) { - fmt.Println("error:", msg) - os.Exit(1) -} - -func appendNewline(s string) string { - return fmt.Sprintf("%s\n", s) -} - func initCobra() { viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) postInitCommands(rootCmd.Commands()) @@ -104,45 +97,6 @@ func presetRequiredFlags(cmd *cobra.Command) { }) } -func newFetchCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "fetch", - Short: "fetch logs for a given job name", - Run: 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 -} - func (f Fetcher) Slug() string { return fmt.Sprintf("%s/%s/%s", f.projectType, f.projectUsername, f.projectRepo) } @@ -217,69 +171,6 @@ func (f Fetcher) FetchStepOutputs(jobNumber, stepNumber int64) ([]JobStepOutput, return stepOutputs, nil } -func executeFetchLogs(cmd *cobra.Command, args []string) { - 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 { - er(err) - } - defer logFile.Close() - - workflowJobs, err := fetcher.FetchWorkflowJobs() - if err != nil { - er(err) - } - - var jobNumber int64 - for _, workflowJob := range workflowJobs.Items { - if workflowJob.Name == fetcher.jobName { - jobNumber = workflowJob.JobNumber - break - } - } - - if jobNumber == 0 { - er(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 { - er(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 \"\"", 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 -} - func newRootCmd() *cobra.Command { cmd := &cobra.Command{ Use: "circleci-fetch-logs", @@ -288,6 +179,7 @@ func newRootCmd() *cobra.Command { cmd.PersistentFlags().StringP(flagAuthToken, "t", "", "circleci api token") cmd.AddCommand(newFetchCmd()) + cmd.AddCommand(newFetchAllCmd()) return cmd } @@ -295,6 +187,7 @@ func newRootCmd() *cobra.Command { func main() { cobra.OnInitialize(initCobra) if err := rootCmd.Execute(); err != nil { - er(err) + fmt.Println("error:", err) + os.Exit(1) } } diff --git a/cmd/circleci-logs/main_test.go b/cmd/circleci-logs/main_test.go new file mode 100644 index 0000000..d2b7f1d --- /dev/null +++ b/cmd/circleci-logs/main_test.go @@ -0,0 +1,306 @@ +package main + +import ( + "reflect" + "testing" + + "github.com/spf13/cobra" +) + +func Test_initCobra(t *testing.T) { + tests := []struct { + name string + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + initCobra() + }) + } +} + +func Test_postInitCommands(t *testing.T) { + type args struct { + commands []*cobra.Command + } + tests := []struct { + name string + args args + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + postInitCommands(tt.args.commands) + }) + } +} + +func Test_presetRequiredFlags(t *testing.T) { + type args struct { + cmd *cobra.Command + } + tests := []struct { + name string + args args + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + presetRequiredFlags(tt.args.cmd) + }) + } +} + +func TestFetcher_Slug(t *testing.T) { + type fields struct { + apiToken string + workflowID string + projectType string + projectUsername string + projectRepo string + jobName string + outputPath string + outputDir string + } + tests := []struct { + name string + fields fields + want string + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f := Fetcher{ + apiToken: tt.fields.apiToken, + workflowID: tt.fields.workflowID, + projectType: tt.fields.projectType, + projectUsername: tt.fields.projectUsername, + projectRepo: tt.fields.projectRepo, + jobName: tt.fields.jobName, + outputPath: tt.fields.outputPath, + outputDir: tt.fields.outputDir, + } + if got := f.Slug(); got != tt.want { + t.Errorf("Fetcher.Slug() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestFetcher_fetchURL(t *testing.T) { + type fields struct { + apiToken string + workflowID string + projectType string + projectUsername string + projectRepo string + jobName string + outputPath string + outputDir string + } + type args struct { + url string + } + tests := []struct { + name string + fields fields + args args + want []byte + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f := Fetcher{ + apiToken: tt.fields.apiToken, + workflowID: tt.fields.workflowID, + projectType: tt.fields.projectType, + projectUsername: tt.fields.projectUsername, + projectRepo: tt.fields.projectRepo, + jobName: tt.fields.jobName, + outputPath: tt.fields.outputPath, + outputDir: tt.fields.outputDir, + } + got, err := f.fetchURL(tt.args.url) + if (err != nil) != tt.wantErr { + t.Errorf("Fetcher.fetchURL() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Fetcher.fetchURL() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestFetcher_FetchWorkflowJobs(t *testing.T) { + type fields struct { + apiToken string + workflowID string + projectType string + projectUsername string + projectRepo string + jobName string + outputPath string + outputDir string + } + tests := []struct { + name string + fields fields + want *WorkflowJobs + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f := Fetcher{ + apiToken: tt.fields.apiToken, + workflowID: tt.fields.workflowID, + projectType: tt.fields.projectType, + projectUsername: tt.fields.projectUsername, + projectRepo: tt.fields.projectRepo, + jobName: tt.fields.jobName, + outputPath: tt.fields.outputPath, + outputDir: tt.fields.outputDir, + } + got, err := f.FetchWorkflowJobs() + if (err != nil) != tt.wantErr { + t.Errorf("Fetcher.FetchWorkflowJobs() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Fetcher.FetchWorkflowJobs() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestFetcher_FetchJob(t *testing.T) { + type fields struct { + apiToken string + workflowID string + projectType string + projectUsername string + projectRepo string + jobName string + outputPath string + outputDir string + } + type args struct { + jobNumber int64 + } + tests := []struct { + name string + fields fields + args args + want *Job + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f := Fetcher{ + apiToken: tt.fields.apiToken, + workflowID: tt.fields.workflowID, + projectType: tt.fields.projectType, + projectUsername: tt.fields.projectUsername, + projectRepo: tt.fields.projectRepo, + jobName: tt.fields.jobName, + outputPath: tt.fields.outputPath, + outputDir: tt.fields.outputDir, + } + got, err := f.FetchJob(tt.args.jobNumber) + if (err != nil) != tt.wantErr { + t.Errorf("Fetcher.FetchJob() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Fetcher.FetchJob() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestFetcher_FetchStepOutputs(t *testing.T) { + type fields struct { + apiToken string + workflowID string + projectType string + projectUsername string + projectRepo string + jobName string + outputPath string + outputDir string + } + type args struct { + jobNumber int64 + stepNumber int64 + } + tests := []struct { + name string + fields fields + args args + want []JobStepOutput + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f := Fetcher{ + apiToken: tt.fields.apiToken, + workflowID: tt.fields.workflowID, + projectType: tt.fields.projectType, + projectUsername: tt.fields.projectUsername, + projectRepo: tt.fields.projectRepo, + jobName: tt.fields.jobName, + outputPath: tt.fields.outputPath, + outputDir: tt.fields.outputDir, + } + got, err := f.FetchStepOutputs(tt.args.jobNumber, tt.args.stepNumber) + if (err != nil) != tt.wantErr { + t.Errorf("Fetcher.FetchStepOutputs() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Fetcher.FetchStepOutputs() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_newRootCmd(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 := newRootCmd(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("newRootCmd() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_main(t *testing.T) { + tests := []struct { + name string + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + main() + }) + } +} diff --git a/cmd/packagecloudpruner/fetcher_test.go b/cmd/packagecloudpruner/fetcher_test.go new file mode 100644 index 0000000..78ede71 --- /dev/null +++ b/cmd/packagecloudpruner/fetcher_test.go @@ -0,0 +1,68 @@ +package main + +import ( + "reflect" + "testing" + + resty "github.com/go-resty/resty/v2" +) + +func TestPackageFetcher_HasNextPage(t *testing.T) { + type fields struct { + client *resty.Client + nextPageURL string + perPage string + } + tests := []struct { + name string + fields fields + want bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &PackageFetcher{ + client: tt.fields.client, + nextPageURL: tt.fields.nextPageURL, + perPage: tt.fields.perPage, + } + if got := p.HasNextPage(); got != tt.want { + t.Errorf("PackageFetcher.HasNextPage() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestPackageFetcher_RequestCurrentPage(t *testing.T) { + type fields struct { + client *resty.Client + nextPageURL string + perPage string + } + tests := []struct { + name string + fields fields + want []Package + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &PackageFetcher{ + client: tt.fields.client, + nextPageURL: tt.fields.nextPageURL, + perPage: tt.fields.perPage, + } + got, err := p.RequestCurrentPage() + if (err != nil) != tt.wantErr { + t.Errorf("PackageFetcher.RequestCurrentPage() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("PackageFetcher.RequestCurrentPage() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/cmd/packagecloudpruner/main_test.go b/cmd/packagecloudpruner/main_test.go new file mode 100644 index 0000000..ff3d7d3 --- /dev/null +++ b/cmd/packagecloudpruner/main_test.go @@ -0,0 +1,41 @@ +package main + +import ( + "testing" + + resty "github.com/go-resty/resty/v2" +) + +func Test_handleRestyError(t *testing.T) { + type args struct { + resp *resty.Response + i interface{} + } + 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 := handleRestyError(tt.args.resp, tt.args.i); (err != nil) != tt.wantErr { + t.Errorf("handleRestyError() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_main(t *testing.T) { + tests := []struct { + name string + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + main() + }) + } +} diff --git a/cmd/packagecloudpruner/package_test.go b/cmd/packagecloudpruner/package_test.go new file mode 100644 index 0000000..9d1b812 --- /dev/null +++ b/cmd/packagecloudpruner/package_test.go @@ -0,0 +1,80 @@ +package main + +import ( + "testing" + "time" +) + +func TestPackage_DaysOld(t *testing.T) { + type fields struct { + Name string + DistroVersion string + CreatedAt time.Time + Version string + Release string + Epoch int + DestroyURL string + } + tests := []struct { + name string + fields fields + want int + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &Package{ + Name: tt.fields.Name, + DistroVersion: tt.fields.DistroVersion, + CreatedAt: tt.fields.CreatedAt, + Version: tt.fields.Version, + Release: tt.fields.Release, + Epoch: tt.fields.Epoch, + DestroyURL: tt.fields.DestroyURL, + } + if got := p.DaysOld(); got != tt.want { + t.Errorf("Package.DaysOld() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestPackage_OlderThan(t *testing.T) { + type fields struct { + Name string + DistroVersion string + CreatedAt time.Time + Version string + Release string + Epoch int + DestroyURL string + } + type args struct { + days int + } + tests := []struct { + name string + fields fields + args args + want bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &Package{ + Name: tt.fields.Name, + DistroVersion: tt.fields.DistroVersion, + CreatedAt: tt.fields.CreatedAt, + Version: tt.fields.Version, + Release: tt.fields.Release, + Epoch: tt.fields.Epoch, + DestroyURL: tt.fields.DestroyURL, + } + if got := p.OlderThan(tt.args.days); got != tt.want { + t.Errorf("Package.OlderThan() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/cmd/packagecloudpruner/remover_test.go b/cmd/packagecloudpruner/remover_test.go new file mode 100644 index 0000000..a599db5 --- /dev/null +++ b/cmd/packagecloudpruner/remover_test.go @@ -0,0 +1,34 @@ +package main + +import ( + "testing" + + resty "github.com/go-resty/resty/v2" +) + +func TestPackageRemover_RemovePackage(t *testing.T) { + type fields struct { + client *resty.Client + } + type args struct { + pkg Package + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &PackageRemover{ + client: tt.fields.client, + } + if err := r.RemovePackage(tt.args.pkg); (err != nil) != tt.wantErr { + t.Errorf("PackageRemover.RemovePackage() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/dockerfiles/linux-builder/Dockerfile b/dockerfiles/linux-builder/Dockerfile index c3038a7..81eba38 100644 --- a/dockerfiles/linux-builder/Dockerfile +++ b/dockerfiles/linux-builder/Dockerfile @@ -33,6 +33,10 @@ RUN curl -Lo /tmp/goreleaser.tar.gz https://github.com/goreleaser/goreleaser/rel RUN tar -C /tmp -zxf /tmp/goreleaser.tar.gz RUN mv /tmp/goreleaser /usr/bin/ +# Install gomplate +RUN curl -Lo /usr/bin/gomplate https://github.com/hairyhenderson/gomplate/releases/download/v3.7.0/gomplate_linux-amd64-slim +RUN chmod +x /usr/bin/gomplate + # Install the packagecloud cli tool RUN gem install package_cloud -v 0.3.05