<?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: Fernando Ultremare</title>
    <description>The latest articles on DEV Community by Fernando Ultremare (@feroult).</description>
    <link>https://dev.to/feroult</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%2F1513347%2Fab93aac2-4e37-4782-8105-4032b4401b60.jpg</url>
      <title>DEV Community: Fernando Ultremare</title>
      <link>https://dev.to/feroult</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/feroult"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>Fernando Ultremare</dc:creator>
      <pubDate>Sat, 14 Dec 2024 13:46:51 +0000</pubDate>
      <link>https://dev.to/feroult/-57a9</link>
      <guid>https://dev.to/feroult/-57a9</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/feroult" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&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%2Fuser%2Fprofile_image%2F1513347%2Fab93aac2-4e37-4782-8105-4032b4401b60.jpg" alt="feroult"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/feroult/bigdag-a-simple-tool-for-managing-bigquery-workflows-23o2" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;BigDAG: A Simple Tool for Managing BigQuery Workflows&lt;/h2&gt;
      &lt;h3&gt;Fernando Ultremare ・ Dec 14&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>BigDAG: A Simple Tool for Managing BigQuery Workflows</title>
      <dc:creator>Fernando Ultremare</dc:creator>
      <pubDate>Sat, 14 Dec 2024 13:38:56 +0000</pubDate>
      <link>https://dev.to/feroult/bigdag-a-simple-tool-for-managing-bigquery-workflows-23o2</link>
      <guid>https://dev.to/feroult/bigdag-a-simple-tool-for-managing-bigquery-workflows-23o2</guid>
      <description>&lt;p&gt;Hey everyone,&lt;/p&gt;

&lt;p&gt;I wanted to share a tool I've been working on called &lt;strong&gt;BigDAG&lt;/strong&gt;. It's born out of my own frustrations with managing data transformations in BigQuery, and I figured others might find it useful too.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Problem: BigQuery Complexity
&lt;/h2&gt;

&lt;p&gt;BigQuery is fantastic for data analysis, but when you start building real pipelines, things can get messy. You end up with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;SQL Files Everywhere:&lt;/strong&gt; Queries for tables, views, and transformations scattered across different files.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Manual Dependency Tracking:&lt;/strong&gt;  Figuring out the correct order to run scripts becomes a chore.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Repetitive &lt;code&gt;bq&lt;/code&gt; Commands:&lt;/strong&gt;  Creating datasets, tables, and views involves running similar commands repeatedly.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Deployment Headaches:&lt;/strong&gt;  Pushing changes to BigQuery can be a manual and error-prone process.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I needed a way to bring some order to this chaos, to define my workflows as code, and to automate the deployment process. That's the motivation behind BigDAG.&lt;/p&gt;

&lt;h2&gt;
  
  
  BigDAG: A Practical Solution
&lt;/h2&gt;

&lt;p&gt;BigDAG is a Python tool that helps you manage data workflows in BigQuery. It's not trying to be a full-fledged data orchestration platform; it's a practical tool to solve a specific problem. Here's the basic idea:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Organize Your Files:&lt;/strong&gt; You structure your SQL scripts, schema definitions, and external table definitions in a folder. This folder represents your data pipeline.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Dependency Detection:&lt;/strong&gt; BigDAG automatically detects dependencies between your SQL scripts by looking at the queries. If a view uses another table, BigDAG knows it needs to be created first.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Manual Overrides:&lt;/strong&gt; If automatic detection isn't enough, you can specify dependencies in a &lt;code&gt;deps.yaml&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Simple CLI:&lt;/strong&gt; BigDAG provides a command-line interface (CLI) to execute your workflows. You can create, update, or delete objects in BigQuery with a single command.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Basic Templating:&lt;/strong&gt; BigDAG uses simple templating to inject your project ID and dataset name into your SQL queries.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Dependency Management:&lt;/strong&gt; Automatically figures out the order to run your scripts.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Code-Based Workflows:&lt;/strong&gt; Define your pipelines using files and YAML.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;CLI for Deployment:&lt;/strong&gt; Deploy changes to BigQuery with a single command.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Dry Run Option:&lt;/strong&gt; See the commands that will be executed before running them.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Recreation Support:&lt;/strong&gt; Easily recreate your entire dataset and all objects.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A Simple DAG Example
&lt;/h2&gt;

