From 6b1b32a22eaea6319fe78f4144a9d2622841f1f1 Mon Sep 17 00:00:00 2001 From: Timothe Date: Wed, 22 Jun 2022 09:25:37 +0200 Subject: [PATCH 1/4] first commit (adding glab pipe gen and creating artifact function) --- .../azure-devops/pipeline_generator.sh | 13 ++ .../pipelines/common/pipeline_generator.lib | 22 +-- .../pipelines/github/pipeline_generator.sh | 17 +- .../pipelines/gitlab/pipeline_generator.sh | 180 ++++++++++++++++++ 4 files changed, 211 insertions(+), 21 deletions(-) create mode 100644 scripts/pipelines/gitlab/pipeline_generator.sh diff --git a/scripts/pipelines/azure-devops/pipeline_generator.sh b/scripts/pipelines/azure-devops/pipeline_generator.sh index b680b6a2c..b9cfd03ec 100644 --- a/scripts/pipelines/azure-devops/pipeline_generator.sh +++ b/scripts/pipelines/azure-devops/pipeline_generator.sh @@ -54,6 +54,17 @@ function obtainHangarPath { hangarPath=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && cd ../../.. && pwd ) } +function addAdditionalArtifact { + # Check if an extra artifact to store is supplied. + if test ! -z "$artifactPath" + then + # Add the extra step to the YAML. + cat "${hangarPath}/${commonTemplatesPath}/store-extra-path.yml" >> "${localDirectory}/${pipelinePath}/${yamlFile}" + else + echo "The '-a' flag has not been set, skipping the step to add additional artifact." + fi +} + function createPipeline { echo -e "${green}Generating the pipeline from the YAML template..." echo -ne ${white} @@ -150,6 +161,8 @@ createNewBranch copyYAMLFile +addAdditionalArtifact + copyCommonScript type copyScript &> /dev/null && copyScript diff --git a/scripts/pipelines/common/pipeline_generator.lib b/scripts/pipelines/common/pipeline_generator.lib index 55fab26a0..9061d8152 100644 --- a/scripts/pipelines/common/pipeline_generator.lib +++ b/scripts/pipelines/common/pipeline_generator.lib @@ -107,6 +107,9 @@ function checkInstallations { elif ([ "$provider" == "azure-devops" ] && ! [ -x "$(command -v az)" ]); then echo -e "${red}Error: Azure CLI is not installed." >&2 exit 127 + elif ([ "$provider" == "gitlab" ] && ! [ -x "$(command -v glab)" ]); then + echo -e "${red}Error: Azure CLI is not installed." >&2 + exit 127 fi # Check if Python is installed @@ -136,25 +139,6 @@ function copyYAMLFile { # We cannot use a variable in the definition of resource in the pipeline so we have to use a placeholder to replace it with the value we need commonEnvSubstList='${buildPipelineName} ${testPipelineName} ${qualityPipelineName} ${pipelineName}' envsubst "${commonEnvSubstList} ${specificEnvSubstList}" < "${hangarPath}/${templatesPath}/${yamlFile}.template" > "${localDirectory}/${pipelinePath}/${yamlFile}" - - # Check if an extra artifact to store is supplied. - if test ! -z "$artifactPath" - then - # Add the extra step to the YAML. - if [ "$provider" == "azure-devops" ] - then - cat "${hangarPath}/${commonTemplatesPath}/store-extra-path.yml" >> "${localDirectory}/${pipelinePath}/${yamlFile}" - elif [ "$provider" == "github" ] - then - storeExtraPathContent="\n - name: Publish Additional Output Artifact\n uses: actions\/upload-artifact@v3\n with:\n name: additional-pipeline-output\n path: \"\${{ env.artifactPath }}\"" - sed -i "s/# mark to insert step for additonal artifact #/$storeExtraPathContent\n/" "${localDirectory}/${pipelinePath}/${yamlFile}" - fi - else - if [ "$provider" == "github" ] - then - sed -i '/# mark to insert step for additonal artifact #/d' "${localDirectory}/${pipelinePath}/${yamlFile}" - fi - fi } function copyCommonScript { diff --git a/scripts/pipelines/github/pipeline_generator.sh b/scripts/pipelines/github/pipeline_generator.sh index 274f0b753..f64c735e7 100644 --- a/scripts/pipelines/github/pipeline_generator.sh +++ b/scripts/pipelines/github/pipeline_generator.sh @@ -52,6 +52,19 @@ function obtainHangarPath { hangarPath=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && cd ../../.. && pwd ) } +function addAdditionalArtifact { + # Check if an extra artifact to store is supplied. + if test ! -z "$artifactPath" + then + # Add the extra step to the YAML. + storeExtraPathContent="\n - name: Publish Additional Output Artifact\n uses: actions\/upload-artifact@v3\n with:\n name: additional-pipeline-output\n path: \"\${{ env.artifactPath }}\"" + sed -i "s/# mark to insert step for additonal artifact #/$storeExtraPathContent\n/" "${localDirectory}/${pipelinePath}/${yamlFile}" + else + echo "The '-a' flag has not been set, skipping the step to add additional artifact." + sed -i '/# mark to insert step for additonal artifact #/d' "${localDirectory}/${pipelinePath}/${yamlFile}" + fi +} + # Function that adds the variables to be used in the pipeline. function addCommonPipelineVariables { if test -z "${artifactPath}" @@ -137,6 +150,8 @@ type addPipelineVariables &> /dev/null && addPipelineVariables copyYAMLFile +addAdditionalArtifact + copyCommonScript type copyScript &> /dev/null && copyScript @@ -148,6 +163,4 @@ commitCommonFiles type commitFiles &> /dev/null && commitFiles -# createPipeline - createPR diff --git a/scripts/pipelines/gitlab/pipeline_generator.sh b/scripts/pipelines/gitlab/pipeline_generator.sh new file mode 100644 index 000000000..0e90f3d58 --- /dev/null +++ b/scripts/pipelines/gitlab/pipeline_generator.sh @@ -0,0 +1,180 @@ +#!/bin/bash +set -e +FLAGS=$(getopt -a --options c:n:d:a:b:l:i:u:p:hw --long "config-file:,pipeline-name:,local-directory:,artifact-path:,target-branch:,language:,build-pipeline-name:,sonar-url:,sonar-token:,image-name:,registry-user:,registry-password:,resource-group:,storage-account:,storage-container:,cluster-name:,s3-bucket:,s3-key-path:,quality-pipeline-name:,dockerfile:,test-pipeline-name:,aws-access-key:,aws-secret-access-key:,aws-region:,help" -- "$@") + +eval set -- "$FLAGS" +while true; do + case "$1" in + -c | --config-file) configFile=$2; shift 2;; + -n | --pipeline-name) export pipelineName=$2; shift 2;; + -d | --local-directory) localDirectory=$2; shift 2;; + -a | --artifact-path) artifactPath=$2; shift 2;; + -b | --target-branch) targetBranch=$2; shift 2;; + -l | --language) language=$2; shift 2;; + --build-pipeline-name) export buildPipelineName=$2; shift 2;; + --sonar-url) sonarUrl=$2; shift 2;; + --sonar-token) sonarToken=$2; shift 2;; + -i | --image-name) imageName=$2; shift 2;; + -u | --registry-user) dockerUser=$2; shift 2;; + -p | --registry-password) dockerPassword=$2; shift 2;; + --resource-group) resourceGroupName=$2; shift 2;; + --storage-account) storageAccountName=$2; shift 2;; + --storage-container) storageContainerName=$2; shift 2;; + --cluster-name) clusterName=$2; shift 2;; + --s3-bucket) s3Bucket=$2; shift 2;; + --s3-key-path) s3KeyPath=$2; shift 2;; + --quality-pipeline-name) export qualityPipelineName=$2; shift 2;; + --test-pipeline-name) export testPipelineName=$2; shift 2;; + --dockerfile) dockerFile=$2; shift 2;; + --aws-access-key) awsAccessKey="$2"; shift 2;; + --aws-secret-access-key) awsSecretAccessKey="$2"; shift 2;; + --aws-region) awsRegion="$2"; shift 2;; + -h | --help) help="true"; shift 1;; + -w) webBrowser="true"; shift 1;; + --) shift; break;; + esac +done + +# Colours for the messages. +white='\e[1;37m' +green='\e[1;32m' +red='\e[0;31m' + +# Common var +commonTemplatesPath="scripts/pipelines/gitlab/templates/common" # Path for common files of the pipelines +pipelinePath=".pipelines" # Path to the pipelines. +scriptFilePath=".pipelines/scripts" # Path to the scripts. +gitlabCiFile=".gitlab-ci.yml" +export provider="gitlab" + +function obtainHangarPath { + + # This line goes to the script directory independent of wherever the user is and then jumps 3 directories back to get the path + hangarPath=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && cd ../../.. && pwd ) +} + +function addAdditionalArtifact { + # Check if an extra artifact to store is supplied. + if test ! -z "$artifactPath" + then + # Add the extra step to the YAML. + grep " artifacts:" "${localDirectory}/${pipelinePath}/${yamlFile}" && storeExtraPathContent=" - \"$artifactPath\"" + grep " artifacts:" "${localDirectory}/${pipelinePath}/${yamlFile}" || storeExtraPathContent="\n artifacts:\n - name: addtionalArtifactOutput\n paths:\n - \"$artifactPath\"" + sed -i "s/# mark to insert step for additonal artifact #/$storeExtraPathContent\n/" "${localDirectory}/${pipelinePath}/${yamlFile}" + else + echo "The '-a' flag has not been set, skipping the step to add additional artifact." + sed -i '/# mark to insert step for additonal artifact #/d' "${localDirectory}/${pipelinePath}/${yamlFile}" + fi +} + +# Function that adds the variables to be used in the pipeline. +function addCommonPipelineVariables { + if test -z "${artifactPath}" + then + echo "Skipping creation of the variable artifactPath as the flag has not been used." + # Delete the commentary to set the artifactPath input/var + sed -i '/# mark to insert additional artifact env var #/d' "${localDirectory}/${pipelinePath}/${yamlFile}" + else + # add the input for the additional artifact + grep "variables:" "${localDirectory}/${pipelinePath}/${yamlFile}" > /dev/null && textArtifactPathVar=" artifactPath: ${artifactPath//\//\\/}" + grep "variables:" "${localDirectory}/${pipelinePath}/${yamlFile}" > /dev/null || textArtifactPathVar="variables:\n artifactPath: \"${artifactPath//\//\\/}\"" + sed -i "s/# mark to insert additional artifact env var #/$textArtifactPathVar/" "${localDirectory}/${pipelinePath}/${yamlFile}" + fi +} + +function addCiFile { + echo -e "${green}Copying and commiting the gitlab ci file." + echo -ne ${white} + + cp "${hangarPath}/${commonTemplatesPath}/${gitlabCiFile}" "${localDirectory}/${gitlabCiFile}" + testCommit=$(git status) + if echo "$testCommit" | grep "nothing to commit, working tree clean" > /dev/null + then + echo "gilab-ci file already present with same content, nothing to commit." + else + git add "${gitlabCiFile}" -f + git commit -m "adding gitlab-ci.yml" + git push + fi +} + +function createPR { + # Check if a target branch is supplied. + if test -z "$targetBranch" + then + # No branch specified in the parameters, no Pull Request is created, the code will be stored in the current branch. + echo -e "${green}No branch specified to do the Pull Request, changes left in the ${sourceBranch} branch." + exit + else + echo -e "${green}Creating a Pull Request..." + echo -ne "${white}" + repoURL=$(git config --get remote.origin.url) + repoNameWithGit="${repoURL/https:\/\/github.com\/}" + repoName="${repoNameWithGit/.git}" + # Create the Pull Request to merge into the specified branch. + #debug + echo "glab mr create -b \"$targetBranch\" -d \"merge request $sourceBranch\" -s \"$sourceBranch\" -H \"${repoName}\" -t \"merge $sourceBranch\"" + pr=$(glab mr create -b "$targetBranch" -d "merge request $sourceBranch" -s "$sourceBranch" -H "${repoName}" -t "merge $sourceBranch") + + # trying to merge + if glab mr merge -s $(basename "$pr") -y + then + # Pull Request merged successfully. + echo -e "${green}Pull Request merged into $targetBranch branch successfully." + exit + else + # Check if the -w flag is activated. + if [[ "$webBrowser" == "true" ]] + then + # -w flag is activated and a page with the corresponding Pull Request is opened in the web browser. + echo -e "${green}Pull Request successfully created." + echo -e "${green}Opening the Pull Request on the web browser..." + python -m webbrowser "$pr" + exit + else + # -w flag is not activated and the URL to the Pull Request is shown in the console. + echo -e "${green}Pull Request successfully created." + echo -e "${green}To review the Pull Request and accept it, click on the following link:" + echo "${pr}" + exit + fi + fi + fi +} + + +obtainHangarPath + +# Load common functions +. "$hangarPath/scripts/pipelines/common/pipeline_generator.lib" + +if [[ "$help" == "true" ]]; then help; fi + +ensurePathFormat + +importConfigFile + +checkInstallations + +createNewBranch + +type addPipelineVariables &> /dev/null && addPipelineVariables + +copyYAMLFile + +addAdditionalArtifact + +copyCommonScript + +type copyScript &> /dev/null && copyScript + +# This function does not exists for the github pipeline generator at this moment, but I let the line with 'type' to keep the same structure as the others pipeline generator +type addCommonPipelineVariables &> /dev/null && addCommonPipelineVariables + +commitCommonFiles + +type commitFiles &> /dev/null && commitFiles + +addCiFile + +createPR From 54a04e1b6db732dca23b04099c5f920c3dd485b2 Mon Sep 17 00:00:00 2001 From: Timothe Date: Wed, 22 Jun 2022 09:27:29 +0200 Subject: [PATCH 2/4] adding gitlab-ci file --- .../pipelines/gitlab/templates/common/.gitlab-ci.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 scripts/pipelines/gitlab/templates/common/.gitlab-ci.yml diff --git a/scripts/pipelines/gitlab/templates/common/.gitlab-ci.yml b/scripts/pipelines/gitlab/templates/common/.gitlab-ci.yml new file mode 100644 index 000000000..34904840a --- /dev/null +++ b/scripts/pipelines/gitlab/templates/common/.gitlab-ci.yml @@ -0,0 +1,12 @@ +include: + - '.pipelines/*.yml' + +stages: + - build + - test + - quality + - package + +default: + image: maven:3-jdk-11 + tags: ['docker_ruby'] \ No newline at end of file From edf91dcd7613ec179feedaaa5a732a185e922b9f Mon Sep 17 00:00:00 2001 From: Timothe Date: Thu, 7 Jul 2022 10:00:52 +0200 Subject: [PATCH 3/4] fixing some little errors --- scripts/pipelines/gitlab/pipeline_generator.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/pipelines/gitlab/pipeline_generator.sh b/scripts/pipelines/gitlab/pipeline_generator.sh index 0e90f3d58..3f04b4d29 100644 --- a/scripts/pipelines/gitlab/pipeline_generator.sh +++ b/scripts/pipelines/gitlab/pipeline_generator.sh @@ -58,8 +58,8 @@ function addAdditionalArtifact { if test ! -z "$artifactPath" then # Add the extra step to the YAML. - grep " artifacts:" "${localDirectory}/${pipelinePath}/${yamlFile}" && storeExtraPathContent=" - \"$artifactPath\"" - grep " artifacts:" "${localDirectory}/${pipelinePath}/${yamlFile}" || storeExtraPathContent="\n artifacts:\n - name: addtionalArtifactOutput\n paths:\n - \"$artifactPath\"" + grep " artifacts:" "${localDirectory}/${pipelinePath}/${yamlFile}" > /dev/null && storeExtraPathContent=" - \"$artifactPath\"" + grep " artifacts:" "${localDirectory}/${pipelinePath}/${yamlFile}" > /dev/null || storeExtraPathContent="\n artifacts:\n paths:\n - \"$artifactPath\"" sed -i "s/# mark to insert step for additonal artifact #/$storeExtraPathContent\n/" "${localDirectory}/${pipelinePath}/${yamlFile}" else echo "The '-a' flag has not been set, skipping the step to add additional artifact." @@ -109,7 +109,7 @@ function createPR { echo -e "${green}Creating a Pull Request..." echo -ne "${white}" repoURL=$(git config --get remote.origin.url) - repoNameWithGit="${repoURL/https:\/\/github.com\/}" + repoNameWithGit="${repoURL/https:\/\/gitlab.com\/}" repoName="${repoNameWithGit/.git}" # Create the Pull Request to merge into the specified branch. #debug From 115cf29b6be135ac24053ea28c5573a554bb6a7b Mon Sep 17 00:00:00 2001 From: Timothe Date: Mon, 17 Oct 2022 11:13:35 +0200 Subject: [PATCH 4/4] L pipeline generator lib --- .../pipelines/common/pipeline_generator.lib | 456 +++++++++--------- 1 file changed, 228 insertions(+), 228 deletions(-) diff --git a/scripts/pipelines/common/pipeline_generator.lib b/scripts/pipelines/common/pipeline_generator.lib index 01c4dd4ef..5eb49ef01 100644 --- a/scripts/pipelines/common/pipeline_generator.lib +++ b/scripts/pipelines/common/pipeline_generator.lib @@ -1,229 +1,229 @@ -#!/bin/bash -function help { - echo "" - echo "Generates a $pipeline_type on $provider based on the given definition." - echo "" - echo "Common flags:" - echo " -c, --config-file [Required] Configuration file containing $pipeline_type definition." - echo " -n, --pipeline-name [Required] Name that will be set to the $pipeline_type." - echo " -d, --local-directory [Required] Local directory of your project." - echo " -a, --artifact-path Path to be persisted as an artifact after $pipeline_type execution, e.g. where the application stores logs or any other blob on runtime." - echo " -b, --target-branch Name of the branch to which the Pull Request will target. PR is not created if the flag is not provided." - echo " -w Open the Pull Request on the web browser if it cannot be automatically merged. Requires -b flag." - echo "" - echo "Build $pipeline_type flags:" - echo " -l, --language [Required] Language or framework of the project." - echo " -t, --target-directory Target directory of build process. Takes precedence over the language/framework default one." - echo "" - echo "Test $pipeline_type flags:" - echo " -l, --language [Required] Language or framework of the project." - [ "$provider" == "azure-devops" ] && echo " --build-pipeline-name [Required] Build $pipeline_type name." - echo "" - echo "Quality $pipeline_type flags:" - echo " -l, --language [Required] Language or framework of the project." - echo " --sonar-url [Required] Sonarqube URL." - echo " --sonar-token [Required] Sonarqube token." - [ "$provider" == "azure-devops" ] && echo " --build-pipeline-name [Required] Build $pipeline_type name." - [ "$provider" == "azure-devops" ] && echo " --test-pipeline-name [Required] Test $pipeline_type name." - echo "" - [ "$provider" == "github" ] && echo "" - [ "$provider" == "github" ] && echo "CI pipeline flags" - [ "$provider" == "github" ] && echo " --build-pipeline-name [Required] Name of the job calling the build $pipeline_type." - [ "$provider" == "github" ] && echo " --test-pipeline-name Name of the job calling the test $pipeline_type." - [ "$provider" == "github" ] && echo " --quality-pipeline-name Name of the job calling the quality $pipeline_type." - [ "$provider" == "github" ] && echo "" - echo "Package pipeline flags:" - echo " -l, --language [Required, if dockerfile not set] Language or framework of the project." - echo " --dockerfile [Required, if language not set] Path from the root of the project to its Dockerfile. Takes precedence over the language/framework default one." - [ "$provider" == "azure-devops" ] && echo " --build-pipeline-name [Required] Build $pipeline_type name." - [ "$provider" == "azure-devops" ] && echo " --quality-pipeline-name [Required] Quality $pipeline_type name." - [ "$provider" == "github" ] && echo " --ci-pipeline-name [Required] CI $pipeline_type name." - echo " -i, --image-name [Required] Name (excluding tag) for the generated container image." - echo " -u, --registry-user [Required, unless AWS] Container registry login user." - echo " -p, --registry-password [Required, unless AWS] Container registry login password." - echo " --aws-access-key [Required, if AWS] AWS account access key ID. Takes precedence over registry credentials." - echo " --aws-secret-access-key [Required, if AWS] AWS account secret access key." - echo " --aws-region [Required, if AWS] AWS region for ECR." - echo "" - echo "Library package $pipeline_type flags:" - echo " -l, --language [Required] Language or framework of the project." - echo "" - echo "Azure AKS provisioning $pipeline_type flags:" - echo " --resource-group [Required] Name of the resource group for the cluster." - echo " --storage-account [Required] Name of the storage account for the cluster." - echo " --storage-container [Required] Name of the storage container where the Terraform state of the cluster will be stored." - echo " --rancher Install Rancher to manage the cluster." - echo "" - echo "AWS EKS provisioning $pipeline_type flags:" - echo " --cluster-name [Required] Name for the cluster." - echo " --s3-bucket [Required] Name of the S3 bucket where the Terraform state of the cluster will be stored." - echo " --s3-key-path [Required] Path within the S3 bucket where the Terraform state of the cluster will be stored." - echo " --aws-access-key [Required, on first run] AWS account access key ID." - echo " --aws-secret-access-key [Required, on first run] AWS account secret access key." - echo " --aws-region [Required, on first run] AWS region for provisioning resources." - - echo " --rancher Install Rancher to manage the cluster." - echo "" - echo "Deploy $pipeline_type flags:" - echo "" - echo " --package-pipeline-name [Required] Package $pipeline_type name." - echo " --env-provision-pipeline-name [Required] Environment provisioning $pipeline_type name." - echo " --k8s-provider [Required] Kubernetes cluster provider name. Accepted values: EKS, AKS." - echo " --k8s-namespace [Required] Kubernetes namespace where the application will be deployed." - echo " --k8s-deploy-files-path [Required] Path from the root of the project to the YAML manifests directory." - echo " --k8s-image-pull-secret-name Name for the generated secret containing registry credentials. Required when using a private registry to host images." - echo "" - - exit -} - -function validateRegistryLoginCredentials { - # if the user chose to push to a registry and the user has not already given a password - # then prompt the user - if [ -v dockerUser ] && [ ! -v dockerPassword ] - then - read -rsp "Please enter Docker registry password..." dockerPassword - fi - - if [ -v awsRegion ] && [ -v awsAccessKey ] && [ ! -v awsSecretAccessKey ] - then - read -rsp "Please enter AWS secret access key..." awsSecretAccessKey - fi -} - -function ensurePathFormat { - currentDirectory=$(pwd) - - # When necessary, converts a relative path into an absolute path, and a Windows-style path (e.g. "C:\Users" or C:/Users) into a - # Unix-style path using forward slashes (e.g. "/c/Users"). - localDirectory=${localDirectory//'\'/"/"} - cd "${localDirectory}" || { echo -e "${red}Error: Local directory '${localDirectory}' does not exist. Check provided path (missing quotes?)."; exit 1; } - localDirectory=$(pwd) - - # Return to initial directory - cd "$currentDirectory" -} - -function importConfigFile { - # Import config file. - source $configFile - IFS=, read -ra flags <<< "$mandatoryFlags" - - # Check if the config file was supplied. - if test -z "$configFile" - then - echo -e "${red}Error: $pipeline_type definition configuration file not specified." >&2 - exit 2 - fi - - # Check if the required flags in the config file have been activated. - for flag in "${flags[@]}" - do - if test -z $flag - then - echo -e "${red}Error: Missing parameters, some flags are mandatory." >&2 - echo -e "${red}Use -h or --help flag to display help." >&2 - exit 2 - fi - done -} - -function checkInstallations { - # Check if Git is installed - if ! [ -x "$(command -v git)" ]; then - echo -e "${red}Error: Git is not installed." >&2 - exit 127 - fi - - # Check if the CLI tool for the provider is installed - if ([ "$provider" == "github" ] && ! [ -x "$(command -v gh)" ]); then - echo -e "${red}Error: Github CLI is not installed." >&2 - exit 127 - elif ([ "$provider" == "azure-devops" ] && ! [ -x "$(command -v az)" ]); then - echo -e "${red}Error: Azure CLI is not installed." >&2 - exit 127 - elif ([ "$provider" == "gitlab" ] && ! [ -x "$(command -v glab)" ]); then - echo -e "${red}Error: Azure CLI is not installed." >&2 - exit 127 - fi - - # Check if Python is installed - if ! [ -x "$(command -v python)" ]; then - echo -e "${red}Error: Python is not installed." >&2 - exit 127 - fi -} - -function createNewBranch { - echo -e "${green}Creating the new branch: ${sourceBranch}..." - echo -ne ${white} - - # Create the new branch. - cd "${localDirectory}" - git checkout -b ${sourceBranch} -} - -function copyYAMLFile { - echo -e "${green}Copying the corresponding files into your directory..." - echo -ne ${white} - - # Create .pipelines and scripts if they do not exist. - mkdir -p "${localDirectory}/$scriptFilePath" - - # Generate pipeline YAML from template and put it in the repository. - # We cannot use a variable in the definition of resource in the pipeline so we have to use a placeholder to replace it with the value we need - commonEnvSubstList='${buildPipelineName} ${testPipelineName} ${qualityPipelineName} ${pipelineName} ${ciPipelineName} ${packagePipelineName}' - envsubst "${commonEnvSubstList} ${specificEnvSubstList}" < "${hangarPath}/${templatesPath}/${yamlFile}.template" > "${localDirectory}/${pipelinePath}/${yamlFile}" - - # Check if an extra artifact to store is supplied. - if test ! -z "$artifactPath" - then - # Add the extra step to the YAML. - if [ "$provider" == "azure-devops" ] - then - cat "${hangarPath}/${commonTemplatesPath}/store-extra-path.yml" >> "${localDirectory}/${pipelinePath}/${yamlFile}" - elif [ "$provider" == "github" ] - then - storeExtraPathContent="\n - name: Publish Additional Output Artifact\n uses: actions\/upload-artifact@v3\n with:\n name: additional-pipeline-output\n path: \"\${{ env.artifactPath }}\"" - sed -i "s/# mark to insert step for additonal artifact #/$storeExtraPathContent\n/" "${localDirectory}/${pipelinePath}/${yamlFile}" - fi - else - if [ "$provider" == "github" ] - then - sed -i '/# mark to insert step for additonal artifact #/d' "${localDirectory}/${pipelinePath}/${yamlFile}" - fi - fi -} - -function copyCommonScript { - echo -e "${green}Copying the script(s) common to any $pipeline_type files into your directory..." - echo -ne ${white} - - ! (ls "${hangarPath}/${commonTemplatesPath}"/*.sh) &> /dev/null || cp "${hangarPath}/${commonTemplatesPath}"/*.sh "${localDirectory}/${scriptFilePath}" -} - -function commitCommonFiles { - echo -e "${green}Commiting and pushing into Git remote..." - echo -ne ${white} - - # Move into the project's directory and pushing the template into the Azure DevOps repository. - cd "${localDirectory}" - - # Add the YAML files. - git add "$pipelinePath" -f - - # Git commit and push it into the repository. - # changing all files to be executable - find "$pipelinePath" -type f -name '*.sh' -exec git update-index --chmod=+x {} \; - - git commit -m "Adding the source YAML" - git push -u origin ${sourceBranch} -} - -function setTargetDirectory { - case $language in - node | angular) targetDirectory="./" ;; - quarkus*) targetDirectory="./target/" ;; - *) echo -e "${red}Error: Specified language '${language}' is not supported." >&2; exit 1 - esac +#!/bin/bash +function help { + echo "" + echo "Generates a $pipeline_type on $provider based on the given definition." + echo "" + echo "Common flags:" + echo " -c, --config-file [Required] Configuration file containing $pipeline_type definition." + echo " -n, --pipeline-name [Required] Name that will be set to the $pipeline_type." + echo " -d, --local-directory [Required] Local directory of your project." + echo " -a, --artifact-path Path to be persisted as an artifact after $pipeline_type execution, e.g. where the application stores logs or any other blob on runtime." + echo " -b, --target-branch Name of the branch to which the Pull Request will target. PR is not created if the flag is not provided." + echo " -w Open the Pull Request on the web browser if it cannot be automatically merged. Requires -b flag." + echo "" + echo "Build $pipeline_type flags:" + echo " -l, --language [Required] Language or framework of the project." + echo " -t, --target-directory Target directory of build process. Takes precedence over the language/framework default one." + echo "" + echo "Test $pipeline_type flags:" + echo " -l, --language [Required] Language or framework of the project." + [ "$provider" == "azure-devops" ] && echo " --build-pipeline-name [Required] Build $pipeline_type name." + echo "" + echo "Quality $pipeline_type flags:" + echo " -l, --language [Required] Language or framework of the project." + echo " --sonar-url [Required] Sonarqube URL." + echo " --sonar-token [Required] Sonarqube token." + [ "$provider" == "azure-devops" ] && echo " --build-pipeline-name [Required] Build $pipeline_type name." + [ "$provider" == "azure-devops" ] && echo " --test-pipeline-name [Required] Test $pipeline_type name." + echo "" + [ "$provider" == "github" ] && echo "" + [ "$provider" == "github" ] && echo "CI pipeline flags" + [ "$provider" == "github" ] && echo " --build-pipeline-name [Required] Name of the job calling the build $pipeline_type." + [ "$provider" == "github" ] && echo " --test-pipeline-name Name of the job calling the test $pipeline_type." + [ "$provider" == "github" ] && echo " --quality-pipeline-name Name of the job calling the quality $pipeline_type." + [ "$provider" == "github" ] && echo "" + echo "Package pipeline flags:" + echo " -l, --language [Required, if dockerfile not set] Language or framework of the project." + echo " --dockerfile [Required, if language not set] Path from the root of the project to its Dockerfile. Takes precedence over the language/framework default one." + [ "$provider" == "azure-devops" ] && echo " --build-pipeline-name [Required] Build $pipeline_type name." + [ "$provider" == "azure-devops" ] && echo " --quality-pipeline-name [Required] Quality $pipeline_type name." + [ "$provider" == "github" ] && echo " --ci-pipeline-name [Required] CI $pipeline_type name." + echo " -i, --image-name [Required] Name (excluding tag) for the generated container image." + echo " -u, --registry-user [Required, unless AWS] Container registry login user." + echo " -p, --registry-password [Required, unless AWS] Container registry login password." + echo " --aws-access-key [Required, if AWS] AWS account access key ID. Takes precedence over registry credentials." + echo " --aws-secret-access-key [Required, if AWS] AWS account secret access key." + echo " --aws-region [Required, if AWS] AWS region for ECR." + echo "" + echo "Library package $pipeline_type flags:" + echo " -l, --language [Required] Language or framework of the project." + echo "" + echo "Azure AKS provisioning $pipeline_type flags:" + echo " --resource-group [Required] Name of the resource group for the cluster." + echo " --storage-account [Required] Name of the storage account for the cluster." + echo " --storage-container [Required] Name of the storage container where the Terraform state of the cluster will be stored." + echo " --rancher Install Rancher to manage the cluster." + echo "" + echo "AWS EKS provisioning $pipeline_type flags:" + echo " --cluster-name [Required] Name for the cluster." + echo " --s3-bucket [Required] Name of the S3 bucket where the Terraform state of the cluster will be stored." + echo " --s3-key-path [Required] Path within the S3 bucket where the Terraform state of the cluster will be stored." + echo " --aws-access-key [Required, on first run] AWS account access key ID." + echo " --aws-secret-access-key [Required, on first run] AWS account secret access key." + echo " --aws-region [Required, on first run] AWS region for provisioning resources." + + echo " --rancher Install Rancher to manage the cluster." + echo "" + echo "Deploy $pipeline_type flags:" + echo "" + echo " --package-pipeline-name [Required] Package $pipeline_type name." + echo " --env-provision-pipeline-name [Required] Environment provisioning $pipeline_type name." + echo " --k8s-provider [Required] Kubernetes cluster provider name. Accepted values: EKS, AKS." + echo " --k8s-namespace [Required] Kubernetes namespace where the application will be deployed." + echo " --k8s-deploy-files-path [Required] Path from the root of the project to the YAML manifests directory." + echo " --k8s-image-pull-secret-name Name for the generated secret containing registry credentials. Required when using a private registry to host images." + echo "" + + exit +} + +function validateRegistryLoginCredentials { + # if the user chose to push to a registry and the user has not already given a password + # then prompt the user + if [ -v dockerUser ] && [ ! -v dockerPassword ] + then + read -rsp "Please enter Docker registry password..." dockerPassword + fi + + if [ -v awsRegion ] && [ -v awsAccessKey ] && [ ! -v awsSecretAccessKey ] + then + read -rsp "Please enter AWS secret access key..." awsSecretAccessKey + fi +} + +function ensurePathFormat { + currentDirectory=$(pwd) + + # When necessary, converts a relative path into an absolute path, and a Windows-style path (e.g. "C:\Users" or C:/Users) into a + # Unix-style path using forward slashes (e.g. "/c/Users"). + localDirectory=${localDirectory//'\'/"/"} + cd "${localDirectory}" || { echo -e "${red}Error: Local directory '${localDirectory}' does not exist. Check provided path (missing quotes?)."; exit 1; } + localDirectory=$(pwd) + + # Return to initial directory + cd "$currentDirectory" +} + +function importConfigFile { + # Import config file. + source $configFile + IFS=, read -ra flags <<< "$mandatoryFlags" + + # Check if the config file was supplied. + if test -z "$configFile" + then + echo -e "${red}Error: $pipeline_type definition configuration file not specified." >&2 + exit 2 + fi + + # Check if the required flags in the config file have been activated. + for flag in "${flags[@]}" + do + if test -z $flag + then + echo -e "${red}Error: Missing parameters, some flags are mandatory." >&2 + echo -e "${red}Use -h or --help flag to display help." >&2 + exit 2 + fi + done +} + +function checkInstallations { + # Check if Git is installed + if ! [ -x "$(command -v git)" ]; then + echo -e "${red}Error: Git is not installed." >&2 + exit 127 + fi + + # Check if the CLI tool for the provider is installed + if ([ "$provider" == "github" ] && ! [ -x "$(command -v gh)" ]); then + echo -e "${red}Error: Github CLI is not installed." >&2 + exit 127 + elif ([ "$provider" == "azure-devops" ] && ! [ -x "$(command -v az)" ]); then + echo -e "${red}Error: Azure CLI is not installed." >&2 + exit 127 + elif ([ "$provider" == "gitlab" ] && ! [ -x "$(command -v glab)" ]); then + echo -e "${red}Error: Azure CLI is not installed." >&2 + exit 127 + fi + + # Check if Python is installed + if ! [ -x "$(command -v python)" ]; then + echo -e "${red}Error: Python is not installed." >&2 + exit 127 + fi +} + +function createNewBranch { + echo -e "${green}Creating the new branch: ${sourceBranch}..." + echo -ne ${white} + + # Create the new branch. + cd "${localDirectory}" + git checkout -b ${sourceBranch} +} + +function copyYAMLFile { + echo -e "${green}Copying the corresponding files into your directory..." + echo -ne ${white} + + # Create .pipelines and scripts if they do not exist. + mkdir -p "${localDirectory}/$scriptFilePath" + + # Generate pipeline YAML from template and put it in the repository. + # We cannot use a variable in the definition of resource in the pipeline so we have to use a placeholder to replace it with the value we need + commonEnvSubstList='${buildPipelineName} ${testPipelineName} ${qualityPipelineName} ${pipelineName} ${ciPipelineName} ${packagePipelineName}' + envsubst "${commonEnvSubstList} ${specificEnvSubstList}" < "${hangarPath}/${templatesPath}/${yamlFile}.template" > "${localDirectory}/${pipelinePath}/${yamlFile}" + + # Check if an extra artifact to store is supplied. + if test ! -z "$artifactPath" + then + # Add the extra step to the YAML. + if [ "$provider" == "azure-devops" ] + then + cat "${hangarPath}/${commonTemplatesPath}/store-extra-path.yml" >> "${localDirectory}/${pipelinePath}/${yamlFile}" + elif [ "$provider" == "github" ] + then + storeExtraPathContent="\n - name: Publish Additional Output Artifact\n uses: actions\/upload-artifact@v3\n with:\n name: additional-pipeline-output\n path: \"\${{ env.artifactPath }}\"" + sed -i "s/# mark to insert step for additonal artifact #/$storeExtraPathContent\n/" "${localDirectory}/${pipelinePath}/${yamlFile}" + fi + else + if [ "$provider" == "github" ] + then + sed -i '/# mark to insert step for additonal artifact #/d' "${localDirectory}/${pipelinePath}/${yamlFile}" + fi + fi +} + +function copyCommonScript { + echo -e "${green}Copying the script(s) common to any $pipeline_type files into your directory..." + echo -ne ${white} + + ! (ls "${hangarPath}/${commonTemplatesPath}"/*.sh) &> /dev/null || cp "${hangarPath}/${commonTemplatesPath}"/*.sh "${localDirectory}/${scriptFilePath}" +} + +function commitCommonFiles { + echo -e "${green}Commiting and pushing into Git remote..." + echo -ne ${white} + + # Move into the project's directory and pushing the template into the Azure DevOps repository. + cd "${localDirectory}" + + # Add the YAML files. + git add "$pipelinePath" -f + + # Git commit and push it into the repository. + # changing all files to be executable + find "$pipelinePath" -type f -name '*.sh' -exec git update-index --chmod=+x {} \; + + git commit -m "Adding the source YAML" + git push -u origin ${sourceBranch} +} + +function setTargetDirectory { + case $language in + node | angular) targetDirectory="./" ;; + quarkus*) targetDirectory="./target/" ;; + *) echo -e "${red}Error: Specified language '${language}' is not supported." >&2; exit 1 + esac } \ No newline at end of file