ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信

使用初衷

公司产品版本比较多,很多版本都需要独立化部署。导致产生了每个地址需要打包,每次打包就是一件麻烦的事情,常常一下午的时间过去了却都浪费在打包这件事情上了。So…研究了下Fastlane+Jenkins+蒲公英。可以方便的发布和管理版本。爬了很多坑。写出来让大家参考下。

蒲公英也可以用fir.im代替,后续也可以持续集成webhook机器人,邮件等通知推送。

方案选择

方案很多种,看自己情况选择。

1、利用空闲苹果电脑做服务器,跑Jenkins用来自动监听,只要git合并上传,就自动打包。

2、本地安装jerkins(不建议)。

3、本地主动触发/利用Alfred编写插件执行流程。

基础名词

  • Ruby,一种简单快捷的面向对象(面向对象程序设计)脚本语言

  • RVM是一个命令行工具,它允许您轻松地安装,管理和使用从解释器到多组gems多个ruby环境。

  • Gems RubyGems简称gems,是一个用于对 Ruby组件进行打包的 Ruby 打包系统。例如经常用的cocoapods就需要 gem 来管理。

  • Fastlane是Felix Krause大神写的fastlane是一套使用Ruby写的自动化工具集,用于iOS和Android的自动化打包、发布等工作。

  • pgyer/fir 第三方发布测试平台。

爬坑攻略

如果你在安装期间想很顺利的话,请务必按照步骤去做,本教程只针对Mac。

此文档是二次更新的,因为第一次写的时候,就根据安装记录写出来了,第二次在别的机器上部署的时候,就出现了各种问题,所以就有了这次的更新内容。

Ruby处理

使用Mac的小伙伴都应该能感受OSX的好处,本人对Mac中毒很深,要是让我说一点什么不好的话,就是价格太高。

各种环境,系统都自带了ruby,python等等,且不会出现全家桶的问候。

Mac系统自带ruby,但是是给系统自己使用,权限很小,所以在平时使用过程中,会出现很多问题,而且版本一般都比较低,所以大多人都在自己使用RVM来管理自己使用的ruby.

参照此文档来安装 RVM使用ruby 使用RVM来管理rubyhttps://www.jianshu.com/p/c44ef74d99f9

ruby安装完成之后,来进行下一步。

Fastlane 安装初始化

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第1张图片

  • 确保你已经安装了最新版本的Xcode命令行工具:
xcode-select --install

下方图片是已经安装则不用处理。

图片

  • 官方文档支持三种方式安装,我使用的是ruby,刚才不是使用RVM来管理自己的ruby 么,接下来我们来安装fastlane

    gem install fastlane -NV

方法 os支持 描述
Homebrew 苹果系统 brew cask install fastlane
安装程序脚本 苹果系统 下载zip文件。然后双击install脚本(或在终端窗口中运行)。
RubyGems 带有Ruby版本> 2.0.0 gem install fastlane -NV

如果使用的是系统自带的ruby,可能会报错没有权限。

ERROR:  While executing gem ... (Gem::FilePermissionError)    You don't have write permissions for the /Library/Ruby/Gems/2.3.0 directory.ERROR:  While executing gem ... (Gem::FilePermissionError)    You don't have write permissions for the /usr/bin directory.

查看自己是否使用的是自己管理的ruby

 which ruby> 返回/Users/seven/.rvm/rubies/ruby-2.4.1/bin/ruby> 则说明已经使用RVM管理的Ruby了,非系统自带的。> 如果是usr/bin这个目录下的则说明还在使用系统自带的,可能会出现一系列权限的问题,请更换。

如果你非要使用系统自带的,也可以,也可能出现问题,也可能没事,跟系统版本也有关系,

下面给出系统自带问题的一些些方法。如跟我一样使用非系统自带ruby,请跳过此步骤。

> 方法一:sudo gem install fastlane -NV //提升权限,部分系统权限还是不够。> 方法二:安装到别的目录使用指定目录安装: sudo gem install -n /usr/local/bin fastlane> 方法三:还是使用RVM管理ruby
  • 初始化

安装完毕后,在终端进入到你的项目目录下。初始化fastlane

fastlane init

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第2张图片

如果没有报错,就跳过此条继续报错Exit status of command ‘bundle exec pod install’ was 1 instead of 0. bundler: failed to load command: pod (/usr/local/bin/pod)解决方法:在项目根目录下 fastlane/Fastfile 修改文件中的cocoapods’ cocoapods(use_bundle_exec: false)’

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第3张图片

