Merge branch 'build'
* build: one less pylint exception tests for windows, simplify pylint lbryum is in requirements now build daemon separately in teamcity
|
@ -1,13 +1,36 @@
|
||||||
version: 1.0.{build}
|
version: 1.0.{build}
|
||||||
|
|
||||||
|
environment:
|
||||||
|
GH_TOKEN:
|
||||||
|
secure: LiI5jyuHUw6XbH4kC3gP1HX4P/v4rwD/gCNtaFhQu2AvJz1/1wALkp5ECnIxRyS
|
||||||
|
# key_pass:
|
||||||
|
# secure: u6DydPcdrUJlxGL9uc7yQRYG8+5rY6aAEE9nfCSzFyNzZlX9NniOp8Uh5ZKQqX7bGEngLI6ipbLfiJvn0XFnhbn2iTkOuMqOXVJVOehvwlQ=
|
||||||
|
# pfx_key:
|
||||||
|
# secure: 1mwqyRy7hDqDjDK+TIAoaXyXzpNgwruFNA6TPkinUcVM7A+NLD33RQLnfnwVy+R5ovD2pUfhQ6+N0Fqebv6tZh436LIEsock+6IOdpgFwrg=
|
||||||
|
|
||||||
notifications:
|
#
|
||||||
- provider: Slack
|
#notifications:
|
||||||
incoming_webhook:
|
# - provider: Slack
|
||||||
secure: LuxwG5OZnnA//gmSXzCKu8/FRqYjCgGfVFqajSsGHeQ1HQNp7rYNhQpsO8/3PK63xKJj3wzt86DJekf9q9Q5OcHa9AHXUQbEveX0psd7elw=
|
# incoming_webhook:
|
||||||
|
# secure: LuxwG5OZnnA//gmSXzCKu8/FRqYjCgGfVFqajSsGHeQ1HQNp7rYNhQpsO8/3PK63xKJj3wzt86DJekf9q9Q5OcHa9AHXUQbEveX0psd7elw=
|
||||||
|
|
||||||
|
|
||||||
clone_folder: c:\projects\lbry
|
clone_folder: c:\projects\lbry
|
||||||
|
|
||||||
|
test_script:
|
||||||
|
- cd C:\projects\lbry\
|
||||||
|
- pip install cython
|
||||||
|
- pip install mock pylint unqlite
|
||||||
|
- pylint lbrynet
|
||||||
|
- python -m twisted.trial tests # avoids having to set PYTHONPATH=. (see https://twistedmatrix.com/trac/ticket/9035)
|
||||||
|
#- rvm use 2.3.1 && gem install danger --version '~> 4.0' && danger
|
||||||
|
|
||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
- echo "Not currently building on appveyor"
|
- cd C:\projects\lbry\build\
|
||||||
|
- ps: .\build.ps1
|
||||||
|
|
||||||
|
|
||||||
|
artifacts:
|
||||||
|
- path: build\dist\*.zip
|
||||||
|
name: lbrynet-daemon
|
||||||
|
|
6
.gitignore
vendored
|
@ -9,8 +9,10 @@
|
||||||
*.prof
|
*.prof
|
||||||
.#*
|
.#*
|
||||||
|
|
||||||
/build
|
/build/build
|
||||||
/dist
|
/build/dist
|
||||||
|
/bulid/requirements_base.txt
|
||||||
|
|
||||||
/lbrynet.egg-info
|
/lbrynet.egg-info
|
||||||
/docs_build
|
/docs_build
|
||||||
/lbry-venv
|
/lbry-venv
|
||||||
|
|
58
.pylintrc
|
@ -65,7 +65,60 @@ confidence=
|
||||||
# --enable=similarities". If you want to run only the classes checker, but have
|
# --enable=similarities". If you want to run only the classes checker, but have
|
||||||
# no Warning level messages displayed, use"--disable=all --enable=classes
|
# no Warning level messages displayed, use"--disable=all --enable=classes
|
||||||
# --disable=W"
|
# --disable=W"
|
||||||
disable=import-star-module-level,old-octal-literal,oct-method,print-statement,unpacking-in-except,parameter-unpacking,backtick,old-raise-syntax,old-ne-operator,long-suffix,dict-view-method,dict-iter-method,metaclass-assignment,next-method-called,raising-string,indexing-exception,raw_input-builtin,long-builtin,file-builtin,execfile-builtin,coerce-builtin,cmp-builtin,buffer-builtin,basestring-builtin,apply-builtin,filter-builtin-not-iterating,using-cmp-argument,useless-suppression,range-builtin-not-iterating,suppressed-message,no-absolute-import,old-division,cmp-method,reload-builtin,zip-builtin-not-iterating,intern-builtin,unichr-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,input-builtin,round-builtin,hex-method,nonzero-method,map-builtin-not-iterating
|
disable=
|
||||||
|
anomalous-backslash-in-string,
|
||||||
|
arguments-differ,
|
||||||
|
attribute-defined-outside-init,
|
||||||
|
bad-continuation,
|
||||||
|
bare-except,
|
||||||
|
broad-except,
|
||||||
|
cell-var-from-loop,
|
||||||
|
consider-iterating-dictionary,
|
||||||
|
cyclic-import,
|
||||||
|
dangerous-default-value,
|
||||||
|
duplicate-code,
|
||||||
|
exec-used,
|
||||||
|
fixme,
|
||||||
|
global-statement,
|
||||||
|
inherit-non-class,
|
||||||
|
invalid-name,
|
||||||
|
locally-disabled,
|
||||||
|
logging-not-lazy,
|
||||||
|
missing-docstring,
|
||||||
|
multiple-imports,
|
||||||
|
no-init,
|
||||||
|
no-member,
|
||||||
|
no-self-use,
|
||||||
|
pointless-string-statement,
|
||||||
|
protected-access,
|
||||||
|
redefined-builtin,
|
||||||
|
redefined-outer-name,
|
||||||
|
redefined-variable-type,
|
||||||
|
relative-import,
|
||||||
|
signature-differs,
|
||||||
|
singleton-comparison,
|
||||||
|
super-init-not-called,
|
||||||
|
too-few-public-methods,
|
||||||
|
too-many-arguments,
|
||||||
|
too-many-branches,
|
||||||
|
too-many-instance-attributes,
|
||||||
|
too-many-lines,
|
||||||
|
too-many-locals,
|
||||||
|
too-many-nested-blocks,
|
||||||
|
too-many-public-methods,
|
||||||
|
too-many-return-statements,
|
||||||
|
too-many-statements,
|
||||||
|
trailing-newlines,
|
||||||
|
undefined-loop-variable,
|
||||||
|
ungrouped-imports,
|
||||||
|
unidiomatic-typecheck,
|
||||||
|
unnecessary-lambda,
|
||||||
|
unused-argument,
|
||||||
|
unused-variable,
|
||||||
|
useless-else-on-loop,
|
||||||
|
wildcard-import,
|
||||||
|
wrong-import-order,
|
||||||
|
wrong-import-position
|
||||||
|
|
||||||
|
|
||||||
[REPORTS]
|
[REPORTS]
|
||||||
|
@ -294,7 +347,8 @@ ignore-mixin-members=yes
|
||||||
# (useful for modules/projects where namespaces are manipulated during runtime
|
# (useful for modules/projects where namespaces are manipulated during runtime
|
||||||
# and thus existing member attributes cannot be deduced by static analysis. It
|
# and thus existing member attributes cannot be deduced by static analysis. It
|
||||||
# supports qualified module names, as well as Unix pattern matching.
|
# supports qualified module names, as well as Unix pattern matching.
|
||||||
ignored-modules=twisted.internet.reactor,leveldb
|
ignored-modules=twisted.internet.reactor,leveldb,distutils
|
||||||
|
# Ignoring distutils because: https://github.com/PyCQA/pylint/issues/73
|
||||||
|
|
||||||
# List of classes names for which member attributes should not be checked
|
# List of classes names for which member attributes should not be checked
|
||||||
# (useful for classes with attributes dynamically set). This supports can work
|
# (useful for classes with attributes dynamically set). This supports can work
|
||||||
|
|
|
@ -33,12 +33,11 @@ before_install:
|
||||||
install:
|
install:
|
||||||
- pip install -U pip
|
- pip install -U pip
|
||||||
- pip install -r requirements.txt
|
- pip install -r requirements.txt
|
||||||
- pip install git+https://github.com/lbryio/lbryum.git
|
|
||||||
- pip install .
|
- pip install .
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- pip install cython
|
- pip install cython
|
||||||
- pip install mock pylint unqlite
|
- pip install mock pylint unqlite
|
||||||
- ./run_pylint.sh
|
- pylint lbrynet
|
||||||
- ./run_tests.sh
|
- PYTHONPATH=. trial tests
|
||||||
- rvm use 2.3.1 && gem install danger --version '~> 4.0' && danger
|
- rvm use 2.3.1 && gem install danger --version '~> 4.0' && danger
|
||||||
|
|
30
build/build.ps1
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
$env:Path += ";C:\MinGW\bin\"
|
||||||
|
|
||||||
|
$env:Path += ";C:\Program Files (x86)\Windows Kits\10\bin\x86\"
|
||||||
|
gcc --version
|
||||||
|
mingw32-make --version
|
||||||
|
|
||||||
|
# build/install miniupnpc manually
|
||||||
|
tar zxf miniupnpc-1.9.tar.gz
|
||||||
|
cd miniupnpc-1.9
|
||||||
|
mingw32-make -f Makefile.mingw
|
||||||
|
python setupmingw32.py build --compiler=mingw32
|
||||||
|
python setupmingw32.py install
|
||||||
|
cd ..\
|
||||||
|
Remove-Item -Recurse -Force miniupnpc-1.9
|
||||||
|
|
||||||
|
# copy requirements from lbry, but remove gmpy and miniupnpc (installed manually)
|
||||||
|
Get-Content ..\requirements.txt | Select-String -Pattern 'gmpy|miniupnpc' -NotMatch | Out-File requirements_base.txt
|
||||||
|
# add in gmpy wheel
|
||||||
|
Add-Content requirements.txt "./gmpy-1.17-cp27-none-win32.whl"
|
||||||
|
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
python set_version.py
|
||||||
|
python set_build.py
|
||||||
|
|
||||||
|
pyinstaller -y daemon.onefile.spec
|
||||||
|
pyinstaller -y cli.onefile.spec
|
||||||
|
|
||||||
|
python zip_daemon.py
|
||||||
|
python release_on_tag.py
|
61
build/build.sh
Executable file
|
@ -0,0 +1,61 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
set -x
|
||||||
|
|
||||||
|
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )"
|
||||||
|
cd "$ROOT"
|
||||||
|
BUILD_DIR="$ROOT/build"
|
||||||
|
|
||||||
|
FULL_BUILD="${FULL_BUILD:-false}"
|
||||||
|
if [ -n "${TEAMCITY_VERSION:-}" -o -n "${APPVEYOR:-}" ]; then
|
||||||
|
FULL_BUILD="true"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -d "$BUILD_DIR/bulid" ] && rm -rf "$BUILD_DIR/build"
|
||||||
|
[ -d "$BUILD_DIR/dist" ] && rm -rf "$BUILD_DIR/dist"
|
||||||
|
|
||||||
|
if [ "$FULL_BUILD" == "true" ]; then
|
||||||
|
# install dependencies
|
||||||
|
$BUILD_DIR/prebuild.sh
|
||||||
|
|
||||||
|
VENV="$BUILD_DIR/venv"
|
||||||
|
if [ -d "$VENV" ]; then
|
||||||
|
rm -rf "$VENV"
|
||||||
|
fi
|
||||||
|
virtualenv "$VENV"
|
||||||
|
set +u
|
||||||
|
source "$VENV/bin/activate"
|
||||||
|
set -u
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
cp "$ROOT/requirements.txt" "$BUILD_DIR/requirements_base.txt"
|
||||||
|
(
|
||||||
|
cd "$BUILD_DIR"
|
||||||
|
pip install -r requirements.txt
|
||||||
|
)
|
||||||
|
|
||||||
|
if [ "$FULL_BUILD" == "true" ]; then
|
||||||
|
python "$BUILD_DIR/set_version.py"
|
||||||
|
python "$BUILD_DIR/set_build.py"
|
||||||
|
fi
|
||||||
|
|
||||||
|
(
|
||||||
|
cd "$BUILD_DIR"
|
||||||
|
pyinstaller -y daemon.onefile.spec
|
||||||
|
pyinstaller -y cli.onefile.spec
|
||||||
|
)
|
||||||
|
|
||||||
|
python "$BUILD_DIR/zip_daemon.py"
|
||||||
|
|
||||||
|
if [ "$FULL_BUILD" == "true" ]; then
|
||||||
|
# electron-build has a publish feature, but I had a hard time getting
|
||||||
|
# it to reliably work and it also seemed difficult to configure. Not proud of
|
||||||
|
# this, but it seemed better to write my own.
|
||||||
|
python "$BUILD_DIR/release_on_tag.py"
|
||||||
|
|
||||||
|
deactivate
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo 'Build complete.'
|
59
build/cli.onefile.spec
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
# -*- mode: python -*-
|
||||||
|
import platform
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
dir = 'build';
|
||||||
|
cwd = os.getcwd()
|
||||||
|
if os.path.basename(cwd) != dir:
|
||||||
|
raise Exception('pyinstaller build needs to be run from the ' + dir + ' directory')
|
||||||
|
repo_base = os.path.abspath(os.path.join(cwd, '..'))
|
||||||
|
|
||||||
|
|
||||||
|
system = platform.system()
|
||||||
|
if system == 'Darwin':
|
||||||
|
icns = os.path.join(repo_base, 'build', 'icon.icns')
|
||||||
|
elif system == 'Linux':
|
||||||
|
icns = os.path.join(repo_base, 'build', 'icons', '256x256.png')
|
||||||
|
elif system == 'Windows':
|
||||||
|
icns = os.path.join(repo_base, 'build', 'icons', 'lbry256.ico')
|
||||||
|
else:
|
||||||
|
print 'Warning: System {} has no icons'.format(system)
|
||||||
|
icns = None
|
||||||
|
|
||||||
|
block_cipher = None
|
||||||
|
|
||||||
|
|
||||||
|
a = Analysis(
|
||||||
|
['cli.py'],
|
||||||
|
pathex=[cwd],
|
||||||
|
binaries=None,
|
||||||
|
datas=[],
|
||||||
|
hiddenimports=[],
|
||||||
|
hookspath=[],
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
win_no_prefer_redirects=False,
|
||||||
|
win_private_assemblies=False,
|
||||||
|
cipher=block_cipher
|
||||||
|
)
|
||||||
|
|
||||||
|
pyz = PYZ(
|
||||||
|
a.pure,
|
||||||
|
a.zipped_data,
|
||||||
|
cipher=block_cipher
|
||||||
|
)
|
||||||
|
|
||||||
|
exe = EXE(
|
||||||
|
pyz,
|
||||||
|
a.scripts,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
|
name='lbrynet-cli',
|
||||||
|
debug=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
console=True,
|
||||||
|
icon=icns
|
||||||
|
)
|
7
build/cli.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
from lbrynet.lbrynet_daemon import DaemonCLI
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logging.basicConfig()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
DaemonCLI.main()
|
78
build/daemon.onefile.spec
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
# -*- mode: python -*-
|
||||||
|
import platform
|
||||||
|
import os
|
||||||
|
|
||||||
|
import lbryum
|
||||||
|
|
||||||
|
|
||||||
|
dir = 'build';
|
||||||
|
cwd = os.getcwd()
|
||||||
|
if os.path.basename(cwd) != dir:
|
||||||
|
raise Exception('pyinstaller build needs to be run from the ' + dir + ' directory')
|
||||||
|
repo_base = os.path.abspath(os.path.join(cwd, '..'))
|
||||||
|
|
||||||
|
|
||||||
|
system = platform.system()
|
||||||
|
if system == 'Darwin':
|
||||||
|
icns = os.path.join(repo_base, 'build', 'icon.icns')
|
||||||
|
elif system == 'Linux':
|
||||||
|
icns = os.path.join(repo_base, 'build', 'icons', '256x256.png')
|
||||||
|
elif system == 'Windows':
|
||||||
|
icns = os.path.join(repo_base, 'build', 'icons', 'lbry256.ico')
|
||||||
|
else:
|
||||||
|
print 'Warning: System {} has no icons'.format(system)
|
||||||
|
icns = None
|
||||||
|
|
||||||
|
|
||||||
|
block_cipher = None
|
||||||
|
|
||||||
|
|
||||||
|
languages = (
|
||||||
|
'chinese_simplified.txt', 'japanese.txt', 'spanish.txt',
|
||||||
|
'english.txt', 'portuguese.txt'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
datas = [
|
||||||
|
(
|
||||||
|
os.path.join(os.path.dirname(lbryum.__file__), 'wordlist', language),
|
||||||
|
'lbryum/wordlist'
|
||||||
|
)
|
||||||
|
for language in languages
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
a = Analysis(
|
||||||
|
['daemon.py'],
|
||||||
|
pathex=[cwd],
|
||||||
|
binaries=None,
|
||||||
|
datas=datas,
|
||||||
|
hiddenimports=[],
|
||||||
|
hookspath=[],
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
win_no_prefer_redirects=False,
|
||||||
|
win_private_assemblies=False,
|
||||||
|
cipher=block_cipher
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
pyz = PYZ(
|
||||||
|
a.pure, a.zipped_data,
|
||||||
|
cipher=block_cipher
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
exe = EXE(
|
||||||
|
pyz,
|
||||||
|
a.scripts,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
|
name='lbrynet-daemon',
|
||||||
|
debug=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
console=True,
|
||||||
|
icon=icns
|
||||||
|
)
|
4
build/daemon.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
from lbrynet.lbrynet_daemon import DaemonControl
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
DaemonControl.start()
|
BIN
build/icons/128x128.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
build/icons/256x256.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
build/icons/32x32.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
build/icons/48x48.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
build/icons/96x96.png
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
build/icons/lbry128.ico
Normal file
After Width: | Height: | Size: 97 KiB |
BIN
build/icons/lbry16.ico
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
build/icons/lbry256.ico
Normal file
After Width: | Height: | Size: 361 KiB |
BIN
build/icons/lbry32.ico
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
build/icons/lbry48.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
build/icons/lbry96.ico
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
build/miniupnpc-1.9.tar.gz
Normal file
81
build/prebuild.sh
Executable file
|
@ -0,0 +1,81 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
set -x
|
||||||
|
|
||||||
|
|
||||||
|
LINUX=false
|
||||||
|
OSX=false
|
||||||
|
|
||||||
|
if [ "$(uname)" == "Darwin" ]; then
|
||||||
|
OSX=true
|
||||||
|
elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
|
||||||
|
LINUX=true
|
||||||
|
else
|
||||||
|
echo "Platform detection failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
SUDO=''
|
||||||
|
if $LINUX && (( $EUID != 0 )); then
|
||||||
|
SUDO='sudo'
|
||||||
|
fi
|
||||||
|
|
||||||
|
cmd_exists() {
|
||||||
|
command -v "$1" >/dev/null 2>&1
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
set +eu
|
||||||
|
GITUSERNAME=$(git config --global --get user.name)
|
||||||
|
if [ -z "$GITUSERNAME" ]; then
|
||||||
|
git config --global user.name "$(whoami)"
|
||||||
|
fi
|
||||||
|
GITEMAIL=$(git config --global --get user.email)
|
||||||
|
if [ -z "$GITEMAIL" ]; then
|
||||||
|
git config --global user.email "$(whoami)@lbry.io"
|
||||||
|
fi
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
|
||||||
|
if $LINUX; then
|
||||||
|
INSTALL="$SUDO apt-get install --no-install-recommends -y"
|
||||||
|
$INSTALL build-essential libssl-dev libffi-dev libgmp3-dev python2.7-dev
|
||||||
|
elif $OSX && ! cmd_exists brew ; then
|
||||||
|
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
if ! cmd_exists python; then
|
||||||
|
if $LINUX; then
|
||||||
|
$INSTALL python2.7
|
||||||
|
elif $OSX; then
|
||||||
|
brew install python
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
PYTHON_VERSION=$(python -c 'import sys; print(".".join(map(str, sys.version_info[:2])))')
|
||||||
|
if [ "$PYTHON_VERSION" != "2.7" ]; then
|
||||||
|
echo "Python 2.7 required"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! cmd_exists pip; then
|
||||||
|
if $LINUX; then
|
||||||
|
$INSTALL python-pip
|
||||||
|
$SUDO pip install --upgrade pip
|
||||||
|
else
|
||||||
|
echo "Pip required"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if $LINUX && [ "$(pip list --format=columns | grep setuptools | wc -l)" -ge 1 ]; then
|
||||||
|
#$INSTALL python-setuptools
|
||||||
|
$SUDO pip install setuptools
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! cmd_exists virtualenv; then
|
||||||
|
$SUDO pip install virtualenv
|
||||||
|
fi
|
128
build/release_on_tag.py
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
import glob
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import github
|
||||||
|
import requests
|
||||||
|
import uritemplate
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
this_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
try:
|
||||||
|
current_tag = subprocess.check_output(
|
||||||
|
['git', 'describe', '--exact-match', 'HEAD']).strip()
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
print 'Stopping as we are not currently on a tag'
|
||||||
|
return
|
||||||
|
|
||||||
|
if 'GH_TOKEN' not in os.environ:
|
||||||
|
print 'Must set GH_TOKEN in order to publish assets to a release'
|
||||||
|
return
|
||||||
|
|
||||||
|
gh_token = os.environ['GH_TOKEN']
|
||||||
|
auth = github.Github(gh_token)
|
||||||
|
repo = auth.get_repo('lbryio/lbry')
|
||||||
|
|
||||||
|
if not check_repo_has_tag(repo, current_tag):
|
||||||
|
print 'Tag {} is not in repo {}'.format(current_tag, repo)
|
||||||
|
# TODO: maybe this should be an error
|
||||||
|
return
|
||||||
|
|
||||||
|
daemon_zip = glob.glob(this_dir + '/dist/*.zip')[0]
|
||||||
|
release = get_release(repo, current_tag)
|
||||||
|
upload_asset(release, daemon_zip, gh_token)
|
||||||
|
|
||||||
|
|
||||||
|
def check_repo_has_tag(repo, target_tag):
|
||||||
|
tags = repo.get_tags().get_page(0)
|
||||||
|
for tag in tags:
|
||||||
|
if tag.name == target_tag:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_release(current_repo, current_tag):
|
||||||
|
for release in current_repo.get_releases():
|
||||||
|
if release.tag_name == current_tag:
|
||||||
|
return release
|
||||||
|
raise Exception('No release for {} was found'.format(current_tag))
|
||||||
|
|
||||||
|
|
||||||
|
def upload_asset(release, asset_to_upload, token):
|
||||||
|
basename = os.path.basename(asset_to_upload)
|
||||||
|
if is_asset_already_uploaded(release, basename):
|
||||||
|
return
|
||||||
|
count = 0
|
||||||
|
while count < 10:
|
||||||
|
try:
|
||||||
|
return _upload_asset(release, asset_to_upload, token, _curl_uploader)
|
||||||
|
except Exception:
|
||||||
|
print 'Failed uploading on attempt {}'.format(count + 1)
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
|
||||||
|
def _upload_asset(release, asset_to_upload, token, uploader):
|
||||||
|
basename = os.path.basename(asset_to_upload)
|
||||||
|
upload_uri = uritemplate.expand(release.upload_url, {'name': basename})
|
||||||
|
output = uploader(upload_uri, asset_to_upload, token)
|
||||||
|
if 'errors' in output:
|
||||||
|
raise Exception(output)
|
||||||
|
else:
|
||||||
|
print 'Successfully uploaded to {}'.format(output['browser_download_url'])
|
||||||
|
|
||||||
|
|
||||||
|
# requests doesn't work on windows / linux / osx.
|
||||||
|
def _requests_uploader(upload_uri, asset_to_upload, token):
|
||||||
|
print 'Using requests to upload {} to {}'.format(asset_to_upload, upload_uri)
|
||||||
|
with open(asset_to_upload, 'rb') as f:
|
||||||
|
response = requests.post(upload_uri, data=f, auth=('', token))
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
|
||||||
|
# curl -H "Content-Type: application/json" -X POST -d '{"username":"xyz","password":"xyz"}' http://localhost:3000/api/login
|
||||||
|
|
||||||
|
|
||||||
|
def _curl_uploader(upload_uri, asset_to_upload, token):
|
||||||
|
# using requests.post fails miserably with SSL EPIPE errors. I spent
|
||||||
|
# half a day trying to debug before deciding to switch to curl.
|
||||||
|
#
|
||||||
|
# TODO: actually set the content type
|
||||||
|
print 'Using curl to upload {} to {}'.format(asset_to_upload, upload_uri)
|
||||||
|
cmd = [
|
||||||
|
'curl',
|
||||||
|
'-sS',
|
||||||
|
'-X', 'POST',
|
||||||
|
'-u', ':{}'.format(os.environ['GH_TOKEN']),
|
||||||
|
'--header', 'Content-Type: application/octet-stream',
|
||||||
|
'--data-binary', '@-',
|
||||||
|
upload_uri
|
||||||
|
]
|
||||||
|
# '-d', '{"some_key": "some_value"}',
|
||||||
|
print 'Calling curl:'
|
||||||
|
print cmd
|
||||||
|
print
|
||||||
|
with open(asset_to_upload, 'rb') as fp:
|
||||||
|
p = subprocess.Popen(cmd, stdin=fp, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||||
|
stdout, stderr = p.communicate()
|
||||||
|
print 'curl return code:', p.returncode
|
||||||
|
if stderr:
|
||||||
|
print 'stderr output from curl:'
|
||||||
|
print stderr
|
||||||
|
print 'stdout from curl:'
|
||||||
|
print stdout
|
||||||
|
return json.loads(stdout)
|
||||||
|
|
||||||
|
|
||||||
|
def is_asset_already_uploaded(release, basename):
|
||||||
|
for asset in release.raw_data['assets']:
|
||||||
|
if asset['name'] == basename:
|
||||||
|
print 'File {} has already been uploaded to {}'.format(basename, release.tag_name)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
13
build/requirements.txt
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# install daemon requirements (copied from root, with possible modifications. see build.sh, build.ps1)
|
||||||
|
-r requirements_base.txt
|
||||||
|
|
||||||
|
# install daemon itself. make sure you run `pip install` from this dir. this is how you do relative file paths with pip
|
||||||
|
file:../.
|
||||||
|
|
||||||
|
# install other build requirements
|
||||||
|
PyInstaller==3.2.1
|
||||||
|
requests[security]==2.13.0
|
||||||
|
GitPython==2.1.1
|
||||||
|
PyGithub==1.32
|
||||||
|
uritemplate==3.0.0
|
||||||
|
git+https://github.com/lbryio/bumpversion.git
|
1
build/requirements_base.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
THIS FILE GETS FILLED IN BY BUILD SCRIPT AND INCLUDED IN requirements.txt
|
29
build/set_build.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
"""Set the build version to be 'dev', 'qa', 'rc', 'release'"""
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
build = get_build()
|
||||||
|
root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||||
|
with open(os.path.join(root_dir, 'lbrynet', 'build_type.py'), 'w') as f:
|
||||||
|
f.write("BUILD = '{}'\n".format(build))
|
||||||
|
|
||||||
|
|
||||||
|
def get_build():
|
||||||
|
try:
|
||||||
|
tag = subprocess.check_output(['git', 'describe', '--exact-match']).strip()
|
||||||
|
if re.match('v\d+\.\d+\.\d+rc\d+', tag):
|
||||||
|
return 'rc'
|
||||||
|
else:
|
||||||
|
return 'release'
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
# if the build doesn't have a tag
|
||||||
|
return 'qa'
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
52
build/set_version.py
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
"""Set the package version to the output of `git describe`"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os.path
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('--version', help="defaults to the output of `git describe`")
|
||||||
|
args = parser.parse_args()
|
||||||
|
if args.version:
|
||||||
|
version = args.version
|
||||||
|
else:
|
||||||
|
tag = subprocess.check_output(['git', 'describe']).strip()
|
||||||
|
try:
|
||||||
|
version = get_version_from_tag(tag)
|
||||||
|
except InvalidVersionTag:
|
||||||
|
# this should be an error but its easier to handle here
|
||||||
|
# than in the calling scripts.
|
||||||
|
print 'Tag cannot be converted to a version. Exiting.'
|
||||||
|
return
|
||||||
|
set_version(version)
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidVersionTag(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def get_version_from_tag(tag):
|
||||||
|
match = re.match('v([\d.]+)', tag)
|
||||||
|
if match:
|
||||||
|
return match.group(1)
|
||||||
|
else:
|
||||||
|
raise InvalidVersionTag('Failed to parse version from tag {}'.format(tag))
|
||||||
|
|
||||||
|
|
||||||
|
def set_version(version):
|
||||||
|
root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||||
|
with open(os.path.join(root_dir, 'lbrynet', '__init__.py'), 'w') as fp:
|
||||||
|
fp.write(LBRYNET_TEMPLATE.format(version=version))
|
||||||
|
|
||||||
|
|
||||||
|
LBRYNET_TEMPLATE = """
|
||||||
|
__version__ = "{version}"
|
||||||
|
version = tuple(__version__.split('.'))
|
||||||
|
"""
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
29
build/zip_daemon.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
this_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
tag = subprocess.check_output(['git', 'describe']).strip()
|
||||||
|
zipfilename = 'lbrynet-daemon-{}-{}.zip'.format(tag, get_system_label())
|
||||||
|
full_filename = os.path.join(this_dir, 'dist', zipfilename)
|
||||||
|
executables = ['lbrynet-daemon', 'lbrynet-cli']
|
||||||
|
ext = '.exe' if platform.system() == 'Windows' else ''
|
||||||
|
with zipfile.ZipFile(full_filename, 'w') as myzip:
|
||||||
|
for executable in executables:
|
||||||
|
myzip.write(os.path.join(this_dir, 'dist', executable + ext), executable + ext)
|
||||||
|
|
||||||
|
|
||||||
|
def get_system_label():
|
||||||
|
system = platform.system()
|
||||||
|
if system == 'Darwin':
|
||||||
|
return 'macos'
|
||||||
|
else:
|
||||||
|
return system.lower()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
|
@ -239,9 +239,9 @@ class RequestHelper(object):
|
||||||
|
|
||||||
|
|
||||||
def _handle_incoming_blob(response_dict, peer, request):
|
def _handle_incoming_blob(response_dict, peer, request):
|
||||||
if not request.response_identifier in response_dict:
|
if request.response_identifier not in response_dict:
|
||||||
return InvalidResponseError("response identifier not in response")
|
return InvalidResponseError("response identifier not in response")
|
||||||
if not type(response_dict[request.response_identifier]) == dict:
|
if type(response_dict[request.response_identifier]) != dict:
|
||||||
return InvalidResponseError("response not a dict. got %s" %
|
return InvalidResponseError("response not a dict. got %s" %
|
||||||
type(response_dict[request.response_identifier]))
|
type(response_dict[request.response_identifier]))
|
||||||
response = response_dict[request.response_identifier]
|
response = response_dict[request.response_identifier]
|
||||||
|
@ -255,14 +255,14 @@ def _handle_incoming_blob(response_dict, peer, request):
|
||||||
return InvalidResponseError("Got an unknown error from the peer: %s" %
|
return InvalidResponseError("Got an unknown error from the peer: %s" %
|
||||||
(response['error'],))
|
(response['error'],))
|
||||||
else:
|
else:
|
||||||
if not 'blob_hash' in response:
|
if 'blob_hash' not in response:
|
||||||
return InvalidResponseError("Missing the required field 'blob_hash'")
|
return InvalidResponseError("Missing the required field 'blob_hash'")
|
||||||
if not response['blob_hash'] == request.request_dict['requested_blob']:
|
if not response['blob_hash'] == request.request_dict['requested_blob']:
|
||||||
return InvalidResponseError(
|
return InvalidResponseError(
|
||||||
"Incoming blob does not match expected. Incoming: %s. Expected: %s" %
|
"Incoming blob does not match expected. Incoming: %s. Expected: %s" %
|
||||||
(response['blob_hash'], request.request_dict['requested_blob'])
|
(response['blob_hash'], request.request_dict['requested_blob'])
|
||||||
)
|
)
|
||||||
if not 'length' in response:
|
if 'length' not in response:
|
||||||
return InvalidResponseError("Missing the required field 'length'")
|
return InvalidResponseError("Missing the required field 'length'")
|
||||||
if not request.blob.set_length(response['length']):
|
if not request.blob.set_length(response['length']):
|
||||||
return InvalidResponseError("Could not set the length of the blob")
|
return InvalidResponseError("Could not set the length of the blob")
|
||||||
|
@ -358,7 +358,7 @@ class AvailabilityRequest(RequestHelper):
|
||||||
class PriceRequest(RequestHelper):
|
class PriceRequest(RequestHelper):
|
||||||
"""Ask a peer if a certain price is acceptable"""
|
"""Ask a peer if a certain price is acceptable"""
|
||||||
def can_make_request(self):
|
def can_make_request(self):
|
||||||
if len(self.available_blobs) and not self.protocol in self.protocol_prices:
|
if len(self.available_blobs) and self.protocol not in self.protocol_prices:
|
||||||
return self.get_rate() is not None
|
return self.get_rate() is not None
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
|
@ -21,9 +21,6 @@ class DHTPeerFinder(object):
|
||||||
self.next_manage_call = None
|
self.next_manage_call = None
|
||||||
|
|
||||||
def run_manage_loop(self):
|
def run_manage_loop(self):
|
||||||
|
|
||||||
from twisted.internet import reactor
|
|
||||||
|
|
||||||
self._manage_peers()
|
self._manage_peers()
|
||||||
self.next_manage_call = reactor.callLater(60, self.run_manage_loop)
|
self.next_manage_call = reactor.callLater(60, self.run_manage_loop)
|
||||||
|
|
||||||
|
|
|
@ -106,7 +106,7 @@ class IterableContainer(object):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class Checker:
|
class Checker(object):
|
||||||
"""The looping calls the daemon runs"""
|
"""The looping calls the daemon runs"""
|
||||||
INTERNET_CONNECTION = 'internet_connection_checker'
|
INTERNET_CONNECTION = 'internet_connection_checker'
|
||||||
VERSION = 'version_checker'
|
VERSION = 'version_checker'
|
||||||
|
|
|
@ -414,9 +414,6 @@ class AuthJSONRPCServer(AuthorizedBase):
|
||||||
return True
|
return True
|
||||||
allowed_server, allowed_port = self.get_server_port(allowed_origin)
|
allowed_server, allowed_port = self.get_server_port(allowed_origin)
|
||||||
return (allowed_server, allowed_port) == (server, port)
|
return (allowed_server, allowed_port) == (server, port)
|
||||||
return (
|
|
||||||
server == conf.settings['api_host'] and
|
|
||||||
port == conf.settings['api_port'])
|
|
||||||
|
|
||||||
def get_server_port(self, origin):
|
def get_server_port(self, origin):
|
||||||
parsed = urlparse.urlparse(origin)
|
parsed = urlparse.urlparse(origin)
|
||||||
|
|
|
@ -20,7 +20,7 @@ class GUID(ctypes.Structure):
|
||||||
|
|
||||||
|
|
||||||
# http://msdn.microsoft.com/en-us/library/windows/desktop/dd378457.aspx
|
# http://msdn.microsoft.com/en-us/library/windows/desktop/dd378457.aspx
|
||||||
class FOLDERID:
|
class FOLDERID(object):
|
||||||
# pylint: disable=bad-whitespace
|
# pylint: disable=bad-whitespace
|
||||||
AccountPictures = UUID('{008ca0b1-55b4-4c56-b8a8-4de4b299d3be}')
|
AccountPictures = UUID('{008ca0b1-55b4-4c56-b8a8-4de4b299d3be}')
|
||||||
AdminTools = UUID('{724EF170-A42D-4FEF-9F26-B60E846FBA4F}')
|
AdminTools = UUID('{724EF170-A42D-4FEF-9F26-B60E846FBA4F}')
|
||||||
|
@ -119,7 +119,7 @@ class FOLDERID:
|
||||||
|
|
||||||
|
|
||||||
# http://msdn.microsoft.com/en-us/library/windows/desktop/bb762188.aspx
|
# http://msdn.microsoft.com/en-us/library/windows/desktop/bb762188.aspx
|
||||||
class UserHandle:
|
class UserHandle(object):
|
||||||
current = wintypes.HANDLE(0)
|
current = wintypes.HANDLE(0)
|
||||||
common = wintypes.HANDLE(-1)
|
common = wintypes.HANDLE(-1)
|
||||||
|
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
#! /bin/bash
|
|
||||||
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
# Ignoring distutils because: https://github.com/PyCQA/pylint/issues/73
|
|
||||||
# TODO: as code quality improves, make pylint be more strict
|
|
||||||
pylint -E --disable=inherit-non-class --disable=no-member \
|
|
||||||
--ignored-modules=distutils \
|
|
||||||
--enable=unused-import \
|
|
||||||
--enable=bad-whitespace \
|
|
||||||
--enable=line-too-long \
|
|
||||||
--enable=trailing-whitespace \
|
|
||||||
--enable=missing-final-newline \
|
|
||||||
--enable=mixed-indentation \
|
|
||||||
lbrynet $@
|
|
12
run_tests.sh
|
@ -1,12 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -o xtrace
|
|
||||||
|
|
||||||
if [ -z "$@" ]; then
|
|
||||||
TESTS=tests
|
|
||||||
else
|
|
||||||
TESTS="$@"
|
|
||||||
fi
|
|
||||||
|
|
||||||
find -iname "*.pyc" -delete
|
|
||||||
PYTHONPATH=. trial $TESTS
|
|
4
setup.py
|
@ -11,7 +11,9 @@ from setuptools import setup, find_packages
|
||||||
# dependencies of the lbrynet library. requirements.txt includes
|
# dependencies of the lbrynet library. requirements.txt includes
|
||||||
# dependencies of dependencies and specific versions that we know
|
# dependencies of dependencies and specific versions that we know
|
||||||
# all work together.
|
# all work together.
|
||||||
# See https://packaging.python.org/requirements/ for more details.
|
#
|
||||||
|
# See https://packaging.python.org/requirements/ and
|
||||||
|
# https://caremad.io/posts/2013/07/setup-vs-requirement/ for more details.
|
||||||
requires = [
|
requires = [
|
||||||
'Twisted',
|
'Twisted',
|
||||||
'appdirs',
|
'appdirs',
|
||||||
|
|