diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..c3e9c0a --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,213 @@ +name: main + +on: [push] + +jobs: + setup: + runs-on: ubuntu-22.04 + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + cache: "pip" + - name: Install dependencies + run: make ci-setup + - name: Cache venv + id: cache-venv + uses: actions/cache@v4 + with: + path: .venv + key: ${{ runner.os }}-venv + + lint: + runs-on: ubuntu-22.04 + needs: [setup] + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + cache: "pip" + - name: Restore venv + id: cache-venv-restore + uses: actions/cache/restore@v4 + with: + path: .venv + key: ${{ runner.os }}-venv + - name: Run the linter + run: | + source .venv/bin/activate + make lint-ci + + type-check: + runs-on: ubuntu-22.04 + needs: [setup] + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + cache: "pip" + - name: Restore venv + id: cache-venv-restore + uses: actions/cache/restore@v4 + with: + path: .venv + key: ${{ runner.os }}-venv + - name: Run the type-checker + run: | + source .venv/bin/activate + make type-check-ci + + test: + runs-on: ubuntu-22.04 + needs: [lint,type-check] + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + cache: "pip" + - name: Restore venv + id: cache-venv-restore + uses: actions/cache/restore@v4 + with: + path: .venv + key: ${{ runner.os }}-venv + - name: Run the test suite and coverage + run: | + source .venv/bin/activate + make coverage-ci + make doctest-ci + + docs: + runs-on: ubuntu-22.04 + needs: [lint,type-check] + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + cache: "pip" + - name: Restore venv + id: cache-venv-restore + uses: actions/cache/restore@v4 + with: + path: .venv + key: ${{ runner.os }}-venv + - name: Build the docs + run: | + source .venv/bin/activate + make docs-ci + + upload-docs: + if: success() && github.ref == 'refs/heads/main' + needs: [test,docs] + runs-on: ubuntu-22.04 + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + cache: "pip" + - name: Restore venv + id: cache-venv-restore + uses: actions/cache/restore@v4 + with: + path: .venv + key: ${{ runner.os }}-venv + - name: Build docs for publishing + run: | + source .venv/bin/activate + make pages-ci + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docs/build/public + + pages: + needs: [upload-docs] + if: success() && github.ref == 'refs/heads/main' + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-22.04 + steps: + - name: Publish to pages + id: deployment + uses: actions/deploy-pages@v4 + + build: + needs: [test,docs] + if: success() && github.ref == 'refs/heads/main' + runs-on: ubuntu-22.04 + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + cache: "pip" + - name: Restore venv + id: cache-venv-restore + uses: actions/cache/restore@v4 + with: + path: .venv + key: ${{ runner.os }}-venv + - name: Build the package + run: | + source .venv/bin/activate + make build-ci + - name: Upload build + uses: actions/upload-artifact@v3 + with: + name: package-build + path: ./dist/* + + publish: + needs: [build] + if: success() && startsWith(github.ref, 'refs/tags') + runs-on: ubuntu-22.04 + environment: + name: testpypi + url: https://test.pypi.org/p/crypto-condor + permissions: + contents: write + id-token: write + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + cache: "pip" + - name: Download build + uses: actions/download-artifact@v3 + with: + # By omitting the name we are downloading all the artifacts + path: ./dist/ + - name: Publish the package + uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ + diff --git a/Makefile b/Makefile index 991be62..ff34450 100644 --- a/Makefile +++ b/Makefile @@ -19,24 +19,6 @@ help: # Show help for each of the Makefile recipes. all: lint coverage docs doctest -lint: # Format with black and lint with ruff. - @echo "[+] Linting" - black --check . - ruff check . - -lint-ci: # Format with black, lint with ruff, generate report for CI. - @echo "[+] Linting (CI)" - black --check . - ruff check --output-format=gitlab . | tee code-quality-report.json - -type-check: # Run mypy. - @echo "[+] Type checking" - mypy --config-file pyproject.toml . - -type-check-ci: # Run mypy, generate report for CI. - @echo "[+] Type checking (CI)" - mypy --config-file pyproject.toml --junit-xml mypy.xml . - .PHONY: import-nist-vectors import-nist-vectors: # Serialize NIST test vectors with protobuf. import-nist-vectors: $(PROTO_FILES:%.proto=%.imported) @@ -50,6 +32,12 @@ compile-primitives: # Compile primitives written in C. cd crypto_condor/primitives && $(MAKE) all -j4 @echo +compile-primitives-ci: # Compile primitives written in C. + @echo "[+] Compiling primitives (CI)" + sudo apt-get install -y --no-install-recommends pandoc texlive texlive-latex-extra + cd crypto_condor/primitives && $(MAKE) all -j4 + @echo + copy-guides: # Copy guides from the docs for the method command. @echo "[+] Copying guides from the documentation" python utils/copy_guides.py @@ -66,13 +54,52 @@ install: # Install using poetry. poetry install --with=dev,docs @echo +ci-setup: # Basic commands to run before the other CI targets. +ci-setup: + @echo "[+] Setup CI" + export PYTHONDONTWRITEBYTECODE=1 + export POETRY_VIRTUALENVS_IN_PROJECT=1 + python --version + python -m pip --version + python -m pip install poetry + poetry --version + poetry install --with=dev,docs + init: # Common requirements for several targets. init: install import-nist-vectors compile-primitives copy-guides copy-contributing +init-ci: # Common requirements before other CI targets. +init-ci: ci-setup import-nist-vectors copy-guides copy-contributing + +lint: # Format with black and lint with ruff. + @echo "[+] Linting" + black --check . + ruff check . + +lint-ci: # Format with black, lint with ruff, generate report for CI. +lint-ci: init-ci + @echo "[+] Linting (CI)" + poetry run black --check . + poetry run ruff check --output-format=github . + +type-check: # Run mypy. + @echo "[+] Type checking" + mypy --config-file pyproject.toml . + +type-check-ci: # Run mypy, generate report for CI. +type-check-ci: init-ci + @echo "[+] Type checking (CI)" + poetry run mypy --config-file pyproject.toml --junit-xml mypy.xml . + doctest: # Run doctest -doctest: install +doctest: init $(MAKE) -C docs doctest +doctest-ci: # Run doctest +doctest-ci: init-ci + sudo apt-get install -y --no-install-recommends pandoc + . .venv/bin/activate && $(MAKE) -C docs doctest + test: # Run pytest. test: init @echo "[+] Testing" @@ -81,14 +108,14 @@ test: init coverage: # Run coverage, generate HTML report. coverage: init @echo "[+] Testing and checking coverage" - pytest --cov="crypto_condor" --cov-report html -n auto tests/ + pytest --cov="crypto_condor" --cov-report html --numprocesses=auto tests/ coverage-ci: # Run coverage, generate JUnit test report and XML coverage report. -coverage-ci: init +coverage-ci: init-ci compile-primitives-ci @echo "[+] Testing and checking coverage (CI)" - pytest -v --junitxml=report.xml --cov="crypto_condor" --cov-report xml -n auto tests/ + poetry run pytest --verbose --junitxml=junit/test-results.xml --cov="crypto_condor" --cov-report=xml --numprocesses=auto tests/ # Print coverage report so that CI picks up stats - coverage report + poetry run coverage report # Separate build target to fully build locally. build: # Build the package. @@ -99,19 +126,18 @@ build: init # This is redundant since publish-ci also builds the package, but we use this to check # for building errors before trying to publish. build-ci: # Build the package in the CI. -build-ci: init +build-ci: init-ci compile-primitives-ci @echo "[+] Building package (CI)" # Ensure that the tag and version match to avoid pushing a package without # the corresponding documentation. - python utils/check_tag_and_version.py + . .venv/bin/activate && python utils/check_tag_and_version.py poetry build publish-ci: # Publish package using the CI pipeline. publish-ci: init @echo "[+] Publishing package (CI)" - poetry config repositories.gitlab $(CI_API_V4_URL)/projects/$(CI_PROJECT_ID)/packages/pypi -# JOB_TOKEN is an ephemeral token that is valid while the pipeline is running. - @poetry publish -v --build --repository gitlab --cert $(CI_SERVER_TLS_CA_FILE) -u gitlab-ci-token -p $(CI_JOB_TOKEN) + @poetry config pypi-token.pypi $(PYPI_TOKEN) + @poetry publish -v --build compile-proto: # Compile .proto files and prints current protoc version. compile-proto: $(PB2_FILES) @@ -124,26 +150,24 @@ compile-proto: $(PB2_FILES) # This should only be called on main since the published docs are based on that # branch. pages-ci: # Build the documentation for GitLab Pages. -pages-ci: install +pages-ci: init-ci @echo "[+] Building all docs" - $(MAKE) -C docs all-versions - mv docs/build/public . -# Move the current docs to devel - mv public/main public/devel + sudo apt-get install -y --no-install-recommends pandoc + . .venv/bin/activate && $(MAKE) -C docs all-versions + mv docs/build/public/main docs/build/public/devel # Move latest tag to latest. -# This step might fail if the version is bumped since the CI runs once for the -# commit and once more for the tag, so the latest tag might not be present in -# the first run. We ignore the error and copy what would be the staging docs to -# stable. Since this should only occur when pushing a tag, the staging docs -# *are* the stable docs and the CI pipeline for the tag will overwrite these -# anyway. - -LATEST_TAG="$(shell git describe --tags --abbrev=0 --exclude='*rc[0-9]')"; cp -R public/$$LATEST_TAG public/latest + -LATEST_TAG="$(shell git describe --tags --abbrev=0 --exclude='*rc[0-9]')" && cp -R docs/build/public/$$LATEST_TAG docs/build/public/latest .PHONY: docs docs: # Build the documentation docs: install $(MAKE) -C docs html +docs-ci: # Build the documentation +docs-ci: init-ci + sudo apt-get install -y --no-install-recommends pandoc + . .venv/bin/activate && $(MAKE) -C docs html + livedocs: # Build the documentation with live reload. livedocs: install $(MAKE) -C docs livehtml diff --git a/crypto_condor/primitives/TestU01.py b/crypto_condor/primitives/TestU01.py index 279ad08..e7ac513 100644 --- a/crypto_condor/primitives/TestU01.py +++ b/crypto_condor/primitives/TestU01.py @@ -91,34 +91,21 @@ def install_testu01(): with Progress() as progress: task = progress.add_task("Compiling TestU01, please wait", total=None) + # Only capture output if it's not running in the CI, otherwise we want to see + # what's going on in case of an error. + capture_output = not os.environ.get("GITHUB_ACTIONS", False) try: - result = subprocess.run( + _ = subprocess.run( [str(make)], cwd=t_dir, - capture_output=True, + capture_output=capture_output, text=True, check=True, - timeout=60, + timeout=300, ) progress.update(task, completed=True) except (subprocess.CalledProcessError, subprocess.TimeoutExpired): logger.exception("Could not compile TestU01") - # The compilation log is very long so we save it to a file to make it - # easier to read/share. In CI, print it so we get it in the captured output. - if os.environ.get("GITLAB_CI", False): # pragma: no cover (CI) - print("STDOUT:", result.stdout) - print("STDERR:", result.stderr) - else: - try: - with open("testu01.log", "w") as tlog: - tlog.write("STDOUT:") - tlog.write(result.stdout) - tlog.write("\n\n\nSTDERR:") - tlog.write(result.stderr) - logger.info("Saved compilation log to testu01.log") - except OSError: - logger.exception("Could not save compilation log to testu01.log") - logger.info("Dumping log to stdout") raise diff --git a/docs/Makefile b/docs/Makefile index befad0b..34e5aa8 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -14,7 +14,7 @@ help: .PHONY: help Makefile -VERSIONS = main 2024.02.05 2024.01.08 +VERSIONS = main all-versions: $(VERSIONS) git checkout main diff --git a/pyproject.toml b/pyproject.toml index aa2ea1e..c11c283 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "crypto-condor" -version = "2024.02.05" +version = "2024.05.30-rc1" description = "Compliance testing for implementations of cryptographic primitives" license = "Apache-2.0" authors = [