&lt;p&gt;Let's look at a simple example of how you might structure your DAG folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my_dag/
├── raw/
│   └── sales.sheet.def.json
│   └── sales.sheet.schema.json
├── trusted/
│   └── sales.view.sql
└── refined/
    └── monthly_sales.table.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what the files might contain:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;my_dag/raw/sales.sheet.def.json&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sourceFormat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GOOGLE_SHEETS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sourceUris"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"https://docs.google.com/spreadsheets/d/FAKE_SPREADSHEET_ID"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"googleSheetsOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"range"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sales!A1:Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"skipLeadingRows"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;my_dag/raw/sales.sheet.schema.json&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sale_date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DATE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"REQUIRED"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"product_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"STRING"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"REQUIRED"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NUMERIC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"REQUIRED"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;my_dag/trusted/sales.view.sql&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- SQL script for creating the sales view&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
    &lt;span class="n"&gt;raw_sales&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;my_dag/refined/monthly_sales.table.sql&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- SQL script for creating the monthly_sales table&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;date_trunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'month'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sale_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_sales&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
    &lt;span class="nv"&gt;`{{project_id}}.{{dataset}}.trusted_sales`&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
    &lt;span class="k"&gt;month&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;sales.sheet.def.json&lt;/code&gt; and &lt;code&gt;sales.sheet.schema.json&lt;/code&gt; define an external table based on a Google Sheet.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;sales.view.sql&lt;/code&gt; creates a view on top of the raw sales data.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;monthly_sales.table.sql&lt;/code&gt; creates a table based on the trusted sales view.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;BigDAG will automatically infer that &lt;code&gt;monthly_sales.table.sql&lt;/code&gt; depends on &lt;code&gt;trusted_sales&lt;/code&gt;, and that &lt;code&gt;trusted_sales&lt;/code&gt; depends on &lt;code&gt;raw_sales&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use It (Quickly)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Install:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/feroult/bigdag.git
&lt;span class="nb"&gt;cd &lt;/span&gt;bigdag
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Set up your DAG folder:&lt;/strong&gt; Organize your SQL scripts and definitions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Run the CLI:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bigdag &lt;span class="nt"&gt;--folder&lt;/span&gt; path/to/your/dag &lt;span class="nt"&gt;--project&lt;/span&gt; your_project_id &lt;span class="nt"&gt;--dataset&lt;/span&gt; your_dataset_name
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Goal: Making Life Easier
&lt;/h2&gt;

&lt;p&gt;BigDAG is about making data workflows more manageable. It's about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Reducing Manual Errors:&lt;/strong&gt; Automating repetitive tasks to avoid mistakes.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Improving Workflow Clarity:&lt;/strong&gt; Making it easier to understand and maintain your data pipelines.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Saving Time:&lt;/strong&gt; Spending less time on infrastructure and more time on analysis.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Check it Out
&lt;/h2&gt;

&lt;p&gt;If you're dealing with similar challenges in BigQuery, you might find BigDAG useful. It's open-source, so feel free to take a look, contribute, or just use it as is.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/feroult/bigdag" rel="noopener noreferrer"&gt;https://github.com/feroult/bigdag&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hope it helps!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Vulcan - Exposing Eclipse JDT Programmatically</title>
      <dc:creator>Fernando Ultremare</dc:creator>
      <pubDate>Wed, 22 May 2024 03:16:22 +0000</pubDate>
      <link>https://dev.to/feroult/vulcan-exposing-eclipse-jdt-programmatically-109g</link>
      <guid>https://dev.to/feroult/vulcan-exposing-eclipse-jdt-programmatically-109g</guid>
      <description>&lt;p&gt;Hey Dev.to community,&lt;/p&gt;

&lt;p&gt;I wanted to share a project I worked on a while back called &lt;strong&gt;Vulcan&lt;/strong&gt;. It's a tool that lets you use Eclipse JDT (Java Development Tools) programmatically via command line or REST API, without needing to run the Eclipse workbench.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Vulcan?
&lt;/h2&gt;

&lt;p&gt;Vulcan is designed to expose the Eclipse JDT OSGi bundle in a programmatic way. It allows you to perform various refactorings such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Extract Method&lt;/strong&gt;: Extracts a block of code into a new method.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rename Type&lt;/strong&gt;: Renames a class or interface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rename Method&lt;/strong&gt;: Renames a method.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rename Field&lt;/strong&gt;: Renames a field.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rename Local Variable&lt;/strong&gt;: Renames a local variable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chained Refactorings&lt;/strong&gt;: Allows multiple refactorings to be applied in sequence.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why I Built It
&lt;/h2&gt;

&lt;p&gt;I used Vulcan to automate refactorings before AI started taking over these tasks. It was super handy for improving code quality and maintaining consistency across projects. With all the new AI advances in refactoring, Vulcan has become a bit obsolete. But I learned a ton while working on it, and it was a fun project.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setting Up OSGi
&lt;/h3&gt;

&lt;p&gt;Setting up the OSGi environment involves selecting and finding the necessary packages from the p2 repository. Here’s how you can do it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create a &lt;code&gt;bndrun&lt;/code&gt; File&lt;/strong&gt;: This file specifies the OSGi bundles and their versions. You can find the complete &lt;code&gt;bndrun&lt;/code&gt; file &lt;a href="https://github.com/feroult/vulcan/blob/main/refactor/application.bndrun"&gt;here&lt;/a&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# refactor/application.bndrun
&lt;/span&gt;
&lt;span class="py"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;target/index.xml;name="app"&lt;/span&gt;

&lt;span class="py"&gt;-standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${index}&lt;/span&gt;

