diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index cc5acac0..868df96b 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -12,9 +12,36 @@ on: env: PACKAGE_NAME: ansys-tools-common MAIN_PYTHON_VERSION: 3.13 - DOCUMENTATION_CNAME: bookish-adventure-16g5prl.pages.github.io + DOCUMENTATION_CNAME: tools.docs.pyansys.com jobs: + + update-changelog: + name: "Update CHANGELOG (on release)" + if: github.event_name == 'push' && contains(github.ref, 'refs/tags') + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - uses: ansys/actions/doc-deploy-changelog@v10 + with: + token: ${{ secrets.PYANSYS_CI_BOT_TOKEN }} + bot-user: ${{ secrets.PYANSYS_CI_BOT_USERNAME }} + bot-email: ${{ secrets.PYANSYS_CI_BOT_EMAIL }} + + # TODO uncomment after the first public release + # check-vulnerabilities: + # name: "Check library vulnerabilities" + # runs-on: ubuntu-latest + # steps: + # - uses: ansys/actions/check-vulnerabilities@v10.0 + # with: + # python-version: ${{ env.MAIN_PYTHON_VERSION }} + # token: ${{ secrets.PYANSYS_CI_BOT_TOKEN }} + # python-package-name: ${{ env.PACKAGE_NAME }} + # dev-mode: ${{ github.ref != 'refs/heads/main' }} + style: name: Code style runs-on: ubuntu-latest @@ -40,8 +67,8 @@ jobs: python-version: ${{ matrix.python-version }} whitelist-license-check: "termcolor" # Has MIT license, but it's not recognized - build-tests: - name: Build and Testing + tests: + name: Testing runs-on: ubuntu-latest needs: [smoke-tests] env: @@ -75,8 +102,18 @@ jobs: # with: # token: ${{ secrets.CODECOV_TOKEN }} + docs-style: + name: Documentation Style Check + runs-on: ubuntu-latest + steps: + - name: PyAnsys documentation style checks + uses: ansys/actions/doc-style@v10 + with: + token: ${{ secrets.GITHUB_TOKEN }} + doc-build: name: Build documentation + needs: [docs-style] runs-on: ubuntu-latest steps: - name: Build documentation @@ -88,7 +125,7 @@ jobs: package: name: Package library runs-on: ubuntu-latest - needs: [smoke-tests] + needs: [tests, doc-build] steps: - name: Build library source and wheel artifacts uses: ansys/actions/build-library@v10 @@ -96,16 +133,54 @@ jobs: library-name: ${{ env.PACKAGE_NAME }} python-version: ${{ env.MAIN_PYTHON_VERSION }} - upload_dev_docs: - name: Upload dev documentation - if: github.ref == 'refs/heads/main' + release: + name: Release project + if: ${{ github.event_name == 'push' && contains(github.ref, 'refs/tags') }} + needs: [package] runs-on: ubuntu-latest - needs: [doc-build] + environment: release + permissions: + id-token: write + contents: write steps: - - name: Deploy the latest documentation - uses: ansys/actions/doc-deploy-dev@v10 + - name: "Download the library artifacts from build-library step" + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: ${{ env.PACKAGE_NAME }}-artifacts + path: ${{ env.PACKAGE_NAME }}-artifacts + + # TODO uncomment after the first public release + # - name: "Upload artifacts to PyPI using trusted publisher" + # uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 + # with: + # repository-url: "https://upload.pypi.org/legacy/" + # print-hash: true + # packages-dir: ${{ env.PACKAGE_NAME }}-artifacts + # skip-existing: false + + - name: "Release to the private PyPI repository" + uses: ansys/actions/release-pypi-private@v9 + with: + library-name: "ansys-api-meshing-maestro" + twine-username: "__token__" + twine-token: ${{ secrets.PYANSYS_PYPI_PRIVATE_PAT }} + + - name: Release to GitHub + uses: ansys/actions/release-github@v10 + with: + token: ${{ secrets.GITHUB_TOKEN }} + library-name: ${{ env.PACKAGE_NAME }} + + upload_docs_release: + name: Upload release documentation + if: github.event_name == 'push' && contains(github.ref, 'refs/tags') + runs-on: ubuntu-latest + needs: [release] + steps: + - name: Deploy the stable documentation + uses: ansys/actions/doc-deploy-stable@v10 with: cname: ${{ env.DOCUMENTATION_CNAME }} token: ${{ secrets.PYANSYS_CI_BOT_TOKEN }} bot-user: ${{ secrets.PYANSYS_CI_BOT_USERNAME }} - bot-email: ${{ secrets.PYANSYS_CI_BOT_EMAIL }} + bot-email: ${{ secrets.PYANSYS_CI_BOT_EMAIL }} \ No newline at end of file diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml new file mode 100644 index 00000000..65bf3a36 --- /dev/null +++ b/.github/workflows/label.yml @@ -0,0 +1,115 @@ +name: Labeler +on: + pull_request: + # opened, reopened, and synchronize are default for pull_request + # edited - when PR title or body is changed + # labeled - when labels are added to PR + types: [opened, reopened, synchronize, edited, labeled] + push: + branches: [ main ] + paths: + - '../labels.yml' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + + label-syncer: + name: Syncer + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: micnncim/action-label-syncer@3abd5ab72fda571e69fffd97bd4e0033dd5f495c # v1.3.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + labeler: + name: Set labels + needs: [label-syncer] + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + steps: + + # Label based on modified files + - name: Label based on changed files + uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + sync-labels: true + + # Label based on branch name + - uses: actions-ecosystem/action-add-labels@18f1af5e3544586314bbe15c0273249c770b2daf # v1.1.3 + if: | + startsWith(github.event.pull_request.head.ref, 'doc') || + startsWith(github.event.pull_request.head.ref, 'docs') + with: + labels: documentation + github_token: ${{ secrets.GITHUB_TOKEN }} + + - uses: actions-ecosystem/action-add-labels@18f1af5e3544586314bbe15c0273249c770b2daf # v1.1.3 + if: | + startsWith(github.event.pull_request.head.ref, 'maint') || + startsWith(github.event.pull_request.head.ref, 'no-ci') || + startsWith(github.event.pull_request.head.ref, 'ci') + with: + labels: maintenance + github_token: ${{ secrets.GITHUB_TOKEN }} + + - uses: actions-ecosystem/action-add-labels@18f1af5e3544586314bbe15c0273249c770b2daf # v1.1.3 + if: startsWith(github.event.pull_request.head.ref, 'feat') + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + labels: | + enhancement + + - uses: actions-ecosystem/action-add-labels@18f1af5e3544586314bbe15c0273249c770b2daf # v1.1.3 + if: | + startsWith(github.event.pull_request.head.ref, 'fix') || + startsWith(github.event.pull_request.head.ref, 'patch') + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + labels: bug + + commenter: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - name: Suggest to add labels + uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0 + # Execute only when no labels have been applied to the pull request + if: toJSON(github.event.pull_request.labels.*.name) == '{}' + with: + issue-number: ${{ github.event.pull_request.number }} + body: | + Please add one of the following labels to add this contribution to the Release Notes :point_down: + - [bug](https://github.com/ansys-internal/ansys-tools-common/pulls?q=label%3Abug+) + - [documentation](https://github.com/ansys-internal/ansys-tools-common/pulls?q=label%3Adocumentation+) + - [enhancement](https://github.com/ansys-internal/ansys-tools-common/pulls?q=label%3Aenhancement+) + - [good first issue](https://github.com/ansys-internal/ansys-tools-common/pulls?q=label%3Agood+first+issue) + - [maintenance](https://github.com/ansys-internal/ansys-tools-common/pulls?q=label%3Amaintenance+) + - [release](https://github.com/ansys-internal/ansys-tools-common/pulls?q=label%3Arelease+) + + changelog-fragment: + name: "Create changelog fragment" + needs: [labeler] + permissions: + contents: write + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: ansys/actions/doc-changelog@v10 + with: + token: ${{ secrets.PYANSYS_CI_BOT_TOKEN }} + use-conventional-commits: true + use-default-towncrier-config: true + bot-user: ${{ secrets.PYANSYS_CI_BOT_USERNAME }} + bot-email: ${{ secrets.PYANSYS_CI_BOT_EMAIL }} \ No newline at end of file diff --git a/.github/workflows/run_mapdl_tests.yml b/.github/workflows/run_mapdl_tests.yml index 7908fe30..6bcd891e 100644 --- a/.github/workflows/run_mapdl_tests.yml +++ b/.github/workflows/run_mapdl_tests.yml @@ -17,7 +17,7 @@ jobs: build-tests: runs-on: ubuntu-22.04 container: - image: ghcr.io/ansys/pymapdl/mapdl:v22.2-ubuntu + image: ghcr.io/ansys/mapdl:v22.2-ubuntu options: "-u=0:0 --entrypoint /bin/bash" credentials: username: ${{ secrets.GH_USERNAME }} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..8f8a3f96 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +This document contains the release notes for the project. See release notes for v{latest-version} and earlier +in `CHANGELOG.md `_. + \ No newline at end of file diff --git a/doc/.vale.ini b/doc/.vale.ini index c148a37a..1cc4ad98 100644 --- a/doc/.vale.ini +++ b/doc/.vale.ini @@ -28,4 +28,6 @@ Vocab = ANSYS [*.{rst}] BasedOnStyles = Vale, Google Vale.Terms = NO -Google.Headings = NO \ No newline at end of file +Google.Headings = NO +SkippedScopes = script, style, pre, figure, code-block, literal-block + diff --git a/doc/changelog.d/34.maintenance.md b/doc/changelog.d/34.maintenance.md new file mode 100644 index 00000000..e53e26a8 --- /dev/null +++ b/doc/changelog.d/34.maintenance.md @@ -0,0 +1 @@ +Finish cicd \ No newline at end of file diff --git a/doc/source/conf.py b/doc/source/conf.py index 789b23d5..a637d56e 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -70,6 +70,11 @@ }, } +linkcheck_ignore = [] + +if switcher_version != "dev": + linkcheck_ignore.append(f"https://github.com/ansys-internal/ansys-tools-common/releases/tag/v{__version__}") + # Sphinx extensions extensions = [ "sphinx.ext.intersphinx", diff --git a/doc/source/contributing.rst b/doc/source/contributing.rst index 98c75511..ede604b7 100644 --- a/doc/source/contributing.rst +++ b/doc/source/contributing.rst @@ -6,7 +6,7 @@ Contribute Overall guidance on contributing to a PyAnsys library appears in the `Contributing `_ topic in the *PyAnsys developer's guide*. Ensure that you are thoroughly familiar -with this guide before attempting to contribute to the Ansys tools common repo. +with this guide before attempting to contribute to the Ansys tools common repository. The following contribution information is specific to the Ansys tools common. diff --git a/doc/source/index.rst b/doc/source/index.rst index 28c28e8d..a3f48d48 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -47,4 +47,4 @@ The Ansys tools project is a collection of tools for the PyAnsys ecosystem. getting_started/index user_guide/index api/index - contributing \ No newline at end of file + contributing diff --git a/doc/styles/config/vocabularies/ANSYS/accept.txt b/doc/styles/config/vocabularies/ANSYS/accept.txt index 359302da..64937a12 100644 --- a/doc/styles/config/vocabularies/ANSYS/accept.txt +++ b/doc/styles/config/vocabularies/ANSYS/accept.txt @@ -1,4 +1,14 @@ (?i)Ansys -pytest ANS -astroid \ No newline at end of file +astroid +downloader +mypy +pytest +pathing +subprocess + + +s.P +e.M +terminate +kill \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index b5bec27d..5e6d76eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,7 @@ dependencies = [ "scooby>=0.5.12", "appdirs>=1.4.4", "typing-extensions>=4.5.0", + "requests>=2.32.4", ] [project.optional-dependencies] diff --git a/src/ansys/tools/common/example_download.py b/src/ansys/tools/common/example_download.py index 14bbdaf1..f62f38c1 100644 --- a/src/ansys/tools/common/example_download.py +++ b/src/ansys/tools/common/example_download.py @@ -25,8 +25,9 @@ import tempfile from threading import Lock from typing import Optional -from urllib.parse import urljoin -import urllib.request +from urllib.parse import urljoin, urlparse + +import requests __all__ = ["DownloadManager"] @@ -186,17 +187,23 @@ def _retrieve_data(self, url: str, filename: str, dest: str = None, force: bool str The local path where the file was saved. """ - local_path = "" if dest is None: - dest = tempfile.gettempdir() # Use system temp directory if no destination is provided - local_path = Path(dest) / Path(filename).name - if not force and Path(local_path).is_file(): - return local_path - try: - local_path, _ = urllib.request.urlretrieve(url, filename=local_path) - except urllib.error.HTTPError: - raise FileNotFoundError(f"Failed to download {filename} from {url}, file does not exist.") - return local_path + dest = tempfile.gettempdir() + local_path = Path(dest) / Path(filename).name + + if not force and local_path.is_file(): + return str(local_path) + + parsed_url = urlparse(url) + if parsed_url.scheme not in ("http", "https"): + raise ValueError(f"Unsafe URL scheme: {parsed_url.scheme}") + + response = requests.get(url, timeout=60) + response.raise_for_status() + + Path(local_path).write_bytes(response.content) + + return str(local_path) # Create a singleton instance of DownloadManager diff --git a/tests/test_example_download.py b/tests/test_example_download.py index 422ef452..964cb584 100644 --- a/tests/test_example_download.py +++ b/tests/test_example_download.py @@ -24,6 +24,7 @@ from pathlib import Path import pytest +import requests from ansys.tools.common.example_download import download_manager @@ -34,14 +35,14 @@ def test_download(): directory = "pymapdl/cfx_mapping" # Download the file - local_path = download_manager.download_file(filename, directory) - - assert Path.is_file(local_path) + local_path_str = download_manager.download_file(filename, directory) + local_path = Path(local_path_str) + assert local_path.is_file() # Check that file is cached local_path2 = download_manager.download_file(filename, directory) - assert local_path2 == local_path + assert local_path2 == local_path_str download_manager.clear_download_cache() @@ -54,7 +55,7 @@ def test_non_existent_file(): directory = "pymapdl/cfx_mapping" # Attempt to download the non-existent file - with pytest.raises(FileNotFoundError): + with pytest.raises(requests.exceptions.HTTPError): download_manager.download_file(filename, directory)