<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: project-laguardia</title>
    <description>The latest articles on DEV Community by project-laguardia (@projectlaguardia).</description>
    <link>https://dev.to/projectlaguardia</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3344089%2F69b68ab2-7828-410a-a10d-531f5d964448.png</url>
      <title>DEV Community: project-laguardia</title>
      <link>https://dev.to/projectlaguardia</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/projectlaguardia"/>
    <language>en</language>
    <item>
      <title>LuCI on MGMT - Wk 2 - Day 08</title>
      <dc:creator>project-laguardia</dc:creator>
      <pubDate>Thu, 17 Jul 2025 05:22:16 +0000</pubDate>
      <link>https://dev.to/projectlaguardia/luci-on-mgmt-wk-2-day-08-1fbe</link>
      <guid>https://dev.to/projectlaguardia/luci-on-mgmt-wk-2-day-08-1fbe</guid>
      <description>&lt;h1&gt;
  
  
  Hurdle 11: Prioritizing Ports and Setting Up VCS
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Previously:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/project-laguardia/lumi.working/blob/9b6835f4b7f116db9fdc83a63b6ee7a868e53b3a/porting/week/0/DAY%207.md" rel="noopener noreferrer"&gt;https://github.com/project-laguardia/lumi.working/blob/9b6835f4b7f116db9fdc83a63b6ee7a868e53b3a/porting/week/0/DAY%207.md&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Task 1: Addressing Tracking Challenges:
&lt;/h2&gt;

&lt;p&gt;We can identify what files were modified by the LuMI project what was modified upstream with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git diff &lt;span class="nt"&gt;--name-only&lt;/span&gt; upstream/main..HEAD &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; upstream-changes.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also use &lt;code&gt;cherry&lt;/code&gt;, &lt;code&gt;cherry-pick -n&lt;/code&gt;, and tagging to help automate staging upstream changes.&lt;/p&gt;

&lt;p&gt;However, in order for that to work, the root of our repo must also be the same root as LuCI. So we will be renaming this repo to &lt;code&gt;lumi.working&lt;/code&gt; and then creating a new repo called &lt;code&gt;lumi&lt;/code&gt; containing our ported files.&lt;/p&gt;

&lt;p&gt;We may need to develop our own solution to ensure commits are properly staged. In order to better enforce this, we must also protect the main branch of the &lt;code&gt;lumi&lt;/code&gt; repo.&lt;/p&gt;

&lt;p&gt;For now, the tag &lt;code&gt;fork&lt;/code&gt; has been created on &lt;code&gt;lumi&lt;/code&gt; to mark the point where we forked from LuCI.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git cherry&lt;/code&gt; looks like it is going to be the most useful tool for tracking changes. However, it is still limited. It tells us all of the commits that haven't been applied to our branch. It doesn't provide us with a way to mark which commits have already been reviewed.&lt;/p&gt;

