Repository: diggerhq/digger
Pull request #394 (2023-06-20 15:37:58)

Update actions/checkout action to v3
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/checkout](https://togithub.com/actions/checkout) | action | major | `v2` -> `v3` |

---

### Release Notes

<details>
<summary>actions/checkout</summary>

### [`v3`](https://togithub.com/actions/checkout/blob/HEAD/CHANGELOG.md#v353)

[Compare Source](https://togithub.com/actions/checkout/compare/v2...v3)

-   [Fix: Checkout fail in self-hosted runners when faulty submodule are checked-in](https://togithub.com/actions/checkout/pull/1196)
-   [Fix typos found by codespell](https://togithub.com/actions/checkout/pull/1287)
-   [Add support for sparse checkouts](https://togithub.com/actions/checkout/pull/1369)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/diggerhq/digger).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNS4xMzEuMCIsInVwZGF0ZWRJblZlciI6IjM1LjEzMS4wIiwidGFyZ2V0QnJhbmNoIjoiZGV2ZWxvcCJ9-->



Patch:
diff --git a/.github/workflows/stats.yml b/.github/workflows/stats.yml
index 8055ecd9..ab82b6cd 100644
--- a/.github/workflows/stats.yml
+++ b/.github/workflows/stats.yml
@@ -12,7 +12,7 @@ jobs:
 
     steps:
       - name: Clone repository
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
 
       - name: Count unique clones
         id: clones
diff --git a/action.yml b/action.yml
index df46e022..c8a8580a 100644
--- a/action.yml
+++ b/action.yml
@@ -71,15 +71,11 @@ inputs:
     description: Disable locking
     required: false
     default: 'false'
-  policy-check-enabled:
-    description: Enable policy check
+  digger-token:
+    description: Digger token
     required: false
-    default: 'false'
-  digger-cloud-token:
-    description: Digger cloud token
-    required: false
-  digger-cloud-hostname:
-    description: Digger cloud hostname
+  digger-hostname:
+    description: Digger hostname
     required: false
     default: 'https://cloud.digger.dev'
   setup-tfenv:
@@ -138,7 +134,7 @@ runs:
       uses: google-github-actions/setup-gcloud@v1
       if: inputs.setup-google-cloud == 'true'
 
-    - name: Configure Test AWS credentials
+    - name: Configure AWS credentials
       uses: aws-actions/configure-aws-credentials@v2
       with:
         aws-access-key-id: ${{ inputs.aws-access-key-id }}
@@ -187,9 +183,8 @@ runs:
         PLAN_UPLOAD_DESTINATION: ${{ inputs.upload-plan-destination }}
         ACTIVATE_VENV: ${{ inputs.setup-checkov == 'true' }}
         DISABLE_LOCKING: ${{ inputs.disable-locking == 'true' }}
-        POLICY_CHECK_ENABLED: ${{ inputs.policy-check-enabled == 'true' }}
-        DIGGER_CLOUD_TOKEN: ${{ inputs.digger-cloud-token }}
-        DIGGER_CLOUD_HOSTNAME: ${{ inputs.digger-cloud-hostname }}
+        DIGGER_TOKEN: ${{ inputs.digger-token }}
+        DIGGER_HOSTNAME: ${{ inputs.digger-hostname }}
         ACCUMULATE_PLANS: ${{ inputs.post-plans-as-one-comment == 'true' }}
       run: |
           cd ${{ github.action_path }}
@@ -205,9 +200,8 @@ runs:
         PLAN_UPLOAD_DESTINATION: ${{ inputs.upload-plan-destination }}
         ACTIVATE_VENV: ${{ inputs.setup-checkov == 'true' }}
         DISABLE_LOCKING: ${{ inputs.disable-locking == 'true' }}
-        POLICY_CHECK_ENABLED: ${{ inputs.policy-check-enabled == 'true' }}
-        DIGGER_CLOUD_TOKEN: ${{ inputs.digger-cloud-token }}
-        DIGGER_CLOUD_HOSTNAME: ${{ inputs.digger-cloud-hostname }}
+        DIGGER_TOKEN: ${{ inputs.digger-token }}
+        DIGGER_HOSTNAME: ${{ inputs.digger-hostname }}
         ACCUMULATE_PLANS: ${{ inputs.post-plans-as-one-comment == 'true' }}
       id: digger
       shell: bash
diff --git a/cmd/digger/main.go b/cmd/digger/main.go
index 5dfcb2ed..5d22b2f8 100644
--- a/cmd/digger/main.go
+++ b/cmd/digger/main.go
@@ -61,7 +61,7 @@ func gitHubCI(lock core_locking.Lock, policyChecker core_policy.Checker) {
 
 	walker := configuration.FileSystemDirWalker{}
 
-	diggerConfig, err := configuration.LoadDiggerConfig("./", &walker)
+	diggerConfig, dependencyGraph, err := configuration.LoadDiggerConfig("./", &walker)
 	if err != nil {
 		reportErrorAndExit(githubActor, fmt.Sprintf("Failed to read Digger config. %s", err), 4)
 	}
@@ -115,7 +115,7 @@ func gitHubCI(lock core_locking.Lock, policyChecker core_policy.Checker) {
 	if err != nil {
 		reportErrorAndExit(githubActor, fmt.Sprintf("Failed to get current dir. %s", err), 4)
 	}
-	allAppliesSuccessful, atLeastOneApply, err := digger.RunCommandsPerProject(commandsToRunPerProject, parsedGhContext.Repository, githubActor, eventName, prNumber, githubPrService, lock, reporter, planStorage, policyChecker, currentDir)
+	allAppliesSuccessful, atLeastOneApply, err := digger.RunCommandsPerProject(commandsToRunPerProject, &dependencyGraph, parsedGhContext.Repository, githubActor, eventName, prNumber, githubPrService, lock, reporter, planStorage, policyChecker, currentDir)
 	if err != nil {
 		reportErrorAndExit(githubActor, fmt.Sprintf("Failed to run commands. %s", err), 8)
 	}
@@ -153,7 +153,7 @@ func gitLabCI(lock core_locking.Lock, policyChecker core_policy.Checker) {
 	}
 	fmt.Printf("main: working dir: %s \n", currentDir)
 
-	diggerConfig, err := configuration.LoadDiggerConfig(currentDir, &walker)
+	diggerConfig, dependencyGraph, err := configuration.LoadDiggerConfig(currentDir, &walker)
 	if err != nil {
 		reportErrorAndExit(projectNamespace, fmt.Sprintf("Failed to read Digger config. %s", err), 4)
 	}
@@ -204,7 +204,7 @@ func gitLabCI(lock core_locking.Lock, policyChecker core_policy.Checker) {
 		CiService: gitlabService,
 		PrNumber:  *gitLabContext.MergeRequestIId,
 	}
-	allAppliesSuccess, atLeastOneApply, err := digger.RunCommandsPerProject(commandsToRunPerProject, diggerProjectNamespace, gitLabContext.GitlabUserName, gitLabContext.EventType.String(), *gitLabContext.MergeRequestIId, gitlabService, lock, reporter, planStorage, policyChecker, currentDir)
+	allAppliesSuccess, atLeastOneApply, err := digger.RunCommandsPerProject(commandsToRunPerProject, &dependencyGraph, diggerProjectNamespace, gitLabContext.GitlabUserName, gitLabContext.EventType.String(), *gitLabContext.MergeRequestIId, gitlabService, lock, reporter, planStorage, policyChecker, currentDir)
 
 	if err != nil {
 		fmt.Printf("failed to execute command, %v", err)
@@ -247,7 +247,7 @@ func azureCI(lock core_locking.Lock, policyChecker core_policy.Checker) {
 	}
 	fmt.Printf("main: working dir: %s \n", currentDir)
 
-	diggerConfig, err := configuration.LoadDiggerConfig(currentDir, &walker)
+	diggerConfig, dependencyGraph, err := configuration.LoadDiggerConfig(currentDir, &walker)
 	if err != nil {
 		reportErrorAndExit(parsedAzureContext.BaseUrl, fmt.Sprintf("Failed to read Digger config. %s", err), 4)
 	}
@@ -282,7 +282,7 @@ func azureCI(lock core_locking.Lock, policyChecker core_policy.Checker) {
 		CiService: azureService,
 		PrNumber:  prNumber,
 	}
-	allAppliesSuccess, atLeastOneApply, err := digger.RunCommandsPerProject(commandsToRunPerProject, diggerProjectNamespace, parsedAzureContext.BaseUrl, parsedAzureContext.EventType, prNumber, azureService, lock, reporter, planStorage, policyChecker, currentDir)
+	allAppliesSuccess, atLeastOneApply, err := digger.RunCommandsPerProject(commandsToRunPerProject, &dependencyGraph, diggerProjectNamespace, parsedAzureContext.BaseUrl, parsedAzureContext.EventType, prNumber, azureService, lock, reporter, planStorage, policyChecker, currentDir)
 	if err != nil {
 		reportErrorAndExit(parsedAzureContext.BaseUrl, fmt.Sprintf("Failed to run commands. %s", err), 8)
 	}
@@ -328,11 +328,11 @@ func main() {
 		os.Exit(0)
 	}
 	var policyChecker core_policy.Checker
-	if os.Getenv("POLICY_CHECK_ENABLED") == "true" {
+	if os.Getenv("DIGGER_TOKEN") != "" {
 		policyChecker = policy.DiggerPolicyChecker{
 			PolicyProvider: &policy.DiggerHttpPolicyProvider{
-				DiggerHost: os.Getenv("DIGGER_CLOUD_HOSTNAME"),
-				AuthToken:  os.Getenv("DIGGER_CLOUD_TOKEN"),
+				DiggerHost: os.Getenv("DIGGER_HOSTNAME"),
+				AuthToken:  os.Getenv("DIGGER_TOKEN"),
 				HttpClient: http.DefaultClient,
 			}}
 	} else {
diff --git a/go.mod b/go.mod
index db032180..539490f7 100644
--- a/go.mod
+++ b/go.mod
@@ -14,10 +14,12 @@ require (
 	github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5
 	github.com/open-policy-agent/opa v0.53.1
 	github.com/stretchr/testify v1.8.4
-	github.com/xanzy/go-gitlab v0.85.0
+	github.com/xanzy/go-gitlab v0.86.0
 	gopkg.in/yaml.v3 v3.0.1
 )
 
+require github.com/dominikbraun/graph v0.22.3
+
 require (
 	cloud.google.com/go v0.110.0 // indirect
 	cloud.google.com/go/compute v1.18.0 // indirect
diff --git a/go.sum b/go.sum
index d46b13c7..63917b5d 100644
--- a/go.sum
+++ b/go.sum
@@ -42,6 +42,7 @@ github.com/caarlos0/env/v8 v8.0.0/go.mod h1:7K4wMY9bH0esiXSSHlfHLX5xKGQMnkH5Fk4T
 github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
 github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
@@ -57,6 +58,8 @@ github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWa
 github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g=
 github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
 github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
+github.com/dominikbraun/graph v0.22.3 h1:2bbNzuMX+iKpM+XW1YPu7ch8IyXi/VbG7UYrRj2FHuI=
+github.com/dominikbraun/graph v0.22.3/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc=
 github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -69,6 +72,7 @@ github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
 github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
+github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
 github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -77,6 +81,7 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre
 github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
 github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
 github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
 github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -137,6 +142,9 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y
 github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
 github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
 github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
+github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
 github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -147,6 +155,9 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfr
 github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5 h1:YH424zrwLTlyHSH/GzLMJeu5zhYVZSx5RQxGKm1h96s=
 github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5/go.mod h1:PoGiBqKSQK1vIfQ+yVaFcGjDySHvym6FM1cNYnwzbrY=
 github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/open-policy-agent/opa v0.53.1 h1:APN8iA7Txgel13kSkc6S8dbUulydiPojXt6iyubmB7Q=
 github.com/open-policy-agent/opa v0.53.1/go.mod h1:j3wl8FqSz/+u33Scl72Ms2wxkZx4yZPdqSCrOqBqdsA=
 github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
@@ -183,6 +194,8 @@ github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BG
 github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
 github.com/xanzy/go-gitlab v0.85.0 h1:E/wjnsd/mM5kV6O9y5+i6zxjx+wfAwa97sgcT1ETNwk=
 github.com/xanzy/go-gitlab v0.85.0/go.mod h1:5ryv+MnpZStBH8I/77HuQBsMbBGANtVpLWC15qOjWAw=
+github.com/xanzy/go-gitlab v0.86.0 h1:jR8V9cK9jXRQDb46KOB20NCF3ksY09luaG0IfXE6p7w=
+github.com/xanzy/go-gitlab v0.86.0/go.mod h1:5ryv+MnpZStBH8I/77HuQBsMbBGANtVpLWC15qOjWAw=
 github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
 github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
 github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
diff --git a/pkg/azure/azure.go b/pkg/azure/azure.go
index d5911390..c5b2a329 100644
--- a/pkg/azure/azure.go
+++ b/pkg/azure/azure.go
@@ -299,7 +299,18 @@ func (a *AzureReposService) IsClosed(prNumber int) (bool, error) {
 	if err != nil {
 		return false, err
 	}
-	return *pullRequest.Status == git.PullRequestStatusValues.Completed || *pullRequest.Status == git.PullRequestStatusValues.Abandoned, nil
+	return *pullRequest.Status == git.PullRequestStatusValues.Abandoned, nil
+}
+
+func (a *AzureReposService) IsMerged(prNumber int) (bool, error) {
+	pullRequest, err := a.Client.GetPullRequestById(context.Background(), git.GetPullRequestByIdArgs{
+		Project:       &a.ProjectName,
+		PullRequestId: &prNumber,
+	})
+	if err != nil {
+		return false, err
+	}
+	return *pullRequest.Status == git.PullRequestStatusValues.Completed, nil
 }
 
 func ProcessAzureReposEvent(azureEvent interface{}, diggerConfig *configuration.DiggerConfig, ciService ci.CIService) ([]configuration.Project, *configuration.Project, int, error) {
diff --git a/pkg/ci/ci.go b/pkg/ci/ci.go
index d61c5471..591a9c3d 100644
--- a/pkg/ci/ci.go
+++ b/pkg/ci/ci.go
@@ -7,7 +7,11 @@ type CIService interface {
 	SetStatus(prNumber int, status string, statusContext string) error
 	GetCombinedPullRequestStatus(prNumber int) (string, error)
 	MergePullRequest(prNumber int) error
+	// IsMergeable is still open and ready to be merged
 	IsMergeable(prNumber int) (bool, error)
+	// IsMerged merged and closed
+	IsMerged(prNumber int) (bool, error)
+	// IsClosed closed without merging
 	IsClosed(prNumber int) (bool, error)
 	GetUserTeams(organisation string, user string) ([]string, error)
 }
diff --git a/pkg/configuration/config.go b/pkg/configuration/config.go
index 7b90a17c..a6c384d6 100644
--- a/pkg/configuration/config.go
+++ b/pkg/configuration/config.go
@@ -10,13 +10,14 @@ type DiggerConfig struct {
 }
 
 type Project struct {
-	Name            string
-	Dir             string
-	Workspace       string
-	Terragrunt      bool
-	Workflow        string
-	IncludePatterns []string
-	ExcludePatterns []string
+	Name               string
+	Dir                string
+	Workspace          string
+	Terragrunt         bool
+	Workflow           string
+	IncludePatterns    []string
+	ExcludePatterns    []string
+	DependencyProjects []string
 }
 
 type Workflow struct {
diff --git a/pkg/configuration/converters.go b/pkg/configuration/converters.go
index 98d2a8bc..1e67bc60 100644
--- a/pkg/configuration/converters.go
+++ b/pkg/configuration/converters.go
@@ -4,6 +4,7 @@ import (
 	"digger/pkg/core/models"
 	"digger/pkg/utils"
 	"fmt"
+	"github.com/dominikbraun/graph"
 	"path/filepath"
 )
 
@@ -17,6 +18,7 @@ func copyProjects(projects []*ProjectYaml) []Project {
 			p.Workflow,
 			p.IncludePatterns,
 			p.ExcludePatterns,
+			p.DependencyProjects,
 		}
 		result[i] = item
 	}
@@ -99,7 +101,7 @@ func copyWorkflows(workflows map[string]*WorkflowYaml) map[string]Workflow {
 	return result
 }
 
-func ConvertDiggerYamlToConfig(diggerYaml *DiggerConfigYaml, workingDir string, walker DirWalker) (*DiggerConfig, error) {
+func ConvertDiggerYamlToConfig(diggerYaml *DiggerConfigYaml, workingDir string, walker DirWalker) (*DiggerConfig, graph.Graph[string, string], error) {
 	var diggerConfig DiggerConfig
 	const defaultWorkflowName = "default"
 
@@ -145,15 +147,24 @@ func ConvertDiggerYamlToConfig(diggerYaml *DiggerConfigYaml, workingDir string,
 	projectNames := make(map[string]bool)
 	for _, project := range diggerConfig.Projects {
 		if projectNames[project.Name] {
-			return nil, fmt.Errorf("project name '%s' is duplicated", project.Name)
+			return nil, nil, fmt.Errorf("project name '%s' is duplicated", project.Name)
 		}
 		projectNames[project.Name] = true
 	}
 
+	// check project dependencies exist
+	for _, project := range diggerConfig.Projects {
+		for _, dependency := range project.DependencyProjects {
+			if !projectNames[dependency] {
+				return nil, nil, fmt.Errorf("project '%s' depends on '%s' which does not exist", project.Name, dependency)
+			}
+		}
+	}
+
 	if diggerYaml.GenerateProjectsConfig != nil {
 		dirs, err := walker.GetDirs(workingDir)
 		if err != nil {
-			return nil, err
+			return nil, nil, err
 		}
 
 		for _, dir := range dirs {
@@ -166,6 +177,11 @@ func ConvertDiggerYamlToConfig(diggerYaml *DiggerConfigYaml, workingDir string,
 		}
 	}
 
+	dependencyGraph, err := CreateProjectDependencyGraph(diggerConfig.Projects)
+	if err != nil {
+		return nil, nil, fmt.Errorf("failed to create project dependency graph: %s", err.Error())
+	}
+
 	// if one of the workflows is missing Plan or Apply we copy default values
 	for _, w := range diggerConfig.Workflows {
 		defaultWorkflow := *defaultWorkflow()
@@ -177,5 +193,36 @@ func ConvertDiggerYamlToConfig(diggerYaml *DiggerConfigYaml, workingDir string,
 		}
 	}
 
-	return &diggerConfig, nil
+	return &diggerConfig, dependencyGraph, nil
+}
+
+func CreateProjectDependencyGraph(projects []Project) (graph.Graph[string, string], error) {
+	g := graph.New(graph.StringHash, graph.Directed(), graph.PreventCycles())
+	for _, project := range projects {
+		v, _ := g.Vertex(project.Name)
+
+		if v == "" {
+			err := g.AddVertex(project.Name)
+			if err != nil {
+				return nil, err
+			}
+		}
+		for _, dependency := range project.DependencyProjects {
+			v, _ := g.Vertex(dependency)
+
+			if v == "" {
+				err := g.AddVertex(dependency)
+
+				if err != nil {
+					return nil, err
+				}
+			}
+
+			err := g.AddEdge(dependency, project.Name)
+			if err != nil {
+				return nil, err
+			}
+		}
+	}
+	return g, nil
 }
diff --git a/pkg/configuration/digger_config.go b/pkg/configuration/digger_config.go
index d0c98ab4..d6212fe1 100644
--- a/pkg/configuration/digger_config.go
+++ b/pkg/configuration/digger_config.go
@@ -4,6 +4,7 @@ import (
 	"digger/pkg/utils"
 	"errors"
 	"fmt"
+	"github.com/dominikbraun/graph"
 	"gopkg.in/yaml.v3"
 	"os"
 	"path"
@@ -61,50 +62,53 @@ func (walker *FileSystemDirWalker) GetDirs(workingDir string) ([]string, error)
 
 var ErrDiggerConfigConflict = errors.New("more than one digger config file detected, please keep either 'digger.yml' or 'digger.yaml'")
 
-func LoadDiggerConfig(workingDir string, walker DirWalker) (*DiggerConfig, error) {
+func LoadDiggerConfig(workingDir string, walker DirWalker) (*DiggerConfig, graph.Graph[string, string], error) {
 	configYaml := &DiggerConfigYaml{}
 	config := &DiggerConfig{}
 	fileName, err := retrieveConfigFile(workingDir)
 	if err != nil {
 		if errors.Is(err, ErrDiggerConfigConflict) {
-			return nil, fmt.Errorf("error while retrieving config file: %v", err)
+			return nil, nil, fmt.Errorf("error while retrieving config file: %v", err)
 		}
 	}
 
 	if fileName == "" {
 		fmt.Println("No digger config found, using default one")
 		config.Projects = make([]Project, 1)
-		config.Projects[0] = defaultProject()
+		project := defaultProject()
+		config.Projects[0] = project
 		config.Workflows = make(map[string]Workflow)
 		config.Workflows["default"] = *defaultWorkflow()
-		return config, nil
+		g := graph.New(graph.StringHash)
+		g.AddVertex(project.Name)
+		return config, g, nil
 	}
 
 	data, err := os.ReadFile(fileName)
 	if err != nil {
-		return nil, fmt.Errorf("failed to read config file %s: %v", fileName, err)
+		return nil, nil, fmt.Errorf("failed to read config file %s: %v", fileName, err)
 	}
 
 	if err := yaml.Unmarshal(data, configYaml); err != nil {
-		return nil, fmt.Errorf("error parsing '%s': %v", fileName, err)
+		return nil, nil, fmt.Errorf("error parsing '%s': %v", fileName, err)
 	}
 
 	if (configYaml.Projects == nil || len(configYaml.Projects) == 0) && configYaml.GenerateProjectsConfig == nil {
-		return nil, fmt.Errorf("no projects configuration found in '%s'", fileName)
+		return nil, nil, fmt.Errorf("no projects configuration found in '%s'", fileName)
 	}
 
-	c, err := ConvertDiggerYamlToConfig(configYaml, workingDir, walker)
+	config, projectDependencyGraph, err := ConvertDiggerYamlToConfig(configYaml, workingDir, walker)
 	if err != nil {
-		return nil, err
+		return nil, nil, err
 	}
 
-	for _, p := range c.Projects {
-		_, ok := c.Workflows[p.Workflow]
+	for _, p := range config.Projects {
+		_, ok := config.Workflows[p.Workflow]
 		if !ok {
-			return nil, fmt.Errorf("failed to find workflow config '%s' for project '%s'", p.Workflow, p.Name)
+			return nil, nil, fmt.Errorf("failed to find workflow config '%s' for project '%s'", p.Workflow, p.Name)
 		}
 	}
-	return c, nil
+	return config, projectDependencyGraph, nil
 }
 
 func defaultProject() Project {
diff --git a/pkg/configuration/yaml.go b/pkg/configuration/yaml.go
index c0b80b2b..2d26e161 100644
--- a/pkg/configuration/yaml.go
+++ b/pkg/configuration/yaml.go
@@ -14,13 +14,14 @@ type DiggerConfigYaml struct {
 }
 
 type ProjectYaml struct {
-	Name            string   `yaml:"name"`
-	Dir             string   `yaml:"dir"`
-	Workspace       string   `yaml:"workspace"`
-	Terragrunt      bool     `yaml:"terragrunt"`
-	Workflow        string   `yaml:"workflow"`
-	IncludePatterns []string `yaml:"include_patterns,omitempty"`
-	ExcludePatterns []string `yaml:"exclude_patterns,omitempty"`
+	Name               string   `yaml:"name"`
+	Dir                string   `yaml:"dir"`
+	Workspace          string   `yaml:"workspace"`
+	Terragrunt         bool     `yaml:"terragrunt"`
+	Workflow           string   `yaml:"workflow"`
+	IncludePatterns    []string `yaml:"include_patterns,omitempty"`
+	ExcludePatterns    []string `yaml:"exclude_patterns,omitempty"`
+	DependencyProjects []string `yaml:"depends_on,omitempty"`
 }
 
 type WorkflowYaml struct {
diff --git a/pkg/core/policy/policy.go b/pkg/core/policy/policy.go
index 6a991aaa..a5d5dadc 100644
--- a/pkg/core/policy/policy.go
+++ b/pkg/core/policy/policy.go
@@ -5,5 +5,5 @@ type Provider interface {
 }
 
 type Checker interface {
-	Check(organisation string, namespace string, projectname string, input interface{}) (bool, error)
+	Check(organisation string, namespace string, projectname string, command string, requestedBy string) (bool, error)
 }
diff --git a/pkg/digger/digger.go b/pkg/digger/digger.go
index a1026f1e..23adee0a 100644
--- a/pkg/digger/digger.go
+++ b/pkg/digger/digger.go
@@ -15,6 +15,7 @@ import (
 	"digger/pkg/usage"
 	"errors"
 	"fmt"
+	"github.com/dominikbraun/graph"
 	"log"
 	"os"
 	"path"
@@ -60,6 +61,7 @@ func DetectCI() CIName {
 
 func RunCommandsPerProject(
 	commandsPerProject []models.ProjectCommand,
+	dependencyGraph *graph.Graph[string, string],
 	projectNamespace string,
 	requestedBy string,
 	eventName string,
@@ -76,23 +78,14 @@ func RunCommandsPerProject(
 	plansToPublish := make([]string, 0)
 
 	organisation := strings.Split(projectNamespace, "/")[0]
-	teams, err := ciService.GetUserTeams(organisation, requestedBy)
-	if err != nil {
-		fmt.Printf("Error while fetching user teams for CI service: %v", err)
-	}
+
+	commandsPerProject = SortedCommandsByDependency(commandsPerProject, dependencyGraph)
 
 	for _, projectCommands := range commandsPerProject {
 		for _, command := range projectCommands.Commands {
+			fmt.Printf("Running '%s' for project '%s'\n", command, projectCommands.ProjectName)
 
-			policyInput := map[string]interface{}{
-				"user":         requestedBy,
-				"organisation": organisation,
-				"teams":        teams,
-				"action":       command,
-				"project":      projectCommands.ProjectName,
-			}
-
-			allowedToPerformCommand, err := policyChecker.Check(organisation, projectNamespace, projectCommands.ProjectName, policyInput)
+			allowedToPerformCommand, err := policyChecker.Check(organisation, projectNamespace, projectCommands.ProjectName, command, requestedBy)
 
 			if err != nil {
 				return false, false, fmt.Errorf("error checking policy: %v", err)
@@ -139,14 +132,24 @@ func RunCommandsPerProject(
 				projectLock,
 				planStorage,
 			}
+
 			switch command {
 			case "digger plan":
-				usage.SendUsageRecord(requestedBy, eventName, "plan")
-				ciService.SetStatus(prNumber, "pending", projectCommands.ProjectName+"/plan")
+				err := usage.SendUsageRecord(requestedBy, eventName, "plan")
+				if err != nil {
+					return false, false, fmt.Errorf("failed to send usage report. %v", err)
+				}
+				err = ciService.SetStatus(prNumber, "pending", projectCommands.ProjectName+"/plan")
+				if err != nil {
+					return false, false, fmt.Errorf("failed to set PR status. %v", err)
+				}
 				planPerformed, plan, err := diggerExecutor.Plan()
 				if err != nil {
 					log.Printf("Failed to run digger plan command. %v", err)
-					ciService.SetStatus(prNumber, "failure", projectCommands.ProjectName+"/plan")
+					err := ciService.SetStatus(prNumber, "failure", projectCommands.ProjectName+"/plan")
+					if err != nil {
+						return false, false, fmt.Errorf("failed to set PR status. %v", err)
+					}
 					return false, false, fmt.Errorf("failed to run digger plan command. %v", err)
 				} else if planPerformed {
 					if plan != "" {
@@ -160,45 +163,73 @@ func RunCommandsPerProject(
 							}
 						}
 					}
-					ciService.SetStatus(prNumber, "success", projectCommands.ProjectName+"/plan")
+					err := ciService.SetStatus(prNumber, "success", projectCommands.ProjectName+"/plan")
+					if err != nil {
+						return false, false, fmt.Errorf("failed to set PR status. %v", err)
+					}
 				}
 			case "digger apply":
 				appliesPerProject[projectCommands.ProjectName] = false
-				usage.SendUsageRecord(requestedBy, eventName, "apply")
-				ciService.SetStatus(prNumber, "pending", projectCommands.ProjectName+"/apply")
+				err := usage.SendUsageRecord(requestedBy, eventName, "apply")
+				if err != nil {
+					return false, false, fmt.Errorf("failed to send usage report. %v", err)
+				}
+				err = ciService.SetStatus(prNumber, "pending", projectCommands.ProjectName+"/apply")
+				if err != nil {
+					return false, false, fmt.Errorf("failed to set PR status. %v", err)
+				}
+
+				isMerged, err := ciService.IsMerged(prNumber)
+				if err != nil {
+					return false, false, fmt.Errorf("error checking if PR is merged: %v", err)
+				}
 
 				// this might go into some sort of "appliability" plugin later
 				isMergeable, err := ciService.IsMergeable(prNumber)
 				if err != nil {
 					return false, false, fmt.Errorf("error validating is PR is mergeable: %v", err)
 				}
-				if !isMergeable {
+				fmt.Printf("PR status, mergeable: %v, merged: %v\n", isMergeable, isMerged)
+				if !isMergeable && !isMerged {
 					comment := "Cannot perform Apply since the PR is not currently mergeable."
+					fmt.Println(comment)
 					err = ciService.PublishComment(prNumber, comment)
 					if err != nil {
-						fmt.Printf("error publishing comment: %v", err)
+						fmt.Printf("error publishing comment: %v\n", err)
 					}
 					return false, false, nil
 				} else {
 					applyPerformed, err := diggerExecutor.Apply()
 					if err != nil {
 						log.Printf("Failed to run digger apply command. %v", err)
-						ciService.SetStatus(prNumber, "failure", projectCommands.ProjectName+"/apply")
+						err := ciService.SetStatus(prNumber, "failure", projectCommands.ProjectName+"/apply")
+						if err != nil {
+							return false, false, fmt.Errorf("failed to set PR status. %v", err)
+						}
 						return false, false, fmt.Errorf("failed to run digger apply command. %v", err)
 					} else if applyPerformed {
-						ciService.SetStatus(prNumber, "success", projectCommands.ProjectName+"/apply")
+						err := ciService.SetStatus(prNumber, "success", projectCommands.ProjectName+"/apply")
+						if err != nil {
+							return false, false, fmt.Errorf("failed to set PR status. %v", err)
+						}
 						appliesPerProject[projectCommands.ProjectName] = true
 					}
 				}
 			case "digger unlock":
-				usage.SendUsageRecord(requestedBy, eventName, "unlock")
-				err := diggerExecutor.Unlock()
+				err := usage.SendUsageRecord(requestedBy, eventName, "unlock")
+				if err != nil {
+					return false, false, fmt.Errorf("failed to send usage report. %v", err)
+				}
+				err = diggerExecutor.Unlock()
 				if err != nil {
 					return false, false, fmt.Errorf("failed to unlock project. %v", err)
 				}
 			case "digger lock":
-				usage.SendUsageRecord(requestedBy, eventName, "lock")
-				err := diggerExecutor.Lock()
+				err := usage.SendUsageRecord(requestedBy, eventName, "lock")
+				if err != nil {
+					return false, false, fmt.Errorf("failed to send usage report. %v", err)
+				}
+				err = diggerExecutor.Lock()
 				if err != nil {
 					return false, false, fmt.Errorf("failed to lock project. %v", err)
 				}
@@ -225,6 +256,24 @@ func RunCommandsPerProject(
 	return allAppliesSuccess, atLeastOneApply, nil
 }
 
+func SortedCommandsByDependency(project []models.ProjectCommand, dependencyGraph *graph.Graph[string, string]) []models.ProjectCommand {
+	var sortedCommands []models.ProjectCommand
+	sortedGraph, err := graph.StableTopologicalSort(*dependencyGraph, func(s string, s2 string) bool {
+		return s < s2
+	})
+	if err != nil {
+		log.Fatalf("failed to sort commands by dependency, %v", err)
+	}
+	for _, node := range sortedGraph {
+		for _, command := range project {
+			if command.ProjectName == node {
+				sortedCommands = append(sortedCommands, command)
+			}
+		}
+	}
+	return sortedCommands
+}
+
 func MergePullRequest(ciService ci.CIService, prNumber int) {
 	time.Sleep(5 * time.Second)
 	combinedStatus, err := ciService.GetCombinedPullRequestStatus(prNumber)
diff --git a/pkg/github/github.go b/pkg/github/github.go
index c7f85202..a72e3948 100644
--- a/pkg/github/github.go
+++ b/pkg/github/github.go
@@ -128,15 +128,26 @@ func (svc *GithubService) IsMergeable(prNumber int) (bool, error) {
 	pr, _, err := svc.Client.PullRequests.Get(context.Background(), svc.Owner, svc.RepoName, prNumber)
 	if err != nil {
 		log.Fatalf("error getting pull request: %v", err)
+		return false, err
 	}
 
 	return pr.GetMergeable() && isMergeableState(pr.GetMergeableState()), nil
 }
 
+func (svc *GithubService) IsMerged(prNumber int) (bool, error) {
+	pr, _, err := svc.Client.PullRequests.Get(context.Background(), svc.Owner, svc.RepoName, prNumber)
+	if err != nil {
+		log.Fatalf("error getting pull request: %v", err)
+		return false, err
+	}
+	return *pr.Merged, nil
+}
+
 func (svc *GithubService) IsClosed(prNumber int) (bool, error) {
 	pr, _, err := svc.Client.PullRequests.Get(context.Background(), svc.Owner, svc.RepoName, prNumber)
 	if err != nil {
 		log.Fatalf("error getting pull request: %v", err)
+		return false, err
 	}
 
 	return pr.GetState() == "closed", nil
diff --git a/pkg/gitlab/gitlab.go b/pkg/gitlab/gitlab.go
index ca48ff8e..d92547de 100644
--- a/pkg/gitlab/gitlab.go
+++ b/pkg/gitlab/gitlab.go
@@ -213,23 +213,32 @@ func (gitlabService GitLabService) IsMergeable(mergeRequestID int) (bool, error)
 }
 
 func (gitlabService GitLabService) IsClosed(mergeRequestID int) (bool, error) {
+	mergeRequest := getMergeRequest(gitlabService)
+	if mergeRequest.State == "closed" {
+		return true, nil
+	}
+	return false, nil
+}
+
+func (gitlabService GitLabService) IsMerged(mergeRequestID int) (bool, error) {
+	mergeRequest := getMergeRequest(gitlabService)
+	if mergeRequest.State == "merged" {
+		return true, nil
+	}
+	return false, nil
+}
+
+func getMergeRequest(gitlabService GitLabService) *go_gitlab.MergeRequest {
 	projectId := *gitlabService.Context.ProjectId
 	mergeRequestIID := *gitlabService.Context.MergeRequestIId
-
-	fmt.Printf("IsClosed mergeRequestIID : %d, projectId: %d \n", mergeRequestIID, projectId)
+	fmt.Printf("getMergeRequest mergeRequestIID : %d, projectId: %d \n", mergeRequestIID, projectId)
 	opt := &go_gitlab.GetMergeRequestsOptions{}
-
 	mergeRequest, _, err := gitlabService.Client.MergeRequests.GetMergeRequest(projectId, mergeRequestIID, opt)
-
 	if err != nil {
 		fmt.Printf("Failed to get a MergeRequest: %d, %v \n", mergeRequestIID, err)
 		print(err.Error())
 	}
-
-	if mergeRequest.State == "closed" || mergeRequest.State == "merged" {
-		return true, nil
-	}
-	return false, nil
+	return mergeRequest
 }
 
 type GitLabEvent struct {
diff --git a/pkg/policy/policy.go b/pkg/policy/policy.go
index 06ccc734..26b5fb23 100644
--- a/pkg/policy/policy.go
+++ b/pkg/policy/policy.go
@@ -2,6 +2,7 @@ package policy
 
 import (
 	"context"
+	"digger/pkg/ci"
 	"errors"
 	"fmt"
 	"github.com/open-policy-agent/opa/rego"
@@ -23,7 +24,7 @@ type DiggerHttpPolicyProvider struct {
 type NoOpPolicyChecker struct {
 }
 
-func (p NoOpPolicyChecker) Check(_ string, _ string, _ string, _ interface{}) (bool, error) {
+func (p NoOpPolicyChecker) Check(_ string, _ string, _ string, _ string, _ string) (bool, error) {
 	return true, nil
 }
 
@@ -73,23 +74,25 @@ func (p *DiggerHttpPolicyProvider) getPolicyForNamespace(namespace string, proje
 
 }
 
-// GetPolicy fetches policy for entire organisation, if not found for organisation then it will fetch it for project
+// GetPolicy fetches policy for particular project,  if not found then it will fallback to org level policy
 func (p *DiggerHttpPolicyProvider) GetPolicy(organisation string, namespace string, projectName string) (string, error) {
-	content, resp, err := p.getPolicyForOrganisation(organisation)
+	content, resp, err := p.getPolicyForNamespace(namespace, projectName)
 	if err != nil {
 		return "", err
 	}
 	if resp.StatusCode == 200 {
 		return content, nil
 	} else if resp.StatusCode == 404 {
-		content, resp, err := p.getPolicyForNamespace(namespace, projectName)
+		content, resp, err := p.getPolicyForOrganisation(organisation)
 		if err != nil {
 			return "", err
 		}
 		if resp.StatusCode == 200 {
 			return content, nil
+		} else if resp.StatusCode == 404 {
+			return "", nil
 		} else {
-			return "", errors.New(fmt.Sprintf("unexpected response while fetching namespace policy: %v, code %v", content, resp.StatusCode))
+			return "", errors.New(fmt.Sprintf("unexpected response while fetching organisation policy: %v, code %v", content, resp.StatusCode))
 		}
 	} else {
 		return "", errors.New(fmt.Sprintf("unexpected response while fetching org policy: %v code %v", content, resp.StatusCode))
@@ -98,14 +101,30 @@ func (p *DiggerHttpPolicyProvider) GetPolicy(organisation string, namespace stri
 
 type DiggerPolicyChecker struct {
 	PolicyProvider PolicyProvider
+	ciService      ci.CIService
 }
 
-func (p DiggerPolicyChecker) Check(organisation string, namespace string, projectName string, input interface{}) (bool, error) {
+func (p DiggerPolicyChecker) Check(organisation string, namespace string, projectName string, command string, requestedBy string) (bool, error) {
 	policy, err := p.PolicyProvider.GetPolicy(organisation, namespace, projectName)
 
+	teams, err := p.ciService.GetUserTeams(organisation, requestedBy)
 	if err != nil {
+		fmt.Printf("Error while fetching user teams for CI service: %v", err)
 		return false, err
 	}
+
+	input := map[string]interface{}{
+		"user":         requestedBy,
+		"organisation": organisation,
+		"teams":        teams,
+		"action":       command,
+		"project":      projectName,
+	}
+
+	if policy == "" {
+		return true, nil
+	}
+
 	ctx := context.Background()
 	fmt.Printf("DEBUG: passing the following input policy: %v ||| text: %v", input, policy)
 	query, err := rego.New(
diff --git a/pkg/usage/usage.go b/pkg/usage/usage.go
index c6d4c105..b48c6348 100644
--- a/pkg/usage/usage.go
+++ b/pkg/usage/usage.go
@@ -77,7 +77,7 @@ func init() {
 		fmt.Printf("Failed to get current dir. %s", err)
 	}
 	walker := configuration.FileSystemDirWalker{}
-	config, err := configuration.LoadDiggerConfig(currentDir, &walker)
+	config, _, err := configuration.LoadDiggerConfig(currentDir, &walker)
 	if err != nil {
 		return
 	}
diff --git a/pkg/utils/mocks.go b/pkg/utils/mocks.go
index 077e4f79..50366fd7 100644
--- a/pkg/utils/mocks.go
+++ b/pkg/utils/mocks.go
@@ -42,16 +42,17 @@ func (lock *MockLock) GetLock(resource string) (*int, error) {
 type MockPolicyChecker struct {
 }
 
-func (t MockPolicyChecker) Check(organisation string, namespace string, projectname string, input interface{}) (bool, error) {
+func (t MockPolicyChecker) Check(organisation string, namespace string, projectname string, command string, requestedBy string) (bool, error) {
 	return false, nil
 }
 
 type MockPullRequestManager struct {
 	ChangedFiles []string
+	Teams        []string
 }
 
 func (t MockPullRequestManager) GetUserTeams(organisation string, user string) ([]string, error) {
-	return []string{}, nil
+	return t.Teams, nil
 }
 
 func (t MockPullRequestManager) GetChangedFiles(prNumber int) ([]string, error) {
@@ -77,6 +78,10 @@ func (t MockPullRequestManager) IsMergeable(prNumber int) (bool, error) {
 	return true, nil
 }
 
+func (t MockPullRequestManager) IsMerged(prNumber int) (bool, error) {
+	return false, nil
+}
+
 func (t MockPullRequestManager) DownloadLatestPlans(prNumber int) (string, error) {
 	return "", nil
 }
diff --git a/pkg/utils/pullrequestmanager_mock.go b/pkg/utils/pullrequestmanager_mock.go
index 60cabdd4..895c9e3c 100644
--- a/pkg/utils/pullrequestmanager_mock.go
+++ b/pkg/utils/pullrequestmanager_mock.go
@@ -47,3 +47,8 @@ func (mockGithubPullrequestManager *MockGithubPullrequestManager) IsClosed(prNum
 	mockGithubPullrequestManager.commands = append(mockGithubPullrequestManager.commands, "IsClosed")
 	return false, nil
 }
+
+func (mockGithubPullrequestManager *MockGithubPullrequestManager) IsMerged(prNumber int) (bool, error) {
+	mockGithubPullrequestManager.commands = append(mockGithubPullrequestManager.commands, "IsClosed")
+	return false, nil
+}


Test patch:
diff --git a/cmd/digger/main_test.go b/cmd/digger/main_test.go
index f663f61c..13b13fa5 100644
--- a/cmd/digger/main_test.go
+++ b/cmd/digger/main_test.go
@@ -9,6 +9,7 @@ import (
 	"digger/pkg/reporting"
 	"digger/pkg/utils"
 	"fmt"
+	"github.com/dominikbraun/graph"
 	"testing"
 
 	"github.com/davecgh/go-spew/spew"
@@ -875,6 +876,7 @@ func TestGitHubNewPullRequestContext(t *testing.T) {
 	eventName := context.EventName
 
 	diggerConfig := configuration.DiggerConfig{}
+	depGraph := graph.New(graph.StringHash, graph.Directed())
 	lock := &utils.MockLock{}
 	prManager := &utils.MockPullRequestManager{ChangedFiles: []string{"dev/test.tf"}}
 	planStorage := &utils.MockPlanStorage{}
@@ -887,7 +889,7 @@ func TestGitHubNewPullRequestContext(t *testing.T) {
 		PrNumber:  prNumber,
 	}
 	commandsToRunPerProject, _, err := github.ConvertGithubEventToCommands(ghEvent, impactedProjects, requestedProject, map[string]configuration.Workflow{})
-	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, context.RepositoryOwner, context.Repository, eventName, prNumber, prManager, lock, reporter, planStorage, policyChecker, "")
+	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, &depGraph, context.RepositoryOwner, context.Repository, eventName, prNumber, prManager, lock, reporter, planStorage, policyChecker, "")
 
 	assert.NoError(t, err)
 	if err != nil {
@@ -904,6 +906,7 @@ func TestGitHubNewCommentContext(t *testing.T) {
 	ghEvent := context.Event
 	eventName := context.EventName
 	diggerConfig := configuration.DiggerConfig{}
+	depGraph := graph.New(graph.StringHash, graph.Directed())
 	lock := &utils.MockLock{}
 	prManager := &utils.MockPullRequestManager{ChangedFiles: []string{"dev/test.tf"}}
 	planStorage := &utils.MockPlanStorage{}
@@ -916,9 +919,9 @@ func TestGitHubNewCommentContext(t *testing.T) {
 	policyChecker := &utils.MockPolicyChecker{}
 
 	commandsToRunPerProject, _, err := github.ConvertGithubEventToCommands(ghEvent, impactedProjects, requestedProject, map[string]configuration.Workflow{})
-	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, context.RepositoryOwner, context.Repository, eventName, prNumber, prManager, lock, reporter, planStorage, policyChecker, "")
+	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, &depGraph, context.RepositoryOwner, context.Repository, eventName, prNumber, prManager, lock, reporter, planStorage, policyChecker, "")
 
-	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, context.RepositoryOwner, context.Repository, eventName, prNumber, prManager, lock, reporter, planStorage, policyChecker, "")
+	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, &depGraph, context.RepositoryOwner, context.Repository, eventName, prNumber, prManager, lock, reporter, planStorage, policyChecker, "")
 
 	assert.NoError(t, err)
 	if err != nil {
diff --git a/pkg/configuration/digger_config_test.go b/pkg/configuration/digger_config_test.go
index 8193b108..0496a7a8 100644
--- a/pkg/configuration/digger_config_test.go
+++ b/pkg/configuration/digger_config_test.go
@@ -2,6 +2,7 @@ package configuration
 
 import (
 	"digger/pkg/core/models"
+	"github.com/dominikbraun/graph"
 	"log"
 	"os"
 	"path"
@@ -27,7 +28,7 @@ func setUp() (string, func()) {
 }
 
 func TestDiggerConfigFileDoesNotExist(t *testing.T) {
-	dg, err := LoadDiggerConfig("", &FileSystemDirWalker{})
+	dg, _, err := LoadDiggerConfig("", &FileSystemDirWalker{})
 	assert.NoError(t, err, "expected error to be not nil")
 	assert.Equal(t, dg.Projects[0].Name, "default", "expected default project to have name 'default'")
 	assert.Equal(t, dg.Projects[0].Dir, ".", "expected default project dir to be '.'")
@@ -47,7 +48,7 @@ func TestDiggerConfigWhenMultipleConfigExist(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	dg, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
+	dg, _, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
 	assert.Error(t, err, "expected error to be returned")
 	assert.ErrorContains(t, err, ErrDiggerConfigConflict.Error(), "expected error to match target error")
 	assert.Nil(t, dg, "expected diggerConfig to be nil")
@@ -67,7 +68,7 @@ projects:
 	deleteFile := createFile(path.Join(tempDir, "digger.yaml"), diggerCfg)
 	defer deleteFile()
 
-	dg, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
+	dg, _, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
 	assert.NoError(t, err, "expected error to be nil")
 	assert.NotNil(t, dg, "expected digger config to be not nil")
 	assert.Equal(t, "path/to/module/test", dg.GetDirectory("prod"))
@@ -87,7 +88,7 @@ projects:
 	deleteFile := createFile(path.Join(tempDir, "digger.yaml"), diggerCfg)
 	defer deleteFile()
 
-	dg, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
+	dg, _, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
 
 	assert.NoError(t, err, "expected error to be nil")
 	assert.NotNil(t, dg, "expected digger config to be not nil")
@@ -122,7 +123,7 @@ projects:
 	deleteFile := createFile(path.Join(tempDir, "digger.yaml"), diggerCfg)
 	defer deleteFile()
 
-	dg, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
+	dg, _, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
 	assert.NoError(t, err, "expected error to be nil")
 	assert.NotNil(t, dg, "expected digger config to be not nil")
 	assert.Equal(t, "default", dg.Projects[0].Workflow)
@@ -144,7 +145,7 @@ projects:
 	deleteFile := createFile(path.Join(tempDir, "digger.yml"), diggerCfg)
 	defer deleteFile()
 
-	dg, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
+	dg, _, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
 	assert.NoError(t, err, "expected error to be nil")
 	assert.NotNil(t, dg, "expected digger config to be not nil")
 	assert.Equal(t, "path/to/module", dg.GetDirectory("dev"))
@@ -169,7 +170,7 @@ workflows:
 	deleteFile := createFile(path.Join(tempDir, "digger.yaml"), diggerCfg)
 	defer deleteFile()
 
-	dg, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
+	dg, _, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
 	assert.NoError(t, err, "expected error to be nil")
 	assert.Equal(t, models.Step{Action: "run", Value: "echo \"hello\"", Shell: ""}, dg.Workflows["myworkflow"].Plan.Steps[0], "parsed struct does not match expected struct")
 }
@@ -214,7 +215,7 @@ workflows:
 	deleteFile := createFile(path.Join(tempDir, "digger.yaml"), diggerCfg)
 	defer deleteFile()
 
-	dg, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
+	dg, _, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
 	assert.NoError(t, err, "expected error to be nil")
 	assert.Equal(t, []EnvVar{
 		{Name: "TF_VAR_state", Value: "s3://mybucket/terraform.tfstate"},
@@ -254,7 +255,7 @@ workflows:
 	deleteFile := createFile(path.Join(tempDir, "digger.yaml"), diggerCfg)
 	defer deleteFile()
 
-	dg, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
+	dg, _, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
 	assert.NoError(t, err, "expected error to be nil")
 	assert.Equal(t, models.Step{Action: "run", Value: "rm -rf .terraform", Shell: ""}, dg.Workflows["dev"].Plan.Steps[0], "parsed struct does not match expected struct")
 	assert.Equal(t, models.Step{Action: "init", ExtraArgs: nil, Shell: ""}, dg.Workflows["dev"].Plan.Steps[1], "parsed struct does not match expected struct")
@@ -284,7 +285,7 @@ generate_projects:
 	walker.Files = append(walker.Files, "dev/project")
 	walker.Files = append(walker.Files, "testtt")
 
-	dg, err := LoadDiggerConfig(tempDir, walker)
+	dg, _, err := LoadDiggerConfig(tempDir, walker)
 	assert.NoError(t, err, "expected error to be nil")
 	assert.NotNil(t, dg, "expected digger config to be not nil")
 	assert.Equal(t, "test1", dg.Projects[0].Name)
@@ -314,7 +315,7 @@ generate_projects:
 	walker.Files = append(walker.Files, "dev/project")
 	walker.Files = append(walker.Files, "testtt")
 
-	dg, err := LoadDiggerConfig(tempDir, walker)
+	dg, _, err := LoadDiggerConfig(tempDir, walker)
 	assert.NoError(t, err, "expected error to be nil")
 	assert.NotNil(t, dg, "expected digger config to be not nil")
 	assert.Equal(t, "test1", dg.Projects[0].Name)
@@ -343,7 +344,7 @@ generate_projects:
 	walker.Files = append(walker.Files, "dev/project")
 	walker.Files = append(walker.Files, "testtt")
 
-	dg, err := LoadDiggerConfig(tempDir, walker)
+	dg, _, err := LoadDiggerConfig(tempDir, walker)
 	assert.NoError(t, err, "expected error to be nil")
 	assert.NotNil(t, dg, "expected digger config to be not nil")
 	assert.Equal(t, "dev", dg.Projects[0].Name)
@@ -360,7 +361,7 @@ func TestMissingProjectsReturnsError(t *testing.T) {
 	defer deleteFile()
 	walker := &MockDirWalker{}
 
-	_, err := LoadDiggerConfig(tempDir, walker)
+	_, _, err := LoadDiggerConfig(tempDir, walker)
 	assert.ErrorContains(t, err, "no projects configuration found")
 }
 
@@ -383,7 +384,7 @@ workflows:
 	deleteFile := createFile(path.Join(tempDir, "digger.yaml"), diggerCfg)
 	defer deleteFile()
 
-	dg, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
+	dg, _, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
 	assert.NoError(t, err, "expected error to be nil")
 	assert.NotNil(t, dg, "expected digger config to be not nil")
 	assert.Equal(t, "my_custom_workflow", dg.Projects[0].Workflow)
@@ -405,7 +406,7 @@ projects:
 	deleteFile := createFile(path.Join(tempDir, "digger.yaml"), diggerCfg)
 	defer deleteFile()
 
-	_, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
+	_, _, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
 	assert.Error(t, err, "failed to find workflow config 'my_custom_workflow' for project 'my-first-app'")
 
 	// steps block is missing for workflows
@@ -420,7 +421,7 @@ workflows:
 	deleteFile = createFile(path.Join(tempDir, "digger.yaml"), diggerCfg)
 	defer deleteFile()
 
-	diggerConfig, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
+	diggerConfig, _, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
 	assert.Equal(t, "my_custom_workflow", diggerConfig.Projects[0].Workflow)
 	workflow, ok := diggerConfig.Workflows["my_custom_workflow"]
 	assert.True(t, ok)
@@ -449,11 +450,119 @@ workflows:
 	deleteFile := createFile(path.Join(tempDir, "digger.yaml"), diggerCfg)
 	defer deleteFile()
 
-	_, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
+	_, _, err := LoadDiggerConfig(tempDir, &FileSystemDirWalker{})
 	assert.Equal(t, "failed to find workflow config 'my_custom_workflow' for project 'my-first-app'", err.Error())
 
 }
 
+func TestDiggerConfigDependencyGraph(t *testing.T) {
+	p1 := Project{
+		Name:               "A",
+		DependencyProjects: []string{"B", "C"},
+	}
+
+	p2 := Project{
+		Name:               "B",
+		DependencyProjects: []string{"C"},
+	}
+
+	p3 := Project{
+		Name: "C",
+	}
+
+	p4 := Project{
+		Name: "D",
+	}
+
+	p5 := Project{
+		Name:               "E",
+		DependencyProjects: []string{"A"},
+	}
+
+	p6 := Project{
+		Name:               "F",
+		DependencyProjects: []string{"A", "B"},
+	}
+
+	projects := []Project{p1, p2, p3, p4, p5, p6}
+
+	g, err := CreateProjectDependencyGraph(projects)
+
+	assert.NoError(t, err, "expected error to be nil")
+
+	orderedProjects, _ := graph.StableTopologicalSort(g, func(s string, s2 string) bool {
+		return s < s2
+	})
+
+	assert.Equal(t, 6, len(orderedProjects))
+	assert.Equal(t, []string{"C", "D", "B", "A", "E", "F"}, orderedProjects)
+}
+
+func TestDiggerConfigDependencyGraph2(t *testing.T) {
+	p1 := Project{
+		Name:               "A",
+		DependencyProjects: []string{"B", "C", "D"},
+	}
+
+	p2 := Project{
+		Name:               "B",
+		DependencyProjects: []string{"E", "F"},
+	}
+
+	p3 := Project{
+		Name: "C",
+		DependencyProjects: []string{
+			"G",
+		},
+	}
+
+	p4 := Project{
+		Name: "D",
+		DependencyProjects: []string{
+			"H", "I",
+		},
+	}
+
+	projects := []Project{p1, p2, p3, p4}
+
+	g, err := CreateProjectDependencyGraph(projects)
+
+	assert.NoError(t, err, "expected error to be nil")
+
+	orderedProjects, _ := graph.StableTopologicalSort(g, func(s string, s2 string) bool {
+		return s > s2
+	})
+
+	assert.Equal(t, 9, len(orderedProjects))
+	assert.Equal(t, []string{"I", "H", "G", "F", "E", "D", "C", "B", "A"}, orderedProjects)
+}
+
+func TestDiggerConfigDependencyGraphWithCyclesFails(t *testing.T) {
+	p1 := Project{
+		Name:               "A",
+		DependencyProjects: []string{"B"},
+	}
+
+	p2 := Project{
+		Name:               "B",
+		DependencyProjects: []string{"C"},
+	}
+
+	p3 := Project{
+		Name: "C",
+		DependencyProjects: []string{
+			"A",
+		},
+	}
+
+	projects := []Project{p1, p2, p3}
+
+	_, err := CreateProjectDependencyGraph(projects)
+
+	assert.Error(t, err, "expected error on cycle")
+	assert.Equal(t, "edge would create a cycle", err.Error())
+}
+
 func createTempDir() string {
 	dir, err := os.MkdirTemp("", "tmp")
 	if err != nil {
diff --git a/pkg/digger/digger_test.go b/pkg/digger/digger_test.go
index 397a921f..b04f958b 100644
--- a/pkg/digger/digger_test.go
+++ b/pkg/digger/digger_test.go
@@ -5,6 +5,7 @@ import (
 	"digger/pkg/core/models"
 	"digger/pkg/reporting"
 	"digger/pkg/utils"
+	"github.com/dominikbraun/graph"
 	"sort"
 	"strconv"
 	"strings"
@@ -94,6 +95,11 @@ func (m *MockPRManager) DownloadLatestPlans(prNumber int) (string, error) {
 	return "plan", nil
 }
 
+func (m *MockPRManager) IsMerged(prNumber int) (bool, error) {
+	m.Commands = append(m.Commands, RunInfo{"IsClosed", strconv.Itoa(prNumber), time.Now()})
+	return false, nil
+}
+
 func (m *MockPRManager) IsClosed(prNumber int) (bool, error) {
 	m.Commands = append(m.Commands, RunInfo{"IsClosed", strconv.Itoa(prNumber), time.Now()})
 	return false, nil
@@ -278,6 +284,57 @@ func allCommandsInOrderWithParams(terraformExecutor *MockTerraformExecutor, comm
 	return commandStrings
 }
 
+func TestSortedCommandByDependency(t *testing.T) {
+	//	commandsPerProject []models.ProjectCommand,
+	//	dependencyGraph *graph.Graph[string, string],
+
+	commandsPerProject := []models.ProjectCommand{
+		{
+			ProjectName: "project1",
+			Commands: []string{
+				"command1", "command2",
+			},
+		},
+		{
+			ProjectName: "project2",
+			Commands: []string{
+				"command3", "command4",
+			},
+		},
+		{
+			ProjectName: "project3",
+			Commands: []string{
+				"command5",
+			},
+		},
+		{
+			ProjectName: "project4",
+			Commands: []string{
+				"command6",
+			},
+		},
+	}
+
+	dependencyGraph := graph.New(graph.StringHash, graph.PreventCycles(), graph.Directed())
+
+	dependencyGraph.AddVertex("project1")
+	dependencyGraph.AddVertex("project2")
+	dependencyGraph.AddVertex("project3")
+	dependencyGraph.AddVertex("project4")
+
+	dependencyGraph.AddEdge("project2", "project1")
+	dependencyGraph.AddEdge("project3", "project2")
+	dependencyGraph.AddEdge("project4", "project1")
+
+	sortedCommands := SortedCommandsByDependency(commandsPerProject, &dependencyGraph)
+
+	assert.Equal(t, "project3", sortedCommands[0].ProjectName)
+	assert.Equal(t, "project4", sortedCommands[1].ProjectName)
+	assert.Equal(t, "project2", sortedCommands[2].ProjectName)
+	assert.Equal(t, "project1", sortedCommands[3].ProjectName)
+
+}
+
 func TestParseWorkspace(t *testing.T) {
 	var commentTests = []struct {
 		in  string
diff --git a/pkg/integration/integration_test.go b/pkg/integration/integration_test.go
index 89b2d379..622ddd1c 100644
--- a/pkg/integration/integration_test.go
+++ b/pkg/integration/integration_test.go
@@ -352,7 +352,7 @@ func TestHappyPath(t *testing.T) {
 	terraform.CreateValidTerraformTestFile(dir)
 	terraform.CreateSingleEnvDiggerYmlFile(dir)
 
-	diggerConfig, err := configuration.LoadDiggerConfig(dir, &configuration.FileSystemDirWalker{})
+	diggerConfig, depGraph, err := configuration.LoadDiggerConfig(dir, &configuration.FileSystemDirWalker{})
 	assert.NoError(t, err)
 
 	lock, err := locking.GetLock()
@@ -408,7 +408,7 @@ func TestHappyPath(t *testing.T) {
 
 	diggerProjectNamespace := repoOwner + "/" + repositoryName
 
-	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, lock, reporter, planStorage, nil, dir)
+	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, &depGraph, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, lock, reporter, planStorage, nil, dir)
 	assert.NoError(t, err)
 
 	projectLock := &locking.PullRequestLock{
@@ -434,7 +434,7 @@ func TestHappyPath(t *testing.T) {
 	assert.NoError(t, err)
 	commandsToRunPerProject, _, err = dg_github.ConvertGithubEventToCommands(ghEvent, impactedProjects, requestedProject, diggerConfig.Workflows)
 	assert.NoError(t, err)
-	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, lock, reporter, planStorage, nil, dir)
+	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, &depGraph, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, lock, reporter, planStorage, nil, dir)
 	assert.NoError(t, err)
 
 	println("--- digger apply comment ---")
@@ -448,7 +448,7 @@ func TestHappyPath(t *testing.T) {
 	assert.NoError(t, err)
 	commandsToRunPerProject, _, err = dg_github.ConvertGithubEventToCommands(ghEvent, impactedProjects, requestedProject, diggerConfig.Workflows)
 	assert.NoError(t, err)
-	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, lock, reporter, planStorage, nil, dir)
+	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, &depGraph, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, lock, reporter, planStorage, nil, dir)
 	assert.NoError(t, err)
 
 	projectLock = &locking.PullRequestLock{
@@ -471,7 +471,7 @@ func TestHappyPath(t *testing.T) {
 	assert.NoError(t, err)
 	commandsToRunPerProject, _, err = dg_github.ConvertGithubEventToCommands(ghEvent, impactedProjects, requestedProject, diggerConfig.Workflows)
 	assert.NoError(t, err)
-	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, lock, reporter, planStorage, nil, dir)
+	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, &depGraph, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, lock, reporter, planStorage, nil, dir)
 	assert.NoError(t, err)
 
 	projectLock = &locking.PullRequestLock{
@@ -501,7 +501,7 @@ func TestMultiEnvHappyPath(t *testing.T) {
 	terraform.CreateValidTerraformTestFile(dir)
 	terraform.CreateMultiEnvDiggerYmlFile(dir)
 
-	diggerConfig, err := configuration.LoadDiggerConfig(dir, &configuration.FileSystemDirWalker{})
+	diggerConfig, depGraph, err := configuration.LoadDiggerConfig(dir, &configuration.FileSystemDirWalker{})
 	assert.NoError(t, err)
 
 	sess, err := session.NewSessionWithOptions(session.Options{
@@ -563,7 +563,7 @@ func TestMultiEnvHappyPath(t *testing.T) {
 		CiService: githubPrService,
 		PrNumber:  prNumber,
 	}
-	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, &dynamoDbLock, reporter, planStorage, nil, dir)
+	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, &depGraph, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, &dynamoDbLock, reporter, planStorage, nil, dir)
 	assert.NoError(t, err)
 
 	projectLock := &locking.PullRequestLock{
@@ -588,7 +588,7 @@ func TestMultiEnvHappyPath(t *testing.T) {
 	assert.NoError(t, err)
 	commandsToRunPerProject, _, err = dg_github.ConvertGithubEventToCommands(ghEvent, impactedProjects, requestedProject, diggerConfig.Workflows)
 	assert.NoError(t, err)
-	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, &dynamoDbLock, reporter, planStorage, nil, dir)
+	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, &depGraph, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, &dynamoDbLock, reporter, planStorage, nil, dir)
 	assert.NoError(t, err)
 
 	println("--- digger apply comment ---")
@@ -602,7 +602,7 @@ func TestMultiEnvHappyPath(t *testing.T) {
 	assert.NoError(t, err)
 	commandsToRunPerProject, _, err = dg_github.ConvertGithubEventToCommands(ghEvent, impactedProjects, requestedProject, diggerConfig.Workflows)
 	assert.NoError(t, err)
-	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, &dynamoDbLock, reporter, planStorage, nil, dir)
+	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, &depGraph, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, &dynamoDbLock, reporter, planStorage, nil, dir)
 	assert.NoError(t, err)
 
 	projectLock = &locking.PullRequestLock{
@@ -625,7 +625,7 @@ func TestMultiEnvHappyPath(t *testing.T) {
 	assert.NoError(t, err)
 	commandsToRunPerProject, _, err = dg_github.ConvertGithubEventToCommands(ghEvent, impactedProjects, requestedProject, diggerConfig.Workflows)
 	assert.NoError(t, err)
-	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, &dynamoDbLock, reporter, planStorage, nil, dir)
+	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, &depGraph, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, &dynamoDbLock, reporter, planStorage, nil, dir)
 	assert.NoError(t, err)
 
 	projectLock = &locking.PullRequestLock{
@@ -721,7 +721,7 @@ workflows:
 	terraform.CreateValidTerraformTestFile(dir)
 	terraform.CreateCustomDiggerYmlFile(dir, diggerCfg)
 
-	diggerConfig, err := configuration.LoadDiggerConfig(dir, &configuration.FileSystemDirWalker{})
+	diggerConfig, depGraph, err := configuration.LoadDiggerConfig(dir, &configuration.FileSystemDirWalker{})
 	assert.NoError(t, err)
 
 	assert.NotNil(t, diggerConfig.Workflows)
@@ -782,7 +782,7 @@ workflows:
 		PrNumber:  prNumber,
 	}
 
-	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, lock, reporter, planStorage, nil, dir)
+	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, &depGraph, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, lock, reporter, planStorage, nil, dir)
 	assert.NoError(t, err)
 
 	projectLock := &locking.PullRequestLock{
@@ -808,7 +808,7 @@ workflows:
 	assert.NoError(t, err)
 	commandsToRunPerProject, _, err = dg_github.ConvertGithubEventToCommands(ghEvent, impactedProjects, requestedProject, diggerConfig.Workflows)
 	assert.NoError(t, err)
-	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, lock, reporter, planStorage, nil, dir)
+	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, &depGraph, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, lock, reporter, planStorage, nil, dir)
 	assert.NoError(t, err)
 
 	println("--- digger apply comment ---")
@@ -822,7 +822,7 @@ workflows:
 	assert.NoError(t, err)
 	commandsToRunPerProject, _, err = dg_github.ConvertGithubEventToCommands(ghEvent, impactedProjects, requestedProject, diggerConfig.Workflows)
 	assert.NoError(t, err)
-	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, lock, reporter, planStorage, nil, dir)
+	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, &depGraph, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, lock, reporter, planStorage, nil, dir)
 	assert.NoError(t, err)
 
 	projectLock = &locking.PullRequestLock{
@@ -844,7 +844,7 @@ workflows:
 	assert.NoError(t, err)
 	commandsToRunPerProject, _, err = dg_github.ConvertGithubEventToCommands(ghEvent, impactedProjects, requestedProject, diggerConfig.Workflows)
 	assert.NoError(t, err)
-	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, lock, reporter, planStorage, nil, dir)
+	_, _, err = digger.RunCommandsPerProject(commandsToRunPerProject, &depGraph, diggerProjectNamespace, repoOwner, eventName, prNumber, githubPrService, lock, reporter, planStorage, nil, dir)
 	assert.NoError(t, err)
 
 	projectLock = &locking.PullRequestLock{
diff --git a/pkg/policy/policy_test.go b/pkg/policy/policy_test.go
index 501963f3..3079b275 100644
--- a/pkg/policy/policy_test.go
+++ b/pkg/policy/policy_test.go
@@ -1,6 +1,7 @@
 package policy
 
 import (
+	"digger/pkg/utils"
 	"testing"
 )
 
@@ -95,87 +96,58 @@ func TestDiggerPolicyChecker_Check(t *testing.T) {
 		args         args
 		want         bool
 		wantErr      bool
+		command      string
+		requestedBy  string
 	}{
-		{
-			name: "test opa example",
-			fields: fields{
-				PolicyProvider: &OpaExamplePolicyProvider{},
-			},
-			args: args{
-				input: map[string]interface{}{
-					"organisation": "diggerhq",
-					"user":         "alice",
-					"action":       "read",
-					"object":       "server123",
-				},
-			},
-			want:    true,
-			wantErr: false,
-		},
 		{
 			name: "test digger example",
 			fields: fields{
 				PolicyProvider: &DiggerExamplePolicyProvider{},
 			},
-			args: args{
-				input: map[string]interface{}{
-					"user":   "motatoes",
-					"action": "digger plan",
-				},
-			},
-			want:    true,
-			wantErr: false,
+			want:        true,
+			wantErr:     false,
+			command:     "digger plan",
+			requestedBy: "motatoes",
 		},
 		{
 			name: "test digger example 2",
 			fields: fields{
 				PolicyProvider: &DiggerExamplePolicyProvider{},
 			},
-			args: args{
-				input: map[string]interface{}{
-					"user":   "Spartakovic",
-					"action": "digger unlock",
-				},
-			},
-			want:    false,
-			wantErr: false,
+			want:        false,
+			wantErr:     false,
+			command:     "digger unlock",
+			requestedBy: "Spartakovic",
 		},
 		{
 			name: "test digger example 3",
 			fields: fields{
 				PolicyProvider: &DiggerExamplePolicyProvider{},
 			},
-			args: args{
-				input: map[string]interface{}{
-					"user":   "rando",
-					"action": "digger apply",
-				},
-			},
-			want:    false,
-			wantErr: false,
+			want:        false,
+			wantErr:     false,
+			command:     "digger apply",
+			requestedBy: "rando",
 		},
 		{
 			name: "test digger example 4",
 			fields: fields{
 				PolicyProvider: &DiggerExamplePolicyProvider2{},
 			},
-			args: args{
-				input: map[string]interface{}{
-					"user":   "motatoes",
-					"action": "digger plan",
-				},
-			},
-			want:    true,
-			wantErr: false,
+			want:        true,
+			wantErr:     false,
+			command:     "digger plan",
+			requestedBy: "motatoes",
 		},
 	}
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			p := &DiggerPolicyChecker{
+			var p = &DiggerPolicyChecker{
 				PolicyProvider: tt.fields.PolicyProvider,
+				ciService:      utils.MockPullRequestManager{Teams: []string{"engineering"}},
 			}
-			got, err := p.Check(tt.organisation, tt.name, tt.name, tt.args.input)
+			got, err := p.Check(tt.organisation, tt.name, tt.name, tt.command, tt.requestedBy)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("DiggerPolicyChecker.Check() error = %v, wantErr %v", err, tt.wantErr)
 				return