这4个选项意思分别是:

  • 自动截屏。

  • 自动发布beta版本用于TestFlight。

  • 自动的App Store发布包。我们的目标是要提交审核到APP Store,按道理应该选这个,但这里我们先不选,因为选择了以后会需要输入用户名密码,以及下载meta信息,需要花费一定时间,这些数据我们可以后期进行配置。

  • 手动设置。(推荐)

如果选择了3 会提示让你输入账号(我选择手动配置)

按照提示输入开发者账号(付费账号) 输入Bundle Identifier 用于初始化配置,后期可在配置文件改。随便输入根据提供的信息,fastlane会自动为您生成一个配置。

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第4张图片

您可以看到新创建的./fastlane目录,包含Appfile和Fastfile文件。

最有趣的文件是fastlane/Fastfile,其中包含分发您的应用程序所需的所有信息。

这样就完成了初始化

蒲公英插件安装

初始化完毕后直接在终端输入

//fastlane fastlane search_plugins//列出可用插件fastlane add_plugin pgyer//安装插件

图片

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第5张图片

插件安装成功./fastlane目录下会生成Pluginfile的一个文件。插件安装不成功,就是fastlane安装的有问题。

配置Fastlane

文件名 描述
Appfile 从 Apple Developer Portal 获取和项目相关的信息 详情
Fastfile 核心文件,存放lane任务
Deliverfile deliver的配置文件,从 iTunes Connect 获取和项目相关的信息详细
metadata 同步iTC中的元数据
screenshots 同步iTC中的截图

fastlane工具集是配置fastlane最重要的

文件名 描述
match 证书和配置文件管理工具会重置证书,推荐新项目使用。
cert 自动创建管理iOS代码签名证书,会去自动创建证书,永远不会撤销现有的证书。如不能创建会报错。
sigh 用来创建、更新、下载、修复Provisioning Profile的工具
gym 自动化编译打包工具.shenzhen的代替品.【本文使用gym打包】
pem 自动生成、更新推送配置文件
produce 如果你的产品还没在iTunes Connect(iTC)或者Apple Developer Center(ADC)建立,produce可以自动帮你完成这些工作
deliver 自动上传截图,APP的元数据,二进制(ipa)文件到iTunes Connect
snapshot 自动截图(基于Xcode7的UI test)
frameit 可以把截的图片自动套上一层外边框
pilot 管理TestFlight的测试用户,上传二进制文件
boarding 建立一个添加测试用户界面,发给测试者,可自行添加邮件地址,并同步到iTunes Connect(iTC)
scan 自动运行测试工具,并且可以生成漂亮的HTML报告
spaceship 为pilot,boarding和deliver等工具提供和 iTC 和 ADC 的交互API。spaceship本来是个独立的项目,后来被Fastlane收编进来
WatchBuild 是一个独立的iTC监控工具,开启WatchBuild可以监控iTC上的文件状态,弹出MacOS自带的Notification
supply Android自动上传到Google Play工具
screengrab Android的自动截图工具
关于Appfile:
  • app_identifier(“bundle identifier”) # The bundle identifier of your app

  • apple_id(“[email protected]”) # Your Apple email address

  • itc_team_id(“App Store Connect Team ID”) # App Store Connect Team ID

  • team_id(“Developer Portal Team ID”) # Developer Portal Team ID

    这些在xcode或者appstore就能看到。

关于fastfile:
  • 里面存放了很多lane,每个lane相当于按顺序执行的工作流。每个lane可以存放多个action,action可以看做具体的执行动作

  • 生命周期

执行顺序 方法名 说明
1 before_all 在执行 lane 之前只执行一次
2 before_each 每次执行 lane 之前都会执行一次
3 lane 自定义的任务
4 after_each 每次执行 lane 之后都会执行一次
5 after_all 在执行 lane 成功结束之后执行一次
6 error 在执行上述情况任意环境报错都会中止并执行一次
  • 任务定义
定义 是否必须 说明 备注
desc false 方法描述 可多次使用达到换行的目的
name true 方法名 符号化的方法名
options false 方法参数 返回 Hash 类型
task true 方法主体 参考 ruby 的方法代码且支持 ruby 代码
  • 一个简单的lane
lane :deploy do  # 执行 pod instasll  cocoapods  # 执行 carthage bootstrap  carthage  # 增加build版本号  increment_build_number  # 编译代码  gym  # 发布到Apple Store  deliver(force: true)end
  • fastlane示例

  • 这个是我自己 配置到蒲公英的。同时导出ipa到./build文件夹下,以版本号和打包时间命名。这样就保留了每一版本包了。记得要在git中忽略./build文件夹。否则你会后悔的。

