AutoUpdate Server & Client

제작 스토리

저는 Studio XID 에서

근무하는 Mark 라고 합니다.

자기 소개

  • 8년차 소프트웨어 엔지니어
  • 2015년부터 NW.js 사용
    • 프로덕션으로 nw.js-0.12.2 를 사용
    • 가상터치 솔루션 제작
  • 2015년 중반부터 electron 으로 갈아탐
    • atom-shell 에서 electron 으로 바뀐 후
  • Smart TV, Smart Car, Smart Home 등의 솔루션을 electron 으로 제작
  • 현재는 Studio XID 에서 'ProtoPie' 를 만들고 있습니다.
    • https://www.protopie.io/
    • electron 1.4.15
    • typescript 2.2.0
  • 주로 스튜디오 개발과, 인프라 관리를 합니다.

ProtoPie Studio

사실 입사한지 7개월 밖에 안댐 - 수많은 릴리즈 ㅜ

Release History
===============


v3.2.3-win (2016-03-31)

-------------------

- <strong>Official version for Windows</strong>
- Thank you for your effort to improve ProtoPie for Windows.
- Beta test program is over on March 31, 2017 in KST.
- Please jump in and ride on the official version. ;)


v3.2.3 (2016-03-21)

-------------------

- <strong>Special thanks to Jaret(郑皓), 田中良樹, and Shue(尹舒)</strong> (in alphabetical order)
- Fixed the problem to lose images under a certain procedure
- Improved the stability of the application
- Added ProtoPie improvement program


v3.2.2 (2016-02-28)
-------------------

- (3.2.2) Fixed window control bug


v3.2.1 (2016-02-27)
-------------------

- (3.2.1) Fixed dialog bug


v3.2.0 (2016-02-27)
-------------------

- Replace image layers
- Copy and paste scenes across files
- Open recent files
- Rename Interaction Pieces' names
- Keyboard shortcuts for panning and zooming canvas panel
- Spinner interface to adjust values on the property panel
- Rebuilt Sketch Import feature
- Enhanced Preview's performance and interactions
- Improved usability and fixed minor bugs


v3.1.0 (2016-02-06)
-------------------

- Able to add Video layers and play them
- Able to scale layers by factor
- Able to align and distribute layers on the canvas panel
- Possible to copy and paste images from other graphic tools through clipboard
- Support Slide out transition among scenes
- Changed the short cut key for multi-selection from Command to Shift
- Fixed minor bugs


v3.0.1 (2016-01-17)
-------------------

- Enhanced panning feature on the canvas panel
- Tuned up reorder behaviors of interaction pieces
- Improved alert information for trial period
- Fixed installation problem caused by user locale setting
- Fixed minor bugs and improved performance


v3.0.0 (2016-01-05)
-------------------

- the Official version, v.3.0 Donut
- Offers PREVIEW on the desktop
- Generates intuitive default names for layers
- Improved the discard process while quitting
- Fixed minor bugs and improved the performance


v2.3.1 (2016-12-06)
-------------------

- Resolved the icon related issue


v2.3.0 (2016-12-05)
-------------------

- Conditional interactions by Condition and Range Trigger
- Android USB connection
- Adjustable hit area
- Fling Trigger
- Custom easing options such as Cubic Bezier and Spring
- Multi-selection and editing for layers and interaction pieces
- Floating menu for adding pieces between existing ones


v2.2.1 (2016-11-17)
-------------------

- [Hotfix] Sketch 버전 41 import 호환성 오류 수정

Protopie Studio 의 Windows 버전을 준비하며 겪은

자동 업데이트 과정에 대한 스토리입니다.

기초 지식

npm i electron

