Skip to content

fix(ci): Android Lint 리포터가 매 run reviewdog 을 네트워크 설치 — 실패 시 required check 가 red, 재시도로는 못 푼다 #1093

Closed

Description

@OnlyReadOneBook

현황

lint.yml 의 Report Android Lint (Reviewdog) 스텝이 lint 위반이 0건인 PR 에서도 job 을 실패시킨다.

Check Project Issues (Android Lint) 는 develop ruleset 의 required status check 다. 즉 이 실패는 로그 노이즈가 아니라 머지 차단이다.

실측 — run 32742963935 / job 97481566012 (PR #1080):

스텝 결과 소요
Build dvdandroid/action-android-lint@master 성공 7초
Run Android Lint 성공 — BUILD SUCCESSFUL 287초
Merge module lint XMLs into one 성공 — merged 0 issues from 26 modules 0초
Report Android Lint (Reviewdog) 실패 — /entrypoint.sh: line 6: reviewdog: not found 1초

원인 구조

dvdandroid/action-android-lint 는 도커 액션이고, Dockerfile 이 매 run 마다 reviewdog 을 네트워크에서 설치한다.

FROM alpine:3.11
RUN apk --no-cache --update add git python3 && rm -rf /var/cache/apk/*
RUN wget -O - -q https://raw.githubusercontent.com/reviewdog/reviewdog/fd59714416d6d9a1c0692d872e38e7f8448df4fc/install.sh| sh -s
COPY entrypoint.sh /entrypoint.sh
COPY converter.py /usr/local/bin/converter.py

wget -O - -q ... | sh -s — 파이프라인의 exit status 는 마지막 sh 의 것이다. wget 이 실패해도 sh 는 빈 입력을 받고 exit 0 을 낸다. -q 라 경고조차 안 나온다. 그래서 이미지 빌드는 성공하고, 몇 분 뒤 entrypoint 에서 reviewdog: not found 로 죽는다.

실측 빈도

리포터 스텝이 존재한 전 기간 — lint.yml run 1173건 (2026-03-23 ~ 2026-08-24) 전수 조사:

분류 건수 비고
리포터 단독 실패 (lint 는 성공) 2건 2026-03-27 fix/43, 2026-08-24 refactor/941
lint + 리포터 동시 실패 95건 전부 2026-03 ~ 2026-05. 아래 참조
Run Android Lint 단독 실패 73건 실제 lint 위반 — 정상 동작

동시 실패 95건은 다른 원인이고 이미 해소됐다. 2026-05-24 이전에는 lint_xml_file 이 app/build/reports/lint-results-debug.xml (:app 하나)을 직접 가리켜, lint 가 실패하면 그 파일이 없어 리포터도 따라 죽었다. #279 의 merge 스텝(if: always())이 항상 파일을 만들면서 2026-06 이후 0건이다.

빈도가 낮은데 왜 다루는가

공급망 상태

재시도(안 a)가 원리적으로 듣지 않는 이유

도커 액션 이미지는 job 시작 시 1회만 빌드된다. 위 job 에서 Build dvdandroid/... 는 15:08:04 에 이미지 c97262:3363d02d… 를 만들고, 스텝 9 의 docker run 은 15:13:01 에 그 태그를 그대로 실행한다.

reviewdog 부재는 빌드 시점에 이미 확정돼 있다. 같은 job 안에서 스텝을 몇 번 다시 돌려도 같은 이미지가 뜨므로 결과가 바뀌지 않는다. 게다가 uses: 스텝에는 네이티브 재시도 옵션이 없다(retry 계열 액션은 run: 만 감싼다). 회복 경로는 job 전체 재실행뿐이고, 그건 lint 287초를 다시 태운다.

대응안 비교

안 하는 일 flake 제거 인라인 코멘트 외부 의존 판정
(a) 리포터 재시도 스텝 재시도 ❌ 무효 — 같은 이미지 재사용 유지 그대로 기각
(b) reviewdog/action-setup + 자체 스텝 도커 액션 제거, 러너에 바이너리 설치 창구 축소(도커 빌드 함정은 사라지나 설치는 여전히 네트워크) 유지 액션 1개 교체 + checkstyle 변환 자체 유지보수 차선
(c) continue-on-error 리포터 실패를 job 실패에서 분리 증상만 차단 실패 시 조용히 소실 그대로(EOL 이미지 잔존) 부분
(d) 리포터 제거 + job summary 스텝 삭제, merged XML 을 summary 로 렌더 ✅ 원인 제거 대체(summary) 0 채택

(d) 를 고른 근거

  1. 원인 제거: (a)는 무효, (c)는 EOL 도커 액션을 그대로 둔 채 증상만 덮는다. (b)는 개선이지만 "네트워크에서 리포터를 받아 실행한다"는 실패 구조를 유지하고, entrypoint 의 converter.py(lint XML→checkstyle) 몫을 우리가 떠안는다.
  2. 선례: perf(ci): ktlint 를 PR 당 두 번 검사 — reviewdog 경로 32초·버전 이원화 정리 #1012 에서 같은 저장소가 ktlint 의 reviewdog 경로를 같은 논리로 걷어냈다 — "인라인 코멘트의 원래 수요자는 로그를 뒤지기 싫은 사람인데, 이 저장소는 작업자 전원이 에이전트이고 에이전트는 로그를 직접 읽는다." 재사용할 구현·테스트 패턴(render-ktlint-summary.mjs + .test.mjs)이 이미 있다.
  3. 효용 실측 0: 위 참조. 인라인이 실제로 달린 적이 없다.
  4. 가시성은 오히려 개선: 인라인은 PR diff 에 걸린 줄만 표시하지만, summary 는 merged XML 전량(26개 모듈)을 보여준다. chore(ci): Reviewdog 가 :feature/* · :core/* 모듈 lint 도 PR 인라인 표시하도록 multi-module 지원 #279 가 풀려던 문제를 더 넓게 커버한다.
  5. 공급망: EOL 베이스 + 방치된 @master 액션이 통째로 사라진다. Actions 참조 SHA 고정 · secret scanning · push protection 적용 #847 의 고정 대상에서도 빠진다.

#1012 는 "Android Lint 는 대상이 아니다 — 검사를 다시 하지 않으므로 추가 비용이 거의 없다" 로 유지를 택했다. 그 판단을 뒤집는 게 아니라, 그때 계산에 없던 비용을 더하는 것이다: 이 스텝은 시간(1초)이 아니라 required check 를 red 로 만들 확률을 비용으로 갖는다.

범위

  1. lint.yml 의 Report Android Lint (Reviewdog) 스텝을 삭제한다.
  2. Merge module lint XMLs into one 도 함께 삭제한다 — 모듈별 XML 을 한 파일로 합치던 유일한 소비자가 reviewdog 이었다. summary 스크립트가 모듈별 XML 을 직접 읽으므로 셸+xmllint 로 XML 을 문자열 조작하는 단계가 사라지고, 합치면서 잃던 위반의 모듈 귀속(:core:common 등)도 보존된다.
  3. .github/scripts/render-android-lint-summary.mjs 를 추가해 모듈별 lint XML 을 job summary 로 렌더한다. 모듈 · 파일:라인 · 규칙 ID · severity 를 표로. render-ktlint-summary.mjs 형식을 따른다.
  4. 단위 테스트(.test.mjs)를 붙인다 — 위반 0건, 다중 모듈, 잘린 XML 픽스처.
  5. Run Android Lint 는 그대로 게이트로 남긴다(--continue 유지).

완료 조건

관련

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't working

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions