Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .azure-pipelines/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,12 @@ jobs:
name: VSEng-MicroBuildVS2017
steps:
- template: templates/osx/pack.signed/step4-signpack.yml

- job: osx_step5
displayName: macOS (Prepare for distribution)
dependsOn: osx_step4
condition: succeeded()
pool:
vmImage: macOS 10.13
steps:
- template: templates/osx/pack.signed/step5-dist.yml
2 changes: 1 addition & 1 deletion .azure-pipelines/templates/osx/compile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ steps:
projects: 'Git-Credential-Manager.sln'
arguments: '--configuration=Mac$(configuration)'
publishTestResults: true
#testRunTitle: 'Unit tests - common (macOS)' # option not yet available
testRunTitle: 'Unit tests - common (macOS)'
26 changes: 3 additions & 23 deletions .azure-pipelines/templates/osx/pack.signed/step4-signpack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,8 @@ steps:
Remove-Item $dir\gcmcorepkg.zip -Force
displayName: 'Unzip signed package file'

- task: DownloadPipelineArtifact@1
displayName: Download signed payload
inputs:
buildType: 'current'
artifactName: 'tmp.macpayload_signed'
downloadPath: '$(Build.StagingDirectory)\payload'

- task: DownloadPipelineArtifact@1
displayName: Download symbols
inputs:
buildType: 'current'
artifactName: 'tmp.macsymbols'
downloadPath: '$(Build.StagingDirectory)\symbols'

- script: |
xcopy "$(Build.StagingDirectory)\pkg\*.pkg" "$(Build.StagingDirectory)\publish\"
xcopy "$(Build.StagingDirectory)\payload" "$(Build.StagingDirectory)\publish\payload\"
xcopy "$(Build.StagingDirectory)\symbols" "$(Build.StagingDirectory)\publish\payload.sym\"
displayName: Prepare final build artifact

- task: PublishPipelineArtifact@0
displayName: Publish signed installer artifacts
displayName: Upload signed installer
inputs:
artifactName: 'Installer.Mac.Signed'
targetPath: '$(Build.StagingDirectory)\publish'
artifactName: 'tmp.macinstaller_signed'
targetPath: '$(Build.StagingDirectory)\pkg'
36 changes: 36 additions & 0 deletions .azure-pipelines/templates/osx/pack.signed/step5-dist.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
steps:
- task: DownloadPipelineArtifact@1
displayName: Download signed installer
inputs:
buildType: 'current'
artifactName: 'tmp.macinstaller_signed'
downloadPath: '$(Build.StagingDirectory)\pkg'

- task: DownloadPipelineArtifact@1
displayName: Download signed payload
inputs:
buildType: 'current'
artifactName: 'tmp.macpayload_signed'
downloadPath: '$(Build.StagingDirectory)\payload'

- task: DownloadPipelineArtifact@1
displayName: Download symbols
inputs:
buildType: 'current'
artifactName: 'tmp.macsymbols'
downloadPath: '$(Build.StagingDirectory)\symbols'

- script: src/osx/SignFiles.Mac/notarize-pkg.sh -id "$(AppleId)" -p "$(AppleIdPassword)" -pkg '$(Build.StagingDirectory)\pkg\*.pkg'
displayName: Notarize and staple installer package

- script: |
cp "$(Build.StagingDirectory)/pkg/*.pkg" "$(Build.StagingDirectory)/publish/"
cp "$(Build.StagingDirectory)/payload" "$(Build.StagingDirectory)/publish/payload/"
cp "$(Build.StagingDirectory)/symbols" "$(Build.StagingDirectory)/publish/payload.sym/"
displayName: Prepare final build artifact

- task: PublishPipelineArtifact@0
displayName: Publish signed installer artifacts
inputs:
artifactName: 'Installer.Mac.Signed'
targetPath: '$(Build.StagingDirectory)/publish'
18 changes: 10 additions & 8 deletions .azure-pipelines/templates/windows/compile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@ steps:
otherConsoleOptions: '/Framework:.NETCoreApp,Version=2.1'
testRunTitle: 'Unit tests - common (Windows)'

