본문 바로가기
개발

“GitHub Actions에서 어떤 프로그램이든 셸처럼 활용하기”

by mean. 2025. 4. 12.

 

github actions으로 모든 프로그램을 사용하는 방법

1. 들어가며

**“GitHub Actions”**는 많은 개발자들이 사용하는 CI/CD(지속적 통합 및 배포) 플랫폼입니다.

  • 저장소에 코드를 푸시하거나 PR(풀 리퀘스트)을 생성했을 때 자동으로 테스트가 돌게 만들 수 있고,
  • 조건에 따라 빌드, 배포까지 자동화할 수 있는 편리한 도구죠.

보통은 YAML 파일에 run: 블록을 작성할 때, 명령을 실행하는 **셸(shell)**을 지정합니다.

  • 예: shell: bash, shell: pwsh 등등.
  • Linux나 macOS는 기본적으로 bash, Windows는 pwsh가 지정됨.

그런데 “실제로는 어떤 실행 파일(프로그램)이든, $PATH에만 있다면 shell로 사용 가능하다” 라는 사실, 알고 계셨나요?

이번 글에서는 깃허브 액션의 shell 키워드를 통해 임의의 프로그램을 셸처럼 활용하는 방법을 소개해 보겠습니다.


2. 기본 동작 원리: shell키와 {0} 플레이스홀더

2.1 shell: bash가 실제로 하는 일

보통 깃허브 액션에서 다음과 같이 작성하면:

- name: My Step
  run: echo "Hello, world!"
  shell: bash
  • 깃허브 액션은 내부적으로 임시 스크립트 파일을 생성하고,
  • bash --noprofile --norc -eo pipefail path/to/script.sh 형태로 명령을 실행하게 됩니다.
  • 즉, shell: bash가 선언되어 있으면 깃허브 액션은 $PATH 상에서 bash를 찾은 뒤, 특정 옵션들과 함께 실행시키는 식이죠.

2.2 실제로는 {0} 자리 표시자를 사용해 스크립트를 전달

깃허브 액션은 우리가 run: 블록에 적은 명령어들을 하나의 작은 스크립트 파일로 만들어 내며, 해당 파일 경로를 {0}라는 플레이스홀더로 전달합니다.

예를 들어,

shell: bash
run: |
  echo "Hello"
  echo "World"

위 코드는 내부적으로 대략 다음과 같은 커맨드를 실행합니다.

bash --noprofile --norc -eo pipefail /tmp/runner-script-XXXXXX.sh

({0} 자리에 /tmp/runner-script-XXXXXX.sh가 들어가는 형태)

2.3 임의의 프로그램도 “셸”로 지정 가능

중요 포인트는 이 “bash” 자리에 사실상 아무 실행 파일이나 들어갈 수 있다는 점입니다.

조건은 딱 하나:

  1. 해당 실행 파일이 깃허브 액션 러너(Runner) 환경의 $PATH 내에 존재해야 함.

예를 들어, shell: python으로 지정한다면, 깃허브 액션은

python /tmp/runner-script-XXXXXX.sh

이런 식으로 스크립트를 파이썬에게 넘기려고 시도할 것입니다.

단, 파이썬이 기본적으로 .py 파일을 실행하는 언어이므로, .sh 형식의 쉘 스크립트를 바로 이해하지 못합니다(문법 오류 발생).

즉, “Python이 셸 스크립트를 이해하려면 뭔가 중간 처리가 필요하다”는 문제가 생기죠.

그럼에도 불구하고, 이처럼 내가 원하는 어떤 실행 파일이든 넣어서, 그걸 깃허브 액션이 “셸”이라고 착각(?)하도록 만들 수 있습니다.


3. 예시: C 컴파일러나 goeval, tcc 등을 “셸”로 쓰기

3.1 tcc(작은 C 컴파일러)를 셸처럼?

