개발자 인생 첫 PR!
https://github.com/electron-userland/electron-builder/pull/8539
결론적으로 얘기하자면 결국 반려 당하긴 했지만
개발자로 있으면서 말로만 들어봤던 "오픈소스 기여" 라는 것을 한 번이나 해볼 수 있을까? 막연하게만 생각하던 것을 내 예상보다 훨씬 빠르게 시도를 해볼 수 있었고 비록 반영이 되진 않았지만 아직까지도 왜 안됐는지 살짝 이해가 안 가가기도 하고 나름 첫 PR 이니 만큼 그 과정을 블로그에 다뤄보고 싶어 글을 써보고자 한다!
(사실 말만 거추장스럽지 언제 반려돼도 이상하지 않는 PR 이라고 생각은 한다)
어쩌다가?
https://github.com/electron-userland/electron-builder
회사에서 electron 으로 데스크탑 트레이 프로그램 프로젝트를 하는 중 만든 프로그램이 윈도우 뿐만 아니라 몇 가지 설정만 해주면 linux 에서도 호환이 가능해보여 linux 환경에서도 적용을 해보고 있었다.
현재 내가 구성한 프로젝트는 electron-builder 라이브러리의 electorn-updater 패키지를 통해 Github Release 가 됨에 따라 자동 업데이트 확인 및 설치를 진행하도록 해놓았는데
windows 에서는 문제 없이 진행이 되나
linux 에서는
업데이트 확인 -> (새로운 릴리즈 존재하면) 업데이트 파일 다운 -> 다운 완료 후 no such file or directory -> 업데이트 확인 -> 업데이트 파일 다운 -> ~~~
과정이 계속 반복이 되었다.
문제 확인
Github Release 에 있는 최신 버전을 잘 감지하였고 appimage 를 다운을 성공적으로 다 받았는데 왜 자꾸 no such file or directory 가 뜨는지 로그를 봤는데
[error] Error: Error: ENOENT: no such file or directory, unlink '/home/gukjang/Documents/project-1.4.1.AppImage'
이런 로그가 찍혀 있었고
저 경로가 뭔지 봤더니 코드 상에서 내가 process.env.APPIMAGE 로 지정해준 경로였다.
linux 용으로 빌드시 process.env.APPIMAGE 를 지정해주지 않으면 APPIMAGE env is not defined 에러가 뜨면서 빌드가 되질 않아서 설정해주었던걸로 기억한다.
왜 이 경로가 앱 업데이트하는거랑 관련이 있는지 내 코드를 먼저 살펴봤지만 뭔가 발견되진 않았고
결국 함수를 타고 타고 들어와 electron-builder 라이브러리 안으로 들어오게 되었다.
관련 내용은 electron-builder > packages > eletron-updater > src > AppImageUpdater.ts 에 doInstall 함수에서 찾아볼 수 있었다.
doInstall 함수를 보면
protected doInstall(options: InstallOptions): boolean {
const appImageFile = process.env["APPIMAGE"]!
if (appImageFile == null) {
throw newError("APPIMAGE env is not defined", "ERR_UPDATER_OLD_FILE_NOT_FOUND")
}
// https://stackoverflow.com/a/1712051/1910191
unlinkSync(appImageFile)
let destination: string
const existingBaseName = path.basename(appImageFile)
// https://github.com/electron-userland/electron-builder/issues/2964
// if no version in existing file name, it means that user wants to preserve current custom name
if (path.basename(options.installerPath) === existingBaseName || !/\d+\.\d+\.\d+/.test(existingBaseName)) {
// no version in the file name, overwrite existing
destination = appImageFile
} else {
destination = path.join(path.dirname(appImageFile), path.basename(options.installerPath))
}
execFileSync("mv", ["-f", options.installerPath, destination])
if (destination !== appImageFile) {
this.emit("appimage-filename-updated", destination)
}
const env: NodeJS.ProcessEnv = {
...process.env,
APPIMAGE_SILENT_INSTALL: "true",
}
if (options.isForceRunAfter) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.spawnLog(destination, [], env)
} else {
env.APPIMAGE_EXIT_AFTER_INSTALL = "true"
execFileSync(destination, [], { env })
}
return true
}
이렇게 되어 있는데
아까 위에서 언급했던 process.env.APPIMAGE 가 null 이면 APPIMAGE env is not defined 에러를 내뿜는다고 나와있고 에러의 내용은 ERR_UPDATER_OLD_FILE_NOT_FOUND 으로 보아 OLD_FILE 이 해당 경로에 있어야 한다는 뜻인 것 같았다.
나는 코드에서 경로를 /home/gukjang/Documents 로 지정을 해두었는데 업데이트 파일이 다운 완료되고 unlinksync 를 실행하려고 보니 해당 경로에 이전 버전의 파일이 없어서 no such file or directory 가 뜨는 것이었다.
내 생각
위 프로세스대로 업데이트가 성공적으로 진행되려면
Github Release 에서 최신 버전의 APPIMAGE 를 다 받아오기 전까지 현재 linux 시스템에서 실행되고 있는 버전의 APPIMAGE 가 process.env.APPIMAGE 에 지정된 경로에 위치하고 있어야 하는데
이 조건을 만족 시켜주면 앱 업데이트는
1. /home/{user}/.cache/{project}-updater/pending/ 폴더에 최신 버전의 APPIMAGE 가 다운된다.
2. 다운로드 완료 후 process.env.APPIMAGE 에 현재 실행 중인 버전의 APPIMAGE 가 존재하는지 확인한다.
3. 존재한다면 unlinkSync 로 파일은 삭제되고 ~/pending 폴더에 다운 받아진 최신 버전의 APPIMAGE 는
process.env.APPIMAGE 경로로 옮겨지면서 실행된다.
와 같은 방식으로 진행이 되는 것을 확인했다.
여기서 나는 약간의 의문점이 들었고 이 생각의 끝은 결국 PR 을 제출해보게 되었는데
이 프로그램이 만약 나 혼자만 쓰는 것이라면 process.env.APPIMAGE 도 내가 지정을 하였기 때문에 내가 알아서 이전 버전의 파일을 해당 경로에 놔둬두면 처음부터 아무 문제없이 업데이트 프로세스를 거치게 된다.
하지만 이 프로그램은 나 뿐만 아니라 회사 사람들이 다 쓰는 프로그램이 되어야하는데
그럼 자동 업데이트 기능을 쓰기 위해선 사용자들로 하여금 최초로 다운 받은 APPIMAGE 파일을 꼭 지정된 경로에 수동으로 옮겨두라고 고지를 해야하는건가?
싶은 의문점이 생겼다.
물론 시스템에서 최초로 다운 받은 APPIMAGE 를 처음부터 지정된 경로로 옮겨둔다면 그 후로는 업데이트 하는데에 지장이 없다.
문제는 제일 최초로 다운을 받으면 보통 /Downloads 폴더에나 저장되거나 이 다운로드 경로 또한 개인 사용자가 따로 설정을 할 수 있는데 이걸 일일이 수동으로 옮겨줘야 한다는 과정이 내 입장에선 많이 어색해보였다.
또 굳이 현재 버전의 프로그램을 삭제 (unlinkSync) 를 해야만 다음 버전을 사용할 수 있어야하나... 싶기도 하고...
과정이 너무 어색하여 나름의 방안으로 생각해본 2가지는
- find 같은 명령어를 사용해서 사용자의 기본 다운로드 폴더의 경로를 파악하여 process.env.APPIMAGE 를 동적으로 지정할 수 있게 한다.
- electron-builder 라이브러리를 수정한다.
인데
방법1은 권한 문제도 있고 이런 식으로 find 해서 지정을 하는게 맞는건지 의문이 들어 생각만 잠시 해보고 시도해보진 않았고
방법2 쪽으로 진행을 해보았다.
Try, try!
말은 거창하게 라이브러리 수정 이지만 솔직히 정말 뭐 한건 없다...
현재 문제는 지정된 APPIMAGE 위치에 파일이 존재하지 않고 unlinkSync 가 실행이 성공적으로 되지 않기 때문에 발생하는 것으로 정말 간단하게 try-catch 문으로 작성을 해서 unlinkSync 가 실행이 성공적으로 되지 않아도 그 다음으로 넘어갈 수 있게끔 살짝 바꾸어줬다.
(기존 라이브러리 코드)
const appImageFile = process.env["APPIMAGE"]!
if (appImageFile == null) {
throw newError("APPIMAGE env is not defined", "ERR_UPDATER_OLD_FILE_NOT_FOUND")
}
unlinkSync(appImageFile)
(내가 수정한 코드)
const appImageFile = process.env["APPIMAGE"]!
if (appImageFile == null) {
throw newError("APPIMAGE env is not defined", "ERR_UPDATER_OLD_FILE_NOT_FOUND")
}
try {
unlinkSync(appImageFile)
} catch(e: any) {
this._logger.warn("Previous version of AppImage is not available");
}
이런 식으로 작성하여
해당 경로에 파일이 없더라도 경고 로그만 하나 쏘고 그 다음 과정으로 진행되게 바꾸어 보았다.
그러고 다시 빌드 후 실행하여 업데이트 과정을 진행해보았는데
해당 경로에 파일이 없어도 로그 하나 방출 후 그 다음 과정을 쭉쭉 실행하였고 결국 업데이트까지 성공적으로 완료가 되는 것을 확인할 수 있었다!!
글이 너무 길어져서 본격적인 PR 날리는 부분은 2편에 이어서 쓰겠다.
'개발 ━━━━━ > Dev Life' 카테고리의 다른 글
electron-builder 오픈 소스에 인생 첫 PR 을 날려보다! - ② (2) | 2024.11.27 |
---|