Python을 배워보자

Git Push 알림 시스템: Python으로 Windows 트레이 아이콘과 팝업 구현(터미널 없이 Git Push 알림 실행)하기

_Blue_Sky_ 2025. 2. 27. 22:13
728x90
 
안녕하세요! 이번 포스트에서는 Git 저장소에서 상대방이 push를 하면 즉시 알림을 받고, 이를 Windows 시스템 트레이에 아이콘으로 표시하며, 내가 pull해서 병합할 때까지 1분 주기로 팝업 알림을 띄우는 Python 프로그램을 만들어 보겠습니다. 이 과정에서 주기적 체크를 통해 Git 상태를 모니터링하고, 사용자 친화적인 인터페이스를 제공하는 방법을 단계별로 설명하겠습니다.
 

목표
  1. Git 저장소 모니터링: 로컬 Git 저장소에서 원격 저장소의 push 여부를 주기적으로 확인.
  2. Windows 트레이 아이콘: 시스템 트레이에 아이콘을 띄워 프로그램이 실행 중임을 표시.
  3. 주기적 팝업: 1분마다 팝업으로 알림을 띄우고, 병합이 완료될 때까지 반복.
  4. Python 기반: 간단하고 확장 가능한 코드를 작성.

준비물
  • Python: 3.x 버전 설치 (최신 버전 권장).
  • 필요한 라이브러리:
    • pygit2: Git 저장소를 다루기 위한 Python 라이브러리.
    • pystray: 시스템 트레이 아이콘 생성.
    • plyer: 팝업 알림 생성.
    • pillow: 트레이 아이콘 이미지 처리.
  • Git 저장소: 모니터링할 로컬 Git 저장소 준비.
  • 아이콘 이미지: .ico 형식의 작은 아이콘 파일 (예: 16x16 또는 32x32 픽셀).
라이브러리 설치 명령어:
pip install pygit2 pystray plyer pillow

전체 코드 구조
코드는 다음과 같은 흐름으로 작성됩니다:
  1. Git 저장소 상태 확인 함수.
  2. 트레이 아이콘 설정 및 실행.
  3. 주기적 체크와 팝업 알림 로직.
아래는 완성된 소스 코드입니다. 이후 각 부분을 자세히 설명하겠습니다.
 
 
import pygit2
import pystray
from PIL import Image
import time
from plyer import notification
import threading
import os

# Git 저장소 경로 (사용자 로컬 저장소 경로로 변경 필요)
REPO_PATH = "C:/path/to/your/repo"
CHECK_INTERVAL = 60  # 1분 (60초)
ICON_PATH = "icon.ico"  # 아이콘 파일 경로

# Git 상태 확인 함수
def check_git_status():
    repo = pygit2.Repository(REPO_PATH)
    branch = repo.head.shorthand  # 현재 브랜치 이름
    remote_name = f"origin/{branch}"

    # 원격 저장소 가져오기
    for remote in repo.remotes:
        remote.fetch()

    # 로컬과 원격 커밋 비교
    local_commit = repo[repo.head.target]
    remote_commit = repo.lookup_reference(f"refs/remotes/{remote_name}").target

    return local_commit.hex != remote_commit.hex

# 알림 및 주기적 체크 함수
def monitor_repo(icon):
    icon.visible = True
    notified = False

    while icon.visible:
        if check_git_status():
            if not notified:
                notification.notify(
                    title="Git Push Detected!",
                    message="Someone pushed to the repo. Pull and merge now!",
                    app_icon=ICON_PATH,
                    timeout=10
                )
                notified = True
            else:
                notification.notify(
                    title="Reminder: Merge Pending",
                    message="You still need to pull and merge the latest changes!",
                    app_icon=ICON_PATH,
                    timeout=10
                )
        else:
            notified = False  # 병합 완료 시 알림 초기화
        time.sleep(CHECK_INTERVAL)

# 트레이 아이콘 설정
def setup_tray():
    image = Image.open(ICON_PATH)
    menu = pystray.Menu(pystray.MenuItem("Exit", lambda: icon.stop()))
    icon = pystray.Icon("Git Monitor", image, "Git Push Monitor", menu)

    # 모니터링을 별도 스레드에서 실행
    thread = threading.Thread(target=monitor_repo, args=(icon,))
    thread.daemon = True
    thread.start()

    icon.run()

# 메인 실행
if __name__ == "__main__":
    if not os.path.exists(REPO_PATH):
        print("Error: Git repository path does not exist!")
    else:
        setup_tray()

코드 설명: 단계별 분석
1. Git 상태 확인 (check_git_status)
  • pygit2를 사용해 로컬 Git 저장소에 접근합니다.
  • 현재 브랜치와 원격 브랜치(origin/브랜치명)의 커밋 해시를 비교합니다.
  • remote.fetch()로 원격 저장소의 최신 상태를 가져오고, 로컬과 원격이 다르면 True를 반환합니다.