C:\Users\mark\electron-install>npm i electron
`-- electron@1.6.6
  +-- electron-download@3.3.0
  | +-- debug@2.6.4
  | | `-- ms@0.7.3
  | +-- fs-extra@0.30.0
  | | +-- graceful-fs@4.1.11
  | | +-- jsonfile@2.4.0
  | | +-- klaw@1.3.1
  | | +-- path-is-absolute@1.0.1
  | | `-- rimraf@2.6.1
  | |   `-- glob@7.1.1
  | |     +-- fs.realpath@1.0.0
  | |     +-- inflight@1.0.6
  | |     | `-- wrappy@1.0.2
  | |     +-- minimatch@3.0.3
  | |     | `-- brace-expansion@1.1.7
  | |     |   +-- balanced-match@0.4.2
  | |     |   `-- concat-map@0.0.1
  | |     `-- once@1.4.0
  | +-- home-path@1.0.5
  | +-- minimist@1.2.0
  | +-- nugget@2.0.1
  | | +-- pretty-bytes@1.0.4
  | | | +-- get-stdin@4.0.1
  | | | `-- meow@3.7.0
  | | |   +-- camelcase-keys@2.1.0
  | | |   | `-- camelcase@2.1.1
  | | |   +-- decamelize@1.2.0
  | | |   +-- loud-rejection@1.6.0
  | | |   | +-- currently-unhandled@0.4.1
  | | |   | | `-- array-find-index@1.0.2
  | | |   | `-- signal-exit@3.0.2
  | | |   +-- map-obj@1.0.1
  | | |   +-- normalize-package-data@2.3.8
  | | |   | +-- hosted-git-info@2.4.2
  | | |   | +-- is-builtin-module@1.0.0
  | | |   | | `-- builtin-modules@1.1.1
  | | |   | `-- validate-npm-package-license@3.0.1
  | | |   |   +-- spdx-correct@1.0.2
  | | |   |   | `-- spdx-license-ids@1.2.2
  | | |   |   `-- spdx-expression-parse@1.0.4
  | | |   +-- object-assign@4.1.1
  | | |   +-- read-pkg-up@1.0.1
  | | |   | +-- find-up@1.1.2
  | | |   | `-- read-pkg@1.1.0
  | | |   |   +-- load-json-file@1.1.0
  | | |   |   | +-- parse-json@2.2.0
  | | |   |   | | `-- error-ex@1.3.1
  | | |   |   | |   `-- is-arrayish@0.2.1
  | | |   |   | +-- pify@2.3.0
  | | |   |   | `-- strip-bom@2.0.0
  | | |   |   |   `-- is-utf8@0.2.1
  | | |   |   `-- path-type@1.1.0
  | | |   +-- redent@1.0.0
  | | |   | +-- indent-string@2.1.0
  | | |   | | `-- repeating@2.0.1
  | | |   | |   `-- is-finite@1.0.2
  | | |   | `-- strip-indent@1.0.1
  | | |   `-- trim-newlines@1.0.0
  | | +-- progress-stream@1.2.0
  | | | +-- speedometer@0.1.4
  | | | `-- through2@0.2.3
  | | |   +-- readable-stream@1.1.14
  | | |   | `-- isarray@0.0.1
  | | |   `-- xtend@2.1.2
  | | |     `-- object-keys@0.4.0
  | | +-- request@2.81.0
  | | | +-- aws-sign2@0.6.0
  | | | +-- aws4@1.6.0
  | | | +-- caseless@0.12.0
  | | | +-- combined-stream@1.0.5
  | | | | `-- delayed-stream@1.0.0
  | | | +-- extend@3.0.0
  | | | +-- forever-agent@0.6.1
  | | | +-- form-data@2.1.4
  | | | | `-- asynckit@0.4.0
  | | | +-- har-validator@4.2.1
  | | | | +-- ajv@4.11.7
  | | | | | +-- co@4.6.0
  | | | | | `-- json-stable-stringify@1.0.1
  | | | | |   `-- jsonify@0.0.0
  | | | | `-- har-schema@1.0.5
  | | | +-- hawk@3.1.3
  | | | | +-- boom@2.10.1
  | | | | +-- cryptiles@2.0.5
  | | | | +-- hoek@2.16.3
  | | | | `-- sntp@1.0.9
  | | | +-- http-signature@1.1.1
  | | | | +-- assert-plus@0.2.0
  | | | | +-- jsprim@1.4.0
  | | | | | +-- assert-plus@1.0.0
  | | | | | +-- extsprintf@1.0.2
  | | | | | +-- json-schema@0.2.3
  | | | | | `-- verror@1.3.6
  | | | | `-- sshpk@1.13.0
  | | | |   +-- asn1@0.2.3
  | | | |   +-- assert-plus@1.0.0
  | | | |   +-- bcrypt-pbkdf@1.0.1
  | | | |   +-- dashdash@1.14.1
  | | | |   | `-- assert-plus@1.0.0
  | | | |   +-- ecc-jsbn@0.1.1
  | | | |   +-- getpass@0.1.7
  | | | |   | `-- assert-plus@1.0.0
  | | | |   +-- jodid25519@1.0.2
  | | | |   +-- jsbn@0.1.1
  | | | |   `-- tweetnacl@0.14.5
  | | | +-- is-typedarray@1.0.0
  | | | +-- isstream@0.1.2
  | | | +-- json-stringify-safe@5.0.1
  | | | +-- mime-types@2.1.15
  | | | | `-- mime-db@1.27.0
  | | | +-- oauth-sign@0.8.2
  | | | +-- performance-now@0.2.0
  | | | +-- qs@6.4.0
  | | | +-- safe-buffer@5.0.1
  | | | +-- stringstream@0.0.5
  | | | +-- tough-cookie@2.3.2
  | | | | `-- punycode@1.4.1
  | | | +-- tunnel-agent@0.6.0
  | | | `-- uuid@3.0.1
  | | +-- single-line-log@1.1.2
  | | | `-- string-width@1.0.2
  | | |   +-- code-point-at@1.1.0
  | | |   +-- is-fullwidth-code-point@1.0.0
  | | |   | `-- number-is-nan@1.0.1
  | | |   `-- strip-ansi@3.0.1
  | | |     `-- ansi-regex@2.1.1
  | | `-- throttleit@0.0.2
  | +-- path-exists@2.1.0
  | | `-- pinkie-promise@2.0.1
  | |   `-- pinkie@2.0.4
  | +-- rc@1.2.1
  | | +-- deep-extend@0.4.1
  | | +-- ini@1.3.4
  | | `-- strip-json-comments@2.0.1
  | +-- semver@5.3.0
  | `-- sumchecker@1.3.1
  |   `-- es6-promise@4.1.0
  `-- extract-zip@1.6.0
    +-- concat-stream@1.5.0
    | +-- inherits@2.0.3
    | +-- readable-stream@2.0.6
    | | +-- core-util-is@1.0.2
    | | +-- isarray@1.0.0
    | | +-- process-nextick-args@1.0.7
    | | +-- string_decoder@0.10.31
    | | `-- util-deprecate@1.0.2
    | `-- typedarray@0.0.6
    +-- debug@0.7.4
    +-- mkdirp@0.5.0
    | `-- minimist@0.0.8
    `-- yauzl@2.4.1
      `-- fd-slicer@1.0.1
        `-- pend@1.2.0

npm i electron

  • step1. 모듈 및 의존 모듈
    • electron 모듈 설치
    • electron-download 모듈 설치
    • 기타 의존 모듈 설치
  • step2. postinstall 실행
    • node_modules/electron/install.js 실행
    • electron-download 모듈을 통해 OS / Architecture 에 맞는 prebuilt 된 실행 파일 다운로드하여,
    • node_modules/electron/dist/ 에 저장
    • 그래서 얼마 전까지는 모듈 이름도 'electron-prebuilt' (deprecated)

npm start

{
  "name": "ProtoPie-Authoring",
  "productName": "ProtoPie",
  "companyName": "Studio XID",
  "version": "3.3.0-qa.1",
  "author": "Studio XID <eng@protopie.io> (https://protopie.io)",
  "description": "Super-Awesome Interaction Prototyping Tool",
  "homepage": "https://www.protopie.io/",
  "main": "./output/electron/main.js",
  "private": true,
  "scripts": {
    "start": "electron ."
  },
  "devDependencies": {
    "electron": "1.4.15"
  }
}

npm start

  • step1. electron . 실행
    • npm script 의 명령은 우선 node_modules/.bin/ 안에서 찾는다.
    • node/modules/.bin/electron.cmd . 실행
  • step2. node_modules/electron/cli.js 실행
    • node_modules/electron/path.txt 를 읽어서, ${실행 파일}을 찾는다.
    • package.json 이 있는 폴더를 인자로 넣어서node_modules/electron/${실행 파일}을 실행
C:\Users\mark\electron-install>type node_modules\electron\path.txt
dist/electron.exe

결국

OS / Architecture 에 맞게 미리 빌드된

실행파일을 실행하는것 !

 

node_modules/electron/dist/electron.exe

프로덕션용 앱 만들고 배포하기

step1. prebuilt 된 실행파일(.zip)을 구합니다.

step2. 개발 완료된 소스를 위치에 맞게 넣습니다.

// macOS
electron/Electron.app/Contents/Resources/app/
├── package.json
├── main.js
└── index.html

// macOS - asar
electron/Electron.app/Contents/Resources/
└── app.asar

// Windows
electron/resources/app
├── package.json
├── main.js
└── index.html

// Windows - asar
electron/resources/
└── app.asar

step3. Rebranding

  • 실행파일의 이름이나 아이콘을 각자에 맞게 수정하는일
    • 어어어엄청 귀찮아요
    • 개발자가 할짓은 아닌듯
  • electron 을 fork 떠서 처리해야할 필요가 없다. (공식 문서)
  • 룰에 맞게 수작업을 할 수 있다.
  • 하지만 귀찮으니 공식 문서에서 추천하는 모듈들을 사용한다.
    • electron-packager
    • electron-builder (현재 사용 중)
    • I LOVE 'electron-userland'

step4. '설치 파일'로 만들기

  • 토이 프로젝트나, 내부용 앱이라면 step3 에서 멈춰도 충분하지만,
  • 진정한 상용 앱이라면 .app / .zip 으로 줄순 없자나요?
    • macOS => .dmg 로 만들기
    • Windows => .exe 로 만들기 (NOT 실행 프로그램)
  • 이것도 만들어서 다 할수 있지만,
    • 우리에겐 'electron-builder' 가 있습니다.
    • 'electron-packager' 는 step3 까지만 해줍니다.
  • 그리고 설치 파일로 만들기 전, 후에는 codesign 이 필요합니다.

step5. 배포용 서버에 설치파일 올리기

[macOS] 자동 업데이트 (feat. Squirrel)

자동 업데이트 ??

autoUpdater 모듈 - Squirrel interface

autoUpdater 기본

// 업데이트 서버 주소 설정 및 초기화
autoUpdater.setFeedURL(`http://127.0.0.1:${port}/update/latest`);

