Repository: firecow/gitlab-ci-local
Pull request #1408 (2024-11-04 00:54:57)

chore(deps): lock file maintenance all non-major
This PR contains the following updates:

| Package | Type | Update | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|---|---|
|  |  | lockFileMaintenance | All locks refreshed |  |  |  |  |
| [@types/node](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node) ([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node)) | devDependencies | minor | [`22.8.2` -> `22.9.0`](https://renovatebot.com/diffs/npm/@types%2fnode/22.8.2/22.9.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@types%2fnode/22.9.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@types%2fnode/22.9.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@types%2fnode/22.8.2/22.9.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@types%2fnode/22.8.2/22.9.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@yao-pkg/pkg](https://redirect.github.com/yao-pkg/pkg) | devDependencies | minor | [`6.0.0` -> `6.1.0`](https://renovatebot.com/diffs/npm/@yao-pkg%2fpkg/6.0.0/6.1.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@yao-pkg%2fpkg/6.1.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@yao-pkg%2fpkg/6.1.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@yao-pkg%2fpkg/6.0.0/6.1.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@yao-pkg%2fpkg/6.0.0/6.1.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

🔧 This Pull Request updates lock files to use the latest dependency versions.

---

### Release Notes

<details>
<summary>yao-pkg/pkg (@&#8203;yao-pkg/pkg)</summary>

### [`v6.1.0`](https://redirect.github.com/yao-pkg/pkg/blob/HEAD/CHANGELOG.md#610-2024-11-04)

[Compare Source](https://redirect.github.com/yao-pkg/pkg/compare/v6.0.1...v6.1.0)

##### Features

-   bump fetch 3.5.17 with node 22.11.0 support ([2aae943](https://redirect.github.com/yao-pkg/pkg/commit/2aae94355b55537c361aedc2f81223b2c847955f))

##### Chores

-   fix yarn.lock issues ([fa4f911](https://redirect.github.com/yao-pkg/pkg/commit/fa4f91110b93c805b931c720ed3e5e45a3c2b28f))

### [`v6.0.1`](https://redirect.github.com/yao-pkg/pkg/blob/HEAD/CHANGELOG.md#601-2024-11-04)

[Compare Source](https://redirect.github.com/yao-pkg/pkg/compare/v6.0.0...v6.0.1)

##### Bug Fixes

-   avoid endless loop when handling symlinks ([#&#8203;109](https://redirect.github.com/yao-pkg/pkg/issues/109)) by [@&#8203;q0rban](https://redirect.github.com/q0rban) ([#&#8203;109](https://redirect.github.com/yao-pkg/pkg/issues/109)) ([e41355c](https://redirect.github.com/yao-pkg/pkg/commit/e41355c5de3ad8a2597e61c6708cefe065d5ab0d))
-   deprecated `_extend` in bootstrap.js ([#&#8203;118](https://redirect.github.com/yao-pkg/pkg/issues/118)) ([4acb187](https://redirect.github.com/yao-pkg/pkg/commit/4acb1879343287204957ae2f5bf4790cd83451e5))
-   follow exact SEA baking procedure for Mac ([#&#8203;116](https://redirect.github.com/yao-pkg/pkg/issues/116)) ([d818a04](https://redirect.github.com/yao-pkg/pkg/commit/d818a043b20e55d381cc978220f1158e9059e160))

##### Test added

-   fix verify pkg version test ([81bd04a](https://redirect.github.com/yao-pkg/pkg/commit/81bd04ae5be7826bf695033de8e80b21c9cb7beb))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC), 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.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired.

---

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

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/firecow/gitlab-ci-local).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOC4xNDIuNCIsInVwZGF0ZWRJblZlciI6IjM4LjE0Mi40IiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=firecow_gitlab-ci-local&pullRequest=1408) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=firecow_gitlab-ci-local&pullRequest=1408&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=firecow_gitlab-ci-local&pullRequest=1408&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=firecow_gitlab-ci-local&pullRequest=1408&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=firecow_gitlab-ci-local&pullRequest=1408&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=firecow_gitlab-ci-local&pullRequest=1408&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=firecow_gitlab-ci-local&pullRequest=1408)


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=firecow_gitlab-ci-local&pullRequest=1408) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=firecow_gitlab-ci-local&pullRequest=1408&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=firecow_gitlab-ci-local&pullRequest=1408&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=firecow_gitlab-ci-local&pullRequest=1408&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=firecow_gitlab-ci-local&pullRequest=1408&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=firecow_gitlab-ci-local&pullRequest=1408&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=firecow_gitlab-ci-local&pullRequest=1408)


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=firecow_gitlab-ci-local&pullRequest=1408) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=firecow_gitlab-ci-local&pullRequest=1408&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=firecow_gitlab-ci-local&pullRequest=1408&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=firecow_gitlab-ci-local&pullRequest=1408&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=firecow_gitlab-ci-local&pullRequest=1408&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=firecow_gitlab-ci-local&pullRequest=1408&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=firecow_gitlab-ci-local&pullRequest=1408)


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=firecow_gitlab-ci-local&pullRequest=1408) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=firecow_gitlab-ci-local&pullRequest=1408&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=firecow_gitlab-ci-local&pullRequest=1408&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=firecow_gitlab-ci-local&pullRequest=1408&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=firecow_gitlab-ci-local&pullRequest=1408&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=firecow_gitlab-ci-local&pullRequest=1408&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=firecow_gitlab-ci-local&pullRequest=1408)



Patch:
diff --git a/examples/docker-swarm-php/composer.lock b/examples/docker-swarm-php/composer.lock
index 83c6b05..2058fe8 100644
--- a/examples/docker-swarm-php/composer.lock
+++ b/examples/docker-swarm-php/composer.lock
@@ -1737,38 +1737,35 @@
         },
         {
             "name": "symfony/polyfill-php80",
-            "version": "v1.22.1",
+            "version": "v1.31.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php80.git",
-                "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91"
+                "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91",
-                "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91",
+                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
+                "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.1"
+                "php": ">=7.2"
             },
             "type": "library",
             "extra": {
-                "branch-alias": {
-                    "dev-main": "1.22-dev"
-                },
                 "thanks": {
                     "name": "symfony/polyfill",
                     "url": "https://github.com/symfony/polyfill"
                 }
             },
             "autoload": {
-                "psr-4": {
-                    "Symfony\\Polyfill\\Php80\\": ""
-                },
                 "files": [
                     "bootstrap.php"
                 ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php80\\": ""
+                },
                 "classmap": [
                     "Resources/stubs"
                 ]
@@ -1800,7 +1797,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-php80/tree/v1.22.1"
+                "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0"
             },
             "funding": [
                 {
@@ -1816,25 +1813,25 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-01-07T16:49:33+00:00"
+            "time": "2024-09-09T11:45:10+00:00"
         },
         {
             "name": "symfony/process",
-            "version": "v5.2.4",
+            "version": "v5.4.46",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/process.git",
-                "reference": "313a38f09c77fbcdc1d223e57d368cea76a2fd2f"
+                "reference": "01906871cb9b5e3cf872863b91aba4ec9767daf4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/process/zipball/313a38f09c77fbcdc1d223e57d368cea76a2fd2f",
-                "reference": "313a38f09c77fbcdc1d223e57d368cea76a2fd2f",
+                "url": "https://api.github.com/repos/symfony/process/zipball/01906871cb9b5e3cf872863b91aba4ec9767daf4",
+                "reference": "01906871cb9b5e3cf872863b91aba4ec9767daf4",
                 "shasum": ""
             },
             "require": {
                 "php": ">=7.2.5",
-                "symfony/polyfill-php80": "^1.15"
+                "symfony/polyfill-php80": "^1.16"
             },
             "type": "library",
             "autoload": {
@@ -1862,7 +1859,7 @@
             "description": "Executes commands in sub-processes",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/process/tree/v5.2.4"
+                "source": "https://github.com/symfony/process/tree/v5.4.46"
             },
             "funding": [
                 {
@@ -1878,7 +1875,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-01-27T10:15:41+00:00"
+            "time": "2024-11-06T09:18:28+00:00"
         },
         {
             "name": "symfony/service-contracts",
@@ -2114,5 +2111,5 @@
         "ext-json": "*"
     },
     "platform-dev": [],
-    "plugin-api-version": "2.0.0"
+    "plugin-api-version": "2.6.0"
 }
diff --git a/package-lock.json b/package-lock.json
index b783b16..8768fb0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -79,9 +79,9 @@
       }
     },
     "node_modules/@babel/code-frame": {
-      "version": "7.26.0",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.0.tgz",
-      "integrity": "sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g==",
+      "version": "7.26.2",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
+      "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -94,9 +94,9 @@
       }
     },
     "node_modules/@babel/compat-data": {
-      "version": "7.26.0",
-      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.0.tgz",
-      "integrity": "sha512-qETICbZSLe7uXv9VE8T/RWOdIE5qqyTucOt4zLYMafj2MRO271VGgLd4RACJMeBO37UPWhXiKMBk7YlJ0fOzQA==",
+      "version": "7.26.2",
+      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz",
+      "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -145,13 +145,13 @@
       }
     },
     "node_modules/@babel/generator": {
-      "version": "7.26.0",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.0.tgz",
-      "integrity": "sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w==",
+      "version": "7.26.2",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz",
+      "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/parser": "^7.26.0",
+        "@babel/parser": "^7.26.2",
         "@babel/types": "^7.26.0",
         "@jridgewell/gen-mapping": "^0.3.5",
         "@jridgewell/trace-mapping": "^0.3.25",
@@ -275,9 +275,9 @@
       }
     },
     "node_modules/@babel/parser": {
-      "version": "7.26.1",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.1.tgz",
-      "integrity": "sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==",
+      "version": "7.26.2",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz",
+      "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -1012,9 +1012,9 @@
       }
     },
     "node_modules/@eslint-community/regexpp": {
-      "version": "4.12.0",
-      "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.0.tgz",
-      "integrity": "sha512-gh7PdNombP8ftL8TinYC8Xd7WEypB8EKV4PI2h0eMzndKjPCXuo2zUiZtD2Hu+MSPt02Ty2MdS788ADl9ai1rA==",
+      "version": "4.12.1",
+      "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
+      "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -1891,14 +1891,13 @@
       }
     },
     "node_modules/@types/base64url": {
-      "version": "2.0.7",
-      "resolved": "https://registry.npmjs.org/@types/base64url/-/base64url-2.0.7.tgz",
-      "integrity": "sha512-7YxYhlfX8p0hCVn4WcKH9E1avCnS2tkV0CJdu2w9bmqa/PdVipS8r3kweS2xIMOsS3sF2Se/UyWHxXbBqIJnKA==",
-      "deprecated": "This is a stub types definition. base64url provides its own type definitions, so you do not need this installed.",
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/@types/base64url/-/base64url-2.0.3.tgz",
+      "integrity": "sha512-OiiHhKSxekQ1sE5ykAvHO/8erbszyzLz4MRRr0Ntns65tMR5Nm/1ndGc2mjveL4hZC8RB+w1LM4b5Boo4FRg+g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "base64url": "*"
+        "@types/node": "*"
       }
     },
     "node_modules/@types/braces": {
@@ -2037,9 +2036,9 @@
       "license": "MIT"
     },
     "node_modules/@types/node": {
-      "version": "22.8.2",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.2.tgz",
-      "integrity": "sha512-NzaRNFV+FZkvK/KLCsNdTvID0SThyrs5SHB6tsD/lajr22FGC73N2QeDPM2wHtVde8mgcXuSsHQkH5cX1pbPLw==",
+      "version": "22.9.0",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz",
+      "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -2356,16 +2355,16 @@
       "license": "MIT"
     },
     "node_modules/@yao-pkg/pkg": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/@yao-pkg/pkg/-/pkg-6.0.0.tgz",
