Repository: firecow/gitlab-ci-local
Pull request #1194 (2024-04-22 01:15:57)

chore(deps): lock file maintenance all non-major
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|---|---|
|  |  | lockFileMaintenance | All locks refreshed |  |  |  |  |
| [@typescript-eslint/eslint-plugin](https://typescript-eslint.io/packages/eslint-plugin) ([source](https://togithub.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin)) | devDependencies | minor | [`7.7.0` -> `7.8.0`](https://renovatebot.com/diffs/npm/@typescript-eslint%2feslint-plugin/7.7.0/7.8.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@typescript-eslint%2feslint-plugin/7.8.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@typescript-eslint%2feslint-plugin/7.8.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@typescript-eslint%2feslint-plugin/7.7.0/7.8.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@typescript-eslint%2feslint-plugin/7.7.0/7.8.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@typescript-eslint/parser](https://typescript-eslint.io/packages/parser) ([source](https://togithub.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser)) | devDependencies | minor | [`7.7.0` -> `7.8.0`](https://renovatebot.com/diffs/npm/@typescript-eslint%2fparser/7.7.0/7.8.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@typescript-eslint%2fparser/7.8.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@typescript-eslint%2fparser/7.8.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@typescript-eslint%2fparser/7.7.0/7.8.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@typescript-eslint%2fparser/7.7.0/7.8.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [ajv](https://ajv.js.org) ([source](https://togithub.com/ajv-validator/ajv)) | dependencies | minor | [`8.12.0` -> `8.13.0`](https://renovatebot.com/diffs/npm/ajv/8.12.0/8.13.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/ajv/8.13.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/ajv/8.13.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/ajv/8.12.0/8.13.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/ajv/8.12.0/8.13.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>typescript-eslint/typescript-eslint (@&#8203;typescript-eslint/eslint-plugin)</summary>

### [`v7.8.0`](https://togithub.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#780-2024-04-29)

[Compare Source](https://togithub.com/typescript-eslint/typescript-eslint/compare/v7.7.1...v7.8.0)

##### 🩹 Fixes

-   **eslint-plugin:** \[no-unsafe-argument] handle  tagged templates

-   **eslint-plugin:** \[prefer-optional-chain] suggests optional chaining during strict null equality check

-   **eslint-plugin:** \[consistent-type-assertions] handle tagged templates

-   **eslint-plugin:** \[no-unsafe-return] handle union types

-   **eslint-plugin:** \[no-unused-vars] clear error report range

##### ❤️  Thank You

-   auvred
-   Josh Goldberg ✨
-   jsfm01
-   Kim Sang Du
-   YeonJuan

You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website.

### [`v7.7.1`](https://togithub.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#771-2024-04-22)

[Compare Source](https://togithub.com/typescript-eslint/typescript-eslint/compare/v7.7.0...v7.7.1)

##### 🩹 Fixes

-   **eslint-plugin:** \[no-unsafe-assignment] handle shorthand property assignment

-   **eslint-plugin:** \[explicit-function-return-type] fix checking wrong ancestor's return type

-   **eslint-plugin:** \[prefer-optional-chain] only look at left operand for `requireNullish`

-   **eslint-plugin:** \[no-for-in-array] refine report location

-   **eslint-plugin:** \[no-unnecessary-type-assertion] allow non-null assertion for void type

##### ❤️  Thank You

-   Abraham Guo
-   Kirk Waiblinger
-   YeonJuan

You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website.

</details>

<details>
<summary>typescript-eslint/typescript-eslint (@&#8203;typescript-eslint/parser)</summary>

### [`v7.8.0`](https://togithub.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/parser/CHANGELOG.md#780-2024-04-29)

[Compare Source](https://togithub.com/typescript-eslint/typescript-eslint/compare/v7.7.1...v7.8.0)

This was a version bump only for parser to align it with other projects, there were no code changes.

You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website.

### [`v7.7.1`](https://togithub.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/parser/CHANGELOG.md#771-2024-04-22)

[Compare Source](https://togithub.com/typescript-eslint/typescript-eslint/compare/v7.7.0...v7.7.1)

This was a version bump only for parser to align it with other projects, there were no code changes.

You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website.

</details>

<details>
<summary>ajv-validator/ajv (ajv)</summary>

### [`v8.13.0`](https://togithub.com/ajv-validator/ajv/releases/tag/v8.13.0)

[Compare Source](https://togithub.com/ajv-validator/ajv/compare/v8.12.0...v8.13.0)

-   add named exports
-   update dependencies
-   update node.js

</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://togithub.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- 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/firecow/gitlab-ci-local).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4zMTMuMSIsInVwZGF0ZWRJblZlciI6IjM3LjMyMS4yIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->


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=1194) **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=1194&resolved=false&inNewCodePeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/component_measures?id=firecow_gitlab-ci-local&pullRequest=1194&metric=new_accepted_issues&view=list)

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=1194&resolved=false&inNewCodePeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/no-data-16px.png '') No data about Coverage  
![](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=1194&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=firecow_gitlab-ci-local&pullRequest=1194)


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=1194) **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=1194&resolved=false&inNewCodePeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/component_measures?id=firecow_gitlab-ci-local&pullRequest=1194&metric=new_accepted_issues&view=list)

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=1194&resolved=false&inNewCodePeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/no-data-16px.png '') No data about Coverage  
![](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=1194&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=firecow_gitlab-ci-local&pullRequest=1194)


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=1194) **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=1194&resolved=false&inNewCodePeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/component_measures?id=firecow_gitlab-ci-local&pullRequest=1194&metric=new_accepted_issues&view=list)

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=1194&resolved=false&inNewCodePeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/no-data-16px.png '') No data about Coverage  
![](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=1194&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=firecow_gitlab-ci-local&pullRequest=1194)


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=1194) **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=1194&resolved=false&inNewCodePeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/component_measures?id=firecow_gitlab-ci-local&pullRequest=1194&metric=new_accepted_issues&view=list)

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=1194&resolved=false&inNewCodePeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/no-data-16px.png '') No data about Coverage  
![](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=1194&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=firecow_gitlab-ci-local&pullRequest=1194)


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=1194) **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=1194&resolved=false&inNewCodePeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/component_measures?id=firecow_gitlab-ci-local&pullRequest=1194&metric=new_accepted_issues&view=list)

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=1194&resolved=false&inNewCodePeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/no-data-16px.png '') No data about Coverage  
![](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=1194&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=firecow_gitlab-ci-local&pullRequest=1194)



Patch:
diff --git a/README.md b/README.md
index 1159b30..0afa48f 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,7 @@ Get rid of all those dev specific shell scripts and make files.
     * [docker-compose-nodejs](./examples/docker-compose-nodejs)
     * [docker-swarm-php](./examples/docker-swarm-php)
     * [docker-in-docker-build](./examples/docker-in-docker-build)
