Packaging: Igneum Miner for Mac and Windows, one-tap installs with the coin on every file and window
Mac: bundle renamed Igneum Miner.app (network.igneum.miner, 0.1.0, LSMinimumSystemVersion 11.0, igneum.icns), Terminal title Igneum Miner, seed line in the first-run text, branded DMG (volume icon, background, icon slots via dmgbuild), dist/Igneum-Miner-0.1.0.dmg at 16.2 MB, tested synced against the live node on 27400/27401. Windows: packaging/windows with windres .rc files and embed-resources.sh (relink commands for the next cross-build), Igneum-Miner.iss (Program Files, Start Menu group, firewall rule, uninstall stops the processes, licence, seed line), BUILD-INSTALLER.bat and build-installer.ps1 for the project lead's PC (winget Inno Setup 6, rcedit fallback for the exe icon). Icons: brand/icons/make-icons.py makes igneum.icns, igneum.ico, the Inno wizard art and the DMG background. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
BIN
brand/icons/dmg-background.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
brand/icons/dmg-background.tiff
Normal file
BIN
brand/icons/dmg-background@2x.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
brand/icons/igneum-256.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
brand/icons/igneum.icns
Normal file
BIN
brand/icons/igneum.ico
Normal file
|
After Width: | Height: | Size: 353 KiB |
BIN
brand/icons/inno-banner-150x57.bmp
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
brand/icons/inno-wizard-164x314.bmp
Normal file
|
After Width: | Height: | Size: 151 KiB |
BIN
brand/icons/inno-wizard-small-55x58.bmp
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
179
brand/icons/make-icons.py
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Builds the Igneum Miner icon and installer art from brand/igneum-coin-1024.png (3 October 2026).
|
||||
|
||||
Outputs, all in brand/icons/:
|
||||
igneum.icns Mac app and DMG volume icon (iconutil from the full 10-file iconset)
|
||||
igneum.ico Windows icon, 16 24 32 48 64 128 256 px, uncompressed BMP entries
|
||||
igneum-256.png the coin cut out on transparency (the source of both)
|
||||
inno-wizard-164x314.bmp Inno Setup WizardImageFile (left side of the welcome and finish pages)
|
||||
inno-wizard-small-55x58.bmp Inno Setup WizardSmallImageFile (top right of the other pages)
|
||||
inno-banner-150x57.bmp header banner (NSIS size; kept for the Windows README and any header-style page)
|
||||
dmg-background.png 660x400 DMG window background
|
||||
dmg-background@2x.png the same at 1320x800 for Retina; dmg-background.tiff holds both when tiffutil is present
|
||||
Needs Pillow and, on a Mac, iconutil and tiffutil (both ship with macOS).
|
||||
python3 brand/icons/make-icons.py
|
||||
"""
|
||||
import os, subprocess, sys, shutil, tempfile
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
BRAND = os.path.dirname(HERE)
|
||||
COIN = os.path.join(BRAND, 'igneum-coin-1024.png')
|
||||
FONT = os.path.join(BRAND, 'Unbounded.ttf')
|
||||
OBSIDIAN = (12, 12, 14)
|
||||
EMBER = (242, 84, 27)
|
||||
MOLTEN = (255, 179, 92)
|
||||
BONE = (244, 241, 236)
|
||||
RADIUS = 480.0 # the ember ring's outer edge, measured on the 1024 px coin
|
||||
CENTRE = (511.5, 511.5)
|
||||
|
||||
def font(size, weight='SemiBold'):
|
||||
f = ImageFont.truetype(FONT, size)
|
||||
try:
|
||||
f.set_variation_by_name(weight)
|
||||
except Exception:
|
||||
pass
|
||||
return f
|
||||
|
||||
def coin_rgba():
|
||||
"""The coin on transparency: a 4x supersampled circular mask at the ring's outer edge."""
|
||||
im = Image.open(COIN).convert('RGBA')
|
||||
big = Image.new('L', (4096, 4096), 0)
|
||||
d = ImageDraw.Draw(big)
|
||||
cx, cy, r = CENTRE[0] * 4 + 2, CENTRE[1] * 4 + 2, (RADIUS - 0.5) * 4
|
||||
d.ellipse((cx - r, cy - r, cx + r, cy + r), fill=255)
|
||||
mask = big.resize((1024, 1024), Image.LANCZOS)
|
||||
im.putalpha(mask)
|
||||
return im
|
||||
|
||||
def fit(im, size):
|
||||
return im.resize((size, size), Image.LANCZOS)
|
||||
|
||||
def make_icns(coin):
|
||||
tmp = tempfile.mkdtemp()
|
||||
iconset = os.path.join(tmp, 'igneum.iconset')
|
||||
os.mkdir(iconset)
|
||||
for s in (16, 32, 128, 256, 512):
|
||||
fit(coin, s).save(os.path.join(iconset, f'icon_{s}x{s}.png'))
|
||||
fit(coin, s * 2).save(os.path.join(iconset, f'icon_{s}x{s}@2x.png'))
|
||||
out = os.path.join(HERE, 'igneum.icns')
|
||||
subprocess.run(['iconutil', '-c', 'icns', iconset, '-o', out], check=True)
|
||||
shutil.rmtree(tmp)
|
||||
return out
|
||||
|
||||
def make_ico(coin):
|
||||
out = os.path.join(HERE, 'igneum.ico')
|
||||
sizes = [(s, s) for s in (16, 24, 32, 48, 64, 128, 256)]
|
||||
base = fit(coin, 256)
|
||||
try:
|
||||
base.save(out, format='ICO', sizes=sizes, bitmap_format='bmp')
|
||||
except TypeError:
|
||||
base.save(out, format='ICO', sizes=sizes)
|
||||
return out
|
||||
|
||||
def wordmark(text, size, colour=BONE, weight='SemiBold'):
|
||||
f = font(size, weight)
|
||||
l, t, r, b = f.getbbox(text)
|
||||
im = Image.new('RGBA', (r - l + 4, b - t + 4), (0, 0, 0, 0))
|
||||
ImageDraw.Draw(im).text((2 - l, 2 - t), text, font=f, fill=colour)
|
||||
return im
|
||||
|
||||
def paste_centre(dst, src, cx, cy):
|
||||
dst.alpha_composite(src, (int(cx - src.width / 2), int(cy - src.height / 2)))
|
||||
|
||||
def bmp(im, path):
|
||||
im.convert('RGB').save(path, format='BMP')
|
||||
|
||||
def make_inno(coin):
|
||||
# 164x314: the coin above a two-line wordmark
|
||||
big = Image.new('RGBA', (164, 314), OBSIDIAN + (255,))
|
||||
paste_centre(big, fit(coin, 112), 82, 110)
|
||||
paste_centre(big, wordmark('IGNEUM', 19, BONE, 'Bold'), 82, 205)
|
||||
paste_centre(big, wordmark('MINER', 13, MOLTEN, 'Medium'), 82, 232)
|
||||
bmp(big, os.path.join(HERE, 'inno-wizard-164x314.bmp'))
|
||||
# 55x58: the coin alone
|
||||
small = Image.new('RGBA', (55, 58), OBSIDIAN + (255,))
|
||||
paste_centre(small, fit(coin, 50), 27.5, 29)
|
||||
bmp(small, os.path.join(HERE, 'inno-wizard-small-55x58.bmp'))
|
||||
# 150x57: coin left, wordmark right
|
||||
banner = Image.new('RGBA', (150, 57), OBSIDIAN + (255,))
|
||||
paste_centre(banner, fit(coin, 44), 28, 28.5)
|
||||
w1 = wordmark('IGNEUM', 15, BONE, 'Bold')
|
||||
w2 = wordmark('MINER', 10, MOLTEN, 'Medium')
|
||||
banner.alpha_composite(w1, (56, 14))
|
||||
banner.alpha_composite(w2, (57, 34))
|
||||
bmp(banner, os.path.join(HERE, 'inno-banner-150x57.bmp'))
|
||||
|
||||
def arrow(draw, x0, x1, y, colour, width):
|
||||
draw.line((x0, y, x1 - width * 2, y), fill=colour, width=width)
|
||||
h = width * 3
|
||||
draw.polygon([(x1, y), (x1 - h * 1.6, y - h), (x1 - h * 1.6, y + h)], fill=colour)
|
||||
|
||||
# Finder icon slots on the DMG (1x pixels, icon size 96): the app at APP_SLOT, the Applications link at APPS_SLOT,
|
||||
# README.txt and the Stop command in the row below. build-dmg.sh uses the same numbers.
|
||||
ICON = 96
|
||||
APP_SLOT = (165, 130)
|
||||
APPS_SLOT = (495, 130)
|
||||
ROW2_Y = 330
|
||||
|
||||
def dashed_box(im, cx, cy, r, colour, width=3, dash=16, gap=10, radius=18):
|
||||
"""A dashed rounded square: the outline drawn once, then masked by diagonal bars."""
|
||||
W, H = im.size
|
||||
layer = Image.new('RGBA', (W, H), (0, 0, 0, 0))
|
||||
ImageDraw.Draw(layer).rounded_rectangle((cx - r, cy - r, cx + r, cy + r), radius=radius, outline=colour, width=width)
|
||||
bars = Image.new('L', (W, H), 0)
|
||||
bd = ImageDraw.Draw(bars)
|
||||
k = -H
|
||||
while k < W + H:
|
||||
bd.line((k, 0, k + H, H), fill=255, width=dash)
|
||||
k += dash + gap
|
||||
alpha = layer.split()[3]
|
||||
alpha = Image.composite(alpha, Image.new('L', (W, H), 0), bars)
|
||||
layer.putalpha(alpha)
|
||||
im.alpha_composite(layer)
|
||||
|
||||
def make_dmg_background(coin):
|
||||
"""660x400 at 1x; drawn at 2x and downsampled. The coin is painted under the app slot, so the slot reads as the
|
||||
product even before Finder draws the app icon on top of it."""
|
||||
S = 2
|
||||
W, H = 660 * S, 400 * S
|
||||
im = Image.new('RGBA', (W, H), OBSIDIAN + (255,))
|
||||
d = ImageDraw.Draw(im)
|
||||
ax, ay = APP_SLOT[0] * S, APP_SLOT[1] * S
|
||||
bx, by = APPS_SLOT[0] * S, APPS_SLOT[1] * S
|
||||
glow = Image.new('RGBA', (W, H), (0, 0, 0, 0))
|
||||
gd = ImageDraw.Draw(glow)
|
||||
for i, a in ((76 * S, 10), (66 * S, 12), (58 * S, 14)):
|
||||
gd.ellipse((ax - i, ay - i, ax + i, ay + i), fill=EMBER + (a,))
|
||||
im.alpha_composite(glow)
|
||||
paste_centre(im, fit(coin, ICON * S), ax, ay)
|
||||
dashed_box(im, bx, by, (ICON // 2 + 14) * S, MOLTEN + (190,), width=2 * S, dash=9 * S, gap=6 * S, radius=9 * S)
|
||||
arrow(d, (APP_SLOT[0] + 78) * S, (APPS_SLOT[0] - 78) * S, ay, EMBER + (255,), 4 * S)
|
||||
paste_centre(im, wordmark('Drag Igneum Miner to Applications', 16 * S, BONE, 'Medium'), 330 * S, 236 * S)
|
||||
paste_centre(im, wordmark('Then open it from Applications. Nobody from Igneum will ever ask for your seed.', 8 * S, (150, 148, 145), 'Regular'), 330 * S, 262 * S)
|
||||
im2x = im.convert('RGB')
|
||||
im2x.save(os.path.join(HERE, 'dmg-background@2x.png'))
|
||||
im1x = im2x.resize((660, 400), Image.LANCZOS)
|
||||
im1x.save(os.path.join(HERE, 'dmg-background.png'))
|
||||
tiff = os.path.join(HERE, 'dmg-background.tiff')
|
||||
if shutil.which('tiffutil'):
|
||||
subprocess.run(['tiffutil', '-cathidpicheck', os.path.join(HERE, 'dmg-background.png'),
|
||||
os.path.join(HERE, 'dmg-background@2x.png'), '-out', tiff], check=True)
|
||||
|
||||
def main():
|
||||
coin = coin_rgba()
|
||||
fit(coin, 256).save(os.path.join(HERE, 'igneum-256.png'))
|
||||
if sys.platform == 'darwin' and shutil.which('iconutil'):
|
||||
print('icns:', make_icns(coin))
|
||||
else:
|
||||
print('icns skipped: iconutil is a macOS tool')
|
||||
print('ico:', make_ico(coin))
|
||||
make_inno(coin)
|
||||
make_dmg_background(coin)
|
||||
for f in sorted(os.listdir(HERE)):
|
||||
p = os.path.join(HERE, f)
|
||||
if os.path.isfile(p) and not f.endswith('.py'):
|
||||
print(f'{os.path.getsize(p):>8} {f}')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,38 +1,44 @@
|
|||
# Igneum Devnet for Mac (packaging/mac)
|
||||
# Igneum Miner for Mac (packaging/mac)
|
||||
|
||||
A DMG a friend can open on an Apple silicon Mac to run a devnet node and the Metal miner, and show up on
|
||||
igneum.network/live. Built 3 October 2026 from the binaries already compiled in this repo; nothing is compiled here.
|
||||
A DMG a miner can open on an Apple silicon Mac to run a node and the Metal miner, and show up on igneum.network/live.
|
||||
Built 3 October 2026 from the binaries already compiled in this repo; nothing is compiled here. Renamed from "Igneum
|
||||
Devnet" to "Igneum Miner" the same evening (bundle id network.igneum.miner, version 0.1.0); the devnet is still the only
|
||||
network and its name appears in the status lines, never in a window title.
|
||||
|
||||
## Build
|
||||
|
||||
packaging/mac/build-dmg.sh
|
||||
|
||||
Output: `packaging/mac/dist/igneum-devnet-mac.dmg` (about 17 MB, lzfse). The script takes
|
||||
Output: `packaging/mac/dist/Igneum-Miner-0.1.0.dmg` (about 16 MB, lzfse). The script takes
|
||||
`vendor/igneum-node/target-rename/release/igneumd` (falls back to `target/release/kaspad` renamed, and says so),
|
||||
`vendor/igneum-node/target/release/igneum-miner` and `proto-metal/igneum-bench` (the plain build, the one whose
|
||||
`--serve` protocol matches that miner), copies them into the bundle, strips the copies and signs them ad hoc again
|
||||
(strip invalidates the linker signature and arm64 macOS refuses an unsigned binary), and checks that each copy still
|
||||
runs. The originals are untouched. The icon comes from `site/icon-512.png`. `build/` and `dist/` are ignored by git.
|
||||
runs. The originals are untouched. The icon is `brand/icons/igneum.icns` and the DMG art `brand/icons/dmg-background.tiff`
|
||||
(both from `python3 brand/icons/make-icons.py`). The image is laid out by `dmgbuild` (`pip3 install dmgbuild`, settings in
|
||||
`dmg/settings.py`: volume "Igneum Miner", hidden `.VolumeIcon.icns` with the custom-icon flag, background, icon slots app
|
||||
(165,130), Applications (495,130), README and Stop command on the row below, no Finder scripting needed). Without dmgbuild
|
||||
the script builds a plain hdiutil image and says so. `build/` and `dist/` are ignored by git.
|
||||
|
||||
## What is on the DMG
|
||||
|
||||
| Item | What it is |
|
||||
|---|---|
|
||||
| `Igneum Devnet.app` | the launcher bundle (below) |
|
||||
| `README.txt` | 9 lines for the friend (`dmg/README.txt`) |
|
||||
| `Stop Igneum.command` | stops a copy whose window was closed without Ctrl+C (`app/Stop Igneum.command`) |
|
||||
| `Igneum Miner.app` | the launcher bundle (below) |
|
||||
| `README.txt` | 10 lines for the miner (`dmg/README.txt`), with the seed line |
|
||||
| `Stop Igneum Miner.command` | stops a copy whose window was closed without Ctrl+C (`app/Stop Igneum Miner.command`) |
|
||||
| `Applications` | symlink, for the drag |
|
||||
|
||||
## The app bundle
|
||||
|
||||
Igneum Devnet.app/Contents/MacOS/Igneum Devnet app/launcher.sh: removes quarantine from Resources, opens the script in Terminal
|
||||
Igneum Devnet.app/Contents/Resources/igneum-devnet.sh the run script (app/igneum-devnet.sh)
|
||||
Igneum Devnet.app/Contents/Resources/bin/igneumd, igneum-miner, igneum-bench
|
||||
Igneum Devnet.app/Contents/Resources/AppIcon.icns
|
||||
Igneum Devnet.app/Contents/Info.plist app/Info.plist (CFBundleVersion = build stamp)
|
||||
Igneum Miner.app/Contents/MacOS/Igneum Miner app/launcher.sh: removes quarantine from Resources, opens the script in Terminal
|
||||
Igneum Miner.app/Contents/Resources/igneum-miner.sh the run script (app/igneum-miner.sh); sets the Terminal title "Igneum Miner"
|
||||
Igneum Miner.app/Contents/Resources/bin/igneumd, igneum-miner, igneum-bench
|
||||
Igneum Miner.app/Contents/Resources/igneum.icns
|
||||
Igneum Miner.app/Contents/Info.plist app/Info.plist (CFBundleVersion = build stamp, LSMinimumSystemVersion 11.0)
|
||||
|
||||
Why a shell launcher and not an AppleScript applet: `open -a Terminal <script>` needs no Automation consent;
|
||||
`tell application "Terminal" to do script` would prompt "Igneum Devnet wants to control Terminal" on first run.
|
||||
`tell application "Terminal" to do script` would prompt "Igneum Miner wants to control Terminal" on first run.
|
||||
The launcher strips `com.apple.quarantine` from its own Resources first, because after Gatekeeper lets the app
|
||||
through, every binary inside would still be checked on its first exec and refused as unidentified. That only works on
|
||||
a writable volume, so the app refuses (with a dialog) to run straight from the DMG.
|
||||
|
|
@ -59,17 +65,17 @@ is synced again, miner exit 42 (hourly program change) restarted at once, other
|
|||
Stopping: Ctrl+C (SIGINT), the window closing (SIGHUP) or SIGTERM set a flag; the loop then stops the miners and their
|
||||
workers (TERM, 8 s, KILL), the node (TERM, 30 s, KILL), caffeinate, uploads the logs once more and prints a summary.
|
||||
Children are started with SIGHUP ignored (bash already makes background children of a script ignore SIGINT), so the
|
||||
launcher controls the order. `igneum-devnet.sh --stop` and `Stop Igneum.command` signal the launcher from the pid file
|
||||
and fall back to `pkill -f 'Igneum Devnet.app/Contents/Resources/bin/'`.
|
||||
launcher controls the order. `igneum-miner.sh --stop` and `Stop Igneum Miner.command` signal the launcher from the pid file
|
||||
and fall back to `pkill -f 'Igneum Miner.app/Contents/Resources/bin/'` (the bundle path only, never a bare name).
|
||||
|
||||
The miner identity shows on igneum.network/live as the first 8 hex of its vote key hash; the window prints it.
|
||||
|
||||
## Gatekeeper (expected on macOS 15 and 26)
|
||||
|
||||
The app is unsigned and not notarized. Double-click gives "Apple could not verify ... is free of malware" with no
|
||||
Open button. Right-click > Open offers Open on older systems; on macOS 15 and later the route is System Settings >
|
||||
Open button (unsigned and not notarized until the foundation has an Apple developer account). Right-click > Open offers Open on older systems; on macOS 15 and later the route is System Settings >
|
||||
Privacy & Security > "Open Anyway" (the button appears after the first refusal), then open the app again. Both are in
|
||||
README.txt. Removing the flag by hand also works: `xattr -dr com.apple.quarantine "/Applications/Igneum Devnet.app"`.
|
||||
README.txt. Removing the flag by hand also works: `xattr -dr com.apple.quarantine "/Applications/Igneum Miner.app"`.
|
||||
The first time igneumd listens on 0.0.0.0:26611 the macOS firewall (if on) asks to allow incoming connections; Allow.
|
||||
|
||||
## Test (3 October 2026, this Mac, macOS 26.6.2, M5 Max)
|
||||
|
|
@ -94,3 +100,15 @@ Two things the test caught and fixed: an unquoted `$NODE_EXE` in the version lin
|
|||
`uname -m` as the Apple silicon check (an x86_64 process, Rosetta, reports x86_64; now `sysctl hw.optional.arm64`).
|
||||
Note for harness tests: a script started as a background job of a non-interactive shell has SIGINT ignored at entry
|
||||
and cannot trap it; run it in the foreground or through `perl -e '$SIG{INT}="DEFAULT"; exec ...'`.
|
||||
|
||||
## Test of the renamed bundle (3 October 2026, 23:20, this Mac)
|
||||
|
||||
Mounted `Igneum-Miner-0.1.0.dmg` (16,994,318 bytes): volume "Igneum Miner", `.VolumeIcon.icns` present and the custom-icon
|
||||
flag set on the root (`xattr -px com.apple.FinderInfo`), `.DS_Store` with the four icon slots, `.background.tiff`. Copied the
|
||||
app out and ran its script in a real Terminal window (`open -a Terminal`, as the launcher does) with `MINERS=0
|
||||
SEED_PEERS=127.0.0.1:26611 IGNEUM_RPC_PORT=27400 IGNEUM_P2P_PORT=27401` and scratch data and log folders: the window title
|
||||
read "joshm, Igneum Miner, sleep, igneum-miner.sh, 80x24" (Terminal joins those with its own dashes), the first two log lines are the version line and the seed
|
||||
line, synced in 77 s (11,995 blocks, the Mac was under load 25+), `igneum-miner.sh --stop` stopped it in 4 s through the
|
||||
pid file, no leftover process, ports free. The live node on 26610/26611 was only a peer. Note: `proto-metal/igneum-bench`
|
||||
carries `minos 16.0` in its load command (built without a `-target`), so on a Mac older than macOS 26 the worker may refuse
|
||||
to load even though the bundle says 11.0; a rebuild with `-target arm64-apple-macos11` fixes that.
|
||||
|
|
|
|||
|
|
@ -3,23 +3,27 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key>
|
||||
<string>Igneum Devnet</string>
|
||||
<string>Igneum Miner</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Igneum Devnet</string>
|
||||
<string>Igneum Miner</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>network.igneum.devnet</string>
|
||||
<string>network.igneum.miner</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>VERSION_STAMP</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.1</string>
|
||||
<string>0.1.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Igneum Devnet</string>
|
||||
<string>Igneum Miner</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>AppIcon</string>
|
||||
<string>igneum</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>13.3</string>
|
||||
<string>11.0</string>
|
||||
<key>LSArchitecturePriority</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
|
|
@ -27,6 +31,6 @@
|
|||
<key>LSRequiresNativeExecution</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Igneum devnet. Nothing is bought or sold.</string>
|
||||
<string>Igneum. Nothing is bought or sold.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -1,20 +1,21 @@
|
|||
#!/bin/bash
|
||||
# Stops a running Igneum Devnet (miners, Metal worker, then the node) when its window was closed without Ctrl+C.
|
||||
[ -t 1 ] && printf '\033]0;Stop Igneum Miner\007'
|
||||
# Stops a running Igneum Miner (miners, Metal worker, then the node) when its window was closed without Ctrl+C.
|
||||
# Finds the running copy through the run file the launcher writes, and falls back to the process list.
|
||||
DATA="${IGNEUM_DATA:-$HOME/Library/Application Support/Igneum/devnet}"
|
||||
RUNFILE="$DATA/igneum-devnet.run"
|
||||
RUNFILE="$DATA/igneum-miner.run"
|
||||
script="$(sed -n 's/^script=//p' "$RUNFILE" 2>/dev/null)"
|
||||
if [ -n "$script" ] && [ -x "$script" ]; then
|
||||
exec "$script" --stop
|
||||
fi
|
||||
left="$(pgrep -f 'Igneum Devnet.app/Contents/Resources/bin/' 2>/dev/null)"
|
||||
left="$(pgrep -f 'Igneum Miner.app/Contents/Resources/bin/' 2>/dev/null)"
|
||||
if [ -n "$left" ]; then
|
||||
echo "stopping Igneum processes: $(echo $left | tr '\n' ' ')"
|
||||
pkill -TERM -f 'Igneum Devnet.app/Contents/Resources/bin/' 2>/dev/null
|
||||
pkill -TERM -f 'Igneum Miner.app/Contents/Resources/bin/' 2>/dev/null
|
||||
sleep 5
|
||||
pkill -KILL -f 'Igneum Devnet.app/Contents/Resources/bin/' 2>/dev/null
|
||||
rm -f "$DATA/igneum-devnet.pid" "$RUNFILE"
|
||||
echo "Igneum is stopped."
|
||||
pkill -KILL -f 'Igneum Miner.app/Contents/Resources/bin/' 2>/dev/null
|
||||
rm -f "$DATA/igneum-miner.pid" "$RUNFILE"
|
||||
echo "Igneum Miner is stopped."
|
||||
else
|
||||
echo "Igneum is not running."
|
||||
echo "Igneum Miner is not running."
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/bin/bash
|
||||
# Igneum devnet for Apple silicon Macs: the node and the Metal miner in one Terminal window.
|
||||
# Lives in "Igneum Devnet.app/Contents/Resources" next to bin/ (igneumd, igneum-miner, igneum-bench).
|
||||
# The app's launcher opens this script in Terminal. 3 October 2026.
|
||||
# Igneum Miner for Apple silicon Macs: the node and the Metal miner in one Terminal window. The devnet is the only
|
||||
# network for now. Lives in "Igneum Miner.app/Contents/Resources" next to bin/ (igneumd, igneum-miner, igneum-bench).
|
||||
# The app's launcher opens this script in Terminal. 3 October 2026 (was the devnet launcher, renamed the same day).
|
||||
#
|
||||
# What it does, in order:
|
||||
# 1. Checks the Mac (Apple silicon), the binaries, the ports, and that no other copy is running.
|
||||
|
|
@ -16,12 +16,14 @@
|
|||
# 5. Prints a plain status line every 30 s, uploads the logs to the intake every 60 s, keeps the Mac
|
||||
# awake with caffeinate, restarts the node (5 to 60 s later) or a miner after a crash, restarts a
|
||||
# miner at once when it exits with code 42 (the hourly program change).
|
||||
# 6. Ctrl+C, closing the window (SIGHUP) or `igneum-devnet.sh --stop` stops the miners, their workers,
|
||||
# 6. Ctrl+C, closing the window (SIGHUP) or `igneum-miner.sh --stop` stops the miners, their workers,
|
||||
# then the node, then uploads the logs one last time.
|
||||
#
|
||||
# Nothing is written outside the app except the data directory and the log directory below.
|
||||
|
||||
# ---- settings ---------------------------------------------------------------------------------------------
|
||||
VERSION="0.1.0"
|
||||
NETWORK="devnet"
|
||||
# The seed node(s) to peer with. For now the project lead's Mac on the house LAN; a public seed node address
|
||||
# (host:port) replaces it when one exists. A comma list is allowed: "1.2.3.4:26611,5.6.7.8:26611".
|
||||
SEED_PEERS="${SEED_PEERS:-192.168.68.64:26611}"
|
||||
|
|
@ -45,8 +47,8 @@ NODE_EXE="$BIN/igneumd"
|
|||
MINER_EXE="$BIN/igneum-miner"
|
||||
WORKER_EXE="$BIN/igneum-bench"
|
||||
RPC_URL="grpc://127.0.0.1:$RPC_PORT"
|
||||
PIDFILE="$DATA/igneum-devnet.pid"
|
||||
RUNFILE="$DATA/igneum-devnet.run"
|
||||
PIDFILE="$DATA/igneum-miner.pid"
|
||||
RUNFILE="$DATA/igneum-miner.run"
|
||||
STAMP="$(date +%Y%m%d-%H%M%S)"
|
||||
HOST="$(scutil --get LocalHostName 2>/dev/null || hostname -s)"
|
||||
HOST="$(printf '%s' "$HOST" | tr -c 'A-Za-z0-9-' '-' | sed -e 's/^-*//' -e 's/-*$//')"
|
||||
|
|
@ -71,12 +73,15 @@ uptime_txt() {
|
|||
printf '%02d:%02d:%02d' $(( s / 3600 )) $(( s % 3600 / 60 )) $(( s % 60 ))
|
||||
}
|
||||
|
||||
# ---- --stop: stop a running instance (also used by "Stop Igneum.command") ----------------------------------
|
||||
# The Terminal window and tab title: the product name, never the script path (OSC 0 sets both in Terminal.app).
|
||||
[ -t 1 ] && printf '\033]0;Igneum Miner\007'
|
||||
|
||||
# ---- --stop: stop a running instance (also used by "Stop Igneum Miner.command") ----------------------------
|
||||
stop_mode() {
|
||||
local pid="" waited=0 left
|
||||
[ -f "$PIDFILE" ] && pid="$(cat "$PIDFILE" 2>/dev/null)"
|
||||
if alive "$pid"; then
|
||||
echo "stopping Igneum (launcher pid $pid): miners first, then the node"
|
||||
echo "stopping Igneum Miner (launcher pid $pid): miners first, then the node"
|
||||
kill -TERM "$pid" 2>/dev/null
|
||||
while alive "$pid" && [ $waited -lt 60 ]; do sleep 1; waited=$(( waited + 1 )); done
|
||||
if alive "$pid"; then echo "the launcher did not stop in 60 s; forcing"; kill -KILL "$pid" 2>/dev/null; fi
|
||||
|
|
@ -84,27 +89,27 @@ stop_mode() {
|
|||
echo "no running launcher found (no live pid in $PIDFILE)"
|
||||
fi
|
||||
# Anything left from this app (a launcher killed with SIGKILL leaves its children behind)
|
||||
left="$(pgrep -f 'Igneum Devnet.app/Contents/Resources/bin/' 2>/dev/null)"
|
||||
left="$(pgrep -f 'Igneum Miner.app/Contents/Resources/bin/' 2>/dev/null)"
|
||||
if [ -n "$left" ]; then
|
||||
echo "stopping leftover processes: $(echo $left | tr '\n' ' ')"
|
||||
pkill -TERM -f 'Igneum Devnet.app/Contents/Resources/bin/' 2>/dev/null
|
||||
pkill -TERM -f 'Igneum Miner.app/Contents/Resources/bin/' 2>/dev/null
|
||||
sleep 5
|
||||
pkill -KILL -f 'Igneum Devnet.app/Contents/Resources/bin/' 2>/dev/null
|
||||
pkill -KILL -f 'Igneum Miner.app/Contents/Resources/bin/' 2>/dev/null
|
||||
fi
|
||||
rm -f "$PIDFILE" "$RUNFILE"
|
||||
echo "Igneum is stopped."
|
||||
echo "Igneum Miner is stopped."
|
||||
exit 0
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
--stop) stop_mode ;;
|
||||
--help|-h) sed -n '2,20p' "$0"; echo; echo "usage: igneum-devnet.sh [--stop] (settings: SEED_PEERS, MINERS, STATUS_SECS, UPLOAD_SECS at the top)"; exit 0 ;;
|
||||
--help|-h) sed -n '2,20p' "$0"; echo; echo "usage: igneum-miner.sh [--stop] (settings: SEED_PEERS, MINERS, STATUS_SECS, UPLOAD_SECS at the top)"; exit 0 ;;
|
||||
esac
|
||||
|
||||
# ---- 1. checks --------------------------------------------------------------------------------------------
|
||||
# sysctl rather than uname -m: a Terminal running under Rosetta reports x86_64 from uname on an Apple silicon Mac.
|
||||
if [ "$(sysctl -n hw.optional.arm64 2>/dev/null)" != "1" ]; then
|
||||
echo "Igneum devnet needs an Apple silicon Mac (M1 or later). This Mac reports $(uname -m) and no arm64 support. Nothing was started."
|
||||
echo "Igneum Miner needs an Apple silicon Mac (M1 or later). This Mac reports $(uname -m) and no arm64 support. Nothing was started."
|
||||
exit 1
|
||||
fi
|
||||
for f in "$NODE_EXE" "$MINER_EXE" "$WORKER_EXE"; do
|
||||
|
|
@ -112,22 +117,23 @@ for f in "$NODE_EXE" "$MINER_EXE" "$WORKER_EXE"; do
|
|||
done
|
||||
mkdir -p "$DATA" "$LOGS" || { echo "cannot create $DATA or $LOGS"; exit 1; }
|
||||
if [ -f "$PIDFILE" ] && alive "$(cat "$PIDFILE" 2>/dev/null)"; then
|
||||
echo "Igneum is already running in another window (launcher pid $(cat "$PIDFILE")). Press Ctrl+C there, or run Stop Igneum."
|
||||
echo "Igneum Miner is already running in another window (launcher pid $(cat "$PIDFILE")). Press Ctrl+C there, or open Stop Igneum Miner."
|
||||
exit 1
|
||||
fi
|
||||
for port in "$RPC_PORT" "$P2P_PORT"; do
|
||||
busy="$(lsof -nP -iTCP:"$port" -sTCP:LISTEN -t 2>/dev/null | head -1)"
|
||||
if [ -n "$busy" ]; then
|
||||
echo "port $port is already in use by pid $busy ($(ps -p "$busy" -o comm= 2>/dev/null)). Is a node already running? Run Stop Igneum first."
|
||||
echo "port $port is already in use by pid $busy ($(ps -p "$busy" -o comm= 2>/dev/null)). Is a node already running? Open Stop Igneum Miner first."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
case "$MINERS" in ''|*[!0-9]*) echo "MINERS must be a number (got '$MINERS')"; exit 1 ;; esac
|
||||
|
||||
echo "$$" > "$PIDFILE"
|
||||
printf 'pid=%s\nscript=%s\nrpc=%s\nrun=%s\n' "$$" "$RES/igneum-devnet.sh" "$RPC_URL" "$RUN_ID" > "$RUNFILE"
|
||||
printf 'pid=%s\nscript=%s\nrpc=%s\nrun=%s\n' "$$" "$RES/igneum-miner.sh" "$RPC_URL" "$RUN_ID" > "$RUNFILE"
|
||||
|
||||
log "Igneum devnet on $HOST (run $RUN_ID)"
|
||||
log "Igneum Miner $VERSION on $HOST, network $NETWORK (run $RUN_ID)"
|
||||
log "Nobody from Igneum will ever ask for your seed. Nothing is bought or sold on this network."
|
||||
log "node: RPC 127.0.0.1:$RPC_PORT, p2p 0.0.0.0:$P2P_PORT, data $DATA"
|
||||
log "seed peers: $SEED_PEERS; miners: $MINERS; logs: $LOGS"
|
||||
log "node $("$NODE_EXE" --version 2>&1 | head -1); worker $(basename "$WORKER_EXE") (Metal)"
|
||||
|
|
@ -147,7 +153,7 @@ start_node() {
|
|||
NODE_STARTS=$(( NODE_STARTS + 1 ))
|
||||
seg=""; [ $NODE_STARTS -gt 1 ] && seg="-r$NODE_STARTS"
|
||||
NODE_LOG="$LOGS/node-$STAMP$seg.log"
|
||||
args=(--devnet "--appdir=$DATA" "--rpclisten=127.0.0.1:$RPC_PORT" "--listen=0.0.0.0:$P2P_PORT" --nodnsseed --disable-upnp --nologfiles)
|
||||
args=("--$NETWORK" "--appdir=$DATA" "--rpclisten=127.0.0.1:$RPC_PORT" "--listen=0.0.0.0:$P2P_PORT" --nodnsseed --disable-upnp --nologfiles)
|
||||
for p in $(printf '%s' "$SEED_PEERS" | tr ',' ' '); do args+=("--addpeer=$p"); done
|
||||
# Children ignore SIGHUP (the window closing) so the launcher can stop them in order; bash already makes
|
||||
# background children ignore SIGINT (Ctrl+C) in a script.
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#!/bin/bash
|
||||
# Bundle executable of "Igneum Devnet.app" (Contents/MacOS/Igneum Devnet). Finder runs this with no terminal, so
|
||||
# it opens Resources/igneum-devnet.sh in Terminal and exits. A shell script, not an AppleScript applet: Terminal
|
||||
# Bundle executable of "Igneum Miner.app" (Contents/MacOS/Igneum Miner). Finder runs this with no terminal, so
|
||||
# it opens Resources/igneum-miner.sh in Terminal and exits. A shell script, not an AppleScript applet: Terminal
|
||||
# opening a file it was handed by `open -a Terminal` needs no Automation consent, an AppleScript `tell Terminal
|
||||
# to do script` would prompt "Igneum Devnet wants to control Terminal" on the first run.
|
||||
# to do script` would prompt "Igneum Miner wants to control Terminal" on the first run.
|
||||
# Before that it removes the quarantine flag from the bundle's own files: once the user has let the app through
|
||||
# Gatekeeper, the binaries inside would still be checked one by one on their first exec and refused as
|
||||
# unidentified. The flag can only be removed on a writable volume, so the app must be run from Applications
|
||||
|
|
@ -10,10 +10,10 @@
|
|||
CONTENTS="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
RES="$CONTENTS/Resources"
|
||||
if ! xattr -dr com.apple.quarantine "$RES" 2>/dev/null; then
|
||||
if [ -n "$(xattr -p com.apple.quarantine "$RES/igneum-devnet.sh" 2>/dev/null)" ]; then
|
||||
osascript -e 'display dialog "Drag Igneum Devnet into Applications first, then open it from there. It cannot start from the disk image." buttons {"OK"} default button 1 with title "Igneum Devnet"' >/dev/null 2>&1
|
||||
if [ -n "$(xattr -p com.apple.quarantine "$RES/igneum-miner.sh" 2>/dev/null)" ]; then
|
||||
osascript -e 'display dialog "Drag Igneum Miner into Applications first, then open it from there. It cannot start from the disk image." buttons {"OK"} default button 1 with title "Igneum Miner"' >/dev/null 2>&1
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
chmod +x "$RES/igneum-devnet.sh" "$RES/bin/"* 2>/dev/null
|
||||
exec /usr/bin/open -a Terminal "$RES/igneum-devnet.sh"
|
||||
chmod +x "$RES/igneum-miner.sh" "$RES/bin/"* 2>/dev/null
|
||||
exec /usr/bin/open -a Terminal "$RES/igneum-miner.sh"
|
||||
|
|
|
|||
|
|
@ -1,22 +1,27 @@
|
|||
#!/bin/bash
|
||||
# Builds packaging/mac/dist/igneum-devnet-mac.dmg: "Igneum Devnet.app" + README.txt + "Stop Igneum.command" + a link
|
||||
# to /Applications. Reuses the binaries already built in the repo (nothing is compiled here); copies of the
|
||||
# binaries are stripped and re-signed ad hoc (strip invalidates the linker signature, and arm64 macOS refuses to
|
||||
# run an unsigned binary). The originals are not touched. 3 October 2026.
|
||||
# Builds packaging/mac/dist/Igneum-Miner-<version>.dmg: "Igneum Miner.app" + README.txt + "Stop Igneum Miner.command" +
|
||||
# a link to /Applications, on a branded image (volume "Igneum Miner", coin volume icon, background with the drag arrow,
|
||||
# icons placed). Reuses the binaries already built in the repo (nothing is compiled here); copies of the binaries are
|
||||
# stripped and re-signed ad hoc (strip invalidates the linker signature, and arm64 macOS refuses to run an unsigned
|
||||
# binary). The originals are not touched. 3 October 2026.
|
||||
#
|
||||
# packaging/mac/build-dmg.sh build
|
||||
# packaging/mac/build-dmg.sh build
|
||||
# NODE=<path> packaging/mac/build-dmg.sh use another node binary
|
||||
# Needs: brand/icons/igneum.icns and dmg-background.tiff (python3 brand/icons/make-icons.py) and dmgbuild
|
||||
# (pip3 install dmgbuild) for the icon layout. Without dmgbuild a plain hdiutil image is built and said so.
|
||||
set -euo pipefail
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
ROOT="$(cd "$HERE/../.." && pwd)"
|
||||
VERSION="0.1.0"
|
||||
NODE="${NODE:-$ROOT/vendor/igneum-node/target-rename/release/igneumd}"
|
||||
MINER="${MINER:-$ROOT/vendor/igneum-node/target/release/igneum-miner}"
|
||||
WORKER="${WORKER:-$ROOT/proto-metal/igneum-bench}"
|
||||
ICON_PNG="$ROOT/site/icon-512.png"
|
||||
ICONS="$ROOT/brand/icons"
|
||||
BUILD="$HERE/build"
|
||||
DIST="$HERE/dist"
|
||||
DMG="$DIST/igneum-devnet-mac.dmg"
|
||||
APP="$BUILD/dmg/Igneum Devnet.app"
|
||||
DMG="$DIST/Igneum-Miner-$VERSION.dmg"
|
||||
STAGE="$BUILD/dmg"
|
||||
APP="$STAGE/Igneum Miner.app"
|
||||
STAMP="$(date -u +%Y%m%d%H%M)"
|
||||
|
||||
if [ ! -x "$NODE" ]; then
|
||||
|
|
@ -31,19 +36,22 @@ for f in "$MINER" "$WORKER"; do [ -x "$f" ] || { echo "missing: $f"; exit 1; };
|
|||
for f in "$NODE" "$MINER" "$WORKER"; do
|
||||
file "$f" | grep -q 'arm64' || { echo "$f is not an arm64 binary"; exit 1; }
|
||||
done
|
||||
[ -f "$ICONS/igneum.icns" ] || { echo "no $ICONS/igneum.icns; run: python3 brand/icons/make-icons.py"; exit 1; }
|
||||
|
||||
rm -rf "$BUILD"
|
||||
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources/bin" "$DIST"
|
||||
|
||||
# the bundle
|
||||
sed "s/VERSION_STAMP/$STAMP/" "$HERE/app/Info.plist" > "$APP/Contents/Info.plist"
|
||||
plutil -lint "$APP/Contents/Info.plist" >/dev/null
|
||||
printf 'APPL????' > "$APP/Contents/PkgInfo"
|
||||
cp "$HERE/app/launcher.sh" "$APP/Contents/MacOS/Igneum Devnet"
|
||||
cp "$HERE/app/igneum-devnet.sh" "$APP/Contents/Resources/igneum-devnet.sh"
|
||||
cp "$HERE/app/launcher.sh" "$APP/Contents/MacOS/Igneum Miner"
|
||||
cp "$HERE/app/igneum-miner.sh" "$APP/Contents/Resources/igneum-miner.sh"
|
||||
cp "$ICONS/igneum.icns" "$APP/Contents/Resources/igneum.icns"
|
||||
cp "$NODE" "$APP/Contents/Resources/bin/igneumd"
|
||||
cp "$MINER" "$APP/Contents/Resources/bin/igneum-miner"
|
||||
cp "$WORKER" "$APP/Contents/Resources/bin/igneum-bench"
|
||||
chmod 755 "$APP/Contents/MacOS/Igneum Devnet" "$APP/Contents/Resources/igneum-devnet.sh" "$APP/Contents/Resources/bin/"*
|
||||
chmod 755 "$APP/Contents/MacOS/Igneum Miner" "$APP/Contents/Resources/igneum-miner.sh" "$APP/Contents/Resources/bin/"*
|
||||
|
||||
# strip the copies, then sign them ad hoc again
|
||||
for b in "$APP/Contents/Resources/bin/"*; do
|
||||
|
|
@ -58,25 +66,19 @@ v="$("$APP/Contents/Resources/bin/igneumd" --version 2>&1 || true)"; echo "node:
|
|||
v="$("$APP/Contents/Resources/bin/igneum-miner" 2>&1 || true)"; case "$v" in usage*) ;; *) echo "igneum-miner copy does not run: $v"; exit 1 ;; esac
|
||||
v="$(echo quit | "$APP/Contents/Resources/bin/igneum-bench" --serve 2>&1)"; case "$v" in "ready metal"*) echo "worker: $v" ;; *) echo "igneum-bench copy does not serve: $v"; exit 1 ;; esac
|
||||
|
||||
# icon from the site's 512 px icon
|
||||
if [ -f "$ICON_PNG" ]; then
|
||||
ICONSET="$BUILD/AppIcon.iconset"
|
||||
mkdir -p "$ICONSET"
|
||||
for s in 16 32 128 256 512; do
|
||||
sips -z $s $s "$ICON_PNG" --out "$ICONSET/icon_${s}x${s}.png" >/dev/null
|
||||
d=$(( s * 2 ))
|
||||
if [ $d -le 1024 ]; then sips -z $d $d "$ICON_PNG" --out "$ICONSET/icon_${s}x${s}@2x.png" >/dev/null; fi
|
||||
done
|
||||
iconutil -c icns "$ICONSET" -o "$APP/Contents/Resources/AppIcon.icns"
|
||||
fi
|
||||
|
||||
# the rest of the image
|
||||
cp "$HERE/dmg/README.txt" "$BUILD/dmg/README.txt"
|
||||
cp "$HERE/app/Stop Igneum.command" "$BUILD/dmg/Stop Igneum.command"
|
||||
chmod 755 "$BUILD/dmg/Stop Igneum.command"
|
||||
ln -s /Applications "$BUILD/dmg/Applications"
|
||||
cp "$HERE/dmg/README.txt" "$STAGE/README.txt"
|
||||
cp "$HERE/app/Stop Igneum Miner.command" "$STAGE/Stop Igneum Miner.command"
|
||||
chmod 755 "$STAGE/Stop Igneum Miner.command"
|
||||
|
||||
rm -f "$DMG"
|
||||
hdiutil create -volname "Igneum Devnet" -srcfolder "$BUILD/dmg" -ov -format ULFO -fs HFS+ "$DMG" >/dev/null
|
||||
if command -v dmgbuild >/dev/null 2>&1; then
|
||||
dmgbuild -s "$HERE/dmg/settings.py" -D "app=$APP" -D "stage=$STAGE" -D "icons=$ICONS" "Igneum Miner" "$DMG"
|
||||
else
|
||||
echo "note: dmgbuild is not installed (pip3 install dmgbuild); building a plain image without icon positions or background"
|
||||
ln -s /Applications "$STAGE/Applications"
|
||||
cp "$ICONS/igneum.icns" "$STAGE/.VolumeIcon.icns"
|
||||
hdiutil create -volname "Igneum Miner" -srcfolder "$STAGE" -ov -format ULFO -fs HFS+ "$DMG" >/dev/null
|
||||
fi
|
||||
hdiutil verify "$DMG" >/dev/null
|
||||
echo "built $DMG ($(du -h "$DMG" | cut -f1), $(stat -f %z "$DMG") bytes, version $STAMP)"
|
||||
echo "built $DMG ($(du -h "$DMG" | cut -f1), $(stat -f %z "$DMG") bytes, version $VERSION build $STAMP)"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
Igneum Devnet for Mac (Apple silicon only: M1 or later). Nothing is bought or sold; this is a test network.
|
||||
1. Drag "Igneum Devnet" into Applications. It will not start from this disk image.
|
||||
2. First time: in Applications, right-click "Igneum Devnet" and choose Open, then Open again. The app is unsigned.
|
||||
Igneum Miner 0.1.0 for Mac (Apple silicon only: M1 or later). Test network; nothing is bought or sold.
|
||||
Nobody from Igneum will ever ask for your seed.
|
||||
1. Drag "Igneum Miner" into Applications. It will not start from this disk image.
|
||||
2. First time: in Applications, right-click "Igneum Miner" and choose Open, then Open again. The app is unsigned.
|
||||
If macOS still refuses, open System Settings > Privacy & Security, scroll down, click "Open Anyway", open the app again.
|
||||
3. A Terminal window opens. It starts the node, shows "syncing, N blocks" until it has the chain (usually under a minute),
|
||||
then starts the Metal miner. Every 30 s one status line: accepted blocks, MH/s, template age, node blocks and peers.
|
||||
3. A Terminal window called "Igneum Miner" opens. It starts the node, shows "syncing, N blocks" until it has the chain
|
||||
(usually under a minute), then starts the Metal miner. Every 30 s one status line: blocks, MH/s, node blocks and peers.
|
||||
4. Your miner appears on igneum.network/live within about a minute of its first block, under the id the window prints.
|
||||
5. To stop: press Ctrl+C in that window (miners stop first, then the node). If the window was closed, open "Stop Igneum".
|
||||
5. To stop: press Ctrl+C in that window (miners stop first, then the node). If the window was closed, open "Stop Igneum Miner".
|
||||
The Mac stays awake while the window is open. Data: ~/Library/Application Support/Igneum, logs: ~/Library/Logs/Igneum.
|
||||
|
|
|
|||
40
packaging/mac/dmg/settings.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# dmgbuild settings for Igneum-Miner-<version>.dmg (used by build-dmg.sh; pip3 install dmgbuild).
|
||||
# Defines passed in: app (the built .app), stage (the folder with README.txt and the Stop command), icons (brand/icons).
|
||||
# Icon slots match brand/icons/make-icons.py (APP_SLOT, APPS_SLOT, ROW2_Y, ICON) and the arrow painted on the background.
|
||||
import os
|
||||
app = defines['app']
|
||||
stage = defines['stage']
|
||||
icons = defines['icons']
|
||||
appname = os.path.basename(app)
|
||||
|
||||
format = 'ULFO'
|
||||
filesystem = 'HFS+'
|
||||
size = None
|
||||
files = [app, os.path.join(stage, 'README.txt'), os.path.join(stage, 'Stop Igneum Miner.command')]
|
||||
symlinks = {'Applications': '/Applications'}
|
||||
icon = os.path.join(icons, 'igneum.icns') # the volume icon (.VolumeIcon.icns, hidden, custom-icon flag set)
|
||||
background = os.path.join(icons, 'dmg-background.tiff') if os.path.exists(os.path.join(icons, 'dmg-background.tiff')) else os.path.join(icons, 'dmg-background.png')
|
||||
show_status_bar = False
|
||||
show_tab_view = False
|
||||
show_toolbar = False
|
||||
show_pathbar = False
|
||||
show_sidebar = False
|
||||
sidebar_width = 180
|
||||
window_rect = ((200, 120), (660, 400))
|
||||
default_view = 'icon-view'
|
||||
show_icon_preview = False
|
||||
include_icon_view_settings = 'auto'
|
||||
include_list_view_settings = 'auto'
|
||||
arrange_by = None
|
||||
grid_offset = (0, 0)
|
||||
grid_spacing = 100
|
||||
scroll_position = (0, 0)
|
||||
label_pos = 'bottom'
|
||||
text_size = 12
|
||||
icon_size = 96
|
||||
icon_locations = {
|
||||
appname: (165, 130),
|
||||
'Applications': (495, 130),
|
||||
'README.txt': (165, 330),
|
||||
'Stop Igneum Miner.command': (495, 330),
|
||||
}
|
||||
3
packaging/windows/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# built outputs: the windres objects, the assembled payload, the downloaded tools, the installer
|
||||
build/
|
||||
dist/
|
||||
17
packaging/windows/BUILD-INSTALLER.bat
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
@echo off
|
||||
rem Builds Igneum-Miner-Setup-0.1.0.exe on a Windows PC. Double-click this file. The first run needs the internet:
|
||||
rem Inno Setup 6 comes through winget if it is missing, rcedit stamps the icon into the exes if they do not carry it yet.
|
||||
rem The payload is the extracted igneum-windows folder next to this file, or the zip at the URL in build-installer.ps1.
|
||||
cd /d "%~dp0"
|
||||
where powershell >nul 2>nul
|
||||
if errorlevel 1 (
|
||||
echo PowerShell was not found. Windows 10 and 11 ship it.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0build-installer.ps1" %*
|
||||
if errorlevel 1 (
|
||||
echo.
|
||||
echo The build did not finish. The reason is in the lines above.
|
||||
)
|
||||
pause
|
||||
112
packaging/windows/Igneum-Miner.iss
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
; Igneum Miner installer for Windows, Inno Setup 6.3 or newer. Compiled by build-installer.ps1, which passes
|
||||
; /DPayload=<assembled payload folder> and /DArtDir=<brand\icons>. The defaults below are for a run from the repo.
|
||||
#ifndef Payload
|
||||
#define Payload "build\payload"
|
||||
#endif
|
||||
#ifndef ArtDir
|
||||
#define ArtDir "..\..\brand\icons"
|
||||
#endif
|
||||
#ifndef AppVersion
|
||||
#define AppVersion "0.1.0"
|
||||
#endif
|
||||
#define AppName "Igneum Miner"
|
||||
#define Publisher "Igneum"
|
||||
#define Url "https://igneum.network"
|
||||
#define FirewallRule "Igneum Miner node"
|
||||
|
||||
[Setup]
|
||||
AppId={{A4C1F0E2-6B8D-4E7A-9F31-2C5D8E7B9A01}
|
||||
AppName={#AppName}
|
||||
AppVersion={#AppVersion}
|
||||
AppVerName={#AppName} {#AppVersion}
|
||||
AppPublisher={#Publisher}
|
||||
AppPublisherURL={#Url}
|
||||
AppSupportURL={#Url}
|
||||
AppUpdatesURL={#Url}
|
||||
AppCopyright=Igneum. Nothing is bought or sold.
|
||||
VersionInfoVersion={#AppVersion}.0
|
||||
VersionInfoCompany={#Publisher}
|
||||
VersionInfoProductName={#AppName}
|
||||
VersionInfoDescription={#AppName} Setup
|
||||
DefaultDirName={autopf}\{#AppName}
|
||||
DefaultGroupName={#AppName}
|
||||
DisableProgramGroupPage=yes
|
||||
LicenseFile=LICENSE.txt
|
||||
OutputDir=dist
|
||||
OutputBaseFilename=Igneum-Miner-Setup-{#AppVersion}
|
||||
SetupIconFile={#ArtDir}\igneum.ico
|
||||
UninstallDisplayIcon={app}\igneum.ico
|
||||
UninstallDisplayName={#AppName}
|
||||
WizardStyle=modern
|
||||
WizardImageFile={#ArtDir}\inno-wizard-164x314.bmp
|
||||
WizardSmallImageFile={#ArtDir}\inno-wizard-small-55x58.bmp
|
||||
Compression=lzma2/max
|
||||
SolidCompression=yes
|
||||
ArchitecturesAllowed=x64compatible
|
||||
ArchitecturesInstallIn64BitMode=x64compatible
|
||||
PrivilegesRequired=admin
|
||||
MinVersion=10.0
|
||||
CloseApplications=no
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
||||
[Messages]
|
||||
english.WelcomeLabel2=This will install [name/ver] on your computer.%n%nOne window runs your node and the GPU miners. The install is about 60 MB. Nothing is bought or sold on this network.
|
||||
english.FinishedHeadingLabel=Igneum Miner is installed
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"
|
||||
Name: "firewall"; Description: "Let other Igneum nodes connect to this PC (Windows Firewall rule for igneumd.exe on private networks)"; GroupDescription: "Network:"
|
||||
|
||||
[Dirs]
|
||||
; The launcher writes its logs, the hourly program packs and the built GPU workers next to its scripts.
|
||||
Name: "{app}"; Permissions: users-modify
|
||||
|
||||
[Files]
|
||||
Source: "{#Payload}\*"; DestDir: "{app}"; Flags: recursesubdirs createallsubdirs ignoreversion; Excludes: "*.log,*.seeds,*.DS_Store,packs\*"
|
||||
Source: "{#ArtDir}\igneum.ico"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "wrappers\Igneum Miner.cmd"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "wrappers\Stop Igneum Miner.cmd"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
||||
[Icons]
|
||||
Name: "{group}\Igneum Miner"; Filename: "{app}\Igneum Miner.cmd"; WorkingDir: "{app}"; IconFilename: "{app}\igneum.ico"; Comment: "Start the node and mine"
|
||||
Name: "{group}\Stop Igneum Miner"; Filename: "{app}\Stop Igneum Miner.cmd"; WorkingDir: "{app}"; IconFilename: "{app}\igneum.ico"; Comment: "Stop the miners and the node"
|
||||
Name: "{group}\Igneum Miner logs"; Filename: "{app}"; IconFilename: "{app}\igneum.ico"; Comment: "The folder the log files land in"
|
||||
Name: "{group}\Uninstall Igneum Miner"; Filename: "{uninstallexe}"; IconFilename: "{app}\igneum.ico"
|
||||
Name: "{autodesktop}\Igneum Miner"; Filename: "{app}\Igneum Miner.cmd"; WorkingDir: "{app}"; IconFilename: "{app}\igneum.ico"; Tasks: desktopicon
|
||||
|
||||
[Run]
|
||||
Filename: "netsh.exe"; Parameters: "advfirewall firewall delete rule name=""{#FirewallRule}"""; Flags: runhidden; Tasks: firewall
|
||||
Filename: "netsh.exe"; Parameters: "advfirewall firewall add rule name=""{#FirewallRule}"" dir=in action=allow enable=yes profile=private,domain protocol=TCP program=""{app}\igneumd.exe"""; Flags: runhidden; Tasks: firewall; StatusMsg: "Adding the firewall rule for the node"
|
||||
; Started through the shortcut so the console window carries the coin icon, and as the signed-in user, not as administrator.
|
||||
Filename: "{group}\Igneum Miner.lnk"; Description: "Start Igneum Miner now"; Flags: shellexec postinstall nowait skipifsilent runasoriginaluser unchecked
|
||||
|
||||
[UninstallRun]
|
||||
Filename: "powershell.exe"; Parameters: "-NoProfile -ExecutionPolicy Bypass -File ""{app}\stop-igneum.ps1"""; Flags: runhidden waituntilterminated; RunOnceId: "StopIgneum"
|
||||
Filename: "netsh.exe"; Parameters: "advfirewall firewall delete rule name=""{#FirewallRule}"""; Flags: runhidden; RunOnceId: "FirewallRule"
|
||||
|
||||
[UninstallDelete]
|
||||
; Logs, packs and the GPU workers built on this PC are not in the install log; remove them with the folder.
|
||||
Type: filesandordirs; Name: "{app}"
|
||||
|
||||
[Code]
|
||||
// Stops a running copy before the files are replaced (an upgrade over a running miner).
|
||||
function PrepareToInstall(var NeedsRestart: Boolean): String;
|
||||
var
|
||||
StopScript: String;
|
||||
ResultCode: Integer;
|
||||
begin
|
||||
Result := '';
|
||||
StopScript := ExpandConstant('{app}\stop-igneum.ps1');
|
||||
if FileExists(StopScript) then
|
||||
Exec('powershell.exe', '-NoProfile -ExecutionPolicy Bypass -File "' + StopScript + '"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||
end;
|
||||
|
||||
procedure CurPageChanged(CurPageID: Integer);
|
||||
begin
|
||||
if CurPageID = wpFinished then
|
||||
WizardForm.FinishedLabel.Caption := WizardForm.FinishedLabel.Caption + #13#10#13#10 +
|
||||
'Nobody from Igneum will ever ask for your seed.';
|
||||
end;
|
||||
20
packaging/windows/LICENSE.txt
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
Igneum Miner 0.1.0
|
||||
|
||||
Licence: MIT (placeholder; the final licence is pending the foundation's decision).
|
||||
|
||||
Copyright (c) 2026 Igneum
|
||||
|
||||
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:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
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.
|
||||
|
||||
This is a test network. Nothing is bought or sold. Nobody from Igneum will ever ask for your seed.
|
||||
11
packaging/windows/README.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Igneum Miner for Windows (packaging/windows)
|
||||
|
||||
Inno Setup installer around the windows-app payload (proto-cuda/windows-app/make-package.sh makes igneum-windows.zip).
|
||||
On the PC: put the extracted `igneum-windows` folder next to this folder (or let the script fetch `IGNEUM_PAYLOAD_URL`,
|
||||
default dl.igneum.network/igneum-windows.zip), double-click `BUILD-INSTALLER.bat`, read `dist\Igneum-Miner-Setup-0.1.0.exe`.
|
||||
It installs Inno Setup 6 through winget if missing and stamps the coin icon into the exes with rcedit unless they already
|
||||
carry it. On the Mac, `embed-resources.sh` compiles `resources/*.rc` with windres and (with `--relink`) relinks both exes
|
||||
after a cross-build, so the next make-package ships them branded. The installer: Program Files\Igneum Miner, Start Menu
|
||||
group (Igneum Miner, Stop Igneum Miner, Igneum Miner logs, Uninstall), optional desktop icon and firewall rule, uninstall
|
||||
stops the processes first, licence page (MIT placeholder, pending), finish page carries the seed line.
|
||||
Not possible without accounts: Authenticode signing (SmartScreen warns until a code-signing certificate exists).
|
||||
163
packaging/windows/build-installer.ps1
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
# Builds dist\Igneum-Miner-Setup-<version>.exe on a Windows PC (started by BUILD-INSTALLER.bat, or run directly).
|
||||
# 1. Finds ISCC.exe (Inno Setup 6.3 or newer); installs Inno Setup 6 through winget when it is missing.
|
||||
# 2. Finds the art (brand\icons in the repo layout, or an art\ folder next to this script).
|
||||
# 3. Finds the payload: the extracted igneum-windows folder (START-IGNEUM.bat, igneumd.exe, igneum-miner.exe) next to
|
||||
# this script, in the parent folder, or given with -Payload; otherwise the zip at -PayloadUrl is downloaded.
|
||||
# 4. Copies the payload into build\payload without logs, packs and per-PC worker builds.
|
||||
# 5. Stamps the coin icon and the version block into igneumd.exe and igneum-miner.exe with rcedit when the exes do not
|
||||
# carry them yet (the Mac relink with packaging\windows\embed-resources.sh --relink makes this a no-op).
|
||||
# 6. Compiles Igneum-Miner.iss and prints where the installer is.
|
||||
#
|
||||
# build-installer.ps1 [-Payload <folder>] [-PayloadUrl <zip url>] [-Version 0.1.0] [-NoRcedit] [-NoWinget]
|
||||
param(
|
||||
[string]$Payload = '',
|
||||
[string]$PayloadUrl = $(if ($env:IGNEUM_PAYLOAD_URL) { $env:IGNEUM_PAYLOAD_URL } else { 'https://dl.igneum.network/igneum-windows.zip' }),
|
||||
[string]$Version = '0.1.0',
|
||||
[switch]$NoRcedit,
|
||||
[switch]$NoWinget
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$build = Join-Path $here 'build'
|
||||
$dist = Join-Path $here 'dist'
|
||||
$rceditUrl = 'https://github.com/electron/rcedit/releases/download/v2.0.0/rcedit-x64.exe'
|
||||
New-Item -ItemType Directory -Force -Path $build, $dist | Out-Null
|
||||
|
||||
function Say([string]$text) { Write-Host ('[build] ' + $text) }
|
||||
|
||||
function Find-Iscc {
|
||||
$candidates = @()
|
||||
foreach ($base in @(${env:ProgramFiles(x86)}, $env:ProgramFiles, (Join-Path $env:LOCALAPPDATA 'Programs'))) {
|
||||
if ($base) { $candidates += (Join-Path $base 'Inno Setup 6\ISCC.exe') }
|
||||
}
|
||||
foreach ($key in @('HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Inno Setup 6_is1',
|
||||
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Inno Setup 6_is1',
|
||||
'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Inno Setup 6_is1')) {
|
||||
try {
|
||||
$loc = (Get-ItemProperty -Path $key -ErrorAction Stop).InstallLocation
|
||||
if ($loc) { $candidates += (Join-Path $loc 'ISCC.exe') }
|
||||
} catch { }
|
||||
}
|
||||
foreach ($c in $candidates) { if ($c -and (Test-Path $c)) { return $c } }
|
||||
return $null
|
||||
}
|
||||
|
||||
# ---- 1. Inno Setup ----------------------------------------------------------------------------------------------------
|
||||
$iscc = Find-Iscc
|
||||
if (-not $iscc) {
|
||||
if ($NoWinget) { throw 'Inno Setup 6 is not installed (and -NoWinget was given). Install it from https://jrsoftware.org/isdl.php and run again.' }
|
||||
if (-not (Get-Command winget.exe -ErrorAction SilentlyContinue)) { throw 'Inno Setup 6 is not installed and winget is not available. Install Inno Setup 6 from https://jrsoftware.org/isdl.php and run again.' }
|
||||
Say 'Inno Setup 6 is not installed; installing it through winget (a UAC prompt may appear)'
|
||||
& winget.exe install --id JRSoftware.InnoSetup --exact --silent --accept-package-agreements --accept-source-agreements
|
||||
$iscc = Find-Iscc
|
||||
if (-not $iscc) { throw 'winget finished but ISCC.exe was not found. Install Inno Setup 6 from https://jrsoftware.org/isdl.php and run again.' }
|
||||
}
|
||||
$isccVersion = (Get-Item $iscc).VersionInfo.FileVersion
|
||||
Say "Inno Setup $isccVersion at $iscc"
|
||||
$v = [Version]($isccVersion -replace '[^0-9.].*$', '')
|
||||
if ($v -lt [Version]'6.3.0') { throw "Inno Setup $isccVersion is too old; 6.3 or newer is needed (winget upgrade --id JRSoftware.InnoSetup)." }
|
||||
|
||||
# ---- 2. the art ------------------------------------------------------------------------------------------------------
|
||||
$art = $null
|
||||
foreach ($dir in @((Join-Path $here '..\..\brand\icons'), (Join-Path $here 'art'))) {
|
||||
if (Test-Path (Join-Path $dir 'igneum.ico')) { $art = (Resolve-Path $dir).Path; break }
|
||||
}
|
||||
if (-not $art) { throw "No art folder: expected brand\icons two levels up (the repo layout) or an art\ folder next to this script with igneum.ico, inno-wizard-164x314.bmp and inno-wizard-small-55x58.bmp (made by brand\icons\make-icons.py)." }
|
||||
foreach ($f in @('igneum.ico', 'inno-wizard-164x314.bmp', 'inno-wizard-small-55x58.bmp')) {
|
||||
if (-not (Test-Path (Join-Path $art $f))) { throw "missing $f in $art" }
|
||||
}
|
||||
Say "art: $art"
|
||||
|
||||
# ---- 3. the payload --------------------------------------------------------------------------------------------------
|
||||
function Test-PayloadDir([string]$dir) {
|
||||
if (-not $dir -or -not (Test-Path $dir)) { return $false }
|
||||
foreach ($f in @('START-IGNEUM.bat', 'start-igneum.ps1', 'igneum-common.ps1', 'stop-igneum.ps1', 'igneumd.exe', 'igneum-miner.exe')) {
|
||||
if (-not (Test-Path (Join-Path $dir $f))) { return $false }
|
||||
}
|
||||
return $true
|
||||
}
|
||||
$source = $null
|
||||
if ($Payload) {
|
||||
if (-not (Test-PayloadDir $Payload)) { throw "-Payload $Payload does not hold START-IGNEUM.bat, the .ps1 files and both exes." }
|
||||
$source = (Resolve-Path $Payload).Path
|
||||
} else {
|
||||
foreach ($dir in @((Join-Path $here 'igneum-windows'), (Join-Path $here '..\igneum-windows'), (Join-Path $here 'payload'), (Join-Path $here '..\..\igneum-windows'))) {
|
||||
if (Test-PayloadDir $dir) { $source = (Resolve-Path $dir).Path; break }
|
||||
}
|
||||
}
|
||||
if (-not $source) {
|
||||
$zip = Join-Path $build 'igneum-windows.zip'
|
||||
$unpacked = Join-Path $build 'payload-src'
|
||||
Say "no igneum-windows folder next to this script; downloading $PayloadUrl"
|
||||
Invoke-WebRequest -Uri $PayloadUrl -OutFile $zip -UseBasicParsing
|
||||
if (Test-Path $unpacked) { Remove-Item -Recurse -Force $unpacked }
|
||||
Expand-Archive -Path $zip -DestinationPath $unpacked -Force
|
||||
$hit = Get-ChildItem -Path $unpacked -Recurse -Filter 'START-IGNEUM.bat' | Select-Object -First 1
|
||||
if (-not $hit -or -not (Test-PayloadDir $hit.DirectoryName)) { throw "the zip from $PayloadUrl holds no complete payload (START-IGNEUM.bat with the .ps1 files and both exes)." }
|
||||
$source = $hit.DirectoryName
|
||||
}
|
||||
Say "payload source: $source"
|
||||
|
||||
# ---- 4. assemble build\payload ---------------------------------------------------------------------------------------
|
||||
$payloadDir = Join-Path $build 'payload'
|
||||
if (Test-Path $payloadDir) { Remove-Item -Recurse -Force $payloadDir }
|
||||
New-Item -ItemType Directory -Force -Path $payloadDir | Out-Null
|
||||
$skipNames = @('.DS_Store')
|
||||
Get-ChildItem -Path $source -Recurse -Force | ForEach-Object {
|
||||
$rel = $_.FullName.Substring($source.Length).TrimStart('\')
|
||||
if ($rel -match '(^|\\)packs(\\|$)') { return }
|
||||
if ($rel -match '\.(log|seeds)$') { return }
|
||||
if ($rel -match '^proto-(cuda|opencl)\\[^\\]+\.exe$') { return } # GPU workers are built per PC on the first run
|
||||
if ($skipNames -contains $_.Name) { return }
|
||||
$dest = Join-Path $payloadDir $rel
|
||||
if ($_.PSIsContainer) { New-Item -ItemType Directory -Force -Path $dest | Out-Null }
|
||||
else {
|
||||
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $dest) | Out-Null
|
||||
Copy-Item -Path $_.FullName -Destination $dest -Force
|
||||
}
|
||||
}
|
||||
$count = (Get-ChildItem -Path $payloadDir -Recurse -File | Measure-Object).Count
|
||||
$bytes = (Get-ChildItem -Path $payloadDir -Recurse -File | Measure-Object -Property Length -Sum).Sum
|
||||
Say ("payload: {0} files, {1:N1} MB" -f $count, ($bytes / 1MB))
|
||||
|
||||
# ---- 5. icon and version block in the exes ---------------------------------------------------------------------------
|
||||
$rcedit = $null
|
||||
foreach ($exeName in @('igneumd.exe', 'igneum-miner.exe')) {
|
||||
$exe = Join-Path $payloadDir $exeName
|
||||
$info = (Get-Item $exe).VersionInfo
|
||||
if ($info.ProductName -eq 'Igneum Miner') { Say "$exeName already carries the Igneum Miner version block (relinked on the Mac)"; continue }
|
||||
if ($NoRcedit) { Say "$exeName has no version block; -NoRcedit given, so Explorer shows it without the coin icon"; continue }
|
||||
if (-not $rcedit) {
|
||||
if ($env:RCEDIT -and (Test-Path $env:RCEDIT)) { $rcedit = $env:RCEDIT }
|
||||
else {
|
||||
$rcedit = Join-Path $build 'rcedit-x64.exe'
|
||||
if (-not (Test-Path $rcedit)) { Say "downloading rcedit from $rceditUrl"; Invoke-WebRequest -Uri $rceditUrl -OutFile $rcedit -UseBasicParsing }
|
||||
}
|
||||
}
|
||||
$desc = if ($exeName -eq 'igneumd.exe') { 'Igneum Miner node' } else { 'Igneum Miner' }
|
||||
$rcArgs = @($exe, '--set-icon', (Join-Path $art 'igneum.ico'),
|
||||
'--set-version-string', 'CompanyName', 'Igneum',
|
||||
'--set-version-string', 'ProductName', 'Igneum Miner',
|
||||
'--set-version-string', 'FileDescription', $desc,
|
||||
'--set-version-string', 'OriginalFilename', $exeName,
|
||||
'--set-version-string', 'LegalCopyright', 'Igneum. Nothing is bought or sold.',
|
||||
'--set-file-version', $Version,
|
||||
'--set-product-version', $Version)
|
||||
& $rcedit @rcArgs
|
||||
if ($LASTEXITCODE -ne 0) { throw "rcedit failed on $exeName (exit $LASTEXITCODE)" }
|
||||
Say "$exeName stamped: coin icon, Igneum Miner $Version"
|
||||
}
|
||||
|
||||
# ---- 6. compile ------------------------------------------------------------------------------------------------------
|
||||
$iss = Join-Path $here 'Igneum-Miner.iss'
|
||||
$isccArgs = @('/Qp', "/DPayload=$payloadDir", "/DArtDir=$art", "/DAppVersion=$Version", "/O$dist", $iss)
|
||||
Say "compiling $iss"
|
||||
& $iscc @isccArgs
|
||||
if ($LASTEXITCODE -ne 0) { throw "ISCC failed (exit $LASTEXITCODE)" }
|
||||
$out = Join-Path $dist "Igneum-Miner-Setup-$Version.exe"
|
||||
if (-not (Test-Path $out)) { throw "ISCC finished but $out is missing" }
|
||||
$size = (Get-Item $out).Length
|
||||
Say ("done: {0} ({1:N1} MB)" -f $out, ($size / 1MB))
|
||||
Say 'The installer is unsigned until Igneum has a code-signing certificate: SmartScreen shows "Windows protected your PC" (More info > Run anyway).'
|
||||
61
packaging/windows/embed-resources.sh
Executable file
|
|
@ -0,0 +1,61 @@
|
|||
#!/usr/bin/env bash
|
||||
# Gives igneumd.exe and igneum-miner.exe the coin icon and a version block (CompanyName Igneum, ProductName
|
||||
# Igneum Miner, 0.1.0). Two steps, both on the Mac:
|
||||
# 1. windres compiles resources/igneumd.rc and resources/igneum-miner.rc into COFF objects (cheap, always done).
|
||||
# 2. --relink runs `cargo rustc` for each binary with `-C link-arg=<object>`: only the final crate is recompiled and
|
||||
# relinked, every dependency comes from the cache, so it costs a few minutes after a normal cross-build. The
|
||||
# objects are linked in as extra inputs, nothing in vendor/ changes. Without --relink the exact commands are
|
||||
# printed, and build-installer.ps1 stamps the resources with rcedit on the PC instead.
|
||||
# The resulting exes land where proto-cuda/windows-app/make-package.sh already picks them up
|
||||
# (vendor/igneum-node-win/target/x86_64-pc-windows-gnu/release/).
|
||||
#
|
||||
# packaging/windows/embed-resources.sh compile the objects, print the relink commands
|
||||
# packaging/windows/embed-resources.sh --relink also relink both exes (needs a completed cross-build in the cache)
|
||||
set -euo pipefail
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
ROOT="$(cd "$HERE/../.." && pwd)"
|
||||
ICONS="$ROOT/brand/icons"
|
||||
OUT="$HERE/build/res"
|
||||
NODE="${NODE_WORKTREE:-$ROOT/vendor/igneum-node-win}"
|
||||
MINGW="/opt/homebrew/opt/mingw-w64/toolchain-x86_64/x86_64-w64-mingw32"
|
||||
command -v x86_64-w64-mingw32-windres >/dev/null || { echo "x86_64-w64-mingw32-windres not found (brew install mingw-w64)" >&2; exit 1; }
|
||||
[ -f "$ICONS/igneum.ico" ] || { echo "no $ICONS/igneum.ico; run python3 brand/icons/make-icons.py first" >&2; exit 1; }
|
||||
mkdir -p "$OUT"
|
||||
for name in igneumd igneum-miner; do
|
||||
x86_64-w64-mingw32-windres -I "$ICONS" -i "$HERE/resources/$name.rc" -O coff -o "$OUT/$name.res.o"
|
||||
echo "compiled $OUT/$name.res.o ($(stat -f %z "$OUT/$name.res.o") bytes)"
|
||||
done
|
||||
|
||||
# The same environment as proto-cuda/windows-node/cross-build.sh, so the cached dependencies are reused.
|
||||
export PATH="$HOME/.cargo/bin:/opt/homebrew/bin:$PATH"
|
||||
export CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-target}"
|
||||
export CC_x86_64_pc_windows_gnu=x86_64-w64-mingw32-gcc
|
||||
export CXX_x86_64_pc_windows_gnu=x86_64-w64-mingw32-g++
|
||||
export AR_x86_64_pc_windows_gnu=x86_64-w64-mingw32-ar
|
||||
export LIBCLANG_PATH=/opt/homebrew/opt/llvm/lib
|
||||
export BINDGEN_EXTRA_CLANG_ARGS_x86_64_pc_windows_gnu="--target=x86_64-w64-mingw32 --sysroot=$MINGW -I$MINGW/include"
|
||||
export CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUSTFLAGS="-C link-arg=-static -C link-arg=-static-libgcc"
|
||||
|
||||
relink_cmd() { # $1 package, $2 bin, $3 object
|
||||
printf 'nice -n 19 cargo rustc --release -j 2 -p %s --bin %s --features igneum-pow --target x86_64-pc-windows-gnu -- -C link-arg=%q\n' "$1" "$2" "$3"
|
||||
}
|
||||
if [ "${1:-}" = "--relink" ]; then
|
||||
cd "$NODE"
|
||||
for spec in "kaspad igneumd" "igneum-miner igneum-miner"; do
|
||||
set -- $spec
|
||||
echo "relinking $2.exe"
|
||||
eval "$(relink_cmd "$1" "$2" "$OUT/$2.res.o")"
|
||||
exe="$CARGO_TARGET_DIR/x86_64-pc-windows-gnu/release/$2.exe"
|
||||
x86_64-w64-mingw32-objdump -h "$exe" | grep -q '\.rsrc' && echo "$exe: .rsrc section present ($(stat -f %z "$exe") bytes)" || { echo "$exe has no .rsrc section" >&2; exit 1; }
|
||||
done
|
||||
else
|
||||
cat <<TXT
|
||||
|
||||
Not relinked (no --relink). After the next cross-build (proto-cuda/windows-node/cross-build.sh), from $NODE
|
||||
with the same environment variables as that script, run:
|
||||
$(relink_cmd kaspad igneumd "$OUT/igneumd.res.o")
|
||||
$(relink_cmd igneum-miner igneum-miner "$OUT/igneum-miner.res.o")
|
||||
Then proto-cuda/windows-app/make-package.sh ships the branded exes. Check with:
|
||||
x86_64-w64-mingw32-objdump -h vendor/igneum-node-win/target/x86_64-pc-windows-gnu/release/igneum-miner.exe | grep rsrc
|
||||
TXT
|
||||
fi
|
||||
34
packaging/windows/resources/igneum-miner.rc
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// Windows resources for igneum-miner.exe: the coin icon and the version block Explorer shows under Properties > Details.
|
||||
// Compiled by embed-resources.sh with x86_64-w64-mingw32-windres (icon path relative to brand/icons, passed with -I).
|
||||
#include <winver.h>
|
||||
|
||||
1 ICON "igneum.ico"
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION 0,1,0,0
|
||||
PRODUCTVERSION 0,1,0,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
FILEFLAGS 0x0L
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_APP
|
||||
FILESUBTYPE VFT2_UNKNOWN
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904B0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Igneum"
|
||||
VALUE "FileDescription", "Igneum Miner"
|
||||
VALUE "FileVersion", "0.1.0"
|
||||
VALUE "InternalName", "igneum-miner"
|
||||
VALUE "LegalCopyright", "Igneum. Nothing is bought or sold."
|
||||
VALUE "OriginalFilename", "igneum-miner.exe"
|
||||
VALUE "ProductName", "Igneum Miner"
|
||||
VALUE "ProductVersion", "0.1.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
34
packaging/windows/resources/igneumd.rc
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// Windows resources for igneumd.exe: the coin icon and the version block Explorer shows under Properties > Details.
|
||||
// Compiled by embed-resources.sh with x86_64-w64-mingw32-windres (icon path relative to brand/icons, passed with -I).
|
||||
#include <winver.h>
|
||||
|
||||
1 ICON "igneum.ico"
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION 0,1,0,0
|
||||
PRODUCTVERSION 0,1,0,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
FILEFLAGS 0x0L
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_APP
|
||||
FILESUBTYPE VFT2_UNKNOWN
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904B0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Igneum"
|
||||
VALUE "FileDescription", "Igneum Miner node"
|
||||
VALUE "FileVersion", "0.1.0"
|
||||
VALUE "InternalName", "igneumd"
|
||||
VALUE "LegalCopyright", "Igneum. Nothing is bought or sold."
|
||||
VALUE "OriginalFilename", "igneumd.exe"
|
||||
VALUE "ProductName", "Igneum Miner"
|
||||
VALUE "ProductVersion", "0.1.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
9
packaging/windows/wrappers/Igneum Miner.cmd
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
@echo off
|
||||
rem Igneum Miner: what the Start Menu and Desktop shortcuts run. Names the window, prints the first lines, then hands
|
||||
rem over to START-IGNEUM.bat in the same folder (settings are at the top of that file).
|
||||
title Igneum Miner
|
||||
cd /d "%~dp0"
|
||||
echo Igneum Miner 0.1.0
|
||||
echo Nobody from Igneum will ever ask for your seed.
|
||||
echo.
|
||||
call "%~dp0START-IGNEUM.bat"
|
||||
5
packaging/windows/wrappers/Stop Igneum Miner.cmd
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
@echo off
|
||||
rem Stop Igneum Miner: the Start Menu entry for a copy whose window was closed without Ctrl+C.
|
||||
title Stop Igneum Miner
|
||||
cd /d "%~dp0"
|
||||
call "%~dp0STOP-IGNEUM.bat"
|
||||