11name : sign-pkg-mac
22description :
3- Sign and package the macOS Linden viewer.
3+ Sign and package the macOS Alchemy viewer.
44
55inputs :
66 imagename :
@@ -39,7 +39,7 @@ inputs:
3939 description : " setting for all steps"
4040 type : string
4141 required : false
42- default : " Second Life "
42+ default : " Alchemy "
4343
4444runs :
4545 using : composite
@@ -50,127 +50,93 @@ runs:
5050 name : macOS-app
5151 path : .tarball
5252
53- - name : Unpack the tarball
53+ - name : Unpack the tarball and set vars
5454 id : unpack
5555 shell : bash
5656 run : |
5757 set -x
5858 mkdir -p ".app"
5959 tar xJf .tarball/* -C ".app"
6060
61- - name : Set up the app sparseimage
62- shell : bash
63- run : |
64- set -x -e
65- # MBW -- If the mounted volume name changes, it breaks the .DS_Store's
66- # background image and icon positioning. If we really need
67- # differently named volumes, we'll need to create multiple DS_Store
68- # file images, or use some other trick.
69- # DO NOT CHANGE without understanding comment above
70- volname="${{ inputs.channel_vendor_base }} Installer"
71- sparsename="${{ inputs.imagename}}.sparseimage"
72- rm "$sparsename" || true
73- echo "sparsename=$sparsename" >> "$GITHUB_ENV"
74-
75- # The capacity of the .sparseimage, which sometimes needs to be
76- # changed, is hard-coded here instead of defined as an input because
77- # changing it here allows us to rerun just this job. Changing it in
78- # the viewer's build.yaml would require first rebuilding the viewer.
79- hdiutil create "$sparsename" -volname "$volname" -fs HFS+ \
80- -type SPARSE -megabytes 2000 -layout SPUD
81-
82- # mount the image and get the name of the mount point and device node
83- hdi_output="$(hdiutil attach -private "$sparsename")"
84-
85- # with set -e in effect, test has the force of an assert
86- [[ "$hdi_output" =~ (/dev/disk[0-9]+)[^s] ]]
87- devfile="${BASH_REMATCH[1]}"
88- echo "devfile=$devfile" >> "$GITHUB_ENV"
89- [[ "$hdi_output" =~ HFS[[:space:]]+(.+) ]]
90- volpath="${BASH_REMATCH[1]}"
91-
92- # copy everything to the mounted sparseimage
93-
94- # What follows is oddly based on a predefined .DS_Store file, which
95- # apparently used to be one of several -- despite the presence of
96- # dmg-cleanup.applescript since at least 2009. Leaving legacy behavior
97- # for now, albeit with files transplanted from the viewer repo.
98- dmg_prefill="${{ github.action_path }}/installer/release-dmg"
99- for f in _VolumeIcon.icns _DS_Store background.jpg
100- do
101- dest="$volpath/${f/_/.}"
102- cp -v "$dmg_prefill/$f" "$dest"
103- # hide the files used only to control the Finder display
104- SetFile -a V "$dest"
105- done
106-
107- # don't forget the application itself
10861 artifact_app="$(ls -dt .app/*.app | head -n 1)"
109- cp -a "$artifact_app" "$volpath/"
110- app_name="$(basename "$artifact_app")"
111- echo "app_path=$volpath/$app_name" >> "$GITHUB_ENV"
112-
113- # Create the alias file (which is a resource file) from the .r
114- Rez "$dmg_prefill/Applications-alias.r" -o "$volpath/Applications"
62+ echo "app_path=$artifact_app" >> "$GITHUB_ENV"
11563
116- # Set the alias file's alias bit
117- SetFile -a A "$volpath/Applications"
64+ - name : Setup Python
65+ uses : actions/setup-python@v6
66+ with :
67+ python-version : ' 3.13'
11868
119- # Set the disk image root's custom icon bit
120- SetFile -a C "$volpath"
69+ - name : Install Python dependencies
70+ shell : bash
71+ run : pip install "dmgbuild[badge_icons]"
12172
122- - name : Sign and notarize the app
73+ - name : Sign the app bundle
12374 if : inputs.cert_base64 && inputs.cert_name && inputs.cert_pass && inputs.note_user && inputs.note_pass && inputs.note_team
12475 shell : bash
12576 env :
12677 cert_base64 : " ${{ inputs.cert_base64 }}"
12778 cert_name : " ${{ inputs.cert_name }}"
12879 cert_pass : " ${{ inputs.cert_pass }}"
129- note_user : " ${{ inputs.note_user }}"
130- note_pass : " ${{ inputs.note_pass }}"
131- note_team : " ${{ inputs.note_team }}"
13280 run : |
133- # Sign the app; do this in the copy that's in the .dmg so that the
134- # extended attributes used by the signature are preserved; moving the
135- # files would leave them behind and invalidate the signatures.
13681 "${{ github.action_path }}/sign.sh" "${{ env.app_path }}"
13782
138- - name : Unmount the sparseimage
139- # unmount even if the above fails
140- if : ${{ ! cancelled() }}
83+ - name : Build DMG
14184 shell : bash
14285 run : |
143- # Empirically, on GitHub we've hit errors like:
144- # hdiutil: couldn't eject "disk10" - Resource busy
145- retries=3
146- retry_wait=2
147- for (( attempt=0; attempt < $retries; attempt+=1 ))
148- do
149- if [[ $attempt -gt 0 ]]
150- then
151- echo "detach $attempt failed, waiting $retry_wait seconds before retrying" >&2
152- sleep $retry_wait
153- (( retry_wait*=2 ))
154- fi
155- hdiutil detach -force ${{ env.devfile }} && break
156- done
157- if [[ $? -ne 0 ]]
158- then echo "::warning::$retries attempts to detach ${{ env.devfile }} failed"
159- fi
86+ mkdir -p .installer
87+ dmgbuild -s "${{ github.action_path }}/dmgsettings.py" -D app="${{ env.app_path }}" "${{ inputs.channel_vendor_base }} Installer" .installer/${{ inputs.imagename }}.dmg
88+ installer=".installer/${{ inputs.imagename }}.dmg"
89+ echo "installer=$installer" >> "$GITHUB_ENV"
16090
161- - name : Package the sparseimage as .dmg
91+ - name : Sign and notarize the dmg
92+ if : inputs.cert_base64 && inputs.cert_name && inputs.cert_pass && inputs.note_user && inputs.note_pass && inputs.note_team
16293 shell : bash
94+ env :
95+ cert_name : " ${{ inputs.cert_name }}"
96+ note_user : " ${{ inputs.note_user }}"
97+ note_pass : " ${{ inputs.note_pass }}"
98+ note_team : " ${{ inputs.note_team }}"
16399 run : |
100+ codesign --verbose --force --timestamp --keychain viewer.keychain --sign "$cert_name" "${{ env.installer }}"
101+
102+ set -x -e
103+
104+ credentials=(--apple-id "$note_user" --password "$note_pass" --team-id "$note_team")
105+
106+ # Here we send the notarization request to Apple's Notarization service,
107+ # waiting for the result. This typically takes a few seconds inside a CI
108+ # environment, but it might take more depending on the App characteristics.
109+ # Visit the Notarization docs for more information and strategies on how to
110+ # optimize it if you're curious.
111+ # emit notarytool output to stderr in real time but also capture in variable
112+ set +e
113+ output="$(xcrun notarytool submit "${{ env.installer }}" --wait \
114+ "${credentials[@]}" 2>&1 | \
115+ tee /dev/stderr ; \
116+ exit "${PIPESTATUS[0]}")"
117+ # Without the final 'exit' above, we'd be checking the rc from 'tee' rather
118+ # than 'notarytool'.
119+ rc=$?
120+ set +x
121+ [[ "$output" =~ 'id: '([^[:space:]]+) ]]
122+ match=$?
164123 set -x
165- mkdir -p .installer
166- installer=".installer/${{ inputs.imagename }}.dmg"
167- rm "$installer" || true
168- # pass installer to next step
169- echo "installer=$installer" >> "$GITHUB_ENV"
124+ # Run notarytool log if we find an id: anywhere in the output, regardless of
125+ # rc: notarytool can terminate with rc 0 even if it fails.
126+ if [[ $match -eq 0 ]]
127+ then
128+ xcrun notarytool log "${BASH_REMATCH[1]}" "${credentials[@]}"
129+ fi
130+ [[ $rc -ne 0 ]] && exit $rc
131+ set -e
132+
133+ # Finally, we need to "attach the staple" to our executable, which will allow
134+ # our app to be validated by macOS even when an internet connection is not
135+ # available.
136+ xcrun stapler staple "${{ env.installer }}"
170137
171- hdiutil convert "${{ env.sparsename }}" -format ULMO \
172- -o "$installer"
173- rm "${{ env.sparsename }}"
138+ spctl -a -tinstall -vvvv "${{ env.installer }}"
139+ xcrun stapler validate "${{ env.installer }}"
174140
175141 - name : Post the installer
176142 uses : actions/upload-artifact@v4
0 commit comments