From 9a0fbe63d865a1a7cabc9a1462bae25b33fd11c6 Mon Sep 17 00:00:00 2001
From: Teake Nutma <t.a.nutma@rug.nl>
Date: Tue, 14 Jun 2022 12:39:06 +0200
Subject: [PATCH] Add report_changes job

---
 .gitlab-ci.yml                                | 44 ++++++++--------
 README.rst                                    |  3 +-
 dockerfiles/ci-tools/Dockerfile               |  6 ++-
 .../testbadge.py => ci-tools/report_badge.py} |  0
 dockerfiles/ci-tools/report_compare.py        | 50 +++++++++++++++++++
 dockerfiles/testbadge/Dockerfile              |  9 ----
 templates/{testbadge.yml => report/badge.yml} | 10 ++--
 templates/report/changes.yml                  | 20 ++++++++
 8 files changed, 104 insertions(+), 38 deletions(-)
 rename dockerfiles/{testbadge/testbadge.py => ci-tools/report_badge.py} (100%)
 create mode 100755 dockerfiles/ci-tools/report_compare.py
 delete mode 100644 dockerfiles/testbadge/Dockerfile
 rename templates/{testbadge.yml => report/badge.yml} (58%)
 create mode 100644 templates/report/changes.yml

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index a60f364..35fd1ef 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -14,25 +14,25 @@ include:
   - local: '/templates/shared/all.yml'
   - local: '/templates/changedfiles/all.yml'
 
-#build_containers:
-#  stage: build
-#  # Note: the docker-builder image has to be build and pushed manually once to bootstrap this job.
-#  image: ${CI_REGISTRY_IMAGE}/docker-builder:master
-#  rules:
-#    - !reference [.primary_ref_jobs, rules]
-#    - !reference [.merge_request_jobs, rules]
-#  script:
-#    # Find the directories that contain a file named 'Dockerfile', and (re)build the ones
-#    # that contain changed files.
-#    - |
-#      find . -name Dockerfile -type f | while read FILE;
-#      do
-#          # Strip leading './' and trailing '/<filename>'
-#          DOCKER_DIR=$(echo "${FILE}" | sed -r 's|^\./||' | xargs dirname)
-#          if grep -q "^${DOCKER_DIR}" changed_files.log;
-#          then
-#              echo "Detected a changed file inside ./${DOCKER_DIR}/. (Re)build the container."
-#              IMAGE_NAME=$(echo "${DOCKER_DIR}" | sed -r 's|^dockerfiles/||')
-#              buildimage "${DOCKER_DIR}" "${IMAGE_NAME}:${CI_COMMIT_REF_SLUG}"
-#          fi
-#      done
+build_containers:
+  stage: build
+  # Note: the docker-builder image has to be build and pushed manually once to bootstrap this job.
+  image: ${CI_REGISTRY_IMAGE}/docker-builder:master
+  rules:
+    - !reference [.primary_ref_jobs, rules]
+    - !reference [.merge_request_jobs, rules]
+  script:
+    # Find the directories that contain a file named 'Dockerfile', and (re)build the ones
+    # that contain changed files.
+    - |
+      find . -name Dockerfile -type f | while read FILE;
+      do
+          # Strip leading './' and trailing '/<filename>'
+          DOCKER_DIR=$(echo "${FILE}" | sed -r 's|^\./||' | xargs dirname)
+          if grep -q "^${DOCKER_DIR}" changed_files.log;
+          then
+              echo "Detected a changed file inside ./${DOCKER_DIR}/. (Re)build the container."
+              IMAGE_NAME=$(echo "${DOCKER_DIR}" | sed -r 's|^dockerfiles/||')
+              buildimage "${DOCKER_DIR}" "${IMAGE_NAME}:${CI_COMMIT_REF_SLUG}"
+          fi
+      done
diff --git a/README.rst b/README.rst
index bde3f7f..b523dd8 100644
--- a/README.rst
+++ b/README.rst
@@ -307,7 +307,8 @@ a job, respectively. The full list of variables is as follows:
 `latex_pdf_diff`     CI_AWE_SKIP_LATEX_PDF_DIFF   CI_AWE_RUN_LATEX_PDF_DIFF   `templates/latex.yml`
 `sonar_branch`       CI_AWE_SKIP_SONAR_BRANCH     CI_AWE_RUN_SONAR_BRANCH     `templates/sonarqube.yml`
 `sonar_mr`           CI_AWE_SKIP_SONAR_MR         CI_AWE_RUN_SONAR_MR         `templates/sonarqube.yml`
-`test_badge`         CI_AWE_SKIP_TEST_BADGE       CI_AWE_RUN_TEST_BADGE       `templates/testbadge.yml`
+`report_badge`       CI_AWE_SKIP_REPORT_BADGE     CI_AWE_RUN_REPORT_BADGE     `templates/report/badge.yml`
+`report_changes`     CI_AWE_SKIP_REPORT_CHANGES   CI_AWE_RUN_REPORT_CHANGES   `templates/report/changes.yml`
 
 In addition, you can use the ``CI_AWE_SKIP_ALL` and ``CI_AWE_RUN_ALL` variables to
 control whether any or all of these jobs run.
diff --git a/dockerfiles/ci-tools/Dockerfile b/dockerfiles/ci-tools/Dockerfile
index 36d557a..5a56838 100644
--- a/dockerfiles/ci-tools/Dockerfile
+++ b/dockerfiles/ci-tools/Dockerfile
@@ -10,12 +10,16 @@ RUN apt-get update && apt-get -y install \
     gettext \
     && apt-get clean
 # Install via pip
+# https://github.com/weiwei/junitparser/pull/89 is in junitparser 2.6.0. \
+# https://github.com/weiwei/junitparser/pull/90 is not yet merged :(.
 RUN pip install \
     black==22.3.0 \
     autopep8 \
     python-gitlab \
     python-compare-ast \