2. 주기적 체크와 알림 (monitor_repo)
  • check_git_status()True를 반환하면 누군가 push한 것이므로 알림을 띄웁니다.
  • 첫 번째 알림 후에는 "Reminder" 메시지로 1분마다 반복 알림을 띄웁니다.
  • 병합이 완료되면 (local_commit == remote_commit) 알림이 초기화됩니다.
  • plyer.notification으로 Windows 팝업을 생성하며, timeout=10은 10초간 표시를 의미합니다.
3. Windows 트레이 아이콘 (setup_tray)
  • pystray로 시스템 트레이에 아이콘을 추가합니다.
  • PIL.ico 파일을 로드하며, "Exit" 메뉴를 통해 프로그램 종료 가능.
  • 모니터링은 메인 스레드를 막지 않도록 별도 스레드에서 실행됩니다.
4. 환경 설정
  • REPO_PATH: 모니터링할 Git 저장소 경로를 지정하세요.
  • ICON_PATH: 트레이와 알림에 사용할 .ico 파일 경로를 지정하세요.
  • CHECK_INTERVAL: 60초(1분)로 설정되어 있습니다.

실행 방법
  1. 코드를 파일(예: git_monitor.py)로 저장합니다.
  2. REPO_PATHICON_PATH를 사용자 환경에 맞게 수정합니다.
  3. 터미널에서 실행:
    python git_monitor.py
  4. 트레이에 아이콘이 나타나고, 원격 저장소에 push가 발생하면 팝업이 뜹니다.

추가 개선 아이디어
  • 다양한 저장소 모니터링: 여러 Git 저장소를 동시에 체크하도록 확장.
  • 커스터마이징: 알림 메시지나 주기를 사용자 입력으로 설정 가능하게.
  • 로그 남기기: 알림 기록을 파일에 저장.
  • GUI: 간단한 설정 창 추가.

 

728x90

이 프로그램은 Git 협업 환경에서 실시간 알림을 받고 병합 지연을 방지하는 데 유용합니다. Python의 강력한 라이브러리를 활용해 간단히 구현할 수 있으며, 필요에 따라 쉽게 확장할 수 있습니다. 질문이나 피드백이 있다면 댓글로 남겨주세요!

 


맞아요! python git_monitor.py로 실행하면 터미널 창이 계속 열려 있어야 프로그램이 동작하니 불편할 수 있죠. 이 문제를 해결하려면 터미널 창 없이 백그라운드에서 실행되도록 두 가지 방법을 적용할 수 있습니다:
  1. Python 스크립트를 .pyw로 변경: 터미널 창을 띄우지 않고 실행.
  2. EXE 파일로 변환: PyInstaller를 사용해 독립 실행 파일로 만들어 터미널 없이 실행.
아래에서 각 방법을 블로그 형식으로 자세히 설명하겠습니다.

터미널 없이 Git Push 알림 실행하기

 

방법 1: .pyw 파일로 실행
Python에서 .py 대신 .pyw 확장자를 사용하면 터미널 창(콘솔)이 열리지 않고 스크립트가 백그라운드에서 실행됩니다. 이 방법은 간단하고 추가 설치 없이 가능합니다.

 

단계별 구현
  1. 파일 이름 변경:
    • 기존 git_monitor.pygit_monitor.pyw로 이름을 바꿉니다.
    • 코드 내용은 그대로 유지하면 됩니다.
  2. 실행:
    • 파일을 더블클릭하거나, 명령어로 실행:
       
      pythonw git_monitor.pyw
    • pythonw는 Python의 GUI 실행 모드로, 콘솔 없이 실행됩니다.
  3. 확인:
    • 시스템 트레이에 아이콘이 나타나고, 터미널 창 없이 알림이 동작합니다.
장점
  • 추가 설치 없이 간단히 해결.
  • 개발 중 빠르게 테스트 가능.
단점
  • 오류 메시지가 터미널에 표시되지 않으므로 디버깅이 어려울 수 있음.
  • Python이 설치되어 있어야 실행 가능.

 

 
 
방법 2: PyInstaller로 EXE 파일 생성
더 깔끔한 해결책으로, PyInstaller를 사용해 스크립트를 단일 실행 파일(.exe)로 변환하면 Python 설치 없이도 실행 가능하고 터미널 창이 나타나지 않습니다.
준비물
  • PyInstaller 설치:
    bash
     
    pip install pyinstaller
