Skip to content

Build from source

ernolf edited this page Aug 18, 2026 · 7 revisions

🏗️ Build from source

You do not need to build anything to use NcDavTray — a release archive is ready to unpack. This page is for contributors and for anyone who would rather assemble the script themselves than trust a download.

Tip

TL;DR — Clone the repository, run make.cmd check. That assembles build\NcDavTray\NDT.ps1 from the sources and runs every static check CI runs. make.cmd dist additionally packs the release archive. No toolchain, no dependencies: Windows PowerShell 5.1 and git are the whole list — only the changelog needs one more tool.

📂 What is in the repository

The repository holds sources. The thing you run is assembled from them.

Folder Holds
templates\ the script skeleton, NDT.ps1.in
modules\ one function per file, over 200 of them
meta\ the single definition of version, app name, author, project URL
i18n\ the language packs
assets\ logo, icon, screenshots
installer\ Installer.cmd, the bootstrap that ships alongside the script
tools\ build, checks, dist, i18n tooling — plus tools\diagnostics\
build\ the result. Not under version control

Note

There is no NDT.ps1 in the repository. Deliverables are never committed, so a build result can never drift from the sources it was built from. If you want to read the code, read modules\ — one function per file is far easier to navigate than the assembled script anyway.

🎛️ The targets

Everything goes through make.cmd, which hands over to make.ps1. check is the default.

Target Does
build assemble build\NcDavTray from templates, modules, assets, i18n and installer
check build, then run the static checks over the result — the default
dist build, then pack build\NcDavTray_v<version>.zip
clean delete build\
changelog draft the CHANGELOG.md section for the current version — needs git-cliff, see below
i18n write build\i18n-todo\: per language the keys it is missing, with the English text as the value
i18n-merge read translated files from -From <dir> and write their values back into i18n\
i18n-normalize rewrite i18n\ in canonical form, values unchanged
help list the targets
make.cmd            same as: make.cmd check
make.cmd dist
make.cmd i18n-merge -From C:\path\to\translations

CI runs these targets and nothing else — so what a release is built from is exactly what you can run at home.

📝 The changelog

CHANGELOG.md follows Keep a Changelog, and make.cmd changelog drafts the section for the version in meta\Version.ps1 from the commits since the last tag. It is the one target that needs something installed:

winget install orhun.git-cliff

cliff.toml holds the rules: feat becomes Added, fix becomes Fixed, perf becomes Changed, a breaking change goes to Changed whatever type carries it, and everything else — build, ci, chore, docs, refactor, style, test — is left out.

What comes out is a draft, and it is meant to be edited before it is committed. Commit subjects are written for the people working on the program: they name modules, and they list every fix made along the way. A fix to code that did not exist in the last release fixed nothing anybody ever had, and belongs out of the section. What stays is what someone updating from the previous release can notice.

The section is placed above the previous one, with its link reference; the header, the preamble and every hand-written section stay untouched, and a section that already exists is never written twice.

⚙️ How the build works

A template is an ordinary PowerShell script in which single lines have been replaced by placeholders. Each placeholder is a whole line starting at column 0, and the build swaps it for the content it points at:

Placeholder Inserts
#__inc:<path>__ the file's text, verbatim
#__b64:<path>__ the file's bytes as a single base64 line — this is how the icons get inside a single-file script
#__i18n:<path>__ the language pack, reduced to the keys this script actually uses
flowchart LR
    A["templates\NDT.ps1.in"] --> D{{build.ps1}}
    B["modules\*.ps1"] --> D
    C["assets, i18n, installer"] --> D
    D --> E["build\NcDavTray\NDT.ps1"]
    D --> F["build\NcDavTray\Installer.cmd<br>+ assets + i18n"]
    E --> G{{dist.ps1}}
    F --> G
    G --> H["build\NcDavTray_v2.x.y.zip"]
Loading

#__i18n: is resolved in a second pass, after every #__inc: has been put in — it needs the assembled script to see which keys it refers to.

The output is UTF-8 without BOM, with CRLF line endings and ASCII-only characters. That last one is not fussiness: a PowerShell 5.1 script read on a machine with a different code page must not change meaning, and ASCII is the only way to guarantee that.

🔎 What the checks catch

The deliverable is a single file with no modules and no test harness, so the checks stand in for what importing a module would otherwise reveal:

Check Catches
syntax the file parses
commands every command called is defined in the file or comes from PowerShell
variables every variable read is assigned somewhere — the scripts run without Set-StrictMode, so an undefined variable would silently be $null
i18n every literal key handed to T() exists in the embedded language data
members no method called on a window is protected on Form or Control — a call that would only fail when that window opens
style one code style: no backtick continuations, no alignment padding, tabs for indentation, no brace on a line of its own, no blank line at end of file
actions every -Action that Installer.cmd passes is one the script accepts
json / keys / placeholders packs parse, a translation has exactly the English keys, and carries the same {placeholders}

Style is reported against the source file, because that is where the line can be changed.

Important

Run the checks with Windows PowerShell 5.1, not PowerShell 7. That is what the deliverable targets, and each step is deliberately started in its own powershell.exe so the parser doing the checking is the parser that will run the script.

🤖 Continuous integration

Workflow Runs on Jobs
checks.yml every push and pull request REUSE compliance (Linux), build and static checks (Windows), release archive builds (Windows)
release.yml a published release rebuilds from the tag, verifies the tag matches meta\Version.ps1, packs the archive and attaches it to the release

The version guard exists for a concrete reason: once a release is out, an installation compares its own version against the latest release tag. A tag that disagreed with the version compiled into the script would make every installation report an update that never arrives.

The project is REUSE-compliant — every file carries its copyright and licence, checked in CI.

🤝 Contributing

  • Branch off v2, name the branch <type>/<short-description>, open the pull request against v2.
  • Run make.cmd check before pushing. It is the same thing CI will run, so a green run at home is a green run there.
  • One change per branch. A pull request that fixes two unrelated things is two pull requests.
  • Commits are signed off (DCO). New files need an SPDX header.

Tip

Translations are the easiest way in and are always welcome — see Languages. make.cmd i18n prepares exactly the file a translator needs.

Clone this wiki locally