// 업데이트 있는지 체크            
autoUpdater.checkForUpdates();
            
// 업데이트 파일이 다운로드 다 되었는지 확인하는 이벤트 바인딩
autoUpdater.on('update-downloaded', async() => {

    // 앱 종료 후 업데이트 후 재시작
    autoUpdater.quitAndInstall();
            
});

macOS 에서의 Squirrel

  • 사용이 생각보다 어렵지 않습니다.
    • 업데이트용 파일인 .zip 파일을 electron-builder 에서 지원
  • 설치 및 실행 파일에 별도의 추가 변경이 필요하지 않습니다.
    • Windows 는 실행 파일 구조 자체가 변합니다.
    • macOS 는 Squirrel.mac 이 built-in 되어 있습니다.
    • codesign 이 꼭 되어있어야 합니다.
  • 단점
    • 다운로드 진행 상태를 알수 없습니다.
      • 파일이 60MB 이상인걸?
      • 역대급 꼼수
    • 무조건 업데이트 파일 다운로드는 처음부터
    • 릴리즈노트는 다운로드 끝나고?

macOS 에서의 Squirrel

일단 자동 업데이트 서버부터!

  • electron-release-server 라는 프로젝트를 사용​
    • https://github.com/ArekSredzki/electron-release-server
    • squirrel 지원
    • 다양한 다운로드 링크 제공
    • 소스만 받아서 서버에 설치 후 쉽게 사용 가능
    • sails.js 사용 - 전 잘 몰라요
    • DB 는 postgresql (RDS)

