Skip to content

Commit

Permalink
Support for "Options/Flags" to pass to pyinstaller (#2)
Browse files Browse the repository at this point in the history
- [NEW] Added support of pyinstaller options
    - Provide all options in "inputs.options: <comma-seperated-options-here>"
    - .py and .spec type of specs, both supports d/f types of options
    - Look at readme for the list of supported options

- README updated
    - Information about new input, i.e "options"
    - List of all supported options
    - List of all inputs and outputs with their description and default values
  • Loading branch information
sayyid5416 authored Dec 3, 2022
1 parent 8921d4f commit 3f23c8b
Show file tree
Hide file tree
Showing 5 changed files with 302 additions and 160 deletions.
46 changes: 42 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,61 @@ You can also use any major tags like `@v1` for any `@v1.*.*`

### 💠 .py and .spec support
- You can use either `.py` or `.spec` file to create the executable.
- Specify it in `inputs.spec: <file.py/file.spec>`.
- When `.py` file is used, generated `.spec` file will also be uploaded as artifact.
- Modify your `.spec` file according to your needs.

### 💠 Many python versions

### 💠 Third party modules
- Write your third party modules in a file _(Ex: `requirements.txt`)_ , and
- Use `inputs.requirements: <path-to-your-requirement-file>`.

### 💠 Pyinstaller options
- Specify pyinstaller options in `inputs.options: <comma-seperated-options-here>`.
- `.py` and `.spec` both supports different kind of options.
- Check list of all [supported options here](#-supported-pyinstaller-options).

### 💠 Python versions
- You can specify any python version for the executable.
- Specify specific python-version in `inputs.python_ver: <python-version-here>`.

### 💠 Executable uploads
- You can control if generated executable needs to be uploaded as artifact.
- You can choose a name of your liking.
- Specify the artifact name in `inputs.upload_exe_with_name: <name-here>`.


<br>


# 🔰 Inputs & Outputs

- Some **inputs** are **required**, while rest are optional.
- Check all inputs & outputs [here](/action.yml).
- Check detailed info about these inputs & outputs [here](/action.yml).

### 💠 Available Inputs
| Input | Default <br> _(`-` = empty string)_ | Description
|-----------------------|:--------:|-------------
| `spec` _(required)_ | - | Path of your `.py` or `.spec` file
| `requirements` | - | Path of your `requirements.txt` file
| `options` | - | Options to set for pyinstaller command
| `python_ver` | 3.10 | Specific python version you want to use
| `exe_path` | ./dist | Path on runner-os, where generated executable files are stored
| `upload_exe_with_name`| - | If passed, uploads executable artifact with this name. Else, artifact won't be uploaded.

### 💠 Available Outputs
| Output | Description
|-----------------------|-------------
| `executable_path` | Path on runner-os, where generated executable files are stored
| `is_uploaded` | `true`, if packaged executable has been uploaded as artifact

### 💠 Supported [Pyinstaller options](https://pyinstaller.org/en/stable/usage.html#options)
| For `.py` | For `.py` | For `.py` | For `.spec`
|-------------------|-----------------------------------------|-----------------------------------------|------------
| `--uac-admin` | `--onedir`, `-D` | `--upx-dir <UPX_DIR>` | `--ascii`, `-a`
| `--uac-uiaccess` | `--onefile`, `-F` | `--key <KEY>` | `--upx-dir <UPX_DIR>`
| `--noupx` | `--ascii`, `-a` | `--upx-exclude <FILE>` |
| | `--console`, `--nowindowed`, `-c` | `--name <NAME>`, `-n <NAME>` |
| | `--windowed`, `--noconsole`, `-w` | `--icon <FILEICON>`, `-i <FILEICON>` |


<br>

Expand All @@ -60,4 +97,5 @@ jobs:
spec: 'src/build.spec'
requirements: 'src/requirements.txt'
upload_exe_with_name: 'My executable'
options: --onefile, --name "My App", --windowed,
```
248 changes: 128 additions & 120 deletions action.yml
Original file line number Diff line number Diff line change
@@ -1,120 +1,128 @@
name: Versatile PyInstaller
author: '@sayyid5416'
description: GitHub Action to package python scripts into executables
branding:
icon: hard-drive
color: yellow


inputs:
spec:
description: >
path of your '.py' or '.spec' file.
- This file will be used to create executable.
- If .py: Generated spec file will also be uploaded as artifact
required: true
default: ''
requirements:
description: path of your requirements.txt file
default: ''
python_ver:
description: specific python version you want to use
default: '3.10'
exe_path:
description: path where generated executable will be saved, on runner-os
default: './dist'
upload_exe_with_name:
description: If passed, executable will be uploaded with this name as artifact. Else, artifact won't be uploaded.
default: ''

outputs:
executable_path:
description: path on runner-os, where generated executable files are stored
value: ${{ inputs.exe_path }}
is_uploaded:
description: true, if packaged executable has been uploaded as artifact
value: ${{ steps.exe_uploading.outputs.uploaded }}



runs:
using: 'composite'
steps:

- name: checks
shell: bash
run: python "${{ github.action_path }}/src/checks.py"
env:
spec: ${{ inputs.spec }}
upload_exe_with_name: ${{ inputs.upload_exe_with_name }}

- name: Setting modified outputs
id: mods
shell: bash
run: python "${{ github.action_path }}/src/mods.py"
env:
spec: ${{ inputs.spec }}

- name: Checkout
uses: actions/checkout@v3

- name: (Install) python
uses: actions/setup-python@v4
with:
python-version: ${{ inputs.python_ver }}

- name: (Install) python dev tools
shell: bash
run: python -m pip install pip wheel setuptools

- name: (Install) dependencies
if: inputs.requirements != ''
run: python -m pip install -r "${{ inputs.requirements }}"
shell: bash

- name: (Install) pyinstaller
shell: bash
run: pip install pyinstaller

- name: (Create) Executable
shell: bash
run: |
pyinstaller \
--clean \
--noconfirm \
--dist ${{ inputs.exe_path }} \
"${{ inputs.spec }}"
echo "✔️ Executable created successfully at _'${{ inputs.exe_path }}'_" >> $GITHUB_STEP_SUMMARY
echo " - Python version used: '${{ inputs.python_ver }}'" >> $GITHUB_STEP_SUMMARY
- name: (Upload) Executable
id: artifact_upload
if: inputs.upload_exe_with_name != ''
uses: actions/upload-artifact@v3
with:
name: ${{ inputs.upload_exe_with_name }}
path: ${{ inputs.exe_path }}

- name: (Upload) generated spec file - if .py
if: endsWith(inputs.spec, '.py')
uses: actions/upload-artifact@v3
with:
name: Generated spec file
path: ${{ steps.mods.outputs.spec_name }}.spec

- name: If executable upload success
id: exe_uploading
if: steps.artifact_upload.conclusion == 'success'
shell: bash
run: |
echo "✔️ Executable **_(${{ inputs.upload_exe_with_name }})_** uploaded successfully" >> $GITHUB_STEP_SUMMARY
echo "uploaded='true'" >> $GITHUB_OUTPUT
- name: If executable upload fails
if: failure() && steps.artifact_upload.conclusion == 'failure'
shell: bash
run: |
echo "::warning title=Failed-Upload::\
Executable couldn't upload. \
Check available storage at: 'settings > billing > Storage for Actions and Packages'."
name: Versatile PyInstaller
author: '@sayyid5416'
description: GitHub Action to package python scripts into executables
branding:
icon: hard-drive
color: yellow


inputs:
spec:
description: >
path of your '.py' or '.spec' file.
- This file will be used to create executable.
- If .py: Generated spec file will also be uploaded as artifact
required: true
default: ''
requirements:
description: path of your requirements.txt file
default: ''
options:
description: >
Options to set for pyinstaller command
Ex: options: '--onedir, -F' (seperated by comma and space)
- Supported options: Check readme
default: ''
python_ver:
description: specific python version you want to use
default: '3.10'
exe_path:
description: Path on runner-os, where generated executable files are stored
default: './dist'
upload_exe_with_name:
description: If passed, uploads executable artifact with this name. Else, artifact won't be uploaded.
default: ''

outputs:
executable_path:
description: path on runner-os, where generated executable files are stored
value: ${{ inputs.exe_path }}
is_uploaded:
description: true, if packaged executable has been uploaded as artifact
value: ${{ steps.exe_uploading.outputs.uploaded }}



runs:
using: 'composite'
steps:

- name: checks
shell: bash
run: python "${{ github.action_path }}/src/checks.py"
env:
spec: ${{ inputs.spec }}
upload_exe_with_name: ${{ inputs.upload_exe_with_name }}

- name: (Set) modified outputs
id: mods
shell: bash
run: python "${{ github.action_path }}/src/mods.py"
env:
spec: ${{ inputs.spec }}
options: ${{ inputs.options }}

- name: Checkout
uses: actions/checkout@v3

- name: (Install) python
uses: actions/setup-python@v4
with:
python-version: ${{ inputs.python_ver }}

- name: (Install) python dev tools
shell: bash
run: python -m pip install pip wheel setuptools

- name: (Install) dependencies
if: inputs.requirements != ''
run: python -m pip install -r "${{ inputs.requirements }}"
shell: bash

- name: (Install) pyinstaller
shell: bash
run: pip install pyinstaller

- name: (Create) Executable
shell: bash
run: |
pyinstaller \
--clean \
--noconfirm \
--dist ${{ inputs.exe_path }} \
${{ steps.mods.outputs.supported_options }} \
"${{ inputs.spec }}"
echo "✔️ Executable created successfully at _'${{ inputs.exe_path }}'_" >> $GITHUB_STEP_SUMMARY
echo " - Python version used: '${{ inputs.python_ver }}'" >> $GITHUB_STEP_SUMMARY
- name: (Upload) Executable
id: artifact_upload
if: inputs.upload_exe_with_name != ''
uses: actions/upload-artifact@v3
with:
name: ${{ inputs.upload_exe_with_name }}
path: ${{ inputs.exe_path }}

- name: (Upload) generated spec file - if .py
if: endsWith(inputs.spec, '.py')
uses: actions/upload-artifact@v3
with:
name: Generated spec file
path: ${{ steps.mods.outputs.spec_name }}.spec

- name: If executable upload success
id: exe_uploading
if: steps.artifact_upload.conclusion == 'success'
shell: bash
run: |
echo "✔️ Executable **_(${{ inputs.upload_exe_with_name }})_** uploaded successfully" >> $GITHUB_STEP_SUMMARY
echo "uploaded='true'" >> $GITHUB_OUTPUT
- name: If executable upload fails
if: failure() && steps.artifact_upload.conclusion == 'failure'
shell: bash
run: |
echo "::warning title=Failed-Upload::\
Executable couldn't upload. \
Check available storage at: 'settings > billing > Storage for Actions and Packages'."
32 changes: 32 additions & 0 deletions src/actions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import os
from typing import Literal


def env(name:str, _def=''):
""" Returns environment variable """
return os.environ.get(name, _def)


#-bug: Previous step annotations are being overwritten
def set_annotation(
message:str,
title:str='',
_type:Literal['debug', 'notice', 'warning', 'error']='notice',
):
"""
Sets annotation with `message` text
- `title`: If provided, `title` text will be shown as title
- `_type`: Type of annotation to set
"""
title = f' title={title}' if title else ''
print(f'::{_type}{title}::{message}')
if _type == 'error':
exit(1)


def set_output(key:str, value:str):
""" Sets the output to `$GITHUB_OUTPUT` file
- Using `key=value`
"""
with open(env('GITHUB_OUTPUT'), 'a') as f:
f.write(f'{key}={value}\n')
17 changes: 1 addition & 16 deletions src/checks.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,6 @@
import os
from typing import Literal
from actions import *


def env(name:str, _def=''):
return os.environ.get(name, _def)


def set_annotation(
message:str,
title:str=None,
_type:Literal['debug', 'notice', 'warning', 'error']='notice',
):
title = f' title={title}' if title else ''
print(f'::{_type}{title}::{message}')
if _type == 'error':
exit(1)



# ENV
Expand Down
Loading

0 comments on commit 3f23c8b

Please sign in to comment.