使用Travis-CI構(gòu)建Android項(xiàng)目并自動打包部署到GitHub-Release

CI

CI (Continuous Integration,持續(xù)集成),CI 是 XP(Extreme Programming,極限編程)的實(shí)踐之一。近年來
人們普遍使用 Jenkins 等軟件來實(shí)現(xiàn)這一目的柬批。

讓 CI 軟件監(jiān)視倉庫,可以在開發(fā)者發(fā)送提交后立刻執(zhí)行自動測試構(gòu)建味混。通過持續(xù)執(zhí)行這樣一個(gè)操作,可以檢測出開發(fā)者意外發(fā)送的提交或無意的邏輯偏差,讓代碼保持在一定質(zhì)量以上窃植。

在現(xiàn)代的軟件開發(fā)中持續(xù)集成已經(jīng)不可或缺,甚至逐漸成為開發(fā)中的常識箩祥。在開源世界中也是同樣志电。

常見的CI有: Travis CI 骇笔、 Jenkins 、Circle CI

基于Travis CI的Android自動發(fā)布流程

Travis CI對Github上的開源Repo是免費(fèi)的竿拆,私有Repo收費(fèi)宙拉。

可以將表示Travis CI的執(zhí)行結(jié)果的圖片嵌入到markdown中

,這個(gè)圖片在很多GitHub上的項(xiàng)目中都有見到丙笋。

Travis CI使用YAML文件作為構(gòu)建腳本惫企。

這里我們利用Travis CI來自動根據(jù)我們打的Git Tag來構(gòu)建APK并Push到自己的Github Release上(也可設(shè)置分發(fā)到fir.im)晃虫。

使GitHub帳號登錄Travis CI;點(diǎn)擊右上將腳的頭像在岂,點(diǎn)擊"Sync account"按鈕豫缨,然后打開對自己項(xiàng)目的Hook開關(guān)缩举;最后在對應(yīng)的項(xiàng)目中添加.travis.yml文件來配置 Travis CI即可洁奈。

使用Travis CI時(shí)串慰,剛開始一直無法發(fā)現(xiàn)倉庫里面的其它分支;后來再倉庫頁面 setting -> Integrations & services -> ... -> Test serviece 笙各;對其進(jìn)行測試后钉答,Travis CI才有反映。

Android項(xiàng)目對應(yīng)的 .travis.yml 文件

下面是一個(gè)簡單的Android項(xiàng)目下的 .travis.yml 文件

# .travis.yml 文件

# 聲明構(gòu)建語言環(huán)境
language: android
android:        # 配置信息
  components:
    # 你可能需要修改下面兩項(xiàng)的版本
    - build-tools-25.0.0
    - android-25
      # Android Support Repository
    - extra-android-m2repository
      # Support Library
    - extra-android-support
script:
  # 生成 APK
  - ./gradlew assembleRelease

# 部署
deploy:
  # 部署到GitHub Release酪惭。
  # 除此之外希痴,Travis CI還支持發(fā)布到fir.im者甲、AWS春感、Google App Engine等
  provider: releases
  # Github oauth token
  api_key: "GITHUB OAUTH TOKEN"
  # 部署文件路徑;對于Android就部署生成的 apk 文件
  file: "FILE TO UPLOAD"
  # 避免 Travis CI在部署之前清空生成的APK文件
  skip_cleanup: true
  # 發(fā)布時(shí)機(jī)
  on:
    # tags設(shè)置為true表示只有在有tag的情況下才部署
    tags: true

先來測試上面的文件。 創(chuàng)建一個(gè)簡單的Android工程進(jìn)行測試鲫懒;在工程的根目錄添加 上述 .travis.yml 文件嫩实。

根據(jù)實(shí)際情況修改文件中相關(guān)內(nèi)容,這里修改file: app-release-unsigned.apk窥岩;還有api_key就參考下文 "方法一:將GitHub的token設(shè)置為環(huán)境變量"

然后測試 Travis CI服務(wù):

GitHub對應(yīng)倉庫頁面 setting -> Integrations & services -> ... -> Test serviece 

這里生成的 app-release-unsigned.apk 并沒有進(jìn)行過簽名甲献。

一份示例文件: android_Travis_CI_Test/travis.yml.1.bak
Travis CI中有這樣一句提示: Please note that deploying GitHub Releases works only for tags, not for branches.
在這里可以檢測你的.travis.yml有沒有基本的語法錯(cuò)誤: Validate your .travis.yml file

遇到的問題