macOS 최종 절차

  • step1. 앱 최초 실행시 url 룰에 맞게 릴리즈 서버를 호출
    • 204 면 업데이트 없음.
    • 아니면 (200), 있으니깐 이때 response 에 넣어둔 릴리즈 노트를 받아 사용 
  • step2. 릴리즈 노트를 보여주며, 업데이트 다운로드 버튼을 누르도록
    • 캔슬할수도...
    • 다운로드 버튼을 누르면
  • step3. 로컬에 squirrel 용 .zip 파일을 다운로드
    • 프로그레스 가능
    • 이미 받아둔거 있으면 (체크섬), 다시 사용안하도록 처리 가능
  • step4. 로컬에 짱 박아둔 웹서버를 이용하여, autoUpdater 모듈 사용
    • autoUpdater.setFeedURL(`http://127.0.0.1:${port}/update/latest`);

여기서부터 윈도헬이 시작됩니다.

Squirrel.Windows

  • macOS 와는 다릅니다.
    • 클라이언트에서도 다릅니다.
      • 통으로 새로운 파일을 받아서 갈아끼는 스타일이 아닙니다.
      • 확장자도 .zip 아니고, .nupkg 입니다.
      • 이전 버전과의 diff 를 만들어서 업데이트 처리합니다.
      • 업데이트 설치시 gif 파일이 나타납니다.
      • 설치를 아무데나 할수가 없습니다.
      • 빌드시 이전 버전을 기준삼아 빌드해서 diff 파일을 만듭니다.
    • 서버에서도 다릅니다.
      • 응답이 파일로 옵니다. (헐)
