<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Denis Gukov</title>
    <description>The latest articles on Forem by Denis Gukov (@fiftin).</description>
    <link>https://forem.com/fiftin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F120665%2Fc42ff907-3e21-407f-b55b-da4da239b576.jpg</url>
      <title>Forem: Denis Gukov</title>
      <link>https://forem.com/fiftin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/fiftin"/>
    <language>en</language>
    <item>
      <title>I connected GitHub to Semaphore UI — and instantly fixed my dev workflow</title>
      <dc:creator>Denis Gukov</dc:creator>
      <pubDate>Tue, 18 Nov 2025 08:39:22 +0000</pubDate>
      <link>https://forem.com/semaphoreui/github-and-semaphore-integration-1b5p</link>
      <guid>https://forem.com/semaphoreui/github-and-semaphore-integration-1b5p</guid>
      <description>&lt;p&gt;For one of my customers, I use &lt;strong&gt;Semaphore UI&lt;/strong&gt; to build and deploy a WordPress website.&lt;br&gt;
The pipeline is simple: &lt;strong&gt;Build → Automatic deploy to DEV → Manual deploy to PROD&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The website code and the Ansible playbooks live in separate repositories, so I use a small Ansible task to clone the website repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Update repo from {{ website_branch_prepared }}&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.git&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;git@github.com:*****/{{ slug }}.git&lt;/span&gt;
    &lt;span class="na"&gt;force&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;accept_hostkey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;website_branch_prepared&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tmp_dir.path&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;key_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tmp_key_file.path&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
  &lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;git_info&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since the source code is stored on GitHub, I use a &lt;strong&gt;GitHub webhook&lt;/strong&gt; to trigger the &lt;code&gt;Build&lt;/code&gt; task automatically on every commit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fav18lxjuqknepzj0kt8v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fav18lxjuqknepzj0kt8v.png" alt="GitHub webhook" width="800" height="605"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The GitHub webhook calls the Semaphore Integration. Semaphore supports &lt;strong&gt;HMAC-based GitHub webhook authentication&lt;/strong&gt;, so the setup is straightforward.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fudxzy6popntjj5naxfzo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fudxzy6popntjj5naxfzo.png" alt="Semaphore Integration" width="800" height="605"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everything worked well except for one issue:&lt;br&gt;
I needed Semaphore to build &lt;strong&gt;the same branch that I pushed to&lt;/strong&gt;, without manually updating the template configuration each time.&lt;/p&gt;

&lt;p&gt;GitHub webhooks include the branch name in the &lt;code&gt;ref&lt;/code&gt; field, but in this format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;refs/heads/&amp;lt;branch_name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Semaphore Integrations can extract this field into a variable, but they do not modify or clean up the value.&lt;br&gt;
To solve this, I added a simple Ansible task that prepares the branch name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Remove 'refs/heads/' prefix from branch name&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.set_fact&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;website_branch_prepared&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;website_branch&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;regex_replace('^refs/heads/',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'')&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F89nhfwtk0brleh8dedxv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F89nhfwtk0brleh8dedxv.png" alt="Semaphore value extractor" width="800" height="605"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that's it — now everything works exactly as I need!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgq24cotu11nk59vkzm82.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgq24cotu11nk59vkzm82.png" alt="Result" width="800" height="605"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>semaphore</category>
      <category>devops</category>
    </item>
    <item>
      <title>What's new in Ansible Semaphore 2.8.66</title>
      <dc:creator>Denis Gukov</dc:creator>
      <pubDate>Sat, 01 Oct 2022 13:08:38 +0000</pubDate>
      <link>https://forem.com/fiftin/whats-new-in-ansible-semaphore-2866-165p</link>
      <guid>https://forem.com/fiftin/whats-new-in-ansible-semaphore-2866-165p</guid>
      <description>&lt;p&gt;After half a year of waiting, the release of the Semaphore has finally come out. What's new in the much-anticipated release?&lt;/p&gt;

&lt;h3&gt;
  
  
  Dark mode
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftlhmcft4m8qf9wr3tqeq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftlhmcft4m8qf9wr3tqeq.jpg" alt=" " width="800" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Color highlighting for Ansible output
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fclhqut1aocvi4rk4w4jb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fclhqut1aocvi4rk4w4jb.jpg" alt=" " width="800" height="630"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Local playbooks
&lt;/h3&gt;

&lt;p&gt;Previously Semaphore could only work with playbooks located in Git repositories. Support for local playbooks has been added in this version.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1a9040ctxpyo9d2nf6b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1a9040ctxpyo9d2nf6b.png" alt=" " width="792" height="802"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>devops</category>
      <category>awx</category>
    </item>
    <item>
      <title>Simple CI/CD on Ansible Semaphore UI</title>
      <dc:creator>Denis Gukov</dc:creator>
      <pubDate>Sat, 17 Sep 2022 09:48:04 +0000</pubDate>
      <link>https://forem.com/semaphoreui/simple-cicd-on-ansible-semaphore-4k17</link>
      <guid>https://forem.com/semaphoreui/simple-cicd-on-ansible-semaphore-4k17</guid>
      <description>&lt;p&gt;CI/CD has become an important part of the software development process, both in large and small companies and projects, including open source.&lt;/p&gt;

