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

Upgrading versions and adding Continuous Integration and Delivery #2

Merged
merged 1 commit into from
Aug 26, 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
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.github
.ruff_cache/
.venv/
data/
dist/
docs/
example/
oncodrivefml.egg-info/
184 changes: 184 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
name: Build and Publish

on:
push:
tags:
- "**"
branches:
- "**"

permissions:
contents: read

env:
TERM: xterm
PYTHON_VERSION: 3.11

jobs:
packages-build:
name: Build packages
runs-on: ubuntu-latest
env:
RUFF_FORMAT: github

steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}

- name: Prepare virtual environment
run: make create-env

- name: Check format
run: |
make check-format || true
BOLDRED=$(tput bold && tput setaf 1)
RESET=$(tput sgr0)
echo "${BOLDRED}==> We won't fail on formatting errors for the time being, but we will in the future.${RESET}"

- name: Check lint
run: |
make check-lint || true
BOLDRED=$(tput bold && tput setaf 1)
RESET=$(tput sgr0)
echo "${BOLDRED}==> We won't fail on lint errors for the time being, but we will in the future.${RESET}"

- name: Build packages
run: make build-dist

- name: Upload packages
uses: actions/upload-artifact@v4
with:
name: python-packages
path: dist

docker-build:
name: Build Docker image
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}

- name: Prepare virtual environment
run: make create-env

- name: Check Dockerfile
run: make check-docker

- name: Build Docker image
run: make build-image

# TODO: Enable this when we figure out how to run it without having to download several Gigabytes of data.
# - name: Test Docker image
# run: make run-example

docs-build:
name: Build documentation
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}

- name: Prepare virtual environment
run: make create-env

- name: Check version matching the tag
shell: bash
run: make docs

- name: Upload documentation
uses: actions/upload-artifact@v4
with:
name: documentation
path: docs/build

check-version:
name: Check version
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
needs:
- packages-build
- docker-build
- docs-build

steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}

- name: Prepare virtual environment
run: make create-env

- name: Check version matching the tag
run: make check-version

packages-publish:
name: Publish packages
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
needs:
- check-version

steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}

- name: Prepare virtual environment
run: make create-env

- name: Download packages
uses: actions/download-artifact@v4
with:
name: python-packages

- name: Publish to PyPI
env:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
if: ${{ env.PYPI_TOKEN != '' }}
run: make publish-dist

docker-push:
name: Push Docker image
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
needs:
- check-version

steps:
- if: ${{ env.DOCKER_USERNAME != '' }}
uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}

- name: Login to DockerHub
if: ${{ env.DOCKER_USERNAME != '' }}
run: echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin

- name: Push Docker image
if: ${{ env.DOCKER_USERNAME != '' }}
run: make push-image
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,8 @@ example/cdsOriginal
docs/build

# External users tests
external_checks
external_checks

.ruff_cache/
.venv
data/
2 changes: 2 additions & 0 deletions .hadolint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ignored:
- DL3003
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.11.9
21 changes: 21 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
FROM python:3.11-slim

