diff --git a/.github/workflows/test_action.yml b/.github/workflows/test_action.yml new file mode 100644 index 00000000..00240bab --- /dev/null +++ b/.github/workflows/test_action.yml @@ -0,0 +1,24 @@ +name: test-action + +on: + pull_request: + workflow_dispatch: + +jobs: + test-action: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + # Build your Docker image. + + - name: Run QUIC Interop tests + uses: ./ + with: + name: 'neqo-latest' + # Replace with the name of the freshly-build local docker image. + image: 'ghcr.io/mozilla/neqo-qns:latest' + url: https://github.com/mozilla/neqo + test: handshake + client: neqo-latest,quic-go,ngtcp2,neqo,msquic + server: neqo-latest,quic-go,ngtcp2,neqo,msquic diff --git a/.github/workflows/test_action_comment.yml b/.github/workflows/test_action_comment.yml new file mode 100644 index 00000000..547c9b56 --- /dev/null +++ b/.github/workflows/test_action_comment.yml @@ -0,0 +1,56 @@ +# Post test results as pull request comment. +# +# This is done as a separate workflow as it requires write permissions. The +# tests itself might run off of a fork, i.e. an untrusted environment and should +# thus not be granted write permissions. + +name: test-action-comment + +on: + workflow_run: + workflows: ["test-action"] + types: + - completed + +jobs: + comment: + permissions: + pull-requests: write + runs-on: ubuntu-latest + if: > + github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.conclusion == 'failure' + steps: + - name: Download comment-data + uses: actions/download-artifact@v4 + with: + run-id: ${{ github.event.workflow_run.id }} + name: comment-data + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Format GitHub comment + run: | + pwd + ls -la + echo '[**QUIC Interop Runner**](https://github.com/quic-interop/quic-interop-runner)' >> comment + echo '' >> comment + echo '```' >> comment + cat summary >> comment + echo '```' >> comment + echo '' >> comment + echo 'Download artifacts [here](' >> comment + cat logs-url >> comment + echo ').' >> comment + shell: bash + + - name: Read PR Number + id: pr-number + run: echo "::set-output name=number::$(cat pr-number)" + shell: bash + + - name: Comment PR + uses: thollander/actions-comment-pull-request@v2 + with: + filePath: comment + pr_number: ${{ steps.pr-number.outputs.number }} + comment_tag: quic-network-simulator-comment diff --git a/action.yml b/action.yml new file mode 100644 index 00000000..34a1ce46 --- /dev/null +++ b/action.yml @@ -0,0 +1,113 @@ +name: 'QUIC Interop Runner Action' +description: 'Run the QUIC Interop Runner tests.' +author: 'mxinden' + +inputs: + name: + description: 'Name of the QUIC implementation' + required: true + image: + description: 'Docker image to be tested. Needs to reside either locally, or on some registry.' + required: true + url: + description: 'URL of the QUIC implementation' + required: true + role: + description: 'client/server/both' + required: false + default: 'both' + client: + description: 'client implementations (comma-separated)' + required: false + default: '' + server: + description: 'server implementations (comma-separated)' + required: false + default: '' + test: + description: 'test cases (comma-separatated)' + required: false + default: '' + +runs: + using: "composite" + steps: + # TODO: Update repository and ref + - name: Checkout quic-interop/quic-interop-runner repository + uses: actions/checkout@v4 + with: + repository: 'mxinden/quic-interop-runner' + ref: 'action' + path: 'quic-interop-runner' + + - name: Enable IPv6 support + run: sudo modprobe ip6table_filter + shell: bash + + - name: Install dependencies + run: | + sudo add-apt-repository ppa:wireshark-dev/stable + sudo apt-get update + sudo apt-get install -y wireshark tshark jq + shell: bash + + - uses: actions/setup-python@v5 + with: + python-version: 3.8 + cache: 'pip' + cache-dependency-path: 'quic-interop-runner/requirements.txt' + + - name: Install Python packages + run: | + cd quic-interop-runner + pip install -U pip + pip install -r requirements.txt + shell: bash + + - name: Run tests + id: test-run + run: | + cd quic-interop-runner + jq --arg key "${{ inputs.name }}" --argjson newEntry '{"image": "${{ inputs.image }}", "url": "${{ inputs.url }}", "role": "${{ inputs.role }}"}' '.[$key] = $newEntry' implementations.json > temp.$$ && mv temp.$$ implementations.json + cat implementations.json + ARGS="--log-dir logs --markdown --must-include ${{ inputs.name }}" + if [ -n "${{ inputs.client }}" ]; then + ARGS="$ARGS --client ${{ inputs.client }}" + fi + if [ -n "${{ inputs.server }}" ]; then + ARGS="$ARGS --server ${{ inputs.server }}" + fi + if [ -n "${{ inputs.test }}" ]; then + ARGS="$ARGS --test ${{ inputs.test }}" + fi + python run.py $ARGS 2>&1 | tee summary + shell: bash + + - uses: actions/upload-artifact@v4 + id: artifact-upload-step + if: always() + with: + name: logs + path: quic-interop-runner/logs + + # This action might be running off of a fork and would thus not have write + # permissions on the origin repository. In order to allow a separate + # priviledged action to post a comment on a pull request, upload the + # necessary metadata. + - name: store comment-data + shell: bash + if: github.event_name == 'pull_request' + env: + PULL_REQUEST_NUMBER: ${{ github.event.number }} + run: | + mkdir comment-data + cat quic-interop-runner/summary | awk '/^\|:--/{flag=1} flag' >> comment-data/summary + echo $PULL_REQUEST_NUMBER > comment-data/pr-number + echo '${{ steps.artifact-upload-step.outputs.artifact-url }}' > comment-data/logs-url + + - name: Upload comment data + uses: actions/upload-artifact@v4 + if: github.event_name == 'pull_request' + with: + name: comment-data + path: ./comment-data diff --git a/interop.py b/interop.py index 0a7126d7..8f2769b8 100644 --- a/interop.py +++ b/interop.py @@ -240,7 +240,7 @@ def get_letters(result): results.append(colored(measurement.abbreviation(), "grey")) elif res.result == TestResult.FAILED: results.append(colored(measurement.abbreviation(), "red")) - row[server] += "\n".join(results) + row[server] = "\n".join(results) t.field_names = [""] + [column for column, _ in columns.items()] for client, results in rows.items(): row = [client]