&lt;p&gt;The most popular CI/CD systems are GitLab and Jenkins. Both of these systems are powerful, expandable and include many additional features. With the help of these systems, you can build CI/CD of any complexity.&lt;/p&gt;

&lt;p&gt;But it often happens, especially on small projects, that the most simple and straightforward solution is needed, rather than functionality. In this case, Ansible Semaphore is a good alternative to GitLab and Jenkins.&lt;/p&gt;

&lt;p&gt;Ansible Semaphore is a web interface for running Ansible scripts with rudimentary CI/CD. With it, you can turn your playbook into a simple CI/CD system. This is an open source project that has been in development since 2015 and currently has 5200 stars on GitHub.&lt;/p&gt;

&lt;p&gt;The Ansible Semaphore interface resembles that of Jenkins and AWX. The description of the interface can be found in the &lt;a href="https://docs.ansible-semaphore.com/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The following assumes that the reader is familiar with Ansible.&lt;/p&gt;

&lt;p&gt;For demo purposes will take the Trunk development model. Unlike Git-flow, this model assumes the same branch for development and production. This greatly simplifies CI/CD.&lt;/p&gt;

&lt;p&gt;Our pipeline will look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftt5mt29an7u10e3ry0ow.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftt5mt29an7u10e3ry0ow.png" alt="Pipeline" width="671" height="211"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All elements of the scheme are easily implemented on Ansible. And thanks to versioning and scheduling of Ansible Semaphore, all this will work automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Firstly, create a repository on GitHub
&lt;/h2&gt;

&lt;p&gt;We need two playbooks and the corresponding roles for them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;build.yml&lt;/code&gt; - to build the application and send it to S3 storage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;deploy.yml&lt;/code&gt; - for delivering the application to the servers of the dev and production environments.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the source code: &lt;a href="//github.com/fiftin/ansible-semaphore-deploy-test"&gt;github.com/fiftin/ansible-semaphore-deploy-test&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web interface of Ansible Semaphore
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3zr25sp0ga41vlxausf.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3zr25sp0ga41vlxausf.jpeg" alt="Web Interface" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For those who are familiar with AWX, the interface will be clear. For the rest I will briefly tell. The interface has the following key entities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Task&lt;/em&gt; - The process of executing the Ansible playbook.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Task Template&lt;/em&gt; - the template on the basis of which the task is created.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Inventory&lt;/em&gt; - a list of servers for which the task will be performed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Environment&lt;/em&gt; - environment variables (extra vars in Ansible terminology).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Key&lt;/em&gt; – SSH key or login/password that Ansible will use to connect to servers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Repository&lt;/em&gt; - a git repository where the Ansible playbook code is stored.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Project setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Let's create 3 Inventories (&lt;a href="https://demo.ansible-semaphore.com/project/1/inventory" rel="noopener noreferrer"&gt;link&lt;/a&gt;):
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Build&lt;/em&gt; - indicates on which server the application will be built.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Dev&lt;/em&gt; - a list of servers to which the application will be deployed in the dev environment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Production&lt;/em&gt; - a list of servers to which the application will be deployed in the production environment.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Let's create 3 environments (&lt;a href="https://demo.ansible-semaphore.com/project/1/environment" rel="noopener noreferrer"&gt;link&lt;/a&gt;):
&lt;/h3&gt;

&lt;p&gt;Build, Dev, Production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's create 3 task templates (&lt;a href="https://demo.ansible-semaphore.com/project/1/templates" rel="noopener noreferrer"&gt;link&lt;/a&gt;):
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Build&lt;/em&gt; - to build the application and send it to S3 storage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Deploy to Dev&lt;/em&gt; - for delivering the application to the servers of the dev environment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Deploy to Production&lt;/em&gt; - for delivering the application to the servers of the production environment.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will deploy this simple web application: &lt;a href="https://github.com/fiftin/ansible-semaphore-test-app" rel="noopener noreferrer"&gt;github.com/fiftin/ansible-semaphore-test-app&lt;/a&gt; :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Launch
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Build&lt;/strong&gt; task is executed automatically when a new commit appears in the repository. Successful builds are loaded into S3 storage.&lt;/p&gt;

&lt;p&gt;After each successful build, the &lt;strong&gt;Deploy to Dev&lt;/strong&gt; task is automatically launched, which deploys the application to the Dev environment. A demo dev site is available at &lt;a href="https://demo-dev.ansible-semaphore.com" rel="noopener noreferrer"&gt;demo-dev.ansible-semaphore.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can play around with the above example at &lt;a href="https://demo.ansible-semaphore.com" rel="noopener noreferrer"&gt;demo.ansible-semaphore.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
