2P by GN⁺ | ★ favorite | 댓글 1개
  • 복잡한 CI를 GitHub Actions로 되돌리는 과정에서 merge queue, 여러 러너, Rust 빌드, Docker 이미지, 통합 테스트가 맞물리며 설정보다 디버깅 비용이 더 크게 드러남
  • main에 들어가는 변경은 모두 테스트를 통과해야 하고, 포맷·미사용 의존성·린트 같은 사소한 오류는 자동 수정되며, CI 산출물이 릴리스 산출물과 같아야 함
  • merge queue 전후 검증을 모두 강제하려면 두 단계의 잡 이름을 동일하게 맞춰야 했고, 그렇지 않으면 체크가 멈추거나 실패한 변경이 병합될 수 있음
  • GITHUB_TOKEN, workflow permissions, 커스텀 토큰, fork·self-hosted runner 예외가 얽혀 보안 모델을 이해하기 어렵고 실수 여지가 커짐
  • Docker 컨테이너 실행, YAML 워크플로, 제한적인 로컬 테스트 때문에 개발 속도가 느려졌지만, 새 CI 스크립트는 병합 시간을 크게 줄임

GitHub Actions로 돌아온 복잡한 CI

  • 지난 2주 동안 CI 스크립트를 GitHub Actions로 다시 작성함
  • CI 구성은 이번이 세 번째 개편임
    • 처음은 GitHub Actions
    • 이후 Earthly로 이동
    • Earthly가 중단되어 다시 GitHub Actions로 돌아옴
  • 현재 CI는 merge queue, 여러 러너, Rust 빌드, Docker 이미지, 무거운 통합 테스트를 함께 다룸
    • 러너는 self-hosted, blacksmith.sh, GitHub-hosted를 함께 사용함
    • PR 하나를 병합할 때 여러 병렬 러너에서 약 1시간의 CI 시간이 소모됨

CI가 만족해야 하는 조건

  • main에 들어가는 모든 변경은 모든 테스트를 통과해야 함
  • 포맷, 미사용 의존성, 린트 문제 같은 사소한 실수는 실패로 끝나기보다 자동 수정되어야 함
  • CI에서 테스트한 산출물이 실제 릴리스 산출물과 같아야 함
  • 개발자 경험을 위해 CI는 빠르게 끝나야 함
  • GitHub Actions는 이 조건들을 기술적으로 지원하지만, 설정 과정에는 숨은 함정과 일관성 없는 동작, 어려운 디버깅이 따라옴

Merge queue와 상태 체크

  • 깨끗한 main 브랜치를 유지하는 핵심은 GitHub의 merge queue
    • merge queue는 CI 실행 전에 PR을 main 위로 rebase함
  • 필요한 CI 실행은 두 단계로 나뉨
    • 큐에 들어가기 전에 CI를 실행해 사소한 문제를 자동 수정함
    • 큐 안에서 다시 CI를 실행해 최종 병합 상태를 검증함
  • GitHub Actions에서 두 실행을 모두 필수로 만들려면 두 단계의 잡 이름을 동일하게 지정해야 함
    • GitHub가 두 실행을 같은 체크로 취급해 둘 다 성공해야 병합 가능해짐
    • 이 방식은 몇 시간 디버깅한 뒤 Stack Overflow 답변을 통해 찾음
  • 다른 방식은 상태 체크가 큐 진입 전에 대기되어 잡이 시작되지 않거나, merge queue 안에서 실패해야 할 잡이 실패해도 병합되는 결과로 이어질 수 있음

이해하기 어려운 GitHub Actions 보안 모델

  • 최근 인기 GitHub Action이 침해된 사건 이후 “의존성을 해시에 고정하라”는 대응이 나왔지만, 댓글에서는 거의 아무도 그렇게 하지 않는다는 반응이 이어짐
  • GitHub Actions에는 기본 토큰인 GITHUB_TOKEN이 있음
    • 이 토큰은 기본 권한으로 초기화됨
    • 기본 권한은 저장소 설정의 Actions → General → Workflow Permissions에서 설정할 수 있음
  • GITHUB_TOKEN 기본 권한이 제한적이면 필요한 action과 명령 실행을 위해 권한을 올려야 하고, 기본 권한이 허용적이면 워크플로 파일에서 일부 권한을 제거할 수 있음
  • 더 나은 기본값은 권한 없음에서 시작해 필요한 권한만 사용자가 추가하는 방식임
  • 권한 종류는 많고, GitHub 전문가가 아니면 각각이 무엇을 보호하는지 파악하기 어려움

토큰과 권한 예외

  • softprops/action-gh-release로 GitHub 릴리스를 자동 생성할 때 커스텀 토큰 CI_RELEASE를 사용함
