Important
This GitHub Action is pre-1.0, expect breaking changes!
Release NSIS packages from GitHub: plugins built from source, or headers, graphics and language files with nothing to build.
Build on every push. When a release is published, attach these files to it:
- a Release Archive
- a Package Installer
- checksums, and build attestations on request
on:
push:
pull_request:
release:
types: [published]
jobs:
plugin:
runs-on: windows-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v7
- uses: nsis-dev/release-package@v0
with:
name: Hello
sources: Contrib/Hello/*.cA repository with nothing to build (headers, graphics, language files) sets neither sources nor project, only name.
A plugin whose source is lost can set toolchain: prebuilt and commit Plugins/<target>/<name>.dll instead. This is an antipattern and the action warns about it: nobody can rebuild or verify those DLLs, and an attestation only proves the workflow copied them. Use it only to keep an otherwise abandoned plugin available.
A release of tag v1.0.0 gets these assets:
| Asset | Contents |
|---|---|
Hello-1.0.0.zip |
Plugins/<target>/Hello.dll, your Docs/, Examples/ and Include/, LICENSE, README |
Hello-1.0.0-setup.exe |
Installs those folders into NSISDIR, showing the LICENSE on its license page |
SHA256SUMS |
Checksums of the above |
Other builds upload the zip and installer as the workflow artifact <name>-<toolchain>, versioned by the short commit SHA.
To attest build provenance, set attestations: true and add the id-token: write and attestations: write permissions.
Attestations are free on public repositories; a private one needs GitHub Team or Enterprise.
Check where a released file came from with gh attestation verify Hello-1.0.0.zip --repo <owner>/<repo>.
To also build with a second Toolchain as a check, use a matrix and release only one of them:
jobs:
plugin:
strategy:
matrix:
include:
- { toolchain: msvc, os: windows-latest, release: true }
- { toolchain: mingw, os: ubuntu-latest, release: false }
runs-on: ${{ matrix.os }}
permissions:
contents: write
steps:
- uses: actions/checkout@v7
- uses: nsis-dev/release-package@v0
with:
name: Hello
sources: Contrib/Hello/*.c
toolchain: ${{ matrix.toolchain }}
release: ${{ matrix.release }}A Package repository is laid out like NSISDIR, as specified in SPEC.md for other tools to implement. The action checks this first and fails before building if it isn't:
| Path | Rule |
|---|---|
Contrib/<name>/ |
Required, holds the source, not shipped. With toolchain: none it is optional and ships as-is, e.g. Contrib/Graphics/ or Contrib/Language files/. With prebuilt it is optional and not shipped |
LICENSE |
Required (a warning with strict: false) at the top level or in Docs/<name>/, any extension or suffix (LICENSE-MIT), or LICENCE, COPYING, UNLICENSE; several are joined on the installer's license page |
README |
Suggested at the top level, any extension |
Docs/<name>/ |
Optional, nothing else in Docs/, ships as-is |
Examples/<name>/ |
Optional, nothing else in Examples/, ships as-is |
Include/ |
Optional, ships as-is |
Plugins/, *.dll |
Not committed, the action builds them. With toolchain: prebuilt, Plugins/<target>/<name>.dll is committed and each DLL's PE machine must match its target |
Contrib, Docs, Examples, Include and Plugins must be spelled that way.
The build runs as if the repository were the NSIS source tree: before compiling, the action puts the plugin API at Contrib/ExDLL/ (pluginapi.h, pluginapi.c, nsis_tchar.h, nsis.pas) and Source/exehead/api.h, so includes like "../ExDLL/pluginapi.h" resolve. Don't commit those folders; the layout check fails if you do. #include "pluginapi.h" and <nsis/pluginapi.h> work too.
| Name | Default | Description |
|---|---|---|
name |
Package name; for a Plugin, the DLL basename scripts call as Name::Func. |
|
sources |
msvc, mingw and zig only: C/C++ source globs (.c, .cpp, .cxx, .cc, .rc). |
|
project |
fpc and rust only: path of the Pascal project file (.dpr, .lpr, .pas) or the plugin's Cargo.toml. |
|
targets |
x86-ansi,x86-unicode,amd64-unicode |
Any of x86-ansi, x86-unicode, amd64-unicode, arm64-unicode. arm64-unicode is opt-in. |
toolchain |
from sources/project |
msvc, fpc or rust on a Windows runner, mingw on a Linux runner, zig on any runner, none to build nothing, prebuilt to ship the committed DLLs (an antipattern, see above). Left out, it is none without sources or project, msvc with sources, rust for a Cargo.toml project and fpc otherwise. none ships Include/, Examples/, Docs/ and Contrib/: headers, graphics, language files. prebuilt ignores targets and ships every committed one. |
crt |
static |
C/C++ only: static links the C runtime in; none builds without it, entry point DllMain. |
strict |
true |
Fail on every layout violation. false is for legacy packages: a missing LICENSE is only a warning, and the installer has no license page. |
release |
true |
Attach the files to the release that triggered the run. |
attestations |
false |
Attest build provenance, needs the id-token and attestations write permissions. Free on public repositories; a private one needs GitHub Team or Enterprise. |
Each Toolchain takes exactly one of sources and project; the action fails before building if the other one is set.
List inputs are comma or newline separated, so paths may contain spaces.
There are deliberately no inputs for defines, libraries, include directories or raw flags:
- Every directory holding a matched source is an include directory.
- A fixed list of common Windows import libraries is linked; unused ones add no imports. If a Plugin needs one that's missing, open an issue.
- Put defines in a header.
- The NSIS version of the Plugin API, the Free Pascal version and the Zig version are pinned, and only change in a release release.
| Name | Description |
|---|---|
plugins-dir |
Directory containing Plugins/<target>/<name>.dll. |
archive |
Path of the Release Archive. |
installer |
Path of the Package Installer. |
Include the Plugin API the way NSIS installs it:
#include <windows.h>
#include <nsis/pluginapi.h>pluginapi.c is compiled in. UNICODE and _UNICODE are defined for *-unicode targets. A C++ plugin needs extern "C" on its exported functions.
Note
arm64-unicode builds with msvc and zig; Ubuntu's MinGW has no arm64 compiler.
Official NSIS releases only ship x86 stubs. amd64 plugins work with forks such as
negrutiu/nsis.
The C/C++ Toolchains differ in which C runtime a crt: static Plugin depends on:
| Toolchain | C runtime |
|---|---|
msvc |
Linked into the DLL. |
mingw |
Imported from msvcrt.dll, which every Windows has. |
zig |
Imported from the Universal CRT, built into Windows 10 and later; Vista to 8.1 need update KB2999226. |
With crt: none no Toolchain imports a C runtime. zig then leaves uuid out of the linked libraries, so a Plugin using COM GUIDs such as IID_IUnknown defines them itself with INITGUID.
Use the unit NSIS ships:
library Hello;
uses
nsis, Windows;
procedure Greet(const hwndParent: HWND; const string_size: integer; const variables: NSISPTChar; const stacktop: pointer); cdecl;
begin
Init(hwndParent, string_size, variables, stacktop);
PushString('Hello, ' + PopString() + '!');
end;
exports
Greet;
begin
end.Code compiles in Delphi mode (-Mdelphi) unless the source sets {$mode}. UNICODE is defined for *-unicode targets, which switches nsis.pas to wide strings. The official Free Pascal installer is downloaded from SourceForge, checked against the MD5 listed there, and cached.
Note
Free Pascal is not Delphi. Plugins that use the VCL (Graphics), Delphi-only
units such as WinSvc, or assembler calling into Delphi's RTL need porting.
Free Pascal 3.2 has no arm64 Windows target.
Keep the crate in Contrib/<name>/, or put a workspace Cargo.toml at the top level with Contrib/<name> as a member, and point project at the Plugin's own Cargo.toml. The action builds it with Cargo for the *-pc-windows-msvc targets and relies on two things only:
[lib] crate-type = ["cdylib"]. The DLL is renamed to<name>.dll, so the crate name doesn't have to match.*-unicodetargets build the crate's default features.x86-ansibuilds with--no-default-features --features ansi, so a crate that buildsx86-ansineeds anansifeature that switches it to ANSI strings.
Export functions as #[unsafe(no_mangle)] pub extern "C" fn. No Plugin API is supplied; bring a crate for it or write the stack handling yourself. A rust-toolchain.toml next to the manifest picks the Rust version, otherwise the runner's stable Rust is used. Size settings such as opt-level, lto and panic = "abort" belong in the crate's [profile.release].
mise installs the tools (Python, ruff, actionlint, hk) and the hk pre-commit hook:
mise install
mise run check # lint, formatting, script self-check
mise run fix # apply lint and formatting fixesaction.yml runs stdlib-only Python scripts from scripts/, which also run locally:
NAME=HelloC SOURCES='test/hello/Contrib/HelloC/*.c' TOOLCHAIN=mingw python3 scripts/build_c.py buildRelease NSIS package is not certified by GitHub. It is provided by a third-party and is governed by separate terms of service, privacy policy, and support documentation.