@@ -129,80 +129,77 @@ jobs:
129129 path : ${{ env.DMG_PATH }}
130130 if-no-files-found : error
131131
132- - name : Extract tag annotation as "What's new" section
133- id : tagnotes
132+ - name : Build release body file
134133 if : startsWith(github.ref, 'refs/tags/')
135134 run : |
136- # Pull the contents of the annotated tag message (subject + body)
137- # without the trailing PGP signature, if any.
138- MSG=$(git for-each-ref "refs/tags/${{ github.ref_name }}" \
135+ # Extract the annotated-tag message (subject + body), stripping any
136+ # PGP signature block. Writing to a file (rather than a multi-line
137+ # job output) avoids the YAML/templating escape pitfalls that
138+ # silently drop the section when fed via `body:` in softprops/action.
139+ TAGMSG=$(git for-each-ref "refs/tags/${{ github.ref_name }}" \
139140 --format='%(contents:subject)%0a%0a%(contents:body)' \
140141 | /usr/bin/sed -e '/^-----BEGIN PGP SIGNATURE-----$/,$d')
141- # Fallback if the tag is lightweight (no annotation).
142- if [[ -z "$(printf '%s' "$MSG" | tr -d '[:space:]')" ]]; then
143- MSG="Tag ${{ github.ref_name }} — see commit history for details."
142+ if [[ -z "$(printf '%s' "$TAGMSG" | tr -d '[:space:]')" ]]; then
143+ TAGMSG="Tag ${{ github.ref_name }} — see commit history for details."
144144 fi
145- {
146- echo "notes<<NOTES_EOF"
147- echo "$MSG"
148- echo "NOTES_EOF"
149- } >> "$GITHUB_OUTPUT"
150- # Echo for debugging in CI logs
151- printf '%s\n' "----- release notes preview -----"
152- printf '%s\n' "$MSG"
153- printf '%s\n' "---------------------------------"
145+ cat > build/release-body.md <<MARKDOWN
146+ ## What's new in ${{ github.ref_name }}
154147
155- - name : Create GitHub Release
156- if : startsWith(github.ref, 'refs/tags/')
157- uses : softprops/action-gh-release@v2
158- with :
159- files : ${{ env.DMG_PATH }}
160- generate_release_notes : true
161- fail_on_unmatched_files : true
162- body : |
163- ## What's new in ${{ github.ref_name }}
164-
165- ${{ steps.tagnotes.outputs.notes }}
148+ $TAGMSG
166149
167- ---
150+ ---
168151
152+ ## ⚠️ First Launch — Read This First
169153
170- ## ⚠️ First Launch — Read This First
154+ This release is **ad-hoc signed** (not notarized by Apple, because
155+ notarization requires a paid Developer ID). On first launch macOS
156+ Gatekeeper will likely refuse to open the app with one of:
171157
172- This release is **ad-hoc signed** (not notarized by Apple, because
173- notarization requires a paid Developer ID). On first launch macOS
174- Gatekeeper will likely refuse to open the app with one of:
158+ - **"FreeDisplay" is damaged and can't be opened. You should move it to the Trash.**
159+ - **"FreeDisplay" can't be opened because Apple cannot check it for malicious software.**
175160
176- - **"FreeDisplay" is damaged and can't be opened. You should move it to the Trash.**
177- - **"FreeDisplay" can't be opened because Apple cannot check it for malicious software.**
161+ **Neither message is real damage** — both are Gatekeeper warnings
162+ against unsigned/un-notarized apps. Pick one of the two fixes:
178163
179- **Neither message is real damage** — both are Gatekeeper warnings
180- against unsigned/un-notarized apps. Pick one of the two fixes:
164+ **Option A (recommended, one terminal command):**
165+ \`\`\`bash
166+ sudo xattr -rd com.apple.quarantine /Applications/FreeDisplay.app
167+ \`\`\`
168+ Then double-click FreeDisplay to launch.
181169
182- **Option A (recommended, one terminal command):**
183- ```bash
184- sudo xattr -rd com.apple.quarantine /Applications/FreeDisplay.app
185- ```
186- Then double-click FreeDisplay to launch.
170+ **Option B (GUI):**
171+ System Settings → Privacy & Security → scroll down to the
172+ "FreeDisplay was blocked…" message → click **Open Anyway**.
187173
188- **Option B (GUI):**
189- System Settings → Privacy & Security → scroll down to the
190- "FreeDisplay was blocked…" message → click **Open Anyway**.
174+ After this one-time approval the app launches normally forever.
191175
192- After this one-time approval the app launches normally forever.
176+ ---
193177
194- ---
178+ ## Installation
195179
196- ## Installation
180+ 1. Download \`FreeDisplay-${{ steps.ver.outputs.ref }}.dmg\` below
181+ 2. Open the DMG, drag **FreeDisplay.app** into **Applications**
182+ 3. Apply one of the fixes above
183+ 4. Click the display icon in your menu bar to use FreeDisplay
197184
198- 1. Download `FreeDisplay-${{ steps.ver.outputs.ref }}.dmg` below
199- 2. Open the DMG, drag **FreeDisplay.app** into **Applications**
200- 3. Apply one of the fixes above
201- 4. Click the display icon in your menu bar to use FreeDisplay
185+ ## Documentation
202186
203- ## Documentation
187+ - [English README](https://github.com/Akuwatoga/FreeDisplay/blob/main/README.md)
188+ - [简体中文 README](https://github.com/Akuwatoga/FreeDisplay/blob/main/README.zh-CN.md)
189+ MARKDOWN
204190
205- - [English README](https://github.com/Akuwatoga/FreeDisplay/blob/main/README.md)
206- - [简体中文 README](https://github.com/Akuwatoga/FreeDisplay/blob/main/README.zh-CN.md)
191+ echo "----- release body preview -----"
192+ /usr/bin/cat build/release-body.md
193+ echo "----- end preview -----"
207194
208- ---
195+ - name : Create GitHub Release
196+ if : startsWith(github.ref, 'refs/tags/')
197+ uses : softprops/action-gh-release@v2
198+ with :
199+ files : ${{ env.DMG_PATH }}
200+ body_path : build/release-body.md
201+ # We provide the full body via body_path; let GitHub still append
202+ # the auto-generated "Full Changelog" link below it.
203+ generate_release_notes : true
204+ append_body : true
205+ fail_on_unmatched_files : true
0 commit comments