Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
brentvollebregt committed Mar 4, 2020
2 parents f8536e9 + 62674ec commit cbf9333
Show file tree
Hide file tree
Showing 10 changed files with 190 additions and 19 deletions.
99 changes: 99 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
on:
push:
tags:
- 'v*'

name: Build and Upload Release Assets

jobs:
create-release:
name: Create Release and Upload Built Assets
runs-on: windows-latest
steps:
# Checkout
- name: Checkout code
uses: actions/checkout@v2

# Install Python dependencies so the module can be run
- name: Install Module Dependencies
run: python -m pip install -r requirements.txt

# Get current module version
- name: Get Module Version
id: get_module_version
shell: bash
run: |
echo "Module version: $(python run.py --version)"
echo "::set-output name=version::$(python run.py --version)"
# Fail if the tag and version do not match. Also match this to github.ref to make sure we aren't creating a release for an old version
- name: Fail on Tag and Version Mismatch
if: (!endsWith(github.ref, steps.get_module_version.outputs.version))
run: |
echo "Ref that triggered release: ${{ github.ref }}"
echo "Current module version: ${{ steps.get_module_version.outputs.version }}"
exit 1
# Clear out __pycache__ directories
- name: Clear __pycache__
shell: bash
run: find . -name __pycache__ -type d -print0|xargs -0 rm -r --

# Build the React frontend (will be copied into the python module)
- name: Build Webapp
run: |
cd webapp
npm install
npm run build
cd ../
# Zip up the module that now contains has the build webapp
- name: Zip Module
shell: pwsh
run: Compress-Archive -Path .\whos_on_my_network, README.md, requirements.txt, run.py -CompressionLevel Optimal -DestinationPath .\whos_on_my_network.zip

# Build a binary that uses the default method of scanning
- name: Build Binary
shell: cmd
run: |
python -m pip install pyinstaller
pyinstaller -y -F --add-data "whos_on_my_network/static";"whos_on_my_network/static/" --distpath ./ -n whos_on_my_network.exe run.py
# Zip up the binary
- name: Zip Module
shell: pwsh
run: Compress-Archive -Path whos_on_my_network.exe, README.md -CompressionLevel Optimal -DestinationPath .\whos_on_my_network_binary.zip

# Create the release for this version
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.get_module_version.outputs.version }}
release_name: Release ${{ steps.get_module_version.outputs.version }}
draft: false
prerelease: false

# Upload the zip to the created release
- name: Upload Built Module to Release
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./whos_on_my_network.zip
asset_name: whos_on_my_network.zip
asset_content_type: application/zip

# Upload the zip to the created release
- name: Upload Binary to Release
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./whos_on_my_network_binary.zip
asset_name: whos_on_my_network_binary.zip
asset_content_type: application/zip
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.idea/
*.sqlite
config.json
__pycache__/
whos_on_my_network/static
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Flask
click
peewee
scapy
scapy
requests
9 changes: 9 additions & 0 deletions run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""
This script is provided as an easy entry point to the whos_on_my_network module
It is also used to help create binary releases of whos_on_my_network
"""
from whos_on_my_network.__main__ import cli


if __name__ == '__main__':
cli()
7 changes: 1 addition & 6 deletions whos_on_my_network/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1 @@
import os
from pathlib import Path

# Check if the static folder has been created by the webapp build step
if not (Path(__file__).absolute().parent / 'static').exists():
print('WARNING: static directory does not exist in module root. Build the webapp to populate this directory.')
version = '2.0.2'
7 changes: 5 additions & 2 deletions whos_on_my_network/__main__.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
import click
from typing import Optional

from . import version
from . import config
from .api import app
from .api import app, pre_flight_checks
from .service import scanning as scanning_service


@click.group()
@click.version_option(version, message='v%(version)s')
def cli():
""" Who's On My Network """


@cli.command()
@click.option('-h', '--host', 'host', default=config.HOST)
@click.option('-p', '--port', 'port', type=int, default=config.PORT)
def start(host, port):
def start(host: bool, port: bool):
""" Start the web server """
print(f'Server starting at http://{host}:{port}')
pre_flight_checks()
app.run(host=host, port=port)


