<?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>GIT Archives | DMC, Inc.</title>
	<atom:link href="https://static.dmcinfo.com/blog/tag/git/feed/index.xml" rel="self" type="application/rss+xml" />
	<link></link>
	<description></description>
	<lastBuildDate>Thu, 13 Aug 2026 17:42:16 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.2</generator>

<image>
	<url>https://static.dmcinfo.com/wp-content/uploads/2025/04/site-icon-150x150.png</url>
	<title>GIT Archives | DMC, Inc.</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Triggering Gateway File Syncs for Ignition 8.3 Using Git Hooks</title>
		<link>https://static.dmcinfo.com/blog/44721/ignition-8-3-gateway-file-sync-git/</link>
		
		<dc:creator><![CDATA[Wenjing Deng]]></dc:creator>
		<pubDate>Wed, 03 Jun 2026 23:00:00 +0000</pubDate>
				<category><![CDATA[HMI and SCADA]]></category>
		<category><![CDATA[Ignition]]></category>
		<category><![CDATA[Manufacturing Automation & Intelligence]]></category>
		<category><![CDATA[GIT]]></category>
		<category><![CDATA[SCADA]]></category>
		<category><![CDATA[Workflows]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/?p=44721</guid>

					<description><![CDATA[<p>Ignition 8.3’s file-based project structure makes it much easier to use standard Git workflows with SCADA projects. Project resources can now be version-controlled directly as files, which makes branch switching, pull requests, and external editing more practical. This also enables patterns&#160;such as&#160;using symbolic links to share project resources across environments. One issue that arises with [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/44721/ignition-8-3-gateway-file-sync-git/">Triggering Gateway File Syncs for Ignition 8.3 Using Git Hooks</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Ignition 8.3’s file-based project structure makes it much easier to use standard Git workflows with SCADA projects. Project resources can now be version-controlled directly as files, which makes branch switching, pull requests, and external editing more practical. This also enables patterns&nbsp;such as&nbsp;<a href="https://static.dmcinfo.com/blog/44390/symbolic-links-shared-ignition-project-libraries/">using symbolic links</a> to share project resources across environments.</p>



<p class="wp-block-paragraph" style="padding-bottom:var(--wp--preset--spacing--30)">One issue that arises with this workflow is that Ignition does not always immediately detect file changes made outside the Designer. After checking out a branch, merging changes, or pulling updates, the gateway will still need a manual file scan from the Gateway before the latest project or configuration files are reflected.</p>



<p class="wp-block-paragraph">To make development workflows smoother for our projects at DMC, we created a script that uses the Ignition 8.3 Gateway API endpoints to trigger project and configuration file scans. By integrating this script into Git hooks, file scans can run automatically after branch checkouts or merges, eliminating the need to manually navigate to the Gateway webpage and trigger them each time (fig1 and fig2). Additionally, developers can manually run the script from Bash or the command line to trigger file scans on demand.</p>



<p class="wp-block-paragraph">In this blog, we have provided a sample script and .env file, and we will walk you through the steps to set this up.</p>



<figure class="wp-block-image aligncenter size-full has-custom-border"><img fetchpriority="high" decoding="async" width="1429" height="277" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3.-git-workflow-image-1.png" alt="Screenshot of the &quot;Platform Overview&quot; page in the Ignition Gateway UI." class="has-border-color has-custom-light-blue-border-color wp-image-44728" style="border-width:2px;border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3.-git-workflow-image-1.png 1429w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3.-git-workflow-image-1-300x58.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3.-git-workflow-image-1-1024x198.png 1024w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3.-git-workflow-image-1-768x149.png 768w" sizes="(max-width: 1429px) 100vw, 1429px" /><figcaption class="wp-element-caption"><em>Figure 1: Platform File System Scan Button</em></figcaption></figure>



<figure class="wp-block-image aligncenter size-full has-custom-border"><img decoding="async" width="1427" height="334" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3.-git-workflow-image-2.png" alt="Screenshot of the Projects section of Ignition." class="has-border-color has-custom-light-blue-border-color wp-image-44729" style="border-width:2px;border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3.-git-workflow-image-2.png 1427w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3.-git-workflow-image-2-300x70.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3.-git-workflow-image-2-1024x240.png 1024w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3.-git-workflow-image-2-768x180.png 768w" sizes="(max-width: 1427px) 100vw, 1427px" /><figcaption class="wp-element-caption"><em>Figure 2: Project File System Scan Button</em></figcaption></figure>



<h2 id="h-why-this-step-matters" class="wp-block-heading">Why This Step Matters</h2>



<p class="wp-block-paragraph">There are a variety of reasons why automating this step is both important and beneficial. This automation reduces friction in the development workflow, reducing some of the common sources of errors and delays while improving team-wide consistency and clarity. By using Git hooks to automate file syncing in Ignition, developers avoid repeated trips to the Gateway UI and reduce mental load.</p>



<p class="wp-block-paragraph">By adding automated file scans into the development workflow, teams can make Ignition 8.3 development feel more like regular software development, where Git-based practices like branching, merging, and pull requests fit naturally into SCADA projects.</p>



<h2 id="h-set-up-gateway-api-key" class="wp-block-heading">Set Up Gateway API Key</h2>



<p class="wp-block-paragraph">In order to access the API endpoint, we need to set up an API key in the Gateway. Follow <a href="https://www.docs.inductiveautomation.com/docs/8.3/platform/security/api-keys" target="_blank" rel="noreferrer noopener">Ignition’s manual</a> for this.</p>



<p class="wp-block-paragraph">By default, Ignition requires API keys to be used over secure HTTPS connections. Since this script commonly runs against local development Gateways using standard HTTP, the Require secure connections for API Keys setting must be disabled for the requests to succeed.</p>



<p class="wp-block-paragraph">Make sure to copy the ENTIRE api key. We will add it to the <em><strong>.env</strong></em> file in the next section.</p>



<h2 id="h-repo-and-environment-variables-set-up" class="wp-block-heading">Repo and Environment Variables Set Up</h2>



<p class="wp-block-paragraph">Copy the example script (included in the section below) into your desired location in your repo. For example, in this repo, we have put it at the following location and called it <strong>gateway_scan.py</strong>.</p>



<p class="wp-block-paragraph">Path: <em><strong>ignition-docker-8-3\scripts\gateway_scan</strong></em></p>



<figure class="wp-block-image size-full has-custom-border"><img decoding="async" width="1798" height="453" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3.-git-workflow-image-4.png" alt="Screenshot of a file explorer window displaying a Python source file named gateway_scan.py." class="wp-image-44730" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3.-git-workflow-image-4.png 1798w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3.-git-workflow-image-4-300x76.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3.-git-workflow-image-4-1024x258.png 1024w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3.-git-workflow-image-4-768x193.png 768w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3.-git-workflow-image-4-1536x387.png 1536w" sizes="(max-width: 1798px) 100vw, 1798px" /></figure>



<p class="wp-block-paragraph">Next, add the following variables to your .env file:</p>



<p class="wp-block-paragraph"><strong><em>GATEWAY_IP= (your gateway’s IP address)</em><br><em>HTTP_PORT = (your gateway’s port)</em><br><em>GATEWAY_API_TOKEN=(your generated API token for the gateway)</em></strong></p>



<p class="wp-block-paragraph">The example script looks for these variables; if they are already in your <strong>.env</strong> and are named differently, open the example script and do a find-and-replace to update the variable names.</p>



<h2 id="h-git-hook-set-up" class="wp-block-heading">Git Hook Set Up</h2>



<p class="wp-block-paragraph">Git hooks let us automatically run scripts when certain Git actions occur. For this workflow, we typically set up Git hooks to run the gateway_scan.py script after branch checkouts and merges.</p>



<p class="wp-block-paragraph">This means that when a developer switches branches, pulls changes, or merges updates, the Gateway file scan can run automatically without having to open the Gateway webpage and manually trigger it.</p>



<p class="wp-block-paragraph">To set this up, create two files in the <strong>.git/hooks</strong> folder:</p>



<p class="wp-block-paragraph"><em><strong>.git/hooks/post-checkout<br>.git/hooks/post-merge</strong></em></p>



<p class="wp-block-paragraph">Paste the following code into both files:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>#!/bin/sh

# Move to the root of the git repository
cd "$(git rev-parse --show-toplevel)" || exit 1

# Run file scan script
py scripts/gateway_scan/gateway_scan.py</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">#!/bin/sh</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"># Move to the root of the git repository</span></span>
<span class="line"><span style="color: #D4D4D4">cd </span><span style="color: #CE9178">&quot;$(git rev-parse --show-toplevel)&quot;</span><span style="color: #D4D4D4"> || exit </span><span style="color: #B5CEA8">1</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"># Run file scan script</span></span>
<span class="line"><span style="color: #D4D4D4">py scripts/gateway_scan/gateway_scan.py</span></span></code></pre></div>



<p class="wp-block-paragraph">The post-checkout hook runs after switching branches. The post-merge hook runs after a merge, which also commonly happens when pulling new changes from the remote repository.</p>



<p class="wp-block-paragraph">The first command moves the terminal to the root of the Git repository. This helps ensure the script runs from the correct location, even if Git triggers the hook from within the repository.</p>



<p class="wp-block-paragraph">The second command runs the Gateway scan script:</p>



<p class="wp-block-paragraph"><em><strong>py scripts/gateway_scan/gateway_scan.py</strong></em></p>



<p class="wp-block-paragraph">Developers can also run this command manually from Bash or the command line whenever they want to trigger a Gateway file scan on demand.</p>



<p class="wp-block-paragraph">If the repository contains multiple gateways you want to automatically file scan for, add a separate set of environment variables for each gateway in the <strong>.env file</strong>, create an API key for each gateway, and then update the <strong>gateway_scan.py</strong> script following the comments, so the script knows which gateways to scan.</p>



<h2 id="h-example-script-amp-env-file" class="wp-block-heading">Example Script &amp; env. File</h2>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(3 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>#------------------Gateway Scan Trigger Script Example------------------
"""
Run script to trigger file scans for gateways.
"""
import os
from pathlib import Path
from urllib import error, request

REQUEST_TIMEOUT_SECONDS = 5

print("Syncing Ignition Gateway...", flush=True)


def load_env_file(env_path):
	"""
	Load simple KEY=value pairs from a .env file without third-party dependencies.
	"""
	if not env_path.exists():
		return

	for line in env_path.read_text(encoding="utf-8").splitlines():
		line = line.strip()

		if not line or line.startswith("#") or "=" not in line:
			continue

		key, value = line.split("=", 1)
		os.environ.setdefault(key.strip(), value.strip().strip("\"'"))


# Read in .env file
load_env_file(Path(__file__).resolve().parents&#91;2&#93; / ".env")

# Grab environment variables
# TODO: VERIFY YOUR .ENV FILE HAS THESE VARIABLES SET CORRECTLY
# CREATE ADDITIONAL ENVIRONMENT VARIABLES FOR ADDITIONAL GATEWAYS IF NEEDED
# BY COPYING THE FOLLOWING FORMAT
GATEWAY_PORT = os.getenv("HTTP_PORT_IGN")
GATEWAY_IP = os.getenv("GATEWAY_IP")
GATEWAY_API_TOKEN = os.getenv("GATEWAY_API_TOKEN")

# TODO: ADD ADDITIONAL GATEWAYS HERE IF NEEDED!
gateways = {
	"Ignition": {"ip": GATEWAY_IP, "port": GATEWAY_PORT, "token": GATEWAY_API_TOKEN}
	# Additional gateways can be added here in the same format
}

actions = &#91;"projects", "config"&#93;

def post_scan_request(url, headers):
	"""
	Post a gateway scan request and return the HTTP status code.
	"""
	req = request.Request(url, headers=headers, method="POST")

	with request.urlopen(req, timeout=REQUEST_TIMEOUT_SECONDS) as response:
		return response.status


def trigger_scans():
	"""
	Trigger scans on the specified gateways.
	"""
	for name, config in gateways.items():
		ip = config&#91;"ip"&#93;
		token = config&#91;"token"&#93;
		port = config&#91;"port"&#93;

		if not ip or not token or not port:
			var_name = "ip address" if not ip else "API token" if not token else "Port"
			print(f"Skipping {name}: Missing environment variable '{var_name}'.")
			continue

		headers = {
			"X-Ignition-API-Token": f"{token}"
		}

		for action in actions:
			# Ignition uses the final path segment to choose the scan type
			url = f""

			try:
				status_code = post_scan_request(url, headers)

				if status_code == 200:
					print(f"Success: File Scan {name} {action}")
				else:
					print(f"Failed: File Scan {name} {action} (Status: {status_code})")
			except TimeoutError:
				print(f"Timeout: {name} took too long to respond.")
			except error.HTTPError as err:
				print(f"Failed: File Scan {name} {action} (Status: {err.code})")
			except error.URLError as err:
				print(f"Connection Error: Could not reach {name} at {ip}. ({err.reason})")


if __name__ == "__main__":
	trigger_scans()

# ---------------------.env file example for one gateway--------------------
# HTTP_PORT_IGN= "your gateway's port"
# GATEWAY_IP= "your gateway's IP address"
# GATEWAY_API_TOKEN= "your gateway's API token"</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">#------------------Gateway Scan Trigger Script Example------------------</span></span>
<span class="line"><span style="color: #CE9178">&quot;&quot;&quot;</span></span>
<span class="line"><span style="color: #CE9178">Run script to trigger file scans for gateways.</span></span>
<span class="line"><span style="color: #CE9178">&quot;&quot;&quot;</span></span>
<span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> os</span></span>
<span class="line"><span style="color: #C586C0">from</span><span style="color: #D4D4D4"> pathlib </span><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> Path</span></span>
<span class="line"><span style="color: #C586C0">from</span><span style="color: #D4D4D4"> urllib </span><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> error, request</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">REQUEST_TIMEOUT_SECONDS = </span><span style="color: #B5CEA8">5</span></span>
<span class="line"></span>
<span class="line"><span style="color: #DCDCAA">print</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Syncing Ignition Gateway...&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">flush</span><span style="color: #D4D4D4">=</span><span style="color: #569CD6">True</span><span style="color: #D4D4D4">)</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">load_env_file</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">env_path</span><span style="color: #D4D4D4">):</span></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #CE9178">&quot;&quot;&quot;</span></span>
<span class="line"><span style="color: #CE9178">	Load simple KEY=value pairs from a .env file without third-party dependencies.</span></span>
<span class="line"><span style="color: #CE9178">	&quot;&quot;&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">not</span><span style="color: #D4D4D4"> env_path.exists():</span></span>
<span class="line"><span style="color: #D4D4D4">		</span><span style="color: #C586C0">return</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #C586C0">for</span><span style="color: #D4D4D4"> line </span><span style="color: #C586C0">in</span><span style="color: #D4D4D4"> env_path.read_text(</span><span style="color: #9CDCFE">encoding</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;utf-8&quot;</span><span style="color: #D4D4D4">).splitlines():</span></span>
<span class="line"><span style="color: #D4D4D4">		line = line.strip()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">		</span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">not</span><span style="color: #D4D4D4"> line </span><span style="color: #569CD6">or</span><span style="color: #D4D4D4"> line.startswith(</span><span style="color: #CE9178">&quot;#&quot;</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">or</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;=&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">not</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">in</span><span style="color: #D4D4D4"> line:</span></span>
<span class="line"><span style="color: #D4D4D4">			</span><span style="color: #C586C0">continue</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">		key, value = line.split(</span><span style="color: #CE9178">&quot;=&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">		os.environ.setdefault(key.strip(), value.strip().strip(</span><span style="color: #CE9178">&quot;</span><span style="color: #D7BA7D">\&quot;</span><span style="color: #CE9178">&apos;&quot;</span><span style="color: #D4D4D4">))</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955"># Read in .env file</span></span>
<span class="line"><span style="color: #D4D4D4">load_env_file(Path(</span><span style="color: #9CDCFE">__file__</span><span style="color: #D4D4D4">).resolve().parents&#91;</span><span style="color: #B5CEA8">2</span><span style="color: #D4D4D4">&#93; / </span><span style="color: #CE9178">&quot;.env&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955"># Grab environment variables</span></span>
<span class="line"><span style="color: #6A9955"># </span><span style="color: #569CD6">TODO</span><span style="color: #6A9955">: VERIFY YOUR .ENV FILE HAS THESE VARIABLES SET CORRECTLY</span></span>
<span class="line"><span style="color: #6A9955"># CREATE ADDITIONAL ENVIRONMENT VARIABLES FOR ADDITIONAL GATEWAYS IF NEEDED</span></span>
<span class="line"><span style="color: #6A9955"># BY COPYING THE FOLLOWING FORMAT</span></span>
<span class="line"><span style="color: #D4D4D4">GATEWAY_PORT = os.getenv(</span><span style="color: #CE9178">&quot;HTTP_PORT_IGN&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">GATEWAY_IP = os.getenv(</span><span style="color: #CE9178">&quot;GATEWAY_IP&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">GATEWAY_API_TOKEN = os.getenv(</span><span style="color: #CE9178">&quot;GATEWAY_API_TOKEN&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955"># </span><span style="color: #569CD6">TODO</span><span style="color: #6A9955">: ADD ADDITIONAL GATEWAYS HERE IF NEEDED!</span></span>
<span class="line"><span style="color: #D4D4D4">gateways = {</span></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #CE9178">&quot;Ignition&quot;</span><span style="color: #D4D4D4">: {</span><span style="color: #CE9178">&quot;ip&quot;</span><span style="color: #D4D4D4">: GATEWAY_IP, </span><span style="color: #CE9178">&quot;port&quot;</span><span style="color: #D4D4D4">: GATEWAY_PORT, </span><span style="color: #CE9178">&quot;token&quot;</span><span style="color: #D4D4D4">: GATEWAY_API_TOKEN}</span></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #6A9955"># Additional gateways can be added here in the same format</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">actions = &#91;</span><span style="color: #CE9178">&quot;projects&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&quot;config&quot;</span><span style="color: #D4D4D4">&#93;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">post_scan_request</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">url</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">headers</span><span style="color: #D4D4D4">):</span></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #CE9178">&quot;&quot;&quot;</span></span>
<span class="line"><span style="color: #CE9178">	Post a gateway scan request and return the HTTP status code.</span></span>
<span class="line"><span style="color: #CE9178">	&quot;&quot;&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">	req = request.Request(url, </span><span style="color: #9CDCFE">headers</span><span style="color: #D4D4D4">=headers, </span><span style="color: #9CDCFE">method</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;POST&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #C586C0">with</span><span style="color: #D4D4D4"> request.urlopen(req, </span><span style="color: #9CDCFE">timeout</span><span style="color: #D4D4D4">=REQUEST_TIMEOUT_SECONDS) </span><span style="color: #C586C0">as</span><span style="color: #D4D4D4"> response:</span></span>
<span class="line"><span style="color: #D4D4D4">		</span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> response.status</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">trigger_scans</span><span style="color: #D4D4D4">():</span></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #CE9178">&quot;&quot;&quot;</span></span>
<span class="line"><span style="color: #CE9178">	Trigger scans on the specified gateways.</span></span>
<span class="line"><span style="color: #CE9178">	&quot;&quot;&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #C586C0">for</span><span style="color: #D4D4D4"> name, config </span><span style="color: #C586C0">in</span><span style="color: #D4D4D4"> gateways.items():</span></span>
<span class="line"><span style="color: #D4D4D4">		ip = config&#91;</span><span style="color: #CE9178">&quot;ip&quot;</span><span style="color: #D4D4D4">&#93;</span></span>
<span class="line"><span style="color: #D4D4D4">		token = config&#91;</span><span style="color: #CE9178">&quot;token&quot;</span><span style="color: #D4D4D4">&#93;</span></span>
<span class="line"><span style="color: #D4D4D4">		port = config&#91;</span><span style="color: #CE9178">&quot;port&quot;</span><span style="color: #D4D4D4">&#93;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">		</span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">not</span><span style="color: #D4D4D4"> ip </span><span style="color: #569CD6">or</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">not</span><span style="color: #D4D4D4"> token </span><span style="color: #569CD6">or</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">not</span><span style="color: #D4D4D4"> port:</span></span>
<span class="line"><span style="color: #D4D4D4">			var_name = </span><span style="color: #CE9178">&quot;ip address&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">not</span><span style="color: #D4D4D4"> ip </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;API token&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">not</span><span style="color: #D4D4D4"> token </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;Port&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">			</span><span style="color: #DCDCAA">print</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">f</span><span style="color: #CE9178">&quot;Skipping </span><span style="color: #569CD6">{</span><span style="color: #D4D4D4">name</span><span style="color: #569CD6">}</span><span style="color: #CE9178">: Missing environment variable &apos;</span><span style="color: #569CD6">{</span><span style="color: #D4D4D4">var_name</span><span style="color: #569CD6">}</span><span style="color: #CE9178">&apos;.&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">			</span><span style="color: #C586C0">continue</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">		headers = {</span></span>
<span class="line"><span style="color: #D4D4D4">			</span><span style="color: #CE9178">&quot;X-Ignition-API-Token&quot;</span><span style="color: #D4D4D4">: </span><span style="color: #569CD6">f</span><span style="color: #CE9178">&quot;</span><span style="color: #569CD6">{</span><span style="color: #D4D4D4">token</span><span style="color: #569CD6">}</span><span style="color: #CE9178">&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">		}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">		</span><span style="color: #C586C0">for</span><span style="color: #D4D4D4"> action </span><span style="color: #C586C0">in</span><span style="color: #D4D4D4"> actions:</span></span>
<span class="line"><span style="color: #D4D4D4">			</span><span style="color: #6A9955"># Ignition uses the final path segment to choose the scan type</span></span>
<span class="line"><span style="color: #D4D4D4">			url = </span><span style="color: #569CD6">f</span><span style="color: #CE9178">&quot;http://</span><span style="color: #569CD6">{</span><span style="color: #D4D4D4">ip</span><span style="color: #569CD6">}</span><span style="color: #CE9178">:</span><span style="color: #569CD6">{</span><span style="color: #D4D4D4">port</span><span style="color: #569CD6">}</span><span style="color: #CE9178">/data/api/v1/scan/</span><span style="color: #569CD6">{</span><span style="color: #D4D4D4">action</span><span style="color: #569CD6">}</span><span style="color: #CE9178">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">			</span><span style="color: #C586C0">try</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">				status_code = post_scan_request(url, headers)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">				</span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> status_code == </span><span style="color: #B5CEA8">200</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">					</span><span style="color: #DCDCAA">print</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">f</span><span style="color: #CE9178">&quot;Success: File Scan </span><span style="color: #569CD6">{</span><span style="color: #D4D4D4">name</span><span style="color: #569CD6">}</span><span style="color: #CE9178"> </span><span style="color: #569CD6">{</span><span style="color: #D4D4D4">action</span><span style="color: #569CD6">}</span><span style="color: #CE9178">&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">				</span><span style="color: #C586C0">else</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">					</span><span style="color: #DCDCAA">print</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">f</span><span style="color: #CE9178">&quot;Failed: File Scan </span><span style="color: #569CD6">{</span><span style="color: #D4D4D4">name</span><span style="color: #569CD6">}</span><span style="color: #CE9178"> </span><span style="color: #569CD6">{</span><span style="color: #D4D4D4">action</span><span style="color: #569CD6">}</span><span style="color: #CE9178"> (Status: </span><span style="color: #569CD6">{</span><span style="color: #D4D4D4">status_code</span><span style="color: #569CD6">}</span><span style="color: #CE9178">)&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">			</span><span style="color: #C586C0">except</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TimeoutError</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">				</span><span style="color: #DCDCAA">print</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">f</span><span style="color: #CE9178">&quot;Timeout: </span><span style="color: #569CD6">{</span><span style="color: #D4D4D4">name</span><span style="color: #569CD6">}</span><span style="color: #CE9178"> took too long to respond.&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">			</span><span style="color: #C586C0">except</span><span style="color: #D4D4D4"> error.HTTPError </span><span style="color: #C586C0">as</span><span style="color: #D4D4D4"> err:</span></span>
<span class="line"><span style="color: #D4D4D4">				</span><span style="color: #DCDCAA">print</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">f</span><span style="color: #CE9178">&quot;Failed: File Scan </span><span style="color: #569CD6">{</span><span style="color: #D4D4D4">name</span><span style="color: #569CD6">}</span><span style="color: #CE9178"> </span><span style="color: #569CD6">{</span><span style="color: #D4D4D4">action</span><span style="color: #569CD6">}</span><span style="color: #CE9178"> (Status: </span><span style="color: #569CD6">{</span><span style="color: #D4D4D4">err.code</span><span style="color: #569CD6">}</span><span style="color: #CE9178">)&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">			</span><span style="color: #C586C0">except</span><span style="color: #D4D4D4"> error.URLError </span><span style="color: #C586C0">as</span><span style="color: #D4D4D4"> err:</span></span>
<span class="line"><span style="color: #D4D4D4">				</span><span style="color: #DCDCAA">print</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">f</span><span style="color: #CE9178">&quot;Connection Error: Could not reach </span><span style="color: #569CD6">{</span><span style="color: #D4D4D4">name</span><span style="color: #569CD6">}</span><span style="color: #CE9178"> at </span><span style="color: #569CD6">{</span><span style="color: #D4D4D4">ip</span><span style="color: #569CD6">}</span><span style="color: #CE9178">. (</span><span style="color: #569CD6">{</span><span style="color: #D4D4D4">err.reason</span><span style="color: #569CD6">}</span><span style="color: #CE9178">)&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">__name__</span><span style="color: #D4D4D4"> == </span><span style="color: #CE9178">&quot;__main__&quot;</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">	trigger_scans()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955"># ---------------------.env file example for one gateway--------------------</span></span>
<span class="line"><span style="color: #6A9955"># HTTP_PORT_IGN= &quot;your gateway&apos;s port&quot;</span></span>
<span class="line"><span style="color: #6A9955"># GATEWAY_IP= &quot;your gateway&apos;s IP address&quot;</span></span>
<span class="line"><span style="color: #6A9955"># GATEWAY_API_TOKEN= &quot;your gateway&apos;s API token&quot;</span></span></code></pre></div>



<h2 id="h-conclusion" class="wp-block-heading" style="padding-top:var(--wp--preset--spacing--40)">Conclusion</h2>



<p class="wp-block-paragraph" style="padding-bottom:var(--wp--preset--spacing--40)">Automating file syncs in Ignition 8.3 is a simple configuration change that can deliver meaningful improvements to your development workflow. By reducing manual steps and ensuring the Gateway always reflects the latest project state, teams can work more efficiently and with greater confidence. For engineers using Git to manage their Ignition projects, this approach unlocks the full benefits of modern version control while keeping SCADA development reliable and consistent.</p>



<div class="wp-block-group alignwide has-custom-light-blue-background-color has-background is-layout-flow wp-container-core-group-is-layout-dbd34961 wp-block-group-is-layout-flow" style="border-radius:20px;margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50);padding-top:var(--wp--preset--spacing--50);padding-right:0;padding-bottom:var(--wp--preset--spacing--50);padding-left:0">
<div class="wp-block-columns alignwide are-vertically-aligned-center is-layout-flex wp-container-core-columns-is-layout-43efaee5 wp-block-columns-is-layout-flex" style="padding-right:var(--wp--preset--spacing--60);padding-left:var(--wp--preset--spacing--60)">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:85%">
<h3 class="wp-block-heading has-text-align-left" id="h-have-an-upcoming-project-dmc-can-help-you-take-the-next-step"><strong>Starting an Ignition project? DMC can help you take the next step.</strong></h3>



<p class="has-text-align-left wp-block-paragraph" id="h-need-help-turning-ideas-into-outcomes-automation-project-to-the-next-level-contact-us-today-to-learn-more-about-our-solutions-and-how-we-can-help-you-achieve-your-goals">Find out more about our <a href="https://static.dmcinfo.com/services/manufacturing-automation-and-intelligence/" target="_blank" rel="noreferrer noopener">Manufacturing Automation</a> and <a href="https://static.dmcinfo.com/services/manufacturing-automation-and-intelligence/hmi-and-scada-programming/" target="_blank" rel="noreferrer noopener">SCADA programming</a> solutions and reach out to an expert today.</p>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:15%">
<div class="wp-block-buttons is-horizontal is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-2236275c wp-block-buttons-is-layout-flex">
<div class="wp-block-button is-style-fill"><a class="wp-block-button__link has-base-contrast-color has-text-color has-link-color wp-element-button" href="https://static.dmcinfo.com/contact/">Contact Us</a></div>
</div>
</div>
</div>
</div>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://static.dmcinfo.com/blog/44721/ignition-8-3-gateway-file-sync-git/">Triggering Gateway File Syncs for Ignition 8.3 Using Git Hooks</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Setting Up Symbolic Links for Shared Project Libraries Across Ignition 8.3 Gateways</title>
		<link>https://static.dmcinfo.com/blog/44390/symbolic-links-shared-ignition-project-libraries/</link>
		
		<dc:creator><![CDATA[Wenjing Deng]]></dc:creator>
		<pubDate>Fri, 15 May 2026 11:00:00 +0000</pubDate>
				<category><![CDATA[Ignition]]></category>
		<category><![CDATA[Manufacturing Automation & Intelligence]]></category>
		<category><![CDATA[GIT]]></category>
		<category><![CDATA[ignition]]></category>
		<category><![CDATA[Libraries]]></category>
		<category><![CDATA[Workflows]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/?p=44390</guid>

					<description><![CDATA[<p>Ignition 8.3’s file-based project structure makes it practical to treat gateway resources as standard source-controlled assets, allowing teams to track changes in Git, review diffs, and modify certain files directly outside of the Designer when needed. This level of transparency and control significantly improves the development workflow. In a recent Ignition 8.3 project, we had [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/44390/symbolic-links-shared-ignition-project-libraries/">Setting Up Symbolic Links for Shared Project Libraries Across Ignition 8.3 Gateways</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Ignition 8.3’s file-based project structure makes it practical to treat gateway resources as standard source-controlled assets, allowing teams to track changes in Git, review diffs, and modify certain files directly outside of the Designer when needed. This level of transparency and control significantly improves the development workflow.</p>



<p class="wp-block-paragraph">In a recent Ignition 8.3 project, we had two gateways in a single Git repository that needed to share library elements. While 8.3 enables a more Git-centric workflow, it does not provide a built-in mechanism to keep shared resources synchronized across multiple gateways. Without an automatic way to manage this, shared libraries can quickly drift and become difficult to maintain.</p>



<p class="wp-block-paragraph">We addressed this by creating symbolic links from each gateway’s library folders to a shared “source of truth” directory. A <strong>symbolic link</strong> (symlink) is a special file that points to another file or folder—like a shortcut at the filesystem level. When you access it, the system transparently redirects you to the target location. This approach allows both gateways to reference the same underlying files while still fitting cleanly into a Git-based workflow, making it a simple, low-friction way to keep shared resources consistent.</p>



<p class="wp-block-paragraph">This blog walks through how to set up shared libraries between two Ignition gateways in a single Git repository using symlinks on Windows, helping you avoid duplication and keep shared code synchronized in one place.</p>



<p class="wp-block-paragraph">With this setup, any time a symlinked file is modified in a Designer and saved, it’s immediately overwritten in the shared “source of truth” location. After changes are made and tested in one gateway project’s Designer, the files can be committed and pushed to the repo as usual.</p>



<p class="wp-block-paragraph">We’ll walk through setting up a shared script library folder as an example. You can use the same pattern to share views, styles, and additional files as needed for your project.</p>



<h2 class="wp-block-heading" id="h-what-are-symbolic-links">What are Symbolic Links?</h2>



<p class="wp-block-paragraph">A symbolic link, often referred to as a <strong>symlink</strong>, is a reference point in a filesystem that points one file or folder to another location. Instead of duplicating the files, a symlink acts as a pointer. When an application accesses the symlinked path, the operating system (OS) redirects it to the target destination.</p>



<p class="wp-block-paragraph">In Ignition 8.3, a symlinked folder behaves exactly like a project directory. Associated scripts, views, and resources load as one would expect; however, the actual files live somewhere else in the filesystem.</p>



<h2 class="wp-block-heading" id="h-sample-repository-structure">Sample Repository Structure</h2>



<p class="wp-block-paragraph">Here is a visual representation of what the structure would look like after the symlinks are set up. The file structure for <strong>gateway1</strong> and <strong>gateway2</strong> down to script-python is default to ignition. You will have to create the “shared” folder location and structure manually in your repo, and you can organize it anyway you want.</p>



<p class="wp-block-paragraph" style="padding-bottom:var(--wp--preset--spacing--50)"><strong>repo/<br>└── gateways/<br>&nbsp; &nbsp;├── shared/<br>&nbsp; &nbsp;│ &nbsp; └── script-python/<br>&nbsp; &nbsp;│ &nbsp; &nbsp; &nbsp; └── library/ (real files live here for this example)<br>&nbsp; &nbsp;│<br>&nbsp; &nbsp;├── gateway1/<br>&nbsp; &nbsp;│ &nbsp; └── projects/<br>&nbsp; &nbsp;│ &nbsp; &nbsp; &nbsp; └── project1/<br>&nbsp; &nbsp;│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; └── ignition/<br>&nbsp; &nbsp;│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; └── script-python/<br>&nbsp; &nbsp;│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; └── library/ -&gt; symlink to ../../../../../shared/script-python/library<br>&nbsp; &nbsp;│<br>&nbsp; &nbsp;└── gateway2/<br>&nbsp; &nbsp; &nbsp; &nbsp;└── projects/<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;└── project2/<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;└── ignition/<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;└── script-python/<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;└── library/ -&gt; symlink to ../../../../../shared/script-python/library</strong></p>



<h2 class="wp-block-heading" id="h-setup-steps-for-windows">Setup Steps for Windows</h2>



<p class="wp-block-paragraph">Before creating any symbolic links, complete the following one-time setup steps. <strong>Every developer working with this repository must perform these steps on their local machine</strong>. Skipping them can result in broken links when the repo is cloned or checked out.</p>



<h3 class="wp-block-heading" id="h-prerequisites">Prerequisites</h3>



<p class="wp-block-paragraph"><strong>1. Enable Git symlink support</strong></p>



<p class="wp-block-paragraph">By default, Git on Windows may treat symlinks as plain text files. This setting ensures symlinks are created and checked out correctly.</p>



<p class="wp-block-paragraph">Run the following command in <strong>Git Bash</strong>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>git config --global core.symlinks true</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">git config --global core.symlinks </span><span style="color: #569CD6">true</span></span></code></pre></div>



<figure class="wp-block-image aligncenter size-full has-custom-border"><img decoding="async" width="1082" height="209" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-1.png" alt="Open command window with Git command to enable the use of symbolic links across all repositories on a machine." class="wp-image-44460" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-1.png 1082w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-1-300x58.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-1-1024x198.png 1024w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-1-768x148.png 768w" sizes="(max-width: 1082px) 100vw, 1082px" /></figure>



<p class="wp-block-paragraph"><strong>2. Enable Windows Developer Mode</strong></p>



<p class="wp-block-paragraph">Developer Mode<strong> </strong>allows non-admin users to create symbolic links without permission-related issues.</p>



<p class="wp-block-paragraph"><strong>Settings &gt; Privacy &amp; Security &gt; For Developers &gt; Developer Mode</strong></p>



<p class="wp-block-paragraph">Turn Developer Mode <strong>on</strong>.</p>



<p class="wp-block-paragraph"><strong>3. Restart your computer</strong></p>



<p class="wp-block-paragraph">Restarting your computer ensures Developer Mode is fully applied before creating symlinks.</p>



<figure class="wp-block-image aligncenter size-full is-resized has-custom-border"><img decoding="async" width="624" height="387" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-2.png" alt="Advanced system settings in Windows, specifically highlighting options for developers and terminal management." class="wp-image-44414" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px;aspect-ratio:1.6124476361460203;width:624px;height:auto" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-2.png 624w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-2-300x186.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></figure>



<h2 class="wp-block-heading" id="h-initial-creation-of-symbolic-links">Initial Creation of Symbolic Links</h2>



<p class="wp-block-paragraph">Follow the steps below to set up the initial symlinks for the <code>script-python</code> library folder in our example. The same approach can be used for other shared resources.</p>



<h3 class="wp-block-heading" id="h-gateway-1"><strong>Gateway 1:</strong></h3>



<p class="wp-block-paragraph"><strong>1. Open Command Prompt as an Administrator</strong></p>



<p class="wp-block-paragraph"><strong>2. Navigate to the target directory for your symbolic links</strong></p>



<p class="wp-block-paragraph" style="padding-top:0;padding-bottom:var(--wp--preset--spacing--40)">Move to the directory where the symlink will live in <strong>Gateway 1</strong>: <em><strong>cd gateways\gateway1\projects\project1\ignition\script-python</strong></em></p>



<figure class="wp-block-image aligncenter size-large is-resized has-custom-border" style="margin-top:0;margin-bottom:var(--wp--preset--spacing--60)"><img decoding="async" width="1024" height="129" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-3-1024x129.png" alt="Administrator: Command Prompt window active in a specific directory path." class="wp-image-44417" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px;aspect-ratio:7.9392168322618355;object-fit:cover;width:691px;height:auto" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-3-1024x129.png 1024w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-3-300x38.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-3-768x97.png 768w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-3.png 1444w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph" style="padding-top:0;padding-bottom:var(--wp--preset--spacing--40)"><strong>3. Move any existing library files:</strong></p>



<p class="wp-block-paragraph" style="padding-top:0;padding-bottom:var(--wp--preset--spacing--40)">If you already have files in the library folder and/or the library folder exists, move the entire library folder to the shared directory<span style="box-sizing: border-box; margin: 0px; padding: 0px;">&nbsp;at&nbsp;<em><strong>gateways/shared/script-python</strong></em></span>. If not, skip this step.</p>



<p class="wp-block-paragraph" style="padding-top:0;padding-bottom:var(--wp--preset--spacing--40)">If you leave the library folder here: <em><strong>\gateways\gateway1\projects\project1\ignition\script-python</strong>. </em>The following error will occur because Windows cannot create a link where a directory already exists:</p>



<figure class="wp-block-image aligncenter size-full has-custom-border" style="margin-top:0;margin-bottom:var(--wp--preset--spacing--60)"><img decoding="async" width="2306" height="161" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-4.png" alt="The error message &quot;Cannot create a file when that file already exists&quot; indicates that a symbolic link or file named library already exists in the destination directory." class="wp-image-44419" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px;aspect-ratio:14.424507658643327" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-4.png 2306w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-4-300x21.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-4-1024x71.png 1024w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-4-768x54.png 768w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-4-1536x107.png 1536w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-4-2048x143.png 2048w" sizes="(max-width: 2306px) 100vw, 2306px" /></figure>



<p class="wp-block-paragraph" style="padding-top:0;padding-bottom:var(--wp--preset--spacing--40)"><strong>4. Create the symbolic link</strong></p>



<p class="wp-block-paragraph" style="padding-top:0;padding-bottom:0">Run the command below to create the symlink linked to the actual files in the shared location:</p>



<p class="wp-block-paragraph"><em><strong>mklink /D library &#8230;&#8230;&#8230;.\shared\script-python\library</strong></em></p>



<figure class="wp-block-image aligncenter size-full has-custom-border" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:var(--wp--preset--spacing--60)"><img decoding="async" width="2325" height="293" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-5.png" alt="GIT command used to successfully create a symbolic link to a folder." class="wp-image-44421" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-5.png 2325w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-5-300x38.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-5-1024x129.png 1024w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-5-768x97.png 768w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-5-1536x194.png 1536w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-5-2048x258.png 2048w" sizes="(max-width: 2325px) 100vw, 2325px" /></figure>



<p class="wp-block-paragraph"><strong>5. Verify the symlink was created successfully</strong></p>



<p class="wp-block-paragraph">Verify the link has been created by running the command <em>dir</em> at the symlink location in the command prompt. If the symlink was created correctly, then you should see it in your output.</p>



<figure class="wp-block-image aligncenter size-full has-custom-border" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:var(--wp--preset--spacing--60)"><img decoding="async" width="1950" height="499" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-6-1-e1778267616906.png" alt="A [dir] command output shows a directory listing in a Windows Command Prompt, highlighting a symbolic link." class="wp-image-44422" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-6-1-e1778267616906.png 1950w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-6-1-e1778267616906-300x77.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-6-1-e1778267616906-1024x262.png 1024w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-6-1-e1778267616906-768x197.png 768w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-6-1-e1778267616906-1536x393.png 1536w" sizes="(max-width: 1950px) 100vw, 1950px" /></figure>



<p class="wp-block-paragraph" style="padding-bottom:var(--wp--preset--spacing--40)">In addition, you can visually verify that the folder was created correctly in File Explorer by looking for the arrow icon in the bottom left. When you double-click into this symlinked folder, it will take you to the location it is linked to.</p>



<figure class="wp-block-image aligncenter size-full has-custom-border" style="margin-top:var(--wp--preset--spacing--30);margin-bottom:var(--wp--preset--spacing--60)"><img decoding="async" width="650" height="271" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/Picture12.png" alt="Windows 11 File Explorer window for a directory named script-python." class="wp-image-44431" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/Picture12.png 650w, https://static.dmcinfo.com/wp-content/uploads/2026/05/Picture12-300x125.png 300w" sizes="(max-width: 650px) 100vw, 650px" /></figure>



<h3 class="wp-block-heading" id="h-gateway-2">Gateway 2</h3>



<p class="wp-block-paragraph">Repeat the same steps for <strong>Gateway 2</strong>, adjusting the path as needed:</p>



<p class="wp-block-paragraph"><em><strong>cd gateways\gateway2\projects\project2\ignition\script-python</strong></em></p>



<p class="wp-block-paragraph">Create the symlink pointing to the same library location.</p>



<p class="wp-block-paragraph">Once both gateways are linked, the shared library folder is ready for use. Any changes made through one gateway’s <strong>Ignition Designer</strong> will be written directly to the shared directory and can be committed to Git as usual.</p>



<h2 class="wp-block-heading" id="h-ignition-designer-gateway-refresh-and-test">Ignition Designer/Gateway Refresh and Test</h2>



<p class="wp-block-paragraph">After creating the symbolic links, the final step is to confirm that Ignition detects and uses the shared files properly. Because Ignition doesn’t automatically reload filesystem changes, users must manually initiate file and project scans to update each gateway.</p>



<p class="wp-block-paragraph"><strong>1. Close all open Ignition Designer sessions</strong></p>



<p class="wp-block-paragraph">Closing active sessions of Designer resolves any potential validation interference caused by cached projects.</p>



<p class="wp-block-paragraph"><strong>2. Trigger file and project scans on both Gateways</strong></p>



<p class="wp-block-paragraph">On each gateway, run the following:</p>



<ul class="wp-block-list">
<li>Platform → Overview →<strong> Scan File System</strong></li>



<li>Platform → System → Projects → <strong>Scan Projects</strong></li>
</ul>



<p class="wp-block-paragraph"><strong>3. Open Ignition Designer for Gateway 1</strong></p>



<ul class="wp-block-list">
<li>Create a new script or modify an existing script in the share library.</li>



<li>Save the changes.</li>
</ul>



<p class="wp-block-paragraph"><strong>4. Refresh Gateway 2</strong></p>



<ul class="wp-block-list">
<li style="padding-bottom:var(--wp--preset--spacing--40)">From the <strong>Gateway 2</strong> web UI, trigger both the file system scan and project scan again.</li>
</ul>



<p class="wp-block-paragraph"><strong>5. Open Ignition Developer for Gateway 2</strong></p>



<ul class="wp-block-list">
<li style="padding-bottom:var(--wp--preset--spacing--40)">Open Ignition Developer for the second Gateway and verify that the adjustments are present.</li>
</ul>



<figure class="wp-block-image aligncenter size-full is-resized has-custom-border"><img decoding="async" width="1124" height="218" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-8.png" alt="Platform File Scan button in Ignition 8.3" class="has-border-color has-custom-light-gray-neutral-2-border-color wp-image-44424" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px;width:1200px;height:auto" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-8.png 1124w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-8-300x58.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-8-1024x199.png 1024w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-8-768x149.png 768w" sizes="(max-width: 1124px) 100vw, 1124px" /><figcaption class="wp-element-caption">Platform File Scan Button</figcaption></figure>



<figure class="wp-block-image aligncenter size-full is-resized has-custom-border"><img decoding="async" width="1128" height="265" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-9.png" alt="Project File Scan button in Ignition 8.3" class="has-border-color has-custom-light-gray-neutral-2-border-color wp-image-44426" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px;width:1200px;height:auto" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-9.png 1128w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-9-300x70.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-9-1024x241.png 1024w, https://static.dmcinfo.com/wp-content/uploads/2026/05/ignition-8.3-symlinks-image-9-768x180.png 768w" sizes="(max-width: 1128px) 100vw, 1128px" /><figcaption class="wp-element-caption">Project File Scan Button</figcaption></figure>



<p class="wp-block-paragraph">Now the library symlinks are set up and ready for use. If the changes appear as expected, the symbolic link configuration is working correctly, and both gateways are pulling from the same shared source of truth. Once validated, follow your project&#8217;s specific git workflow to push these changes to your repository.</p>



<h2 class="wp-block-heading" id="h-final-thoughts-and-best-practices">Final Thoughts and Best Practices</h2>



<p class="wp-block-paragraph">It&#8217;s important to keep in mind that whenever a file in the gateway filesystem is changed, the gateway doesn’t automatically update to reflect those changes. You will need to perform two file scans for the latest changes to be reflected. The screenshots below show where to trigger these scans.</p>



<p class="wp-block-paragraph">At some point during testing, you may need to view/test a library file in both gateways. In this scenario, it is important to regularly perform the above file and project scans, as changes are being made and saved. Below is an example of a possible order of operations:</p>



<ol class="wp-block-list">
<li>Make changes to library file in <strong>Gateway 1</strong> project designer.</li>



<li>Save changes to library file in <strong>Gateway 1</strong> project designer.</li>



<li>Open <strong>Gateway 2</strong> webpage UI.</li>



<li>Press <strong>Scan File System</strong> on both <strong>Platform</strong> <strong>&gt; Overview</strong> page and <strong>Platform &gt; System &gt; Projects</strong> page for <strong>Gateway 2</strong>.</li>



<li>Open a fresh designer session of <strong>Gateway 2’s</strong> project to view latest changes.</li>
</ol>



<p class="wp-block-paragraph">Keep in mind that if changes are made in both gateways to the same library object, the last place it was saved will be the effective, latest state of the file. In the above example, if a change is made in <strong>Gateway 2’s</strong> project designer session and saved there, <strong>Gateway 1</strong> would have stale library files internally, requiring file scans to refresh them.</p>



<p class="wp-block-paragraph">To avoid accidentally overwriting changes, the best practice is to separate work into separate commits and modify a given shared file from only one gateway at a time.</p>



<p class="wp-block-paragraph" style="padding-bottom:var(--wp--preset--spacing--40)">Finally, make sure the other developers run through the setup steps on their machines to avoid accidentally breaking any symbolic links during initial setup.</p>



<div class="wp-block-group alignwide has-custom-light-blue-background-color has-background is-layout-flow wp-container-core-group-is-layout-dbd34961 wp-block-group-is-layout-flow" style="border-radius:20px;margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50);padding-top:var(--wp--preset--spacing--50);padding-right:0;padding-bottom:var(--wp--preset--spacing--50);padding-left:0">
<div class="wp-block-columns alignwide are-vertically-aligned-center is-layout-flex wp-container-core-columns-is-layout-43efaee5 wp-block-columns-is-layout-flex" style="padding-right:var(--wp--preset--spacing--60);padding-left:var(--wp--preset--spacing--60)">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:85%">
<h3 class="wp-block-heading has-text-align-left" id="h-have-an-upcoming-project-dmc-can-help-you-take-the-next-step">Planning your next automation project? DMC can help you take the next step.</h3>



<p class="has-text-align-left wp-block-paragraph" id="h-need-help-turning-ideas-into-outcomes-automation-project-to-the-next-level-contact-us-today-to-learn-more-about-our-solutions-and-how-we-can-help-you-achieve-your-goals">Get expert <a href="https://static.dmcinfo.com/services/manufacturing-automation-and-intelligence/">Automation</a> engineering and <a href="https://static.dmcinfo.com/services/manufacturing-automation-and-intelligence/hmi-and-scada-programming/ignition-programming/">Ignition Programming</a> solutions from DMC. Talk with an Ignition expert today.</p>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:15%">
<div class="wp-block-buttons is-horizontal is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-2236275c wp-block-buttons-is-layout-flex">
<div class="wp-block-button is-style-fill"><a class="wp-block-button__link has-base-contrast-color has-text-color has-link-color wp-element-button" href="https://static.dmcinfo.com/contact/">Contact Us</a></div>
</div>
</div>
</div>
</div>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://static.dmcinfo.com/blog/44390/symbolic-links-shared-ignition-project-libraries/">Setting Up Symbolic Links for Shared Project Libraries Across Ignition 8.3 Gateways</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