- name: Release on GitHub
  if: env.version_exists == 'false'
  uses: softprops/action-gh-release@v2
  with:
    tag_name: v${{ env.CURRENT_VERSION }}
    generate_release_notes: true
    make_latest: true
    token: ${{ secrets.CI_RELEASE }}
  • 기본 토큰으로도 릴리스 자체는 완료되지만, 릴리스 이후 워크플로가 트리거되지 않음
    • 별도 표시가 없어 같은 문제를 겪은 사람이 남긴 이슈를 찾아야 원인을 알 수 있음
  • 워크플로 YAML 안에서 권한을 올릴 수도 있음
    • 보호하려는 코드 안에서 권한을 올리는 구조가 낯설게 느껴짐
  • GitHub 문서에 따르면 permissions 키로 fork 저장소의 읽기 권한을 추가·제거할 수 있지만, 보통 쓰기 권한은 부여할 수 없음
    • 예외는 관리자가 GitHub Actions 설정에서 Send write tokens to workflows from pull requests 옵션을 선택한 경우임
  • 예외와 함정이 많아 GitHub Actions 보안 모델은 강력하면서도 공격 표면과 실수 가능성을 함께 키움

Self-hosted runner의 불확실성

  • GitHub 문서는 공개 저장소에서 self-hosted runner 사용을 권장하지 않음
    • 공개 저장소의 fork가 PR을 통해 self-hosted runner 머신에서 위험한 코드를 실행할 수 있기 때문임
  • GitHub에는 외부 기여자의 PR 실행을 승인하도록 요구하는 self-hosted runner 설정도 있음
  • 이 설정과 self-hosted runner를 함께 쓰면 안전한지에 대한 명확한 답은 문서에 없고, 인터넷에서도 합의가 없음
  • 복잡도가 높아 100% 확신하기 어려운 상태로 남아 있음

Docker와 GitHub Actions의 충돌

  • GitHub Actions는 잡을 컨테이너 안에서 실행할 수 있음
    • 매번 의존성을 설치하지 않고 dev container에 미리 패키징할 수 있다는 장점이 있음
  • 실제 사용에서는 파일 권한 문제가 반복됨
    • 컨테이너가 한 사용자로 파일을 빌드하지만 GitHub runner는 다른 uid/gid로 실행할 수 있음
    • 그 결과 컨테이너 내부 파일이나 GitHub workspace, 임시 호스트 디렉터리에 접근하지 못할 수 있음
  • $HOME 디렉터리도 어긋날 수 있음
    • dev container는 /home/ubuntu에 도구를 설치할 수 있음
    • GitHub Actions 안에서는 $HOME/github/home이 될 수 있음
    • $HOME 아래 파일에 의존하는 도구는 필요한 파일을 찾지 못할 수 있음
  • 호스트 시스템과 상호작용하는 action도 깨질 수 있음
    • GitHub 캐시는 10GB로 제한되어 blacksmith의 sticky disk action으로 캐싱용 NVMe 드라이브를 마운트함
    • 컨테이너 안에서는 동작하지 않았고, blacksmith.sh의 수정 이후 해결됨
  • container 필드 자체에도 제한이 있음
    • entrypoint를 override할 수 없음
    • 일부 step만 컨테이너 안에서 실행하고 나머지는 밖에서 실행하는 방식도 불가능함

YAML 워크플로 개발과 디버깅

  • GitHub Actions 로직은 YAML로 작성되며, 복잡해질수록 실수하기 쉬움
  • RustRover의 GitHub YAML linter 검사가 도움이 되었지만, 더 나은 정적 검사가 필요함
  • 로컬에서는 실제 CI 동작을 충분히 시험하기 어려움
    • act는 알려진 도구지만 CI에서 하려는 작업 중 작은 부분만 지원함
  • 가장 나은 디버깅 방식은 동일한 저장소를 하나 더 만들고, CI가 기대대로 동작할 때까지 git commit -a -m "wip" && git push test-ci branch를 반복하는 것이었음

워크플로 분리와 산출물 재사용

  • 매번 전체 CI 파이프라인을 돌리지 않기 위해 개별 workflow를 작게 유지함
  • 각 workflow 끝에서 산출물을 업로드하고, 이후 workflow가 이를 다운로드해 처음부터 다시 빌드하지 않도록 구성함
  • 이전 실행의 산출물을 내려받아 workflow를 격리 테스트할 수 있음
    • 다만 이전 실행에서 내려받을 때 download-artifact action에 토큰을 제공해야 함
    • 이 토큰은 기본 토큰이어도 되지만, 왜 여전히 명시해야 하는지는 해결되지 않은 의문으로 남음
  • 메인 workflow 파일은 다른 YAML 파일을 호출하는 체인이 됨
