[python] gitHub REST API 를 이용한 자동 업데이트 프로젝트 - (2)

KimJiHong·2023년 11월 4일
1

My Project

목록 보기
1/3

지난게시글 - (1) 과 이어지는 게시글 입니다!

INTRO.

내가 만든 프로젝트는 cx_Freeze 를 사용해서 exe 실행파일로 패키징해 배포하고 있다.

만약 readme.txt 라는 파일 하나만 새로 업데이트 되었는데

변경되지 않은 파일도 같이 다운받아 한번에 덮어씌우게 된다면 관리하는 방법에서 편할지 몰라도

용량관리 측면에서는 많이 비효율 적이라 생각해

새로 업데이트 된 파일만 다운받아 해당 파일이 있는 경로에 업데이트 파일을 덮어씌우는 로직으로 구현하게 되었다.


이전 게시글의 END-Think 부분


Logic

아래 예시 사진을 보면

만약 내가 file1.py, file4.py, file5.py 3개의 파일만 업데이트가 되었다고 해보자.

만약 업데이트된 파일 구조가 원본의 파일 구조와 동일하지 않는다면
어떤 파일위에 덮어씌울 것 인지 매우 어려워진다.

업데이트된 파일명을 검색해 원본 파일에서 경로를 추출해서 덮어쓰기를 하는 방식으로 한다면

서로 다른 디렉토리에 동일한 이름을 가진 파일이 존재한다면 어느 경로에 덮어써야 하는지 애매해져 경로 검색 방법은 사용하지 않았다.

git 수정된 파일만 추출

그럼 어떻게 변경된 파일만 원본 구조를 만족하여 추출할 수 있을까?

아래의 git 명령어를 통해 변경사항이 있는 파일만 추출할 수 있고
또 해당 파일을 압축 파일로 저장할 수 있다.

$ git archive -o test.zip HEAD $(git diff --name-only HEAD^)
>> 바로 이전 커밋과 현재 커밋 사이의 수정사항을 test.zip 으로 추출

$ git archive -o test.zip HEAD $(git diff --name-only v1.0.2..v1.0.5)
>> tag name 이 v1.0.2 부터 v1.0.5 까지의 수정사항을 test.zip 으로 추출


update_new_assets.py

update_files class

나는 update_new_assets.py 라는 파일을 만들고

해당 파일안에 update_files 라는 클래스로 정의했다.

# auto_updater.py

update_new_assets.update_files(update_temp_DIR= update_temp_DIR)
# update_new_assets.py
import os, shutil

class update_files:
    def __init__(self, update_temp_DIR):
        self.BASE_PATH = f".\\{update_temp_DIR}"

        self.fileUpdate(self.BASE_PATH) # 디렉토리 안으로 이동 후 순회 시작
        
        shutil.rmtree(f".\\{update_temp_DIR}") # 끝난뒤 업데이트 파일 삭제
        
    # update_temp_DIR 디렉토리 내 파일들을 순회하며 원본파일 위에 덮어 씌우기
    def fileUpdate(self, now_checkPath):
    
		# ...

먼저 압축 해제한 디렉토리 명 update_temp_DIR 을 인자로 받아
업데이트 파일의 기본 경로로 설정했다.

그리고 update_temp_DIR 디렉토리 내 파일을 하나씩 순회하여
원본 파일 위헤 덮어 씌우는 작업을 진행하는 fileUpdate 함수를 호출하도록 하였다.

fileUpdate function

이제 update_temp_DIR 디렉토리 내 저장된 것이 디렉토리인지 파일인지에 동작 방식을 다르게 해야 한다.

  • is Directory ?
    • 디렉토리 안으로 이동 후 해당 디렉토리 내 파일 탐색 (fileUpdate 함수 재호출)
      즉, 재귀 호출(recursive call) 방식으로 동작해야 한다!
  • is File ?
    • shutil 의 copy2 함수를 사용해 원본 파일에 덮어 씌우기

그럼 해당 로직을 코드로 작성해보자!

class update_files:
	# ...
    def fileUpdate(self, now_checkPath):
    	# now_checkPath 디렉토리 내 파일과 디렉토리 list 가져오기
    	itemList = os.listdir(now_checkPath)
        
        for item in itemList:
            # 현재 item 의 경로
            nowItem_path = os.path.join(now_checkPath, item)
			# 원본 파일의 경로
            originFile_path = ".\\" + os.path.relpath(nowItem_path, self.BASE_PATH)

            if os.path.isfile(nowItem_path):
                # nowItem_path 를 복사하여 originFile_path 로 덮어쓰기
                shutil.copy2(nowItem_path, originFile_path)
            
            elif os.path.isdir(nowItem_path):
                # 존재하는 디렉토리 시
                if os.path.exists(originFile_path):
                    # 함수 재호출
                    self.fileUpdate(nowItem_path)
                
                # 존재하지 않는 디렉토리 시
                else:
                    create_path = ".\\" + os.path.relpath(now_checkPath, self.BASE_PATH)
                    shutil.move(nowItem_path, create_path)

원본 파일의 경로 originFile_path 와 업데이트 파일의 경로 nowItem_path 를 추출하고

shutilcopy 메소드를 사용해 원본 파일 위에 업데이트 파일을 덮어 씌움으로써

자동 업데이트 프로그램의 로직을 완성시켰다!


END - Think

git archive 명령어를 통해서 삭제된 파일 또는 디렉토리를 따로 추적하여 test.zip 에 저장하지 않는다.

그래서 만약 삭제된 파일이나 디렉토리가 존재하게 된다면

새로운 업데이트 파일 전체를 가져와 기존 파일을 대체 하거나

diff 명령어를 통해 삭제된 디렉토리나 파일의 경로를 추척해 해당 경로를 명시한 파일을 업데이트 파일에 같이 포함시켜서 클라이언트가 해당 경로의 파일이나 디렉토리를 삭제하는 새로운 로직이 필요하다.

내가 배포중인 서비스에서 사용중인 방법은 메이저 버전이 바뀌게 된다면 새로운 파일 전체를 가져와 기존 파일을 대체하는 방법을 사용하고 있다.

예를 들어, v1.4.9 버전에서 v2.1.0 버전으로 업데이트가 되었을 때
새로운 파일 전체를 가져와 기존 파일을 대체하게 된다.

참고 version 표시법 간단 설명

v1.5.9 일 때

  • Major version - 하위 버전과 호환되지 않는 변화 (v1.5.9 에서의 1)
  • Minor version - 하위 버전과 호환되는 변화 (v1.5.9 에서의 5)
  • Patch version - 버그 수정 (v1.5.9 에서의 9)


전체 소스코드 gitHub

profile
대충 할거면 시작도 하지 말자.

0개의 댓글