From f5c559b6ca0f545e282aaa1db45a6c40867d7543 Mon Sep 17 00:00:00 2001 From: Philipp Heuer Date: Fri, 30 Aug 2019 20:52:28 +0200 Subject: [PATCH] feature: initial commit --- .gitattributes | 6 + .github/workflows/main.yml | 28 ++++ .gitignore | 2 + README.md | 25 ++++ docs/spec/types.md | 5 + docs/spec/variables.md | 62 ++++++++ go.mod | 8 + go.sum | 83 ++++++++++ pkg/azuredevops/README.md | 9 ++ pkg/azuredevops/azuredevops.go | 90 +++++++++++ pkg/azuredevops/azuredevops_test.go | 191 ++++++++++++++++++++++++ pkg/common/common.go | 93 ++++++++++++ pkg/common/common_test.go | 19 +++ pkg/common/git.go | 114 ++++++++++++++ pkg/githubactions/README.md | 69 +++++++++ pkg/githubactions/githubactions.go | 90 +++++++++++ pkg/githubactions/githubactions_test.go | 111 ++++++++++++++ pkg/gitlabci/README.md | 9 ++ pkg/gitlabci/gitlabci.go | 82 ++++++++++ pkg/gitlabci/gitlabci_test.go | 126 ++++++++++++++++ src/app.go | 73 +++++++++ src/app_test.go | 9 ++ src/common.go | 8 + 23 files changed, 1312 insertions(+) create mode 100644 .gitattributes create mode 100644 .github/workflows/main.yml create mode 100644 .gitignore create mode 100644 README.md create mode 100644 docs/spec/types.md create mode 100644 docs/spec/variables.md create mode 100644 go.mod create mode 100644 go.sum create mode 100644 pkg/azuredevops/README.md create mode 100644 pkg/azuredevops/azuredevops.go create mode 100644 pkg/azuredevops/azuredevops_test.go create mode 100644 pkg/common/common.go create mode 100644 pkg/common/common_test.go create mode 100644 pkg/common/git.go create mode 100644 pkg/githubactions/README.md create mode 100644 pkg/githubactions/githubactions.go create mode 100644 pkg/githubactions/githubactions_test.go create mode 100644 pkg/gitlabci/README.md create mode 100644 pkg/gitlabci/gitlabci.go create mode 100644 pkg/gitlabci/gitlabci_test.go create mode 100644 src/app.go create mode 100644 src/app_test.go create mode 100644 src/common.go diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..ce4d10d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Bash Scripts +*.sh text eol=lf +*.bash text eol=lf diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..c0886e5 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,28 @@ +# docs: https://help.github.com/en/articles/workflow-syntax-for-github-actions +name: CI + +on: [push] + +jobs: + # stages + build: + # runtime environment + runs-on: ubuntu-latest + + # jobs + steps: + # checkout + - uses: actions/checkout@v1 + with: + fetch-depth: 1 + # prepare environment + - name: Prepare Environment + run: | + curl https://raw.githubusercontent.com/EnvCLI/modular-pipeline/master/install.sh | sh + # pipeline steps + - name: Test + run: | + go-test + - name: Build + run: | + go-build diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7df71d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# generated artifacts +dist/* diff --git a/README.md b/README.md new file mode 100644 index 0000000..a507c79 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# normalize.ci + +> A tool to turn the continious integration / deployment variables into a common format for generally usable scripts without any dependencies. + +## usage + +dowonload the provided library and run it ... + +```bash +// TODO +``` + +## normalized variables + +- [Specification: Variables](docs/spec/variables.md) + +## supported systems + +NAME | SLUG +--- | --- | +[Azure DevOps Pipeline](docs/system/azure-devops-pipeline.md) | `azure-devops` +[GitLab CI/CD](docs/system/gitlab-ci.md) | `gitlab-ci` +[GitHub Actions](docs/system/github-actions.md) | `github-actions` + +*Note:* If none of the above systems is detected, repository information is determined based on the local Git repository. diff --git a/docs/spec/types.md b/docs/spec/types.md new file mode 100644 index 0000000..d5ccd44 --- /dev/null +++ b/docs/spec/types.md @@ -0,0 +1,5 @@ +# Variable Types + +## SLUG + +> lowercased, and with everything except 0-9 and a-z replaced with -. No leading / trailing -. diff --git a/docs/spec/variables.md b/docs/spec/variables.md new file mode 100644 index 0000000..a9c10db --- /dev/null +++ b/docs/spec/variables.md @@ -0,0 +1,62 @@ +# Variables + +If a system already provides `NCI` compliant variables, then it can set `NCI` to true to prevent this script from being executed. +In that case it's required to set `NCI_VERSION` to the implemented specification, to allow scripts to run actions if a newer spec is released. + +## common + +Variable | Description +--- | --- | +`NCI` | Will be set the true, if the variables have been normalized. (this script) +`NCI_VERSION` | The revision of nci that was used to generate the normalized variables. +`NCI_SERVICE_NAME` | The commercial name of the used ci service. (e.g. GitLab CI, Travis CI, CircleCI, Jenkins) +`NCI_SERVICE_SLUG` | The commercial name normalized as slug for use in scripts, will not be changed. + +## server + +Variable | Description +--- | --- | +`NCI_SERVER_NAME` | The commercial name of the vcs server. +`NCI_SERVER_HOST` | The hostname that the vcs server is running on. +`NCI_SERVER_VERSION` | The running version of the vcs server. + +## worker + +Variable | Description +--- | --- | +`NCI_WORKER_ID` | A unique id of the ci worker. +`NCI_WORKER_NAME` | The human readable name of the ci worker. +`NCI_WORKER_VERSION` | The version of the ci worker. +`NCI_WORKER_ARCH` | The arch of the ci worker. (ie. linux/amd64) + +## pipeline + +Variable | Description +--- | --- | +`NCI_PIPELINE_TRIGGER` | What triggered the pipeline. (ie. manual/push/trigger/api/schedule/pull_request/build) +`NCI_PIPELINE_STAGE_NAME` | Human readable name of the current stage. +`NCI_PIPELINE_STAGE_SLUG` | Slug of the current stage. +`NCI_PIPELINE_JOB_NAME` | Human readable name of the current job. +`NCI_PIPELINE_JOB_SLUG` | Slug of the current job. +`NCI_PIPELINE_PULL_REQUEST_ID` | The number of the pull request, is only present if `NCI_PIPELINE_TRIGGER` = pull_request. + +## project + +Variable | Description +--- | --- | +`NCI_PROJECT_ID` | Unique project id, can be used in deployments. +`NCI_PROJECT_NAME` | Unique project id, can be used in deployments. + +## repository + +Variable | Description +--- | --- | +`NCI_REPOSITORY_KIND` | The used version control system. (git) +`NCI_REPOSITORY_REMOTE` | The remote url pointing at the repository. (git remote url or `local` if no remote was found) +`NCI_COMMIT_REF_TYPE` | The reference type. (branch / tag) +`NCI_COMMIT_REF_NAME` | Human readable name of the current repository reference. +`NCI_COMMIT_REF_SLUG` | Slug of the current repository reference. +`NCI_COMMIT_SHA` | A unique hash, that each commit gets. +`NCI_COMMIT_SHA_SHORT` | A short form of the unique commit hash. (8 chars) +`NCI_COMMIT_TITLE` | The title of the latest commit on the current reference. +`NCI_COMMIT_DESCRIPTION` | The description of the latest commit on the current reference. diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..28bdad8 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/PhilippHeuer/normalize-ci + +go 1.12 + +require ( + github.com/sirupsen/logrus v1.4.2 + gopkg.in/src-d/go-git.v4 v4.13.1 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..258335a --- /dev/null +++ b/go.sum @@ -0,0 +1,83 @@ +github.com/PhilippHeuer/normalize.ci v0.0.0-20190815223534-5a3ca3eceacb h1:oSuhFc/YfIoBavi9t5Y5D5kzd0XjoVNa/DwrGcPQMxY= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= +github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= +github.com/philippheuer/normalize-ci v0.0.0-20190815223534-5a3ca3eceacb h1:PJb+htauJY9RY5FqBINZGm6P07hcPrYobm/1sflywew= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= +github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= +github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e h1:D5TXcfTk7xF7hvieo4QErS3qqCB4teTffacDWr7CI+0= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= +golang.org/x/tools v0.0.0-20190820033707-85edb9ef3283/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= +gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= +gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= +gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE= +gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/pkg/azuredevops/README.md b/pkg/azuredevops/README.md new file mode 100644 index 0000000..6fa26a6 --- /dev/null +++ b/pkg/azuredevops/README.md @@ -0,0 +1,9 @@ +# Azure DevOps Pipeline + +## sources + +- [Predefined variables](https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&abs=yaml#agent-variables) + +## detection + +By checking for the presence of the environment variable `TF_BUILD`. diff --git a/pkg/azuredevops/azuredevops.go b/pkg/azuredevops/azuredevops.go new file mode 100644 index 0000000..8001685 --- /dev/null +++ b/pkg/azuredevops/azuredevops.go @@ -0,0 +1,90 @@ +package azuredevops + +import ( + "runtime" + + "github.com/PhilippHeuer/normalize-ci/pkg/common" +) + +// Normalizer is the implementation of the normalizer +type Normalizer struct { + version string + name string + slug string +} + +// GetName returns the name of the normalizer +func (n Normalizer) GetName() string { + return n.name +} + +// Check validates that the current environment is gitlab ci +func (n Normalizer) Check(env []string) bool { + if common.IsEnvironmentSetTo(env, "TF_BUILD", "true") { + return true + } + + return false +} + +// Normalize normalizes the environment variables into the common format +func (n Normalizer) Normalize(env []string) []string { + var normalized []string + + // common + normalized = append(normalized, "NCI=true") + normalized = append(normalized, "NCI_VERSION="+n.version) + normalized = append(normalized, "NCI_SERVICE_NAME="+n.name) + normalized = append(normalized, "NCI_SERVICE_SLUG="+n.slug) + + // server + normalized = append(normalized, "NCI_SERVER_NAME="+common.GetEnvironment(env, "BUILD_REPOSITORY_PROVIDER")) + normalized = append(normalized, "NCI_SERVER_HOST="+common.GetHostFromURL(common.GetEnvironment(env, "BUILD_REPOSITORY_URI"))) + normalized = append(normalized, "NCI_SERVER_VERSION=") + + // worker + normalized = append(normalized, "NCI_WORKER_ID="+common.GetEnvironment(env, "AGENT_ID")) + normalized = append(normalized, "NCI_WORKER_NAME="+common.GetEnvironment(env, "AGENT_MACHINENAME")) + normalized = append(normalized, "NCI_WORKER_VERSION="+common.GetEnvironment(env, "AGENT_VERSION")) + normalized = append(normalized, "NCI_WORKER_ARCH="+runtime.GOOS+"/"+runtime.GOARCH) + + // pipeline + if common.GetEnvironment(env, "BUILD_REASON") == "Manual" { + normalized = append(normalized, "NCI_PIPELINE_TRIGGER=manual") + } else if common.GetEnvironment(env, "BUILD_REASON") == "IndividualCI" || common.GetEnvironment(env, "BUILD_REASON") == "BatchedCI" { + normalized = append(normalized, "NCI_PIPELINE_TRIGGER=push") + } else if common.GetEnvironment(env, "BUILD_REASON") == "Schedule" { + normalized = append(normalized, "NCI_PIPELINE_TRIGGER=schedule") + } else if common.GetEnvironment(env, "BUILD_REASON") == "PullRequest" { + normalized = append(normalized, "NCI_PIPELINE_TRIGGER=pull_request") + } else if common.GetEnvironment(env, "BUILD_REASON") == "BuildCompletion" { + normalized = append(normalized, "NCI_PIPELINE_TRIGGER=build") + } else { + normalized = append(normalized, "NCI_PIPELINE_TRIGGER=unknown") + } + normalized = append(normalized, "NCI_PIPELINE_STAGE_NAME="+common.GetEnvironment(env, "SYSTEM_STAGENAME")) // SYSTEM_STAGEDISPLAYNAME + normalized = append(normalized, "NCI_PIPELINE_STAGE_SLUG="+common.GetSlug(common.GetEnvironment(env, "SYSTEM_STAGENAME"))) + normalized = append(normalized, "NCI_PIPELINE_JOB_NAME="+common.GetEnvironment(env, "SYSTEM_JOBNAME")) // SYSTEM_JOBDISPLAYNAME + normalized = append(normalized, "NCI_PIPELINE_JOB_SLUG="+common.GetSlug(common.GetEnvironment(env, "SYSTEM_JOBNAME"))) + + // project + normalized = append(normalized, "NCI_PROJECT_ID="+common.GetEnvironment(env, "SYSTEM_TEAMPROJECTID")) + normalized = append(normalized, "NCI_PROJECT_NAME="+common.GetEnvironment(env, "SYSTEM_TEAMPROJECT")) + normalized = append(normalized, "NCI_PROJECT_SLUG="+common.GetSlug(common.GetEnvironment(env, "SYSTEM_TEAMPROJECT"))) + + // repository + normalized = append(normalized, common.GetSCMArguments(common.GetGitDirectory())...) + + return normalized +} + +// NewNormalizer gets a instance of the normalizer +func NewNormalizer() Normalizer { + entity := Normalizer{ + version: "0.1.0", + name: "Azure DevOps Pipeline", + slug: "azure-devops", + } + + return entity +} diff --git a/pkg/azuredevops/azuredevops_test.go b/pkg/azuredevops/azuredevops_test.go new file mode 100644 index 0000000..8156a74 --- /dev/null +++ b/pkg/azuredevops/azuredevops_test.go @@ -0,0 +1,191 @@ +package azuredevops + +import ( + "testing" + "os" + + "github.com/PhilippHeuer/normalize-ci/pkg/common" +) + +var testEnvironment = []string{ + "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI=https://heuer.visualstudio.com/", + "TAG=8", + "BUILD_SOURCEBRANCH=refs/heads/master", + "SYSTEM_TASKDEFINITIONSURI=https://heuer.visualstudio.com/", + "AGENT_VERSION=2.155.1", + "SYSTEM_JOBATTEMPT=1", + "LEIN_HOME=/usr/local/lib/lein", + "BUILD_QUEUEDBY=GitHub", + "SYSTEM_HOSTTYPE=build", + "SYSTEM_COLLECTIONURI=https://heuer.visualstudio.com/", + "GOROOT_1_11_X64=/usr/local/go1.11", + "SYSTEM_JOBPARALLELISMTAG=Public", + "BUILD_REPOSITORY_GIT_SUBMODULECHECKOUT=False", + "GOROOT_1_9_X64=/usr/local/go1.9", + "ANDROID_HOME=/usr/local/lib/android/sdk", + "BUILD_STAGINGDIRECTORY=/home/vsts/work/1/a", + "RUNNER_TOOLSDIRECTORY=", + "AGENT_MACHINENAME=fv-az679", + "SYSTEM_WORKFOLDER=/home/vsts/work", + "COMMON_TESTRESULTSDIRECTORY=/home/vsts/work/1/TestResults", + "GRADLE_HOME=/usr/share/gradle", + "AGENT_JOBNAME=Build", + "ANDROID_SDK_ROOT=/usr/local/lib/android/sdk", + "MSDEPLOY_HTTP_USER_AGENT=VSTS_8c7f0be9-cf3c-4627-9df8-fa7d1cb80b37_build_1_0", + "JAVA_HOME_8_X64=/usr/lib/jvm/zulu-8-azure-amd64", + "AGENT_OSARCHITECTURE=X64", + "BUILD_SOURCEVERSIONAUTHOR=Philipp Heuer", + "BUILD_REQUESTEDFOREMAIL=philipp.heuer@outlook.com", + "AGENT_ACCEPTTEEEULA=True", + "SYSTEM_STAGEATTEMPT=1", + "ANT_HOME=/usr/share/ant", + "GIT_TERMINAL_PROMPT=0", + "SYSTEM_DEFINITIONNAME=PhilippHeuer.azure-pipeline-test", + "SYSTEM_CULTURE=en-US", + "JAVA_HOME_11_X64=/usr/lib/jvm/zulu-11-azure-amd64", + "AGENT_TEMPDIRECTORY=/home/vsts/work/_temp", + "BUILD_REPOSITORY_CLEAN=False", + "BUILD_SOURCEBRANCHNAME=master", + "BUILD_REPOSITORY_PROVIDER=GitHub", + "USER=vsts", + "SYSTEM_JOBIDENTIFIER=Build.Build.__default", + "SYSTEM_TEAMFOUNDATIONSERVERURI=https://heuer.visualstudio.com/", + "TF_BUILD=True", + "AZURE_HTTP_USER_AGENT=VSTS_8c7f0be9-cf3c-4627-9df8-fa7d1cb80b37_build_1_0", + "BUILD_QUEUEDBYID=271ebd76-ab97-4f2c-b79e-e90b0bf28f01", + "SYSTEM_TASKDISPLAYNAME=CmdLine", + "SYSTEM_STAGENAME=Build", + "ImageVersion=156.2", + "AGENT_DISABLELOGPLUGIN_TESTRESULTLOGPLUGIN=false", + "SYSTEM_TEAMPROJECTID=d1b384a8-f33f-427d-86fd-f021826a54ea", + "AGENT_ROOTDIRECTORY=/home/vsts/work", + "VSTS_PROCESS_LOOKUP_ID=vsts_e6211052-b261-4175-b9a8-f7fd395fa8ce", + "AGENT_HOMEDIRECTORY=/home/vsts/agents/2.155.1", + "SYSTEM_TEAMPROJECT=azure-test", + "AGENT_TOOLSDIRECTORY=/opt/hostedtoolcache", + "BUILD_REPOSITORY_ID=PhilippHeuer/azure-pipeline-test", + "BUILD_SOURCEVERSIONMESSAGE=feature: initial commit", + "BUILD_REPOSITORY_LOCALPATH=/home/vsts/work/1/s", + "SYSTEM_JOBDISPLAYNAME=Build", + "agent.jobstatus=Succeeded", + "AGENT_BUILDDIRECTORY=/home/vsts/work/1", + "SYSTEM=build", + "BUILD_REASON=IndividualCI", + "SYSTEM_PIPELINESTARTTIME=2019-08-11 21:29:12+00:00", + "AGENT_OS=Linux", + "BUILD_SOURCESDIRECTORY=/home/vsts/work/1/s", + "DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1", + "PATH=/usr/share/rust/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin", + "SYSTEM_PHASEATTEMPT=1", + "LEIN_JAR=/usr/local/lib/lein/self-installs/leiningen-2.9.1-standalone.jar", + "SYSTEM_ISSCHEDULED=False", + "VSTS_AGENT_PERFLOG=/home/vsts/agentperf", + "PWD=/home/vsts/work/1/s", + "BUILD_BUILDURI=vstfs:///Build/Build/8", + "SYSTEM_PULLREQUEST_ISFORK=False", + "CONDA=/usr/share/miniconda", + "GOROOT_1_10_X64=/usr/local/go1.10", + "SYSTEM_DEFINITIONID=1", + "JAVA_HOME=/usr/lib/jvm/zulu-8-azure-amd64", + "SYSTEM_STAGEID=6884a131-87da-5381-61f3-d7acc3b91d76", + "AGENT_DISABLELOGPLUGIN_TESTFILEPUBLISHERPLUGIN=true", + "VCPKG_INSTALLATION_ROOT=/usr/local/share/vcpkg", + "JAVA_HOME_7_X64=/usr/lib/jvm/zulu-7-azure-amd64", + "JAVA_HOME_12_X64=/usr/lib/jvm/zulu-12-azure-amd64", + "SYSTEM_ENABLEACCESSTOKEN=SecretVariable", + "LANG=en_US.UTF-8", + "SYSTEM_TASKINSTANCENAME=CmdLine", + "SYSTEM_PHASEDISPLAYNAME=Build", + "SYSTEM_SERVERTYPE=Hosted", + "BUILD_REPOSITORY_NAME=PhilippHeuer/azure-pipeline-test", + "GOROOT_1_12_X64=/usr/local/go1.12", + "BUILD_REPOSITORY_URI=https://github.com/PhilippHeuer/azure-pipeline-test", + "PIPELINE_WORKSPACE=/home/vsts/work/1", + "BUILD_DEFINITIONNAME=PhilippHeuer.azure-pipeline-test", + "AGENT_WORKFOLDER=/home/vsts/work", + "SYSTEM_JOBNAME=Build", + "BUILD_REQUESTEDFOR=Philipp Heuer", + "SYSTEM_ARTIFACTSDIRECTORY=/home/vsts/work/1/a", + "SYSTEM_TIMELINEID=74fa3252-420c-48e3-8a50-1e7827a17aa6", + "SHLVL=1", + "AGENT_ID=5", + "BOOST_ROOT_1_69_0=/usr/local/share/boost/1.69.0", + "M2_HOME=/usr/share/apache-maven-3.6.1", + "HOME=/home/vsts", + "GOROOT=/usr/local/go1.12", + "AGENT_RETAINDEFAULTENCODING=false", + "CI=true", + "SYSTEM_JOBPOSITIONINPHASE=1", + "BUILD_REQUESTEDFORID=daed2ec0-0b40-4031-b5b4-d5d1a9356542", + "BUILD_ARTIFACTSTAGINGDIRECTORY=/home/vsts/work/1/a", + "BUILD_BINARIESDIRECTORY=/home/vsts/work/1/b", + "BUILD_BUILDID=8", + "SYSTEM_TASKINSTANCEID=45862c6e-83c5-515f-73e2-e7009eff9f9b", + "BUILD_SOURCEVERSION=7cb9c91a16950e79d209b710eb33ad56db564e0c", + "CHROME_BIN=/usr/bin/google-chrome", + "BOOST_ROOT=/usr/local/share/boost/1.69.0", + "SYSTEM_DEFAULTWORKINGDIRECTORY=/home/vsts/work/1/s", + "SYSTEM_JOBID=3dc8fd7e-4368-5a92-293e-d53cefc8c4b3", + "SYSTEM_TOTALJOBSINPHASE=1", + "AGENT_NAME=Hosted Agent", + "SYSTEM_STAGEDISPLAYNAME=Build image", + "BUILD_DEFINITIONVERSION=1", + "SYSTEM_PLANID=74fa3252-420c-48e3-8a50-1e7827a17aa6", + "SYSTEM_PHASEID=a11efe29-9b58-5a6c-3fa4-3e36996dcbd8", + "SYSTEM_COLLECTIONID=8c7f0be9-cf3c-4627-9df8-fa7d1cb80b37", + "TASK_DISPLAYNAME=CmdLine", + "AGENT_JOBSTATUS=Succeeded", + "ENDPOINT_URL_SYSTEMVSSCONNECTION=https://heuer.visualstudio.com/", + "BUILD_BUILDNUMBER=20190811.8", + "SYSTEM_PHASENAME=Build", + "BUILD_CONTAINERID=3045488", +} + +func TestMain(m *testing.M) { + common.SetupTestLogger() + code := m.Run() + os.Exit(code) +} + +func TestEnvironmentCheck(t *testing.T) { + var normalizer = NewNormalizer() + if normalizer.Check(testEnvironment) != true { + t.Errorf("Check should succeed with the provided sample data!") + } +} + +func TestEnvironmentNormalizer(t *testing.T) { + var normalizer = NewNormalizer() + var normalized = normalizer.Normalize(testEnvironment) + + // log all normalized values + for _, envvar := range normalized { + t.Log(envvar) + } + + // validate fields + // - common + common.AssertThatEnvEquals(t, normalized, "NCI", "true") + common.AssertThatEnvEquals(t, normalized, "NCI_VERSION", normalizer.version) + common.AssertThatEnvEquals(t, normalized, "NCI_SERVICE_NAME", normalizer.name) + common.AssertThatEnvEquals(t, normalized, "NCI_SERVICE_SLUG", normalizer.slug) + // - server + common.AssertThatEnvEquals(t, normalized, "NCI_SERVER_NAME", "GitHub") + common.AssertThatEnvEquals(t, normalized, "NCI_SERVER_HOST", "github.com") + common.AssertThatEnvEquals(t, normalized, "NCI_SERVER_VERSION", "") + // - worker + common.AssertThatEnvEquals(t, normalized, "NCI_WORKER_ID", "5") + common.AssertThatEnvEquals(t, normalized, "NCI_WORKER_NAME", "fv-az679") + common.AssertThatEnvEquals(t, normalized, "NCI_WORKER_VERSION", "2.155.1") + common.AssertThatEnvEquals(t, normalized, "NCI_WORKER_ARCH", "linux/amd64") + // - pipeline + common.AssertThatEnvEquals(t, normalized, "NCI_PIPELINE_TRIGGER", "push") + common.AssertThatEnvEquals(t, normalized, "NCI_PIPELINE_STAGE_NAME", "build") + common.AssertThatEnvEquals(t, normalized, "NCI_PIPELINE_STAGE_SLUG", "build") + common.AssertThatEnvEquals(t, normalized, "NCI_PIPELINE_JOB_NAME", "build") + common.AssertThatEnvEquals(t, normalized, "NCI_PIPELINE_JOB_SLUG", "build") + // - project + common.AssertThatEnvEquals(t, normalized, "NCI_PROJECT_ID", "d1b384a8-f33f-427d-86fd-f021826a54ea") + common.AssertThatEnvEquals(t, normalized, "NCI_PROJECT_NAME", "azure-test") + common.AssertThatEnvEquals(t, normalized, "NCI_PROJECT_SLUG", "azure-test") +} diff --git a/pkg/common/common.go b/pkg/common/common.go new file mode 100644 index 0000000..592cc81 --- /dev/null +++ b/pkg/common/common.go @@ -0,0 +1,93 @@ +package common + +import ( + "net/url" + "os" + "strings" + "testing" + + log "github.com/sirupsen/logrus" +) + +// IsEnvironmentSet checks if the provided env variables are setting a value +func IsEnvironmentSet(env []string, key string) bool { + for _, envvar := range env { + if strings.HasPrefix(envvar, key+"=") { + return true + } + } + + return false +} + +// IsEnvironmentSetTo checks if the provided env variables are setting a value +func IsEnvironmentSetTo(env []string, key string, value string) bool { + for _, envvar := range env { + z := strings.SplitN(envvar, "=", 2) + if strings.ToLower(key) == strings.ToLower(z[0]) && strings.ToLower(value) == strings.ToLower(z[1]) { + return true + } + } + + return false +} + +// GetEnvironment gets the value of a environment property +func GetEnvironment(env []string, key string) string { + for _, envvar := range env { + z := strings.SplitN(envvar, "=", 2) + if key == z[0] { + return z[1] + } + + } + + return "" +} + +// GetSlug turns the provided value into a slug +func GetSlug(value string) string { + slug := value + + // ToLower + slug = strings.ToLower(slug) + + // Replace + slug = strings.Replace(slug, "_", "", -1) + slug = strings.Replace(slug, "/", "-", -1) + + return slug + // everything except 0-9 and a-z replaced with -. No leading / trailing -. +} + +// AssertThatEnvEquals is a helper function that asserts that a env key has a specific value +func AssertThatEnvEquals(t *testing.T, env []string, key string, value string) { + if IsEnvironmentSetTo(env, key, value) == false { + t.Errorf(key + " should be " + value) + } +} + +// CheckForError checks if a error happend and logs it, and ends the process +func CheckForError(err error) { + if err != nil { + panic(err) + } +} + +// GetHostFromURL gets the host from a url +func GetHostFromURL(addr string) string { + u, err := url.Parse(addr) + if err != nil { + log.Fatal(err) + panic(err) + } + + return u.Host +} + +// SetupTestLogger prepares the logger for test execution +func SetupTestLogger() { + // Logging + log.SetOutput(os.Stdout) + log.SetLevel(log.DebugLevel) +} diff --git a/pkg/common/common_test.go b/pkg/common/common_test.go new file mode 100644 index 0000000..fedd892 --- /dev/null +++ b/pkg/common/common_test.go @@ -0,0 +1,19 @@ +package common + +import ( + "testing" + "os" +) + +func TestMain(m *testing.M) { + SetupTestLogger() + code := m.Run() + os.Exit(code) +} + +func TestGetHostFromURL(t *testing.T) { + host := GetHostFromURL("http://github.com/user/repository") + if host != "github.com" { + t.Errorf("Host should be github.com, not "+host+"!") + } +} \ No newline at end of file diff --git a/pkg/common/git.go b/pkg/common/git.go new file mode 100644 index 0000000..5e9a182 --- /dev/null +++ b/pkg/common/git.go @@ -0,0 +1,114 @@ +package common + +import ( + "bufio" + "io" + "os" + "path/filepath" + "regexp" + "strings" + + log "github.com/sirupsen/logrus" + "gopkg.in/src-d/go-git.v4" + "gopkg.in/src-d/go-git.v4/plumbing/object" +) + +// GetGitDirectory finds the first git directory from the current working directory upwards +func GetGitDirectory() string { + currentDirectory, _ := os.Getwd() + var projectDirectory = "" + directoryParts := strings.Split(currentDirectory, string(os.PathSeparator)) + + for projectDirectory == "" { + if _, err := os.Stat(filepath.Join(currentDirectory, "/.git")); err == nil { + return currentDirectory + } + + if directoryParts[0]+"\\" == currentDirectory || currentDirectory == "/" { + return "" + } + + currentDirectory = filepath.Dir(currentDirectory) + } + + return "" +} + +// GetSCMArguments gets the common git values +func GetSCMArguments(projectDir string) []string { + var info []string + + // open repository from local path + log.Debug("Using git repository at " + projectDir) + repository, _ := git.PlainOpen(projectDir) + remote, remoteErr := repository.Remote("origin") + ref, _ := repository.Head() + + // repository kind and remote + info = append(info, "NCI_REPOSITORY_KIND=git") + if remoteErr == nil { + info = append(info, "NCI_REPOSITORY_REMOTE="+remote.Config().URLs[0]) + } else { + info = append(info, "NCI_REPOSITORY_REMOTE=local") + } + + // pass + if strings.HasPrefix(ref.Name().String(), "refs/heads/") { + // branch + info = append(info, "NCI_COMMIT_REF_TYPE=branch") + info = append(info, "NCI_COMMIT_REF_NAME="+strings.TrimLeft(ref.Name().String(), "refs/heads/")) + info = append(info, "NCI_COMMIT_REF_SLUG="+GetSlug(strings.TrimLeft(ref.Name().String(), "refs/heads/"))) + } else if ref.Name().String() == "HEAD" { + // detached HEAD, look into the reflog to determinate the true branch + gitRefLogFile := projectDir + "/.git/logs/HEAD" + lastLine := readLastLine(gitRefLogFile) + log.Debug(lastLine) + + pattern := regexp.MustCompile(`.*checkout: moving from (?P.*) to (?P.*)$`) + match := pattern.FindStringSubmatch(lastLine) + log.Debug("Found a reflog entry showing that there was a checkout based on " + match[1] + " to " + match[2]) + + info = append(info, "NCI_COMMIT_REF_TYPE=tag") + info = append(info, "NCI_COMMIT_REF_NAME="+match[2]) + info = append(info, "NCI_COMMIT_REF_SLUG="+GetSlug(match[2])) + } else { + panic("Unsupported!") + } + + info = append(info, "NCI_COMMIT_SHA="+ref.Hash().String()) + info = append(info, "NCI_COMMIT_SHA_SHORT="+ref.Hash().String()[0:8]) + + cIter, _ := repository.Log(&git.LogOptions{From: ref.Hash()}) + cIter.ForEach(func(commit *object.Commit) error { + commitinfo := strings.Split(commit.Message, "\n") + + info = append(info, "NCI_COMMIT_TITLE="+commitinfo[0]) + info = append(info, "NCI_COMMIT_DESCRIPTION="+strings.Join(commitinfo[2:], "\n")) + + return nil + }) + + return info +} + +// readLastLine gets the last line from a file +func readLastLine(filename string) string { + file, err := os.Open(filename) + if err != nil { + panic(err) + } + + defer file.Close() + reader := bufio.NewReader(file) + + lastLine := "" + for { + line, _, err := reader.ReadLine() + if err == io.EOF { + break + } + lastLine = string(line) + } + + return lastLine +} diff --git a/pkg/githubactions/README.md b/pkg/githubactions/README.md new file mode 100644 index 0000000..ff715c3 --- /dev/null +++ b/pkg/githubactions/README.md @@ -0,0 +1,69 @@ +# GitHub Actions + +## sources + +- [Default Variables](https://help.github.com/en/articles/virtual-environments-for-github-actions#default-environment-variables) +- [Predefined variables](https://developer.github.com/actions/creating-github-actions/accessing-the-runtime-environment/#environment-variables) + +## detection + +GitHub Actions are detected by the presence of `GITHUB_ACTIONS`. + +## resources + +- [Run locally](https://github.com/nektos/act) + +## example variables + +```bash +LEIN_HOME=/usr/local/lib/lein +M2_HOME=/usr/share/apache-maven-3.6.1 +BOOST_ROOT=/usr/local/share/boost/1.69.0 +GOROOT_1_11_X64=/usr/local/go1.11 +ANDROID_HOME=/usr/local/lib/android/sdk +JAVA_HOME_11_X64=/usr/lib/jvm/zulu-11-azure-amd64 +LANG=C.UTF-8 +INVOCATION_ID=c5ef6287439c412aa6edcf0dc9dcd685 +JAVA_HOME_12_X64=/usr/lib/jvm/zulu-12-azure-amd64 +ANDROID_SDK_ROOT=/usr/local/lib/android/sdk +RUNNER_TOOL_CACHE=/opt/hostedtoolcache +JAVA_HOME=/usr/lib/jvm/zulu-8-azure-amd64 +RUNNER_TRACKING_ID=github_a9744cbc-8514-42e9-ac4a-4fdcad31014c +GITHUB_ACTIONS=true +DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 +USER=runner +GITHUB_HEAD_REF= +GITHUB_ACTOR=PhilippHeuer +GITHUB_ACTION=run +GRADLE_HOME=/usr/share/gradle +PWD=/home/runner/work/normalize.ci/normalize.ci +HOME=/home/runner +GOROOT=/usr/local/go1.12 +JOURNAL_STREAM=9:27802 +JAVA_HOME_8_X64=/usr/lib/jvm/zulu-8-azure-amd64 +RUNNER_TEMP=/home/runner/work/_temp +CONDA=/usr/share/miniconda +BOOST_ROOT_1_69_0=/usr/local/share/boost/1.69.0 +RUNNER_WORKSPACE=/home/runner/work/normalize.ci +GITHUB_REF=refs/heads/master +GITHUB_SHA=dfe4a05873251d35f5fc4771106074a932751be1 +GOROOT_1_12_X64=/usr/local/go1.12 +DEPLOYMENT_BASEPATH=/opt/runner +GITHUB_EVENT_PATH=/home/runner/work/_temp/_github_workflow/event.json +RUNNER_OS=Linux +GITHUB_BASE_REF= +VCPKG_INSTALLATION_ROOT=/usr/local/share/vcpkg +PERFLOG_LOCATION_SETTING=RUNNER_PERFLOG +JAVA_HOME_7_X64=/usr/lib/jvm/zulu-7-azure-amd64 +RUNNER_USER=runner +SHLVL=1 +GITHUB_REPOSITORY=PhilippHeuer/normalize.ci +GITHUB_EVENT_NAME=push +LEIN_JAR=/usr/local/lib/lein/self-installs/leiningen-2.9.1-standalone.jar +RUNNER_PERFLOG=/home/runner/perflog +GITHUB_WORKFLOW=CI +ANT_HOME=/usr/share/ant +PATH=/usr/share/rust/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin +GITHUB_WORKSPACE=/home/runner/work/normalize.ci/normalize.ci +CHROME_BIN=/usr/bin/google-chrome +``` diff --git a/pkg/githubactions/githubactions.go b/pkg/githubactions/githubactions.go new file mode 100644 index 0000000..48dd12b --- /dev/null +++ b/pkg/githubactions/githubactions.go @@ -0,0 +1,90 @@ +package githubactions + +import ( + "runtime" + + "github.com/PhilippHeuer/normalize-ci/pkg/common" +) + +// Normalizer is the implementation of the normalizer +type Normalizer struct { + version string + name string + slug string +} + +// GetName returns the name of the normalizer +func (n Normalizer) GetName() string { + return n.name +} + +// Check validates that the current environment is gitlab ci +func (n Normalizer) Check(env []string) bool { + if common.IsEnvironmentSetTo(env, "GITHUB_ACTIONS", "true") { + return true + } + + return false +} + +// Normalize normalizes the environment variables into the common format +func (n Normalizer) Normalize(env []string) []string { + var normalized []string + + // common + normalized = append(normalized, "NCI=true") + normalized = append(normalized, "NCI_VERSION="+n.version) + normalized = append(normalized, "NCI_SERVICE_NAME="+n.name) + normalized = append(normalized, "NCI_SERVICE_SLUG="+n.slug) + + // server + normalized = append(normalized, "NCI_SERVER_NAME=GitHub") + normalized = append(normalized, "NCI_SERVER_HOST=github.com") + normalized = append(normalized, "NCI_SERVER_VERSION=") + + // worker + normalized = append(normalized, "NCI_WORKER_ID="+common.GetEnvironment(env, "RUNNER_TRACKING_ID")) + normalized = append(normalized, "NCI_WORKER_NAME="+common.GetEnvironment(env, "RUNNER_TRACKING_ID")) + normalized = append(normalized, "NCI_WORKER_VERSION="+common.GetEnvironment(env, "ImageVersion")) + normalized = append(normalized, "NCI_WORKER_ARCH="+runtime.GOOS+"/"+runtime.GOARCH) + + // pipeline + pipelineEvent := common.GetEnvironment(env, "GITHUB_EVENT_NAME") + switch pipelineEvent { + case "push": + normalized = append(normalized, "NCI_PIPELINE_TRIGGER=push") + case "pull_request": + normalized = append(normalized, "NCI_PIPELINE_TRIGGER=pull_request") + default: + normalized = append(normalized, "NCI_PIPELINE_TRIGGER=unknown") + } + if common.GetEnvironment(normalized, "NCI_PIPELINE_TRIGGER") == "pull_request" { + // PR + normalized = append(normalized, "NCI_PIPELINE_PULL_REQUEST_ID=unknown") // not supported by GH yet. + } + normalized = append(normalized, "NCI_PIPELINE_STAGE_NAME="+common.GetEnvironment(env, "GITHUB_WORKFLOW")) + normalized = append(normalized, "NCI_PIPELINE_STAGE_SLUG="+common.GetSlug(common.GetEnvironment(env, "GITHUB_WORKFLOW"))) + normalized = append(normalized, "NCI_PIPELINE_JOB_NAME="+common.GetEnvironment(env, "GITHUB_ACTION")) + normalized = append(normalized, "NCI_PIPELINE_JOB_SLUG="+common.GetSlug(common.GetEnvironment(env, "GITHUB_ACTION"))) + + // project + normalized = append(normalized, "NCI_PROJECT_ID="+common.GetSlug(common.GetEnvironment(env, "GITHUB_REPOSITORY"))) + normalized = append(normalized, "NCI_PROJECT_NAME="+common.GetEnvironment(env, "GITHUB_REPOSITORY")) + normalized = append(normalized, "NCI_PROJECT_SLUG="+common.GetSlug(common.GetEnvironment(env, "GITHUB_REPOSITORY"))) + + // repository + normalized = append(normalized, common.GetSCMArguments(common.GetGitDirectory())...) + + return normalized +} + +// NewNormalizer gets a instance of the normalizer +func NewNormalizer() Normalizer { + entity := Normalizer{ + version: "0.1.0", + name: "GitHub Actions", + slug: "github-actions", + } + + return entity +} diff --git a/pkg/githubactions/githubactions_test.go b/pkg/githubactions/githubactions_test.go new file mode 100644 index 0000000..deabf0c --- /dev/null +++ b/pkg/githubactions/githubactions_test.go @@ -0,0 +1,111 @@ +package githubactions + +import ( + "os" + "testing" + + "github.com/PhilippHeuer/normalize-ci/pkg/common" +) + +var testEnvironment = []string{ + "LEIN_HOME=/usr/local/lib/lein", + "M2_HOME=/usr/share/apache-maven-3.6.1", + "BOOST_ROOT=/usr/local/share/boost/1.69.0", + "GOROOT_1_11_X64=/usr/local/go1.11", + "ANDROID_HOME=/usr/local/lib/android/sdk", + "JAVA_HOME_11_X64=/usr/lib/jvm/zulu-11-azure-amd64", + "ImageVersion=157.1", + "LANG=C.UTF-8", + "INVOCATION_ID=f571ba9eda014d2b85ac026677077d76", + "JAVA_HOME_12_X64=/usr/lib/jvm/zulu-12-azure-amd64", + "ANDROID_SDK_ROOT=/usr/local/lib/android/sdk", + "RUNNER_TOOL_CACHE=/opt/hostedtoolcache", + "JAVA_HOME=/usr/lib/jvm/zulu-8-azure-amd64", + "RUNNER_TRACKING_ID=github_1f3d9475-6c94-40ee-a160-8b3fd282c3a1", + "GITHUB_ACTIONS=true", + "DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1", + "USER=runner", + "GITHUB_HEAD_REF=", + "GITHUB_ACTOR=PhilippHeuer", + "GITHUB_ACTION=run", + "GRADLE_HOME=/usr/share/gradle", + "PWD=/home/runner/work/normalize-ci/normalize-ci", + "HOME=/home/runner", + "GOROOT=/usr/local/go1.12", + "JOURNAL_STREAM=9:31931", + "JAVA_HOME_8_X64=/usr/lib/jvm/zulu-8-azure-amd64", + "RUNNER_TEMP=/home/runner/work/_temp", + "CONDA=/usr/share/miniconda", + "BOOST_ROOT_1_69_0=/usr/local/share/boost/1.69.0", + "RUNNER_WORKSPACE=/home/runner/work/normalize-ci", + "GITHUB_REF=refs/heads/master", + "GITHUB_SHA=abe7b23a948559a871f1158ec2db3caaef726854", + "GOROOT_1_12_X64=/usr/local/go1.12", + "DEPLOYMENT_BASEPATH=/opt/runner", + "GITHUB_EVENT_PATH=/home/runner/work/_temp/_github_workflow/event.json", + "RUNNER_OS=Linux", + "GITHUB_BASE_REF=", + "VCPKG_INSTALLATION_ROOT=/usr/local/share/vcpkg", + "PERFLOG_LOCATION_SETTING=RUNNER_PERFLOG", + "JAVA_HOME_7_X64=/usr/lib/jvm/zulu-7-azure-amd64", + "RUNNER_USER=runner", + "SHLVL=1", + "GITHUB_REPOSITORY=PhilippHeuer/normalize-ci", + "GITHUB_EVENT_NAME=push", + "LEIN_JAR=/usr/local/lib/lein/self-installs/leiningen-2.9.1-standalone.jar", + "RUNNER_PERFLOG=/home/runner/perflog", + "GITHUB_WORKFLOW=CI", + "ANT_HOME=/usr/share/ant", + "PATH=/usr/share/rust/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin", + "GITHUB_WORKSPACE=/home/runner/work/normalize-ci/normalize-ci", + "CHROME_BIN=/usr/bin/google-chrome", +} + +func TestMain(m *testing.M) { + common.SetupTestLogger() + code := m.Run() + os.Exit(code) +} + +func TestEnvironmentCheck(t *testing.T) { + var normalizer = NewNormalizer() + if normalizer.Check(testEnvironment) != true { + t.Errorf("Check should succeed with the provided sample data!") + } +} + +func TestEnvironmentNormalizer(t *testing.T) { + var normalizer = NewNormalizer() + var normalized = normalizer.Normalize(testEnvironment) + + // log all normalized values + for _, envvar := range normalized { + t.Log(envvar) + } + + // validate fields + // - common + common.AssertThatEnvEquals(t, normalized, "NCI", "true") + common.AssertThatEnvEquals(t, normalized, "NCI_VERSION", normalizer.version) + common.AssertThatEnvEquals(t, normalized, "NCI_SERVICE_NAME", normalizer.name) + common.AssertThatEnvEquals(t, normalized, "NCI_SERVICE_SLUG", normalizer.slug) + // - server + common.AssertThatEnvEquals(t, normalized, "NCI_SERVER_NAME", "GitHub") + common.AssertThatEnvEquals(t, normalized, "NCI_SERVER_HOST", "github.com") + common.AssertThatEnvEquals(t, normalized, "NCI_SERVER_VERSION", "") + // - worker + common.AssertThatEnvEquals(t, normalized, "NCI_WORKER_ID", "github_1f3d9475-6c94-40ee-a160-8b3fd282c3a1") + common.AssertThatEnvEquals(t, normalized, "NCI_WORKER_NAME", "github_1f3d9475-6c94-40ee-a160-8b3fd282c3a1") + common.AssertThatEnvEquals(t, normalized, "NCI_WORKER_VERSION", "157.1") + common.AssertThatEnvEquals(t, normalized, "NCI_WORKER_ARCH", "linux/amd64") + // - pipeline + common.AssertThatEnvEquals(t, normalized, "NCI_PIPELINE_TRIGGER", "push") + common.AssertThatEnvEquals(t, normalized, "NCI_PIPELINE_STAGE_NAME", "CI") + common.AssertThatEnvEquals(t, normalized, "NCI_PIPELINE_STAGE_SLUG", "ci") + common.AssertThatEnvEquals(t, normalized, "NCI_PIPELINE_JOB_NAME", "run") + common.AssertThatEnvEquals(t, normalized, "NCI_PIPELINE_JOB_SLUG", "run") + // - project + common.AssertThatEnvEquals(t, normalized, "NCI_PROJECT_ID", "philippheuer-normalize-ci") + common.AssertThatEnvEquals(t, normalized, "NCI_PROJECT_NAME", "PhilippHeuer/normalize-ci") + common.AssertThatEnvEquals(t, normalized, "NCI_PROJECT_SLUG", "philippheuer-normalize-ci") +} diff --git a/pkg/gitlabci/README.md b/pkg/gitlabci/README.md new file mode 100644 index 0000000..9f5b6e8 --- /dev/null +++ b/pkg/gitlabci/README.md @@ -0,0 +1,9 @@ +# GitLab CI/CD + +## sources + +- [Predefined variables](https://docs.gitlab.com/ce/ci/variables/predefined_variables.html) + +## detection + +By checking for the presence of the environment variable `GITLAB_CI`. diff --git a/pkg/gitlabci/gitlabci.go b/pkg/gitlabci/gitlabci.go new file mode 100644 index 0000000..3b2de1e --- /dev/null +++ b/pkg/gitlabci/gitlabci.go @@ -0,0 +1,82 @@ +package gitlabci + +import ( + "runtime" + + "github.com/PhilippHeuer/normalize-ci/pkg/common" +) + +// Normalizer is the implementation of the normalizer +type Normalizer struct { + version string + name string + slug string +} + +// GetName returns the name of the normalizer +func (n Normalizer) GetName() string { + return n.name +} + +// Check validates that the current environment is gitlab ci +func (n Normalizer) Check(env []string) bool { + if common.IsEnvironmentSetTo(env, "GITLAB_CI", "true") { + return true + } + + return false +} + +// Normalize normalizes the environment variables into the common format +func (n Normalizer) Normalize(env []string) []string { + var normalized []string + + // common + normalized = append(normalized, "NCI=true") + normalized = append(normalized, "NCI_VERSION="+n.version) + normalized = append(normalized, "NCI_SERVICE_NAME="+n.name) + normalized = append(normalized, "NCI_SERVICE_SLUG="+n.slug) + + // server + normalized = append(normalized, "NCI_SERVER_NAME="+common.GetEnvironment(env, "CI_SERVER_NAME")) + normalized = append(normalized, "NCI_SERVER_HOST="+common.GetEnvironment(env, "CI_SERVER_HOST")) + normalized = append(normalized, "NCI_SERVER_VERSION="+common.GetEnvironment(env, "CI_SERVER_VERSION")) + + // worker + normalized = append(normalized, "NCI_WORKER_ID="+common.GetEnvironment(env, "CI_RUNNER_ID")) + normalized = append(normalized, "NCI_WORKER_NAME="+common.GetEnvironment(env, "CI_RUNNER_DESCRIPTION")) + normalized = append(normalized, "NCI_WORKER_VERSION="+common.GetEnvironment(env, "CI_RUNNER_VERSION")) + normalized = append(normalized, "NCI_WORKER_ARCH="+runtime.GOOS+"/"+runtime.GOARCH) + + // pipeline + normalized = append(normalized, "NCI_PIPELINE_TRIGGER="+common.GetEnvironment(env, "CI_PIPELINE_SOURCE")) + if common.GetEnvironment(normalized, "NCI_PIPELINE_TRIGGER") == "pull_request" { + // PR + normalized = append(normalized, "NCI_PIPELINE_PULL_REQUEST_ID="+common.GetEnvironment(env, "CI_MERGE_REQUEST_IID")) + } + normalized = append(normalized, "NCI_PIPELINE_STAGE_NAME="+common.GetEnvironment(env, "CI_JOB_STAGE")) + normalized = append(normalized, "NCI_PIPELINE_STAGE_SLUG="+common.GetSlug(common.GetEnvironment(env, "CI_JOB_STAGE"))) + normalized = append(normalized, "NCI_PIPELINE_JOB_NAME="+common.GetEnvironment(env, "CI_JOB_NAME")) + normalized = append(normalized, "NCI_PIPELINE_JOB_SLUG="+common.GetSlug(common.GetEnvironment(env, "CI_JOB_NAME"))) + + // project + normalized = append(normalized, "NCI_PROJECT_ID="+common.GetEnvironment(env, "CI_PROJECT_ID")) + normalized = append(normalized, "NCI_PROJECT_NAME="+common.GetEnvironment(env, "CI_PROJECT_NAME")) + normalized = append(normalized, "NCI_PROJECT_SLUG="+common.GetSlug(common.GetEnvironment(env, "CI_PROJECT_PATH"))) + + // repository + normalized = append(normalized, common.GetSCMArguments(common.GetGitDirectory())...) + + return normalized +} + +// NewNormalizer gets a instance of the normalizer +func NewNormalizer() Normalizer { + entity := Normalizer{ + version: "0.1.0", + name: "GitLab CI", + slug: "gitlab-ci", + } + + return entity +} diff --git a/pkg/gitlabci/gitlabci_test.go b/pkg/gitlabci/gitlabci_test.go new file mode 100644 index 0000000..a07c0d2 --- /dev/null +++ b/pkg/gitlabci/gitlabci_test.go @@ -0,0 +1,126 @@ +package gitlabci + +import ( + "testing" + + "github.com/PhilippHeuer/normalize-ci/pkg/common" +) + +var testEnvironment = []string{ + "FF_CMD_DISABLE_DELAYED_ERROR_LEVEL_EXPANSION=false", + "FF_USE_LEGACY_BUILDS_DIR_FOR_DOCKER=false", + "FF_USE_LEGACY_VOLUMES_MOUNTING_ORDER=false", + "CI_BUILDS_DIR=/builds", + "CI_PROJECT_DIR=/builds/PhilippHeuer/citest", + "CI_CONCURRENT_ID=18", + "CI_CONCURRENT_PROJECT_ID=0", + "CI_SERVER=yes", + "CI_PIPELINE_ID=77135359", + "CI_PIPELINE_URL=https://gitlab.com/PhilippHeuer/citest/pipelines/77135359", + "CI_JOB_ID=275323310", + "CI_JOB_URL=https://gitlab.com/PhilippHeuer/citest/-/jobs/275323310", + "CI_JOB_TOKEN=secret", + "CI_BUILD_ID=275323310", + "CI_BUILD_TOKEN=secret", + "CI_REGISTRY_USER=gitlab-ci-token", + "CI_REGISTRY_PASSWORD=secret", + "CI_REPOSITORY_URL=https://gitlab-ci-token:[MASKED]@gitlab.com/PhilippHeuer/citest.git", + "CI=true", + "GITLAB_CI=true", + "GITLAB_FEATURES=audit_events,burndown_charts,code_owners,contribution_analytics,elastic_search,export_issues,group_bulk_edit,group_burndown_charts,group_webhooks,issuable_default_templates,issue_board_focus_mode,issue_weights,jenkins_integration,ldap_group_sync,member_lock,merge_request_approvers,multiple_ldap_servers,multiple_issue_assignees,multiple_merge_request_assignees,push_rules,protected_refs_for_users,related_issues,repository_mirrors,repository_size_limit,scoped_issue_board,usage_quotas,visual_review_app,admin_audit_log,auditor_user,blocking_merge_requests,board_assignee_lists,board_milestone_lists,cross_project_pipelines,custom_file_templates,custom_file_templates_for_namespace,email_additional_text,db_load_balancing,deploy_board,extended_audit_events,file_locks,geo,github_project_service_integration,jira_dev_panel_integration,scoped_labels,ldap_group_sync_filter,multiple_clusters,multiple_group_issue_boards,multiple_approval_rules,merge_request_performance_metrics,object_storage,group_saml,service_desk,smartcard_auth,unprotection_restrictions,reject_unsigned_commits,commit_committer_check,external_authorization_service_api_management,ci_cd_projects,default_project_deletion_protection,protected_environments,custom_project_templates,packages,code_owner_approval_required,feature_flags,batch_comments,issues_analytics,merge_pipelines,merge_trains,design_management,operations_dashboard,dependency_proxy,metrics_reports,custom_prometheus_metrics,required_ci_templates,project_aliases,cycle_analytics_for_groups,security_dashboard,dependency_scanning,dependency_list,license_management,sast,container_scanning,cluster_health,dast,epics,pod_logs,pseudonymizer,prometheus_alerts,tracing,insights,web_ide_terminal,incident_management,report_approver_rules,group_ip_restriction", + "CI_SERVER_HOST=gitlab.com", + "CI_SERVER_NAME=GitLab", + "CI_SERVER_VERSION=12.2.0-pre", + "CI_SERVER_VERSION_MAJOR=12", + "CI_SERVER_VERSION_MINOR=2", + "CI_SERVER_VERSION_PATCH=0", + "CI_SERVER_REVISION=36c4e152270", + "CI_JOB_NAME=build", + "CI_JOB_STAGE=build", + "CI_COMMIT_SHA=c2cc4ef1015fce6cd4ba80ca9f43077e8bbc843c", + "CI_COMMIT_SHORT_SHA=c2cc4ef1", + "CI_COMMIT_BEFORE_SHA=a9a3893b44a4e5d2292926e15ff1dc39200beae8", + "CI_COMMIT_REF_NAME=master", + "CI_COMMIT_REF_SLUG=master", + "CI_NODE_TOTAL=1", + "CI_BUILD_REF=c2cc4ef1015fce6cd4ba80ca9f43077e8bbc843c", + "CI_BUILD_BEFORE_SHA=a9a3893b44a4e5d2292926e15ff1dc39200beae8", + "CI_BUILD_REF_NAME=master", + "CI_BUILD_REF_SLUG=master", + "CI_BUILD_NAME=build", + "CI_BUILD_STAGE=build", + "CI_PROJECT_ID=13882743", + "CI_PROJECT_NAME=citest", + "CI_PROJECT_PATH=PhilippHeuer/citest", + "CI_PROJECT_PATH_SLUG=philippheuer-citest", + "CI_PROJECT_NAMESPACE=PhilippHeuer", + "CI_PROJECT_URL=https://gitlab.com/PhilippHeuer/citest", + "CI_PROJECT_VISIBILITY=public", + "CI_PAGES_DOMAIN=gitlab.io", + "CI_PAGES_URL=https://philippheuer.gitlab.io/citest", + "CI_REGISTRY=registry.gitlab.com", + "CI_REGISTRY_IMAGE=registry.gitlab.com/philippheuer/citest", + "CI_API_V4_URL=https://gitlab.com/api/v4", + "CI_PIPELINE_IID=5", + "CI_CONFIG_PATH=.gitlab-ci.yml", + "CI_PIPELINE_SOURCE=push", + "CI_COMMIT_MESSAGE=Update .gitlab-ci.yml", + "CI_COMMIT_TITLE=Update .gitlab-ci.yml", + "CI_COMMIT_DESCRIPTION=", + "CI_COMMIT_REF_PROTECTED=true", + "CI_RUNNER_ID=380987", + "CI_RUNNER_DESCRIPTION=shared-runners-manager-6.gitlab.com", + "CI_RUNNER_TAGS=docker, gce", + "GITLAB_USER_ID=1193975", + "GITLAB_USER_EMAIL=git@philippheuer.me", + "GITLAB_USER_LOGIN=PhilippHeuer", + "GITLAB_USER_NAME=Philipp Heuer", + "CI_DISPOSABLE_ENVIRONMENT=true", + "CI_RUNNER_VERSION=12.1.0", + "CI_RUNNER_REVISION=de7731dd", + "CI_RUNNER_EXECUTABLE_ARCH=linux/amd64", + "GIT_LFS_SKIP_SMUDGE=1", +} + +func TestEnvironmentCheck(t *testing.T) { + var normalizer = NewNormalizer() + if normalizer.Check(testEnvironment) != true { + t.Errorf("Check should succeed with the provided gitlab ci sample data!") + } +} + +func TestEnvironmentNormalizer(t *testing.T) { + var normalizer = NewNormalizer() + var normalized = normalizer.Normalize(testEnvironment) + + // log all normalized values + for _, envvar := range normalized { + t.Log(envvar) + } + + // validate fields + // - common + common.AssertThatEnvEquals(t, normalized, "NCI", "true") + common.AssertThatEnvEquals(t, normalized, "NCI_VERSION", normalizer.version) + common.AssertThatEnvEquals(t, normalized, "NCI_SERVICE_NAME", normalizer.name) + common.AssertThatEnvEquals(t, normalized, "NCI_SERVICE_SLUG", normalizer.slug) + // - server + common.AssertThatEnvEquals(t, normalized, "NCI_SERVER_NAME", "GitLab") + common.AssertThatEnvEquals(t, normalized, "NCI_SERVER_HOST", "gitlab.com") + common.AssertThatEnvEquals(t, normalized, "NCI_SERVER_VERSION", "12.2.0-pre") + // - worker + common.AssertThatEnvEquals(t, normalized, "NCI_WORKER_ID", "380987") + common.AssertThatEnvEquals(t, normalized, "NCI_WORKER_NAME", "shared-runners-manager-6.gitlab.com") + common.AssertThatEnvEquals(t, normalized, "NCI_WORKER_VERSION", "12.1.0") + common.AssertThatEnvEquals(t, normalized, "NCI_WORKER_ARCH", "linux/amd64") + // - pipeline + common.AssertThatEnvEquals(t, normalized, "NCI_PIPELINE_TRIGGER", "push") + common.AssertThatEnvEquals(t, normalized, "NCI_PIPELINE_STAGE_NAME", "build") + common.AssertThatEnvEquals(t, normalized, "NCI_PIPELINE_STAGE_SLUG", "build") + common.AssertThatEnvEquals(t, normalized, "NCI_PIPELINE_JOB_NAME", "build") + common.AssertThatEnvEquals(t, normalized, "NCI_PIPELINE_JOB_SLUG", "build") + // - project + common.AssertThatEnvEquals(t, normalized, "NCI_PROJECT_ID", "13882743") + common.AssertThatEnvEquals(t, normalized, "NCI_PROJECT_NAME", "citest") + common.AssertThatEnvEquals(t, normalized, "NCI_PROJECT_SLUG", "philippheuer-citest") +} diff --git a/src/app.go b/src/app.go new file mode 100644 index 0000000..5d4edd0 --- /dev/null +++ b/src/app.go @@ -0,0 +1,73 @@ +package main + +import ( + "fmt" + "io" + "os" + "strings" + + log "github.com/sirupsen/logrus" + + "github.com/PhilippHeuer/normalize-ci/pkg/common" + + "github.com/PhilippHeuer/normalize-ci/pkg/azuredevops" + "github.com/PhilippHeuer/normalize-ci/pkg/githubactions" + "github.com/PhilippHeuer/normalize-ci/pkg/gitlabci" +) + +// App Properties +var Version string + +// Init Hook +func init() { + // Logging + log.SetOutput(os.Stdout) + log.SetLevel(log.WarnLevel) +} + +// CLI Main Entrypoint +func main() { + // detect debug mode + debugValue, debugIsSet := os.LookupEnv("DEBUG") + if debugIsSet && strings.ToLower(debugValue) == "true" { + log.SetLevel(log.TraceLevel) + } + + // initialize normalizers + var normalizers []Normalizer + normalizers = append(normalizers, azuredevops.NewNormalizer()) + normalizers = append(normalizers, githubactions.NewNormalizer()) + normalizers = append(normalizers, gitlabci.NewNormalizer()) + + // get all environment variables + var env []string // current environment + for _, entry := range os.Environ() { + env = append(env, entry) + log.Debug("ENV: ", entry) + } + + // normalize (iterate over all supported systems and normalize variables if possible) + var normalized []string + for _, normalizer := range normalizers { + if normalizer.Check(env) == true { + log.Debug("Matched " + normalizer.GetName() + ", not checking for any other matches.") + normalized = normalizer.Normalize(env) + break + } else { + log.Debug("Didn't match in " + normalizer.GetName()) + } + } + + // set normalized variables in current session + for _, entry := range normalized { + entrySplit := strings.SplitN(entry, "=", 2) + log.Debug("Setting var in current session: " + entry) + + err := os.Setenv(entrySplit[0], entrySplit[1]) + common.CheckForError(err) + + // print via stdout + s := fmt.Sprintf("export %s=%s\n", entrySplit[0], entrySplit[1]) + io.WriteString(os.Stdout, s) // Ignoring error for simplicity. + } +} diff --git a/src/app_test.go b/src/app_test.go new file mode 100644 index 0000000..ded683f --- /dev/null +++ b/src/app_test.go @@ -0,0 +1,9 @@ +package main + +import ( + "testing" +) + +func TestEnvironmentCheck(t *testing.T) { + // this is a fake test, since there are no tests for /src yet +} diff --git a/src/common.go b/src/common.go new file mode 100644 index 0000000..4c5e5cb --- /dev/null +++ b/src/common.go @@ -0,0 +1,8 @@ +package main + +// Normalizer is a common interface to work with all normalizers +type Normalizer interface { + GetName() string + Check(env []string) bool + Normalize(env []string) []string +}