jobs:
  invoke-build-rust:
    name: Build Rust
    uses: ./.github/workflows/build-rust.yml
  invoke-build-java:
    name: Build Java
    uses: ./.github/workflows/build-java.yml
  invoke-tests-unit:
    name: Unit Tests
    needs: [invoke-build-rust, invoke-build-java]
    uses: ./.github/workflows/test-unit.yml
  invoke-tests-adapter:
    name: Adapter Tests
    needs: [invoke-build-rust]
    uses: ./.github/workflows/test-adapters.yml
    secrets: inherit
  invoke-build-docker:
    name: Build Docker
    needs: [invoke-build-rust, invoke-build-java]
    uses: ./.github/workflows/build-docker.yml
  invoke-tests-integration:
    name: Integration Tests
    needs: [invoke-build-docker]
    uses: ./.github/workflows/test-integration.yml
  invoke-tests-java:
    name: Java Tests
    needs: [invoke-build-java]
    uses: ./.github/workflows/test-java.yml
  • 일부 잡에는 secrets: inherit가 필요함
    • workflow가 다른 workflow를 호출할 때 secret은 기본적으로 공유되지 않음
    • 전체 CI 파이프라인에서는 실패하지만 개별 step 실행에서는 동작하는 문제의 원인이었음

빨라진 병합, 여전히 비싼 디버깅

  • 새 CI 스크립트는 병합 시간을 크게 줄였고 결과에는 만족함
  • 다만 그 상태에 도달하기까지 너무 많은 시간이 들었고, 문제가 생겼을 때 디버깅이 더 쉬워져야 함

댓글과 토론

