Jenkins 除了可以和 Local 端專案整合,也可和github一起工作。
這篇紀錄如何整合jenkins和github。
1.先在github上建立空專案。
2.在Build Server(jenkins),先 git clone github下來,並建立 android project,注意要使用 ant 成功建立專案(ant release or ant debug),
注意,不要加入local.properties 和 ant.properties,可以加入.gitignore來忽略。
完成後git push到github上
3.在jenkins建立專案並設定git path為github的路徑。
4.若有需要先在github上merge第3步驟的push commit,完成後jenkins 可以build成功。
其他詳細可參考 https://wiki.jenkins-ci.org/display/JENKINS/Building+an+Android+app+and+test+project
Descriptoin:
註冊完成gerrit 帳號後,在預設的情況下必須透過 email 來驗證帳號,否則無法進行其他動作。
但若 server 本身並無 mail server 的功能,即無法發送接收信件,或是其他因素無法使用email,即無法驗證帳號。
Solution:
可修改 gerrit 使用的 database, 由於 database 存放所有 gerrit 使用的資料,不僅可以修改驗證還可以修改使用者權限等等。
但此方法必須具有登入並 root server 的權限才可。
Step 1.
登入 gerrit server 並停止 gerrit 服務。
sudo sh /your_gerrit_shell_path/gerrit.sh stop
Step 2.
利用 gerrit.war 執行 gsql 指令,以登入 sql。
java -jar gerrit.war gsql
如果出現以下error,必須注意先移動到相關 review_site 資料夾再執行 gerrit.war,
fatal: not a Gerrit site: '/xxx' fatal: Perhaps you need to run init first?
以我為例,我的gerrit.war 是位於 review_site 的上一層資料夾,所以必須先移動到 review_site,再執行 gerrit.war
cd /xxx/review_site sudo java -jar ../gerrit.war gsql
成功的話應該可以登入到 sql 中,接著準備修改相關資料
➜ review_site sudo java -jar ../gerrit-2.1.8.war gsql Welcome to Gerrit Code Review 2.1.8 (MySQL 5.5.38-0ubuntu0.12.04.1) Type '\h' for help. Type '\r' to clear the buffer. gerrit>
Step 3.
由於資料表相當多,這邊只介紹如何手動加入驗證 email, 以及修改使用者權限,有興趣的話可以修改其它數值玩玩看。
手動加入驗證 email 必須修改 account_external_ids 資料表
使用 show databases; 查詢所有資料庫,使用 show tables; 查詢某資料庫中所有資料表。使用 use databasename; 選擇資料庫。
gerrit> show databases; Database ------------------ information_schema reviewdb (2 rows; 27 ms) gerrit> use reviewdb; UPDATE 0; 1 ms gerrit> show tables; Tables_in_reviewdb ---------------------------- account_agreements account_diff_preferences account_external_ids account_group_agreements account_group_id account_group_includes account_group_includes_audit account_group_members account_group_members_audit account_group_names account_groups account_id account_patch_reviews account_project_watches account_ssh_keys accounts approval_categories approval_category_values change_id change_message_id change_messages changes contributor_agreement_id contributor_agreements patch_comments patch_set_ancestors patch_set_approvals patch_sets projects ref_rights schema_version starred_changes system_config tracking_ids (34 rows; 24 ms) gerrit>
從上圖中可以看到 account_external_ids 資料表是位於 reviewdb 資料庫。
修改使用者驗證信箱
接著看看 account _external_ids 資料表的內容,我們的目標為修改 testuser,讓其驗證信箱為驗證完成狀態。
gerrit> select * from account_external_ids; account_id | email_address | password | external_id -----------+------------------+----------+----------------- 2 | NULL | NULL | gerrit:testuser 1 | NULL | NULL | gerrit:xxx 2 | NULL | NULL | username:testuser 1 | NULL | NULL | username:xxx (4 rows; 2 ms)
我們的目標就是修改 testuser 的 email_address , 讓它不可為NULL。
使用以下指令修改欄位值
gerrit> update account_external_ids set email_address='example@example' where account_id=2; UPDATE 2; 5 ms
修改完成後再查看account_external_ids table
gerrit> select * from account_external_ids; account_id | email_address | password | external_id -----------+------------------+----------+----------------- 2 | example@example | NULL | gerrit:testuser 1 | NULL | NULL | gerrit:xxx 2 | example@example | NULL | username:testuser 1 | NULL | NULL | username:xxx (4 rows; 2 ms)
離開sql , 重啟 gerrit 看到修改後的結果。
gerrit> \q Bye sudo /etc/init.d/apache2 restart sudo sh /your_gerrit_shell_path/gerrit.sh run
修改使用者權限為 admin
首先確認使用者的 account_id, 下表還有一些內容因為用不到所以忽略,只要找到 user_name 和所對應的 account_id 即可,可以發現 testuser 的 account_id 為 2。
gerrit> select * from accounts; full_name | account_id --------------------------- | testuser | 2 (2 rows; 2 ms)(2 rows; 2 ms)
查詢 admin 對應的 group_id,可以確認 admin 所屬的 group_id 為 1。
gerrit> select * from account_groups; name | owner_group_id | description | group_type | external_name | visible_to_all | email_only_authors | group_id ----------------------+----------------+-------------------------------------------+------------+---------------+----------------+--------------------+------ Administrators | 1 | Gerrit Site Administrators | INTERNAL | NULL | N | N | 1 Anonymous Users | 1 | Any user, signed-in or not | SYSTEM | NULL | N | N | 2 Registered Users | 1 | Any signed-in user | SYSTEM | NULL | N | N | 3 (6 rows; 3 ms)
查詢 account_id 所屬的 group_id
gerrit> select * from account_group_members; account_id | group_id -----------+--------- 1 | 1 2 | NULL
可以看到 account_id 為 2 的部份,其所屬的 group_id 為 NULL,所以只要把 NULL 修改為 1 ,就能讓 testuser 權限變更為 admin。
使用 update 來更改數值
gerrit> update account_group_members set group_id=1 where account_id=2; UPDATE 1; 30 ms
重新檢查一次 account_group_members , 已經完成修改。
gerrit> select * from account_group_members; account_id | group_id -----------+--------- 1 | 1 2 | 1 (2 rows; 1 ms)
離開 sql , 重啟 gerrit,進入 gerrit 頁面就可看到成果。
gerrit> \q Bye sudo /etc/init.d/apache2 restart sudo sh /your_gerrit_shell_path/gerrit.sh run
1. git clone autojump
git clone https://github.com/joelthelion/autojump.git
after clone, it will create a dir named autojump
2. install autojump
cd autojump ./install.sh
after install autojump, it will create config file of autojump at ~/.autojump
3. setup config file for autojump
3-1. vim ~/.zshrc and add autojump at plugins=(xxx autojump)
... # Which plugins would you like to load? (plugins can be found in ~/.oh-my-zsh/plugins/*) # Custom plugins may be added to ~/.oh-my-zsh/custom/plugins/ # Example format: plugins=(rails git textmate ruby lighthouse) # Add wisely, as too many plugins slow down shell startup. plugins=(git autojump) ...
3-2 check shell file name of autojump is correct e.g. ~/.autojump/etc/profile.d/autojump.sh or ~/.autojump/etc/profile.d/autojump.zsh
add shell file of autojump file in ~/.zshrc
... [[ -s ~/.autojump/etc/profile.d/autojump.zsh ]] && . ~/.autojump/etc/profile.d/autojump.zsh
4. reload config file
source ~/.zshrc
5. Now Jump it!!
1.Install zsh
$ sudo apt-get install zsh
2.Install oh-my-zsh
$ wget --no-check-certificate https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh -O - | sh
若發生以下錯誤訊息代表目前電腦不信任 git 伺服器憑證。該憑證是自行簽署的或是其他未知的機構簽署。
fatal: unable to access 'https://github.com/ohmyzsh/ohmyzsh.git/': server certificate verification failed. CAfile: none CRLfile: none
Error: git clone of oh-my-zsh repo failed
可以使用以下指令暫時忽略憑證
export GIT_SSL_NO_VERIFY=1
3.Now you can modify config file of zsh at
~/.zshrc
Description :
當在git project 底下,若有些檔案無法加入(git add),很有可能是被 git ignore了。
詳細說明參考這篇(http://stackoverflow.com/questions/1084969/unable-to-track-files-within-git-submodules)
以下節錄相關的說明
大部分的情況下,檔案被 git ignore,有2個主要原因:.gitignore 和 submodule。
.gitignore 是一個說明檔和 .git 位於同一層,主要的用途就是忽略不需要追蹤的檔案。
以 android project 為例,當進行clean 和build 的動作都會讓bin/ 資料夾內的檔案重新生成。
這些重新生成的檔案多半是不需要追蹤,因此就能藉由 .gitignore 說明檔來描述哪些檔案不需要追蹤。
以下為簡單的範例
這是剛建立完成android project並初始化 git(git init)的情況。可以看到 bin/ 資料夾。
# On branch master # # Initial commit # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # .classpath # .project # AndroidManifest.xml # bin/ # gen/ # ic_launcher-web.png # libs/ # proguard-project.txt # project.properties # res/ # src/
接著建立 .gitignore 檔案。
vim .gitignore 並填入 bin/ (bin/ 的意思為忽略 bin/ 內所有檔案)
再輸入 git status 觀察,可以看到多了.gitignore ,少了bin/
# On branch master # # Initial commit # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # .classpath # .gitignore # .project # AndroidManifest.xml # gen/ # ic_launcher-web.png # libs/ # proguard-project.txt # project.properties # res/ # src/
此即為第一種 git ignore 的情況,若直接輸入 git add bin/ , git 會說明情況
The following paths are ignored by one of your .gitignore files: bin Use -f if you really want to add them. fatal: no files added
強制加入請輸入 git add –force bin/
# On branch master # # Initial commit # # Changes to be committed: # (use "git rm --cached <file>..." to unstage) # # new file: bin/AndroidManifest.xml # new file: bin/R.txt # new file: bin/classes/android/support/v7/appcompat/R$anim.class # new file: bin/classes/android/support/v7/appcompat/R$attr.class # new file: bin/classes/android/support/v7/appcompat/R$bool.class # new file: bin/classes/android/support/v7/appcompat/R$color.class # new file: bin/classes/android/support/v7/appcompat/R$dimen.class # new file: bin/classes/android/support/v7/appcompat/R$drawable.class # new file: bin/classes/android/support/v7/appcompat/R$id.class ...
第2種情況為 submodule。
這種情況比較複雜一些,必須進行4個步驟。
Note : 注意這4個步驟可能會毀損 git repo,若不確定情況,可以先試試第3步驟。
1. 在.gitmodule中刪除相關的連結
2. 在.git/config中刪除相關的連結
3. 執行 git rm –cached path_to_submodule
rm 'path_to_submodule'
4. 執行 git commit
a[master 5c270a9] [xxxx] Remove file in submodule. 1 file changed, 1 deletion(-) delete mode 160000 path_to_submodule
Description:
以 android project 為例,大多數的情況下,不需要追蹤 /bin 資料夾裡的檔案。但若已經將bin資料夾加入追蹤。要如何讓 bin 回到 untrack 的狀態?
Solution:
1.移動到 project 根目錄下輸入
git rm –cached -r bin
rm 'bin/AndroidManifest.xml' rm 'bin/R.txt' rm 'bin/classes/android/support/v7/appcompat/R$anim.class' rm 'bin/classes/android/support/v7/appcompat/R$attr.class' rm 'bin/classes/android/support/v7/appcompat/R$bool.class' rm 'bin/classes/android/support/v7/appcompat/R$color.class' rm 'bin/classes/android/support/v7/appcompat/R$dimen.class' rm 'bin/classes/android/support/v7/appcompat/R$drawable.class' rm 'bin/classes/android/support/v7/appcompat/R$id.class' rm 'bin/classes/android/support/v7/appcompat/R$integer.class' rm 'bin/classes/android/support/v7/appcompat/R$layout.class' rm 'bin/classes/android/support/v7/appcompat/R$string.class' rm 'bin/classes/android/support/v7/appcompat/R$style.class' ...
2.
commit 該動作。(git commit)
[master 846ecc5] [Test] Ignore all files in /bin folder. 31 files changed, 620 deletions(-) delete mode 100644 bin/AndroidManifest.xml delete mode 100644 bin/R.txt delete mode 100644 bin/classes/android/support/v7/appcompat/R$anim.class delete mode 100644 bin/classes/android/support/v7/appcompat/R$attr.class delete mode 100644 bin/classes/android/support/v7/appcompat/R$bool.class delete mode 100644 bin/classes/android/support/v7/appcompat/R$color.class delete mode 100644 bin/classes/android/support/v7/appcompat/R$dimen.class delete mode 100644 bin/classes/android/support/v7/appcompat/R$drawable.class delete mode 100644 bin/classes/android/support/v7/appcompat/R$id.class delete mode 100644 bin/classes/android/support/v7/appcompat/R$integer.class delete mode 100644 bin/classes/android/support/v7/appcompat/R$layout.class delete mode 100644 bin/classes/android/support/v7/appcompat/R$string.class delete mode 100644 bin/classes/android/support/v7/appcompat/R$style.class delete mode 100644 bin/classes/android/support/v7/appcompat/R$styleable.class ...
3.
使用 git status 查詢,可以看到 bin 資料夾已經回到 untracked 狀態。
# On branch master # Untracked files: # (use "git add <file>..." to include in what will be committed) # # bin/ nothing added to commit but untracked files present (use "git add" to track)
Law of Demeter (LOD)
定義:
也稱最少知識原則(Principle of Least Knowledge)。
模組應該儘可能的減少其他模組交互,目的在於降低彼此之間的依賴。
說明:
以下為必須遵循 LOD 的條件
類別 O 的任何方法 m 只能呼叫屬於以下情況的方法
1. 類別 O 本身的方法。
2. 傳入 m 的參數的方法。
3. 在 m 中建立對象的方法。
4. 任何直接持有的對象方法。
Example Code :
public class Man {
private Vehicle mVehicle;
private void methodInMan(){}
public void driveVehicle(Vehicle vehicle){
methodInMan();// Rule 1
vehicle.drive();// Rule 2
new Car().drive();// Rule 3
mVehicle = new Bike();
mVehicle.drive();//Rule 4
new Bike().getClass().getName(); //violation at getName()
}
}
如何修改遵循 LOD ,可以在 Bike 中直接建立方法來取得 class name,如下
public class Bike implements Vehicle{
...
public String getClassName(){
return getClass().getName();
}
}
client 端呼叫
public class Man {
private Vehicle mVehicle;
private void methodInMan(){}
public void driveVehicle(Vehicle vehicle){
methodInMan();// Rule 1
vehicle.drive();// Rule 2
new Car().drive();// Rule 3
mVehicle = new Bike();
mVehicle.drive();//Rule 4
new Bike().getClassName(); //Rule 3
}
}
注意:
在重構中的程式碼壞味道第15點 Message Chain (過度耦合的消息鏈) 其本身也是描述相同的問題:
一個對象請求另一個對象,然後再向後者請求一個對象。
代表對象之間的結構緊密耦合,一旦對象關係發生變化,客戶端不得不做出相應修改。
可以使用 Hide Delegate 來重構!!
Interface Segregation Principle (ISP)
定義:
客戶端不應該被強迫依賴不需要的方法。(介面應該只提供客戶端需要的方法)
說明:
一個過大的 interface ,通常代表其中有某些功能是客戶端不需要的,如果客戶端實作了不需要的功能 ,這些功能會造成不必要的耦合。
我們可以把過大的 interface 分離,將其中某些功能拆離到另一個 interface 中。
Example Code:
回到 OCP 的初始範例,其中 Bike , Car 都有類似的方法( driveBike , driveCar)。
public class Bike {
public void driveBike(){
System.out.println("drive Bike");
}
}
public class Car {
public void driveCar(){
System.out.println("drive Car");
}
public void openWindow(){
System.out.println("open window");
}
}
現在必須建立抽象層 Vehicle,和 OCP 不同的是我們把 openWindow 也放到 Vehicle 中。
public interface Vehicle {
public void drive();
public void openWindow();
}
對 Bike 而言,必須實作 openWindow 行為,但該行為對 Bike 是沒有意義,也違反了 ISP。
public class Bike implements Vehicle{
public void drive(){
System.out.println("drive Bike");
}
@Override
public void openWindow() {
// unmeaning action for Bike
}
}
為了遵循 ISP,可以另外建立 Window 介面,並把 openWindow 移到 Window 介面中。
public interface Window {
public void openWindow();
}
現在 Bike 可以不必實作 openWindow 方法。
public class Bike implements Vehicle{
public void drive(){
System.out.println("drive Bike");
}
}
Dependence Inversion Principle (DIP)
定義:
高層模組不應該相依於低層模組,兩者都應該相依於抽象。
抽象不應該相依於細節,細節應該相依於抽象。
說明:
傳統的軟體開發方法,如結構化分析與設計傾向於創造高階模組相依於低階模組。目標就是描述高階模組如何呼叫低階模組的子程式結構。
相較於以傳統程序化方法的相依結構,設計良好的物件導向程式的相依結構應該是反向的。
高層指的是依賴者 , 低層指的是被依賴者。如下圖

模組之間的依賴應該透過抽象,而不應該透過具體的方式(針對介面寫程式)
對象的引用盡量是抽象型態而不是具體型態。
因此使用DIP之後應該為

HighLevel 和 LowLevel 都相依於 abstract level。
注意:
若是具體類別已經相當穩定,不太會變化,依賴於該具體類別也是無妨。
Example Code:
以先前的 LSP 的範例 來說明
public interface Vehicle {
public void drive();
}
public class Bike implements Vehicle{
public void drive(){
System.out.println("drive Bike");
}
}
public class Car implements Vehicle{
public void drive(){
System.out.println("drive car");
}
public void openWindow(){
System.out.println("open window");
}
}
若用一個 Man 來代表使用者,並讓 Man 可以操縱 Bike,在未加考慮的情況會直接寫出以下 code,並違反了 DIP。
在 driveBike 的方法參數,並沒有依賴抽象(Vehicle),相反的依賴具體型態(Bike)。
public class Man {
public void driveBike(Bike bike){
bike.drive();
}
}
修改的方法很簡單,把參數型態改為 Vehicle。
另一個依賴抽象的好處是可以減少多餘的code,在依賴具體 Bike 的情況下, 若我們想讓 Man 可以駕駛 Car, 必須還要寫出另一個 driveCar 方法。
public class Man {
public void driveBike(Bike bike){
bike.drive();
}
public void driveCar(Car car){
car.drive();
}
}
修改 Man 的 driveVehicle 方法,讓其參數型態依賴抽象(Vehicle)。
public class Man {
public void driveVehicle(Vehicle vehicle){
vehicle.drive();
}
}
調用端呼叫 driveVehicle 方法可以根據傳入參數型態的不同,具有不同的行為。
public class Main {
public static void main(String[] args) {
Man man = new Man();
man.driveVehicle(new Bike());
man.driveVehicle(new Car());
}
}
另一種依賴具體的形式為基本型別。如
public int getSummary(int a, int b)
{
...
}
如果需要支援多種 Summary 算法,可以引入抽象層為參數。
public int getSummary(CustomType a, CustomType b){
...
}
public interface CustomType{
...
}
Liskov Substitution Principle (LSP)
定義:
衍生類別(Sub Type)必須能夠替換成他們的基礎類別(Base Type)。
衍生類別應該可以替換任何基楚類別出現的位置,且程序還能正常工作。
說明:
1. 不能僅用 is-a 的關係就建立繼承,必須考慮是否在基礎類別中有些方法對衍生類別而言是不需要或是無意義的。
這些沒有意義的方法會造成不可預期的結果。
2.一些違反 LSP 觀察點(基本上從衍生類別刪除基礎類別的功能就代表了無法取代基礎類別,因而違反了LSP)
1.退化函式(不一定絕對違反LSP,但需要檢查一下),基本上退化函式就是繼承基礎類別的函式,但在該函式中沒有做任何事,這種函式就是沒有意義的。
public class BaseClass {
public void method(){
//do something
}
}
public class DeriveClass extends BaseClass{
@Override
public void method() {
//do nothing
}
}
DeriveClass 的 method 可能違反了LSP
2.在衍生類別丟出基礎類別沒有的異常
注意:
必須要有繼承關係才需要考慮 LSP ,而 LSP 是讓設計達到 OCP 的規則之一 。
Example Code :
Vehicle 有2個簡單的方法, 分別是 drive (駕駛)和 openWindow (打開車窗)。
public class Vehicle {
public void drive(){
System.out.println("drive vehicle");
}
public void openWindow(){
System.out.println("open window");
}
}
若單以語意而言 Vehicle 和 Bike 有 is-a 的關係,也有共同的方法 drive , 但 openWindow 對 Bike 而言是沒有意義的。
若強制讓 Bike 繼承 Vehicle 會有2種情況。
1. Bike 不覆寫 openWindow 方法
public class Bike extends Vehicle{
public void drive(){
System.out.println("drive Bike");
}
}
2. Bike 覆寫 openWindow 方法,但方法內容令人迷惑。
public class Bike extends Vehicle{
public void drive(){
System.out.println("drive Bike");
}
public void openWindow(){
System.out.println("Bike can't open window");
}
}
不論哪種方法都無法到達呼叫端預期的結果 e.g.
Vehicle bike = new Bike(); bike.openWindow();// why bike can open window?
因此 Bike 繼承 Vehicle 不符合 LSP 。
如何讓 Bike 符合 LSP?
我們可以重新建立抽象層 Vehicle 並提取 Bike 和 Car 共同的部份並放入 Vehicle 中,並讓2者都派生 Vehicle。
public interface Vehicle {
public void drive();
}
Bike 實作 Vehicle
public class Bike implements Vehicle{
public void drive(){
System.out.println("drive Bike");
}
}
Car 實作 Vehicle
public class Car implements Vehicle{
public void drive(){
System.out.println("drive car");
}
public void openWindow(){
System.out.println("open window");
}
}
最後以語意來說 Car , Bike 都是 Vehicle。 Vehicle 的方法對於 Car , Bike 都是有意義的。