http://download.myapp.com/update/win32/:version/RELEASES
http://download.myapp.com/update/win64/:version/RELEASES
http://download.myapp.com/update/win32/:version/:channel/RELEASES
http://download.myapp.com/update/win64/:version/:channel/RELEASES

Squirrel.Windows 포기

  • VSCode 의 영향이 컸습니다.
    • 업데이트를 누르면 재설치 파일이 실행됩니다.
    • inno-setup 을 사용합니다.
      • 사실 nsis, squirrel 의 문제점을 알고, 대안을 모색하다 발견
        • https://blogs.msdn.microsoft.com/vscode/2015/09/11/updated-windows-installer-for-vs-code/
      • 역시 난 틀리지 않았어를 외치며, 한동안 어깨뽕 들어감.
      • 개발 회의를 통해 설명하고, 깔끔하게 inno-setup 으로 전환
      • VSCode 는 electron 계의 표준전과

Updated Windows Installer For VS Code

Windows 최종 절차 (계획안) - macOS 따라쟁이

  • step1. 앱 최초 실행시 url 룰에 맞게 릴리즈 서버를 호출
    • 204 면 업데이트 없음.
    • 아니면 (200), 있으니깐 이때 response 에 넣어둔 릴리즈 노트를 받아 사용 
  • step2. 릴리즈 노트를 보여주며, 업데이트 다운로드 버튼을 누르도록
    • 캔슬할수도...
    • 다운로드 버튼을 누르면
  • step3. 로컬에 inno-setup 으로 빌드된 .exe 파일을 다운로드
    • 프로그레스 가능
    • 이미 받아둔거 있으면(체크섬), 다시 사용안하도록 처리 가능
  • step4. 프로그램을 종료시키면서 Setup.exe 파일을 실행
    • shell.openItem(path);