只需要项目根目录下执行即可(暂时还不行,需要看下下方gym配置)

fastlane topgyer desc:更新内容
desc "上传到测试版本到蒲公英"
  desc "生成本地版本"
  lane :topgyer do|option|
    puts "*************| 开始打包喽 |*************"

    #自动增加build
    # increment_build_number

    #自动生成证书
    cert

    #自动生成配置文件
    # sigh(force: true)//我使用的是手动配置关闭这个。如需自动则打开即可

    #gym配置,打包输出。

    #fastlane gym --export_method ad-hoc
    #fastlane gym --export_method enterprise
    #fastlane gym --export_method app-store
    scheme_name = "xxxx"//应用名词

    
    version = get_version_number_from_plist(xcodeproj: "#{scheme_name}.xcodeproj",
                                        target: scheme_name,
                                        plist_build_setting_support: true, 
                                        build_configuration_name: 'Release')

    build = get_build_number_from_plist(xcodeproj: "#{scheme_name}.xcodeproj",
                                        target: scheme_name, 
                                        plist_build_setting_support: true,
                                        build_configuration_name: 'Release')
    # 获取version和build版本号() 
    # version = get_info_plist_value(path: "./#{scheme_name}/Info.plist", key: "CFBundleShortVersionString")
    # build = get_info_plist_value(path: "./#{scheme_name}/Info.plist", key: "CFBundleVersion")
    
    #导出路径
    output_directory = "./build"
    
    #导出名称
    output_name = "#{scheme_name}_#{version}_#{build}_#{Time.now.strftime('%Y%m%d%H%M%S')}.ipa"

    gym(
      export_method: "enterprise",//企业账号
      scheme: scheme_name, //名词
      clean: true,//是否清理上次编译
      output_directory: output_directory,//导出路径
      output_name: output_name//导出名词
     )
    
    puts "*************| 打包完成,开始上传到蒲公英 |*************"
    
    pgyer(api_key: "1212121", update_description: "#{option[:desc]}-#{version}_#{build}")//蒲公英设置查看key
    
    puts "*************| 上传蒲公英成功 |*************"
    puts "*************| 上传蒲公英成功 |*************"
    puts "*************| 上传蒲公英成功 |*************"
    
    #钉钉webhook机器人 url就是上方机器人信息中的webhook地址
    curl = %Q{
        curl 'url' \
        -H 'Content-Type:application/json' \
        -d '{
          "msgtype":"markdown",
          "markdown":{
            "title":"测试包新版本来啦!",
            "text":"### 测试包新版本来啦!  
                    #### 下载地址:下载地址  ![](这里是对应的二维码图片地址) 
                    ##### @所有人"
          },
          "at":{
            "isAtAll":true
          }
      }'
    }
    system curl
    
  end
scheme 设置

没有在gym或者fastfile写应用的名字的话。打包的时候会自动检测当前目录下的文件。列出来供你选择。也可以设置一下在gym中直接赋值,或者fastfile中增加即可。

关于Gymfile:
  • gym是用来自动化编译打包工具.shenzhen的代替品.【本文使用gym打包】

  • fastlane初始化Gym fastlane gym init会生成一个Gymfile的文件。

    ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第6张图片

  • 然后手动配置证书-Gymfile增加下面代码,同时不要使用自动生成配置文件这个选项sigh(force: true)

  • export_options(
    provisioningProfiles: {
    "com.xxx.xxx" => "profilesName",
    "com.xxx.xxxxxx" => "profilesName1"
    })
    
Fastlane问题汇总

使用过程中出现了很多问题,大部分问题都已经有人躺过水了,所以有问题先去看看issues

https://github.com/fastlane/fastlane/issues

gym编译报错解决不了请先检查三遍证书是否有问题
初始化报错请检查安装路径是否有问题。
问题 问题
ruby -v 版本低于2.0.0 gem版本不对 请升级ruby请更新gems
cocoapods没有放到Gemfile中 Gemfile文件中增加cocoapods
fastlane init 提示sudo 请检查ruby路径。如果没问题请检查user目录下.bash_profile。环境变量设置是否有效。export PATH=“ H O M E / . f a s t l a n e / b i n : HOME/.fastlane/bin: HOME/.fastlane/bin:PATH”
The generated archive is invalid, this can have various reasons:Usually it’s caused by the Skip Install option in Xcode, set it to NO xcode中修改build settings Skip Install 选项为NO
如果初始化成功 其他的问题大部分都是证书问题了。