단계별 구현
  1. 기존 코드 수정:
    • 오류 로그를 파일에 남기도록 약간 수정하면 디버깅에 유용합니다. 아래는 수정된 코드 일부:
      python
       
      import logging
      
      # 맨 위에 추가
      logging.basicConfig(filename="git_monitor.log", level=logging.INFO)
      
      # check_git_status 함수 내 예외 처리
      def check_git_status():
          try:
              repo = pygit2.Repository(REPO_PATH)
              branch = repo.head.shorthand
              remote_name = f"origin/{branch}"
              for remote in repo.remotes:
                  remote.fetch()
              local_commit = repo[repo.head.target]
              remote_commit = repo.lookup_reference(f"refs/remotes/{remote_name}").target
              return local_commit.hex != remote_commit.hex
          except Exception as e:
              logging.error(f"Error in check_git_status: {e}")
              return False
  2. EXE 파일 생성:
    • 터미널에서 다음 명령어 실행:
      pyinstaller --onefile --noconsole --icon=icon.ico git_monitor.py
    • --onefile: 단일 실행 파일로 생성.
    • --noconsole: 콘솔 창을 띄우지 않음.
    • --icon=icon.ico: 실행 파일에 아이콘 적용 (선택 사항).
  3. 결과물 확인:
    • dist 폴더에 git_monitor.exe가 생성됩니다.
    • 이 파일을 더블클릭하면 터미널 없이 백그라운드에서 실행됩니다.
  4. 실행 테스트:
    • 트레이 아이콘이 나타나고, 알림이 정상적으로 동작하는지 확인하세요.
    • 오류가 발생하면 git_monitor.log 파일을 열어 확인합니다.
장점
  • Python 없이도 실행 가능 (배포에 유리).
  • 터미널 창 완전 제거.
  • 아이콘 추가로 깔끔한 UI 제공.
단점
  • PyInstaller 설치 및 설정 필요.
  • 파일 크기가 커질 수 있음 (약 10~20MB).

추가 팁: 시작 프로그램에 등록
Windows에서 부팅 시 자동 실행되도록 설정하려면:
  1. git_monitor.exeshell:startup 폴더에 바로가기 추가:
    • Win + R -> shell:startup 입력 -> 폴더 열림.
    • .exe 파일의 바로가기를 여기에 붙여넣기.

어떤 방법을 선택할까?
  • 빠르고 간단히: .pyw로 변경해서 사용.
  • 완성도 높게: PyInstaller.exe 파일 생성 후 배포.
개인적으로 .exe 방법을 추천드립니다. 초기 설정은 조금 번거롭지만, 터미널 없이 깔끔하게 실행되고 다른 사람과 공유하기도 편리하니까요.

최종 코드 (EXE용)
git_monitor.py.exe로 변환하기 위한 최종 버전입니다:
python
 
import pygit2
import pystray
from PIL import Image
import time
from plyer import notification
import threading
import os
import logging

REPO_PATH = "C:/path/to/your/repo"
CHECK_INTERVAL = 60
ICON_PATH = "icon.ico"
logging.basicConfig(filename="git_monitor.log", level=logging.INFO)

def check_git_status():
    try:
        repo = pygit2.Repository(REPO_PATH)
        branch = repo.head.shorthand
        remote_name = f"origin/{branch}"
        for remote in repo.remotes:
            remote.fetch()
        local_commit = repo[repo.head.target]
        remote_commit = repo.lookup_reference(f"refs/remotes/{remote_name}").target
        return local_commit.hex != remote_commit.hex
    except Exception as e:
        logging.error(f"Error in check_git_status: {e}")
        return False

def monitor_repo(icon):
    icon.visible = True
    notified = False
    while icon.visible:
        if check_git_status():
            if not notified:
                notification.notify(
                    title="Git Push Detected!",
                    message="Someone pushed to the repo. Pull and merge now!",
                    app_icon=ICON_PATH,
                    timeout=10
                )
                notified = True
            else:
                notification.notify(
                    title="Reminder: Merge Pending",
                    message="You still need to pull and merge the latest changes!",
                    app_icon=ICON_PATH,
                    timeout=10
                )
        else:
            notified = False
        time.sleep(CHECK_INTERVAL)

def setup_tray():
    image = Image.open(ICON_PATH)
    menu = pystray.Menu(pystray.MenuItem("Exit", lambda: icon.stop()))
    icon = pystray.Icon("Git Monitor", image, "Git Push Monitor", menu)
    thread = threading.Thread(target=monitor_repo, args=(icon,))
    thread.daemon = True
    thread.start()
    icon.run()

if __name__ == "__main__":
    if not os.path.exists(REPO_PATH):
        logging.error("Git repository path does not exist!")
    else:
        setup_tray()

 
이제 터미널 창 걱정 없이 Git 모니터링을 즐길 수 있습니다! .pyw로 빠르게 테스트하거나, PyInstaller로 완성도 높은 실행 파일을 만들어 보세요. 질문이 있다면 언제든 물어보세요!
 
728x90