어떤 커뮤니티에서는 “**tcc(작은 C 컴파일러)**를 셸처럼 써볼 수 있다!”라는 웃픈(?) 시도를 하기도 합니다.

  • tcc는 C 코드를 실시간으로 컴파일 & 실행할 수 있는 툴로 유명하죠.
  • “tcc가 셸처럼 동작한다”는 것은, 깃허브 액션이 생성한 임시 .sh 파일을 tcc가 인식하도록 코드를 가공해보는 실험적 시나리오일 수 있습니다.

사실 이는 실용적이라기보다, **“깃허브 액션이 어떤 실행 파일에든 스크립트를 넘길 수 있다”**는 것을 보여주는 재미있는 데모라고 보면 됩니다.

3.2 goeval로 스크립트 실행

또는 goeval 같은 도구를 이용해, 고(Go) 언어 코드를 즉석에서 평가(evaluate)할 수도 있습니다.

  • goeval 자체가 stdin이나 파일 입력을 바로 코드로 컴파일·실행해주는 방식이 아니면, {0}로 넘어가는 임시 스크립트 파일을 어떻게 처리할지 추가 설정이 필요할 수 있습니다.

하지만 아이디어 자체는 “YAML에서 Go 코드를 직접 적어서, 깃허브 액션이 임시 파일을 만들고, goeval이 그 파일을 실행” 하는 것이죠.

  • 다만 goeval이 아직 파일 입력을 완벽히 지원하지 않으면, 중간에 쉘 트릭이 필요해질 수 있습니다.

이처럼 **“어떤 언어든지 깃허브 액션이 셸 스크립트라고 넘겨주면 받아줄 수 있다면, 그 언어로 CI 로직을 직접 짤 수도 있다”**는 말입니다.


4. 보안과 경로 이슈: 주의할 점

4.1 $PATH 우선순위, 가짜 bash

GitHub Actions에서는 shell: bash를 쓴다고 해서, /bin/bash절대 경로로 호출하는 게 아닙니다.

  • 실제론 $PATH를 탐색해서 bash라는 이름의 실행 파일을 찾습니다.
  • 즉, 만약 $PATH 상단에 **“bash라는 가짜 바이너리”**가 있다면, 깃허브 액션은 그걸 진짜 bash 대신 실행할 수도 있다는 얘기죠.

이는 곧 보안적 이슈로 이어질 수 있습니다.

  • CI 환경 내에서 $PATH를 변조하면, 예기치 못한 바이너리가 실행될 가능성이 있습니다.
  • “CI가 돌아가는 컨테이너나 VM을 누구나 접근할 수 있는 건 아니니까 괜찮겠지”라고 해도, 액션 재사용이나 오픈소스 PR 상황 등에선 사고가 날 수 있으므로 유의해야 합니다.

4.2 임시 스크립트 파일에 대한 주의

깃허브 액션은 우리 run: 블록을 임시 파일로 떨어뜨리고, 그걸 “셸”이라고 지정한 실행 파일에 넘깁니다.

  • 만약 이 임시 파일의 내용이나 위치가 유출되면, 민감한 정보가 들어갈 수도 있습니다(예: 토큰 등).
  • echo $GITHUB_ENV 등으로 중요한 값들을 출력해버리면, 로그에 노출될 위험도 있죠.

일반적으로는 GitHub Actions가 secrets 관련 정보를 잘 마스킹해주지만, 이런 “임의 프로그램 셸” 트릭을 사용할 때 예상치 못한 버그가 생길 수도 있으니, 민감정보를 다룰 때는 더욱 신중해야 합니다.


5. 이 기능이 왜 유용할까?

5.1 “CI에서 스크립트를 길게 작성하기 싫다”는 사람들에게

CI/CD 파이프라인을 구성할 때, 사람들은 흔히 YAML 안에 빼곡히 스크립트를 작성합니다.

  • 하지만 YAML은 구조적 데이터를 표현하기 좋은 도구이지, 복잡한 로직을 담기엔 좀 불편하죠.
  • “bash 10줄” 정도면 괜찮지만, 100줄 넘어가면 가독성 폭탄이 찾아옵니다.

이 때, “아예 CI 작업에 필요한 로직을 Makefile이나 Python / Go 스크립트로 정리해두고, 깃허브 액션에서는 간단히 그 스크립트를 한 줄로 호출만 하자!”라는 방법이 있습니다.

  • 그중 하나의 극단적인 예로, “Python을 셸로 지정해서 그 임시 파일을 곧바로 실행” 같은 기법을 생각해볼 수도 있다는 것이죠(물론, 그 전에 임시 파일이 .py 형태여야 하는 등 추가 작업이 필요합니다).

5.2 로컬 디버깅과 CI 디버깅을 쉽게 통일

많은 개발자는 **“로컬에서의 빌드/테스트”**와 CI 환경에서의 자동화가 최대한 비슷하게 동작하길 원합니다.

  • 일반적으로 Makefile, npm script, 혹은 Go 커맨드 등으로 로컬에서 실행한 뒤,
  • CI에서도 똑같은 명령을 실행하면 디버깅이 편하죠.

그렇다면 깃허브 액션에서는 “shell” 자리에 바로 그 프로그램(예: go run, make, python, …)을 지정해주면, YAML 안에서 삽질을 줄이고 최대한 로직을 외부 스크립트에 몰아넣는 식으로 작업할 수 있습니다.

5.3 특별한 이벤트, 고급 트릭과의 결합

여기서 한 걸음 더 나아가,

  • repository_dispatch 이벤트를 자동으로 처리해주는 “중앙 집중식 릴리스 파이프라인” 구성,
  • Go나 Python 코드를 통해 GitHub API를 더 세밀하게 호출,
  • 임시 파일을 어떻게 가공해서 통과시키는지 등등,

고급 트릭들과 결합하면 정말 신기한 자동화를 만들 수도 있습니다.

HN(해커 뉴스) 커뮤니티나 yossarian.net에서 다뤄진 사례들을 보면,

  • repository_dispatch 이벤트 이름에 와일드카드를 사용해 매칭한다거나,
  • shell: python 형태로 무언가를 실행하는데 --some-custom-arg를 붙여서 어떤 작업을 하게 만든다거나,
  • 스스로 만든 CLI(goeval, pyexec 등)을 셸 대신 쓰는 것 등이 소개되고 있죠.

6. 실전 예시: Go 코드를 직접 실행하기

아래는 상상 속의 간단한 예시입니다. (실제 동작 보장은 없지만 개념 시연용으로 작성)

name: Example with goeval
on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Install goeval
        run: |
          curl -L https://example.com/goeval/install.sh | bash
          echo "::add-path::$(pwd)/goeval/bin"  # goeval이 PATH에 있도록 처리

      - name: My Go code step
        run: |
          import (
            "fmt"
            "os"
          )

          func main() {
            fmt.Println("Hello from Go!")
            fmt.Printf("Args: %v\n", os.Args)
          }
        shell: goeval
  1. shell: goeval 부분이 핵심입니다.
  2. 깃허브 액션은 내부적으로 임시 스크립트 파일을 만들고, 그 경로를 {0}라는 자리표시자로 goeval에 넘겨줍니다.
  3. goeval이 그 임시 스크립트를 “Go 코드”로 인식해 실행하게 만드는 구조죠.

물론 실제 환경에서 goeval이 저런 식으로 스크립트 파일을 바로 받아들일 수 있도록 설계되어 있어야겠지만, 개념상으로는 충분히 가능해 보입니다.

 


7. 주의사항: 동작 실패와 디버깅

7.1 예기치 못한 에러

  • 프로그램이 .sh 스크립트를 이해하지 못하는 경우
  • {0} 자리에 전달되는 파일 확장자 문제
  • $PATH 문제(“python”이라고 했는데 실은 다른 파이썬이 호출되는 상황 등)

이런 이유로 실패할 수 있습니다.

특히 “shell로 지정된 프로그램이 사실상 스크립트 파일을 어떻게 해석해야 하는지 전혀 모른다”면, 문법 에러만 뜨고 끝나기 쉽습니다.

(“shell에서 echo "Hello"라는 문법이 통과되는지, 프로그램이 파이썬 코드로 인식하려는지 등등)

7.2 로그 찍기 & -x 플래그

bash를 사용할 땐 -x 플래그를 통해 실행되는 모든 명령어를 로그로 찍어서 디버깅할 수 있습니다.

  • GitHub Actions YAML에서 shell: bash -x {0} 식으로 커스텀하게 넣어줄 수도 있죠.

하지만 “bash -x”가 아닌 다른 프로그램을 셸로 설정했다면, 그 프로그램에서 지원하는 디버그 모드를 별도로 켜줘야 할 수 있습니다.

(파이썬이면 -u(unbuffered)나 -m pdb 등을, goeval이면 별도의 디버그 옵션을 등등)


8. 보안적 시사점 & 한계

깃허브 액션은 기본적으로 별도 격리된 러너(컨테이너/VM)에서 동작하기 때문에, 어느 정도 안전장치가 있습니다. 하지만 오픈소스 리포지토리에서 외부인의 PR까지 액션이 돌도록 허용하는 경우에는 정말 조심해야 합니다.

  • Pull Request가 공격 코드를 섞어두면, shell이 오염될 수 있음
  • $PATH 우선순위를 악용해 “가짜 python”이나 “가짜 bash”를 슬쩍 끼워넣을 수도 있음
  • CI에서 쓸모없는 토큰을 (예: ACTIONS_STEP_DEBUG 등) 출력해서 로그로 노출시키는 공격

따라서 이러한 트릭을 활용할 때는,

  • private 리포지토리 또는 멤버 제한이 확실한 리포지토리에서
  • 리뷰 절차를 확실히 거쳐서
  • 필요한 경우에만 제한적으로 써야 합니다.

9. 실무 팁: 깃허브 액션에서 할 일 최소화하기

일부 개발자들은 **“GitHub Actions에서 스크립트 로직을 많이 넣을수록 관리가 복잡해진다”**고 지적합니다.

  • YAML 파일은 버전 관리를 잘하려면 “조금” 유지보수하기 쉽지 않은 구조입니다.
  • 코드 에디터의 자동 완성이나 디버깅 툴을 쓰기에도 제한적이고요.

그래서 다음 전략을 권장합니다.

  1. 빌드 로직을 Makefile(혹은 npm, Gradle, etc.)에 최대한 담기
  2. 깃허브 액션에서는 shell: bash(혹은 shell: make) 정도로 간단히 실행
  3. 별도 스크립트(예: Python, Go)로 중요한 로직을 작성하고, 깃허브 액션에선 한 줄로 호출하기

이렇게 하면, 로컬에서도 Makefile(또는 같은 스크립트)로 쉽게 테스트 가능하고, CI에서도 그걸 그대로 돌리면 됩니다.

  • 덕분에 “내 로컬에선 잘 되는데 CI에서만 깨진다”는 상황을 줄일 수 있고,
  • CI가 깨지면 스크립트 자체를 디버깅하면 되므로, 개발 생산성이 올라갑니다.

 


10. 정리: github actions에서 “셸”을 자유자재로 다루자

  • GitHub Actions의 shell: 키워드는 기본 셸을 bash나 pwsh로 잡아주지만, 실제로 $PATH에 있는 어떤 실행 파일이든 지정할 수 있습니다.
  • {0}라는 플레이스홀더를 통해 깃허브 액션이 임시 스크립트 파일을 그 실행 파일에 넘겨주죠.
  • 보안, 파일 확장자, 실제 문법 등을 조심하면서, Python이나 Go, 심지어 tcc같은 C 컴파일러를 셸처럼 활용할 수 있습니다.

이러한 트릭이 “아직 실험적”이라고 느껴질 수도 있습니다. 그러나 깃허브 액션의 자유도가 얼마나 큰지 보여주는 예시이기도 합니다. 적절히 응용한다면, CI 파이프라인에서 매우 독창적인 자동화를 구축할 수 있을 거예요.


