2013년 1월 26일 토요일

[git] fork 해온 저장소를 최신 상태로 유지하기



Go 관련 번역 작업을 할 일이 있어서 했던 일들을 기록해둔다.
이슈에 내가 할 작업 분량을 공지하고 (다른 사람이 두번 삽질 안하게)
바로 fork 해서 내 저장소로 옮겨놓고
수정하고 pull request 까지 끝낸 뒤 원본에서 merge까지 끝났다.

그 다음은?

내가 fork 한 저장소의 작업과 별개로 원본의 작업은 진행되고 있고 아마 내 저장소의 것들은 상대적으로 구버전일 것이다.
원본에서 pull로 땡겨와서 merge 하기 위해 remote 를 먼저 등록해두자.

$ git remote add upstream git@github.com:golanger/go-tour-translator.git

확인해본다.
$ git remote
origin
upstream

origin 과 새로 추가한 upstream. 두개를 확인할 수 있다.
그러면 upstream으로부터 pull 해보자.

$ git pull upstream
From github.com:golanger/go-tour-translator
 * [new branch]      master     -> upstream/master
You asked to pull from the remote 'upstream', but did not specify
a branch. Because this is not the default configured remote
for your current branch, you must specify a branch on the command line.

그냥 pull 하니까 오류를 뱉는다. 어떤 브랜치인지 지정 안했다는 얘기지뭐. 디폴트로 master를 해줬으면 좋겠지만 확실한게 좋다.
upstream에서 master를 가져오도록 해보자.

$ git pull upstream master
From github.com:golanger/go-tour-translator
 * branch            master     -> FETCH_HEAD
Already up-to-date.
변경이 있으면 변경이 된 내용이 나올 것이다.
pull은 내 저장소에서 pull 하는 것고 별반 다르지 않다.
충돌이 있으면 merge 하든가 알아서 하자.
이것이 git 의 유연한 점이라고 생각한다.

여기까지 했으면 내 로컬의 형상과 내 저장소의 형상이 달라졌을 것이다.
commit 할게 있으면 commit 하고 내 저장소에 push 해주자

$ git push

upstream쪽에서 내가 수정하고 있는 파일에 대한 수정을 여러 사람이 제각각 fork + pull request 한다면 좀 불편할 수도 있겠다.
아마 번역이 딱 이런 예일 듯 싶지만서도.
필요하면 issue 에서 할당받을 양을 따로 파일로 찢고 branch를 딴 후 최종 머지할때 합하는 것도 좋은 생각이겠다.

2013년 1월 2일 수요일

go를 사용하여 Google App Engine에 정적 웹서버를 올려보자.

작년에 미뤄뒀던 Go를 다시 보고 있다.
동기부여를 위해 회사 안내 홈페이지를 망할 일도 없고 비용이 안드는 곳으로 옮기고 싶은 생각이 있었는데
Google App Engine(이하 GAE)정도면 뭐 망할 일이 없겠지 싶다.
Google Apps 처럼 유료화해도 먼저 만들어 놓은 서비스/계정은 안건드니까 먼저 침이라도 발라놓아야지.

개발환경 세팅은 간단했다.
Download 에서 package 를 받고 설치한 다음
goide를 쓰고 싶었으나 아직 완성도 문제가 있어서 sublime을 사용하기로 결정.
http://wbond.net/sublime_packages/package_control/installation 는 전에 설치해놓았다.
그저 sublime을 띄운 상태에서 ctrl+` (OS X에서도 ctrl+` 임)을 누르면 나타나는 하단 콘솔창에

import urllib2,os; pf='Package Control.sublime-package'; ipp=sublime.installed_packages_path(); os.makedirs(ipp) if not os.path.exists(ipp) else None; urllib2.install_opener(urllib2.build_opener(urllib2.ProxyHandler())); open(os.path.join(ipp,pf),'wb').write(urllib2.urlopen('http://sublime.wbond.net/'+pf.replace(' ','%20')).read()); print 'Please restart Sublime Text to finish installation'
요 내용을 붙여넣으면 끝.