-      "integrity": "sha512-2XlkWA4Y/QhW0Lhvo8f2sE/dBZGIyp9uMX3wxaq8WvWgCaXcFpeSprxQoKF2BFyUqVAr/TmDsQ6+1RukK9XZag==",
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/@yao-pkg/pkg/-/pkg-6.1.0.tgz",
+      "integrity": "sha512-77wFdRj3fWlWdCsF+vCYYzTHClvSEZvFJ+yZ98/Jr3D0VTsrd1tIpxJRnhS6xvbV9UEPM+IXPSC1qXVD6mOH+w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@babel/generator": "^7.23.0",
         "@babel/parser": "^7.23.0",
         "@babel/types": "^7.23.0",
-        "@yao-pkg/pkg-fetch": "3.5.16",
+        "@yao-pkg/pkg-fetch": "3.5.17",
         "into-stream": "^6.0.0",
         "minimist": "^1.2.6",
         "multistream": "^4.1.0",
@@ -2386,9 +2385,9 @@
       }
     },
     "node_modules/@yao-pkg/pkg-fetch": {
-      "version": "3.5.16",
-      "resolved": "https://registry.npmjs.org/@yao-pkg/pkg-fetch/-/pkg-fetch-3.5.16.tgz",
-      "integrity": "sha512-mCnZvZz0/Ylpk4TGyt34pqWJyBGYJM8c3dPoMRV8Knodv2QhcYS4iXb5kB/JNWkrRtCKukGZIKkMLXZ3TQlzPg==",
+      "version": "3.5.17",
+      "resolved": "https://registry.npmjs.org/@yao-pkg/pkg-fetch/-/pkg-fetch-3.5.17.tgz",
+      "integrity": "sha512-2gD2K8JUwHwvFFZbwVXwmm90P0U3s8Kqiym4w7t2enTajH28LMhpXaqh/x+SzKeNwvPGoaRUhV0h2nPtWTDoDA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -3001,9 +3000,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001673",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001673.tgz",
-      "integrity": "sha512-WTrjUCSMp3LYX0nE12ECkV0a+e6LC85E0Auz75555/qr78Oc8YWhEPNfDd6SHdtlCMSzqtuXY0uyEMNRcsKpKw==",
+      "version": "1.0.30001677",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001677.tgz",
+      "integrity": "sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog==",
       "dev": true,
       "funding": [
         {
@@ -3098,11 +3097,14 @@
       }
     },
     "node_modules/chownr": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
-      "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
+      "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
       "dev": true,
-      "license": "ISC"
+      "license": "BlueOak-1.0.0",
+      "engines": {
+        "node": ">=18"
+      }
     },
     "node_modules/ci-info": {
       "version": "3.9.0",
@@ -3593,9 +3595,9 @@
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.5.47",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.47.tgz",
-      "integrity": "sha512-zS5Yer0MOYw4rtK2iq43cJagHZ8sXN0jDHDKzB+86gSBSAI4v07S97mcq+Gs2vclAxSh1j7vOAHxSVgduiiuVQ==",
+      "version": "1.5.51",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.51.tgz",
+      "integrity": "sha512-kKeWV57KSS8jH4alKt/jKnvHPmJgBxXzGUSbMd4eQF+iOsVPl7bz2KUmu6eo80eMP8wVioTfTyTzdMgM15WXNg==",
       "dev": true,
       "license": "ISC"
     },
@@ -7522,6 +7524,13 @@
         "tar-stream": "^2.1.4"
       }
     },
+    "node_modules/tar-fs/node_modules/chownr": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+      "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+      "dev": true,
+      "license": "ISC"
+    },
     "node_modules/tar-stream": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
@@ -7554,16 +7563,6 @@
         "node": ">= 6"
       }
     },
-    "node_modules/tar/node_modules/chownr": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
-      "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
-      "dev": true,
-      "license": "BlueOak-1.0.0",
-      "engines": {
-        "node": ">=18"
-      }
-    },
     "node_modules/tar/node_modules/yallist": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