- task: VSTest@2
displayName: Run helpers unit tests
inputs:
testAssemblyVer2: |
out\windows\*.Tests\bin\**\*.Tests.dll
configuration: 'Windows$(configuration)'
otherConsoleOptions: '/Framework:.NETFramework,Version=v4.6.1'
testRunTitle: 'Unit tests - helpers (Windows)'
# Uncomment once Windows helpers have unit tests
# - task: VSTest@2
# displayName: Run helpers unit tests
# inputs:
# testAssemblyVer2: |
# out\windows\*.Tests\bin\**\*.Tests.dll
# configuration: 'Windows$(configuration)'
# otherConsoleOptions: '/Framework:.NETFramework,Version=v4.6.1'
# testRunTitle: 'Unit tests - helpers (Windows)'

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT license.

#import "AHAppDelegate.h"
#import <Cocoa/Cocoa.h>

extern const NSString* kErrorDomain;

Expand All @@ -21,7 +22,10 @@ -(id)initWithBlock:(AHAppWorkBlock)block logger:(AHLogger*)logger;

-(void)run
{
NSApplication * application = [NSApplication sharedApplication];
NSApplication *application = [NSApplication sharedApplication];
NSMenu *mainMenu = [self createMainMenu];
[application setMainMenu:mainMenu];

[self setApplication:application];
[application setDelegate:self];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
Expand Down Expand Up @@ -57,6 +61,35 @@ -(void) stop
[[self application] postEvent:event atStart:NO];
}

-(NSMenu*) createMainMenu
{
NSMenu *mainMenu = [NSMenu new];

// Create top-level menu items
NSMenuItem *appMenuItem = [NSMenuItem new];
NSMenuItem *editMenuItem = [NSMenuItem new];
[mainMenu addItem:appMenuItem];
[mainMenu addItem:editMenuItem];

// Create app menu items
NSMenu *appMenu = [NSMenu new];
[appMenuItem setSubmenu:appMenu];
[appMenu addItemWithTitle:@"Quit"
action:@selector(terminate:)
keyEquivalent:@"q"];

// Create edit menu items
NSMenu *editMenu = [[NSMenu alloc] initWithTitle:@"Edit"];
[editMenuItem setSubmenu:editMenu];
[editMenu addItemWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"x"];
[editMenu addItemWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"c"];
[editMenu addItemWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"v"];
[editMenu addItemWithTitle:@"Delete" action:@selector(delete:) keyEquivalent:@""];
[editMenu addItemWithTitle:@"Select All" action:@selector(selectAll:) keyEquivalent:@"a"];

return mainMenu;
}

+ (NSError*)runDelegate:(AHAppWorkBlock)completionBlock logger:(AHLogger*)logger
{
AHAppDelegate * delegate = [[AHAppDelegate alloc] initWithBlock:completionBlock logger:logger];
Expand Down
163 changes: 163 additions & 0 deletions src/osx/SignFiles.Mac/notarize-pkg.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
#!/bin/bash

# This file was based on https://github.com/microsoft/BuildXL/blob/8c2348ff04e6ca78726bb945fb2a0f6a55a5c7d6/Private/macOS/notarize.sh
#
# For detailed explanation see: https://developer.apple.com/documentation/security/notarizing_your_app_before_distribution/customizing_the_notarization_workflow

usage() {
cat <<EOM
$(basename $0) - Handy script to notarize an installer package (.pkg)
Usage: $(basename $0) -id <apple_id> -p <password> -pkg <path_to_pkg>
-id or --appleid # A valid Apple ID email address, account must have correct certificates available
-p or --password # The password for the specified Apple ID or Apple One-Time password (to avoid 2FA)
-pkg or --package # The path to an already signed flat-package
EOM
exit 0
}

declare arg_AppleId=""
declare arg_Password=""
declare arg_PackagePath=""

[ $# -eq 0 ] && { usage; }

function parseArgs() {
arg_Positional=()
while [[ $# -gt 0 ]]; do
cmd="$1"
case $cmd in
--help | -h)
usage
shift
exit 0
;;
--appleid | -id)
arg_AppleId=$2
shift
;;
--password | -p)
arg_Password="$2"
shift
;;
--package | -pkg)
arg_PackagePath="$2"
shift
;;
*)
arg_Positional+=("$1")
shift
;;
esac
done
}

