<?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>DEV Community: Prashant Gurung</title>
    <description>The latest articles on DEV Community by Prashant Gurung (@prashant_grg).</description>
    <link>https://dev.to/prashant_grg</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%2F2113146%2F46b680d5-f28f-4748-b465-6f75d6254f09.png</url>
      <title>DEV Community: Prashant Gurung</title>
      <link>https://dev.to/prashant_grg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/prashant_grg"/>
    <language>en</language>
    <item>
      <title>Starlark-Powered Pipelines in Woodpecker CI</title>
      <dc:creator>Prashant Gurung</dc:creator>
      <pubDate>Thu, 11 Sep 2025 10:49:11 +0000</pubDate>
      <link>https://dev.to/jankaritech/starlark-powered-pipelines-in-woodpecker-ci-3ogf</link>
      <guid>https://dev.to/jankaritech/starlark-powered-pipelines-in-woodpecker-ci-3ogf</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Continuous Integration tools are essential to build and test modern software automatically and regularly. Woodpecker CI is one such open-source tool, a lightweight CI/CD system. &lt;a href="https://woodpecker-ci.org/" rel="noopener noreferrer"&gt;Woodpecker CI&lt;/a&gt; is an open-source continuous integration and delivery system that help developers in automating deployment, builds, and testing of code. It's an open-source fork and alternative to the popular Drone CI project with numerous design similarities and Drone pipeline compatibility. Traditionally, Woodpecker pipelines are YAML, but now that programmable pipelines are in vogue, we can finally have the liberty of defining our CI configuration through Starlark — a Python-ish config language.&lt;/p&gt;

&lt;p&gt;In this article, I will walk you through setting up Woodpecker CI with Traefik as a reverse proxy and &lt;a href="https://github.com/opencloud-eu/woodpecker-ci-config-service/" rel="noopener noreferrer"&gt;WCCS&lt;/a&gt; (Woodpecker CI Config Service) to convert Starlark configurations into YAML files so that we can write pipelines in &lt;code&gt;.star&lt;/code&gt; files. Here is a high-level overview of what we are going through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Spinning up a Woodpecker server with Traefik&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Authenticating with GitHub&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enabling a repository&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Connecting WCCS with the Woodpecker server&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end, you’ll be able to write CI pipelines in Starlark and dynamically convert them to YAML during runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Woodpecker Server and Agent
&lt;/h2&gt;

&lt;p&gt;Let's start by setting up the Woodpecker server and agent services in the &lt;code&gt;docker-compose.yml&lt;/code&gt; file as show below:&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="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;woodpecker-server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;woodpeckerci/woodpecker-server:v3&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_LOG_LEVEL=debug&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_OPEN=false&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_HOST=http://&amp;lt;your-ci-server&amp;gt;&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_GITHUB=true&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_GITHUB_CLIENT=xxxxxxx&lt;/span&gt;   &lt;span class="c1"&gt;# GitHub OAuth client ID&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_GITHUB_SECRET=xxxxxxx&lt;/span&gt;   &lt;span class="c1"&gt;# GitHub OAuth secret&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_AGENT_SECRET=xxxxxxx&lt;/span&gt;    &lt;span class="c1"&gt;# shared secret between server and agents&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_ADMIN=your-github-username&lt;/span&gt;
      &lt;span class="c1"&gt;# The following variable is where we set our Woodpecker CI Config Service (Starlark Conversion Service) later&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_CONFIG_SERVICE_ENDPOINT=http://&amp;lt;your-config-service-server&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;woodpecker-server-data:/var/lib/woodpecker/&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;woodpecker-net&lt;/span&gt;

  &lt;span class="na"&gt;woodpecker-agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;woodpeckerci/woodpecker-agent:v3&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;woodpecker-server&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;woodpecker-agent-config:/etc/woodpecker&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_LOG_LEVEL=debug&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_SERVER=woodpecker-server:9000&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET}&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;woodpecker-net&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;woodpecker-server-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;woodpecker-agent-config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;woodpecker-net&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Traefik Configuration for HTTPS
&lt;/h2&gt;

