Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate to github actions #214

Merged
merged 4 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/actions/initialize/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: 'Initialize'
description: 'Checkout repo and install dependencies'
inputs:
latest:
description: 'If true, ignore requirements.txt and the versions pinned there.'
default: 'false'
documentation:
description: 'If true, install documentation build frameworks.'
default: 'false'
runs:
using: "composite"
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: Upgrade pip
run: python -m pip install --upgrade pip
shell: bash
- name: Install minimum-version runtime dependencies + GEMD
run: python -m pip install --only-binary ':all:' -r requirements.txt
shell: bash
if: ${{ inputs.latest == 'false' }}
- name: Install test dependencies
run: python -m pip install --only-binary ':all:' -r test_requirements.txt
shell: bash
- name: Install documentation building framework
run: python -m pip install --only-binary ':all:' -r doc_requirements.txt
shell: bash
if: ${{ inputs.documentation == 'true' }}
- name: Install gemd-python, along with the latest version of any outstanding dependencies
run: python -m pip install --only-binary ':all:' -e .
shell: bash
18 changes: 18 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# GEMD Python PR

## Description
Please briefly explain the goal of the changes/this PR.
The reviewer should be able to understand why the change is being made by reading this description
and its links (e.g. JIRA tickets).

### PR Type:
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Maintenance (non-breaking change to assist developers)

### Adherence to team decisions
- [ ] I have added tests for 100% coverage
- [ ] I have written Numpy-style docstrings for every method and class.
- [ ] I have communicated the downstream consequences of the PR to others.
- [ ] I have bumped the version in __version\__.py
43 changes: 43 additions & 0 deletions .github/workflows/build-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Build and Deploy Docs

on:
release:
types: [published]

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false

jobs:
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Initialize the environment
uses: ./.github/actions/initialize
with:
documentation: 'true'
- name: Build Docs
run: |
make -C docs/ html
touch docs/_build/html/.nojekyll
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: 'docs/_build/html'
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
19 changes: 19 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Deploy to PyPI

on:
release:
types: [published]

jobs:
publish:
name: Publish package to PyPI
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: Build
run: python setup.py sdist bdist_wheel
- name: Publish
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
48 changes: 48 additions & 0 deletions .github/workflows/pr-checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: PR Checks

on:
pull_request:
branches:
- main
- 'release/**'

jobs:
check-version:
name: Check version bumped
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Initialize the environment
uses: ./.github/actions/initialize
- name: Check version
run: python scripts/validate_version_bump.py
linting:
name: Run linting with flake8
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Initialize the environment
uses: ./.github/actions/initialize
- name: Lint the source directory
run: flake8 gemd
check-deprecated:
name: Find code marked for removal in this version
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Initialize the environment
uses: ./.github/actions/initialize
- name: Deprecated check
run: derp . gemd/__version__.py
check-docs:
name: Check docs for warnings
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Initialize the environment
uses: ./.github/actions/initialize
with:
documentation: 'true'
- name: Build Docs
continue-on-error: true
run: make -C docs/ html SPHINXOPTS='-W --keep-going'
48 changes: 48 additions & 0 deletions .github/workflows/pr-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: PR Tests

on:
pull_request:
branches:
- main
- 'release/**'

jobs:
run-tests:
name: Execute unit tests
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
anoto-moniz marked this conversation as resolved.
Show resolved Hide resolved
steps:
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- uses: actions/checkout@v4
- name: Initialize the environment
uses: ./.github/actions/initialize
- name: Execute unit tests
run: pytest --cov=gemd --cov-report term-missing:skip-covered --cov-config=tox.ini --no-cov-on-fail --cov-fail-under=100 tests/
run-tests-against-latest:
# These runs are intended to confirm the latest minor version of our dependencies we claim to
# support don't break with our latest changes. Since they're not the versions we directly state
# you should use (i.e. in requirements.txt), they arguably aren't critical, hence not blocking.
name: Non-blocking - Execute unit tests against latest version of dependencies
runs-on: ubuntu-latest
continue-on-error: true
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
steps:
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- uses: actions/checkout@v4
- name: Initialize the environment
uses: ./.github/actions/initialize
with:
latest: 'true'
- name: Execute unit tests
run: pytest tests/
41 changes: 35 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,43 @@
# Contributing

## Documentation