# hadolint ignore=DL3013
RUN pip install --no-cache-dir --upgrade pip

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && \
apt-get install -y --no-install-recommends --no-install-suggests \
build-essential=12.9 \
zlib1g-dev=1:1.2.13.dfsg-1 && \
rm -rf /var/lib/apt/lists/*

# hadolint ignore=DL3042
RUN --mount=type=cache,target=/root/.cache/pip \
--mount=type=bind,target=/project,rw \
cd /project && pip install .

RUN oncodrivefml --help

ENTRYPOINT [ "/usr/local/bin/oncodrivefml" ]
145 changes: 145 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
ROOT_DIR := $(shell echo $(dir $(lastword $(MAKEFILE_LIST))) | sed 's|/*$$||')

SHELL := /bin/bash

DOCS_DIR := $(ROOT_DIR)/docs
VENV_DIR := $(ROOT_DIR)/.venv

VERSION ?= $(shell $(VENV_DIR)/bin/python -c 'from oncodrivefml import __version__; print(f"{__version__}")')

GIT_TAG_OR_SHA = $(shell git describe --tags --exact-match 2>/dev/null || git rev-parse --short HEAD)

IMAGE_TAG ?= $(VERSION)
IMAGE := bbglab/oncodrivefml:$(IMAGE_TAG)

BOLDRED := $(shell tput bold && tput setaf 1)
BOLDGREEN := $(shell tput bold && tput setaf 2)
BOLDYELLOW := $(shell tput bold && tput setaf 3)
WHITE := $(shell tput sgr0 && tput setaf 7)
RESET := $(shell tput sgr0)


.PHONY: help
help:
@echo "$(BOLDYELLOW)Available targets:$(RESET)"
@echo
@echo "$(BOLDGREEN) checks $(WHITE)-> Run all the checks (format and lint)"
@echo "$(BOLDGREEN) check-format $(WHITE)-> Check for formatting errors"
@echo "$(BOLDGREEN) check-lint $(WHITE)-> Check for lint errors"
@echo "$(BOLDGREEN) check-docker $(WHITE)-> Check the Dockerfile"
@echo "$(BOLDGREEN) format $(WHITE)-> Format source code"
@echo "$(BOLDGREEN) build-dist $(WHITE)-> Build source and wheel distribution files"
@echo "$(BOLDGREEN) install-dev $(WHITE)-> Install the packages in editable mode"
@echo "$(BOLDGREEN) create-env $(WHITE)-> Create a virtual environment"
@echo "$(BOLDGREEN) remove-env $(WHITE)-> Remove the virtual environment"
@echo "$(BOLDGREEN) build-image $(WHITE)-> Build the Docker image"
@echo "$(BOLDGREEN) push-image $(WHITE)-> Push the Docker image into DockerHub"
@echo "$(BOLDGREEN) docs $(WHITE)-> Generate the documentation"
@echo "$(BOLDGREEN) run-example $(WHITE)-> Run the included example using the Docker image"
@echo "$(BOLDGREEN) clean $(WHITE)-> Clean the working directory (build files, virtual environments, caches)"
@echo "$(RESET)"

$(VENV_DIR):
@echo "$(BOLDYELLOW)Preparing virtual environment ...$(RESET)"
python -m venv $(VENV_DIR)
$(VENV_DIR)/bin/pip install -U pip ruff setuptools wheel build twine
@echo "$(BOLDGREEN)==> Success!$(RESET)"

.PHONY: checks
checks: check-format check-lint check-docker

.PHONY: check-format
check-format: $(VENV_DIR)
@echo "$(BOLDGREEN)Checking code format ...$(RESET)"
$(VENV_DIR)/bin/ruff format --check
@echo "$(BOLDGREEN)==> Success!$(RESET)"

.PHONY: check-lint
check-lint: $(VENV_DIR)
@echo "$(BOLDGREEN)Checking lint ...$(RESET)"
$(VENV_DIR)/bin/ruff check
@echo "$(BOLDGREEN)==> Success!$(RESET)"

.PHONY: check-docker
check-docker:
@echo "$(BOLDGREEN)Checking Dockerfile ...$(RESET)"
docker run --rm -i \
-v $$(pwd):/project \
hadolint/hadolint hadolint \
--config /project/.hadolint.yaml \
/project/Dockerfile
@echo "$(BOLDGREEN)==> Success!$(RESET)"

.PHONY: check-version
check-version: $(VENV_DIR)
@echo "$(BOLDGREEN)Checking that the version matches the tag ...$(RESET)"
@if [ "$(VERSION)" != "$(GIT_TAG_OR_SHA)" ]; then \
echo "$(BOLDRED)==> Version $(BOLDYELLOW)$(VERSION)$(BOLDRED) doesn't match the git tag $(BOLDYELLOW)$(GIT_TAG_OR_SHA)$(BOLDRED) !!!$(RESET)"; \
echo "$(BOLDRED)==> Please update the $(BOLDYELLOW)__version__$(BOLDRED) in $(BOLDYELLOW)oncodrivefml/__init__.py$(BOLDRED) and re-create the tag.$(RESET)"; \
exit 1; \
fi
@echo "$(BOLDGREEN)==> Success!$(RESET)"

.PHONY: format
format: $(VENV_DIR)
@echo "$(BOLDGREEN)Formatting code ...$(RESET)"
$(VENV_DIR)/bin/ruff format

.PHONY: build-dist
build-dist: $(VENV_DIR)
@echo "$(BOLDGREEN)Building packages ...$(RESET)"
$(VENV_DIR)/bin/python -m build

.PHONY: publish-dist
publish-dist: $(VENV_DIR)
@echo "$(BOLDGREEN)Publishing OncodriveFML $(BOLDYELLOW)$(VERSION)$(BOLDGREEN) to PyPI ...$(RESET)"
@[[ -z "$(PYPI_USERNAME)" || -z "$(PYPI_PASSWORD)" ]] && (echo "$(BOLDRED)==> Missing PyPI credentials !!!$(RESET)"; exit 1)
$(VENV_DIR)/bin/twine upload --username __token__ --password $(PYPI_TOKEN) dist/*

.PHONY: install
install-dev: $(VENV_DIR)
$(VENV_DIR)/bin/pip install -e .

.PHONY: create-env
create-env: $(VENV_DIR)

.PHONY: remove-env
remove-env:
@echo "$(BOLDGREEN)Removing virtual environment ...$(RESET)"
rm -rf $(VENV_DIR)

.PHONY: build-image
build-image:
@echo "$(BOLDGREEN)Building Docker image $(BOLDYELLOW)$(IMAGE)$(BOLDGREEN) ...$(RESET)"
docker build --progress=plain -t $(IMAGE) .
@echo "$(BOLDGREEN)==> Success!$(RESET)"

.PHONY: build-image
push-image:
@echo "$(BOLDGREEN)Pushing the Docker image into the DockerHub ...$(RESET)"
docker push $(IMAGE)
@echo "$(BOLDGREEN)==> Success!$(RESET)"

.PHONY: docs
docs: $(VENV_DIR)
@if ! which $(VENV_DIR)/bin/sphinx-build > /dev/null; then \
$(VENV_DIR)/bin/pip install -r optional-requirements.txt; \
fi
(source $(VENV_DIR)/bin/activate; make -C $(DOCS_DIR) html)

.PHONY: run-example
run-example:
@echo "$(BOLDGREEN)Running example ...$(RESET)"
docker run --rm -i \
-v $${BGDATA_LOCAL:-$${HOME}/.bgdata}:/root/.bgdata \
-v $$(pwd)/example:/data \
--workdir /data \
$(IMAGE) -i paad.txt.gz -e cds.tsv.gz --signature-correction wx --seed 123 --force
@echo "$(BOLDGREEN)==> Success!$(RESET)"

.PHONY: clean
clean:
@echo "$(BOLDGREEN)Cleaning the repository ...$(RESET)"
rm -rf ./oncodrivefml.egg-info ./dist $(VENV_DIR) ./.ruff_cache ./.eggs $(DOCS_DIR)/build
find oncodrivefml \( -name '*.c' -o -name '*.so' \) -type f -exec rm {} +
find . -name "__pycache__" -type d -exec rm -r {} +
Loading