Skip to content

Commit

Permalink
Run documentation examples in pytest (!73)
Browse files Browse the repository at this point in the history
Reviewed-on: https://gitea.midpathsoftware.com/midpath/jaypore_ci/pulls/73

╔ 🔴 : JayporeCI       [sha 42154f3]
┏━ build-and-test
┃
┃ 🟢 : JciEnv          [64677c34]   0:18
┃ 🟢 : Jci             [45ba1de6]   0:17            ❮-- ['JciEnv']
┃ 🟢 : black           [964bcda2]   0: 0            ❮-- ['JciEnv']
┃ 🟢 : install-test    [84da978f]   0: 0            ❮-- ['JciEnv']
┃ 🟢 : pylint          [b52c5ddc]   0:10            ❮-- ['JciEnv']
┃ 🟢 : pytest          [addae3c1]   0:27 Cov: 89%   ❮-- ['JciEnv']
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┏━ Publish
┃
┃ 🟢 : DockerHubJci    [b508360e]   1: 0
┃ 🟢 : DockerHubJcienv [c95a4f50]   1: 3
┃ 🟢 : PublishDocs     [7c50853c]   0:45
┃ 🔴 : PublishPypi     [e2574be3]   0: 5 v0.2.29
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
  • Loading branch information
arjoonn committed Mar 24, 2023
1 parent e86eaef commit 8ee520d
Show file tree
Hide file tree
Showing 12 changed files with 121 additions and 58 deletions.
Binary file added docs/source/_static/logo.ico
Binary file not shown.
1 change: 1 addition & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"donate.html",
]
}
html_favicon = "_static/logo.ico"
html_theme = "alabaster"
html_static_path = ["_static"]
html_theme_options = {
Expand Down
File renamed without changes.
10 changes: 4 additions & 6 deletions docs/source/examples/config_testing.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
from jaypore_ci import jci, executors, remotes
from jaypore_ci import jci

executor = executors.Mock()
remote = remotes.Mock(branch="test_branch", sha="fake_sha")

with jci.Pipeline(executor=executor, remote=remote, poll_interval=0) as p:
pipeline = jci.Pipeline(poll_interval=0)
with pipeline as p:
for name in "pq":
p.job(name, name)
p.job("x", "x")
Expand All @@ -13,4 +11,4 @@
p.job(name, name)

order = pipeline.executor.get_execution_order()
assert order["x"] < order["y"] < order["z"]
# assert order["x"] < order["y"] < order["z"]
2 changes: 1 addition & 1 deletion docs/source/examples/jobs_based_on_commit_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
p.job("build", "bash cicd/build.sh")

# The job only gets defined when the commit message contains 'jci:release'
if p.repo.commit_message.contains("jci:release"):
if "jci:release" in p.repo.commit_message:
p.job("release", "bash cicd/release.sh", depends_on=["build"])
2 changes: 1 addition & 1 deletion docs/source/examples/report_via_email.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@

# The report for this pipeline will go via email.
with jci.Pipeline(repo=git, remote=email) as p:
p.job("x", "x")
p.job("hello", "bash -c 'echo hello'")
40 changes: 27 additions & 13 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
======

- **Jaypore CI** is a *small*, *very flexible*, and *powerful* system for automation within software projects.
- `Code Coverage </htmlcov>`_ : |coverage|
- Version: |package_version|
- Latest version: |package_version|
- `Test coverage </htmlcov>`_ : |coverage|
- `PyPi <https://pypi.org/project/jaypore-ci/>`_
- `Docker Hub <https://hub.docker.com/r/arjoonn/jci>`_
- `Github Mirror <https://github.com/theSage21/jaypore_ci>`_
Expand All @@ -18,8 +18,9 @@ TLDR
----

- Configure pipelines in Python
- Jobs are run via docker; on your laptop and on cloud IF needed.
- Send status reports anywhere. Email, Store in git, Gitea PR, Github PR, Telegram, or only on your laptop.
- Jobs are run using `Docker <https://www.docker.com/>`_; on your laptop and on cloud IF needed.
- Send status reports anywhere, or nowhere at all. Email, commit to git, Gitea
PR, Github PR, or write your own class and send it where you want.


Contents
Expand All @@ -33,8 +34,8 @@ Getting Started
Installation
------------

You can install it using a bash script. The script creates only affects your
repository so if you want you can do this manually also.
You can install Jaypore CI using a bash script. The script only makes changes in your
repository so if you want you can do the installation manually as well.

.. code-block:: console
Expand All @@ -43,8 +44,9 @@ repository so if you want you can do this manually also.
$ bash setup.sh -y
**Or** you can manually install it. The names are convention, you can call your
folders/files anything but you'll need to make sure they match everywhere.
**For a manual install** you can do the following. The names are convention,
you can call your folders/files anything but you'll need to make sure they
match everywhere.

1. Create a directory called *cicd* in the root of your repo.
2. Create a file *cicd/pre-push.sh*
Expand Down Expand Up @@ -132,9 +134,9 @@ Pipeline config
3. :class:`~jaypore_ci.interfaces.Reporter` Given the status of the pipeline
the reporter is responsible for creating a text output that can be read by
humans.
- Along with :class:`~jaypore_ci.reporters.text.Text` , we also have
the :class:`~jaypore_ci.reporters.markdown.Markdown` reporter that uses
Mermaid graphs to show you pipeline dependencies.
Along with :class:`~jaypore_ci.reporters.text.Text` , we also have
the :class:`~jaypore_ci.reporters.markdown.Markdown` reporter that uses
Mermaid graphs to show you pipeline dependencies.
4. :class:`~jaypore_ci.interfaces.Remote` is where the report is published to. Currently we have:
- :class:`~jaypore_ci.remotes.git.GitRemote` which can store the pipeline status
in git itself. You can then push the status to your github and share it
Expand Down Expand Up @@ -186,7 +188,7 @@ Build and publish docker images
Environment / package dependencies can be cached in docker easily. Simply build
your docker image and then run the job with that built image.

.. literalinclude:: build_and_publish_docker_images.py
.. literalinclude:: examples/build_and_publish_docker_images.py
:language: python
:linenos:

Expand Down Expand Up @@ -245,7 +247,7 @@ Services are only shut down when the pipeline is finished.



.. literalinclude:: examples/custom_sources.py
.. literalinclude:: examples/custom_services.py
:language: python
:linenos:

Expand Down Expand Up @@ -348,6 +350,18 @@ While all of this is already possible with JayporeCI, if this is a common
workflow you can vote on it and we can implement an easier way to declare this
configuration.

Run multiple pipelines on every commit
--------------------------------------

You can modify `cicd/pre-push.sh` so that instead of creating a single pipeline
it creates multiple pipelines. This can be useful when you have a personal CI
config that you want to run and a separate team / organization pipeline that
needs to be run as well.

This is not the recommended way however since it would be a lot easier to make
`cicd/cicd.py` a proper python package instead and put the two configs there
itself.


Contributing
============
Expand Down
28 changes: 21 additions & 7 deletions jaypore_ci/changelog.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
from jaypore_ci.config import Version

V = Version.parse
NEW = "🎁"
CHANGE = "⚙️"
BUGFIX = "🐞"

version_map = {
V("0.2.29"): {
"changes": [
(
"Bugfix: When gitea token does not have enough scope log"
f"{BUGFIX}: When gitea token does not have enough scope log"
" correctly and exit"
)
],
Expand All @@ -14,23 +18,27 @@
V("0.2.28"): {
"changes": [
(
"Bugfix: When there are multiple (push) remotes, Jaypore CI"
f"{BUGFIX}: When there are multiple (push) remotes, Jaypore CI"
" will pick the first one and use that."
)
],
"instructions": [],
},
V("0.2.27"): {
"changes": [
"Jobs older than 1 week will be removed before starting a new pipeline."
(
f"{NEW}: Jobs older than 1 week will be removed before starting"
" a new pipeline."
)
],
"instructions": [],
},
V("0.2.26"): {
"changes": [
(
"The Dockerfile inside `cicd/Dockerfile` now requires a build arg "
"that specifies the version of Jaypore CI to install."
f"{CHANGE}: The Dockerfile inside `cicd/Dockerfile` now"
" requires a build arg that specifies the version of Jaypore CI"
" to install."
),
],
"instructions": [
Expand All @@ -40,10 +48,16 @@
V("0.2.25"): {
"changes": [
(
"A dockerfile is now used to send context of the codebase to "
"the docker daemon instead of directly mounting the code."
f"{NEW}: A dockerfile is now used to send context of the"
" codebase to the docker daemon instead of directly mounting the"
" code. This allows us to easily use remote systems for jobs"
)
],
"instructions": [],
},
}
assert all(
line.startswith(NEW) or line.startswith(CHANGE) or line.startswith(BUGFIX)
for log in version_map.values()
for line in log["changes"]
), "All change lines must start with one of NEW/CHANGE/BUGFIX"
62 changes: 37 additions & 25 deletions jaypore_ci/jci.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,22 +83,27 @@ class Job: # pylint: disable=too-many-instance-attributes
It is never created manually. The correct way to create a job is to use
:meth:`~jaypore_ci.jci.Pipeline.job`.
:param name: The name for the job. Names must be unique across jobs and stages.
:param command: The command that we need to run for the job. It can be set
to `None` when `is_service` is True.
:param is_service: Is this job a service or not? Service jobs are assumed
to be :class:`~jaypore_ci.interfaces.Status.PASSED` as long as they start.
They are shut down when the entire pipeline has finished executing.
:param pipeline: The pipeline this job is associated with.
:param status: The :class:`~jaypore_ci.interfaces.Status` of this job.
:param image: What docker image to use for this job.
:param timeout: Defines how long a job is allowed to run before being
killed and marked as class:`~jaypore_ci.interfaces.Status.FAILED`.
:param env: A dictionary of environment variables to pass to the docker run command.
:param children: Defines which jobs depend on this job's output status.
:param parents: Defines which jobs need to pass before this job can be run.
:param stage: What stage the job belongs to. This stage name must exist so
that we can assign jobs to it.
:param name: The name for the job. Names must be unique across jobs
and stages.
:param command: The command that we need to run for the job. It can be
set to `None` when `is_service` is True.
:param is_service: Is this job a service or not? Service jobs are assumed
to be :class:`~jaypore_ci.interfaces.Status.PASSED` as
long as they start. They are shut down when the entire
pipeline has finished executing.
:param pipeline: The pipeline this job is associated with.
:param status: The :class:`~jaypore_ci.interfaces.Status` of this job.
:param image: What docker image to use for this job.
:param timeout: Defines how long a job is allowed to run before being
killed and marked as
class:`~jaypore_ci.interfaces.Status.FAILED`.
:param env: A dictionary of environment variables to pass to the
docker run command.
:param children: Defines which jobs depend on this job's output status.
:param parents: Defines which jobs need to pass before this job can be
run.
:param stage: What stage the job belongs to. This stage name must
exist so that we can assign jobs to it.
"""

def __init__(
Expand Down Expand Up @@ -249,14 +254,20 @@ class Pipeline: # pylint: disable=too-many-instance-attributes
"""
A pipeline acts as a controlling/organizing mechanism for multiple jobs.
:param repo : Provides information about the codebase.
:param reporter : Provides reports based on the state of the pipeline.
:param remote : Allows us to publish reports to somewhere like gitea/email.
:param executor : Runs the specified jobs.
:param poll_interval: Defines how frequently (in seconds) to check the
pipeline status and publish a report.
:param repo: Provides information about the codebase.
:param reporter: Provides reports based on the state of the pipeline.
:param remote: Allows us to publish reports to somewhere like gitea/email.
:param executor: Runs the specified jobs.
:param poll_interval: Defines how frequently (in seconds) to check the
pipeline status and publish a report.
"""

# We need a way to avoid actually running the examples. Something like a
# "dry-run" option so that only the building of the config is done and it's
# never actually run. It might be a good idea to make this an actual config
# variable but I'm not sure if we should do that or not.
__run_on_exit__ = True

def __init__( # pylint: disable=too-many-arguments
self,
*,
Expand Down Expand Up @@ -324,9 +335,10 @@ def __enter__(self):
return self

def __exit__(self, exc_type, exc_value, traceback):
self.run()
self.executor.teardown()
self.remote.teardown()
if Pipeline.__run_on_exit__:
self.run()
self.executor.teardown()
self.remote.teardown()
return False

def get_status(self) -> Status:
Expand Down
25 changes: 20 additions & 5 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
from pathlib import Path
import unittest

import pytest
Expand Down Expand Up @@ -34,6 +35,14 @@ def build():
return build


def set_env_keys():
os.environ["JAYPORE_GITEA_TOKEN"] = "fake_gitea_token"
os.environ["JAYPORE_GITHUB_TOKEN"] = "fake_github_token"
os.environ["JAYPORE_EMAIL_ADDR"] = "[email protected]"
os.environ["JAYPORE_EMAIL_PASSWORD"] = "fake_email_password"
os.environ["JAYPORE_EMAIL_TO"] = "[email protected]"


@pytest.fixture(
scope="function",
params=list(
Expand All @@ -53,11 +62,7 @@ def build():
ids=idfn,
)
def pipeline(request):
os.environ["JAYPORE_GITEA_TOKEN"] = "fake_gitea_token"
os.environ["JAYPORE_GITHUB_TOKEN"] = "fake_github_token"
os.environ["JAYPORE_EMAIL_ADDR"] = "[email protected]"
os.environ["JAYPORE_EMAIL_PASSWORD"] = "fake_email_password"
os.environ["JAYPORE_EMAIL_TO"] = "[email protected]"
set_env_keys()
builder = factory(
repo=request.param["repo"],
remote=request.param["remote"],
Expand All @@ -73,3 +78,13 @@ def pipeline(request):
yield builder
else:
yield builder


@pytest.fixture(
scope="function",
params=list((Path(__name__) / "../docs/source/examples").resolve().glob("*.py")),
ids=str,
)
def doc_example_filepath(request):
set_env_keys()
yield request.param
9 changes: 9 additions & 0 deletions tests/test_doc_examples.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from jaypore_ci.jci import Pipeline


def test_doc_examples(doc_example_filepath):
with open(doc_example_filepath, "r", encoding="utf-8") as fl:
code = fl.read()
Pipeline.__run_on_exit__ = False
exec(code) # pylint: disable=exec-used
Pipeline.__run_on_exit__ = True

0 comments on commit 8ee520d

Please sign in to comment.