&lt;p&gt;So far our woodpecker server runs on HTTP, so to serve it on HTTPS we are going to configure Traefik.&lt;br&gt;
Traefik plays a crucial role in this architecture by handling all the networking complexities - SSL termination, routing, and load balancing.&lt;br&gt;
Add the following Traefik service right above the &lt;code&gt;woodpecker-server&lt;/code&gt; service in the same &lt;code&gt;docker-compose.yml&lt;/code&gt; file:&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="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;traefik&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik:v3.1&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--log.level=DEBUG"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--providers.docker=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--providers.docker.exposedbydefault=false"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.web.address=:80"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.websecure.address=:443"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.letsencrypt.acme.httpchallenge=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.letsencrypt.acme.email=example@example.com"&lt;/span&gt; &lt;span class="c1"&gt;#letsEncrypt contact email&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:80"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;443:443"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./letsencrypt:/letsencrypt"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock:ro"&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;woodpecker-net&lt;/span&gt;

  &lt;span class="na"&gt;woodpecker-server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;woodpeckerci/woodpecker-server:v3&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;woodpecker-agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;woodpeckerci/woodpecker-agent:v3&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sets up Traefik to listen on ports 80 and 443&lt;/li&gt;
&lt;li&gt;Configures automatic SSL certificate management via &lt;a href="https://letsencrypt.org/" rel="noopener noreferrer"&gt;letsEncrypt&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Enables Docker provider to automatically detect new services&lt;/li&gt;
&lt;li&gt;Mounts necessary volumes for certificate storage and Docker socket access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we have Traefik configured, update the Woodpecker server service by adding following labels:&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="na"&gt;woodpecker-server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;labels&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.enable=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.woodpecker-secure.rule=Host(`your-ci-server`)"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.woodpecker-secure.entrypoints=websecure"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.woodpecker-secure.tls.certresolver=letsencrypt"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.woodpecker-secure.tls=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.services.woodpecker-secure.loadbalancer.server.port=8000"&lt;/span&gt;
      &lt;span class="c1"&gt;# HTTP router + redirect to HTTPS&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.woodpecker-http.rule=Host(`your-ci-server`)"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.woodpecker-http.entrypoints=web"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.woodpecker-http.middlewares=redirect-to-https"&lt;/span&gt;
      &lt;span class="c1"&gt;# Redirect middleware&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"&lt;/span&gt;
  &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;woodpecker-net&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Authenticating with GitHub
&lt;/h2&gt;

&lt;p&gt;Woodpecker cannot do anything by itself, it needs a forge (the service where the repositories are hosted). Woodpecker supports &lt;a href="https://woodpecker-ci.org/docs/administration/configuration/forges/overview" rel="noopener noreferrer"&gt;various forges&lt;/a&gt;, for this blog we will focus on GitHub.&lt;/p&gt;

&lt;p&gt;To connect GitHub with Woodpecker:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Register a new OAuth application in your GitHub developer &lt;a href="https://github.com/settings/applications/new" rel="noopener noreferrer"&gt;settings&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In your GitHub OAuth App, set the authorization callback URL to &lt;code&gt;https://&amp;lt;your-ci-server&amp;gt;/authorize&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&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%2Fn1s7vooyrfyzl6t51dqu.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%2Fn1s7vooyrfyzl6t51dqu.png" alt="Callback URL" width="550" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Copy the generated Client ID and Secret.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use these values in the &lt;code&gt;WOODPECKER_GITHUB_CLIENT&lt;/code&gt; and &lt;code&gt;WOODPECKER_GITHUB_SECRET&lt;/code&gt; env vars.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After that, you can log into the Woodpecker web UI using your GitHub ac&lt;br&gt;
count.&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%2F70iiyvo5p0jt724uz0vf.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%2F70iiyvo5p0jt724uz0vf.png" alt="GitHub Login" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Enabling a Repository
&lt;/h2&gt;

&lt;p&gt;Before enabling a repo in the Woodpecker CI server, make sure to have a GitHub repository ready. You can use an existing repo of your own or create a new empty one. We will be making a pull request to the repo to trigger CI later.&lt;/p&gt;

&lt;p&gt;From the Woodpecker UI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click on Add Repository.&lt;/li&gt;
&lt;/ul&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%2Fwrt93gjarmjo7nqcuav1.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%2Fwrt93gjarmjo7nqcuav1.png" alt="Add Repo" width="800" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable it. For this blog, I am using an existing repo called &lt;code&gt;Drum-KIT&lt;/code&gt; which has already been enabled (as you can see in the last row of the image below).&lt;/li&gt;
&lt;/ul&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%2Fo1kl4ipyqsysg4uuobg7.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%2Fo1kl4ipyqsysg4uuobg7.png" alt="Enable Repo" width="800" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Woodpecker will automatically add the necessary webhooks to the repo.&lt;/p&gt;
&lt;h2&gt;
  
  
  Triggering Your First CI Run
