Allure Report history 유지, CI 캐시 설정법

3줄 요약
- Allure Report 2의 트렌드와 History 탭은 리포트를 만들 때 나오는
history폴더를 다음 실행의 결과 디렉터리로 되돌려 놓아야 이어집니다. CI에서 이 한 줄이 빠지면 리포트는 정상으로 보이는데 트렌드만 매번 1칸입니다. - 26회를 쌓은 상태에서 복사를 한 번 빠뜨려 보니 트렌드 20칸이 1칸으로,
statistic.total이 26에서 1로 돌아갔습니다. 되돌릴 방법은 이전 리포트 아티팩트를 다시 찾는 것뿐입니다. - CI 캐시나 GitHub Pages 브랜치로
history를 영속화하고, flaky율은 20개 상한이 걸린items가 아니라 무한 누적되는statistic으로 계산하시면 됩니다.
지난주에 팀 저장소에 Allure Report를 붙였습니다. pip install allure-pytest 하고 --alluredir을 붙이니 30분도 안 걸려 리포트가 떴습니다. 팀 슬랙에 링크를 올렸더니 반응이 좋았습니다.
그런데 다음 날 두 번째 실행이 끝난 리포트를 열었을 때, Overview의 트렌드 그래프에 막대가 하나만 서 있었습니다. 어제 것이 있어야 할 자리가 비어 있었습니다. History 탭은 "no history"였고, 제가 리포트를 붙인 진짜 이유였던 "이 테스트가 어제도 실패했나"는 여전히 알 수 없었습니다. 갈림길은 세 갈래였습니다. Allure 2를 쓰면서 history 폴더를 CI에서 직접 나르거나, Allure 3으로 올라가 JSONL 파일 하나로 관리하거나, 트렌드를 포기하고 CI 요약으로 끝내는 것입니다.
결론부터 적겠습니다. Allure 2를 쓰신다면 history 폴더를 CI에 영속화하는 작업이 도입의 절반입니다. 리포트를 띄우는 30분보다 이쪽이 오래 걸리고, 이 배관은 한 번만 새도 쌓은 이력이 통째로 사라집니다. 실험 환경은 allure-pytest 2.16.0, allure-commandline 2.43.0(내부 Allure 2), allure 3.16.0, OpenJDK 11.0.32입니다.
1. 리포트는 예쁘게 나왔는데 트렌드 칸이 하나였던 이유
Allure Report는 두 단계로 동작합니다. 어댑터(allure-pytest 같은 것)가 실행 중에 allure-results로 JSON 조각을 흘려 놓고, allure generate가 그 조각을 읽어 정적 HTML을 만듭니다. 여기까지는 한 번의 실행 안에서 끝나는 일이라 어제를 알 방법이 없습니다.
이력을 잇는 통로가 따로 있습니다. allure generate는 리포트를 만들 때 allure-report/history 폴더에 요약 데이터를 같이 씁니다. 다음 실행에서 이 폴더를 결과 디렉터리 쪽으로 되돌려 놓아야 그때의 allure generate가 이전 회차를 인식합니다. 공식 History and retries 문서의 다섯 단계가 결국 "이전 리포트의 history를 새 allure-results/history로 복사하라"는 얘기입니다.
제가 놓친 것이 이 지점입니다. CI 잡은 매번 깨끗한 워크스페이스에서 시작하니 복사할 원본 자체가 없었습니다. 그래도 리포트는 정상 생성되므로 파이프라인은 초록색이고 경고도 나오지 않습니다.
history 폴더에 들어 있는 다섯 개 파일
파일마다 담당하는 그래프가 다릅니다. 4회 실행 후 직접 확인한 목록입니다.
| 파일 | 무엇을 그리는가 | 한 항목의 핵심 필드 |
|---|---|---|
history.json |
테스트별 History 탭, Test statuses 차트 | statistic(상태별 누적 횟수), items(회차별 상세) |
history-trend.json |
Overview의 Trend 차트 | data.passed / failed / broken / skipped / total |
duration-trend.json |
Duration trend 차트 | data.duration |
retry-trend.json |
Retries trend 차트 | data.run, data.retry |
categories-trend.json |
Categories trend 차트 | 카테고리명별 건수 |
트렌드 네 개는 모두 buildOrder로 정렬되고, 이 값은 결과 디렉터리에 넣는 executor.json에서 옵니다. CI에서 이 파일을 만들어 주지 않으면 트렌드 x축 라벨이 비고 이전 회차로 넘어가는 링크도 생기지 않습니다. 저는 이것도 빼먹어서, 막대는 여러 개인데 어느 빌드인지 알 수 없는 상태를 잠깐 겪었습니다.
재현 스크립트 — 두 회차만 실패시켜 4번 돌리기
아래는 오늘 돌린 그대로입니다. FAIL_FLAKY=1일 때만 한 테스트가 실패하게 해 두고, 회차마다 history를 옮기면서 4번 돌립니다.
#!/usr/bin/env bash
# Allure 2 히스토리 누적 재현 — 결과 디렉터리는 매번 비우고 history만 되돌립니다
set -euo pipefail
run_once () {
local n=$1 fail=$2
rm -rf allure-results
# 핵심 한 줄: 이전 리포트의 history를 새 결과 디렉터리로 되돌립니다
if [ -d allure-report/history ]; then
mkdir -p allure-results && cp -r allure-report/history allure-results/history
fi
mkdir -p allure-results
# buildOrder가 없으면 트렌드 x축이 비고 이전 회차 링크도 안 생깁니다
cat > allure-results/executor.json <<EOF
{"name":"GitHub Actions","type":"github","buildOrder":$n,"reportName":"Report #$n"}
EOF
FAIL_FLAKY=$fail pytest -q --alluredir=allure-results || true
allure generate allure-results -o allure-report --clean
}
for i in 1 2 3 4; do
f=0; { [ "$i" = 2 ] || [ "$i" = 4 ]; } && f=1
run_once "$i" "$f"
done
4회차 리포트의 history-trend.json에는 buildOrder 4부터 1까지 네 항목이 내림차순으로 들어 있었고, 실패 회차는 failed: 1 / passed: 4, 성공 회차는 failed: 0 / passed: 5로 정확히 구분됐습니다. 배관이 붙으면 잘 동작합니다. 문제는 배관이 새는 경우입니다.
2. 첫 번째 함정 — 문서에 적힌 파일 이름과 단위가 실제와 다릅니다
트렌드가 붙은 다음 하려던 일은 "flaky율이 임계값을 넘으면 슬랙 알림"이었습니다. 그러려면 history 파일을 스크립트로 파싱해야 하는데, 문서를 보고 쓴 첫 스크립트가 두 곳에서 조용히 어긋났습니다. 예외가 나지 않고 값만 이상해진다는 점이 고약했습니다.
retries-trend.json이라는 파일은 없습니다
History files 문서는 목차에서 이 파일을 retry-trend.json으로 적어 두고, 바로 아래 본문에서는 "history/retries-trend.json 파일은…"이라고 씁니다. 두 표기가 한 페이지에 같이 있습니다. 26회를 돌린 뒤 디렉터리를 찍어 보니 실제로 생성되는 이름은 retry-trend.json 쪽이었습니다.
파싱 스크립트를 retries-trend.json으로 짜면 파일이 없어 예외가 나고, 그걸 try/except로 감싸 놓았다면 "retry 데이터가 없다"로 조용히 넘어갑니다. 저는 재시도 추이가 계속 0인 것을 한동안 실제로 재시도가 없는 것으로 알고 있었습니다.
duration은 초가 아니라 밀리초입니다
같은 문서가 duration-trend.json의 data.duration을 "number of seconds"라고, history.json의 time.duration을 "the difference in seconds"라고 설명합니다. time.sleep(2) 하나만 있는 테스트로 확인해 봤습니다.
duration-trend: [{"buildOrder":1,"reportName":"R1","data":{"duration":2008}}]
history.json : {'start': 1788390738732, 'stop': 1788390740740, 'duration': 2008}
2초를 잔 테스트가 2008로 나왔습니다. start와 stop이 이미 밀리초 단위 UNIX 타임스탬프이고 duration은 그 차이니, 당연히 밀리초입니다. 문서 표현이 잘못됐습니다.
| 항목 | 문서 표기 | 실측값 | 잘못 믿으면 |
|---|---|---|---|
| 재시도 추이 파일명 | 본문 retries-trend.json / 목차 retry-trend.json |
retry-trend.json |
파일 없음 → 재시도 추이가 항상 0 |
duration-trend.json의 duration |
"number of seconds" | 밀리초 (2초 → 2008) |
임계값 600을 초로 알고 걸면 0.6초에서 경보 |
history.json의 time.duration |
"difference in seconds" | 밀리초 | 느린 테스트 상위 목록이 1000배 어긋남 |
제 해석으로는 문서가 코드보다 늦게 갱신된 흔적입니다. 그래서 이후로는 history 파일을 다루는 스크립트를 쓸 때 첫 실행 결과를 눈으로 한 번 찍어 보고 필드 이름과 단위를 확정하는 순서로 바꿨습니다. 5분이면 끝나고, 위 같은 착각을 며칠씩 끌지 않습니다.
3. 가장 비싼 함정 — 복사 한 번을 빠뜨리면 누적이 통째로 사라집니다
위 스크립트를 26회까지 돌려 이력을 쌓았습니다. history-trend.json에는 항목 20개가 있었고 buildOrder는 26부터 7까지였습니다. 그 상태에서 27회차만 history 복사 줄을 빼고 돌렸습니다. 캐시 스텝을 잊었거나 캐시 키가 바뀌어 미스가 난 상황을 재현한 것입니다.
27회차 전 : trend 항목 20개, buildOrder 26 ~ 7
27회차 후 : trend 항목 1개, buildOrder [27]
history.json statistic → {'passed': 1, 'total': 1}, items 1개
20칸이 1칸이 됐습니다. statistic.total도 26에서 1로 돌아갔습니다. 리포트는 오류 없이 만들어지고 파이프라인은 초록색입니다. 다음 회차부터 다시 쌓이기는 하는데, 출발점이 0입니다.
되돌릴 방법은 하나뿐입니다. 26회차 리포트 아티팩트가 남아 있으면 그 안의 history 폴더를 꺼내 다시 심는 것입니다. Actions 아티팩트에는 보관 기간이 있고 Pages에 덮어쓰기로 배포했다면 이미 사라졌을 수 있습니다. 실제 저장소에서 이런 일이 나면 "지난 석 달 추이" 슬라이드가 그냥 없어집니다.
그래서 history를 어디에 둘 것인가
QA 4~6명, 자동화 초기 단계라는 조건에서는 CI 캐시가 가장 싸고, 결과를 URL로 열어 보게 만들려면 Pages 브랜치가 낫습니다.
| 방법 | 영속성 | 도입 난이도 | 주의할 점 |
|---|---|---|---|
actions/cache에 history만 저장 |
캐시 정리 정책에 따라 만료 가능 | 낮음 — 스텝 두 개 | 캐시 키를 브랜치별로 나누면 이력이 갈라집니다 |
| GitHub Pages 브랜치에 리포트 통째로 커밋 | 커밋 이력에 남아 사실상 영구 | 중간 — 배포 액션 필요 | 리포트 전체가 저장소에 쌓여 용량이 늡니다 |
| 아티팩트 업로드 후 다음 잡에서 내려받기 | 아티팩트 보관 기간까지 | 중간 | 보관 기간 만료가 곧 이력 소실 |
아래가 캐시 방식의 최소 구성입니다. 두 곳이 핵심입니다. restore-keys를 열어 두어 정확한 키가 없어도 최근 캐시를 집어 오게 하는 것, 그리고 if: always()로 테스트가 실패해도 캐시를 갱신하는 것입니다. 실패한 회차야말로 이력에 남아야 하는데, 기본값으로 두면 빠집니다.
# .github/workflows/test.yml — Allure 2 히스토리를 캐시로 영속화합니다
name: test
on: [push]
jobs:
pytest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with: { python-version: "3.12" }
- run: pip install pytest allure-pytest
# 1) 이전 회차의 history를 복원합니다. 정확한 키가 없어도 최근 것을 집어 옵니다
- name: allure history 복원
uses: actions/cache/restore@v4
with:
path: allure-history
key: allure-history-${{ github.ref_name }}-${{ github.run_number }}
restore-keys: |
allure-history-${{ github.ref_name }}-
# 2) 결과 디렉터리를 만들고 history와 executor.json을 심습니다
- name: 결과 디렉터리 준비
run: |
rm -rf allure-results && mkdir -p allure-results
[ -d allure-history ] && cp -r allure-history allure-results/history || true
cat > allure-results/executor.json <<EOF
{"name":"GitHub Actions","type":"github",
"buildOrder":${{ github.run_number }},
"reportName":"Report #${{ github.run_number }}",
"buildUrl":"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}
EOF
- name: 테스트 실행
run: pytest --alluredir=allure-results
continue-on-error: true
- name: 리포트 생성
if: always()
run: |
npx --yes allure-commandline generate allure-results -o allure-report --clean
rm -rf allure-history && cp -r allure-report/history allure-history
# 3) 실패한 회차도 이력에 남아야 하므로 always() 입니다
- name: allure history 저장
if: always()
uses: actions/cache/save@v4
with:
path: allure-history
key: allure-history-${{ github.ref_name }}-${{ github.run_number }}
여기서 쓴 npx allure-commandline에 대해 한 줄 덧붙입니다. 이 npm 패키지는 Allure 2를 감싼 래퍼인데 버전이 본체보다 뒤처집니다.
| 패키지 / 저장소 | 최신 버전 | 배포일 | 확인 경로 |
|---|---|---|---|
allure-framework/allure2 (본체) |
2.46.0 | 2026-08-28 | releases.atom |
npm allure-commandline (래퍼) |
2.43.0 | 2026-06-19 | 레지스트리 time 필드 |
npm allure (Allure 3) |
3.16.0 | 2026-08-25 | 레지스트리 time 필드 |
PyPI allure-pytest |
2.16.0 | 2026-04-27 | PyPI JSON API |
세 마이너 버전, 약 70일 차이입니다. 릴리스 자산을 직접 내려받는 쪽이 최신이지만 zip을 풀고 JRE를 챙기는 비용이 붙습니다. 저는 초기 단계 팀이라면 npx 쪽 편의가 70일치 차이보다 값지다고 봅니다.
4. 두 번째로 비싼 함정 — statistic과 items의 상한이 다릅니다
앞에서 트렌드가 20개에서 멈춘 이유를 미뤄 뒀습니다. 26회를 돌린 뒤 history.json을 열어 봤습니다.
# history.json에서 테스트별 누적을 뽑습니다. statistic과 items의 개수가 다릅니다
import json
h = json.load(open("allure-report/history/history.json"))
for history_id, v in h.items():
st, items = v["statistic"], v["items"]
# statistic.total = 이 테스트가 등장한 리포트 총 개수 (상한 없음)
# items = 회차별 상세 (최근 20개만 유지)
rate = st["failed"] / st["total"] if st["total"] else 0
print(f"{history_id[:12]} total={st['total']:3d} items={len(items):3d} "
f"passed={st['passed']:3d} failed={st['failed']:3d} 실패율={rate:.1%}")
출력은 이랬습니다.
| historyId | statistic.total |
items 개수 |
passed | failed | statistic 기준 실패율 |
|---|---|---|---|---|---|
737800f8741a |
26 | 20 | 26 | 0 | 0.0% |
85485d4daf9a |
26 | 20 | 26 | 0 | 0.0% |
797df7c69941 |
26 | 20 | 19 | 7 | 26.9% |
1ad76a8faa10 |
26 | 20 | 26 | 0 | 0.0% |
60eb0e799019 |
26 | 20 | 26 | 0 | 0.0% |
statistic.total은 26까지 올라가는데 items는 20에서 멈춥니다. 문서가 "up to 20 latest reports"라고 적어 둔 상한이 items와 트렌드 배열에만 걸려 있고 statistic에는 걸려 있지 않습니다. 같은 파일에서 flaky율을 뽑는데도 어느 필드를 쓰느냐로 값이 달라집니다.
flaky율을 어느 쪽으로 계산해야 하는가
둘 다 쓸 수 있고 뜻이 다릅니다. statistic은 이력을 쌓기 시작한 순간부터의 전체 평균이라 최근에 고친 테스트가 오래된 실패를 계속 끌고 다닙니다. items는 최근 20회 창이어서 지금 상태를 보여 주지만 표본이 얕습니다.
저는 팀 지표로는 items 쪽을 씁니다. "고쳤는데도 지표가 안 내려간다"는 말이 나오는 순간 아무도 지표를 안 보기 때문입니다. 대신 statistic은 이 테스트를 아예 지울지 판단할 때 봅니다. 최근 20회는 깨끗한데 전체 실패율이 30%라면, 고쳐졌다기보다 실행 조건이 바뀌어 실패가 드러나지 않게 됐을 가능성을 의심해 볼 만합니다.
크기도 재 뒀습니다. 테스트 5개에 items 20개가 찬 history.json이 14,140바이트, 테스트당 약 2,828바이트입니다. 이 비율이면 테스트 300개에서 약 830KB이고 상한이 20이니 더 자라지 않습니다.
환경을 합치면 절반이 retry로 접힙니다
같은 테스트를 두 브라우저에서 돌려 결과를 한 디렉터리에 합쳤습니다. 결과 파일은 4개인데 리포트에는 테스트가 2개만 떴습니다.
결과 파일 수: 4
summary statistic: {'passed': 2, 'total': 2}
retry-trend : [{'data': {'run': 2, 'retry': 2}}]
historyId 3790776415359d1451d6 → 2건 ['test_login_ok', 'test_login_ok']
historyId 9cb8d6fe47d815f38cc3 → 2건 ['test_cart_add', 'test_cart_add']
절반이 retry로 접혔습니다. Allure는 같은 테스트인지를 historyId로 판단하고, 이 값은 테스트의 전체 이름과 제외되지 않은 파라미터로 계산됩니다. 브라우저를 환경변수로만 넘겼으니 Allure가 보기에는 같은 테스트를 두 번 돌린 것이고, 규칙대로 뒤엣것을 재시도로 분류했습니다. 재시도 추이의 retry: 2도 실제 재시도가 아니라 이 오분류입니다. 해법은 환경을 구분하는 파라미터를 알려 주는 것으로, allure-pytest에서는 autouse 픽스처 하나면 됩니다.
# conftest.py — 실행 환경을 파라미터로 주입해 historyId를 갈라 줍니다
import os
import allure
import pytest
@pytest.fixture(autouse=True)
def _allure_env_parameter():
# 이 한 줄이 historyId 계산에 들어갑니다.
# 문서상 JUnit 5 등 일부 연동은 '동적' 파라미터를 구분하지 못하지만
# allure-pytest 2.16.0에서는 정상 반영되는 것을 확인했습니다
allure.dynamic.parameter("browser", os.environ.get("BROWSER", "unknown"))
yield
이 픽스처를 넣고 같은 실험을 반복하니 historyId가 226454fa8649…와 8cc03301ba99…로 갈라졌고, retry-trend는 run: 2, retry: 0이 됐습니다. 오분류가 사라졌습니다.
| 조건 | 결과 파일 | 리포트 테스트 수 | retry-trend |
|---|---|---|---|
| 파라미터 없음, 두 환경 병합 | 4 | 2 | run: 2, retry: 2 |
allure.dynamic.parameter 주입 |
2 | 2 | run: 2, retry: 0 |
반대 방향의 함정도 있습니다. 파라미터 값이 현재 시각이나 난수에서 나오면 회차마다 historyId가 달라져, 같은 테스트가 매번 새 테스트로 잡히고 History 탭이 영원히 비어 있습니다. 타임스탬프를 파라미터에 넣고 있다면 excluded 처리하거나 빼는 편이 낫습니다.
5. Allure Report 3으로 넘어가면 무엇이 달라지는가
Allure Report 3은 TypeScript로 다시 쓴 별개 계보입니다. 폴더를 나르는 대신 설정 파일에 historyPath를 적으면, 생성할 때마다 그 JSONL 파일을 읽고 새 줄을 덧붙입니다.
// allurerc.mjs — Allure Report 3 설정
export default {
name: "하루 QA 리포트",
// 회차마다 한 줄이 덧붙는 JSONL 파일 하나로 이력을 관리합니다
historyPath: "./.allure/history.jsonl",
// 이 값을 비워 두면 줄이 무한히 쌓입니다 (아래 실측 참고)
historyLimit: 20,
output: "./allure-report",
plugins: {
awesome: { options: { reportName: "하루 QA 리포트" } },
},
};
폴더 복사 스텝이 없어져 CI 설정이 눈에 띄게 짧아집니다. history.jsonl 한 파일만 캐시하면 되고, 복원 실패도 파일 하나의 존재 여부로 확인됩니다. 여기까지는 분명한 개선입니다.
기본값이 무제한입니다
그런데 historyLimit을 지정하지 않고 30회를 돌리니 파일이 30줄, 114,334바이트가 됐습니다. 줄당 약 3,811바이트, 테스트 5개 기준이니 테스트 하나당 회차당 약 762바이트입니다. Allure 2처럼 20에서 스스로 멈추지 않습니다. 설치된 패키지 코드를 열어 보니 historyLimit !== undefined ? historyDataPoints.slice(...) : historyDataPoints 형태라, 값을 안 주면 자르지 않는 것이 맞았습니다. historyLimit: 5를 넣고 33회까지 돌리니 정확히 5줄로 유지됐습니다.
테스트 300개를 하루 한 번 돌리는 저장소로 환산하면 이렇습니다.
| 구성 | 회차당 증가 | 250회(약 1년) 누적 | 상한 |
|---|---|---|---|
Allure 2, history 폴더 |
0 (20회분에서 포화) | 약 830KB | 20회 고정, 변경 불가 |
Allure 3, historyLimit 미지정 |
약 223KB | 약 56MB | 없음 |
Allure 3, historyLimit: 20 |
0 (20회분에서 포화) | 약 4.4MB | 설정값 |
56MB를 매 실행마다 복원하고 저장하는 것은 초기 단계 팀이 낼 만한 비용이 아닙니다. Allure 3을 쓰신다면 historyLimit을 처음부터 명시하시는 편이 낫습니다. 다만 상한을 조절할 수 있다는 것 자체는 나은 점입니다. Allure 2의 20회는 바꿀 수 없어서, 주 5회 실행하는 팀은 4주치만 보게 됩니다.
생성 속도는 이번 규모에서 판단 근거가 못 됐습니다. 테스트 5개 결과로 재니 Allure 3이 0.24초, Allure 2가 0.18초였습니다. JVM 기동 비용이 붙는 Allure 2가 오히려 빨랐고 차이도 무의미합니다. 속도 때문에 옮길 이유는 없다는 정도로만 적어 두겠습니다.
도입 난이도 / 유지보수 비용 / 팀 학습곡선
QA 4~6명, 자동화 초기 단계 조건에서 세 선택지를 평가했습니다.
| 선택지 | 도입 난이도 | 유지보수 비용 | 팀 학습곡선 |
|---|---|---|---|
| Allure 2 + 캐시로 history 영속화 | 중간 — 캐시 복원·저장 스텝과 executor.json까지 4개 스텝 |
중간 — 복사 누락이 곧 이력 소실. 캐시 키를 브랜치별로 나눌지 결정해야 함 | 낮음 — 자료와 예제가 가장 많음 |
Allure 3 + historyLimit 명시 |
낮음 — 설정 파일 한 개, 캐시 대상은 파일 하나 | 낮음 — 다만 상한을 안 적으면 파일이 무한히 자람 | 중간 — 한국어 자료가 적고 플러그인 구조가 Allure 2와 다름 |
| 리포팅을 CI 요약으로 끝내고 트렌드 포기 | 매우 낮음 | 매우 낮음 | 없음 |
셋째 줄을 진지하게 넣었습니다. 이력이 없어도 되는 팀이 있습니다. 스위트가 50개 미만이고 실패 원인을 실행 로그에서 5분 안에 찾을 수 있다면, 배관을 유지하는 비용이 얻는 것보다 큽니다. 이때는 지난주에 정리한 report-log로 flaky율을 뽑아 GITHUB_STEP_SUMMARY에 올리는 방식이 더 쌉니다.
6. 권할 만한 경우 / 권하지 않는 경우
권할 만한 경우는 세 가지입니다. "이 테스트가 원래 이렇게 자주 깨졌나"를 묻는 대화가 팀에서 반복될 때, 스위트가 100개를 넘어 실행 로그를 눈으로 훑는 것이 불가능해졌을 때, 품질 상태를 정기적으로 보고해야 할 때입니다.
권하지 않는 경우도 명확합니다. 스위트가 아직 수십 개라면 배관을 유지하는 시간이 계속 나갑니다. history 영속화를 뒤로 미룬 채 도입하는 것도 권하지 않습니다. 트렌드가 안 쌓이는 리포트는 예쁜 실행 로그일 뿐이고 몇 주면 아무도 열지 않습니다. 캐시 키 설계를 정하지 않고 시작하는 것도 위험합니다. 브랜치별로 이력이 갈라져 어느 그래프가 진짜인지 모르게 됩니다.
마지막으로 점검 스크립트를 남깁니다. 두 번째 실행이 끝난 직후에 돌리시면 배관이 붙었는지 즉시 알 수 있습니다.
#!/usr/bin/env python3
"""Allure 2 히스토리 배관 점검 — 2회차 실행 직후에 돌립니다."""
import json
import pathlib
import sys
H = pathlib.Path("allure-report/history")
problems = []
if not H.is_dir():
sys.exit("history 폴더가 없습니다 — allure generate가 실패했는지 확인하십시오")
# 1) 다섯 개 파일이 다 있는가 (retries-trend가 아니라 retry-trend 입니다)
expected = {"history.json", "history-trend.json", "duration-trend.json",
"retry-trend.json", "categories-trend.json"}
missing = expected - {p.name for p in H.iterdir()}
if missing:
problems.append(f"누락 파일: {sorted(missing)}")
# 2) 트렌드가 2칸 이상인가 — 1칸이면 history 복사가 안 된 것입니다
trend = json.loads((H / "history-trend.json").read_text())
if len(trend) < 2:
problems.append(f"트렌드 항목이 {len(trend)}개 — history 복사 스텝을 확인하십시오")
# 3) buildOrder가 붙었는가 — 없으면 executor.json이 빠진 것입니다
if trend and "buildOrder" not in trend[0]:
problems.append("buildOrder 없음 — allure-results/executor.json을 확인하십시오")
# 4) retry가 실제 재시도인가 — 환경 병합 오분류를 걸러 냅니다
retry = json.loads((H / "retry-trend.json").read_text())
if retry and retry[0]["data"]["retry"] > 0:
problems.append(f"retry {retry[0]['data']['retry']}건 — "
"재시도 설정이 없다면 historyId 충돌을 의심하십시오")
print("\n".join(f"[확인 필요] {p}" for p in problems) if problems
else "히스토리 배관 정상 (파일 5개, 트렌드 2칸 이상, buildOrder 있음)")
sys.exit(1 if problems else 0)
리포트를 붙이는 일은 반나절이면 됩니다. 그 리포트가 석 달 뒤에도 의미 있는 그래프를 보여 주게 만드는 일이 나머지입니다. 저는 그 차이가 cp -r 한 줄과 캐시 스텝 두 개에 있다는 것을 20칸이 1칸으로 돌아가는 것을 보고 나서야 제대로 이해했습니다.
댓글
댓글 쓰기