From d22c6ca73e36f732ff729173a2a3f527a07a6104 Mon Sep 17 00:00:00 2001 From: Deepak Date: Tue, 14 Nov 2023 12:49:34 -0600 Subject: [PATCH] initial commit --- .editorconfig | 7 + .flake8 | 3 + .gitattributes | 3 + .gitignore | 146 ++++++++++++++++++ .versionrc | 10 ++ Jenkinsfile | 81 ++++++++++ README.md | 2 + do.sh | 69 +++++++++ flake.nix | 55 +++++++ pyproject.toml | 42 +++++ renovate.json | 3 + scripts/release.sh | 45 ++++++ scripts/standard-version/pyproject-updater.js | 11 ++ tests/__init__.py | 0 tests/test_tantri.py | 6 + 15 files changed, 483 insertions(+) create mode 100755 .editorconfig create mode 100755 .flake8 create mode 100755 .gitattributes create mode 100755 .gitignore create mode 100755 .versionrc create mode 100755 Jenkinsfile create mode 100755 README.md create mode 100755 do.sh create mode 100755 flake.nix create mode 100755 pyproject.toml create mode 100755 renovate.json create mode 100755 scripts/release.sh create mode 100755 scripts/standard-version/pyproject-updater.js create mode 100755 tests/__init__.py create mode 100755 tests/test_tantri.py diff --git a/.editorconfig b/.editorconfig new file mode 100755 index 0000000..cddfd28 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +# insert_final_newline = true +indent_style = tab diff --git a/.flake8 b/.flake8 new file mode 100755 index 0000000..2a961c9 --- /dev/null +++ b/.flake8 @@ -0,0 +1,3 @@ +[flake8] +ignore = W191, E501, W503, E203 +max-line-length = 120 diff --git a/.gitattributes b/.gitattributes new file mode 100755 index 0000000..a71de1d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +* text=auto + +*.py text diff=python diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..5d1242b --- /dev/null +++ b/.gitignore @@ -0,0 +1,146 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +pytest.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +#direnv +.envrc +.direnv + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +.csv +.idea diff --git a/.versionrc b/.versionrc new file mode 100755 index 0000000..7889a88 --- /dev/null +++ b/.versionrc @@ -0,0 +1,10 @@ +{ + "bumpFiles": [ + { + "filename": "pyproject.toml", + "updater": "scripts/standard-version/pyproject-updater.js" + } + ], + "sign": true, + "tag-prefix": "" +} diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100755 index 0000000..ee3c53c --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,81 @@ +pipeline { + agent { + kubernetes { + label 'tantri' // all your pods will be named with this prefix, followed by a unique id + idleMinutes 5 // how long the pod will live after no jobs have run on it + yamlFile 'jenkins/ci-agent-pod.yaml' // path to the pod definition relative to the root of our project + defaultContainer 'poetry' // define a default container if more than a few stages use it, will default to jnlp container + } + } + + options { + parallelsAlwaysFailFast() + } + + stages { + stage('Build') { + steps { + echo 'Building...' + sh 'python --version' + sh 'poetry --version' + sh 'poetry install' + } + } + stage('Test') { + parallel{ + stage('pytest') { + steps { + sh 'poetry run pytest' + } + } + stage('lint') { + steps { + sh 'poetry run flake8 tantri tests' + } + } + stage('mypy') { + steps { + sh 'poetry run mypy tantri' + } + } + } + } + + stage('Deploy') { + environment { + PYPI=credentials("pypi-tantri") + } + + when { + buildingTag() + } + steps { + echo 'Deploying...' + sh 'poetry publish -u ${PYPI_USR} -p ${PYPI_PSW} --build' + } + } + + } + post { + always { + echo 'This will always run' + junit 'pytest.xml' + cobertura coberturaReportFile: 'coverage.xml' + mail (bcc: '', + body: "Project: ${env.JOB_NAME}
Build Number: ${env.BUILD_NUMBER}
Build URL: ${env.BUILD_URL}", cc: '', charset: 'UTF-8', from: 'jenkins@jenkins.deepak.science', mimeType: 'text/html', replyTo: 'dmallubhotla+jenkins@gmail.com', subject: "${env.JOB_NAME} #${env.BUILD_NUMBER}: Build ${currentBuild.currentResult}", to: "dmallubhotla+ci@gmail.com") + } + success { + echo 'This will run only if successful' + } + failure { + echo 'This will run only if failed' + } + unstable { + echo 'This will run only if the run was marked as unstable' + } + changed { + echo 'This will run only if the state of the Pipeline has changed' + echo 'For example, if the Pipeline was previously failing but is now successful' + } + } +} diff --git a/README.md b/README.md new file mode 100755 index 0000000..f7da31a --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# tantri - generating telegraph noise + diff --git a/do.sh b/do.sh new file mode 100755 index 0000000..275d82e --- /dev/null +++ b/do.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +# Do - The Simplest Build Tool on Earth. +# Documentation and examples see https://github.com/8gears/do + +set -Eeuo pipefail # -e "Automatic exit from bash shell script on error" -u "Treat unset variables and parameters as errors" + +checknix() { + if [[ "${DO_NIX_CUSTOM:=0}" -eq 1 ]]; then + echo "In an interactive nix env." + else + echo "Using poetry as runner, no nix detected." + fi +} + +build() { + echo "I am ${FUNCNAME[0]}ing" + poetry build +} + +fmt() { + if [[ "${DO_NIX_CUSTOM:=0}" -eq 1 ]]; then + black . + else + poetry run black . + fi + find . -type f -name "*.py" -exec sed -i -e 's/ /\t/g' {} \; +} + +test() { + echo "I am ${FUNCNAME[0]}ing" + if [[ "${DO_NIX_CUSTOM:=0}" -eq 1 ]]; then + flake8 tantri tests + mypy tantri + pytest + else + poetry run flake8 tantri tests + poetry run mypy tantri + poetry run pytest + fi +} + +updatesnap() { + echo "I am ${FUNCNAME[0]}ing" + if [[ "${DO_NIX_CUSTOM:=0}" -eq 1 ]]; then + pytest --snapshot-update + else + poetry run pytest --snapshot-update + fi +} + +htmlcov() { + if [[ "${DO_NIX_CUSTOM:=0}" -eq 1 ]]; then + pytest --cov-report=html + else + poetry run pytest --cov-report=html + fi +} + +release() { + ./scripts/release.sh +} + +all() { + build && fmt && test +} + +"$@" # <- execute the task + +[ "$#" -gt 0 ] || printf "Usage:\n\t./do.sh %s\n" "($(compgen -A function | grep '^[^_]' | paste -sd '|' -))" diff --git a/flake.nix b/flake.nix new file mode 100755 index 0000000..0c89394 --- /dev/null +++ b/flake.nix @@ -0,0 +1,55 @@ +{ + description = "Application packaged using poetry2nix"; + + inputs.flake-utils.url = "github:numtide/flake-utils?rev=0f8662f1319ad6abf89b3380dd2722369fc51ade"; + inputs.nixpkgs.url = "github:NixOS/nixpkgs?rev=e194871435cad8ffb1d64b64fb7df3b2b8a10088"; + inputs.poetry2nix.url = "github:nix-community/poetry2nix?rev=7b71679fa7df00e1678fc3f1d1d4f5f372341b63"; + + outputs = { self, nixpkgs, flake-utils, poetry2nix }: + { + # Nixpkgs overlay providing the application + overlay = nixpkgs.lib.composeManyExtensions [ + poetry2nix.overlay + (final: prev: { + # The application + tantri = prev.poetry2nix.mkPoetryApplication { + overrides = [ + prev.poetry2nix.defaultPoetryOverrides + ]; + projectDir = ./.; + }; + tantriEnv = prev.poetry2nix.mkPoetryEnv { + overrides = [ + prev.poetry2nix.defaultPoetryOverrides + ]; + projectDir = ./.; + }; + }) + ]; + } // (flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { + inherit system; + overlays = [ self.overlay ]; + }; + in + { + apps = { + tantri = pkgs.tantri; + }; + + defaultApp = pkgs.tantri; + devShell = pkgs.mkShell { + buildInputs = [ + pkgs.poetry + pkgs.tantriEnv + pkgs.tantri + ]; + shellHook = '' + export DO_NIX_CUSTOM=1 + ''; + packages = [ pkgs.nodejs-16_x pkgs.gnupg ]; + }; + + })); +} diff --git a/pyproject.toml b/pyproject.toml new file mode 100755 index 0000000..1326692 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,42 @@ +[tool.poetry] +name = "tantri" +version = "0.9.2" +description = "Python dipole model evaluator" +authors = ["Deepak "] +license = "GPL-3.0-only" +readme = "README.md" + +[tool.poetry.dependencies] +python = ">=3.8.1,<3.10" +numpy = "^1.22.3" +scipy = "~1.10" + +[tool.poetry.dev-dependencies] +pytest = ">=6" +flake8 = "^4.0.0" +pytest-cov = "^4.1.0" +mypy = "^0.961" +ipython = "^8.2.0" +black = "^22.3.0" +syrupy = "^4.0.8" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.pytest.ini_options] +testpaths = ["tests"] +addopts = "--junitxml pytest.xml --cov tantri --cov-report=xml:coverage.xml --cov-fail-under=50 --cov-report=html" +junit_family = "xunit1" +log_format = "%(asctime)s | %(levelname)s | %(pathname)s:%(lineno)d | %(message)s" +log_level = "WARNING" + +[tool.mypy] +plugins = "numpy.typing.mypy_plugin" + +[[tool.mypy.overrides]] +module = [ + "scipy", + "scipy.optimize" +] +ignore_missing_imports = true diff --git a/renovate.json b/renovate.json new file mode 100755 index 0000000..344c613 --- /dev/null +++ b/renovate.json @@ -0,0 +1,3 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json" +} diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 0000000..1152a43 --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +if [ -z "$(git status --porcelain)" ]; then + branch_name=$(git symbolic-ref -q HEAD) + branch_name=${branch_name##refs/heads/} + branch_name=${branch_name:-HEAD} + if [ $branch_name != "master" ]; then + echo "The current branch is not master!" + echo "I'd feel uncomfortable releasing from here..." + exit 3 + fi + + release_needed=false + if \ + { git log "$( git describe --tags --abbrev=0 )..HEAD" --format='%s' | cut -d: -f1 | sort -u | sed -e 's/([^)]*)//' | grep -q -i -E '^feat|fix|perf|refactor|revert$' ; } || \ + { git log "$( git describe --tags --abbrev=0 )..HEAD" --format='%s' | cut -d: -f1 | sort -u | sed -e 's/([^)]*)//' | grep -q -E '\!$' ; } || \ + { git log "$( git describe --tags --abbrev=0 )..HEAD" --format='%b' | grep -q -E '^BREAKING CHANGE:' ; } + then + release_needed=true + fi + + if ! [ "$release_needed" = true ]; then + echo "No release needed..." + exit 0 + fi + + # Working directory clean + echo "Doing a dry run..." + npx standard-version --dry-run + read -p "Does that look good? [y/N] " -n 1 -r + echo # (optional) move to a new line + if [[ $REPLY =~ ^[Yy]$ ]] + then + # do dangerous stuff + npx standard-version + git push --follow-tags origin master + else + echo "okay, never mind then..." + exit 2 + fi +else + echo "Can't create release, working tree unclean..." + exit 1 +fi diff --git a/scripts/standard-version/pyproject-updater.js b/scripts/standard-version/pyproject-updater.js new file mode 100755 index 0000000..7b1beb1 --- /dev/null +++ b/scripts/standard-version/pyproject-updater.js @@ -0,0 +1,11 @@ +const pattern = /(\[tool\.poetry\]\nname = "tantri"\nversion = ")(?\d+\.\d+\.\d)(")/mg; + +module.exports.readVersion = function (contents) { + const result = pattern.exec(contents); + return result.groups.vers; +} + +module.exports.writeVersion = function (contents, version) { + const newContents = contents.replace(pattern, `$1${version}$3`); + return newContents; +} diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/tests/test_tantri.py b/tests/test_tantri.py new file mode 100755 index 0000000..d2af898 --- /dev/null +++ b/tests/test_tantri.py @@ -0,0 +1,6 @@ +from tantri import __version__ +import tantri + + +def test_version(): + assert tantri.get_version() == __version__