&lt;/h2&gt;

&lt;p&gt;While we are trying to integrate a starlark conversion service, woodpecker CI supports the default &lt;code&gt;.yml&lt;/code&gt; pipelines.&lt;br&gt;
So. now we can trigger our first CI using a traditional YAML configuration as show below.&lt;br&gt;
Create a new branch  and add the following configuration in the root of your enabled github repo with name &lt;code&gt;.woodpecker.yml&lt;/code&gt;.&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="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&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;hello&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;alpine&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Hello from CI"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "--------------------"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "This is my first CI BUILD"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "--------------------"&lt;/span&gt;
    &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pull_request&lt;/span&gt;
      &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;  &lt;span class="c1"&gt;# configure it as your repo's base branch.(in my case, it's `master`)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that you have your &lt;code&gt;.woodpecker.yaml&lt;/code&gt; file ready, you can commit and push it to your enabled GitHub repository and then, open a Pull Request from your branch  to &lt;code&gt;master/main&lt;/code&gt; according to you repo. GitHub's default branch is set to &lt;code&gt;main&lt;/code&gt;.&lt;br&gt;
Once the PR is created, Woodpecker will automatically fetch the pipeline configuration, and start your first CI run. You can then head over to the Woodpecker UI to watch the build logs in real time.&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%2F1ldveaxw5afspm4a67lw.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%2F1ldveaxw5afspm4a67lw.png" alt="Yaml Pipeline" width="800" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have already set up the woodpecker server and triggered pipelines using the traditional &lt;code&gt;.yaml&lt;/code&gt; configuration, let's now integrate WCCS to unlock flexibility and define our pipelines in Starlark.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting Up WCCS-Woodpecker CI Config Service (Starlark Conversion Service)
&lt;/h2&gt;

&lt;p&gt;The Woodpecker Config Conversion Service (WCCS) is a lightweight web service created and maintained by &lt;a href="https://opencloud.eu/en" rel="noopener noreferrer"&gt;Opencloud-eu&lt;/a&gt;. It enables Woodpecker CI to convert pipeline definitions written in Starlark into standard YAML on the fly by receiving a signed POST request from Woodpecker.&lt;br&gt;
You can easily deploy WCCS using their official Docker image available on Docker Hub: &lt;a href="https://hub.docker.com/r/opencloudeu/wccs" rel="noopener noreferrer"&gt;opencloudeu/wccs&lt;/a&gt;.&lt;br&gt;
Update your &lt;code&gt;docker-compose.yml&lt;/code&gt; to include the WCCS service:&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="nn"&gt;...&lt;/span&gt;

  &lt;span class="na"&gt;wccs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;opencloudeu/wccs:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wccs&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;server&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WCCS_LOG_LEVEL=debug&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WCCS_SERVER_PUBLIC_KEY=/keys/public.pem&lt;/span&gt; &lt;span class="c1"&gt;# path to public key(generated in the section below)&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/opt/woodpecker/keys:/keys&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/etc/ssl/certs:/etc/ssl/certs:ro&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.enable=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.wccs.rule=Host(`your-wccs-server`)"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.wccs.entrypoints=websecure"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.wccs.tls.certresolver=letsencrypt"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.services.wccs.loadbalancer.server.port=8080"&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;woodpecker-net&lt;/span&gt;

&lt;span class="nn"&gt;...&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;woodpecker-server-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;woodpecker-agent-config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;woodpecker-net&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Generating the Public Key
&lt;/h2&gt;

&lt;p&gt;Before running any pipeline, woodpecker server sends a POST request to an external config service (in our case WCCS) with all current config file and build information of the current repository. The external service takes the information and sends back the pipeline configurations. In our case, this is how a starlark file is being converted into yaml configurations.&lt;/p&gt;

&lt;p&gt;And before WCCS can accept and process those requests, it needs to verify that the requests are trusted and coming from the woodpecker server.&lt;/p&gt;

