Skip to content

Commit

Permalink
Add macOS support (#62)
Browse files Browse the repository at this point in the history
  • Loading branch information
Beyley authored Jun 4, 2024
2 parents e077327 + 6bfc9d8 commit 08102da
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 18 deletions.
51 changes: 49 additions & 2 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest, windows-latest ]
os: [ ubuntu-latest, windows-latest, macos-latest ]
name: Build, Test, and Upload Builds (${{ matrix.os }})
runs-on: ${{ matrix.os }}
steps:
Expand All @@ -35,13 +35,51 @@ jobs:
if: matrix.os == 'windows-latest'
shell: bash
run: dotnet publish -c Release -r win-x64 --self-contained Refresher //p:Version=${VERSION}

- name: Publish for macOS x64
if: matrix.os == 'macos-latest'
shell: bash
run: dotnet publish -c Release -r osx-x64 --self-contained Refresher /p:Version=${VERSION}

- name: Publish for macOS ARM64
if: matrix.os == 'macos-latest'
shell: bash
run: dotnet publish -c Release -r osx-arm64 --self-contained Refresher /p:Version=${VERSION}

- name: Create macOS universal2 binary
if: matrix.os == 'macos-latest'
shell: bash
working-directory: Refresher/bin/Release/net8.0/
run: |
mkdir -p osx-universal2/publish/Refresher.app/Contents/MacOS
cp -r osx-arm64/publish/*.app/Contents/ osx-universal2/publish/Refresher.app/Contents/
for file in $(find osx-arm64/publish/*.app/Contents/MacOS); do
if [[ "$(file $file)" == *"Mach-O"* ]]; then
if [[ "$(lipo -archs $file)" != *"x86_64 arm64"* ]]; then
lipo -create osx-arm64/publish/*.app/Contents/MacOS/$(basename $file) osx-x64/publish/*.app/Contents/MacOS/$(basename $file) -output osx-universal2/publish/Refresher.app/Contents/MacOS/$(basename $file);
fi;
fi;
done
rm -rfv osx-universal2/publish/Refresher.app/Contents/MacOS/*.app
codesign -fs - --deep osx-universal2/publish/Refresher.app
# We need to tarball our macOS and Linux builds, since the ZIP files created by upload-artifact do not retain extended unix file permissions,
# which means the executable bit is lost, which is un-ideal for end users, who will hit weird errors (especially relating to code signing on macOS)
- name: 'Tar macOS universal2 build'
if: matrix.os == 'macos-latest'
working-directory: Refresher/bin/Release/net8.0/osx-universal2/publish/
run: tar -cvf ../../../../../../Refresher-macOS.tar *.app
- name: 'Tar Linux x64'
if: matrix.os == 'ubuntu-latest'
working-directory: Refresher/bin/Release/net8.0/linux-x64/publish/
run: tar -cvf ../../../../../../Refresher.tar *

- name: Upload Linux x64 build
if: matrix.os == 'ubuntu-latest'
uses: actions/upload-artifact@v3.1.1
with:
name: "Refresher for Linux x64"
path: "Refresher/bin/Release/net8.0/linux-x64/publish/"
path: "Refresher.tar"
if-no-files-found: error
retention-days: 30

Expand All @@ -53,3 +91,12 @@ jobs:
path: "Refresher/bin/Release/net8.0-windows/win-x64/publish/"
if-no-files-found: error
retention-days: 30

- name: Upload macOS universal2 build
if: matrix.os == 'macos-latest'
uses: actions/upload-artifact@v3.1.1
with:
name: "Refresher for macOS universal2"
path: "Refresher-macOS.tar"
if-no-files-found: error
retention-days: 30
71 changes: 61 additions & 10 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest, windows-latest ]
os: [ ubuntu-latest, windows-latest, macos-latest ]
name: Build, Test, and Upload Builds (${{ matrix.os }})
runs-on: ${{ matrix.os }}
steps:
Expand Down Expand Up @@ -39,12 +39,48 @@ jobs:
shell: bash
run: dotnet publish -c Release -r win-x64 --self-contained Refresher //p:Version=${VERSION}

- name: Publish for macOS x64
if: matrix.os == 'macos-latest'
shell: bash
run: dotnet publish -c Release -r osx-x64 --self-contained Refresher /p:Version=${VERSION}

- name: Publish for macOS ARM64
if: matrix.os == 'macos-latest'
shell: bash
run: dotnet publish -c Release -r osx-arm64 --self-contained Refresher /p:Version=${VERSION}

- name: Create macOS universal2 binary
if: matrix.os == 'macos-latest'
shell: bash
working-directory: Refresher/bin/Release/net8.0/
run: |
mkdir -p osx-universal2/publish/Refresher.app/Contents/MacOS
cp -r osx-arm64/publish/*.app/Contents/ osx-universal2/publish/Refresher.app/Contents/
for file in $(find osx-arm64/publish/*.app/Contents/MacOS); do
if [[ "$(file $file)" == *"Mach-O"* ]]; then
if [[ "$(lipo -archs $file)" != *"x86_64 arm64"* ]]; then
lipo -create osx-arm64/publish/*.app/Contents/MacOS/$(basename $file) osx-x64/publish/*.app/Contents/MacOS/$(basename $file) -output osx-universal2/publish/Refresher.app/Contents/MacOS/$(basename $file);
fi;
fi;
done
rm -rfv osx-universal2/publish/Refresher.app/Contents/MacOS/*.app
codesign -fs - --deep osx-universal2/publish/Refresher.app
- name: 'Tar macOS universal2 build'
if: matrix.os == 'macos-latest'
working-directory: Refresher/bin/Release/net8.0/osx-universal2/publish/
run: tar -czvf ../../../../../../Refresher_for_macOS.tar.gz *.app
- name: 'Tar Linux x64'
if: matrix.os == 'ubuntu-latest'
working-directory: Refresher/bin/Release/net8.0/linux-x64/publish/
run: tar -czvf ../../../../../../Refresher_for_Linux_x64.tar.gz *

- name: Upload Linux x64 build
if: matrix.os == 'ubuntu-latest'
uses: actions/upload-artifact@v3.1.3
with:
name: "Refresher_for_Linux_x64"
path: "Refresher/bin/Release/net8.0/linux-x64/publish/"
path: "Refresher_for_Linux_x64.tar.gz"
if-no-files-found: error
retention-days: 1

Expand All @@ -56,28 +92,43 @@ jobs:
path: "Refresher/bin/Release/net8.0-windows/win-x64/publish/"
if-no-files-found: error
retention-days: 1

- name: Upload macOS universal2 build
if: matrix.os == 'macos-latest'
uses: actions/upload-artifact@v3.1.1
with:
name: "Refresher_for_macOS"
path: "Refresher_for_macOS.tar.gz"
if-no-files-found: error
retention-days: 1
release:
name: Release Built Artifacts
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Download artifacts
id: download
uses: actions/download-artifact@v3.0.2

- name: Create zip files
run: |
cd ${{steps.download.outputs.download-path}}/Refresher_for_Windows_x64
zip -r "${{steps.download.outputs.download-path}}/Refresh_for_Windows_x64.zip" .
cd ${{steps.download.outputs.download-path}}/Refresher_for_Linux_x64
zip -r "${{steps.download.outputs.download-path}}/Refresh_for_Linux_x64.zip" .
zip -r "${{steps.download.outputs.download-path}}/Refresher_for_Windows_x64.zip" .
cd ${{steps.download.outputs.download-path}}
mv ${{steps.download.outputs.download-path}}/Refresher_for_Linux_x64/*.tar.gz .
mv ${{steps.download.outputs.download-path}}/Refresher_for_macOS/*.tar.gz .
- uses: marvinpinto/action-automatic-releases@latest
- uses: ncipollo/release-action@v1.14.0
name: "Create release"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
token: "${{ secrets.GITHUB_TOKEN }}"
prerelease: false
draft: true
files: |
*.zip
generateReleaseNotes: true
bodyFile: "RELEASE_NOTES.md"
artifacts: |
*.zip
*.tar.gz
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -348,3 +348,6 @@ MigrationBackup/

# Ionide (cross platform F# VS Code tools) working folder
.ionide/

# macOS .DS_Store
.DS_Store
5 changes: 5 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
**NOTE:** Due to restrictions imposed on ad-hoc signed apps, macOS users may need to run the following command in Terminal before launching the app. Replace `/path/to` with the folder containing `Refresher.app`.

```
xattr -d com.apple.quarantine /path/to/Refresher.app
```
20 changes: 20 additions & 0 deletions Refresher/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key>
<string>com.littlebigrefresh.refresher</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2024 LittleBigRefresh</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CFBundleIconFile</key>
<string>refresher.icns</string>
<key>CFBundleName</key>
<string>Refresher</string>
<key>CFBundleExecutable</key>
<string>Refresher</string>
</dict>
</plist>
39 changes: 33 additions & 6 deletions Refresher/Refresher.csproj
Original file line number Diff line number Diff line change
@@ -1,11 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">

<!-- Horrible code wart to detect the target, there does not seem to be a proper way to do this in dotnet... -->

<!-- OS Detection default values -->
<PropertyGroup>
<IsWindows>False</IsWindows>
<IsOSX>False</IsOSX>
<IsLinux>False</IsLinux>
</PropertyGroup>

<!-- Check if we are targetting windows -->
<PropertyGroup Condition="($(RuntimeIdentifier.StartsWith('win')) And !$(RuntimeIdentifier.Equals('')) ) Or ( $(OS.Equals('Windows_NT')) And $(RuntimeIdentifier.Equals('')) ) ">
<IsWindows>True</IsWindows>
</PropertyGroup>

<!-- Check if we are targetting OSX -->
<PropertyGroup Condition="($(RuntimeIdentifier.StartsWith('osx')) And !$(RuntimeIdentifier.Equals('')) ) Or ( $([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX))) And $(RuntimeIdentifier.Equals('')) ) ">
<IsOSX>True</IsOSX>
</PropertyGroup>

<!-- Check if we target Linux (Too many names to check, so we base it off the previous two checks) -->
<PropertyGroup Condition=" !$(IsWindows) And !$(IsOSX)">
<IsLinux>True</IsLinux>
</PropertyGroup>

<PropertyGroup>
<OutputType>Exe</OutputType>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TargetFramework Condition="!$([MSBuild]::IsOSPlatform('Windows'))">net8.0</TargetFramework>
<TargetFramework Condition="$([MSBuild]::IsOSPlatform('Windows'))">net8.0-windows</TargetFramework>
<ApplicationId>Refresher</ApplicationId>
<TargetFramework Condition="!$(IsWindows)">net8.0</TargetFramework>
<TargetFramework Condition="$(IsWindows)">net8.0-windows</TargetFramework>
<RuntimeIdentifiers Condition="$(IsOSX)">osx-x64;osx-arm64</RuntimeIdentifiers>
<ApplicationIcon>Resources\refresher.ico</ApplicationIcon>
<BuiltInComInteropSupport Condition="'$(TargetFramework)' == 'net8.0-windows'">true</BuiltInComInteropSupport>
</PropertyGroup>
Expand All @@ -18,15 +44,16 @@
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="ELFSharp" Version="2.17.3" />
<PackageReference Include="Eto.Forms" Version="2.8.3" />
<PackageReference Condition="'$(TargetFramework)' != 'net8.0-windows'" Include="Eto.Platform.Gtk" Version="2.8.3" />
<PackageReference Condition="'$(TargetFramework)' == 'net8.0-windows'" Include="Eto.Platform.Wpf" Version="2.8.3" />
<!-- This could be IsLinux, but GTK is available on basically everything, so might as well use it as the true default -->
<PackageReference Condition="!$(IsWindows) And !$(IsOSX)" Include="Eto.Platform.Gtk" Version="2.8.3" />
<PackageReference Condition="$(IsOSX)" Include="Eto.Platform.Mac64" Version="2.8.3" />
<PackageReference Condition="$(IsWindows)" Include="Eto.Platform.Wpf" Version="2.8.3" />
<EmbeddedResource Include="Resources\refresher.ico" LogicalName="refresher.ico" />
<PackageReference Include="FluentFTP" Version="49.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SCEToolSharp" Version="1.1.5" />
<EmbeddedResource Include="Resources\Allefresher.prx" />
<PackageReference Include="Sentry" Version="4.4.0" />
<Content Include="Resources/refresher.icns" Link="refresher.icns" Condition="$(IsOSX)" />
</ItemGroup>


</Project>
Binary file added Refresher/Resources/refresher.icns
Binary file not shown.
12 changes: 12 additions & 0 deletions Refresher/UI/RefresherForm.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Eto.Drawing;
using Eto.Forms;
using Sentry;
using System.Runtime.InteropServices;

namespace Refresher.UI;

Expand All @@ -22,6 +23,9 @@ protected RefresherForm(string subtitle, Size size, bool padBottom = true)
this.Padding = new Padding(10, 10, 10, padBottom ? 10 : 0);

this.Icon = Icon.FromResource("refresher.ico");

if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
Menu = new RefresherMenuBar();
}

/// <summary>
Expand All @@ -38,4 +42,12 @@ protected RefresherForm(string subtitle, Size size, bool padBottom = true)

if (close) this.Visible = false;
}

public class RefresherMenuBar : MenuBar
{
public RefresherMenuBar() {
Style = "MenuBar";
IncludeSystemItems = MenuBarSystemItems.All;
}
}
}

0 comments on commit 08102da

Please sign in to comment.