&lt;p&gt;There are 2 ways we can handle this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We can use &lt;code&gt;git notes&lt;/code&gt; to mark commits that have been reviewed.&lt;/li&gt;
&lt;li&gt;We can use tags to mark the last reviewed commit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notes seem like a decent option, but they are not as visible as tags. I think I'm going to use tags. One problem will be that these get mixed in with release tags, so we will need to use a naming convention that makes it clear these are not release tags.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To solve this, we can use a prefix like &lt;code&gt;reviewed/&lt;/code&gt; for these tags.&lt;/li&gt;
&lt;li&gt;We can also make sure they are lightweight tags (they don't need metadata).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Though, after thinking about it and deciding on review tags, I think the simplest solution is the best one. Instead of using &lt;code&gt;git cherry&lt;/code&gt; and dealing with its limitations, we can just track the changes between LuCI's head and the last reviewed commit. We will also need an in-progress tag though if we do this, to denote the last commit reviewed, but not yet merged/implemented.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task 2: Setting Up VCS
&lt;/h2&gt;

&lt;p&gt;For convenience, it may be best to dump the list of commits between reviewed, in-progress, and upstream head into a file. So we don't have to run the command every time we want to see what has changed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function Get-UnmergedCommits {
    param (
        [string] $Upstream = "https://github.com/openwrt/luci",
        [string] $Repo = "./lumi",
        [switch] $InProgress, # reviewed .. in-progress
        [switch] $Pending, # in-progress .. upstream head
        [switch] $Verbose
    )

    pushd $Repo
    # ensure upstream is set

    # Hack to reset $LASTEXITCODE to 0
    &amp;amp; (Resolve-Path "$PSHome\p*w*sh*" | Where-Object {
        $Path = "$_"

        if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
            return $false
        }

        if ($IsWindows) {
            $ext = [IO.Path]::GetExtension($Path)
            $pathext = ($env:PATHEXT -split ';') -replace '^\.', ''
            return $pathext -contains $ext.TrimStart('.').ToUpperInvariant()
        } else {
            $escapedPath = $Path.Replace("'", "''")

            # Prefer bash, fallback to sh
            $shell = if (Get-Command bash -ErrorAction SilentlyContinue) { "bash" } else { "sh" }

            &amp;amp; $shell -c "test -x '$escapedPath'"
            return ($LASTEXITCODE -eq 0)
        }
    } | Select-Object -First 1) -v | Out-Null

    $remotes = @{}
    git remote -v 2&amp;gt;$null | ForEach-Object {
        if ($_ -match '^(\S+)\s+(\S+)') {
            $remotes[$matches[1]] = $matches[2]
        }
    }
    $remote = $remotes.Keys | ForEach-Object { $_ } | Where-Object {
        $remotes."$_" -eq $Upstream
    }
    If( "$remote".Trim() -eq "" ){
        $remote = "upstream"
        git remote set-url $remote $Upstream 2&amp;gt;&amp;amp;1 | ForEach-Object {
            If( $LASTEXITCODE -ne 0 ){
                throw "$_"
            }
            If( $_ -is [System.Management.Automation.ErrorRecord] ) {
                throw $_.Exception
            }
            If( $Verbose ) {
                If ( $_ -is [System.Management.Automation.WarningRecord] ) {
                    Write-Host $_.Message -ForegroundColor Yellow
                } else {
                    Write-Host "$Repository`: $_" -ForegroundColor DarkGray
                }
            }
        }
    }

    git remote set-url upstream $Upstream

    git fetch --tags

    $range = if (([bool]$InProgress) -eq ([bool]$Pending)) {
        "full"
    } elseif ($InProgress) {
        "in-progress"
    } else {
        "pending"
    }

    $reviewed = git tag --list "reviewed/*" | Select-Object -Last 1
    $in_progress = git tag --list "in-progress/*" | Select-Object -Last 1

    $start = if( ($range -eq "pending") -and ("$in_progress".Trim() -ne "") ) {
        $in_progress
    } else {
        if ("$reviewed".Trim() -ne "") {
            $reviewed
        } else {
            "fork"
        }
    }
    $end = if( ($range -eq "in-progress") -and ("$in_progress".Trim() -ne "")) {
        $in_progress
    } else {
        "upstream/main"
    }

    $commits = git rev-list --reverse "$start..$end"

    $diffs = [ordered]@{}
    # get the diffs
    foreach ($commit in $commits) {
        $diff = git show --format= --no-prefix $sha
        if ("$diff".Trim() -ne "") {
            $diffs[$commit] = $diff
        }
    }
    popd
    return @{
        Start = $start
        End = $end
        Range = $range
        Commits = $commits
        Diffs = $diffs
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script should give me a serializable hashtable that I can write to file for roadmapping upstream changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task 2: Cleaning Up My Devtools
&lt;/h2&gt;

&lt;p&gt;Since my repository now has a slew of new tools, I feel a need to clean them up and organize them into their own directory. I will create a &lt;code&gt;sdk&lt;/code&gt; directory here and move the &lt;code&gt;~search.ps1&lt;/code&gt; script into it. This will help keep the root directory clean and make it easier to develop more tools in the future.&lt;/p&gt;

&lt;p&gt;I also need to update my scripts to accept a repo and upstream as parameters at the file level. This should be an easy fix... and done.&lt;/p&gt;

&lt;p&gt;My 2 tools are now in the &lt;code&gt;sdk&lt;/code&gt; directory:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;unmerged.ps1&lt;/code&gt;: This script will help me track unmerged commits between my fork and the upstream LuCI repository.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;search.ps1&lt;/code&gt;: This script will help me search through the LuCI codebase for specific patterns or files. Useful for building out the initial roadmap&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Last remark for Hurdle 11&lt;/strong&gt;: &lt;code&gt;unmerged.ps1&lt;/code&gt; will likely not be needed for some time.&lt;/p&gt;

&lt;p&gt;I will continue tomorrow with Task 3: Prioritizing Ports.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>LuCI on MGMT - Day 7</title>
      <dc:creator>project-laguardia</dc:creator>
      <pubDate>Wed, 16 Jul 2025 03:58:01 +0000</pubDate>
      <link>https://dev.to/projectlaguardia/luci-on-mgmt-day-7-1c2h</link>
      <guid>https://dev.to/projectlaguardia/luci-on-mgmt-day-7-1c2h</guid>
      <description>&lt;h1&gt;
  
  
  Hurdle 7: Backtracking on &lt;code&gt;ucode&lt;/code&gt; Files
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Previously:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/project-laguardia/lumi/blob/main/porting/DAY%206.md" rel="noopener noreferrer"&gt;https://github.com/project-laguardia/lumi/blob/main/porting/DAY%206.md&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After some realization, it was discovered that &lt;code&gt;ucode&lt;/code&gt; files were being omitted from the search results.&lt;/p&gt;

&lt;p&gt;Luckily, LuCI on &lt;code&gt;ucode&lt;/code&gt; is still in its early stages, so only a minimal amount of files were missed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;uci&lt;/code&gt; results:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-base/ucode/zoneinfo.uc" rel="noopener noreferrer"&gt;modules/luci-base/ucode/zoneinfo.uc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Result by mistake.&lt;/em&gt; (does not need to be ported)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-base/ucode/dispatcher.uc" rel="noopener noreferrer"&gt;modules/luci-base/ucode/dispatcher.uc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;This is the current HTTP Router for LuCI&lt;/li&gt;
&lt;li&gt;Will definitely have to be ported to LuMI&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-base/ucode/runtime.uc" rel="noopener noreferrer"&gt;modules/luci-base/ucode/runtime.uc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Handles .ut template file generation&lt;/li&gt;
&lt;li&gt;Adds a Lua bridge to the ucode runtime&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-base/ucode/controller/admin/uci.uc" rel="noopener noreferrer"&gt;modules/luci-base/ucode/controller/admin/uci.uc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Adds low-level &lt;code&gt;uci&lt;/code&gt; and &lt;code&gt;ubus&lt;/code&gt; interop to LuCI's ucode runtime&lt;/li&gt;
&lt;li&gt;Will definitely have to be ported to LuMI&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;/etc/config&lt;/code&gt; results: none noteworthy&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;luci.config&lt;/code&gt; results: none noteworthy&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  Hurdle 8: Identifying Any &lt;code&gt;rpc&lt;/code&gt; Interop
&lt;/h1&gt;

&lt;p&gt;Here are the results of the &lt;code&gt;rpc&lt;/code&gt; code search:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/libs/luci-lib-nixio/axTLS/www/lua/test_lib.lua" rel="noopener noreferrer"&gt;libs/luci-lib-nixio/axTLS/www/lua/test_lib.lua&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;This is a legacy test file that can probably be dropped&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/libs/luci-lib-px5g/src/library/timing.c" rel="noopener noreferrer"&gt;libs/luci-lib-px5g/src/library/timing.c&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This was returned by mistake. It references assembly instructions for the &lt;code&gt;px5g&lt;/code&gt; library, which has &lt;code&gt;rpcc&lt;/code&gt; as part of the instruction set.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/libs/rpcd-mod-luci/src/luci.c" rel="noopener noreferrer"&gt;libs/rpcd-mod-luci/src/luci.c&lt;/a&gt; and &lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/libs/rpcd-mod-rrdns/src/rrdns.c" rel="noopener noreferrer"&gt;libs/rpcd-mod-rrdns/src/rrdns.c&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;rpc-mod-luci&lt;/code&gt; is a &lt;code&gt;rpcd&lt;/code&gt; plugin written by LuCI developers that exposes some functions that LuCI was worth sharing with &lt;code&gt;rpcd&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;This is already planned for porting.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-base/ucode/dispatcher.uc" rel="noopener noreferrer"&gt;modules/luci-base/ucode/dispatcher.uc&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is the current HTTP Router for LuCI&lt;/li&gt;
&lt;li&gt;This is already planned for porting.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-base/ucode/controller/admin/index.uc" rel="noopener noreferrer"&gt;modules/luci-base/ucode/controller/admin/index.uc&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This serves as the LuCI API gateway.&lt;/li&gt;
&lt;li&gt;This is a high-value target for porting.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-base/ucode/controller/admin/uci.uc" rel="noopener noreferrer"&gt;modules/luci-base/ucode/controller/admin/uci.uc&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adds low-level &lt;code&gt;uci&lt;/code&gt; and &lt;code&gt;ubus&lt;/code&gt; interop to LuCI's ucode runtime&lt;/li&gt;
&lt;li&gt;Already planned for porting.
&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/luasrc/dispatcher.lua" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/luasrc/dispatcher.lua&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;This is the older HTTP Router for LuCI&lt;/li&gt;
&lt;li&gt;This is already planned for porting.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/luasrc/model/uci.lua" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/luasrc/model/uci.lua&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is a wrapper around the &lt;code&gt;uci&lt;/code&gt; module that provides a more Lua-like interface.&lt;/li&gt;
&lt;li&gt;Already planned for porting.
&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-mod-rpc/luasrc/jsonrpc.lua" rel="noopener noreferrer"&gt;modules/luci-mod-rpc/luasrc/jsonrpc.lua&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;This is the JSON-RPC implementation for LuCI.&lt;/li&gt;
&lt;li&gt;High-value target for porting.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-mod-rpc/luasrc/controller/rpc.lua" rel="noopener noreferrer"&gt;modules/luci-mod-rpc/luasrc/controller/rpc.lua&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is a utility for doing RPC calls in LuCI.&lt;/li&gt;
&lt;li&gt;Already planned for porting.
&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-mod-rpc/luasrc/jsonrpcbind/uci.lua" rel="noopener noreferrer"&gt;modules/luci-mod-rpc/luasrc/jsonrpcbind/uci.lua&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A convenience wrapper around &lt;code&gt;uci&lt;/code&gt;. Honestly seems kind of redundant with &lt;code&gt;luci.model.uci&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Already planned for porting.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  Hurdle 9: Identifying Any &lt;code&gt;ubus&lt;/code&gt; Interop
&lt;/h1&gt;

&lt;p&gt;Here are the results of the &lt;code&gt;ubus&lt;/code&gt; code search:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/libs/luci-lib-nixio/axTLS/config/scripts/config/mconf.c" rel="noopener noreferrer"&gt;libs/luci-lib-nixio/axTLS/config/scripts/config/mconf.c&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Resulted by mistake. For some reason axTLS includes menuconfig.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/libs/luci-lib-nixio/axTLS/crypto/bigint.c" rel="noopener noreferrer"&gt;libs/luci-lib-nixio/axTLS/crypto/bigint.c&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Also resulted by mistake.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/libs/luci-lib-nixio/src/poll.c" rel="noopener noreferrer"&gt;libs/luci-lib-nixio/src/poll.c&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Also resulted by mistake.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-compat/luasrc/model/network/proto_modemmanager.lua" rel="noopener noreferrer"&gt;modules/luci-compat/luasrc/model/network/proto_modemmanager.lua&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Also resulted by mistake.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-compat/luasrc/model/network/proto_qmi.lua" rel="noopener noreferrer"&gt;modules/luci-compat/luasrc/model/network/proto_qmi.lua&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Also resulted by mistake.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/libs/luci-lib-base/luasrc/util.lua" rel="noopener noreferrer"&gt;libs/luci-lib-base/luasrc/util.lua&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;provides string manipulation and serialization functions as well as methods to interact with the &lt;code&gt;ubus&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;High-value target for porting.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/libs/rpcd-mod-luci/src/luci.c" rel="noopener noreferrer"&gt;libs/rpcd-mod-luci/src/luci.c&lt;/a&gt; and &lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/libs/rpcd-mod-rrdns/src/rrdns.c" rel="noopener noreferrer"&gt;libs/rpcd-mod-rrdns/src/rrdns.c&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;rpc-mod-luci&lt;/code&gt; is a &lt;code&gt;rpcd&lt;/code&gt; plugin written by LuCI developers that exposes some functions that LuCI was worth sharing with &lt;code&gt;rpcd&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;This is already planned for porting.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-base/ucode/dispatcher.uc" rel="noopener noreferrer"&gt;modules/luci-base/ucode/dispatcher.uc&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;This is the current HTTP Router for LuCI&lt;/li&gt;
&lt;li&gt;This is already planned for porting.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-base/ucode/controller/admin/index.uc" rel="noopener noreferrer"&gt;modules/luci-base/ucode/controller/admin/index.uc&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;This serves as the LuCI API gateway.&lt;/li&gt;
&lt;li&gt;This is already planned for porting.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-base/ucode/controller/admin/uci.uc" rel="noopener noreferrer"&gt;modules/luci-base/ucode/controller/admin/uci.uc&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Adds low-level &lt;code&gt;uci&lt;/code&gt; and &lt;code&gt;ubus&lt;/code&gt; interop to LuCI's ucode runtime&lt;/li&gt;
&lt;li&gt;Already planned for porting.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-compat/luasrc/model/network.lua" rel="noopener noreferrer"&gt;modules/luci-compat/luasrc/model/network.lua&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Provides a model for configuring network interfaces, devices, and L2 (sometimes L3) protocols.&lt;/li&gt;
&lt;li&gt;Already planned for porting.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-compat/luasrc/tools/webadmin.lua" rel="noopener noreferrer"&gt;modules/luci-compat/luasrc/tools/webadmin.lua&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Minimally interacts with &lt;code&gt;ubus&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Provides a small set of utility tools for LuCI.&lt;/li&gt;
&lt;li&gt;Already planned for porting.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/luasrc/dispatcher.lua" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/luasrc/dispatcher.lua&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;This is the older HTTP Router for LuCI&lt;/li&gt;
&lt;li&gt;Already planned for porting.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/luasrc/sys.lua" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/luasrc/sys.lua&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;This module provides a set of system utilities for LuCI.&lt;/li&gt;
&lt;li&gt;It minimally interacts with &lt;code&gt;uci&lt;/code&gt;. In fact its only interaction appears to be pulling IPs from it.&lt;/li&gt;
&lt;li&gt;Already planned for porting.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/luasrc/model/uci.lua" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/luasrc/model/uci.lua&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;This is a wrapper around the &lt;code&gt;uci&lt;/code&gt; module that provides a more Lua-like interface.&lt;/li&gt;
&lt;li&gt;Already planned for porting.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-mod-rpc/luasrc/controller/rpc.lua" rel="noopener noreferrer"&gt;modules/luci-mod-rpc/luasrc/controller/rpc.lua&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;This is a utility for doing RPC calls in LuCI.&lt;/li&gt;
&lt;li&gt;I believe that &lt;code&gt;mgmt&lt;/code&gt; uses a different RPC mechanism, so any ubus and RPC functionality may also have to be ported.&lt;/li&gt;
&lt;li&gt;Already planned for porting.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  Hurdle 10: Identifying Any Other Languages Used
&lt;/h1&gt;

&lt;p&gt;At this point, we need to ensure that we have identified all languages used in LuCI.&lt;/p&gt;

&lt;p&gt;For this, we will be adding &lt;code&gt;Enry&lt;/code&gt; to our dev chain. We will be using temporary installs to avoid polluting the system with Go dependencies.&lt;/p&gt;

&lt;h1&gt;
  
  
  Task 1: Setup Enry
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$languages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$tmp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;New-TemporaryFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ni&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ItemType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Directory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;pushd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$tmp&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tempmod&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;github.com/go-enry/enry&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;/enry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;github.com/go-enry/enry&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;popd&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;pushd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;luci&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$tmp&lt;/span&gt;&lt;span class="s2"&gt;/enry"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-all&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-json&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;popd&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$tmp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Out-Null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConvertFrom-Json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When enry is used like this, it is only good for summarizing the languages within cwd. I will have to iterate over each file in order to create the language mapping that I want.&lt;/p&gt;

&lt;p&gt;... (6 hours later) ...&lt;/p&gt;

&lt;p&gt;Took a lot to get language detection working, but &lt;code&gt;~search.ps1&lt;/code&gt; has been updated to use &lt;code&gt;Enry&lt;/code&gt; to detect languages.&lt;/p&gt;

&lt;p&gt;This should be the final hurdle in the research phase of porting LuCI to LuMI. A quick review of the updated results will be needed to make that determination.&lt;/p&gt;

&lt;h1&gt;
  
  
  Task 2: Reviewing the new results
&lt;/h1&gt;

&lt;p&gt;A few new results were found:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/tree/a3cae27ab4f51c374f5cf9ab84f181ff91854d0b/modules/luci-base/root/usr/share/rpcd/ucode/luci" rel="noopener noreferrer"&gt;modules/luci-base/root/usr/share/rpcd/ucode/luci&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/tree/a3cae27ab4f51c374f5cf9ab84f181ff91854d0b/modules/luci-base/ucode/template/sysauth.ut" rel="noopener noreferrer"&gt;modules/luci-base/ucode/template/sysauth.ut&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/tree/a3cae27ab4f51c374f5cf9ab84f181ff91854d0b/modules/luci-lua-runtime/luasrc/model/uci.luadoc" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/luasrc/model/uci.luadoc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/tree/a3cae27ab4f51c374f5cf9ab84f181ff91854d0b/protocols/luci-proto-wireguard/root/usr/share/rpcd/ucode/luci.wireguard" rel="noopener noreferrer"&gt;protocols/luci-proto-wireguard/root/usr/share/rpcd/ucode/luci.wireguard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/tree/a3cae27ab4f51c374f5cf9ab84f181ff91854d0b/modules/luci-base/ucode/template/header.ut" rel="noopener noreferrer"&gt;modules/luci-base/ucode/template/header.ut&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/tree/a3cae27ab4f51c374f5cf9ab84f181ff91854d0b/libs/luci-lib-base/luasrc/util.luadoc" rel="noopener noreferrer"&gt;libs/luci-lib-base/luasrc/util.luadoc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/tree/a3cae27ab4f51c374f5cf9ab84f181ff91854d0b/themes/luci-theme-bootstrap/ucode/template/themes/bootstrap/header.ut" rel="noopener noreferrer"&gt;themes/luci-theme-bootstrap/ucode/template/themes/bootstrap/header.ut&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/tree/a3cae27ab4f51c374f5cf9ab84f181ff91854d0b/themes/luci-theme-material/ucode/template/themes/material/header.ut" rel="noopener noreferrer"&gt;themes/luci-theme-material/ucode/template/themes/material/header.ut&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/tree/a3cae27ab4f51c374f5cf9ab84f181ff91854d0b/themes/luci-theme-openwrt/ucode/template/themes/openwrt.org/header.ut" rel="noopener noreferrer"&gt;themes/luci-theme-openwrt/ucode/template/themes/openwrt.org/header.ut&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/tree/a3cae27ab4f51c374f5cf9ab84f181ff91854d0b/themes/luci-theme-openwrt-2020/ucode/template/themes/openwrt2020/header.ut" rel="noopener noreferrer"&gt;themes/luci-theme-openwrt-2020/ucode/template/themes/openwrt2020/header.ut&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, we have a pretty good idea of what needs to be ported. We can even treat the search results as a sort of "porting roadmap."&lt;/p&gt;

&lt;p&gt;The next 2 challenges are to organize the ports then also ensure that changes from LuCI can be propagated to LuMI.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>networking</category>
      <category>go</category>
      <category>firewall</category>
    </item>
    <item>
      <title>LuCI on MGMT - Day 6</title>
      <dc:creator>project-laguardia</dc:creator>
      <pubDate>Mon, 14 Jul 2025 09:06:05 +0000</pubDate>
      <link>https://dev.to/projectlaguardia/luci-on-mgmt-day-6-292</link>
      <guid>https://dev.to/projectlaguardia/luci-on-mgmt-day-6-292</guid>
      <description>&lt;h1&gt;
  
  
  Hurdle 5: Identifying Any &lt;code&gt;/etc/config&lt;/code&gt; Interop
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Previously:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/project-laguardia/lumi/blob/ac0df8855bf9e0a1d94e9ab3193fbfaefdbf09ca/porting/DAY%205.md" rel="noopener noreferrer"&gt;https://github.com/project-laguardia/lumi/blob/ac0df8855bf9e0a1d94e9ab3193fbfaefdbf09ca/porting/DAY%205.md&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function Find-InSource {
    param(
        [string] $Pattern,
        [string] $Repository = ".\luci",
        [string[]] $Extensions = @(
            ".lua",
            ".c",
            ".js",
            ".mjs"
        ),
        [switch] $Verbose
    )

    # ...
}