以上Fastlane本地配置就完成了,想要继续深入可以往下看利用jenkins持续集成。

Jenkins持续集成


在持续集成(Continuous integration,简称CI)这块,Jenkins无疑是目前使用的比较多的一个开源框架

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第7张图片

JDK

系统要求:必须安装JDK 1.5以上版本,推荐安装最新版本的JDK[注意不支持java9]。可以通过以下命令查看是否安装JDK和JDK版本。JDK下载链接

http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

如需卸载请看下文

java -version

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第8张图片

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第9张图片

  • java卸载方法 以Mac为例子 [shift +cmd+g] 前往文件夹

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第10张图片

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第11张图片

  • 在系统偏好设置面板移除Java

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第12张图片

  • JDK安装

下载完毕JDK后如图按照提示安装即可

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第13张图片

图片

安装 Jenkins

Jenkins的官网 下载最新的 war 包。

https://jenkins.io/download/

推荐使用【Long-term Support (LTS)稳定版】

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第14张图片

下载完成后,打开终端,进入到 war 包所在目录直接运行,也可以将war包丢在Tomcat的webapp目录下面。

终端方式执行以下命令:

java -jar jenkins.war//默认对端口是8080如需指定可更改java -jar jenkins.war --httpPort=8888 //指定8888端口二者选一即可
初始化Jenkins

待Jenkins启动后,在浏览器页面输入以下地址:

http://localhost:8888//端口指定的多少写多少
注意不要关闭终端

第一次运行会出现如下界面,提示需要填写指定路径文件里面的内容(该内容也可以在终端上面看到)。

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第15张图片

根据提示目录打开initialAdminPassword文件,复制出密码,填写,Continue.

Jenkins用户/secrets/目录是没有读写权限的。修改权限。

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第16张图片ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第17张图片

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第18张图片

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第19张图片

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第20张图片

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第21张图片

这样Jenkins初始化完毕了,可以快乐的使用了。还是介绍下如何使用吧。

Jenkins使用
  • 启动方式

上文说了,如何初始化进入Jenkins,那么如果我关了再次怎么进入呢。别急hold~ 两种方式都可以

  • 方式一 跟初始化一样,进入war包所在目录通过命令启动,
java -jar jenkins.war
  • 方式二 运行命令来创建配置文件
sudo touch /Library/LaunchDaemons/org.jenkins-ci.plist

如果您使用了不同的用户名,请务必在plist中使用它。需要指定用户名,否则会以系统根用户的身份运行,这会让您在使用Jenkins时遇到麻烦

plist内容如下

      Label    Jenkins    KeepAlive        ProgramArguments          /usr/bin/java      -Dmail.smtp.starttls.enable=true      -jar      /usr/local/opt/jenkins/libexec/jenkins.war      --httpListenAddress=127.0.0.1      --httpPort=8080        RunAtLoad        UserName    jenkins  

现在可以重新启动或者运行下方命令来查看运行在http://localhost:8888的Jenkins 。

sudo launchctl load -w /Library/LaunchDaemons/org.jenkins-ci.plist
Jenkins配置

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第22张图片

插件管理

使用默认方式安装的基本插件都包含了,如果有需要可以自己来管理插件

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第23张图片

安全配置

在工作中可能要在Jenkins中创建用户,这样你的团队的其他成员就可以访问自己的工作。首先打开左侧菜单中的“管理Jenkins”页面,然后转到“配置全局安全性”。

启用安全性,如果不那么将允许任何登录用户做任何事情。

  • 进入系统管理 -> Configure Global Security -> 点击”启用安全” 。启用安全下面选择,“Jenkins专有用户数据库”,勾选允许用户可以注册。然后在“授权策略”中选择“任何用户可以做任何事(没有任何限制)”。当然,也可以使用LDAP身份认证机制,直接使用外部统一的身份机制来做认证。

  • 返回注册一个账户,登录之后再次进入安全管理。勾选“登录用户可以做任何事”,这样就只有登录用户才能做操作了。

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第24张图片

创建项目
  • “新建” —> 勾选“构建一个自由风格的软件项目” ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第25张图片

  • Generalios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第26张图片

  • 源码管理–Gitios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第27张图片

  • 设置源码的仓库,以便让 Jenkins 知道我们的 iOS 项目的代码在哪里。因为我的代码放在自己的仓库中(如果你用 Github 等其他仓库也是类似),所以要先告诉 Jenkins 如何获取代码。RepositoryUrl就是git地址