@@ -7687,9 +7686,9 @@
       "license": "MIT"
     },
     "node_modules/ts-api-utils": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
-      "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz",
+      "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==",
       "dev": true,
       "license": "MIT",
       "engines": {
diff --git a/src/argv.ts b/src/argv.ts
index ad64815..804b822 100644
--- a/src/argv.ts
+++ b/src/argv.ts
@@ -24,9 +24,10 @@ async function gitRootPath () {
 export class Argv {
     static readonly default = {
         "variablesFile": ".gitlab-ci-local-variables.yml",
+        "evaluateRuleChanges": true,
     };
 
-    private map: Map<string, any> = new Map<string, any>();
+    map: Map<string, any> = new Map<string, any>();
     private writeStreams: WriteStreams | undefined;
 
     private async fallbackCwd (args: any) {
@@ -92,6 +93,10 @@ export class Argv {
         return this.map.get("variablesFile") ?? Argv.default.variablesFile;
     }
 
+    get evaluateRuleChanges (): boolean {
+        return this.map.get("evaluateRuleChanges") ?? Argv.default.evaluateRuleChanges;
+    }
+
     get file (): string {
         return this.map.get("file") ?? ".gitlab-ci.yml";
     }
@@ -276,4 +281,8 @@ export class Argv {
     get maximumIncludes (): number {
         return this.map.get("maximumIncludes") ?? 150; // https://docs.gitlab.com/ee/administration/settings/continuous_integration.html#maximum-includes
     }
+
+    get childPipelineDepth (): number {
+        return this.map.get("childPipelineDepth");
+    }
 }
diff --git a/src/commander.ts b/src/commander.ts
index 4fbbe2a..c9b8684 100644
--- a/src/commander.ts
+++ b/src/commander.ts
@@ -133,9 +133,12 @@ export class Commander {
 
         if (preScripts.successful.length !== 0) {
             preScripts.successful.sort((a, b) => stages.indexOf(a.stage) - stages.indexOf(b.stage));
-            preScripts.successful.forEach(({coveragePercent, name, prettyDuration}) => {
+            preScripts.successful.forEach(({argv, coveragePercent, name, prettyDuration}) => {
+                let prefix = "";
+                if (argv.childPipelineDepth > 0) prefix = `[${argv.variable.GCL_TRIGGERER}] -> `;
+
                 const namePad = name.padEnd(jobNamePad);
-                writeStreams.stdout(chalk`{black.bgGreenBright  PASS }${renderDuration(prettyDuration)} {blueBright ${namePad}}`);
+                writeStreams.stdout(chalk`{black.bgGreenBright  PASS }${renderDuration(prettyDuration)} {blueBright ${prefix}${namePad}}`);
                 if (coveragePercent) {
                     writeStreams.stdout(chalk` ${coveragePercent}% {grey coverage}`);
                 }
diff --git a/src/handler.ts b/src/handler.ts
index 3b76346..0bc5f6f 100644
--- a/src/handler.ts
+++ b/src/handler.ts
@@ -21,8 +21,9 @@ const generateGitIgnore = (cwd: string, stateDir: string) => {
     }
 };
 
-export async function handler (args: any, writeStreams: WriteStreams, jobs: Job[] = []) {
-    const argv = await Argv.build(args, writeStreams);
+export async function handler (args: any, writeStreams: WriteStreams, jobs: Job[] = [], childPipelineDepth = 0) {
+    assert(childPipelineDepth <= 2, "Parent and child pipelines have a maximum depth of two levels of child pipelines.");
+    const argv = await Argv.build({...args, childPipelineDepth: childPipelineDepth}, writeStreams);
     const cwd = argv.cwd;
     const stateDir = argv.stateDir;
     const file = argv.file;
@@ -87,13 +88,13 @@ export async function handler (args: any, writeStreams: WriteStreams, jobs: Job[
     } else {
         generateGitIgnore(cwd, stateDir);
         const time = process.hrtime();
-        await fs.remove(`${cwd}/${stateDir}/artifacts`);
+        if (childPipelineDepth == 0) await fs.remove(`${cwd}/${stateDir}/artifacts`);
         await state.incrementPipelineIid(cwd, stateDir);
         const pipelineIid = await state.getPipelineIid(cwd, stateDir);
         parser = await Parser.create(argv, writeStreams, pipelineIid, jobs);
         await Utils.rsyncTrackedFiles(cwd, stateDir, ".docker");
         await Commander.runPipeline(argv, parser, writeStreams);
-        writeStreams.stderr(chalk`{grey pipeline finished} in {grey ${prettyHrtime(process.hrtime(time))}}\n`);
+        if (childPipelineDepth == 0) writeStreams.stderr(chalk`{grey pipeline finished} in {grey ${prettyHrtime(process.hrtime(time))}}\n`);
     }
     writeStreams.flush();
 
diff --git a/src/index.ts b/src/index.ts
index c4779e2..e1809ea 100755
--- a/src/index.ts
+++ b/src/index.ts
@@ -117,6 +117,12 @@ process.on("SIGUSR2", async () => await cleanupJobResources(jobs));
             description: "Generate tab completion script",
             requiresArg: false,
         })
+        .option("evaluate-rule-changes", {
+            type: "boolean",
+            description: "Whether to evaluate rule:changes. If set to false, rules:changes will always evaluate to true",
+            requiresArg: false,
+            default: Argv.default.evaluateRuleChanges,
+        })
         .option("needs", {
             type: "boolean",
             description: "Run needed jobs, when executing specific jobs",
diff --git a/src/job.ts b/src/job.ts
index 08efe83..40c4f54 100644
--- a/src/job.ts
+++ b/src/job.ts
@@ -12,6 +12,11 @@ import {Argv} from "./argv.js";
 import execa from "execa";
 import {CICDVariable} from "./variables-from-files.js";
 import {GitlabRunnerCPUsPresetValue, GitlabRunnerMemoryPresetValue, GitlabRunnerPresetValues} from "./gitlab-preset.js";
+import {handler} from "./handler.js";
+import * as yaml from "js-yaml";
+import {Parser} from "./parser.js";
+import globby from "globby";
+import {validateIncludeLocal} from "./parser-includes.js";
 
 const CI_PROJECT_DIR = "/gcl-builds";
 const GCL_SHELL_PROMPT_PLACEHOLDER = "<gclShellPromptPlaceholder>";
@@ -105,6 +110,8 @@ export class Job {
     };
 
     private readonly _globalVariables: {[key: string]: string} = {};
+
+    readonly variablesForDownstreamPipeline: {[key: string]: string} = {};
     private readonly _variables: {[key: string]: string} = {};
     private _dotenvVariables: {[key: string]: string} = {};
     private _prescriptsExitCode: number | null = null;
@@ -220,7 +227,7 @@ export class Job {
 
         // Set {when, allowFailure} based on rules result
         if (this.rules) {
-            const ruleResult = Utils.getRulesResult({cwd, rules: this.rules, variables: this._variables}, this.gitData, this.when, this.allowFailure);
+            const ruleResult = Utils.getRulesResult({argv, cwd, rules: this.rules, variables: this._variables}, this.gitData, this.when, this.allowFailure);
             this.when = ruleResult.when;
             this.allowFailure = ruleResult.allowFailure;
             this._variables = {...this.globalVariables, ...jobVariables, ...ruleResult.variables, ...matrixVariables, ...predefinedVariables, ...envMatchedVariables, ...argvVariables};
@@ -310,12 +317,14 @@ export class Job {
     }
 
     get formattedJobName () {
+        let prefix = "";
+        if (this.argv.childPipelineDepth > 0) prefix = "\t".repeat(this.argv.childPipelineDepth) + `[${this.argv.variable.GCL_TRIGGERER}] -> `;
         const timestampPrefix = this.argv.showTimestamps
             ? `[${dateFormatter.format(new Date())} ${this.prettyDuration.padStart(7)}] `
             : "";
 
         // [16:33:19 1.37 min] my-job     > hello world
-        return chalk`${timestampPrefix}{blueBright ${this.name.padEnd(this.jobNamePad)}}`;
+        return chalk`${timestampPrefix}{blueBright ${prefix}${this.name.padEnd(this.jobNamePad)}}`;
     }
 
     get safeJobName () {
@@ -433,6 +442,24 @@ export class Job {
         return this.jobData["trigger"];
     }
 
+    async startTriggerPipeline () {
+        this.writeStreams.memoStdout(chalk`{bgYellowBright  WARN } downstream pipeline is experimental in gitlab-ci-local\n`);
+        await this.fetchTriggerInclude();
+        const variablesForDownstreamPipeline = Object.entries({...this.globalVariables, ...this.jobData.variables}).map(([key, value]) => `${key}=${value}`);
+
+        const gclTriggerer = this.argv.variable["GCL_TRIGGERER"]
+            ? `${this.argv.variable["GCL_TRIGGERER"]} -> ${this.name}`
+            : this.name;
+        await handler({
+            ...Object.fromEntries(this.argv.map),
+            file: `${this.argv.stateDir}/includes/triggers/${this.name}.yml`,
+            variable: [
+                chalk`GCL_TRIGGERER=${gclTriggerer}`,
+                "CI_PIPELINE_SOURCE=parent_pipeline",
+            ].concat(variablesForDownstreamPipeline),
+        }, this.writeStreams, [], this.argv.childPipelineDepth + 1);
+    }
+
     get preScriptsExitCode () {
         return this._prescriptsExitCode;
     }
@@ -470,6 +497,12 @@ export class Job {
     }
 
     async start (): Promise<void> {
+        if (this.trigger) {
+            await this.startTriggerPipeline();
+            this._prescriptsExitCode = 0; // NOTE: so that `this.finished` will implicitly be set to true
+            return;
+        }
+
         this._running = true;
 
         const argv = this.argv;
@@ -1368,6 +1401,45 @@ export class Job {
             }));
         }
     }
+
+    private async fetchTriggerInclude () {
+        const {cwd, stateDir} = this.argv;
+
+        fs.mkdirpSync(`${cwd}/${stateDir}/includes/triggers`);
+
+        let contents: any = {};
+
+        for (const include of this.jobData.trigger?.include ?? []) {
+            if (include["local"]) {
+                validateIncludeLocal(include["local"]);
+                const files = await globby(include["local"].replace(/^\//, ""), {dot: true, cwd});
+                if (files.length == 0) {
+                    throw new AssertionError({message: `Local include file \`${include["local"]}\` specified in \`.${this.name}\` cannot be found!`});
+                }
+
+                for (const file of files) {
+                    const content = await Parser.loadYaml(`${cwd}/${file}`, {});
+                    contents = {
+                        ...contents,
+                        ...content,
+                    };
+                }
+            } else if (include["artifact"]) {
+                const content = await Parser.loadYaml(`${cwd}/${stateDir}/artifacts/${include["job"]}/${include["artifact"]}`, {});
+                contents = {
+                    ...contents,
+                    ...content,
+                };
+            } else if (include["project"]) {
+                this.writeStreams.memoStdout(chalk`{bgYellowBright  WARN } \`{blueBright ${this.name}.trigger.include.*.project}\` will be no-op. It is currently not implemented\n`);
+
+            } else if (include["template"]) {
+                this.writeStreams.memoStdout(chalk`{bgYellowBright  WARN } \`{blueBright ${this.name}.trigger.include.*.template}\` will be no-op. It is currently not implemented\n`);
+            }
+        }
+        const target = `${cwd}/${stateDir}/includes/triggers/${this.name}.yml`;
+        fs.writeFileSync(target, yaml.dump(contents));
+    }
 }
 
 export async function cleanupJobResources (jobs?: Iterable<Job>) {
diff --git a/src/parser-includes.ts b/src/parser-includes.ts
index d7633d2..2ebec23 100644
--- a/src/parser-includes.ts
+++ b/src/parser-includes.ts
@@ -1,3 +1,4 @@
+import {Argv} from "./argv.js";
 import {Utils} from "./utils.js";
 import fs from "fs-extra";
 import {WriteStreams} from "./write-streams.js";
@@ -10,6 +11,7 @@ import globby from "globby";
 import path from "path";
 
 type ParserIncludesInitOptions = {
+    argv: Argv;
     cwd: string;
     stateDir: string;
     writeStreams: WriteStreams;
@@ -27,7 +29,21 @@ export class ParserIncludes {
         this.count = 0;
     }
 
+    private static normalizeTriggerInclude (gitlabData: any, opts: ParserIncludesInitOptions) {
+        const {writeStreams} = opts;
+        for (const [jobName, jobData] of Object.entries<any>(gitlabData ?? {})) {
+            if (typeof jobData.trigger?.include === "string") {
+                jobData.trigger.include = [{
+                    local: jobData.trigger.include,
+                } ];
+            } else if (jobData.trigger?.project) {
+                writeStreams.memoStdout(chalk`{bgYellowBright  WARN } The job: \`{blueBright ${jobName}}\` will be no-op. Multi-project pipeline is not supported by gitlab-ci-local\n`);
+            }
+        }
+    }
+
     static async init (gitlabData: any, opts: ParserIncludesInitOptions): Promise<any[]> {
+        const {argv} = opts;
         this.count++;
         assert(
             this.count <= opts.maximumIncludes + 1, // 1st init call is not counted
@@ -39,11 +55,12 @@ export class ParserIncludes {
 
         const include = this.expandInclude(gitlabData?.include, opts.variables);
 
+        this.normalizeTriggerInclude(gitlabData, opts);
         // Find files to fetch from remote and place in .gitlab-ci-local/includes
         for (const value of include) {
             if (value["rules"]) {
                 const include_rules = value["rules"];
-                const rulesResult = Utils.getRulesResult({cwd, rules: include_rules, variables: opts.variables}, gitData);
+                const rulesResult = Utils.getRulesResult({argv, cwd, rules: include_rules, variables: opts.variables}, gitData);
                 if (rulesResult.when === "never") {
                     continue;
                 }
@@ -73,7 +90,7 @@ export class ParserIncludes {
         for (const value of include) {
             if (value["rules"]) {
                 const include_rules = value["rules"];
-                const rulesResult = Utils.getRulesResult({cwd, rules: include_rules, variables: opts.variables}, gitData);
+                const rulesResult = Utils.getRulesResult({argv, cwd, rules: include_rules, variables: opts.variables}, gitData);
                 if (rulesResult.when === "never") {
                     continue;
                 }
@@ -95,7 +112,7 @@ export class ParserIncludes {
                     fileDoc["include"].forEach((inner: any, i: number) => {
                         if (!inner["local"]) return;
                         if (inner["rules"]) {
-                            const rulesResult = Utils.getRulesResult({cwd: opts.cwd, variables: opts.variables, rules: inner["rules"]}, gitData);
+                            const rulesResult = Utils.getRulesResult({argv, cwd: opts.cwd, variables: opts.variables, rules: inner["rules"]}, gitData);
                             if (rulesResult.when === "never") {
                                 return;
                             }
@@ -268,7 +285,7 @@ export class ParserIncludes {
     }
 }
 
-function validateIncludeLocal (filePath: string) {
+export function validateIncludeLocal (filePath: string) {
     assert(!filePath.startsWith("./"), `\`${filePath}\` for include:local is invalid. Gitlab does not support relative path (ie. cannot start with \`./\`).`);
     assert(!filePath.includes(".."), `\`${filePath}\` for include:local is invalid. Gitlab does not support directory traversal.`);
 }
diff --git a/src/parser.ts b/src/parser.ts
index 1b34ec3..e0d2310 100644
--- a/src/parser.ts
+++ b/src/parser.ts
@@ -70,7 +70,7 @@ export class Parser {
         const pathToExpandedGitLabCi = path.join(argv.cwd, argv.stateDir, "expanded-gitlab-ci.yml");
         fs.mkdirpSync(path.join(argv.cwd, argv.stateDir));
         fs.writeFileSync(pathToExpandedGitLabCi, yaml.dump(parser.gitlabData));
-        writeStreams.stderr(chalk`{grey parsing and downloads finished in ${prettyHrtime(parsingTime)}.}\n`);
+        if (argv.childPipelineDepth == 0) writeStreams.stderr(chalk`{grey parsing and downloads finished in ${prettyHrtime(parsingTime)}.}\n`);
 
         for (const warning of warnings) {
             writeStreams.stderr(chalk`{yellow ${warning}}\n`);
@@ -83,7 +83,7 @@ export class Parser {
                 pathToExpandedGitLabCi,
                 gitLabCiConfig: parser.gitlabData,
             });
-            writeStreams.stderr(chalk`{grey json schema validated in ${prettyHrtime(process.hrtime(time))}}\n`);
+            if (argv.childPipelineDepth == 0) writeStreams.stderr(chalk`{grey json schema validated in ${prettyHrtime(process.hrtime(time))}}\n`);
         }
         return parser;
     }
@@ -106,11 +106,11 @@ export class Parser {
         let yamlDataList: any[] = [{stages: [".pre", "build", "test", "deploy", ".post"]}];
         const gitlabCiData = await Parser.loadYaml(`${cwd}/${file}`, {}, this.expandVariables);
 
-        yamlDataList = yamlDataList.concat(await ParserIncludes.init(gitlabCiData, {cwd, stateDir, writeStreams, gitData, fetchIncludes, variables: expanded, expandVariables: this.expandVariables, maximumIncludes: argv.maximumIncludes}));
+        yamlDataList = yamlDataList.concat(await ParserIncludes.init(gitlabCiData, {argv, cwd, stateDir, writeStreams, gitData, fetchIncludes, variables: expanded, expandVariables: this.expandVariables, maximumIncludes: argv.maximumIncludes}));
         ParserIncludes.resetCount();
 
         const gitlabCiLocalData = await Parser.loadYaml(`${cwd}/.gitlab-ci-local.yml`, {}, this.expandVariables);
-        yamlDataList = yamlDataList.concat(await ParserIncludes.init(gitlabCiLocalData, {cwd, stateDir, writeStreams, gitData, fetchIncludes, variables: expanded, expandVariables: this.expandVariables, maximumIncludes: argv.maximumIncludes}));
+        yamlDataList = yamlDataList.concat(await ParserIncludes.init(gitlabCiLocalData, {argv, cwd, stateDir, writeStreams, gitData, fetchIncludes, variables: expanded, expandVariables: this.expandVariables, maximumIncludes: argv.maximumIncludes}));
         ParserIncludes.resetCount();
 
         const gitlabData: any = deepExtend({}, ...yamlDataList);
diff --git a/src/utils.ts b/src/utils.ts
index f596863..55ab4c3 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -11,8 +11,10 @@ import globby from "globby";
 import micromatch from "micromatch";
 import axios from "axios";
 import path from "path";
+import {Argv} from "./argv.js";
 
 type RuleResultOpt = {
+    argv: Argv;
     cwd: string;
     rules: JobRule[];
     variables: {[key: string]: string};
@@ -172,6 +174,7 @@ export class Utils {
 
     static getRulesResult (opt: RuleResultOpt, gitData: GitData, jobWhen: string = "on_success", jobAllowFailure: boolean | {exit_codes: number | number[]} = false): {when: string; allowFailure: boolean | {exit_codes: number | number[]}; variables?: {[name: string]: string}} {
         let when = "never";
+        const {evaluateRuleChanges} = opt.argv;
 
         // optional manual jobs allowFailure defaults to true https://docs.gitlab.com/ee/ci/jobs/job_control.html#types-of-manual-jobs
         let allowFailure = jobWhen === "manual" ? true : jobAllowFailure;
@@ -180,7 +183,7 @@ export class Utils {
         for (const rule of opt.rules) {
             if (!Utils.evaluateRuleIf(rule.if, opt.variables)) continue;
             if (!Utils.evaluateRuleExist(opt.cwd, rule.exists)) continue;
-            if (!Utils.evaluateRuleChanges(gitData.branches.default, rule.changes)) continue;
+            if (evaluateRuleChanges && !Utils.evaluateRuleChanges(gitData.branches.default, rule.changes)) continue;
 
             when = rule.when ? rule.when : jobWhen;
             allowFailure = rule.allow_failure ?? allowFailure;
@@ -218,7 +221,7 @@ export class Utils {
                 default:
                     throw operator;
             }
-            return `.match(${rhs}${flags})${remainingTokens} ${_operator} null `;
+            return `.match(${rhs}${flags}) ${_operator} null${remainingTokens}`;
         });
 
         // Scenario when RHS is surrounded by double-quotes


Test patch:
diff --git a/tests/rules-regex.test.ts b/tests/rules-regex.test.ts
index 476b1cc..9747b2c 100644
--- a/tests/rules-regex.test.ts
+++ b/tests/rules-regex.test.ts
@@ -1,14 +1,16 @@
-
+import {Argv} from "../src/argv";
 import {Utils} from "../src/utils";
 import {GitData} from "../src/git-data";
 import {WriteStreamsMock} from "../src/write-streams";
 
 let writeStreams;
 let gitData: GitData;
-//
+let argv: Argv;
+
 beforeEach(async () => {
     writeStreams = new WriteStreamsMock();
     gitData = await GitData.init("tests", writeStreams);
+    argv = await Argv.build({}, writeStreams);
     import.meta.jest.clearAllMocks();
 });
 
@@ -77,6 +79,16 @@ const tests = [
         jsExpression: 'null && false && null == "true"',
         evalResult: false,
     },
+    {
+        rule: '($CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^perf_.*$/)',
+        jsExpression: '(false)', // ((null.match(/^perf_.*$/) != null)) => (false)
+        evalResult: false,
+    },
+    {
+        rule: '("qwerty" =~ /^perf_.*$/)',
+        jsExpression: '("qwerty".match(/^perf_.*$/) != null)',
+        evalResult: false,
+    },
 ];
 /* eslint-enable @typescript-eslint/quotes */
 
@@ -88,7 +100,7 @@ describe("gitlab rules regex", () => {
                 const evalSpy = import.meta.jest.spyOn(global, "eval");
                 const evaluateRuleIfSpy = import.meta.jest.spyOn(Utils, "evaluateRuleIf");
 
-                Utils.getRulesResult({cwd: "", rules, variables: {}}, gitData);
+                Utils.getRulesResult({argv, cwd: "", rules, variables: {}}, gitData);
                 expect(evalSpy).toHaveBeenCalledWith(t.jsExpression);
                 expect(evaluateRuleIfSpy).toHaveReturnedWith(t.evalResult);
             });
@@ -102,7 +114,7 @@ describe("gitlab rules regex [invalid]", () => {
                 const rules = [ {if: t.rule} ];
 
                 try {
-                    Utils.getRulesResult({cwd: "", rules, variables: {}}, gitData);
+                    Utils.getRulesResult({argv, cwd: "", rules, variables: {}}, gitData);
                 } catch (e) {
                     return;
                 }
diff --git a/tests/rules.test.ts b/tests/rules.test.ts
index 52044f0..a0b879b 100644
--- a/tests/rules.test.ts
+++ b/tests/rules.test.ts
@@ -1,13 +1,16 @@
 import {Utils} from "../src/utils.js";
 import {GitData} from "../src/git-data.js";
 import {WriteStreamsMock} from "../src/write-streams.js";
+import {Argv} from "../src/argv.js";
 
 let writeStreams;
 let gitData: GitData;
+let argv: Argv;
 
 beforeAll(async () => {
     writeStreams = new WriteStreamsMock();
     gitData = await GitData.init("tests", writeStreams);
+    argv = await Argv.build({}, writeStreams);
 });
 
 
@@ -16,7 +19,7 @@ test("GITLAB_CI on_success", () => {
         {if: "$GITLAB_CI == 'false'"},
     ];
     const variables = {GITLAB_CI: "false"};
-    const rulesResult = Utils.getRulesResult({cwd: "", rules, variables}, gitData);
+    const rulesResult = Utils.getRulesResult({argv, cwd: "", rules, variables}, gitData);
     expect(rulesResult).toEqual({when: "on_success", allowFailure: false, variables: undefined});
 });
 
@@ -25,7 +28,7 @@ test("Regex on undef var", () => {
         {if: "$CI_COMMIT_TAG =~ /^v\\d+.\\d+.\\d+/"},
     ];
     const variables = {};
-    const rulesResult = Utils.getRulesResult({cwd: "", rules, variables}, gitData);
+    const rulesResult = Utils.getRulesResult({argv, cwd: "", rules, variables}, gitData);
     expect(rulesResult).toEqual({when: "never", allowFailure: false, variables: undefined});
 });
 
@@ -34,7 +37,7 @@ test("Negated regex on undef var", () => {
         {if: "$CI_COMMIT_TAG !~ /^v\\d+.\\d+.\\d+/"},
     ];
     const variables = {};
-    const rulesResult = Utils.getRulesResult({cwd: "", rules, variables}, gitData);
+    const rulesResult = Utils.getRulesResult({argv, cwd: "", rules, variables}, gitData);
     expect(rulesResult).toEqual({when: "never", allowFailure: false, variables: undefined});
 });
 
@@ -44,7 +47,7 @@ test("GITLAB_CI fail and fallback", () => {
         {when: "manual"},
     ];
     const variables = {GITLAB_CI: "false"};
-    const rulesResult = Utils.getRulesResult({cwd: "", rules, variables}, gitData);
+    const rulesResult = Utils.getRulesResult({argv, cwd: "", rules, variables}, gitData);
     expect(rulesResult).toEqual({when: "manual", allowFailure: false, variables: undefined});
 });
 
@@ -53,7 +56,7 @@ test("Undefined if", () => {
         {when: "on_success"},
     ];
     const variables = {};
-    const rulesResult = Utils.getRulesResult({cwd: "", rules, variables}, gitData);
+    const rulesResult = Utils.getRulesResult({argv, cwd: "", rules, variables}, gitData);
     expect(rulesResult).toEqual({when: "on_success", allowFailure: false, variables: undefined});
 });
 
@@ -62,7 +65,7 @@ test("Undefined when", () => {
         {if: "$GITLAB_CI", allow_failure: false},
     ];
     const variables = {GITLAB_CI: "false"};
-    const rulesResult = Utils.getRulesResult({cwd: "", rules, variables}, gitData);
+    const rulesResult = Utils.getRulesResult({argv, cwd: "", rules, variables}, gitData);
     expect(rulesResult).toEqual({when: "on_success", allowFailure: false, variables: undefined});
 });
 
@@ -72,7 +75,7 @@ test("Early return", () => {
         {when: "on_success"},
     ];
     const variables = {GITLAB_CI: "false"};
-    const rulesResult = Utils.getRulesResult({cwd: "", rules, variables}, gitData);
+    const rulesResult = Utils.getRulesResult({argv, cwd: "", rules, variables}, gitData);
     expect(rulesResult).toEqual({when: "never", allowFailure: false, variables: undefined});
 });
 
@@ -250,14 +253,14 @@ test("https://github.com/firecow/gitlab-ci-local/issues/350", () => {
         {if: "$CI_COMMIT_BRANCH =~ /master$/", when: "manual"},
     ];
     variables = {CI_COMMIT_BRANCH: "master"};
-    rulesResult = Utils.getRulesResult({cwd: "", rules, variables}, gitData);
+    rulesResult = Utils.getRulesResult({argv, cwd: "", rules, variables}, gitData);
     expect(rulesResult).toEqual({when: "manual", allowFailure: false, variables: undefined});
 
     rules = [
         {if: "$CI_COMMIT_BRANCH =~ /$BRANCHNAME/", when: "manual"},
     ];
     variables = {CI_COMMIT_BRANCH: "master", BRANCHNAME: "master"};
-    rulesResult = Utils.getRulesResult({cwd: "", rules, variables}, gitData);
+    rulesResult = Utils.getRulesResult({argv, cwd: "", rules, variables}, gitData);
     expect(rulesResult).toEqual({when: "never", allowFailure: false, variables: undefined});
 });
 
@@ -267,14 +270,14 @@ test("https://github.com/firecow/gitlab-ci-local/issues/300", () => {
         {if: "$VAR1 && (($VAR2 =~ /ci-skip-job-/ && $VAR2 =~ $VAR3) || ($VAR2 =~ /ci-skip-stage-/ && $VAR2 =~ $VAR3))", when: "manual"},
     ];
     variables = {VAR1: "val", VAR2: "ci-skip-job-", VAR3: "/ci-skip-job-/"};
-    rulesResult = Utils.getRulesResult({cwd: "", rules, variables}, gitData);
+    rulesResult = Utils.getRulesResult({argv, cwd: "", rules, variables}, gitData);
     expect(rulesResult).toEqual({when: "manual", allowFailure: false, variables: undefined});
 
     rules = [
         {if: "$VAR1 && (($VAR2 =~ /ci-skip-job-/ && $VAR2 =~ $VAR3) || ($VAR2 =~ /ci-skip-stage-/ && $VAR2 =~ $VAR3))", when: "manual"},
     ];
     variables = {VAR1: "val", VAR2: "ci-skip-stage-", VAR3: "/ci-skip-stage-/"};
-    rulesResult = Utils.getRulesResult({cwd: "", rules, variables}, gitData);
+    rulesResult = Utils.getRulesResult({argv, cwd: "", rules, variables}, gitData);
     expect(rulesResult).toEqual({when: "manual", allowFailure: false, variables: undefined});
 });
 
@@ -283,7 +286,7 @@ test("https://github.com/firecow/gitlab-ci-local/issues/424", () => {
         {if: "$CI_COMMIT_REF_NAME =~ /^(develop$|release\\/.*|master$)/", when: "manual"},
     ];
     const variables = {CI_COMMIT_REF_NAME: "develop"};
-    const rulesResult = Utils.getRulesResult({cwd: "", rules, variables}, gitData);
+    const rulesResult = Utils.getRulesResult({argv, cwd: "", rules, variables}, gitData);
     expect(rulesResult).toEqual({when: "manual", allowFailure: false, variables: undefined});
 });
 
@@ -292,7 +295,7 @@ test("https://github.com/firecow/gitlab-ci-local/issues/609", () => {
         {if: "$CI_COMMIT_REF_NAME =~ $PROD_REF", when: "manual"},
     ];
     const variables = {CI_COMMIT_REF_NAME: "main", PROD_REF: "/^(master|main)$/"};
-    const rulesResult = Utils.getRulesResult({cwd: "", rules, variables}, gitData);
+    const rulesResult = Utils.getRulesResult({argv, cwd: "", rules, variables}, gitData);
     expect(rulesResult).toEqual({when: "manual", allowFailure: false, variables: undefined});
 });
 
@@ -302,7 +305,7 @@ test("optional manual job", () => {
         {if: "$GITLAB_CI == 'false'"},
     ];
     const variables = {GITLAB_CI: "false"};
-    const rulesResult = Utils.getRulesResult({cwd: "", rules, variables}, gitData, jobWhen);
+    const rulesResult = Utils.getRulesResult({argv, cwd: "", rules, variables}, gitData, jobWhen);
     expect(rulesResult).toEqual({when: "manual", allowFailure: true, variables: undefined});
 });
 
@@ -312,6 +315,6 @@ test("https://github.com/firecow/gitlab-ci-local/issues/1252", () => {
         {if: "$VAR2 == 'val2'", when: "never"},
     ];
     const variables = {VAR1: "val1", VAR2: "val2"};
-    const rulesResult = Utils.getRulesResult({cwd: "", rules, variables}, gitData);
+    const rulesResult = Utils.getRulesResult({argv, cwd: "", rules, variables}, gitData);
     expect(rulesResult).toEqual({when: "on_success", allowFailure: false, variables: undefined});
 });
diff --git a/tests/test-cases/include-project-file/.gitlab-ci-3.yml b/tests/test-cases/include-project-file/.gitlab-ci-3.yml
new file mode 100644
index 0000000..83d407e
--- /dev/null
+++ b/tests/test-cases/include-project-file/.gitlab-ci-3.yml
@@ -0,0 +1,7 @@
+---
+include:
+  # https://gitlab.com/ANGkeith/gitlab-ci-local-test
+  - project: angkeith/gitlab-ci-local-test
+    ref: issue/#1011
+    file:
+      - .gitlab-ci.yml
diff --git a/tests/test-cases/include-project-file/integration.test.ts b/tests/test-cases/include-project-file/integration.test.ts
index 5394f13..a261d31 100644
--- a/tests/test-cases/include-project-file/integration.test.ts
+++ b/tests/test-cases/include-project-file/integration.test.ts
@@ -49,3 +49,22 @@ test("include:project should target default branch when ref is missing", async (
     const filteredStdout = writeStreams.stdoutLines.filter(f => f.startsWith("job >")).join("\n");
     expect(filteredStdout).toEqual(expected);
 });
+
+test("include:project should respect rules specified in included project", async () => {
+    const writeStreams = new WriteStreamsMock();
+
+    await handler({
+        file: ".gitlab-ci-3.yml",
+        cwd: "tests/test-cases/include-project-file",
+        noColor: true,
+        list: true,
+    }, writeStreams);
+
+
+    const expected = [
+        "name                                    description  stage   when        allow_failure  needs",
+        "should execute since rule eval to true               test    on_success  false      ",
+    ];
+
+    expect(writeStreams.stdoutLines.join("\n")).toEqual(expected.join("\n"));
+});
diff --git a/tests/test-cases/parent-child/.gitignore b/tests/test-cases/parent-child/.gitignore
new file mode 100644
index 0000000..11c5046
--- /dev/null
+++ b/tests/test-cases/parent-child/.gitignore
@@ -0,0 +1,1 @@
+generated-config.yml
diff --git a/tests/test-cases/parent-child/.gitlab-ci-1.yml b/tests/test-cases/parent-child/.gitlab-ci-1.yml
new file mode 100644
index 0000000..73d1846
--- /dev/null
+++ b/tests/test-cases/parent-child/.gitlab-ci-1.yml
@@ -0,0 +1,10 @@
+---
+variables:
+  GLOBAL_VAR: i am global var
+
+parent:
+  variables:
+    PARENT_JOB_VAR: i am parent job var
+  trigger:
+    include:
+      - local: child-pipeline.yml
diff --git a/tests/test-cases/parent-child/.gitlab-ci-2.yml b/tests/test-cases/parent-child/.gitlab-ci-2.yml
new file mode 100644
index 0000000..61b1d93
--- /dev/null
+++ b/tests/test-cases/parent-child/.gitlab-ci-2.yml
@@ -0,0 +1,12 @@
+---
+variables:
+  GLOBAL_VAR: i am global var
+
+parent:
+  variables:
+    PARENT_JOB_VAR: i am parent job var
+  inherit:
+    variables: false
+  trigger:
+    include:
+      - local: child-pipeline.yml
diff --git a/tests/test-cases/parent-child/.gitlab-ci-3.yml b/tests/test-cases/parent-child/.gitlab-ci-3.yml
new file mode 100644
index 0000000..fcc71b6
--- /dev/null
+++ b/tests/test-cases/parent-child/.gitlab-ci-3.yml
@@ -0,0 +1,16 @@
+---
+variables:
+  GLOBAL_VAR_1: i am global var 1
+  GLOBAL_VAR_2: i am global var 2
+  GLOBAL_VAR_3: i am global var 3
+
+parent:
+  variables:
+    PARENT_JOB_VAR: i am parent job var
+  inherit:
+    variables:
+      - GLOBAL_VAR_1
+      - GLOBAL_VAR_3
+  trigger:
+    include:
+      - local: child-pipeline.yml
diff --git a/tests/test-cases/parent-child/.gitlab-ci-4.yml b/tests/test-cases/parent-child/.gitlab-ci-4.yml
new file mode 100644
index 0000000..849f155
--- /dev/null
+++ b/tests/test-cases/parent-child/.gitlab-ci-4.yml
@@ -0,0 +1,10 @@
+---
+variables:
+  GLOBAL_VAR: i am global var
+
+parent:
+  variables:
+    JOB_VAR: i have a higher precedence
+  trigger:
+    include:
+      - local: child-pipeline.yml
diff --git a/tests/test-cases/parent-child/.gitlab-ci-5.yml b/tests/test-cases/parent-child/.gitlab-ci-5.yml
new file mode 100644
index 0000000..f5b01ae
--- /dev/null
+++ b/tests/test-cases/parent-child/.gitlab-ci-5.yml
@@ -0,0 +1,22 @@
+---
+generate-config:
+  image: busybox
+  stage: build
+  script:
+    - |
+      cat > generated-config.yml << EOF
+      ---
+      child:
+        image: busybox
+        script:
+        - echo i am generated
+      EOF
+  artifacts:
+    paths:
+      - generated-config.yml
+
+dynamic-pipeline:
+  trigger:
+    include:
+      - artifact: generated-config.yml
+        job: generate-config
diff --git a/tests/test-cases/parent-child/.gitlab-ci-6.yml b/tests/test-cases/parent-child/.gitlab-ci-6.yml
new file mode 100644
index 0000000..e81c5bd
--- /dev/null
+++ b/tests/test-cases/parent-child/.gitlab-ci-6.yml
@@ -0,0 +1,5 @@
+---
+parent:
+  trigger:
+    include:
+      - local: nested-child-1.yml
diff --git a/tests/test-cases/parent-child/.gitlab-ci-7.yml b/tests/test-cases/parent-child/.gitlab-ci-7.yml
new file mode 100644
index 0000000..d60c02e
--- /dev/null
+++ b/tests/test-cases/parent-child/.gitlab-ci-7.yml
@@ -0,0 +1,29 @@
+---
+generate-config:
+  image: busybox
+  stage: build
+  script:
+    - sleep 3
+    - |
+      cat > generated-config.yml << EOF
+      ---
+      child:
+        image: busybox
+        script:
+        - echo i am generated
+      EOF
+  artifacts:
+    paths:
+      - generated-config.yml
+
+dynamic-pipeline-1:
+  trigger:
+    include:
+      - artifact: generated-config.yml
+        job: generate-config
+
+dynamic-pipeline-2:
+  trigger:
+    include:
+      - artifact: generated-config.yml
+        job: generate-config
diff --git a/tests/test-cases/parent-child/child-pipeline.yml b/tests/test-cases/parent-child/child-pipeline.yml
new file mode 100644
index 0000000..f9d1f78
--- /dev/null
+++ b/tests/test-cases/parent-child/child-pipeline.yml
@@ -0,0 +1,7 @@
+---
+child:
+  image: busybox
+  variables:
+    JOB_VAR: "i am job var"
+  script:
+    - env | sort | grep VAR
diff --git a/tests/test-cases/parent-child/integration.test.ts b/tests/test-cases/parent-child/integration.test.ts
new file mode 100644
index 0000000..87ada0b
--- /dev/null
+++ b/tests/test-cases/parent-child/integration.test.ts
@@ -0,0 +1,117 @@
+import {WriteStreamsMock} from "../../../src/write-streams.js";
+import {handler} from "../../../src/handler.js";
+import {initSpawnSpy} from "../../mocks/utils.mock.js";
+import {WhenStatics} from "../../mocks/when-statics.js";
+
+beforeAll(() => {
+    initSpawnSpy(WhenStatics.all);
+});
+
+const cwd = "tests/test-cases/parent-child";
+
+test("by default inherit all global variables", async () => {
+
+    const writeStreams = new WriteStreamsMock();
+    await handler({
+        file: ".gitlab-ci-1.yml",
+        cwd: cwd,
+        noColor: true,
+    }, writeStreams);
+
+    const expected = `	[parent] -> child $ env | sort | grep VAR
+	[parent] -> child > GLOBAL_VAR=i am global var
+	[parent] -> child > JOB_VAR=i am job var
+	[parent] -> child > PARENT_JOB_VAR=i am parent job var`;
+
+    const filteredStdout = writeStreams.stdoutLines.filter(f => f.startsWith("	[parent] -> child $") || f.startsWith("	[parent] -> child >")).join("\n");
+    expect(filteredStdout).toEqual(expected);
+});
+
+test("prevent global variables from being inherited", async () => {
+
+    const writeStreams = new WriteStreamsMock();
+    await handler({
+        file: ".gitlab-ci-2.yml",
+        cwd: cwd,
+        noColor: true,
+    }, writeStreams);
+
+    const expected = `	[parent] -> child $ env | sort | grep VAR
+	[parent] -> child > JOB_VAR=i am job var
+	[parent] -> child > PARENT_JOB_VAR=i am parent job var`;
+
+    const filteredStdout = writeStreams.stdoutLines.filter(f => f.startsWith("	[parent] -> child $") || f.startsWith("	[parent] -> child >")).join("\n");
+    expect(filteredStdout).toEqual(expected);
+});
+
+test("inherit selective global variable", async () => {
+
+    const writeStreams = new WriteStreamsMock();
+    await handler({
+        file: ".gitlab-ci-4.yml",
+        cwd: cwd,
+        noColor: true,
+    }, writeStreams);
+
+    const expected = `	[parent] -> child $ env | sort | grep VAR
+	[parent] -> child > GLOBAL_VAR=i am global var
+	[parent] -> child > JOB_VAR=i have a higher precedence`;
+
+    const filteredStdout = writeStreams.stdoutLines.filter(f => f.startsWith("	[parent] -> child $") || f.startsWith("	[parent] -> child >")).join("\n");
+    expect(filteredStdout).toEqual(expected);
+});
+
+test("dynamic pipeline", async () => {
+
+    const writeStreams = new WriteStreamsMock();
+    await handler({
+        file: ".gitlab-ci-5.yml",
+        cwd: cwd,
+        noColor: true,
+        concurrency: 1,
+    }, writeStreams);
+
+    const expected = `	[dynamic-pipeline] -> child $ echo i am generated
+	[dynamic-pipeline] -> child > i am generated`;
+    const filteredStdout = writeStreams.stdoutLines.filter(f => f.startsWith("	[dynamic-pipeline] -> child $") || f.startsWith("	[dynamic-pipeline] -> child >")).join("\n");
+    expect(filteredStdout).toEqual(expected);
+});
+
+
+test("nested child pipeline", async () => {
+    const writeStreams = new WriteStreamsMock();
+    await handler({
+        file: ".gitlab-ci-6.yml",
+        cwd: cwd,
+        noColor: true,
+    }, writeStreams);
+
+    const expected = `		[parent -> nested-child-1] -> nested-child-2 $ echo i am nested child 2
+		[parent -> nested-child-1] -> nested-child-2 > i am nested child 2`;
+
+    const filteredStdout = writeStreams.stdoutLines.filter(f => f.startsWith("		[parent -> nested-child-1] -> nested-child-2 >") || f.startsWith("		[parent -> nested-child-1] -> nested-child-2 $")).join("\n");
+    expect(filteredStdout).toEqual(expected);
+});
+
+test("2 dynamic pipeline with concurrency set to 1", async () => {
+    const writeStreams = new WriteStreamsMock();
+    await handler({
+        file: ".gitlab-ci-7.yml",
+        cwd: cwd,
+        noColor: true,
+        concurrency: 1,
+    }, writeStreams);
+
+    const expected = `	[dynamic-pipeline-1] -> child $ echo i am generated
+	[dynamic-pipeline-1] -> child > i am generated
+	[dynamic-pipeline-2] -> child $ echo i am generated
+	[dynamic-pipeline-2] -> child > i am generated`;
+
+    const filteredStdout = writeStreams.stdoutLines.filter(f =>
+        f.startsWith("	[dynamic-pipeline-1] -> child >")
+            || f.startsWith("	[dynamic-pipeline-1] -> child $")
+            || f.startsWith("	[dynamic-pipeline-2] -> child >")
+            || f.startsWith("	[dynamic-pipeline-2] -> child $")
+    ).join("\n");
+    expect(filteredStdout).toEqual(expected);
+});
diff --git a/tests/test-cases/parent-child/nested-child-1.yml b/tests/test-cases/parent-child/nested-child-1.yml
new file mode 100644
index 0000000..4899214
--- /dev/null
+++ b/tests/test-cases/parent-child/nested-child-1.yml
@@ -0,0 +1,4 @@
+---
+nested-child-1:
+  trigger:
+    include: nested-child-2.yml
diff --git a/tests/test-cases/parent-child/nested-child-2.yml b/tests/test-cases/parent-child/nested-child-2.yml
new file mode 100644
index 0000000..82102db
--- /dev/null
+++ b/tests/test-cases/parent-child/nested-child-2.yml
@@ -0,0 +1,5 @@
+---
+nested-child-2:
+  image: busybox
+  script:
+    - echo i am nested child 2
diff --git a/tests/test-cases/rules-changes/.gitlab-ci-rules:changes:paths.yml b/tests/test-cases/rules-changes/.gitlab-ci-2.yml
similarity index 100%
rename from tests/test-cases/rules-changes/.gitlab-ci-rules:changes:paths.yml
rename to tests/test-cases/rules-changes/.gitlab-ci-2.yml
diff --git a/tests/test-cases/rules-changes/integration.rules-changes.test.ts b/tests/test-cases/rules-changes/integration.rules-changes.test.ts
index ae0d405..6ef7f89 100644
--- a/tests/test-cases/rules-changes/integration.rules-changes.test.ts
+++ b/tests/test-cases/rules-changes/integration.rules-changes.test.ts
@@ -32,7 +32,7 @@ test("rules:changes:paths (has changes)", async () => {
     }]);
     await handler({
         cwd: "tests/test-cases/rules-changes",
-        file: ".gitlab-ci-rules:changes:paths.yml",
+        file: ".gitlab-ci-2.yml",
     }, writeStreams);
 
     expect(writeStreams.stdoutLines).toEqual(expect.arrayContaining([
@@ -57,6 +57,23 @@ test("rules:changes (no changes)", async () => {
     ]));
 });
 
+test("rules:changes --no-evaluate-rule-changes (no changes)", async () => {
+    const writeStreams = new WriteStreamsMock();
+    initSyncSpawnSpy([{
+        cmdArgs: ["git", "diff", "--name-only", "origin/main"],
+        returnValue: {stdout: ""},
+    }]);
+    await handler({
+        cwd: "tests/test-cases/rules-changes",
+        evaluateRuleChanges: false,
+    }, writeStreams);
+
+    expect(writeStreams.stdoutLines).toEqual(expect.arrayContaining([
+        chalk`{blueBright alpine       } {greenBright >} Job is running`,
+        chalk`{blueBright matrix: [foo]} {greenBright >} Job is running`,
+    ]));
+});
+
 test("rules:changes:paths (no changes)", async () => {
     const writeStreams = new WriteStreamsMock();
     initSyncSpawnSpy([{
@@ -65,7 +82,7 @@ test("rules:changes:paths (no changes)", async () => {
     }]);
     await handler({
         cwd: "tests/test-cases/rules-changes",
-        file: ".gitlab-ci-rules:changes:paths.yml",
+        file: ".gitlab-ci-2.yml",
     }, writeStreams);
 
     expect(writeStreams.stdoutLines).not.toEqual(expect.arrayContaining([