+    * [docker-in-docker with a local registry](./examples/docker-in-docker-build-with-local-registry)
 * [Installation](#installation)
 * [Convenience](#convenience)
     * [CLI options via shell](#cli-options-via-shell)
diff --git a/examples/docker-in-docker-build-with-local-registry/.gitignore b/examples/docker-in-docker-build-with-local-registry/.gitignore
new file mode 100644
index 0000000..5e2d7ba
--- /dev/null
+++ b/examples/docker-in-docker-build-with-local-registry/.gitignore
@@ -0,0 +1,1 @@
+.gitlab-ci-local/
diff --git a/examples/docker-in-docker-build-with-local-registry/.gitlab-ci-local-env b/examples/docker-in-docker-build-with-local-registry/.gitlab-ci-local-env
new file mode 100644
index 0000000..29cca56
--- /dev/null
+++ b/examples/docker-in-docker-build-with-local-registry/.gitlab-ci-local-env
@@ -0,0 +1,4 @@
+PRIVILEGED=true
+ULIMIT=8000:16000
+VOLUME=certs:/certs/client
+VARIABLE="DOCKER_TLS_CERTDIR=/certs"
diff --git a/examples/docker-in-docker-build-with-local-registry/.gitlab-ci-local-variables.yml b/examples/docker-in-docker-build-with-local-registry/.gitlab-ci-local-variables.yml
new file mode 100644
index 0000000..ba937fc
--- /dev/null
+++ b/examples/docker-in-docker-build-with-local-registry/.gitlab-ci-local-variables.yml
@@ -0,0 +1,2 @@
+---
+CI_REGISTRY_IMAGE: "10.11.22.33:5000/docker-in-docker-build-with-local-registry"
diff --git a/examples/docker-in-docker-build-with-local-registry/.gitlab-ci.yml b/examples/docker-in-docker-build-with-local-registry/.gitlab-ci.yml
new file mode 100644
index 0000000..db7813b
--- /dev/null
+++ b/examples/docker-in-docker-build-with-local-registry/.gitlab-ci.yml
@@ -0,0 +1,19 @@
+---
+# @Description Build alpine
+alpine-image:
+  services:
+    - name: docker:dind
+      command:
+        - --insecure-registry=10.11.22.33:5000
+  needs: []
+  image: docker:latest
+  stage: build
+  script:
+    - docker pull $CI_REGISTRY_IMAGE:latest || true
+    - docker build
+      --cache-from $CI_REGISTRY_IMAGE:latest
+      --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
+      --tag $CI_REGISTRY_IMAGE:latest
+      .
+    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
+    - docker push $CI_REGISTRY_IMAGE:latest
diff --git a/examples/docker-in-docker-build-with-local-registry/Dockerfile b/examples/docker-in-docker-build-with-local-registry/Dockerfile
new file mode 100644
index 0000000..233bf8e
--- /dev/null
+++ b/examples/docker-in-docker-build-with-local-registry/Dockerfile
@@ -0,0 +1,3 @@
+FROM alpine
+
+RUN apk add nano
diff --git a/examples/docker-in-docker-build-with-local-registry/README.md b/examples/docker-in-docker-build-with-local-registry/README.md
new file mode 100644
index 0000000..481c5f4
--- /dev/null
+++ b/examples/docker-in-docker-build-with-local-registry/README.md
@@ -0,0 +1,15 @@
+# Building Docker in Docker with a local registry example
+
+#### !!! Important !!!
+See how the project is configured in [.gitlab-ci-local-env](.gitlab-ci-local-env)
+
+#### Before you can run if you don't have any local registry.
+```bash
+docker network create gitlab-ci-local-registry --subnet=10.11.22.0/24
+docker run -d -p 5000:5000 --network gitlab-ci-local-registry --ip=10.11.22.33 --name gitlab-ci-local-registry registry
+```
+
+#### Start by calling.
+```bash
+gitlab-ci-local --cwd examples/docker-in-docker-build-with-local-registry/
+```
diff --git a/package-lock.json b/package-lock.json
index a001ba1..4a1e358 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -61,15 +61,6 @@
         "node": ">=16.10.0"
       }
     },
-    "node_modules/@aashutoshrathi/word-wrap": {
-      "version": "1.2.6",
-      "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
-      "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/@ampproject/remapping": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
@@ -106,21 +97,21 @@
       }
     },
     "node_modules/@babel/core": {
-      "version": "7.24.4",
-      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz",
-      "integrity": "sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==",
+      "version": "7.24.5",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz",
+      "integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==",
       "dev": true,
       "dependencies": {
         "@ampproject/remapping": "^2.2.0",
         "@babel/code-frame": "^7.24.2",
-        "@babel/generator": "^7.24.4",
+        "@babel/generator": "^7.24.5",
         "@babel/helper-compilation-targets": "^7.23.6",
-        "@babel/helper-module-transforms": "^7.23.3",
-        "@babel/helpers": "^7.24.4",
-        "@babel/parser": "^7.24.4",
+        "@babel/helper-module-transforms": "^7.24.5",
+        "@babel/helpers": "^7.24.5",
+        "@babel/parser": "^7.24.5",
         "@babel/template": "^7.24.0",
-        "@babel/traverse": "^7.24.1",
-        "@babel/types": "^7.24.0",
+        "@babel/traverse": "^7.24.5",
+        "@babel/types": "^7.24.5",
         "convert-source-map": "^2.0.0",
         "debug": "^4.1.0",
         "gensync": "^1.0.0-beta.2",
@@ -145,12 +136,12 @@
       }
     },
     "node_modules/@babel/generator": {
-      "version": "7.24.4",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.4.tgz",
-      "integrity": "sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==",
+      "version": "7.24.5",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.5.tgz",
+      "integrity": "sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==",
       "dev": true,
       "dependencies": {
-        "@babel/types": "^7.24.0",
+        "@babel/types": "^7.24.5",
         "@jridgewell/gen-mapping": "^0.3.5",
         "@jridgewell/trace-mapping": "^0.3.25",
         "jsesc": "^2.5.1"
@@ -231,16 +222,16 @@
       }
     },
     "node_modules/@babel/helper-module-transforms": {
-      "version": "7.23.3",
-      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz",
-      "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==",
+      "version": "7.24.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz",
+      "integrity": "sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==",
       "dev": true,
       "dependencies": {
         "@babel/helper-environment-visitor": "^7.22.20",
-        "@babel/helper-module-imports": "^7.22.15",
-        "@babel/helper-simple-access": "^7.22.5",
-        "@babel/helper-split-export-declaration": "^7.22.6",
-        "@babel/helper-validator-identifier": "^7.22.20"
+        "@babel/helper-module-imports": "^7.24.3",
+        "@babel/helper-simple-access": "^7.24.5",
+        "@babel/helper-split-export-declaration": "^7.24.5",
+        "@babel/helper-validator-identifier": "^7.24.5"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -250,33 +241,33 @@
       }
     },
     "node_modules/@babel/helper-plugin-utils": {
-      "version": "7.24.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz",
-      "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==",
+      "version": "7.24.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz",
+      "integrity": "sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ==",
       "dev": true,
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/helper-simple-access": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz",
-      "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==",
+      "version": "7.24.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz",
+      "integrity": "sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==",
       "dev": true,
       "dependencies": {
-        "@babel/types": "^7.22.5"
+        "@babel/types": "^7.24.5"
       },
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/helper-split-export-declaration": {
-      "version": "7.22.6",
-      "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
-      "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
+      "version": "7.24.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz",
+      "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==",
       "dev": true,
       "dependencies": {
-        "@babel/types": "^7.22.5"
+        "@babel/types": "^7.24.5"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -292,9 +283,9 @@
       }
     },
     "node_modules/@babel/helper-validator-identifier": {
-      "version": "7.22.20",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
-      "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
+      "version": "7.24.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz",
+      "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==",
       "dev": true,
       "engines": {
         "node": ">=6.9.0"
@@ -310,26 +301,26 @@
       }
     },
     "node_modules/@babel/helpers": {
-      "version": "7.24.4",
-      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.4.tgz",
-      "integrity": "sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==",
+      "version": "7.24.5",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.5.tgz",
+      "integrity": "sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==",
       "dev": true,
       "dependencies": {
         "@babel/template": "^7.24.0",
-        "@babel/traverse": "^7.24.1",
-        "@babel/types": "^7.24.0"
+        "@babel/traverse": "^7.24.5",
+        "@babel/types": "^7.24.5"
       },
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/highlight": {
-      "version": "7.24.2",
-      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz",
-      "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==",
+      "version": "7.24.5",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz",
+      "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==",
       "dev": true,
       "dependencies": {
-        "@babel/helper-validator-identifier": "^7.22.20",
+        "@babel/helper-validator-identifier": "^7.24.5",
         "chalk": "^2.4.2",
         "js-tokens": "^4.0.0",
         "picocolors": "^1.0.0"
@@ -410,9 +401,9 @@
       }
     },
     "node_modules/@babel/parser": {
-      "version": "7.24.4",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz",
-      "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==",
+      "version": "7.24.5",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz",
+      "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==",
       "dev": true,
       "bin": {
         "parser": "bin/babel-parser.js"
@@ -613,19 +604,19 @@
       }
     },
     "node_modules/@babel/traverse": {
-      "version": "7.24.1",
-      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz",
-      "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==",
+      "version": "7.24.5",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.5.tgz",
+      "integrity": "sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==",
       "dev": true,
       "dependencies": {
-        "@babel/code-frame": "^7.24.1",
-        "@babel/generator": "^7.24.1",
+        "@babel/code-frame": "^7.24.2",
+        "@babel/generator": "^7.24.5",
         "@babel/helper-environment-visitor": "^7.22.20",
         "@babel/helper-function-name": "^7.23.0",
         "@babel/helper-hoist-variables": "^7.22.5",
-        "@babel/helper-split-export-declaration": "^7.22.6",
-        "@babel/parser": "^7.24.1",
-        "@babel/types": "^7.24.0",
+        "@babel/helper-split-export-declaration": "^7.24.5",
+        "@babel/parser": "^7.24.5",
+        "@babel/types": "^7.24.5",
         "debug": "^4.3.1",
         "globals": "^11.1.0"
       },
@@ -634,13 +625,13 @@
       }
     },
     "node_modules/@babel/types": {
-      "version": "7.24.0",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz",
-      "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==",
+      "version": "7.24.5",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz",
+      "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==",
       "dev": true,
       "dependencies": {
-        "@babel/helper-string-parser": "^7.23.4",
-        "@babel/helper-validator-identifier": "^7.22.20",
+        "@babel/helper-string-parser": "^7.24.1",
+        "@babel/helper-validator-identifier": "^7.24.5",
         "to-fast-properties": "^2.0.0"
       },
       "engines": {
@@ -1600,16 +1591,16 @@
       "dev": true
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "7.7.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.7.0.tgz",
-      "integrity": "sha512-GJWR0YnfrKnsRoluVO3PRb9r5aMZriiMMM/RHj5nnTrBy1/wIgk76XCtCKcnXGjpZQJQRFtGV9/0JJ6n30uwpQ==",
+      "version": "7.8.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.8.0.tgz",
+      "integrity": "sha512-gFTT+ezJmkwutUPmB0skOj3GZJtlEGnlssems4AjkVweUPGj7jRwwqg0Hhg7++kPGJqKtTYx+R05Ftww372aIg==",
       "dev": true,
       "dependencies": {
         "@eslint-community/regexpp": "^4.10.0",
-        "@typescript-eslint/scope-manager": "7.7.0",
-        "@typescript-eslint/type-utils": "7.7.0",
-        "@typescript-eslint/utils": "7.7.0",
-        "@typescript-eslint/visitor-keys": "7.7.0",
+        "@typescript-eslint/scope-manager": "7.8.0",
+        "@typescript-eslint/type-utils": "7.8.0",
+        "@typescript-eslint/utils": "7.8.0",
+        "@typescript-eslint/visitor-keys": "7.8.0",
         "debug": "^4.3.4",
         "graphemer": "^1.4.0",
         "ignore": "^5.3.1",
@@ -1635,15 +1626,15 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "7.7.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.7.0.tgz",
-      "integrity": "sha512-fNcDm3wSwVM8QYL4HKVBggdIPAy9Q41vcvC/GtDobw3c4ndVT3K6cqudUmjHPw8EAp4ufax0o58/xvWaP2FmTg==",
+      "version": "7.8.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.8.0.tgz",
+      "integrity": "sha512-KgKQly1pv0l4ltcftP59uQZCi4HUYswCLbTqVZEJu7uLX8CTLyswqMLqLN+2QFz4jCptqWVV4SB7vdxcH2+0kQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/scope-manager": "7.7.0",
-        "@typescript-eslint/types": "7.7.0",
-        "@typescript-eslint/typescript-estree": "7.7.0",
-        "@typescript-eslint/visitor-keys": "7.7.0",
+        "@typescript-eslint/scope-manager": "7.8.0",
+        "@typescript-eslint/types": "7.8.0",
+        "@typescript-eslint/typescript-estree": "7.8.0",
+        "@typescript-eslint/visitor-keys": "7.8.0",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -1663,13 +1654,13 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "7.7.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.7.0.tgz",
-      "integrity": "sha512-/8INDn0YLInbe9Wt7dK4cXLDYp0fNHP5xKLHvZl3mOT5X17rK/YShXaiNmorl+/U4VKCVIjJnx4Ri5b0y+HClw==",
+      "version": "7.8.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.8.0.tgz",
+      "integrity": "sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.7.0",
-        "@typescript-eslint/visitor-keys": "7.7.0"
+        "@typescript-eslint/types": "7.8.0",
+        "@typescript-eslint/visitor-keys": "7.8.0"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -1680,13 +1671,13 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "7.7.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.7.0.tgz",
-      "integrity": "sha512-bOp3ejoRYrhAlnT/bozNQi3nio9tIgv3U5C0mVDdZC7cpcQEDZXvq8inrHYghLVwuNABRqrMW5tzAv88Vy77Sg==",
+      "version": "7.8.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.8.0.tgz",
+      "integrity": "sha512-H70R3AefQDQpz9mGv13Uhi121FNMh+WEaRqcXTX09YEDky21km4dV1ZXJIp8QjXc4ZaVkXVdohvWDzbnbHDS+A==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "7.7.0",
-        "@typescript-eslint/utils": "7.7.0",
+        "@typescript-eslint/typescript-estree": "7.8.0",
+        "@typescript-eslint/utils": "7.8.0",
         "debug": "^4.3.4",
         "ts-api-utils": "^1.3.0"
       },
@@ -1707,9 +1698,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "7.7.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.7.0.tgz",
-      "integrity": "sha512-G01YPZ1Bd2hn+KPpIbrAhEWOn5lQBrjxkzHkWvP6NucMXFtfXoevK82hzQdpfuQYuhkvFDeQYbzXCjR1z9Z03w==",
+      "version": "7.8.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.8.0.tgz",
+      "integrity": "sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==",
       "dev": true,
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -1720,13 +1711,13 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "7.7.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.7.0.tgz",
-      "integrity": "sha512-8p71HQPE6CbxIBy2kWHqM1KGrC07pk6RJn40n0DSc6bMOBBREZxSDJ+BmRzc8B5OdaMh1ty3mkuWRg4sCFiDQQ==",
+      "version": "7.8.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.8.0.tgz",
+      "integrity": "sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.7.0",
-        "@typescript-eslint/visitor-keys": "7.7.0",
+        "@typescript-eslint/types": "7.8.0",
+        "@typescript-eslint/visitor-keys": "7.8.0",
         "debug": "^4.3.4",
         "globby": "^11.1.0",
         "is-glob": "^4.0.3",
@@ -1748,17 +1739,17 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "7.7.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.7.0.tgz",
-      "integrity": "sha512-LKGAXMPQs8U/zMRFXDZOzmMKgFv3COlxUQ+2NMPhbqgVm6R1w+nU1i4836Pmxu9jZAuIeyySNrN/6Rc657ggig==",
+      "version": "7.8.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.8.0.tgz",
+      "integrity": "sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
         "@types/json-schema": "^7.0.15",
         "@types/semver": "^7.5.8",
-        "@typescript-eslint/scope-manager": "7.7.0",
-        "@typescript-eslint/types": "7.7.0",
-        "@typescript-eslint/typescript-estree": "7.7.0",
+        "@typescript-eslint/scope-manager": "7.8.0",
+        "@typescript-eslint/types": "7.8.0",
+        "@typescript-eslint/typescript-estree": "7.8.0",
         "semver": "^7.6.0"
       },
       "engines": {
@@ -1773,12 +1764,12 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "7.7.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.7.0.tgz",
-      "integrity": "sha512-h0WHOj8MhdhY8YWkzIF30R379y0NqyOHExI9N9KCzvmu05EgG4FumeYa3ccfKUSphyWkWQE1ybVrgz/Pbam6YA==",
+      "version": "7.8.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.8.0.tgz",
+      "integrity": "sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.7.0",
+        "@typescript-eslint/types": "7.8.0",
         "eslint-visitor-keys": "^3.4.3"
       },
       "engines": {
@@ -1796,59 +1787,59 @@
       "dev": true
     },
     "node_modules/@vue/compiler-core": {
-      "version": "3.4.22",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.22.tgz",
-      "integrity": "sha512-FBDRCBE/rFPA8OfTUrARx2c49N7zoImlGT7hsFikv0pZxQlFhffQwewpEXaLynZW0/DspVXmNA+QQ9dXINpWmg==",
+      "version": "3.4.26",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.26.tgz",
+      "integrity": "sha512-N9Vil6Hvw7NaiyFUFBPXrAyETIGlQ8KcFMkyk6hW1Cl6NvoqvP+Y8p1Eqvx+UdqsnrnI9+HMUEJegzia3mhXmQ==",
       "dev": true,
       "dependencies": {
-        "@babel/parser": "^7.24.1",
-        "@vue/shared": "3.4.22",
+        "@babel/parser": "^7.24.4",
+        "@vue/shared": "3.4.26",
         "entities": "^4.5.0",
         "estree-walker": "^2.0.2",
         "source-map-js": "^1.2.0"
       }
     },
     "node_modules/@vue/compiler-dom": {
-      "version": "3.4.22",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.22.tgz",
-      "integrity": "sha512-YkAS+jZc6Ip360kT3lZbMQZteiYBbHDSVKr94Jdd8Zjr7VjSkkXKAFFR/FW+2tNtBYXOps6xrWlOquy3GeYB0w==",
+      "version": "3.4.26",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.26.tgz",
+      "integrity": "sha512-4CWbR5vR9fMg23YqFOhr6t6WB1Fjt62d6xdFPyj8pxrYub7d+OgZaObMsoxaF9yBUHPMiPFK303v61PwAuGvZA==",
       "dev": true,
       "dependencies": {
-        "@vue/compiler-core": "3.4.22",
-        "@vue/shared": "3.4.22"
+        "@vue/compiler-core": "3.4.26",
+        "@vue/shared": "3.4.26"
       }
     },
     "node_modules/@vue/compiler-sfc": {
-      "version": "3.4.22",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.22.tgz",
-      "integrity": "sha512-Pncp5Vc8E2Ef1o5uveO8WA1IqM7rt0R1jN8D4qitQYOUxC97iITGYA8oMInQ3UcDS7ip+SegyA2HbAEB4V6NMQ==",
+      "version": "3.4.26",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.26.tgz",
+      "integrity": "sha512-It1dp+FAOCgluYSVYlDn5DtZBxk1NCiJJfu2mlQqa/b+k8GL6NG/3/zRbJnHdhV2VhxFghaDq5L4K+1dakW6cw==",
       "dev": true,
       "dependencies": {
-        "@babel/parser": "^7.24.1",
-        "@vue/compiler-core": "3.4.22",
-        "@vue/compiler-dom": "3.4.22",
-        "@vue/compiler-ssr": "3.4.22",
-        "@vue/shared": "3.4.22",
+        "@babel/parser": "^7.24.4",
+        "@vue/compiler-core": "3.4.26",
+        "@vue/compiler-dom": "3.4.26",
+        "@vue/compiler-ssr": "3.4.26",
+        "@vue/shared": "3.4.26",
         "estree-walker": "^2.0.2",
-        "magic-string": "^0.30.8",
+        "magic-string": "^0.30.10",
         "postcss": "^8.4.38",
         "source-map-js": "^1.2.0"
       }
     },
     "node_modules/@vue/compiler-ssr": {
-      "version": "3.4.22",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.22.tgz",
-      "integrity": "sha512-ycb2sL0SW6AkgVMrvaU/TIAEk7FQWyv/oYya44E/V9xURM+ij9Oev5bVobSS7GLJzkUieWW3SrYcK/PZpb5i4A==",
+      "version": "3.4.26",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.26.tgz",
+      "integrity": "sha512-FNwLfk7LlEPRY/g+nw2VqiDKcnDTVdCfBREekF8X74cPLiWHUX6oldktf/Vx28yh4STNy7t+/yuLoMBBF7YDiQ==",
       "dev": true,
       "dependencies": {
-        "@vue/compiler-dom": "3.4.22",
-        "@vue/shared": "3.4.22"
+        "@vue/compiler-dom": "3.4.26",
+        "@vue/shared": "3.4.26"
       }
     },
     "node_modules/@vue/shared": {
-      "version": "3.4.22",
-      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.22.tgz",
-      "integrity": "sha512-cg7R9XNk4ovV3bKka/1a464O2oY0l5Fyt0rwGR4hSJRPjUJ0WVjrPdsr4W0JbUriwiM8EKcCcCjeKN5pRMs2Zg==",
+      "version": "3.4.26",
+      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.26.tgz",
+      "integrity": "sha512-Fg4zwR0GNnjzodMt3KRy2AWGMKQXByl56+4HjN87soxLNU9P5xcJkstAlIeEF3cU6UYOzmJl1tV0dVPGIljCnQ==",
       "dev": true
     },
     "node_modules/abbrev": {
@@ -1912,14 +1903,14 @@
       }
     },
     "node_modules/ajv": {
-      "version": "8.12.0",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
-      "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
+      "version": "8.13.0",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz",
+      "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==",
       "dependencies": {
-        "fast-deep-equal": "^3.1.1",
+        "fast-deep-equal": "^3.1.3",
         "json-schema-traverse": "^1.0.0",
         "require-from-string": "^2.0.2",
-        "uri-js": "^4.2.2"
+        "uri-js": "^4.4.1"
       },
       "funding": {
         "type": "github",
@@ -2385,9 +2376,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001610",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001610.tgz",
-      "integrity": "sha512-QFutAY4NgaelojVMjY63o6XlZyORPaLfyMnsl3HgnWdJUcX6K0oaJymHjH8PT5Gk7sTm8rvC/c5COUQKXqmOMA==",
+      "version": "1.0.30001614",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001614.tgz",
+      "integrity": "sha512-jmZQ1VpmlRwHgdP1/uiKzgiAuGOfLEJsYFP4+GBou/QQ4U6IOJCB4NP1c+1p9RGLpwObcT94jA5/uO+F1vBbog==",
       "dev": true,
       "funding": [
         {
@@ -2497,9 +2488,9 @@
       }
     },
     "node_modules/cjs-module-lexer": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz",
-      "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==",
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz",
+      "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==",
       "dev": true
     },
     "node_modules/clean-stack": {
@@ -2915,9 +2906,9 @@
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.4.736",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.736.tgz",
-      "integrity": "sha512-Rer6wc3ynLelKNM4lOCg7/zPQj8tPOCB2hzD32PX9wd3hgRRi9MxEbmkFCokzcEhRVMiOVLjnL9ig9cefJ+6+Q==",
+      "version": "1.4.751",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.751.tgz",
+      "integrity": "sha512-2DEPi++qa89SMGRhufWTiLmzqyuGmNF3SK4+PQetW1JKiZdEpF4XQonJXJCzyuYSA6mauiMhbyVhqYAP45Hvfw==",
       "dev": true
     },
     "node_modules/emittery": {
@@ -4819,15 +4810,12 @@
       }
     },
     "node_modules/magic-string": {
-      "version": "0.30.9",
-      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.9.tgz",
-      "integrity": "sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==",
+      "version": "0.30.10",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz",
+      "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==",
       "dev": true,
       "dependencies": {
         "@jridgewell/sourcemap-codec": "^1.4.15"
-      },
-      "engines": {
-        "node": ">=12"
       }
     },
     "node_modules/make-dir": {
@@ -5070,9 +5058,9 @@
       "dev": true
     },
     "node_modules/node-abi": {
-      "version": "3.57.0",
-      "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.57.0.tgz",
-      "integrity": "sha512-Dp+A9JWxRaKuHP35H77I4kCKesDy5HUDEmScia2FyncMTOXASMyg251F5PhFoDA5uqBrDDffiLpbqnrZmNXW+g==",
+      "version": "3.62.0",
+      "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.62.0.tgz",
+      "integrity": "sha512-CPMcGa+y33xuL1E0TcNIu4YyaZCxnnvkVaEXrsosR3FxN+fV8xvb7Mzpb7IgKler10qeMkE6+Dp8qJhpzdq35g==",
       "dev": true,
       "dependencies": {
         "semver": "^7.3.5"
@@ -5259,17 +5247,17 @@
       }
     },
     "node_modules/optionator": {
-      "version": "0.9.3",
-      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
-      "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
+      "version": "0.9.4",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+      "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
       "dev": true,
       "dependencies": {
-        "@aashutoshrathi/word-wrap": "^1.2.3",
         "deep-is": "^0.1.3",
         "fast-levenshtein": "^2.0.6",
         "levn": "^0.4.1",
         "prelude-ls": "^1.2.1",
-        "type-check": "^0.4.0"
+        "type-check": "^0.4.0",
+        "word-wrap": "^1.2.5"
       },
       "engines": {
         "node": ">= 0.8.0"
@@ -5901,9 +5889,9 @@
       }
     },
     "node_modules/react-is": {
-      "version": "18.2.0",
-      "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
-      "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
+      "version": "18.3.1",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+      "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
       "dev": true
     },
     "node_modules/readable-stream": {
@@ -6776,6 +6764,15 @@
         "node": ">= 8"
       }
     },
