commit b8d8ece3f7014296a30a39266a11a84f5e997be4
parent f38e8fc63b17a02456c5ec4bb55ab41283285783
Author: arjoonn <arjoonn@noreply.localhost>
Date: Mon, 16 Jan 2023 08:58:36 +0000
path_fix (!23)
Branch auto created by JayporeCI
```jayporeci
╔ 🟢 : JayporeCI [sha 49f90a0483]
┏━ Docker
┃
┃ 🟢 : JciEnv [1f1a354a]
┃ 🟢 : Jci [124d562f]
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┏━ Jobs
┃
┃ 🟢 : pytest [53569f47]
┃ 🟢 : pylint [ca3755c2]
┃ 🟢 : black [7d2c2955]
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┏━ Publish
┃
┃ 🟢 : DockerHubJcienv [05bd16ed]
┃ 🟢 : PublishPypi [d652ea7e]
┃ 🟢 : DockerHubJci [f5220056]
┃ 🟢 : PublishDocs [4e1ce715]
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
```
Co-authored-by: arjoonn sharma <arjoonn@midpathsoftware.com>
Reviewed-on: https://gitea.midpathsoftware.com/midpath/jaypore_ci/pulls/23
Diffstat:
22 files changed, 148 insertions(+), 207 deletions(-)
diff --git a/README.md b/README.md
@@ -4,13 +4,5 @@ Documentation is at : https://www.jayporeci.in
## Usage
-```bash
-# Install once
-curl https://get.jayporeci.in | bash
-git add -Av
-git commit -m 'Add Jaypore CI'
-git push origin
-
-# Trigger CI whenver you push
-git push origin
-```
+- Install : `curl https://get.jayporeci.in | bash`
+- Trigger : `git push origin`
diff --git a/cicd/build_and_publish_docs.sh b/cicd/build_and_publish_docs.sh
@@ -6,9 +6,11 @@ set -o pipefail
build() {
echo "Building docs"
+ sphinx-apidoc -o docs/source/reference ./jaypore_ci
sphinx-build docs/source/ docs/build
(cd docs/build && zip -r ../../website.zip ./)
}
+
publish() {
echo "Publishing docs"
curl -H "Content-Type: application/zip" \
diff --git a/cicd/pre-push.sh b/cicd/pre-push.sh
@@ -5,18 +5,8 @@ set -o nounset
set -o pipefail
-editenv() {
- NAME=$1
- SOPS_AGE_KEY_FILE=secrets/$NAME.age sops --decrypt --input-type dotenv --output-type dotenv secrets/$NAME.enc > secrets/$NAME.env
- vim secrets/$NAME.env
- sops --encrypt --age $(age-keygen -y secrets/$NAME.age) secrets/$NAME.env > secrets/$NAME.enc
- rm secrets/$NAME.env
-}
-
run() {
- export SECRETS_PATH=secrets
- export SECRETS_FILENAME=jaypore_ci
- export $(SOPS_AGE_KEY_FILE=/jaypore_ci/repo/$SECRETS_PATH/$SECRETS_FILENAME.age sops --decrypt --input-type dotenv --output-type dotenv /jaypore_ci/repo/$SECRETS_PATH/$SECRETS_FILENAME.enc | xargs)
+ source /jaypore_ci/repo/secrets/bin/set_env.sh ci
cp -r /jaypore_ci/repo/. /jaypore_ci/run
cd /jaypore_ci/run/
git clean -fdx
diff --git a/docs/.gitignore b/docs/.gitignore
@@ -1 +1,2 @@
build/
+reference/
diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst
@@ -1,44 +0,0 @@
-Getting Started
-===============
-
-Installation
-------------
-
-
-To use **Jaypore CI**, first install it using a bash script.
-
-.. code-block:: console
-
- $ curl https://get.jayporeci.in | bash
-
-
-Doing this will:
-
-1. Create a directory called `cicd` in the root of your repo.
-2. Create a file `cicd/pre-push.sh`
-3. Create a file `cicd/cicd.py`
-4. Update your repo's pre-push git hook so that it runs the `cicd/pre-push.sh` file when you push.
-
-
-Basic config
-------------
-
-Your entire config is inside `cicd/cicd.py`. Edit it to whatever you like! A basic config would look like this:
-
-.. code-block:: python
-
- from jaypore_ci import jci
-
- with jci.Pipeline(image='mydocker/image') as p:
- p.job("Black", "black --check .")
- p.job("Pylint", "pylint mycode/ tests/")
- p.job("PyTest", "pytest tests/")
-
-
-After you make these changes you can `git add -Av` and `git commit -m 'added Jaypore CI'`.
-
-When you do a `git push origin`, that's when the CI system will get triggered and will run the CI.
-
-This config will run three jobs in parallel, using the `mydocker/image` docker image.
-
-See :doc:`examples` for more complex examples and :doc:`ideas` for understanding how it works.
diff --git a/docs/source/index.rst b/docs/source/index.rst
@@ -24,19 +24,61 @@ For example, here's a CI pipeline for a project.
with jci.Pipeline(image='mydockerhub/env_image') as p:
p.job("Black", "black --check .")
p.job("Pylint", "pylint mycode/ tests/")
+ p.job("PyTest", "pytest tests/", depends_on=["Black", "Pylint"])
+
+
+Getting Started
+===============
+
+Installation
+------------
+
+
+To use **Jaypore CI**, first install it using a bash script.
+
+.. code-block:: console
+
+ $ curl https://get.jayporeci.in | bash
+
+
+Doing this will:
+
+1. Create a directory called `cicd` in the root of your repo.
+2. Create a file `cicd/pre-push.sh`
+3. Create a file `cicd/cicd.py`
+4. Update your repo's pre-push git hook so that it runs the `cicd/pre-push.sh` file when you push.
+
+
+Basic config
+------------
+
+Your entire config is inside `cicd/cicd.py`. Edit it to whatever you like! A basic config would look like this:
+
+.. code-block:: python
+
+ from jaypore_ci import jci
+
+ with jci.Pipeline(image='mydocker/image') as p:
+ p.job("Black", "black --check .")
+ p.job("Pylint", "pylint mycode/ tests/")
p.job("PyTest", "pytest tests/")
-Go through the :doc:`getting_started` doc to set up your first instance.
+After you make these changes you can `git add -Av` and `git commit -m 'added Jaypore CI'`.
+
+When you do a `git push origin`, that's when the CI system will get triggered and will run the CI.
+
+This config will run three jobs in parallel, using the `mydocker/image` docker image.
+
+See :doc:`examples` for more complex examples and :doc:`ideas` for understanding how it works.
Contents
----------------
+--------
.. toctree::
:glob:
- getting_started
ideas
examples
reference/modules.rst
diff --git a/docs/source/reference/jaypore_ci.rst b/docs/source/reference/jaypore_ci.rst
@@ -1,53 +0,0 @@
-jaypore\_ci package
-===================
-
-Submodules
-----------
-
-jaypore\_ci.docker module
--------------------------
-
-.. automodule:: jaypore_ci.docker
- :members:
- :undoc-members:
- :show-inheritance:
-
-jaypore\_ci.gitea module
-------------------------
-
-.. automodule:: jaypore_ci.gitea
- :members:
- :undoc-members:
- :show-inheritance:
-
-jaypore\_ci.interfaces module
------------------------------
-
-.. automodule:: jaypore_ci.interfaces
- :members:
- :undoc-members:
- :show-inheritance:
-
-jaypore\_ci.jci module
-----------------------
-
-.. automodule:: jaypore_ci.jci
- :members:
- :undoc-members:
- :show-inheritance:
-
-jaypore\_ci.logging module
---------------------------
-
-.. automodule:: jaypore_ci.logging
- :members:
- :undoc-members:
- :show-inheritance:
-
-Module contents
----------------
-
-.. automodule:: jaypore_ci
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/reference/modules.rst b/docs/source/reference/modules.rst
@@ -1,7 +0,0 @@
-jaypore_ci
-==========
-
-.. toctree::
- :maxdepth: 4
-
- jaypore_ci
diff --git a/jaypore_ci/executors/docker.py b/jaypore_ci/executors/docker.py
@@ -9,6 +9,18 @@ from jaypore_ci.interfaces import Executor, TriggerFailed
from jaypore_ci.logging import logger
+def __check_output__(cmd):
+ """
+ Common arguments that need to be provided while
+ calling subprocess.check_output
+ """
+ return (
+ subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
+ .decode()
+ .strip()
+ )
+
+
class Docker(Executor):
"""
Run jobs via docker.
@@ -19,17 +31,6 @@ class Docker(Executor):
- Clean up all jobs when the pipeline exits.
"""
- def __check_output__(self, cmd):
- """
- Common arguments that need to be provided while
- calling subprocess.check_output
- """
- return (
- subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
- .decode()
- .strip()
- )
-
def __init__(self):
super().__init__()
self.pipe_id = None
@@ -88,7 +89,7 @@ class Docker(Executor):
return net_ls
self.logging().info(
"Create network",
- subprocess=self.__check_output__(
+ subprocess=__check_output__(
f"docker network create -d bridge {self.get_net()}"
),
)
@@ -107,7 +108,7 @@ class Docker(Executor):
if job.run_id is not None and not job.run_id.startswith("pyrun_"):
self.logging().info(
"Stop job:",
- subprocess=self.__check_output__(f"docker stop -t 1 {job.run_id}"),
+ subprocess=__check_output__(f"docker stop -t 1 {job.run_id}"),
)
job.check_job(with_update_report=False)
if job is not None:
@@ -121,7 +122,7 @@ class Docker(Executor):
assert self.pipe_id is not None, "Cannot delete network if pipe is not set"
self.logging().info(
"Delete network",
- subprocess=self.__check_output__(
+ subprocess=__check_output__(
f"docker network rm {self.get_net()} || echo 'No such net'"
),
)
@@ -175,15 +176,15 @@ class Docker(Executor):
"""
Given a run_id, it will get the status for that run.
"""
- ps_out = self.__check_output__(f"docker ps -f 'id={run_id}' --no-trunc")
+ ps_out = __check_output__(f"docker ps -f 'id={run_id}' --no-trunc")
is_running = run_id in ps_out
# --- exit code
- exit_code = self.__check_output__(
+ exit_code = __check_output__(
f"docker inspect {run_id}" " --format='{{.State.ExitCode}}'"
)
exit_code = int(exit_code)
# --- logs
- logs = self.__check_output__(f"docker logs {run_id}")
+ logs = __check_output__(f"docker logs {run_id}")
self.logging().debug(
"Check status",
run_id=run_id,
diff --git a/jaypore_ci/interfaces.py b/jaypore_ci/interfaces.py
@@ -12,7 +12,7 @@ class TriggerFailed(Exception):
class Status(Enum):
- "Each pipeline can be in any one of these statuses"
+ "Each pipeline can ONLY be in any one of these statuses"
PENDING = 10
RUNNING = 30
FAILED = 40
@@ -23,11 +23,11 @@ class Status(Enum):
class Executor:
"""
+ An executor is something used to run a job.
It could be docker / podman / shell etc.
- Something that allows us to run a job.
- Must define `__enter__` and `__exit__` so that it can be used as a context
- manager.
+ It must define `__enter__` and `__exit__` so that it can be used as a context manager.
+
"""
def run(self, job: "Job") -> str:
@@ -38,7 +38,7 @@ class Executor:
self.pipe_id = None
self.pipeline = None
- def set_pipeline(self, pipeline):
+ def set_pipeline(self, pipeline: "Pipeline") -> None:
"""Set the current pipeline to the given one."""
self.pipe_id = id(pipeline)
self.pipeline = pipeline
@@ -47,13 +47,15 @@ class Executor:
return self
def __exit__(self, exc_type, exc_value, traceback):
- pass
+ """
+ On exit the executor must clean up any pending / stuck / zombie jobs that are still there.
+ """
class Remote:
"""
+ Something that allows us to show other people the status of the CI job.
It could be gitea / github / gitlab / email system.
- Something that allows us to post the status of the CI.
Must define `__enter__` and `__exit__` so that it can be used as a context
manager.
@@ -77,15 +79,21 @@ class Remote:
@classmethod
def from_env(cls):
+ """
+ This function should create a Remote instance from the given environment.
+ It can read git information / look at environment variables etc.
+ """
raise NotImplementedError()
class Reporter:
"""
- Something that allows us to report the status of a pipeline
+ Something that generates the status of a pipeline.
+
+ It can be used to generate reports in markdown, plaintext, html, pdf etc.
"""
- def render(self, pipeline):
+ def render(self, pipeline: "Pipeline") -> str:
"""
Render a report for the pipeline.
"""
diff --git a/jaypore_ci/jci.py b/jaypore_ci/jci.py
@@ -22,6 +22,7 @@ __all__ = ["Pipeline", "Job"]
# All of these statuses are considered "finished" statuses
FIN_STATUSES = (Status.FAILED, Status.PASSED, Status.TIMEOUT, Status.SKIPPED)
+PREFIX = "JAYPORE_"
class Job: # pylint: disable=too-many-instance-attributes
@@ -157,7 +158,15 @@ class Job: # pylint: disable=too-many-instance-attributes
Gets the environment variables for a given job by interpolating it with
the pipeline's environment.
"""
- return {**os.environ, **self.pipeline.pipe_kwargs.get("env", {}), **self.env}
+ return {
+ **{
+ k[len(PREFIX) :]: v
+ for k, v in os.environ.items()
+ if k.startswith(PREFIX)
+ },
+ **self.pipeline.pipe_kwargs.get("env", {}),
+ **self.env,
+ }
class Pipeline: # pylint: disable=too-many-instance-attributes
diff --git a/jaypore_ci/logging.py b/jaypore_ci/logging.py
@@ -1,7 +1,5 @@
"""
The basic logging module.
-
-All logs are collected and posted along with the report in the remote PR.
"""
import logging
from typing import Any
@@ -14,6 +12,12 @@ jaypore_logs = []
class JayporeLogger:
+ """
+ This is mainly used to collect logs into a single global variable so that
+ the logs of the CI runner itself can also be posted as part of the CI
+ report.
+ """
+
def __getstate__(self) -> str:
return "stdout"
diff --git a/pyproject.toml b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "jaypore_ci"
-version = "0.1.6"
+version = "0.1.8"
description = ""
authors = ["arjoonn sharma <arjoonn.94@gmail.com>"]
diff --git a/secrets/.gitignore b/secrets/.gitignore
@@ -0,0 +1,3 @@
+*.key
+*.plaintext
+*.env
diff --git a/secrets/bin/age b/secrets/bin/age
Binary files differ.
diff --git a/secrets/bin/age-keygen b/secrets/bin/age-keygen
Binary files differ.
diff --git a/secrets/bin/edit_env.sh b/secrets/bin/edit_env.sh
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+
+set -o errexit
+set -o pipefail
+
+main (){
+ NAME=$1
+ BIN=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
+ SECRETS=$(echo "$BIN/..")
+ KEY_FILE=$(echo "$SECRETS/$NAME.key")
+ ENC_FILE=$(echo "$SECRETS/$NAME.enc")
+ PLAINTEXT_FILE=$(echo "$SECRETS/$NAME.plaintext")
+ echo $BIN
+ echo $SECRETS
+ echo $KEY_FILE
+ echo $ENC_FILE
+ echo $PLAINTEXT_FILE
+ if [[ -f "$SECRETS/$NAME.enc" ]]; then
+ SOPS_AGE_KEY_FILE=$KEY_FILE $BIN/sops --decrypt --input-type dotenv --output-type dotenv $ENC_FILE > $PLAINTEXT_FILE
+ fi
+ vim $PLAINTEXT_FILE
+ $BIN/sops --input-type dotenv --output-type dotenv --encrypt --age $($BIN/age-keygen -y $KEY_FILE) $PLAINTEXT_FILE > $ENC_FILE
+ rm $PLAINTEXT_FILE
+}
+(main $1)
diff --git a/secrets/bin/set_env.sh b/secrets/bin/set_env.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+BIN=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
+SECRETS=$(echo "$BIN/..")
+NAME=$1
+export $(SOPS_AGE_KEY_FILE=$SECRETS/$NAME.key $BIN/sops --decrypt --input-type dotenv --output-type dotenv $SECRETS/$NAME.enc | xargs)
diff --git a/secrets/bin/sops b/secrets/bin/sops
Binary files differ.
diff --git a/secrets/ci.enc b/secrets/ci.enc
@@ -0,0 +1,11 @@
+JAYPORE_DOCKER_USER=ENC[AES256_GCM,data:EFQJF0xzRQ==,iv:FN1Lk12AMJ3AuZ1lXWR4sxS7vVsdEKoO0eWHn+OFT2Q=,tag:52mEjL0Jqe71QiT/idGAjQ==,type:str]
+JAYPORE_DOCKER_PWD=ENC[AES256_GCM,data:93dY+QKRPT67vpPxy+bHvEvLebkr/DjnTN1ILOavEx8pfwyE,iv:YBy3yv+1Nr+HnxwlH4rtOHnk6GEe9sB5ZDXhZIX106I=,tag:Uu1gYvzb08a0C8F6ir/M9Q==,type:str]
+JAYPORE_NETLIFY_TOKEN=ENC[AES256_GCM,data:5bPThyqEcEt84m/2DWJKTZhHMMfipFC95XCN7+7ZOyQq9cH0dGHzqP9m0w==,iv:BMuJScVsgLblV3SQ58Ct03gPR5WkEKCSyo2YVfHhMIQ=,tag:hTaFloLUMaJFrirKVWQQTQ==,type:str]
+JAYPORE_NETLIFY_SITEID=ENC[AES256_GCM,data:5SO6Ynd3uWtsw61rssbQ0ibGudUc4D76mgIKPyMiMEHYxPTn,iv:npbTOB32me8w9RPj3o6ASuZVi0PhutLz4pPOUyzfr+Y=,tag:ffBx0QZhJQydPaUZW6B79Q==,type:str]
+JAYPORE_PYPI_TOKEN=ENC[AES256_GCM,data:IkouQeCYtj/ci3CdHmZ9O+/tmeFtZWbneka/JexhqENdCSUCdpyWGoNppsP6ZNCBYaJMU7seH4vAAlVNBlbJ9aAgZbafHXeAbUb+j5nDuZyxVELSG/GSfoIrIJIH984QTncfrnbZ5tHjOOxl1S+Lh0vxLuMoGUPrKnJsJqhe/n6uJ1+MVoxgQrptuMPwWPGbuQFlF2G0WKZASkGzO89oI3ZclqSXan1qsC0tZMew/hwtsCVQc3w8YHxtBUDDpwbmz/q8HyzWHm8lbfC27KLGHtM=,iv:sHCltgCtsYyvEPmPNOQn+OtI4mtUH1s4wx9HBka+T9I=,tag:9y9QVv1K4vfSQNlEEvcddA==,type:str]
+sops_unencrypted_suffix=_unencrypted
+sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXYnpwdEZVT0Z5TStNR1lh\nUXl2Q2xMZ3M0c0xoV2NBMU5kOXZtRlNNblVZCnNxeXh6b1RXKzBmdVgydkxOWndI\nblJiZW9wUnJCRGwvY0pmNGRWSitOMUUKLS0tIDYzRzlUc1JzeXhZL2dNWjJwZUVP\nTWNoSjRFcUtieXo0Y0lvcTk1bjNxeWsKGuUO8unlGhGgTUP7ySlqYLzrF4mhhhK9\nE0rXRCAPoRxePL+1ND1772THsXBq4RIHk5e1gkOuXt7dzsR4uNdK0A==\n-----END AGE ENCRYPTED FILE-----\n
+sops_mac=ENC[AES256_GCM,data:FKVKM90hkkil5Adz8xRjHF4H0hAOSBD62JeoxeQbVMUtATQOHGMvzH6/RcUtoLHOLxveaQkC7ytM7zC90YR/OkSiwCJo1fnBDPE6KHncMfxdB/xnILYqUZiyJe4RbN9EQybbjszE8qwLp5PJU2+ducQV/dS4WRKds4Jsc1jXCjQ=,iv:jg8QYEI2jXY+kDI52kXBsyRVLTBUK2cEKcG2JQxLraA=,tag:3vQ01L4ohNIX5fHqZiRDVA==,type:str]
+sops_version=3.7.3
+sops_lastmodified=2023-01-16T07:41:23Z
+sops_age__list_0__map_recipient=age1u0zd477nnqqausg4vtxl3laxz73t5tgt9qw8enmpeeadcmes3eusw3v3m9
diff --git a/secrets/edit_env.sh b/secrets/edit_env.sh
@@ -1,38 +0,0 @@
-#!/usr/bin/env bash
-
-set -o errexit
-set -o pipefail
-
-main (){
- NAME=$1
- SOPS_AGE_KEY_FILE=secrets/$NAME.age sops --decrypt --input-type dotenv --output-type dotenv secrets/$NAME.enc > secrets/$NAME.env
- vim secrets/$NAME.env
- sops --encrypt --age $(age-keygen -y secrets/$NAME.age) secrets/$NAME.env > secrets/$NAME.enc
- rm secrets/$NAME.env
-}
-
-help_message (){
-
- echo "
- Easily edit env files.
- Make sure you have age keys available in
-
- ~/.ssh/smaac/agekeys/<envname>.txt
-
- If that is available you can run the following to edit env files.
-
- ./api/bin/edit_env.sh <envname>
-
- Upon exiting the editor the file will be re-encrypted.
- "
-}
-
-if [[ $1 == "--help" || $1 == "-h" ]]; then
- help_message
- exit 0
-fi
-if [ -z $1 ]; then
- help_message
- exit 0
-fi
-(main $1)
diff --git a/secrets/jaypore_ci.enc b/secrets/jaypore_ci.enc
@@ -1,11 +0,0 @@
-DOCKER_USER=ENC[AES256_GCM,data:2CaivUehNQ==,iv:OX1kGJMcZCPWL7prJOWWXyGdWCeH8PRUgaArQAM8Kp4=,tag:n+a5xU8wKoJ2isWPKl/g7A==,type:str]
-DOCKER_PWD=ENC[AES256_GCM,data:WeQDNLJVNDBWitxhUtFCvXSVcn8PVVGjUgGAm8NxOwp85j6m,iv:2SKkjTxI7rAMUfzO2KHBKXDuq+Ud3ofVHZxOc7/6k8s=,tag:Gvg+HvARWPjMymHKg0gYOQ==,type:str]
-NETLIFY_TOKEN=ENC[AES256_GCM,data:Th0pfcRKpVVbpELSties8KTpsHbZ8t8u6+XeIgunYcEKPluOxvfMOHglAQ==,iv:X9mfiz/gl9UIT1NBv/nW8VMr/pUK2ZKxjlyktpluyTo=,tag:5ykXSWGKa4Tyox8pxQZQTg==,type:str]
-NETLIFY_SITEID=ENC[AES256_GCM,data:mRF8fNJ1OkRguScHQdc6REbb+4wBYEA0Plm42FNqsyyGMP2P,iv:PACpEvLBgPEZ+cy9AgHteeKXLLSM8svAM532guKGkGc=,tag:n24uq0hPVdrsDBQi0Qz58Q==,type:str]
-PYPI_TOKEN=ENC[AES256_GCM,data:Xs11z/CmeZublROuasDem9brzTVIH0lIvzcBzXVt/qWyuon0jAwin37R4ZlFGkaFWDuMOlgk0imZsNsUzPxviLI7DB7da/lFekHIqdRt/zHvvsSIE3e+lISUTN53iZRSzQNs1XL07Bot/OcyENnusY2TOJkFdpeg27vC31w2dnBQOfiW9zdn6m+OLRDXfhJ1es0P7T1J5Csb5jZEsDU5pTfZZbGfrO9K5JTf+Z3xVEkn9H5FGUifO6dW+VsW9Mi3hmtZBmc8UeXadIaV8o0GxxE=,iv:ezGLQtkEm/IiY77jV78aZoB7Sr2zK5ZIxSgkmM+ISts=,tag:zLggPvtH3uS5bmf5fZty1A==,type:str]
-sops_version=3.7.3
-sops_age__list_0__map_recipient=age1u0zd477nnqqausg4vtxl3laxz73t5tgt9qw8enmpeeadcmes3eusw3v3m9
-sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1VW9NdUtUeHgrcjRROUxP\nSCtramJjZXptMVM5ajFXeGVNVld6bXZIRlVBClE3dEw1dWdZQmIvUGZQYk9ITDNj\nUVpMUXFiWXp6cGlXZFA5a1haNHUwQjAKLS0tIFJmVGdRWW1KdWsxeHhSRG00cXB2\nYzBtaFhrZGRaRWx6ZjByU1BsYWhLelUK5xY5pQJivhjEekm/FCp2cy/D3xdXb5q6\nt1ovJcko2++uM1rO8xrcqW5zXTqdbLBW2lWL8imSlkaMiFs7aVA8jA==\n-----END AGE ENCRYPTED FILE-----\n
-sops_lastmodified=2022-12-29T08:40:37Z
-sops_unencrypted_suffix=_unencrypted
-sops_mac=ENC[AES256_GCM,data:v+MUgHHzcWglAxkY7k8GrFAh9Mwjx5Y0jAn/cjxvQOcDgzd73Sl2k1GGh/v6t5Qonv0w+TSpADW76iG9ffmvLQzNvJyJduVsvEaAfyvUYUabnSSGZMVL6ytI6FloyipEoEoyV+9Ac+e+6eQZtceHWOXTxutp59y5/LsS+dZD3qU=,iv:SkxSE/ZROwarze2Im8KoO9anuBipyTYjWDXoGnrd3Nc=,tag:DAf2oT8VtnUo5TGIFSV7hA==,type:str]