Commit 8cb964fd authored by Duggan, John's avatar Duggan, John
Browse files

Add issue manager

parent 1502c889
Loading
Loading
Loading
Loading

.gitlab-ci.yml

0 → 100644
+34 −0
Original line number Diff line number Diff line
stages:
  - manage-issues

variables:
  GIT_STRATEGY: clone
  CONTAINER_URL: "${NDIP_DOCKER_REPOSITORY}/${CI_PROJECT_PATH}"

# This import is for the func_rse_docker_* functions
before_script:
  - curl https://code.ornl.gov/rse-deployment/rse-sharables/raw/master/rse-bash-modules.sh -O
  - docker login -u $NDIP_DOCKER_USER -p $NDIP_DOCKER_PASSWORD $NDIP_DOCKER_REPOSITORY
  - source rse-bash-modules.sh
  - func_rse_docker_cleanup

after_script:
  - curl https://code.ornl.gov/rse-deployment/rse-sharables/raw/master/rse-bash-modules.sh -O
  - source rse-bash-modules.sh
  - func_rse_docker_cleanup
  - sudo chown -R gitlab-runner .

manage-issues:
  stage: manage-issues
  script:
    - docker build -f Dockerfile -t issue_manager .
    # Run issue manager
    - >
      docker run
      --rm
      -e GITLAB_ISSUES_API_ENDPOINT=${GITLAB_ISSUES_API_ENDPOINT}
      -e GITLAB_ACCESS_TOKEN=${GITLAB_ACCESS_TOKEN}
      issue_manager python manage_issues.py
  when: manual
  tags:
    - rse-multi-builder

.vscode/settings.json

0 → 100644
+3 −0
Original line number Diff line number Diff line
{
    "python-envs.defaultEnvManager": "ms-python.python:system"
}
 No newline at end of file

Dockerfile

0 → 100644
+16 −0
Original line number Diff line number Diff line
FROM --platform=amd64 regproxy.ornl.gov/hub_proxy/ubuntu:noble

RUN apt-get update && apt-get upgrade -y && apt-get install -y \
    bash curl

COPY . /src
WORKDIR /src

RUN curl -fsSL https://pixi.sh/install.sh | sh
ENV PATH="/root/.pixi/bin:${PATH}"
RUN pixi install
SHELL [ "pixi", "run" ]
ENTRYPOINT [ "pixi", "run" ]

RUN mkdir -p /.cache
RUN chmod og+rwX -R /.cache /src

README.md

0 → 100644
+13 −0
Original line number Diff line number Diff line
# NDIP Ticketing System

This project holds tickets for [NDIP](https://ndip.ornl.gov). You can find existing tickets (open and closed) in the work items section of this project.

## Creating a Ticket

Generally, we ask that you create a ticket via NDIP or the NOVA Dashboard directly via their issue reporting mechanisms. If you are ORNL staff, though, you should be able to create a work item to open a new ticket.

If you are an external user, then in addition to the issue reporting mechanisms you can also contact us via email at [ndip@ornl.gov](mailto:ndip@ornl.gov).

## Closing Tickets

Once a ticket is waiting on user feedback, we will close the ticket automatically after 14 days of inactivity.

manage_issues.py

0 → 100644
+61 −0
Original line number Diff line number Diff line
"""Class for automatic management of NDIP tickets."""

import os
from datetime import UTC
from datetime import datetime as dt
from typing import Any, Dict, List

from requests import get, post, put


class IssueManager:
    """Class for automatic management of NDIP tickets."""

    def __init__(self) -> None:
        """Configure IssueManager via environment variables and start management checks."""
        self.endpoint = os.getenv("GITLAB_ISSUES_API_ENDPOINT")
        self.access_token = os.getenv("GITLAB_ACCESS_TOKEN")
        self.warning_text = (
            "This issue has been waiting on feedback for one week. It will auto-close in 7 days if no more activity is "
            "recorded."
        )

        self.check_issues()

    def check_issues(self) -> None:
        issues = self.get_issues()
        for issue in issues:
            issue_iid = issue["iid"]

            updated_at = dt.fromisoformat(issue["updated_at"])
            now = dt.now(UTC)
            difference_in_days = (now - updated_at).days
            if difference_in_days != 7:
                continue

            last_note = get(
                f"{self.endpoint}/{issue_iid}/notes?sort=desc&order_by=created_at",
                headers={"PRIVATE-TOKEN": self.access_token},
            )
            last_note_text = last_note.json()[0]["body"]
            if last_note_text != self.warning_text:
                self.warn_about_closure(issue_iid)
            else:
                self.close_issue(issue_iid)

    def close_issue(self, issue_iid: str) -> None:
        put(f"{self.endpoint}/{issue_iid}?state_event=close", headers={"PRIVATE-TOKEN": self.access_token})

    def get_issues(self) -> List[Dict[str, Any]]:
        response = get(f"{self.endpoint}?labels=Waiting+on+Feedback&state=opened")

        return response.json()

    def warn_about_closure(self, issue_iid: str) -> None:
        post(
            f"{self.endpoint}/{issue_iid}/notes?body={self.warning_text}", headers={"PRIVATE-TOKEN": self.access_token}
        )


if __name__ == "__main__":
    IssueManager()
Loading