Documentation is built using [Sphinx](https://www.sphinx-doc.org/en/master/) with the
[autodoc](https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#module-sphinx.ext.autodoc)
extension. In order to build the documentation, you can install the necessary packages and then use make:
```shell
pip install -r doc_requirements.txt
make make -C docs/ html
```

All documentation source files and configuration are in the `docs/source` directory.
Documentation for all modules is autogenerated and populated in the `reference` subdirectory.
Additional reference material, such as tutorials, should be referenced in `index.rst`.

## Testing

Changes are gated on:
* Passing unit tests
* 100% unit test coverage
* Passing unit tests with minimum supported library versions
* 100% test coverage
* PEP8 style compliance, with some exceptions in the [tox file](tox.ini)
* Incrementing the package version number in [setup.py](setup.py)

Travis runs the tests in `scripts/run_tests.sh`, which gives a convenient one-line invocation for testing.
Additionally, PRs are checked against (but not blocked by):
* Passing unit tests with latest nominally supported versions of all dependencies
* Documentation builds without warnings

In order to run tests locally, you'll need to install additional packages:
```shell
pip install -r test_requirements.txt
```

A test runner is available in `scripts/run_tests.sh`, which gives a convenient one-line invocation for testing.

As it can be easy to forget to verify these prior to pushing, it's possible to use [git hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) to enforce compliance during normal workflows.
Consider editing `.git/hooks/pre-commit` or `.git/hooks/pre-push` (or adding them and marking them as executable: `chmod +x <file>`).
As it can be easy to forget to verify these prior to pushing,
it's possible to use [git hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks)
to enforce compliance during normal workflows.
Consider editing `.git/hooks/pre-commit` or `.git/hooks/pre-push`
(or adding them and marking them as executable: `chmod +x <file>`).
For example, you could set your local `.git/hooks/pre-commit` to be
```shell
scripts/run_tests.sh --quiet --exitfirst
Expand All @@ -24,7 +50,10 @@ This project follows [PEP8](https://www.python.org/dev/peps/pep-0008/), with the

Additionally:
* Type hints are strongly encouraged, but not required.
* Positional arguments are strongly discouraged for methods with multiple arguments. Keyword-only arguments are preferred instead. Every positional argument should be required.
* Positional arguments are strongly discouraged for methods with multiple arguments,
especially for multiple arguments of the same type.
Keyword-only arguments are preferred instead.
Every positional argument should be required.

For additional (non-binding) inspiration, check out the [Google Python Style Guide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md).

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# GEMD-python
Python binding for Citrine's nextgen data model, GEMD.
Python binding for Citrine's data model, GEMD.

This package provides a framework for storing information about the processes that create materials, the materials themselves, and measurements performed on those materials.

Expand Down
9 changes: 6 additions & 3 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import gemd
import os
import sys
sys.path.insert(0, os.path.abspath('../..'))
sys.path.insert(0, os.path.abspath('../../gemd'))


# -- Project information -----------------------------------------------------
Expand All @@ -36,7 +36,8 @@
extensions = [
'sphinxcontrib.apidoc',
'sphinx.ext.napoleon',
'sphinx.ext.intersphinx'
'sphinx.ext.intersphinx',
'sphinx_rtd_theme'
]

# Use the sphinxcontrib.apidoc extension to wire in the sphinx-apidoc invocation
Expand All @@ -48,7 +49,6 @@
apidoc_output_dir = 'reference'
apidoc_excluded_paths = ['tests', '*impl*']
apidoc_separate_modules = True
apidoc_toc_file = False

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
Expand Down Expand Up @@ -79,6 +79,9 @@
autodoc_member_order = 'groupwise'
# autodoc_mock_imports allows Sphinx to ignore any external modules listed in the array
autodoc_mock_imports = []
autodoc_default_options = {
'ignore-module-all': True
}

html_favicon = '_static/favicon.png'
html_logo = '_static/logo.png'
Expand Down
8 changes: 2 additions & 6 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ or a specific version can be installed, for example:

.. code:: bash

pip install gemd==1.17.1
pip install gemd==2.1.1

Table of Contents
-----------------
Expand All @@ -37,15 +37,11 @@ Table of Contents

depth/unit_parsing
depth/serialization
API Reference <reference/modules>

Indices
------------------

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

.. toctree::
:maxdepth: 1

reference/gemd
2 changes: 1 addition & 1 deletion gemd/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "2.1.1"
__version__ = "2.1.2"
27 changes: 12 additions & 15 deletions scripts/validate_version_bump.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
#!python
from os import getcwd, popen
from os import getcwd, popen, system
from os.path import relpath
from packaging.version import Version
import re
import sys
from typing import TextIO


def main():
repo_dir = popen("git rev-parse --show-toplevel", mode="r").read().rstrip()
version_path = relpath(f'{repo_dir}/gemd/__version__.py', getcwd())

try:
repo_dir = popen("git rev-parse --show-toplevel", mode="r").read().rstrip()
version_path = relpath(f'{repo_dir}/gemd/__version__.py', getcwd())
with open(version_path, "r") as fh:
new_version = extract_version(fh)
new_version = extract_version(fh.read())
except Exception as e:
raise ValueError(f"Couldn't extract version from {version_path}") from e
raise ValueError(f"Couldn't extract version from working directory") from e

try:
with popen(f"git show main:gemd/__version__.py", mode="r") as fh:
old_version = extract_version(fh)
system("git fetch origin main")
with popen("git show origin/main:gemd/__version__.py", mode="r") as fh:
old_version = extract_version(fh.read())
except Exception as e:
raise ValueError(f"Couldn't extract version from main branch") from e

Expand Down Expand Up @@ -47,12 +46,10 @@ def main():
return 4


def extract_version(handle: TextIO) -> Version:
text = handle.read()
if not re.search(r"\S", text):
raise ValueError(f"No content")
match = re.search(r"""^\s*_*version_*\s*=\s*(['"])(\S+)\1""", text, re.MULTILINE)
if match:
def extract_version(text: str) -> Version:
version_re = r'''^\s*__version__\s*=\s*(['"])([\w\.]+)\1$'''

if match := re.search(version_re, text, re.MULTILINE):
return Version(match.group(2))
else:
raise ValueError(f"No version found\n{text}")
Expand Down
Loading
Loading