-
-
Notifications
You must be signed in to change notification settings - Fork 0
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 targets
- The changelog
- How the build works
- What the checks catch
- Continuous integration
- How the updater is tested
- Contributing
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.
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.
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.
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"]
#__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.
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.
| 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.
These two are the project's own. The other workflows under .github/workflows/ come from upstream templates and are kept current by a bot — see Maintaining the repository, which also covers the ruleset that requires these checks to pass.
Nothing about a program is worse than an update mechanism that does not work. It is the one feature that runs on a machine nobody can look at, replaces the program while it is running, and has no second chance if it gets it wrong. So it is not shipped on the strength of a code review.
Every change to it is exercised end to end against real GitHub releases before it reaches anyone — but not against this project's releases, because throwaway versions published here would be offered to every installation in the world. They go to a separate repository instead: ernolf/NcDavTray-test. It holds no source, only releases.
flowchart LR
A["Build with the project URL<br>pointed at the test repository"] --> B["Package 2.0.0<br>installed by hand"]
A --> C["Package 2.0.1<br>published as a release"]
B --> D["Check for updates<br>→ Update now"]
C --> D
D --> E["2.0.1 running,<br>settings and mounts intact"]
That works because the address the update check uses is not written down anywhere of its own: it is derived from $ProjectUrl in meta\, the same value the About box links to. Changing that one line points a build at the test track, and there is no second address that could be forgotten. The two packages are otherwise ordinary builds, made the way a release is made — make.cmd dist, same targets, same archive.
Both modes are tested separately. Installed and portable take different paths through the handover: the installed one stops the running instance and refreshes the icon the shortcuts use, the portable one rewrites its launchers in the folder it lives in.
Note
The test repository is public only while a round is running, and private the rest of the time. If you find it and it is reachable, a test is in progress. Nothing in it is meant to be installed, and its version numbers mean nothing outside the test.
This is also why some screenshots in Updates show a project link ending in NcDavTray-test: they were taken from a real update, on the track where a real update can be run twice.
- Branch from
mainand name the branch<your-github-username>/<type>/<short-description>, with<type>from the Conventional Commits vocabulary — someone with the handleoctocatfixing the folder picker would useoctocat/fix/folder-picker. Open the pull request againstmain. - Run
make.cmd checkbefore 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.
© 2025 - 2026 [ernolf] Raphael Gradenwitz · GPL-3.0-or-later · Report an issue
Getting started
Using it
When something is wrong
Windows internals
Keeping it current
Development
Reference
NcDavTray 1.x