+    "node_modules/word-wrap": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+      "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/wordwrap": {
       "version": "0.0.3",
       "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
diff --git a/src/argv.ts b/src/argv.ts
index d4caae7..d9f5010 100644
--- a/src/argv.ts
+++ b/src/argv.ts
@@ -112,6 +112,10 @@ export class Argv {
         return typeof val == "string" ? val.split(" ") : val;
     }
 
+    get pullPolicy (): string {
+        return this.map.get("pullPolicy") ?? "if-not-present";
+    }
+
     get remoteVariables (): string {
         return this.map.get("remoteVariables");
     }
diff --git a/src/git-data.ts b/src/git-data.ts
index 00193be..f58f701 100644
--- a/src/git-data.ts
+++ b/src/git-data.ts
@@ -82,24 +82,53 @@ export class GitData {
 
     private async initRemoteData (cwd: string, writeStreams: WriteStreams): Promise<void> {
         try {
-            const {stdout: gitRemote} = await Utils.spawn(["git", "remote", "-v"], cwd);
-            const gitRemoteMatch = /(?<schema>git|https?)(?::\/\/|@)(?<host>[^:/]*)(:(?<port>\d+)\/|:|\/)(?<group>.*)\/(?<project>[^ .]+)(?:\.git)?.*/.exec(gitRemote);
-
-            assert(gitRemoteMatch?.groups != null, "git remote -v didn't provide valid matches");
+            let gitRemoteMatch;
+            let gitRemote;
+            try {
+                // NOTE: For power user that wishes to customize the remote url
+                const res = await Utils.spawn(["git", "remote", "get-url", "gcl-origin"], cwd);
+                gitRemote = res.stdout;
+            } catch {
+                const res = await Utils.spawn(["git", "remote", "get-url", "origin"], cwd);
+                gitRemote = res.stdout;
+            }
 
-            this.remote.schema = gitRemoteMatch.groups.schema;
-            if (this.remote.schema === "git") {
+            if (gitRemote.startsWith("http")) {
+                gitRemoteMatch = /(?<schema>https?):\/\/(?:(\w+):([\w-]+)@)?(?<host>[^/:]+):?(?<port>\d+)?\/(?<group>\S+)\/(?<project>\S+)\.git/.exec(gitRemote); // regexr.com/7ve8l
+                assert(gitRemoteMatch?.groups != null, "git remote get-url origin didn't provide valid matches");
+
+                let port = "443";
+                if (gitRemoteMatch.groups.schema === "https") {
+                    port = gitRemoteMatch.groups.port ?? "443";
+                } else if (gitRemoteMatch.groups.schema === "http") {
+                    port = gitRemoteMatch.groups.port ?? "80";
+                }
+                this.remote.host = gitRemoteMatch.groups.host;
+                this.remote.group = gitRemoteMatch.groups.group;
+                this.remote.project = gitRemoteMatch.groups.project;
+                this.remote.schema = gitRemoteMatch.groups.schema;
+                this.remote.port = port;
+            } else if (gitRemote.startsWith("ssh://")) {
+                gitRemoteMatch = /(?<schema>ssh):\/\/(\w+)@(?<host>[^/:]+):?(?<port>\d+)?\/(?<group>\S+)\/(?<project>\S+)\.git/.exec(gitRemote); // regexr.com/7vjq4
+                assert(gitRemoteMatch?.groups != null, "git remote get-url origin didn't provide valid matches");
+
+                this.remote.host = gitRemoteMatch.groups.host;
+                this.remote.group = gitRemoteMatch.groups.group;
+                this.remote.project = gitRemoteMatch.groups.project;
+                this.remote.schema = gitRemoteMatch.groups.schema;
                 this.remote.port = gitRemoteMatch.groups.port ?? "22";
+            } else {
+                gitRemoteMatch = /(?<username>\S+)@(?<host>[^:]+):(?<group>\S+)\/(?<project>[^ .]+)\.git/.exec(gitRemote); // regexr.com/7vjoq
+                assert(gitRemoteMatch?.groups != null, "git remote get-url origin didn't provide valid matches");
+
+                const {stdout} = await Utils.spawn(["ssh", "-G", `${gitRemoteMatch.groups.username}@${gitRemoteMatch.groups.host}`]);
+                const port = stdout.split("\n").filter((line) => line.startsWith("port "))[0].split(" ")[1];
+                this.remote.host = gitRemoteMatch.groups.host;
+                this.remote.group = gitRemoteMatch.groups.group;
+                this.remote.project = gitRemoteMatch.groups.project;
+                this.remote.schema = "git";
+                this.remote.port = port;
             }
-            if (this.remote.schema === "https") {
-                this.remote.port = gitRemoteMatch.groups.port ?? "443";
-            }
-            if (this.remote.schema === "http") {
-                this.remote.port = gitRemoteMatch.groups.port ?? "80";
-            }
-            this.remote.host = gitRemoteMatch.groups.host;
-            this.remote.group = gitRemoteMatch.groups.group;
-            this.remote.project = gitRemoteMatch.groups.project;
         } catch (e) {
             if (e instanceof AssertionError) {
                 writeStreams.stderr(chalk`{yellow ${e.message}}\n`);
diff --git a/src/index.ts b/src/index.ts
index 9f4ee02..b17e77a 100755
--- a/src/index.ts
+++ b/src/index.ts
@@ -199,6 +199,11 @@ process.on("SIGUSR2", async () => await cleanupJobResources(jobs));
             description: "Add extra docker host entries",
             requiresArg: false,
         })
+        .option("pull-policy", {
+            type: "string",
+            description: "Set image pull-policy (always or if-not-present)",
+            requiresArg: false,
+        })
         .option("fetch-includes", {
             type: "boolean",
             description: "Fetch all external includes one more time",
diff --git a/src/job.ts b/src/job.ts
index d6081db..d201d32 100644
--- a/src/job.ts
+++ b/src/job.ts
@@ -829,14 +829,23 @@ export class Job {
     }
 
     private async pullImage (writeStreams: WriteStreams, imageToPull: string) {
-        try {
-            await Utils.spawn([this.argv.containerExecutable, "image", "inspect", imageToPull]);
-        } catch (e: any) {
+        const pullPolicy = this.argv.pullPolicy;
+        const actualPull = async () => {
             const time = process.hrtime();
             await Utils.spawn([this.argv.containerExecutable, "pull", imageToPull]);
             const endTime = process.hrtime(time);
             writeStreams.stdout(chalk`${this.formattedJobName} {magentaBright pulled} ${imageToPull} in {magenta ${prettyHrtime(endTime)}}\n`);
             this.refreshLongRunningSilentTimeout(writeStreams);
+        };
+
+        if (pullPolicy === "always") {
+            await actualPull();
+            return;
+        }
+        try {
+            await Utils.spawn([this.argv.containerExecutable, "image", "inspect", imageToPull]);
+        } catch (e: any) {
+            await actualPull();
         }
     }
 
diff --git a/src/parser-includes.ts b/src/parser-includes.ts
index 0675293..911fef0 100644
--- a/src/parser-includes.ts
+++ b/src/parser-includes.ts
@@ -104,6 +104,23 @@ export class ParserIncludes {
 
                     includeDatas = includeDatas.concat(await this.init(fileDoc, opts));
                 }
+            } else if (value["component"]) {
+                const {domain, projectPath, componentName, ref} = this.parseIncludeComponent(value["component"]);
+                // converts component to project
+                const files = [`${componentName}.yml`, `${componentName}/template.yml`, null];
+                for (const f of files) {
+                    assert(f !== null, `This GitLab CI configuration is invalid: component: \`${value["component"]}\`. One of the file [${files}] must exists in \`${domain}/${projectPath}\``);
+                    if (!(await Utils.remoteFileExist(f, ref, domain, projectPath, gitData.remote.schema))) continue;
+                    const fileDoc = {
+                        include: {
+                            project: projectPath,
+                            file: f,
+                            ref: ref,
+                        },
+                    };
+                    includeDatas = includeDatas.concat(await this.init(fileDoc, opts));
+                    break;
+                }
             } else if (value["template"]) {
                 const {project, ref, file, domain} = this.covertTemplateToProjectFile(value["template"]);
                 const fsUrl = Utils.fsUrl(`https://${domain}/${project}/-/raw/${ref}/${file}`);
@@ -164,6 +181,21 @@ export class ParserIncludes {
         };
     }
 
+    static parseIncludeComponent (component: string): {domain: string; projectPath: string; componentName: string; ref: string} {
+        assert(!component.includes("://"), `This GitLab CI configuration is invalid: component: \`${component}\` should not contain protocol`);
+        // eslint-disable-next-line no-useless-escape
+        const pattern = /(?<domain>[^/\s]+)\/(?<projectPath>.+)\/(?<componentName>[^@]+)@(?<ref>.+)/; // regexr.com/7v7hm
+        const gitRemoteMatch = pattern.exec(component);
+
+        if (gitRemoteMatch?.groups == null) throw new Error(`This is a bug, please create a github issue if this is something you're expecting to work. input: ${component}`);
+        return {
+            domain: gitRemoteMatch.groups["domain"],
+            projectPath: gitRemoteMatch.groups["projectPath"],
+            componentName: `templates/${gitRemoteMatch.groups["componentName"]}`,
+            ref: gitRemoteMatch.groups["ref"],
+        };
+    }
+
     static async downloadIncludeRemote (cwd: string, stateDir: string, url: string, fetchIncludes: boolean): Promise<void> {
         const fsUrl = Utils.fsUrl(url);
         try {
diff --git a/src/utils.ts b/src/utils.ts
index 6fe2cd5..badf985 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -7,6 +7,7 @@ import execa from "execa";
 import assert from "assert";
 import {CICDVariable} from "./variables-from-files";
 import globby from "globby";
+import axios from "axios";
 
 type RuleResultOpt = {
     cwd: string;
@@ -259,4 +260,29 @@ export class Utils {
     static isObject (v: any) {
         return Object.getPrototypeOf(v) === Object.prototype;
     }
+
+    static async remoteFileExist (file: string, ref: string, domain: string, projectPath: string, protocol: string) {
+        switch (protocol) {
+            case "git":
+                try {
+                    await Utils.bash(`git archive --remote=ssh://git@${domain}/${projectPath}.git ${ref} ${file} > /dev/null`);
+                    return true;
+                } catch (e: any) {
+                    if (!e.stderr.includes(`remote: fatal: pathspec '${file}' did not match any files`)) throw new Error(e);
+                    return false;
+                }
+
+            case "http":
+            case "https": {
+                try {
+                    const {status} = await axios.get(`${protocol}://${domain}/${projectPath}/-/raw/${ref}/${file}`);
+                    return (status === 200);
+                } catch (e) {
+                    return false;
+                }
+            }
+            default:
+                throw new Error(`${protocol} not supported!`);
+        }
+    }
 }


Test patch:
diff --git a/tests/git-data.test.ts b/tests/git-data.test.ts
new file mode 100644
index 0000000..80c50ec
--- /dev/null
+++ b/tests/git-data.test.ts
@@ -0,0 +1,112 @@
+import {GitData} from "../src/git-data";
+import {WriteStreamsMock} from "../src/write-streams";
+import {initSpawnSpy, initSpawnSpyReject} from "./mocks/utils.mock";
+
+
+const tests = [
+    {
+        input: "ssh://git@gitlab.example.com:5555/myrepo/myproject.git",
+        expected: {
+            schema: "ssh",
+            port: "5555",
+            host: "gitlab.example.com",
+            group: "myrepo",
+            project: "myproject",
+        },
+    },
+    {
+        input: "ssh://git@gitlab.example.com/myrepo/myproject.git",
+        expected: {
+            schema: "ssh",
+            port: "22",
+            host: "gitlab.example.com",
+            group: "myrepo",
+            project: "myproject",
+        },
+    },
+    {
+        input: "git@github.com:firecow/gitlab-ci-local.git",
+        expected: {
+            schema: "git",
+            port: "22",
+            host: "github.com",
+            group: "firecow",
+            project: "gitlab-ci-local",
+        },
+    },
+    {
+        input: "https://oauth2:glpat-qwerty12345@somegitlab.com/vendor/package.git",
+        expected: {
+            schema: "https",
+            port: "443",
+            host: "somegitlab.com",
+            group: "vendor",
+            project: "package",
+        },
+    },
+    {
+        input: "https://oauth2:glpat-qwerty12345@somegitlab.com:8080/vendor/package.git",
+        expected: {
+            schema: "https",
+            port: "8080",
+            host: "somegitlab.com",
+            group: "vendor",
+            project: "package",
+        },
+    },
+    {
+        input: "https://example.com:8443/1/2/3package.git",
+        expected: {
+            schema: "https",
+            port: "8443",
+            host: "example.com",
+            group: "1/2",
+            project: "3package",
+        },
+    },
+    {
+        input: "https://example.com:1/vendor/package.git",
+        expected: {
+            schema: "https",
+            port: "1",
+            host: "example.com",
+            group: "vendor",
+            project: "package",
+        },
+    },
+    {
+        input: "http://example.com/vendor/package.git",
+        expected: {
+            schema: "http",
+            port: "80",
+            host: "example.com",
+            group: "vendor",
+            project: "package",
+        },
+    },
+];
+
+
+describe("initRemoteData", () => {
+    tests.forEach((t) => {
+        test(t.input, async () => {
+            const writeStreams = new WriteStreamsMock();
+
+            initSpawnSpyReject([{
+                cmdArgs: "git remote get-url gcl-origin".split(" "),
+                rejection: {
+                    exitCode: 2,
+                    stderr: "error: No such remote 'gcl-origin'",
+                },
+            }]);
+            initSpawnSpy([{
+                cmdArgs: "git remote get-url origin".split(" "),
+                returnValue: {stdout: t.input},
+            }]);
+
+            const {remote} = await GitData.init("tests", writeStreams);
+
+            expect(remote).toStrictEqual(t.expected);
+        });
+    });
+});
diff --git a/tests/mocks/utils.mock.ts b/tests/mocks/utils.mock.ts
index 8078a4c..93d0535 100644
--- a/tests/mocks/utils.mock.ts
+++ b/tests/mocks/utils.mock.ts
@@ -1,11 +1,12 @@
 import {when} from "jest-when";
 import {Utils} from "../../src/utils";
 
-export function initBashSpyReject (spyMocks: {cmd: string; rejection: string}[]) {
+export function initBashSpyReject (spyMocks: {cmd: string; rejection: any}[]) {
     const spyOn = jest.spyOn(Utils, "bash");
 
     for (const spyMock of spyMocks) {
-        when(spyOn).calledWith(spyMock.cmd, expect.any(String)).mockRejectedValue(new Error(spyMock.rejection));
+        when(spyOn).calledWith(spyMock.cmd, expect.any(String)).mockRejectedValue(spyMock.rejection);
+        when(spyOn).calledWith(spyMock.cmd).mockRejectedValue(spyMock.rejection);
     }
 }
 
@@ -14,6 +15,7 @@ export function initBashSpy (spyMocks: {cmd: string; returnValue: any}[]) {
 
     for (const spyMock of spyMocks) {
         when(spyOn).calledWith(spyMock.cmd, expect.any(String)).mockResolvedValue(spyMock.returnValue);
+        when(spyOn).calledWith(spyMock.cmd).mockResolvedValue(spyMock.returnValue);
     }
 }
 
@@ -22,5 +24,14 @@ export function initSpawnSpy (spyMocks: {cmdArgs: string[]; returnValue: any}[])
 
     for (const spyMock of spyMocks) {
         when(spyOn).calledWith(spyMock.cmdArgs, expect.any(String)).mockResolvedValue(spyMock.returnValue);
+        when(spyOn).calledWith(spyMock.cmdArgs).mockResolvedValue(spyMock.returnValue);
+    }
+}
+
+export function initSpawnSpyReject (spyMocks: {cmdArgs: string[]; rejection: any}[]) {
+    const spyOn = jest.spyOn(Utils, "spawn");
+
+    for (const spyMock of spyMocks) {
+        when(spyOn).calledWith(spyMock.cmdArgs, expect.any(String)).mockRejectedValue(spyMock.rejection);
     }
 }
diff --git a/tests/mocks/when-statics.ts b/tests/mocks/when-statics.ts
index 1436e8c..371ebff 100644
--- a/tests/mocks/when-statics.ts
+++ b/tests/mocks/when-statics.ts
@@ -13,8 +13,8 @@ export class WhenStatics {
         returnValue: {stdout: "990\n"},
     };
     public static readonly mockGitRemote = {
-        cmdArgs: ["git", "remote", "-v"],
-        returnValue: {stdout: "origin\tgit@gitlab.com:gcl/test-project.git (fetch)\norigin\tgit@gitlab.com:gcl/test-project.git (push)\n"},
+        cmdArgs: ["git", "remote", "get-url", "origin"],
+        returnValue: {stdout: "git@gitlab.com:gcl/test-project.git"},
     };
     public static readonly mockGitDefaultBranch = {
         cmdArgs: ["git", "symbolic-ref", "--short", "refs/remotes/origin/HEAD"],
@@ -44,4 +44,10 @@ export class WhenStatics {
         WhenStatics.mockGitCommitShaShort,
     ];
 
+    public static readonly mockGitRemoteHttp = {
+        cmdArgs: ["git", "remote", "get-url", "origin"],
+        returnValue: {
+            stdout: "https://gitlab.com/gcl/test-project.git",
+        },
+    };
 }
diff --git a/tests/test-cases/cache-docker-mount-globstar/integration.cache-docker-mount-invalid.test.ts b/tests/test-cases/cache-docker-mount-globstar/integration.cache-docker-mount-invalid.test.ts
index 1db326f..13ea22d 100644
--- a/tests/test-cases/cache-docker-mount-globstar/integration.cache-docker-mount-invalid.test.ts
+++ b/tests/test-cases/cache-docker-mount-globstar/integration.cache-docker-mount-invalid.test.ts
@@ -9,8 +9,8 @@ jest.setTimeout(30000);
 
 beforeAll(() => {
     const spyGitRemote = {
-        cmdArgs: ["git", "remote", "-v"],
-        returnValue: {stdout: "origin\tgit@gitlab.com:gcl/cache-docker-mount-invalid.git (fetch)\norigin\tgit@gitlab.com:gcl/cache-docker-mount-invalid.git (push)\n"},
+        cmdArgs: ["git", "remote", "get-url", "origin"],
+        returnValue: {stdout: "git@gitlab.com:gcl/cache-docker-mount-invalid.git"},
     };
     initSpawnSpy([...WhenStatics.all, spyGitRemote]);
 });
diff --git a/tests/test-cases/cache-docker-mount/integration.cache-docker-mount.test.ts b/tests/test-cases/cache-docker-mount/integration.cache-docker-mount.test.ts
index a131710..f6fe7ca 100644
--- a/tests/test-cases/cache-docker-mount/integration.cache-docker-mount.test.ts
+++ b/tests/test-cases/cache-docker-mount/integration.cache-docker-mount.test.ts
@@ -9,8 +9,8 @@ jest.setTimeout(45000);
 
 beforeAll(() => {
     const spyGitRemote = {
-        cmdArgs: ["git", "remote", "-v"],
-        returnValue: {stdout: "origin\tgit@gitlab.com:gcl/cache-docker-mount.git (fetch)\norigin\tgit@gitlab.com:gcl/cache-docker-mount.git (push)\n"},
+        cmdArgs: ["git", "remote", "get-url", "origin"],
+        returnValue: {stdout: "git@gitlab.com:gcl/cache-docker-mount.git"},
     };
     initSpawnSpy([...WhenStatics.all, spyGitRemote]);
 });
diff --git a/tests/test-cases/custom-home/integration.custom-home.test.ts b/tests/test-cases/custom-home/integration.custom-home.test.ts
index d68d57b..43dd0f1 100644
--- a/tests/test-cases/custom-home/integration.custom-home.test.ts
+++ b/tests/test-cases/custom-home/integration.custom-home.test.ts
@@ -6,8 +6,8 @@ import {WhenStatics} from "../../mocks/when-statics";
 
 beforeAll(() => {
     const spyGitRemote = {
-        cmdArgs: ["git", "remote", "-v"],
-        returnValue: {stdout: "origin\tgit@gitlab.com:gcl/custom-home.git (fetch)\norigin\tgit@gitlab.com:gcl/custom-home.git (push)\n"},
+        cmdArgs: ["git", "remote", "get-url", "origin"],
+        returnValue: {stdout: "git@gitlab.com:gcl/custom-home.git"},
     };
     initSpawnSpy([...WhenStatics.all, spyGitRemote]);
 });
diff --git a/tests/test-cases/include-component/component/.gitlab-ci.yml b/tests/test-cases/include-component/component/.gitlab-ci.yml
new file mode 100644
index 0000000..b34abff
--- /dev/null
+++ b/tests/test-cases/include-component/component/.gitlab-ci.yml
@@ -0,0 +1,14 @@
+---
+include:
+  - component: gitlab.com/components/go/full-pipeline@0.3.1
+    inputs:
+      stage_format: format
+      stage_build: build
+      stage_test: test
+      go_version: latest
+
+stages:
+  - format
+  - build
+  - test
+  - latest
diff --git a/tests/test-cases/include-component/integration.include-component.test.ts b/tests/test-cases/include-component/integration.include-component.test.ts
new file mode 100644
index 0000000..f832d42
--- /dev/null
+++ b/tests/test-cases/include-component/integration.include-component.test.ts
@@ -0,0 +1,70 @@
+import {WriteStreamsMock} from "../../../src/write-streams";
+import {handler} from "../../../src/handler";
+import assert, {AssertionError} from "assert";
+import {initSpawnSpy} from "../../mocks/utils.mock";
+import {WhenStatics} from "../../mocks/when-statics";
+
+beforeAll(() => {
+    initSpawnSpy(WhenStatics.all);
+});
+
+test.concurrent("include-component no component template file (protocol: https)", async () => {
+    initSpawnSpy([WhenStatics.mockGitRemoteHttp]);
+
+    const writeStreams = new WriteStreamsMock();
+    try {
+        await handler({
+            cwd: "tests/test-cases/include-component/no-component-template-file",
+            preview: true,
+        }, writeStreams);
+        expect(true).toBe(false);
+    } catch (e: any) {
+        assert(e instanceof AssertionError, `Unexpected error thrown:\n ${e}`);
+        expect(e.message).toBe("This GitLab CI configuration is invalid: component: `gitlab.com/components/go/potato@0.3.1`. One of the file [templates/potato.yml,templates/potato/template.yml,] must exists in `gitlab.com/components/go`");
+    }
+});
+
+test.concurrent("include-component component (protocol: https)", async () => {
+    initSpawnSpy([WhenStatics.mockGitRemoteHttp]);
+
+    const writeStreams = new WriteStreamsMock();
+    await handler({
+        cwd: "tests/test-cases/include-component/component",
+        preview: true,
+    }, writeStreams);
+
+
+    const expected = `---
+stages:
+  - .pre
+  - format
+  - build
+  - test
+  - latest
+  - .post
+format-latest:
+  image:
+    name: golang:latest
+  stage: format
+  script:
+    - go fmt $(go list ./... | grep -v /vendor/)
+    - go vet $(go list ./... | grep -v /vendor/)
+build-latest:
+  image:
+    name: golang:latest
+  stage: build
+  script:
+    - mkdir -p mybinaries
+    - go build -o mybinaries ./...
+  artifacts:
+    paths:
+      - mybinaries
+test-latest:
+  image:
+    name: golang:latest
+  stage: test
+  script:
+    - go test -race $(go list ./... | grep -v /vendor/)`;
+
+    expect(writeStreams.stdoutLines[0]).toEqual(expected);
+});
diff --git a/tests/test-cases/include-component/no-component-template-file/.gitlab-ci.yml b/tests/test-cases/include-component/no-component-template-file/.gitlab-ci.yml
new file mode 100644
index 0000000..edfcff2
--- /dev/null
+++ b/tests/test-cases/include-component/no-component-template-file/.gitlab-ci.yml
@@ -0,0 +1,5 @@
+---
+include:
+  - component: gitlab.com/components/go/potato@0.3.1
+stages:
+  - format
diff --git a/tests/test-cases/include-invalid-project-file-ref/integration.include-invalid-project-ref-file.test.ts b/tests/test-cases/include-invalid-project-file-ref/integration.include-invalid-project-ref-file.test.ts
index c86b626..fdb2194 100644
--- a/tests/test-cases/include-invalid-project-file-ref/integration.include-invalid-project-ref-file.test.ts
+++ b/tests/test-cases/include-invalid-project-file-ref/integration.include-invalid-project-ref-file.test.ts
@@ -7,8 +7,8 @@ import {WhenStatics} from "../../mocks/when-statics";
 test("include-invalid-project-file-ref", async () => {
     try {
         const spyGitRemote = {
-            cmdArgs: ["git", "remote", "-v"],
-            returnValue: {stdout: "origin\tgit@gitlab.com:gcl/test-hest.git (fetch)\norigin\tgit@gitlab.com:gcl/test-hest.git (push)\n"},
+            cmdArgs: ["git", "remote", "get-url", "origin"],
+            returnValue: {stdout: "git@gitlab.com:gcl/test-hest.git"},
         };
 
         initSpawnSpy([...WhenStatics.all, spyGitRemote]);
diff --git a/tests/test-cases/include-project-file-ref-with-inner-local/integration.include-project-file-ref-with-inner-local.test.ts b/tests/test-cases/include-project-file-ref-with-inner-local/integration.include-project-file-ref-with-inner-local.test.ts
index 7eeb0ba..4f1a2e6 100644
--- a/tests/test-cases/include-project-file-ref-with-inner-local/integration.include-project-file-ref-with-inner-local.test.ts
+++ b/tests/test-cases/include-project-file-ref-with-inner-local/integration.include-project-file-ref-with-inner-local.test.ts
@@ -10,8 +10,8 @@ test("include-project-file-ref-with-inner-local", async () => {
     await fs.rm(`${cwd}/.gitlab-ci-local/`, {recursive: true, force: true});
     const writeStreams = new WriteStreamsMock();
     const spyGitRemote = {
-        cmdArgs: ["git", "remote", "-v"],
-        returnValue: {stdout: "origin\tgit@gitlab.com:gcl/test-hest.git (fetch)\norigin\tgit@gitlab.com:gcl/test-hest.git (push)\n"},
+        cmdArgs: ["git", "remote", "get-url", "origin"],
+        returnValue: {stdout: "git@gitlab.com:gcl/test-hest.git"},
     };
     const target = ".gitlab-ci-local/includes/gitlab.com/firecow/gitlab-ci-local-includes/include-string-list/";
     const spyGitArchive1 = {
diff --git a/tests/test-cases/include-project-file-ref/integration.include-project-file-ref.test.ts b/tests/test-cases/include-project-file-ref/integration.include-project-file-ref.test.ts
index a1a9bd8..5a3cab2 100644
--- a/tests/test-cases/include-project-file-ref/integration.include-project-file-ref.test.ts
+++ b/tests/test-cases/include-project-file-ref/integration.include-project-file-ref.test.ts
@@ -9,8 +9,8 @@ test("include-project-file-ref <deploy-job>", async () => {
     await fs.rm("tests/test-cases/include-project-file-ref/.gitlab-ci-local", {recursive: true, force: true});
     const writeStreams = new WriteStreamsMock();
     const spyGitRemote = {
-        cmdArgs: ["git", "remote", "-v"],
-        returnValue: {stdout: "origin\tgit@gitlab.com:gcl/test-hest.git (fetch)\norigin\tgit@gitlab.com:gcl/test-hest.git (push)\n"},
+        cmdArgs: ["git", "remote", "get-url", "origin"],
+        returnValue: {stdout: "git@gitlab.com:gcl/test-hest.git"},
     };
     const target = ".gitlab-ci-local/includes/gitlab.com/firecow/gitlab-ci-local-includes/HEAD/";
     const spyGitArchive = {
diff --git a/tests/test-cases/predefined-variables/integration.predefined-variables.test.ts b/tests/test-cases/predefined-variables/integration.predefined-variables.test.ts
index b19f7fb..4c94e4a 100644
--- a/tests/test-cases/predefined-variables/integration.predefined-variables.test.ts
+++ b/tests/test-cases/predefined-variables/integration.predefined-variables.test.ts
@@ -7,8 +7,8 @@ import {WhenStatics} from "../../mocks/when-statics";
 test("predefined-variables <test-job>", async () => {
     const writeStreams = new WriteStreamsMock();
     const spyGitRemote = {
-        cmdArgs: ["git", "remote", "-v"],
-        returnValue: {stdout: "origin\tgit@gitlab.com:GCL/predefined-variables.git (fetch)\norigin\tgit@gitlab.com:GCL/predefined-variables.git (push)\n"},
+        cmdArgs: ["git", "remote", "get-url", "origin"],
+        returnValue: {stdout: "git@gitlab.com:GCL/predefined-variables.git"},
     };
     initSpawnSpy([...WhenStatics.all, spyGitRemote]);
     await handler({
diff --git a/tests/test-cases/pull-policy/.gitlab-ci.yml b/tests/test-cases/pull-policy/.gitlab-ci.yml
new file mode 100644
index 0000000..4029ddc
--- /dev/null
+++ b/tests/test-cases/pull-policy/.gitlab-ci.yml
@@ -0,0 +1,5 @@
+---
+alpine:
+  image: alpine
+  script:
+    - echo "Hey hey"
diff --git a/tests/test-cases/pull-policy/integration.pull-policy.test.ts b/tests/test-cases/pull-policy/integration.pull-policy.test.ts
new file mode 100644
index 0000000..4f17558
--- /dev/null
+++ b/tests/test-cases/pull-policy/integration.pull-policy.test.ts
@@ -0,0 +1,26 @@
+import {WriteStreamsMock} from "../../../src/write-streams";
+import {handler} from "../../../src/handler";
+import {initSpawnSpy} from "../../mocks/utils.mock";
+import {WhenStatics} from "../../mocks/when-statics";
+import {Utils} from "../../../src/utils";
+
+jest.setTimeout(60000);
+
+beforeAll(() => {
+    initSpawnSpy(WhenStatics.all);
+});
+
+test.concurrent("pull-policy <alpine-guest> --pull-policy=always", async () => {
+    const writeStreams = new WriteStreamsMock();
+    await handler({
+        pullPolicy: "always",
+        cwd: "tests/test-cases/pull-policy/",
+        job: ["alpine"],
+    }, writeStreams);
+
+    // Pre pull image
+    await Utils.spawn(["docker", "pull", "alpine"]);
+
+    // Expect its getting pulld again
+    expect(writeStreams.stdoutLines.join("\n")).toMatch(/pulled/);
+});
diff --git a/tests/utils.test.ts b/tests/utils.test.ts
new file mode 100644
index 0000000..23f6c36
--- /dev/null
+++ b/tests/utils.test.ts
@@ -0,0 +1,45 @@
+import {Utils} from "../src/utils";
+import {initBashSpyReject, initBashSpy} from "./mocks/utils.mock";
+
+import {isSshDirFound} from "./utils";
+
+test("remoteFileExist protocol: git", async () => {
+    const file = "templates/test.yml";
+    const ref = "0.3.1";
+    const domain = "gitlab.com";
+    const projectPath = "components/go";
+
+    // NOTE: Only mocks git archive command if `~/.ssh` ssh dir is not found
+    if (!isSshDirFound()) {
+        const spyGitArchive = {
+            cmd: `git archive --remote=ssh://git@${domain}/${projectPath}.git ${ref} ${file} > /dev/null`,
+            returnValue: {output: ""},
+        };
+        initBashSpy([spyGitArchive]);
+    }
+
+    const fileExist = await Utils.remoteFileExist(file, ref, domain, projectPath, "git");
+    expect(fileExist).toBe(true);
+});
+
+test("remoteFileDoesNotExist protocol: git", async () => {
+    const file = "templates/potato.yml";
+    const ref = "0.3.1";
+    const domain = "gitlab.com";
+    const projectPath = "components/go";
+
+    // NOTE: Only mocks git archive command if `~/.ssh` ssh dir is not found
+    if (!isSshDirFound()) {
+        const spyGitArchive = {
+            cmd: `git archive --remote=ssh://git@${domain}/${projectPath}.git ${ref} ${file} > /dev/null`,
+            rejection: {
+                stderr: `fatal: sent error to the client: git upload-archive: archiver died with error
+remote: fatal: pathspec 'templates/potato.yml' did not match any files
+remote: git upload-archive: archiver died with error`,
+            },
+        };
+        initBashSpyReject([spyGitArchive]);
+    }
+    const fileExist = await Utils.remoteFileExist(file, ref, domain, projectPath, "git");
+    expect(fileExist).toBe(false);
+});
diff --git a/tests/utils.ts b/tests/utils.ts
new file mode 100644
index 0000000..fcb7dc3
--- /dev/null
+++ b/tests/utils.ts
@@ -0,0 +1,10 @@
+import * as fs from "fs-extra";
+
+export function isSshDirFound () {
+    try {
+        fs.statSync(`${process.env.HOME}/.ssh`);
+        return true;
+    } catch {
+        return false;
+    }
+}