Git에서 GPG key로 signoff 하기

공헌자들 패치를 받아서 커밋할 때, 컨트리뷰터의 author를 유지하면서 또한 커미터가 직접 확인했다는 증명을 signoff 기능을 통해 할 수 가 있다. 그런데 signoff 가 단순하게 커밋로그에 남는 ‘메일 주소와 이름 뿐’인 태그일 뿐이라서 진짜 그 사람이 signoff를 했는지 증명하는 것이 어렵다는 문제가 있다. 다행히 git에서는 GPG key를 이용한 signoff 기능을 제공한다. 우선은 내가 볼 목적으로 그리고 동료들에게 설명할 목적으로 정리를 해본다.

GPG에 대한 사용법은 생략한다. 우선 자신의 Key id를 얻는다.


$ gpg –list-secret-keys | grep ^sec
sub 4096R/4CFE2390 2013-10-16 [expires: 2017-10-16]
# ^— your key id

view raw

list_gpg_key.sh

hosted with ❤ by GitHub

그리고 git config의 user.signingkey로 GPG key id를 설정한다. 각 repository 마다 설정하고 싶으면 –global 옵션을 빼면 지정 repository에만 (현재 디렉토리의 git repository) 지정할 수 있다.


# remove –global to use this key only on the current repository
$ git config –global user.signingkey 4CFE2390
# ^- replace with your key id

그리고 커밋을 한다. 아래 옵션이 많지만 GPG key로 사인을 위한 옵션은 -S 이다.


hyunsik@workstation:~/Code/tajo/tajo$ git commit -S -m "TAJO-1909: Eliminate remained explicit diamond expressions."</code>
You need a passphrase to unlock the secret key for
user: "Hyunsik Choi <hyunsik@apache.o….>"
4096-bit RSA key, ID 4CFE2390, created 2013-10-16 (main key ID AC3885B9)
[master 6bc9fbb] TAJO-1909: Eliminate remained explicit diamond expressions.
Author: Dongkyu Hwangbo <hwang….@gma……>;
Date: Thu Oct 8 15:02:58 2015 -0700
72 files changed, 150 insertions(+), 147 deletions(-)

GPG key-agent가 켜져 있다면 패스워드 직접 입력없이 자동으로 위에서 설정한 키로 사인과 함께 커밋이 된다. 그럼 확인해보자.


hyunsik@workstation:~/Code/tajo/tajo$ git log –show-signature
commit 6bc9fbb50fb8b45d3fd58d9f10f74fefe62106fe
gpg: Signature made Thu 08 Oct 2015 03:15:39 PM PDT using RSA key ID 4CFE2390
gpg: Good signature from "Hyunsik Choi <hyunsik@apach.o..>"
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: 9141 BD40 6A84 DBA1 F4BD 5F04 024C 922A AC38 85B9
Subkey fingerprint: 1BB1 D697 599C 74BD C917 2F89 FE68 DD32 4CFE 2390

‘not certified’라고 나올 수 가 있다. key가 로컬 머신에 등록되어 있지만 trust key로 설정되어 있지 않아서 그렇다고 한다 (자세히는 모름). 해결은 아래와 같이 --edit-key 옵션으로 한다.


hyunsik@workstation:~/Code/tajo/tajo$ gpg –edit-key 4CFE2390 trust
gpg (GnuPG) 1.4.18; Copyright (C) 2014 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Secret key is available.
pub 4096R/AC3885B9 created: 2013-10-16 expires: 2017-10-16 usage: SCEA
trust: unknown validity: unknown
sub 4096R/4CFE2390 created: 2013-10-16 expires: 2017-10-16 usage: SEA
[ unknown] (1). Hyunsik Choi <hyunsik@apach.o….>;
pub 4096R/AC3885B9 created: 2013-10-16 expires: 2017-10-16 usage: SCEA
trust: unknown validity: unknown
sub 4096R/4CFE2390 created: 2013-10-16 expires: 2017-10-16 usage: SEA
[ unknown] (1). Hyunsik Choi <hyunsik@apach.o….>;
Please decide how far you trust this user to correctly verify other users' keys
(by looking at passports, checking fingerprints from different sources, etc.)
1 = I don't know or won't say
2 = I do NOT trust
3 = I trust marginally
4 = I trust fully
5 = I trust ultimately
m = back to the main menu
Your decision? 5
Do you really want to set this key to ultimate trust? (y/N) y

view raw

edit-key.sh

hosted with ❤ by GitHub

그리고 다시 확인해보자.


hyunsik@workstation:~/Code/tajo/tajo$ git log –show-signature
commit 5d470bc6001bb4fe096d7a8d221e51c18c683899
gpg: Signature made Thu 08 Oct 2015 03:21:24 PM PDT using RSA key ID 4CFE2390
gpg: Good signature from "Hyunsik Choi "

Warning이 없어진 것을 알 수 있다.

See Also


근래 배운 몇 가지 패턴 정리: Provider, Builder, Delegation

Provider