Expand Down
11 changes: 11 additions & 0 deletions whos_on_my_network/api.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from pathlib import Path

from flask import Flask, request, jsonify

from . import config
Expand All @@ -13,6 +15,15 @@
app = Flask(__name__, static_url_path='')


def pre_flight_checks() -> bool:
""" Checks to make before the server starts. Server can start if any of these fail """
# Check if the static folder has been created by the webapp build step
if not (Path(__file__).absolute().parent / 'static').exists():
print('WARNING: static directory does not exist in module root. Build the webapp to populate this directory.')
return False
return True


@app.route("/", methods=["GET"], endpoint='home')
def root():
""" Serve React application to all frontend routes """
Expand Down
64 changes: 55 additions & 9 deletions whos_on_my_network/config.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,60 @@
import os
from pathlib import Path
import json

# Database
DATABASE_SQLITE_FILE_LOCATION = os.getenv('DATABASE_SQLITE_FILE_LOCATION', Path(__file__).absolute().parent / 'database.sqlite')
from . import utils

# Web interface
HOST = os.getenv('HOST', '0.0.0.0')
PORT = int(os.getenv('PORT', 8080))

# General Application
VERBOSE = False
DEFAULT_NETWORK_ID = '192.168.1.0/24'
DEFAULT_PLUGIN = None
CONFIG_FILENAME = 'config.json'
DATABASE_FILENAME = 'database.sqlite'


__packaged = utils.is_packaged() or os.getenv('FORCE_PACKAGED_MODE', 'false') == 'true'


def __get_data_folder() -> Path:
""" Identify a directory for where the settings and database reside """
if not __packaged:
return Path(__file__).absolute().parent.parent # Top level of the project for development
elif os.name == 'nt':
return Path(os.getenv('APPDATA')) / 'WhoIsOnMyNetwork' # Windows default is in local Appdata
else:
return Path.home() / 'WhoIsOnMyNetwork' # Default is the home directory


def __get_default_configuration() -> dict:
""" The default contents of {CONFIG_FILENAME} """
default_config = {
'host': '0.0.0.0',
'port': 8080,
'verbose': False,
'default_network_id': '192.168.1.0/24'
}

if not __packaged:
default_config['default_plugin'] = None

return default_config


# Make sure the root exists
root = __get_data_folder()
root.mkdir(parents=True, exist_ok=True)

# Populate config with default data if it doesn't exist already
config_file = (root / CONFIG_FILENAME)
if not config_file.exists():
with config_file.open('w') as f:
json.dump(__get_default_configuration(), f, indent=4)

# Read in config
with config_file.open('r') as f:
raw_config_data = json.load(f)

# Get expected config values
DATABASE_FILE_LOCATION = str(root / DATABASE_FILENAME)
HOST = os.getenv('HOST', raw_config_data['host'])
PORT = int(str(os.getenv('PORT', raw_config_data['port'])))
VERBOSE = str(os.getenv('VERBOSE', raw_config_data['verbose'])).lower() == 'true'
DEFAULT_NETWORK_ID = os.getenv('DEFAULT_NETWORK_ID', raw_config_data['default_network_id'])
DEFAULT_PLUGIN = None if __packaged else os.getenv('DEFAULT_PLUGIN', raw_config_data['default_plugin'])
2 changes: 1 addition & 1 deletion whos_on_my_network/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@


database = peewee.SqliteDatabase(
config.DATABASE_SQLITE_FILE_LOCATION,
config.DATABASE_FILE_LOCATION,
pragmas={
'foreign_keys': 1 # Enforce foreign-key constraints
}
Expand Down
6 changes: 6 additions & 0 deletions whos_on_my_network/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import datetime
import sys


def get_utc_datetime():
Expand Down Expand Up @@ -45,3 +46,8 @@ def serialize_dict(_dict: dict) -> dict:
value = _dict[key]
_dict[key] = __serialize_value(value)
return _dict


def is_packaged() -> bool:
""" Identify whether we are running in a bundled package by PyInstaller """
return hasattr(sys, 'frozen') and getattr(sys, 'frozen') and hasattr(sys, '_MEIPASS')

0 comments on commit cbf9333

Please sign in to comment.