Conversation
Switched packager: PyInstaller → py2app (more macOS-native, better notarization story, no Windows-AV-style baggage). New workflow: .github/workflows/build-macos-dmg-py2app.yaml replaces the old build-macos-dmg-pyinstaller.yaml. New py2app config: setup/macos/setup_py2app.py — excludes WebEngine/Qml/Quick/WebSockets to trim bundle size. Reuses part_assets.yml: compiles .ts → .qm, builds sample.zip, the PDF manual, and optional icon themes once, then shares them with the macOS jobs (addresses all three of the maintainer's feedback points). Builds both architectures: matrix over macos-14 (Apple Silicon / arm64) and macos-13 (Intel / x86_64). Canonical Info.plist: runs pkgutils.py gen-plist and overwrites py2app's default so version, bundle identifier (io.novelwriter.novelWriter), and .nwx document type are correct. Asset path fix-up: post-build cp -R places assets/ where config.py expects it in frozen mode (Contents/Resources/lib/python3.X/assets/). libenchant bundled: copies libenchant-2.dylib, enchant-2/ plugins, and share/enchant/ into the .app so spell-check works on machines without Homebrew (dictionaries themselves still TBD). Ad-hoc signed with entitlements: uses App.entitlements + Hardened Runtime, with a plain-ad-hoc fallback. Structured to swap in Developer ID + notarization once the Apple Developer account is set up. DMG output: novelWriter-<version>-<arch>.dmg + .sha256, uploaded as a workflow artifact and published to a rolling latest-mac prerelease.
|
Thanks! I'll have a look when I get a chance, although I'm a bit swamped for the next few days. |
| python -m pip install --upgrade pip | ||
| pip install pyinstaller PyQt6 pyenchant |
There was a problem hiding this comment.
The pkgutils can generate a requirements.txt file, which is safer to use because it is updated if the project requirements change.
|
The workflow itself looks fine. I had one comment, but I can easily fix that later. I'm still looking at how to get a signing key set up and just do it inside the project. Claude claims the needed secrets can be generated on Linux, so I don't actually need access to a Mac. I haven't verified this information. If someone else can maintain the build workflow and keep it up to date when something changes, I can release the DMGs like I have done up until now. |
|
That is great news.If you are happy to add the requirement.txt please do go ahead. I am happy to keep an eye on it. And test the apple silicon build on my machine. |
The signing runs on macos-latest on GitHub actions. But preparing the secrets from whatever it is Apple provides in terms of certificates can be done on Linux according to the answer I got. Of course, it's AI, so there's always the chance it is wrong. But I will investigate a bit on my own. I know a few Apple developers I can ask as well. Edit: Not working at Apple, developing for Apple platforms 😃 |
|
I was out for a bit as well, where did we get to with this ? I had a read of the documentation , I think it will work , I am happy to test getting rcodesign working, it needs an API key from apple which we can do from a paid developer account (i can test it with mine) , I am running Linux here and Mac OS so should be able to test this, do not want to start something if this has already been investigated though ! |
|
While it is clear how to build the DMGs once you have a developer key, it is still not clear how we actually want to run these signings in practice unless I actually get my own key and do it as part of the normal release process. You can mirror all the tags and releases on a straight up fork of the repo. Not sure if that can be automated, but the commands are not that complicated to run locally and can be scripted: https://stackoverflow.com/a/70678097 If the releases are generated from the tags, and on the same commits as on the main repo, then linking to those DMGs is fairly easy. |
|
Hi, we defo need a Mac to generate the p12 cert and then upload this to GitHub. I think the approach of forking and creating a release works for now. I have downloaded the build and tested it and everything looks okay to me and passes the code signing check. The dmg does take about ten minutes to build from the release itself, so if you don't see it immediately it will appear shortly. (link below) https://github.com/markiehill/novelWriter/releases/tag/v26.1.1 |
Actually - I need to remove that release , as I have actually built and released the main branch. I need to align it to your release. brb |
|
I have got all this working, sorry it took me a moment ! link to the release page below. We do need a Mac to generate the cert that is used for signing , I could find no way around that. That seems to be a one off thing and now it's done everything else is automated thanks to the script. https://github.com/markiehill/novelWriter/releases/tag/v2.0.6
Right , release here. https://github.com/markiehill/novelWriter/releases/tag/v26.1.1 |
|
Do you have access to the command line output of novelWriter when running with a --debug flag? It should give more context for where that error/warning is triggered. By default, novelWriter should pick your OS language as the default spell check language. If that check fails, it will be set to None, but that None shouldn't be loaded. Does the error go away if you try to set a spell check language in Preferences? If no spell check languages show up, and you do have dictionaries installed in the OS, then there is likely an issue with access to this info from the OS. This has been an issue before. |
I can take a look at the code later , but the system is set to use British GB and here is the log output: Loaded project language file: project_en_GB.json |
|
Ok, this is a packaging issue where the DMG cannot reach the OS dictionaries. It is either due to missing enchant dependencies (they are not always installed by default just because pyenchant is) or some permission is missing. Maybe it's the same issue as reported in #2705? If so, it's likely an issue with the build, @dm807cam |
PyInstaller runtime hook: point pyenchant at the bundled libenchant.
|
@vkbo @markiehill I tried to add a workaround for the missing enchant dependencies. I realized I have enchant installed on my machine via homebrew. So would appreciate if you could test it. |
Already building , will let you know.... |
Different error ... I have removed the app and made sure enchant is not installed and then created a new build from your file. I wont be able to do anymore today , will try and look tomorrow.
|
|
While we wait for this, any of you able to run the test suite locally and figure out why the tests on MacOS keep segfaulting? There are several examples here: #2861 There's a script that can be called which will run the tests over and over and collect crash logs. The script is written for Linux, so I don't know if it works on MacOS or not, but I suppose it can be adapted? It's in Update: I've managed to do a fair bit of backtrace debugging in CI, and it isn't particularly helpful. This may very well be isolated to tests, but it would be nice if someone could stress test the current main branch a little on a Mac. |
I had to rush out to India , so only got back Friday , I had a quick look at this just now .... I cannot reproduce the fault locally by running those tests, will have a closer look in the morning:
|
I have this running , so will leave it and check in the morning. |
|
@markiehill If you're OK with AI being used, I have access to a couple of Macs (M4 and M5) and rather a lot of tokens. Would be happy to get some testing done. My main machine is Linux, but my laptops are all Macs, so I'm invested in seeing NovelWriter back on Mac. |
I can reproduce the crash , if I run 20 instances. I managed to get a stack trace after writing a new debug script and quite a bit of messing around. I put the stack trace through AI , though I can send it to you if you want the raw data, here is what it says, hopefully you know where to look .... Looking at the metadata, macOS explicitly flags the cause: The issue stems from a classic PyQt wrapper lifetime bug. When a widget is initialized in Python without an explicit parent, Python considers it orphan memory. If the garbage collector runs right before QSplitter.addWidget() finishes transferring C++ ownership to the layout, the underlying C++ pointer gets deleted mid-flight. Bug Report: Flaky Segfault in testGuiMain (PyQt6 Memory Race Condition) |
If you are able to debug the open issue on the dictionary that would be good, my main focus was getting the build automated which is working, so if you can help debug what is going on with that message that would be great and allow me to keep looking at the crashing tests. You should be able to clone my repository and get what you need if you have your own local developer account. |
Thanks! That's the same wild goose chase Claude put me through when tracking the issue. The problem is that the segfault happens when pytest-qt's qtbot tries to clean up widgets after a test closes. The crash is caused by some randomised pointer that will report wildly different results each time, so the backtrace is unfortunately worthless. However, by simple bisection, I tracked the issue down to commit 4245723, and reverting that seems to resolve it. Claude already pegged line 228 in textblock.py in that commit as potentially problematic. I have it set up to scan Qt's source code, and it found something interesting about orphaned QTextCursors there. This may very well be the cause. I will patch it now. I don't really want to add too much noise to this PR about this. I was mainly concerned if it crashed the app, which is of relevance since this PR is part of enabling MacOS releases again. On that note, I'm almost ready to start the 2026.2 pre-release cycle. |
@markiehill I have raised a PR against your fork which deals with the dictionary issue. Tested good on my M4 Air: markiehill#1 |
Brilliant - I will take a look at this on Friday as I am away from my Mac, I have a clean environment in parallels so can test this with no other software installed. |
@jamesbannan at first glance this is still throwing an error (the same error) on my clean test machine. I have merged your pull request which means your Mac-build.yml is being used for the release. I created a new release and downloaded this onto a clean machine and I am still seeing the same error message, the only thing different about my environment is that I am based in the UK , do you think the changes you have made are making an assumption that the user is in the US ? I have just got back from travelling with work so there is a possibility I have made an error but I have checked everything as best as I can tonight ... will take a look with fresh eyes tomorrow. |
|
Hey, have a look at #2894 which narrows the install of |
I think I have a good idea what is going on here, so working on a fix , will report back. |
@markiehill Sorry just getting to this - my local build probably did default to |
|
Just a heads up that I'm doing the 26.2 Beta 1 release tomorrow, which means that the final release is likely 4 weeks away. I usually do 2 weeks of beta and 2 weeks on RC 1 for i18n to catch up. That means that the target branch here has moved to the releases branch instead, but right now it doesn't matter because the two branches are in sync. You may want to merge in the changes though, so we're testing building the code that will be released. The previous MacOS segfault issues I had have been fixed. There's also a formatting issue in the Python file that blocks the PR. If you have uv tooling installed, running Just to sum up what I've been thinking about the release strategy. When the main repo has a release, that release should be mirrored in your fork on the same commit with the same tag. I have scripts that pull the release info, and generate download links, so I will just extend it to include these DMG files, and note that they are contributed releases, and link to that release. |
|
@markiehill OK - another PR for you to have a look at against your fork. See whether it helps: markiehill#2 |
I can confirm that your PR fixes the issue on my test machine, well done. |
The other option to reduce complexity would be that I just give you my secrets to store on the main repo and then you can build everything at once, that would decrease all the moving parts. |
That kindof defeats the purpose of the signature though, and although you're the one taking the risk here, I'm not comfortable with doing that. I'd rather then just buy my own set of keys, except if I need a Mac to actually prepare them, then it's a little more than I'm willing to spend. |
Fair , I am happy enough with the plan. You do need a mac to generate the cert , no way around as far as I know. |
Although if you had a developer account and I was to allow you remote access onto my mac with your own account you could generate your own set of secrets. Or if you were to rent a mac in the cloud as and when needed you could also do it that way. That would be much lower cost. I am happy enough to do it , just thinking about ways of making things simple :) |
|
It's not a problem finding someone with a mac for that part. 😃 |
|
What's the status on this? I'm moving ahead with more pre-releases of the next version, and it would be nice to have a DMG in the pre-release too so it can be tested by more people. As for who does the build, maybe @markiehill can do it on a fork for now, and we see later on how we solve it. This project does not have a very high release frequency, so it shouldn't be too much of an issue staying in sync for a bit. A release is always associated with a PR, so I can ping you when I make those. I will figure out a more permanent solution once we're all set up. |
I have been testing it the last week, I am pretty happy with it. My build is behind yours so I need to get it in sync. I will be taking a look tomorrow. Yep happy to do the builds , not a problem. |
|
Not sure if this PR is up to date so it can be merged though. Your fork should be in sync with this repo with identical main and release branches, so whatever is needed for the build should be in this repo first and then mirrored across. There's a minor linting error in the Python script added in this PR, which is why the pipeline fails. Could you fix that @dm807cam? |
I have (I hope) submitted the PR request that @jamesbannan submitted to me which fixed this issue, if you can accept that then I will test it again against your pre-release. |






Brings back macOS DMG releases via a GitHub Actions workflow that builds, signs, notarizes, and attaches a .dmg to the release tag. Built with PyInstaller, reusing the existing part_assets.yml so the bundle contains everything novelWriter expects at runtime.