Skip to content

Commit 4373ecb

Browse files
bordeuxclaude
andcommitted
feat: add Nix, MSI, and DMG packaging support
- Add flake.nix for NixOS/Nix users with multi-arch support - Add WiX configuration for Windows MSI installer (per-user install) - Add create-dmg.sh script for macOS DMG with drag-to-install - Update release workflow to build and upload MSI and DMG artifacts - Add cargo-make tasks: build-nix, build-msi, build-dmg, build-deb, build-rpm 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent b568d29 commit 4373ecb

7 files changed

Lines changed: 394 additions & 0 deletions

File tree

.github/workflows/release.yml

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,16 +100,19 @@ jobs:
100100
os: macos-latest
101101
artifact_name: tmpltool
102102
asset_name: tmpltool-macos-x86_64
103+
dmg_arch: x86_64
103104

104105
- target: aarch64-apple-darwin
105106
os: macos-latest
106107
artifact_name: tmpltool
107108
asset_name: tmpltool-macos-aarch64
109+
dmg_arch: aarch64
108110

109111
- target: x86_64-pc-windows-msvc
110112
os: windows-latest
111113
artifact_name: tmpltool.exe
112114
asset_name: tmpltool-windows-x86_64.exe
115+
msi_arch: x64
113116

114117
steps:
115118
- name: Checkout code
@@ -176,6 +179,68 @@ jobs:
176179
cd target/${{ matrix.target }}/release
177180
Compress-Archive -Path ${{ matrix.artifact_name }} -DestinationPath ../../../${{ matrix.asset_name }}.zip
178181
182+
- name: Build DMG package (macOS)
183+
if: matrix.dmg_arch != ''
184+
run: |
185+
VERSION=${{ needs.semantic-release.outputs.new_release_version }}
186+
ARCH=${{ matrix.dmg_arch }}
187+
DMG_NAME="tmpltool-${VERSION}-darwin-${ARCH}.dmg"
188+
189+
# Create staging directory
190+
STAGING_DIR=$(mktemp -d)
191+
192+
# Copy binary
193+
cp target/${{ matrix.target }}/release/tmpltool "${STAGING_DIR}/tmpltool"
194+
chmod +x "${STAGING_DIR}/tmpltool"
195+
196+
# Copy docs
197+
cp README.md "${STAGING_DIR}/"
198+
cp LICENSE "${STAGING_DIR}/"
199+
200+
# Create Applications symlink
201+
ln -s /Applications "${STAGING_DIR}/Applications"
202+
203+
# Create DMG
204+
hdiutil create \
205+
-volname "tmpltool ${VERSION}" \
206+
-srcfolder "$STAGING_DIR" \
207+
-ov \
208+
-format UDZO \
209+
"$DMG_NAME"
210+
211+
# Cleanup
212+
rm -rf "$STAGING_DIR"
213+
214+
echo "Created ${DMG_NAME}"
215+
216+
- name: Upload DMG artifact
217+
if: matrix.dmg_arch != ''
218+
uses: actions/upload-artifact@v4
219+
with:
220+
name: tmpltool-dmg-${{ matrix.dmg_arch }}
221+
path: tmpltool-${{ needs.semantic-release.outputs.new_release_version }}-darwin-${{ matrix.dmg_arch }}.dmg
222+
223+
- name: Install cargo-wix (Windows MSI)
224+
if: matrix.msi_arch != ''
225+
shell: pwsh
226+
run: cargo install cargo-wix
227+
228+
- name: Build MSI package (Windows)
229+
if: matrix.msi_arch != ''
230+
shell: pwsh
231+
run: |
232+
cargo wix --no-build --nocapture
233+
$version = "${{ needs.semantic-release.outputs.new_release_version }}"
234+
$msiFile = Get-ChildItem -Path "target/wix" -Filter "*.msi" | Select-Object -First 1
235+
Copy-Item $msiFile.FullName -Destination "tmpltool-$version-windows-x86_64.msi"
236+
237+
- name: Upload MSI artifact
238+
if: matrix.msi_arch != ''
239+
uses: actions/upload-artifact@v4
240+
with:
241+
name: tmpltool-msi-${{ matrix.msi_arch }}
242+
path: tmpltool-${{ needs.semantic-release.outputs.new_release_version }}-windows-x86_64.msi
243+
179244
- name: Install cargo-deb (Linux DEB)
180245
if: matrix.deb_arch != ''
181246
run: cargo install cargo-deb

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,9 @@ assets = [
6464
{ source = "README.md", dest = "/usr/share/doc/tmpltool/README.md", mode = "0644" },
6565
{ source = "LICENSE", dest = "/usr/share/doc/tmpltool/LICENSE", mode = "0644" },
6666
]
67+
68+
[package.metadata.wix]
69+
upgrade-guid = "E8B9F4A2-3C5D-4E6F-8A1B-2C3D4E5F6A7B"
70+
path-guid = "A1B2C3D4-E5F6-7890-ABCD-EF1234567890"
71+
license = false
72+
eula = false

