-
Couldn't load subscription status.
- Fork 7
Implemented CI Handler for adding CI files to a repo #48
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| repos: | ||
| - repo: https://github.com/pre-commit/pre-commit-hooks | ||
| rev: v4.4.0 | ||
| hooks: | ||
| - id: trailing-whitespace | ||
| - id: end-of-file-fixer | ||
| - id: check-yaml | ||
| - id: check-added-large-files |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| - repo: https://github.com/dnephin/pre-commit-golang | ||
| rev: master | ||
| hooks: | ||
| - id: go-fmt | ||
| - id: go-vet | ||
| - id: go-imports | ||
| - id: golangci-lint | ||
| - id: go-unit-tests | ||
| - id: go-mod-tidy | ||
| - id: go-mod-vendor | ||
| - id: go-build |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| - repo: https://github.com/pre-commit/mirrors-eslint | ||
| rev: v8.37.0 | ||
| hooks: | ||
| - id: eslint |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| - repo: https://github.com/psf/black | ||
| rev: 22.12.0 | ||
| hooks: | ||
| - id: black | ||
| language_version: python3.10 | ||
| - repo: https://github.com/pycqa/flake8 | ||
| rev: 6.0.0 | ||
| hooks: | ||
| - id: flake8 | ||
| - repo: https://github.com/asottile/reorder_python_imports | ||
| rev: v3.9.0 | ||
| hooks: | ||
| - id: reorder-python-imports | ||
| - repo: https://github.com/Lucas-C/pre-commit-hooks-safety | ||
| rev: v1.3.0 | ||
| hooks: | ||
| - id: python-safety-dependencies-check | ||
| files: requirements.txt |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| #!/usr/bin/env bash | ||
| # | ||
| # Exit on error. Append "|| true" if you expect an error. | ||
| set -o errexit | ||
| # Exit on error inside any functions or subshells. | ||
| set -o errtrace | ||
| # Do not allow use of undefined vars. Use ${VAR:-} to use an undefined VAR | ||
| set -o nounset | ||
| # Catch the error in case mysqldump fails (but gzip succeeds) in `mysqldump |gzip` | ||
| set -o pipefail | ||
|
|
||
| __repo_url=${1} | ||
| __repo_branch=${2} | ||
|
|
||
| __repo_name=$(basename ${__repo_url} .git) #get name of git repository | ||
| __temp_dir=$(mktemp -d) | ||
| __repo_dir="${__temp_dir}/${__repo_name}" | ||
| mkdir -p "${__repo_dir}" | ||
|
|
||
| ci_dir_path = "../precommit/CI/" | ||
|
|
||
|
|
||
| ## @brief Pulls repository in the given path | ||
| ## @param $1 repo url to clone. | ||
| ## @param $2 path on which to clone the repo. | ||
| ## @param $3 branch name of the git repository. | ||
| pullRepository() { | ||
| local repo_url=${1} | ||
| local repo_path=${2} | ||
| pushd "${repo_path}" | ||
| if [ "$#" -gt 2 ] ; then | ||
| local repo_branch=${3} | ||
| git clone -b "${repo_branch}" "${repo_url}" . | ||
| else | ||
| git clone "${repo_url}" . | ||
| fi | ||
| chmod -R 755 "${repo_path}" | ||
| echo "Repository successfully pulled - ${repo_url}" | ||
| popd | ||
| } | ||
|
|
||
|
|
||
| addCI(){ | ||
| local repo_path=${1} | ||
| local ci_dir_path=${2} | ||
|
|
||
| pushd "${repo_path}" | ||
| cp -r "${ci_dir_path}" . | ||
| echo "Git hooks successfully added" | ||
| popd | ||
| } | ||
|
|
||
|
|
||
| ## @brief Pushes repository to Github | ||
| ## @param $1 path where updates to the repository are stored. | ||
| ## @param $2 branch of the repository. | ||
| pushRepo(){ | ||
| local repo_path=${1} | ||
| local repo_branch=${2} | ||
|
|
||
| pushd "${repo_path}" | ||
| git add -A . | ||
| git commit -m "[DeployBot] Initialized gitsecret and added git hooks" | ||
| git pull || true | ||
| git push -u origin "${repo_branch}" | ||
| echo "Git Repo successfully initialized and pushed" | ||
| popd | ||
| } | ||
|
|
||
| cleanup() { | ||
| local repo_path=$1 | ||
| rm -rf "${repo_path}" | ||
| rm -rf "${__temp_dir}" | ||
| rm -rf "${__hooks_temp_dir}" | ||
| echo "Cleanup successfull" | ||
| } | ||
|
|
||
| pullRepository "${__repo_url}" "${__repo_dir}" "${__repo_branch}" | ||
| addCI "${__repo_dir}" "${ci_dir_path}" | ||
| pushRepo "${__repo_dir}" "${__repo_branch}" | ||
| cleanup "${__repo_dir}" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,12 @@ var ( | |
| // githubUserName is the name of the user which is a member of the GitHub | ||
| //organization. githubAccessToken is a personal access token of this user | ||
| githubUserName string | ||
|
|
||
| // githubActionToken is used to verify the request coming from GitHub Actions | ||
| githubActionToken string | ||
|
|
||
| // Logdir is the directory where all logs will be stored | ||
| logDir string | ||
| ) | ||
|
|
||
| const ( | ||
|
|
@@ -26,6 +32,14 @@ const ( | |
|
|
||
| // apiURL is the url at which all APIs of github are rooted | ||
| apiURL = "https://api.github.com" | ||
|
|
||
| // root path of pre-commit files | ||
| BASE_FILES_PATH string = "../../precommit/" | ||
| // pre-commit config file | ||
| PRE_COMMIT_CONFIG string = "../../precommit/CI/pre-commit-config.yaml" | ||
|
Comment on lines
+37
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's keep the style consistent, maybe change it to camel case as is with the other variables? |
||
|
|
||
| // CI script name | ||
| ciScriptName = "ci.sh" | ||
| ) | ||
|
|
||
| // Repository is the type that is used to store information about a repository | ||
|
|
@@ -35,6 +49,19 @@ type Repository struct { | |
| Branches []string `json:"branches"` | ||
| } | ||
|
|
||
| // CIAction : Used for unmarshalling the CI request | ||
| type CIAction struct { | ||
| Repo string `json:"repo"` | ||
| Python string `json:"python"` | ||
| Golang string `json:"golang"` | ||
| Node string `json:"node"` | ||
| Ts string `json:"ts"` | ||
| Flutter string `json:"flutter"` | ||
| Dart string `json:"dart"` | ||
| Docker string `json:"docker"` | ||
| Shell string `json:"shell"` | ||
|
Comment on lines
+55
to
+62
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need all their types to be string. Can't we unmarshall to bool instead from the json request? |
||
| } | ||
|
|
||
| func init() { | ||
| githubSecret = helper.Env("GITHUB_SECRET", "None") | ||
| if githubSecret == "None" { | ||
|
|
@@ -50,4 +77,11 @@ func init() { | |
| if githubAccessToken == "None" { | ||
| log.Fatal("GITHUB_ACCESS_TOKEN is not present in environment, Exiting") | ||
| } | ||
|
|
||
| githubActionToken = helper.Env("GITHUB_ACTION_TOKEN", "None") | ||
| if githubActionToken == "None" { | ||
| log.Fatal("GITHUB_ACTION_TOKEN is not present in environment, Exiting") | ||
| } | ||
|
|
||
| logDir = helper.Env("LOG_DIR", "/var/logs/deploybot/") | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,7 +9,10 @@ import ( | |
| "io/ioutil" | ||
| "net/http" | ||
| "net/url" | ||
| "os" | ||
| "os/exec" | ||
| "path" | ||
| "reflect" | ||
|
|
||
| "github.com/devclub-iitd/DeployBot/src/helper" | ||
| log "github.com/sirupsen/logrus" | ||
|
|
@@ -181,3 +184,122 @@ func CreatedRepo(r *http.Request) (*Repository, int, error) { | |
| }, | ||
| }, 200, nil | ||
| } | ||
|
|
||
| func validateActionRequest(r *http.Request) bool { | ||
| headerValue := r.Header.Get("Authorization") | ||
| return "Bearer " + githubActionToken == headerValue | ||
| } | ||
|
|
||
| // CIHandler handles the request to add CI Files to a repo | ||
| func CIHandler(w http.ResponseWriter, r *http.Request) { | ||
|
|
||
| if !validateActionRequest(r) { | ||
| log.Info("Request verification from Github Action: FAILED") | ||
| w.WriteHeader(403) | ||
| return | ||
| } | ||
| log.Info("Request verification from Github Action: SUCCESS") | ||
|
|
||
| if r.Method != "POST" { | ||
| w.WriteHeader(405) | ||
| log.Errorf("received a %s request, expected POST for CI Handler", r.Method) | ||
| return | ||
| } | ||
|
|
||
| r_body, err := ioutil.ReadAll(r.Body) | ||
| if err != nil { | ||
| w.WriteHeader(500) | ||
| log.Errorf("cannot read request body - %v", err) | ||
| return | ||
| } | ||
|
|
||
| var ci CIAction | ||
| if err := json.Unmarshal(r_body, &ci); err != nil { | ||
| w.WriteHeader(500) | ||
| log.Errorf("cannot unmarshal request body - %v", err) | ||
| return | ||
| } | ||
|
|
||
| var ciList []string | ||
| // add Default checks | ||
| ciList = append(ciList, "Default") | ||
| v := reflect.ValueOf(ci) | ||
| for i := 0; i < v.NumField(); i++ { | ||
| if v.Field(i).String() == "true" { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If CIAction can have bool types this should be changed accordingly. |
||
| ciList = append(ciList, v.Type().Field(i).Name) | ||
| } | ||
| } | ||
| addChecks(ciList) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this function call never return an error? |
||
| log.Info("Added checks to pre-commit-config.yaml") | ||
|
|
||
| // call a script to add this file to the repo | ||
|
|
||
| // get ssh_url of the repo | ||
| repositories, err := Repos() | ||
| if err != nil { | ||
| log.Error("Error getting repositories %v", err) | ||
| return | ||
| } | ||
| var repo Repository | ||
| for _, repository := range repositories { | ||
| if repository.Name == ci.Repo { | ||
| repo = repository | ||
| } | ||
| } | ||
| if repo.Name == "" { | ||
| log.Error("Repository not found") | ||
| return | ||
| } | ||
|
|
||
| for _, branch := range repo.Branches { | ||
| go addCI(repo.URL, repo.Name, branch) | ||
| } | ||
|
|
||
| } | ||
|
|
||
| func addChecks(ciList []string) { | ||
| // combine the data of all files in ciList to a file named .pre-commit-config.yaml | ||
|
|
||
| // create the file even if it exists | ||
| os.Create(PRE_COMMIT_CONFIG) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not very comfortable with the idea of using the same file
|
||
| f, err := os.OpenFile(PRE_COMMIT_CONFIG, os.O_APPEND|os.O_WRONLY, 0600) | ||
| if err != nil { | ||
| log.Error("Error opening file:pre-commit.config") | ||
| return | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe return the err from here |
||
| } | ||
|
|
||
| // close the file after the function returns | ||
| defer f.Close() | ||
|
|
||
| for i := 0; i < len(ciList); i++ { | ||
| // add the data from the file to the pre-commit-config file | ||
|
|
||
| var file_name string = ciList[i] + ".ci" | ||
| var file_path string = BASE_FILES_PATH + file_name | ||
|
|
||
| content, err := os.ReadFile(file_path) | ||
| if err != nil { | ||
| log.Error("Error reading file:" + file_path) | ||
| return | ||
| } | ||
|
|
||
| // append the data to the pre-commit-config file | ||
| _, err = fmt.Fprintf(f, string(content)+"\n") | ||
| if err != nil { | ||
| fmt.Printf("Error writing to file:%s\n", PRE_COMMIT_CONFIG) | ||
| return | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Return err here as well |
||
| } | ||
| } | ||
| } | ||
|
|
||
| func addCI(repoURL,repoName, branchName string) { | ||
| // call the script ciScriptName | ||
| log.Infof("Calling script %s for repo:%s and branch:%s",ciScriptName, repoName, branchName) | ||
| output,err := exec.Command(ciScriptName, repoURL, branchName).CombinedOutput() | ||
| helper.WriteToFile(path.Join(logDir, "git", fmt.Sprintf("%s:%s.txt", repoName, branchName)), string(output)) | ||
| if err != nil { | ||
| log.Errorf("Add CI to git repo - %s: FAILED - %v", repoURL, err) | ||
| } else { | ||
| log.Infof("Add CI to git repo - %s: SUCCESS",repoURL) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need a separate logDir, can't we use the existing logger?