정확히 말하면 디자인 패턴은 아니라고 한다. 자세한 설명은 [1]에 있다. Factory 패턴과 유사하나 외부 설정에 따라 다른 객체를 생성하는 패턴을 칭한다. 나쁜 패턴이라며 [1]과 함께 Constructor Injection 같은 방법을 써야 한다는 주장이 있지만, 실제로 잘 작성된 오픈소스 프로젝트들에서도 이러한 구현을 꽤 많이 볼 수 있다.

Builder

생성자에 전달되어야 할 파라메터가 다양해서 골치 아픈 경우 Builder 패턴이 좋은 해결책이 된다. 방법은 Builder 객체를 만들고 setter 를 통해 필요한 파라메터를 설정 한 후에 build() 메소드 호출을 통해 실제 객체를 생성한다.


let storage = StorageBuilder.newBuilder()
.setURL("file:///tmp/test.csv")
.setType("csv")
.setFieldDelimiter(",")
.setLineDelimiter("\n")
.setEncoding("UTF-8")
.build();

view raw

builder.java

hosted with ❤ by GitHub

Delegation

처음 Delegation 패턴을 봤을 때는 Interface의 구현과 차이점을 잘 발견하지 못했었다. 위키 피디아에도 설명이 있지만 언제 써야 하는지가 설명되어 있지 않았다. [3] 에서 이유를 찾았는데. 요약을 해보면,

  • 원래 있는 객체의 동작을 그대로 유지하면서 동작의 앞뒤에 처리를 추가하고 싶을 때
  • 호환되지 않는 인터페이스를 위한 Proxy 를 구현할 때
  • 실제 구현 사용 시 복잡도가 높은 콜 루틴을 단순하게 제공하려고 할 때

경우에 따라 서브클래싱과 함께 쓸 수 있을 것 같으며 데코레이터 패턴에서 주로 나타나는 패턴인 것 같다.

See Also

[1] Provider Model Design Pattern and Specification, Part 1
[2] Provider is not a pattern
[3] http://stackoverflow.com/questions/7168714/what-is-the-purpose-of-a-delegation-pattern/7168737#7168737


Jni Native를 통한 Rust 함수 호출

회사 허락을 맡아 홀로 프로젝트를 하나 시작했다. 큰 그림은 일부 컴포넌트를 Rust로 구현하고 컴포넌트간 연결은 rpc로 하는 것인데 아직 Rust 로 rpc 구현을 하기에 시간이 더 필요하다. 임시적인 수단으로 JNI를 통해 기존 컴포넌트에 연결을 하려고 한다.

그 외 프로젝트에 자세한 이야기는 나중에 설명하고 위 목적으로 Stackoverflow 에서 참고하고 https://github.com/Monnoroch/RustJni 를 참고해서 JNI를 테스트를 해봤다.

C 바인딩이 쉬운것은 Rust의 장점 중 하나인데 JNI 바인딩 역시 순조로웠다. 방법은 우선 아래와 같이 native 함수 인터페이스를 작성하고


public class NativeInvoke {
public static native void procedure();
public static native void stringArg(String str);
public static native String returnString();
}

아래와 같이 Rust 코드를 작성하면 된다.


#![crate_type="dylib"]
#![feature(libc)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(unused_variables)]
extern crate libc;
extern crate jni;
use libc::c_void;
use std::ptr;
use jni::native::*;
use jni::helper::*;
#[no_mangle]
pub extern fn Java_org_github_hyunsik_NativeInvoke_procedure(jre: *mut JNIEnv,
class: *const c_void) {
println!("Invoked native method, jre: {:p}, class: {:p}", jre, class);
unsafe {
let v = ((**jre).GetVersion)(jre);
println!(">> version: {:?}", v);
}
}
#[no_mangle]
pub extern fn Java_org_github_hyunsik_NativeInvoke_stringArg(jre: *mut JNIEnv,
class: *const c_void, name: jstring) {
unsafe {
let string = ((**jre).GetStringUTFChars)(jre, name, ptr::null_mut());
println!("{}", chars_to_str(string));
((**jre).ReleaseStringUTFChars)(jre, name, string);
}
}
#[no_mangle]
pub extern fn Java_org_github_hyunsik_NativeInvoke_returnString(jre: *mut JNIEnv,
class: *const c_void) -> jstring {
unsafe {
return str_to_jstring(jre, "jni native");
}
}

view raw

lib.rs

hosted with ❤ by GitHub

이게 전부다. 위에서 사용된 chars_to_str와 str_to_jstring 는 아래 github repository에 있다.

https://github.com/hyunsik/jni-rs/blob/master/src/helper.rs

위 repository 는 https://github.com/Monnoroch/RustJni를 fork 해서 JNI 뿐 아니라 JNI 프로그램 작성 중에 반복되는 코드들에 대한 유틸리티 함수들을 추가할 계획이다.

그리고 아래는 JNI Native + Rust 를 위한 템플릿 프로젝트이다.

https://github.com/hyunsik/rust-jni-template