From 474c268d74c76edd2b43f41a95b8640d89bf59c6 Mon Sep 17 00:00:00 2001 From: Sergei Korolev Date: Wed, 26 Jul 2023 17:37:41 +0300 Subject: [PATCH 1/9] chore: version bump --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d73d0ca..cb01993 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "flit_core.buildapi" [project] name = "yascheduler" -version = "1.0.11" +version = "1.0.12" description = """Yet another computing scheduler and cloud orchestration engine""" authors = [ {name = "Evgeny Blokhin", email = "eb@tilde.pro"}, From 906b51e230e94a60216c0cbedb383c3c1c3ec2be Mon Sep 17 00:00:00 2001 From: Sergei Korolev Date: Wed, 26 Jul 2023 19:05:04 +0300 Subject: [PATCH 2/9] style: setup linters --- .flake8 | 2 + .github/workflows/linter.yml | 65 ++++++++------------- .pre-commit-config.yaml | 107 ++++++++++++++++------------------- pyproject.toml | 1 - 4 files changed, 73 insertions(+), 102 deletions(-) diff --git a/.flake8 b/.flake8 index bc1fd8c..5c7eb86 100644 --- a/.flake8 +++ b/.flake8 @@ -11,3 +11,5 @@ ignore = E203,E501,W503 classmethod-decorators = classmethod validator +per-file-ignores = + */__init__.py: F401 diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 93ffee2..e0bb7b7 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -2,10 +2,9 @@ # yamllint disable rule:line-length name: Lint Code Base -on: - pull_request: - push: - branches-ignore: [master, main] +on: # yamllint disable-line rule:truthy rule:comments + # allow this workflow to be called from other workflows + workflow_call: jobs: lint: @@ -18,56 +17,38 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 + if: ${{ !env.ACT }} # skip during local actions testing with: fetch-depth: 0 - - name: Fetch git base ref - run: | - git fetch --depth=1 \ - origin +${{ github.base_ref || github.event.repository.default_branch }} - - - name: Get npm cache directory - id: npm-cache-dir - run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - - name: npm cache - uses: actions/cache@v3 - with: - path: ${{ steps.npm-cache-dir.outputs.dir }} - key: ${{ runner.os }}-node-${{ hashFiles('.github/workflows/linter.yml') }} - restore-keys: | - ${{ runner.os }}-node- - - - name: Install node tools - run: npm i --silent -g markdownlint-cli@0.31.1 - - - uses: mfinelli/setup-shfmt@v2 - with: - shfmt-version: 3.5.0 - name: Setup Python uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} cache: pip - cache-dependency-path: | - pyproject.toml + cache-dependency-path: pyproject.toml - - name: Install pip deps + - name: Install dependencies run: | python -m pip install --upgrade pip pip install .[lint] - - name: pre-commit cache - uses: actions/cache@v3 - with: - path: ~/.cache/pre-commit - key: pre-commit-${{ env.pythonLocation }}-${{ hashFiles('.pre-commit-config.yaml') }} - restore-keys: | - pre-commit-${{ env.pythonLocation }}- + - name: Autoflake + run: autoflake -c -r yascheduler + + - name: Black + run: black --check --diff yascheduler - - name: Run pre-commit + - name: isort + run: isort -c --diff **/*.py + + - name: flake8 run: | - git branch - pre-commit run --show-diff-on-failure --color=always \ - --from-ref ${{ format('remotes/origin/{0}', github.base_ref || github.event.repository.default_branch) }} \ - --to-ref ${{ github.sha }} + flake8 yascheduler + + - name: pylint + # only errors now + run: pylint -E yascheduler + + - name: pyupgrade + run: pyupgrade --py38=plus --keep-percent-format diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ef8ab71..b4c0e79 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,42 +17,6 @@ repos: - id: mixed-line-ending - id: trailing-whitespace - - repo: https://github.com/myint/autoflake - rev: v1.4 - hooks: - - id: autoflake - args: - - --expand-star-imports - - --ignore-init-module-imports - - --in-place - - --remove-all-unused-imports - - --remove-duplicate-keys - - --remove-unused-variables - - - repo: https://github.com/psf/black - rev: 21.12b0 - hooks: - - id: black - entry: black - args: - - --target-version - - py37 - additional_dependencies: ["click<8.1.0"] - - - repo: https://github.com/pycqa/flake8 - rev: 4.0.1 - hooks: - - id: flake8 - name: flake8 - exclude: /__init__\.py$ - additional_dependencies: ["flake8-bugbear==22.4.25"] - - - repo: https://github.com/timothycrosley/isort - rev: 5.11.5 - hooks: - - id: isort - args: [--settings, .] - - repo: https://github.com/igorshubovych/markdownlint-cli rev: v0.31.1 hooks: @@ -63,14 +27,6 @@ repos: hooks: - id: prettier - - repo: https://github.com/asottile/pyupgrade - rev: v2.37.3 - hooks: - - id: pyupgrade - args: - - --py37-plus - - --keep-percent-format - - repo: https://github.com/adrienverge/yamllint rev: v1.26.3 hooks: @@ -87,18 +43,51 @@ repos: - --simplify - --list - # TODO: - # - repo: local - # hooks: - # - id: pylint - # name: pylint - # entry: pylint - # language: system - # types: [python] - # require_serial: true - # args: - # - --rcfile=.pylintrc -# TODO: cpp -# TODO: clang format -# TODO: gitleaks -# TODO: mypy + - repo: local + hooks: + - id: autoflake + name: autoflake + entry: autoflake + language: system + types: [python] + args: + - --in-place + + - id: black + name: black + entry: black + language: system + types: [python] + + - id: flake8 + name: flake8 + entry: flake8 + language: system + types: [python] + args: + - yascheduler + + - id: isort + name: isort + entry: isort + language: system + types: [python] + + - id: pylint-errors + name: pylint only errors + entry: pylint + language: system + types: [python] + require_serial: true + args: + - -E + - yascheduler + + - id: pyupgrade + name: pyupgrade + entry: pyupgrade + language: system + types: [python] + args: + - --py38-plus + - --keep-percent-format diff --git a/pyproject.toml b/pyproject.toml index cb01993..9070042 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,6 @@ lint = [ "flake8", "flake8-bugbear", "isort", - "pre-commit", "pylint", "pylint-per-file-ignores >= 1", "pyupgrade", From 80c39ee04834e0504fea9be2d339ad958451b649 Mon Sep 17 00:00:00 2001 From: Sergei Korolev Date: Wed, 26 Jul 2023 19:05:31 +0300 Subject: [PATCH 3/9] style: fixes for linters --- yascheduler/aiida_plugin.py | 14 +++-- yascheduler/client.py | 2 +- yascheduler/clouds/adapters.py | 2 +- yascheduler/clouds/hetzner.py | 2 +- yascheduler/config/local.py | 2 +- yascheduler/remote_machine/checks.py | 54 +++++++++++++------- yascheduler/remote_machine/remote_machine.py | 28 +++++----- yascheduler/scheduler.py | 6 ++- yascheduler/utils.py | 45 ++++++++++------ 9 files changed, 98 insertions(+), 57 deletions(-) diff --git a/yascheduler/aiida_plugin.py b/yascheduler/aiida_plugin.py index ac40f0b..b9aabf4 100644 --- a/yascheduler/aiida_plugin.py +++ b/yascheduler/aiida_plugin.py @@ -3,8 +3,10 @@ with respect to the supported yascheduler engines """ -import aiida.schedulers -from aiida.orm import load_node +import aiida.schedulers # pylint: disable=import-error +from aiida.orm import load_node # pylint: disable=import-error + +# pylint: disable=import-error from aiida.schedulers.datastructures import JobInfo, JobState, NodeNumberJobResource _MAP_STATUS_YASCHEDULER = { @@ -39,6 +41,8 @@ def _get_joblist_command(self, jobs=None, user=None): """ The command to report full information on existing jobs. """ + + # pylint: disable=import-error from aiida.common.exceptions import FeatureNotAvailable if user: @@ -79,8 +83,10 @@ def _get_submit_script_header(self, job_tmpl): # so that the required input file(s) can be deduced lines = [f"ENGINE={aiida_node.inputs.code.label.lower()}"] - try: lines.append(f"PARENT={aiida_node.caller.uuid}") - except AttributeError: pass + try: + lines.append(f"PARENT={aiida_node.caller.uuid}") + except AttributeError: + pass lines.append(f"LABEL={job_tmpl.job_name}") return "\n".join(lines) diff --git a/yascheduler/client.py b/yascheduler/client.py index 5a863aa..77b0d8e 100644 --- a/yascheduler/client.py +++ b/yascheduler/client.py @@ -3,7 +3,7 @@ import asyncio import logging from pathlib import PurePath -from typing import Any, Mapping, MutableMapping, Optional, Sequence, Union +from typing import Any, Mapping, Optional, Sequence, Union from attrs import asdict diff --git a/yascheduler/clouds/adapters.py b/yascheduler/clouds/adapters.py index bdd2934..c147ef8 100644 --- a/yascheduler/clouds/adapters.py +++ b/yascheduler/clouds/adapters.py @@ -71,7 +71,7 @@ def create( op_limit=op_limit, ) - @lru_cache() # noqa: B019 + @lru_cache # noqa: B019 def get_op_semaphore(self) -> asyncio.Semaphore: return asyncio.Semaphore(self.op_limit) diff --git a/yascheduler/clouds/hetzner.py b/yascheduler/clouds/hetzner.py index 472942c..dedf6f8 100644 --- a/yascheduler/clouds/hetzner.py +++ b/yascheduler/clouds/hetzner.py @@ -27,7 +27,7 @@ def get_client(cfg: ConfigCloudHetzner) -> HClient: return HClient(cfg.token) -@lru_cache() +@lru_cache def get_ssh_key_id(client: HClient, key: ASSHKey) -> int: "Get Hetzner ssh id" key_name = get_key_name(key) diff --git a/yascheduler/config/local.py b/yascheduler/config/local.py index 1d3791b..31803c4 100644 --- a/yascheduler/config/local.py +++ b/yascheduler/config/local.py @@ -39,7 +39,7 @@ class ConfigLocal: def get_private_keys(self) -> Sequence[PurePath]: "List private key file paths" - filepaths = filter(lambda x: x.is_file(), self.keys_dir.iterdir()) + filepaths = filter(lambda x: x.is_file(), Path(self.keys_dir).iterdir()) return list(filepaths) @classmethod diff --git a/yascheduler/remote_machine/checks.py b/yascheduler/remote_machine/checks.py index 2303db5..5feb4f9 100644 --- a/yascheduler/remote_machine/checks.py +++ b/yascheduler/remote_machine/checks.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +"OS checks" from typing import Optional, Tuple @@ -8,66 +8,82 @@ @lru_cache async def check_is_linux(conn: SSHClientConnection) -> bool: - r = await conn.run("uname") - return r.returncode == 0 and r.stdout is not None and r.stdout.strip() == "Linux" + "Check for generic Linux" + proc = await conn.run("uname") + return ( + proc.returncode == 0 + and proc.stdout is not None + and proc.stdout.strip() == "Linux" + ) @lru_cache async def _get_os_release(conn: SSHClientConnection) -> Optional[Tuple[str, str, str]]: - r = await conn.run("source /etc/os-release; echo $ID@@@$ID_LIKE@@@$VERSION_ID") - if r.returncode != 0 or not r.stdout: + "Get os release string on linuxes" + proc = await conn.run("source /etc/os-release; echo $ID@@@$ID_LIKE@@@$VERSION_ID") + if proc.returncode != 0 or not proc.stdout: return None - return tuple(map(lambda x: x.strip(), str(r.stdout).split("@@@", maxsplit=3))) + return tuple(map(lambda x: x.strip(), str(proc.stdout).split("@@@", maxsplit=3))) async def check_is_debian_like(conn: SSHClientConnection) -> bool: + "Check for any Debian-like" os_release = await _get_os_release(conn) return "debian" in [os_release[0], os_release[1]] if os_release else False async def check_is_debian(conn: SSHClientConnection) -> bool: + "Check for any Debian" os_release = await _get_os_release(conn) return os_release[0] == "debian" if os_release else False async def check_is_debian_buster(conn: SSHClientConnection) -> bool: + "Check for Debian 10" os_release = await _get_os_release(conn) return os_release[2] == "10" if os_release else False async def check_is_debian_bullseye(conn: SSHClientConnection) -> bool: + "Check for Debian 11" os_release = await _get_os_release(conn) return os_release[2] == "11" if os_release else False @lru_cache async def check_is_windows(conn: SSHClientConnection) -> bool: - r = await conn.run("[environment]::OSVersion") - return r.returncode == 0 + "Check for any Windows with Powershell" + proc = await conn.run("[environment]::OSVersion") + return proc.returncode == 0 @lru_cache async def get_wmi_w32_os_caption(conn: SSHClientConnection) -> Optional[str]: - r = await conn.run("(Get-WmiObject -class Win32_OperatingSystem).Caption") - if r.stdout: - return str(r.stdout) + "Get OS caption from WMI object" + proc = await conn.run("(Get-WmiObject -class Win32_OperatingSystem).Caption") + if proc.stdout: + return str(proc.stdout) async def check_is_windows7(conn: SSHClientConnection) -> bool: - c = await get_wmi_w32_os_caption(conn) - return "7" in c if c else False + "Check for Windows 7" + caption = await get_wmi_w32_os_caption(conn) + return "7" in caption if caption else False async def check_is_windows8(conn: SSHClientConnection) -> bool: - c = await get_wmi_w32_os_caption(conn) - return "8" in c if c else False + "Check for Windows 8" + caption = await get_wmi_w32_os_caption(conn) + return "8" in caption if caption else False async def check_is_windows10(conn: SSHClientConnection) -> bool: - c = await get_wmi_w32_os_caption(conn) - return "10" in c if c else False + "Check for Windows 10" + caption = await get_wmi_w32_os_caption(conn) + return "10" in caption if caption else False async def check_is_windows11(conn: SSHClientConnection) -> bool: - c = await get_wmi_w32_os_caption(conn) - return "11" in c if c else False + "Check for Windows 11" + caption = await get_wmi_w32_os_caption(conn) + return "11" in caption if caption else False diff --git a/yascheduler/remote_machine/remote_machine.py b/yascheduler/remote_machine/remote_machine.py index 7e7c008..b128e9d 100644 --- a/yascheduler/remote_machine/remote_machine.py +++ b/yascheduler/remote_machine/remote_machine.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +"Remote machine" import asyncio import logging @@ -208,7 +208,7 @@ async def with_limit(conn: SSHClientConnection, fn: SSHCheck): await aall(amap(lambda y: with_limit(conn, y), x.checks)) for x in ADAPTERS ] - for candidate, check in zip(ADAPTERS, checks): + for candidate, check in zip(ADAPTERS, checks, strict=True): if check: platforms.append(candidate.platform) if check and not adapter: @@ -308,11 +308,12 @@ async def get_conn(self) -> SSHClientConnection: @property def path(self) -> Type[PurePath]: + "Return path of the adapter" return self.adapter.path - def quote(self, s: str) -> str: + def quote(self, string: str) -> str: "Platform-specific shell quoting" - return self.adapter.quote(s) + return self.adapter.quote(string) @my_backoff_exc() async def run( @@ -362,6 +363,7 @@ async def setup_node(self, engines: PEngineRepository): """ self.log.info(f"CPUs count: {await self.get_cpu_cores()}") conn = await self.get_conn() + # pylint: disable=redundant-keyword-arg retry = my_backoff_exc(exception=AllSSHRetryExc) await retry(self.adapter.setup_node)( conn=conn, @@ -380,16 +382,16 @@ async def occupancy_check(self, engine: PEngine) -> bool: try: if [x async for x in self.pgrep(engine.check_pname)]: return True - except SSHRetryExc as e: - self.log.info(f"Node {self.hostname} failed pgrep: {e}") + except SSHRetryExc as exc: + self.log.info(f"Node {self.hostname} failed pgrep: {exc}") await self.renew_conn() if engine.check_cmd: try: - r = await self.run(engine.check_cmd) - if r.returncode == engine.check_cmd_code: + proc = await self.run(engine.check_cmd) + if proc.returncode == engine.check_cmd_code: return True - except SSHRetryExc as e: - self.log.info(f"Node {self.hostname} failed command: {e}") + except SSHRetryExc as exc: + self.log.info(f"Node {self.hostname} failed command: {exc}") await self.renew_conn() return False @@ -407,9 +409,9 @@ async def occupancy_checker(): self.occupancy_check(engine), timeout=engine.sleep_interval ) except asyncio.TimeoutError: - t = "Engine {} busy check timeouted on {}" - self.log.warning(t.format(engine.name, self.hostname)) - except Exception as err: + tmpl = "Engine {} busy check timeouted on {}" + self.log.warning(tmpl.format(engine.name, self.hostname)) + except Exception as err: # pylint: disable=broad-exception-caught self.log.warning(err) await asyncio.sleep(engine.sleep_interval) diff --git a/yascheduler/scheduler.py b/yascheduler/scheduler.py index a21ae39..a5ea843 100755 --- a/yascheduler/scheduler.py +++ b/yascheduler/scheduler.py @@ -292,7 +292,9 @@ async def allocate_task(self, task: TaskModel) -> bool: engine: Optional[Engine] = self.config.engines.get(engine_name) if engine is None: self.log.warning( - "Unsupported engine '%s' for task_id=%s" % (engine_name, task.task_id) + "Unsupported engine '{}' for task_id={}".format( + engine_name, task.task_id + ) ) await self.db.set_task_error( task.task_id, metadata=task.metadata, error="unsupported engine" @@ -386,7 +388,7 @@ async def job(): try: await sftp_get_retry(job)() except Exception as err: - self.log.warning("Cannot scp from %s: %s" % (remote_folder, err)) + self.log.warning(f"Cannot scp from {remote_folder}: {err}") sftp_errors.append((remote_folder, err)) if sftp_errors: diff --git a/yascheduler/utils.py b/yascheduler/utils.py index cdb9042..ee28f77 100644 --- a/yascheduler/utils.py +++ b/yascheduler/utils.py @@ -44,7 +44,9 @@ def submit(): pass label = script_params.get("LABEL", "AiiDA job") - metadata: Mapping[str, Any] = {"local_folder": os.getcwd()} # NB AiiDA chdirs to repo, but if not? + metadata: Mapping[str, Any] = { + "local_folder": os.getcwd() + } # NB AiiDA chdirs to repo, but if not? if not script_params.get("ENGINE"): raise ValueError("Script has not defined an engine") @@ -118,7 +120,10 @@ async def _check_status(): # noqa: C901 if args.convergence: try: + # pylint: disable=import-error from numpy import nan + + # pylint: disable=import-error from pycrystal import CRYSTOUT, CRYSTOUT_Error local_parsing_ready = True @@ -270,7 +275,6 @@ def _init_systemd(install_path: Path): def _init_sysv(install_path: Path): - print("Installing SysV service") # create sysv script in /etc/init.d @@ -312,16 +316,25 @@ async def _show_nodes(): tasks = await db.get_tasks_by_status(statuses=[TaskStatus.RUNNING]) nodes = await db.get_all_nodes() for node in nodes: + tmpl = ( + "ip={ip} ncpus={ncpus} enabled={enabled} " + "occupied_by={occ} (task_id={tid}) {cloud}" + ) node_tasks = list(filter(lambda x: x.ip == node.ip, tasks)) - node_task = ["-", "-"] + node_label = "-" + task_id = "-" for x in node_tasks: - node_task = [x.label, x.task_id] - data = tuple( - [node.ip, node.ncpus or "MAX", node.enabled] - + node_task - + [node.cloud or ""] + node_label = x.label + task_id = x.task_id + msg = tmpl.format( + ip=node.ip, + ncpus=node.ncpus or "MAX", + enabled=node.enabled, + occ=node_label, + tid=task_id, + cloud=node.cloud or "", ) - print("ip=%s ncpus=%s enabled=%s occupied_by=%s (task_id=%s) %s" % data) + print(msg) def show_nodes(): @@ -385,7 +398,9 @@ async def _manage_node(): for task_id in task_ids: await db.update_task_status(task_id, TaskStatus.DONE) print( - "An associated task %s at %s is now marked done!" % (task_id, args.host) + "An associated task {} at {} is now marked done!".format( + task_id, args.host + ) ) await db.remove_node(args.host) @@ -444,10 +459,11 @@ def daemonize(log_file=None): logger = get_logger(log_file, level=logging._nameToLevel[args.log_level]) async def on_signal( - y: Scheduler, shield: Sequence[asyncio.Task], signame: str, signum: int + y: Scheduler, shield: Sequence[asyncio.Task], sig: signal.Signals ): + signame = signal.strsignal(sig) logger.info(f"Received signal {signame}") - if signum in [signal.SIGTERM, signal.SIGINT]: + if sig in [signal.SIGTERM, signal.SIGINT]: await y.stop() shielded = [*shield, asyncio.current_task()] tasks = [t for t in asyncio.all_tasks() if t not in shielded] @@ -468,9 +484,8 @@ async def run(): for sig in [signal.SIGTERM, signal.SIGINT]: def handler(): - return asyncio.create_task( - on_signal(yac, shielded, sig.name, sig.value) - ) + task = on_signal(yac, shielded, sig) # noqa: B023 + return asyncio.create_task(task) loop.add_signal_handler(sig, handler) From 863c5814be6c1776ae899f10b5372963268c94a0 Mon Sep 17 00:00:00 2001 From: Sergei Korolev Date: Wed, 26 Jul 2023 19:06:46 +0300 Subject: [PATCH 4/9] ci(actions): setup release automations --- .github/workflows/pr.yml | 36 ++++++++++++++++++ .github/workflows/push.yml | 70 +++++++++++++++++++++++++++++++++++ .github/workflows/release.yml | 40 ++++++++++++++++++++ 3 files changed, 146 insertions(+) create mode 100644 .github/workflows/pr.yml create mode 100644 .github/workflows/push.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..07c8dab --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,36 @@ +--- +# yamllint disable rule:line-length +name: PR action + +on: + pull_request: + types: [opened, reopened, synchronize] + +jobs: + lint: + uses: ./.github/workflows/linter.yml + + build: + name: Build + runs-on: ubuntu-latest + needs: test + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + if: ${{ !env.ACT }} # skip during local actions testing + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + cache: pip + cache-dependency-path: pyproject.toml + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install .[release] + + - name: Build + run: flit build diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 0000000..e206982 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,70 @@ +--- +# yamllint disable rule:line-length +name: Push action + +on: + push: + branches: + - master + +jobs: + release: + name: Bump version and create draft release + runs-on: ubuntu-latest + needs: test + permissions: + contents: write + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + if: ${{ !env.ACT }} # skip during local actions testing + with: + fetch-depth: 0 + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + cache: pip + cache-dependency-path: pyproject.toml + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install .[release] + + - name: Create bump and changelog + id: cz + if: github.repository == 'tilde-lab/yascheduler' + uses: commitizen-tools/commitizen-action@e41bf7f2029bc8175af362badd6fd0860a329b0f + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + changelog_increment_filename: .CHANGELOG-CURRENT.md + push: true + commit: true + + - name: Print new version + if: github.repository == 'tilde-lab/yascheduler' + run: echo "Bumped to version ${{ steps.cz.outputs.version }}" + + - name: Build + run: flit build + + - name: Stop if no bump + id: no-bump + continue-on-error: true + # will fail if not on exact tag + run: git describe --tags --exact-match + + - name: Create Release Draft + uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 + if: steps.no-bump.outcome == 'success' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + draft: true + tag_name: v${{ steps.cz.outputs.version }} + body_path: .CHANGELOG-CURRENT.md + fail_on_unmatched_files: true + files: dist/* diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..5be1d96 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,40 @@ +--- +# yamllint disable rule:line-length +name: Publish release to pypi + +on: + release: + types: [published] + + workflow_dispatch: + +jobs: + pypi: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + if: ${{ !env.ACT }} # skip during local actions testing + with: + fetch-depth: 0 + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + cache: pip + cache-dependency-path: pyproject.toml + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install .[release] + + - name: Build + run: flit build + + - name: Publish package to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: ${{ secrets.PYPI_API_TOKEN }} From 3491ae727a02a774f7134c68c6fb82ff96601399 Mon Sep 17 00:00:00 2001 From: Sergei Korolev Date: Wed, 26 Jul 2023 19:10:48 +0300 Subject: [PATCH 5/9] docs(changelog): add placeholder --- CHANGELOG.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e69de29 From 2a11d3e5cce0c4738a33581b16891e0d3854f2ae Mon Sep 17 00:00:00 2001 From: Sergei Korolev Date: Wed, 26 Jul 2023 19:12:56 +0300 Subject: [PATCH 6/9] ci(actions): setup concurrency --- .github/workflows/push.yml | 2 ++ .github/workflows/release.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index e206982..249b97d 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -14,6 +14,8 @@ jobs: needs: test permissions: contents: write + concurrency: + group: release steps: - name: Checkout repository diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5be1d96..f459046 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,6 +11,8 @@ on: jobs: pypi: runs-on: ubuntu-latest + concurrency: + group: release steps: - name: Checkout repository From 03dd66b9e6f544808d91d904a0ab3f38fb88b5d5 Mon Sep 17 00:00:00 2001 From: Sergei Korolev Date: Wed, 26 Jul 2023 19:16:21 +0300 Subject: [PATCH 7/9] ci(actions): fix deps --- .github/workflows/pr.yml | 1 - .github/workflows/push.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 07c8dab..96d821c 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -13,7 +13,6 @@ jobs: build: name: Build runs-on: ubuntu-latest - needs: test steps: - name: Checkout repository diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 249b97d..8fe33eb 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -11,7 +11,6 @@ jobs: release: name: Bump version and create draft release runs-on: ubuntu-latest - needs: test permissions: contents: write concurrency: From 38c408a58aaebd4182f803f598f5ec40888cf642 Mon Sep 17 00:00:00 2001 From: Sergei Korolev Date: Wed, 26 Jul 2023 19:19:52 +0300 Subject: [PATCH 8/9] ci(actions): fix typo --- .github/workflows/linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index e0bb7b7..a893719 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -51,4 +51,4 @@ jobs: run: pylint -E yascheduler - name: pyupgrade - run: pyupgrade --py38=plus --keep-percent-format + run: pyupgrade --py38-plus --keep-percent-format From e76ee43d85209a47863c035c7effea56862922f5 Mon Sep 17 00:00:00 2001 From: Sergei Korolev Date: Wed, 26 Jul 2023 19:21:31 +0300 Subject: [PATCH 9/9] docs(license): add license file --- LICENSE | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..20a9396 --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright © 2019-2023 Tilde Materials Informatics + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.