Skip to content

Commit

Permalink
feat: Use event payload to communicate between ci and post processing
Browse files Browse the repository at this point in the history
  • Loading branch information
FHeilmann committed Jan 5, 2024
1 parent a21133b commit 29da267
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 94 deletions.
45 changes: 13 additions & 32 deletions .github/workflows/test_pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,56 +4,32 @@ on:
pull_request:
types: [opened, reopened, synchronize, labeled]
env:
VORON_TOOLKIT_OUTPUT_DIR: /workflow_output
VORON_TOOLKIT_OUTPUT_DIR: ${{ github.workspace }}/workflow_output
VORON_TOOLKIT_INPUT_DIR: ${{ github.workspace }}/tests/test_repository_root/printer_mods
VORON_TOOLKIT_GH_STEP_SUMMARY: true
VORON_TOOLKIT_VERBOSE: true
GITHUB_EVENT_CONTEXT: ${{ toJson(github.event) }}
jobs:
voron_ci_dismiss_labels:
# If the PR was opened, reopened or new commits were pushed, dismiss all labels during post processing
if: ${{ github.event_name == 'pull_request' && github.event.action != 'labeled' }}
runs-on: ubuntu-latest
steps:
- name: Dismiss all CI labels 🚮
id: dismiss-labels
run: |
mkdir -p ${{ env.VORON_TOOLKIT_OUTPUT_DIR }}
echo -n '{"pr_number": ${{ github.event.number }}, "action": "dismiss_labels"}' > ${{ env.VORON_TOOLKIT_OUTPUT_DIR }}/ci_result.json
echo -n ${{ github.event }} > ${{ env.VORON_TOOLKIT_OUTPUT_DIR }}/event.json
# Upload Artifact
- name: Upload build artifacts 📦
uses: actions/upload-artifact@65d862660abb392b8c4a3d1195a2108db131dd05
with:
name: ci_output
path: ${{ env.VORON_TOOLKIT_OUTPUT_DIR }}
voron_ci_skip:
# The PR was labeled with any label OTHER than "Ready for CI", which means we should skip the CI run and the post processing
if: ${{ github.event_name == 'pull_request' && github.event.action == 'labeled' && !contains( github.event.pull_request.labels.*.name, 'Ready for CI')}}
if: ${{ github.event.action != 'labeled' || !contains( github.event.pull_request.labels.*.name, 'Ready for CI')}}
runs-on: ubuntu-latest
steps:
- name: Skip CI ⏩
id: skip-ci
- name: Save Github Event Payload 💾
id: save-payload
run: |
mkdir -p ${{ env.VORON_TOOLKIT_OUTPUT_DIR }}
echo -n '{"pr_number": ${{ github.event.number }}, "action": "skip"}' > ${{ env.VORON_TOOLKIT_OUTPUT_DIR }}/ci_result.json
echo -n ${{ github.event }} > ${{ env.VORON_TOOLKIT_OUTPUT_DIR }}/event.json
echo -n "$GITHUB_EVENT_CONTEXT" >> ${{ env.VORON_TOOLKIT_OUTPUT_DIR }}/event.json
# Upload Artifact
- name: Upload build artifacts 📦
uses: actions/upload-artifact@65d862660abb392b8c4a3d1195a2108db131dd05
if: '!cancelled()'
with:
name: ci_output
path: ${{ env.VORON_TOOLKIT_OUTPUT_DIR }}
voron_ci:
# The PR was labeled with the "Ready for CI label", which menas we should run the CI and post processing
if: ${{ github.event_name == 'pull_request' && github.event.action == 'labeled' && contains( github.event.pull_request.labels.*.name, 'Ready for CI')}}
if: ${{ github.event.action == 'labeled' && contains( github.event.pull_request.labels.*.name, 'Ready for CI')}}
runs-on: ubuntu-latest
steps:
- name: Save PR number 💾
id: run-ci
run: |
mkdir -p ${{ env.VORON_TOOLKIT_OUTPUT_DIR }}
echo -n '{"pr_number": ${{ github.event.number }}, "action": "post_process"}' > ${{ env.VORON_TOOLKIT_OUTPUT_DIR }}/ci_result.json
echo -n ${{ github.event }} > ${{ env.VORON_TOOLKIT_OUTPUT_DIR }}/event.json
- id: changed-files
if: ${{ !cancelled() }}
name: Get changed files 🔀
Expand All @@ -79,6 +55,11 @@ jobs:
path: ${{ github.workspace }}
sparse-checkout: ${{ steps.sanitize_file_list.outputs.SPARSE_CHECKOUT_HELPER_OUTPUT }}
sparse-checkout-cone-mode: false
- name: Save Github Event Payload💾
id: save-payload
run: |
mkdir -p ${{ env.VORON_TOOLKIT_OUTPUT_DIR }}
echo -n "$GITHUB_EVENT_CONTEXT" >> ${{ env.VORON_TOOLKIT_OUTPUT_DIR }}/event.json
# Run whitespace/licenses/file sizes based on files in the test directory
- name: Check files for whitespace/licenses/file sizes 🔍
if: ${{ !cancelled() }}
Expand Down
28 changes: 21 additions & 7 deletions voron_toolkit/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,31 @@
import os
from collections import defaultdict
from dataclasses import dataclass
from enum import Enum
from enum import Enum, StrEnum
from typing import Any, NamedTuple, Self