function getPackageId {
local PKG=$(cd "$(dirname "$1")"; pwd)/$(basename "$1")
local PKGDEST=$(mktemp -d | tr -d '\r')
xar -x -f "${PKG}" --exclude '^(?:(?!PackageInfo).)*$' -C "${PKGDEST}"
if [ ! -e "${PKGDEST}/PackageInfo" ]; then
echo "error: can't find 'PackageInfo'; maybe meta-package"
return 1
fi
cat "${PKGDEST}/PackageInfo" | tr -d '\r' | tr -d '\n' | sed 's:^.*identifier="\([^"]*\)".*$:\1:g'
rm -rf "${PKGDEST}"
}

parseArgs "$@"

if [[ -z $arg_AppleId ]]; then
echo "[ERROR] Must supply valid / non-empty Apple ID!"
exit 1
fi

if [[ -z $arg_Password ]]; then
echo "[ERROR] Must supply valid / non-empty password!"
exit 1
fi

if [[ ! -f "$arg_PackagePath" ]]; then
echo "[ERROR] Must supply valid / non-empty path to package!"
exit 1
fi

declare bundle_id=$(getPackageId ${arg_PackagePath})

if [[ -z $bundle_id ]]; then
echo "[ERROR] No identifier found in package info!"
exit 1
fi

echo "Notarizating $arg_PackagePath"

echo -e "Current state:\n"
xcrun stapler validate -v "$arg_PackagePath"

if [[ $? -eq 0 ]]; then
echo "$arg_PackagePath already notarized and stapled, nothing to do!"
exit 0
fi

set -e

declare start_time=$(date +%s)

declare output="/tmp/progress.xml"

echo "Uploading package to notarization service, please wait..."
xcrun altool --notarize-app -t osx -f $arg_PackagePath --primary-bundle-id $bundle_id -u $arg_AppleId -p $arg_Password --output-format xml | tee $output

declare request_id=$(/usr/libexec/PlistBuddy -c "print :notarization-upload:RequestUUID" $output)

echo "Checking notarization request validity..."
if [[ $request_id =~ ^\{?[A-F0-9a-f]{8}-[A-F0-9a-f]{4}-[A-F0-9a-f]{4}-[A-F0-9a-f]{4}-[A-F0-9a-f]{12}\}?$ ]]; then
declare attempts=5

while :
do
echo "Waiting a bit before checking on notarization status again..."

sleep 20
xcrun altool --notarization-info $request_id -u $arg_AppleId -p $arg_Password --output-format xml | tee $output

declare status=$(/usr/libexec/PlistBuddy -c "print :notarization-info:Status" $output)
echo "Status: $status"

if [[ -z $status ]]; then
echo "Left attempts: $attempts"

if (($attempts <= 0)); then
break
fi

((attempts--))
else
if [[ $status != "in progress" ]]; then
break
fi
fi
done

declare end_time=$(date +%s)
echo -e "Completed in $(($end_time-$start_time)) seconds\n"

if [[ "$status" != "success" ]]; then
echo "Error notarizing, exiting..." >&2
exit 1
else
declare url=$(/usr/libexec/PlistBuddy -c "print :notarization-info:LogFileURL" $output)

if [ "$url" ]; then
curl $url
fi

# Staple the ticket to the package
xcrun stapler staple "$arg_PackagePath"

echo -e "State after notarization:\n"
xcrun stapler validate -v "$arg_PackagePath"
echo -e "Stapler exit code: $? (must be zero on success!)\n"
fi
else
echo "Invalid request id found in 'altool' output, aborting!" >&2
exit 1
fi