&amp;amp; { # Search for `uci` in the source code
    $hits = Find-InSource -Pattern '(?&amp;lt;!l)uci' -Verbose
    $hits | ConvertTo-Json -Depth 5 | Out-File "porting/searches/uci/hits.json" -Encoding UTF8
    $hits.Keys | Out-File "porting/searches/uci/hits.txt" -Encoding UTF8
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Task 1: Reviewing Search Results
&lt;/h2&gt;

&lt;p&gt;Some new searches have been added to &lt;code&gt;porting/~search.ps1&lt;/code&gt;. &lt;code&gt;/etc/config&lt;/code&gt; interop search is saved to &lt;code&gt;porting/searches/config/etc.hits.*&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Only noteable hit was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/luasrc/model/uci.lua" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/luasrc/model/uci.lua&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;This is a Lua abstraction for UCI&lt;/li&gt;
&lt;li&gt;Already planned to be ported to LuMI&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  Hurdle 6: Identifying Any &lt;code&gt;luci.config&lt;/code&gt; interop
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Task 1: Reviewing Search Results
&lt;/h2&gt;

&lt;p&gt;This search has also been added to &lt;code&gt;porting/~search.ps1&lt;/code&gt;. The results are saved to &lt;code&gt;porting/searches/module.hits.*&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;A few noteable hits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-compat/luasrc/cbi.lua" rel="noopener noreferrer"&gt;modules/luci-compat/luasrc/cbi.lua&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;This is a part of &lt;a href="https://github.com/openwrt/luci/blob/e75cb4f5ba4030bffb18eec9ca039678b5a508f4/libs/cbi/luasrc/cbi.lua#L2" rel="noopener noreferrer"&gt;LuCI's Configuration Bind Interface (CBI)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Helps map UCI configuration files to HTML forms.&lt;/li&gt;
&lt;li&gt;Already planned to be ported to LuMI&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/luasrc/cacheloader.lua" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/luasrc/cacheloader.lua&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;This is a new one.&lt;/li&gt;
&lt;li&gt;It's a small cache enabler for LuCI. It appears to be an artifact of the old LuCI architecture (pre-ucode)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/luasrc/config.lua" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/luasrc/config.lua&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Quite literally the "luci.config" module.&lt;/li&gt;
&lt;li&gt;Already planned to be ported to LuMI&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/luasrc/template.lua" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/luasrc/template.lua&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Modifies &lt;code&gt;luci.config.template&lt;/code&gt; and reads &lt;code&gt;luci.config.main&lt;/code&gt; to provide a template engine for LuCI.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/luasrc/model/uci.lua" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/luasrc/model/uci.lua&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;This is a wrapper around the &lt;code&gt;uci&lt;/code&gt; module that provides a more Lua-like interface.&lt;/li&gt;
&lt;li&gt;Already planned to be ported to LuMI&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/openwrt/luci/tree/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-mod-rpc/luasrc/controller/rpc.lua" rel="noopener noreferrer"&gt;modules/luci-mod-rpc/luasrc/controller/rpc.lua&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Primarily referenced for getting &lt;code&gt;uci&lt;/code&gt; session time setting.&lt;/li&gt;
&lt;li&gt;Already planned to be ported to LuMI&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Today's load will be light, as yesterday's took quite a bit of time to go through. Tomorrow, we will be backstepping as we missed reviewing search results within &lt;code&gt;ucode&lt;/code&gt; files.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>go</category>
      <category>networking</category>
      <category>firewall</category>
    </item>
    <item>
      <title>LuCI on MGMT - Day 05</title>
      <dc:creator>project-laguardia</dc:creator>
      <pubDate>Mon, 14 Jul 2025 00:55:17 +0000</pubDate>
      <link>https://dev.to/projectlaguardia/luci-on-mgmt-day-05-alb</link>
      <guid>https://dev.to/projectlaguardia/luci-on-mgmt-day-05-alb</guid>
      <description>&lt;h1&gt;
  
  
  Hurdle 4: Identifying Where &lt;code&gt;uci&lt;/code&gt; Interop Actually Occurs
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Previously:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/project-laguardia/lumi/blob/24344c6562d3d719f0bd03d97e515eb88f3a54ab/porting/DAY%204.md" rel="noopener noreferrer"&gt;https://github.com/project-laguardia/lumi/blob/24344c6562d3d719f0bd03d97e515eb88f3a54ab/porting/DAY%204.md&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Task 1: Code-Search
&lt;/h2&gt;

&lt;p&gt;I believe &lt;code&gt;uci&lt;/code&gt; interop occurs in the following modules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;luci.model.uci&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Like we've discussed before, LuCI uses an older version of Lua that allows the &lt;code&gt;module&lt;/code&gt; function to be used. This allows LuCI to name modules consistently regradless of whether they are Lua or C modules.&lt;/p&gt;

&lt;p&gt;After searching for &lt;code&gt;"luci.model.uci"&lt;/code&gt;, we discover that it is defined in Lua:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/luasrc/model/uci.lua" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/luasrc/model/uci.lua&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The API doc for &lt;code&gt;luci.model.uci&lt;/code&gt; is available at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API docs: &lt;a href="https://openwrt.github.io/luci/api/modules/luci.model.uci.html" rel="noopener noreferrer"&gt;https://openwrt.github.io/luci/api/modules/luci.model.uci.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One can see how &lt;code&gt;uci&lt;/code&gt; may be interacted with by using regex search &lt;code&gt;(?&amp;lt;!l&amp;gt;)uci&lt;/code&gt; in code search. Unfortunately, Github code search does not support regex, so we will have to do this locally. Here is a PowerShell script that does that for us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function Find-InSource {
    param(
        [string] $Pattern,
        [string] $Repository = ".\luci",
        [string[]] $Extensions = @(
            ".lua",
            ".c",
            ".js",
            ".mjs"
        ),
        [switch] $Verbose
    )

    $gitRoot = (git rev-parse --show-toplevel 2&amp;gt;$null)

    $output = [ordered]@{}

    If( $Verbose ) {
        Write-Host "Aggregating source files with extensions: $($Extensions -join ', ')" -ForegroundColor Yellow
        Write-Host "- Wait a moment, this may take a bit..." -ForegroundColor DarkGray
    }

    $files = Get-ChildItem -Path $Repository -File -Recurse | Where-Object {
        $_.Extension -in $Extensions
    }

    if ($files.Count -eq 0) {
        Write-Host "No files found with the specified extensions." -ForegroundColor Yellow
        return $output
    } elseif ( $Verbose ) {
        Write-Host "Searching in $($files.Count) files with extensions: $($Extensions -join ', ')" -ForegroundColor Black -BackgroundColor DarkYellow
    }

    $files | ForEach-Object {
        $filename = $_.FullName.Replace($gitRoot, '').TrimStart('\/')
        $fileContent = Get-Content $_.FullName

        $hits = for ($ln = 0; $ln -lt $fileContent.Count; $ln++) {
            $line = $fileContent | Select-Object -Index $ln
            if ($line -match $pattern) {
                @{
                    Line = $ln + 1
                    Content = $line.Trim()
                }
            }
        }

        if ($hits.Count -gt 0) {
            $output."$filename" = [ordered]@{}

            $hits | ForEach-Object {
                $output."$filename"."$($_.Line)" = $_.Content
            }

            If( $Verbose ){
                Write-Host "$filename`:" -ForegroundColor Cyan

                $hits | ForEach-Object {
                    Write-Host "L$($_.Line)" -NoNewline -ForegroundColor Gray
                    Write-Host ": $($_.Content)"
                }
                Write-Host ""
            }
        }
    }

    If( $Verbose ) {
        $total_files = $output.Keys.Count
        $total_hits = ($output.Values | ForEach-Object { $_.Count }) -as [int[]] | Measure-Object -Sum | Select-Object -ExpandProperty Sum

        Write-Host "Found $total_hits hits in $total_files files." -ForegroundColor Green
    }

    return $output
}