CI_PASSED_LABEL: str = "CI: Passed"
CI_FAILURE_LABEL: str = "CI: Issues identified"
CI_ERROR_LABEL: str = "Warning: CI Error"
READY_FOR_CI_LABEL: str = "Ready for CI"
READY_TO_MERGE_LABEL: str = "Ready to merge"
LABEL_CI_PASSED: str = "CI: Passed"
LABEL_CI_ISSUES_FOUND: str = "CI: Issues identified"
LABEL_CI_ERROR: str = "Warning: CI Error"
LABEL_READY_FOR_CI: str = "Ready for CI"
LABEL_READY_TO_MERGE: str = "Ready to merge"
PR_COMMENT_TAG: str = "<!-- voron_docker_toolkit -->"
PR_COMMENT_TOOLKIT_VERSION: str = f"<!-- Toolkit version {os.environ.get('VORON_TOOLKIT_VERSION', '<unknown>')} -->"
ALL_CI_LABELS: list[str] = [CI_PASSED_LABEL, CI_FAILURE_LABEL, CI_ERROR_LABEL, READY_FOR_CI_LABEL, READY_TO_MERGE_LABEL]
LABELS_CI_ALL: list[str] = [LABEL_CI_PASSED, LABEL_CI_ISSUES_FOUND, LABEL_CI_ERROR, LABEL_READY_FOR_CI, LABEL_READY_TO_MERGE]


class PrAction(StrEnum):
opened = "opened"
edited = "edited"
closed = "closed"
reopened = "reopened"
assigned = "assigned"
unassigned = "unassigned"
review_requested = "review_requested"
review_request_removed = "review_request_removed"
labeled = "labeled"
unlabeled = "unlabeled"
synchronize = "synchronize"


class ExtendedResult(NamedTuple):
Expand Down
113 changes: 58 additions & 55 deletions voron_toolkit/voronuser_utils/pr_helper.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import json
import os
import sys
import tempfile
from collections import defaultdict
from pathlib import Path
Expand All @@ -10,13 +9,15 @@
from loguru import logger