Hacker News 의견들
  • GitLab이 더 낫다는 말이 벌써 보이는데, 맞긴 하지만 다른 방식으로 별로임
    Jenkins, GitLab, GitHub를 겪고 난 결론은 CI 로직은 최대한 자기 코드로 두는 게 좋다는 것임
    shell script, make, just, doit, mage 무엇이든 상관없지만 유지보수 가능한 제대로 된 코드여야 함
    파이프라인은 가능한 한 개발자 로컬 머신에서도 실행되게 투자해야 하고, 아니면 테스트와 디버깅이 악몽이 됨
    YAML은 최대한 피하고, 결국 수익화해야 하는 VC 투자 기반의 “CI를 영원히 해결할” 새 도구에 묶이지 않는 게 좋음
    가능하면 온프레미스에서 자체 러너를 쓰는 편이 낫다

    • 몇 년 전 첫 Tech Lead 역할에서 우연히 정확히 같은 결론에 도달했음
      대형 엔터프라이즈 CMS 프로젝트였고, 클라이언트는 호스팅 플랫폼 보안 때문에 배포 자동화가 불가능하다고 해서 특정 지원 엔지니어가 복잡한 run sheet를 따라 코드와 설정을 모두 수동 배포하고 있었음
      처음엔 내 로컬 서버에 패키징·배포하는 bash 스크립트를 만들어 내 고통을 줄였고, 이후 팀들에 공유해 그들의 문제도 해결함
      버그가 잡힌 뒤에는 로컬에서 개발 인스턴스로 배포하도록 바꿨고, Jenkins도 같은 bash 스크립트를 호출하게 해 dev/test까지 완전한 CI/CD가 돌아가게 됨
      결국 플랫폼 지원 담당자도 수동 run sheet에 질려 성숙해진 스크립트로 stage/prod 배포를 자동화했고, 클라이언트가 나중에 방향을 무시한 걸 알았을 때는 로컬부터 prod까지 반복 가능하고 오류 없는 자동 배포가 생겨 매우 만족했음
    • Nix와 Bazel처럼 강하게 격리된 시스템은 로컬 재현성을 쉽게 확보하는 데 훌륭함
      모든 CI “플랫폼”은 병렬 실행 시각화, Groovy/JS로 API 호출, 알림·배지 생성 같은 걸 위해 작업을 단계로 쪼개도록 유혹함
      귀엽긴 하지만 결국 꼬리가 몸통을 흔드는 꼴이고, 빌드를 관리하고 순서를 정해야 하는 건 GUI가 아니라 기본 빌드 도구여야 함
      다음 세대 CI는 로컬 우선 도구에 깊게 훅을 걸 수 있으면 좋겠음
      CI에 여러 “단계”를 정의하게 하지 말고 빌드 도구와 통신해서, 빌드 도구가 무엇을 하는지 순서와 개별 로그를 보여주면 됨
      테스트 실행기도 마찬가지로, 자체적으로 불투명한 병렬화 방식을 쓰고 마지막에 XML로 뱉는 것만 볼 수 있는 현실은 답답함
      테스트 실행기가 fork한 프로세스, 각 로그 파일 위치, 종료 상태를 CI에 알려줄 수 있어야 함
    • 반도체 업계에서는 모든 게 로컬 호스팅 Jenkins + bash 스크립트였음
      Jenkins 작업은 Perforce에 저장된 bash 스크립트를 실행할 뿐이라, 로컬에서 돌리려면 같은 스크립트를 실행하면 됐음
      첫 웹 SaaS 스타트업에 들어갔을 때는 문화 충격이 컸고, 모든 게 각자 독자 설정·언어를 가진 서드파티 서비스에서 돌아갔음
      POSIX/Linux 같은 기본 지식은 거의 쓸모가 없어졌고, 지금은 익숙해졌지만 더 나아졌다고 확신하진 못함
      추상화 계층이 너무 많아져서 전체를 진짜 이해하는 사람이 있는지 모르겠음
    • 로컬 러너 기반 CI/CD 흐름에 많이 투자했음
      처음엔 gogs와 drone을 썼고, 지금은 forgejo와 woodpecker CI 포크를 씀
      분산된 하드웨어에 복제하기 쉬운 구성이라 여러 중복성으로 돌리고 있음
      조금 아픈 지점은 인증과 시스템 간 pull request라, feature branch 병합과 코드 리뷰에는 여전히 단일 장애 지점이 필요함
      모든 것을 Go로 빌드하기 때문에 항상 /toolchain/build.go를 두기로 했고, CI/CD podman/docker 이미지에서도 bash 없이 단일 언어만 쓰도록 함
      FROM scratch에 Go만 넣는 식이며, 예외는 eBPF 커널 모듈을 컴파일하거나 다시 빌드해야 할 때뿐임
      개인적으로 2024년 8월 GitHub Actions CVE가 결정타였고, 기술적으로 더 자세히 https://cookie.engineer/weblog/articles/malware-insights-git...에 썼음
      지난주 TJ actions가 침해된 이유도 GitHub가 1년 뒤에도 고치지 않는 같은 공격 표면이었음
      이런 취약점까지 검증하는 도구는 아는 한 zizmor뿐이고, 다른 도구들은 스키마만 검증하지 취약점과 약점을 검증하지 않음: https://github.com/woodruffw/zizmor
    • 가능하면 GitHub Actions는 Makefile을 감싸는 얇은 래퍼로만 쓰고 있고, 그 뒤로 경험이 훨씬 좋아졌음
      Makefile이 필요한 의존성 설치와 관련 빌드·테스트 명령을 처리하므로, 이 스레드의 다른 댓글들이 말한 긴 피드백 루프 없이 로컬에서도 다시 테스트할 수 있음
  • HN의 재미가 바로 이런 데 있음
    GitHub Actions와 DevOps가 그렇게 보편적으로 미움받는다는 걸 읽는 게 진심으로 흥미로웠음
    둘 다 여러 해 동안 거의 문제 없이 써왔고, 실제로 제공하는 가치를 좋아하고 있음
    이 스레드 밖에서는 이렇게 많은 사람이 싫어한다고 생각해 본 적도 없었음
    Actions 설정과 테스트가 좀 번거로운 건 맞고, 다시 실행하려고 별 의미 없는 커밋을 해야 하는 것도 짜증남
    하지만 한 번 동작하면 그냥 설정해 두고 잊어버리며, Node 버전 업데이트 말고는 4년쯤 워크플로를 거의 건드리지 않았음
    아마 내 요구사항이 충분히 단순해서 더 복잡한 문제에 부딪히지 않는 듯함

    • 무엇을 하느냐에 따라 크게 달라짐
      GitHub CI는 컴파일이 없거나 매우 싼 언어, 상대적으로 잘 격리된 프로젝트, 복잡한 통합 테스트가 없는 경우, 강한 컴플라이언스 강제가 필요 없는 경우, 개발자 권한이 대략 같은 경우, 빠른 CI에서는 잘 맞도록 설계된 편임
      여기서 벗어나는 순간 점점 무너지고, 위 제약에 맞지 않는 회사들은 GitHub Actions로 계속 문제를 겪는 걸 봤음
      더 나쁜 부분은 이미 GitHub 비용을 내고 있으면 싸거나 무료처럼 보이고, 추가 계약·청구·서드파티 심사·자체 CI 서비스 운영이 필요 없다는 점임
      그래서 계속 문제를 일으켜도 초기에 회사 입장에서는 더 “싼” 해법처럼 보임
      나중에 자체 GitHub runner를 구성해야 한다는 걸 깨달으면 개발자가 CI 문제를 고치는 데 쓰는 시간까지 제대로 계산할 때 전혀 싸지 않을 수 있음
      그래도 이미 GitHub Actions를 동작시키는 데 시간을 많이 썼고 옮기려면 다 포팅해야 하니 매몰 비용 오류도 생김
      현실적으로 다른 CI 해법들도 약간 나은 정도인 경우가 많음
    • GH actions를 쓰지만 모든 빌드 시스템처럼 잘하는 일만 맡기고 나머지는 shell script나 별도 Docker 컨테이너로 둬야 함
      복잡해지면 “이 스크립트 실행”으로 단순화하는 게 낫고, 수천 줄 YAML보다 스크립트가 훨씬 작성·디버깅하기 쉬움
      문제는 GitHub Actions 자체가 아니라 빌드·CI 시스템에 온갖 맞춤형 잡동사니를 과하게 넣는 사람들임
      20년 전 Ant와 Hudson에서도 같은 걸 하려면 힘들었을 것이고, 이유도 같음
      이런 시스템은 bash 대체재로 그리 좋지 않음
      Ant는 Java에서 Maven, Gradle로 넘어가기 전 인기 있던 빌드 시스템이었고, Maven 파일이 플러그인으로 복잡한 일을 하려다 bash 두세 줄이면 될 일을 만드는 경우를 많이 봤음
      Gradle도 더 낫지 않고, Ant는 적어도 “무언가 하기” 위한 단순 원시 연산이 있었지만 XML로 다 써야 했음
      빌드와 CI 시스템은 주로 소프트웨어 빌드 같은 단순한 일을 해야 하고, 조건 로직·맞춤 부수 효과·달과 별의 위치에 따라 일어날 수도 안 일어날 수도 있는 이상한 동작이 많아지면 실패 시 디버깅이 정말 괴로움
      YAML에는 YAML 생성기가 도움이 됨
      Kotlin 기반 생성기를 한동안 썼는데 자동완성, 문법 안정성, 타입 검사, 컴파일되면 실행된다는 장점이 있었고 새 파라미터나 플러그인 버전 업데이트를 찾기도 쉬웠음
    • 여러 조직을 안쪽에서 많이 보게 되는 사업을 하다 보니, DevOps로 고생하는 현실을 보면 먼저 최대한 단순화하고 서드파티 제공자 의존성을 제거해 GitHub Actions의 hello world 수준처럼 다시 돌아가게 만들려고 함
      불평하는 팀들은 대체로 HN, subreddit, Discord에서 본 것 때문에 과설계하고 쓸데없는 것들을 집어넣는 경우가 많음
      고생하는 팀에 들어가 커밋과 구성을 보면 지난 1년 동안 패키지 관리자나 번들러를 5번 바꾼 흔적 같은 게 나오기도 함
      10년 넘은 코드베이스에서 JS, TS, Python, Go, Rust가 섞여 있고 이유를 물으면 성능 때문이라고 하지만, 대부분 업무용 앱에서 언어가 병목이었던 적은 거의 없음
      다부서 또는 전사 규모의 수백만 달러 프로젝트에서도 데이터베이스 구성이 얼마나 나쁜지 보면 놀랄 사람이 많을 것임
      대기업의 “나쁜 Java/Oracle IT”가 허용하지 않아서 부서 예산으로 자체 운영하는 경우가 많고, 이런 오래 못 갈 새 도구들에 돈이 많이 타고 있음
    • 다른 사람들과 같은 고통 지점을 겪고 있는 것 같고, 단지 더 잘 무시하고 있을 뿐으로 보임
      현 상태보다 더 나아질 수 있다는 원문 작성자 쪽에 동의함
    • 잘못 이해한 게 아니라면, Actions를 트리거하려고 쓸모없는 커밋을 만들 필요 없이 workflow_dispatch를 쓸 수 있음
  • 아직 언급되지 않은 게 하나 있는데, 바로 최악의 피드백 루프
    30~60초짜리 피드백 루프보다 나쁜 게 없다고 느낌
    화면에 붙들어두지만 생산성은 전혀 없는 시간임
    GHA 환경을 로컬에 재현하려고 오래 시도했지만 내 맥락에서는 불가능했고, 그래서 모든 변경이 “push, GH가 잡을 때까지 대기, 사소한 오타나 불일치 처리, 반복”이 됨
    “이번 한 번만 더 하면 돌겠지” 하는 슬롯머신처럼 집중력과 시간을 먹어치움
    5초짜리 빌드 과정을 얻는 데 25분이 걸렸고, GHA로 순진하게 빌드하면 의존성 때문에 3분이 걸림
    캐싱을 추가해 보자고 하면 10시간이 날아감
    실패 비용과 집중력 저하가 엄청남

    • 이 고통이 매우 공감됨
      GitHub Action 컨테이너 빌드를 디버깅하는데 빌드마다 40분 넘게 걸리면, 하루 종일 여섯다섯 번 변경을 테스트하는 데 다 써버릴 수 있음
      더 나은 방법이 있어야 하는데 왜 아직 아무도 해결하지 못했는지 모르겠음
    • GitLab에서는 https://github.com/firecow/gitlab-ci-local이 GitLab 파이프라인 작업 때 엄청난 시간 절약 도구였음
      GitHub의 https://github.com/nektos/act와 비슷함
      다만 GitLab/GitHub가 기본으로 이런 방식을 제공해 주면 좋겠음
    • 큰 좌절의 상당 부분이 바로 끔찍한 피드백 루프와 그게 낭비하는 정신적 공간임
      원문도 끝부분에서 끝없는 “wip” 커밋을 돌볼 수밖에 없는 문제를 다룸
    • nektos/act를 강력 추천함
      충분히 복잡한 작업이면 서버에 SSH로 들어가 조사할 수도 있고, 이를 도와주는 action도 많음
    • 요즘 LLM을 많은 일에 쓰지만, 어쩌면 가장 중요한 용도는 이런 중간 길이 비동기 작업에서 집중력 보존 장치로 쓰는 것임
      몇 분보다 길면 기다리는 동안 다른 작업으로 전환할 수 있지만, 3~10분짜리 대기는 견디기 힘듦
      집중은 잃을 만큼 길지만 문맥 전환하기엔 충분히 길지 않기 때문임
      이제는 기다리는 동안 LLM과 작업 관련 이야기를 하며 집중을 유지할 수 있음
  • 10년 동안 GitLab을 쓰는 회사에서 일하며 러너에 익숙해졌음
    최근 GitHub를 쓰는 회사로 옮겼고, 규모가 크니 제공 기능에 감탄할 줄 알았음
    실제로 놀라긴 했지만 기대한 방향이 아니었고, 비교하면 정말 형편없어서 어떻게 이런 상태가 됐는지 혼란스러움
    회사를 운영하며 둘 중 골라야 한다면 GitHub Actions 때문에 매번 GitLab을 고를 것임

    • 나만 그런 게 아니라 다행임
      GitLab runners는 그냥 이해가 됨
      스크립트를 실행하는 컨테이너임
      개인 프로젝트에 GitHub actions를 조금 쓰는데, 어떤 이유에서인지 설정이 훨씬 더 혼란스러움
    • 몇 년 전 포기하고 더 단순한 GHA 호환 에이전트를 만들고 싶었음
      첫 목표 중 하나가 1차 진행 UI 경험을 갖는 것이었는데, 코드에서 GitHub API를 어디서 호출하는지도 찾을 수 없었음
      어디서 들었는지 모르니 강한 전언 주의가 필요하지만, GitHub 내부에서도 아무도 파악하지 못한다는 얘기가 있었음
  • GitHub Actions는 빠르게 반복 개선하던 초반에는 좋았지만, 지금은 GitHub가 관심을 놓았고 개선이 거의 멈춘 것처럼 보임
    요즘 Actions가 얼마나 적은 관심을 받는지 정말 속상함
    <https://github.com/orgs/community/discussions/categories/act...>를 보면 가장 인기 있는 이슈들이 완전히 답을 받지 못한 상태임
    Earthly가 개발을 중단하고 Dagger가 AI 열차에 올라탄 것도 아쉬움
    제대로 된 대안이 나오길 바람
    관련해서 https://www.blacksmith.sh/를 고려한다면 https://depot.dev/도 꼭 봐야 함
    둘 다 평가했지만 Depot을 선택한 이유는 팀이 엄청 똑똑하고 꽤 멋진 난제들을 풀었기 때문임
    특히 캐싱이 기본 actions/cache action과 동작해서, 인기 있는 서드파티 action을 패치된 것으로 바꿀 필요가 전혀 없는 점이 좋음

    • Dagger CEO임
      Dagger의 새 사용 사례인 AI 에이전트 실행을 홍보하고 있지만, 원래 사용 사례인 복잡한 빌드와 테스트 실행도 계속 지원하고 있음
      Dagger는 항상 범용 엔진이었고 커뮤니티도 CI 외 용도로 계속 써왔음
      엔진, CLI, SDK, 관측성 스택은 그대로이며 제품을 중단하는 게 아니라 오히려 플랫폼 위 워크로드를 늘려 모든 사용자에게 이익이 되게 하는 방향임
    • 11월 해고로 많은 GH actions 팀이 영향을 받았음
      예: https://github.com/actions/runner/pull/2477#issuecomment-244...
    • 아마 GH가 Actions 가격을 너무 낮게 잡아서, 사용량을 더 늘려도 매출이 늘지 않으니 개선할 가치가 낮아진 게 문제로 보임
      그리고 모두가 Actions 가격을 기준으로 삼으면서 다른 업체 가격도 내려가게 된 듯함
    • Earthly가 개발을 중단했다는 소식을 놓쳤을 수 있는데, 관련 내용을 찾지 못했음
      무슨 일이 있었는지 궁금함
    • 지난주 Depot으로 전환했음
      Rust 빌드가 20분 이상에서 4~8분으로 줄었고, 쉬운 설정과 빠른 캐싱이 붙은 Docker 빌드가 정말 좋음
  • “포맷팅, 미사용 의존성, lint 문제 같은 사소한 실수는 실패시키지 말고 자동 수정해야 한다”는 걸 정말 모범 사례로 보나? 동의하지 않음
    CI가 내 코드를 건드리는 걸 전혀 원하지 않음
    CI가 내 코드에 했을 수도 안 했을 수도 있는 변경 위로 rebase해야 한다는 걸 기억하고 싶지 않음
    모든 linter가 자동 수정 가능한 것도 아니어서 어차피 일부는 내 노트북에서 고쳐야 함
    사소한 검사라면 애초에 pre-commit hook으로 돌려야 함
    다음엔 CI가 실패한 테스트 케이스를 LLM으로 자동 수정해야 하나?
    실제로 CI가 뭔가 자동 수정하는 걸 선호하는 사람이 있는지 궁금함

    • 이 지점에서 원문이 탈선한 것 같음
      CI가 돌고 있는 같은 브랜치에 다시 커밋하는 건 어떤 CI 시스템에서도 함정이 너무 많음
      첫 문제는 원격 브랜치가 로컬 브랜치에서 예상치 못하게 즉시 벗어난다는 점이고, 그 커밋이 추가 CI 실행을 트리거하지 않도록 신경 써야 함
    • 그런 일은 pre-commit으로 처리함
      CI에서 하는 건 커밋을 push한 뒤 원격 브랜치로 다시 맞추는 복잡도를 키우는 것처럼 들림
      최악의 경우 로컬에서는 동작하는 코드를 실제로 망가뜨릴 수도 있음
    • 그 부분을 보자마자 글에서 바로 빠져나왔음
      재앙을 부르는 방식이고, 새로운 실패 모드를 잔뜩 가져오는 불필요한 복잡도처럼 들림
      모범 사례가 아님
      PR의 사소한 실수는 거의 항상 더 큰 오류의 신호임
    • CI 자동 수정은 처음 접하는 중이고, 초기 경험은 엇갈림
      코드에 손대는 게 짜증나지만, 때로는 PR이 CI 시스템을 더 지나가서 나중에 더 유용한 피드백을 만들게 해주기도 함
      이후 다른 방식으로 수정한 브랜치를 force push하는 경우가 많아서, CI 자동 수정 내용은 squash하거나 다른 방식으로 적용해 함께 접음
      대부분의 자동 수정은 그냥 cargo fmt 실행임
  • 흥미로운 글이었고 작성자가 당장 떠올린 고통 지점과 거친 모서리를 잘 보여줬음
    하지만 내 경험으로는 이 목록이 결코 완전하지 않고 실제로는 훨씬 더 많음
    GitHub의 10GB 캐시 제한, 러너 유형 기반 동시성 제한, 더 큰 GitHub runner의 비싼 가격 같은 것들이 있고, 보안 문제는 그다음임
    지난 2.5년 동안 Depot[0]을 만들며 사람들이 YAML 워크플로를 억지로 구부려 쓰는 방식을 보기 전까지는 깨닫지 못하는 GitHub Actions의 함정이 정말 많다는 걸 알게 됨
    특히 container job이 놀라웠음
    사람들은 빌드를 위한 재현 가능한 CI 샌드박스를 만들려고 쓰지만, 권한이 이상하고 Docker layer caching은 느리고 제한적이며 경로도 생각한 대로 동작하지 않음
    Depot은 GitHub Actions를 훨씬 빠르게 만들고 이런 거친 부분을 최대한 제거하는 데 집중해 왔음
    처음엔 Docker 이미지 빌드를 훨씬 빠르게 만드는 것으로 시작했고, 이제는 그 아키텍처와 성능을 자체 GHA runners[1]에도 가져왔음
    러너 주변의 컴퓨트와 프로세스를 구축·최적화해 작업을 매우 빠르게 만들고, 별도 캐시 action을 교체하거나 사용하지 않아도 캐싱을 2~10배 빠르게 함
    Docker 이미지 빌더도 빠른 캐싱이 있는 전용 컴퓨트 바로 옆에 있어 이미지를 빠르게 빌드하고, 그 이미지를 레지스트리에서 바로 빌드 작업에 쓸 수 있으므로 container job이 훨씬 나아짐
    전체적으로 GHA는 엄청나게 인기 있지만, 가장 큰 팬들조차 훨씬 더 좋아질 수 있다고 느끼는 분위기임
    [0] https://depot.dev/
    [1] https://depot.dev/products/github-actions

    • 어떤 기준으로 “지수적으로 빠르다”는 건지 궁금함
      GH가 워크플로 단계 수에 대해 지수 시간으로 걸리진 않을 것임
    • Depot은 좋아 보이지만 꽤 비싸 보임
      개발자 10명짜리 작은 B2B 회사인데도 빌드와 CI만으로 200+500, 즉 월 $700이 됨
      속도 향상이 정말 필요하다면 합리적일 수 있지만, 더 나은 GHA 경험도 제공한다면 엄청난 속도가 꼭 필요하지 않은 팀을 위한 다른 요금제가 있으면 좋겠음
    • Depot은 훌륭함
      강력 추천할 수 있음
      러너만 바꿨는데 빌드가 5분 이상에서 1분으로 줄어드는 건 마법 같음
    • “GitHub의 10GB 캐시 제한”이라니
      10,000,000,000바이트면 누구에게나 충분해야 함
      정말 많은 바이트임
  • 며칠 전 인기 있는 GitHub Action이 침해됐고, 대응은 “의존성을 hash에 고정하라”였음
    그런데 댓글들도 지적했듯 거의 아무도 그렇게 하지 않음
    금융 서비스 앱을 만들 때 GitHub actions를 썼기 때문에 Action 의존성은 당연히 hash로 지정했음
    이게 기본값이거나 필수 방식이어야 한다는 데 동의하지만, 자기 위험을 말하면서 “거의 아무도 안 한다”는 건 약한 변명임
    다른 사람이 무엇을 하든 내가 선택할 수 있는 옵션과는 무관함
    Actions를 가져올 때는 hash에 고정해야 하고, 훨씬 안전함

    • HN 커뮤니티가 며칠 전 꽤 배운 게 있었던 것 같음
      “기본값이 중요하다”는 말은 흔하지만, 모두가 추천하는 패턴과 예제 문서도 중요하다는 말도 똑같이 참임
      더 깊게 이해했거나 데어 본 똑똑한 개인은 올바른 결정을 할 수 있어도, 사용자 집단은 기본 사용 패턴에 큰 영향을 받고 힘들게 배우거나 교육받아야 함
      데이터를 먹어버리는 MySQL의 흔한 사용 패턴을 비판할 수 있듯, GH Actions의 사용 방식도 비판할 수 있음
    • 의존성 고정은 한 문제를 다른 문제와 맞바꾸는 것임
      한동안 빌드는 기대한 대로 동작하겠지만 그 기간은 언젠가 끝남
      어느 날 고정된 의존성을 업데이트해야 할 때 여러 메이저 버전을 한꺼번에 넘기며 깨지는 변경과 파이프라인 나머지에 미치는 연쇄 효과를 감당해야 할 수 있음
      의존성의 rolling update를 허용하면 소프트웨어 수명 동안 이런 유지보수 작업을 작고 관리 가능하게 유지하는 데 도움이 됨
    • 패키지 관리자 lockfile 같은 전이적 잠금이 없음
      good/foo@hash에 의존하는데 그쪽이 bad/hacked@v1에 의존하고, V1이 악성 버전으로 옮겨지면 당함
      composite actions의 경우가 그렇고, JS actions도 의존성을 잠그지 않고 action 설정 시점에 최신 패키지를 가져오면 같은 문제가 생김
      전부 전이적으로 fork해서 직접 고정하고 계속 업데이트해야 함
    • renovate를 쓰고 있고, hash를 자동으로 고정하고 업데이트해 줌
      게으르게 지내다가 renovate PR이 열리면 새 hash만 검토하면 됨: https://docs.renovatebot.com/modules/manager/github-actions/...
  • CI를 로컬에서 테스트할 수 있게 하는 가장 좋은 방법은 지금까지는 검사들이 Nix로 실행 가능하게 만들고, CI 설정 자체는 최대한 단순하게 유지해 Nix만 호출하는 것이었음
    CI 설정의 boilerplate를 줄이는 측면에서 GitHub Actions는 함수를 지원하는 프로그래밍 언어임
    다만 함수 호출은 프로그램의 매우 제한된 위치, 즉 steps 안에서만 가능하고, 함수를 정의하려면 Git 저장소를 만들어야 함
    함수 호출 문법도 조금 특이해서 uses 키워드로 씀
    그래서 이 방식으로 제거할 수 없는 boilerplate가 많지만, GitHub Actions 안에는 일부를 해결하는 여러 YAML eDSL이 숨어 있음
    예를 들어 matrix로 반복을 만들 수 있지만 범용 반복은 아니고 매우 특정한 문법 위치에만 나타날 수 있음
    YAML 블록을 복사·붙여넣기하지 않고 진짜 중복 제거를 하려면, 과거에는 Nix와 Python으로 JSON을 생성했음
    지금은 RCL을 씀: https://rcl-lang.org
    이들은 모두 루프나 함수 호출을 원하는 곳 어디에나 둘 수 있는 범용 YAML 중복 제거기임

  • 보통 GitHub Actions를 쓰는 건 써야만 해서임
    내 경험상 최선의 전략은 사용을 최소화하는 것임
    바이너리나 shell script를 호출하고, GHA 세계에 대한 의존을 최대한 줄이면 로컬 테스트도 쉬워짐

    • 나도 그렇게 함
      로직의 90%를 Go 바이너리에 넣었고 GitHub Actions는 특정 단계에서 그것을 호출만 함
      결국 GHA에는 그나마 잘하는 일인 파이프라인용 로컬 UI 제공만 남음
      가장 좋은 점은 단위 테스트를 할 수 있고, 도구를 자기 파이프라인에서 직접 써볼 수 있으며, CLI만 곁에 있으면 로컬에서도 실행할 수 있다는 것임
    • 마이그레이션도 쉬워짐
      GitHub나 GitLab 등은 소스 코드 호스팅과 이벤트 트리거 플랫폼으로만 두고, 그 이벤트를 어떻게 처리할지는 직접 결정하는 편이 낫음
      CI 자체는 애플리케이션 코드의 얇은 CI 계층이 호출해 사용할 기능을 제공하는 별도 소스 관리 저장소여야 함
      그러면 꽤 현실적인 방식으로 CI를 로컬에서 실행할 수 있음
      Jenkins pipeline에서 쓰는 Groovy CI 라이브러리로 비슷한 걸 해봤지만, 많은 부분이 Jenkins를 전제로 해서 아주 단순하진 않았음
      특정 기반 플랫폼을 가정하지 않는 더 깔끔한 오픈소스 옵션이 있는지 궁금함
    • “보통 강제로 써야 해서 쓴다”는 건 Teams 같은 경우와 비슷함