inno setup 으로 설치파일 만들기

npm run pack-prod

  • step1. 앱 실행파일 만들기
    • electron-builder
    • package.json 에 build.win.target: [] 로 설정하고, 아이콘만...
    • 그러면 dist/win-ia32-unpacked 폴더가 생기고, 실행파일이 생김
  • step2. 실행 폴더 안의 모든 파일을 codesign 처리
    • 인증서 처리 스폐셜 땡쓰 투 Tom Kim@Drama & Company
  • step3. inno setup 을 통해 -Setup.exe 파일 생성
  • step4. 그 설치 파일을 codesign

npm run pack-prod

inno setup

  • http://www.jrsoftware.org
  • free installer
  • 설치 파일 생성 마법사도 있음
  • npm 모듈도 있음
    • innosetup-compiler
    • https://www.npmjs.com/package/innosetup-compiler
  • 스크립트로 정의할수 있는데 스크립트는 Delphi
    • 델파이 모르면 베끼기
    • https://github.com/Microsoft/vscode/blob/master/build/win32/code.iss
  • 레지스트리 제어 (.pie 확장자 아이콘 처리)
  • 설치시 추가 기능 제공
  • 삭제시 할일 처리

delphi script

; 프로그램 전체에 대한 설정을 정의한다.
[Setup]
AppId={#AppId}
AppName={#NameLong}
AppVerName={#NameVersion}
AppPublisher=Studio XID, Inc.
AppPublisherURL={#Homepage}
AppSupportURL={#Homepage}docs/
;AppUpdatesURL={#Homepage}
DefaultDirName={pf}\{#DirName}
DefaultGroupName={#NameLong}
AllowNoIcons=yes
OutputDir={#RepoDir}\dist
OutputBaseFilename=ProtoPie-{#Version}-Setup
Compression=lzma
SolidCompression=yes
AppMutex={#AppMutex}
WizardImageFile={#RepoDir}\build\win32\inno_big.bmp
WizardSmallImageFile={#RepoDir}\build\win32\inno_small.bmp
SetupIconFile={#RepoDir}\build\win32\icon_setup.ico
UninstallDisplayIcon={app}\{#ExeBasename}.exe
ChangesEnvironment=true
ChangesAssociations=true
MinVersion=6.1.7600
SourceDir={#SourceDir}
AppVersion={#Version}
VersionInfoVersion={#RawVersion}
ShowLanguageDialog=auto
CloseApplications=force
; PrivilegesRequired=admin

; 프로그램이 사용하는 언어를 정의한다.
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl,{#RepoDir}\build\win32\i18n\messages.en.isl"
Name: "simplifiedChinese"; MessagesFile: "{#RepoDir}\build\win32\i18n\Default.zh-cn.isl,{#RepoDir}\build\win32\i18n\messages.zh-cn.isl"

; 설치시에 제거 할 파일이 있으면 정의한다.
[InstallDelete]
Type: filesandordirs; Name: {app}\resources

; 설치 프로세스를 사용자 측에서 상세하게 설정하는 경우에 각각의 처리(작업)에 대해 정의한다.
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 0,6.1
Name: "associatewithfiles"; Description: "{cm:AssociateWithFiles,{#NameShort}}"; GroupDescription: "{cm:Other}"
Name: "runcode"; Description: "{cm:RunAfter,{#NameShort}}"; GroupDescription: "{cm:Other}"; Check: WizardSilent

...

계획안에 맞게 기존 릴리즈 서버 수정

수정 실패

  • 고칠게 너무 많았음
    • 윈도우쪽을 전부 날림
    • 사실 sails.js 를 잘 모르기도 했음
  • 평소에 디플로이를 위한 프로세스도 별로 마음에 안들었음.
    • S3에 설치파일, 업데이트 파일 올리고,
    • 릴리즈 서버에 정보 올리고,
      • QA 는 자동 스크립트
      • Production 은 수동은 아님
  • 그러다 옆자리의 데니스가 그냥 만들면 어떠냐고...
  • 어차피 다운로드 링크는 별도로 cdn 과 s3 static 을 쓰기 때문에 쉽게 할수 있을것으로 예상함.

업데이트 서버 제작 계획안

// make `ProtoPie-${Version}-Setup.exe` file
npm run pack-prod

// make `${Version}.json` file
// upload to S3 - `${Version.json}` file, `ProtoPie-${Version}-Setup.exe` file
npm run deploy

[macOS] response 마이그레이션

{
    "url": "http://release.protopie.io/ProtoPie-3.2.3-mac.zip",
    "name": "3.2.3",
    "notes": "## 3.2.3\n### 3.2.3\n- <strong>Special thanks to Jaret(郑皓), 田中良樹, and Shue(尹舒)</strong> (in alphabetical order)\n- Fixed the problem to lose images under a certain procedure\n- Improved the stability of the application\n- Added ProtoPie improvement program\nmetadata:{\"osx\":{\"size\":75391013,\"checksum\":\"39432d1cb5b4214515a443d4bec719e9d2b787ab\"}}\n## 3.2.2\n### 3.2.2\n- (3.2.2) Fixed window control bug\n- (3.2.1) Fixed dialog bug\n- Replace image layers\n- Copy and paste scenes across files\n- Open recent files\n- Rename Interaction Pieces' names\n- Keyboard shortcuts for panning and zooming canvas panel\n- Spinner interface to adjust values on the property panel\n- Rebuilt Sketch Import feature\n- Enhanced Preview's performance and interactions\n- Improved usability and fixed minor bugs\nmetadata:{\"osx\":{\"size\":72656810,\"checksum\":\"4c45300147ac566e6cb5975cd7c637da0a3d6fcd\"}}",
    "metadata": {
        "osx": {
            "size": 75391013,
            "checksum": "39432d1cb5b4214515a443d4bec719e9d2b787ab"
        }
    },
    "pub_date": "2017-03-21T09:54:30.939Z"
}

계획안에 맞게 릴리즈 서버 만들기

[macOS] {Version}.json

{
    "version": "3.2.3",
    "note": "### 3.2.3\n- <strong>Special thanks to Jaret(郑皓), 田中良樹, and Shue(尹舒)</strong> (in alphabetical order)\n- Fixed the problem to lose images under a certain procedure\n- Improved the stability of the application\n- Added ProtoPie improvement program",
    "file": "ProtoPie-3.2.3-mac.zip",
    "metadata": {
        "osx": {
            "size": 75391013,
            "checksum": "39432d1cb5b4214515a443d4bec719e9d2b787ab"
        }
    },
    "pub_date": "2017-03-21T09:54:30.939Z",
    "state": "enabled"
}

[Windows] {Version}.json

{
    "version": "3.2.3",
    "note": "### 3.2.3\n- <strong>Official version for Windows</strong>\n- Thank you for your effort to improve ProtoPie for Windows.\n- Beta test program is over on March 31, 2017 in KST.\n- Please jump in and ride on the official version. ;)",
    "file": "ProtoPie-3.2.3-Setup.exe",
    "metadata": {
        "win32": {
            "size": 55642520,
            "checksum": "5df925f649af2924e48bbd935eb9a3f4807bad65"
        }
    },
    "pub_date": "2017-03-31T05:55:56.133Z",
    "state": "enabled"
}

S3

버전 처리

  • 다양한 버전의 우선순위
    • v3.3.0
    • v3.3.0-qa.1
    • v3.3.0-winrc.1
    • v3.3.0-rc.1
  • http://semver.org/ => Semantic Versioning
  • https://www.npmjs.com/package/semver
import * as semver from 'semver';

semver.gt(version, '3.3.0');

semver.lte(version, '3.1.0')

ec2 - express

// 라우팅 핸들러 설정
router.get('/:platform/:version/', ctrl.index);

// 라우팅 함수
async function index(req, res) {
    // 플랫폼과 버전을 받음
    const platform: string = req.params.platform;
    const version: string = req.params.version;

    if (platform !== 'darwin_x64' && platform !== 'win32') {
        return res.status(400).end();
    }

    if (semver.valid(version) === null) {
        return res.status(400).end();
    }

    // 버전 관리자를 통해 S3 로 부터 최신 버전 리스트를 업데이트
    const versionManager: IVersionManager = VersionManager.getInstance();    
    await versionManager.update();
    // 최신 버전 리스트에서 요청받은 플랫폼과 버전레 맞춰 response 에 담을 내용을 만들어 냄.
    const result = versionManager.getResult(platform, version);

    if (result) {
        res.json(result);
    } else {
        res.sendStatus(204);
    }
}

VersionManager.ts

export class VersionManager implements IVersionManager {
    // 매니저는 단일 객체

    public getResult(platfrom: string, version: string) {
        const platfromId: string = (platfrom === 'darwin_x64') ? 'darwin' : platfrom;
        const versionInPlatform: IVersion[] = this._versionList.filter(v => {
            if ((v.getPlatform() === platfromId) && semver.gt(v.getVersion(), version)) {
                if (MODE === 'production' || MODE === 'qa') {
                    return v.getState() === 'enabled';
                } else {
                    return v.getState() !== 'disabled';
                }
            } else {
                return false;
            }
        });
        versionInPlatform.sort((l, r) => semver.gt(l.getVersion(), r.getVersion()) ? -1 : 1);
        
        if (versionInPlatform.length > 0) {
            const url = versionInPlatform[0].getFile();
            const name = versionInPlatform[0].getVersion();
            const notes = this._getNotes(versionInPlatform);
            const metadata = versionInPlatform[0].getMetadata();
            const pub_date = versionInPlatform[0].getDate();

            return {
                url,
                name,
                notes,
                metadata,
                pub_date
            };
        } else {
            return null;
        }
    }

    public async update(): Promise<void> {
        let s3List = null;
        try {
            s3List = await getS3List();
        } catch (e) {
            await sendSNS('Autoupdate Server : getS3List 실패', '자동업데이트 서버에서 S3 로 부터 JSON 리스트를 읽어오다가 실패함.');
            return;
        }

        let s3Objects = null;
        try {
            s3Objects = await getS3Objects(s3List);
        } catch (e) {
            await sendSNS('Autoupdate Server : getS3Objects 실패', '자동업데이트 서버에서 S3 로 부터 JSON 파일을 읽어오다가 실패함.');
            return;
        }

        this._setVersions(s3List, s3Objects);
    }

    private _setVersions(s3List: any[], s3Objects: any[]): void {
        this._versionList = [];
        s3List.forEach((item, index) => {
           this._versionList.push(new Version(s3List[index], s3Objects[index]));
        });
    }

    private _getNotes(versionInPlatform): string {
        const notes = [];
        versionInPlatform.forEach((item, index) => {
            if (index < 3) {
                notes.push(`## ${item.getVersion()}\n${item.getNote()}\nmetadata:${JSON.stringify(item.getMetadata())}`);
            }
        });
        return notes.join('\n');
    }
}

Staging Server

Admin Page (1)

Admin Page (2)

감사합니다 !

자동 업데이트 서버 및 클라이언트 제작 스토리

By Woongjae Lee

자동 업데이트 서버 및 클라이언트 제작 스토리

일렉트론 코리아 4월 밋업 발표

  • 2,796