&lt;span class="py"&gt;-runfw&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;org.eclipse.osgi&lt;/span&gt;
&lt;span class="py"&gt;-runproperties.equinox&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;osgi.console=, osgi.console.enable.builtin=true, osgi.ws=gtk, osgi.os=linux, osgi.arch=x86_64&lt;/span&gt;

&lt;span class="py"&gt;-runee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;JavaSE-17&lt;/span&gt;

&lt;span class="py"&gt;-runproperties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;osgi.instance.area=../../../../vulcan-test-workspace,&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;jetty.home.bundle=org.eclipse.jetty.osgi.boot&lt;/span&gt;

&lt;span class="py"&gt;-runrequires&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;bnd.identity;id=org.eclipse.jdt.core,&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;bnd.identity;id=org.eclipse.jdt.ui,&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;bnd.identity;id=vulcan-refactor&lt;/span&gt;

&lt;span class="py"&gt;-runbundles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;org.eclipse.jdt.core;version='[3.35.0,3.35.1)',&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;org.eclipse.jdt.ui;version='[3.30.0,3.30.1)',&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;vulcan-refactor;version='[1.0.0,1.0.1)'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Add Dependencies in &lt;code&gt;pom.xml&lt;/code&gt;&lt;/strong&gt;: Ensure all required OSGi bundles are included. You can find the complete &lt;code&gt;pom.xml&lt;/code&gt; file &lt;a href="https://github.com/feroult/vulcan/blob/main/refactor/pom.xml"&gt;here&lt;/a&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.eclipse.jdt&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;org.eclipse.jdt.core&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.35.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.eclipse.jdt&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;org.eclipse.jdt.ui&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.30.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- Add other dependencies as needed --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting Up the Workspace
&lt;/h3&gt;

&lt;p&gt;To set up the workspace, you need to create or open an existing workspace and load the project. You can find the complete implementation &lt;a href="https://github.com/feroult/vulcan/blob/main/refactor/src/main/java/vulcan/utils/ProjectUtils.java"&gt;here&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.eclipse.core.resources.IProject&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.eclipse.core.resources.ResourcesPlugin&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.eclipse.core.runtime.CoreException&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.eclipse.jdt.core.IJavaProject&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.eclipse.jdt.core.JavaCore&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProjectUtils&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;IJavaProject&lt;/span&gt; &lt;span class="nf"&gt;getJavaProject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;projectName&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;CoreException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;IProject&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ResourcesPlugin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getWorkspace&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getRoot&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getProject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projectName&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exists&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="nc"&gt;IJavaProject&lt;/span&gt; &lt;span class="n"&gt;javaProject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JavaCore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;javaProject&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Accessing Project Information
&lt;/h3&gt;

&lt;p&gt;Once the project is loaded, you can access information like source directories and classpaths. You can find the complete implementation &lt;a href="https://github.com/feroult/vulcan/blob/main/refactor/src/main/java/vulcan/refactorings/BrowseInvoker.java"&gt;here&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.eclipse.jdt.core.IJavaProject&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.eclipse.jdt.core.JavaModelException&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProjectInfo&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;printProjectInfo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;IJavaProject&lt;/span&gt; &lt;span class="n"&gt;javaProject&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;JavaModelException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Source folders:"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;Arrays&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;javaProject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPackageFragmentRoots&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
              &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getKind&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;IPackageFragmentRoot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;K_SOURCE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
              &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPath&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;

        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Classpath entries:"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;Arrays&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;javaProject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRawClasspath&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
              &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPath&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handling Internals
&lt;/h3&gt;

&lt;p&gt;Some refactorings do not expose a public API, and you may need to dig into internals. For example, to perform a rename refactoring, you might need to use internal classes and methods. You can find the complete implementation &lt;a href="https://github.com/feroult/vulcan/blob/main/refactor/src/main/java/vulcan/refactorings/core/RenameType.java"&gt;here&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.eclipse.jdt.core.IType&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.eclipse.jdt.core.refactoring.descriptors.RenameJavaElementDescriptor&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.eclipse.ltk.core.refactoring.RefactoringContribution&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.eclipse.ltk.core.refactoring.RefactoringCore&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RenameTypeRefactoring&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;RenameJavaElementDescriptor&lt;/span&gt; &lt;span class="nf"&gt;createRenameDescriptor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;IType&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;newName&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;RefactoringContribution&lt;/span&gt; &lt;span class="n"&gt;contribution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RefactoringCore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRefactoringContribution&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;IJavaRefactorings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RENAME_TYPE&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;RenameJavaElementDescriptor&lt;/span&gt; &lt;span class="n"&gt;descriptor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RenameJavaElementDescriptor&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;contribution&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createDescriptor&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setProject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getJavaProject&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getElementName&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setJavaElement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setNewName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newName&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setUpdateReferences&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;I decided to share Vulcan now in case anyone finds it interesting or useful. Check it out on GitHub: &lt;a href="https://github.com/feroult/vulcan"&gt;Vulcan&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