그 다음 sublime용 golang plugin인 GoSublime을 추가
데모 동영상을 보면서 사용법을 간단하게 알아보면

0:11즈음에 나오는 package 삽입 기능은 ctrl(cmd)+. , ctrl(cmd)+p 사용

package 삽입 뿐만이 아니라 삭제도 되어서 안쓰는 package가 있는 경우 컴파일 오류를 뿜어내는 go 에선 아주 편리.
ctrl(cmd)+b(0:22에 나옴)하면 go shell을 사용.
ctrl(cmd)+. , ctrl(cmd)+. (0:28 즈음) 연타하고 share 하면 play.golang.org 에 code snippet 올리고 URL까지 뽑아줌. 재밌는 기능.
실행시엔 그냥 ctrl(cmd)+. , ctrl(cmd)+R 하면 된다. (ctrl 혹은 cmd 키를 누른채로 .과 R을 연속해서 입력)

생각보다 정적 웹서버를 구현하는 건 어이없을 정도로 간단.

package main

import (
"log"
"net/http"
)

func main() {
log.Fatal(http.ListenAndServe(":3000", http.FileServer(http.Dir("./public"))))
}

이게 전부다. 진짜다;


:3000포트로 들어온 http Request를 FileServer를 통해 public 디렉토리 하위에 있는 문서랑 연결하는 방식이다.

node.js와 coffeescript로 twitter 포스팅용 simpleHTTPServer 소스(https://gist.github.com/4414368)를 올린적이 있는데
go도 역시 만만치 않다. 물론 실제로 FileServer를 풀어 헤쳐서 구현하려면 손이 더 타겠지만 일단은 간단하다. (하지만, node.js 의 방식이 더 누군가에게 설명하거나 이야기 하긴 더 좋다)

다음은 GAE Go SDK를 설치한다.
압축파일(https://developers.google.com/appengine/downloads)을 풀고
적당히 google_appengine 을 이동한뒤 PATH를 잡아준다.
https://developers.google.com/appengine/docs/go/gettingstarted/devenvironment 참조

자 이제 준비는 끝났다.

GAE용 어플리케이션을 만드는게 목적이므로 main 대신 init 으로 변경하자.
근데 문제가 있다.

http.ListenAndServer를 이미 GAE GO 에서 사용하고 있으므로
http.HandleFunc 를 처리해 줘야한다.

이런 식으로 코드를 짜보았다. node용 simpleHTTPServer 와 비슷하게.




package server

import (
"appengine"
"fmt"
"io/ioutil"
"net/http"
)

func init() {
http.HandleFunc("/", handler)
}

func handler(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
path := r.URL.Path
if path == "/" {
path = "/index.html"
}
file, err := ioutil.ReadFile("./public" + path)
if err != nil {
file, err = ioutil.ReadFile("./public/404.html")
c.Infof("404:Requested URL Not Found: %v", r.URL.Path)
}
fmt.Fprint(w, string(file))
}

이렇게 해도 되나 참내;;

디렉토리 구조는 아래와 같이
home/
  app.yaml
  server/
    server.go
  public/
    index.html
    404.html
   ....
    

작동이 정상적으로 되는 걸 확인하면 등록하자. 경로로 r.RequestURI를 사용했더니 로컬에선 되는데 서버에서 문제가 있음. 삽질하지말고 r.URL.Path 쓰자.
https://appengine.google.com 에서 어플리케이션 명을 등록하고 그 이름으로 app.yaml 파일을 수정한다.

application: <application name>
version: 1
runtime: go
api_version: go1

handlers:
- url: /.*
  script: _go_app

여기까지 하고
appcfg.py update .
를 사용하여 서버에 올리자. 올라가는 속도가 썩 빠르지는 않다.

Python으로 할껄 괜히 go로 한다고 삽질한 듯도 싶고.
그래도 잘 올라가니까 좋네.ㅎ