&amp;amp; { # Search for `uci` in the source code
    $hits = Find-InSource -Pattern '(?&amp;lt;!l)uci' -Verbose
    $hits | ConvertTo-Json -Depth 5 | Out-File "porting/searches/uci/hits.json" -Encoding UTF8
    $hits.Keys | Out-File "porting/searches/uci/hits.txt" -Encoding UTF8
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I will be making this script available in the &lt;code&gt;porting&lt;/code&gt; directory of the repository, so that it can be used for future searches.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;see the README for usage instructions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After running our first search, I've aggregated the following results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Results of interest:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;applications:&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/applications/luci-app-dockerman/luasrc/controller/dockerman.lua" rel="noopener noreferrer"&gt;applications/luci-app-dockerman/luasrc/controller/dockerman.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/applications/luci-app-dockerman/luasrc/model/docker.lua" rel="noopener noreferrer"&gt;applications/luci-app-dockerman/luasrc/model/docker.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/container.lua" rel="noopener noreferrer"&gt;applications/luci-app-dockerman/luasrc/model/cbi/dockerman/container.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/applications/luci-app-lxc/luasrc/controller/lxc.lua" rel="noopener noreferrer"&gt;applications/luci-app-lxc/luasrc/controller/lxc.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/applications/luci-app-bmx7/root/usr/lib/lua/luci/controller/bmx7.lua" rel="noopener noreferrer"&gt;applications/luci-app-bmx7/root/usr/lib/lua/luci/controller/bmx7.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua" rel="noopener noreferrer"&gt;applications/luci-app-nft-qos/luasrc/model/cbi/nft-qos/nft-qos.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/applications/luci-app-ocserv/luasrc/model/cbi/ocserv/users.lua" rel="noopener noreferrer"&gt;applications/luci-app-ocserv/luasrc/model/cbi/ocserv/users.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/applications/luci-app-openvpn/luasrc/controller/openvpn.lua" rel="noopener noreferrer"&gt;applications/luci-app-openvpn/luasrc/controller/openvpn.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/applications/luci-app-openvpn/luasrc/model/cbi/openvpn-file.lua" rel="noopener noreferrer"&gt;applications/luci-app-openvpn/luasrc/model/cbi/openvpn-file.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/applications/luci-app-openvpn/luasrc/model/cbi/openvpn.lua" rel="noopener noreferrer"&gt;applications/luci-app-openvpn/luasrc/model/cbi/openvpn.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/applications/luci-app-splash/luasrc/controller/splash/splash.lua" rel="noopener noreferrer"&gt;applications/luci-app-splash/luasrc/controller/splash/splash.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/applications/luci-app-splash/luasrc/model/cbi/splash/splash.lua" rel="noopener noreferrer"&gt;applications/luci-app-splash/luasrc/model/cbi/splash/splash.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;contrib:&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/contrib/package/ucode-mod-html/src/html.c" rel="noopener noreferrer"&gt;contrib/package/ucode-mod-html/src/html.c&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;libs:&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/libs/luci-lib-nixio/axTLS/crypto/aes.c" rel="noopener noreferrer"&gt;libs/luci-lib-nixio/axTLS/crypto/aes.c&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/libs/rpcd-mod-luci/src/luci.c" rel="noopener noreferrer"&gt;libs/rpcd-mod-luci/src/luci.c&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;modules:&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-base/src/contrib/lemon.c" rel="noopener noreferrer"&gt;modules/luci-base/src/contrib/lemon.c&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-compat/luasrc/cbi.lua" rel="noopener noreferrer"&gt;modules/luci-compat/luasrc/cbi.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-compat/luasrc/cbi/datatypes.lua" rel="noopener noreferrer"&gt;modules/luci-compat/luasrc/cbi/datatypes.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-compat/luasrc/model/firewall.lua" rel="noopener noreferrer"&gt;modules/luci-compat/luasrc/model/firewall.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-compat/luasrc/model/network.lua" rel="noopener noreferrer"&gt;modules/luci-compat/luasrc/model/network.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-compat/luasrc/tools/webadmin.lua" rel="noopener noreferrer"&gt;modules/luci-compat/luasrc/tools/webadmin.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/luasrc/config.lua" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/luasrc/config.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/luasrc/dispatcher.lua" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/luasrc/dispatcher.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/luasrc/sys.lua" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/luasrc/sys.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/luasrc/model/uci.lua" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/luasrc/model/uci.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/luasrc/sys/zoneinfo/tzdata.lua" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/luasrc/sys/zoneinfo/tzdata.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/src/contrib/lemon.c" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/src/contrib/lemon.c&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-mod-rpc/luasrc/controller/rpc.lua" rel="noopener noreferrer"&gt;modules/luci-mod-rpc/luasrc/controller/rpc.lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-mod-rpc/luasrc/jsonrpcbind/uci.lua" rel="noopener noreferrer"&gt;modules/luci-mod-rpc/luasrc/jsonrpcbind/uci.lua&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;The rest of the results can be found here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/project-laguardia/lumi/tree/24344c6562d3d719f0bd03d97e515eb88f3a54ab/porting/searches/uci" rel="noopener noreferrer"&gt;porting/searches/uci&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Task 2: Reviewing the Results
&lt;/h2&gt;

&lt;p&gt;For this we are going scope it down to just libs, and modules. Applications are third-party. We may want to port them later, but for now we will focus on the core LuCI codebase. Contrib was matched by mistake. The referenced C file is a html special character parser for ucode. The search script matched the &lt;code&gt;&amp;amp;ucirc;&lt;/code&gt; which is html for &lt;code&gt;û&lt;/code&gt;, which is not what we are looking for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Libs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/libs/luci-lib-nixio/axTLS/crypto/aes.c" rel="noopener noreferrer"&gt;libs/luci-lib-nixio/axTLS/crypto/aes.c&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;This only had one match, and it was a mistake: &lt;code&gt;"/* Perform doubling in Galois Field GF(2^8) using the irreducible polynomial"&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-base/src/contrib/lemon.c" rel="noopener noreferrer"&gt;modules/luci-base/src/contrib/lemon.c&lt;/a&gt; - also a mistake

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/src/contrib/lemon.c" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/src/contrib/lemon.c&lt;/a&gt; - also a mistake&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/luasrc/sys/zoneinfo/tzdata.lua" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/luasrc/sys/zoneinfo/tzdata.lua&lt;/a&gt; - also a mistake&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/libs/rpcd-mod-luci/src/luci.c" rel="noopener noreferrer"&gt;libs/rpcd-mod-luci/src/luci.c&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This C file is a plugin for &lt;code&gt;rpcd&lt;/code&gt;. It shares certain RPC functions useful for LuCI with the entire ubus. Not entirely sure why LuCI does this, because I believe LuCI is the only user of these functions.&lt;/li&gt;
&lt;li&gt;These will have to be ported to lumi. We may not use the same mechanism, but we can use some of the logic.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-compat/luasrc/cbi.lua" rel="noopener noreferrer"&gt;modules/luci-compat/luasrc/cbi.lua&lt;/a&gt; and &lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-compat/luasrc/cbi/datatypes.lua" rel="noopener noreferrer"&gt;modules/luci-compat/luasrc/cbi/datatypes.lua&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is a part of &lt;a href="https://github.com/openwrt/luci/blob/e75cb4f5ba4030bffb18eec9ca039678b5a508f4/libs/cbi/luasrc/cbi.lua#L2" rel="noopener noreferrer"&gt;LuCI's Configuration Bind Interface (CBI)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;This is a high-value target for porting. It helps map UCI configuration files to HTML forms.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-compat/luasrc/model/firewall.lua" rel="noopener noreferrer"&gt;modules/luci-compat/luasrc/model/firewall.lua&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provides a model for the configuring the firewall in LuCI.&lt;/li&gt;
&lt;li&gt;This is a valuable target for porting.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-compat/luasrc/model/network.lua" rel="noopener noreferrer"&gt;modules/luci-compat/luasrc/model/network.lua&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provides a model for configuring network interfaces, devices, and L2 (sometimes L3) protocols.&lt;/li&gt;
&lt;li&gt;This is a valuable target for porting.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-compat/luasrc/tools/webadmin.lua" rel="noopener noreferrer"&gt;modules/luci-compat/luasrc/tools/webadmin.lua&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provides a small set of utility tools for LuCI.&lt;/li&gt;
&lt;li&gt;Low value target for porting, but a target nonetheless.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/luasrc/config.lua" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/luasrc/config.lua&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is a convenience wrapper around the &lt;code&gt;uci&lt;/code&gt; module that provides a more Lua-like interface.&lt;/li&gt;
&lt;li&gt;Codebase and changes required are small, but a very high-value target for porting.&lt;/li&gt;
&lt;li&gt;It is called 'luci.config' in LuCI,&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/luasrc/dispatcher.lua" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/luasrc/dispatcher.lua&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is the HTTP Router for LuCI&lt;/li&gt;
&lt;li&gt;It mainly provides auth protection for &lt;code&gt;uci&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Valuable target for porting&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/luasrc/sys.lua" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/luasrc/sys.lua&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This module provides a set of system utilities for LuCI.&lt;/li&gt;
&lt;li&gt;It minimally interacts with &lt;code&gt;uci&lt;/code&gt;. In fact its only interaction appears to be pulling IPs from it.&lt;/li&gt;
&lt;li&gt;We need to port this, but low priority&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-lua-runtime/luasrc/model/uci.lua" rel="noopener noreferrer"&gt;modules/luci-lua-runtime/luasrc/model/uci.lua&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is a wrapper around the &lt;code&gt;uci&lt;/code&gt; module that provides a more Lua-like interface.&lt;/li&gt;
&lt;li&gt;Definitely a high-value target for porting.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-mod-rpc/luasrc/controller/rpc.lua" rel="noopener noreferrer"&gt;modules/luci-mod-rpc/luasrc/controller/rpc.lua&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is a utility for doing RPC calls in LuCI.&lt;/li&gt;
&lt;li&gt;I believe that &lt;code&gt;mgmt&lt;/code&gt; uses a different RPC mechanism, so any ubus and RPC functionality may also have to be ported.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://github.com/openwrt/luci/blob/4715c6aa1a53762f3792c94a907340a2ae83cd2f/modules/luci-mod-rpc/luasrc/jsonrpcbind/uci.lua" rel="noopener noreferrer"&gt;modules/luci-mod-rpc/luasrc/jsonrpcbind/uci.lua&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A convenience wrapper around &lt;code&gt;uci&lt;/code&gt;. Honestly seems kind of redundant with &lt;code&gt;luci.model.uci&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Will still be ported, but may iterate over LuCI packages and drop its usage in favor of &lt;code&gt;luci.model.uci&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>LuCI on MGMT - Day 04</title>
      <dc:creator>project-laguardia</dc:creator>
      <pubDate>Sat, 12 Jul 2025 03:47:18 +0000</pubDate>
      <link>https://dev.to/projectlaguardia/luci-on-mgmt-day-04-10eh</link>
      <guid>https://dev.to/projectlaguardia/luci-on-mgmt-day-04-10eh</guid>
      <description>&lt;h1&gt;
  
  
  Hurdle 3: Scoping OpenWRT's Build System Down (Continued)
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Previously:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/project-laguardia/lumi/blob/main/porting/DAY%203.md" rel="noopener noreferrer"&gt;https://github.com/project-laguardia/lumi/blob/main/porting/DAY%203.md&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;When using the SDK (or the full buildroot), you will often be instructed to use &lt;code&gt;feeds&lt;/code&gt; quite rigorously mainly for healing your SDK, buildroot, or if you are building using the OS itself, your OpenWRT installation. It can also be used to install dependencies for your project as well.&lt;br&gt;
...&lt;br&gt;
LuCI instructs that you run:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;./scripts/feeds update
./scripts/feeds &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; luci
make menuconfig
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;...&lt;br&gt;
(from SDK container &amp;gt; &lt;code&gt;setup.sh&lt;/code&gt;):&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PACKAGES&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
  &lt;span class="c"&gt;# compile all packages in feed&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;FEED &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$ALL_CUSTOM_FEEDS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
      &lt;/span&gt;group &lt;span class="s2"&gt;"feeds install -p &lt;/span&gt;&lt;span class="nv"&gt;$FEED&lt;/span&gt;&lt;span class="s2"&gt; -f -a"&lt;/span&gt;
      ./scripts/feeds &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FEED&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;
      endgroup
  &lt;span class="k"&gt;done

  &lt;/span&gt;&lt;span class="nv"&gt;RET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

  make &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nv"&gt;BUILD_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BUILD_LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nv"&gt;CONFIG_SIGNED_PACKAGES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CONFIG_SIGNED_PACKAGES&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nv"&gt;IGNORE_ERRORS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$IGNORE_ERRORS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nv"&gt;CONFIG_AUTOREMOVE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;y &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nv"&gt;V&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$V&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-j&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;nproc&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;RET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
...
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;...&lt;br&gt;
Now that we understand how they build the packages automatically, we can now use this knowledge for building our own way. For now, we will use the recommended &lt;code&gt;make menuconfig&lt;/code&gt; as described in Task 1, but we will likely switch to using the GH Actions way of building in the future.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Task 1: Digesting &lt;code&gt;./scripts/feeds&lt;/code&gt; (continued)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/openwrt/openwrt/openwrt/-/blob/9ea174c7bf64ec34e96871ce223d7a597ca80d26/scripts/feeds" rel="noopener noreferrer"&gt;https://gitlab.com/openwrt/openwrt/openwrt/-/blob/9ea174c7bf64ec34e96871ce223d7a597ca80d26/scripts/feeds&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To continue from where we left off, we will now look at how &lt;code&gt;./scripts/feeds&lt;/code&gt; works in more detail. The &lt;code&gt;feeds&lt;/code&gt; system is a way to manage packages and their dependencies in OpenWRT. It allows you to update, install, and manage packages easily.&lt;/p&gt;

&lt;p&gt;First thing we'll notice is the used libraries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Getopt::&lt;/span&gt;&lt;span class="nv"&gt;Std&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;FindBin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;Cwd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;lib&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$FindBin&lt;/span&gt;&lt;span class="s2"&gt;::Bin&lt;/span&gt;&lt;span class="p"&gt;";&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;warnings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;Cwd&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abs_path&lt;/span&gt;&lt;span class="p"&gt;';&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of these are standard Perl libraries, except for &lt;code&gt;metadata&lt;/code&gt;, which is another part of the OpenWRT build system, and I believe we will need it to learn how the OpenWRT SDK makes config files for menuconfig. For now, we will continue digesting &lt;code&gt;./scripts/feeds&lt;/code&gt; and come back to it later.&lt;/p&gt;

&lt;p&gt;The first thing &lt;code&gt;feeds&lt;/code&gt; does is setup the working environment and verify make:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;
&lt;span class="nb"&gt;chdir&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$FindBin&lt;/span&gt;&lt;span class="s2"&gt;::Bin/..&lt;/span&gt;&lt;span class="p"&gt;";&lt;/span&gt;
&lt;span class="nv"&gt;$ENV&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;TOPDIR&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;getcwd&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nb"&gt;chdir&lt;/span&gt; &lt;span class="nv"&gt;$ENV&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;TOPDIR&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nv"&gt;$ENV&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;GIT_CONFIG_PARAMETERS&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'core.autocrlf=false'&lt;/span&gt;&lt;span class="p"&gt;";&lt;/span&gt;
&lt;span class="nv"&gt;$ENV&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;GREP_OPTIONS&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;"";&lt;/span&gt;

&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$mk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;`&lt;/span&gt;&lt;span class="sb"&gt;command -v gmake 2&amp;gt;/dev/null&lt;/span&gt;&lt;span class="p"&gt;`;&lt;/span&gt;  &lt;span class="c1"&gt;# select the right 'make' program&lt;/span&gt;
&lt;span class="nb"&gt;chomp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$mk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;# trim trailing newline&lt;/span&gt;
&lt;span class="nv"&gt;$mk&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nv"&gt;$mk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;make&lt;/span&gt;&lt;span class="p"&gt;";&lt;/span&gt;    &lt;span class="c1"&gt;# default to 'make'&lt;/span&gt;

&lt;span class="c1"&gt;# check version of make&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;@mkver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;split&lt;/span&gt; &lt;span class="sr"&gt;/\s+/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;&lt;span class="si"&gt;$mk&lt;/span&gt;&lt;span class="sb"&gt; -v&lt;/span&gt;&lt;span class="p"&gt;`,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$valid_mk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$mkver&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/^GNU/&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nv"&gt;$valid_mk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$mkver&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/^Make/&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nv"&gt;$valid_mk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$mkv1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$mkv2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;split&lt;/span&gt; &lt;span class="sr"&gt;/\./&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$mkver&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$mkv1&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$mkv1&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$mkv2&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;81&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nv"&gt;$valid_mk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$valid_mk&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nb"&gt;die&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unsupported version of make found: &lt;/span&gt;&lt;span class="si"&gt;$mk&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="p"&gt;";&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we are provided with a good handful of different functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;parse_file&lt;/span&gt;&lt;span class="p"&gt;($$);&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;parse_config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;update_location&lt;/span&gt;&lt;span class="p"&gt;($$);&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;update_index&lt;/span&gt;&lt;span class="p"&gt;($);&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;update_feed_via&lt;/span&gt;&lt;span class="p"&gt;($$$$$$$);&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;get_targets&lt;/span&gt;&lt;span class="p"&gt;($);&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;get_feed&lt;/span&gt;&lt;span class="p"&gt;($);&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;get_installed&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;search_feed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;list_feed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;do_install_src&lt;/span&gt;&lt;span class="p"&gt;($$);&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;do_install_target&lt;/span&gt;&lt;span class="p"&gt;($);&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;lookup_src&lt;/span&gt;&lt;span class="p"&gt;($$);&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;lookup_package&lt;/span&gt;&lt;span class="p"&gt;($$);&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;lookup_target&lt;/span&gt;&lt;span class="p"&gt;($$);&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;is_core_src&lt;/span&gt;&lt;span class="p"&gt;($);&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;install_target&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;install_src&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;install_package&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;install_target_or_package&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;refresh_config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;uninstall_target&lt;/span&gt;&lt;span class="p"&gt;($);&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;update_feed&lt;/span&gt;&lt;span class="p"&gt;($$$$$$);&lt;/span&gt;

&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;feed_config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;uninstall&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a few useful hashtables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;%update_method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src-svn&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;init&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;svn checkout '%s' '%s'&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;svn update&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;controldir&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.svn&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;revision&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;svn info | grep 'Revision' | cut -d ' ' -f 2 | tr -d '&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="p"&gt;"},&lt;/span&gt;
    &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src-cpy&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;init&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cp -Rf '%s' '%s'&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;revision&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;echo -n 'local'&lt;/span&gt;&lt;span class="p"&gt;"},&lt;/span&gt;
    &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src-link&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;init&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ln -s '%s' '%s'&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;revision&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;echo -n 'local'&lt;/span&gt;&lt;span class="p"&gt;"},&lt;/span&gt;
    &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src-dummy&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;init&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true '%s' &amp;amp;&amp;amp; mkdir '%s'&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;revision&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;echo -n 'dummy'&lt;/span&gt;&lt;span class="p"&gt;"},&lt;/span&gt;
    &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src-git&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;init&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;          &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git clone --depth 1 '%s' '%s'&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;init_branch&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git clone --depth 1 --branch '%s' '%s' '%s'&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;init_commit&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git clone '%s' '%s' &amp;amp;&amp;amp; cd '%s' &amp;amp;&amp;amp; git checkout -b '%s' '%s' &amp;amp;&amp;amp; cd -&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git pull --ff-only&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update_rebase&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git pull --rebase=merges&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update_stash&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git pull --rebase=merges --autostash&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update_force&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git pull --ff-only || (git reset --hard HEAD; git pull --ff-only; exit 1)&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;post_update&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git submodule update --init --recursive&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;controldir&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.git&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;revision&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git rev-parse HEAD | tr -d '&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="p"&gt;"},&lt;/span&gt;
    &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src-git-full&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;init&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;          &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git clone '%s' '%s'&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;init_branch&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git clone --branch '%s' '%s' '%s'&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;init_commit&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git clone '%s' '%s' &amp;amp;&amp;amp; cd '%s' &amp;amp;&amp;amp; git checkout -b '%s' '%s' &amp;amp;&amp;amp; cd -&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git pull --ff-only&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update_rebase&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git pull --rebase=merges&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update_stash&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git pull --rebase=merges --autostash&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update_force&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git pull --ff-only || (git reset --hard HEAD; git pull --ff-only; exit 1)&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;post_update&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git submodule update --init --recursive&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;controldir&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.git&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;revision&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git rev-parse HEAD | tr -d '&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="p"&gt;"},&lt;/span&gt;
    &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src-gitsvn&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;init&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git svn clone -r HEAD '%s' '%s'&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git svn rebase&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;controldir&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.git&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;revision&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git rev-parse HEAD | tr -d '&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="p"&gt;"},&lt;/span&gt;
    &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src-bzr&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;init&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bzr checkout --lightweight '%s' '%s'&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bzr update&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;controldir&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.bzr&lt;/span&gt;&lt;span class="p"&gt;"},&lt;/span&gt;
    &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src-hg&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;init&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hg clone '%s' '%s'&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hg pull --update&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;controldir&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.hg&lt;/span&gt;&lt;span class="p"&gt;"},&lt;/span&gt;
    &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src-darcs&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;init&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;darcs get '%s' '%s'&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;darcs pull -a&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;controldir&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_darcs&lt;/span&gt;&lt;span class="p"&gt;"},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;%commands&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;list&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;install&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;install&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;search&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;search&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;uninstall&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;uninstall&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;feed_config&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;feed_config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;system&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;rm -rf ./feeds ./package/feeds ./target/linux/feeds&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some important things to note about the update methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;src-cpy&lt;/code&gt;, &lt;code&gt;src-link&lt;/code&gt;, and &lt;code&gt;src-dummy&lt;/code&gt; are used for local sources that basically do not require any updates.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;src-cpy&lt;/code&gt; does, however it is just a copy of an already local resource.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;src-git&lt;/code&gt;, &lt;code&gt;src-git-full&lt;/code&gt;, &lt;code&gt;src-gitsvn&lt;/code&gt;, &lt;code&gt;src-bzr&lt;/code&gt;, &lt;code&gt;src-hg&lt;/code&gt;,&lt;code&gt;src-svn&lt;/code&gt; and &lt;code&gt;src-darcs&lt;/code&gt; are all used for version control systems.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;src-git&lt;/code&gt; is the most common one, and it is used for Git repositories.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src-git-full&lt;/code&gt; is used for full Git repositories, which may include all branches and history.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src-gitsvn&lt;/code&gt; is used for Git repositories that are backed by a Subversion repository.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src-bzr&lt;/code&gt;, &lt;code&gt;src-hg&lt;/code&gt;, and &lt;code&gt;src-darcs&lt;/code&gt; are used for Bazaar, Mercurial, and Darcs repositories respectively.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src-svn&lt;/code&gt; is used for Subversion repositories.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;We have already gone over introducing the commands, but as a refresher, here they are once more:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;&amp;lt;EOF;
Usage: $0 &amp;lt;command&amp;gt; [options]

Commands:
    list [options]: List feeds, their content and revisions (if installed)
    Options:
        -n :            List of feed names.
        -s :            List of feed names and their URL.
        -r &amp;lt;feedname&amp;gt;:  List packages of specified feed.
        -d &amp;lt;delimiter&amp;gt;: Use specified delimiter to distinguish rows (default: spaces)
        -f :            List feeds in opkg feeds.conf compatible format (when using -s).

    install [options] &amp;lt;package&amp;gt;: Install a package
    Options:
        -a :           Install all packages from all feeds or from the specified feed using the -p option.
        -p &amp;lt;feedname&amp;gt;: Prefer this feed when installing packages.
        -d &amp;lt;y|m|n&amp;gt;:    Set default for newly installed packages.
        -f :           Install will be forced even if the package exists in core OpenWrt (override)

    search [options] &amp;lt;substring&amp;gt;: Search for a package
    Options:
        -r &amp;lt;feedname&amp;gt;: Only search in this feed

    uninstall -a|&amp;lt;package&amp;gt;: Uninstall a package
    Options:
        -a :           Uninstalls all packages.

    update -a|&amp;lt;feedname(s)&amp;gt;: Update packages and lists of feeds in feeds.conf .
    Options:
        -a :           Update all feeds listed within feeds.conf. Otherwise the specified feeds will be updated.
        -r :           Update by rebase. (git only. Useful if local commits exist)
        -s :           Update by rebase and autostash. (git only. Useful if local commits and uncommited changes exist)
        -i :           Recreate the index only. No feed update from repository is performed.
        -f :           Force updating feeds even if there are changed, uncommitted files.

    clean:             Remove downloaded/generated files.

EOF
&lt;/span&gt;    &lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After some review of &lt;code&gt;feeds&lt;/code&gt; source code, most of the "installing" is just linking and copying source files to the appropriate directories. Build doesn't actually occur here, so I will have to dig into &lt;code&gt;make&lt;/code&gt; invocation during &lt;code&gt;make menuconfig&lt;/code&gt;/&lt;code&gt;make defconfig&lt;/code&gt; to see how it is done. I suspect that &lt;a href="https://gitlab.com/openwrt/openwrt/openwrt/-/blob/master/include/toplevel.mk" rel="noopener noreferrer"&gt;include/toplevel.mk&lt;/a&gt; and &lt;a href="https://gitlab.com/openwrt/openwrt/openwrt/-/blob/master/scripts/package-metadata.pl" rel="noopener noreferrer"&gt;scripts/package-metadata.pl&lt;/a&gt; are the two files that actually make the necessary config files for menuconfig. Though, I'm not too sure. OpenWRT's SDK is quite opaque and minimally documented, so it is hard to tell what is actually going on even if you can read the source code.&lt;/p&gt;

&lt;p&gt;Before moving on there is one thing I do want to point out. &lt;code&gt;feeds&lt;/code&gt; has a &lt;code&gt;refresh_config&lt;/code&gt; function that calls &lt;code&gt;make defconfig&lt;/code&gt;/&lt;code&gt;make oldconfig&lt;/code&gt; and that function is called every time the commands &lt;code&gt;install&lt;/code&gt;, &lt;code&gt;uninstall&lt;/code&gt;, and &lt;code&gt;update&lt;/code&gt; are called.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task 2: Digesting &lt;code&gt;make menuconfig&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The menuconfig make target is defined in the "Top Level" Makefile, which is a highly referenced file in the OpenWRT build system.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;./include/toplevel.mk&lt;/code&gt; is referenced &lt;a href="https://gitlab.com/openwrt/openwrt/openwrt/-/blob/9ea174c7bf64ec34e96871ce223d7a597ca80d26/Makefile#L33" rel="noopener noreferrer"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;./include/toplevel.mk&lt;/code&gt; defines the &lt;code&gt;menuconfig&lt;/code&gt; target &lt;a href="https://gitlab.com/openwrt/openwrt/openwrt/-/blob/master/include/toplevel.mk#L135-140" rel="noopener noreferrer"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This target is really just a wrapper for &lt;code&gt;./scripts/config/mconf ./Config.in&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;toplevel.mk&lt;/code&gt; is run once every time &lt;code&gt;make&lt;/code&gt; is called. From the SDK root Makefile, this is ensured using this mechanism:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="k"&gt;ifneq&lt;/span&gt; &lt;span class="nv"&gt;($(OPENWRT_BUILD),1)&lt;/span&gt;
  &lt;span class="nb"&gt;override&lt;/span&gt; &lt;span class="nv"&gt;OPENWRT_BUILD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;OPENWRT_BUILD&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;OPENWRT_BUILD&lt;/code&gt; is acting like a flag to indicate that make is already running. Note that at any time during the make process, you can clear &lt;code&gt;OPENWRT_BUILD&lt;/code&gt; if you want to run &lt;code&gt;make&lt;/code&gt; inside your build script in a way that it thinks it isn't running. This has a handful of uses across the SD, even inside &lt;code&gt;./scripts/feeds&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;update_index&lt;/span&gt;&lt;span class="p"&gt;($)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;d&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./feeds/&lt;/span&gt;&lt;span class="si"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;.tmp&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./feeds/&lt;/span&gt;&lt;span class="si"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;.tmp&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;d&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./feeds/&lt;/span&gt;&lt;span class="si"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;.tmp/info&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./feeds/&lt;/span&gt;&lt;span class="si"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;.tmp/info&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nb"&gt;system&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="si"&gt;$mk&lt;/span&gt;&lt;span class="s2"&gt; -s prepare-mk OPENWRT_BUILD= TMP_DIR=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;$ENV&lt;/span&gt;&lt;span class="s2"&gt;{TOPDIR}/feeds/&lt;/span&gt;&lt;span class="si"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;.tmp&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;
    &lt;span class="nb"&gt;system&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="si"&gt;$mk&lt;/span&gt;&lt;span class="s2"&gt; -s -f include/scan.mk IS_TTY=1 SCAN_TARGET=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;packageinfo&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; SCAN_DIR=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;feeds/&lt;/span&gt;&lt;span class="si"&gt;$name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; SCAN_NAME=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;package&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; SCAN_DEPTH=5 SCAN_EXTRA=&lt;/span&gt;&lt;span class="se"&gt;\"\"&lt;/span&gt;&lt;span class="s2"&gt; TMP_DIR=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;$ENV&lt;/span&gt;&lt;span class="s2"&gt;{TOPDIR}/feeds/&lt;/span&gt;&lt;span class="si"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;.tmp&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;
    &lt;span class="nb"&gt;system&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="si"&gt;$mk&lt;/span&gt;&lt;span class="s2"&gt; -s -f include/scan.mk IS_TTY=1 SCAN_TARGET=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;targetinfo&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; SCAN_DIR=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;feeds/&lt;/span&gt;&lt;span class="si"&gt;$name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; SCAN_NAME=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;target&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; SCAN_DEPTH=5 SCAN_EXTRA=&lt;/span&gt;&lt;span class="se"&gt;\"\"&lt;/span&gt;&lt;span class="s2"&gt; SCAN_MAKEOPTS=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;TARGET_BUILD=1&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; TMP_DIR=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;$ENV&lt;/span&gt;&lt;span class="s2"&gt;{TOPDIR}/feeds/&lt;/span&gt;&lt;span class="si"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;.tmp&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;
    &lt;span class="nb"&gt;system&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;ln -sf &lt;/span&gt;&lt;span class="si"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;.tmp/.packageinfo ./feeds/&lt;/span&gt;&lt;span class="si"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;.index&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;
    &lt;span class="nb"&gt;system&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;ln -sf &lt;/span&gt;&lt;span class="si"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;.tmp/.targetinfo ./feeds/&lt;/span&gt;&lt;span class="si"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;.targetindex&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;get_installed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;system&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="si"&gt;$mk&lt;/span&gt;&lt;span class="s2"&gt; -s prepare-tmpinfo OPENWRT_BUILD=&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;
    &lt;span class="nv"&gt;clear_packages&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;parse_package_metadata&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;./tmp/.packageinfo&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;
    &lt;span class="nv"&gt;%installed_pkg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;%vpackage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;%installed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;%srcpackage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;%installed_targets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;get_targets&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;./tmp/.targetinfo&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we need to address how the SDK generates the config files for &lt;code&gt;menuconfig&lt;/code&gt;. &lt;code&gt;toplevel.mk&lt;/code&gt; defines another target called &lt;code&gt;prepare-tmpinfo&lt;/code&gt;. This target invokes the aforementioned &lt;code&gt;./scripts/package-metadata.pl&lt;/code&gt; script, which is used to generate the &lt;code&gt;.in&lt;/code&gt; files for &lt;code&gt;menuconfig&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Another interesting discovery is that it may also invoke &lt;code&gt;./scripts/target-metadata.pl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I think this is enough information to start working on porting LuCI now. If there is any questions about what parts of the SDK variables or scripts may refer to, we now have the files for looking them up. One last step for me is to organize my findings and post them in the README.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>LuCI on MGMT - Day 03</title>
      <dc:creator>project-laguardia</dc:creator>
      <pubDate>Fri, 11 Jul 2025 02:27:47 +0000</pubDate>
      <link>https://dev.to/projectlaguardia/luci-on-mgmt-day-03-b9d</link>
      <guid>https://dev.to/projectlaguardia/luci-on-mgmt-day-03-b9d</guid>
      <description>&lt;h1&gt;
  
  
  Hurdle 3: Scoping OpenWRT's Build System Down (Continued)
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Previously:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/project-laguardia/lumi/blob/92ec839f81b08e7dab9e785dd73dc0fb5b2b27f7/porting/DAY%202.md" rel="noopener noreferrer"&gt;https://github.com/project-laguardia/lumi/blob/92ec839f81b08e7dab9e785dd73dc0fb5b2b27f7/porting/DAY%202.md&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;./scripts/feeds&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;feeds&lt;/code&gt; is the main script for OpenWRT's package feed management written in Perl, abstracting all the repository operations and package install/uninstall logic in a uniform manner, so users don't have to worry themselves with details of where packages come from or how they are versioned.&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;(About understanding their build system:) I've just realized that another way to approach this is to look at their GH Actions. Upon inspection, it appears that they do have a build flow used to test LuCI, and we even have some example runs to look at&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Task 1: Digesting &lt;code&gt;./scripts/feeds&lt;/code&gt; and &lt;code&gt;./scripts/feeds.conf.default&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Previously, I failed to mention why we are going over &lt;code&gt;feeds&lt;/code&gt; and &lt;code&gt;feeds.conf.default&lt;/code&gt;. When using the SDK (or the full buildroot), you will often be instructed to use &lt;code&gt;feeds&lt;/code&gt; quite rigorously mainly for healing your SDK, buildroot, or if you are building using the OS itself, your OpenWRT installation. It can also be used to install dependencies for your project as well.&lt;/p&gt;

&lt;p&gt;Some, but not all instances of it being referenced in OpenWRT/LuCI documentation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://openwrt.org/docs/guide-developer/toolchain/using_the_sdk" rel="noopener noreferrer"&gt;https://openwrt.org/docs/guide-developer/toolchain/using_the_sdk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/luci/wiki/Installation" rel="noopener noreferrer"&gt;https://github.com/openwrt/luci/wiki/Installation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;LuCI instructs that you run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./scripts/feeds update
./scripts/feeds &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; luci
make menuconfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...but as we have discovered previously, it may be better for us to use feeds the way their GH Actions do&lt;/p&gt;

&lt;h2&gt;
  
  
  Task 2: Reviewing SDK Usage in GH Actions:
&lt;/h2&gt;

&lt;p&gt;LuCI's interaction with the SDK at the workflow level is quite minimal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/openwrt/luci/blob/4678d6a82c5c36626edb88cdfb93edbb7e93e0fa/.github/workflows/build.yml#L52-L68" rel="noopener noreferrer"&gt;https://github.com/openwrt/luci/blob/4678d6a82c5c36626edb88cdfb93edbb7e93e0fa/.github/workflows/build.yml#L52-L68&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openwrt/gh-action-sdk@v7&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ARCH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.arch }}-${{ env.BRANCH }}&lt;/span&gt;
    &lt;span class="na"&gt;FEEDNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;packages_ci&lt;/span&gt;
    &lt;span class="na"&gt;V&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;s&lt;/span&gt;

&lt;span class="c1"&gt;# nothing actually happens between invoking the SDK and moving the packages around:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Move created packages to project dir&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cp bin/packages/${{ matrix.arch }}/packages_ci/* . || &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It seems that most of the building is done automatically by the SDK action:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/gh-action-sdk/blob/b8cc97d1072dedff455e2945a73fc43f5c7e1749/entrypoint.sh" rel="noopener noreferrer"&gt;https://github.com/openwrt/gh-action-sdk/blob/b8cc97d1072dedff455e2945a73fc43f5c7e1749/entrypoint.sh&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The available environment variables are documented here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/openwrt/gh-action-sdk/tree/main?tab=readme-ov-file#environmental-variables" rel="noopener noreferrer"&gt;https://github.com/openwrt/gh-action-sdk/tree/main?tab=readme-ov-file#environmental-variables&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ARCH&lt;/code&gt; determines the used OpenWRT SDK Docker container. E.g. &lt;code&gt;x86_64&lt;/code&gt; or &lt;code&gt;x86_64-22.03.2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ARTIFACTS_DIR&lt;/code&gt; determines where the built packages and build logs are saved. Defaults to the default working directory (&lt;code&gt;GITHUB_WORKSPACE&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BUILD_LOG&lt;/code&gt; stores the build logs in &lt;code&gt;./logs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CONTAINER&lt;/code&gt; can set other SDK containers than &lt;code&gt;openwrt/sdk&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EXTRA_FEEDS&lt;/code&gt; are added to the &lt;code&gt;feeds.conf&lt;/code&gt;, where &lt;code&gt;|&lt;/code&gt; are replaced by white spaces.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FEED_DIR&lt;/code&gt; used in the created &lt;code&gt;feeds.conf&lt;/code&gt; for the current repo. Defaults to the default working directory (&lt;code&gt;GITHUB_WORKSPACE&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FEEDNAME&lt;/code&gt; is used in the created &lt;code&gt;feeds.conf&lt;/code&gt; for the current repo. Defaults to &lt;code&gt;action&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IGNORE_ERRORS&lt;/code&gt; can ignore failing package builds.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;INDEX&lt;/code&gt; makes the action build the package index. Default is 0. Set to 1 to enable.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;KEY_BUILD&lt;/code&gt; can be a private Signify/&lt;code&gt;usign&lt;/code&gt; key to sign the packages (ipk) feed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PRIVATE_KEY&lt;/code&gt; can be a private key to sign the packages (apk) feed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NO_DEFAULT_FEEDS&lt;/code&gt; disable adding the default SDK feeds&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NO_REFRESH_CHECK&lt;/code&gt; disable check if patches need a refresh&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NO_SHFMT_CHECK&lt;/code&gt; disable check if init files are formatted&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PACKAGES&lt;/code&gt; (Optional) specify the list of packages (space separated) to be built&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;V&lt;/code&gt; changes the build verbosity level.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;setup.sh&lt;/code&gt; Breakdown:&lt;/strong&gt;&lt;br&gt;
First, it calls &lt;a href="https://github.com/openwrt/docker/blob/main/setup.sh" rel="noopener noreferrer"&gt;setup.sh from ghcr.io/openwrt/sdk&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;group &lt;span class="s2"&gt;"bash setup.sh"&lt;/span&gt;
&lt;span class="c"&gt;# snapshot containers don't ship with the SDK to save bandwidth&lt;/span&gt;
&lt;span class="c"&gt;# run setup.sh to download and extract the SDK&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; setup.sh &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; bash setup.sh
endgroup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, it sets up the feed configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;FEEDNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FEEDNAME&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
...
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"src-link &lt;/span&gt;&lt;span class="nv"&gt;$FEEDNAME&lt;/span&gt;&lt;span class="s2"&gt; /feed/"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; feeds.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which we know &lt;a href="https://github.com/project-laguardia/lumi/blob/92ec839f81b08e7dab9e785dd73dc0fb5b2b27f7/porting/DAY%202.md" rel="noopener noreferrer"&gt;from before&lt;/a&gt; is &lt;code&gt;packages_ci&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It appears they use openwrt/gh-action-sdk with a few env vars:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ARCH&lt;/code&gt; - &lt;code&gt;&amp;lt;arch&amp;gt;-&amp;lt;branch&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FEEDNAME&lt;/code&gt; - &lt;code&gt;packages_ci&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Noteworthy (but unused by LuCI) is the fact that you can optionally disable the default feeds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NO_DEFAULT_FEEDS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s,https://git.openwrt.org/feed/,https://github.com/openwrt/,'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s,https://git.openwrt.org/openwrt/,https://github.com/openwrt/,'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s,https://git.openwrt.org/project/,https://github.com/openwrt/,'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        feeds.conf.default &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; feeds.conf
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the script starts adding in the extra feeds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;ALL_CUSTOM_FEEDS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FEEDNAME&lt;/span&gt;&lt;span class="s2"&gt; "&lt;/span&gt;
&lt;span class="c"&gt;#shellcheck disable=SC2153&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;EXTRA_FEED &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$EXTRA_FEEDS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$EXTRA_FEED&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'|'&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; feeds.conf
    ALL_CUSTOM_FEEDS+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$EXTRA_FEED&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;'|'&lt;/span&gt; &lt;span class="nt"&gt;-f2&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; "&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, it updates the feeds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;group &lt;span class="s2"&gt;"feeds.conf"&lt;/span&gt;
&lt;span class="nb"&gt;cat &lt;/span&gt;feeds.conf
endgroup

group &lt;span class="s2"&gt;"feeds update -a"&lt;/span&gt;
./scripts/feeds update &lt;span class="nt"&gt;-a&lt;/span&gt;
endgroup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then it builds &lt;code&gt;defconfig&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;group &lt;span class="s2"&gt;"make defconfig"&lt;/span&gt;
make defconfig
endgroup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're not gonna go over defconfig, unless I later determine we need it, but more on it can be found here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/openwrt/openwrt/openwrt/-/blob/master/include/toplevel.mk#L120-124" rel="noopener noreferrer"&gt;https://gitlab.com/openwrt/openwrt/openwrt/-/blob/master/include/toplevel.mk#L120-124&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;It is a makefile target that generates a &lt;code&gt;.config&lt;/code&gt; file used in kconfig build systems.&lt;/li&gt;
&lt;li&gt;It is used in place of &lt;code&gt;make menuconfig&lt;/code&gt; to generate a default configuration file for the build system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The majority of the rest of the script is determined by whether &lt;code&gt;$PACKAGES&lt;/code&gt; is set or not. LuCI does not set it, so it uses the default behavior:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PACKAGES&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="c"&gt;# compile all packages in feed&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;FEED &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$ALL_CUSTOM_FEEDS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
        &lt;/span&gt;group &lt;span class="s2"&gt;"feeds install -p &lt;/span&gt;&lt;span class="nv"&gt;$FEED&lt;/span&gt;&lt;span class="s2"&gt; -f -a"&lt;/span&gt;
        ./scripts/feeds &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FEED&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;
        endgroup
    &lt;span class="k"&gt;done

    &lt;/span&gt;&lt;span class="nv"&gt;RET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

    make &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nv"&gt;BUILD_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BUILD_LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nv"&gt;CONFIG_SIGNED_PACKAGES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CONFIG_SIGNED_PACKAGES&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nv"&gt;IGNORE_ERRORS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$IGNORE_ERRORS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nv"&gt;CONFIG_AUTOREMOVE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;y &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nv"&gt;V&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$V&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-j&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;nproc&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;RET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
...
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then it wraps up the build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INDEX&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'1'&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="k"&gt;then
    &lt;/span&gt;group &lt;span class="s2"&gt;"make package/index"&lt;/span&gt;
    make package/index
    endgroup
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; bin/ &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;mv &lt;/span&gt;bin/ /artifacts/
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; logs/ &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;mv &lt;/span&gt;logs/ /artifacts/
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RET&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we understand how they build the packages automatically, we can now use this knowledge for building our own way. For now, we will use the recommended &lt;code&gt;make menuconfig&lt;/code&gt; as described in Task 1, but we will likely switch to using the GH Actions way of building in the future.&lt;/p&gt;

&lt;p&gt;I think the next 2 steps is to finish digesting &lt;code&gt;feeds&lt;/code&gt;, then play around with &lt;code&gt;make menuconfig&lt;/code&gt; to get a better understanding of the available options.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>go</category>
      <category>networking</category>
      <category>firewall</category>
    </item>
  </channel>
</rss>