-    coverage-fixpaths
+    coverage-fixpaths \
+    anybadge \
+    "junitparser>=2.6"
 
 # Install Gitlab release-cli
 RUN curl --location --output /usr/local/bin/release-cli "https://gitlab.com/gitlab-org/release-cli/-/releases/permalink/latest/downloads/bin/release-cli-linux-amd64" \
diff --git a/dockerfiles/testbadge/testbadge.py b/dockerfiles/ci-tools/report_badge.py
similarity index 100%
rename from dockerfiles/testbadge/testbadge.py
rename to dockerfiles/ci-tools/report_badge.py
diff --git a/dockerfiles/ci-tools/report_compare.py b/dockerfiles/ci-tools/report_compare.py
new file mode 100755
index 0000000..fea475b
--- /dev/null
+++ b/dockerfiles/ci-tools/report_compare.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python3
+
+import argparse
+
+from junitparser import JUnitXml, TestSuite
+
+
+def compare_reports(path_before, path_after, path_out):
+    before = JUnitXml.fromfile(path_before)
+    after = JUnitXml.fromfile(path_after)
+
+    passed = []
+    for suite in before:
+        for case in suite:
+            if case.is_passed:
+                passed.append(case)
+
+    failed = []
+    for suite in after:
+        for case in suite:
+            if not case.is_passed and not case.is_skipped:
+                failed.append(case)
+
+    newly_failed = []
+    for f in failed:
+        for p in passed:
+            if (f.classname, f.name) == (p.classname, p.name):
+                newly_failed.append(f)
+
+    xml = JUnitXml()
+    suite = TestSuite('newly_failed_tests')
+    suite.add_testcases(newly_failed)
+    xml.add_testsuite(suite)
+    xml.update_statistics()
+    xml.write(path_out, to_console=False)
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        description='Create a new report with testcases that passed before but fail or error now.'
+    )
+    parser.add_argument("before", help="Path of the initial XML report to compare.")
+    parser.add_argument("after", help="Path of the re-run XML report to compare.")
+    parser.add_argument("output", help='Path to write compare report to.')
+    args = parser.parse_args()
+    compare_reports(args.before, args.after, args.output)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/dockerfiles/testbadge/Dockerfile b/dockerfiles/testbadge/Dockerfile
deleted file mode 100644
index 9c03df6..0000000
--- a/dockerfiles/testbadge/Dockerfile
+++ /dev/null
@@ -1,9 +0,0 @@
-FROM python:3-slim
-
-# https://github.com/weiwei/junitparser/pull/89 is in junitparser 2.6.0.
-# https://github.com/weiwei/junitparser/pull/90 is not yet merged :(.
-RUN pip install anybadge "junitparser>=2.6"
-
-COPY testbadge.py /usr/local/bin/testbadge
-
-CMD ["/bin/bash"]
diff --git a/templates/testbadge.yml b/templates/report/badge.yml
similarity index 58%
rename from templates/testbadge.yml
rename to templates/report/badge.yml
index 97eed28..afb1a5c 100644
--- a/templates/testbadge.yml
+++ b/templates/report/badge.yml
@@ -1,17 +1,17 @@
 include:
   - local: '/templates/shared/all.yml'
 
-test_badge:
-  image: ${CI_AWE_IMAGE_BASE}/testbadge:${CI_AWE_IMAGE_TAG}
+report_badge:
+  image: ${CI_AWE_IMAGE_BASE}/ci-tools:${CI_AWE_IMAGE_TAG}
   stage: test_post
   rules:
-    - if: $CI_AWE_SKIP_TEST_BADGE
+    - if: $CI_AWE_SKIP_REPORT_BADGE
       when: never
-    - if: $CI_AWE_RUN_TEST_BADGE
+    - if: $CI_AWE_RUN_REPORT_BADGE
     - !reference [.primary_ref_jobs, rules]
     - !reference [.merge_request_jobs, rules]
   script:
-    - testbadge report.xml report.svg
+    - report_badge report.xml report.svg
   artifacts:
     paths:
       - report.svg
diff --git a/templates/report/changes.yml b/templates/report/changes.yml
new file mode 100644
index 0000000..f3edf84
--- /dev/null
+++ b/templates/report/changes.yml
@@ -0,0 +1,20 @@
+report_changes:
+  image: ${CI_AWE_IMAGE_BASE}/ci-tools:${CI_AWE_IMAGE_TAG}
+  variables:
+    REPORT_CHANGES_JOB: conda_test
+    REPORT_CHANGES_ARTIFACT_FILE: report.xml
+    REPORT_CHANGES_ARTIFACT_URL: "$CI_API_V4_URL/projects/$CI_PROJECT_ID/jobs/artifacts/$CI_COMMIT_REF_NAME/raw/$REPORT_CHANGES_ARTIFACT_FILE?job=$REPORT_CHANGES_JOB"
+  stage: test_post
+  rules:
+    - if: $CI_AWE_SKIP_REPORT_CHANGES
+      when: never
+    - if: $CI_AWE_RUN_REPORT_CHANGES
+    - !reference [.primary_ref_jobs, rules]
+  script:
+    - 'curl --output "$REPORT_CHANGES_FILE.old" --location --header "JOB-TOKEN: $CI_JOB_TOKEN" "$REPORT_CHANGES_ARTIFACT_URL"'
+    - report_compare "$REPORT_CHANGES_FILE.old" "$REPORT_CHANGES_ARTIFACT_FILE" report_changes.xml
+    - junitparser verify report_changes.xml
+  artifacts:
+    expire_in: 1 day
+    reports:
+      junit: report_changes.xml
-- 
GitLab