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
38 changes: 32 additions & 6 deletions .github/workflows/build_executable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ jobs:
exclude:
- os: ubuntu-22.04
mode: onedir
- os: windows-2022
mode: onedir

runs-on: ${{ matrix.os }}

Expand Down Expand Up @@ -249,18 +247,46 @@ jobs:
C:\Windows\System32\certutil.exe -csp "DigiCert Signing Manager KSP" -key -user
smctl windows certsync --keypair-alias=%SM_KEYPAIR_ALIAS%

:: sign executable
signtool.exe sign /sha1 %SM_CODE_SIGNING_CERT_SHA1_HASH% /tr http://timestamp.digicert.com /td SHA256 /fd SHA256 ".\dist\cycode-cli.exe"
:: sign executable (in onedir mode the exe lives inside the collected directory)
set "EXE_PATH=.\dist\cycode-cli.exe"
if "${{ matrix.mode }}"=="onedir" set "EXE_PATH=.\dist\cycode-cli\cycode-cli.exe"
signtool.exe sign /sha1 %SM_CODE_SIGNING_CERT_SHA1_HASH% /tr http://timestamp.digicert.com /td SHA256 /fd SHA256 "%EXE_PATH%"

- name: Sign unsigned onedir binaries (Windows)
if: runner.os == 'Windows' && matrix.mode == 'onedir'
shell: powershell
env:
SM_HOST: ${{ secrets.SM_HOST }}
SM_API_KEY: ${{ secrets.SM_API_KEY }}
SM_CLIENT_CERT_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD }}
SM_CODE_SIGNING_CERT_SHA1_HASH: ${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH }}
run: |
# Vendor binaries (PSF-signed stdlib .pyds, python3xx.dll, Microsoft VC runtime) already
# carry valid signatures; re-signing would replace them with ours. Sign only the unsigned
# ones (PyInstaller-generated and third-party wheel binaries).
$files = Get-ChildItem -Path dist\cycode-cli\_internal -Recurse -Include *.dll,*.pyd,*.exe |
Where-Object { (Get-AuthenticodeSignature $_.FullName).Status -eq 'NotSigned' }
if (-not $files) {
Write-Host 'No unsigned binaries found'
exit 0
}
Write-Host "Signing $($files.Count) unsigned binaries:"
$files.FullName | Write-Host
signtool.exe sign /sha1 $env:SM_CODE_SIGNING_CERT_SHA1_HASH /tr http://timestamp.digicert.com /td SHA256 /fd SHA256 @($files.FullName)
exit $LASTEXITCODE

- name: Test Windows signed executable
if: runner.os == 'Windows'
shell: cmd
run: |
set "EXE_PATH=.\dist\cycode-cli.exe"
if "${{ matrix.mode }}"=="onedir" set "EXE_PATH=.\dist\cycode-cli\cycode-cli.exe"

:: call executable and expect correct output
.\dist\cycode-cli.exe status
"%EXE_PATH%" status

:: verify signature
signtool.exe verify /v /pa ".\dist\cycode-cli.exe"
signtool.exe verify /v /pa "%EXE_PATH%"

- name: Prepare files for artifact and release (rename and calculate sha256)
run: echo "ARTIFACT_NAME=$(./process_executable_file.py dist/cycode-cli)" >> $GITHUB_ENV
Expand Down
13 changes: 7 additions & 6 deletions process_executable_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
_OS_TO_CLI_DIST_TEMPLATE = {
'darwin': Template('cycode-mac$suffix$ext'),
'linux': Template('cycode-linux$suffix$ext'),
'windows': Template('cycode-win$suffix.exe$ext'),
'windows': Template('cycode-win$suffix$ext'),
}
_WINDOWS = 'windows'
_WINDOWS_EXECUTABLE_SUFFIX = '.exe'
Expand Down Expand Up @@ -87,8 +87,7 @@ def get_cli_file_name(suffix: str = '', ext: str = '') -> str:
if os_name not in _OS_TO_CLI_DIST_TEMPLATE:
raise Exception(f'Unsupported OS: {os_name}')

template = _OS_TO_CLI_DIST_TEMPLATE[os_name]
return template.substitute(suffix=suffix, ext=ext)
return _OS_TO_CLI_DIST_TEMPLATE[os_name].substitute(suffix=suffix, ext=ext)


def get_cli_file_suffix(is_onedir: bool) -> str:
Expand Down Expand Up @@ -117,23 +116,25 @@ def write_hashes_db_to_file(hashes: DirHashes, output_path: str) -> None:


def get_cli_filename(is_onedir: bool) -> str:
return get_cli_file_name(get_cli_file_suffix(is_onedir))
# onedir is distributed as an archive of a directory, so only onefile carries .exe
ext = _WINDOWS_EXECUTABLE_SUFFIX if get_os_name() == _WINDOWS and not is_onedir else ''
return get_cli_file_name(suffix=get_cli_file_suffix(is_onedir), ext=ext)


def get_cli_path(output_path: Path, is_onedir: bool) -> str:
return os.path.join(output_path, get_cli_filename(is_onedir))


def get_cli_hash_filename(is_onedir: bool) -> str:
return get_cli_file_name(suffix=get_cli_file_suffix(is_onedir), ext=_HASH_FILE_EXT)
return get_cli_filename(is_onedir) + _HASH_FILE_EXT


def get_cli_hash_path(output_path: Path, is_onedir: bool) -> str:
return os.path.join(output_path, get_cli_hash_filename(is_onedir))


def get_cli_archive_filename(is_onedir: bool) -> str:
return get_cli_file_name(suffix=get_cli_file_suffix(is_onedir))
return get_cli_filename(is_onedir)


def get_cli_archive_path(output_path: Path, is_onedir: bool) -> str:
Expand Down
Loading