11. 마무리: 언제, 어떻게 적용하면 좋을까?

  • 작은 프로젝트: 로컬과 CI의 환경을 통일하고 싶을 때. Make나 Python 스크립트를 셸로 선언해, YAML 로직을 최소화할 수 있음.
  • 실험적/연구적 목적: 깃허브 액션이 임시 스크립트로 넘기는 파일을 다른 언어로 파싱해본다거나, 특정 목적(예: 릴리스 자동화)에 특화된 CLI를 직접 셸처럼 써볼 수 있음.
  • 보안에 민감한 프로젝트: 가짜 바이너리나 $PATH 변조가 없도록, 환경 변수 관리와 액션 설정을 엄격하게 해야 함. public 리포지토리에서 여러 명이 PR을 날리는 상황이면 좀 더 신중해야 함.

결국은 “선택은 개발자의 자유”입니다.

  • 단순히 bash/pwsh만 사용해도 된다면, 복잡하게 가지 않아도 됩니다.
  • 하지만 “나만의 특별한 환경” 혹은 “기존 스크립트를 그대로 CI에 녹이고 싶다”라는 요구가 있다면, 이 강력한 트릭을 연구해볼 가치가 충분합니다.

 


12. 더 알아보기 & 참고 자료

  1. GitHub Actions 문서:
  2. yossarian.net 블로그:
  3. Hacker News 토론:
    • 깃허브 액션에서 와일드카드 repository_dispatch, 중앙 집중식 릴리스 파이프라인 등에 대한 다양한 아이디어가 오갑니다.
    • 실제로 써본 사람들이 공유한 디버깅 경험, 장단점도 알 수 있죠.
  4. ScriptHandler.cs (GitHub Actions Runner 코드):
    • GitHub Actions Runner 오픈소스 내의 ScriptHandler.cs 파일을 살펴보면, 깃허브 액션이 셸 실행 파일에 어떤 인수들을 넘기는지 원리 파악 가능.

13. 결론

“깃허브 액션에서 ‘run:’ 블록을 실행할 때 사용하는 셸은 bash/pwsh뿐인가?”

아닙니다. 사실상 원하는 어떤 프로그램이든, 한 번 “셸”이라고 속여 넘겨줄 수 있습니다.

이를 통해:

새로운 언어로 CI 로직을 작성하는 모험을 해볼 수도 있고,

  • 보안, 디버깅 측면의 다양한 고려 사항을 접하게 되고,
  • CI/CD 자동화를 훨씬 유연하게 구성할 수도 있습니다.

물론, 무조건 이 방식을 써야 한다는 건 아니지만,

  • CI YAML 내부 로직을 가능한 한 단순화하고,
  • 실제 비즈니스 로직외부 스크립트(python, go, make 등)에 담아두고,”
  • GitHub Actions에선 간단히 그 실행 파일만 호출하는 전략을 취하면,
  • 장기적으로 유지보수성과 생산성을 크게 높일 수 있다는 점에서 매력적인 방법이 될 것입니다.

한 번 시도해보세요!

  • 깃허브 액션에 조금 색다른 접근법을 도입해보면,
  • CI/CD 자동화가 얼마나 유연하고 파워풀해질 수 있는지 체감하게 될 겁니다.

(끝)

위에서 다룬 내용들이 **“github actions으로 모든 프로그램을 사용하는 방법”**에 대한 전반적인 개념과 실제 활용 아이디어를 제공해 드렸기를 바랍니다.

깃허브 액션을 조금 더 깊이 파고들어 보고 싶다면, 이번 글의 정보를 바탕으로 흥미로운 실험을 시도해 보세요.

CI/CD 워크플로우가 지루하고 딱딱하기만 한 것이 아니라, 상상 이상으로 창의적일 수 있다는 걸 직접 느끼게 되실 겁니다!

  • 요약: GitHub Actions → shell 키 → $PATH 상의 어떤 실행 파일이든 지정 가능 → 내부적으로는 {0}에 임시 스크립트 전달 → 보안/경로 이슈만 조심하면 강력하게 활용 가능!

감사합니다. 즐거운 코드 자동화 라이프 되시길!

728x90