Skip to content

Commit

Permalink
Add template flag (#18)
Browse files Browse the repository at this point in the history
* Add template flag

* Add template file

* Remove persistant flag comment for workflow

* Remove comment
  • Loading branch information
therealkujo authored Dec 19, 2023
1 parent 12f38ce commit 18ba34a
Show file tree
Hide file tree
Showing 4 changed files with 250 additions and 19 deletions.
35 changes: 30 additions & 5 deletions cmd/codescanning.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,25 @@ import (
var Organization string
var WorkflowFile string
var LogFile string

var TemplateFile string
var Branch string
var CsvFile string
var Force bool
var Errors = make(map[string]error)

func init() {
codeScanningCmd.PersistentFlags().StringVarP(&Organization, "organization", "o", "", "specify Organisation to implement code scanning")
codeScanningCmd.PersistentFlags().StringVarP(&CsvFile, "csv", "c", "", "specify the location of csv file")
codeScanningCmd.MarkFlagsMutuallyExclusive("csv", "organization")
codeScanningCmd.PersistentFlags().StringVarP(&WorkflowFile, "workflow", "w", "", "specify the path to the code scanning workflow file")
codeScanningCmd.MarkPersistentFlagRequired("workflow")
codeScanningCmd.PersistentFlags().StringVarP(&TemplateFile, "template", "t", "", "specify the path to the code scanning workflow template file")
codeScanningCmd.MarkFlagsMutuallyExclusive("workflow", "template")
codeScanningCmd.PersistentFlags().StringVarP(&LogFile, "log", "l", "gh-add-files.log", "specify the path where the log file will be saved")
codeScanningCmd.PersistentFlags().StringVarP(&CsvFile, "csv", "c", "", "specify the location of csv file")
// MarkFlagsOneRequired is only available in cobra v1.8.0 that still isn't released yet (https://github.com/spf13/cobra/issues/1936#issuecomment-1669126066)
// codeScanningCmd.MarkFlagsOneRequired("csv", "organization")
codeScanningCmd.MarkFlagsMutuallyExclusive("csv", "organization")
// codeScanningCmd.MarkFlagsOneRequired("workflow", "template")
codeScanningCmd.PersistentFlags().BoolVarP(&Force, "force", "f", false, "force enable code scanning advanced setup or update the existing code scanning workflow file")

}

var codeScanningCmd = &cobra.Command{
Expand Down Expand Up @@ -63,6 +66,13 @@ var codeScanningCmd = &cobra.Command{
log.Fatalln("ERROR: You cannot provide both csv flag and repository names as arguments")
}

// check if workflow or template file is provided
if len(WorkflowFile) <= 0 && len(TemplateFile) <= 0 {
log.Fatalln("ERROR: Either workflow flag or template flag must be provided")
} else if len(WorkflowFile) > 0 && len(TemplateFile) > 0 {
log.Fatalln("ERROR: You cannot provide both workflow flag and template flag")
}

var repos []Repository

if len(CsvFile) > 0 {
Expand Down Expand Up @@ -201,7 +211,22 @@ var codeScanningCmd = &cobra.Command{
}
log.Printf("Ref created succesfully at : %s\n", newbranchref)

createdFile, err := repo.createWorkflowFile(WorkflowFile, sha)
var workflowFile []byte
if len(TemplateFile) > 0 {
workflowFile, err = repo.generateCodeqlWorkflowFile(TemplateFile)
if err != nil {
Errors[repo.FullName] = err
continue
}
} else {
workflowFile, err = repo.readCodeqlWorkflowFile(WorkflowFile)
if err != nil {
Errors[repo.FullName] = err
continue
}
}

createdFile, err := repo.commitWorkflowFile(workflowFile, sha)
if err != nil {
log.Println(err)
continue
Expand Down
32 changes: 27 additions & 5 deletions cmd/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,22 +318,44 @@ func (repo *Repository) doesCodeqlWorkflowExist() (bool, string, error) {
}
}

func (repo *Repository) createWorkflowFile(WorkflowFile string, commitSha string) (string, error) {

func (repo *Repository) readCodeqlWorkflowFile(WorkflowFile string) ([]byte, error) {
//Open file on disk
f, err := os.Open(WorkflowFile)
if err != nil {
log.Println(err)
return "", err
return []byte{}, err
}
reader := bufio.NewReader(f)
content, err := io.ReadAll(reader)
if err != nil {
log.Println(err)
return "", err
return []byte{}, err
}

encoded := base64.StdEncoding.EncodeToString((content))
return content, nil
}

func (repo *Repository) generateCodeqlWorkflowFile(TemplateWorkflowFile string) ([]byte, error) {
//Open file on disk
f, err := os.Open(TemplateWorkflowFile)
if err != nil {
log.Printf("ERROR: Unable to open template workflow file %s\n", TemplateWorkflowFile)
return []byte{}, err
}
reader := bufio.NewReader(f)
content, err := io.ReadAll(reader)
if err != nil {
log.Printf("ERROR: Unable to read template workflow file %s\n", TemplateWorkflowFile)
return []byte{}, err
}

//replace repo name
workflowFile := strings.ReplaceAll(string(content), "{{ .DefaultBranch }}", repo.DefaultBranch)
return []byte(workflowFile), nil
}

func (repo *Repository) commitWorkflowFile(WorkflowFile []byte, commitSha string) (string, error) {
encoded := base64.StdEncoding.EncodeToString((WorkflowFile))

type Commiter struct {
Name string `json:"name"`
Expand Down
191 changes: 182 additions & 9 deletions cmd/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -730,14 +730,188 @@ func TestRepository_doesCodeqlWorkflowExist(t *testing.T) {
}
}

func TestRepository_createWorkflowFile(t *testing.T) {
func TestRepository_readCodeqlWorkflowFile(t *testing.T) {
type fields struct {
FullName string
Name string
DefaultBranch string
}
type args struct {
WorkflowFile string
}
tests := []struct {
name string
fields fields
args args
want []byte
wantErr bool
}{
// TODO: Add test cases.
// Write test cases for the following scenarios:
// 1. When a codeql workflow file is provided and is valid
// 2. When a codeql workflow file is provided and is invalid
// 3. When a codeql workflow file is not provided

// Test case 1
{
name: "When a codeql workflow file is provided and is valid",
fields: fields{
FullName: "paradisisland/maria",
Name: "maria",
DefaultBranch: "main",
},
args: args{
WorkflowFile: "../examples/codeql.yml",
},
want: []byte("name: CodeQL \non:\n push:\n branches: [ \"main\" ]\n pull_request:\n branches: [ \"main\" ]\n workflow_dispatch:\n\njobs:\n code_analysis:\n uses: advanced-security-demo/central-repo-test/.github/workflows/code_analysis.yml@main\n"),
wantErr: false,
},

// Test case 2
{
name: "When a codeql workflow file is provided and is invalid",
fields: fields{
FullName: "paradisisland/maria",
Name: "maria",
DefaultBranch: "main",
},
args: args{
WorkflowFile: "../examples/codeql_invalid.yml",
},
want: []byte(""),
wantErr: true,
},

// Test case 3
{
name: "When a codeql workflow file is not provided",
fields: fields{
FullName: "paradisisland/maria",
Name: "maria",
DefaultBranch: "main",
},
args: args{
WorkflowFile: "",
},
want: []byte(""),
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
repo := &Repository{
FullName: tt.fields.FullName,
Name: tt.fields.Name,
DefaultBranch: tt.fields.DefaultBranch,
}
got, err := repo.readCodeqlWorkflowFile(tt.args.WorkflowFile)
if (err != nil) != tt.wantErr {
t.Errorf("Repository.readCodeqlWorkflowFile() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Repository.readCodeqlWorkflowFile() = %v, want %v", got, tt.want)
}
})
}
}

func TestRepository_generateCodeqlWorkflowFile(t *testing.T) {
type fields struct {
FullName string
Name string
DefaultBranch string
}
type args struct {
TemplateWorkflowFile string
}
tests := []struct {
name string
fields fields
args args
want []byte
wantErr bool
}{
// TODO: Add test cases.
// Write test cases for the following scenarios:
// 1. When a template workflow file is provided and is valid
// 2. When a template workflow file is provided and is invalid
// 3. When a template workflow file is not provided

// Test case 1
{
name: "When a template workflow file is provided and is valid",
fields: fields{
FullName: "paradisisland/maria",
Name: "maria",
DefaultBranch: "totallyuniquebranchname",
},
args: args{
TemplateWorkflowFile: "../examples/codeql-template.yml",
},
want: []byte("name: CodeQL \non:\n push:\n branches: [ \"totallyuniquebranchname\" ]\n pull_request:\n branches: [ \"totallyuniquebranchname\" ]\n workflow_dispatch:\n\njobs:\n code_analysis:\n uses: advanced-security-demo/central-repo-test/.github/workflows/code_analysis.yml@main\n"),
wantErr: false,
},

// Test case 2
{
name: "When a template workflow file is provided and is invalid",
fields: fields{
FullName: "paradisisland/maria",
Name: "maria",
DefaultBranch: "main",
},
args: args{
TemplateWorkflowFile: "../examples/codeql-template-invalid.yml",
},
want: []byte(""),
wantErr: true,
},

// Test case 3
{
name: "When a template workflow file is not provided",
fields: fields{
FullName: "paradisisland/maria",
Name: "maria",
DefaultBranch: "main",
},
args: args{
TemplateWorkflowFile: "",
},
want: []byte(""),
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
repo := &Repository{
FullName: tt.fields.FullName,
Name: tt.fields.Name,
DefaultBranch: tt.fields.DefaultBranch,
}
got, err := repo.generateCodeqlWorkflowFile(tt.args.TemplateWorkflowFile)
if (err != nil) != tt.wantErr {
t.Errorf("Repository.generateCodeqlWorkflowFile() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Repository.generateCodeqlWorkflowFile() = %v, want %v", got, tt.want)
}
})
}
}



func TestRepository_commitWorkflowFile(t *testing.T) {
type fields struct {
FullName string
Name string
DefaultBranch string
}
type args struct {
WorkflowFile []byte
commitSha string
}
tests := []struct {
Expand All @@ -763,7 +937,7 @@ func TestRepository_createWorkflowFile(t *testing.T) {
DefaultBranch: "main",
},
args: args{
WorkflowFile: "../examples/codeql.yml",
WorkflowFile: []byte("name: CodeQL \non:\n push:\n branches: [ \"main\" ]\n pull_request:\n branches: [ \"main\" ]\n workflow_dispatch:\n\njobs:\n code_analysis:\n uses: advanced-security-demo/central-repo-test/.github/workflows/code_analysis.yml@main\n"),
commitSha: "",
},
want: "codeql.yml",
Expand All @@ -779,7 +953,7 @@ func TestRepository_createWorkflowFile(t *testing.T) {
DefaultBranch: "main",
},
args: args{
WorkflowFile: "../examples/codeql.yml",
WorkflowFile: []byte("name: CodeQL \non:\n push:\n branches: [ \"main\" ]\n pull_request:\n branches: [ \"main\" ]\n workflow_dispatch:\n\njobs:\n code_analysis:\n uses: advanced-security-demo/central-repo-test/.github/workflows/code_analysis.yml@main\n"),
commitSha: "",
},
want: "",
Expand All @@ -795,7 +969,7 @@ func TestRepository_createWorkflowFile(t *testing.T) {
DefaultBranch: "main",
},
args: args{
WorkflowFile: "../examples/codeql.yml",
WorkflowFile: []byte("name: CodeQL \non:\n push:\n branches: [ \"main\" ]\n pull_request:\n branches: [ \"main\" ]\n workflow_dispatch:\n\njobs:\n code_analysis:\n uses: advanced-security-demo/central-repo-test/.github/workflows/code_analysis.yml@main\n"),
commitSha: "",
},
want: "",
Expand All @@ -811,7 +985,7 @@ func TestRepository_createWorkflowFile(t *testing.T) {
DefaultBranch: "main",
},
args: args{
WorkflowFile: "../examples/codeql.yml",
WorkflowFile: []byte("name: CodeQL \non:\n push:\n branches: [ \"main\" ]\n pull_request:\n branches: [ \"main\" ]\n workflow_dispatch:\n\njobs:\n code_analysis:\n uses: advanced-security-demo/central-repo-test/.github/workflows/code_analysis.yml@main\n"),
commitSha: "0ae040b692ec3e927163db2b984135aa3c088cba",
},
want: "codeql.yml",
Expand All @@ -826,13 +1000,13 @@ func TestRepository_createWorkflowFile(t *testing.T) {
Name: tt.fields.Name,
DefaultBranch: tt.fields.DefaultBranch,
}
got, err := repo.createWorkflowFile(tt.args.WorkflowFile, tt.args.commitSha)
got, err := repo.commitWorkflowFile(tt.args.WorkflowFile, tt.args.commitSha)
if (err != nil) != tt.wantErr {
t.Errorf("Repository.createWorkflowFile() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("Repository.commitWorkflowFile() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("Repository.createWorkflowFile() = %v, want %v", got, tt.want)
t.Errorf("Repository.commitWorkflowFile() = %v, want %v", got, tt.want)
}
})
}
Expand Down Expand Up @@ -987,4 +1161,3 @@ func TestRepository_deleteBranch(t *testing.T) {
}
}


11 changes: 11 additions & 0 deletions examples/codeql-template.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: CodeQL
on:
push:
branches: [ "{{ .DefaultBranch }}" ]
pull_request:
branches: [ "{{ .DefaultBranch }}" ]
workflow_dispatch:

jobs:
code_analysis:
uses: advanced-security-demo/central-repo-test/.github/workflows/code_analysis.yml@main

0 comments on commit 18ba34a

Please sign in to comment.