錯(cuò)誤1:

Error: Could not find or load main class org.gradle.wrapper.GradleWrapperMain

原因:推送到GitHub中的Android項(xiàng)目中沒有: gradle/wrapper/gradle-wrapper.jar 文件(被.gitignore_global忽略了)。

錯(cuò)誤2:

FAILURE: Build failed with an exception.
* What went wrong:
A problem occurred configuring project ':app'.
> You have not accepted the license agreements of the following SDK components:
  [ConstraintLayout for Android 1.0.2, Solver for ConstraintLayout 1.0.2].
  Before building your project, you need to accept the license agreements and complete the installation of the missing components using the Android Studio SDK Manager.

如果使用了ConstraintLayout則會出現(xiàn)錯(cuò)誤2颂翼。
上面的意思是:您尚未接受以下SDK組件的許可協(xié)議:[ConstraintLayout for Android 1.0.2晃洒,Solver for ConstraintLayout 1.0.2]。在構(gòu)建項(xiàng)目之前朦乏,您需要接受許可協(xié)議球及,并使用Android Studio SDK Manager完成缺少組件的安裝。

解決辦法在文件中添加如下內(nèi)容:

before_install:
  - mkdir "$ANDROID_HOME/licenses" || true
  - echo -e "\n8933bad161af4178b1185d1a37fbf41ea5269c55" > "$ANDROID_HOME/licenses/android-sdk-license"
  - echo -e "\n84831b9409646a918e30573bab4c9c91346d8abd" > "$ANDROID_HOME/licenses/android-sdk-preview-license"

Accept ConstraintLayout licenses · Issue #6617 · travis-ci/travis-ci
android - Travis CI failed because cannot accept license Constrain Layout - Stack Overflow

錯(cuò)誤3:

Dpl does not have permission to upload assets. Make sure your token contains the repo or public_repo scope.
dpl.2
Preparing deploy
failed to deploy

之前誤以為 public access就是public_repo呻疹;回到 GitHub頁面修改token的訪問權(quán)限吃引,添加 public_repo scope 然后更新。

錯(cuò)誤4:

/home/travis/.rvm/gems/ruby-2.2.7/gems/octokit-4.6.2/lib/octokit/client/releases.rb:86:in `initialize': No such file or directory @ rb_sysopen - app/build/outputs/apk/app-release.apk (Errno::ENOENT)

/home/travis/.rvm/gems/ruby-2.2.7/gems/octokit-4.6.2/lib/octokit/client/releases.rb:86:in `initialize': No such file or directory @ rb_sysopen - app/build/outputs/apk/app-debug.apk (Errno::ENOENT)

在部署前使用ls命令查看app/build/outputs/apk/路徑下的文件刽锤。添加如下語句:

before_deploy:
  - ls app/build/outputs/apk/

Log輸出:

$ ls app/build/outputs/apk/
app-release-unsigned.apk

解決方法:

為構(gòu)建添加緩存

構(gòu)建速度好慢镊尺,嘗試添加緩存:

before_cache:
 - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
 - rm -fr $HOME/.gradle/caches/*/plugin-resolution/

cache:
 directories:
 - $HOME/.gradle/caches/
 - $HOME/.gradle/wrapper/

手動清楚緩存:

參考:Caching Dependencies and Directories - Travis CI

加密自動化構(gòu)建中使用到的密碼和token

自動化構(gòu)建中需要用到密碼和證書并思,我們必須保證的安全性庐氮。為此Travis CI為此提供了2種解決方案:

  • 一種是對敏感信息、密碼纺荧、證書旭愧、token等進(jìn)行對稱加密,在CI構(gòu)建環(huán)境時(shí)解密
  • 另一種是將密碼等通過Travis CI的控制臺(即網(wǎng)站)設(shè)置為構(gòu)建時(shí)的環(huán)境變量宙暇。

獲取GitHub的認(rèn)證

將apk部署到GitHub Release需要向Github傳送文件输枯,就需要獲得GitHub的相關(guān)認(rèn)證。

獲取認(rèn)證的方法:

  • 可以是在配置文件.travis.yml中使用明文密碼占贫。極不安全桃熄。
  • 或使用 Github Personal Access Token 進(jìn)行驗(yàn)證。下面介紹型奥。

創(chuàng)建 Github Personal Access Token

在Github -> settings -> Personal access token 生成一個(gè)勾選了public_repo scope的Github Personal Access Token瞳收。

生成 token: Github auth token on TravisCI
Creating a personal access token for the command line - User Documentation

生成了Github Personal Access Token后我們就將其應(yīng)用到 .travis.yml中。

應(yīng)用Github Personal Access Token的兩種方法

方法一:將GitHub的token設(shè)置為環(huán)境變量

  • 在Travis CI網(wǎng)站中對應(yīng)的項(xiàng)目頁面厢汹,點(diǎn)擊"More options"然后選擇上圖中的 setting 螟深。
  • 在“Environment Variables”下添加環(huán)境變量:這里我添加 "GH_TOKEN"變量名稱,值就復(fù)制你之前生成的token烫葬,并確保"Display value in build log" 為"OFF"狀態(tài)界弧。

然后在.travis.yml文件中這樣使用該環(huán)境變量:

api_key:
    secure: ${GH_TOKEN}

Github Personal Access Token在第一次復(fù)制后就會自動隱藏凡蜻,無法再次查看;如果忘記了token就只有重新生成 token垢箕。

方法二:在本地安裝travis并使用travis的相關(guān)命令對token進(jìn)行加密

token可以重復(fù)使用划栓,但是對于特定的倉庫必須重新運(yùn)行上面的命令進(jìn)行加密并指定倉庫。

  • 安裝ruby条获≈臆瘢可參考:Ubuntu安裝ruby到opt下的rbenv目錄
  • 查詢最新的Travis CI的版本: gem search travis。我這里是 travis (1.8.8)
  • 安裝該版本的travis: gem install travis -v 1.8.8 --no-rdoc --no-ri
  • 查看是否安裝完成: travis --version帅掘,然后會提示你“shellcompletion not installed ...” shell補(bǔ)全沒有安裝是否需要安裝委煤,輸入 y。重新打開終端即可對該命令使用tab補(bǔ)全修档。
  • 如果首次運(yùn)行素标,travis會提示需要登錄,可以通過運(yùn)行travis login并輸入Github用戶名密碼即可萍悴⊥吩猓或者如果你不想輸入密碼,使用travis login --github-token <之前生成的github-token>也可進(jìn)行登錄癣诱。
  • 更簡單一點(diǎn)的是直接使用travis encrypt <之前生成的token> -r <owner>/<repo>其中 -r用于指定倉庫计维。然后有根據(jù)命令輸出添加相關(guān)信息到.travis.yml文件。示例:
    deploy:
    api_key:
      secure: "XR0ZYSp+eWOhHwO0az80nEZoOOyaOIeV7brTekEdl7APUfPziQHrV4G/xPxPZB2Fcn13efyiwpPbmXpSVkIqJJZg2Ok/efBviUIwgl5pqGrddgXEskW8+lebHJA/17JjmXoo2ZT9BcmMlVrT0Dj5WCJXH2QOSAXzVy/9DO7e3pEijAImCHgh0oH/hQzlO86IMBn3Fzlb520bplEI4fnuJNU/nKZrl05dTRCso2ZB2/MXt1X5vfYm4zrwjTgZXOpfSVur6kv/EMNQFDiGLa3F22m1FDQULDfHY7evT79hYlcRKVGx7hoQRrOxXr0Ag6wcb+4buvwaxutNPjaEXkH3FfocrIGxZtox1kE19ebqjc6fT/CH85Lf+AqT2rR86Wr5aaEQcKAxNZxUJtir5MAIo0j7vkx1PUi0MCb9K6JHca9lkFsoYP1L0pjDztD8w4J15rv5TCV/JjMxhCGQkGwDH+M3Z7dQOgikoRaO11o6C5/fC8qfCeGKT3GzL9966dy0UY9ZDQbB7y3donlWgoTZHn6xahGNlP4CYiP6Ax94egDmbSmyiJud5Y0NmwXMY0IEuJPWEFkmBdGEbZuqqsL3mxqxebox7q7gz613ToZSsfbNhKw3ouETftjXoMnlfRFIl7LSAOwAtL5Wig5zlwCMafGTFaGpLH0zl02Udjy3UbU="
    
  • 或者使用命令travis encrypt GITHUB_TOKEN=<之前生成的token> -r <owner>/<repo>,加密環(huán)境變量GITHUB_TOKEN撕予。其中 -r用于指定倉庫鲫惶。這里解密之后得到GITHUB_TOKEN=<之前生成的token>而并非<之前生成的token>

對于加密環(huán)境變量GITHUB_TOKEN的形式实抡,需這樣使用:

env:
  global:
    secure: "XR0ZYSp+eWOhHwO0az80nEZoOOyaOIeV7brTekEdl7APUfPziQHrV4G/xPxPZB2Fcn13efyiwpPbmXpSVkIqJJZg2Ok/efBviUIwgl5pqGrddgXEskW8+lebHJA/17JjmXoo2ZT9BcmMlVrT0Dj5WCJXH2QOSAXzVy/9DO7e3pEijAImCHgh0oH/hQzlO86IMBn3Fzlb520bplEI4fnuJNU/nKZrl05dTRCso2ZB2/MXt1X5vfYm4zrwjTgZXOpfSVur6kv/EMNQFDiGLa3F22m1FDQULDfHY7evT79hYlcRKVGx7hoQRrOxXr0Ag6wcb+4buvwaxutNPjaEXkH3FfocrIGxZtox1kE19ebqjc6fT/CH85Lf+AqT2rR86Wr5aaEQcKAxNZxUJtir5MAIo0j7vkx1PUi0MCb9K6JHca9lkFsoYP1L0pjDztD8w4J15rv5TCV/JjMxhCGQkGwDH+M3Z7dQOgikoRaO11o6C5/fC8qfCeGKT3GzL9966dy0UY9ZDQbB7y3donlWgoTZHn6xahGNlP4CYiP6Ax94egDmbSmyiJud5Y0NmwXMY0IEuJPWEFkmBdGEbZuqqsL3mxqxebox7q7gz613ToZSsfbNhKw3ouETftjXoMnlfRFIl7LSAOwAtL5Wig5zlwCMafGTFaGpLH0zl02Udjy3UbU="

deploy:
  api_key:
    secure: ${GITHUB_TOKEN}

這里可以對 secure: 字串的作用做一個(gè)猜測: Travis讀取到 secure 時(shí)便會解密該字串欠母,并將解密后得到的值替換 secure 這行內(nèi)容。比如如果加密的是 變量=值吆寨,那么當(dāng)其出現(xiàn)在上文的 global: 時(shí)赏淌,就是定義了一個(gè)全局的環(huán)境變量,然后就可在后面使用該變量啄清。

可以使用 travis help encrypt查看命令幫助;
使用 travis repos列出所有倉庫辣卒;參考:travis-ci/travis.rb: Travis CI Client (CLI and Ruby library)

Travis CI's use of GitHub API Scopes - Travis CI

加密簽名apk時(shí)使用到的密碼和keystore文件(方式一)

事先創(chuàng)建好key和keystore文件掷贾。 這里我把keystore文件的后綴名變?yōu)榱?keystore,大家使用的都是 .jks

1荣茫、 加密keystore文件my.keystore: travis encrypt-file my.keystore -r FanDean/android_Travis_CI_Test想帅。根據(jù)命令輸出的提示,將加密后的keystore文件 my.keystore.enc 放入Android工程的根目錄啡莉;并在 .travis.yml 文件中添加如下內(nèi)容:

before_install:
  - openssl aes-256-cbc -K $encrypted_f6c9a5f058a8_key -iv $encrypted_f6c6a5f053a8_iv -in my.keystore.enc -out my.keystore -d

2港准、 加密keystore password 憎乙、 key password 和 keyAlias。方法和加密 GitHub Token 一樣叉趣;也可使用添加環(huán)境變量的方式。

這里我們準(zhǔn)備下面三個(gè)環(huán)境變量该押,并通過在travis網(wǎng)站添加環(huán)境變量的形式進(jìn)行添加:

  • KEYSTORE_PWD
  • KEY_PWD
  • KEY_ALIAS

具體添加方式略(后面我們使用另外一種方法)疗杉。

.travis.yml 文件中添加如下內(nèi)容對apk進(jìn)行簽名:

before_deploy:
- cp $TRAVIS_BUILD_DIR/.keystore $HOME
# 記住這里已經(jīng)將當(dāng)前工作目錄切換到 apk 下
- cd app/build/outputs/apk/
# Signing 簽名
- jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore $HOME/my.keystore -storepass $KEYSTORE_PWD -keypass $KEY_PWD app/build/outputs/apk/app-release-unsigned.apk $KEY_ALIAS

# Verification 驗(yàn)證您的 APK 是否已簽署
- jarsigner -verify app-release-unsigned.apk
- "${ANDROID_HOME}/build-tools/24.0.2/zipalign -v 4 app-release-unsigned.apk app-release.apk"

jarsigner - JAR Signing and Verification Tool;也可以用來對apk進(jìn)行簽名蚕礼。

看起來比較麻煩烟具。

加密簽名apk時(shí)使用到的密碼和keystore文件(方式二)

參考下面的文章,我們可以使用一種新的方式:

創(chuàng)建并加密 keystore.properties 文件

在Android項(xiàng)目根目錄創(chuàng)建 keystore.properties文件保存keyPwd等變量奠蹬。

storePwd=***
keyPwd=***
keyAlias=test_key
storeFileLocal=/home/fan/.android/my_keystore/my.keystore
storeFileCI=../my.keystore

同樣加密 keystore.properties 文件: travis encrypt-file keystore.properties -r FanDean/android_Travis_CI_Test
同樣添加 "openssl aes-256-cbc -K $encrypted_ad23ea9b533c_key -iv $encrypted_ad23ca6e583c_iv -in keystore.properties.enc -out keystore.properties -d" 到 before_install 下面朝聋。

修改app/build.gradle 文件

然后在 app/build.gradle 文件中添加如下內(nèi)容,用于保護(hù)您的密鑰和判斷當(dāng)前是在本地還是在 CI服務(wù)器:

// Create a variable called keystorePropertiesFile, and initialize it to your
// keystore.properties file, in the rootProject folder.
def keystorePropertiesFile = rootProject.file("keystore.properties")

// Initialize a new Properties() object called keystoreProperties.
def keystoreProperties = new Properties()

// Load your keystore.properties file into the keystoreProperties object.
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))

def keyStoreFile = file(keystoreProperties['storeFileLocal'])

// 判斷是位于本地還是CI服務(wù)器
if (!keyStoreFile.exists()){
    keyStoreFile = file(keystoreProperties['storeFileCI'])
}

android {
    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPwd']
            storeFile keyStoreFile
            storePassword keystoreProperties['storePwd']
        }
    }

  ...

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }

  ...

忽略 keystore.properties文件

在 .gitignore_global 文件中添加:

keystore.properties

示例文件: android_Travis_CI_Test/.travis.yml

一些配置

script: ./gradlew testRelease assembleRelease

script:
- "./gradlew clean build connectedCheck -PdisablePreDex --stacktrace"

Travis Config · kawa89/Testing Wiki

script:
  - ./travis/test.sh

./gradlew clean testDebugUnitTest
./gradlew connectedAndroidTest --info
./gradlew clean assembleDebug

GoIV/.travis.yml at master · farkam135/GoIV

script:
   - ./gradlew assembleDebug testOnlineDebugUnitTest testOfflineDebugUnitTest checkstyle

查找android .travis.yml 的相關(guān)配置囤躁,可嘗試在GitHub中搜索 "android travis"然后選擇查看 issues 和 wiki 冀痕。

參考:

Jenkins學(xué)習(xí)資料 Jenkins Overview

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市宵距,隨后出現(xiàn)的幾起案子腊尚,更是在濱河造成了極大的恐慌,老刑警劉巖满哪,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件婿斥,死亡現(xiàn)場離奇詭異,居然都是意外死亡哨鸭,警方通過查閱死者的電腦和手機(jī)民宿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來像鸡,“玉大人勘高,你說我怎么就攤上這事》匚Γ” “怎么了华望?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長仅乓。 經(jīng)常有香客問我赖舟,道長,這世上最難降的妖魔是什么夸楣? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任宾抓,我火速辦了婚禮子漩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘石洗。我一直安慰自己幢泼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布讲衫。 她就那樣靜靜地躺著缕棵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪涉兽。 梳的紋絲不亂的頭發(fā)上招驴,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天,我揣著相機(jī)與錄音枷畏,去河邊找鬼别厘。 笑死,一個(gè)胖子當(dāng)著我的面吹牛拥诡,可吹牛的內(nèi)容都是我干的触趴。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼渴肉,長吁一口氣:“原來是場噩夢啊……” “哼雕蔽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起宾娜,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤批狐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后前塔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嚣艇,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年华弓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了食零。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡寂屏,死狀恐怖贰谣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情迁霎,我是刑警寧澤吱抚,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站考廉,受9級特大地震影響秘豹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜昌粤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一既绕、第九天 我趴在偏房一處隱蔽的房頂上張望啄刹。 院中可真熱鬧,春花似錦凄贩、人聲如沸誓军。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽昵时。三九已至,卻和暖如春评肆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背非区。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工瓜挽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人征绸。 一個(gè)月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓久橙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親管怠。 傳聞我的和親對象是個(gè)殘疾皇子淆衷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容