首先,我们需要配置 SSH,我们可以在 Jenkins 的证书管理中添加 SSH。在 Jenkins 管理页面,选择“Credentials”,然后选择“Global credentials (unrestricted)”,点击“Add Credentials”,如下图所示,我们填写自己的 SSH 信息,然后点击“Save”,这样就把 SSH 添加到 Jenkins 的全局域中去了。ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第28张图片

  • 类型SSH

  • userName 就是git中的名字 能知道是谁上传下载的就可以

  • PrivateKey ssh中的私钥,/Users/用户名/.ssh/id_rsa

  • Passphrase git密码。

  • 关于如何获取SSH大家可以看文尾,熟悉的人应该都会,不在此介绍

  • 回到刚刚新建的任务中,在源码管理中,选择 Git,按下图填好相关信息。注意:Credentials 不需要选择。

  • 同时也试了使用账号密码的Credentials。选择你账号密码创建的Credentials也可以。默认使用上边方法即可。

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第29张图片

自动测试忽略,不需要设置触发器。
构建环境设置
  • 直接用 fastlane 这个工具,所以这里不需要特别设置。
构建设置
  • 选择使用shell进行构建 更新内容自己维护
cd /users/xxx/xxx   //你项目根目录fastlane topgyer desc:更新内容!
构建后增加步骤
  • 可以邮件通知人员,逗号隔开,通知构建失败

来个整体截图

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第30张图片

开始构建
  • 返回面板会发现自己刚才新建的任务

    ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第31张图片

  • 点击任务名称进入详情、可以修改任务配置项和构建版本

    ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第32张图片

  • 点击构建

  • 休息一会

  • 登陆蒲公英就会发现多了一个版本。

构建日志查看
  • 构建失败或者想看到输出的,可以在任务详情,构建历史中,选择构建中的版本。

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第33张图片

  • 选择Console Output即可看到控制台输出

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第34张图片

  • 截取下最后输出success送给你

ios持续化集成-fastlane+jenkins+蒲公英+alfred+Webhook通知企业微信_第35张图片

节点管理可以参考以下,暂时不介绍

http://blog.csdn.net/birthmarkqiqi/article/details/62236437https://www.jianshu.com/p/047362b11403

附加1 本地alfred自动构建后webhook通知

  • 创建关键字

  • 执行shell脚本

    cd && cd '/Users/name/Desktop/test/'rm -r buildfastlane topgyer desc:{query}exit 0
    

    这样就不用手动去打开页面,然后去执行了,省去了繁琐步骤。

  • webhook

    webhook的话,如果用企业微信,蒲公英上传程序后默认可以集成,只需在蒲公英后台url中填写企业微信中群聊添加机器人Webhook的Url 就行了。

    企业微信,钉钉,飞书集成可以看这个蒲公英官方文档。https://seed.pgyer.com/WGNQkEpP

    当然也可以去各个应用端自己看api对接。

附加2 SSH设置

https://code.aliyun.com/help/ssh/README#ssh-keys

SSH key 可以让你在你的电脑和Code服务器之间建立安全的加密连接。先执行以下语句来判断是否已经存在本地公钥:

cat ~/.ssh/id_rsa.pub

如果你看到一长串以 ssh-rsassh-dsa开头的字符串, 你可以跳过 ssh-keygen的步骤。

提示: 最好的情况是一个密码对应一个ssh key,但是那不是必须的。你完全可以跳过创建密码这个步骤。请记住设置的密码并不能被修改或获取。

你可以按如下命令来生成ssh key:

ssh-keygen -t rsa -C "你的邮箱@xx.com"

这个指令会要求你提供一个位置和文件名去存放键值对和密码,你可以点击Enter键去使用默认值。

用以下命令获取你生成的公钥:

cat ~/.ssh/id_rsa.pub

复制这个公钥放到你的个人设置中的SSH/My SSH Keys下,请完整拷贝从ssh-开始直到你的用户名和主机名为止的内容。

如果打算拷贝你的公钥到你的粘贴板下,请参考你的操作系统使用以下的命令:

Windows:

clip < ~/.ssh/id_rsa.pub

Mac:

pbcopy < ~/.ssh/id_rsa.pub

GNU/Linux (requires xclip):

xclip -sel clip < ~/.ssh/id_rsa.pub

《一两浊酒》是我的个人公众号,我会分享一些自己的感悟,技术,理财和学习方法。如果您喜欢我的文章,可以关注公众号,获取最新内容及专辑。

你可能感兴趣的:(ios,jenkins,微信)