<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>cubeSQL &#8211; Xojo Programming Blog</title>
	<atom:link href="https://blog.xojo.com/tag/cubesql/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.xojo.com</link>
	<description>Blog about the Xojo programming language and IDE</description>
	<lastBuildDate>Tue, 25 Feb 2025 21:38:53 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>Build a Xojo Plugin with GitHub Actions</title>
		<link>https://blog.xojo.com/2025/02/25/build-a-xojo-plugin-with-github-actions/</link>
		
		<dc:creator><![CDATA[Jürg Otter]]></dc:creator>
		<pubDate>Tue, 25 Feb 2025 22:30:00 +0000</pubDate>
				<category><![CDATA[Community]]></category>
		<category><![CDATA[Cross-Platform]]></category>
		<category><![CDATA[Desktop]]></category>
		<category><![CDATA[Guest Post]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[#YearofCode]]></category>
		<category><![CDATA[cubeSQL]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[GitHub]]></category>
		<category><![CDATA[Plugins]]></category>
		<category><![CDATA[Year of Code]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=14509</guid>

					<description><![CDATA[As part of Xojo&#8217;s 2025 Year of Code initiative, February&#8217;s theme is databases—a perfect fit for my recent work on the Open Source CubeSQLPlugin. This&#8230;]]></description>
										<content:encoded><![CDATA[
<p>As part of <a href="https://forum.xojo.com/t/2025-year-of-code-february/" target="_blank" rel="noreferrer noopener">Xojo&#8217;s 2025 Year of Code</a> initiative, <a href="https://forum.xojo.com/t/2025-year-of-code-february/" target="_blank" rel="noreferrer noopener">February&#8217;s theme is databases</a>—a perfect fit for my recent work on the <a href="https://github.com/marcobambini/cubesqlplugin" target="_blank" rel="noreferrer noopener">Open Source CubeSQLPlugin</a>. This article shares insights into automating its build process with <a href="https://docs.github.com/en/actions/writing-workflows" target="_blank" rel="noreferrer noopener">GitHub Actions Workflows</a>.</p>



<p>Xojo plugins are add-ons that extend the functionality of the Xojo development environment, allowing developers to add additional features, integrate third-party libraries, and much more.</p>



<h2 class="wp-block-heading">Write a Xojo Plugin</h2>



<p>This article assumes familiarity with the <a href="https://documentation.xojo.com/topics/custom_controls/plugins_sdk.html" target="_blank" rel="noreferrer noopener">Xojo Plugin SDK</a> and focuses on automating plugin builds. If you&#8217;re new to plugin development, check out the resources in Xojo’s &#8220;Extras&#8221; folder, YouTube tutorials, and the Xojo Forum.</p>



<p>It builds on my previous blog post <a href="https://blog.xojo.com/2022/10/19/xojo-github-actions/" target="_blank" rel="noreferrer noopener">Xojo Github Actions</a> and involves <a href="https://www.docker.com/" target="_blank" rel="noreferrer noopener">Docker</a>, which has also been a part of a previous blog posts (<a href="https://blog.xojo.com/2024/06/03/docker-database-servers-and-xojo/" target="_blank" rel="noreferrer noopener">Docker, Database Servers and Xojo</a>, <a href="https://blog.xojo.com/2021/05/17/running-xojo-web-applications-in-docker/" target="_blank" rel="noreferrer noopener">Running Xojo Web Applications in Docker</a>). So read up there on what a <a href="https://docs.github.com/en/actions/writing-workflows" target="_blank" rel="noreferrer noopener">GitHub Actions workflow</a> is, what the <code>.yaml</code> files in the Folder <code>.github/workflows</code> are used for.</p>



<h2 class="wp-block-heading"><strong>Build a Xojo Plugin</strong></h2>



<p>Since Xojo is a cross platform development environment, a Xojo plugin needs to be built for all the supported platforms <em>(Windows, macOS, Linux)</em> and all their architectures <em>(Intel/ARM, 32/64Bit)</em>.</p>



<p>All the plugin parts (the Windows <code>.dll</code>&#8216;s, .macOS <code>.dylib</code>&#8216;s and Linux <code>.so</code>&#8216;s) are being packaged into a <code>.xojo_plugin</code> file.</p>



<p>One of the challenges is to have an environment to build all that. At the first glance it seems that one would need a Mac, a Windows machine &#8211; as well as some Linux environment <em>(any of which could also just be a virtual machine)</em>. And one would first think that there will be a lot of manual steps involved &#8211; building the plugin source on various machines, copying all the built plugin parts together to finally assemble the resulting <code>.xojo_plugin</code>.</p>



<p>However &#8211; all that is not required. You can build a Xojo plugin without your own machine being involved, fully automated. GitHub Actions provides all we need.</p>



<h2 class="wp-block-heading"><strong>Example: CubeSQLPlugin</strong></h2>



<p>Let us have a look at this existing <a href="https://github.com/marcobambini/cubesqlplugin" target="_blank" rel="noreferrer noopener">Open Source Plugin</a>.</p>



<p><a href="https://github.com/marcobambini/cubesqlplugin" target="_blank" rel="noreferrer noopener"><em>CubeSQLPlugin</em></a><em> is an extension for the Xojo programming environment that allows developers to use </em><a href="https://sqlabs.com/cubesql" target="_blank" rel="noreferrer noopener"><em>CubeSQL</em></a><em>, a fully featured and high performance relational database management system built on top of the SQLite database engine.</em></p>



<p>The plugin is being used in the Open Sourced Administrations Tools for CubeSQL, which are both written in Xojo: <a href="https://github.com/cubesql/webadmin" target="_blank" rel="noreferrer noopener">CubeSQL Web Admin</a> and <a href="https://github.com/cubesql/cubeSQLAdmin" target="_blank" rel="noreferrer noopener">CubeSQL (Desktop) Admin</a>.</p>



<p>Internally the plugin uses <a href="https://www.libressl.org" target="_blank" rel="noreferrer noopener">LibreSSL</a> for TLS Connections of Database Clients to CubeSQL Server. This means that first the LibreSSL libs need to be built for all build targets. Only then can the plugin source be built again for all build targets. And finally, all the plugin parts need to be assembled into the Xojo plugin structure for the final <code>.xojo_plugin</code>.</p>



<p>We have automated the whole build process of the plugin using <a href="https://docs.github.com/de/actions" target="_blank" rel="noreferrer noopener">GitHub Actions</a> by having created three workflows. You&#8217;ll find their source in the folder <a href="https://github.com/marcobambini/cubesqlplugin/tree/master/.github/workflows" target="_blank" rel="noreferrer noopener">.github/workflows</a>. All workflow executions you&#8217;ll find on GitHub in the tab named <a href="https://github.com/marcobambini/cubesqlplugin/actions" target="_blank" rel="noreferrer noopener">Actions</a> <em>(as long as they have not been deleted or removed)</em>.</p>



<p><strong>LibreSSL</strong><br>This workflow will download the <a href="https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/" target="_blank" rel="noreferrer noopener">LibreSSL Source</a>, and build it for all needed build targets. It needs to be run only once, e.g. if a new <a href="https://www.libressl.org" target="_blank" rel="noreferrer noopener">LibreSSL</a> version is available that should be included for the next plugin build. That&#8217;s why it&#8217;s a workflow that can be run manually. It asks which LibreSSL version should be built and whether the built libraries should be committed and pushed to the repository.</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="367" src="https://blog.xojo.com/wp-content/uploads/2025/02/01_Workflow_LibreSSL-1024x367.png" alt="" class="wp-image-14514" srcset="https://blog.xojo.com/wp-content/uploads/2025/02/01_Workflow_LibreSSL-1024x367.png 1024w, https://blog.xojo.com/wp-content/uploads/2025/02/01_Workflow_LibreSSL-300x108.png 300w, https://blog.xojo.com/wp-content/uploads/2025/02/01_Workflow_LibreSSL-768x276.png 768w, https://blog.xojo.com/wp-content/uploads/2025/02/01_Workflow_LibreSSL-1536x551.png 1536w, https://blog.xojo.com/wp-content/uploads/2025/02/01_Workflow_LibreSSL-2048x735.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p><strong>CubeSQLPlugin</strong><br>This workflow will build the <a href="https://github.com/marcobambini/cubesqlplugin" target="_blank" rel="noreferrer noopener">CubeSQL Xojo Plugin</a> for all build targets using the currently committed LibreSSL libs in the repository. It will commit and push all the built <code>.dll</code>&#8216;s, .<code>dylib</code>&#8216;s and <code>.so</code>&#8216;s to the repository.</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="496" src="https://blog.xojo.com/wp-content/uploads/2025/02/02_Workflow-Plugin-1024x496.png" alt="" class="wp-image-14515" srcset="https://blog.xojo.com/wp-content/uploads/2025/02/02_Workflow-Plugin-1024x496.png 1024w, https://blog.xojo.com/wp-content/uploads/2025/02/02_Workflow-Plugin-300x145.png 300w, https://blog.xojo.com/wp-content/uploads/2025/02/02_Workflow-Plugin-768x372.png 768w, https://blog.xojo.com/wp-content/uploads/2025/02/02_Workflow-Plugin-1536x744.png 1536w, https://blog.xojo.com/wp-content/uploads/2025/02/02_Workflow-Plugin-2048x992.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p><strong>Create Release</strong><br>That one is pretty obvious, right? It builds the structure of the final <code>.xojo_plugin</code> and creates a new <a href="https://github.com/marcobambini/cubesqlplugin/releases" target="_blank" rel="noreferrer noopener">Release</a> in the repository with the final Xojo plugin &#8211; ready to be downloaded.</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="535" src="https://blog.xojo.com/wp-content/uploads/2025/02/03_Plugin-Release-1024x535.png" alt="" class="wp-image-14516" srcset="https://blog.xojo.com/wp-content/uploads/2025/02/03_Plugin-Release-1024x535.png 1024w, https://blog.xojo.com/wp-content/uploads/2025/02/03_Plugin-Release-300x157.png 300w, https://blog.xojo.com/wp-content/uploads/2025/02/03_Plugin-Release-768x401.png 768w, https://blog.xojo.com/wp-content/uploads/2025/02/03_Plugin-Release-1536x802.png 1536w, https://blog.xojo.com/wp-content/uploads/2025/02/03_Plugin-Release-2048x1069.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h3 class="wp-block-heading"><strong>Build for all Platforms and Architectures</strong></h3>



<p>The two workflows &#8220;LibreSSL&#8221; and &#8220;CubeSQLPlugin&#8221; are similar in their approach to building for all the supported platforms <em>(Windows, macOS, Linux)</em> and all their architectures <em>(Intel/ARM, 32/64Bit)</em>.</p>



<p>GitHub offers hosted <a href="https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners" target="_blank" rel="noreferrer noopener">virtual machines to run workflows</a>. All virtual machines contain an environment of tools, packages, and settings available for <a href="https://docs.github.com/en/actions/writing-workflows" target="_blank" rel="noreferrer noopener">GitHub Actions workflows</a> to use.</p>



<p>So our workflows can simply use these environments. The workflows consist of several steps, each of which can be executed on a different runner. Remember that I&#8217;ve written: &#8220;You can build a Xojo plugin without your own machine involved&#8221;?</p>



<p>In the following ScreenShot we&#8217;re seeing that the Linux-ARM64 plugin part is being created on a Ubuntu 24.04.1 ARM machine.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="475" src="https://blog.xojo.com/wp-content/uploads/2025/02/04_Build-Linux-ARM64-1024x475.png" alt="" class="wp-image-14517" srcset="https://blog.xojo.com/wp-content/uploads/2025/02/04_Build-Linux-ARM64-1024x475.png 1024w, https://blog.xojo.com/wp-content/uploads/2025/02/04_Build-Linux-ARM64-300x139.png 300w, https://blog.xojo.com/wp-content/uploads/2025/02/04_Build-Linux-ARM64-768x356.png 768w, https://blog.xojo.com/wp-content/uploads/2025/02/04_Build-Linux-ARM64-1536x712.png 1536w, https://blog.xojo.com/wp-content/uploads/2025/02/04_Build-Linux-ARM64-2048x949.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h4 class="wp-block-heading"><strong>macOS</strong></h4>



<ul class="wp-block-list">
<li>Runs on a GitHub macOS virtual machine.</li>



<li>Uses the preinstalled Xcode command line tools to build &#8220;Intel x86_64&#8221; and &#8220;ARM64&#8221; and combine the two to a universal binary.</li>



<li>CodeSigns with credentials added as repository secrets.</li>
</ul>



<h4 class="wp-block-heading"><strong>Linux</strong></h4>



<ul class="wp-block-list">
<li>ARM32, ARM64
<ul class="wp-block-list">
<li>Runs on a GitHub Linux virtual ARM machine.</li>



<li>To always have exactly the same build toolset <em>(e.g. so that it doesn&#8217;t matter if GitHub updates to a newer Ubuntu version)</em> it leverages a Docker container (e.g. <a href="https://hub.docker.com/r/arm64v8/gcc/" target="_blank" rel="noreferrer noopener">arm64v8/gcc</a>) &#8211; and actually builds the source inside the Docker container.</li>
</ul>
</li>



<li>i386, x86_64
<ul class="wp-block-list">
<li>Runs on a GitHub Linux virtual Intel machine.</li>



<li>Also uses a <a href="https://hub.docker.com/_/gcc" target="_blank" rel="noreferrer noopener">gcc Docker Image</a> to build the source.</li>
</ul>
</li>
</ul>



<h4 class="wp-block-heading"><strong>Windows</strong></h4>



<ul class="wp-block-list">
<li>Runs on a GitHub Windows virtual machine.</li>



<li>Uses the preinstalled Visual Studio environment to build all &#8220;win32&#8221;, &#8220;x64&#8221; and &#8220;ARM64&#8221;.</li>
</ul>



<h2 class="wp-block-heading"><strong>How to Build your Xojo Plugin</strong></h2>



<p>I&#8217;m very well aware that this article can&#8217;t help you build your plugin. Each one is individual and needs its own steps and processes.</p>



<p>However, should you consider automating your plugin build process. You might want to look in more detail in the open-source plugin source and the <a href="https://github.com/marcobambini/cubesqlplugin/tree/master/.github/workflows" target="_blank" rel="noreferrer noopener">workflows</a> which are building <a href="https://github.com/marcobambini/cubesqlplugin" target="_blank" rel="noreferrer noopener">CubeSQLPlugin</a> for all required targets and architectures.</p>



<h2 class="wp-block-heading"><strong>How to Transition from Manual to Automated</strong></h2>



<p>While creating a plugin, one doesn&#8217;t think of its build process in the early stages, maybe not even for the first few released versions.&nbsp;</p>



<p>Plugin building is usually a manual process, requiring manual execution of a lot of steps and commands on various machines. It is tedious and prone to errors. It takes time, which adds up the more you build. There&#8217;s a point when one should think about streamlining and automate the process, allowing for building new versions without too much effort.<br>In addition, it&#8217;s certainly a good idea to have a consistent build environment other than your own development machine as you could install and change things on there which could impact the builds. That&#8217;s why building inside a Docker container is an interesting approach &#8211; it will remain exactly the same each and every time, even if the host machine is changed <em>(e.g. by being updated to a new operating system version)</em>.</p>



<p>All that used to be like that before having written the GitHub workflow.</p>



<p>How can you transition from a manual to an automated process for your own project? I won&#8217;t be able to provide all answers. But, I&#8217;d like to give a few ideas of possible steps, with no intention of them being complete or the best choice for your project.</p>



<ul class="wp-block-list">
<li>Use a version control system.
<ul class="wp-block-list">
<li>Git and GitHub are (for many use cases) free.</li>



<li>Using a Cloud solution serves as a backup should something happen to your machines.</li>



<li>You can additionally benefit from other services such as the GitHub Actions workflows mentioned in this article.</li>
</ul>
</li>



<li>Everything that is only in your head should go into some written form. Once you don&#8217;t need to do something for some time, you might forget or miss something.
<ul class="wp-block-list">
<li>Start with documenting all steps, commands, and actions.</li>
</ul>
</li>



<li>Try to get rid of manual steps. Are you copy-and-pasting several commands to a terminal each and every time?
<ul class="wp-block-list">
<li>Put them in a shell script, so that you only need to run one command.</li>



<li>You can use these scripts later when going to a fully-automated system.</li>
</ul>
</li>



<li>When initial development is finished and you&#8217;re ready to release:
<ul class="wp-block-list">
<li>Don&#8217;t do releases from your development machine. It might be defunct, broken, stolen, or you&#8217;re installing some new cool stuff and suddenly notice issues with other projects. Having a dedicated build machine is one idea. </li>



<li>Another is going the &#8220;GitHub Actions&#8221; way which also has its benefits.
<ul class="wp-block-list">
<li>Runners are virtual machines that are provisioned and &#8220;fresh&#8221; every time. It&#8217;s always the very same environment to start with for each build, no matter what you&#8217;re doing during a build.</li>



<li>You don&#8217;t need to maintain many different build machines yourself. This can even solve some security issues, as it&#8217;s probably not your main interest of keeping everything up to date. I prefer to focus on programming and not on system maintenance.</li>



<li>However, the runners might be outdated in a few years &#8211; and new runners (with newer operating systems) will be available.
<ul class="wp-block-list">
<li>That&#8217;s usually not a very big issue on macOS and Windows since their build tools are usually backwards compatible for quite a long time.</li>



<li>Especially for Linux we have decided not to rely on the provided host but use a Docker Image and run the builds inside a Docker container. So you can, for example, use a Docker container with an image that perfectly fits your lowest compatibility needs, while the host itself might only have (too) new libraries installed.</li>
</ul>
</li>
</ul>
</li>



<li>Start putting all your steps and scripts into a workflow &#8211; be it just on your dedicated build environment, or using GitHub Actions.
<ul class="wp-block-list">
<li>Certainly separate them per required host (macOS / Windows / Linux).</li>



<li>Split a long script into smaller parts, e.g: Download LibreSSL, Build x86_64, Build arm64, Build Universal.
<ul class="wp-block-list">
<li>In case of an error, it&#8217;s easier to see that &#8220;macOS: Build arm64 has failed&#8221;, compared to &#8220;Build for macOS has failed&#8221; (because it&#8217;s all in a lengthy script).</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>



<li>Running the workflows manually is great and likely more than good enough.<br>Once the project activity grows (e.g. you&#8217;re receiving a lot of pull request or have many developers working on it):
<ul class="wp-block-list">
<li>Ensure that the build and test workflows are running on each pull request, require them to complete successfully before being able to merge. That by the way has been explained in the previous blog post <a href="https://blog.xojo.com/2022/10/19/xojo-github-actions/">Xojo GitHub Actions</a>. After all, no one wants to find out after many commits that the build is no longer working, and then have to search the history for what might have caused the problem.</li>
</ul>
</li>



<li>You&#8217;ll notice that once it&#8217;s all automated you no longer miss it. It causes no additional effort for you, instead bringing a lot of benefits <em>(such as noticing early when code changes cause oddities in tests and builds)</em>.</li>
</ul>



<h3 class="wp-block-heading"><strong>A Fun Fact</strong></h3>



<p>In the early days a build of the CubeSQLPlugin often took a couple of hours &#8211; and with personal attention needed so that one couldn&#8217;t work on much else during that time.</p>



<p>The first iteration of the workflows reduced the entire build (LibreSSL + Plugin) to about 25 minutes. With no attention and manual interaction needed &#8211; which means more time for other work. What took the longest was the LibreSSL ARM builds &#8211; they have been run on Intel hardware with QEMU for ARM emulation.</p>



<p>Back to the beginning of this article and <a href="https://forum.xojo.com/t/2025-year-of-code-february/" target="_blank" rel="noreferrer noopener">Xojo&#8217;s 2025 Year of Code</a> initiative &#8211; in February I have improved the workflows to use the newly available GitHub Actions runners that run on ARM hardware. Which means that no more emulation is required &#8211; and the build time has now come down to about 4 minutes for LibreSSL Libs <em>(required only once per new LibreSSL version)</em> and 2.5 minutes for the Xojo database plugin for all its supported platforms and architectures!</p>



<h2 class="wp-block-heading"><strong>That&#8217;s All Folks!</strong></h2>



<p>I hope this brief introduction of how to use <a href="https://docs.github.com/en/actions/writing-workflows" target="_blank" rel="noreferrer noopener">GitHub Actions workflows</a> and <a href="https://www.docker.com" target="_blank" rel="noreferrer noopener">Docker</a> to build a <a href="https://documentation.xojo.com/topics/custom_controls/plugins_sdk.html" target="_blank" rel="noreferrer noopener">Xojo plugin</a> has been helpful to some, food for thought to others.</p>



<p><em><em>Jürg Otter is a long term user of Xojo and working for </em><a href="https://www.cmiag.ch/"><em>CM Informatik AG</em></a><em>. Their Application </em><a href="https://cmi-bildung.ch/"><em>CMI LehrerOffice</em></a><em> is a Xojo Design Award Winner 2018. In his leisure time Jürg provides some </em><a href="https://www.jo-tools.ch/xojo/"><em>bits and pieces for Xojo Developers</em></a><em>.</em></em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>



<p></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Docker, Database Servers and Xojo</title>
		<link>https://blog.xojo.com/2024/06/03/docker-database-servers-and-xojo/</link>
		
		<dc:creator><![CDATA[Jürg Otter]]></dc:creator>
		<pubDate>Mon, 03 Jun 2024 16:15:00 +0000</pubDate>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[Guest Post]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[cubeSQL]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[SQL]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=12939</guid>

					<description><![CDATA[There are many ways to develop with Xojo and use Database Servers. You might be working on a multi user Desktop Application that stores data in a Database. Or you're creating a multi user Xojo Web Application that needed to be scaled up to a couple of running instances - and use a Database for data storage. Or maybe you've written a mobile application that connects to a Backend REST API - again possibly written in Xojo (e.g. with the open source Express, or with Xojo Web).]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">Xojo and Database Servers</h2>



<p>There are many ways to develop with Xojo and use database servers. You might be working on a multi-user desktop application that stores data in a database. Or you&#8217;re creating a multi-user Xojo Web application that needed to be scaled up to a couple of running instances &#8211; and use a database for data storage. Or maybe you&#8217;ve written a mobile application that connects to a Backend REST API &#8211; again possibly written in Xojo (e.g. with the open source <a href="https://github.com/sworteu/Express">Express</a>, or with <a href="https://xojo.com/products/web.php">Xojo Web</a>).</p>



<h3 class="wp-block-heading">As a Developer</h3>



<p>When working on such projects, you&#8217;re facing quite some challenges with the various database servers available.</p>



<ul class="wp-block-list">
<li>You need to have a local development environment running the database server(s).</li>



<li>You want to test your application against a newer version of a database server.</li>



<li>A customer reports an issue occurring with a database server version that you don&#8217;t have installed.</li>
</ul>



<h3 class="wp-block-heading">Developer Machine</h3>



<p>I like to have a development machine that&#8217;s as cleaned up as possible. So I don&#8217;t like the idea of installing various servers just for some tests. It&#8217;s so tedious to clean up, update to other versions, and remove all remnants of an installation when no longer required. And I don&#8217;t want to have various services running on my machine that I don&#8217;t need all the time, as they&#8217;re just eating precious memory and slowing down the machine.</p>



<p>Wouldn&#8217;t it be cool if there was an easy way to spin up various database servers when needed without all the hassle? Well there is &#8211; let&#8217;s have a look at Docker and Docker Compose.</p>



<h2 class="wp-block-heading">Docker Compose and Database Servers</h2>



<p>With Docker, it&#8217;s a breeze to develop with and for various database servers.</p>



<h3 class="wp-block-heading">What is Docker?</h3>



<p>If you haven&#8217;t heard of Docker, go ahead and read their excellent Documentation: <a href="https://docs.docker.com/get-started/overview/">Docker &#8211; Overview</a>. Or read my previous Guest Blog Post: <a href="https://blog.xojo.com/2021/05/17/running-xojo-web-applications-in-docker/">Running Xojo Web Applications in Docker</a>. I&#8217;m just going to repeat and quote some basics from the Overview to get a very brief introduction:</p>



<p><em><a href="https://www.docker.com/">Docker</a> is an open platform for developing, shipping, and running applications. It provides the ability to package and run an application in a loosely isolated environment called a container. The isolation and security allow you to run many containers simultaneously on a given host. Containers are lightweight and contain everything needed to run the application, so you do not need to rely on what is currently installed on the host.</em></p>



<p><em>A <a href="https://docs.docker.com/guides/docker-concepts/the-basics/what-is-a-container/">Docker Container</a> is a runnable instance of an image. You can create, start, stop, move, or delete a container. By default, a container is relatively well-isolated from other containers and its host machine. You can control how isolated a container’s network, storage, or other underlying subsystems are from other containers or from the host machine. A container is defined by its image as well as any configuration options you provide to it when you create or start it.</em></p>



<p><em><a href="https://docs.docker.com/compose/">Docker Compose</a> is a tool for defining and running multi-container applications. It is the key to unlocking a streamlined and efficient development and deployment experience. Compose simplifies the control of your entire application stack, making it easy to manage services, networks, and volumes in a single, comprehensible YAML configuration file. Then, with a single command, you create and start all the services from your configuration file.</em></p>



<h3 class="wp-block-heading">Docker Compose for Database Server and Administration</h3>



<p>This sounds great. Just write a YAML configuration file, and run a database server on the host (meaning: your developer machine). And be able to start, stop, delete at any time &#8211; without messing with Preferences, Launch Daemons.</p>



<p>But why Docker Compose for &#8220;multi container applications&#8221;? Well, the database server is one part: one application, one running docker container. And as a developer you most likely want to have some database administration tool running, too &#8211; which again is running as an own isolated container. And in order to have both available with a single command (or by simply pressing &#8220;run&#8221; or &#8220;stop&#8221;) we&#8217;re going to &#8220;compose&#8221; these two parts so that they fit and work together.</p>



<h2 class="wp-block-heading">How to Run a Database Server and Administration Tool with Docker Compose</h2>



<p>Let&#8217;s do that and install and configure three database servers which we then can use to develop an application in Xojo to connect to:</p>



<ul class="wp-block-list">
<li>PostgreSQL &amp; pgAdmin</li>



<li>cubeSQL &amp; cubeSQL Web Admin</li>



<li>MariaDB &amp; phpMyAdmin</li>
</ul>



<p>Note: All the following configuration files are available on GitHub: <a href="https://github.com/jo-tools/docker">jo-tools/docker</a>.</p>



<h3 class="wp-block-heading">Requirements</h3>



<p>Obviously we need to have Docker installed. And since we&#8217;re talking about being a Developer and doing this on our own development machine we&#8217;re going to install <a href="https://www.docker.com/products/docker-desktop/">Docker Desktop</a>. With Docker Desktop you can later easily start, stop and delete the Containers we&#8217;re going to create. When it&#8217;s empty (no containers installed), it looks like this:</p>



<figure class="wp-block-image size-large is-style-default"><img loading="lazy" decoding="async" width="1024" height="589" src="https://blog.xojo.com/wp-content/uploads/2024/05/1_Docker-Desktop-1024x589.png" alt="" class="wp-image-12969" srcset="https://blog.xojo.com/wp-content/uploads/2024/05/1_Docker-Desktop-1024x589.png 1024w, https://blog.xojo.com/wp-content/uploads/2024/05/1_Docker-Desktop-300x172.png 300w, https://blog.xojo.com/wp-content/uploads/2024/05/1_Docker-Desktop-768x441.png 768w, https://blog.xojo.com/wp-content/uploads/2024/05/1_Docker-Desktop-1536x883.png 1536w, https://blog.xojo.com/wp-content/uploads/2024/05/1_Docker-Desktop-2048x1177.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>We&#8217;re now ready to get started…! But wait &#8211; there is one thing we should talk about before.</p>



<h3 class="wp-block-heading">Data Storage</h3>



<p>Where are the database servers going to store their data(bases)?</p>



<p>If you want to know all the details, head over to the Docker Documentation: <a href="https://docs.docker.com/storage/">Manage data in Docker</a>. What we need to know is this:</p>



<p>Docker has two options for containers to store files on the host machine, so that the files are persisted even after the container stops: <a href="https://docs.docker.com/storage/volumes/">Volumes</a>, and <a href="https://docs.docker.com/storage/bind-mounts/">Bind Mounts</a>.</p>



<p>Why is this important to know? Well, let&#8217;s assume we create and start a Container &#8220;PostgreSQL Server&#8221;. Think of that Container as a &#8220;virtual machine&#8221; or a bag. It can save data in there just fine. But once we delete that container or throw away that bag, then all it&#8217;s data is gone, too.</p>



<p>That might well be intended…! For example if you just wish to play with some tool and don&#8217;t care to keep the data you&#8217;re creating with it. However, if you&#8217;d like to easily back up that data, or update a Container from &#8220;v.15&#8221; to &#8220;v.16&#8221; and keep the previous data &#8211; then that&#8217;s when Volumes and Bind Mounts are needed.</p>



<p>Quoted from the docs:</p>



<p><strong>Volumes</strong><br><em>Volumes are the preferred mechanism for persisting data generated by and used by Docker containers. They are completely managed by Docker.</em></p>



<p><strong>Bind Mounts</strong><br><em>Data is stored in a directory on the host machine, which is mounted into the container.</em></p>



<p>Okay &#8211; two options, so what&#8217;s the difference?</p>



<p>The Bind Mounts are a folder on the host (your developer machine). So you can easily see them, view/edit/backup the data. It might be an option if you need easy access to these files.</p>



<p>The Volumes are the preferred way, since they are managed within Docker. Using Docker Desktop you can view the contents &#8211; but it&#8217;s not that simple to view/edit files in there as you can&#8217;t mount it as a volume or folder on your developer machine. Of course there are some more advanced ways, which I&#8217;m not going to explain here. So definitely the best option when you don&#8217;t need to work with and open these files with other tools, too. The other reason we don&#8217;t need Bind Mounts for our database servers is that we can just use their administration tools to import/export data such as database dumps.</p>



<p>Alright &#8211; now let&#8217;s start and set up some database servers.</p>



<h3 class="wp-block-heading">PostgreSQL Server &amp; pgAdmin</h3>



<p>Let&#8217;s use the example setup with Volumes for Data Storage from here: <a href="https://github.com/jo-tools/docker/tree/main/local-postgres-volumes">GitHub: jo-tools/docker &#8211; local-postgres-volumes</a>.</p>



<p>Download the <code>docker-compose.yml</code> file from my <a href="https://github.com/jo-tools/docker">GitHub Repository</a> and place it in a folder on your machine.</p>



<p>I&#8217;m going to save it in: <code>~/Docker/local-postgres-volumes</code></p>



<figure class="wp-block-image size-large is-style-default"><img loading="lazy" decoding="async" width="1024" height="282" src="https://blog.xojo.com/wp-content/uploads/2024/05/2_Local-Folder-1024x282.png" alt="" class="wp-image-12970" srcset="https://blog.xojo.com/wp-content/uploads/2024/05/2_Local-Folder-1024x282.png 1024w, https://blog.xojo.com/wp-content/uploads/2024/05/2_Local-Folder-300x83.png 300w, https://blog.xojo.com/wp-content/uploads/2024/05/2_Local-Folder-768x211.png 768w, https://blog.xojo.com/wp-content/uploads/2024/05/2_Local-Folder.png 1170w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p><strong>YAML file: <strong><code>docker-compose.yml</code></strong></strong><br>A word of precaution when writing and editing YAML files:</p>



<ul class="wp-block-list">
<li>Indentation is done by spaces (not by tabs!)</li>



<li>Indentation has to be exact (use always 2 spaces), or expect to get syntax errors</li>
</ul>



<p>Let&#8217;s look at the content of this <code>docker-compose.yml.</code></p>



<pre class="wp-block-code"><code>x-common-timezone: &amp;services-timezone Etc/UTC

name: local_postgresql
services:
  postgres-server:
    container_name: postgresql
    hostname: postgresql
    image: postgres:16-alpine
    volumes:
      - postgresql_data:/var/lib/postgresql/data
    environment:
      TZ: *services-timezone
      PGTZ: *services-timezone
      POSTGRES_PASSWORD: postgres
      POSTGRES_USER: postgres
      POSTGRES_DB: postgres
    networks:
      - local_postgresql_net
    ports:
      - 5432:5432
    restart: unless-stopped

  pgadmin:
    container_name: pgadmin4
    hostname: pgadmin4
    image: dpage/pgadmin4:8
    environment:
      TZ: *services-timezone
      PGADMIN_DEFAULT_EMAIL: admin@postgres.sql
      PGADMIN_DEFAULT_PASSWORD: admin
      PGADMIN_CONFIG_UPGRADE_CHECK_ENABLED: "False"
      PGADMIN_CONFIG_SERVER_MODE: "False"
      PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: "False"
    networks:
      - local_postgresql_net
    volumes:
      - pgadmin4_data:/var/lib/pgadmin
    ports:
      - 5433:80
    restart: unless-stopped
    depends_on:
      - postgres-server

volumes:
  postgresql_data:
    driver: local
    name: postgresql_data
  pgadmin4_data:
    driver: local
    name: pgadmin4_data

networks:
  local_postgresql_net:
    name: local_postgresql</code></pre>



<p>In the first hierarchy there are:</p>



<p>• <code>x-common-timezone: &amp;services-timezone Etc/UTC</code> <br>In this extension we define a scalar value for the timezone, which will be used for the environment variables in both services. The template uses UTC (which is also the default for Docker). You can change it to your timezone, e.g.: Europe/Zurich. This might be desired if you&#8217;re going to select the current date/time from the database server later.<br> • <code>name: local_postgresql </code><br>That&#8217;s the name of this &#8220;setup&#8221;. We&#8217;ll later see this in Docker Desktop&#8217;s Containers. There we can also start/stop/delete this setup at any time.<br>• <code>services:</code> <br>There are two services in this setup: <code>postgres-server</code> and <code>pgadmin</code><br>• <code>volumes:</code><br>And two volumes: one for the database server, and one for the administration tool.<br>• <code>networks: </code><br>Both these two containers will be in the same network.</p>



<p>When we look at the two services <code>postgres-server</code> and <code>pgadmin4</code>:<br>• <code>container_name:</code> <br>The name of the Container. We&#8217;ll later see this as a sub-element of this docker-compose setup named <code>local_postgresql</code> <br>• <code>hostname: </code><br>Think of a container as a virtual machine. Every one has its hostname. And the two containers in this setup we have placed in the same network. So they can talk to each other via hostname. <br> •<code>image:</code> <br> That&#8217;s basically the content of the container. We&#8217;re going to install: <br> • <code>postgres:16-alpine</code> <br> PostgreSQL Server in Version 16.x, running on Linux Alpine (a very lightweight one).<br> • <code>dpage/pgadmin4:8 </code><br> pgAdmin 4 in Version 8.x as the administration tool.<br>• <code>environment:</code> <br>Here the environment variables of the containers are being defined. TZ defines the Timezone, and uses the value we have defined in the extension on the very top. The other Environment Variables contain the defaults and standards &#8211; here used when the containers are being started the first time to define the default logins, users and passwords. Our PostgreSQL server will have a default login with both username and password: <code>postgres</code>. pgAdmin we have configured to auto-login without authentication required, and disabled update checks. <br>• <code>networks:</code> <br>Remember &#8211; both in the same one (defined below) <br>• <code>volumes:</code> <br>The format here is: <code>volume_name:/mount/in/folder</code> <br>We&#8217;re going to see these volumes later in Docker Desktop. They&#8217;ll contain the data. Usually the documentation of the Docker Images explain which folders are being used to save data, so they can be mounted here to either a Volume (or a Bind Mount). <br>• <code>ports</code>:<br> The format here is: <code>host-port:container-port</code> <br>For PostgreSQL Server we&#8217;re using 5432:5432, meaning that we can connect on our developer machine to Port 5432 (and this will be forwarded to the container running PostgreSQL Server, listening on Port 5432 inside the Container). pgAdmin is running on Port 80 within the Container, but we connect to it from our developer machine via Port 5433. <br>• <code>restart: unless-stopped </code><br>This basically means: if you have this setup running and reboot your host (developer machine), then this setup gets restarted automatically. If you&#8217;re going to stop this setup (because you don&#8217;t need it at the time), then it&#8217;ll be stopped after a reboot, too.</p>



<h2 class="wp-block-heading">Start Docker Compose Setup</h2>



<p>As we can&#8217;t use Docker Desktop to start a Docker Compose setup (maybe this feature will be added in the future) we have to use Terminal. Change Directory to where the <code>docker-compose.yml</code> file is and type: <code>docker-compose up -d</code></p>



<figure class="wp-block-image size-large is-style-default"><img loading="lazy" decoding="async" width="1024" height="289" src="https://blog.xojo.com/wp-content/uploads/2024/05/3_docker-compose-up-1024x289.png" alt="" class="wp-image-12976" srcset="https://blog.xojo.com/wp-content/uploads/2024/05/3_docker-compose-up-1024x289.png 1024w, https://blog.xojo.com/wp-content/uploads/2024/05/3_docker-compose-up-300x85.png 300w, https://blog.xojo.com/wp-content/uploads/2024/05/3_docker-compose-up-768x217.png 768w, https://blog.xojo.com/wp-content/uploads/2024/05/3_docker-compose-up.png 1140w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>The referenced Images will be pulled, the defined network created and the Containers started.</p>



<p>Open the Dashboard in Docker Desktop and look at the Containers &#8211; you&#8217;ll find our setup named <code>local_postgresql</code> with the two Containers <code>postgresql</code> and <code>pgadmin4</code>:</p>



<figure class="wp-block-image size-large is-style-default"><img loading="lazy" decoding="async" width="1024" height="444" src="https://blog.xojo.com/wp-content/uploads/2024/05/4_DockerDesktop-Containers-1024x444.png" alt="" class="wp-image-12977" srcset="https://blog.xojo.com/wp-content/uploads/2024/05/4_DockerDesktop-Containers-1024x444.png 1024w, https://blog.xojo.com/wp-content/uploads/2024/05/4_DockerDesktop-Containers-300x130.png 300w, https://blog.xojo.com/wp-content/uploads/2024/05/4_DockerDesktop-Containers-768x333.png 768w, https://blog.xojo.com/wp-content/uploads/2024/05/4_DockerDesktop-Containers-1536x666.png 1536w, https://blog.xojo.com/wp-content/uploads/2024/05/4_DockerDesktop-Containers-2048x889.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>They are currently up and running.<br>If you want to stop this setup simply press the button under &#8220;Actions&#8221;. It then will be stopped even after a reboot. And should you need to run the setup again later, just hit the button &#8220;Run&#8221; here.</p>



<p>Under Volumes we see the created ones:</p>



<figure class="wp-block-image size-large is-style-default"><img loading="lazy" decoding="async" width="1024" height="444" src="https://blog.xojo.com/wp-content/uploads/2024/05/5_DockerDesktop-Volumes-1024x444.png" alt="" class="wp-image-12978" srcset="https://blog.xojo.com/wp-content/uploads/2024/05/5_DockerDesktop-Volumes-1024x444.png 1024w, https://blog.xojo.com/wp-content/uploads/2024/05/5_DockerDesktop-Volumes-300x130.png 300w, https://blog.xojo.com/wp-content/uploads/2024/05/5_DockerDesktop-Volumes-768x333.png 768w, https://blog.xojo.com/wp-content/uploads/2024/05/5_DockerDesktop-Volumes-1536x666.png 1536w, https://blog.xojo.com/wp-content/uploads/2024/05/5_DockerDesktop-Volumes-2048x889.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>You can click on one to see it&#8217;s contents &#8211; here the data of postgresql:</p>



<figure class="wp-block-image size-large is-style-default"><img loading="lazy" decoding="async" width="1024" height="444" src="https://blog.xojo.com/wp-content/uploads/2024/05/6_DockerDesktop-Volume-Content-1024x444.png" alt="" class="wp-image-12979" srcset="https://blog.xojo.com/wp-content/uploads/2024/05/6_DockerDesktop-Volume-Content-1024x444.png 1024w, https://blog.xojo.com/wp-content/uploads/2024/05/6_DockerDesktop-Volume-Content-300x130.png 300w, https://blog.xojo.com/wp-content/uploads/2024/05/6_DockerDesktop-Volume-Content-768x333.png 768w, https://blog.xojo.com/wp-content/uploads/2024/05/6_DockerDesktop-Volume-Content-1536x666.png 1536w, https://blog.xojo.com/wp-content/uploads/2024/05/6_DockerDesktop-Volume-Content-2048x889.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p><strong><strong>Connect With PGADMIN4</strong></strong><br>Either switch again to &#8220;Containers&#8221; and click the link &#8220;5433:80&#8221; in the column &#8220;Port&#8221; of the Container &#8220;pgadmin4&#8221; in Docker Desktop, or open a browser and go to  <code>http://localhost:5433</code>.</p>



<p>Since this is the first launch of pgAdmin4 we need to add a server &#8211; I have given it the name &#8220;Docker&#8221;:</p>



<figure class="wp-block-image size-large is-style-default"><img loading="lazy" decoding="async" width="1024" height="541" src="https://blog.xojo.com/wp-content/uploads/2024/05/7_pgadmin-launch-1024x541.png" alt="" class="wp-image-12980" srcset="https://blog.xojo.com/wp-content/uploads/2024/05/7_pgadmin-launch-1024x541.png 1024w, https://blog.xojo.com/wp-content/uploads/2024/05/7_pgadmin-launch-300x158.png 300w, https://blog.xojo.com/wp-content/uploads/2024/05/7_pgadmin-launch-768x406.png 768w, https://blog.xojo.com/wp-content/uploads/2024/05/7_pgadmin-launch-1536x811.png 1536w, https://blog.xojo.com/wp-content/uploads/2024/05/7_pgadmin-launch-2048x1082.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>In the register &#8220;Connection&#8221; we have to enter the connection data:</p>



<figure class="wp-block-image size-large is-style-default"><img loading="lazy" decoding="async" width="1024" height="541" src="https://blog.xojo.com/wp-content/uploads/2024/05/8_pgadmin-add-server-1024x541.png" alt="" class="wp-image-12981" srcset="https://blog.xojo.com/wp-content/uploads/2024/05/8_pgadmin-add-server-1024x541.png 1024w, https://blog.xojo.com/wp-content/uploads/2024/05/8_pgadmin-add-server-300x158.png 300w, https://blog.xojo.com/wp-content/uploads/2024/05/8_pgadmin-add-server-768x406.png 768w, https://blog.xojo.com/wp-content/uploads/2024/05/8_pgadmin-add-server-1536x811.png 1536w, https://blog.xojo.com/wp-content/uploads/2024/05/8_pgadmin-add-server-2048x1082.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>Username and password are the default ones (remember the environment variables?). And for this local developer setup I have chosen to save the password.</p>



<p>The most interesting part is the hostname.</p>



<ul class="wp-block-list">
<li><strong>You can&#8217;t use localhost here! <br></strong>The reason is simple: pgAdmin is running in a Container (think of it again as a virtual machine), so it&#8217;s &#8220;localhost&#8221; is the Container running pgadmin4. Not your developer machine, and not the Container where postgres is running.</li>



<li>I have entered: <code>host.docker.internal </code><br>This is explained in the <a href="https://docs.docker.com/desktop/networking/">Docker Documentation: networking:</a> The host has a changing IP address, or none if you have no network access. We recommend that you connect to the special DNS name host.docker.internal, which resolves to the internal IP address used by the host.</li>



<li>Another option would be to just use the hostname: <code>postgresql</code> <br>And why does this work? Well &#8211; we have configured these two containers to be in the same network, and the server is running in the host named &#8220;postgresql&#8221;.</li>
</ul>



<p>And here we go: Let&#8217;s expand the entries of the just added server &#8220;Docker&#8221;, click on the main database &#8220;postgres&#8221; and execute a query: <code>SELECT version();</code></p>



<figure class="wp-block-image size-large is-style-default"><img loading="lazy" decoding="async" width="1024" height="541" src="https://blog.xojo.com/wp-content/uploads/2024/05/9_pgadmin-connect-1024x541.png" alt="" class="wp-image-12982" srcset="https://blog.xojo.com/wp-content/uploads/2024/05/9_pgadmin-connect-1024x541.png 1024w, https://blog.xojo.com/wp-content/uploads/2024/05/9_pgadmin-connect-300x158.png 300w, https://blog.xojo.com/wp-content/uploads/2024/05/9_pgadmin-connect-768x406.png 768w, https://blog.xojo.com/wp-content/uploads/2024/05/9_pgadmin-connect-1536x811.png 1536w, https://blog.xojo.com/wp-content/uploads/2024/05/9_pgadmin-connect-2048x1082.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p><strong>Connect with Xojo</strong><br>We&#8217;re ready to use Xojo now and work on applications using our local PostgreSQL Server.<br>Let&#8217;s connect to our local development server running in Docker. As a simple test I have placed the following code in the Opening event of a Label:</p>



<pre class="wp-block-code"><code>Var db As New PostgreSQLDatabase
db.Host = "localhost"
db.UserName = "postgres"
db.Password = "postgres"
db.Port = 5432

Call db.Connect

Var rs As RowSet = db.SelectSQL("SELECT version();")
Me.Text = rs.Column("version").StringValue</code></pre>



<figure class="wp-block-image size-large is-style-default"><img loading="lazy" decoding="async" width="1024" height="573" src="https://blog.xojo.com/wp-content/uploads/2024/05/10_Xojo-connect-1024x573.png" alt="" class="wp-image-13046" srcset="https://blog.xojo.com/wp-content/uploads/2024/05/10_Xojo-connect-1024x573.png 1024w, https://blog.xojo.com/wp-content/uploads/2024/05/10_Xojo-connect-300x168.png 300w, https://blog.xojo.com/wp-content/uploads/2024/05/10_Xojo-connect-768x429.png 768w, https://blog.xojo.com/wp-content/uploads/2024/05/10_Xojo-connect-1536x859.png 1536w, https://blog.xojo.com/wp-content/uploads/2024/05/10_Xojo-connect-2048x1145.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h3 class="wp-block-heading">Additional Information</h3>



<p>You&#8217;ve seen how to easily spin up PostgreSQL with Docker Compose and connect to it with pgAdmin4 and Xojo. What else do we need to know?</p>



<p><strong>Images</strong><br>You&#8217;ll find available Images on <a href="https://hub.docker.com">Docker Hub</a>. In the above setup we have used <a href="https://hub.docker.com/_/postgres">postgres</a> and <a href="https://hub.docker.com/r/dpage/pgadmin4">pgAdmin4</a>. If you look at them on Docker Hub you&#8217;ll see that they provides many images. One can select them by Tag. The used Tag &#8220;postgres:16-alpine&#8221; here means that we get the latest version 16 (e.g. 16.2, and some month later maybe 16.3 or 16.4). If for some reason we want to get exactly version 16.1, then we can use the corresponding Tag, e.g. &#8220;postgres:16.1-alpine&#8221;.</p>



<p><strong>A Setup using Bind Mounts</strong><br>If you&#8217;re going to try the <code>docker-compose.yml</code> with Bind Mounts from here: <a href="https://github.com/jo-tools/docker/tree/main/local-postgres-bind-mounts">GitHub: jo-tools/docker &#8211; local-postgres-bindmounts</a>, then you&#8217;ll notice the difference in the configuration here:</p>



<pre class="wp-block-code"><code>volumes:
  - ./postgresql_data:/var/lib/postgresql/data</code></pre>



<p>You won&#8217;t get a Volume in Docker, but instead this will create a folder named <code>postgresql_data</code> in the folder where you&#8217;ve placed <code>docker-compose.yml</code> and launched the setup with <code>docker-compose up -d</code>.</p>



<p><strong>Docker Compose Commands</strong><br>We&#8217;ve already mentioned that the first launch of a Docker Compose setup will require Terminal. The following commands are to be executed in the same directory containing the yaml file, too:</p>



<p><code>docker-compose up -d</code><br>This looks for the file <code>docker-compose.yml</code>, pull the images, do all the setup and launch the containers. You can also use this command to restart this setup, should you have stopped it.</p>



<p><code>docker-compose stop</code><br>Stops all services in this setup. Or do it using the buttons in Register &#8220;Containers&#8221; in Docker Desktop.</p>



<p><code>docker-compose down -v</code><br>This removes all services and volumes from this setup. Use this to get rid of all Containers, Volumes AND data. Haven&#8217;t we mentioned that &#8211; it&#8217;s super easy to clean up <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f609.png" alt="😉" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br>You could also click the Delete buttons in Docker Desktop in both &#8220;Containers&#8221; and &#8220;Volumes&#8221; (and optionally &#8220;Images&#8221;).<br>If you omit the option &#8220;-v&#8221;, it will leave the Volumes in place. This might be of interest if you intend to keep the data, in case you&#8217;re going to spin up this setup again some time later.</p>



<p><code>docker-compose down</code><br><code>docker-compose pull</code> <br><code>docker-compose up -d</code><br>These 3 commands will first tear down the existing containers (leaving volumes and data in place), then pull the latest versions from Docker Hub and set everything up again. Remember that we have used the Image &#8220;postgres:16-alpine&#8221;, and currently got PostgreSQL 16.2? Once PostgreSQL 16.3 is available we can execute these commands and get the latest version. Again super easy to update, isn&#8217;t it?</p>



<h2 class="wp-block-heading">cubeSQL &amp; cubeSQL Web Admin</h2>



<p>Another popular database server for Xojo is <a href="https://sqlabs.com/cubesql">cubeSQL</a>. It basically adds multi-user handling (including groups, privileges, und much more) on top of SQLite.</p>



<p><em>cubeSQL is a fully-featured and high-performance relational database management system built on top of the SQLite database engine.</em></p>



<p><em>It is the ideal database server for both developers who want to convert a single user database solution to a multi-user project and for companies looking for an affordable, easy to use and easy to maintain database management system.</em></p>



<p>You&#8217;ll find an example setup for Docker Compose here: <a href="https://github.com/jo-tools/docker/tree/main/local-cubesql-volumes">GitHub: jo-tools/docker &#8211; local-cubesql-volumes.</a> To connect with Xojo you need a plugin, which can be downloaded from <a href="https://github.com/cubesql/cubeSQLAdmin">GitHub: cubesql/cubeSQLAdmin</a></p>



<figure class="wp-block-image size-large is-style-default"><img loading="lazy" decoding="async" width="1024" height="753" src="https://blog.xojo.com/wp-content/uploads/2024/05/11_cubesql-webadmin-1-1024x753.png" alt="" class="wp-image-12983" srcset="https://blog.xojo.com/wp-content/uploads/2024/05/11_cubesql-webadmin-1-1024x753.png 1024w, https://blog.xojo.com/wp-content/uploads/2024/05/11_cubesql-webadmin-1-300x221.png 300w, https://blog.xojo.com/wp-content/uploads/2024/05/11_cubesql-webadmin-1-768x565.png 768w, https://blog.xojo.com/wp-content/uploads/2024/05/11_cubesql-webadmin-1-1536x1129.png 1536w, https://blog.xojo.com/wp-content/uploads/2024/05/11_cubesql-webadmin-1.png 1638w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h3 class="wp-block-heading">cubeSQL Web Admin &#8211; Written with Xojo Web</h3>



<p>cubeSQL has a desktop application for administration, which you&#8217;ll find in the above GitHub <a href="https://github.com/cubesql/cubeSQLAdmin">Repository</a>, too. As you can guess a desktop based application is not quite convenient when it comes to an easy docker compose setup.</p>



<p>While I&#8217;m using Xojo for desktop and console applications for quite some time, I&#8217;ve never really used Xojo Web before. That&#8217;s why I took this opportunity and gave it a try. And behold &#8211; in very short time (most functionality in just a weekend&#8217;s leisure time) I&#8217;ve written &#8220;<a href="https://github.com/cubesql/webadmin">Xojo Web Admin</a>&#8220;. It&#8217;s been a pleasure to create something more than just some simple examples &#8211; I like the idea of creating something useful even while learning something new. And I&#8217;ll be honest &#8211; I&#8217;ve found a couple of bugs in Xojo&#8217;s Web Framework while writing <a href="https://github.com/cubesql/webadmin">cubeSQL Web Admin</a>, but most of them are already fixed and implemented for the next Xojo version. And finishing and polishing the project to make it available to the public also took an additional couple of evenings.</p>



<p><a href="https://github.com/cubesql/webadmin">cubeSQL Web Admin is Open Source</a> &#8211; so go ahead and have a look at the source (or even better: contribute to it by adding more features, find bugs and fix them). There might be some things of interest, such as:</p>



<ul class="wp-block-list">
<li>How to use Xojo Web to build an app that runs in a Docker container.  See also this Guest Blog Post: <a href="https://blog.xojo.com/2021/05/17/running-xojo-web-applications-in-docker/">Running Xojo Web Applications in Docker</a>
<ul class="wp-block-list">
<li>A new feature added while developing <a href="https://github.com/cubesql/webadmin">GitHub: cubeSQL Web Admin:</a> The Post Build Script builds a &#8220;Multi Architecture&#8221; Docker Image (<code>linux/amd64</code> &amp; <code>linux/arm64v8</code>) by building the Xojo Web App for BuildTargets Linux x86 64-bit and Linux ARM 64-bit. This allows to support running the Docker Image natively on Macs with both Intel and Apple processors.</li>
</ul>
</li>



<li>How to use Launch Arguments and Environment Variables for configuration. This allowed us to configure the Environment Variables in our Docker setup to preconfigure the connection data.</li>



<li>The approach with subclassed WebContainer&#8217;s that implement the WebDataSource Interface. The quite generic base class cntDatasourceBase can be configured to:
<ul class="wp-block-list">
<li>be searchable (Main Window shows/hides the WebSearchField if the subclassed Container defines to (not) be searchable).</li>



<li>define which Fields of a RowSet should be displayed (cubeSQL uses special custom commands for administration, so we can&#8217;t select distinct columns ourselves, and there&#8217;s no paging within these custom commands).</li>



<li>define virtual Fields, which will be shown in the WebListbox, but aren&#8217;t part of a RowSet. Internally the base class uses a Dictionary Array which gets built from the RowSet, so one could use it with other external DataSources, too.</li>



<li>show the Fields in the WebListBox, while allowing each Container to override the default behaviour (see for example the Databases, which use a WebListboxImageRenderer for a virtual field &#8211; depending on a couple of Columns in the RowSet an appropriate Status Icon is being displayed).</li>
</ul>
</li>
</ul>



<figure class="wp-block-image size-large is-style-default"><img loading="lazy" decoding="async" width="1024" height="546" src="https://blog.xojo.com/wp-content/uploads/2024/05/12_cubesql-webadmin-2-1024x546.png" alt="" class="wp-image-12984" srcset="https://blog.xojo.com/wp-content/uploads/2024/05/12_cubesql-webadmin-2-1024x546.png 1024w, https://blog.xojo.com/wp-content/uploads/2024/05/12_cubesql-webadmin-2-300x160.png 300w, https://blog.xojo.com/wp-content/uploads/2024/05/12_cubesql-webadmin-2-768x410.png 768w, https://blog.xojo.com/wp-content/uploads/2024/05/12_cubesql-webadmin-2-1536x819.png 1536w, https://blog.xojo.com/wp-content/uploads/2024/05/12_cubesql-webadmin-2-2048x1092.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>To connect to this cubeSQL setup with Xojo I have placed the following code in the Opening event of a Label:</p>



<pre class="wp-block-code"><code>Var db As New CubeSQLServer
db.Host = "localhost"
db.UserName = "admin"
db.Password = "admin"
db.Port = 4430

Call db.Connect

Var rs As RowSet = db.SelectSQL("SHOW INFO FOR KEY server_version")
Me.Text = rs.Column("value").StringValue</code></pre>



<h2 class="wp-block-heading">MariaDB &amp; phpMyAdmin</h2>



<p><em>MariaDB Server is a high performing open source relational database, forked from MySQL.</em></p>



<p>You&#8217;ll find an example setup for Docker Compose here: <a href="https://github.com/jo-tools/docker/tree/main/local-mariadb-volumes">GitHub: jo-tools/docker &#8211; local-mariadb-volumes.</a></p>



<p>I&#8217;m not going to explain much more here about MariaDB (MySQL) or phpMyAdmin here &#8211; I think you should figure out easily how to launch the Administration tool and connect to the server when looking at the contents of this <code>docker-compose.yml.</code> But, since it&#8217;s a widely used and well-known database server, I wanted to provide a setup for this combo, too.</p>



<p>As a simple test for this setup I have placed the following code in the Opening event of a Label:</p>



<pre class="wp-block-code"><code>Var db As New MySQLCommunityServer
db.Host = "127.0.0.1"
db.UserName = "root"
db.Password = "mariadb"
db.Port = 3306

Call db.Connect

Var rs As RowSet = db.SelectSQL("SELECT version() AS version;")
Me.Text = rs.Column("version").StringValue</code></pre>



<p>For some reason the connection needs an IP Address or a Hostname (and doesn&#8217;t work with <code>localhost</code>).</p>



<h2 class="wp-block-heading">A Word of Caution</h2>



<p>The above setups are only intended as a local test setup &#8211; don&#8217;t use them like this for production. Even on a local developer machine you shouldn&#8217;t run the services with these simple initial login credentials, so go ahead and change them.</p>



<p>The purpose is to explain how Docker Compose works, and how it can help developers to easily setup up local environments to work on database applications. And of course these examples might well be a head start to those that haven&#8217;t used Docker before.</p>



<h2 class="wp-block-heading">That&#8217;s All Folks!</h2>



<p>I hope this brief introduction of how to use Docker and Database servers for and with Xojo has been helpful to some, food for thought to others.</p>



<p><em><em>Jürg Otter is a long term user of Xojo and working for </em><a href="https://www.cmiag.ch/"><em>CM Informatik AG</em></a><em>. Their Application </em><a href="https://cmi-bildung.ch/"><em>CMI LehrerOffice</em></a><em> is a Xojo Design Award Winner 2018. In his leisure time Jürg provides some </em><a href="https://www.jo-tools.ch/xojo/"><em>bits and pieces for Xojo Developers</em></a><em>.</em></em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
