Repository: devtron-labs/devtron
Pull request #3640 (2023-07-17 10:31:54)

feat: edit Ingress for specific host

gitguardian[bot]: #### ⚠️ GitGuardian has uncovered 8 secrets following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

<details>
<summary>🔎 Detected hardcoded secrets in your pull request</summary>
<br>

| GitGuardian id | Secret                    | Commit           | Filename        |                      |
| -------------- | ------------------------- | ---------------- | --------------- | -------------------- |
| [-](https://dashboard.gitguardian.com/incidents/secrets) | Generic High Entropy Secret | 46c262d247a84bf6a73b604023eff8e08a53a0b8 | scripts/devtron-reference-helm-charts/reference-chart_4-17-0/env-values.yaml | [View secret](https://github.com/devtron-labs/devtron/commit/46c262d247a84bf6a73b604023eff8e08a53a0b8#diff-f739fc160871ef91fcc3c2f34d1fa836R48) |
| [-](https://dashboard.gitguardian.com/incidents/secrets) | Generic High Entropy Secret | 46c262d247a84bf6a73b604023eff8e08a53a0b8 | scripts/devtron-reference-helm-charts/reference-chart_4-17-0/secrets-test-values.yaml | [View secret](https://github.com/devtron-labs/devtron/commit/46c262d247a84bf6a73b604023eff8e08a53a0b8#diff-24d9e54abbb4f85bd71299e0a4625c45R1) |
| [-](https://dashboard.gitguardian.com/incidents/secrets) | Generic High Entropy Secret | e084b22cb531e00918d105e6a1f9ce4620781d4c | scripts/devtron-reference-helm-charts/deployment-chart_4-18-0/env-values.yaml | [View secret](https://github.com/devtron-labs/devtron/commit/e084b22cb531e00918d105e6a1f9ce4620781d4c#diff-ea29ed561de8e283c4889a14d72e445cL48) |
| [-](https://dashboard.gitguardian.com/incidents/secrets) | Generic High Entropy Secret | e084b22cb531e00918d105e6a1f9ce4620781d4c | scripts/devtron-reference-helm-charts/deployment-chart_4-18-0/secrets-test-values.yaml | [View secret](https://github.com/devtron-labs/devtron/commit/e084b22cb531e00918d105e6a1f9ce4620781d4c#diff-2705d117de58a00a19ebe48f49ec1225L1) |
| [-](https://dashboard.gitguardian.com/incidents/secrets) | Generic High Entropy Secret | ea8f596b8e534f761ccd03d9092d58546e64e1a4 | scripts/devtron-reference-helm-charts/reference-chart_4-17-0/env-values.yaml | [View secret](https://github.com/devtron-labs/devtron/commit/ea8f596b8e534f761ccd03d9092d58546e64e1a4#diff-f739fc160871ef91fcc3c2f34d1fa836L48) |
| [-](https://dashboard.gitguardian.com/incidents/secrets) | Generic High Entropy Secret | ea8f596b8e534f761ccd03d9092d58546e64e1a4 | scripts/devtron-reference-helm-charts/reference-chart_4-17-0/secrets-test-values.yaml | [View secret](https://github.com/devtron-labs/devtron/commit/ea8f596b8e534f761ccd03d9092d58546e64e1a4#diff-24d9e54abbb4f85bd71299e0a4625c45L1) |
| [-](https://dashboard.gitguardian.com/incidents/secrets) | Generic High Entropy Secret | a7cc26684394dc7dfcc8427a1ac0f5ac827a3294 | scripts/devtron-reference-helm-charts/reference-chart_4-17-0/env-values.yaml | [View secret](https://github.com/devtron-labs/devtron/commit/a7cc26684394dc7dfcc8427a1ac0f5ac827a3294#diff-f739fc160871ef91fcc3c2f34d1fa836L48) |
| [-](https://dashboard.gitguardian.com/incidents/secrets) | Generic High Entropy Secret | a7cc26684394dc7dfcc8427a1ac0f5ac827a3294 | scripts/devtron-reference-helm-charts/reference-chart_4-17-0/secrets-test-values.yaml | [View secret](https://github.com/devtron-labs/devtron/commit/a7cc26684394dc7dfcc8427a1ac0f5ac827a3294#diff-24d9e54abbb4f85bd71299e0a4625c45L1) |



</details>

<details>
<summary>🛠 Guidelines to remediate hardcoded secrets</summary>
<br>

1. Understand the implications of revoking this secret by investigating where it is used in your code.
2. Replace and store your secrets safely. [Learn here](https://blog.gitguardian.com/secrets-api-management?utm_source=product&amp;utm_medium=GitHub_checks&amp;utm_campaign=check_run_comment) the best practices.
3. Revoke and [rotate these secrets](https://docs.gitguardian.com/secrets-detection/detectors/generics/generic_high_entropy_secret#revoke-the-secret?utm_source=product&amp;utm_medium=GitHub_checks&amp;utm_campaign=check_run_comment).
4. If possible, [rewrite git history](https://blog.gitguardian.com/rewriting-git-history-cheatsheet?utm_source=product&amp;utm_medium=GitHub_checks&amp;utm_campaign=check_run_comment). Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider

-   following these [best practices](https://blog.gitguardian.com/secrets-api-management/?utm_source=product&amp;utm_medium=GitHub_checks&amp;utm_campaign=check_run_comment) for managing and storing secrets including API keys and other credentials
-   install [secret detection on pre-commit](https://docs.gitguardian.com/ggshield-docs/integrations/git-hooks/pre-commit?utm_source=product&amp;utm_medium=GitHub_checks&amp;utm_campaign=check_run_comment) to catch secret before it leaves your machine and ease remediation.


</details>

---

<sup>🦉 [GitGuardian](https://dashboard.gitguardian.com/auth/login/?utm_medium=checkruns&amp;utm_source=github&amp;utm_campaign=cr1) detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.<br/><br/>Our GitHub checks need improvements? [Share your feedbacks](https://050127dayho.typeform.com/to/KmeAPTMk)!</sup>
sonarcloud[bot]: SonarCloud Quality Gate failed.&nbsp; &nbsp; [![Quality Gate failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/failed-16px.png 'Quality Gate failed')](https://sonarcloud.io/dashboard?id=devtron-labs_devtron&pullRequest=3640)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=devtron-labs_devtron&pullRequest=3640&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=devtron-labs_devtron&pullRequest=3640&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=devtron-labs_devtron&pullRequest=3640&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=devtron-labs_devtron&pullRequest=3640&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=devtron-labs_devtron&pullRequest=3640&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=devtron-labs_devtron&pullRequest=3640&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=devtron-labs_devtron&pullRequest=3640&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=devtron-labs_devtron&pullRequest=3640&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=devtron-labs_devtron&pullRequest=3640&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=devtron-labs_devtron&pullRequest=3640&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=devtron-labs_devtron&pullRequest=3640&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=devtron-labs_devtron&pullRequest=3640&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=devtron-labs_devtron&pullRequest=3640) No Coverage information  
[![13.1%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/20-16px.png '13.1%')](https://sonarcloud.io/component_measures?id=devtron-labs_devtron&pullRequest=3640&metric=new_duplicated_lines_density&view=list) [13.1% Duplication](https://sonarcloud.io/component_measures?id=devtron-labs_devtron&pullRequest=3640&metric=new_duplicated_lines_density&view=list)



![idea](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/light_bulb-16px.png 'idea') Catch issues before they fail your Quality Gate with our IDE extension ![sonarlint](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/sonarlint-16px.png 'sonarlint') [SonarLint](https://www.sonarsource.com/products/sonarlint/features/connected-mode/?referrer=sonarcloud-welcome)

Patch:
diff --git a/api/bean/AppView.go b/api/bean/AppView.go
index b48d88484..82b823177 100644
--- a/api/bean/AppView.go
+++ b/api/bean/AppView.go
@@ -66,21 +66,28 @@ type JobContainer struct {
 }
 
 type JobCIPipeline struct {
-	CiPipelineId   int       `json:"ciPipelineId"`
-	CiPipelineName string    `json:"ciPipelineName"`
-	Status         string    `json:"status"`
-	LastRunAt      time.Time `json:"lastRunAt"`
-	LastSuccessAt  time.Time `json:"lastSuccessAt"`
+	CiPipelineId                 int       `json:"ciPipelineId"`
+	CiPipelineName               string    `json:"ciPipelineName"`
+	Status                       string    `json:"status"`
+	LastRunAt                    time.Time `json:"lastRunAt"`
+	LastSuccessAt                time.Time `json:"lastSuccessAt"`
+	EnvironmentId                int       `json:"environmentId"`
+	EnvironmentName              string    `json:"environmentName"`
+	LastTriggeredEnvironmentName string    `json:"lastTriggeredEnvironmentName"`
 }
 
 type JobListingContainer struct {
-	JobId          int       `json:"job_id"`
-	JobName        string    `json:"job_name"`
-	Description    string    `json:"description"`
-	CiPipelineID   int       `json:"ci_pipeline_id"`
-	CiPipelineName string    `json:"ci_pipeline_name"`
-	Status         string    `json:"status"`
-	StartedOn      time.Time `json:"started_on"`
+	JobId                        int       `sql:"job_id" json:"jobId"`
+	JobName                      string    `sql:"job_name" json:"jobName"`
+	Description                  string    `sql:"description" json:"description"`
+	CiPipelineID                 int       `sql:"ci_pipeline_id" json:"ciPipelineID"`
+	CiPipelineName               string    `sql:"ci_pipeline_name" json:"ciPipelineName"`
+	Status                       string    `sql:"status" json:"status"`
+	StartedOn                    time.Time `sql:"started_on" json:"startedOn"`
+	EnvironmentId                int       `sql:"environment_id" json:"environmentId"`
+	EnvironmentName              string    `sql:"environment_name" json:"environmentName"`
+	LastTriggeredEnvironmentName string    `sql:"last_triggered_environment_name" json:"lastTriggeredEnvironmentName"`
+	LastTriggeredEnvironmentId   int       `sql:"last_triggered_environment_id" json:"lastEnvironmentId"`
 }
 
 type CiPipelineLastSucceededTime struct {
diff --git a/api/restHandler/ConfigMapRestHandler.go b/api/restHandler/ConfigMapRestHandler.go
index 9db1418c8..30fafdef3 100644
--- a/api/restHandler/ConfigMapRestHandler.go
+++ b/api/restHandler/ConfigMapRestHandler.go
@@ -20,7 +20,6 @@ package restHandler
 import (
 	"encoding/json"
 	"fmt"
-	"github.com/devtron-labs/devtron/api/bean"
 	"github.com/devtron-labs/devtron/api/restHandler/common"
 	"github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig"
 	"github.com/devtron-labs/devtron/pkg/chart"
@@ -54,6 +53,10 @@ type ConfigMapRestHandler interface {
 	CSGlobalFetchForEdit(w http.ResponseWriter, r *http.Request)
 	CSEnvironmentFetchForEdit(w http.ResponseWriter, r *http.Request)
 	ConfigSecretBulkPatch(w http.ResponseWriter, r *http.Request)
+
+	AddEnvironmentToJob(w http.ResponseWriter, r *http.Request)
+	RemoveEnvironmentFromJob(w http.ResponseWriter, r *http.Request)
+	GetEnvironmentsForJob(w http.ResponseWriter, r *http.Request)
 }
 
 type ConfigMapRestHandlerImpl struct {
@@ -660,19 +663,12 @@ func (handler ConfigMapRestHandlerImpl) ConfigSecretBulkPatch(w http.ResponseWri
 	}
 
 	//AUTH - check from casbin db
-	roles, err := handler.userAuthService.CheckUserRoles(userId)
-	if err != nil {
-		common.WriteJsonResp(w, err, []byte("Failed to get user by id"), http.StatusInternalServerError)
-		return
-	}
-	superAdmin := false
-	for _, item := range roles {
-		if item == bean.SUPERADMIN {
-			superAdmin = true
+	isSuperAdmin, err := handler.userAuthService.IsSuperAdmin(int(userId))
+	if !isSuperAdmin || err != nil {
+		if err != nil {
+			handler.Logger.Errorw("request err, CheckSuperAdmin", "err", isSuperAdmin, "isSuperAdmin", isSuperAdmin)
 		}
-	}
-	if superAdmin == false {
-		common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), nil, http.StatusForbidden)
+		common.WriteJsonResp(w, err, "Unauthorized User", http.StatusForbidden)
 		return
 	}
 	//AUTH
@@ -703,3 +699,117 @@ func (handler ConfigMapRestHandlerImpl) ConfigSecretBulkPatch(w http.ResponseWri
 	}
 	common.WriteJsonResp(w, err, true, http.StatusOK)
 }
+
+func (handler ConfigMapRestHandlerImpl) AddEnvironmentToJob(w http.ResponseWriter, r *http.Request) {
+	decoder := json.NewDecoder(r.Body)
+	userId, err := handler.userAuthService.GetLoggedInUser(r)
+	if userId == 0 || err != nil {
+		common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
+		return
+	}
+
+	//AUTH - check from casbin db
+	isSuperAdmin, err := handler.userAuthService.IsSuperAdmin(int(userId))
+	if !isSuperAdmin || err != nil {
+		if err != nil {
+			handler.Logger.Errorw("request err, CheckSuperAdmin", "err", isSuperAdmin, "isSuperAdmin", isSuperAdmin)
+		}
+		common.WriteJsonResp(w, err, "Unauthorized User", http.StatusForbidden)
+		return
+	}
+	//AUTH
+
+	var envOverrideRequest pipeline.CreateJobEnvOverridePayload
+	err = decoder.Decode(&envOverrideRequest)
+	if err != nil {
+		handler.Logger.Errorw("request err, AddEvironmentToJob", "err", err, "payload", envOverrideRequest)
+		common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
+		return
+	}
+	envOverrideRequest.UserId = userId
+	handler.Logger.Infow("request payload, AddEvironmentToJob", "payload", envOverrideRequest)
+	resp, err := handler.configMapService.ConfigSecretEnvironmentCreate(&envOverrideRequest)
+	if err != nil {
+		handler.Logger.Errorw("service err, AddEvironmentToJob", "err", err, "payload", envOverrideRequest)
+		common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
+		return
+	}
+
+	common.WriteJsonResp(w, err, resp, http.StatusOK)
+}
+
+func (handler ConfigMapRestHandlerImpl) RemoveEnvironmentFromJob(w http.ResponseWriter, r *http.Request) {
+	decoder := json.NewDecoder(r.Body)
+	userId, err := handler.userAuthService.GetLoggedInUser(r)
+	if userId == 0 || err != nil {
+		common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
+		return
+	}
+
+	//AUTH - check from casbin db
+	isSuperAdmin, err := handler.userAuthService.IsSuperAdmin(int(userId))
+	if !isSuperAdmin || err != nil {
+		if err != nil {
+			handler.Logger.Errorw("request err, CheckSuperAdmin", "err", isSuperAdmin, "isSuperAdmin", isSuperAdmin)
+		}
+		common.WriteJsonResp(w, err, "Unauthorized User", http.StatusForbidden)
+		return
+	}
+	//AUTH
+
+	var envOverrideRequest pipeline.CreateJobEnvOverridePayload
+	err = decoder.Decode(&envOverrideRequest)
+	if err != nil {
+		handler.Logger.Errorw("request err, RemoveEnvironmentFromJob", "err", err, "payload", envOverrideRequest)
+		common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
+		return
+	}
+	envOverrideRequest.UserId = userId
+	handler.Logger.Infow("request payload, RemoveEnvironmentFromJob", "payload", envOverrideRequest)
+	resp, err := handler.configMapService.ConfigSecretEnvironmentDelete(&envOverrideRequest)
+	if err != nil {
+		handler.Logger.Errorw("service err, RemoveEnvironmentFromJob", "err", err, "payload", envOverrideRequest)
+		common.WriteJsonResp(w, err, resp, http.StatusInternalServerError)
+		return
+	}
+
+	common.WriteJsonResp(w, err, true, http.StatusOK)
+}
+func (handler ConfigMapRestHandlerImpl) GetEnvironmentsForJob(w http.ResponseWriter, r *http.Request) {
+	userId, err := handler.userAuthService.GetLoggedInUser(r)
+	if userId == 0 || err != nil {
+		common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
+		return
+	}
+	vars := mux.Vars(r)
+	appId, err := strconv.Atoi(vars["appId"])
+	if err != nil {
+		handler.Logger.Errorw("request err, GetEnvironmentsForJob", "err", err, "appId", appId)
+		common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
+		return
+	}
+	//AUTH - check from casbin db
+	isSuperAdmin, err := handler.userAuthService.IsSuperAdmin(int(userId))
+	if !isSuperAdmin || err != nil {
+		if err != nil {
+			handler.Logger.Errorw("request err, CheckSuperAdmin", "err", isSuperAdmin, "isSuperAdmin", isSuperAdmin)
+		}
+		common.WriteJsonResp(w, err, "Unauthorized User", http.StatusForbidden)
+		return
+	}
+	//AUTH
+	if err != nil {
+		handler.Logger.Errorw("request err, GetEnvironmentsForJob", "err", err, "appId", appId)
+		common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
+		return
+	}
+	handler.Logger.Infow("request payload, GetEnvironmentsForJob", "appId", appId)
+	resp, err := handler.configMapService.ConfigSecretEnvironmentGet(appId)
+	if err != nil {
+		handler.Logger.Errorw("service err, GetEnvironmentsForJob", "err", err, "appId", appId)
+		common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
+		return
+	}
+
+	common.WriteJsonResp(w, err, resp, http.StatusOK)
+}
diff --git a/api/restHandler/CoreAppRestHandler.go b/api/restHandler/CoreAppRestHandler.go
index 018e61791..90360186d 100644
--- a/api/restHandler/CoreAppRestHandler.go
+++ b/api/restHandler/CoreAppRestHandler.go
@@ -196,7 +196,8 @@ func (handler CoreAppRestHandlerImpl) GetAppAllDetail(w http.ResponseWriter, r *
 
 	//get/build app workflows starts
 	//using empty workflow name because it is optional, if not provided then workflows will be fetched on the basis of app
-	appWorkflows, err, statusCode := handler.buildAppWorkflows(appId, WORKFLOW_NAME_EMPTY)
+	wfCloneRequest := &appWorkflow.WorkflowCloneRequest{AppId: appId}
+	appWorkflows, err, statusCode := handler.buildAppWorkflows(wfCloneRequest)
 	if err != nil {
 		common.WriteJsonResp(w, err, nil, statusCode)
 		return
@@ -611,19 +612,26 @@ func (handler CoreAppRestHandlerImpl) buildAppEnvironmentDeploymentTemplate(appI
 }
 
 // validate and build workflows
-func (handler CoreAppRestHandlerImpl) buildAppWorkflows(appId int, workflowName string) ([]*appBean.AppWorkflow, error, int) {
-	handler.logger.Debugw("Getting app detail - workflows", "appId", appId)
+func (handler CoreAppRestHandlerImpl) buildAppWorkflows(request *appWorkflow.WorkflowCloneRequest) ([]*appBean.AppWorkflow, error, int) {
+	handler.logger.Debugw("Getting app detail - workflows", "appId", request.AppId)
 	var workflowsList []appWorkflow.AppWorkflowDto
 	var err error
-	if len(workflowName) != 0 {
-		workflow, err := handler.appWorkflowService.FindAppWorkflowByName(workflowName, appId)
+	if len(request.WorkflowName) != 0 {
+		workflow, err := handler.appWorkflowService.FindAppWorkflowByName(request.WorkflowName, request.AppId)
 		if err != nil {
-			handler.logger.Errorw("error in fetching workflow by name", "err", err, "workflowName", workflowName, "appId", appId)
+			handler.logger.Errorw("error in fetching workflow by name", "err", err, "workflowName", request.WorkflowName, "appId", request.AppId)
+			return nil, err, http.StatusInternalServerError
+		}
+		workflowsList = []appWorkflow.AppWorkflowDto{workflow}
+	} else if request.WorkflowId > 0 {
+		workflow, err := handler.appWorkflowService.FindAppWorkflowById(request.WorkflowId, request.AppId)
+		if err != nil {
+			handler.logger.Errorw("error in fetching workflow by id", "err", err, "workflowName", request.WorkflowName, "appId", request.AppId)
 			return nil, err, http.StatusInternalServerError
 		}
 		workflowsList = []appWorkflow.AppWorkflowDto{workflow}
 	} else {
-		workflowsList, err = handler.appWorkflowService.FindAppWorkflows(appId)
+		workflowsList, err = handler.appWorkflowService.FindAppWorkflows(request.AppId)
 		if err != nil {
 			handler.logger.Errorw("error in fetching workflows for app in GetAppAllDetail", "err", err)
 			return nil, err, http.StatusInternalServerError
@@ -642,13 +650,13 @@ func (handler CoreAppRestHandlerImpl) buildAppWorkflows(appId int, workflowName
 			if workflowMapping.Type == appWorkflow2.CIPIPELINE {
 				ciPipeline, err := handler.pipelineBuilder.GetCiPipelineById(workflowMapping.ComponentId)
 				if err != nil {
-					handler.logger.Errorw("service err, GetCiPipelineById in GetAppAllDetail", "err", err, "appId", appId)
+					handler.logger.Errorw("service err, GetCiPipelineById in GetAppAllDetail", "err", err, "appId", request.AppId)
 					return nil, err, http.StatusInternalServerError
 				}
 
-				ciPipelineResp, err := handler.buildCiPipelineResp(appId, ciPipeline)
+				ciPipelineResp, err := handler.buildCiPipelineResp(request.AppId, ciPipeline)
 				if err != nil {
-					handler.logger.Errorw("service err, buildCiPipelineResp in GetAppAllDetail", "err", err, "appId", appId)
+					handler.logger.Errorw("service err, buildCiPipelineResp in GetAppAllDetail", "err", err, "appId", request.AppId)
 					return nil, err, http.StatusInternalServerError
 				}
 				workflowResp.CiPipeline = ciPipelineResp
@@ -657,13 +665,16 @@ func (handler CoreAppRestHandlerImpl) buildAppWorkflows(appId int, workflowName
 			if workflowMapping.Type == appWorkflow2.CDPIPELINE {
 				cdPipeline, err := handler.pipelineBuilder.GetCdPipelineById(workflowMapping.ComponentId)
 				if err != nil {
-					handler.logger.Errorw("service err, GetCdPipelineById in GetAppAllDetail", "err", err, "appId", appId)
+					handler.logger.Errorw("service err, GetCdPipelineById in GetAppAllDetail", "err", err, "appId", request.AppId)
 					return nil, err, http.StatusInternalServerError
 				}
-
-				cdPipelineResp, err := handler.buildCdPipelineResp(appId, cdPipeline)
+				if request.EnvironmentId > 0 && request.EnvironmentId != cdPipeline.EnvironmentId {
+					// if environment id present in request it should match cd pipeline, else skip
+					continue
+				}
+				cdPipelineResp, err := handler.buildCdPipelineResp(request.AppId, cdPipeline)
 				if err != nil {
-					handler.logger.Errorw("service err, buildCdPipelineResp in GetAppAllDetail", "err", err, "appId", appId)
+					handler.logger.Errorw("service err, buildCdPipelineResp in GetAppAllDetail", "err", err, "appId", request.AppId)
 					return nil, err, http.StatusInternalServerError
 				}
 				cdPipelinesResp = append(cdPipelinesResp, cdPipelineResp)
@@ -1050,42 +1061,63 @@ func (handler CoreAppRestHandlerImpl) buildEnvironmentOverrides(ctx context.Cont
 	environmentOverrides := make(map[string]*appBean.EnvironmentOverride)
 	if len(appEnvironments) > 0 {
 		for _, appEnvironment := range appEnvironments {
-
-			envId := appEnvironment.EnvironmentId
-
-			//check RBAC for environment
-			object := handler.enforcerUtil.GetEnvRBACNameByAppId(appId, envId)
-			if ok := handler.enforcer.Enforce(token, casbin.ResourceEnvironment, casbin.ActionUpdate, object); !ok {
-				handler.logger.Errorw("Unauthorized User for env update action", "err", err, "appId", appId, "envId", envId)
-				return nil, fmt.Errorf("unauthorized user"), http.StatusForbidden
-			}
-			//RBAC end
-
-			envDeploymentTemplateResp, err, statusCode := handler.buildAppEnvironmentDeploymentTemplate(appId, envId)
-			if err != nil {
-				return nil, err, statusCode
-			}
-
-			envSecretsResp, err, statusCode := handler.buildAppEnvironmentSecrets(appId, envId)
-			if err != nil {
-				return nil, err, statusCode
-			}
-
-			envConfigMapsResp, err, statusCode := handler.buildAppEnvironmentConfigMaps(appId, envId)
+			environmentOverride, err, _ := handler.buildEnvironmentOverride(appId, appEnvironment.EnvironmentId, token)
 			if err != nil {
-				return nil, err, statusCode
+				handler.logger.Errorw("service err", "err", err)
+				return nil, err, http.StatusInternalServerError
 			}
-
+			override := environmentOverride[appEnvironment.EnvironmentName]
 			environmentOverrides[appEnvironment.EnvironmentName] = &appBean.EnvironmentOverride{
-				Secrets:            envSecretsResp,
-				ConfigMaps:         envConfigMapsResp,
-				DeploymentTemplate: envDeploymentTemplateResp,
+				Secrets:            override.Secrets,
+				ConfigMaps:         override.ConfigMaps,
+				DeploymentTemplate: override.DeploymentTemplate,
 			}
 		}
 	}
 	return environmentOverrides, nil, http.StatusOK
 }
 
+// get/build environment overrides
+func (handler CoreAppRestHandlerImpl) buildEnvironmentOverride(appId int, environmentId int, token string) (map[string]*appBean.EnvironmentOverride, error, int) {
+	handler.logger.Debugw("Getting app detail - env override", "appId", appId)
+	environmentOverrides := make(map[string]*appBean.EnvironmentOverride)
+	//check RBAC for environment
+	object := handler.enforcerUtil.GetEnvRBACNameByAppId(appId, environmentId)
+	if ok := handler.enforcer.Enforce(token, casbin.ResourceEnvironment, casbin.ActionUpdate, object); !ok {
+		handler.logger.Errorw("Unauthorized User for env update action", "appId", appId, "envId", environmentId)
+		return nil, fmt.Errorf("unauthorized user"), http.StatusForbidden
+	}
+	//RBAC end
+
+	environment, err := handler.environmentRepository.FindById(environmentId)
+	if err != nil {
+		handler.logger.Errorw("service err, for fetching environment model", "err", err, "appId", appId)
+		return nil, err, http.StatusInternalServerError
+	}
+
+	envDeploymentTemplateResp, err, statusCode := handler.buildAppEnvironmentDeploymentTemplate(appId, environmentId)
+	if err != nil {
+		return nil, err, statusCode
+	}
+	envSecretsResp, err, statusCode := handler.buildAppEnvironmentSecrets(appId, environmentId)
+	if err != nil {
+		return nil, err, statusCode
+	}
+
+	envConfigMapsResp, err, statusCode := handler.buildAppEnvironmentConfigMaps(appId, environmentId)
+	if err != nil {
+		return nil, err, statusCode
+	}
+
+	environmentOverrides[environment.Name] = &appBean.EnvironmentOverride{
+		Secrets:            envSecretsResp,
+		ConfigMaps:         envConfigMapsResp,
+		DeploymentTemplate: envDeploymentTemplateResp,
+	}
+
+	return environmentOverrides, nil, http.StatusOK
+}
+
 //GetApp related methods ends
 
 //Create App related methods starts
@@ -2166,7 +2198,8 @@ func (handler CoreAppRestHandlerImpl) GetAppWorkflow(w http.ResponseWriter, r *h
 
 	//get/build app workflows starts
 	//using empty workflow name because it is optional, if not provided then workflows will be fetched on the basis of app
-	appWorkflows, err, statusCode := handler.buildAppWorkflows(appId, WORKFLOW_NAME_EMPTY)
+	wfCloneRequest := &appWorkflow.WorkflowCloneRequest{AppId: appId}
+	appWorkflows, err, statusCode := handler.buildAppWorkflows(wfCloneRequest)
 	if err != nil {
 		common.WriteJsonResp(w, err, nil, statusCode)
 		return
@@ -2214,24 +2247,39 @@ func (handler CoreAppRestHandlerImpl) GetAppWorkflowAndOverridesSample(w http.Re
 		common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
 		return
 	}
+	wfCloneRequest := &appWorkflow.WorkflowCloneRequest{AppId: appId}
 	workflowName := r.URL.Query().Get("workflowName")
+	wfCloneRequest.WorkflowName = workflowName
+	environmentIdStr := r.URL.Query().Get("environmentId")
+	if len(environmentIdStr) > 0 {
+		environmentId, err := strconv.Atoi(environmentIdStr)
+		if err != nil {
+			handler.logger.Errorw("error on GetAppWorkflowAndOverridesSample", "err", err)
+			common.WriteJsonResp(w, err, "invalid environmentId", http.StatusBadRequest)
+			return
+		}
+		wfCloneRequest.EnvironmentId = environmentId
+	}
+	workflowIdStr := r.URL.Query().Get("workflowId")
+	if len(workflowIdStr) > 0 {
+		workflowId, err := strconv.Atoi(workflowIdStr)
+		if err != nil {
+			handler.logger.Errorw("error on GetAppWorkflowAndOverridesSample", "err", err)
+			common.WriteJsonResp(w, err, "invalid workflowId", http.StatusBadRequest)
+			return
+		}
+		wfCloneRequest.WorkflowId = workflowId
+	}
 	token := r.Header.Get("token")
 	//get/build app workflows starts
-	appWorkflows, err, statusCode := handler.buildAppWorkflows(appId, workflowName)
+	appWorkflows, err, statusCode := handler.buildAppWorkflows(wfCloneRequest)
 	if err != nil {
+		handler.logger.Errorw("error on GetAppWorkflowAndOverridesSample", "err", err)
 		common.WriteJsonResp(w, err, nil, statusCode)
 		return
 	}
 	//get/build app workflows ends
 
-	//get/build environment override starts
-	environmentOverrides, err, statusCode := handler.buildEnvironmentOverrides(r.Context(), appId, token)
-	if err != nil {
-		common.WriteJsonResp(w, err, nil, statusCode)
-		return
-	}
-	//get/build environment override ends
-
 	//build full object for response
 	appDetail := &appBean.AppWorkflowCloneDto{
 		AppId:   appId,
@@ -2243,6 +2291,19 @@ func (handler CoreAppRestHandlerImpl) GetAppWorkflowAndOverridesSample(w http.Re
 		appDetail.AppWorkflows = aw
 	}
 
+	//get/build environment override starts
+	environmentOverrides := make(map[string]*appBean.EnvironmentOverride)
+	if wfCloneRequest.EnvironmentId > 0 {
+		environmentOverrides, err, _ = handler.buildEnvironmentOverride(appId, wfCloneRequest.EnvironmentId, token)
+	} else {
+		environmentOverrides, err, _ = handler.buildEnvironmentOverrides(r.Context(), appId, token)
+	}
+	if err != nil {
+		handler.logger.Errorw("error on GetAppWorkflowAndOverridesSample", "err", err)
+		common.WriteJsonResp(w, err, nil, statusCode)
+		return
+	}
+	//get/build environment override ends
 	if environmentOverrides != nil && len(environmentOverrides) > 0 {
 		eo := make(map[string]*appBean.EnvironmentOverride)
 		for k, v := range environmentOverrides {
@@ -2251,7 +2312,6 @@ func (handler CoreAppRestHandlerImpl) GetAppWorkflowAndOverridesSample(w http.Re
 		}
 		appDetail.EnvironmentOverrides = eo
 	}
-
 	//end
 
 	common.WriteJsonResp(w, nil, appDetail, http.StatusOK)
diff --git a/api/restHandler/app/BuildPipelineRestHandler.go b/api/restHandler/app/BuildPipelineRestHandler.go
index 464d4a28a..89b373364 100644
--- a/api/restHandler/app/BuildPipelineRestHandler.go
+++ b/api/restHandler/app/BuildPipelineRestHandler.go
@@ -301,7 +301,9 @@ func (handler PipelineConfigRestHandlerImpl) PatchCiPipelines(w http.ResponseWri
 			return
 		}
 	}
-
+	if app.AppType == helper.Job {
+		patchRequest.IsJob = true
+	}
 	createResp, err := handler.pipelineBuilder.PatchCiPipeline(&patchRequest)
 	if err != nil {
 		handler.Logger.Errorw("service err, PatchCiPipelines", "err", err, "PatchCiPipelines", patchRequest)
diff --git a/api/router/ConfigMapRouter.go b/api/router/ConfigMapRouter.go
index 4930316fb..39d4ecfb8 100644
--- a/api/router/ConfigMapRouter.go
+++ b/api/router/ConfigMapRouter.go
@@ -35,6 +35,7 @@ func NewConfigMapRouterImpl(restHandler restHandler.ConfigMapRestHandler) *Confi
 }
 
 func (router ConfigMapRouterImpl) initConfigMapRouter(configRouter *mux.Router) {
+
 	configRouter.Path("/global/cm").
 		HandlerFunc(router.restHandler.CMGlobalAddUpdate).Methods("POST")
 	configRouter.Path("/environment/cm").
@@ -76,5 +77,11 @@ func (router ConfigMapRouterImpl) initConfigMapRouter(configRouter *mux.Router)
 		HandlerFunc(router.restHandler.CSEnvironmentFetchForEdit).Methods("GET")
 
 	configRouter.Path("/bulk/patch").HandlerFunc(router.restHandler.ConfigSecretBulkPatch).Methods("POST")
+	configRouter.Path("/environment").
+		HandlerFunc(router.restHandler.AddEnvironmentToJob).Methods("POST")
+	configRouter.Path("/environment").
+		HandlerFunc(router.restHandler.RemoveEnvironmentFromJob).Methods("DELETE")
+	configRouter.Path("/environment/{appId}").
+		HandlerFunc(router.restHandler.GetEnvironmentsForJob).Methods("GET")
 
 }
diff --git a/docs/setup/install/ingress-setup.md b/docs/setup/install/ingress-setup.md
index 484fe09f7..8204bc4be 100644
--- a/docs/setup/install/ingress-setup.md
+++ b/docs/setup/install/ingress-setup.md
@@ -93,31 +93,30 @@ metadata:
 spec:
   ingressClassName: nginx
   rules:
-  - http:
-      paths:
-      - backend:
-          service:
-            name: devtron-service
-            port:
-              number: 80
-        host: devtron.example.com
-        path: /orchestrator
-        pathType: ImplementationSpecific 
-      - backend:
-          service:
-            name: devtron-service
-            port:
-              number: 80
-        host: devtron.example.com
-        path: /dashboard
-        pathType: ImplementationSpecific
-      - backend:
-          service:
-            name: devtron-service
-            port:
-              number: 80
-        path: /grafana
-        pathType: ImplementationSpecific  
+    - host: devtron.example.com
+      http:
+        paths:
+          - backend:
+              service:
+                name: devtron-service
+                port:
+                  number: 80
+            path: /orchestrator
+            pathType: ImplementationSpecific
+          - backend:
+              service:
+                name: devtron-service
+                port:
+                  number: 80
+            path: /dashboard
+            pathType: ImplementationSpecific
+          - backend:
+              service:
+                name: devtron-service
+                port:
+                  number: 80
+            path: /grafana
+            pathType: ImplementationSpecific
 ```
 
 ## Enable HTTPS For Devtron
diff --git a/internal/sql/repository/AppListingRepository.go b/internal/sql/repository/AppListingRepository.go
index 5adb3d4f7..8c0ab9ef8 100644
--- a/internal/sql/repository/AppListingRepository.go
+++ b/internal/sql/repository/AppListingRepository.go
@@ -25,6 +25,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"github.com/devtron-labs/devtron/internal/middleware"
+	repository2 "github.com/devtron-labs/devtron/pkg/cluster/repository"
 	"go.opentelemetry.io/otel"
 	"strconv"
 	"strings"
@@ -38,7 +39,7 @@ import (
 
 type AppListingRepository interface {
 	FetchAppsByEnvironment(appListingFilter helper.AppListingFilter) ([]*bean.AppEnvironmentContainer, error)
-	FetchJobs(appIds []int, statuses []string, sortOrder string) ([]*bean.JobListingContainer, error)
+	FetchJobs(appIds []int, statuses []string, environmentIds []int, sortOrder string) ([]*bean.JobListingContainer, error)
 	FetchOverviewCiPipelines(jobId int) ([]*bean.JobListingContainer, error)
 	FetchJobsLastSucceededOn(ciPipelineIDs []int) ([]*bean.CiPipelineLastSucceededTime, error)
 	FetchAppDetail(ctx context.Context, appId int, envId int) (bean.AppDetailContainer, error)
@@ -80,18 +81,19 @@ type AppListingRepositoryImpl struct {
 	dbConnection                     *pg.DB
 	Logger                           *zap.SugaredLogger
 	appListingRepositoryQueryBuilder helper.AppListingRepositoryQueryBuilder
+	environmentRepository            repository2.EnvironmentRepository
 }
 
-func NewAppListingRepositoryImpl(Logger *zap.SugaredLogger, dbConnection *pg.DB, appListingRepositoryQueryBuilder helper.AppListingRepositoryQueryBuilder) *AppListingRepositoryImpl {
-	return &AppListingRepositoryImpl{dbConnection: dbConnection, Logger: Logger, appListingRepositoryQueryBuilder: appListingRepositoryQueryBuilder}
+func NewAppListingRepositoryImpl(Logger *zap.SugaredLogger, dbConnection *pg.DB, appListingRepositoryQueryBuilder helper.AppListingRepositoryQueryBuilder, environmentRepository repository2.EnvironmentRepository) *AppListingRepositoryImpl {
+	return &AppListingRepositoryImpl{dbConnection: dbConnection, Logger: Logger, appListingRepositoryQueryBuilder: appListingRepositoryQueryBuilder, environmentRepository: environmentRepository}
 }
 
-func (impl AppListingRepositoryImpl) FetchJobs(appIds []int, statuses []string, sortOrder string) ([]*bean.JobListingContainer, error) {
+func (impl AppListingRepositoryImpl) FetchJobs(appIds []int, statuses []string, environmentIds []int, sortOrder string) ([]*bean.JobListingContainer, error) {
 	var jobContainers []*bean.JobListingContainer
 	if len(appIds) == 0 {
 		return jobContainers, nil
 	}
-	jobsQuery := impl.appListingRepositoryQueryBuilder.BuildJobListingQuery(appIds, statuses, sortOrder)
+	jobsQuery := impl.appListingRepositoryQueryBuilder.BuildJobListingQuery(appIds, statuses, environmentIds, sortOrder)
 
 	impl.Logger.Debugw("basic app detail query: ", jobsQuery)
 	_, appsErr := impl.dbConnection.Query(&jobContainers, jobsQuery)
@@ -99,6 +101,7 @@ func (impl AppListingRepositoryImpl) FetchJobs(appIds []int, statuses []string,
 		impl.Logger.Error(appsErr)
 		return jobContainers, appsErr
 	}
+	jobContainers = impl.extractEnvironmentNameFromId(jobContainers)
 	return jobContainers, nil
 }
 func (impl AppListingRepositoryImpl) FetchOverviewCiPipelines(jobId int) ([]*bean.JobListingContainer, error) {
@@ -110,6 +113,7 @@ func (impl AppListingRepositoryImpl) FetchOverviewCiPipelines(jobId int) ([]*bea
 		impl.Logger.Error(appsErr)
 		return jobContainers, appsErr
 	}
+	jobContainers = impl.extractEnvironmentNameFromId(jobContainers)
 	return jobContainers, nil
 }
 
@@ -697,3 +701,33 @@ func (impl AppListingRepositoryImpl) FindAppCount(isProd bool) (int, error) {
 
 	return count, nil
 }
+
+func (impl AppListingRepositoryImpl) extractEnvironmentNameFromId(jobContainers []*bean.JobListingContainer) []*bean.JobListingContainer {
+	var envIds []*int
+	for _, job := range jobContainers {
+		if job.EnvironmentId != 0 {
+			envIds = append(envIds, &job.EnvironmentId)
+		}
+		if job.LastTriggeredEnvironmentId != 0 {
+			envIds = append(envIds, &job.LastTriggeredEnvironmentId)
+		}
+	}
+	envs, _ := impl.environmentRepository.FindByIds(envIds)
+
+	envIdNameMap := make(map[int]string)
+
+	for _, env := range envs {
+		envIdNameMap[env.Id] = env.Name
+	}
+
+	for _, job := range jobContainers {
+		if job.EnvironmentId != 0 {
+			job.EnvironmentName = envIdNameMap[job.EnvironmentId]
+		}
+		if job.LastTriggeredEnvironmentId != 0 {
+			job.LastTriggeredEnvironmentName = envIdNameMap[job.LastTriggeredEnvironmentId]
+		}
+	}
+
+	return jobContainers
+}
diff --git a/internal/sql/repository/app/mocks/AppRepository.go b/internal/sql/repository/app/mocks/AppRepository.go
index 7881806c5..2214fda58 100644
--- a/internal/sql/repository/app/mocks/AppRepository.go
+++ b/internal/sql/repository/app/mocks/AppRepository.go
@@ -1,9 +1,11 @@
-// Code generated by mockery v2.14.0. DO NOT EDIT.
+// Code generated by mockery v2.20.0. DO NOT EDIT.
 
 package mocks
 
 import (
 	app "github.com/devtron-labs/devtron/internal/sql/repository/app"
+	helper "github.com/devtron-labs/devtron/internal/sql/repository/helper"
+
 	mock "github.com/stretchr/testify/mock"
 
 	pg "github.com/go-pg/pg"
@@ -19,6 +21,10 @@ func (_m *AppRepository) CheckAppExists(appNames []string) ([]*app.App, error) {
 	ret := _m.Called(appNames)
 
 	var r0 []*app.App
+	var r1 error
+	if rf, ok := ret.Get(0).(func([]string) ([]*app.App, error)); ok {
+		return rf(appNames)
+	}
 	if rf, ok := ret.Get(0).(func([]string) []*app.App); ok {
 		r0 = rf(appNames)
 	} else {
@@ -27,7 +33,6 @@ func (_m *AppRepository) CheckAppExists(appNames []string) ([]*app.App, error) {
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func([]string) error); ok {
 		r1 = rf(appNames)
 	} else {
@@ -37,11 +42,93 @@ func (_m *AppRepository) CheckAppExists(appNames []string) ([]*app.App, error) {
 	return r0, r1
 }
 
+// FetchAllActiveDevtronAppsWithAppIdAndName provides a mock function with given fields:
+func (_m *AppRepository) FetchAllActiveDevtronAppsWithAppIdAndName() ([]*app.App, error) {
+	ret := _m.Called()
+
+	var r0 []*app.App
+	var r1 error
+	if rf, ok := ret.Get(0).(func() ([]*app.App, error)); ok {
+		return rf()
+	}
+	if rf, ok := ret.Get(0).(func() []*app.App); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]*app.App)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func() error); ok {
+		r1 = rf()
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// FetchAllActiveInstalledAppsWithAppIdAndName provides a mock function with given fields:
+func (_m *AppRepository) FetchAllActiveInstalledAppsWithAppIdAndName() ([]*app.App, error) {
+	ret := _m.Called()
+
+	var r0 []*app.App
+	var r1 error
+	if rf, ok := ret.Get(0).(func() ([]*app.App, error)); ok {
+		return rf()
+	}
+	if rf, ok := ret.Get(0).(func() []*app.App); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]*app.App)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func() error); ok {
+		r1 = rf()
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// FetchAppIdsWithFilter provides a mock function with given fields: jobListingFilter
+func (_m *AppRepository) FetchAppIdsWithFilter(jobListingFilter helper.AppListingFilter) ([]int, error) {
+	ret := _m.Called(jobListingFilter)
+
+	var r0 []int
+	var r1 error
+	if rf, ok := ret.Get(0).(func(helper.AppListingFilter) ([]int, error)); ok {
+		return rf(jobListingFilter)
+	}
+	if rf, ok := ret.Get(0).(func(helper.AppListingFilter) []int); ok {
+		r0 = rf(jobListingFilter)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]int)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(helper.AppListingFilter) error); ok {
+		r1 = rf(jobListingFilter)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
 // FetchAppsByFilterV2 provides a mock function with given fields: appNameIncludes, appNameExcludes, environmentId
 func (_m *AppRepository) FetchAppsByFilterV2(appNameIncludes string, appNameExcludes string, environmentId int) ([]*app.App, error) {
 	ret := _m.Called(appNameIncludes, appNameExcludes, environmentId)
 
 	var r0 []*app.App
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string, string, int) ([]*app.App, error)); ok {
+		return rf(appNameIncludes, appNameExcludes, environmentId)
+	}
 	if rf, ok := ret.Get(0).(func(string, string, int) []*app.App); ok {
 		r0 = rf(appNameIncludes, appNameExcludes, environmentId)
 	} else {
@@ -50,7 +137,6 @@ func (_m *AppRepository) FetchAppsByFilterV2(appNameIncludes string, appNameExcl
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(string, string, int) error); ok {
 		r1 = rf(appNameIncludes, appNameExcludes, environmentId)
 	} else {
@@ -60,11 +146,41 @@ func (_m *AppRepository) FetchAppsByFilterV2(appNameIncludes string, appNameExcl
 	return r0, r1
 }
 
+// FindActiveById provides a mock function with given fields: id
+func (_m *AppRepository) FindActiveById(id int) (*app.App, error) {
+	ret := _m.Called(id)
+
+	var r0 *app.App
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) (*app.App, error)); ok {
+		return rf(id)
+	}
+	if rf, ok := ret.Get(0).(func(int) *app.App); ok {
+		r0 = rf(id)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*app.App)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(int) error); ok {
+		r1 = rf(id)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
 // FindActiveByName provides a mock function with given fields: appName
 func (_m *AppRepository) FindActiveByName(appName string) (*app.App, error) {
 	ret := _m.Called(appName)
 
 	var r0 *app.App
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string) (*app.App, error)); ok {
+		return rf(appName)
+	}
 	if rf, ok := ret.Get(0).(func(string) *app.App); ok {
 		r0 = rf(appName)
 	} else {
@@ -73,7 +189,6 @@ func (_m *AppRepository) FindActiveByName(appName string) (*app.App, error) {
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(string) error); ok {
 		r1 = rf(appName)
 	} else {
@@ -88,6 +203,10 @@ func (_m *AppRepository) FindActiveListByName(appName string) ([]*app.App, error
 	ret := _m.Called(appName)
 
 	var r0 []*app.App
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string) ([]*app.App, error)); ok {
+		return rf(appName)
+	}
 	if rf, ok := ret.Get(0).(func(string) []*app.App); ok {
 		r0 = rf(appName)
 	} else {
@@ -96,7 +215,6 @@ func (_m *AppRepository) FindActiveListByName(appName string) ([]*app.App, error
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(string) error); ok {
 		r1 = rf(appName)
 	} else {
@@ -111,6 +229,10 @@ func (_m *AppRepository) FindAll() ([]*app.App, error) {
 	ret := _m.Called()
 
 	var r0 []*app.App
+	var r1 error
+	if rf, ok := ret.Get(0).(func() ([]*app.App, error)); ok {
+		return rf()
+	}
 	if rf, ok := ret.Get(0).(func() []*app.App); ok {
 		r0 = rf()
 	} else {
@@ -119,7 +241,6 @@ func (_m *AppRepository) FindAll() ([]*app.App, error) {
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func() error); ok {
 		r1 = rf()
 	} else {
@@ -134,6 +255,10 @@ func (_m *AppRepository) FindAllActiveAppsWithTeam() ([]*app.App, error) {
 	ret := _m.Called()
 
 	var r0 []*app.App
+	var r1 error
+	if rf, ok := ret.Get(0).(func() ([]*app.App, error)); ok {
+		return rf()
+	}
 	if rf, ok := ret.Get(0).(func() []*app.App); ok {
 		r0 = rf()
 	} else {
@@ -142,7 +267,6 @@ func (_m *AppRepository) FindAllActiveAppsWithTeam() ([]*app.App, error) {
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func() error); ok {
 		r1 = rf()
 	} else {
@@ -152,22 +276,77 @@ func (_m *AppRepository) FindAllActiveAppsWithTeam() ([]*app.App, error) {
 	return r0, r1
 }
 
-// FindAllMatchesByAppName provides a mock function with given fields: appName
-func (_m *AppRepository) FindAllMatchesByAppName(appName string) ([]*app.App, error) {
-	ret := _m.Called(appName)
+// FindAllActiveAppsWithTeamByAppNameMatch provides a mock function with given fields: appNameMatch
+func (_m *AppRepository) FindAllActiveAppsWithTeamByAppNameMatch(appNameMatch string) ([]*app.App, error) {
+	ret := _m.Called(appNameMatch)
 
 	var r0 []*app.App
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string) ([]*app.App, error)); ok {
+		return rf(appNameMatch)
+	}
 	if rf, ok := ret.Get(0).(func(string) []*app.App); ok {
-		r0 = rf(appName)
+		r0 = rf(appNameMatch)
 	} else {
 		if ret.Get(0) != nil {
 			r0 = ret.Get(0).([]*app.App)
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(string) error); ok {
-		r1 = rf(appName)
+		r1 = rf(appNameMatch)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// FindAllActiveAppsWithTeamWithTeamId provides a mock function with given fields: teamID
+func (_m *AppRepository) FindAllActiveAppsWithTeamWithTeamId(teamID int) ([]*app.App, error) {
+	ret := _m.Called(teamID)
+
+	var r0 []*app.App
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) ([]*app.App, error)); ok {
+		return rf(teamID)
+	}
+	if rf, ok := ret.Get(0).(func(int) []*app.App); ok {
+		r0 = rf(teamID)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]*app.App)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(int) error); ok {
+		r1 = rf(teamID)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// FindAllMatchesByAppName provides a mock function with given fields: appName, appType
+func (_m *AppRepository) FindAllMatchesByAppName(appName string, appType helper.AppType) ([]*app.App, error) {
+	ret := _m.Called(appName, appType)
+
+	var r0 []*app.App
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string, helper.AppType) ([]*app.App, error)); ok {
+		return rf(appName, appType)
+	}
+	if rf, ok := ret.Get(0).(func(string, helper.AppType) []*app.App); ok {
+		r0 = rf(appName, appType)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]*app.App)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(string, helper.AppType) error); ok {
+		r1 = rf(appName, appType)
 	} else {
 		r1 = ret.Error(1)
 	}
@@ -180,6 +359,10 @@ func (_m *AppRepository) FindAppAndProjectByAppId(appId int) (*app.App, error) {
 	ret := _m.Called(appId)
 
 	var r0 *app.App
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) (*app.App, error)); ok {
+		return rf(appId)
+	}
 	if rf, ok := ret.Get(0).(func(int) *app.App); ok {
 		r0 = rf(appId)
 	} else {
@@ -188,7 +371,6 @@ func (_m *AppRepository) FindAppAndProjectByAppId(appId int) (*app.App, error) {
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(int) error); ok {
 		r1 = rf(appId)
 	} else {
@@ -203,6 +385,10 @@ func (_m *AppRepository) FindAppAndProjectByAppName(appName string) (*app.App, e
 	ret := _m.Called(appName)
 
 	var r0 *app.App
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string) (*app.App, error)); ok {
+		return rf(appName)
+	}
 	if rf, ok := ret.Get(0).(func(string) *app.App); ok {
 		r0 = rf(appName)
 	} else {
@@ -211,7 +397,6 @@ func (_m *AppRepository) FindAppAndProjectByAppName(appName string) (*app.App, e
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(string) error); ok {
 		r1 = rf(appName)
 	} else {
@@ -221,11 +406,41 @@ func (_m *AppRepository) FindAppAndProjectByAppName(appName string) (*app.App, e
 	return r0, r1
 }
 
+// FindAppAndProjectByIdsIn provides a mock function with given fields: ids
+func (_m *AppRepository) FindAppAndProjectByIdsIn(ids []int) ([]*app.App, error) {
+	ret := _m.Called(ids)
+
+	var r0 []*app.App
+	var r1 error
+	if rf, ok := ret.Get(0).(func([]int) ([]*app.App, error)); ok {
+		return rf(ids)
+	}
+	if rf, ok := ret.Get(0).(func([]int) []*app.App); ok {
+		r0 = rf(ids)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]*app.App)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func([]int) error); ok {
+		r1 = rf(ids)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
 // FindAppsByEnvironmentId provides a mock function with given fields: environmentId
 func (_m *AppRepository) FindAppsByEnvironmentId(environmentId int) ([]app.App, error) {
 	ret := _m.Called(environmentId)
 
 	var r0 []app.App
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) ([]app.App, error)); ok {
+		return rf(environmentId)
+	}
 	if rf, ok := ret.Get(0).(func(int) []app.App); ok {
 		r0 = rf(environmentId)
 	} else {
@@ -234,7 +449,6 @@ func (_m *AppRepository) FindAppsByEnvironmentId(environmentId int) ([]app.App,
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(int) error); ok {
 		r1 = rf(environmentId)
 	} else {
@@ -249,6 +463,10 @@ func (_m *AppRepository) FindAppsByTeamId(teamId int) ([]*app.App, error) {
 	ret := _m.Called(teamId)
 
 	var r0 []*app.App
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) ([]*app.App, error)); ok {
+		return rf(teamId)
+	}
 	if rf, ok := ret.Get(0).(func(int) []*app.App); ok {
 		r0 = rf(teamId)
 	} else {
@@ -257,7 +475,6 @@ func (_m *AppRepository) FindAppsByTeamId(teamId int) ([]*app.App, error) {
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(int) error); ok {
 		r1 = rf(teamId)
 	} else {
@@ -272,6 +489,10 @@ func (_m *AppRepository) FindAppsByTeamIds(teamId []int, appType string) ([]app.
 	ret := _m.Called(teamId, appType)
 
 	var r0 []app.App
+	var r1 error
+	if rf, ok := ret.Get(0).(func([]int, string) ([]app.App, error)); ok {
+		return rf(teamId, appType)
+	}
 	if rf, ok := ret.Get(0).(func([]int, string) []app.App); ok {
 		r0 = rf(teamId, appType)
 	} else {
@@ -280,7 +501,6 @@ func (_m *AppRepository) FindAppsByTeamIds(teamId []int, appType string) ([]app.
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func([]int, string) error); ok {
 		r1 = rf(teamId, appType)
 	} else {
@@ -295,6 +515,10 @@ func (_m *AppRepository) FindAppsByTeamName(teamName string) ([]app.App, error)
 	ret := _m.Called(teamName)
 
 	var r0 []app.App
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string) ([]app.App, error)); ok {
+		return rf(teamName)
+	}
 	if rf, ok := ret.Get(0).(func(string) []app.App); ok {
 		r0 = rf(teamName)
 	} else {
@@ -303,7 +527,6 @@ func (_m *AppRepository) FindAppsByTeamName(teamName string) ([]app.App, error)
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(string) error); ok {
 		r1 = rf(teamName)
 	} else {
@@ -318,6 +541,10 @@ func (_m *AppRepository) FindById(id int) (*app.App, error) {
 	ret := _m.Called(id)
 
 	var r0 *app.App
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) (*app.App, error)); ok {
+		return rf(id)
+	}
 	if rf, ok := ret.Get(0).(func(int) *app.App); ok {
 		r0 = rf(id)
 	} else {
@@ -326,7 +553,6 @@ func (_m *AppRepository) FindById(id int) (*app.App, error) {
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(int) error); ok {
 		r1 = rf(id)
 	} else {
@@ -341,6 +567,10 @@ func (_m *AppRepository) FindByIds(ids []*int) ([]*app.App, error) {
 	ret := _m.Called(ids)
 
 	var r0 []*app.App
+	var r1 error
+	if rf, ok := ret.Get(0).(func([]*int) ([]*app.App, error)); ok {
+		return rf(ids)
+	}
 	if rf, ok := ret.Get(0).(func([]*int) []*app.App); ok {
 		r0 = rf(ids)
 	} else {
@@ -349,7 +579,6 @@ func (_m *AppRepository) FindByIds(ids []*int) ([]*app.App, error) {
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func([]*int) error); ok {
 		r1 = rf(ids)
 	} else {
@@ -359,22 +588,101 @@ func (_m *AppRepository) FindByIds(ids []*int) ([]*app.App, error) {
 	return r0, r1
 }
 
-// FindIdsByTeamIds provides a mock function with given fields: teamIds
-func (_m *AppRepository) FindIdsByTeamIds(teamIds []int) ([]int, error) {
-	ret := _m.Called(teamIds)
+// FindEnvironmentIdForInstalledApp provides a mock function with given fields: appId
+func (_m *AppRepository) FindEnvironmentIdForInstalledApp(appId int) (int, error) {
+	ret := _m.Called(appId)
+
+	var r0 int
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) (int, error)); ok {
+		return rf(appId)
+	}
+	if rf, ok := ret.Get(0).(func(int) int); ok {
+		r0 = rf(appId)
+	} else {
+		r0 = ret.Get(0).(int)
+	}
+
+	if rf, ok := ret.Get(1).(func(int) error); ok {
+		r1 = rf(appId)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// FindIdsByNames provides a mock function with given fields: appNames
+func (_m *AppRepository) FindIdsByNames(appNames []string) ([]int, error) {
+	ret := _m.Called(appNames)
+
+	var r0 []int
+	var r1 error
+	if rf, ok := ret.Get(0).(func([]string) ([]int, error)); ok {
+		return rf(appNames)
+	}
+	if rf, ok := ret.Get(0).(func([]string) []int); ok {
+		r0 = rf(appNames)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]int)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func([]string) error); ok {
+		r1 = rf(appNames)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// FindIdsByTeamIdsAndTeamNames provides a mock function with given fields: teamIds, teamNames
+func (_m *AppRepository) FindIdsByTeamIdsAndTeamNames(teamIds []int, teamNames []string) ([]int, error) {
+	ret := _m.Called(teamIds, teamNames)
 
 	var r0 []int
-	if rf, ok := ret.Get(0).(func([]int) []int); ok {
-		r0 = rf(teamIds)
+	var r1 error
+	if rf, ok := ret.Get(0).(func([]int, []string) ([]int, error)); ok {
+		return rf(teamIds, teamNames)
+	}
+	if rf, ok := ret.Get(0).(func([]int, []string) []int); ok {
+		r0 = rf(teamIds, teamNames)
 	} else {
 		if ret.Get(0) != nil {
 			r0 = ret.Get(0).([]int)
 		}
 	}
 
+	if rf, ok := ret.Get(1).(func([]int, []string) error); ok {
+		r1 = rf(teamIds, teamNames)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// FindJobByDisplayName provides a mock function with given fields: appName
+func (_m *AppRepository) FindJobByDisplayName(appName string) (*app.App, error) {
+	ret := _m.Called(appName)
+
+	var r0 *app.App
 	var r1 error
-	if rf, ok := ret.Get(1).(func([]int) error); ok {
-		r1 = rf(teamIds)
+	if rf, ok := ret.Get(0).(func(string) (*app.App, error)); ok {
+		return rf(appName)
+	}
+	if rf, ok := ret.Get(0).(func(string) *app.App); ok {
+		r0 = rf(appName)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*app.App)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(string) error); ok {
+		r1 = rf(appName)
 	} else {
 		r1 = ret.Error(1)
 	}
diff --git a/internal/sql/repository/chartConfig/ConfigMapRepository.go b/internal/sql/repository/chartConfig/ConfigMapRepository.go
index d9d9cfd32..69cd68013 100644
--- a/internal/sql/repository/chartConfig/ConfigMapRepository.go
+++ b/internal/sql/repository/chartConfig/ConfigMapRepository.go
@@ -20,7 +20,9 @@ package chartConfig
 import (
 	"github.com/devtron-labs/devtron/pkg/sql"
 	"github.com/go-pg/pg"
+	"github.com/go-pg/pg/orm"
 	"go.uber.org/zap"
+	"time"
 )
 
 type ConfigMapRepository interface {
@@ -101,10 +103,14 @@ type ConfigMapEnvModel struct {
 	EnvironmentId int      `sql:"environment_id,notnull"`
 	ConfigMapData string   `sql:"config_map_data"`
 	SecretData    string   `sql:"secret_data"`
+	Deleted       bool     `sql:"deleted,notnull"`
 	sql.AuditLog
 }
 
 func (impl ConfigMapRepositoryImpl) CreateEnvLevel(model *ConfigMapEnvModel) (*ConfigMapEnvModel, error) {
+	currentTime := time.Now()
+	model.CreatedOn = currentTime
+	model.UpdatedOn = currentTime
 	err := impl.dbConnection.Insert(model)
 	if err != nil {
 		impl.Logger.Errorw("err on config map ", "err;", err)
@@ -125,6 +131,7 @@ func (impl ConfigMapRepositoryImpl) GetAllEnvLevel() ([]ConfigMapEnvModel, error
 }
 
 func (impl ConfigMapRepositoryImpl) UpdateEnvLevel(model *ConfigMapEnvModel) (*ConfigMapEnvModel, error) {
+	model.UpdatedOn = time.Now()
 	err := impl.dbConnection.Update(model)
 	if err != nil {
 		impl.Logger.Errorw("err on config map ", "err;", err)
@@ -141,7 +148,12 @@ func (impl ConfigMapRepositoryImpl) GetByAppIdAndEnvIdEnvLevel(appId int, envId
 
 func (impl ConfigMapRepositoryImpl) GetEnvLevelByAppId(appId int) ([]*ConfigMapEnvModel, error) {
 	var models []*ConfigMapEnvModel
-	err := impl.dbConnection.Model(&models).Where("app_id = ?", appId).Select()
+	err := impl.dbConnection.Model(&models).
+		Where("app_id = ?", appId).
+		WhereGroup(func(query *orm.Query) (*orm.Query, error) {
+			query = query.WhereOr("deleted = ? ", false).WhereOr("deleted IS NULL")
+			return query, nil
+		}).Select()
 	if err != nil {
 		impl.Logger.Errorw("err in getting cm/cs env level", "err", err)
 		return models, err
diff --git a/internal/sql/repository/chartConfig/mocks/ConfigMapRepository.go b/internal/sql/repository/chartConfig/mocks/ConfigMapRepository.go
new file mode 100644
index 000000000..f69277808
--- /dev/null
+++ b/internal/sql/repository/chartConfig/mocks/ConfigMapRepository.go
@@ -0,0 +1,314 @@
+// Code generated by mockery v2.20.0. DO NOT EDIT.
+
+package mocks
+
+import (
+	chartConfig "github.com/devtron-labs/devtron/internal/sql/repository/chartConfig"
+	mock "github.com/stretchr/testify/mock"
+)
+
+// ConfigMapRepository is an autogenerated mock type for the ConfigMapRepository type
+type ConfigMapRepository struct {
+	mock.Mock
+}
+
+// CreateAppLevel provides a mock function with given fields: model
+func (_m *ConfigMapRepository) CreateAppLevel(model *chartConfig.ConfigMapAppModel) (*chartConfig.ConfigMapAppModel, error) {
+	ret := _m.Called(model)
+
+	var r0 *chartConfig.ConfigMapAppModel
+	var r1 error
+	if rf, ok := ret.Get(0).(func(*chartConfig.ConfigMapAppModel) (*chartConfig.ConfigMapAppModel, error)); ok {
+		return rf(model)
+	}
+	if rf, ok := ret.Get(0).(func(*chartConfig.ConfigMapAppModel) *chartConfig.ConfigMapAppModel); ok {
+		r0 = rf(model)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*chartConfig.ConfigMapAppModel)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(*chartConfig.ConfigMapAppModel) error); ok {
+		r1 = rf(model)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// CreateEnvLevel provides a mock function with given fields: model
+func (_m *ConfigMapRepository) CreateEnvLevel(model *chartConfig.ConfigMapEnvModel) (*chartConfig.ConfigMapEnvModel, error) {
+	ret := _m.Called(model)
+
+	var r0 *chartConfig.ConfigMapEnvModel
+	var r1 error
+	if rf, ok := ret.Get(0).(func(*chartConfig.ConfigMapEnvModel) (*chartConfig.ConfigMapEnvModel, error)); ok {
+		return rf(model)
+	}
+	if rf, ok := ret.Get(0).(func(*chartConfig.ConfigMapEnvModel) *chartConfig.ConfigMapEnvModel); ok {
+		r0 = rf(model)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*chartConfig.ConfigMapEnvModel)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(*chartConfig.ConfigMapEnvModel) error); ok {
+		r1 = rf(model)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetAllAppLevel provides a mock function with given fields:
+func (_m *ConfigMapRepository) GetAllAppLevel() ([]chartConfig.ConfigMapAppModel, error) {
+	ret := _m.Called()
+
+	var r0 []chartConfig.ConfigMapAppModel
+	var r1 error
+	if rf, ok := ret.Get(0).(func() ([]chartConfig.ConfigMapAppModel, error)); ok {
+		return rf()
+	}
+	if rf, ok := ret.Get(0).(func() []chartConfig.ConfigMapAppModel); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]chartConfig.ConfigMapAppModel)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func() error); ok {
+		r1 = rf()
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetAllEnvLevel provides a mock function with given fields:
+func (_m *ConfigMapRepository) GetAllEnvLevel() ([]chartConfig.ConfigMapEnvModel, error) {
+	ret := _m.Called()
+
+	var r0 []chartConfig.ConfigMapEnvModel
+	var r1 error
+	if rf, ok := ret.Get(0).(func() ([]chartConfig.ConfigMapEnvModel, error)); ok {
+		return rf()
+	}
+	if rf, ok := ret.Get(0).(func() []chartConfig.ConfigMapEnvModel); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]chartConfig.ConfigMapEnvModel)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func() error); ok {
+		r1 = rf()
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetByAppIdAndEnvIdEnvLevel provides a mock function with given fields: appId, envId
+func (_m *ConfigMapRepository) GetByAppIdAndEnvIdEnvLevel(appId int, envId int) (*chartConfig.ConfigMapEnvModel, error) {
+	ret := _m.Called(appId, envId)
+
+	var r0 *chartConfig.ConfigMapEnvModel
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int, int) (*chartConfig.ConfigMapEnvModel, error)); ok {
+		return rf(appId, envId)
+	}
+	if rf, ok := ret.Get(0).(func(int, int) *chartConfig.ConfigMapEnvModel); ok {
+		r0 = rf(appId, envId)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*chartConfig.ConfigMapEnvModel)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(int, int) error); ok {
+		r1 = rf(appId, envId)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetByAppIdAppLevel provides a mock function with given fields: appId
+func (_m *ConfigMapRepository) GetByAppIdAppLevel(appId int) (*chartConfig.ConfigMapAppModel, error) {
+	ret := _m.Called(appId)
+
+	var r0 *chartConfig.ConfigMapAppModel
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) (*chartConfig.ConfigMapAppModel, error)); ok {
+		return rf(appId)
+	}
+	if rf, ok := ret.Get(0).(func(int) *chartConfig.ConfigMapAppModel); ok {
+		r0 = rf(appId)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*chartConfig.ConfigMapAppModel)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(int) error); ok {
+		r1 = rf(appId)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetByIdAppLevel provides a mock function with given fields: id
+func (_m *ConfigMapRepository) GetByIdAppLevel(id int) (*chartConfig.ConfigMapAppModel, error) {
+	ret := _m.Called(id)
+
+	var r0 *chartConfig.ConfigMapAppModel
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) (*chartConfig.ConfigMapAppModel, error)); ok {
+		return rf(id)
+	}
+	if rf, ok := ret.Get(0).(func(int) *chartConfig.ConfigMapAppModel); ok {
+		r0 = rf(id)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*chartConfig.ConfigMapAppModel)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(int) error); ok {
+		r1 = rf(id)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetByIdEnvLevel provides a mock function with given fields: id
+func (_m *ConfigMapRepository) GetByIdEnvLevel(id int) (*chartConfig.ConfigMapEnvModel, error) {
+	ret := _m.Called(id)
+
+	var r0 *chartConfig.ConfigMapEnvModel
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) (*chartConfig.ConfigMapEnvModel, error)); ok {
+		return rf(id)
+	}
+	if rf, ok := ret.Get(0).(func(int) *chartConfig.ConfigMapEnvModel); ok {
+		r0 = rf(id)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*chartConfig.ConfigMapEnvModel)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(int) error); ok {
+		r1 = rf(id)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetEnvLevelByAppId provides a mock function with given fields: appId
+func (_m *ConfigMapRepository) GetEnvLevelByAppId(appId int) ([]*chartConfig.ConfigMapEnvModel, error) {
+	ret := _m.Called(appId)
+
+	var r0 []*chartConfig.ConfigMapEnvModel
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) ([]*chartConfig.ConfigMapEnvModel, error)); ok {
+		return rf(appId)
+	}
+	if rf, ok := ret.Get(0).(func(int) []*chartConfig.ConfigMapEnvModel); ok {
+		r0 = rf(appId)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]*chartConfig.ConfigMapEnvModel)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(int) error); ok {
+		r1 = rf(appId)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// UpdateAppLevel provides a mock function with given fields: model
+func (_m *ConfigMapRepository) UpdateAppLevel(model *chartConfig.ConfigMapAppModel) (*chartConfig.ConfigMapAppModel, error) {
+	ret := _m.Called(model)
+
+	var r0 *chartConfig.ConfigMapAppModel
+	var r1 error
+	if rf, ok := ret.Get(0).(func(*chartConfig.ConfigMapAppModel) (*chartConfig.ConfigMapAppModel, error)); ok {
+		return rf(model)
+	}
+	if rf, ok := ret.Get(0).(func(*chartConfig.ConfigMapAppModel) *chartConfig.ConfigMapAppModel); ok {
+		r0 = rf(model)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*chartConfig.ConfigMapAppModel)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(*chartConfig.ConfigMapAppModel) error); ok {
+		r1 = rf(model)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// UpdateEnvLevel provides a mock function with given fields: model
+func (_m *ConfigMapRepository) UpdateEnvLevel(model *chartConfig.ConfigMapEnvModel) (*chartConfig.ConfigMapEnvModel, error) {
+	ret := _m.Called(model)
+
+	var r0 *chartConfig.ConfigMapEnvModel
+	var r1 error
+	if rf, ok := ret.Get(0).(func(*chartConfig.ConfigMapEnvModel) (*chartConfig.ConfigMapEnvModel, error)); ok {
+		return rf(model)
+	}
+	if rf, ok := ret.Get(0).(func(*chartConfig.ConfigMapEnvModel) *chartConfig.ConfigMapEnvModel); ok {
+		r0 = rf(model)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*chartConfig.ConfigMapEnvModel)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(*chartConfig.ConfigMapEnvModel) error); ok {
+		r1 = rf(model)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+type mockConstructorTestingTNewConfigMapRepository interface {
+	mock.TestingT
+	Cleanup(func())
+}
+
+// NewConfigMapRepository creates a new instance of ConfigMapRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+func NewConfigMapRepository(t mockConstructorTestingTNewConfigMapRepository) *ConfigMapRepository {
+	mock := &ConfigMapRepository{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/internal/sql/repository/helper/AppListingRepositoryQueryBuilder.go b/internal/sql/repository/helper/AppListingRepositoryQueryBuilder.go
index 776b911d1..5182cf1d2 100644
--- a/internal/sql/repository/helper/AppListingRepositoryQueryBuilder.go
+++ b/internal/sql/repository/helper/AppListingRepositoryQueryBuilder.go
@@ -70,13 +70,14 @@ const (
 	LastDeployedSortBy        = "lastDeployedSort"
 )
 
-func (impl AppListingRepositoryQueryBuilder) BuildJobListingQuery(appIDs []int, statuses []string, sortOrder string) string {
+func (impl AppListingRepositoryQueryBuilder) BuildJobListingQuery(appIDs []int, statuses []string, environmentIds []int, sortOrder string) string {
 	query := "select ci_pipeline.name as ci_pipeline_name,ci_pipeline.id as ci_pipeline_id,app.id as job_id,app.display_name " +
-		"as job_name,app.description,cwr.started_on,cwr.status from app left join ci_pipeline on" +
-		" app.id = ci_pipeline.app_id and ci_pipeline.active=true left join (select cw.ci_pipeline_id, cw.status, cw.started_on " +
-		"  from ci_workflow cw inner join (select ci_pipeline_id, MAX(started_on) max_started_on from ci_workflow group by ci_pipeline_id ) " +
+		"as job_name,app.description,cwr.started_on,cwr.status,cem.environment_id,cwr.environment_id as last_triggered_environment_id from app left join ci_pipeline on" +
+		" app.id = ci_pipeline.app_id and ci_pipeline.active=true left join (select cw.ci_pipeline_id, cw.status, cw.started_on, cw.environment_id " +
+		" from ci_workflow cw inner join (select ci_pipeline_id, MAX(started_on) max_started_on from ci_workflow group by ci_pipeline_id ) " +
 		"cws on cw.ci_pipeline_id = cws.ci_pipeline_id " +
-		"and cw.started_on = cws.max_started_on order by cw.ci_pipeline_id) cwr on cwr.ci_pipeline_id = ci_pipeline.id" +
+		"and cw.started_on = cws.max_started_on order by cw.ci_pipeline_id) cwr on cwr.ci_pipeline_id = ci_pipeline.id " +
+		"LEFT JOIN ci_env_mapping cem on cem.ci_pipeline_id = ci_pipeline.id " +
 		" where app.active = true and app.app_type = 2 "
 	if len(appIDs) > 0 {
 		query += "and app.id IN (" + GetCommaSepratedString(appIDs) + ") "
@@ -84,6 +85,9 @@ func (impl AppListingRepositoryQueryBuilder) BuildJobListingQuery(appIDs []int,
 	if len(statuses) > 0 {
 		query += "and cwr.status IN (" + util.ProcessAppStatuses(statuses) + ") "
 	}
+	if len(environmentIds) > 0 {
+		query += "and cwr.environment_id IN (" + GetCommaSepratedString(environmentIds) + ") "
+	}
 	query += " order by app.display_name"
 	if sortOrder == "DESC" {
 		query += " DESC "
@@ -92,11 +96,13 @@ func (impl AppListingRepositoryQueryBuilder) BuildJobListingQuery(appIDs []int,
 }
 func (impl AppListingRepositoryQueryBuilder) OverviewCiPipelineQuery() string {
 	query := "select ci_pipeline.id as ci_pipeline_id,ci_pipeline.name " +
-		"as ci_pipeline_name,cwr.status,cwr.started_on from ci_pipeline" +
-		" left join (select cw.ci_pipeline_id,cw.status,cw.started_on from ci_workflow cw" +
+		"as ci_pipeline_name,cwr.status,cwr.started_on,cem.environment_id,cwr.environment_id as last_triggered_environment_id from ci_pipeline" +
+		" left join (select cw.ci_pipeline_id,cw.status,cw.started_on,cw.environment_id from ci_workflow cw" +
 		" inner join (SELECT  ci_pipeline_id, MAX(started_on) max_started_on FROM ci_workflow GROUP BY ci_pipeline_id)" +
 		" cws on cw.ci_pipeline_id = cws.ci_pipeline_id and cw.started_on = cws.max_started_on order by cw.ci_pipeline_id)" +
-		" cwr on cwr.ci_pipeline_id = ci_pipeline.id where ci_pipeline.active = true and ci_pipeline.app_id = ? ;"
+		" cwr on cwr.ci_pipeline_id = ci_pipeline.id" +
+		" LEFT JOIN ci_env_mapping cem on cem.ci_pipeline_id = ci_pipeline.id " +
+		"where ci_pipeline.active = true and ci_pipeline.app_id = ? ;"
 	return query
 }
 
diff --git a/internal/sql/repository/imageTagging/ImageTaggingRepository.go b/internal/sql/repository/imageTagging/ImageTaggingRepository.go
index 65993ec72..680a11afd 100644
--- a/internal/sql/repository/imageTagging/ImageTaggingRepository.go
+++ b/internal/sql/repository/imageTagging/ImageTaggingRepository.go
@@ -27,7 +27,7 @@ type ImageTaggingAction int
 
 const ActionSave ImageTaggingAction = 0
 
-//this action is only allowed for imageComments
+// this action is only allowed for imageComments
 const ActionEdit ImageTaggingAction = 1
 const ActionSoftDelete ImageTaggingAction = 2
 const ActionHardDelete ImageTaggingAction = 3
@@ -141,7 +141,7 @@ func (impl *ImageTaggingRepositoryImpl) GetImageCommentsByArtifactIds(artifactId
 	return res, err
 }
 
-//this will update the provided release tag
+// this will update the provided release tag
 func (impl *ImageTaggingRepositoryImpl) UpdateReleaseTagInBulk(tx *pg.Tx, imageTags []*ImageTag) error {
 	//currently tags are not editable, can only be soft deleted or hard delete
 	for _, imageTag := range imageTags {
diff --git a/internal/sql/repository/pipelineConfig/CiPipelineRepository.go b/internal/sql/repository/pipelineConfig/CiPipelineRepository.go
index a3e653bbc..d9a1e5666 100644
--- a/internal/sql/repository/pipelineConfig/CiPipelineRepository.go
+++ b/internal/sql/repository/pipelineConfig/CiPipelineRepository.go
@@ -19,6 +19,7 @@ package pipelineConfig
 
 import (
 	"github.com/devtron-labs/devtron/internal/sql/repository/app"
+	"github.com/devtron-labs/devtron/pkg/cluster/repository"
 	"github.com/devtron-labs/devtron/pkg/sql"
 	"github.com/go-pg/pg"
 	"github.com/go-pg/pg/orm"
@@ -48,6 +49,17 @@ type CiPipeline struct {
 	CiTemplate          *CiTemplate
 }
 
+type CiEnvMapping struct {
+	tableName     struct{} `sql:"ci_env_mapping" pg:",discard_unknown_columns"`
+	Id            int      `sql:"id,pk"`
+	EnvironmentId int      `sql:"environment_id"`
+	CiPipelineId  int      `sql:"ci_pipeline_id"`
+	Deleted       bool     `sql:"deleted,notnull"`
+	CiPipeline    CiPipeline
+	Environment   repository.Environment
+	sql.AuditLog
+}
+
 type ExternalCiPipeline struct {
 	tableName   struct{} `sql:"external_ci_pipeline" pg:",discard_unknown_columns"`
 	Id          int      `sql:"id,pk"`
@@ -72,6 +84,7 @@ type CiPipelineScript struct {
 
 type CiPipelineRepository interface {
 	Save(pipeline *CiPipeline, tx *pg.Tx) error
+	SaveCiEnvMapping(cienvmapping *CiEnvMapping, tx *pg.Tx) error
 	SaveExternalCi(pipeline *ExternalCiPipeline, tx *pg.Tx) (*ExternalCiPipeline, error)
 	UpdateExternalCi(pipeline *ExternalCiPipeline, tx *pg.Tx) (*ExternalCiPipeline, error)
 	FindExternalCiByCiPipelineId(ciPipelineId int) (*ExternalCiPipeline, error)
@@ -87,10 +100,12 @@ type CiPipelineRepository interface {
 	FindByAppIds(appIds []int) (pipelines []*CiPipeline, err error)
 	//find non deleted pipeline
 	FindById(id int) (pipeline *CiPipeline, err error)
+	FindCiEnvMappingByCiPipelineId(ciPipelineId int) (*CiEnvMapping, error)
 	FindParentCiPipelineMapByAppId(appId int) ([]*CiPipeline, []int, error)
 	FindByCiAndAppDetailsById(pipelineId int) (pipeline *CiPipeline, err error)
 	FindByIdsIn(ids []int) ([]*CiPipeline, error)
 	Update(pipeline *CiPipeline, tx *pg.Tx) error
+	UpdateCiEnvMapping(cienvmapping *CiEnvMapping, tx *pg.Tx) error
 	PipelineExistsByName(names []string) (found []string, err error)
 	FindByName(pipelineName string) (pipeline *CiPipeline, err error)
 	FindByParentCiPipelineId(parentCiPipelineId int) ([]*CiPipeline, error)
@@ -150,6 +165,13 @@ func (impl CiPipelineRepositoryImpl) UpdateExternalCi(pipeline *ExternalCiPipeli
 func (impl CiPipelineRepositoryImpl) Save(pipeline *CiPipeline, tx *pg.Tx) error {
 	return tx.Insert(pipeline)
 }
+func (impl CiPipelineRepositoryImpl) SaveCiEnvMapping(cienvmapping *CiEnvMapping, tx *pg.Tx) error {
+	return tx.Insert(cienvmapping)
+}
+func (impl CiPipelineRepositoryImpl) UpdateCiEnvMapping(cienvmapping *CiEnvMapping, tx *pg.Tx) error {
+	return tx.Update(cienvmapping)
+}
+
 func (impl CiPipelineRepositoryImpl) Update(pipeline *CiPipeline, tx *pg.Tx) error {
 	r, err := tx.Model(pipeline).WherePK().UpdateNotNull()
 	impl.logger.Debugf("total rows saved %d", r.RowsAffected())
@@ -267,6 +289,15 @@ func (impl CiPipelineRepositoryImpl) FindById(id int) (pipeline *CiPipeline, err
 
 	return pipeline, err
 }
+func (impl CiPipelineRepositoryImpl) FindCiEnvMappingByCiPipelineId(ciPipelineId int) (*CiEnvMapping, error) {
+	ciEnvMapping := &CiEnvMapping{}
+	err := impl.dbConnection.Model(ciEnvMapping).
+		Where("ci_pipeline_id= ?", ciPipelineId).
+		Where("deleted =? ", false).
+		Select()
+
+	return ciEnvMapping, err
+}
 
 func (impl CiPipelineRepositoryImpl) FindWithMinDataByCiPipelineId(id int) (pipeline *CiPipeline, err error) {
 	pipeline = &CiPipeline{Id: id}
diff --git a/internal/sql/repository/pipelineConfig/CiWorkflowRepository.go b/internal/sql/repository/pipelineConfig/CiWorkflowRepository.go
index 770e98aea..ae4a373f0 100644
--- a/internal/sql/repository/pipelineConfig/CiWorkflowRepository.go
+++ b/internal/sql/repository/pipelineConfig/CiWorkflowRepository.go
@@ -68,6 +68,7 @@ type CiWorkflow struct {
 	CiArtifactLocation string            `sql:"ci_artifact_location"`
 	PodName            string            `sql:"pod_name"`
 	CiBuildType        string            `sql:"ci_build_type"`
+	EnvironmentId      int               `sql:"environment_id"`
 	CiPipeline         *CiPipeline
 }
 
@@ -92,6 +93,8 @@ type WorkflowWithArtifact struct {
 	BlobStorageEnabled bool              `json:"blobStorageEnabled"`
 	CiBuildType        string            `json:"ci_build_type"`
 	IsArtifactUploaded bool              `json:"is_artifact_uploaded"`
+	EnvironmentId      int               `json:"environmentId"`
+	EnvironmentName    string            `json:"environmentName"`
 }
 
 type GitCommit struct {
@@ -163,7 +166,7 @@ func (impl *CiWorkflowRepositoryImpl) FindByStatusesIn(activeStatuses []string)
 
 func (impl *CiWorkflowRepositoryImpl) FindByPipelineId(pipelineId int, offset int, limit int) ([]WorkflowWithArtifact, error) {
 	var wfs []WorkflowWithArtifact
-	queryTemp := "select cia.id as ci_artifact_id, cia.image, cia.is_artifact_uploaded, wf.*, u.email_id from ci_workflow wf left join users u on u.id = wf.triggered_by left join ci_artifact cia on wf.id = cia.ci_workflow_id where wf.ci_pipeline_id = ? order by wf.started_on desc offset ? limit ?;"
+	queryTemp := "select cia.id as ci_artifact_id, env.environment_name, cia.image, cia.is_artifact_uploaded, wf.*, u.email_id from ci_workflow wf left join users u on u.id = wf.triggered_by left join ci_artifact cia on wf.id = cia.ci_workflow_id left join environment env on env.id = wf.environment_id where wf.ci_pipeline_id = ? order by wf.started_on desc offset ? limit ?;"
 	_, err := impl.dbConnection.Query(&wfs, queryTemp, pipelineId, offset, limit)
 	if err != nil {
 		return nil, err
diff --git a/internal/sql/repository/pipelineConfig/mocks/CiPipelineRepository.go b/internal/sql/repository/pipelineConfig/mocks/CiPipelineRepository.go
index 6993176dc..d062a059f 100644
--- a/internal/sql/repository/pipelineConfig/mocks/CiPipelineRepository.go
+++ b/internal/sql/repository/pipelineConfig/mocks/CiPipelineRepository.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.18.0. DO NOT EDIT.
+// Code generated by mockery v2.20.0. DO NOT EDIT.
 
 package mocks
 
@@ -19,6 +19,11 @@ func (_m *CiPipelineRepository) FetchCiPipelinesForDG(parentId int, childCiPipel
 	ret := _m.Called(parentId, childCiPipelineIds)
 
 	var r0 *pipelineConfig.CiPipeline
+	var r1 int
+	var r2 error
+	if rf, ok := ret.Get(0).(func(int, []int) (*pipelineConfig.CiPipeline, int, error)); ok {
+		return rf(parentId, childCiPipelineIds)
+	}
 	if rf, ok := ret.Get(0).(func(int, []int) *pipelineConfig.CiPipeline); ok {
 		r0 = rf(parentId, childCiPipelineIds)
 	} else {
@@ -27,14 +32,12 @@ func (_m *CiPipelineRepository) FetchCiPipelinesForDG(parentId int, childCiPipel
 		}
 	}
 
-	var r1 int
 	if rf, ok := ret.Get(1).(func(int, []int) int); ok {
 		r1 = rf(parentId, childCiPipelineIds)
 	} else {
 		r1 = ret.Get(1).(int)
 	}
 
-	var r2 error
 	if rf, ok := ret.Get(2).(func(int, []int) error); ok {
 		r2 = rf(parentId, childCiPipelineIds)
 	} else {
@@ -49,6 +52,10 @@ func (_m *CiPipelineRepository) FetchParentCiPipelinesForDG() ([]*pipelineConfig
 	ret := _m.Called()
 
 	var r0 []*pipelineConfig.CiPipelinesMap
+	var r1 error
+	if rf, ok := ret.Get(0).(func() ([]*pipelineConfig.CiPipelinesMap, error)); ok {
+		return rf()
+	}
 	if rf, ok := ret.Get(0).(func() []*pipelineConfig.CiPipelinesMap); ok {
 		r0 = rf()
 	} else {
@@ -57,7 +64,6 @@ func (_m *CiPipelineRepository) FetchParentCiPipelinesForDG() ([]*pipelineConfig
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func() error); ok {
 		r1 = rf()
 	} else {
@@ -72,6 +78,10 @@ func (_m *CiPipelineRepository) FinDByParentCiPipelineAndAppId(parentCiPipeline
 	ret := _m.Called(parentCiPipeline, appIds)
 
 	var r0 []*pipelineConfig.CiPipeline
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int, []int) ([]*pipelineConfig.CiPipeline, error)); ok {
+		return rf(parentCiPipeline, appIds)
+	}
 	if rf, ok := ret.Get(0).(func(int, []int) []*pipelineConfig.CiPipeline); ok {
 		r0 = rf(parentCiPipeline, appIds)
 	} else {
@@ -80,7 +90,6 @@ func (_m *CiPipelineRepository) FinDByParentCiPipelineAndAppId(parentCiPipeline
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(int, []int) error); ok {
 		r1 = rf(parentCiPipeline, appIds)
 	} else {
@@ -95,6 +104,10 @@ func (_m *CiPipelineRepository) FindAllPipelineInLast24Hour() ([]*pipelineConfig
 	ret := _m.Called()
 
 	var r0 []*pipelineConfig.CiPipeline
+	var r1 error
+	if rf, ok := ret.Get(0).(func() ([]*pipelineConfig.CiPipeline, error)); ok {
+		return rf()
+	}
 	if rf, ok := ret.Get(0).(func() []*pipelineConfig.CiPipeline); ok {
 		r0 = rf()
 	} else {
@@ -103,7 +116,6 @@ func (_m *CiPipelineRepository) FindAllPipelineInLast24Hour() ([]*pipelineConfig
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func() error); ok {
 		r1 = rf()
 	} else {
@@ -118,6 +130,10 @@ func (_m *CiPipelineRepository) FindAppAndProjectByCiPipelineIds(ciPipelineIds [
 	ret := _m.Called(ciPipelineIds)
 
 	var r0 []*pipelineConfig.CiPipeline
+	var r1 error
+	if rf, ok := ret.Get(0).(func([]int) ([]*pipelineConfig.CiPipeline, error)); ok {
+		return rf(ciPipelineIds)
+	}
 	if rf, ok := ret.Get(0).(func([]int) []*pipelineConfig.CiPipeline); ok {
 		r0 = rf(ciPipelineIds)
 	} else {
@@ -126,7 +142,6 @@ func (_m *CiPipelineRepository) FindAppAndProjectByCiPipelineIds(ciPipelineIds [
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func([]int) error); ok {
 		r1 = rf(ciPipelineIds)
 	} else {
@@ -141,6 +156,10 @@ func (_m *CiPipelineRepository) FindAppIdsForCiPipelineIds(pipelineIds []int) (m
 	ret := _m.Called(pipelineIds)
 
 	var r0 map[int]int
+	var r1 error
+	if rf, ok := ret.Get(0).(func([]int) (map[int]int, error)); ok {
+		return rf(pipelineIds)
+	}
 	if rf, ok := ret.Get(0).(func([]int) map[int]int); ok {
 		r0 = rf(pipelineIds)
 	} else {
@@ -149,7 +168,6 @@ func (_m *CiPipelineRepository) FindAppIdsForCiPipelineIds(pipelineIds []int) (m
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func([]int) error); ok {
 		r1 = rf(pipelineIds)
 	} else {
@@ -164,6 +182,10 @@ func (_m *CiPipelineRepository) FindByAppId(appId int) ([]*pipelineConfig.CiPipe
 	ret := _m.Called(appId)
 
 	var r0 []*pipelineConfig.CiPipeline
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) ([]*pipelineConfig.CiPipeline, error)); ok {
+		return rf(appId)
+	}
 	if rf, ok := ret.Get(0).(func(int) []*pipelineConfig.CiPipeline); ok {
 		r0 = rf(appId)
 	} else {
@@ -172,7 +194,6 @@ func (_m *CiPipelineRepository) FindByAppId(appId int) ([]*pipelineConfig.CiPipe
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(int) error); ok {
 		r1 = rf(appId)
 	} else {
@@ -187,6 +208,10 @@ func (_m *CiPipelineRepository) FindByAppIds(appIds []int) ([]*pipelineConfig.Ci
 	ret := _m.Called(appIds)
 
 	var r0 []*pipelineConfig.CiPipeline
+	var r1 error
+	if rf, ok := ret.Get(0).(func([]int) ([]*pipelineConfig.CiPipeline, error)); ok {
+		return rf(appIds)
+	}
 	if rf, ok := ret.Get(0).(func([]int) []*pipelineConfig.CiPipeline); ok {
 		r0 = rf(appIds)
 	} else {
@@ -195,7 +220,6 @@ func (_m *CiPipelineRepository) FindByAppIds(appIds []int) ([]*pipelineConfig.Ci
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func([]int) error); ok {
 		r1 = rf(appIds)
 	} else {
@@ -210,6 +234,10 @@ func (_m *CiPipelineRepository) FindByCiAndAppDetailsById(pipelineId int) (*pipe
 	ret := _m.Called(pipelineId)
 
 	var r0 *pipelineConfig.CiPipeline
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) (*pipelineConfig.CiPipeline, error)); ok {
+		return rf(pipelineId)
+	}
 	if rf, ok := ret.Get(0).(func(int) *pipelineConfig.CiPipeline); ok {
 		r0 = rf(pipelineId)
 	} else {
@@ -218,7 +246,6 @@ func (_m *CiPipelineRepository) FindByCiAndAppDetailsById(pipelineId int) (*pipe
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(int) error); ok {
 		r1 = rf(pipelineId)
 	} else {
@@ -233,6 +260,10 @@ func (_m *CiPipelineRepository) FindById(id int) (*pipelineConfig.CiPipeline, er
 	ret := _m.Called(id)
 
 	var r0 *pipelineConfig.CiPipeline
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) (*pipelineConfig.CiPipeline, error)); ok {
+		return rf(id)
+	}
 	if rf, ok := ret.Get(0).(func(int) *pipelineConfig.CiPipeline); ok {
 		r0 = rf(id)
 	} else {
@@ -241,7 +272,6 @@ func (_m *CiPipelineRepository) FindById(id int) (*pipelineConfig.CiPipeline, er
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(int) error); ok {
 		r1 = rf(id)
 	} else {
@@ -256,6 +286,10 @@ func (_m *CiPipelineRepository) FindByIdsIn(ids []int) ([]*pipelineConfig.CiPipe
 	ret := _m.Called(ids)
 
 	var r0 []*pipelineConfig.CiPipeline
+	var r1 error
+	if rf, ok := ret.Get(0).(func([]int) ([]*pipelineConfig.CiPipeline, error)); ok {
+		return rf(ids)
+	}
 	if rf, ok := ret.Get(0).(func([]int) []*pipelineConfig.CiPipeline); ok {
 		r0 = rf(ids)
 	} else {
@@ -264,7 +298,6 @@ func (_m *CiPipelineRepository) FindByIdsIn(ids []int) ([]*pipelineConfig.CiPipe
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func([]int) error); ok {
 		r1 = rf(ids)
 	} else {
@@ -279,6 +312,10 @@ func (_m *CiPipelineRepository) FindByName(pipelineName string) (*pipelineConfig
 	ret := _m.Called(pipelineName)
 
 	var r0 *pipelineConfig.CiPipeline
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string) (*pipelineConfig.CiPipeline, error)); ok {
+		return rf(pipelineName)
+	}
 	if rf, ok := ret.Get(0).(func(string) *pipelineConfig.CiPipeline); ok {
 		r0 = rf(pipelineName)
 	} else {
@@ -287,7 +324,6 @@ func (_m *CiPipelineRepository) FindByName(pipelineName string) (*pipelineConfig
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(string) error); ok {
 		r1 = rf(pipelineName)
 	} else {
@@ -302,6 +338,10 @@ func (_m *CiPipelineRepository) FindByParentCiPipelineId(parentCiPipelineId int)
 	ret := _m.Called(parentCiPipelineId)
 
 	var r0 []*pipelineConfig.CiPipeline
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) ([]*pipelineConfig.CiPipeline, error)); ok {
+		return rf(parentCiPipelineId)
+	}
 	if rf, ok := ret.Get(0).(func(int) []*pipelineConfig.CiPipeline); ok {
 		r0 = rf(parentCiPipelineId)
 	} else {
@@ -310,7 +350,6 @@ func (_m *CiPipelineRepository) FindByParentCiPipelineId(parentCiPipelineId int)
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(int) error); ok {
 		r1 = rf(parentCiPipelineId)
 	} else {
@@ -325,6 +364,10 @@ func (_m *CiPipelineRepository) FindByParentCiPipelineIds(parentCiPipelineIds []
 	ret := _m.Called(parentCiPipelineIds)
 
 	var r0 []*pipelineConfig.CiPipeline
+	var r1 error
+	if rf, ok := ret.Get(0).(func([]int) ([]*pipelineConfig.CiPipeline, error)); ok {
+		return rf(parentCiPipelineIds)
+	}
 	if rf, ok := ret.Get(0).(func([]int) []*pipelineConfig.CiPipeline); ok {
 		r0 = rf(parentCiPipelineIds)
 	} else {
@@ -333,7 +376,6 @@ func (_m *CiPipelineRepository) FindByParentCiPipelineIds(parentCiPipelineIds []
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func([]int) error); ok {
 		r1 = rf(parentCiPipelineIds)
 	} else {
@@ -343,11 +385,41 @@ func (_m *CiPipelineRepository) FindByParentCiPipelineIds(parentCiPipelineIds []
 	return r0, r1
 }
 
+// FindCiEnvMappingByCiPipelineId provides a mock function with given fields: ciPipelineId
+func (_m *CiPipelineRepository) FindCiEnvMappingByCiPipelineId(ciPipelineId int) (*pipelineConfig.CiEnvMapping, error) {
+	ret := _m.Called(ciPipelineId)
+
+	var r0 *pipelineConfig.CiEnvMapping
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) (*pipelineConfig.CiEnvMapping, error)); ok {
+		return rf(ciPipelineId)
+	}
+	if rf, ok := ret.Get(0).(func(int) *pipelineConfig.CiEnvMapping); ok {
+		r0 = rf(ciPipelineId)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*pipelineConfig.CiEnvMapping)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(int) error); ok {
+		r1 = rf(ciPipelineId)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
 // FindCiPipelineConfigsByIds provides a mock function with given fields: ids
 func (_m *CiPipelineRepository) FindCiPipelineConfigsByIds(ids []int) ([]*pipelineConfig.CiPipeline, error) {
 	ret := _m.Called(ids)
 
 	var r0 []*pipelineConfig.CiPipeline
+	var r1 error
+	if rf, ok := ret.Get(0).(func([]int) ([]*pipelineConfig.CiPipeline, error)); ok {
+		return rf(ids)
+	}
 	if rf, ok := ret.Get(0).(func([]int) []*pipelineConfig.CiPipeline); ok {
 		r0 = rf(ids)
 	} else {
@@ -356,7 +428,6 @@ func (_m *CiPipelineRepository) FindCiPipelineConfigsByIds(ids []int) ([]*pipeli
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func([]int) error); ok {
 		r1 = rf(ids)
 	} else {
@@ -371,6 +442,10 @@ func (_m *CiPipelineRepository) FindCiScriptsByCiPipelineId(ciPipelineId int) ([
 	ret := _m.Called(ciPipelineId)
 
 	var r0 []*pipelineConfig.CiPipelineScript
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) ([]*pipelineConfig.CiPipelineScript, error)); ok {
+		return rf(ciPipelineId)
+	}
 	if rf, ok := ret.Get(0).(func(int) []*pipelineConfig.CiPipelineScript); ok {
 		r0 = rf(ciPipelineId)
 	} else {
@@ -379,7 +454,6 @@ func (_m *CiPipelineRepository) FindCiScriptsByCiPipelineId(ciPipelineId int) ([
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(int) error); ok {
 		r1 = rf(ciPipelineId)
 	} else {
@@ -394,6 +468,10 @@ func (_m *CiPipelineRepository) FindCiScriptsByCiPipelineIds(ciPipelineId []int)
 	ret := _m.Called(ciPipelineId)
 
 	var r0 []*pipelineConfig.CiPipelineScript
+	var r1 error
+	if rf, ok := ret.Get(0).(func([]int) ([]*pipelineConfig.CiPipelineScript, error)); ok {
+		return rf(ciPipelineId)
+	}
 	if rf, ok := ret.Get(0).(func([]int) []*pipelineConfig.CiPipelineScript); ok {
 		r0 = rf(ciPipelineId)
 	} else {
@@ -402,7 +480,6 @@ func (_m *CiPipelineRepository) FindCiScriptsByCiPipelineIds(ciPipelineId []int)
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func([]int) error); ok {
 		r1 = rf(ciPipelineId)
 	} else {
@@ -417,6 +494,10 @@ func (_m *CiPipelineRepository) FindExternalCiByAppId(appId int) ([]*pipelineCon
 	ret := _m.Called(appId)
 
 	var r0 []*pipelineConfig.ExternalCiPipeline
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) ([]*pipelineConfig.ExternalCiPipeline, error)); ok {
+		return rf(appId)
+	}
 	if rf, ok := ret.Get(0).(func(int) []*pipelineConfig.ExternalCiPipeline); ok {
 		r0 = rf(appId)
 	} else {
@@ -425,7 +506,6 @@ func (_m *CiPipelineRepository) FindExternalCiByAppId(appId int) ([]*pipelineCon
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(int) error); ok {
 		r1 = rf(appId)
 	} else {
@@ -440,6 +520,10 @@ func (_m *CiPipelineRepository) FindExternalCiByAppIds(appIds []int) ([]*pipelin
 	ret := _m.Called(appIds)
 
 	var r0 []*pipelineConfig.ExternalCiPipeline
+	var r1 error
+	if rf, ok := ret.Get(0).(func([]int) ([]*pipelineConfig.ExternalCiPipeline, error)); ok {
+		return rf(appIds)
+	}
 	if rf, ok := ret.Get(0).(func([]int) []*pipelineConfig.ExternalCiPipeline); ok {
 		r0 = rf(appIds)
 	} else {
@@ -448,7 +532,6 @@ func (_m *CiPipelineRepository) FindExternalCiByAppIds(appIds []int) ([]*pipelin
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func([]int) error); ok {
 		r1 = rf(appIds)
 	} else {
@@ -463,6 +546,10 @@ func (_m *CiPipelineRepository) FindExternalCiByCiPipelineId(ciPipelineId int) (
 	ret := _m.Called(ciPipelineId)
 
 	var r0 *pipelineConfig.ExternalCiPipeline
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) (*pipelineConfig.ExternalCiPipeline, error)); ok {
+		return rf(ciPipelineId)
+	}
 	if rf, ok := ret.Get(0).(func(int) *pipelineConfig.ExternalCiPipeline); ok {
 		r0 = rf(ciPipelineId)
 	} else {
@@ -471,7 +558,6 @@ func (_m *CiPipelineRepository) FindExternalCiByCiPipelineId(ciPipelineId int) (
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(int) error); ok {
 		r1 = rf(ciPipelineId)
 	} else {
@@ -486,6 +572,10 @@ func (_m *CiPipelineRepository) FindExternalCiById(id int) (*pipelineConfig.Exte
 	ret := _m.Called(id)
 
 	var r0 *pipelineConfig.ExternalCiPipeline
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) (*pipelineConfig.ExternalCiPipeline, error)); ok {
+		return rf(id)
+	}
 	if rf, ok := ret.Get(0).(func(int) *pipelineConfig.ExternalCiPipeline); ok {
 		r0 = rf(id)
 	} else {
@@ -494,7 +584,6 @@ func (_m *CiPipelineRepository) FindExternalCiById(id int) (*pipelineConfig.Exte
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(int) error); ok {
 		r1 = rf(id)
 	} else {
@@ -509,13 +598,16 @@ func (_m *CiPipelineRepository) FindNumberOfAppsWithCiPipeline(appIds []int) (in
 	ret := _m.Called(appIds)
 
 	var r0 int
+	var r1 error
+	if rf, ok := ret.Get(0).(func([]int) (int, error)); ok {
+		return rf(appIds)
+	}
 	if rf, ok := ret.Get(0).(func([]int) int); ok {
 		r0 = rf(appIds)
 	} else {
 		r0 = ret.Get(0).(int)
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func([]int) error); ok {
 		r1 = rf(appIds)
 	} else {
@@ -530,6 +622,11 @@ func (_m *CiPipelineRepository) FindParentCiPipelineMapByAppId(appId int) ([]*pi
 	ret := _m.Called(appId)
 
 	var r0 []*pipelineConfig.CiPipeline
+	var r1 []int
+	var r2 error
+	if rf, ok := ret.Get(0).(func(int) ([]*pipelineConfig.CiPipeline, []int, error)); ok {
+		return rf(appId)
+	}
 	if rf, ok := ret.Get(0).(func(int) []*pipelineConfig.CiPipeline); ok {
 		r0 = rf(appId)
 	} else {
@@ -538,7 +635,6 @@ func (_m *CiPipelineRepository) FindParentCiPipelineMapByAppId(appId int) ([]*pi
 		}
 	}
 
-	var r1 []int
 	if rf, ok := ret.Get(1).(func(int) []int); ok {
 		r1 = rf(appId)
 	} else {
@@ -547,7 +643,6 @@ func (_m *CiPipelineRepository) FindParentCiPipelineMapByAppId(appId int) ([]*pi
 		}
 	}
 
-	var r2 error
 	if rf, ok := ret.Get(2).(func(int) error); ok {
 		r2 = rf(appId)
 	} else {
@@ -562,6 +657,10 @@ func (_m *CiPipelineRepository) FindWithMinDataByCiPipelineId(id int) (*pipeline
 	ret := _m.Called(id)
 
 	var r0 *pipelineConfig.CiPipeline
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) (*pipelineConfig.CiPipeline, error)); ok {
+		return rf(id)
+	}
 	if rf, ok := ret.Get(0).(func(int) *pipelineConfig.CiPipeline); ok {
 		r0 = rf(id)
 	} else {
@@ -570,7 +669,6 @@ func (_m *CiPipelineRepository) FindWithMinDataByCiPipelineId(id int) (*pipeline
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(int) error); ok {
 		r1 = rf(id)
 	} else {
@@ -585,6 +683,10 @@ func (_m *CiPipelineRepository) GetCiPipelineByArtifactId(artifactId int) (*pipe
 	ret := _m.Called(artifactId)
 
 	var r0 *pipelineConfig.CiPipeline
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) (*pipelineConfig.CiPipeline, error)); ok {
+		return rf(artifactId)
+	}
 	if rf, ok := ret.Get(0).(func(int) *pipelineConfig.CiPipeline); ok {
 		r0 = rf(artifactId)
 	} else {
@@ -593,7 +695,6 @@ func (_m *CiPipelineRepository) GetCiPipelineByArtifactId(artifactId int) (*pipe
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(int) error); ok {
 		r1 = rf(artifactId)
 	} else {
@@ -608,6 +709,10 @@ func (_m *CiPipelineRepository) GetExternalCiPipelineByArtifactId(artifactId int
 	ret := _m.Called(artifactId)
 
 	var r0 *pipelineConfig.ExternalCiPipeline
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) (*pipelineConfig.ExternalCiPipeline, error)); ok {
+		return rf(artifactId)
+	}
 	if rf, ok := ret.Get(0).(func(int) *pipelineConfig.ExternalCiPipeline); ok {
 		r0 = rf(artifactId)
 	} else {
@@ -616,7 +721,6 @@ func (_m *CiPipelineRepository) GetExternalCiPipelineByArtifactId(artifactId int
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(int) error); ok {
 		r1 = rf(artifactId)
 	} else {
@@ -645,6 +749,10 @@ func (_m *CiPipelineRepository) PipelineExistsByName(names []string) ([]string,
 	ret := _m.Called(names)
 
 	var r0 []string
+	var r1 error
+	if rf, ok := ret.Get(0).(func([]string) ([]string, error)); ok {
+		return rf(names)
+	}
 	if rf, ok := ret.Get(0).(func([]string) []string); ok {
 		r0 = rf(names)
 	} else {
@@ -653,7 +761,6 @@ func (_m *CiPipelineRepository) PipelineExistsByName(names []string) ([]string,
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func([]string) error); ok {
 		r1 = rf(names)
 	} else {
@@ -677,6 +784,20 @@ func (_m *CiPipelineRepository) Save(pipeline *pipelineConfig.CiPipeline, tx *pg
 	return r0
 }
 
+// SaveCiEnvMapping provides a mock function with given fields: cienvmapping, tx
+func (_m *CiPipelineRepository) SaveCiEnvMapping(cienvmapping *pipelineConfig.CiEnvMapping, tx *pg.Tx) error {
+	ret := _m.Called(cienvmapping, tx)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(*pipelineConfig.CiEnvMapping, *pg.Tx) error); ok {
+		r0 = rf(cienvmapping, tx)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
 // SaveCiPipelineScript provides a mock function with given fields: ciPipelineScript, tx
 func (_m *CiPipelineRepository) SaveCiPipelineScript(ciPipelineScript *pipelineConfig.CiPipelineScript, tx *pg.Tx) error {
 	ret := _m.Called(ciPipelineScript, tx)
@@ -696,6 +817,10 @@ func (_m *CiPipelineRepository) SaveExternalCi(pipeline *pipelineConfig.External
 	ret := _m.Called(pipeline, tx)
 
 	var r0 *pipelineConfig.ExternalCiPipeline
+	var r1 error
+	if rf, ok := ret.Get(0).(func(*pipelineConfig.ExternalCiPipeline, *pg.Tx) (*pipelineConfig.ExternalCiPipeline, error)); ok {
+		return rf(pipeline, tx)
+	}
 	if rf, ok := ret.Get(0).(func(*pipelineConfig.ExternalCiPipeline, *pg.Tx) *pipelineConfig.ExternalCiPipeline); ok {
 		r0 = rf(pipeline, tx)
 	} else {
@@ -704,7 +829,6 @@ func (_m *CiPipelineRepository) SaveExternalCi(pipeline *pipelineConfig.External
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(*pipelineConfig.ExternalCiPipeline, *pg.Tx) error); ok {
 		r1 = rf(pipeline, tx)
 	} else {
@@ -728,6 +852,20 @@ func (_m *CiPipelineRepository) Update(pipeline *pipelineConfig.CiPipeline, tx *
 	return r0
 }
 
+// UpdateCiEnvMapping provides a mock function with given fields: cienvmapping, tx
+func (_m *CiPipelineRepository) UpdateCiEnvMapping(cienvmapping *pipelineConfig.CiEnvMapping, tx *pg.Tx) error {
+	ret := _m.Called(cienvmapping, tx)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(*pipelineConfig.CiEnvMapping, *pg.Tx) error); ok {
+		r0 = rf(cienvmapping, tx)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
 // UpdateCiPipelineScript provides a mock function with given fields: script, tx
 func (_m *CiPipelineRepository) UpdateCiPipelineScript(script *pipelineConfig.CiPipelineScript, tx *pg.Tx) error {
 	ret := _m.Called(script, tx)
@@ -747,6 +885,10 @@ func (_m *CiPipelineRepository) UpdateExternalCi(pipeline *pipelineConfig.Extern
 	ret := _m.Called(pipeline, tx)
 
 	var r0 *pipelineConfig.ExternalCiPipeline
+	var r1 error
+	if rf, ok := ret.Get(0).(func(*pipelineConfig.ExternalCiPipeline, *pg.Tx) (*pipelineConfig.ExternalCiPipeline, error)); ok {
+		return rf(pipeline, tx)
+	}
 	if rf, ok := ret.Get(0).(func(*pipelineConfig.ExternalCiPipeline, *pg.Tx) *pipelineConfig.ExternalCiPipeline); ok {
 		r0 = rf(pipeline, tx)
 	} else {
@@ -755,7 +897,6 @@ func (_m *CiPipelineRepository) UpdateExternalCi(pipeline *pipelineConfig.Extern
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(*pipelineConfig.ExternalCiPipeline, *pg.Tx) error); ok {
 		r1 = rf(pipeline, tx)
 	} else {
diff --git a/internal/sql/repository/pipelineConfig/mocks/PipelineRepository.go b/internal/sql/repository/pipelineConfig/mocks/PipelineRepository.go
index b6431f279..9cc900366 100644
--- a/internal/sql/repository/pipelineConfig/mocks/PipelineRepository.go
+++ b/internal/sql/repository/pipelineConfig/mocks/PipelineRepository.go
@@ -14,13 +14,13 @@ type PipelineRepository struct {
 	mock.Mock
 }
 
-// Delete provides a mock function with given fields: id, tx
+// Delete provides a mock function with given fields: id, userId, tx
 func (_m *PipelineRepository) Delete(id int, userId int32, tx *pg.Tx) error {
-	ret := _m.Called(id, tx)
+	ret := _m.Called(id, userId, tx)
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(int, *pg.Tx) error); ok {
-		r0 = rf(id, tx)
+	if rf, ok := ret.Get(0).(func(int, int32, *pg.Tx) error); ok {
+		r0 = rf(id, userId, tx)
 	} else {
 		r0 = ret.Error(0)
 	}
diff --git a/internal/util/MergeUtil.go b/internal/util/MergeUtil.go
index 6c46cf63d..df6ca22fd 100644
--- a/internal/util/MergeUtil.go
+++ b/internal/util/MergeUtil.go
@@ -138,7 +138,7 @@ func (m MergeUtil) ConfigMapMerge(appLevelConfigMapJson string, envLevelConfigMa
 	return string(byteData), err
 }
 
-func (m MergeUtil) ConfigSecretMerge(appLevelSecretJson string, envLevelSecretJson string, chartMajorVersion int, chartMinorVersion int) (data string, err error) {
+func (m MergeUtil) ConfigSecretMerge(appLevelSecretJson string, envLevelSecretJson string, chartMajorVersion int, chartMinorVersion int, isJob bool) (data string, err error) {
 	appLevelSecret := bean.ConfigSecretJson{}
 	envLevelSecret := bean.ConfigSecretJson{}
 	secretResponse := bean.ConfigSecretJson{}
@@ -172,7 +172,7 @@ func (m MergeUtil) ConfigSecretMerge(appLevelSecretJson string, envLevelSecretJs
 
 	for _, item := range commonSecrets {
 		if item.ExternalType == util.AWSSecretsManager || item.ExternalType == util.AWSSystemManager || item.ExternalType == util.HashiCorpVault {
-			if item.SecretData != nil && chartMajorVersion <= 3 && chartMinorVersion < 8 {
+			if item.SecretData != nil && ((chartMajorVersion <= 3 && chartMinorVersion < 8) || isJob) {
 				var es []map[string]interface{}
 				esNew := make(map[string]interface{})
 				err = json.Unmarshal(item.SecretData, &es)
diff --git a/pkg/app/AppListingService.go b/pkg/app/AppListingService.go
index fbe35d5a7..c6bbf5dfb 100644
--- a/pkg/app/AppListingService.go
+++ b/pkg/app/AppListingService.go
@@ -269,13 +269,14 @@ func (impl AppListingServiceImpl) FetchJobs(fetchJobListingRequest FetchAppListi
 		Offset:        fetchJobListingRequest.Offset,
 		Size:          fetchJobListingRequest.Size,
 		AppStatuses:   fetchJobListingRequest.AppStatuses,
+		Environments:  fetchJobListingRequest.Environments,
 	}
 	appIds, err := impl.appRepository.FetchAppIdsWithFilter(jobListingFilter)
 	if err != nil {
 		impl.Logger.Errorw("error in fetching app ids list", "error", err, jobListingFilter)
 		return []*bean.JobContainer{}, err
 	}
-	jobListingContainers, err := impl.appListingRepository.FetchJobs(appIds, jobListingFilter.AppStatuses, string(jobListingFilter.SortOrder))
+	jobListingContainers, err := impl.appListingRepository.FetchJobs(appIds, jobListingFilter.AppStatuses, jobListingFilter.Environments, string(jobListingFilter.SortOrder))
 	if err != nil {
 		impl.Logger.Errorw("error in fetching job list", "error", err, jobListingFilter)
 		return []*bean.JobContainer{}, err
@@ -524,10 +525,13 @@ func BuildJobListingResponse(jobContainers []*bean.JobListingContainer, JobsLast
 
 		if jobContainer.CiPipelineID != 0 {
 			ciPipelineObj := bean.JobCIPipeline{
-				CiPipelineId:   jobContainer.CiPipelineID,
-				CiPipelineName: jobContainer.CiPipelineName,
-				Status:         jobContainer.Status,
-				LastRunAt:      jobContainer.StartedOn,
+				CiPipelineId:                 jobContainer.CiPipelineID,
+				CiPipelineName:               jobContainer.CiPipelineName,
+				Status:                       jobContainer.Status,
+				LastRunAt:                    jobContainer.StartedOn,
+				EnvironmentName:              jobContainer.EnvironmentName,
+				EnvironmentId:                jobContainer.EnvironmentId,
+				LastTriggeredEnvironmentName: jobContainer.LastTriggeredEnvironmentName,
 				//LastSuccessAt: jobContainer.LastSuccessAt,
 			}
 			if lastSuccessAt, ok := lastSucceededTimeMapping[jobContainer.CiPipelineID]; ok {
diff --git a/pkg/app/AppService.go b/pkg/app/AppService.go
index a8af6b842..e66abc037 100644
--- a/pkg/app/AppService.go
+++ b/pkg/app/AppService.go
@@ -179,7 +179,7 @@ type AppService interface {
 	TriggerCD(artifact *repository.CiArtifact, cdWorkflowId, wfrId int, pipeline *pipelineConfig.Pipeline, triggeredAt time.Time) error
 	GetConfigMapAndSecretJson(appId int, envId int, pipelineId int) ([]byte, error)
 	UpdateCdWorkflowRunnerByACDObject(app *v1alpha1.Application, cdWfrId int, updateTimedOutStatus bool) error
-	GetCmSecretNew(appId int, envId int) (*bean.ConfigMapJson, *bean.ConfigSecretJson, error)
+	GetCmSecretNew(appId int, envId int, isJob bool) (*bean.ConfigMapJson, *bean.ConfigSecretJson, error)
 	MarkImageScanDeployed(appId int, envId int, imageDigest string, clusterId int) error
 	UpdateDeploymentStatusForGitOpsPipelines(app *v1alpha1.Application, statusTime time.Time, isAppStore bool) (bool, bool, error)
 	WriteCDSuccessEvent(appId int, envId int, override *chartConfig.PipelineOverride)
@@ -1880,7 +1880,7 @@ func (impl *AppServiceImpl) MarkImageScanDeployed(appId int, envId int, imageDig
 }
 
 // FIXME tmp workaround
-func (impl *AppServiceImpl) GetCmSecretNew(appId int, envId int) (*bean.ConfigMapJson, *bean.ConfigSecretJson, error) {
+func (impl *AppServiceImpl) GetCmSecretNew(appId int, envId int, isJob bool) (*bean.ConfigMapJson, *bean.ConfigSecretJson, error) {
 	var configMapJson string
 	var secretDataJson string
 	var configMapJsonApp string
@@ -1912,18 +1912,22 @@ func (impl *AppServiceImpl) GetCmSecretNew(appId int, envId int) (*bean.ConfigMa
 	if err != nil {
 		return nil, nil, err
 	}
+	var chartMajorVersion int
+	var chartMinorVersion int
+	if !isJob {
+		chart, err := impl.commonService.FetchLatestChart(appId, envId)
+		if err != nil {
+			return nil, nil, err
+		}
 
-	chart, err := impl.commonService.FetchLatestChart(appId, envId)
-	if err != nil {
-		return nil, nil, err
-	}
-	chartVersion := chart.ChartVersion
-	chartMajorVersion, chartMinorVersion, err := util2.ExtractChartVersion(chartVersion)
-	if err != nil {
-		impl.logger.Errorw("chart version parsing", "err", err)
-		return nil, nil, err
+		chartVersion := chart.ChartVersion
+		chartMajorVersion, chartMinorVersion, err = util2.ExtractChartVersion(chartVersion)
+		if err != nil {
+			impl.logger.Errorw("chart version parsing", "err", err)
+			return nil, nil, err
+		}
 	}
-	secretDataJson, err = impl.mergeUtil.ConfigSecretMerge(secretDataJsonApp, secretDataJsonEnv, chartMajorVersion, chartMinorVersion)
+	secretDataJson, err = impl.mergeUtil.ConfigSecretMerge(secretDataJsonApp, secretDataJsonEnv, chartMajorVersion, chartMinorVersion, isJob)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -2053,7 +2057,7 @@ func (impl *AppServiceImpl) getConfigMapAndSecretJsonV2(appId int, envId int, pi
 		impl.logger.Errorw("chart version parsing", "err", err)
 		return []byte("{}"), err
 	}
-	secretDataJson, err = impl.mergeUtil.ConfigSecretMerge(secretDataJsonApp, secretDataJsonEnv, chartMajorVersion, chartMinorVersion)
+	secretDataJson, err = impl.mergeUtil.ConfigSecretMerge(secretDataJsonApp, secretDataJsonEnv, chartMajorVersion, chartMinorVersion, false)
 	if err != nil {
 		return []byte("{}"), err
 	}
diff --git a/pkg/appClone/AppCloneService.go b/pkg/appClone/AppCloneService.go
index 04040f099..bc2d1634c 100644
--- a/pkg/appClone/AppCloneService.go
+++ b/pkg/appClone/AppCloneService.go
@@ -19,23 +19,22 @@ package appClone
 
 import (
 	"context"
+	"fmt"
 	bean2 "github.com/devtron-labs/devtron/api/bean"
 	"github.com/devtron-labs/devtron/internal/constants"
 	app2 "github.com/devtron-labs/devtron/internal/sql/repository/app"
-	"github.com/devtron-labs/devtron/internal/sql/repository/helper"
-	"github.com/devtron-labs/devtron/internal/util"
-	"github.com/devtron-labs/devtron/pkg/chart"
-	"github.com/go-pg/pg"
-	"strings"
-
-	"fmt"
 	appWorkflow2 "github.com/devtron-labs/devtron/internal/sql/repository/appWorkflow"
+	"github.com/devtron-labs/devtron/internal/sql/repository/helper"
 	"github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig"
+	"github.com/devtron-labs/devtron/internal/util"
 	"github.com/devtron-labs/devtron/pkg/app"
 	"github.com/devtron-labs/devtron/pkg/appWorkflow"
 	"github.com/devtron-labs/devtron/pkg/bean"
+	"github.com/devtron-labs/devtron/pkg/chart"
 	"github.com/devtron-labs/devtron/pkg/pipeline"
+	"github.com/go-pg/pg"
 	"go.uber.org/zap"
+	"strings"
 )
 
 type AppCloneService interface {
@@ -181,18 +180,20 @@ func (impl *AppCloneServiceImpl) CloneApp(createReq *bean.CreateAppDTO, context
 			impl.logger.Errorw("error in creating deployment template", "ref", cloneReq.RefAppId, "new", newAppId, "err", err)
 			return nil, err
 		}
-		_, err = impl.CreateGlobalCM(cloneReq.RefAppId, newAppId, userId)
+	}
+	_, err = impl.CreateGlobalCM(cloneReq.RefAppId, newAppId, userId)
 
-		if err != nil {
-			impl.logger.Errorw("error in creating global cm", "ref", cloneReq.RefAppId, "new", newAppId, "err", err)
-			return nil, err
-		}
-		_, err = impl.CreateGlobalSecret(cloneReq.RefAppId, newAppId, userId)
-		if err != nil {
-			impl.logger.Errorw("error in creating global secret", "ref", cloneReq.RefAppId, "new", newAppId, "err", err)
-			return nil, err
-		}
-		if isSameProject {
+	if err != nil {
+		impl.logger.Errorw("error in creating global cm", "ref", cloneReq.RefAppId, "new", newAppId, "err", err)
+		return nil, err
+	}
+	_, err = impl.CreateGlobalSecret(cloneReq.RefAppId, newAppId, userId)
+	if err != nil {
+		impl.logger.Errorw("error in creating global secret", "ref", cloneReq.RefAppId, "new", newAppId, "err", err)
+		return nil, err
+	}
+	if isSameProject {
+		if createReq.AppType != helper.Job {
 			_, err = impl.CreateEnvCm(context, cloneReq.RefAppId, newAppId, userId)
 			if err != nil {
 				impl.logger.Errorw("error in creating env cm", "err", err)
@@ -208,6 +209,12 @@ func (impl *AppCloneServiceImpl) CloneApp(createReq *bean.CreateAppDTO, context
 				impl.logger.Errorw("error in cloning  env override", "err", err)
 				return nil, err
 			}
+		} else {
+			_, err := impl.configMapService.ConfigSecretEnvironmentClone(cloneReq.RefAppId, newAppId, userId)
+			if err != nil {
+				impl.logger.Errorw("error in cloning cm cs env override", "err", err)
+				return nil, err
+			}
 		}
 	}
 	_, err = impl.CreateWf(cloneReq.RefAppId, newAppId, userId, gitMaerialMap, context, isSameProject)
@@ -804,11 +811,16 @@ func (impl *AppCloneServiceImpl) CreateCiPipeline(req *cloneCiPipelineRequest) (
 					IsDockerConfigOverridden: refCiPipeline.IsDockerConfigOverridden,
 					PreBuildStage:            preStageDetail,
 					PostBuildStage:           postStageDetail,
+					EnvironmentId:            refCiPipeline.EnvironmentId,
 				},
 				AppId:         req.appId,
 				Action:        bean.CREATE,
 				AppWorkflowId: req.wfId,
 				UserId:        req.userId,
+				IsCloneJob:    true,
+			}
+			if refCiPipeline.EnvironmentId != 0 {
+				ciPatchReq.IsJob = true
 			}
 			if !refCiPipeline.IsExternal && refCiPipeline.IsDockerConfigOverridden {
 				//get template override
diff --git a/pkg/appWorkflow/AppWorkflowService.go b/pkg/appWorkflow/AppWorkflowService.go
index f9cbb07a8..d02e054c8 100644
--- a/pkg/appWorkflow/AppWorkflowService.go
+++ b/pkg/appWorkflow/AppWorkflowService.go
@@ -102,6 +102,14 @@ type WorkflowComponentNamesDto struct {
 	CdPipelines    []string `json:"cdPipelines"`
 }
 
+type WorkflowCloneRequest struct {
+	WorkflowName  string `json:"workflowName,omitempty"`
+	AppId         int    `json:"appId,omitempty"`
+	EnvironmentId int    `json:"environmentId,omitempty"`
+	WorkflowId    int    `json:"workflowId,omitempty"`
+	UserId        int32  `json:"-"`
+}
+
 func NewAppWorkflowServiceImpl(logger *zap.SugaredLogger, appWorkflowRepository appWorkflow.AppWorkflowRepository,
 	ciCdPipelineOrchestrator pipeline.CiCdPipelineOrchestrator, ciPipelineRepository pipelineConfig.CiPipelineRepository,
 	pipelineRepository pipelineConfig.PipelineRepository, enforcerUtil rbac.EnforcerUtil, appGroupService appGroup2.AppGroupService) *AppWorkflowServiceImpl {
@@ -187,13 +195,19 @@ func (impl AppWorkflowServiceImpl) FindAppWorkflows(appId int) ([]AppWorkflowDto
 func (impl AppWorkflowServiceImpl) FindAppWorkflowById(Id int, appId int) (AppWorkflowDto, error) {
 	appWorkflow, err := impl.appWorkflowRepository.FindByIdAndAppId(Id, appId)
 	if err != nil {
-		impl.Logger.Errorw("err", err)
+		impl.Logger.Errorw("err", "error", err)
+		return AppWorkflowDto{}, err
+	}
+	wfrIdVsMappings, err := impl.FindAllAppWorkflowMapping([]int{appWorkflow.Id})
+	if err != nil {
 		return AppWorkflowDto{}, err
 	}
+
 	appWorkflowDto := &AppWorkflowDto{
-		AppId: appWorkflow.AppId,
-		Id:    appWorkflow.Id,
-		Name:  appWorkflow.Name,
+		AppId:                 appWorkflow.AppId,
+		Id:                    appWorkflow.Id,
+		Name:                  appWorkflow.Name,
+		AppWorkflowMappingDto: wfrIdVsMappings[appWorkflow.Id],
 	}
 	return *appWorkflowDto, err
 }
diff --git a/pkg/bean/app.go b/pkg/bean/app.go
index 5e27766df..4de50e5a6 100644
--- a/pkg/bean/app.go
+++ b/pkg/bean/app.go
@@ -115,6 +115,8 @@ type CiPipeline struct {
 	TargetPlatform           string                 `json:"targetPlatform,omitempty"`
 	IsDockerConfigOverridden bool                   `json:"isDockerConfigOverridden"`
 	DockerConfigOverride     DockerConfigOverride   `json:"dockerConfigOverride,omitempty"`
+	EnvironmentId            int                    `json:"environmentId,omitempty"`
+	LastTriggeredEnvId       int                    `json:"lastTriggeredEnvId"`
 }
 
 type DockerConfigOverride struct {
@@ -215,6 +217,8 @@ type CiPatchRequest struct {
 	Action        PatchAction `json:"action"`
 	AppWorkflowId int         `json:"appWorkflowId,omitempty"`
 	UserId        int32       `json:"-"`
+	IsJob         bool        `json:"-"`
+	IsCloneJob    bool        `json:"isCloneJob,omitempty"`
 }
 
 type CiRegexPatchRequest struct {
@@ -266,6 +270,7 @@ type CiTriggerRequest struct {
 	CiPipelineMaterial []CiPipelineMaterial `json:"ciPipelineMaterials" validate:"required"`
 	TriggeredBy        int32                `json:"triggeredBy"`
 	InvalidateCache    bool                 `json:"invalidateCache"`
+	EnvironmentId      int                  `json:"environmentId"`
 }
 
 type CiTrigger struct {
@@ -307,6 +312,7 @@ type CiConfigRequest struct {
 	UpdatedBy         int32                   `sql:"updated_by,type:integer"`
 	IsJob             bool                    `json:"-"`
 	CiGitMaterialId   int                     `json:"ciGitConfiguredId"`
+	IsCloneJob        bool                    `json:"isCloneJob,omitempty"`
 }
 
 type CiPipelineMinResponse struct {
@@ -512,7 +518,7 @@ type CDPipelineConfigObject struct {
 	ChartBaseVersion              string                                 `json:"chartBaseVersion"`
 	ContainerRegistryId           int                                    `json:"containerRegistryId"`
 	RepoUrl                       string                                 `json:"repoUrl"`
-	ManifestStorageType           []string
+	ManifestStorageType           string                                 `json:"manifestStorageType"`
 }
 
 type PreStageConfigMapSecretNames struct {
diff --git a/pkg/chartRepo/repository/mocks/ChartRepoRepository.go b/pkg/chartRepo/repository/mocks/ChartRepoRepository.go
new file mode 100644
index 000000000..5aaf2f4f5
--- /dev/null
+++ b/pkg/chartRepo/repository/mocks/ChartRepoRepository.go
@@ -0,0 +1,218 @@
+// Code generated by mockery v2.20.0. DO NOT EDIT.
+
+package mocks
+
+import (
+	chartRepoRepository "github.com/devtron-labs/devtron/pkg/chartRepo/repository"
+	mock "github.com/stretchr/testify/mock"
+
+	pg "github.com/go-pg/pg"
+)
+
+// ChartRepoRepository is an autogenerated mock type for the ChartRepoRepository type
+type ChartRepoRepository struct {
+	mock.Mock
+}
+
+// FindAll provides a mock function with given fields:
+func (_m *ChartRepoRepository) FindAll() ([]*chartRepoRepository.ChartRepo, error) {
+	ret := _m.Called()
+
+	var r0 []*chartRepoRepository.ChartRepo
+	var r1 error
+	if rf, ok := ret.Get(0).(func() ([]*chartRepoRepository.ChartRepo, error)); ok {
+		return rf()
+	}
+	if rf, ok := ret.Get(0).(func() []*chartRepoRepository.ChartRepo); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]*chartRepoRepository.ChartRepo)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func() error); ok {
+		r1 = rf()
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// FindAllWithDeploymentCount provides a mock function with given fields:
+func (_m *ChartRepoRepository) FindAllWithDeploymentCount() ([]*chartRepoRepository.ChartRepoWithDeploymentCount, error) {
+	ret := _m.Called()
+
+	var r0 []*chartRepoRepository.ChartRepoWithDeploymentCount
+	var r1 error
+	if rf, ok := ret.Get(0).(func() ([]*chartRepoRepository.ChartRepoWithDeploymentCount, error)); ok {
+		return rf()
+	}
+	if rf, ok := ret.Get(0).(func() []*chartRepoRepository.ChartRepoWithDeploymentCount); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]*chartRepoRepository.ChartRepoWithDeploymentCount)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func() error); ok {
+		r1 = rf()
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// FindById provides a mock function with given fields: id
+func (_m *ChartRepoRepository) FindById(id int) (*chartRepoRepository.ChartRepo, error) {
+	ret := _m.Called(id)
+
+	var r0 *chartRepoRepository.ChartRepo
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) (*chartRepoRepository.ChartRepo, error)); ok {
+		return rf(id)
+	}
+	if rf, ok := ret.Get(0).(func(int) *chartRepoRepository.ChartRepo); ok {
+		r0 = rf(id)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*chartRepoRepository.ChartRepo)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(int) error); ok {
+		r1 = rf(id)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// FindByName provides a mock function with given fields: name
+func (_m *ChartRepoRepository) FindByName(name string) (*chartRepoRepository.ChartRepo, error) {
+	ret := _m.Called(name)
+
+	var r0 *chartRepoRepository.ChartRepo
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string) (*chartRepoRepository.ChartRepo, error)); ok {
+		return rf(name)
+	}
+	if rf, ok := ret.Get(0).(func(string) *chartRepoRepository.ChartRepo); ok {
+		r0 = rf(name)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*chartRepoRepository.ChartRepo)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(string) error); ok {
+		r1 = rf(name)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetConnection provides a mock function with given fields:
+func (_m *ChartRepoRepository) GetConnection() *pg.DB {
+	ret := _m.Called()
+
+	var r0 *pg.DB
+	if rf, ok := ret.Get(0).(func() *pg.DB); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*pg.DB)
+		}
+	}
+
+	return r0
+}
+
+// GetDefault provides a mock function with given fields:
+func (_m *ChartRepoRepository) GetDefault() (*chartRepoRepository.ChartRepo, error) {
+	ret := _m.Called()
+
+	var r0 *chartRepoRepository.ChartRepo
+	var r1 error
+	if rf, ok := ret.Get(0).(func() (*chartRepoRepository.ChartRepo, error)); ok {
+		return rf()
+	}
+	if rf, ok := ret.Get(0).(func() *chartRepoRepository.ChartRepo); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*chartRepoRepository.ChartRepo)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func() error); ok {
+		r1 = rf()
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// MarkChartRepoDeleted provides a mock function with given fields: chartRepo, tx
+func (_m *ChartRepoRepository) MarkChartRepoDeleted(chartRepo *chartRepoRepository.ChartRepo, tx *pg.Tx) error {
+	ret := _m.Called(chartRepo, tx)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(*chartRepoRepository.ChartRepo, *pg.Tx) error); ok {
+		r0 = rf(chartRepo, tx)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// Save provides a mock function with given fields: chartRepo, tx
+func (_m *ChartRepoRepository) Save(chartRepo *chartRepoRepository.ChartRepo, tx *pg.Tx) error {
+	ret := _m.Called(chartRepo, tx)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(*chartRepoRepository.ChartRepo, *pg.Tx) error); ok {
+		r0 = rf(chartRepo, tx)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// Update provides a mock function with given fields: chartRepo, tx
+func (_m *ChartRepoRepository) Update(chartRepo *chartRepoRepository.ChartRepo, tx *pg.Tx) error {
+	ret := _m.Called(chartRepo, tx)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(*chartRepoRepository.ChartRepo, *pg.Tx) error); ok {
+		r0 = rf(chartRepo, tx)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+type mockConstructorTestingTNewChartRepoRepository interface {
+	mock.TestingT
+	Cleanup(func())
+}
+
+// NewChartRepoRepository creates a new instance of ChartRepoRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+func NewChartRepoRepository(t mockConstructorTestingTNewChartRepoRepository) *ChartRepoRepository {
+	mock := &ChartRepoRepository{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/pkg/cluster/repository/mocks/EnvironmentRepository.go b/pkg/cluster/repository/mocks/EnvironmentRepository.go
index 4b1ed26e1..586ab567e 100644
--- a/pkg/cluster/repository/mocks/EnvironmentRepository.go
+++ b/pkg/cluster/repository/mocks/EnvironmentRepository.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.18.0. DO NOT EDIT.
+// Code generated by mockery v2.20.0. DO NOT EDIT.
 
 package mocks
 
@@ -33,6 +33,10 @@ func (_m *EnvironmentRepository) FindAll() ([]repository.Environment, error) {
 	ret := _m.Called()
 
 	var r0 []repository.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func() ([]repository.Environment, error)); ok {
+		return rf()
+	}
 	if rf, ok := ret.Get(0).(func() []repository.Environment); ok {
 		r0 = rf()
 	} else {
@@ -41,7 +45,6 @@ func (_m *EnvironmentRepository) FindAll() ([]repository.Environment, error) {
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func() error); ok {
 		r1 = rf()
 	} else {
@@ -56,6 +59,10 @@ func (_m *EnvironmentRepository) FindAllActive() ([]*repository.Environment, err
 	ret := _m.Called()
 
 	var r0 []*repository.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func() ([]*repository.Environment, error)); ok {
+		return rf()
+	}
 	if rf, ok := ret.Get(0).(func() []*repository.Environment); ok {
 		r0 = rf()
 	} else {
@@ -64,7 +71,6 @@ func (_m *EnvironmentRepository) FindAllActive() ([]*repository.Environment, err
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func() error); ok {
 		r1 = rf()
 	} else {
@@ -79,6 +85,10 @@ func (_m *EnvironmentRepository) FindAllActiveEnvOnlyDetails() ([]*repository.En
 	ret := _m.Called()
 
 	var r0 []*repository.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func() ([]*repository.Environment, error)); ok {
+		return rf()
+	}
 	if rf, ok := ret.Get(0).(func() []*repository.Environment); ok {
 		r0 = rf()
 	} else {
@@ -87,7 +97,6 @@ func (_m *EnvironmentRepository) FindAllActiveEnvOnlyDetails() ([]*repository.En
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func() error); ok {
 		r1 = rf()
 	} else {
@@ -102,6 +111,10 @@ func (_m *EnvironmentRepository) FindAllActiveWithFilter() ([]*repository.Enviro
 	ret := _m.Called()
 
 	var r0 []*repository.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func() ([]*repository.Environment, error)); ok {
+		return rf()
+	}
 	if rf, ok := ret.Get(0).(func() []*repository.Environment); ok {
 		r0 = rf()
 	} else {
@@ -110,7 +123,6 @@ func (_m *EnvironmentRepository) FindAllActiveWithFilter() ([]*repository.Enviro
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func() error); ok {
 		r1 = rf()
 	} else {
@@ -125,6 +137,10 @@ func (_m *EnvironmentRepository) FindByClusterId(clusterId int) ([]*repository.E
 	ret := _m.Called(clusterId)
 
 	var r0 []*repository.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) ([]*repository.Environment, error)); ok {
+		return rf(clusterId)
+	}
 	if rf, ok := ret.Get(0).(func(int) []*repository.Environment); ok {
 		r0 = rf(clusterId)
 	} else {
@@ -133,7 +149,6 @@ func (_m *EnvironmentRepository) FindByClusterId(clusterId int) ([]*repository.E
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(int) error); ok {
 		r1 = rf(clusterId)
 	} else {
@@ -148,6 +163,10 @@ func (_m *EnvironmentRepository) FindByClusterIdAndNamespace(namespaceClusterPai
 	ret := _m.Called(namespaceClusterPair)
 
 	var r0 []*repository.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func([]*repository.ClusterNamespacePair) ([]*repository.Environment, error)); ok {
+		return rf(namespaceClusterPair)
+	}
 	if rf, ok := ret.Get(0).(func([]*repository.ClusterNamespacePair) []*repository.Environment); ok {
 		r0 = rf(namespaceClusterPair)
 	} else {
@@ -156,7 +175,6 @@ func (_m *EnvironmentRepository) FindByClusterIdAndNamespace(namespaceClusterPai
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func([]*repository.ClusterNamespacePair) error); ok {
 		r1 = rf(namespaceClusterPair)
 	} else {
@@ -171,6 +189,10 @@ func (_m *EnvironmentRepository) FindByClusterIds(clusterIds []int) ([]*reposito
 	ret := _m.Called(clusterIds)
 
 	var r0 []*repository.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func([]int) ([]*repository.Environment, error)); ok {
+		return rf(clusterIds)
+	}
 	if rf, ok := ret.Get(0).(func([]int) []*repository.Environment); ok {
 		r0 = rf(clusterIds)
 	} else {
@@ -179,7 +201,6 @@ func (_m *EnvironmentRepository) FindByClusterIds(clusterIds []int) ([]*reposito
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func([]int) error); ok {
 		r1 = rf(clusterIds)
 	} else {
@@ -194,6 +215,10 @@ func (_m *EnvironmentRepository) FindByClusterIdsWithFilter(clusterIds []int) ([
 	ret := _m.Called(clusterIds)
 
 	var r0 []*repository.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func([]int) ([]*repository.Environment, error)); ok {
+		return rf(clusterIds)
+	}
 	if rf, ok := ret.Get(0).(func([]int) []*repository.Environment); ok {
 		r0 = rf(clusterIds)
 	} else {
@@ -202,7 +227,6 @@ func (_m *EnvironmentRepository) FindByClusterIdsWithFilter(clusterIds []int) ([
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func([]int) error); ok {
 		r1 = rf(clusterIds)
 	} else {
@@ -217,6 +241,10 @@ func (_m *EnvironmentRepository) FindByEnvName(envName string) ([]*repository.En
 	ret := _m.Called(envName)
 
 	var r0 []*repository.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string) ([]*repository.Environment, error)); ok {
+		return rf(envName)
+	}
 	if rf, ok := ret.Get(0).(func(string) []*repository.Environment); ok {
 		r0 = rf(envName)
 	} else {
@@ -225,7 +253,6 @@ func (_m *EnvironmentRepository) FindByEnvName(envName string) ([]*repository.En
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(string) error); ok {
 		r1 = rf(envName)
 	} else {
@@ -240,6 +267,10 @@ func (_m *EnvironmentRepository) FindByEnvNameAndClusterIds(envName string, clus
 	ret := _m.Called(envName, clusterIds)
 
 	var r0 []*repository.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string, []int) ([]*repository.Environment, error)); ok {
+		return rf(envName, clusterIds)
+	}
 	if rf, ok := ret.Get(0).(func(string, []int) []*repository.Environment); ok {
 		r0 = rf(envName, clusterIds)
 	} else {
@@ -248,7 +279,6 @@ func (_m *EnvironmentRepository) FindByEnvNameAndClusterIds(envName string, clus
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(string, []int) error); ok {
 		r1 = rf(envName, clusterIds)
 	} else {
@@ -263,6 +293,10 @@ func (_m *EnvironmentRepository) FindById(id int) (*repository.Environment, erro
 	ret := _m.Called(id)
 
 	var r0 *repository.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int) (*repository.Environment, error)); ok {
+		return rf(id)
+	}
 	if rf, ok := ret.Get(0).(func(int) *repository.Environment); ok {
 		r0 = rf(id)
 	} else {
@@ -271,7 +305,6 @@ func (_m *EnvironmentRepository) FindById(id int) (*repository.Environment, erro
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(int) error); ok {
 		r1 = rf(id)
 	} else {
@@ -286,6 +319,10 @@ func (_m *EnvironmentRepository) FindByIdentifier(identifier string) (*repositor
 	ret := _m.Called(identifier)
 
 	var r0 *repository.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string) (*repository.Environment, error)); ok {
+		return rf(identifier)
+	}
 	if rf, ok := ret.Get(0).(func(string) *repository.Environment); ok {
 		r0 = rf(identifier)
 	} else {
@@ -294,7 +331,6 @@ func (_m *EnvironmentRepository) FindByIdentifier(identifier string) (*repositor
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(string) error); ok {
 		r1 = rf(identifier)
 	} else {
@@ -309,6 +345,10 @@ func (_m *EnvironmentRepository) FindByIds(ids []*int) ([]*repository.Environmen
 	ret := _m.Called(ids)
 
 	var r0 []*repository.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func([]*int) ([]*repository.Environment, error)); ok {
+		return rf(ids)
+	}
 	if rf, ok := ret.Get(0).(func([]*int) []*repository.Environment); ok {
 		r0 = rf(ids)
 	} else {
@@ -317,7 +357,6 @@ func (_m *EnvironmentRepository) FindByIds(ids []*int) ([]*repository.Environmen
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func([]*int) error); ok {
 		r1 = rf(ids)
 	} else {
@@ -332,6 +371,10 @@ func (_m *EnvironmentRepository) FindByName(name string) (*repository.Environmen
 	ret := _m.Called(name)
 
 	var r0 *repository.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string) (*repository.Environment, error)); ok {
+		return rf(name)
+	}
 	if rf, ok := ret.Get(0).(func(string) *repository.Environment); ok {
 		r0 = rf(name)
 	} else {
@@ -340,7 +383,6 @@ func (_m *EnvironmentRepository) FindByName(name string) (*repository.Environmen
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(string) error); ok {
 		r1 = rf(name)
 	} else {
@@ -355,6 +397,10 @@ func (_m *EnvironmentRepository) FindByNameOrIdentifier(name string, identifier
 	ret := _m.Called(name, identifier)
 
 	var r0 *repository.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string, string) (*repository.Environment, error)); ok {
+		return rf(name, identifier)
+	}
 	if rf, ok := ret.Get(0).(func(string, string) *repository.Environment); ok {
 		r0 = rf(name, identifier)
 	} else {
@@ -363,7 +409,6 @@ func (_m *EnvironmentRepository) FindByNameOrIdentifier(name string, identifier
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(string, string) error); ok {
 		r1 = rf(name, identifier)
 	} else {
@@ -378,6 +423,10 @@ func (_m *EnvironmentRepository) FindByNamespaceAndClusterName(namespaces string
 	ret := _m.Called(namespaces, clusterName)
 
 	var r0 *repository.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string, string) (*repository.Environment, error)); ok {
+		return rf(namespaces, clusterName)
+	}
 	if rf, ok := ret.Get(0).(func(string, string) *repository.Environment); ok {
 		r0 = rf(namespaces, clusterName)
 	} else {
@@ -386,7 +435,6 @@ func (_m *EnvironmentRepository) FindByNamespaceAndClusterName(namespaces string
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(string, string) error); ok {
 		r1 = rf(namespaces, clusterName)
 	} else {
@@ -401,6 +449,10 @@ func (_m *EnvironmentRepository) FindEnvClusterInfosByIds(_a0 []int) ([]*reposit
 	ret := _m.Called(_a0)
 
 	var r0 []*repository.EnvCluserInfo
+	var r1 error
+	if rf, ok := ret.Get(0).(func([]int) ([]*repository.EnvCluserInfo, error)); ok {
+		return rf(_a0)
+	}
 	if rf, ok := ret.Get(0).(func([]int) []*repository.EnvCluserInfo); ok {
 		r0 = rf(_a0)
 	} else {
@@ -409,7 +461,6 @@ func (_m *EnvironmentRepository) FindEnvClusterInfosByIds(_a0 []int) ([]*reposit
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func([]int) error); ok {
 		r1 = rf(_a0)
 	} else {
@@ -424,6 +475,10 @@ func (_m *EnvironmentRepository) FindEnvLinkedWithCiPipelines(externalCi bool, c
 	ret := _m.Called(externalCi, ciPipelineIds)
 
 	var r0 []*repository.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func(bool, []int) ([]*repository.Environment, error)); ok {
+		return rf(externalCi, ciPipelineIds)
+	}
 	if rf, ok := ret.Get(0).(func(bool, []int) []*repository.Environment); ok {
 		r0 = rf(externalCi, ciPipelineIds)
 	} else {
@@ -432,7 +487,6 @@ func (_m *EnvironmentRepository) FindEnvLinkedWithCiPipelines(externalCi bool, c
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(bool, []int) error); ok {
 		r1 = rf(externalCi, ciPipelineIds)
 	} else {
@@ -447,6 +501,10 @@ func (_m *EnvironmentRepository) FindIdsByNames(envNames []string) ([]int, error
 	ret := _m.Called(envNames)
 
 	var r0 []int
+	var r1 error
+	if rf, ok := ret.Get(0).(func([]string) ([]int, error)); ok {
+		return rf(envNames)
+	}
 	if rf, ok := ret.Get(0).(func([]string) []int); ok {
 		r0 = rf(envNames)
 	} else {
@@ -455,7 +513,6 @@ func (_m *EnvironmentRepository) FindIdsByNames(envNames []string) ([]int, error
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func([]string) error); ok {
 		r1 = rf(envNames)
 	} else {
@@ -470,6 +527,10 @@ func (_m *EnvironmentRepository) FindOne(environment string) (*repository.Enviro
 	ret := _m.Called(environment)
 
 	var r0 *repository.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string) (*repository.Environment, error)); ok {
+		return rf(environment)
+	}
 	if rf, ok := ret.Get(0).(func(string) *repository.Environment); ok {
 		r0 = rf(environment)
 	} else {
@@ -478,7 +539,6 @@ func (_m *EnvironmentRepository) FindOne(environment string) (*repository.Enviro
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(string) error); ok {
 		r1 = rf(environment)
 	} else {
@@ -493,6 +553,10 @@ func (_m *EnvironmentRepository) FindOneByNamespaceAndClusterId(namespace string
 	ret := _m.Called(namespace, clusterId)
 
 	var r0 *repository.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string, int) (*repository.Environment, error)); ok {
+		return rf(namespace, clusterId)
+	}
 	if rf, ok := ret.Get(0).(func(string, int) *repository.Environment); ok {
 		r0 = rf(namespace, clusterId)
 	} else {
@@ -501,7 +565,6 @@ func (_m *EnvironmentRepository) FindOneByNamespaceAndClusterId(namespace string
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(string, int) error); ok {
 		r1 = rf(namespace, clusterId)
 	} else {
diff --git a/pkg/clusterTerminalAccess/UserTerminalAccessService.go b/pkg/clusterTerminalAccess/UserTerminalAccessService.go
index 28ef14901..e55c91cce 100644
--- a/pkg/clusterTerminalAccess/UserTerminalAccessService.go
+++ b/pkg/clusterTerminalAccess/UserTerminalAccessService.go
@@ -468,7 +468,7 @@ func (impl *UserTerminalAccessServiceImpl) createPodName(request *models.UserTer
 	return podNameVar
 }
 
-//template data use kubernetes object
+// template data use kubernetes object
 func (impl *UserTerminalAccessServiceImpl) applyTemplateData(ctx context.Context, request *models.UserTerminalSessionRequest, podNameVar string,
 	terminalTemplate *models.TerminalAccessTemplates, isUpdate bool, isAutoSelect bool) error {
 	templateName := terminalTemplate.TemplateName
diff --git a/pkg/commonService/mocks/CommonService.go b/pkg/commonService/mocks/CommonService.go
new file mode 100644
index 000000000..9ae240cc3
--- /dev/null
+++ b/pkg/commonService/mocks/CommonService.go
@@ -0,0 +1,82 @@
+// Code generated by mockery v2.20.0. DO NOT EDIT.
+
+package mocks
+
+import (
+	chartRepoRepository "github.com/devtron-labs/devtron/pkg/chartRepo/repository"
+	commonService "github.com/devtron-labs/devtron/pkg/commonService"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// CommonService is an autogenerated mock type for the CommonService type
+type CommonService struct {
+	mock.Mock
+}
+
+// FetchLatestChart provides a mock function with given fields: appId, envId
+func (_m *CommonService) FetchLatestChart(appId int, envId int) (*chartRepoRepository.Chart, error) {
+	ret := _m.Called(appId, envId)
+
+	var r0 *chartRepoRepository.Chart
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int, int) (*chartRepoRepository.Chart, error)); ok {
+		return rf(appId, envId)
+	}
+	if rf, ok := ret.Get(0).(func(int, int) *chartRepoRepository.Chart); ok {
+		r0 = rf(appId, envId)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*chartRepoRepository.Chart)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(int, int) error); ok {
+		r1 = rf(appId, envId)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GlobalChecklist provides a mock function with given fields:
+func (_m *CommonService) GlobalChecklist() (*commonService.GlobalChecklist, error) {
+	ret := _m.Called()
+
+	var r0 *commonService.GlobalChecklist
+	var r1 error
+	if rf, ok := ret.Get(0).(func() (*commonService.GlobalChecklist, error)); ok {
+		return rf()
+	}
+	if rf, ok := ret.Get(0).(func() *commonService.GlobalChecklist); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*commonService.GlobalChecklist)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func() error); ok {
+		r1 = rf()
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+type mockConstructorTestingTNewCommonService interface {
+	mock.TestingT
+	Cleanup(func())
+}
+
+// NewCommonService creates a new instance of CommonService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+func NewCommonService(t mockConstructorTestingTNewCommonService) *CommonService {
+	mock := &CommonService{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/pkg/pipeline/CdWorkflowService.go b/pkg/pipeline/CdWorkflowService.go
index 56fa27dba..2e9a3ebff 100644
--- a/pkg/pipeline/CdWorkflowService.go
+++ b/pkg/pipeline/CdWorkflowService.go
@@ -199,7 +199,7 @@ func (impl *CdWorkflowServiceImpl) SubmitWorkflow(workflowRequest *CdWorkflowReq
 		return err
 	}
 
-	existingConfigMap, existingSecrets, err := impl.appService.GetCmSecretNew(workflowRequest.AppId, workflowRequest.EnvironmentId)
+	existingConfigMap, existingSecrets, err := impl.appService.GetCmSecretNew(workflowRequest.AppId, workflowRequest.EnvironmentId, false)
 	if err != nil {
 		impl.Logger.Errorw("failed to get configmap data", "err", err)
 		return err
diff --git a/pkg/pipeline/CiCdPipelineOrchestrator.go b/pkg/pipeline/CiCdPipelineOrchestrator.go
index cc3d1b456..1b89119c0 100644
--- a/pkg/pipeline/CiCdPipelineOrchestrator.go
+++ b/pkg/pipeline/CiCdPipelineOrchestrator.go
@@ -104,6 +104,7 @@ type CiCdPipelineOrchestratorImpl struct {
 	gitMaterialHistoryService     history3.GitMaterialHistoryService
 	ciPipelineHistoryService      history3.CiPipelineHistoryService
 	dockerArtifactStoreRepository dockerRegistryRepository.DockerArtifactStoreRepository
+	configMapService              ConfigMapService
 }
 
 func NewCiCdPipelineOrchestrator(
@@ -127,7 +128,8 @@ func NewCiCdPipelineOrchestrator(
 	gitMaterialHistoryService history3.GitMaterialHistoryService,
 	ciPipelineHistoryService history3.CiPipelineHistoryService,
 	ciTemplateService CiTemplateService,
-	dockerArtifactStoreRepository dockerRegistryRepository.DockerArtifactStoreRepository) *CiCdPipelineOrchestratorImpl {
+	dockerArtifactStoreRepository dockerRegistryRepository.DockerArtifactStoreRepository,
+	configMapService ConfigMapService) *CiCdPipelineOrchestratorImpl {
 	return &CiCdPipelineOrchestratorImpl{
 		appRepository:                 pipelineGroupRepository,
 		logger:                        logger,
@@ -151,6 +153,7 @@ func NewCiCdPipelineOrchestrator(
 		ciPipelineHistoryService:      ciPipelineHistoryService,
 		ciTemplateService:             ciTemplateService,
 		dockerArtifactStoreRepository: dockerArtifactStoreRepository,
+		configMapService:              configMapService,
 	}
 }
 
@@ -196,6 +199,37 @@ func (impl CiCdPipelineOrchestratorImpl) PatchMaterialValue(createRequest *bean.
 	if err != nil {
 		return nil, err
 	}
+	var CiEnvMappingObject *pipelineConfig.CiEnvMapping
+	if ciPipelineObject.Id != 0 {
+		CiEnvMappingObject, err = impl.ciPipelineRepository.FindCiEnvMappingByCiPipelineId(ciPipelineObject.Id)
+		if err != nil && err != pg.ErrNoRows {
+			impl.logger.Errorw("error in getting CiEnvMappingObject ", "err", err, "ciPipelineId", createRequest.Id)
+			return nil, err
+		}
+	}
+	if err == nil && CiEnvMappingObject != nil {
+		if CiEnvMappingObject.EnvironmentId != createRequest.EnvironmentId {
+			CiEnvMappingObject.EnvironmentId = createRequest.EnvironmentId
+			CiEnvMappingObject.AuditLog = sql.AuditLog{UpdatedBy: userId, UpdatedOn: time.Now()}
+			err = impl.ciPipelineRepository.UpdateCiEnvMapping(CiEnvMappingObject, tx)
+			if err != nil {
+				impl.logger.Errorw("error in getting CiEnvMappingObject ", "err", err, "ciPipelineId", createRequest.Id)
+				return nil, err
+			}
+			if createRequest.EnvironmentId != 0 {
+				createJobEnvOverrideRequest := &CreateJobEnvOverridePayload{
+					AppId:  createRequest.AppId,
+					EnvId:  createRequest.EnvironmentId,
+					UserId: userId,
+				}
+				_, err = impl.configMapService.ConfigSecretEnvironmentCreate(createJobEnvOverrideRequest)
+				if err != nil && !strings.Contains(err.Error(), "already exists") {
+					impl.logger.Errorw("error in saving env override", "createJobEnvOverrideRequest", createJobEnvOverrideRequest, "err", err)
+					return nil, err
+				}
+			}
+		}
+	}
 	// marking old scripts inactive
 	err = impl.ciPipelineRepository.MarkCiPipelineScriptsInactiveByCiPipelineId(createRequest.Id, tx)
 	if err != nil {
@@ -435,6 +469,20 @@ func (impl CiCdPipelineOrchestratorImpl) PatchMaterialValue(createRequest *bean.
 func (impl CiCdPipelineOrchestratorImpl) DeleteCiPipeline(pipeline *pipelineConfig.CiPipeline, request *bean.CiPatchRequest, tx *pg.Tx) error {
 
 	userId := request.UserId
+	CiEnvMappingObject, err := impl.ciPipelineRepository.FindCiEnvMappingByCiPipelineId(pipeline.Id)
+	if err != nil && err != pg.ErrNoRows {
+		impl.logger.Errorw("error in getting CiEnvMappingObject ", "err", err, "ciPipelineId", pipeline.Id)
+		return err
+	}
+	if err == nil && CiEnvMappingObject != nil {
+		CiEnvMappingObject.AuditLog = sql.AuditLog{UpdatedBy: userId, UpdatedOn: time.Now()}
+		CiEnvMappingObject.Deleted = true
+		err = impl.ciPipelineRepository.UpdateCiEnvMapping(CiEnvMappingObject, tx)
+		if err != nil {
+			impl.logger.Errorw("error in getting CiEnvMappingObject ", "err", err, "ciPipelineId", CiEnvMappingObject.CiPipelineId)
+			return err
+		}
+	}
 
 	p := &pipelineConfig.CiPipeline{
 		Id:                       pipeline.Id,
@@ -444,7 +492,8 @@ func (impl CiCdPipelineOrchestratorImpl) DeleteCiPipeline(pipeline *pipelineConf
 		IsDockerConfigOverridden: pipeline.IsDockerConfigOverridden,
 		AuditLog:                 sql.AuditLog{UpdatedBy: userId, UpdatedOn: time.Now()},
 	}
-	err := impl.ciPipelineRepository.Update(p, tx)
+
+	err = impl.ciPipelineRepository.Update(p, tx)
 	if err != nil {
 		impl.logger.Errorw("error in updating ci pipeline, DeleteCiPipeline", "err", err, "pipelineId", pipeline.Id)
 		return err
@@ -550,6 +599,33 @@ func (impl CiCdPipelineOrchestratorImpl) CreateCiConf(createRequest *bean.CiConf
 			impl.logger.Errorw("error in saving pipeline", "ciPipelineObject", ciPipelineObject, "err", err)
 			return nil, err
 		}
+		if createRequest.IsJob {
+			CiEnvMapping := &pipelineConfig.CiEnvMapping{
+				CiPipelineId:  ciPipeline.Id,
+				EnvironmentId: ciPipeline.EnvironmentId,
+				AuditLog:      sql.AuditLog{UpdatedBy: createRequest.UserId, CreatedBy: createRequest.UserId, UpdatedOn: time.Now(), CreatedOn: time.Now()},
+			}
+			err = impl.ciPipelineRepository.SaveCiEnvMapping(CiEnvMapping, tx)
+			if err != nil {
+				impl.logger.Errorw("error in saving pipeline", "CiEnvMapping", CiEnvMapping, "err", err)
+				return nil, err
+			}
+
+			if ciPipeline.EnvironmentId != 0 && !createRequest.IsCloneJob {
+				createJobEnvOverrideRequest := &CreateJobEnvOverridePayload{
+					AppId:  createRequest.AppId,
+					EnvId:  ciPipeline.EnvironmentId,
+					UserId: createRequest.UserId,
+				}
+				_, err = impl.configMapService.ConfigSecretEnvironmentCreate(createJobEnvOverrideRequest)
+				if err != nil && !strings.Contains(err.Error(), "already exists") {
+					impl.logger.Errorw("error in saving env override", "createJobEnvOverrideRequest", createJobEnvOverrideRequest, "err", err)
+					return nil, err
+				}
+
+			}
+		}
+
 		var pipelineMaterials []*pipelineConfig.CiPipelineMaterial
 		for _, r := range ciPipeline.CiMaterial {
 			material := &pipelineConfig.CiPipelineMaterial{
diff --git a/pkg/pipeline/CiConfig.go b/pkg/pipeline/CiConfig.go
index 0a1f16d68..d6571b78b 100644
--- a/pkg/pipeline/CiConfig.go
+++ b/pkg/pipeline/CiConfig.go
@@ -89,9 +89,11 @@ type CiConfig struct {
 	UseBuildx                        bool                         `env:"USE_BUILDX" envDefault:"false"`
 	ClusterConfig                    *rest.Config
 	NodeLabel                        map[string]string
-	EnableBuildContext               bool `env:"ENABLE_BUILD_CONTEXT" envDefault:"false"`
-	ImageRetryCount                  int  `env:"IMAGE_RETRY_COUNT" envDefault:"0"`
-	ImageRetryInterval               int  `env:"IMAGE_RETRY_INTERVAL" envDefault:"5"` //image retry interval takes value in seconds
+	EnableBuildContext               bool   `env:"ENABLE_BUILD_CONTEXT" envDefault:"false"`
+	ImageRetryCount                  int    `env:"IMAGE_RETRY_COUNT" envDefault:"0"`
+	ImageRetryInterval               int    `env:"IMAGE_RETRY_INTERVAL" envDefault:"5"` //image retry interval takes value in seconds
+	OrchestratorHost                 string `env:"ORCH_HOST" envDefault:"http://devtroncd-orchestrator-service-prod.devtroncd/webhook/msg/nats"`
+	OrchestratorToken                string `env:"ORCH_TOKEN" envDefault:""`
 }
 
 type CiVolumeMount struct {
diff --git a/pkg/pipeline/CiHandler.go b/pkg/pipeline/CiHandler.go
index c6d26f6d0..69fe474c7 100644
--- a/pkg/pipeline/CiHandler.go
+++ b/pkg/pipeline/CiHandler.go
@@ -29,6 +29,7 @@ import (
 	"github.com/devtron-labs/devtron/client/gitSensor"
 	repository2 "github.com/devtron-labs/devtron/internal/sql/repository/imageTagging"
 	appGroup2 "github.com/devtron-labs/devtron/pkg/appGroup"
+	repository3 "github.com/devtron-labs/devtron/pkg/cluster/repository"
 	"github.com/devtron-labs/devtron/util/rbac"
 	"io/ioutil"
 	errors2 "k8s.io/apimachinery/pkg/api/errors"
@@ -99,6 +100,7 @@ type CiHandlerImpl struct {
 	cdPipelineRepository         pipelineConfig.PipelineRepository
 	enforcerUtil                 rbac.EnforcerUtil
 	appGroupService              appGroup2.AppGroupService
+	envRepository                repository3.EnvironmentRepository
 	imageTaggingService          ImageTaggingService
 }
 
@@ -107,7 +109,7 @@ func NewCiHandlerImpl(Logger *zap.SugaredLogger, ciService CiService, ciPipeline
 	ciLogService CiLogService, ciConfig *CiConfig, ciArtifactRepository repository.CiArtifactRepository, userService user.UserService, eventClient client.EventClient,
 	eventFactory client.EventFactory, ciPipelineRepository pipelineConfig.CiPipelineRepository, appListingRepository repository.AppListingRepository,
 	K8sUtil *util.K8sUtil, cdPipelineRepository pipelineConfig.PipelineRepository, enforcerUtil rbac.EnforcerUtil,
-	appGroupService appGroup2.AppGroupService,
+	appGroupService appGroup2.AppGroupService, envRepository repository3.EnvironmentRepository,
 	imageTaggingService ImageTaggingService) *CiHandlerImpl {
 	return &CiHandlerImpl{
 		Logger:                       Logger,
@@ -128,6 +130,7 @@ func NewCiHandlerImpl(Logger *zap.SugaredLogger, ciService CiService, ciPipeline
 		cdPipelineRepository:         cdPipelineRepository,
 		enforcerUtil:                 enforcerUtil,
 		appGroupService:              appGroupService,
+		envRepository:                envRepository,
 		imageTaggingService:          imageTaggingService,
 	}
 }
@@ -154,6 +157,8 @@ type WorkflowResponse struct {
 	IsArtifactUploaded   bool                                        `json:"isArtifactUploaded"`
 	IsVirtualEnvironment bool                                        `json:"isVirtualEnvironment"`
 	PodName              string                                      `json:"podName"`
+	EnvironmentId        int                                         `json:"environmentId"`
+	EnvironmentName      string                                      `json:"environmentName"`
 	ImageReleaseTags     []*repository2.ImageTag                     `json:"imageReleaseTags"`
 	ImageComment         *repository2.ImageComment                   `json:"imageComment"`
 }
@@ -178,6 +183,7 @@ type Trigger struct {
 	TriggeredBy               int32
 	InvalidateCache           bool
 	ExtraEnvironmentVariables map[string]string // extra env variables which will be used for CI
+	EnvironmentId             int
 }
 
 const WorkflowCancel = "CANCELLED"
@@ -198,8 +204,10 @@ func (impl *CiHandlerImpl) HandleCIManual(ciTriggerRequest bean.CiTriggerRequest
 		TriggeredBy:               ciTriggerRequest.TriggeredBy,
 		InvalidateCache:           ciTriggerRequest.InvalidateCache,
 		ExtraEnvironmentVariables: extraEnvironmentVariables,
+		EnvironmentId:             ciTriggerRequest.EnvironmentId,
 	}
 	id, err := impl.ciService.TriggerCiPipeline(trigger)
+
 	if err != nil {
 		return 0, err
 	}
@@ -494,6 +502,8 @@ func (impl *CiHandlerImpl) GetBuildHistory(pipelineId int, appId int, offset int
 			ArtifactId:         w.CiArtifactId,
 			BlobStorageEnabled: w.BlobStorageEnabled,
 			IsArtifactUploaded: w.IsArtifactUploaded,
+			EnvironmentId:      w.EnvironmentId,
+			EnvironmentName:    w.EnvironmentName,
 		}
 		if imageTagsDataMap[w.CiArtifactId] != nil {
 			wfResponse.ImageReleaseTags = imageTagsDataMap[w.CiArtifactId] //if artifact is not yet created,empty list will be sent
@@ -516,14 +526,25 @@ func (impl *CiHandlerImpl) CancelBuild(workflowId int) (int, error) {
 		impl.Logger.Warn("cannot cancel build, build not in progress")
 		return 0, errors.New("cannot cancel build, build not in progress")
 	}
-	runningWf, err := impl.workflowService.GetWorkflow(workflow.Name, workflow.Namespace)
+	var isExt bool
+	var env *repository3.Environment
+	if workflow.Namespace != DefaultCiWorkflowNamespace {
+		isExt = true
+		env, err = impl.envRepository.FindById(workflow.EnvironmentId)
+		if err != nil {
+			impl.Logger.Errorw("could not fetch stage env", "err", err)
+			return 0, err
+		}
+	}
+
+	runningWf, err := impl.workflowService.GetWorkflow(workflow.Name, workflow.Namespace, isExt, env)
 	if err != nil {
 		impl.Logger.Errorw("cannot find workflow ", "err", err)
 		return 0, errors.New("cannot find workflow " + workflow.Name)
 	}
 
 	// Terminate workflow
-	err = impl.workflowService.TerminateWorkflow(runningWf.Name, runningWf.Namespace)
+	err = impl.workflowService.TerminateWorkflow(runningWf.Name, runningWf.Namespace, isExt, env)
 	if err != nil {
 		impl.Logger.Errorw("cannot terminate wf", "err", err)
 		return 0, err
@@ -580,6 +601,15 @@ func (impl *CiHandlerImpl) FetchWorkflowDetails(appId int, pipelineId int, build
 		}
 		ciMaterialsArr = append(ciMaterialsArr, res)
 	}
+	environmentName := ""
+	if workflow.EnvironmentId != 0 {
+		env, err := impl.envRepository.FindById(workflow.EnvironmentId)
+		if err != nil && err != pg.ErrNoRows {
+			impl.Logger.Errorw("error in fetching environment details ", "err", err)
+			return WorkflowResponse{}, err
+		}
+		environmentName = env.Name
+	}
 	workflowResponse := WorkflowResponse{
 		Id:                 workflow.Id,
 		Name:               workflow.Name,
@@ -599,6 +629,8 @@ func (impl *CiHandlerImpl) FetchWorkflowDetails(appId int, pipelineId int, build
 		Artifact:           ciArtifact.Image,
 		ArtifactId:         ciArtifact.Id,
 		IsArtifactUploaded: ciArtifact.IsArtifactUploaded,
+		EnvironmentId:      workflow.EnvironmentId,
+		EnvironmentName:    environmentName,
 	}
 	return workflowResponse, nil
 }
@@ -620,8 +652,28 @@ func (impl *CiHandlerImpl) getWorkflowLogs(pipelineId int, ciWorkflow *pipelineC
 		PodName:   ciWorkflow.PodName,
 		Namespace: ciWorkflow.Namespace,
 	}
-	var clusterConfig util.ClusterConfig
-	logStream, cleanUp, err := impl.ciLogService.FetchRunningWorkflowLogs(ciLogRequest, clusterConfig, false)
+	isExt := false
+	clusterConfig := util.ClusterConfig{}
+	if ciWorkflow.EnvironmentId != 0 {
+		env, err := impl.envRepository.FindById(ciWorkflow.EnvironmentId)
+		if err != nil {
+			return nil, nil, err
+		}
+		configMap := env.Cluster.Config
+		clusterConfig = util.ClusterConfig{
+			Host:                  env.Cluster.ServerUrl,
+			BearerToken:           configMap[util.BearerToken],
+			InsecureSkipTLSVerify: env.Cluster.InsecureSkipTlsVerify,
+		}
+		if env.Cluster.InsecureSkipTlsVerify == false {
+			clusterConfig.KeyData = configMap[util.TlsKey]
+			clusterConfig.CertData = configMap[util.CertData]
+			clusterConfig.CAData = configMap[util.CertificateAuthorityData]
+		}
+		isExt = true
+	}
+
+	logStream, cleanUp, err := impl.ciLogService.FetchRunningWorkflowLogs(ciLogRequest, clusterConfig, isExt)
 	if logStream == nil || err != nil {
 		if !ciWorkflow.BlobStorageEnabled {
 			return nil, nil, errors.New("logs-not-stored-in-repository")
@@ -1384,11 +1436,23 @@ func (impl *CiHandlerImpl) UpdateCiWorkflowStatusFailure(timeoutForFailureCiBuil
 		impl.Logger.Errorw("error while fetching k8s client", "error", err)
 		return err
 	}
+
 	for _, ciWorkflow := range ciWorkflows {
+		var isExt bool
+		var env *repository3.Environment
+		if ciWorkflow.Namespace != DefaultCiWorkflowNamespace {
+			isExt = true
+			env, err = impl.envRepository.FindById(ciWorkflow.EnvironmentId)
+			if err != nil {
+				impl.Logger.Errorw("could not fetch stage env", "err", err)
+				return err
+			}
+		}
+
 		isEligibleToMarkFailed := false
 		if time.Since(ciWorkflow.StartedOn) > (time.Minute * time.Duration(timeoutForFailureCiBuild)) {
 			//check weather pod is exists or not, if exits check its status
-			_, err := impl.workflowService.GetWorkflow(ciWorkflow.Name, DefaultCiWorkflowNamespace)
+			_, err := impl.workflowService.GetWorkflow(ciWorkflow.Name, ciWorkflow.Namespace, isExt, env)
 			if err != nil {
 				impl.Logger.Warnw("unable to fetch ci workflow", "err", err)
 				statusError, ok := err.(*errors2.StatusError)
diff --git a/pkg/pipeline/CiService.go b/pkg/pipeline/CiService.go
index 4d600cc18..4cc895ad2 100644
--- a/pkg/pipeline/CiService.go
+++ b/pkg/pipeline/CiService.go
@@ -20,8 +20,11 @@ package pipeline
 import (
 	"errors"
 	"fmt"
+	appRepository "github.com/devtron-labs/devtron/internal/sql/repository/app"
 	repository3 "github.com/devtron-labs/devtron/internal/sql/repository/dockerRegistry"
+	"github.com/devtron-labs/devtron/internal/sql/repository/helper"
 	"github.com/devtron-labs/devtron/pkg/app"
+	repository1 "github.com/devtron-labs/devtron/pkg/cluster/repository"
 	bean2 "github.com/devtron-labs/devtron/pkg/pipeline/bean"
 	"github.com/devtron-labs/devtron/pkg/pipeline/history"
 	"github.com/devtron-labs/devtron/pkg/pipeline/repository"
@@ -64,6 +67,8 @@ type CiServiceImpl struct {
 	userService                   user.UserService
 	ciTemplateService             CiTemplateService
 	appCrudOperationService       app.AppCrudOperationService
+	envRepository                 repository1.EnvironmentRepository
+	appRepository                 appRepository.AppRepository
 }
 
 func NewCiServiceImpl(Logger *zap.SugaredLogger, workflowService WorkflowService,
@@ -73,7 +78,7 @@ func NewCiServiceImpl(Logger *zap.SugaredLogger, workflowService WorkflowService
 	prePostCiScriptHistoryService history.PrePostCiScriptHistoryService,
 	pipelineStageService PipelineStageService,
 	userService user.UserService,
-	ciTemplateService CiTemplateService, appCrudOperationService app.AppCrudOperationService) *CiServiceImpl {
+	ciTemplateService CiTemplateService, appCrudOperationService app.AppCrudOperationService, envRepository repository1.EnvironmentRepository, appRepository appRepository.AppRepository) *CiServiceImpl {
 	return &CiServiceImpl{
 		Logger:                        Logger,
 		workflowService:               workflowService,
@@ -89,6 +94,8 @@ func NewCiServiceImpl(Logger *zap.SugaredLogger, workflowService WorkflowService
 		userService:                   userService,
 		ciTemplateService:             ciTemplateService,
 		appCrudOperationService:       appCrudOperationService,
+		envRepository:                 envRepository,
+		appRepository:                 appRepository,
 	}
 }
 
@@ -129,16 +136,36 @@ func (impl *CiServiceImpl) TriggerCiPipeline(trigger Trigger) (int, error) {
 		impl.Logger.Errorw("could not fetch ci config", "pipeline", trigger.PipelineId)
 		return 0, err
 	}
+	env, isJob, err := impl.getEnvironmentForJob(pipeline, trigger)
+	if err != nil {
+		return 0, err
+	}
+	if isJob && env != nil {
+		ciWorkflowConfig.Namespace = env.Namespace
+	}
 	if ciWorkflowConfig.Namespace == "" {
 		ciWorkflowConfig.Namespace = impl.ciConfig.DefaultNamespace
 	}
-	savedCiWf, err := impl.saveNewWorkflow(pipeline, ciWorkflowConfig, trigger.CommitHashes, trigger.TriggeredBy)
+
+	preCiSteps, postCiSteps, refPluginsData, err := impl.pipelineStageService.BuildPrePostAndRefPluginStepsDataForWfRequest(pipeline.Id)
+
+	if err != nil {
+		impl.Logger.Errorw("error in getting pre steps data for wf request", "err", err, "ciPipelineId", pipeline.Id)
+		return 0, err
+	}
+
+	if len(preCiSteps) == 0 && isJob {
+		return 0, &util.ApiError{
+			UserMessage: "No tasks are configured in this job pipeline",
+		}
+	}
+	savedCiWf, err := impl.saveNewWorkflow(pipeline, ciWorkflowConfig, trigger.CommitHashes, trigger.TriggeredBy, trigger.EnvironmentId, isJob)
 	if err != nil {
 		impl.Logger.Errorw("could not save new workflow", "err", err)
 		return 0, err
 	}
 
-	workflowRequest, err := impl.buildWfRequestForCiPipeline(pipeline, trigger, ciMaterials, savedCiWf, ciWorkflowConfig, ciPipelineScripts)
+	workflowRequest, err := impl.buildWfRequestForCiPipeline(pipeline, trigger, ciMaterials, savedCiWf, ciWorkflowConfig, ciPipelineScripts, preCiSteps, postCiSteps, refPluginsData)
 	if err != nil {
 		impl.Logger.Errorw("make workflow req", "err", err)
 		return 0, err
@@ -152,8 +179,8 @@ func (impl *CiServiceImpl) TriggerCiPipeline(trigger Trigger) (int, error) {
 	if err != nil {
 		return 0, err
 	}
-
-	createdWf, err := impl.executeCiPipeline(workflowRequest, appLabels)
+	workflowRequest.AppId = pipeline.AppId
+	createdWf, err := impl.executeCiPipeline(workflowRequest, appLabels, env, isJob)
 	if err != nil {
 		impl.Logger.Errorw("workflow error", "err", err)
 		return 0, err
@@ -165,6 +192,29 @@ func (impl *CiServiceImpl) TriggerCiPipeline(trigger Trigger) (int, error) {
 	return savedCiWf.Id, err
 }
 
+func (impl *CiServiceImpl) getEnvironmentForJob(pipeline *pipelineConfig.CiPipeline, trigger Trigger) (*repository1.Environment, bool, error) {
+	app, err := impl.appRepository.FindById(pipeline.AppId)
+	if err != nil {
+		impl.Logger.Errorw("could not find app", "err", err)
+		return nil, false, err
+	}
+
+	var env *repository1.Environment
+	isJob := false
+	if app.AppType == helper.Job {
+		isJob = true
+		if trigger.EnvironmentId != 0 {
+			env, err = impl.envRepository.FindById(trigger.EnvironmentId)
+			if err != nil {
+				impl.Logger.Errorw("could not find environment", "err", err)
+				return nil, isJob, err
+			}
+			return env, isJob, nil
+		}
+	}
+	return nil, isJob, nil
+}
+
 func (impl *CiServiceImpl) WriteCITriggerEvent(trigger Trigger, pipeline *pipelineConfig.CiPipeline, workflowRequest *WorkflowRequest) {
 	event := impl.eventFactory.Build(util2.Trigger, &pipeline.Id, pipeline.AppId, nil, util2.CI)
 	material := &client.MaterialTriggerInfo{}
@@ -213,7 +263,7 @@ func (impl *CiServiceImpl) BuildPayload(trigger Trigger, pipeline *pipelineConfi
 }
 
 func (impl *CiServiceImpl) saveNewWorkflow(pipeline *pipelineConfig.CiPipeline, wfConfig *pipelineConfig.CiWorkflowConfig,
-	commitHashes map[int]bean.GitCommit, userId int32) (wf *pipelineConfig.CiWorkflow, error error) {
+	commitHashes map[int]bean.GitCommit, userId int32, EnvironmentId int, isJob bool) (wf *pipelineConfig.CiWorkflow, error error) {
 	gitTriggers := make(map[int]pipelineConfig.GitCommit)
 	for k, v := range commitHashes {
 		gitCommit := pipelineConfig.GitCommit{
@@ -245,12 +295,16 @@ func (impl *CiServiceImpl) saveNewWorkflow(pipeline *pipelineConfig.CiPipeline,
 		Message:            "",
 		StartedOn:          time.Now(),
 		CiPipelineId:       pipeline.Id,
-		Namespace:          wfConfig.Namespace,
+		Namespace:          impl.ciConfig.DefaultNamespace,
 		BlobStorageEnabled: impl.ciConfig.BlobStorageEnabled,
 		GitTriggers:        gitTriggers,
 		LogLocation:        "",
 		TriggeredBy:        userId,
 	}
+	if isJob {
+		ciWorkflow.Namespace = wfConfig.Namespace
+		ciWorkflow.EnvironmentId = EnvironmentId
+	}
 	err := impl.ciWorkflowRepository.SaveWorkFlow(ciWorkflow)
 	if err != nil {
 		impl.Logger.Errorw("saving workflow error", "err", err)
@@ -260,8 +314,8 @@ func (impl *CiServiceImpl) saveNewWorkflow(pipeline *pipelineConfig.CiPipeline,
 	return ciWorkflow, nil
 }
 
-func (impl *CiServiceImpl) executeCiPipeline(workflowRequest *WorkflowRequest, appLabels map[string]string) (*v1alpha1.Workflow, error) {
-	createdWorkFlow, err := impl.workflowService.SubmitWorkflow(workflowRequest, appLabels)
+func (impl *CiServiceImpl) executeCiPipeline(workflowRequest *WorkflowRequest, appLabels map[string]string, env *repository1.Environment, isJob bool) (*v1alpha1.Workflow, error) {
+	createdWorkFlow, err := impl.workflowService.SubmitWorkflow(workflowRequest, appLabels, env, isJob)
 	if err != nil {
 		impl.Logger.Errorw("workflow error", "err", err)
 		return nil, err
@@ -290,7 +344,8 @@ func (impl *CiServiceImpl) buildDefaultArtifactLocation(ciWorkflowConfig *pipeli
 
 func (impl *CiServiceImpl) buildWfRequestForCiPipeline(pipeline *pipelineConfig.CiPipeline, trigger Trigger,
 	ciMaterials []*pipelineConfig.CiPipelineMaterial, savedWf *pipelineConfig.CiWorkflow,
-	ciWorkflowConfig *pipelineConfig.CiWorkflowConfig, ciPipelineScripts []*pipelineConfig.CiPipelineScript) (*WorkflowRequest, error) {
+	ciWorkflowConfig *pipelineConfig.CiWorkflowConfig, ciPipelineScripts []*pipelineConfig.CiPipelineScript,
+	preCiSteps []*bean2.StepObject, postCiSteps []*bean2.StepObject, refPluginsData []*bean2.RefPluginObject) (*WorkflowRequest, error) {
 	var ciProjectDetails []CiProjectDetails
 	commitHashes := trigger.CommitHashes
 	for _, ciMaterial := range ciMaterials {
@@ -350,23 +405,14 @@ func (impl *CiServiceImpl) buildWfRequestForCiPipeline(pipeline *pipelineConfig.
 			afterDockerBuildScripts = append(afterDockerBuildScripts, ciTask)
 		}
 	}
-	var preCiSteps []*bean2.StepObject
-	var postCiSteps []*bean2.StepObject
-	var refPluginsData []*bean2.RefPluginObject
+
 	var err error
 	if !(len(beforeDockerBuildScripts) == 0 && len(afterDockerBuildScripts) == 0) {
 		//found beforeDockerBuildScripts/afterDockerBuildScripts
 		//building preCiSteps & postCiSteps from them, refPluginsData not needed
 		preCiSteps = buildCiStepsDataFromDockerBuildScripts(beforeDockerBuildScripts)
 		postCiSteps = buildCiStepsDataFromDockerBuildScripts(afterDockerBuildScripts)
-	} else {
-		//beforeDockerBuildScripts & afterDockerBuildScripts not found
-		//getting preCiStepsData, postCiStepsData & refPluginsData
-		preCiSteps, postCiSteps, refPluginsData, err = impl.pipelineStageService.BuildPrePostAndRefPluginStepsDataForWfRequest(pipeline.Id)
-		if err != nil {
-			impl.Logger.Errorw("error in getting pre, post & refPlugin steps data for wf request", "err", err, "ciPipelineId", pipeline.Id)
-			return nil, err
-		}
+		refPluginsData = []*bean2.RefPluginObject{}
 	}
 	dockerImageTag := impl.buildImageTag(commitHashes, pipeline.Id, savedWf.Id)
 	if ciWorkflowConfig.CiCacheBucket == "" {
@@ -499,6 +545,8 @@ func (impl *CiServiceImpl) buildWfRequestForCiPipeline(pipeline *pipelineConfig.
 		CacheInvalidate:            trigger.InvalidateCache,
 		ExtraEnvironmentVariables:  trigger.ExtraEnvironmentVariables,
 		EnableBuildContext:         impl.ciConfig.EnableBuildContext,
+		OrchestratorHost:           impl.ciConfig.OrchestratorHost,
+		OrchestratorToken:          impl.ciConfig.OrchestratorToken,
 		ImageRetryCount:            impl.ciConfig.ImageRetryCount,
 		ImageRetryInterval:         impl.ciConfig.ImageRetryInterval,
 	}
diff --git a/pkg/pipeline/ConfigMapService.go b/pkg/pipeline/ConfigMapService.go
index 8940373a9..bc12e3526 100644
--- a/pkg/pipeline/ConfigMapService.go
+++ b/pkg/pipeline/ConfigMapService.go
@@ -19,14 +19,17 @@ package pipeline
 
 import (
 	"encoding/json"
+	"errors"
 	"fmt"
 	"github.com/devtron-labs/devtron/internal/sql/repository/app"
 	"github.com/devtron-labs/devtron/internal/sql/repository/chartConfig"
 	"github.com/devtron-labs/devtron/internal/util"
 	chartRepoRepository "github.com/devtron-labs/devtron/pkg/chartRepo/repository"
+	repository2 "github.com/devtron-labs/devtron/pkg/cluster/repository"
 	"github.com/devtron-labs/devtron/pkg/commonService"
 	history2 "github.com/devtron-labs/devtron/pkg/pipeline/history"
 	"github.com/devtron-labs/devtron/pkg/pipeline/history/repository"
+	"github.com/devtron-labs/devtron/pkg/sql"
 	util2 "github.com/devtron-labs/devtron/util"
 	"github.com/go-pg/pg"
 	"go.uber.org/zap"
@@ -52,6 +55,13 @@ type ConfigDataRequest struct {
 	UserId        int32         `json:"-"`
 }
 
+type JobEnvOverrideResponse struct {
+	Id              int    `json:"id"`
+	AppId           int    `json:"appId"`
+	EnvironmentId   int    `json:"environmentId,omitempty"`
+	EnvironmentName string `json:"environmentName,omitempty"`
+}
+
 type BulkPatchRequest struct {
 	Payload     []*BulkPatchPayload `json:"payload"`
 	Filter      *BulkPatchFilter    `json:"filter,omitempty"`
@@ -70,6 +80,12 @@ type BulkPatchPayload struct {
 	EnvId int `json:"envId"`
 }
 
+type CreateJobEnvOverridePayload struct {
+	AppId  int   `json:"appId"`
+	EnvId  int   `json:"envId"`
+	UserId int32 `json:"-"`
+}
+
 type BulkPatchFilter struct {
 	AppNameIncludes string `json:"appNameIncludes,omitempty"`
 	AppNameExcludes string `json:"appNameExcludes,omitempty"`
@@ -153,6 +169,11 @@ type ConfigMapService interface {
 	CSEnvironmentFetchForEdit(name string, id int, appId int, envId int) (*ConfigDataRequest, error)
 	ConfigSecretGlobalBulkPatch(bulkPatchRequest *BulkPatchRequest) (*BulkPatchRequest, error)
 	ConfigSecretEnvironmentBulkPatch(bulkPatchRequest *BulkPatchRequest) (*BulkPatchRequest, error)
+
+	ConfigSecretEnvironmentCreate(createJobEnvOverrideRequest *CreateJobEnvOverridePayload) (*CreateJobEnvOverridePayload, error)
+	ConfigSecretEnvironmentDelete(createJobEnvOverrideRequest *CreateJobEnvOverridePayload) (*CreateJobEnvOverridePayload, error)
+	ConfigSecretEnvironmentGet(appId int) ([]JobEnvOverrideResponse, error)
+	ConfigSecretEnvironmentClone(appId int, cloneAppId int, userId int32) ([]chartConfig.ConfigMapEnvModel, error)
 }
 
 type ConfigMapServiceImpl struct {
@@ -166,6 +187,7 @@ type ConfigMapServiceImpl struct {
 	commonService               commonService.CommonService
 	appRepository               app.AppRepository
 	configMapHistoryService     history2.ConfigMapHistoryService
+	environmentRepository       repository2.EnvironmentRepository
 }
 
 func NewConfigMapServiceImpl(chartRepository chartRepoRepository.ChartRepository,
@@ -175,7 +197,7 @@ func NewConfigMapServiceImpl(chartRepository chartRepoRepository.ChartRepository
 	pipelineConfigRepository chartConfig.PipelineConfigRepository,
 	configMapRepository chartConfig.ConfigMapRepository, environmentConfigRepository chartConfig.EnvConfigOverrideRepository,
 	commonService commonService.CommonService, appRepository app.AppRepository,
-	configMapHistoryService history2.ConfigMapHistoryService) *ConfigMapServiceImpl {
+	configMapHistoryService history2.ConfigMapHistoryService, environmentRepository repository2.EnvironmentRepository) *ConfigMapServiceImpl {
 	return &ConfigMapServiceImpl{
 		chartRepository:             chartRepository,
 		logger:                      logger,
@@ -187,6 +209,7 @@ func NewConfigMapServiceImpl(chartRepository chartRepoRepository.ChartRepository
 		commonService:               commonService,
 		appRepository:               appRepository,
 		configMapHistoryService:     configMapHistoryService,
+		environmentRepository:       environmentRepository,
 	}
 }
 
@@ -417,8 +440,6 @@ func (impl ConfigMapServiceImpl) CMEnvironmentAddUpdate(configMapRequest *Config
 		}
 		model.CreatedBy = configMapRequest.UserId
 		model.UpdatedBy = configMapRequest.UserId
-		model.CreatedOn = time.Now()
-		model.UpdatedOn = time.Now()
 
 		configMap, err := impl.configMapRepository.CreateEnvLevel(model)
 		if err != nil {
@@ -798,8 +819,6 @@ func (impl ConfigMapServiceImpl) CSEnvironmentAddUpdate(configMapRequest *Config
 		}
 		model.CreatedBy = configMapRequest.UserId
 		model.UpdatedBy = configMapRequest.UserId
-		model.CreatedOn = time.Now()
-		model.UpdatedOn = time.Now()
 
 		configMap, err := impl.configMapRepository.CreateEnvLevel(model)
 		if err != nil {
@@ -1784,3 +1803,145 @@ func (impl ConfigMapServiceImpl) buildBulkPayload(bulkPatchRequest *BulkPatchReq
 	}
 	return bulkPatchRequest, nil
 }
+
+func (impl ConfigMapServiceImpl) ConfigSecretEnvironmentCreate(createJobEnvOverrideRequest *CreateJobEnvOverridePayload) (*CreateJobEnvOverridePayload, error) {
+	configMap, err := impl.configMapRepository.GetByAppIdAndEnvIdEnvLevel(createJobEnvOverrideRequest.AppId, createJobEnvOverrideRequest.EnvId)
+	if err != nil && err != pg.ErrNoRows {
+		impl.logger.Errorw("error while fetching from db", "error", err)
+		return nil, err
+	}
+	if err != nil {
+		model := &chartConfig.ConfigMapEnvModel{
+			AppId:         createJobEnvOverrideRequest.AppId,
+			EnvironmentId: createJobEnvOverrideRequest.EnvId,
+			Deleted:       false,
+		}
+		model.CreatedBy = createJobEnvOverrideRequest.UserId
+		model.UpdatedBy = createJobEnvOverrideRequest.UserId
+		configMap, err = impl.configMapRepository.CreateEnvLevel(model)
+		if err != nil {
+			impl.logger.Errorw("error while creating env level", "error", err)
+			return nil, err
+		}
+		return createJobEnvOverrideRequest, nil
+
+	}
+	if configMap.Deleted {
+		configMap.Deleted = false
+		_, err = impl.configMapRepository.UpdateEnvLevel(configMap)
+		if err != nil {
+			impl.logger.Errorw("error while creating env level", "error", err)
+			return nil, err
+		}
+		return createJobEnvOverrideRequest, nil
+	}
+	env, err := impl.environmentRepository.FindById(configMap.EnvironmentId)
+	if err != nil {
+		impl.logger.Errorw("error while fetching environment from db", "error", err)
+		return nil, err
+	}
+	impl.logger.Warnw("Environment override in this environment already exits", "appId", createJobEnvOverrideRequest.AppId, "envId", createJobEnvOverrideRequest.EnvId)
+	return nil, errors.New("Environment " + env.Name + " already exists.")
+
+}
+
+func (impl ConfigMapServiceImpl) ConfigSecretEnvironmentDelete(createJobEnvOverrideRequest *CreateJobEnvOverridePayload) (*CreateJobEnvOverridePayload, error) {
+	configMap, err := impl.configMapRepository.GetByAppIdAndEnvIdEnvLevel(createJobEnvOverrideRequest.AppId, createJobEnvOverrideRequest.EnvId)
+	if pg.ErrNoRows == err {
+		impl.logger.Warnw("Environment override in this environment doesn't exits", "appId", createJobEnvOverrideRequest.AppId, "envId", createJobEnvOverrideRequest.EnvId)
+		return nil, err
+	}
+	if err != nil {
+		impl.logger.Errorw("error while fetching from db", "error", err)
+		return nil, err
+	}
+	configMap.Deleted = true
+	configMap.ConfigMapData = ""
+	configMap.SecretData = ""
+	configMap.UpdatedBy = createJobEnvOverrideRequest.UserId
+	_, err = impl.configMapRepository.UpdateEnvLevel(configMap)
+	if err != nil {
+		impl.logger.Errorw("error while updating env level", "error", err)
+		return nil, err
+	}
+	return createJobEnvOverrideRequest, nil
+}
+
+func (impl ConfigMapServiceImpl) ConfigSecretEnvironmentGet(appId int) ([]JobEnvOverrideResponse, error) {
+	configMap, err := impl.configMapRepository.GetEnvLevelByAppId(appId)
+	if err != nil {
+		impl.logger.Errorw("error while fetching envConfig from db", "error", err)
+		return nil, err
+	}
+	var envIds []*int
+	for _, cm := range configMap {
+		envIds = append(envIds, &cm.EnvironmentId)
+	}
+	var jobEnvOverrideResponse []JobEnvOverrideResponse
+
+	if len(envIds) == 0 {
+		return jobEnvOverrideResponse, nil
+	}
+	envs, err := impl.environmentRepository.FindByIds(envIds)
+
+	if err != nil {
+		impl.logger.Errorw("error while fetching environments from db", "error", err)
+		return nil, err
+	}
+
+	envIdNameMap := make(map[int]string)
+
+	for _, env := range envs {
+
+		envIdNameMap[env.Id] = env.Name
+	}
+
+	for _, cm := range configMap {
+		jobEnvOverride := JobEnvOverrideResponse{
+			EnvironmentId:   cm.EnvironmentId,
+			AppId:           cm.AppId,
+			Id:              cm.Id,
+			EnvironmentName: envIdNameMap[cm.EnvironmentId],
+		}
+		jobEnvOverrideResponse = append(jobEnvOverrideResponse, jobEnvOverride)
+	}
+
+	return jobEnvOverrideResponse, nil
+}
+
+func (impl ConfigMapServiceImpl) ConfigSecretEnvironmentClone(appId int, cloneAppId int, userId int32) ([]chartConfig.ConfigMapEnvModel, error) {
+	configMap, err := impl.configMapRepository.GetEnvLevelByAppId(appId)
+	if err != nil {
+		impl.logger.Errorw("error while fetching envConfig from db", "error", err)
+		return nil, err
+	}
+	var jobEnvOverrideResponse []chartConfig.ConfigMapEnvModel
+
+	if err != nil {
+		impl.logger.Errorw("error while fetching environments from db", "error", err)
+		return nil, err
+	}
+
+	for _, cm := range configMap {
+		model := &chartConfig.ConfigMapEnvModel{
+			AppId:         cloneAppId,
+			EnvironmentId: cm.EnvironmentId,
+			ConfigMapData: cm.ConfigMapData,
+			SecretData:    cm.SecretData,
+			Deleted:       cm.Deleted,
+			AuditLog: sql.AuditLog{
+				CreatedBy: userId,
+				UpdatedBy: userId,
+			},
+		}
+
+		_, err := impl.configMapRepository.CreateEnvLevel(model)
+		if err != nil {
+			impl.logger.Errorw("error while creating env level", "error", err)
+			return nil, err
+		}
+		jobEnvOverrideResponse = append(jobEnvOverrideResponse, *model)
+	}
+
+	return jobEnvOverrideResponse, nil
+}
diff --git a/pkg/pipeline/ImageTaggingService.go b/pkg/pipeline/ImageTaggingService.go
index 7f55d4e04..a245cd2e4 100644
--- a/pkg/pipeline/ImageTaggingService.go
+++ b/pkg/pipeline/ImageTaggingService.go
@@ -111,9 +111,9 @@ func (impl ImageTaggingServiceImpl) GetImageTaggingServiceConfig() ImageTaggingS
 }
 
 // GetTagsData returns the following fields in reponse Object
-//ImageReleaseTags -> this will get the tags of the artifact,
-//AppReleaseTags -> all the tags of the given appId,
-//imageComment -> comment of the given artifactId,
+// ImageReleaseTags -> this will get the tags of the artifact,
+// AppReleaseTags -> all the tags of the given appId,
+// imageComment -> comment of the given artifactId,
 // ProdEnvExists -> implies the existence of prod environment in any workflow of given ciPipelineId or its child ciPipeline's
 func (impl ImageTaggingServiceImpl) GetTagsData(ciPipelineId, appId, artifactId int, externalCi bool) (*ImageTaggingResponseDTO, error) {
 	resp := &ImageTaggingResponseDTO{}
diff --git a/pkg/pipeline/PipelineBuilder.go b/pkg/pipeline/PipelineBuilder.go
index 6beceede1..8108f1abc 100644
--- a/pkg/pipeline/PipelineBuilder.go
+++ b/pkg/pipeline/PipelineBuilder.go
@@ -831,6 +831,26 @@ func (impl PipelineBuilderImpl) GetCiPipeline(appId int) (ciConfig *bean.CiConfi
 			ScanEnabled:              pipeline.ScanEnabled,
 			IsDockerConfigOverridden: pipeline.IsDockerConfigOverridden,
 		}
+		ciEnvMapping, err := impl.ciPipelineRepository.FindCiEnvMappingByCiPipelineId(pipeline.Id)
+		if err != nil && err != pg.ErrNoRows {
+			impl.logger.Errorw("error in fetching ciEnvMapping", "ciPipelineId ", pipeline.Id, "err", err)
+			return nil, err
+		}
+		if ciEnvMapping.Id > 0 {
+			ciPipeline.EnvironmentId = ciEnvMapping.EnvironmentId
+		}
+
+		lastTriggeredWorkflowEnv, err := impl.ciWorkflowRepository.FindLastTriggeredWorkflow(pipeline.Id)
+		if err != nil && err != pg.ErrNoRows {
+			impl.logger.Errorw("error in fetching lasTriggeredWorkflowEnv", "ciPipelineId ", pipeline.Id, "err", err)
+			return nil, err
+		}
+		if err == pg.ErrNoRows {
+			ciPipeline.LastTriggeredEnvId = -1
+		} else {
+			ciPipeline.LastTriggeredEnvId = lastTriggeredWorkflowEnv.EnvironmentId
+		}
+
 		if ciTemplateBean, ok := ciOverrideTemplateMap[pipeline.Id]; ok {
 			templateOverride := ciTemplateBean.CiTemplateOverride
 			ciPipeline.DockerConfigOverride = bean.DockerConfigOverride{
@@ -1448,10 +1468,16 @@ func (impl PipelineBuilderImpl) PatchCiPipeline(request *bean.CiPatchRequest) (c
 		request.CiPipeline.ScanEnabled = request.CiPipeline.ScanEnabled || impl.securityConfig.ForceSecurityScanning
 		ciConfig.ScanEnabled = request.CiPipeline.ScanEnabled
 	}
+
+	ciConfig.IsJob = request.IsJob
+	// Check for clone job to not create env override again
+	ciConfig.IsCloneJob = request.IsCloneJob
+
 	switch request.Action {
 	case bean.CREATE:
 		impl.logger.Debugw("create patch request")
 		ciConfig.CiPipelines = []*bean.CiPipeline{request.CiPipeline} //request.CiPipeline
+
 		res, err := impl.addpipelineToTemplate(ciConfig)
 		if err != nil {
 			impl.logger.Errorw("error in adding pipeline to template", "ciConf", ciConfig, "err", err)
@@ -4065,6 +4091,14 @@ func (impl PipelineBuilderImpl) GetCiPipelineById(pipelineId int) (ciPipeline *b
 		ScanEnabled:              pipeline.ScanEnabled,
 		IsDockerConfigOverridden: pipeline.IsDockerConfigOverridden,
 	}
+	ciEnvMapping, err := impl.ciPipelineRepository.FindCiEnvMappingByCiPipelineId(pipelineId)
+	if err != nil && err != pg.ErrNoRows {
+		impl.logger.Errorw("error in fetching ci env mapping", "pipelineId", pipelineId, "err", err)
+		return nil, err
+	}
+	if ciEnvMapping.Id > 0 {
+		ciPipeline.EnvironmentId = ciEnvMapping.EnvironmentId
+	}
 	if !ciPipeline.IsExternal && ciPipeline.IsDockerConfigOverridden {
 		ciTemplateBean, err := impl.ciTemplateService.FindTemplateOverrideByCiPipelineId(ciPipeline.Id)
 		if err != nil {
diff --git a/pkg/pipeline/WorkflowService.go b/pkg/pipeline/WorkflowService.go
index b282d162f..76d6a4e0d 100644
--- a/pkg/pipeline/WorkflowService.go
+++ b/pkg/pipeline/WorkflowService.go
@@ -26,10 +26,15 @@ import (
 	v1alpha12 "github.com/argoproj/argo-workflows/v3/pkg/client/clientset/versioned/typed/workflow/v1alpha1"
 	"github.com/argoproj/argo-workflows/v3/workflow/util"
 	blob_storage "github.com/devtron-labs/common-lib/blob-storage"
+	bean3 "github.com/devtron-labs/devtron/api/bean"
 	"github.com/devtron-labs/devtron/internal/sql/repository"
+	"github.com/devtron-labs/devtron/internal/sql/repository/chartConfig"
 	"github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig"
+	"github.com/devtron-labs/devtron/pkg/app"
 	"github.com/devtron-labs/devtron/pkg/bean"
+	repository2 "github.com/devtron-labs/devtron/pkg/cluster/repository"
 	bean2 "github.com/devtron-labs/devtron/pkg/pipeline/bean"
+	"github.com/devtron-labs/devtron/util/k8s"
 	"go.uber.org/zap"
 	v12 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/resource"
@@ -42,12 +47,12 @@ import (
 )
 
 type WorkflowService interface {
-	SubmitWorkflow(workflowRequest *WorkflowRequest, appLabels map[string]string) (*v1alpha1.Workflow, error)
+	SubmitWorkflow(workflowRequest *WorkflowRequest, appLabels map[string]string, env *repository2.Environment, isJob bool) (*v1alpha1.Workflow, error)
 	DeleteWorkflow(wfName string, namespace string) error
-	GetWorkflow(name string, namespace string) (*v1alpha1.Workflow, error)
+	GetWorkflow(name string, namespace string, isExt bool, environment *repository2.Environment) (*v1alpha1.Workflow, error)
 	ListAllWorkflows(namespace string) (*v1alpha1.WorkflowList, error)
 	UpdateWorkflow(wf *v1alpha1.Workflow) (*v1alpha1.Workflow, error)
-	TerminateWorkflow(name string, namespace string) error
+	TerminateWorkflow(name string, namespace string, isExt bool, environment *repository2.Environment) error
 }
 
 type CiCdTriggerEvent struct {
@@ -57,10 +62,13 @@ type CiCdTriggerEvent struct {
 }
 
 type WorkflowServiceImpl struct {
-	Logger            *zap.SugaredLogger
-	config            *rest.Config
-	ciConfig          *CiConfig
-	globalCMCSService GlobalCMCSService
+	Logger                *zap.SugaredLogger
+	config                *rest.Config
+	ciConfig              *CiConfig
+	globalCMCSService     GlobalCMCSService
+	appService            app.AppService
+	configMapRepository   chartConfig.ConfigMapRepository
+	k8sApplicationService k8s.K8sApplicationService
 }
 
 type WorkflowRequest struct {
@@ -120,6 +128,11 @@ type WorkflowRequest struct {
 	IsPvcMounted               bool                              `json:"IsPvcMounted"`
 	ExtraEnvironmentVariables  map[string]string                 `json:"extraEnvironmentVariables"`
 	EnableBuildContext         bool                              `json:"enableBuildContext"`
+	AppId                      int                               `json:"appId"`
+	EnvironmentId              int                               `json:"environmentId"`
+	OrchestratorHost           string                            `json:"orchestratorHost"`
+	OrchestratorToken          string                            `json:"orchestratorToken"`
+	IsExtRun                   bool                              `json:"isExtRun"`
 	ImageRetryCount            int                               `json:"imageRetryCount"`
 	ImageRetryInterval         int                               `json:"imageRetryInterval"`
 }
@@ -188,19 +201,23 @@ type GitOptions struct {
 }
 
 func NewWorkflowServiceImpl(Logger *zap.SugaredLogger, ciConfig *CiConfig,
-	globalCMCSService GlobalCMCSService) *WorkflowServiceImpl {
+	globalCMCSService GlobalCMCSService, appService app.AppService,
+	configMapRepository chartConfig.ConfigMapRepository, k8sApplicationService k8s.K8sApplicationService) *WorkflowServiceImpl {
 	return &WorkflowServiceImpl{
-		Logger:            Logger,
-		config:            ciConfig.ClusterConfig,
-		ciConfig:          ciConfig,
-		globalCMCSService: globalCMCSService,
+		Logger:                Logger,
+		config:                ciConfig.ClusterConfig,
+		ciConfig:              ciConfig,
+		globalCMCSService:     globalCMCSService,
+		appService:            appService,
+		configMapRepository:   configMapRepository,
+		k8sApplicationService: k8sApplicationService,
 	}
 }
 
 const ciEvent = "CI"
 const cdStage = "CD"
 
-func (impl *WorkflowServiceImpl) SubmitWorkflow(workflowRequest *WorkflowRequest, appLabels map[string]string) (*v1alpha1.Workflow, error) {
+func (impl *WorkflowServiceImpl) SubmitWorkflow(workflowRequest *WorkflowRequest, appLabels map[string]string, env *repository2.Environment, isJob bool) (*v1alpha1.Workflow, error) {
 	containerEnvVariables := []v12.EnvVar{{Name: "IMAGE_SCANNER_ENDPOINT", Value: impl.ciConfig.ImageScannerEndpoint}}
 	if impl.ciConfig.CloudProvider == BLOB_STORAGE_S3 && impl.ciConfig.BlobStorageS3AccessKey != "" {
 		miniCred := []v12.EnvVar{{Name: "AWS_ACCESS_KEY_ID", Value: impl.ciConfig.BlobStorageS3AccessKey}, {Name: "AWS_SECRET_ACCESS_KEY", Value: impl.ciConfig.BlobStorageS3SecretKey}}
@@ -219,7 +236,9 @@ func (impl *WorkflowServiceImpl) SubmitWorkflow(workflowRequest *WorkflowRequest
 		Type:      ciEvent,
 		CiRequest: workflowRequest,
 	}
-
+	if env != nil && env.Id != 0 {
+		workflowRequest.IsExtRun = true
+	}
 	ciCdTriggerEvent.CiRequest.BlobStorageLogsKey = fmt.Sprintf("%s/%s", impl.ciConfig.DefaultBuildLogsKeyPrefix, workflowRequest.WorkflowNamePrefix)
 	ciCdTriggerEvent.CiRequest.InAppLoggingEnabled = impl.ciConfig.InAppLoggingEnabled
 	workflowJson, err := json.Marshal(&ciCdTriggerEvent)
@@ -229,10 +248,21 @@ func (impl *WorkflowServiceImpl) SubmitWorkflow(workflowRequest *WorkflowRequest
 	}
 	impl.Logger.Debugw("workflowRequest ---->", "workflowJson", string(workflowJson))
 
-	wfClient, err := impl.getClientInstance(workflowRequest.Namespace)
-	if err != nil {
-		impl.Logger.Errorw("cannot build wf client", "err", err)
-		return nil, err
+	var wfClient v1alpha12.WorkflowInterface
+
+	if isJob && env != nil && env.Id != 0 {
+		workflowRequest.EnvironmentId = env.Id
+		wfClient, err = impl.getRuntimeEnvClientInstance(env)
+		if err != nil {
+			impl.Logger.Errorw("cannot build wf client", "err", err)
+			return nil, err
+		}
+	} else {
+		wfClient, err = impl.getClientInstance(workflowRequest.Namespace)
+		if err != nil {
+			impl.Logger.Errorw("cannot build wf client", "err", err)
+			return nil, err
+		}
 	}
 
 	privileged := true
@@ -248,27 +278,49 @@ func (impl *WorkflowServiceImpl) SubmitWorkflow(workflowRequest *WorkflowRequest
 
 	entryPoint := CI_WORKFLOW_NAME // template name from where worklow execution will start
 	//getting all cm/cs to be used by default
-	globalCmCsConfigs, err := impl.globalCMCSService.FindAllActiveByPipelineType(repository.PIPELINE_TYPE_CI)
-	if err != nil {
-		impl.Logger.Errorw("error in getting all global cm/cs config", "err", err)
-		return nil, err
-	}
-	if len(globalCmCsConfigs) > 0 {
-		entryPoint = CI_WORKFLOW_WITH_STAGES
-	}
-	for i := range globalCmCsConfigs {
-		globalCmCsConfigs[i].Name = strings.ToLower(globalCmCsConfigs[i].Name) + "-" + strconv.Itoa(workflowRequest.WorkflowId) + "-" + CI_WORKFLOW_NAME
-	}
-
 	steps := make([]v1alpha1.ParallelSteps, 0)
 	volumes := make([]v12.Volume, 0)
 	templates := make([]v1alpha1.Template, 0)
-
-	err = AddTemplatesForGlobalSecretsInWorkflowTemplate(globalCmCsConfigs, &steps, &volumes, &templates)
-	if err != nil {
-		impl.Logger.Errorw("error in creating templates for global secrets", "err", err)
+	var globalCmCsConfigs []*bean2.GlobalCMCSDto
+	if !workflowRequest.IsExtRun {
+		globalCmCsConfigs, err = impl.globalCMCSService.FindAllActiveByPipelineType(repository.PIPELINE_TYPE_CI)
+		if err != nil {
+			impl.Logger.Errorw("error in getting all global cm/cs config", "err", err)
+			return nil, err
+		}
+		if len(globalCmCsConfigs) > 0 {
+			entryPoint = CI_WORKFLOW_WITH_STAGES
+		}
+		for i := range globalCmCsConfigs {
+			globalCmCsConfigs[i].Name = strings.ToLower(globalCmCsConfigs[i].Name) + "-" + strconv.Itoa(workflowRequest.WorkflowId) + "-" + CI_WORKFLOW_NAME
+		}
+		err = AddTemplatesForGlobalSecretsInWorkflowTemplate(globalCmCsConfigs, &steps, &volumes, &templates)
+		if err != nil {
+			impl.Logger.Errorw("error in creating templates for global secrets", "err", err)
+		}
 	}
 
+	var configMaps bean3.ConfigMapJson
+	var secrets bean3.ConfigSecretJson
+	var existingConfigMap *bean3.ConfigMapJson
+	var existingSecrets *bean3.ConfigSecretJson
+	if isJob {
+		existingConfigMap, existingSecrets, err = impl.appService.GetCmSecretNew(workflowRequest.AppId, workflowRequest.EnvironmentId, isJob)
+		if err != nil {
+			impl.Logger.Errorw("failed to get configmap data", "err", err)
+			return nil, err
+		}
+		impl.Logger.Debugw("existing cm sec", "cm", existingConfigMap, "sec", existingSecrets)
+		configMaps, secrets, err = getConfigMapsAndSecrets(workflowRequest, existingConfigMap, existingSecrets)
+		if err != nil {
+			impl.Logger.Errorw("fail to get config map and secret with new name", err)
+		}
+
+		err = processConfigMapsAndSecrets(impl, &configMaps, &secrets, &entryPoint, &steps, &volumes, &templates)
+		if err != nil {
+			impl.Logger.Errorw("fail to append cm/cs", err)
+		}
+	}
 	steps = append(steps, v1alpha1.ParallelSteps{
 		Steps: []v1alpha1.WorkflowStep{
 			{
@@ -316,7 +368,9 @@ func (impl *WorkflowServiceImpl) SubmitWorkflow(workflowRequest *WorkflowRequest
 			ArchiveLogs: &archiveLogs,
 		},
 	}
-
+	if isJob {
+		ciTemplate, err = getCiTemplateWithConfigMapsAndSecrets(&configMaps, &secrets, ciTemplate, existingConfigMap, existingSecrets)
+	}
 	if impl.ciConfig.UseBlobStorageConfigInCiWorkflow {
 		gcpBlobConfig := workflowRequest.GcpBlobConfig
 		blobStorageS3Config := workflowRequest.BlobStorageS3Config
@@ -521,6 +575,195 @@ func (impl *WorkflowServiceImpl) SubmitWorkflow(workflowRequest *WorkflowRequest
 	return createdWf, err
 }
 
+func getConfigMapsAndSecrets(workflowRequest *WorkflowRequest, existingConfigMap *bean3.ConfigMapJson, existingSecrets *bean3.ConfigSecretJson) (bean3.ConfigMapJson, bean3.ConfigSecretJson, error) {
+	configMaps := bean3.ConfigMapJson{}
+	secrets := bean3.ConfigSecretJson{}
+	for _, cm := range existingConfigMap.Maps {
+		if cm.External {
+			continue
+		}
+		configMaps.Maps = append(configMaps.Maps, cm)
+	}
+	for i := range configMaps.Maps {
+		configMaps.Maps[i].Name = configMaps.Maps[i].Name + "-" + strconv.Itoa(workflowRequest.WorkflowId) + "-" + CI_WORKFLOW_NAME
+	}
+
+	for _, s := range existingSecrets.Secrets {
+		if s.External {
+			continue
+		}
+		secrets.Secrets = append(secrets.Secrets, s)
+	}
+	for i := range secrets.Secrets {
+		secrets.Secrets[i].Name = secrets.Secrets[i].Name + "-" + strconv.Itoa(workflowRequest.WorkflowId) + "-" + CI_WORKFLOW_NAME
+	}
+	return configMaps, secrets, nil
+}
+
+func processConfigMapsAndSecrets(impl *WorkflowServiceImpl, configMaps *bean3.ConfigMapJson, secrets *bean3.ConfigSecretJson, entryPoint *string, steps *[]v1alpha1.ParallelSteps, volumes *[]v12.Volume, templates *[]v1alpha1.Template) error {
+
+	var configsMapping, secretsMapping map[string]string
+	var err error
+
+	if len(configMaps.Maps) > 0 {
+		configsMapping, err = processConfigMap(impl, configMaps, entryPoint, steps)
+		if err != nil {
+			return err
+		}
+	}
+
+	if len(secrets.Secrets) > 0 {
+		secretsMapping, err = processSecrets(impl, entryPoint, secrets, steps)
+		if err != nil {
+			return err
+		}
+	}
+	var secretMap []bean3.ConfigSecretMap
+	for _, cs := range secrets.Secrets {
+		secretMap = append(secretMap, *cs)
+	}
+
+	*volumes = ExtractVolumesFromCmCs(configMaps.Maps, secretMap)
+	if len(configsMapping) > 0 {
+		for i, cm := range configMaps.Maps {
+			*templates = append(*templates, getResourceTemplate("cm-"+strconv.Itoa(i), configsMapping[cm.Name]))
+		}
+	}
+
+	if len(secretsMapping) > 0 {
+		for i, s := range secrets.Secrets {
+			*templates = append(*templates, getResourceTemplate("sec-"+strconv.Itoa(i), secretsMapping[s.Name]))
+		}
+	}
+	return nil
+}
+
+func getResourceTemplate(prefix string, manifestName string) v1alpha1.Template {
+	return v1alpha1.Template{
+		Name: prefix,
+		Resource: &v1alpha1.ResourceTemplate{
+			Action:            "create",
+			SetOwnerReference: true,
+			Manifest:          manifestName,
+		},
+	}
+}
+
+func processSecrets(impl *WorkflowServiceImpl, entryPoint *string, secrets *bean3.ConfigSecretJson, steps *[]v1alpha1.ParallelSteps) (map[string]string, error) {
+	secretsMapping := make(map[string]string)
+	*entryPoint = CI_WORKFLOW_WITH_STAGES
+	for i, s := range secrets.Secrets {
+		var datamap map[string][]byte
+		if err := json.Unmarshal(s.Data, &datamap); err != nil {
+			impl.Logger.Errorw("error while unmarshal data", "err", err)
+			return secretsMapping, err
+		}
+		ownerDelete := true
+		secretObject := v12.Secret{
+			TypeMeta: v1.TypeMeta{
+				Kind:       "Secret",
+				APIVersion: "v1",
+			},
+			ObjectMeta: v1.ObjectMeta{
+				Name: s.Name,
+				OwnerReferences: []v1.OwnerReference{{
+					APIVersion:         "argoproj.io/v1alpha1",
+					Kind:               "Workflow",
+					Name:               "{{workflow.name}}",
+					UID:                "{{workflow.uid}}",
+					BlockOwnerDeletion: &ownerDelete,
+				}},
+			},
+			Data: datamap,
+			Type: "Opaque",
+		}
+		secretJson, err := json.Marshal(secretObject)
+		if err != nil {
+			impl.Logger.Errorw("error in building json", "err", err)
+			return secretsMapping, err
+		}
+		secretsMapping[s.Name] = string(secretJson)
+		*steps = append(*steps, v1alpha1.ParallelSteps{
+			Steps: []v1alpha1.WorkflowStep{
+				{
+					Name:     "create-env-sec-" + strconv.Itoa(i),
+					Template: "sec-" + strconv.Itoa(i),
+				},
+			},
+		})
+	}
+	return secretsMapping, nil
+}
+
+func processConfigMap(impl *WorkflowServiceImpl, configMaps *bean3.ConfigMapJson, entryPoint *string, steps *[]v1alpha1.ParallelSteps) (map[string]string, error) {
+	configsMapping := make(map[string]string)
+	*entryPoint = CI_WORKFLOW_WITH_STAGES
+	for i, cm := range configMaps.Maps {
+		var dataMap map[string]string
+		if err := json.Unmarshal(cm.Data, &dataMap); err != nil {
+			impl.Logger.Errorw("error while unmarshal data", "err", err)
+			return configsMapping, err
+		}
+		ownerDelete := true
+		cmBody := v12.ConfigMap{
+			TypeMeta: v1.TypeMeta{
+				Kind:       "ConfigMap",
+				APIVersion: "v1",
+			},
+			ObjectMeta: v1.ObjectMeta{
+				Name: cm.Name,
+				OwnerReferences: []v1.OwnerReference{{
+					APIVersion:         "argoproj.io/v1alpha1",
+					Kind:               "Workflow",
+					Name:               "{{workflow.name}}",
+					UID:                "{{workflow.uid}}",
+					BlockOwnerDeletion: &ownerDelete,
+				}},
+			},
+			Data: dataMap,
+		}
+		cmJson, err := json.Marshal(cmBody)
+		if err != nil {
+			impl.Logger.Errorw("error in building json", "err", err)
+			return configsMapping, err
+		}
+		configsMapping[cm.Name] = string(cmJson)
+
+		*steps = append(*steps, v1alpha1.ParallelSteps{
+			Steps: []v1alpha1.WorkflowStep{
+				{
+					Name:     "create-env-cm-" + strconv.Itoa(i),
+					Template: "cm-" + strconv.Itoa(i),
+				},
+			},
+		})
+	}
+	return configsMapping, nil
+}
+func getCiTemplateWithConfigMapsAndSecrets(configMaps *bean3.ConfigMapJson, secrets *bean3.ConfigSecretJson, ciTemplate v1alpha1.Template, existingConfigMap *bean3.ConfigMapJson, existingSecrets *bean3.ConfigSecretJson) (v1alpha1.Template, error) {
+	var secretMap []bean3.ConfigSecretMap
+	for _, cs := range secrets.Secrets {
+		secretMap = append(secretMap, *cs)
+	}
+	UpdateContainerEnvsFromCmCs(ciTemplate.Container, configMaps.Maps, secretMap)
+	return ciTemplate, nil
+}
+
+func (impl *WorkflowServiceImpl) getRuntimeEnvClientInstance(environment *repository2.Environment) (v1alpha12.WorkflowInterface, error) {
+	restConfig, err := impl.k8sApplicationService.GetRestConfigByClusterId(context.Background(), environment.ClusterId)
+	if err != nil {
+		impl.Logger.Errorw("error in getting rest config buy cluster id", "err", err)
+		return nil, err
+	}
+	clientSet, err := versioned.NewForConfig(restConfig)
+	if err != nil {
+		impl.Logger.Errorw("err", "err", err)
+		return nil, err
+	}
+	wfClient := clientSet.ArgoprojV1alpha1().Workflows(environment.Namespace) // create the workflow client
+	return wfClient, nil
+}
+
 func (impl *WorkflowServiceImpl) getClientInstance(namespace string) (v1alpha12.WorkflowInterface, error) {
 	clientSet, err := versioned.NewForConfig(impl.config)
 	if err != nil {
@@ -531,24 +774,47 @@ func (impl *WorkflowServiceImpl) getClientInstance(namespace string) (v1alpha12.
 	return wfClient, nil
 }
 
-func (impl *WorkflowServiceImpl) GetWorkflow(name string, namespace string) (*v1alpha1.Workflow, error) {
+func (impl *WorkflowServiceImpl) GetWorkflow(name string, namespace string, isExt bool, environment *repository2.Environment) (*v1alpha1.Workflow, error) {
 	impl.Logger.Debug("getting wf", name)
-	wfClient, err := impl.getClientInstance(namespace)
-	if err != nil {
-		impl.Logger.Errorw("cannot build wf client", "err", err)
-		return nil, err
+	var wfClient v1alpha12.WorkflowInterface
+	var err error
+	if isExt {
+		wfClient, err = impl.getRuntimeEnvClientInstance(environment)
+		if err != nil {
+			impl.Logger.Errorw("cannot build wf client", "err", err)
+			return nil, err
+		}
+	} else {
+		wfClient, err = impl.getClientInstance(namespace)
+		if err != nil {
+			impl.Logger.Errorw("cannot build wf client", "err", err)
+			return nil, err
+		}
 	}
+
 	workflow, err := wfClient.Get(context.Background(), name, v1.GetOptions{})
 	return workflow, err
 }
 
-func (impl *WorkflowServiceImpl) TerminateWorkflow(name string, namespace string) error {
+func (impl *WorkflowServiceImpl) TerminateWorkflow(name string, namespace string, isExt bool, environment *repository2.Environment) error {
 	impl.Logger.Debugw("terminating wf", "name", name)
-	wfClient, err := impl.getClientInstance(namespace)
-	if err != nil {
-		impl.Logger.Errorw("cannot build wf client", "err", err)
-		return err
+	var wfClient v1alpha12.WorkflowInterface
+	var err error
+	if isExt {
+		wfClient, err = impl.getRuntimeEnvClientInstance(environment)
+		if err != nil {
+			impl.Logger.Errorw("cannot build wf client", "err", err)
+			return err
+		}
+	} else {
+		wfClient, err = impl.getClientInstance(namespace)
+		if err != nil {
+			impl.Logger.Errorw("cannot build wf client", "err", err)
+			return err
+		}
+
 	}
+
 	err = util.TerminateWorkflow(context.Background(), wfClient, name)
 	return err
 }
diff --git a/pkg/pipeline/history/CiPipelineHistoryService.go b/pkg/pipeline/history/CiPipelineHistoryService.go
index e14a40fa8..db069b665 100644
--- a/pkg/pipeline/history/CiPipelineHistoryService.go
+++ b/pkg/pipeline/history/CiPipelineHistoryService.go
@@ -6,6 +6,7 @@ import (
 	"github.com/devtron-labs/devtron/pkg/pipeline/bean"
 	"github.com/devtron-labs/devtron/pkg/pipeline/history/repository"
 	"github.com/devtron-labs/devtron/pkg/sql"
+	"github.com/go-pg/pg"
 	"go.uber.org/zap"
 	"time"
 )
@@ -17,13 +18,15 @@ type CiPipelineHistoryService interface {
 type CiPipelineHistoryServiceImpl struct {
 	CiPipelineHistoryRepository repository.CiPipelineHistoryRepository
 	logger                      *zap.SugaredLogger
+	ciPipelineRepository        pipelineConfig.CiPipelineRepository
 }
 
 func NewCiPipelineHistoryServiceImpl(CiPipelineHistoryRepository repository.CiPipelineHistoryRepository,
-	logger *zap.SugaredLogger) *CiPipelineHistoryServiceImpl {
+	logger *zap.SugaredLogger, ciPipelineRepository pipelineConfig.CiPipelineRepository) *CiPipelineHistoryServiceImpl {
 	return &CiPipelineHistoryServiceImpl{
 		CiPipelineHistoryRepository: CiPipelineHistoryRepository,
 		logger:                      logger,
+		ciPipelineRepository:        ciPipelineRepository,
 	}
 }
 
@@ -89,6 +92,29 @@ func (impl *CiPipelineHistoryServiceImpl) SaveHistory(pipeline *pipelineConfig.C
 		impl.logger.Errorw("error in saving history of ci pipeline")
 		return err
 	}
+	ciEnvMapping, err := impl.ciPipelineRepository.FindCiEnvMappingByCiPipelineId(pipeline.Id)
+	if err != nil && err != pg.ErrNoRows {
+		impl.logger.Errorw("error in fetching ciEnvMapping", "ciPipelineId ", pipeline.Id, "err", err)
+		return err
+	}
+
+	if ciEnvMapping.Id > 0 {
+		CiEnvMappingHistory := &repository.CiEnvMappingHistory{
+			EnvironmentId: ciEnvMapping.EnvironmentId,
+			CiPipelineId:  ciEnvMapping.CiPipelineId,
+			AuditLog: sql.AuditLog{
+				CreatedOn: time.Now(),
+				CreatedBy: ciEnvMapping.CreatedBy,
+				UpdatedOn: time.Now(),
+				UpdatedBy: ciEnvMapping.UpdatedBy,
+			},
+		}
+		err := impl.CiPipelineHistoryRepository.SaveCiEnvMappingHistory(CiEnvMappingHistory)
+		if err != nil {
+			impl.logger.Errorw("error in saving history of ci Env Mapping", "err", err)
+			return err
+		}
+	}
 
 	return nil
 
diff --git a/pkg/pipeline/history/mocks/ConfigMapHistoryService.go b/pkg/pipeline/history/mocks/ConfigMapHistoryService.go
new file mode 100644
index 000000000..986a8ae35
--- /dev/null
+++ b/pkg/pipeline/history/mocks/ConfigMapHistoryService.go
@@ -0,0 +1,274 @@
+// Code generated by mockery v2.20.0. DO NOT EDIT.
+
+package mocks
+
+import (
+	chartConfig "github.com/devtron-labs/devtron/internal/sql/repository/chartConfig"
+	history "github.com/devtron-labs/devtron/pkg/pipeline/history"
+
+	mock "github.com/stretchr/testify/mock"
+
+	pipelineConfig "github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig"
+
+	repository "github.com/devtron-labs/devtron/pkg/pipeline/history/repository"
+
+	time "time"
+)
+
+// ConfigMapHistoryService is an autogenerated mock type for the ConfigMapHistoryService type
+type ConfigMapHistoryService struct {
+	mock.Mock
+}
+
+// ConvertConfigDataToComponentLevelDto provides a mock function with given fields: config, configType, userHasAdminAccess
+func (_m *ConfigMapHistoryService) ConvertConfigDataToComponentLevelDto(config *history.ConfigData, configType repository.ConfigType, userHasAdminAccess bool) (*history.ComponentLevelHistoryDetailDto, error) {
+	ret := _m.Called(config, configType, userHasAdminAccess)
+
+	var r0 *history.ComponentLevelHistoryDetailDto
+	var r1 error
+	if rf, ok := ret.Get(0).(func(*history.ConfigData, repository.ConfigType, bool) (*history.ComponentLevelHistoryDetailDto, error)); ok {
+		return rf(config, configType, userHasAdminAccess)
+	}
+	if rf, ok := ret.Get(0).(func(*history.ConfigData, repository.ConfigType, bool) *history.ComponentLevelHistoryDetailDto); ok {
+		r0 = rf(config, configType, userHasAdminAccess)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*history.ComponentLevelHistoryDetailDto)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(*history.ConfigData, repository.ConfigType, bool) error); ok {
+		r1 = rf(config, configType, userHasAdminAccess)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// CreateCMCSHistoryForDeploymentTrigger provides a mock function with given fields: pipeline, deployedOn, deployedBy
+func (_m *ConfigMapHistoryService) CreateCMCSHistoryForDeploymentTrigger(pipeline *pipelineConfig.Pipeline, deployedOn time.Time, deployedBy int32) error {
+	ret := _m.Called(pipeline, deployedOn, deployedBy)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(*pipelineConfig.Pipeline, time.Time, int32) error); ok {
+		r0 = rf(pipeline, deployedOn, deployedBy)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// CreateHistoryFromAppLevelConfig provides a mock function with given fields: appLevelConfig, configType
+func (_m *ConfigMapHistoryService) CreateHistoryFromAppLevelConfig(appLevelConfig *chartConfig.ConfigMapAppModel, configType repository.ConfigType) error {
+	ret := _m.Called(appLevelConfig, configType)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(*chartConfig.ConfigMapAppModel, repository.ConfigType) error); ok {
+		r0 = rf(appLevelConfig, configType)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// CreateHistoryFromEnvLevelConfig provides a mock function with given fields: envLevelConfig, configType
+func (_m *ConfigMapHistoryService) CreateHistoryFromEnvLevelConfig(envLevelConfig *chartConfig.ConfigMapEnvModel, configType repository.ConfigType) error {
+	ret := _m.Called(envLevelConfig, configType)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(*chartConfig.ConfigMapEnvModel, repository.ConfigType) error); ok {
+		r0 = rf(envLevelConfig, configType)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// GetDeployedHistoryByPipelineIdAndWfrId provides a mock function with given fields: pipelineId, wfrId, configType
+func (_m *ConfigMapHistoryService) GetDeployedHistoryByPipelineIdAndWfrId(pipelineId int, wfrId int, configType repository.ConfigType) (*repository.ConfigmapAndSecretHistory, bool, []string, error) {
+	ret := _m.Called(pipelineId, wfrId, configType)
+
+	var r0 *repository.ConfigmapAndSecretHistory
+	var r1 bool
+	var r2 []string
+	var r3 error
+	if rf, ok := ret.Get(0).(func(int, int, repository.ConfigType) (*repository.ConfigmapAndSecretHistory, bool, []string, error)); ok {
+		return rf(pipelineId, wfrId, configType)
+	}
+	if rf, ok := ret.Get(0).(func(int, int, repository.ConfigType) *repository.ConfigmapAndSecretHistory); ok {
+		r0 = rf(pipelineId, wfrId, configType)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*repository.ConfigmapAndSecretHistory)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(int, int, repository.ConfigType) bool); ok {
+		r1 = rf(pipelineId, wfrId, configType)
+	} else {
+		r1 = ret.Get(1).(bool)
+	}
+
+	if rf, ok := ret.Get(2).(func(int, int, repository.ConfigType) []string); ok {
+		r2 = rf(pipelineId, wfrId, configType)
+	} else {
+		if ret.Get(2) != nil {
+			r2 = ret.Get(2).([]string)
+		}
+	}
+
+	if rf, ok := ret.Get(3).(func(int, int, repository.ConfigType) error); ok {
+		r3 = rf(pipelineId, wfrId, configType)
+	} else {
+		r3 = ret.Error(3)
+	}
+
+	return r0, r1, r2, r3
+}
+
+// GetDeployedHistoryDetailForCMCSByPipelineIdAndWfrId provides a mock function with given fields: pipelineId, wfrId, configType, userHasAdminAccess
+func (_m *ConfigMapHistoryService) GetDeployedHistoryDetailForCMCSByPipelineIdAndWfrId(pipelineId int, wfrId int, configType repository.ConfigType, userHasAdminAccess bool) ([]*history.ComponentLevelHistoryDetailDto, error) {
+	ret := _m.Called(pipelineId, wfrId, configType, userHasAdminAccess)
+
+	var r0 []*history.ComponentLevelHistoryDetailDto
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int, int, repository.ConfigType, bool) ([]*history.ComponentLevelHistoryDetailDto, error)); ok {
+		return rf(pipelineId, wfrId, configType, userHasAdminAccess)
+	}
+	if rf, ok := ret.Get(0).(func(int, int, repository.ConfigType, bool) []*history.ComponentLevelHistoryDetailDto); ok {
+		r0 = rf(pipelineId, wfrId, configType, userHasAdminAccess)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]*history.ComponentLevelHistoryDetailDto)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(int, int, repository.ConfigType, bool) error); ok {
+		r1 = rf(pipelineId, wfrId, configType, userHasAdminAccess)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetDeployedHistoryList provides a mock function with given fields: pipelineId, baseConfigId, configType, componentName
+func (_m *ConfigMapHistoryService) GetDeployedHistoryList(pipelineId int, baseConfigId int, configType repository.ConfigType, componentName string) ([]*history.DeployedHistoryComponentMetadataDto, error) {
+	ret := _m.Called(pipelineId, baseConfigId, configType, componentName)
+
+	var r0 []*history.DeployedHistoryComponentMetadataDto
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int, int, repository.ConfigType, string) ([]*history.DeployedHistoryComponentMetadataDto, error)); ok {
+		return rf(pipelineId, baseConfigId, configType, componentName)
+	}
+	if rf, ok := ret.Get(0).(func(int, int, repository.ConfigType, string) []*history.DeployedHistoryComponentMetadataDto); ok {
+		r0 = rf(pipelineId, baseConfigId, configType, componentName)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]*history.DeployedHistoryComponentMetadataDto)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(int, int, repository.ConfigType, string) error); ok {
+		r1 = rf(pipelineId, baseConfigId, configType, componentName)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetDeploymentDetailsForDeployedCMCSHistory provides a mock function with given fields: pipelineId, configType
+func (_m *ConfigMapHistoryService) GetDeploymentDetailsForDeployedCMCSHistory(pipelineId int, configType repository.ConfigType) ([]*history.ConfigMapAndSecretHistoryDto, error) {
+	ret := _m.Called(pipelineId, configType)
+
+	var r0 []*history.ConfigMapAndSecretHistoryDto
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int, repository.ConfigType) ([]*history.ConfigMapAndSecretHistoryDto, error)); ok {
+		return rf(pipelineId, configType)
+	}
+	if rf, ok := ret.Get(0).(func(int, repository.ConfigType) []*history.ConfigMapAndSecretHistoryDto); ok {
+		r0 = rf(pipelineId, configType)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]*history.ConfigMapAndSecretHistoryDto)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(int, repository.ConfigType) error); ok {
+		r1 = rf(pipelineId, configType)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetHistoryForDeployedCMCSById provides a mock function with given fields: id, pipelineId, configType, componentName, userHasAdminAccess
+func (_m *ConfigMapHistoryService) GetHistoryForDeployedCMCSById(id int, pipelineId int, configType repository.ConfigType, componentName string, userHasAdminAccess bool) (*history.HistoryDetailDto, error) {
+	ret := _m.Called(id, pipelineId, configType, componentName, userHasAdminAccess)
+
+	var r0 *history.HistoryDetailDto
+	var r1 error
+	if rf, ok := ret.Get(0).(func(int, int, repository.ConfigType, string, bool) (*history.HistoryDetailDto, error)); ok {
+		return rf(id, pipelineId, configType, componentName, userHasAdminAccess)
+	}
+	if rf, ok := ret.Get(0).(func(int, int, repository.ConfigType, string, bool) *history.HistoryDetailDto); ok {
+		r0 = rf(id, pipelineId, configType, componentName, userHasAdminAccess)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*history.HistoryDetailDto)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(int, int, repository.ConfigType, string, bool) error); ok {
+		r1 = rf(id, pipelineId, configType, componentName, userHasAdminAccess)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// MergeAppLevelAndEnvLevelConfigs provides a mock function with given fields: appLevelConfig, envLevelConfig, configType, configMapSecretNames
+func (_m *ConfigMapHistoryService) MergeAppLevelAndEnvLevelConfigs(appLevelConfig *chartConfig.ConfigMapAppModel, envLevelConfig *chartConfig.ConfigMapEnvModel, configType repository.ConfigType, configMapSecretNames []string) (string, error) {
+	ret := _m.Called(appLevelConfig, envLevelConfig, configType, configMapSecretNames)
+
+	var r0 string
+	var r1 error
+	if rf, ok := ret.Get(0).(func(*chartConfig.ConfigMapAppModel, *chartConfig.ConfigMapEnvModel, repository.ConfigType, []string) (string, error)); ok {
+		return rf(appLevelConfig, envLevelConfig, configType, configMapSecretNames)
+	}
+	if rf, ok := ret.Get(0).(func(*chartConfig.ConfigMapAppModel, *chartConfig.ConfigMapEnvModel, repository.ConfigType, []string) string); ok {
+		r0 = rf(appLevelConfig, envLevelConfig, configType, configMapSecretNames)
+	} else {
+		r0 = ret.Get(0).(string)
+	}
+
+	if rf, ok := ret.Get(1).(func(*chartConfig.ConfigMapAppModel, *chartConfig.ConfigMapEnvModel, repository.ConfigType, []string) error); ok {
+		r1 = rf(appLevelConfig, envLevelConfig, configType, configMapSecretNames)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+type mockConstructorTestingTNewConfigMapHistoryService interface {
+	mock.TestingT
+	Cleanup(func())
+}
+
+// NewConfigMapHistoryService creates a new instance of ConfigMapHistoryService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+func NewConfigMapHistoryService(t mockConstructorTestingTNewConfigMapHistoryService) *ConfigMapHistoryService {
+	mock := &ConfigMapHistoryService{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/pkg/pipeline/history/repository/CiPipelineHistoryRepository.go b/pkg/pipeline/history/repository/CiPipelineHistoryRepository.go
index c9bbea520..7bce3d0e8 100644
--- a/pkg/pipeline/history/repository/CiPipelineHistoryRepository.go
+++ b/pkg/pipeline/history/repository/CiPipelineHistoryRepository.go
@@ -66,6 +66,14 @@ type CiPipelineHistory struct {
 	Manual                    bool     `sql:"manual,notnull"`
 }
 
+type CiEnvMappingHistory struct {
+	tableName     struct{} `sql:"ci_env_mapping_history" pg:",discard_unknown_columns"`
+	Id            int      `sql:"id,pk"`
+	CiPipelineId  int      `sql:"ci_pipeline_id"`
+	EnvironmentId int      `sql:"environment_id"`
+	sql.AuditLog
+}
+
 const (
 	TRIGGER_ADD    = "add"
 	TRIGGER_UPDATE = "update"
@@ -74,6 +82,7 @@ const (
 
 type CiPipelineHistoryRepository interface {
 	Save(ciPipelineHistory *CiPipelineHistory) error
+	SaveCiEnvMappingHistory(CiEnvMappingHistory *CiEnvMappingHistory) error
 }
 
 type CiPipelineHistoryRepositoryImpl struct {
@@ -100,3 +109,13 @@ func (impl *CiPipelineHistoryRepositoryImpl) Save(CiPipelineHistory *CiPipelineH
 
 	return nil
 }
+func (impl *CiPipelineHistoryRepositoryImpl) SaveCiEnvMappingHistory(CiEnvMappingHistory *CiEnvMappingHistory) error {
+	err := impl.dbConnection.Insert(CiEnvMappingHistory)
+
+	if err != nil {
+		impl.logger.Errorw("error in saving history for Ci-Env Mapping", "err", err)
+		return err
+	}
+
+	return nil
+}
diff --git a/pkg/pipeline/history/repository/mocks/CiPipelineHistoryRepository.go b/pkg/pipeline/history/repository/mocks/CiPipelineHistoryRepository.go
index 3bd9880cc..d8925e759 100644
--- a/pkg/pipeline/history/repository/mocks/CiPipelineHistoryRepository.go
+++ b/pkg/pipeline/history/repository/mocks/CiPipelineHistoryRepository.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.14.0. DO NOT EDIT.
+// Code generated by mockery v2.20.0. DO NOT EDIT.
 
 package mocks
 
@@ -26,6 +26,20 @@ func (_m *CiPipelineHistoryRepository) Save(ciPipelineHistory *repository.CiPipe
 	return r0
 }
 
+// SaveCiEnvMappingHistory provides a mock function with given fields: CiEnvMappingHistory
+func (_m *CiPipelineHistoryRepository) SaveCiEnvMappingHistory(CiEnvMappingHistory *repository.CiEnvMappingHistory) error {
+	ret := _m.Called(CiEnvMappingHistory)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(*repository.CiEnvMappingHistory) error); ok {
+		r0 = rf(CiEnvMappingHistory)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
 type mockConstructorTestingTNewCiPipelineHistoryRepository interface {
 	mock.TestingT
 	Cleanup(func())
diff --git a/scripts/sql/158_job_ci_env_mapping.down.sql b/scripts/sql/158_job_ci_env_mapping.down.sql
new file mode 100644
index 000000000..44f5d462d
--- /dev/null
+++ b/scripts/sql/158_job_ci_env_mapping.down.sql
@@ -0,0 +1,8 @@
+DROP TABLE IF EXISTS "public"."ci_env_mapping";
+DROP SEQUENCE IF EXISTS public.id_seq_ci_env_mapping;
+
+DROP TABLE IF EXISTS "public"."ci_env_mapping_history";
+DROP SEQUENCE IF EXISTS public.id_seq_ci_env_mapping_history;
+
+ALTER TABLE config_map_env_level DROP COLUMN IF EXISTS deleted;
+ALTER TABLE ci_workflow DROP COLUMN IF EXISTS environment_id;
\ No newline at end of file
diff --git a/scripts/sql/158_job_ci_env_mapping.up.sql b/scripts/sql/158_job_ci_env_mapping.up.sql
new file mode 100644
index 000000000..4e41005b8
--- /dev/null
+++ b/scripts/sql/158_job_ci_env_mapping.up.sql
@@ -0,0 +1,27 @@
+CREATE SEQUENCE IF NOT EXISTS id_seq_ci_env_mapping;
+CREATE TABLE "public"."ci_env_mapping" (
+"id" integer NOT NULL DEFAULT nextval('id_seq_ci_env_mapping'::regclass),
+"environment_id" integer,
+"ci_pipeline_id" integer,
+"deleted"     bool NOT NULL DEFAULT FALSE,
+"created_on" timestamptz,
+"created_by" int4,
+"updated_on" timestamptz,
+"updated_by" int4,
+CONSTRAINT "ci_env_mapping_ci_pipeline_id_fkey" FOREIGN KEY ("ci_pipeline_id") REFERENCES "public"."ci_pipeline" ("id"),
+CONSTRAINT "ci_env_mapping_environment_id_fkey" FOREIGN KEY ("environment_id") REFERENCES "public"."environment" ("id"),
+PRIMARY KEY (id)
+);
+CREATE SEQUENCE IF NOT EXISTS id_seq_ci_env_mapping_history;
+CREATE TABLE "public"."ci_env_mapping_history" (
+"id" integer NOT NULL DEFAULT nextval('id_seq_ci_env_mapping_history'::regclass),
+"ci_pipeline_id" integer,
+"environment_id" integer,
+"created_on" timestamptz,
+"created_by" int4,
+"updated_on" timestamptz,
+"updated_by" int4,
+PRIMARY KEY (id)
+);
+ALTER TABLE config_map_env_level ADD COLUMN IF NOT EXISTS deleted bool;
+ALTER  TABLE ci_workflow ADD  COLUMN IF NOT EXISTS environment_id integer;
diff --git a/util/k8s/k8sCapacityService.go b/util/k8s/k8sCapacityService.go
index b6065a280..1514d78e7 100644
--- a/util/k8s/k8sCapacityService.go
+++ b/util/k8s/k8sCapacityService.go
@@ -54,7 +54,7 @@ const (
 	AWSEKSNodeGroupLabel = "eks.amazonaws.com/nodegroup"
 )
 
-//TODO: add any new nodeGrouplabel in this array
+// TODO: add any new nodeGrouplabel in this array
 var NodeGroupLabels = [5]string{AWSNodeGroupLabel, AzureNodeGroupLabel, GcpNodeGroupLabel, KopsNodeGroupLabel, AWSEKSNodeGroupLabel}
 
 // below const set is used for pod delete status
diff --git a/wire_gen.go b/wire_gen.go
index 24570aaf9..0b1e389a7 100644
--- a/wire_gen.go
+++ b/wire_gen.go
@@ -282,7 +282,7 @@ func InitializeApp() (*App, error) {
 	enforcerImpl := casbin.NewEnforcerImpl(syncedEnforcer, sessionManager, sugaredLogger)
 	enforcerUtilImpl := rbac.NewEnforcerUtilImpl(sugaredLogger, teamRepositoryImpl, appRepositoryImpl, environmentRepositoryImpl, pipelineRepositoryImpl, ciPipelineRepositoryImpl, clusterRepositoryImpl)
 	appListingRepositoryQueryBuilder := helper.NewAppListingRepositoryQueryBuilder(sugaredLogger)
-	appListingRepositoryImpl := repository.NewAppListingRepositoryImpl(sugaredLogger, db, appListingRepositoryQueryBuilder)
+	appListingRepositoryImpl := repository.NewAppListingRepositoryImpl(sugaredLogger, db, appListingRepositoryQueryBuilder, environmentRepositoryImpl)
 	pipelineConfigRepositoryImpl := chartConfig.NewPipelineConfigRepository(db)
 	configMapRepositoryImpl := chartConfig.NewConfigMapRepositoryImpl(sugaredLogger, db)
 	appLevelMetricsRepositoryImpl := repository.NewAppLevelMetricsRepositoryImpl(db, sugaredLogger)
@@ -412,11 +412,12 @@ func InitializeApp() (*App, error) {
 	gitMaterialHistoryRepositoryImpl := repository6.NewGitMaterialHistoryRepositoyImpl(db)
 	gitMaterialHistoryServiceImpl := history.NewGitMaterialHistoryServiceImpl(gitMaterialHistoryRepositoryImpl, sugaredLogger)
 	ciPipelineHistoryRepositoryImpl := repository6.NewCiPipelineHistoryRepositoryImpl(db, sugaredLogger)
-	ciPipelineHistoryServiceImpl := history.NewCiPipelineHistoryServiceImpl(ciPipelineHistoryRepositoryImpl, sugaredLogger)
+	ciPipelineHistoryServiceImpl := history.NewCiPipelineHistoryServiceImpl(ciPipelineHistoryRepositoryImpl, sugaredLogger, ciPipelineRepositoryImpl)
 	ciBuildConfigRepositoryImpl := pipelineConfig.NewCiBuildConfigRepositoryImpl(db, sugaredLogger)
 	ciBuildConfigServiceImpl := pipeline.NewCiBuildConfigServiceImpl(sugaredLogger, ciBuildConfigRepositoryImpl)
 	ciTemplateServiceImpl := pipeline.NewCiTemplateServiceImpl(sugaredLogger, ciBuildConfigServiceImpl, ciTemplateRepositoryImpl, ciTemplateOverrideRepositoryImpl)
-	ciCdPipelineOrchestratorImpl := pipeline.NewCiCdPipelineOrchestrator(appRepositoryImpl, sugaredLogger, materialRepositoryImpl, pipelineRepositoryImpl, ciPipelineRepositoryImpl, ciPipelineMaterialRepositoryImpl, clientImpl, ciConfig, appWorkflowRepositoryImpl, environmentRepositoryImpl, attributesServiceImpl, appListingRepositoryImpl, appCrudOperationServiceImpl, userAuthServiceImpl, prePostCdScriptHistoryServiceImpl, prePostCiScriptHistoryServiceImpl, pipelineStageServiceImpl, ciTemplateOverrideRepositoryImpl, gitMaterialHistoryServiceImpl, ciPipelineHistoryServiceImpl, ciTemplateServiceImpl, dockerArtifactStoreRepositoryImpl)
+	configMapServiceImpl := pipeline.NewConfigMapServiceImpl(chartRepositoryImpl, sugaredLogger, chartRepoRepositoryImpl, utilMergeUtil, pipelineConfigRepositoryImpl, configMapRepositoryImpl, envConfigOverrideRepositoryImpl, commonServiceImpl, appRepositoryImpl, configMapHistoryServiceImpl, environmentRepositoryImpl)
+	ciCdPipelineOrchestratorImpl := pipeline.NewCiCdPipelineOrchestrator(appRepositoryImpl, sugaredLogger, materialRepositoryImpl, pipelineRepositoryImpl, ciPipelineRepositoryImpl, ciPipelineMaterialRepositoryImpl, clientImpl, ciConfig, appWorkflowRepositoryImpl, environmentRepositoryImpl, attributesServiceImpl, appListingRepositoryImpl, appCrudOperationServiceImpl, userAuthServiceImpl, prePostCdScriptHistoryServiceImpl, prePostCiScriptHistoryServiceImpl, pipelineStageServiceImpl, ciTemplateOverrideRepositoryImpl, gitMaterialHistoryServiceImpl, ciPipelineHistoryServiceImpl, ciTemplateServiceImpl, dockerArtifactStoreRepositoryImpl, configMapServiceImpl)
 	propertiesConfigServiceImpl := pipeline.NewPropertiesConfigServiceImpl(sugaredLogger, envConfigOverrideRepositoryImpl, chartRepositoryImpl, chartRefRepositoryImpl, utilMergeUtil, environmentRepositoryImpl, ciCdPipelineOrchestratorImpl, applicationServiceClientImpl, envLevelAppMetricsRepositoryImpl, appLevelMetricsRepositoryImpl, deploymentTemplateHistoryServiceImpl)
 	ecrConfig, err := pipeline.GetEcrConfig()
 	if err != nil {
@@ -438,10 +439,10 @@ func InitializeApp() (*App, error) {
 	imageTaggingServiceImpl := pipeline.NewImageTaggingServiceImpl(imageTaggingRepositoryImpl, ciPipelineRepositoryImpl, pipelineRepositoryImpl, environmentRepositoryImpl, sugaredLogger)
 	pipelineBuilderImpl := pipeline.NewPipelineBuilderImpl(sugaredLogger, ciCdPipelineOrchestratorImpl, dockerArtifactStoreRepositoryImpl, materialRepositoryImpl, appRepositoryImpl, pipelineRepositoryImpl, propertiesConfigServiceImpl, ciTemplateRepositoryImpl, ciPipelineRepositoryImpl, applicationServiceClientImpl, chartRepositoryImpl, ciArtifactRepositoryImpl, ecrConfig, envConfigOverrideRepositoryImpl, environmentRepositoryImpl, clusterRepositoryImpl, pipelineConfigRepositoryImpl, utilMergeUtil, appWorkflowRepositoryImpl, ciConfig, cdWorkflowRepositoryImpl, appServiceImpl, imageScanResultRepositoryImpl, argoK8sClientImpl, gitFactory, attributesServiceImpl, acdAuthConfig, gitOpsConfigRepositoryImpl, pipelineStrategyHistoryServiceImpl, prePostCiScriptHistoryServiceImpl, prePostCdScriptHistoryServiceImpl, deploymentTemplateHistoryServiceImpl, appLevelMetricsRepositoryImpl, pipelineStageServiceImpl, chartRefRepositoryImpl, chartTemplateServiceImpl, chartServiceImpl, helmAppServiceImpl, deploymentGroupRepositoryImpl, ciPipelineMaterialRepositoryImpl, userServiceImpl, ciTemplateServiceImpl, ciTemplateOverrideRepositoryImpl, gitMaterialHistoryServiceImpl, ciTemplateHistoryServiceImpl, ciPipelineHistoryServiceImpl, globalStrategyMetadataRepositoryImpl, globalStrategyMetadataChartRefMappingRepositoryImpl, pipelineDeploymentServiceTypeConfig, appStatusRepositoryImpl, workflowDagExecutorImpl, enforcerUtilImpl, argoUserServiceImpl, ciWorkflowRepositoryImpl, appGroupServiceImpl, chartDeploymentServiceImpl, k8sUtil, attributesRepositoryImpl, imageTaggingServiceImpl)
 	dbMigrationServiceImpl := pipeline.NewDbMogrationService(sugaredLogger, dbMigrationConfigRepositoryImpl)
-	workflowServiceImpl := pipeline.NewWorkflowServiceImpl(sugaredLogger, ciConfig, globalCMCSServiceImpl)
-	ciServiceImpl := pipeline.NewCiServiceImpl(sugaredLogger, workflowServiceImpl, ciPipelineMaterialRepositoryImpl, ciWorkflowRepositoryImpl, ciConfig, eventRESTClientImpl, eventSimpleFactoryImpl, mergeUtil, ciPipelineRepositoryImpl, prePostCiScriptHistoryServiceImpl, pipelineStageServiceImpl, userServiceImpl, ciTemplateServiceImpl, appCrudOperationServiceImpl)
+	workflowServiceImpl := pipeline.NewWorkflowServiceImpl(sugaredLogger, ciConfig, globalCMCSServiceImpl, appServiceImpl, configMapRepositoryImpl, k8sApplicationServiceImpl)
+	ciServiceImpl := pipeline.NewCiServiceImpl(sugaredLogger, workflowServiceImpl, ciPipelineMaterialRepositoryImpl, ciWorkflowRepositoryImpl, ciConfig, eventRESTClientImpl, eventSimpleFactoryImpl, mergeUtil, ciPipelineRepositoryImpl, prePostCiScriptHistoryServiceImpl, pipelineStageServiceImpl, userServiceImpl, ciTemplateServiceImpl, appCrudOperationServiceImpl, environmentRepositoryImpl, appRepositoryImpl)
 	ciLogServiceImpl := pipeline.NewCiLogServiceImpl(sugaredLogger, ciServiceImpl, ciConfig)
-	ciHandlerImpl := pipeline.NewCiHandlerImpl(sugaredLogger, ciServiceImpl, ciPipelineMaterialRepositoryImpl, clientImpl, ciWorkflowRepositoryImpl, workflowServiceImpl, ciLogServiceImpl, ciConfig, ciArtifactRepositoryImpl, userServiceImpl, eventRESTClientImpl, eventSimpleFactoryImpl, ciPipelineRepositoryImpl, appListingRepositoryImpl, k8sUtil, pipelineRepositoryImpl, enforcerUtilImpl, appGroupServiceImpl, imageTaggingServiceImpl)
+	ciHandlerImpl := pipeline.NewCiHandlerImpl(sugaredLogger, ciServiceImpl, ciPipelineMaterialRepositoryImpl, clientImpl, ciWorkflowRepositoryImpl, workflowServiceImpl, ciLogServiceImpl, ciConfig, ciArtifactRepositoryImpl, userServiceImpl, eventRESTClientImpl, eventSimpleFactoryImpl, ciPipelineRepositoryImpl, appListingRepositoryImpl, k8sUtil, pipelineRepositoryImpl, enforcerUtilImpl, appGroupServiceImpl, environmentRepositoryImpl, imageTaggingServiceImpl)
 	gitRegistryConfigImpl := pipeline.NewGitRegistryConfigImpl(sugaredLogger, gitProviderRepositoryImpl, clientImpl)
 	ociRegistryConfigRepositoryImpl := repository5.NewOCIRegistryConfigRepositoryImpl(db)
 	dockerRegistryConfigImpl := pipeline.NewDockerRegistryConfigImpl(sugaredLogger, dockerArtifactStoreRepositoryImpl, dockerRegistryIpsConfigRepositoryImpl, ociRegistryConfigRepositoryImpl)
@@ -450,7 +451,6 @@ func InitializeApp() (*App, error) {
 	appListingServiceImpl := app2.NewAppListingServiceImpl(sugaredLogger, appListingRepositoryImpl, applicationServiceClientImpl, appRepositoryImpl, appListingViewBuilderImpl, pipelineRepositoryImpl, linkoutsRepositoryImpl, appLevelMetricsRepositoryImpl, envLevelAppMetricsRepositoryImpl, cdWorkflowRepositoryImpl, pipelineOverrideRepositoryImpl, environmentRepositoryImpl, argoUserServiceImpl, envConfigOverrideRepositoryImpl, chartRepositoryImpl, ciPipelineRepositoryImpl, dockerRegistryIpsConfigServiceImpl)
 	deploymentEventHandlerImpl := app2.NewDeploymentEventHandlerImpl(sugaredLogger, appListingServiceImpl, eventRESTClientImpl, eventSimpleFactoryImpl)
 	cdHandlerImpl := pipeline.NewCdHandlerImpl(sugaredLogger, cdConfig, userServiceImpl, cdWorkflowRepositoryImpl, cdWorkflowServiceImpl, ciLogServiceImpl, ciArtifactRepositoryImpl, ciPipelineMaterialRepositoryImpl, pipelineRepositoryImpl, environmentRepositoryImpl, ciWorkflowRepositoryImpl, ciConfig, helmAppServiceImpl, pipelineOverrideRepositoryImpl, workflowDagExecutorImpl, appListingServiceImpl, appListingRepositoryImpl, pipelineStatusTimelineRepositoryImpl, applicationServiceClientImpl, argoUserServiceImpl, deploymentEventHandlerImpl, eventRESTClientImpl, pipelineStatusTimelineResourcesServiceImpl, pipelineStatusSyncDetailServiceImpl, pipelineStatusTimelineServiceImpl, appServiceImpl, appStatusServiceImpl, enforcerUtilImpl, installedAppRepositoryImpl, installedAppVersionHistoryRepositoryImpl, appRepositoryImpl, appGroupServiceImpl, imageTaggingServiceImpl)
-	configMapServiceImpl := pipeline.NewConfigMapServiceImpl(chartRepositoryImpl, sugaredLogger, chartRepoRepositoryImpl, utilMergeUtil, pipelineConfigRepositoryImpl, configMapRepositoryImpl, envConfigOverrideRepositoryImpl, commonServiceImpl, appRepositoryImpl, configMapHistoryServiceImpl)
 	appWorkflowServiceImpl := appWorkflow2.NewAppWorkflowServiceImpl(sugaredLogger, appWorkflowRepositoryImpl, ciCdPipelineOrchestratorImpl, ciPipelineRepositoryImpl, pipelineRepositoryImpl, enforcerUtilImpl, appGroupServiceImpl)
 	appCloneServiceImpl := appClone.NewAppCloneServiceImpl(sugaredLogger, pipelineBuilderImpl, materialRepositoryImpl, chartServiceImpl, configMapServiceImpl, appWorkflowServiceImpl, appListingServiceImpl, propertiesConfigServiceImpl, ciTemplateOverrideRepositoryImpl, pipelineStageServiceImpl, ciTemplateServiceImpl, appRepositoryImpl)
 	imageScanObjectMetaRepositoryImpl := security.NewImageScanObjectMetaRepositoryImpl(db, sugaredLogger)


Test patch:
diff --git a/pkg/pipeline/CiCdPipelineOrchestrator_test.go b/pkg/pipeline/CiCdPipelineOrchestrator_test.go
new file mode 100644
index 000000000..835870501
--- /dev/null
+++ b/pkg/pipeline/CiCdPipelineOrchestrator_test.go
@@ -0,0 +1,158 @@
+package pipeline
+
+import (
+	"fmt"
+	"github.com/devtron-labs/devtron/client/gitSensor"
+	"github.com/devtron-labs/devtron/internal/sql/repository"
+	"github.com/devtron-labs/devtron/internal/sql/repository/app"
+	"github.com/devtron-labs/devtron/internal/sql/repository/appWorkflow"
+	"github.com/devtron-labs/devtron/internal/sql/repository/chartConfig"
+	repository2 "github.com/devtron-labs/devtron/internal/sql/repository/dockerRegistry"
+	"github.com/devtron-labs/devtron/internal/sql/repository/helper"
+	"github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig"
+	"github.com/devtron-labs/devtron/internal/util"
+	app2 "github.com/devtron-labs/devtron/pkg/app"
+	repository3 "github.com/devtron-labs/devtron/pkg/cluster/repository"
+	repository4 "github.com/devtron-labs/devtron/pkg/pipeline/history/repository"
+	"log"
+
+	"github.com/devtron-labs/devtron/pkg/attributes"
+	"github.com/devtron-labs/devtron/pkg/bean"
+	"github.com/devtron-labs/devtron/pkg/pipeline/history"
+	"github.com/devtron-labs/devtron/pkg/user"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+var (
+	ciCdPipelineOrchestrator *CiCdPipelineOrchestratorImpl
+)
+
+func TestCiCdPipelineOrchestratorImpl_CreateCiConf(t *testing.T) {
+	t.SkipNow()
+	InitClusterNoteService()
+	type args struct {
+		createRequest *bean.CiConfigRequest
+		templateId    int
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    *bean.CiConfigRequest
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "CreateCiConf success",
+			args: args{
+				createRequest: &bean.CiConfigRequest{},
+				templateId:    123,
+			},
+			want:    &bean.CiConfigRequest{},
+			wantErr: assert.NoError,
+		},
+		{
+			name: "CreateCiConf success with payload",
+			args: args{
+				createRequest: &bean.CiConfigRequest{
+					Id:    12,
+					AppId: 20,
+				},
+				templateId: 123,
+			},
+			want: &bean.CiConfigRequest{
+				Id:    12,
+				AppId: 20,
+			},
+			wantErr: assert.NoError,
+		},
+		{
+			name: "CreateCiConf success with job payload",
+			args: args{
+				createRequest: &bean.CiConfigRequest{
+					Id:    0,
+					AppId: 21,
+					IsJob: true,
+					CiPipelines: []*bean.CiPipeline{{
+						IsExternal: true,
+						IsManual:   false,
+						AppId:      21,
+						CiMaterial: []*bean.CiMaterial{
+							{
+								Source: &bean.SourceTypeConfig{
+									Type:  "SOURCE_TYPE_BRANCH_FIXED",
+									Value: "main",
+									Regex: "",
+								},
+								GitMaterialId:   13,
+								Id:              38,
+								GitMaterialName: "devtron-test",
+								IsRegex:         false,
+							},
+						},
+						EnvironmentId: 3,
+					}},
+					UserId: 1,
+				},
+				templateId: 13,
+			},
+			want: &bean.CiConfigRequest{
+				Id:          0,
+				AppId:       21,
+				CiPipelines: []*bean.CiPipeline{{EnvironmentId: 3}},
+			},
+			wantErr: assert.NoError,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := ciCdPipelineOrchestrator.CreateCiConf(tt.args.createRequest, tt.args.templateId)
+			if !tt.wantErr(t, err, fmt.Sprintf("CreateCiConf(%v, %v)", tt.args.createRequest, tt.args.templateId)) {
+				return
+			}
+			assert.Equal(t, tt.want.AppId, got.AppId, "CreateCiConf(%v, %v)", tt.args.createRequest, tt.args.templateId)
+			assert.Equal(t, tt.want.Id, got.Id, "CreateCiConf(%v, %v)", tt.args.createRequest, tt.args.templateId)
+			assert.Equal(t, tt.want.CiPipelines[0].EnvironmentId, got.CiPipelines[0].EnvironmentId, "CreateCiConf(%v, %v)", tt.args.createRequest, tt.args.templateId)
+		})
+	}
+}
+
+func InitClusterNoteService() {
+	if ciCdPipelineOrchestrator != nil {
+		return
+	}
+	logger, err := util.NewSugardLogger()
+	if err != nil {
+		log.Fatalf("error in logger initialization %s,%s", "err", err)
+	}
+	conn, err := getDbConn()
+	if err != nil {
+		log.Fatalf("error in db connection initialization %s, %s", "err", err)
+	}
+
+	appRepository := app.NewAppRepositoryImpl(conn, logger)
+	materialRepository := pipelineConfig.NewMaterialRepositoryImpl(conn)
+	pipelineRepository := pipelineConfig.NewPipelineRepositoryImpl(conn, logger)
+	ciPipelineRepository := pipelineConfig.NewCiPipelineRepositoryImpl(conn, logger)
+	ciPipelineHistoryRepository := repository4.NewCiPipelineHistoryRepositoryImpl(conn, logger)
+	ciPipelineMaterialRepository := pipelineConfig.NewCiPipelineMaterialRepositoryImpl(conn, logger)
+	GitSensorClient, err := gitSensor.NewGitSensorClient(logger, &gitSensor.ClientConfig{})
+	ciConfig := &CiConfig{}
+	appWorkflowRepository := appWorkflow.NewAppWorkflowRepositoryImpl(logger, conn)
+	envRepository := repository3.NewEnvironmentRepositoryImpl(conn, logger, nil)
+	attributesService := attributes.NewAttributesServiceImpl(logger, nil)
+	appListingRepositoryQueryBuilder := helper.NewAppListingRepositoryQueryBuilder(logger)
+	appListingRepository := repository.NewAppListingRepositoryImpl(logger, conn, appListingRepositoryQueryBuilder, envRepository)
+	appLabelsService := app2.NewAppCrudOperationServiceImpl(nil, logger, nil, nil, nil)
+	userAuthService := user.NewUserAuthServiceImpl(nil, nil, nil, nil, nil, nil, nil)
+	prePostCdScriptHistoryService := history.NewPrePostCdScriptHistoryServiceImpl(logger, nil, nil, nil)
+	prePostCiScriptHistoryService := history.NewPrePostCiScriptHistoryServiceImpl(logger, nil)
+	pipelineStageService := NewPipelineStageService(logger, nil, nil)
+	ciTemplateOverrideRepository := pipelineConfig.NewCiTemplateOverrideRepositoryImpl(conn, logger)
+	ciTemplateService := *NewCiTemplateServiceImpl(logger, nil, nil, nil)
+	gitMaterialHistoryService := history.NewGitMaterialHistoryServiceImpl(nil, logger)
+	ciPipelineHistoryService := history.NewCiPipelineHistoryServiceImpl(ciPipelineHistoryRepository, logger, ciPipelineRepository)
+	dockerArtifactStoreRepository := repository2.NewDockerArtifactStoreRepositoryImpl(conn)
+	configMapRepository := chartConfig.NewConfigMapRepositoryImpl(logger, conn)
+	configMapService := NewConfigMapServiceImpl(nil, nil, nil, util.MergeUtil{}, nil, configMapRepository, nil, nil, appRepository, nil, envRepository)
+	ciCdPipelineOrchestrator = NewCiCdPipelineOrchestrator(appRepository, logger, materialRepository, pipelineRepository, ciPipelineRepository, ciPipelineMaterialRepository, GitSensorClient, ciConfig, appWorkflowRepository, envRepository, attributesService, appListingRepository, appLabelsService, userAuthService, prePostCdScriptHistoryService, prePostCiScriptHistoryService, pipelineStageService, ciTemplateOverrideRepository, gitMaterialHistoryService, ciPipelineHistoryService, ciTemplateService, dockerArtifactStoreRepository, configMapService)
+}
diff --git a/pkg/pipeline/ConfigMapService_test.go b/pkg/pipeline/ConfigMapService_test.go
new file mode 100644
index 000000000..1043742d2
--- /dev/null
+++ b/pkg/pipeline/ConfigMapService_test.go
@@ -0,0 +1,248 @@
+package pipeline
+
+import (
+	"fmt"
+	mocks4 "github.com/devtron-labs/devtron/internal/sql/repository/app/mocks"
+	"github.com/devtron-labs/devtron/internal/sql/repository/chartConfig"
+	mocks2 "github.com/devtron-labs/devtron/internal/sql/repository/chartConfig/mocks"
+	"github.com/devtron-labs/devtron/internal/util"
+	"github.com/devtron-labs/devtron/pkg/chartRepo/repository/mocks"
+	"github.com/devtron-labs/devtron/pkg/cluster/repository"
+	mocks6 "github.com/devtron-labs/devtron/pkg/cluster/repository/mocks"
+	mocks3 "github.com/devtron-labs/devtron/pkg/commonService/mocks"
+	mocks5 "github.com/devtron-labs/devtron/pkg/pipeline/history/mocks"
+	"github.com/go-pg/pg"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+	"testing"
+)
+
+func TestConfigMapServiceImpl_ConfigSecretEnvironmentCreate(t *testing.T) {
+	t.SkipNow()
+	chartRepository := mocks.NewChartRepository(t)
+	sugaredLogger, _ := util.NewSugardLogger()
+
+	repoRepository := mocks.NewChartRepoRepository(t)
+	pipelineConfigRepository := mocks2.NewPipelineConfigRepository(t)
+	configMapRepository := mocks2.NewConfigMapRepository(t)
+	environmentConfigRepository := mocks2.NewEnvConfigOverrideRepository(t)
+	commonService := mocks3.NewCommonService(t)
+	appRepository := mocks4.NewAppRepository(t)
+	configMapHistoryService := mocks5.NewConfigMapHistoryService(t)
+	envRepository := mocks6.NewEnvironmentRepository(t)
+	configMap := &chartConfig.ConfigMapEnvModel{
+		AppId:         22,
+		EnvironmentId: 5,
+	}
+	model := &chartConfig.ConfigMapEnvModel{
+		AppId:         22,
+		EnvironmentId: 5,
+		Deleted:       false,
+	}
+	responseModel := &chartConfig.ConfigMapEnvModel{
+		AppId:         22,
+		EnvironmentId: 5,
+		Deleted:       true,
+	}
+	type args struct {
+		createJobEnvOverrideRequest *CreateJobEnvOverridePayload
+		getByAppError               error
+		getByAppResponse            *chartConfig.ConfigMapEnvModel
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    *chartConfig.ConfigMapEnvModel
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "create environment override",
+			args: args{
+				createJobEnvOverrideRequest: &CreateJobEnvOverridePayload{
+					AppId: 22,
+					EnvId: 5,
+				},
+				getByAppError:    pg.ErrNoRows,
+				getByAppResponse: nil,
+			},
+			want:    configMap,
+			wantErr: assert.NoError,
+		},
+		{
+			name: "create deleted override",
+			args: args{
+				createJobEnvOverrideRequest: &CreateJobEnvOverridePayload{
+					AppId: 22,
+					EnvId: 5,
+				},
+				getByAppError:    nil,
+				getByAppResponse: responseModel,
+			},
+			want:    configMap,
+			wantErr: assert.NoError,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			impl := ConfigMapServiceImpl{
+				chartRepository:             chartRepository,
+				logger:                      sugaredLogger,
+				repoRepository:              repoRepository,
+				mergeUtil:                   util.MergeUtil{},
+				pipelineConfigRepository:    pipelineConfigRepository,
+				configMapRepository:         configMapRepository,
+				environmentConfigRepository: environmentConfigRepository,
+				commonService:               commonService,
+				appRepository:               appRepository,
+				configMapHistoryService:     configMapHistoryService,
+				environmentRepository:       envRepository,
+			}
+			configMapRepository.On("GetByAppIdAndEnvIdEnvLevel", 22, 5).Return(tt.args.getByAppResponse, tt.args.getByAppError).Once()
+			if tt.args.getByAppError == pg.ErrNoRows {
+				configMapRepository.On("CreateEnvLevel", model).Return(model, nil).Once()
+			}
+			if tt.args.getByAppError == nil {
+				configMapRepository.On("UpdateEnvLevel", model).Return(model, nil).Once()
+			}
+			got, err := impl.ConfigSecretEnvironmentCreate(tt.args.createJobEnvOverrideRequest)
+			if !tt.wantErr(t, err, fmt.Sprintf("ConfigSecretEnvironmentCreate(%v)", tt.args.createJobEnvOverrideRequest)) {
+				return
+			}
+			assert.Equal(t, tt.want.AppId, got.AppId, "ConfigSecretEnvironmentCreate(%v)", tt.args.createJobEnvOverrideRequest)
+			assert.Equal(t, tt.want.EnvironmentId, got.EnvId, "ConfigSecretEnvironmentCreate(%v)", tt.args.createJobEnvOverrideRequest)
+			assert.Equal(t, tt.want.Deleted, false, "ConfigSecretEnvironmentCreate(%v)", tt.args.createJobEnvOverrideRequest)
+		})
+	}
+}
+
+func TestConfigMapServiceImpl_ConfigSecretEnvironmentDelete(t *testing.T) {
+	t.SkipNow()
+	chartRepository := mocks.NewChartRepository(t)
+	sugaredLogger, _ := util.NewSugardLogger()
+	repoRepository := mocks.NewChartRepoRepository(t)
+	pipelineConfigRepository := mocks2.NewPipelineConfigRepository(t)
+	configMapRepository := mocks2.NewConfigMapRepository(t)
+	environmentConfigRepository := mocks2.NewEnvConfigOverrideRepository(t)
+	commonService := mocks3.NewCommonService(t)
+	appRepository := mocks4.NewAppRepository(t)
+	configMapHistoryService := mocks5.NewConfigMapHistoryService(t)
+	envRepository := mocks6.NewEnvironmentRepository(t)
+
+	type args struct {
+		createJobEnvOverrideRequest *CreateJobEnvOverridePayload
+	}
+	configMap := &chartConfig.ConfigMapEnvModel{
+		AppId:         1,
+		EnvironmentId: 1,
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    *chartConfig.ConfigMapEnvModel
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "Delete configMap",
+			args: args{
+				createJobEnvOverrideRequest: &CreateJobEnvOverridePayload{
+					AppId: 1,
+					EnvId: 1,
+				},
+			},
+			want:    configMap,
+			wantErr: assert.NoError,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			impl := ConfigMapServiceImpl{
+				chartRepository:             chartRepository,
+				logger:                      sugaredLogger,
+				repoRepository:              repoRepository,
+				mergeUtil:                   util.MergeUtil{},
+				pipelineConfigRepository:    pipelineConfigRepository,
+				configMapRepository:         configMapRepository,
+				environmentConfigRepository: environmentConfigRepository,
+				commonService:               commonService,
+				appRepository:               appRepository,
+				configMapHistoryService:     configMapHistoryService,
+				environmentRepository:       envRepository,
+			}
+			configMapRepository.On("GetByAppIdAndEnvIdEnvLevel", 1, 1).Return(configMap, nil)
+			configMapRepository.On("UpdateEnvLevel", mock.Anything).Return(nil, nil)
+			got, err := impl.ConfigSecretEnvironmentDelete(tt.args.createJobEnvOverrideRequest)
+			if !tt.wantErr(t, err, fmt.Sprintf("ConfigSecretEnvironmentDelete(%v)", tt.args.createJobEnvOverrideRequest)) {
+				return
+			}
+			assert.Equal(t, tt.want.AppId, got.AppId, "ConfigSecretEnvironmentCreate(%v)", tt.args.createJobEnvOverrideRequest)
+			assert.Equal(t, tt.want.EnvironmentId, got.EnvId, "ConfigSecretEnvironmentCreate(%v)", tt.args.createJobEnvOverrideRequest)
+			assert.Equal(t, tt.want.Deleted, true, "ConfigSecretEnvironmentCreate(%v)", tt.args.createJobEnvOverrideRequest)
+		})
+	}
+}
+
+func TestConfigMapServiceImpl_ConfigSecretEnvironmentGet(t *testing.T) {
+	t.SkipNow()
+	chartRepository := mocks.NewChartRepository(t)
+	sugaredLogger, _ := util.NewSugardLogger()
+	repoRepository := mocks.NewChartRepoRepository(t)
+	pipelineConfigRepository := mocks2.NewPipelineConfigRepository(t)
+	configMapRepository := mocks2.NewConfigMapRepository(t)
+	environmentConfigRepository := mocks2.NewEnvConfigOverrideRepository(t)
+	commonService := mocks3.NewCommonService(t)
+	appRepository := mocks4.NewAppRepository(t)
+	configMapHistoryService := mocks5.NewConfigMapHistoryService(t)
+	envRepository := mocks6.NewEnvironmentRepository(t)
+
+	configMap := []*chartConfig.ConfigMapEnvModel{
+		{
+			AppId:         1,
+			EnvironmentId: 1,
+		},
+	}
+	envIds := []*int{&configMap[0].AppId}
+	envResponse := []*repository.Environment{{Name: "devtron-demo", Id: 1}}
+	type args struct {
+		appId int
+	}
+	tests := []struct {
+		name string
+
+		args    args
+		want    []*chartConfig.ConfigMapEnvModel
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "test1",
+			args: args{
+				appId: 1,
+			},
+			want:    configMap,
+			wantErr: assert.NoError,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			impl := ConfigMapServiceImpl{
+				chartRepository:             chartRepository,
+				logger:                      sugaredLogger,
+				repoRepository:              repoRepository,
+				mergeUtil:                   util.MergeUtil{},
+				pipelineConfigRepository:    pipelineConfigRepository,
+				configMapRepository:         configMapRepository,
+				environmentConfigRepository: environmentConfigRepository,
+				commonService:               commonService,
+				appRepository:               appRepository,
+				configMapHistoryService:     configMapHistoryService,
+				environmentRepository:       envRepository,
+			}
+			configMapRepository.On("GetEnvLevelByAppId", 1).Return(configMap, nil)
+			envRepository.On("FindByIds", envIds).Return(envResponse, nil)
+			got, err := impl.ConfigSecretEnvironmentGet(tt.args.appId)
+			if !tt.wantErr(t, err, fmt.Sprintf("ConfigSecretEnvironmentGet(%v)", tt.args.appId)) {
+				return
+			}
+			assert.Equal(t, tt.want[0].EnvironmentId, got[0].EnvironmentId, "ConfigSecretEnvironmentGet(%v)", tt.args.appId)
+		})
+	}
+}
diff --git a/pkg/pipeline/WorkflowService_test.go b/pkg/pipeline/WorkflowService_test.go
new file mode 100644
index 000000000..2495a13f6
--- /dev/null
+++ b/pkg/pipeline/WorkflowService_test.go
@@ -0,0 +1,556 @@
+package pipeline
+
+import (
+	"fmt"
+	"github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1"
+	bean3 "github.com/devtron-labs/devtron/api/bean"
+	"github.com/devtron-labs/devtron/internal/util"
+	"github.com/stretchr/testify/assert"
+	v12 "k8s.io/api/core/v1"
+	"log"
+	"testing"
+)
+
+func Test_getConfigMapsAndSecrets(t *testing.T) {
+	t.SkipNow()
+	type args struct {
+		workflowRequest   *WorkflowRequest
+		existingConfigMap *bean3.ConfigMapJson
+		existingSecrets   *bean3.ConfigSecretJson
+	}
+	workflowRequest := &WorkflowRequest{
+		WorkflowId: 123,
+	}
+	existingConfigMap := &bean3.ConfigMapJson{
+		Enabled: true,
+		Maps: []bean3.ConfigSecretMap{
+			{Name: "job-map",
+				Data:     []byte("{\"abcd\": \"aditya-cm-1-job-test-cm1\"}"),
+				External: false,
+			},
+		},
+	}
+	existingSecrets := &bean3.ConfigSecretJson{
+		Enabled: true,
+		Secrets: []*bean3.ConfigSecretMap{
+			{Name: "job-secret",
+				Data:     []byte("{\"abcd\": \"XCJhZGl0eWEtY20tMS1qb2ItdGVzdC1jbTFcIn0i\"}"),
+				External: false,
+			},
+		},
+	}
+	existingSecrets1 := &bean3.ConfigSecretJson{
+		Enabled: true,
+		Secrets: []*bean3.ConfigSecretMap{
+			{Name: "job-secret",
+				Data:     []byte("{\"abcd\": \"XCJhZGl0eWEtY20tMS1qb2ItdGVzdC1jbTFcIn0i\"}"),
+				External: true,
+			},
+		},
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    bean3.ConfigMapJson
+		want1   bean3.ConfigSecretJson
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "Empty existingConfigMap and existingSecrets",
+			args: args{
+				workflowRequest:   workflowRequest,
+				existingConfigMap: &bean3.ConfigMapJson{},
+				existingSecrets:   &bean3.ConfigSecretJson{},
+			},
+			want:    bean3.ConfigMapJson{},
+			want1:   bean3.ConfigSecretJson{},
+			wantErr: assert.NoError,
+		},
+		{
+			name: "non empty  existingConfigMap and empty existingSecrets",
+			args: args{
+				workflowRequest:   workflowRequest,
+				existingConfigMap: existingConfigMap,
+				existingSecrets:   &bean3.ConfigSecretJson{},
+			},
+			want:    bean3.ConfigMapJson{Maps: []bean3.ConfigSecretMap{{Name: "job-map-123-ci", Data: []byte("{\"abcd\": \"aditya-cm-1-job-test-cm1\"}"), External: false}}},
+			want1:   bean3.ConfigSecretJson{},
+			wantErr: assert.NoError,
+		},
+		{
+			name: "non empty  existingConfigMap and non empty existingSecrets",
+			args: args{
+				workflowRequest:   workflowRequest,
+				existingConfigMap: existingConfigMap,
+				existingSecrets:   existingSecrets,
+			},
+			want:    bean3.ConfigMapJson{Maps: []bean3.ConfigSecretMap{{Name: "job-map-123-ci", Data: []byte("{\"abcd\": \"aditya-cm-1-job-test-cm1\"}"), External: false}}},
+			want1:   bean3.ConfigSecretJson{Secrets: []*bean3.ConfigSecretMap{{Name: "job-secret-123-ci", Data: []byte("{\"abcd\": \"XCJhZGl0eWEtY20tMS1qb2ItdGVzdC1jbTFcIn0i\"}"), External: false}}},
+			wantErr: assert.NoError,
+		},
+		{
+			name: "non empty  existingConfigMap and non empty existingSecrets with external ",
+			args: args{
+				workflowRequest:   workflowRequest,
+				existingConfigMap: existingConfigMap,
+				existingSecrets:   existingSecrets1,
+			},
+			want:    bean3.ConfigMapJson{Maps: []bean3.ConfigSecretMap{{Name: "job-map-123-ci", Data: []byte("{\"abcd\": \"aditya-cm-1-job-test-cm1\"}"), External: false}}},
+			want1:   bean3.ConfigSecretJson{},
+			wantErr: assert.NoError,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, got1, err := getConfigMapsAndSecrets(tt.args.workflowRequest, tt.args.existingConfigMap, tt.args.existingSecrets)
+			if !tt.wantErr(t, err, fmt.Sprintf("getConfigMapsAndSecrets(%v, %v, %v)", tt.args.workflowRequest, tt.args.existingConfigMap, tt.args.existingSecrets)) {
+				return
+			}
+			assert.Equalf(t, tt.want, got, "getConfigMapsAndSecrets(%v, %v, %v)", tt.args.workflowRequest, tt.args.existingConfigMap, tt.args.existingSecrets)
+			assert.Equalf(t, tt.want1, got1, "getConfigMapsAndSecrets(%v, %v, %v)", tt.args.workflowRequest, tt.args.existingConfigMap, tt.args.existingSecrets)
+			if tt.args.existingSecrets == existingSecrets {
+				assert.Equalf(t, tt.want1.Secrets[0].Name, got1.Secrets[0].Name, "getConfigMapsAndSecrets(%v, %v, %v)", tt.args.workflowRequest, tt.args.existingConfigMap, tt.args.existingSecrets)
+
+			}
+		})
+	}
+}
+
+func Test_getCiTemplateWithConfigMapsAndSecrets(t *testing.T) {
+	t.SkipNow()
+	type args struct {
+		configMaps        *bean3.ConfigMapJson
+		secrets           *bean3.ConfigSecretJson
+		ciTemplate        v1alpha1.Template
+		existingConfigMap *bean3.ConfigMapJson
+		existingSecrets   *bean3.ConfigSecretJson
+	}
+	configMaps := &bean3.ConfigMapJson{
+		Enabled: true,
+		Maps: []bean3.ConfigSecretMap{
+			{Name: "job-map",
+				Data:     []byte("{\"abcd\": \"aditya-cm-1-job-test-cm1\"}"),
+				External: false,
+			},
+		},
+	}
+	secrets := &bean3.ConfigSecretJson{
+		Enabled: true,
+		Secrets: []*bean3.ConfigSecretMap{
+			{Name: "job-secret",
+				Data:     []byte("{\"abcd\": \"XCJhZGl0eWEtY20tMS1qb2ItdGVzdC1jbTFcIn0i\"}"),
+				External: false,
+			},
+		},
+	}
+
+	ciTemplate := v1alpha1.Template{
+		Name: CI_WORKFLOW_NAME,
+		Container: &v12.Container{
+			EnvFrom: []v12.EnvFromSource{},
+		},
+	}
+	ciTemplate1 := v1alpha1.Template{
+		Name: CI_WORKFLOW_NAME,
+		Container: &v12.Container{
+			VolumeMounts: []v12.VolumeMount{},
+		},
+	}
+
+	existingConfigMap := &bean3.ConfigMapJson{
+		Enabled: true,
+		Maps: []bean3.ConfigSecretMap{
+			{Name: "job-map",
+				Data:     []byte("{\"abcd\": \"aditya-cm-1-job-test-cm1\"}"),
+				External: false,
+			},
+		},
+	}
+	existingSecrets := &bean3.ConfigSecretJson{
+		Enabled: true,
+		Secrets: []*bean3.ConfigSecretMap{
+			{Name: "job-secret",
+				Data:     []byte("{\"abcd\":\"XCJhZGl0eWEtY20tMS1qb2ItdGVzdC1jbTFcIn0i\"}"),
+				External: false,
+			},
+		},
+	}
+	configMaps1 := &bean3.ConfigMapJson{
+		Enabled: true,
+		Maps: []bean3.ConfigSecretMap{
+			{Name: "job-map",
+				Data:     []byte("{\"abcd\": \"aditya-cm-1-job-test-cm1\"}"),
+				External: false,
+				Type:     "environment",
+			},
+		},
+	}
+	secrets1 := &bean3.ConfigSecretJson{
+		Enabled: true,
+		Secrets: []*bean3.ConfigSecretMap{
+			{Name: "job-secret",
+				Data:     []byte("{\"abcd\": \"XCJhZGl0eWEtY20tMS1qb2ItdGVzdC1jbTFcIn0i\"}"),
+				External: false,
+				Type:     "environment",
+			},
+		},
+	}
+	existingConfigMap1 := &bean3.ConfigMapJson{
+		Enabled: true,
+		Maps: []bean3.ConfigSecretMap{
+			{Name: "job-map",
+				Data:     []byte("{\"abcd\": \"aditya-cm-1-job-test-cm1\"}"),
+				External: false,
+				Type:     "environment",
+			},
+		},
+	}
+	existingSecrets1 := &bean3.ConfigSecretJson{
+		Enabled: true,
+		Secrets: []*bean3.ConfigSecretMap{
+			{Name: "job-secret",
+				Data:     []byte("{\"abcd\":\"XCJhZGl0eWEtY20tMS1qb2ItdGVzdC1jbTFcIn0i\"}"),
+				External: false,
+				Type:     "environment",
+			},
+		},
+	}
+	configMaps2 := &bean3.ConfigMapJson{
+		Enabled: true,
+		Maps: []bean3.ConfigSecretMap{
+			{Name: "job-map",
+				Data:     []byte("{\"abcd\": \"aditya-cm-1-job-test-cm1\"}"),
+				External: false,
+				Type:     "volume",
+			},
+		},
+	}
+	secrets2 := &bean3.ConfigSecretJson{
+		Enabled: true,
+		Secrets: []*bean3.ConfigSecretMap{
+			{Name: "job-secret",
+				Data:     []byte("{\"abcd\": \"XCJhZGl0eWEtY20tMS1qb2ItdGVzdC1jbTFcIn0i\"}"),
+				External: false,
+				Type:     "volume",
+			},
+		},
+	}
+	existingConfigMap2 := &bean3.ConfigMapJson{
+		Enabled: true,
+		Maps: []bean3.ConfigSecretMap{
+			{Name: "job-map",
+				Data:     []byte("{\"abcd\": \"aditya-cm-1-job-test-cm1\"}"),
+				External: false,
+				Type:     "volume",
+			},
+		},
+	}
+	existingSecrets2 := &bean3.ConfigSecretJson{
+		Enabled: true,
+		Secrets: []*bean3.ConfigSecretMap{
+			{Name: "job-secret",
+				Data:     []byte("{\"abcd\":\"XCJhZGl0eWEtY20tMS1qb2ItdGVzdC1jbTFcIn0i\"}"),
+				External: false,
+				Type:     "volume",
+			},
+		},
+	}
+	configMaps3 := &bean3.ConfigMapJson{
+		Enabled: true,
+		Maps: []bean3.ConfigSecretMap{
+			{Name: "job-map",
+				Data:     []byte("{\"abcd\": \"aditya-cm-1-job-test-cm1\"}"),
+				External: true,
+				Type:     "volume",
+			},
+		},
+	}
+	secrets3 := &bean3.ConfigSecretJson{
+		Enabled: true,
+		Secrets: []*bean3.ConfigSecretMap{
+			{Name: "job-secret",
+				Data:     []byte("{\"abcd\": \"XCJhZGl0eWEtY20tMS1qb2ItdGVzdC1jbTFcIn0i\"}"),
+				External: true,
+				Type:     "volume",
+			},
+		},
+	}
+	existingConfigMap3 := &bean3.ConfigMapJson{
+		Enabled: true,
+		Maps: []bean3.ConfigSecretMap{
+			{Name: "job-map",
+				Data:     []byte("{\"abcd\": \"aditya-cm-1-job-test-cm1\"}"),
+				External: true,
+				Type:     "volume",
+			},
+		},
+	}
+	existingSecrets3 := &bean3.ConfigSecretJson{
+		Enabled: true,
+		Secrets: []*bean3.ConfigSecretMap{
+			{Name: "job-secret",
+				Data:     []byte("{\"abcd\":\"XCJhZGl0eWEtY20tMS1qb2ItdGVzdC1jbTFcIn0i\"}"),
+				External: true,
+				Type:     "volume",
+			},
+		},
+	}
+	configMaps4 := &bean3.ConfigMapJson{
+		Enabled: true,
+		Maps: []bean3.ConfigSecretMap{
+			{Name: "job-map",
+				Data:     []byte("{\"abcd\": \"aditya-cm-1-job-test-cm1\"}"),
+				External: true,
+				Type:     "environment",
+			},
+		},
+	}
+	secrets4 := &bean3.ConfigSecretJson{
+		Enabled: true,
+		Secrets: []*bean3.ConfigSecretMap{
+			{Name: "job-secret",
+				Data:     []byte("{\"abcd\": \"XCJhZGl0eWEtY20tMS1qb2ItdGVzdC1jbTFcIn0i\"}"),
+				External: true,
+				Type:     "environment",
+			},
+		},
+	}
+	existingConfigMap4 := &bean3.ConfigMapJson{
+		Enabled: true,
+		Maps: []bean3.ConfigSecretMap{
+			{Name: "job-map",
+				Data:     []byte("{\"abcd\": \"aditya-cm-1-job-test-cm1\"}"),
+				External: true,
+				Type:     "environment",
+			},
+		},
+	}
+	existingSecrets4 := &bean3.ConfigSecretJson{
+		Enabled: true,
+		Secrets: []*bean3.ConfigSecretMap{
+			{Name: "job-secret",
+				Data:     []byte("{\"abcd\":\"XCJhZGl0eWEtY20tMS1qb2ItdGVzdC1jbTFcIn0i\"}"),
+				External: true,
+				Type:     "environment",
+			},
+		},
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    v1alpha1.Template
+		wantErr assert.ErrorAssertionFunc
+	}{
+
+		{name: "test1",
+			args: args{
+				configMaps:        configMaps,
+				secrets:           secrets,
+				ciTemplate:        ciTemplate,
+				existingSecrets:   existingSecrets,
+				existingConfigMap: existingConfigMap,
+			},
+			want:    ciTemplate,
+			wantErr: assert.NoError,
+		},
+		{name: "test2",
+			args: args{
+				configMaps:        configMaps1,
+				secrets:           secrets1,
+				ciTemplate:        ciTemplate,
+				existingSecrets:   existingSecrets1,
+				existingConfigMap: existingConfigMap1,
+			},
+			want:    ciTemplate,
+			wantErr: assert.NoError,
+		},
+		{name: "test3",
+			args: args{
+				configMaps:        configMaps2,
+				secrets:           secrets2,
+				ciTemplate:        ciTemplate1,
+				existingSecrets:   existingSecrets2,
+				existingConfigMap: existingConfigMap2,
+			},
+			want:    ciTemplate1,
+			wantErr: assert.NoError,
+		},
+		{name: "test4",
+			args: args{
+				configMaps:        configMaps3,
+				secrets:           secrets3,
+				ciTemplate:        ciTemplate1,
+				existingSecrets:   existingSecrets3,
+				existingConfigMap: existingConfigMap3,
+			},
+			want:    ciTemplate1,
+			wantErr: assert.NoError,
+		},
+		{name: "test5",
+			args: args{
+				configMaps:        configMaps4,
+				secrets:           secrets4,
+				ciTemplate:        ciTemplate,
+				existingSecrets:   existingSecrets4,
+				existingConfigMap: existingConfigMap4,
+			},
+			want:    ciTemplate,
+			wantErr: assert.NoError,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := getCiTemplateWithConfigMapsAndSecrets(tt.args.configMaps, tt.args.secrets, tt.args.ciTemplate, tt.args.existingConfigMap, tt.args.existingSecrets)
+			if !tt.wantErr(t, err, fmt.Sprintf("getCiTemplateWithConfigMapsAndSecrets(%v, %v, %v, %v, %v)", tt.args.configMaps, tt.args.secrets, tt.args.ciTemplate, tt.args.existingConfigMap, tt.args.existingSecrets)) {
+				return
+			}
+			assert.Equalf(t, tt.want, got, "getCiTemplateWithConfigMapsAndSecrets(%v, %v, %v, %v, %v)", tt.args.configMaps, tt.args.secrets, tt.args.ciTemplate, tt.args.existingConfigMap, tt.args.existingSecrets)
+		})
+	}
+}
+
+func Test_processConfigMapsAndSecrets(t *testing.T) {
+	t.SkipNow()
+	type args struct {
+		impl       *WorkflowServiceImpl
+		configMaps *bean3.ConfigMapJson
+		secrets    *bean3.ConfigSecretJson
+		entryPoint *string
+		steps      *[]v1alpha1.ParallelSteps
+		volumes    *[]v12.Volume
+		templates  *[]v1alpha1.Template
+	}
+	logger, err := util.NewSugardLogger()
+	if err != nil {
+		log.Fatalf("error in logger initialization %s,%s", "err", err)
+	}
+	impl := &WorkflowServiceImpl{Logger: logger}
+	configMaps := &bean3.ConfigMapJson{
+		Enabled: true,
+		Maps: []bean3.ConfigSecretMap{
+			{Name: "job-map",
+				Data:     []byte("{\"abcd\": \"aditya-cm-1-job-test-cm1\"}"),
+				External: false,
+			},
+		},
+	}
+	secrets := &bean3.ConfigSecretJson{
+		Enabled: true,
+		Secrets: []*bean3.ConfigSecretMap{
+			{Name: "job-secret",
+				Data:     []byte("{\"abcd\": \"XCJhZGl0eWEtY20tMS1qb2ItdGVzdC1jbTFcIn0i\"}"),
+				External: false,
+			},
+		},
+	}
+	entryPoint := "\"ci-stages-with-env\""
+	steps := &[]v1alpha1.ParallelSteps{}
+	volumes := &[]v12.Volume{}
+	templates := &[]v1alpha1.Template{}
+	tests := []struct {
+		name    string
+		args    args
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "TEST 1",
+			args: args{
+				impl:       impl,
+				configMaps: configMaps,
+				secrets:    secrets,
+				entryPoint: &entryPoint,
+				steps:      steps,
+				volumes:    volumes,
+				templates:  templates,
+			},
+			wantErr: assert.NoError,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			tt.wantErr(t, processConfigMapsAndSecrets(tt.args.impl, tt.args.configMaps, tt.args.secrets, tt.args.entryPoint, tt.args.steps, tt.args.volumes, tt.args.templates), fmt.Sprintf("processConfigMapsAndSecrets(%v, %v, %v, %v, %v, %v, %v)", tt.args.impl, tt.args.configMaps, tt.args.secrets, tt.args.entryPoint, tt.args.steps, tt.args.volumes, tt.args.templates))
+		})
+	}
+}
+
+func Test_processConfigMapsAndSecrets1(t *testing.T) {
+	t.SkipNow()
+
+	type args struct {
+		impl       *WorkflowServiceImpl
+		configMaps *bean3.ConfigMapJson
+		secrets    *bean3.ConfigSecretJson
+		entryPoint *string
+		steps      *[]v1alpha1.ParallelSteps
+		volumes    *[]v12.Volume
+		templates  *[]v1alpha1.Template
+	}
+
+	logger, err := util.NewSugardLogger()
+	if err != nil {
+		log.Fatalf("error in logger initialization %s,%s", "err", err)
+	}
+	impl := &WorkflowServiceImpl{Logger: logger}
+
+	tests := []struct {
+		name    string
+		args    args
+		wantErr bool
+	}{
+		{
+			name: "NoError",
+			args: args{
+				impl:       impl,
+				configMaps: &bean3.ConfigMapJson{Maps: []bean3.ConfigSecretMap{}},
+				secrets:    &bean3.ConfigSecretJson{Secrets: []*bean3.ConfigSecretMap{}},
+				entryPoint: new(string),
+				steps:      &[]v1alpha1.ParallelSteps{},
+				volumes:    &[]v12.Volume{},
+				templates:  &[]v1alpha1.Template{},
+			},
+			wantErr: false,
+		},
+		{
+			name: "ProcessConfigMapError",
+			args: args{
+				impl:       impl,
+				configMaps: &bean3.ConfigMapJson{Maps: []bean3.ConfigSecretMap{{Name: "job-map"}}},
+				secrets:    &bean3.ConfigSecretJson{Secrets: []*bean3.ConfigSecretMap{}},
+				entryPoint: new(string),
+				steps:      &[]v1alpha1.ParallelSteps{},
+				volumes:    &[]v12.Volume{},
+				templates:  &[]v1alpha1.Template{},
+			},
+			wantErr: true,
+		},
+		{
+			name: "ProcessSecretsError",
+			args: args{
+				impl:       impl,
+				configMaps: &bean3.ConfigMapJson{Maps: []bean3.ConfigSecretMap{}},
+				secrets:    &bean3.ConfigSecretJson{Secrets: []*bean3.ConfigSecretMap{{Name: "job-secret"}}}, // Simulate an error scenario
+				entryPoint: new(string),
+				steps:      &[]v1alpha1.ParallelSteps{},
+				volumes:    &[]v12.Volume{},
+				templates:  &[]v1alpha1.Template{},
+			},
+			wantErr: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			err := processConfigMapsAndSecrets(tt.args.impl, tt.args.configMaps, tt.args.secrets, tt.args.entryPoint, tt.args.steps, tt.args.volumes, tt.args.templates)
+
+			if tt.wantErr {
+				if err == nil {
+					t.Errorf("expected an error, but got none")
+				}
+			} else {
+				if err != nil {
+					t.Errorf("unexpected error: %v", err)
+				}
+			}
+		})
+	}
+}
diff --git a/pkg/pipeline/history/CiPipelineHistoryService_test.go b/pkg/pipeline/history/CiPipelineHistoryService_test.go
index 903981515..7f533750b 100644
--- a/pkg/pipeline/history/CiPipelineHistoryService_test.go
+++ b/pkg/pipeline/history/CiPipelineHistoryService_test.go
@@ -3,6 +3,7 @@ package history
 import (
 	"encoding/json"
 	"github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig"
+	mocks2 "github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig/mocks"
 	"github.com/devtron-labs/devtron/internal/util"
 	bean2 "github.com/devtron-labs/devtron/pkg/pipeline/bean"
 	"github.com/devtron-labs/devtron/pkg/pipeline/history/repository"
@@ -13,15 +14,16 @@ import (
 )
 
 func TestCiPipelineHistoryService(t *testing.T) {
-
+	t.SkipNow()
 	t.Run("SaveHistory", func(t *testing.T) {
 
 		sugaredLogger, err := util.NewSugardLogger()
 		assert.Nil(t, err)
 
 		mockedCiPipelineHistoryRepository := mocks.NewCiPipelineHistoryRepository(t)
+		mockedCiPipelineRepository := mocks2.NewCiPipelineRepository(t)
 
-		CiPipelineHistoryServiceImpl := NewCiPipelineHistoryServiceImpl(mockedCiPipelineHistoryRepository, sugaredLogger)
+		CiPipelineHistoryServiceImpl := NewCiPipelineHistoryServiceImpl(mockedCiPipelineHistoryRepository, sugaredLogger, mockedCiPipelineRepository)
 
 		PipelineObject := pipelineConfig.CiPipeline{
 			Id:                       5,
@@ -108,8 +110,21 @@ func TestCiPipelineHistoryService(t *testing.T) {
 			ScanEnabled:               false,
 			Manual:                    false,
 		}
+		mockedCiPipelineObject := repository.CiEnvMappingHistory{
+			Id:            0,
+			CiPipelineId:  5,
+			EnvironmentId: 1,
+		}
+		CiEnvMapping := &pipelineConfig.CiEnvMapping{
+			Id:            1,
+			EnvironmentId: 1,
+			CiPipelineId:  5,
+			Deleted:       false,
+		}
 
-		mockedCiPipelineHistoryRepository.On("Save", &mockedCiPipelineHistoryObject).Return(nil)
+		mockedCiPipelineHistoryRepository.On("Save", &mockedCiPipelineHistoryObject).Return(nil).Once()
+		mockedCiPipelineRepository.On("FindCiEnvMappingByCiPipelineId", 5).Return(CiEnvMapping, nil).Once()
+		mockedCiPipelineHistoryRepository.On("SaveCiEnvMappingHistory", &mockedCiPipelineObject).Return(nil).Once()
 
 		err = CiPipelineHistoryServiceImpl.SaveHistory(&PipelineObject, PipelineMaterialsObject, &CiTemplateObject, "update")
 
diff --git a/pkg/pipeline/history/ciTemplateHistoryService_test.go b/pkg/pipeline/history/ciTemplateHistoryService_test.go
index 138402ecd..80e4a23ca 100644
--- a/pkg/pipeline/history/ciTemplateHistoryService_test.go
+++ b/pkg/pipeline/history/ciTemplateHistoryService_test.go
@@ -19,13 +19,12 @@ func TestCiTemplateHistoryService(t *testing.T) {
 		assert.Nil(t, err)
 
 		mockedCiTemplateHistoryRepository := mocks.NewCiTemplateHistoryRepository(t)
-
 		CiTemplateHistoryServiceImpl := NewCiTemplateHistoryServiceImpl(mockedCiTemplateHistoryRepository, sugaredLogger)
-
+		var DockerRegistryId *string = nil
 		mockedCiTemplateHistoryObject := repository.CiTemplateHistory{
 			CiTemplateId:       28,
 			AppId:              38,
-			DockerRegistryId:   "prakash",
+			DockerRegistryId:   DockerRegistryId,
 			DockerRepository:   "prakash1001/sams-repository-3",
 			DockerfilePath:     "",
 			Args:               "",
@@ -52,7 +51,7 @@ func TestCiTemplateHistoryService(t *testing.T) {
 			CiTemplate: &pipelineConfig.CiTemplate{
 				Id:                 28,
 				AppId:              38,
-				DockerRegistryId:   "prakash",
+				DockerRegistryId:   DockerRegistryId,
 				DockerRepository:   "prakash1001/sams-repository-3",
 				DockerfilePath:     "",
 				Args:               "",