워크플로 템플릿


이 문서는 GitHub Actions(GHA)를 사용해 Exercism 언어 트랙의 지속적 통합(CI) 워크플로를 설정하는 방법을 설명해요. 직접 빠르고 안정적이며 견고한 CI 워크플로를 만들 수 있도록 모범 사례와 예제를 제공해요. 이 폴더의 GHA 워크플로는 기본 구조가 그대로 유지되기 때문에 어떤 CI에서든 동작하도록 응용할 수 있어요.

이 문서에서는 다음 내용을 다뤄요:

  • 이상적인 CI 워크플로 개요
  • 고려 사항과 권장 사항
  • 사용할 수 있는 몇 가지 템플릿
  • Travis에서 마이그레이션하는 가이드

이 워크플로 파일들의 구현 예시는 exercism/javascript에서 확인할 수 있어요.

도움말: 이건 많은 작업처럼 보여요 😓

이 문서의 나머지 부분은 워크플로가 어떻게 동작하는지 설명하기 위해 만들어졌어요. 급하고, PR 스크립트를 최적화하지 않은 채 Travis나 Circle에서 GHA로 그냥 갈아타고 싶다면, Travis에서 마이그레이션하기 약 10분 가이드를 확인해 봐요.

트랙 CI 액션

저장소 콘텐츠가 온전한지 확인하기 위해 권장되는 액션은 다음과 같아요:

  1. config.json을 검사하기 위한 configlet 린팅
  2. 스텁 확인
  3. 문서 확인 (v3는 새 파일을 요구해요. 이 작업은 configlet으로 옮겨질 수도 있어요)
  4. "maintainers" 설정을 사용한 연습 문제 린팅
  5. 예제/모범 파일을 사용한 연습 문제 테스트 (빌드 단계를 포함할 수 있어요)

트랙별 액션도 있을 수 있어요. 예를 들어:

  1. 연습 문제 설정의 무결성 확인
  2. 연습 문제 파일의 서식 확인

그리고 다음과 같은 더 많은 편의성 검사를 원할 수도 있어요:

  1. CONTRIBUTING이 있는지 확인
  2. 의존성에 대해 적절한 lockfile이 있는지 확인
  3. 마크다운 파일 안의 링크가 유효한지 확인
  4. ...

권장 사항

검사 실행 빈도

각 액션마다 얼마나 자주 실행해야 할지 고민해 봐요.

  • configlet 린팅은 매우 중요해서(config.json이 깨지면 트랙이 망가질 수 있기 때문이에요) 아마 항상 실행해야 해요. 하지만 커밋당 한 번만 실행하면 돼요.
  • 파일의 존재 여부나 무결성 검사는 커밋당 한 번만 실행하면 돼요.
  • 트랙이 여러 런타임 버전이나 컴파일러 버전에서 실행되어야 한다면, 연습 문제의 빌드/테스트는 지원되는 각 버전에 대해 실행해야 해요.
  • PR에서는 아마도 _추가_되거나 변경된 파일에 대해서만 액션을 실행하면 되지만, 파일 하나가 연습 문제 전체에 영향을 줄 수 있으니, 연습 문제의 파일 중 하나가 바뀌면 연습 문제 전체에 대해 액션을 실행하는 편이 더 안전해요.

실행해야 할 액션을 로컬에서도 사용할 수 있게 해 두면 아주 유용해요. 즉, 실제 작업을 수행하는 스크립트를 직접 수동으로도 실행할 수 있다는 뜻이에요. 그러려면 액션을 워크플로 파일 안에 인라인으로 넣지 말고, 독립 실행형 스크립트를 만들어요. 예를 들어 스텁 확인은 워크플로 파일 안에서 bash로 완전히 처리할 수도 있지만, 여기서 권장하는 방식은 실행 가능한 새 스크립트 scripts/ci-check를 만드는 거예요.

"그런데 명령어가 아주 짧잖아요? 예를 들어 eslint . --ext ts --ext tsx 같은 거요."

이 명령어를 업데이트해야 할 때가 되면, 문서와 워크플로 파일 곳곳, 그리고 _메인테이너들의 머릿속_까지 전부 업데이트해야 해요. 이걸 스크립트로 빼내면 그 모든 문제가 해결돼요. 워크플로 파일을 읽는 것 자체도 무척 부담스러울 수 있어요.

연습 문제가 변경된 PR에 대한 검사

scripts/pr와 scripts/pr-check 스크립트(자세한 내용은 템플릿 참고)는 이 PR에서 변경되거나 추가된 각 파일마다 하나씩, 여러 인자를 받아 실행돼요. 예를 들어 two-fer가 업데이트되었다면, 호출은 이렇게 생겼을 수 있어요:

scripts/pr exercises/two-fer/README.md exercises/two-fer/.meta/example.ext

액션은 변경된 _파일_이 아니라 변경된 _연습 문제_에 대해 실행하는 게 좋아요. 파일 하나를 바꾸면 연습 문제 전체(설정, 패키지 등을 떠올려 봐요)에 변경이 생길 가능성이 높기 때문이에요.

아직 준비가 안 됐나요? / 복잡한가요?

이 최적화를 적용하기 전이라면, 그냥 무시해도 괜찮아요! 마이그레이션 가이드에서도 나중 단계에 추가하는 걸 언급하고 있어요. 입력 인자를 무시하면 모든 검사가 모든 연습 문제에 대해 실행돼요. 이건 전혀 문제없어요. 그냥 시간이 더 오래 걸릴 뿐이에요.

