如何生成动态包含 nox 会话列表的 github 动作构建矩阵?

How to generate a github actions build matrix that dynamically includes a list of nox sessions?

我最近开始迁移我的一些 open source python libraries from Travis to Github Actions. To be more independent from the CI/CD platform, I decided to first describe all test environments and configurations using nox

让我们考虑以下原型noxfile.py

import nox

@nox.session(python=["3.7", "3.8"])
def tests(session):
    print("Interesting stuff going on here")

@nox.session(python=["3.7", "3.8"])
@nox.parametrize("a", [-1, 1])
@nox.parametrize("b", [False, True])
def tests_with_params(session, a, b):
    print("Interesting stuff going on here")

@nox.session(python="3.7")
def other(session):
    print("another session")

导致以下 nox --list 输出:

* tests-3.7
* tests-3.8
* tests_with_params-3.7(b=False, a=-1)
* tests_with_params-3.7(b=True, a=-1)
* tests_with_params-3.7(b=False, a=1)
* tests_with_params-3.7(b=True, a=1)
* tests_with_params-3.8(b=False, a=-1)
* tests_with_params-3.8(b=True, a=-1)
* tests_with_params-3.8(b=False, a=1)
* tests_with_params-3.8(b=True, a=1)
* other

我希望 github 操作工作流程 运行 teststests_with_params 的所有会话。在我的工作流 yaml 定义文件中使用硬编码的 build matrix 它可以工作,例如在这里我列出了 tests:

的两个会话
# (in .github/workflows/base.yml)
jobs:
  run_all_tests:
    strategy:
      fail-fast: false
      matrix:
        # Here we manually list two nox sessions
        nox_session: ["tests-3.7", "tests-3.8"]
    name: Run nox session ${{ matrix.nox_session }}
    runs-on: ubuntu-latest
    steps:
      # (other steps before this: checkout code, install python and nox...)
      - run: nox -s "${{ matrix.nox_session }}"

但是如上所示,必须在矩阵中手动复制会话列表。因此,如果我决定更改我的 nox 会话的参数化,则必须对其进行更新。

因此,让 github 操作工作流“询问 nox”以动态获取此列表会容易得多。我们该怎么做?

(回答我自己的问题,因为我找不到关于 SO 的条目)

感谢 this great post,我找到了一种方法来做到这一点。该解决方案基于两个步骤:

  1. 首先创建一个 nox 任务gha_list,它将打印给定基本会话名称的所有会话名称列表
  2. 然后将作业添加到 github 操作工作流,它将利用此任务动态获取列表,并将此列表注入后续作业的构建矩阵。

1。创建一个 nox 任务以打印所需的会话列表

让我们将此任务添加到我们的 noxfile.py:

import itertools
import json

@nox.session(python=False)
def gha_list(session):
    """(mandatory arg: <base_session_name>) Prints all sessions available for <base_session_name>, for GithubActions."""

    # get the desired base session to generate the list for
    if len(session.posargs) != 1:
        raise ValueError("This session has a mandatory argument: <base_session_name>")
    session_func = globals()[session.posargs[0]]

    # list all sessions for this base session
    try:
        session_func.parametrize
    except AttributeError:
        sessions_list = ["%s-%s" % (session_func.__name__, py) for py in session_func.python]
    else:
        sessions_list = ["%s-%s(%s)" % (session_func.__name__, py, param)
                         for py, param in itertools.product(session_func.python, session_func.parametrize)]

    # print the list so that it can be caught by GHA.
    # Note that json.dumps is optional since this is a list of string.
    # However it is to remind us that GHA expects a well-formatted json list of strings.
    print(json.dumps(sessions_list))

我们可以在终端测试它是否有效:

>>> nox -s gha_list -- tests

nox > Running session gha_list
["tests-3.7", "tests-3.8"]
nox > Session gha_list was successful.

>>> nox -s gha_list -- tests_with_params

nox > Running session gha_list
["tests_with_params-3.7(b=False, a=-1)", "tests_with_params-3.7(b=True, a=-1)", "tests_with_params-3.7(b=False, a=1)", "tests_with_params-3.7(b=True, a=1)", "tests_with_params-3.8(b=False, a=-1)", "tests_with_para
ms-3.8(b=True, a=-1)", "tests_with_params-3.8(b=False, a=1)", "tests_with_params-3.8(b=True, a=1)"]
nox > Session gha_list was successful.

2。编辑 github 操作工作流程以动态地将此列表注入构建矩阵

现在我们能够打印所需的列表,我们修改 github 操作工作流程以添加一个调用它的作业 before 作业 运行测试。

# (in .github/workflows/base.yml)
jobs:
  list_nox_test_sessions:
    runs-on: ubuntu-latest
    steps:
        # (other steps before this: checkout code, install python and nox...)

        - name: list all test sessions
          id: set-matrix
          run: echo "::set-output name=matrix::$(nox -s gha_list -- tests)"

    # save the list into the outputs
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}

  run_all_tests:
    needs: list_nox_test_sessions
    strategy:
      fail-fast: false
      matrix:
        nox_session: ${{ fromJson(needs.list_nox_test_sessions.outputs.matrix) }}
    name: Run nox session ${{ matrix.nox_session }}
    runs-on: ubuntu-latest
    steps:
      # (other steps before this: checkout code, install python)
      - run: nox -s "${{ matrix.nox_session }}"

例子

可在 this repo 找到示例,工作流程如下:

其他可行的选择

我还考虑过在 nox --list 输出上使用 grep,这可能也有效。但是,对于那些对 python 感到放心但对 grep.

感到不安的开发人员来说,调试起来可能会更加困难。