diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4def72dc58a89822b46ea4ed09920b4ef84bd37a
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,108 @@
+# Official language image. Look for the different tagged releases at:
+# https://hub.docker.com/r/library/python/tags/
+image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/python:3.6
+
+# Change pip's cache directory to be inside the project directory since we can
+# only cache local items.
+variables:
+  PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
+
+# Pip's cache doesn't store the python packages
+# https://pip.pypa.io/en/stable/reference/pip_install/#caching
+#
+# If you want to also cache the installed packages, you have to install
+# them in a virtualenv and cache it as well.
+cache:
+  paths:
+    - .cache/pip
+    - venv/
+
+before_script:
+  - pip install virtualenv
+  - make show-version # Print out python version for debugging
+  - virtualenv venv
+  - source venv/bin/activate
+  - make deps
+
+stages:          # List of stages for jobs, and their order of execution
+  - test
+  - build
+  - deploy
+
+unit-test-job:  
+  stage: test
+  script:
+    - make test
+
+pylint-test-job:
+  stage: test
+  script:
+    - make pylint
+
+pyflakes-test-job:
+  stage: test
+  script:
+    - make pyflakes
+
+build-job:
+  stage: build
+  script:
+    - make buildbot
+    - git clone https://gitlab-ci-token:$SIGNER_TOKEN@$SIGNER_REPO
+    - bash signer/init_keyring.sh
+    - python signer/signer.py --verbose $GPG_KEY "$PWD/dist"
+  artifacts:
+    paths:
+      - dist
+  only:
+    - devel
+    - master
+
+sphinx-metadata-job:
+  stage: build
+  script:
+    - printf "{\n\"codename\":\"$CI_COMMIT_BRANCH\",\n" > metadata.json
+    - printf "\"suite\":$(if [ "$CI_COMMIT_BRANCH" == "master" ]; then echo \"stable\"; else echo \"unstable\"; fi),\n" >> metadata.json
+    - printf "\"bversion\":\"$(make show-version)\",\n" >> metadata.json
+    - printf "\"revision\":\"$CI_COMMIT_SHA\",\n" >> metadata.json
+    - printf "\"bnumber\":\"$CI_PIPELINE_ID\",\n" >> metadata.json
+    - printf "\"bdate\":\"$(date '+%Y-%m-%d %T')\"\n}\n" >> metadata.json
+  artifacts:
+    paths:
+      - metadata.json
+  only:
+    - master
+    - devel
+
+twine-job:
+  stage: deploy
+  script:
+    - printf "[pypi]\nusername = __token__\npassword = $TWINE_TOKEN\n" > .pypirc
+    - mkdir dist2
+    - cp dist/*.whl dist/*.tar.gz dist2/.
+    - twine upload 'dist2/*' --skip-existing --config-file .pypirc
+  only:
+    - master
+
+pages:
+  stage: deploy
+  script:
+    - make docs
+    - curl -L --header "JOB-TOKEN:$CI_JOB_TOKEN" "$CI_API_V4_URL/projects/$CI_PROJECT_ID/jobs/artifacts/devel/download?job=pages" -o devel.zip
+    - curl -L --header "JOB-TOKEN:$CI_JOB_TOKEN" "$CI_API_V4_URL/projects/$CI_PROJECT_ID/jobs/artifacts/master/download?job=pages" -o master.zip
+    - unzip -n '*.zip' || true # Ignore not existing zipfiles
+    - mkdir -p public/"$CI_COMMIT_BRANCH"
+    - mkdir -p public/"$CI_COMMIT_BRANCH"/files
+    - cp -rf doc/_build/html public/"$CI_COMMIT_BRANCH"
+    - cp -rf dist/* public/"$CI_COMMIT_BRANCH"/files/.
+    - cd public/"$CI_COMMIT_BRANCH"/files
+    - echo "<html><body><h1>Directory listing:</h1>" > ./index.html
+    - find -exec echo "<a href='{}'>{}</a><br/>" \; | sort >> ./index.html
+    - echo "</body></html>" >> ./index.html
+  artifacts:
+    name: "$CI_COMMIT_BRANCH"
+    paths:
+      - public
+  only:
+    - master
+    - devel
diff --git a/Makefile b/Makefile
index 19d31c3cfacdf3e292e44005647d34f0354cc929..9fe22c5e3ae477392d20d3e574c327d9661a2ac2 100644
--- a/Makefile
+++ b/Makefile
@@ -1,21 +1,152 @@
+DIR_LIB = pydgets
+
+# You can set these variables from the command line.
+SPHINXOPTS      =
+SPHINXBUILD     = sphinx-build
+SPHINXPROJ      = Pydgets-Python3ConsoleWidgetLibrary
+SPHINXSOURCEDIR = .
+SPHINXBUILDDIR  = doc/_build
+
+PYTHON    = python
+PIP       = pip
+NOSETESTS = nosetests
+TWINE     = twine
+
 #
-# Minimal makefile for Sphinx documentation
+# Color code definitions for colored terminal output
+# https://stackoverflow.com/questions/5947742/how-to-change-the-output-color-of-echo-in-linux
 #
+RED    = \033[0;31m
+GREEN  = \033[0;32m
+ORANGE = \033[0;33m
+BLUE   = \033[0;34m
+PURPLE = \033[0;35m
+CYAN   = \033[0;36m
+NC     = \033[0m
 
-# You can set these variables from the command line.
-SPHINXOPTS    =
-SPHINXBUILD   = sphinx-build
-SPHINXPROJ    = Pydgets-Python3ConsoleWidgetLibrary
-SOURCEDIR     = .
-BUILDDIR      = doc/_build
-
-# Put it first so that "make" without argument is like "make help".
-help:
-	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
-
-.PHONY: help Makefile
-
-# Catch-all target: route all unknown targets to Sphinx using the new
-# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
-%: Makefile
-	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+#-------------------------------------------------------------------------------
+
+#
+# Default make target, alias for 'help', you must explicitly choose the target.
+#
+default: docs-help
+
+#
+# Perform all reasonable tasks to do full build.
+#
+full: docs archive bdist deploy
+
+#
+# Perform local build.
+#
+build: archive bdist
+
+#
+# Perfom build from automated system.
+#
+buildbot: bdist
+
+#
+# Check the project code.
+#
+check: test
+
+
+#-------------------------------------------------------------------------------
+
+
+docs: docs-html
+
+docs-help: FORCE
+	@$(SPHINXBUILD) -M help "$(SPHINXSOURCEDIR)" "$(SPHINXBUILDDIR)" $(SPHINXOPTS) $(O)
+
+docs-html: FORCE
+	@echo "\n${GREEN}*** Generating project documentation ***${NC}\n"
+	@$(SPHINXBUILD) -M html "$(SPHINXSOURCEDIR)" "$(SPHINXBUILDDIR)" $(SPHINXOPTS) $(O)
+
+
+#-------------------------------------------------------------------------------
+
+
+show-version: FORCE
+	@PYTHONPATH=. $(PYTHON) -c "import pydgets; print(pydgets.__version__);"
+
+
+#-------------------------------------------------------------------------------
+
+
+deps: deps-python deps-python-dev
+
+deps-python: FORCE
+	@echo "\n${GREEN}*** Installing Python dependencies ***${NC}\n"
+	-@$(PIP) install -r requirements.pip --upgrade
+
+deps-python-dev: FORCE
+	@echo "\n${GREEN}*** Installing Python dependencies ***${NC}\n"
+	@$(PIP) install -r requirements-dev.pip --upgrade
+
+
+#-------------------------------------------------------------------------------
+
+
+pyflakes: pyflakes-lib pyflakes-test
+
+pyflakes-lib: FORCE
+	@echo "\n${GREEN}*** Checking code with pyflakes ***${NC}\n"
+	@$(PYTHON) --version
+	@echo ""
+	-@$(PYTHON) -m pyflakes $(DIR_LIB)/*.py
+
+pyflakes-test: FORCE
+	@echo "\n${GREEN}*** Checking test files with pyflakes ***${NC}\n"
+	@$(PYTHON) --version
+	@echo ""
+	-@$(PYTHON) -m pyflakes $(DIR_LIB)/tests/*.py
+
+pylint: pylint-lib pylint-test
+
+pylint-lib: FORCE
+	@echo "\n${GREEN}*** Checking code with pylint ***${NC}\n"
+	@$(PYTHON) --version
+	@echo ""
+	-@$(PYTHON) -m pylint $(DIR_LIB)/*.py --rcfile .pylintrc-lib
+
+pylint-test: FORCE
+	@echo "\n${GREEN}*** Checking test files with pylint ***${NC}\n"
+	@$(PYTHON) --version
+	@echo ""
+	-@$(PYTHON) -m pylint $(DIR_LIB)/tests/*.py --rcfile .pylintrc-test
+
+test: FORCE
+	@echo "\n${GREEN}*** Checking code with nosetests ***${NC}\n"
+	@$(NOSETESTS)
+
+
+#-------------------------------------------------------------------------------
+
+
+archive: FORCE
+	@if ! [ `ls dist/pydgets* | wc -l` = "0" ]; then\
+		echo "\n${GREEN}*** Moving old distribution files to archive ***${NC}\n";\
+		mv -f dist/pydgets* archive;\
+	fi
+
+bdist: FORCE
+	@echo "\n${GREEN}*** Building Python packages ***${NC}\n"
+	@$(PYTHON) --version
+	@echo ""
+	@$(PYTHON) setup.py sdist bdist_wheel
+
+install: FORCE
+	@echo "\n${GREEN}*** Performing local installation ***${NC}\n"
+	@$(PIP) install dist/pydgets*.whl --upgrade
+
+deploy: FORCE
+	@echo "\n${GREEN}*** Deploying packages to PyPI ***${NC}\n"
+	@$(TWINE) upload dist/* --skip-existing
+
+
+# Empty rule as dependency will force make to always perform target
+# Source: https://www.gnu.org/software/make/manual/html_node/Force-Targets.html
+FORCE:
diff --git a/Makefile-dep b/Makefile-dep
deleted file mode 100644
index d89cc32c673d94b8da78fb7683966b79d4b2a71d..0000000000000000000000000000000000000000
--- a/Makefile-dep
+++ /dev/null
@@ -1,44 +0,0 @@
-#-------------------------------------------------------------------------------
-# Copyright (C) since 2016 Jan Mach <honza.mach.ml@gmail.com>
-# Use of this source is governed by the MIT license, see LICENSE file.
-#-------------------------------------------------------------------------------
-
-DOCDIR = doc
-DIST_SIZE:=$(shell ls dist | wc -l)
-
-all: archive bdist deploy
-
-# Perform unit tests
-test:
-	$(info Testing source code)
-	nosetests
-
-# Move old distribution files to archive directory
-archive:
-	$(info Checking if dist archivation is needed)
-	@if ! [ `ls dist | wc -l` = "0" ]; then\
-		echo "Moving old distribution files to archive";\
-		mv -f dist/* archive;\
-	fi
-
-# Build various Python package distributions
-bdist:
-	$(info Building distributions)
-
-	# Build and upload (insecure)
-	#python3 setup.py sdist bdist_wheel upload
-
-	# Build only
-	python3 setup.py sdist bdist_wheel
-
-# Perform installation from local files for both Python v2 and v3
-install:
-	$(info Local installation)
-	sudo pip3 install dist/idea*.whl
-
-# Deploy latest packages to PyPI
-deploy:
-	$(info PyPI deployment)
-
-	# Secure upload with Twine
-	twine upload dist/*
diff --git a/pydgets/__init__.py b/pydgets/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..91bf82336b638b87ee760ac929a2bb124391754c 100644
--- a/pydgets/__init__.py
+++ b/pydgets/__init__.py
@@ -0,0 +1 @@
+__version__ = "0.10"
diff --git a/requirements-dev.pip b/requirements-dev.pip
new file mode 100644
index 0000000000000000000000000000000000000000..e92d9cb3c7fc873c0b1fe2936e839410bac9f1ed
--- /dev/null
+++ b/requirements-dev.pip
@@ -0,0 +1,9 @@
+setuptools
+wheel
+twine
+docutils<0.18
+nose==1.3.7
+pyflakes==2.1.0
+pylint==2.2.2
+sphinx==1.8.4
+sphinx-rtd-theme==0.4.2
diff --git a/setup.py b/setup.py
index dddf393a68c1c61b938cda171fe7f442022d17bb..efdbfc094316a71710c10fe39d78448e294a00f7 100644
--- a/setup.py
+++ b/setup.py
@@ -15,6 +15,14 @@ from setuptools import setup, find_packages
 from codecs import open
 from os import path
 
+import sys
+#
+# Import local version of pynspect library, so that we can insert correct version
+# number into documentation.
+#
+sys.path.insert(0, path.abspath('.'))
+import pydgets
+
 here = path.abspath(path.dirname(__file__))
 
 # Get the long description from the README file
@@ -23,7 +31,7 @@ with open(path.join(here, 'README.rst'), encoding='utf-8') as f:
 
 setup(
     name = 'pydgets',
-    version = '0.9',
+    version = pydgets.__version__,
     description = 'Console widget library for Python 3',
     long_description = long_description,
     classifiers = [