zk_html/work/gitea-4.html

190 lines
28 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html>
<head>
<title>Zk | gitea-4</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Gentium+Plus&family=Lato&family=Ubuntu+Monodisplay=swap" />
<link rel="stylesheet" type="text/css" href="/style.css" />
<meta name="viewport" content="width=device-width" />
<meta charset="utf-8" />
</head>
<body>
<main>
<header>
<h1>Zk | gitea-4</h1>
</header>
<article class="content">
<h1 id="how-to-set-up-cicd-with-drone-ci-and-gitea">How To Set Up CI/CD with Drone CI and Gitea</h1>
<p>In the process of developing and releasing software, one of the most useful tools at a developer&rsquo;s disposal is a solution that performs <strong>continuous integration</strong> and <strong>continuous deployment</strong>, or <strong>CI/CD</strong>.</p>
<p>Continuous integration (CI) refers to the practice of multiple developers merging their changes to a codebase back into the main branch often, perhaps daily or even more frequently, relying on smaller chunks of work that are more easily reviewed to and tested during the process of development. In order to ensure that these changes don&rsquo;t leave the main branch in a broken state, a tool that helps with continuous integration will often include functionality that runs test suites within the software package. When a developer proposes a merge of their work back into the main branch, the tests will run automatically and report back whether or not they pass, which may mean that the code is not ready to land.</p>
<p>Continuous deployment or continuous delivery (CD) refers to the idea that changes to code &mdash; whether they&rsquo;re security patches, bug fixes, or new features &mdash; be released quickly and seamlessly to the end users of the application. In the case of applications that the user runs, this will mean a frequent release schedule, offering incremental improvements to the software they are using. In the case of a service such as a website, this will mean frequent deployments of the new features in the code to the site so that the user&rsquo;s experience is continuously improving.</p>
<p>There are several tools that can help with CI/CD. These run as their own web services, which work with many source code management (SCM) systems to perform these tasks automatically. <a href="https://drone.io">Drone</a> is one such solution, that provides a way of executing <strong>pipelines</strong> &mdash; sets of steps that the service will run through such as building the software and running tests &mdash; that are described in the code itself. Drone is a flexible CI/CD tool that can perform this on many different types of <strong>runners</strong>, containers or machines where the project can be repeatably built.</p>
<p>This tutorial aims to show how Drone can integrate with the source code management tool Gitea in order to offer fully self-hosted solutions for SCM and CI/CD. You will be installing Drone, connecting it with Gitea, and creating a sample project for Drone to run tests on in order to see how the two services work together.</p>
<h3 id="prerequisites">Prerequisites</h3>
<p>In order to complete this tutorial, you will need the following:</p>
<ul>
<li>An installation of Gitea. For more on how to set up your own self-hosted Git SCM, see the <a href="https://www.digitalocean.com/community/tutorials/how-to-install-gitea-on-ubuntu-using-docker">How To Install Gitea on Ubuntu Using Docker</a> tutorial.</li>
<li>An Ubuntu 20.04 server with a non-root user configured with sudo privileges as described in the <a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04">initial server setup for Ubuntu 20.04</a>.</li>
<li>Docker installed on your server. Follow <strong>Steps 1 and 2</strong> of <a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-20-04">How to Install Docker on Ubuntu 20.04</a> to install Docker.</li>
<li>Docker Compose installed on your server. Follow <strong>Step 1</strong> of our guide on <a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-compose-on-ubuntu-20-04#step-1-installing-docker-compose">How to Install and Use Docker Compose on Ubuntu 20.04</a> to set this up.</li>
<li>A domain name pointed at your server. If you are using a DigitalOcean Droplet, you can accomplish this by following our <a href="https://docs.digitalocean.com/products/networking/dns/">Domains and DNS</a> documentation. This tutorial will use <code>&lt;^&gt;your_domain&lt;^&gt;</code> in examples throughout.</li>
</ul>
<h2 id="step-1-creating-an-oauth-application-in-gitea">Step 1 — Creating an OAuth Application in Gitea</h2>
<p>The first step to integrating Drone with Gitea &mdash; before even installing Drone itself &mdash; is to create an OAuth2 application in Gitea. OAuth2 is a way for one service provider to delegate access to another. For instance, you may want to be able to share information on one site without creating an entirely new account there when you already have an account elsewhere with that information. If both sites work with OAuth2, then you can authorize the first site to have access to that information on the second site. In this case, Drone, as an OAuth2 client of Gitea, will be granted access to information such as repositories and pull requests that it will need to run its CI/CD tasks.</p>
<p>OAuth2 works by creating a <strong>client ID</strong> by which a client (Drone, in this case) identifies itself and a <strong>client secret</strong> by which it authenticates itself. Gitea will generate these values for you to provide to Drone when starting it.</p>
<p>To generate the ID and secret, log in to your Gitea instance and click on your icon in the upper right corner to select <strong>Settings</strong> from the drop down menu. In that page, you will see a row of tabs along the top. Click <strong>Applications</strong>, and you&rsquo;ll be presented with a screen allowing you to create OAuth2 Applications.</p>
<p><a href="TODO.html"><img alt="Manage OAuth2 Applications" src="TODO" /></a></p>
<p>Enter <strong>Drone CI</strong> or similar as your application name. For the redirect URI, enter the domain you have chosen for your Drone instance. This should take the form of <code>https://&lt;^&gt;your_domain&lt;^&gt;/login</code> &mdash; it&rsquo;s important that the protocol (HTTPS, in this case) and domain name match exactly, and that you include the <code>/login</code> path at the end of the URL.</p>
<p>When you click <strong>Create Application</strong>, you will be presented with a screen showing the information that you just entered along with the OAuth2 client ID and client secret. Copy these both into a temporary document now, as they&rsquo;ll be hidden as soon as you navigate away from the page. If you do lose them, note the <strong>Regenerate Secret</strong> link, which will allow you to create a new secret that you can use for your Drone installation.</p>
<h2 id="step-2-installing-drone">Step 2 — Installing Drone</h2>
<p>Now that you have your OAuth2 application created in Gitea, you can begin installing Drone. For this section, you will need the client ID and secret created in <strong>Step 1</strong>, the domain names for your Gitea instance and Drone instance, and an RPC secret. For this example, we will be using <code>sammy-says</code>.</p>
<p>On the server you created in the prerequisites, log in as your user and create a new directory named <code>drone</code> and move into it:</p>
<div class="codehilite"><pre><span></span><code>mkdir drone
cd drone
</code></pre></div>
<p>Now, create a new file named <code>docker-compose.yml</code> using your preferred text editor. The following example uses <code>nano</code>. This file will contain the descriptions of the containers that will run as part of your Drone installation:</p>
<div class="codehilite"><pre><span></span><code>nano docker-compose.yml
</code></pre></div>
<p>Add the following into this new file, changing the highlighted values as required:</p>
<div class="codehilite"><pre><span></span><code><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;3&quot;</span><span class="w"></span>
<span class="nt">services</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="nt">server</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">drone/drone:2</span><span class="w"></span>
<span class="w"> </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">server</span><span class="w"></span>
<span class="w"> </span><span class="nt">environment</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">DRONE_GITEA_SERVER=https://mscottclary-test.do-community.com</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">DRONE_GITEA_CLIENT_ID=06e6fcce-4b10-40f3-9672-8190849e6e02</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">DRONE_GITEA_CLIENT_SECRET=hAihmhcdag9kXvQQdGh2XfIZHzKXIBJAxRJirvAXUIKu</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">DRONE_RPC_SECRET=sammy-says</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">DRONE_SERVER_HOST=mscottclary-drone.do-community.com</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">DRONE_SERVER_PROTO=https</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">DRONE_TLS_AUTOCERT=true</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">DRONE_AGENTS_ENABLED=true</span><span class="w"></span>
<span class="w"> </span><span class="nt">volumes</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">./drone:/data</span><span class="w"></span>
<span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="s">&quot;80:80&quot;</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="s">&quot;443:443&quot;</span><span class="w"></span>
<span class="w"> </span><span class="nt">runner</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">drone/drone-runner-docker:1</span><span class="w"></span>
<span class="w"> </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">runner</span><span class="w"></span>
<span class="w"> </span><span class="nt">environment</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">DRONE_RPC_PROTO=http</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">DRONE_RPC_HOST=server</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">DRONE_RPC_SECRET=sammy-says</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">DRONE_RUNNER_CAPACITY=2</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">DRONE_RUNNER_NAME=gitea-runner</span><span class="w"></span>
<span class="w"> </span><span class="nt">volumes</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">/var/run/docker.sock:/var/run/docker.sock</span><span class="w"></span>
<span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="s">&quot;3000:3000&quot;</span><span class="w"></span>
</code></pre></div>
<p>Let&rsquo;s walk through what this file does:</p>
<ul>
<li><code>version: "3"</code>: this lets Docker Compose know what version of configuration file this is.</li>
<li><code>services</code>: for each of the containers that we have, we set up environment variables available to the service, assign volumes for data that&rsquo;s stored on the host, and expose various ports. Here are our services:<ul>
<li><code>server</code>: this service uses the <code>drone/drone:2</code> image, meaning that it runs the drone server itself. The environment variables passed in provide the service with information such as how to connect to and authorize with Gitea, a secret for the <strong>remote procedure calls</strong> (RPC), and with <code>DRONE_TLS_AUTOCERT</code>, we let the drone server provision a SSL certificate using Let&rsquo;s Encrypt. The container exposes port 80 for HTTP and port 443 for HTTPS. The local directory <code>drone</code> will be mapped to the container&rsquo;s <code>/data</code> directory, allowing all of Drone&rsquo;s information to be stored locally, meaning that it can be backed up and persist across containers.</li>
<li><code>runner</code>: this service uses the <code>drone/drone-runner-docker:1</code> image, meaning that it will be in control of provisioning Docker containers for running tests on your code. This runner is what connects to the <code>server</code> container using RPC over port 3000 to start runs and report on the results. Because of this, <code>/var/run/docker.sock</code> is mapped as a volume. This special file is the means by which the runner process can communicate with Docker to start these containers.</li>
</ul>
</li>
</ul>
<p>&lt;$&gt;[note]
<strong>Note:</strong> There are several different types of runners for Drone, each of which provides different benefits. Docker runners are good for ephemeral actions such as running tests, as they are cleaned up after the run completes and they do not persist any data. If you need the ability to persist data &mdash; deploy your service, for instance &mdash; you will need to use a different runner such as the <strong>Exec Runner</strong> or <strong>DigitalOcean</strong> runner. For more information on the available runners and the reasons for using them, the <a href="https://docs.drone.io/runner/overview/">Drone Runner Documentation</a> has instructions for each.
&lt;$&gt;</p>
<p>Now that your Docker Compose file is complete, save and close it. If you used <code>nano</code> to edit the file, you can do so by pressing <code>CTRL + X</code>, <code>Y</code>, and <code>ENTER</code>.</p>
<p>With this file in place you can then bring the containers up using Docker Compose:</p>
<div class="codehilite"><pre><span></span><code>docker-compose up
</code></pre></div>
<p>This command will pull down the images, start the server and runner containers, and will return output like this:</p>
<div class="codehilite"><pre><span></span><code>[<span class="o">+</span>] <span class="nv">Running</span> <span class="mi">5</span><span class="o">/</span><span class="mi">5</span>
<span class="nv">server</span> <span class="nv">Pulled</span>
<span class="mi">79</span><span class="nv">e9f2f55bf5</span> <span class="nv">Pull</span> <span class="nv">complete</span>
<span class="mi">3534</span><span class="nv">e21ebea8</span> <span class="nv">Pull</span> <span class="nv">complete</span>
<span class="mi">2</span><span class="nv">f27386bf47c</span> <span class="nv">Pull</span> <span class="nv">complete</span>
<span class="mi">631</span><span class="nv">cac189eb7</span> <span class="nv">Pull</span> <span class="nv">complete</span>
[<span class="o">+</span>] <span class="nv">Running</span> <span class="mi">2</span><span class="o">/</span><span class="mi">2</span>
<span class="nv">Network</span> <span class="nv">drone_drone</span> <span class="nv">Created</span>
<span class="nv">Container</span> <span class="nv">server</span> <span class="nv">Created</span>
<span class="nv">Attaching</span> <span class="nv">to</span> <span class="nv">server</span>
<span class="nv">server</span> <span class="o">|</span> {<span class="s2">&quot;</span><span class="s">acme</span><span class="s2">&quot;</span>:<span class="nv">true</span>,<span class="s2">&quot;</span><span class="s">host</span><span class="s2">&quot;</span>:<span class="s2">&quot;</span><span class="s">mscottclary-drone.do-community.com</span><span class="s2">&quot;</span>,<span class="s2">&quot;</span><span class="s">level</span><span class="s2">&quot;</span>:<span class="s2">&quot;</span><span class="s">info</span><span class="s2">&quot;</span>,<span class="s2">&quot;</span><span class="s">msg</span><span class="s2">&quot;</span>:<span class="s2">&quot;</span><span class="s">starting the http server</span><span class="s2">&quot;</span>,<span class="s2">&quot;</span><span class="s">port</span><span class="s2">&quot;</span>:<span class="s2">&quot;</span><span class="s">:443</span><span class="s2">&quot;</span>,<span class="s2">&quot;</span><span class="s">proto</span><span class="s2">&quot;</span>:<span class="s2">&quot;</span><span class="s">https</span><span class="s2">&quot;</span>,<span class="s2">&quot;</span><span class="s">time</span><span class="s2">&quot;</span>:<span class="s2">&quot;</span><span class="s">2022-06-29T21:01:12Z</span><span class="s2">&quot;</span>,<span class="s2">&quot;</span><span class="s">url</span><span class="s2">&quot;</span>:<span class="s2">&quot;</span><span class="s">https://mscottclary-drone.do-community.com</span><span class="s2">&quot;</span>}
<span class="nv">server</span> <span class="o">|</span> {<span class="s2">&quot;</span><span class="s">interval</span><span class="s2">&quot;</span>:<span class="s2">&quot;</span><span class="s">30m0s</span><span class="s2">&quot;</span>,<span class="s2">&quot;</span><span class="s">level</span><span class="s2">&quot;</span>:<span class="s2">&quot;</span><span class="s">info</span><span class="s2">&quot;</span>,<span class="s2">&quot;</span><span class="s">msg</span><span class="s2">&quot;</span>:<span class="s2">&quot;</span><span class="s">starting the cron scheduler</span><span class="s2">&quot;</span>,<span class="s2">&quot;</span><span class="s">time</span><span class="s2">&quot;</span>:<span class="s2">&quot;</span><span class="s">2022-06-29T21:01:12Z</span><span class="s2">&quot;</span>}
<span class="nv">server</span> <span class="o">|</span> {<span class="s2">&quot;</span><span class="s">interval</span><span class="s2">&quot;</span>:<span class="s2">&quot;</span><span class="s">24h0m0s</span><span class="s2">&quot;</span>,<span class="s2">&quot;</span><span class="s">level</span><span class="s2">&quot;</span>:<span class="s2">&quot;</span><span class="s">info</span><span class="s2">&quot;</span>,<span class="s2">&quot;</span><span class="s">msg</span><span class="s2">&quot;</span>:<span class="s2">&quot;</span><span class="s">starting the zombie build reaper</span><span class="s2">&quot;</span>,<span class="s2">&quot;</span><span class="s">time</span><span class="s2">&quot;</span>:<span class="s2">&quot;</span><span class="s">2022-06-29T21:01:12Z</span><span class="s2">&quot;</span>}
</code></pre></div>
<p>Give this a few minutes to finish running the Let&rsquo;s Encrypt certificate provisioning.</p>
<p>This will leave the container running in the foreground, however, and it will stop as soon as you exit the process with <code>Ctrl + C</code> or by losing your connection. In order to have the container run in the background as a separate process, you can append the <code>-d</code> flag to the Compose command:</p>
<div class="codehilite"><pre><span></span><code>docker-compose up -d
</code></pre></div>
<p>You will be notified when the container starts and then returned to your shell.</p>
<h2 id="step-3-connecting-drone-to-gitea">Step 3 — Connecting Drone to Gitea</h2>
<p>Now that Drone is up and running, you can connect it to Gitea to authorize runs via OAuth2. Visit your Drone URL in your browser, where you will see a message that says &ldquo;You will be redirected to your source control management system to authenticate&rdquo; above a <strong>Continue</strong> button. If all of the information that has been entered to now has been valid, you will find yourself on a Gitea page asking for your authorization to give Drone permissions.</p>
<p><a href="TODO.html"><img alt="Authorize page" src="TODO" /></a></p>
<p>&lt;$&gt;[note]
<strong>Note:</strong> If you run into an error saying that a client ID or redirect URL was not recognized, check the values entered in your <code>docker-compose.yml</code> to ensure they match your domain names and Gitea OAuth2 information exactly. When you are sure, running <code>docker-compose restart</code> will bring the containers back up with the correct information.
&lt;$&gt;</p>
<p>Once you grant permission, you will be returned to your Drone dashboard, where you will see a list of your repositories.</p>
<h2 id="step-4-setting-up-a-project-for-cicd">Step 4 — Setting Up a Project for CI/CD</h2>
<p>For this tutorial, we will be testing Drone&rsquo;s continuous integration capabilities for automatically running tests. In order to do so, create a sample repository in Gitea. Clone that repository to your development machine and move into the project directory:</p>
<div class="codehilite"><pre><span></span><code>git clone git@&lt;^&gt;your_gitea_domain&lt;^&gt;:sammy/drone-test.git
cd drone-test
</code></pre></div>
<p>Create a new file named <code>drone_test.py</code> and enter the following into it:</p>
<div class="codehilite"><pre><span></span><code><span class="k">assert</span> <span class="mi">1</span> <span class="o">==</span> <span class="mi">1</span>
</code></pre></div>
<p>Save and exit the file, then add it to your repository and push your changes:</p>
<div class="codehilite"><pre><span></span><code>git commit -am &quot;Added test file&quot;
git push origin main
</code></pre></div>
<p>Now, return to your Drone dashboard and click on the repository.</p>
<p>&lt;$&gt;[note]
<strong>Note:</strong> If your repository does not appear in the list, you may need to click the <strong>Sync</strong> button in the upper right corner of the dashboard.
&lt;$&gt;</p>
<p>In the settings tab, click <strong>+ Activate Repository</strong>. This will tell Drone that it should be watching for changes from that repository.</p>
<p>Now that Drone is active for your test project, you will need to create a <strong>pipeline</strong>. Pipelines describe the steps that Drone should take in order to test your software. These are stored in a file named <code>.drone.yml</code>.</p>
<p>In your dev checkout of the project, create a new file named <code>.drone.yml</code> and add the following to it:</p>
<div class="codehilite"><pre><span></span><code><span class="nn">---</span><span class="w"></span>
<span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">pipeline</span><span class="w"></span>
<span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">docker</span><span class="w"></span>
<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">default</span><span class="w"></span>
<span class="nt">steps</span><span class="p">:</span><span class="w"></span>
<span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">test</span><span class="w"></span>
<span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">python</span><span class="w"></span>
<span class="w"> </span><span class="nt">commands</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">python drone_test.py</span><span class="w"></span>
</code></pre></div>
<p>Let&rsquo;s take a look at what this file does. After the header of <code>---</code>, we see that the type of configuration we are working with is a pipeline that runs on Docker containers. It has one step named <code>test</code> using the official <code>python</code> image, and when that container runs and checks out your repository, it will run the command <code>python drone_test.py</code>. Since this file contains a simple assertion that 1 is, indeed, equal to 1, the tests should pass.</p>
<p>Save and exit this file, then add it to your repository and push your changes as you did before.</p>
<p>Visit your Drone dashboard in the browser once more and click on your active repository. You should see that it has started running your pipeline. If it has not started yet, feel free to click the <strong>+ New Build</strong> button in the upper right corner. When you click on the build, you will be presented with a few of the logs from that build updated in real time as your project is cloned and then your <code>test</code> step run. Since it turns out that 1 does, in fact, equal 1, your tests should pass and you will receive a green check mark.</p>
<p>What about 2, though? Check if 1 is equal to 2 by editing <code>drone_test.py</code> and updating the assertion to look like this:</p>
<div class="codehilite"><pre><span></span><code><span class="k">assert</span> <span class="mi">1</span> <span class="o">==</span> <span class="mi">2</span>
</code></pre></div>
<p>Commit and push your work as you did before, and then return to your Drone dashboard. Thankfully, 1 does not equal 2, and so our test fails as expected.</p>
<p>These results are also visible in Gitea, where you can see whether a particular commit broke the build. Visit your repository in Gitea and click on the <strong>Commits</strong> link above the file listing. You should see all of your commits that went into creating this test project. Before the commit where you checked whether 1 was equal to 2, you should see a red <strong>×</strong> indicating that CI has failed, whereas you should see a green <strong></strong> indicating that the build passed. Prior to that, before the repository was hooked up to Drone, you should see no indicators.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In this tutorial, you set up the self-hosted CI/CD service Drone to work with your self-hosted Gitea service. CI/CD services can help keep your project in a clean state and your builds fresh and fast. Drone allows you to automatically run tests, checks, and deploys on your projects. Here, we are simply running a check every time a commit is pushed to Gitea, but the configuration files for Drone are flexible, allowing you to run checks on merges using triggers, run only on certain events using conditionals, and so on. For more information, the <a href="https://docs.drone.io/pipeline/docker/overview/">Drone Pipeline Docs</a> describe many common scenarios.</p>
</article>
<footer>
<p>Page generated on 2023-05-10</p>
</footer>
</main>
<script type="text/javascript">
document.querySelectorAll('.tag').forEach(tag => {
let text = tag.innerText;
tag.innerText = '';
tag.innerHTML = `<a href="/tags.html#${text}">${text}</a>`;
});
</script>
</body>
</html>