Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create workflow to upload msi to storage account. #1122

Merged
merged 37 commits into from
Nov 16, 2023
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
103 changes: 103 additions & 0 deletions .github/workflows/build_msi_installer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
name: Build and Package MSI
YingChen1996 marked this conversation as resolved.
Show resolved Hide resolved

on:
workflow_dispatch:
inputs:
UploadAsLatest:
type: string
default: "False"
required: false
description: 'Also upload the msi installer to storage account as latest'

jobs:
build_msi_installer:
runs-on: windows-latest
name: Build Windows MSI

steps:
- name: Check input parameters
run: |
echo "UploadAsLatest: ${{ inputs.UploadAsLatest }}"

- name: Checkout Repo
uses: actions/checkout@v3
with:
submodules: true

- name: Add msbuild to PATH
uses: microsoft/[email protected]

- name: Install WIX Toolset
shell: pwsh
working-directory: ${{ github.workspace }}/scripts/installer/windows
run: |
Invoke-WebRequest -Uri 'https://azurecliprod.blob.core.windows.net/msi/wix310-binaries-mirror.zip' -OutFile 'wix-archive.zip'
Expand-Archive -Path 'wix-archive.zip' -DestinationPath 'wix'
Remove-Item -Path 'wix-archive.zip'


- name: Install Python and dependencies
uses: actions/setup-python@v4
with:
python-version: 3.9

- name: Install promptflow dependencies
run: |
python -m pip install --upgrade pip
pip install promptflow[azure,executable] promptflow-tools
shell: pwsh

- name: Get promptflow version
id: get-version
run: |
$version=$(python -c "import promptflow; print(promptflow.__version__)")
echo "::set-output name=version::$version"
shell: pwsh

- name: Build Pyinstaller project
working-directory: ${{ github.workspace }}/scripts/installer/windows/scripts
run: |
echo 'Version from promptflow: ${{ steps.get-version.outputs.version }}'
$text = Get-Content "version_info.txt" -Raw

$versionString = '${{ steps.get-version.outputs.version }}'
$versionArray = $versionString.Split('.') + 0
$versionTuple = [string]::Join(', ', $versionArray)
$text = $text -replace '\$\((env\.FILE_VERSION)\)', $versionTuple

$text = $text -replace '\$\((env\.CLI_VERSION)\)', '${{ steps.get-version.outputs.version }}'
$text | Out-File -FilePath "version_info.txt" -Encoding utf8
pyinstaller promptflow.spec
shell: pwsh

- name: Build WIX project
working-directory: ${{ github.workspace }}/scripts/installer/windows
run: |
$text = Get-Content "promptflow.wixproj" -Raw
$text = $text -replace '\$\((env\.CLI_VERSION)\)', '${{ steps.get-version.outputs.version }}'
$text | Out-File -FilePath "promptflow.wixproj" -Encoding utf8

$text = Get-Content "product.wxs" -Raw
$text = $text -replace '\$\((env\.CLI_VERSION)\)', '${{ steps.get-version.outputs.version }}'
$text | Out-File -FilePath "product.wxs" -Encoding utf8

msbuild /t:rebuild /p:Configuration=Release /p:Platform=x64 promptflow.wixproj
shell: pwsh

- name: Azure Login
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}

- name: Upload to Azure Storage
run: |
$msi_files = Get-ChildItem -Path 'scripts/installer/windows/out/' -Filter *.msi
foreach ($msi_file in $msi_files) {
if ($env:INPUT_UPLOADASLATEST -ieq 'True') {
YingChen1996 marked this conversation as resolved.
Show resolved Hide resolved
az storage blob upload --account-name promptflowartifact --container-name msi-installer --file "scripts/installer/windows/out/$($msi_file.Name)" --name "promptflow.msi" --overwrite
az storage blob copy start --account-name promptflowartifact --destination-container msi-installer --destination-blob "$($msi_file.Name)" --source-container msi-installer --source-blob "promptflow.msi"
} else {
az storage blob upload --account-name promptflowartifact --container-name msi-installer --file "scripts/installer/windows/out/$($msi_file.Name)" --name "$($msi_file.Name)" --overwrite
}
}
shell: pwsh
24 changes: 13 additions & 11 deletions scripts/installer/windows/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
Building the Windows MSI Installer
========================
# Building the Windows MSI Installer

This document provides instructions on creating the MSI installer.