무결성 검사

트랙에 단일 "최상위" 의존성 파일이나 기타 설정 파일이 있다면, 무결성 단계를 추가해요. 이 단계는 모든 설정 파일을 모든 연습 문제로 복사하는 scripts/sync나 bin/sync와 나란히 존재하며, 최상위/기본 파일이 연습 문제 디렉터리로 복사된 파일과 같은지 확인해 줘요. 이렇게 하면 의존성을 업데이트하고 저장소 전체에 동기화할 수 있고, 모든 연습 문제가 같은 설정을 가지도록 보장할 수 있어요.

이를 구현하는 흔한 방법은 체크섬을 사용하는 거예요. Ubuntu(그리고 여러 다른 Linux 배포판)에는 sha1sum이라는 도구가 있는데, 설정 파일을 체크섬 값으로 해시하거나 축약하는 데 어떤 방법을 쓰든 (md5, sha1, crc32) 다 잘 동작해요:

$ sha1sum README.md
cd58091c5043bf21f00d39ff1740d8b2976deeff *README.md

보안 검사

트랙이 GitHub 토큰이나 기타 시크릿에 접근해야 하는 추가 워크플로를 사용한다면, 워크플로에서 사용하는 모든 액션을 특정 커밋에 고정하는 게 모범 사례예요. 자세한 내용은 GitHub의 보안 강화 가이드를 참고해요.

예를 들어:

- uses: julia-actions/setup-julia@v1
+ uses: julia-actions/setup-julia@d26d1111976eae5f00db04f0515ab744ec9cd79e # 1.3.1

툴링에 의존성 관리를 위한 lockfile이 있다면, 이를 저장소에 커밋하고 워크플로 파일 안에서 "frozen lockfile"을 사용하는 걸 고려해 봐요. 예를 들어 npm ci, yarn install --frozen-lockfile, bundle install --frozen 같은 거예요. 이렇게 하면 의존성을 바꿀 때 lockfile이 최신 상태로 유지되고, 악성 패키지가 들어오는 걸 막을 수 있어요.

템플릿

이 디렉터리에는 최소한 다음 템플릿이 있어요:

  • configlet.yml: 이 워크플로는 최신 configlet 바이너리를 내려받아 이 저장소를 린팅해요. 모든 커밋에서 실행돼요. PR의 경우, 실제 커밋과 "병합 후" 트리에서 실행돼요.
  • ci.yml: 이 워크플로는 main 브랜치에서만, 커밋마다 한 번 실행돼요.
    1. 모든 연습 문제에 대해 'pre-check' 명령(스텁, 린트, 문서 등 확인)을 실행해요
    2. 모든 연습 문제에 대해 여러 버전으로 'ci' 명령(빌드 및 테스트)을 실행해요
  • pr.ci.yml: 이 워크플로는 PR에서만, 커밋마다 한 번 실행돼요.
    1. 변경된 파일에 대해 'pre-check' 명령(스텁, 린트, 문서 등 확인)을 실행해요
    2. 변경된 연습 문제에 대해 여러 버전으로 'ci' 명령(빌드 및 테스트)을 실행해요

pr이 아닌 워크플로는 workflow_dispatch로도 트리거할 수 있어요.

각 파일 맨 위에는 어떤 "스크립트"가 있어야 하는지 적혀 있어요. 이걸 바이너리로 쓰고 싶다면 scripts/xxx를 bin/xxx로 바꿔요. 일부 툴링은 바이너리가 bin 폴더 안에 있기를 _요구_하기도 해요.

  • scripts/ci: 예제 솔루션을 사용해 테스트에 대고 모든 연습 문제를 빌드하고 테스트하는 스크립트
  • scripts/ci-check: 모든 연습 문제를 린팅하고, 선택적으로 스텁, 설정 무결성 등을 확인하는 스크립트
  • scripts/pr: scripts/ci와 같지만, 입력으로 주어진 경로에서 확인된 연습 문제에만 실행하는 스크립트
  • scripts/pr-check: scripts/ci-check와 같지만, 입력으로 주어진 경로에서 확인된 파일이나 연습 문제에만 실행하는 스크립트

문제 해결

문제가 생기거나 워크플로를 누군가에게 검토받고 싶다면, @exercism/github-actions 팀에 알려 주세요.

모든 연습 문제에 대한 CI 실행을 트리거해야 하는 최상위 파일을 변경했어요

이 글을 쓰는 시점에서 pr.ci.yml은 "확장자" 테스트만 허용해요. 이상적으로는 특정 파일(예를 들어 테스트를 실행하는 바이너리)이 변경될 때마다 항상 트리거되도록 업데이트하는 게 좋아요. 하지만 이런 변경은 자주 일어나지 않고 메인테이너가 처리하기 때문에, ci.yml이 main에서 항상 모든 것에 대해 실행된다는 사실만으로도 아마 충분히 안전해요.

Windows에서 scripts/xxx 파일을 만들었는데 이제 {other OS}에서 동작하지 않아요

기본적으로 Windows에서 만든 파일은 실행 가능 여부에 대한 메타데이터가 git-index에 포함되어 있지 않아요. Windows의 권한 모델이 다르기 때문이에요. Git은 기본적으로 git-index 메타데이터를 사용해 POSIX 기반 시스템에서 파일이 실행 가능해야 하는지 판단하기 때문에, scripts/xxx 파일을 실행할 수 없게 만들어요.

git update-index --chmod=+x scripts/xxx
git commit -m "Make scripts/xxx executable"