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
91 changes: 65 additions & 26 deletions .github/workflows/build_executable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,17 @@ permissions:

jobs:
build:
name: Build on ${{ matrix.os }} (${{ matrix.mode }} mode)
strategy:
fail-fast: false
matrix:
os: [ ubuntu-20.04, macos-11, windows-2019 ]
os: [ ubuntu-20.04, macos-11, macos-13-xlarge, windows-2019 ]
mode: [ 'onefile', 'onedir' ]
exclude:
- os: ubuntu-20.04
mode: onedir
- os: windows-2019
mode: onedir

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

Expand Down Expand Up @@ -48,17 +55,17 @@ jobs:
git checkout $LATEST_TAG
echo "LATEST_TAG=$LATEST_TAG" >> $GITHUB_ENV

- name: Set up Python 3.7
- name: Set up Python 3.12
uses: actions/setup-python@v4
with:
python-version: '3.7'
python-version: '3.12'

- name: Load cached Poetry setup
id: cached-poetry
uses: actions/cache@v3
with:
path: ~/.local
key: poetry-${{ matrix.os }}-0 # increment to reset cache
key: poetry-${{ matrix.os }}-1 # increment to reset cache

- name: Setup Poetry
if: steps.cached-poetry.outputs.cache-hit != 'true'
Expand All @@ -70,15 +77,9 @@ jobs:
run: echo "$HOME/.local/bin" >> $GITHUB_PATH

- name: Install dependencies
run: poetry install
run: poetry install --without dev,test

- name: Build executable
run: poetry run pyinstaller pyinstaller.spec

- name: Test executable
run: ./dist/cycode version

- name: Sign macOS executable
- name: Import macOS signing certificate
if: runner.os == 'macOS'
env:
APPLE_CERT: ${{ secrets.APPLE_CERT }}
Expand All @@ -100,8 +101,25 @@ jobs:
security import $CERTIFICATE_PATH -P "$APPLE_CERT_PWD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH

# sign executable
codesign --deep --force --options=runtime --entitlements entitlements.plist --sign "$APPLE_CERT_NAME" --timestamp dist/cycode
- name: Build executable (onefile)
if: matrix.mode == 'onefile'
env:
APPLE_CERT_NAME: ${{ secrets.APPLE_CERT_NAME }}
run: |
poetry run pyinstaller pyinstaller.spec
echo "PATH_TO_CYCODE_CLI_EXECUTABLE=dist/cycode-cli" >> $GITHUB_ENV

- name: Build executable (onedir)
if: matrix.mode == 'onedir'
env:
CYCODE_ONEDIR_MODE: 1
APPLE_CERT_NAME: ${{ secrets.APPLE_CERT_NAME }}
run: |
poetry run pyinstaller pyinstaller.spec
echo "PATH_TO_CYCODE_CLI_EXECUTABLE=dist/cycode-cli/cycode-cli" >> $GITHUB_ENV

- name: Test executable
run: time $PATH_TO_CYCODE_CLI_EXECUTABLE version

- name: Notarize macOS executable
if: runner.os == 'macOS'
Expand All @@ -114,17 +132,20 @@ jobs:
xcrun notarytool store-credentials "notarytool-profile" --apple-id "$APPLE_NOTARIZATION_EMAIL" --team-id "$APPLE_NOTARIZATION_TEAM_ID" --password "$APPLE_NOTARIZATION_PWD"

# create zip file (notarization does not support binaries)
ditto -c -k --keepParent dist/cycode notarization.zip
ditto -c -k --keepParent dist/cycode-cli notarization.zip

# notarize app (this will take a while)
xcrun notarytool submit notarization.zip --keychain-profile "notarytool-profile" --wait

# we can't staple the app because it's executable. we should only staple app bundles like .dmg
# xcrun stapler staple dist/cycode
# we can't staple the app because it's executable

- name: Test macOS signed executable
if: runner.os == 'macOS'
run: ./dist/cycode version
run: |
time $PATH_TO_CYCODE_CLI_EXECUTABLE version

# verify signature
codesign -dv --verbose=4 $PATH_TO_CYCODE_CLI_EXECUTABLE

- name: Import cert for Windows and setup envs
if: runner.os == 'Windows'
Expand Down Expand Up @@ -155,39 +176,57 @@ jobs:
smksp_cert_sync.exe

:: sign executable
signtool.exe sign /sha1 %SM_CODE_SIGNING_CERT_SHA1_HASH% /tr http://timestamp.digicert.com /td SHA256 /fd SHA256 ".\dist\cycode.exe"
signtool.exe sign /sha1 %SM_CODE_SIGNING_CERT_SHA1_HASH% /tr http://timestamp.digicert.com /td SHA256 /fd SHA256 ".\dist\cycode-cli.exe"

- name: Test Windows signed executable
if: runner.os == 'Windows'
shell: cmd
run: |
:: call executable and expect correct output
.\dist\cycode.exe version
.\dist\cycode-cli.exe version

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

- name: Prepare files on Windows
if: runner.os == 'Windows'
run: |
echo "ARTIFACT_NAME=cycode-win" >> $GITHUB_ENV
mv dist/cycode.exe dist/cycode-win.exe
mv dist/cycode-cli.exe dist/cycode-win.exe
powershell -Command "(Get-FileHash -Algorithm SHA256 dist/cycode-win.exe).Hash" > sha256
head -c 64 sha256 > dist/cycode-win.exe.sha256

- name: Prepare files on macOS
if: runner.os == 'macOS'
- name: Prepare files on Intel macOS (onefile)
if: runner.os == 'macOS' && runner.arch == 'X64' && matrix.mode == 'onefile'
run: |
echo "ARTIFACT_NAME=cycode-mac" >> $GITHUB_ENV
mv dist/cycode dist/cycode-mac
mv dist/cycode-cli dist/cycode-mac
shasum -a 256 dist/cycode-mac > sha256
head -c 64 sha256 > dist/cycode-mac.sha256

- name: Prepare files on Apple Silicon macOS (onefile)
if: runner.os == 'macOS' && runner.arch == 'ARM64' && matrix.mode == 'onefile'
run: |
echo "ARTIFACT_NAME=cycode-mac-arm" >> $GITHUB_ENV
mv dist/cycode-cli dist/cycode-mac-arm
shasum -a 256 dist/cycode-mac-arm > sha256
head -c 64 sha256 > dist/cycode-mac-arm.sha256

- name: Prepare files on Intel macOS (onedir)
if: runner.os == 'macOS' && runner.arch == 'X64' && matrix.mode == 'onedir'
run: |
echo "ARTIFACT_NAME=cycode-mac-onedir" >> $GITHUB_ENV

- name: Prepare files on Apple Silicon macOS (onedir)
if: runner.os == 'macOS' && runner.arch == 'ARM64' && matrix.mode == 'onedir'
run: |
echo "ARTIFACT_NAME=cycode-mac-arm-onedir" >> $GITHUB_ENV

- name: Prepare files on Linux
if: runner.os == 'Linux'
run: |
echo "ARTIFACT_NAME=cycode-linux" >> $GITHUB_ENV
mv dist/cycode dist/cycode-linux
mv dist/cycode-cli dist/cycode-linux
sha256sum dist/cycode-linux > sha256
head -c 64 sha256 > dist/cycode-linux.sha256

Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/tests_full.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,9 @@ jobs:
run: poetry install

- name: Run executable test
if: runner.os == 'Linux'
run: |
poetry run pyinstaller pyinstaller.spec
./dist/cycode version
./dist/cycode-cli version

- name: Run pytest
run: poetry run pytest
26 changes: 13 additions & 13 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 22 additions & 38 deletions pyinstaller.spec
Original file line number Diff line number Diff line change
@@ -1,64 +1,48 @@
# -*- mode: python ; coding: utf-8 -*-
# Run `poetry run pyinstaller pyinstaller.spec` to generate the binary.
# Set the env var `CYCODE_ONEDIR_MODE` to generate a single directory instead of a single file.


block_cipher = None

INIT_FILE_PATH = os.path.join('cycode', '__init__.py')
_INIT_FILE_PATH = os.path.join('cycode', '__init__.py')
_CODESIGN_IDENTITY = os.environ.get('APPLE_CERT_NAME')
_ONEDIR_MODE = os.environ.get('CYCODE_ONEDIR_MODE') is not None

# save the prev content of __init__ file
with open(INIT_FILE_PATH, 'r', encoding='UTF-8') as file:
with open(_INIT_FILE_PATH, 'r', encoding='UTF-8') as file:
prev_content = file.read()

import dunamai as _dunamai

VERSION_PLACEHOLDER = '0.0.0'
CLI_VERSION = _dunamai.get_version('cycode', first_choice=_dunamai.Version.from_git).serialize(
metadata=False, bump=True, style=_dunamai.Style.Pep440
)

# write version from Git Tag to freeze the value and don't depend on Git
with open(INIT_FILE_PATH, 'w', encoding='UTF-8') as file:
# write the version from Git Tag to freeze the value and don't depend on Git
with open(_INIT_FILE_PATH, 'w', encoding='UTF-8') as file:
file.write(prev_content.replace(VERSION_PLACEHOLDER, CLI_VERSION))

a = Analysis(
['cycode/cli/main.py'],
pathex=[],
binaries=[],
scripts=['cycode/cli/main.py'],
datas=[('cycode/cli/config.yaml', 'cycode/cli'), ('cycode/cyclient/config.yaml', 'cycode/cyclient')],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=['tests'],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

exe_args = [PYZ(a.pure, a.zipped_data), a.scripts, a.binaries, a.zipfiles, a.datas]
if _ONEDIR_MODE:
exe_args = [PYZ(a.pure), a.scripts]

exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='cycode',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
*exe_args,
name='cycode-cli',
exclude_binaries=bool(_ONEDIR_MODE),
target_arch=None,
codesign_identity=None,
entitlements_file=None,
codesign_identity=_CODESIGN_IDENTITY,
entitlements_file='entitlements.plist',
)

if _ONEDIR_MODE:
coll = COLLECT(exe, a.binaries, a.datas, name='cycode-cli')

# rollback the prev content of the __init__ file
with open(INIT_FILE_PATH, 'w', encoding='UTF-8') as file:
with open(_INIT_FILE_PATH, 'w', encoding='UTF-8') as file:
file.write(prev_content)
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ coverage = ">=7.2.3,<7.3.0"
responses = ">=0.23.1,<0.24.0"

[tool.poetry.group.executable.dependencies]
pyinstaller = ">=5.13.0,<5.14.0"
pyinstaller = ">=5.13.2,<5.14.0"
dunamai = ">=1.18.0,<1.19.0"

[tool.poetry.group.dev.dependencies]
Expand Down