&lt;p&gt;Every request sent by Woodpecker is signed using a http-signature by a private key (ed25519) generated on the first start of the Woodpecker server. You can get the public key for the verification of the http-signature from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;https://&amp;lt;your-ci-server&amp;gt;&lt;/span&gt;/api/signature/public-key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Store that public key into &lt;code&gt;keys/public.pem&lt;/code&gt; in the same directory level as your docker compose. The &lt;code&gt;public.pem&lt;/code&gt; file is then mounted to the WCCS container as shown in the snippet above and used in env variable &lt;code&gt;WCCS_SERVER_PUBLIC_KEY&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting WCCS with Woodpecker
&lt;/h2&gt;

&lt;p&gt;To allow Woodpecker to fetch pipeline configs from WCCS, we added this to the server env:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;WOODPECKER_CONFIG_SERVICE_ENDPOINT=http://wccs:8080/ciconfig
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Woodpecker now sends a signed JSON payload to WCCS whenever a build is triggered. WCCS verifies the signature using the public key and responds with a YAML pipeline based on your &lt;code&gt;.woodpecker.star&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;After conneting WCCS with the woodpecker server, there's one more thing that you need to do i.e. to set pipeline config path, because by default Woodpecker will take &lt;code&gt;.woodpecker.yaml&lt;/code&gt; as it's pipeline configuration.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;From the settings of your Woodpecker server, go to &lt;code&gt;Repositories&lt;/code&gt; and click on the enabled repo's settings icon as shown below:&lt;/li&gt;
&lt;/ul&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%2Fs9qp9hfi79tw252xq4d6.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%2Fs9qp9hfi79tw252xq4d6.png" alt="Settings" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Then, add &lt;code&gt;.woodpecker.star&lt;/code&gt; as pipeline config path and save it:&lt;/li&gt;
&lt;/ul&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%2Flcxluag6fk8o4gvx7zyc.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%2Flcxluag6fk8o4gvx7zyc.png" alt="Pipeline Config" width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Add a Simple Starlark Pipeline
&lt;/h2&gt;

&lt;p&gt;Here's a simple starlark pipeline configuration example that you can add in your project.&lt;br&gt;
Create a new branch by checking out from your &lt;code&gt;master/main&lt;/code&gt; branch.&lt;br&gt;
Add the following configration in the &lt;code&gt;.woodpecker.star&lt;/code&gt; file in your repo's root&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;def main(ctx):
    return [{
        "name": "hello",
        "steps": [
            {
                "name": "greeting",
                "image": "alpine",
                "commands": [
                    "echo Hello from CI",
                ],
                "when": {
                    "event": ["push", "pull_request"],
&lt;/span&gt;&lt;span class="gp"&gt;                    "branch": ["master"], #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;configure it as your repo&lt;span class="s1"&gt;'s base branch.(in my case, it'&lt;/span&gt;s &lt;span class="sb"&gt;`&lt;/span&gt;master&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;                },
            }
        ]
    }]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Triggering CI with starlark-powered pipeline
&lt;/h2&gt;

&lt;p&gt;Now that you have your &lt;code&gt;.woodpecker.star&lt;/code&gt; file ready, commit and push it to your GitHub repository and then, open a new Pull Request from your branch  to master/main.&lt;br&gt;
Once the PR is created, Woodpecker will automatically send a build request to WCCS, fetch the pipeline configuration, and start your first CI run. You can then head over to the Woodpecker UI to watch the build logs in real time.&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%2Fkut58k0j45g8y58i8zln.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%2Fkut58k0j45g8y58i8zln.png" alt="Converted Yaml" width="800" height="221"&gt;&lt;/a&gt;&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%2F4j5jqhwp9yopqvbdfhxl.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%2F4j5jqhwp9yopqvbdfhxl.png" alt="CI Build" width="800" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This setup provides a robust, secure, and flexible CI/CD pipeline using Woodpecker CI, enhanced with WCCS for configuration management and protected by Traefik. The integration of these components creates a powerful system that can handle complex pipeline configurations while maintaining security and ease of use.&lt;/p&gt;

&lt;p&gt;Now, we can write pipelines in Starlark and let WCCS handle the conversion on the fly. This approach brings flexibility, structure, and the power of logic-based configurations to our CI pipelines.&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>starlark</category>
      <category>traefik</category>
      <category>woodpecker</category>
    </item>
  </channel>
</rss>