from voron_toolkit.constants import (
ALL_CI_LABELS,
CI_ERROR_LABEL,
CI_FAILURE_LABEL,
CI_PASSED_LABEL,
LABEL_CI_ERROR,
LABEL_CI_ISSUES_FOUND,
LABEL_CI_PASSED,
LABEL_READY_FOR_CI,
LABELS_CI_ALL,
VORONUSERS_PR_COMMENT_SECTIONS,
ExtendedResultEnum,
ItemResult,
PrAction,
ToolIdentifierEnum,
ToolResult,
ToolSummaryTable,
Expand Down Expand Up @@ -132,12 +133,12 @@ def _parse_artifact_and_get_labels(self: Self) -> set[str]:
Path(self.tmp_path, pr_step_identifier.tool_id).exists(),
Path(self.tmp_path, pr_step_identifier.tool_id, "tool_result.json").exists(),
)
labels.add(CI_ERROR_LABEL)
labels.add(LABEL_CI_ERROR)
continue
ci_step_result = ToolResult.from_json(Path(self.tmp_path, pr_step_identifier.tool_id, "tool_result.json").read_text())
result_ok = ExtendedResultEnum.WARNING if ci_step_result.tool_ignore_warnings else ExtendedResultEnum.SUCCESS
if ci_step_result.extended_result > result_ok:
labels.add(CI_FAILURE_LABEL)
labels.add(LABEL_CI_ISSUES_FOUND)
logger.success(
"Parsed result for tool {}: Result: {}, Ignore Warnings: {}",
pr_step_identifier,
Expand All @@ -147,19 +148,47 @@ def _parse_artifact_and_get_labels(self: Self) -> set[str]:
self.tool_results[pr_step_identifier] = ci_step_result
if not labels:
logger.success("All CI checks executed without errors!")
labels.add(CI_PASSED_LABEL)
labels.add(LABEL_CI_PASSED)
logger.info("Labels: {}", labels)
return labels

def _get_ci_result(self: Self) -> dict[str, Any]:
if not Path(self.tmp_path, "ci_result.json").exists():
logger.error("Artifact is missing ci_result.json file!")
sys.exit(255)
ci_result_dct: dict[str, Any] = json.loads(Path(self.tmp_path, "ci_result.json").read_text())
if ("pr_number" not in ci_result_dct) or ("action" not in ci_result_dct):
logger.error("The ci_result.json file is missing the 'pr_number' or 'ci_skipped' key!")
sys.exit(255)
return ci_result_dct
def _post_process_pr(self: Self, pr_number: int, pr_action: str) -> None:
logger.info("Post Processing PR #{}, action: {}", pr_number, pr_action)
labels_to_set: set[str] = self._parse_artifact_and_get_labels()
labels_on_pr: list[str] = GithubActionHelper.get_labels_on_pull_request(
repo=self.github_repository,
pull_request_number=pr_number,
)
labels_to_preserve: list[str] = [label for label in labels_on_pr if label not in LABELS_CI_ALL]

updated_labels: list[str] = [*labels_to_set, *labels_to_preserve]

GithubActionHelper.set_labels_on_pull_request(
repo=self.github_repository,
pull_request_number=pr_number,
labels=updated_labels,
)
pr_comment: str = self._generate_pr_comment()
GithubActionHelper.update_or_create_pr_comment(
repo=self.github_repository,
pull_request_number=pr_number,
comment_body=pr_comment,
)

def _dismiss_labels(self: Self, pr_number: int) -> None:
logger.info("PR #{} has new changes. Dismissing all CI labels!", pr_number)
GithubActionHelper.set_labels_on_pull_request(
repo=self.github_repository,
pull_request_number=pr_number,
labels=[
label
for label in GithubActionHelper.get_labels_on_pull_request(
repo=self.github_repository,
pull_request_number=pr_number,
)
if label not in LABELS_CI_ALL
],
)

def run(self: Self) -> None:
logger.info("Downloading artifact '{}' from workflow '{}'", self.artifact_name, self.workflow_run_id)
Expand All @@ -183,45 +212,19 @@ def run(self: Self) -> None:
)
return

ci_result: dict[str, Any] = self._get_ci_result()
pr_number: int = int(ci_result.get("pr_number", 0))
pr_action: str = ci_result.get("action", "skip")
logger.info("Post Processing PR #{}, action: {}", pr_number, pr_action)
if pr_number > 0 and pr_action == "post_process":
labels_to_set: set[str] = self._parse_artifact_and_get_labels()
labels_on_pr: list[str] = GithubActionHelper.get_labels_on_pull_request(
repo=self.github_repository,
pull_request_number=pr_number,
)
labels_to_preserve: list[str] = [label for label in labels_on_pr if label not in ALL_CI_LABELS]

updated_labels: list[str] = [*labels_to_set, *labels_to_preserve]
try:
event_payload: dict[str, Any] = json.loads(Path(self.tmp_path, "event.json").read_text())
pr_number: int = int(event_payload["pull_request"]["number"])
pr_action: str = event_payload["action"]
pr_labels: list[str] = [label["name"] for label in event_payload["pull_request"]["labels"]]
except (FileNotFoundError, KeyError) as e:
logger.error("Failed to parse event.json: {}", e)
return

GithubActionHelper.set_labels_on_pull_request(
repo=self.github_repository,
pull_request_number=pr_number,
labels=updated_labels,
)
pr_comment: str = self._generate_pr_comment()
GithubActionHelper.update_or_create_pr_comment(
repo=self.github_repository,
pull_request_number=pr_number,
comment_body=pr_comment,
)
elif pr_number > 0 and pr_action == "dismiss_labels":
logger.info("PR #{} has new commits. Dismissing all CI labels!", pr_number)
GithubActionHelper.set_labels_on_pull_request(
repo=self.github_repository,
pull_request_number=pr_number,
labels=[
label
for label in GithubActionHelper.get_labels_on_pull_request(
repo=self.github_repository,
pull_request_number=pr_number,
)
if label not in ALL_CI_LABELS
],
)
if LABEL_READY_FOR_CI in pr_labels and pr_action == PrAction.labeled:
self._post_process_pr(pr_number=pr_number, pr_action=pr_action)
elif pr_action != PrAction.labeled:
self._dismiss_labels(pr_number=pr_number)
else:
logger.info("Skipping post processing of PR #{}!", self.workflow_run_id, pr_number)

Expand Down

0 comments on commit 29da267

Please sign in to comment.