Prerequisites
-------------
## Option1: Building with Github Actions
Trigger the [workflow](https://github.com/microsoft/promptflow/actions/workflows/build_msi_installer.yml) manually.


## Option2: Local Building
### Prerequisites

1. Turn on the '.NET Framework 3.5' Windows Feature (required for WIX Toolset).
2. Install 'Microsoft Build Tools 2015'.
Expand All @@ -20,12 +23,11 @@ Prerequisites
- `pip install promptflow[azure,executable] promptflow-tools`


Building
--------
1. `cd scripts/installer/windows/scripts` and run `pyinstaller promptflow.spec`.
2. `cd scripts/installer/windows` and Run `msbuild /t:rebuild /p:Configuration=Release /p:Platform=x64 promptflow.wixproj`.
3. The unsigned MSI will be in the `scripts/installer/windows/out` folder.
### Building
1. Update the version number `$(env.CLI_VERSION)` and `$(env.FILE_VERSION)` in `product.wxs`, `promptflow.wixproj` and `version_info.txt`.
2. `cd scripts/installer/windows/scripts` and run `pyinstaller promptflow.spec`.
3. `cd scripts/installer/windows` and Run `msbuild /t:rebuild /p:Configuration=Release /p:Platform=x64 promptflow.wixproj`.
4. The unsigned MSI will be in the `scripts/installer/windows/out` folder.

Notes
--------
## Notes
- If you encounter "Access is denied" error when running promptflow. Please follow the [link](https://learn.microsoft.com/en-us/microsoft-365/security/defender-endpoint/attack-surface-reduction-rules-deployment-implement?view=o365-worldwide#customize-attack-surface-reduction-rules) to add the executable to the Windows Defender Attack Surface Reduction (ASR) rule.
12 changes: 3 additions & 9 deletions scripts/installer/windows/product.wxs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">

<?define ProductVersion="1.0.0.0" ?>
<?define ProductVersion="$(env.CLI_VERSION)" ?>

<?define ProductName = "promptflow" ?>
<?define ProductDescription = "Command-line tools for promptflow." ?>
<?define ProductDescription = "Command-line tools for prompt flow." ?>
<?define ProductAuthor = "Microsoft Corporation" ?>
<?define ProductResources = ".\resources\" ?>
<?define UpgradeCode32 = "8b748161-e07a-48f2-8cdf-401480df4694" ?>
<?define PromptflowServiceGuid = "d9363a28-693c-4b12-bf0a-30e4b37e9951" ?>

<?if $(var.Platform) = "x64" ?>
<?define PromptflowCliRegistryGuid = "0efd984f-9eec-425b-b230-a3994b69649a" ?>
Expand Down Expand Up @@ -132,11 +131,6 @@
Value="$(var.ProductVersion)"
KeyPath="yes"/>
</Component>
<Component Id="PromptflowService" Directory="DynamicCliDir" Guid="$(var.PromptflowServiceGuid)">
<File Id="PromptflowServiceEXE" Source="scripts\dist\promptflow\pfs.exe" KeyPath="yes" />
<ServiceInstall Id="ServiceInstaller" Name="PromptflowService" DisplayName="Prompt Flow Service" Description="Provides run and connection management for prompt flow" Start="auto" Type="ownProcess" ErrorControl="normal" />
<ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall" Name="PromptflowService" Wait="no" />
</Component>

</ComponentGroup>

Expand Down
3 changes: 1 addition & 2 deletions scripts/installer/windows/promptflow.wixproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<ProductVersion>3.10</ProductVersion>
<ProjectGuid>04ff6707-750d-4474-89b3-7922c84721be</ProjectGuid>
<SchemaVersion>2.0</SchemaVersion>
<OutputName>promptflow</OutputName>
<OutputName>promptflow-$(env.CLI_VERSION)</OutputName>
<OutputType>Package</OutputType>
<WixTargetsPath Condition=" '$(WixTargetsPath)' == '' AND '$(MSBuildExtensionsPath32)' != '' ">$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets</WixTargetsPath>
<WixTargetsPath Condition=" '$(WixTargetsPath)' == '' ">$(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets</WixTargetsPath>
Expand Down Expand Up @@ -61,7 +61,6 @@
<Name>WixUtilExtension</Name>
</WixExtension>
</ItemGroup>
<Import Project="$(WixTargetsPath)" />
<Import Project="$(WixTargetsPath)" Condition=" '$(WixTargetsPath)' != '' " />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\wix.targets" Condition=" '$(WixTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\wix.targets') " />
<Target Name="EnsureWixToolsetInstalled" Condition=" '$(WixTargetsImported)' != 'true' ">
Expand Down
50 changes: 1 addition & 49 deletions scripts/installer/windows/scripts/pfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,7 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# ---------------------------------------------------------
from promptflow._sdk._service.entry import main
import sys

import win32serviceutil # ServiceFramework and commandline helper
import win32service # Events
import servicemanager # Simple setup and logging


class MyService:
"""Silly little application stub"""
def stop(self):
"""Stop the service"""
self.running = False

def run(self):
"""Main service loop. This is where work is done!"""
self.running = True
while self.running:
main() # Important work
servicemanager.LogInfoMsg("Service running...")


class MyServiceFramework(win32serviceutil.ServiceFramework):

_svc_name_ = 'PromptflowService'
_svc_display_name_ = 'Promptflow Service'

def SvcStop(self):
"""Stop the service"""
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
self.service_impl.stop()
self.ReportServiceStatus(win32service.SERVICE_STOPPED)

def SvcDoRun(self):
"""Start the service; does not return until stopped"""
self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
self.service_impl = MyService()
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
# Run the service
self.service_impl.run()


def init():
if len(sys.argv) == 1:
servicemanager.Initialize()
servicemanager.PrepareToHostSingle(MyServiceFramework)
servicemanager.StartServiceCtrlDispatcher()
else:
win32serviceutil.HandleCommandLine(MyServiceFramework)


if __name__ == '__main__':
init()
main()
8 changes: 4 additions & 4 deletions scripts/installer/windows/scripts/version_info.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# UTF-8
# UTF-8
#
# For more details about fixed file info 'ffi' see:
# http://msdn.microsoft.com/en-us/library/ms646997.aspx
VSVersionInfo(
ffi=FixedFileInfo(
# filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)
# Set not needed items to zero 0.
filevers=(1, 0, 0, 0),
filevers=($(env.FILE_VERSION)),
prodvers=(1, 0, 0, 0),
# Contains a bitmask that specifies the valid bits 'flags'r
mask=0x3f,
Expand Down Expand Up @@ -35,8 +35,8 @@ VSVersionInfo(
StringStruct('InternalName', 'setup'),
StringStruct('LegalCopyright', 'Copyright (c) Microsoft Corporation. All rights reserved.'),
StringStruct('ProductName', 'Microsoft prompt flow'),
StringStruct('ProductVersion', '1.0.0.0')])
]),
StringStruct('ProductVersion', '$(env.CLI_VERSION)')])
]),
VarFileInfo([VarStruct('Translation', [1033, 1252])])
]
)
Loading