Skip to content

Commit 9d3750c

Browse files
cmaglieumbynos
andauthored
Fix macosx ventura updater (#768)
* Upload the signed bundle to s3 (#756) * remove some code duplication regarding prerelease calculation * add upload of the notarized bundle to s3 download bucket * add upload of the notarized bundle to s3 download bucket * add json to enable autoupdate with the new agent logic (#759) * add json to enable autoupdate with the new agent logic * binary output of the archive https://unix.stackexchange.com/questions/3675/how-can-i-get-a-base64-encoded-shax-on-the-cli * workaround to allow darwin-arm64 to autoupdate * parallelize bundle creation and notarization. This will be helpful if/when we decide to build for darwin-arm64. For now this is useful because we do not offer a binary for m1 yet, `runtime.GOARCH` on an m1 machine returns ARM64, so the call for the update file would fail * Implemented the autoupdater for MacOS * Added more logging and fixed upgrade procedure * Use MacOS openApplicationAtURL syscall to re-run updated app * Bump minimum required macosx version to 10.15 This is required because the auto-update uses the function warning: 'openApplicationAtURL:configuration:completionHandler:' is only available on macOS 10.15 or newer [-Wunguarded-availability-new] * Removed diff-based updates * Fixed update URL for different update methods * fix invalid workflow * Always complete the old auto-upgrade procedure This is required for clients upgrading from versions <=1.2.7 * Moved the syscall to openApplicationAtURL inside Systray * Added fallback restart for macosx. * added some more logging --------- Co-authored-by: Umberto Baldi <[email protected]> Co-authored-by: Umberto Baldi <[email protected]>
1 parent 6c9aec1 commit 9d3750c

File tree

10 files changed

+363
-130
lines changed

10 files changed

+363
-130
lines changed

Diff for: .github/workflows/publish-go-tester-task.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,9 @@ jobs:
132132

133133
- name: Build the Agent for macos
134134
env:
135-
MACOSX_DEPLOYMENT_TARGET: 10.11 # minimum supported version for mac
136-
CGO_CFLAGS: -mmacosx-version-min=10.11
137-
CGO_LDFLAGS: -mmacosx-version-min=10.11
135+
MACOSX_DEPLOYMENT_TARGET: 10.15 # minimum supported version for mac
136+
CGO_CFLAGS: -mmacosx-version-min=10.15
137+
CGO_LDFLAGS: -mmacosx-version-min=10.15
138138
run: task go:build
139139
if: runner.os == 'macOS'
140140

Diff for: .github/workflows/release.yml

+61-28
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ on:
88
env:
99
# As defined by the Taskfile's PROJECT_NAME variable
1010
PROJECT_NAME: arduino-create-agent
11-
TARGET: "/CreateAgent/Stable"
11+
TARGET: "/CreateAgent/Stable/"
1212
OLD_TARGET: "/CreateBridge/" # compatibility with older releases (we can't change config.ini)
1313
VERSION_TARGET: "arduino-create-static/agent-metadata/"
1414
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
@@ -26,6 +26,8 @@ env:
2626
jobs:
2727
# The build job is responsible for: configuring the environment, testing and compiling process
2828
build:
29+
outputs:
30+
prerelease: ${{ steps.prerelease.outputs.IS_PRE }}
2931
strategy:
3032
matrix:
3133
os: [ubuntu-20.04, windows-2019, macos-12]
@@ -110,9 +112,9 @@ jobs:
110112

111113
- name: Build the Agent for macos
112114
env:
113-
MACOSX_DEPLOYMENT_TARGET: 10.11 # minimum supported version for mac
114-
CGO_CFLAGS: -mmacosx-version-min=10.11
115-
CGO_LDFLAGS: -mmacosx-version-min=10.11
115+
MACOSX_DEPLOYMENT_TARGET: 10.15 # minimum supported version for mac
116+
CGO_CFLAGS: -mmacosx-version-min=10.15
117+
CGO_LDFLAGS: -mmacosx-version-min=10.15
116118
run: task go:build
117119
if: matrix.os == 'macos-12'
118120

@@ -121,6 +123,14 @@ jobs:
121123
run: go-selfupdate ${{ env.PROJECT_NAME }}${{ matrix.ext }} ${TAG_VERSION}
122124
if: matrix.arch != '-386' && steps.prerelease.outputs.IS_PRE != 'true'
123125

126+
# for now we do not distribute m1 build, this is a workaround for now
127+
- name: Copy autoupdate file for darwin-arm64 (m1 arch)
128+
working-directory: public/
129+
run: |
130+
cp darwin-amd64.json darwin-arm64.json
131+
cp ${TAG_VERSION}/darwin-amd64.gz ${TAG_VERSION}/darwin-arm64.gz
132+
if: matrix.os == 'macos-12' && steps.prerelease.outputs.IS_PRE != 'true'
133+
124134
- name: Create autoupdate files for win32
125135
run: go-selfupdate -platform windows${{ matrix.arch }} ${{ env.PROJECT_NAME }}${{ matrix.ext }} ${TAG_VERSION}
126136
if: matrix.arch == '-386' && matrix.os == 'windows-2019' && steps.prerelease.outputs.IS_PRE != 'true'
@@ -142,6 +152,11 @@ jobs:
142152
create-macos-bundle:
143153
needs: build
144154

155+
# for not they are exaclty the same
156+
strategy:
157+
matrix:
158+
arch: [amd64, arm64]
159+
145160
runs-on: macos-12
146161
env:
147162
EXE_PATH: "skel/ArduinoCreateAgent.app/Contents/MacOS/"
@@ -156,7 +171,7 @@ jobs:
156171
- name: Download artifact
157172
uses: actions/download-artifact@v3
158173
with:
159-
name: ${{ env.PROJECT_NAME }}-macos-12-amd64
174+
name: ${{ env.PROJECT_NAME }}-macos-12-amd64 # if we want to support darwin-arm64 in the future for real this has to change.
160175
path: ${{ env.EXE_PATH }}
161176

162177
- name: Remove placeholder file
@@ -195,31 +210,37 @@ jobs:
195210
EOF
196211
197212
- name: Tar bundle to keep permissions
198-
run: tar -cvf ArduinoCreateAgent.app.tar -C skel/ .
213+
run: tar -cvf ArduinoCreateAgent.app_${{ matrix.arch }}.tar -C skel/ .
199214

200215
- name: Upload artifacts
201216
uses: actions/upload-artifact@v3
202217
with:
203218
if-no-files-found: error
204-
name: ArduinoCreateAgent.app
205-
path: ArduinoCreateAgent.app.tar
219+
name: ArduinoCreateAgent.app_${{ matrix.arch }}
220+
path: ArduinoCreateAgent.app_${{ matrix.arch }}.tar
206221

207-
# The notarize-macos job will download the macos bundle from the previous job, sign, notarize and re-upload it.
222+
# The notarize-macos job will download the macos bundle from the previous job, sign, notarize and re-upload it, uploading it also on s3 download servers for the autoupdate.
208223
notarize-macos:
209224
name: Notarize bundle
225+
226+
# for not they are exaclty the same
227+
strategy:
228+
matrix:
229+
arch: [amd64, arm64]
230+
210231
runs-on: macos-12
211232
env:
212233
GON_PATH: ${{ github.workspace }}/gon
213-
needs: create-macos-bundle
234+
needs: [build, create-macos-bundle]
214235

215236
steps:
216237
- name: Download artifact
217238
uses: actions/download-artifact@v3
218239
with:
219-
name: ArduinoCreateAgent.app
240+
name: ArduinoCreateAgent.app_${{ matrix.arch }}
220241

221242
- name: un-Tar bundle
222-
run: tar -xvf ArduinoCreateAgent.app.tar
243+
run: tar -xvf ArduinoCreateAgent.app_${{ matrix.arch }}.tar
223244

224245
- name: Import Code-Signing Certificates
225246
run: |
@@ -270,18 +291,37 @@ jobs:
270291
# Ask Gon for zip output to force notarization process to take place.
271292
# The CI will upload the zip output
272293
zip {
273-
output_path = "ArduinoCreateAgent.app_notarized.zip"
294+
output_path = "ArduinoCreateAgent.app_${{ matrix.arch }}_notarized.zip"
274295
}
275296
EOF
276297
277298
- name: Sign and notarize binary
278299
run: gon -log-level=debug -log-json "${{ env.GON_CONFIG_PATH }}"
300+
301+
- name: Upload autoupdate bundle to Arduino downloads servers
302+
run: aws s3 cp ArduinoCreateAgent.app_${{ matrix.arch }}_notarized.zip s3://${{ secrets.DOWNLOADS_BUCKET }}${{ env.TARGET }}${GITHUB_REF/refs\/tags\//}/ # the version should be created in th the build job
303+
if: ${{ needs.build.outputs.prerelease != 'true' }}
304+
305+
- name: Generate json file used for the new autoupdate
306+
run: |
307+
cat > darwin-${{ matrix.arch }}-bundle.json <<EOF
308+
{
309+
"Version": "${GITHUB_REF/refs\/tags\//}",
310+
"Sha256": "$(shasum -a 256 ArduinoCreateAgent.app_${{ matrix.arch }}_notarized.zip | awk '{print $1}' | xxd -r -p | base64)"
311+
}
312+
EOF
313+
if: ${{ needs.build.outputs.prerelease != 'true' }}
314+
315+
- name: Upload autoupdate files to Arduino downloads servers
316+
run: |
317+
aws s3 cp darwin-${{ matrix.arch }}-bundle.json s3://${{ secrets.DOWNLOADS_BUCKET }}${{ env.TARGET }}
318+
if: ${{ needs.build.outputs.prerelease != 'true' }}
279319

280320
- name: Upload artifact
281321
uses: actions/upload-artifact@v3
282322
with:
283-
name: ArduinoCreateAgent.app_notarized
284-
path: ArduinoCreateAgent.app_notarized.zip
323+
name: ArduinoCreateAgent.app_${{ matrix.arch }}_notarized
324+
path: ArduinoCreateAgent.app_${{ matrix.arch }}_notarized.zip
285325
if-no-files-found: error
286326

287327
# This job is responsible for generating the installers (using installbuilder)
@@ -334,7 +374,8 @@ jobs:
334374
install-builder-name: osx
335375
executable-path: artifacts/macos/ArduinoCreateAgent.app
336376
installer-extension: .app
337-
artifact-name: ArduinoCreateAgent.app_notarized # this artifact contains the Contents directory
377+
artifact-name: ArduinoCreateAgent.app_amd64_notarized # this artifact contains the Contents directory
378+
# here we support only amd64 for macos. Hopefully in the future installbuilder for macOS will be removed, see https://github.com/arduino/arduino-create-agent/issues/739
338379

339380
container:
340381
image: floydpink/ubuntu-install-builder:22.10.0
@@ -493,7 +534,7 @@ jobs:
493534

494535
create-release:
495536
runs-on: ubuntu-20.04
496-
needs: code-sign-mac-installers
537+
needs: [build, code-sign-mac-installers]
497538

498539
steps:
499540
- name: Checkout
@@ -504,14 +545,6 @@ jobs:
504545
- name: Download artifact
505546
uses: actions/download-artifact@v3 # download all the artifacts
506547

507-
- name: Identify Prerelease
508-
# This is a workaround while waiting for create-release action to implement auto pre-release based on tag
509-
id: prerelease
510-
run: |
511-
curl -L -s https://github.com/fsaintjacques/semver-tool/archive/3.1.0.zip -o /tmp/3.1.0.zip
512-
unzip -p /tmp/3.1.0.zip semver-tool-3.1.0/src/semver >/tmp/semver && chmod +x /tmp/semver
513-
if [[ $(/tmp/semver get prerel ${GITHUB_REF/refs\/tags\//}) ]]; then echo "IS_PRE=true" >> $GITHUB_OUTPUT; fi
514-
515548
# mandatory step because upload-release-action does not support multiple folders
516549
- name: prepare artifacts for the release
517550
run: |
@@ -562,7 +595,7 @@ jobs:
562595
release_name: ${{ github.ref }}
563596
body: ${{ steps.release_body.outputs.RBODY}}
564597
draft: false
565-
prerelease: ${{ steps.prerelease.outputs.IS_PRE }}
598+
prerelease: ${{ needs.build.outputs.prerelease }}
566599

567600
- name: Upload release files on Github
568601
uses: svenstaro/upload-release-action@v2
@@ -574,10 +607,10 @@ jobs:
574607

575608
- name: Upload release files on Arduino downloads servers
576609
run: aws s3 sync release/ s3://${{ secrets.DOWNLOADS_BUCKET }}${{ env.TARGET }}
577-
if: steps.prerelease.outputs.IS_PRE != 'true'
610+
if: ${{ needs.build.outputs.prerelease != 'true' }}
578611

579612
- name: Update version file (used by frontend to trigger autoupdate and create filename)
580613
run: |
581614
echo {\"Version\": \"${GITHUB_REF##*/}\"} > /tmp/agent-version.json
582615
aws s3 cp /tmp/agent-version.json s3://${{ env.VERSION_TARGET }}
583-
if: steps.prerelease.outputs.IS_PRE != 'true'
616+
if: ${{ needs.build.outputs.prerelease != 'true' }}

Diff for: go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ require (
1212
github.com/gin-gonic/gin v1.8.1
1313
github.com/go-ini/ini v1.62.0
1414
github.com/googollee/go-socket.io v0.0.0-20181101151912-c8aeb1ed9b49
15-
github.com/kr/binarydist v0.1.0
1615
github.com/mattn/go-shellwords v1.0.12
1716
github.com/mitchellh/go-homedir v1.1.0
1817
github.com/oleksandr/bonjour v0.0.0-20210301155756-30f43c61b915
@@ -56,6 +55,7 @@ require (
5655
github.com/juju/errors v0.0.0-20200330140219-3fe23663418f // indirect
5756
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
5857
github.com/klauspost/compress v1.15.13 // indirect
58+
github.com/kr/binarydist v0.1.0 // indirect
5959
github.com/kr/fs v0.1.0 // indirect
6060
github.com/leodido/go-urn v1.2.1 // indirect
6161
github.com/manveru/faker v0.0.0-20171103152722-9fbc68a78c4d // indirect

Diff for: systray/exec_darwin.go

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package systray
2+
3+
/*
4+
#cgo CFLAGS: -x objective-c
5+
#cgo LDFLAGS: -framework Cocoa
6+
#import <Cocoa/Cocoa.h>
7+
8+
char **makeCharArray(int size) {
9+
return calloc(sizeof(char*), size);
10+
}
11+
12+
void setCharArray(char **a, int n, char *s) {
13+
a[n] = s;
14+
}
15+
16+
void freeCharArray(char **a, int size) {
17+
int i;
18+
for (i = 0; i < size; i++) {
19+
free(a[i]);
20+
}
21+
free(a);
22+
}
23+
24+
void runApplication(const char *path, const char **argv, int argc) {
25+
NSMutableArray<NSString *> *stringArray = [NSMutableArray array];
26+
for (int i=0; i<argc; i++) {
27+
NSString *arg = [NSString stringWithCString:argv[i] encoding:NSUTF8StringEncoding];
28+
[stringArray addObject:arg];
29+
}
30+
NSArray<NSString *> *arguments = [NSArray arrayWithArray:stringArray];
31+
32+
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
33+
NSURL *url = [NSURL fileURLWithPath:@(path) isDirectory:NO];
34+
35+
NSWorkspaceOpenConfiguration* configuration = [NSWorkspaceOpenConfiguration new];
36+
//[configuration setEnvironment:env];
37+
[configuration setPromptsUserIfNeeded:YES];
38+
[configuration setCreatesNewApplicationInstance:YES];
39+
[configuration setArguments:arguments];
40+
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
41+
[ws openApplicationAtURL:url configuration:configuration completionHandler:^(NSRunningApplication* app, NSError* error) {
42+
dispatch_semaphore_signal(semaphore);
43+
}];
44+
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
45+
}
46+
*/
47+
import "C"
48+
import (
49+
"os/exec"
50+
"path/filepath"
51+
52+
"github.com/sirupsen/logrus"
53+
)
54+
55+
func execApp(path string, args ...string) error {
56+
if filepath.Ext(path) != ".app" {
57+
// If not .app, fallback to standard process execution
58+
logrus.WithField("path", path).WithField("args", args).Info("Running new app with os/exec.Exec")
59+
cmd := exec.Command(path, args...)
60+
return cmd.Start()
61+
}
62+
63+
logrus.WithField("path", path).WithField("args", args).Info("Running new app with openApplicationAtURL")
64+
argc := C.int(len(args))
65+
argv := C.makeCharArray(argc)
66+
for i, arg := range args {
67+
C.setCharArray(argv, C.int(i), C.CString(arg))
68+
}
69+
70+
C.runApplication(C.CString(path), argv, argc)
71+
72+
C.freeCharArray(argv, argc)
73+
return nil
74+
}

Diff for: systray/exec_default.go

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2022 Arduino SA
2+
//
3+
// This program is free software: you can redistribute it and/or modify
4+
// it under the terms of the GNU Affero General Public License as published
5+
// by the Free Software Foundation, either version 3 of the License, or
6+
// (at your option) any later version.
7+
//
8+
// This program is distributed in the hope that it will be useful,
9+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
// GNU Affero General Public License for more details.
12+
//
13+
// You should have received a copy of the GNU Affero General Public License
14+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
15+
16+
//go:build !darwin
17+
18+
package systray
19+
20+
import "os/exec"
21+
22+
// default execApp from golang
23+
func execApp(path string, args ...string) error {
24+
cmd := exec.Command(path, args...)
25+
return cmd.Start()
26+
}

Diff for: systray/systray.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package systray
1818
import (
1919
"fmt"
2020
"os"
21-
"os/exec"
2221
"strings"
2322

2423
"github.com/arduino/go-paths-helper"
@@ -69,8 +68,7 @@ func (s *Systray) Restart() {
6968
}
7069

7170
// Launch executable
72-
cmd := exec.Command(s.path, args...)
73-
err := cmd.Start()
71+
err := execApp(s.path, args...)
7472
if err != nil {
7573
log.Printf("Error restarting process: %v\n", err)
7674
return

Diff for: update.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,15 @@ import (
3535
)
3636

3737
func updateHandler(c *gin.Context) {
38-
restartPath, err := updater.CheckForUpdates(version, *updateURL, *updateURL, *appName)
38+
restartPath, err := updater.CheckForUpdates(version, *updateURL, *appName)
3939
if err != nil {
4040
c.JSON(500, gin.H{"error": err.Error()})
4141
return
4242
}
4343
c.JSON(200, gin.H{"success": "Please wait a moment while the agent reboots itself"})
44-
Systray.RestartWith(restartPath)
44+
if restartPath == "quit" {
45+
Systray.Quit()
46+
} else {
47+
Systray.RestartWith(restartPath)
48+
}
4549
}

0 commit comments

Comments
 (0)