Makefile.toml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,44 @@ dependencies = ["clean", "format", "clippy", "test", "build-release"]
201201
description = "Full build and test suite"
202202
dependencies = ["clean", "format", "clippy", "test", "build-release", "docs-build"]
203203

204+
# ========================================
205+
# Package Building Tasks
206+
# ========================================
207+
208+
[tasks.build-nix]
209+
description = "Build with Nix flake"
210+
command = "nix"
211+
args = ["build"]
212+
213+
[tasks.build-msi]
214+
description = "Build Windows MSI installer (requires WiX and cargo-wix)"
215+
install_crate = "cargo-wix"
216+
command = "cargo"
217+
args = ["wix", "--nocapture"]
218+
219+
[tasks.build-dmg]
220+
description = "Build macOS DMG installer"
221+
dependencies = ["build-release"]
222+
script_runner = "@shell"
223+
script = '''
224+
./scripts/create-dmg.sh
225+
'''
226+
227+
[tasks.build-deb]
228+
description = "Build Debian package"
229+
install_crate = "cargo-deb"
230+
command = "cargo"
231+
args = ["deb"]
232+
233+
[tasks.build-rpm]
234+
description = "Build RPM package"
235+
install_crate = "cargo-generate-rpm"
236+
script_runner = "@shell"
237+
script = '''
238+
cargo build --release
239+
cargo generate-rpm
240+
'''
241+
204242
# ========================================
205243
# Example Testing Tasks
206244
# ========================================
@@ -255,6 +293,13 @@ echo " cargo make build-macos-aarch64 - macOS Apple Silicon"
255293
echo " cargo make build-windows-x86_64 - Windows x86_64"
256294
echo " cargo make build-all-platforms - All platforms"
257295
echo ""
296+
echo "Package Building:"
297+
echo " cargo make build-deb - Build Debian package"
298+
echo " cargo make build-rpm - Build RPM package"
299+
echo " cargo make build-msi - Build Windows MSI"
300+
echo " cargo make build-dmg - Build macOS DMG"
301+
echo " cargo make build-nix - Build with Nix flake"
302+
echo ""
258303
echo "Utilities:"
259304
echo " cargo make clean - Clean build artifacts"
260305
echo " cargo make docs - Generate & open docs"

flake.nix

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{
2+
description = "A fast and simple command-line template rendering tool using MiniJinja templates";
3+
4+
inputs = {
5+
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
6+
flake-utils.url = "github:numtide/flake-utils";
7+
};
8+
9+
outputs = { self, nixpkgs, flake-utils }:
10+
flake-utils.lib.eachDefaultSystem (system:
11+
let
12+
pkgs = nixpkgs.legacyPackages.${system};
13+
14+
rustPlatform = pkgs.rustPlatform;
15+
16+
tmpltool = rustPlatform.buildRustPackage {
17+
pname = "tmpltool";
18+
version = "1.2.5";
19+
20+
src = ./.;
21+
22+
cargoLock = {
23+
lockFile = ./Cargo.lock;
24+
};
25+
26+
meta = with pkgs.lib; {
27+
description = "A fast and simple command-line template rendering tool using MiniJinja templates with environment variables";
28+
homepage = "https://github.com/bordeux/tmpltool";
29+
license = licenses.mit;
30+
maintainers = [ ];
31+
mainProgram = "tmpltool";
32+
};
33+
};
34+
in
35+
{
36+
packages = {
37+
default = tmpltool;
38+
tmpltool = tmpltool;
39+
};
40+
41+
apps = {
42+
default = flake-utils.lib.mkApp {
43+
drv = tmpltool;
44+
};
45+
};
46+
47+
devShells.default = pkgs.mkShell {
48+
buildInputs = with pkgs; [
49+
rustc
50+
cargo
51+
rust-analyzer
52+
clippy
53+
rustfmt
54+
cargo-make
55+
];
56+
57+
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
58+
};
59+
}
60+
);
61+
}

scripts/create-dmg.sh

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#!/bin/bash
2+
#
3+
# Create a macOS DMG installer for tmpltool
4+
#
5+
# Usage: ./scripts/create-dmg.sh [binary_path] [output_path]
6+
#
7+
# Arguments:
8+
# binary_path - Path to the tmpltool binary (default: target/release/tmpltool)
9+
# output_path - Output DMG path (default: tmpltool-VERSION-ARCH.dmg)
10+
#
11+
# This script creates a drag-to-install DMG with:
12+
# - tmpltool binary
13+
# - Applications folder symlink
14+
# - README.md
15+
# - LICENSE
16+
#
17+
18+
set -euo pipefail
19+
20+
# Configuration
21+
APP_NAME="tmpltool"
22+
VOLUME_NAME="tmpltool Installer"
23+
24+
# Get version from Cargo.toml
25+
VERSION=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
26+
27+
# Detect architecture
28+
ARCH=$(uname -m)
29+
case "$ARCH" in
30+
x86_64)
31+
ARCH_NAME="x86_64"
32+
;;
33+
arm64|aarch64)
34+
ARCH_NAME="aarch64"
35+
;;
36+
*)
37+
ARCH_NAME="$ARCH"
38+
;;
39+
esac
40+
41+
# Arguments with defaults
42+
BINARY_PATH="${1:-target/release/tmpltool}"
43+
OUTPUT_PATH="${2:-${APP_NAME}-${VERSION}-darwin-${ARCH_NAME}.dmg}"
44+
45+
# Temporary staging directory
46+
STAGING_DIR=$(mktemp -d)
47+
trap "rm -rf '$STAGING_DIR'" EXIT
48+
49+
echo "Creating DMG for ${APP_NAME} v${VERSION} (${ARCH_NAME})..."
50+
echo " Binary: ${BINARY_PATH}"
51+
echo " Output: ${OUTPUT_PATH}"
52+
53+
# Verify binary exists
54+
if [[ ! -f "$BINARY_PATH" ]]; then
55+
echo "Error: Binary not found at ${BINARY_PATH}"
56+
echo "Run 'cargo build --release' first."
57+
exit 1
58+
fi
59+
60+
# Create staging directory structure
61+
echo "Staging files..."
62+
cp "$BINARY_PATH" "${STAGING_DIR}/${APP_NAME}"
63+
chmod +x "${STAGING_DIR}/${APP_NAME}"
64+
65+
# Copy documentation
66+
if [[ -f "README.md" ]]; then
67+
cp "README.md" "${STAGING_DIR}/"
68+
fi
69+
if [[ -f "LICENSE" ]]; then
70+
cp "LICENSE" "${STAGING_DIR}/"
71+
fi
72+
73+
# Create Applications symlink for drag-to-install
74+
# Note: For CLI tools, this symlink is to /usr/local/bin conceptually,
75+
# but users typically copy the binary manually. The symlink provides
76+
# a familiar drag-to-install experience.
77+
ln -s /Applications "${STAGING_DIR}/Applications"
78+
79+
# Create the DMG
80+
echo "Creating DMG..."
81+
82+
# Remove existing DMG if present
83+
rm -f "$OUTPUT_PATH"
84+
85+
# Create DMG using hdiutil
86+
hdiutil create \
87+
-volname "$VOLUME_NAME" \
88+
-srcfolder "$STAGING_DIR" \
89+
-ov \
90+
-format UDZO \
91+
"$OUTPUT_PATH"
92+
93+
echo "DMG created successfully: ${OUTPUT_PATH}"
94+
95+
# Show DMG info
96+
echo ""
97+
echo "DMG contents:"
98+
hdiutil attach -nobrowse "$OUTPUT_PATH" -mountpoint /tmp/tmpltool-dmg-verify 2>/dev/null || true
99+
ls -la /tmp/tmpltool-dmg-verify 2>/dev/null || true
100+
hdiutil detach /tmp/tmpltool-dmg-verify 2>/dev/null || true
101+
102+
echo ""
103+
echo "To install, users should:"
104+
echo " 1. Open the DMG"
105+
echo " 2. Copy 'tmpltool' to /usr/local/bin or add to PATH"
106+
echo " 3. Run: chmod +x /usr/local/bin/tmpltool"

wix/License.rtf

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{\rtf1\ansi\deff0
2+
{\fonttbl{\f0\fnil\fcharset0 Courier New;}}
3+
\viewkind4\uc1\pard\f0\fs20
4+
MIT License\par
5+
\par
6+
Copyright (c) 2024 tmpltool contributors\par
7+
\par
8+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\par
9+
\par
10+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\par
11+
\par
12+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\par
13+
}

0 commit comments

Comments
 (0)