Git commit 中使用 gpg 签名提交

Ubuntu 下,GnuPG 2.0 的支持都在 gnupg2 这个 packages 下,通过下面命令安装:

sudo apt-get install gnupg2

GitHub 要求使用 GnuPG 2.1 及以后的版本。

Mac 下安装使用

在 Mac 下需要安装:

brew install gnupg
brew link --overwrite gnupg
brew install pinentry-mac # 密码输入管理器

然后在 shell 配置 (.bashrc~/.zshrc) 中添加 export GPG_TTY=$(tty).

添加配置

echo "pinentry-program /usr/local/bin/pinentry-mac" >> ~/.gnupg/gpg-agent.conf
killall gpg-agent

然后可以通过 echo "test" | gpg --clearsign 来验证一下。

Mac 下应该会弹出 GUI 窗口进行密码验证。

然后不要忘记配置 git config:

git config --global gpg.program gpg        # 配置全局的 gpg
git config --global commit.gpgsign true    # 配置每一个 commit 都需要 gpg

这里需要注意的一点是 Mac 下命令还叫做 gpg,但是在 Linux 下是 gpg2.

生成 GPG 签名

使用如下命令生成签名:

gpg2 --full-gen-key
  1. 选择默认 RSA and RSA
  2. 推荐 4096
  3. 过期时间
  4. 相关信息包括 Real name, Email, Comment(comment 可以写一些标示)
  5. 密码

如果遇到尝试卡在生成签名的步骤,提示

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

可能需要安装 rng-tools

sudo apt install rng-tools

之后在执行上面的命令。

使用以下命令查看本地密钥

gpg2 --list-keys --keyid-format LONG

结果

/home/einverne/.gnupg/pubring.gpg
---------------------------
pub   rsa4096/F80B65AAAAAAAAAA 2018-01-31 [SC]
uid                 [ultimate] Ein Verne (co) <email@address>
sub   rsa4096/B63A4CAAAAAAAAAA 2018-01-31 [E]

将其中的第三行 sec 中 rsa4096 后面的 ID 记住(F80B65AAAAAAAAAA),拷贝出来。

然后使用

gpg2 --armor --export ID | xclip -sel c

来获取 GPG KEY,拷贝 -----BEGIN PGP PUBLIC KEY BLOCK----------END PGP PUBLIC KEY BLOCK----- 之前,包括这两行的内容到 GitHub。

配合管道命令和 xclip ,使用 | xclip -sel c 可以直接将命令输出结果拷贝到系统粘贴板。

配置 GPG

产生 GPG,并且已经添加到 GitHub 后台,那么需要本地配置,告诉 git 本地签名。查看本地 gpg

gpg2 --list-keys --keyid-format LONG

添加配置,这里记得使用公钥

# 设置 gitconfig 配置签名的公钥 key
git config --global user.signingkey F80B65AAAAAAAAAA
# 设置签名使用的 gpg 软件
git config --global gpg.program gpg2
# 默认全部签名
git config --global commit.gpgsign true

将 GPG 添加到 .bashrc~/.zshrc

echo 'export GPG_TTY=$(tty)' >> ~/.bashrc

签名 commit

在提交时使用 -S 选项,来本地签名提交

git commit -S -m your commit message

提交标签同理

git tag -s mytag

可以使用 -v 来验证

git tag -v mytag

其他常用的 gpg 命令

生成 gpg key

gpg2 --full-generate-key

列出本地所有公钥

gpg2 --list-keys

查看秘钥

gpg2 --list-secret-keys

删除 uid 私钥

gpg2 --delete-secret-keys [uid]

删除 uid 公钥

gpg2 --delete-keys [uid]

问题

使用过程中遇到的一些问题

提交 commit 时 failed to sign the data

git commit -S 时如果遇到:

gpg: signing failed: Operation cancelled
gpg: signing failed: Operation cancelled
error: gpg failed to sign the data
fatal: failed to write commit object

尝试

export GPG_TTY=$(tty)

强制每次 git 提交使用 gpg 加密

git config --global commit.gpgsign true

对应如果选择关闭就直接使用 false 即可。

导出私钥用于不同电脑之间同步

在一台电脑中生成的 gpg secret key 可以用于不同的电脑

  • 首先运行 gpg2 --list-secret-keys 来确认本机的私钥,需要记住私钥的 ID (在第二列)
  • 导出私钥 gpg2 --export-secret-keys $ID > my-private-key.asc
  • 拷贝私钥到目标机器 (scp)
  • 导入私钥 gpg2 --import my-private-key.asc

如果在第二台机器中已经有了公钥,私钥,那么需要分别删除 gpg2 --delete-keysgpg2 --delete-secret-keys

如果熟悉 scp 可以直接将 ~/.gnupg 目录复制到新机器中:

scp -rp ~/.gnupg name@server_ip:~/

reference


2017-10-20 github , git , gpg , linux

升级 Spring MVC 3.2.x 到 4.x 注意事项

把 Spring 版本从 3.2.x 升级到了4.x ,这里记录一下。

新特性

Java 8 Support, 从 4.0 开始支持 Java 8,可以使用 lambda 表达式,等等 Java 8 的特性

Groovy DSL

新增 @RestController 注解,这样就不需要每个方法都使用 @ResponseBody 了。

更多内容可以查看: https://docs.spring.io/spring/docs/4.3.x/spring-framework-reference/htmlsingle/#spring-whats-new

注意事项

添加依赖

加入spring-context-support,以前3的版本不用加,但是4要加上,否则就会报 ClassNotFoundException,

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>4.3.12.RELEASE</version>
</dependency>

替换 Spring MVC jackson 依赖

更换Spring jackson依赖,Spring MVC返回 json 的时候需要依赖jackson的jar包,以前是codehaus.jackson,现在换成了fasterxml.jackson 同时修改配置文件

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.7.0</version>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.7.0</version>
</dependency>

同时还要修改Spring的配置文件

<bean
    class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="messageConverters">
        <list>
             <ref bean="stringHttpMessageConverter" />  
            <bean
                class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            </bean>
        </list>
    </property>
</bean>

<bean id="stringHttpMessageConverter"
    class="org.springframework.http.converter.StringHttpMessageConverter">
    <property name="supportedMediaTypes">
        <list>
            <value>text/plain;charset=UTF-8</value>
        </list>
    </property>
</bean>

xsd 文件版本

更换springxsd文件的版本,直接从 3.0 升级到 4.0 即可

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

修改 quarz 版本

修改quarz版本,用2以上的版本,maven依赖如下

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.2.2</version>
</dependency>

2017-10-19 Spring , Java , Web

Linux 安装 nodejs

nodejs 安装其实非常简单,大部分情况下 Debian/Ubuntu 下只要使用包管理直接安装

sudo apt-get install nodejs
sudo apt-get install npm

即可。

脚本安装

可是今天网络环境太差,不是 npm package not found 就是 update 半天不动。

官网 提供的安装方式

curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs

也是网络环境无法安装。

更加详细的可以参考官网

二进制安装

所以使用 二进制 安装

在官网 https://nodejs.org/en/download/ 找到编译好的二进制文件

然后解压到 /usr/local 目录下

然后配置 vim ~/.zshrc

export NODE_HOME=/usr/local/node-v6.11.4-linux-x64/
export PATH=$NODE_HOME/bin:$PATH

使用命令检查

node -v
npm -v

输出即可。

reference


2017-10-18 linux , nodejs , npm , js , javascript

使用 mutt 在 Bash 中发送邮件及附件

在编写定时备份脚本时遇到一个需求,就是在 Bash 脚本中发送带附件的邮件。于是找到了 mutt。

Mutt 是一个命令行的邮件客户端,Mutt 能够轻松地在命令行发送和读取邮件。 Mutt 支持 POP 和 IMAP 协议。 尽管 Mutt 是基于命令的,但也同样有一个友好的界面。

如果不使用界面,使用 Mutt 命令发邮件也非常方便,只需要一条命令即可发送或者批量发送邮件。

功能说明

E-mail 管理程序。

语法

mutt [-hnpRvxz][-a 文件][-b 地址][-c 地址][-f 邮件文件][-F 配置文件][-H 邮件草稿][-i 文件][-m 类型][-s 主题] 邮件地址

补充说明:mutt 是一个文字模式的邮件管理程序,提供了全屏幕的操作界面。

参数:

  • -a 文件 在邮件中加上附加文件。
  • -b 地址 指定密件副本的收信人地址。
  • -c 地址 指定副本的收信人地址。
  • -f 邮件文件 指定要载入的邮件文件。
  • -F 配置文件 指定 mutt 程序的设置文件,而不读取预设的.muttrc 文件。
  • -h 显示帮助。
  • -H 邮件草稿 将指定的邮件草稿送出。
  • -i 文件 将指定文件插入邮件内文中。
  • -m 类型 指定预设的邮件信箱类型。
  • -n 不要去读取程序培植文件 (/etc/Muttrc)。
  • -p 在 mutt 中编辑完邮件后,而不想将邮件立即送出,可将该邮件暂缓寄出。
  • -R 以只读的方式开启邮件文件。
  • -s 主题 指定邮件的主题。
  • -v 显示 mutt 的版本信息以及当初编译此文件时所给予的参数。
  • -x 模拟 mailx 的编辑方式。
  • -z 与 -f 参数一并使用时,若邮件文件中没有邮件即不启动 mutt。

安装方法

Debian/Ubuntu/Linux Mint 安装

sudo apt-get install -y mutt

使用方法

发送一封简单的邮件(可能会被主流邮箱认为垃圾邮件,垃圾箱查看一下)

echo "Email body" | mutt -s "Email Title" root@einverne.info

进入命令行交互界面之后使用如下快捷键操作

  • 使用 t 改变接受者邮件地址
  • 使用 c 改变 Cc 地址
  • 使用 a 来增加附件
  • 使用 q 退出
  • 使用 y 来发送邮件

发送带附件的邮件

echo "body" | mutt -s "mysql backup" root@einverne.info -a /mysql.tar.gz

读取文本中的信息作为内容

mutt -s "Test" xxxx@qq.com

添加多个附件

echo "body" | mutt -s "web backup" root@einverne.info -a /mysql.tar.gz -a /web.tar.gz

抄送和密送

echo "body" | mutt -s "title" -c cc@gmail.com -b bcc@gmail.com root@einverne.info
  • 使用 -c 来抄送
  • 使用 -b 来添加密送
  • 使用 -s 来添加标题
  • 使用 -a 来添加附件

设置发件人

编辑配置文件

vi /etc/Muttrc

添加如下内容,防止被作为垃圾邮件

set from="mutt@einverne.info"
set use_from=yes
set envelope_from="yes"
set realname="Ein Verne"

mutt@einverne.info 为发信地址

mutt 发送邮件略慢,需要等待一分钟或者更长才能发送成功,作为备份工具好好利用。

reference


2017-10-17 linux , email , mutt

Podcast 频道推荐

Podcast 作为一种特殊的借助声音传播的媒体,有着特定的适用场景和范围。对于我来说每当我需要放空我自己,而又需要使用眼睛和手的时候,比如长时间走路,开车,再比如乘坐地铁等等情况下都会不自觉的打开一集播客。对于固定时间的上下班时间会听固定时长差不多在 1h 左右的内容,而其他时刻根据不同的时长可能会有短到 10 分钟,或者长 30 分钟的内容。

拿播客跟传统电台广播相比,播客的制作可能会更加精良,主题更加集中,信息密度更大一些,大部分往往是针对某一个话题进行介绍或者讨论。

推荐使用通用性客户端订阅收听,比如 Google Podcasts,或者 Pocket Casts 等等,后文有整理。

每期必听

反派影评

反派影评:个人看电影比较多,从偶然听到一期“观影风向标”开始就一直追随者波米到了“反派影评”,这些年陆陆续续也听过不少影评类节目,但只有反派让我一直坚持到现在仍然每期必听。如果你是一个观影爱好者,那就一定不能错过这一档节目。反派影评在 Podcast 上只更新长节目,短节目还是要上微信公众号的。

  • 主题内容:每期主题一部电影,根据这部电影展开,从导演到演员等等
  • 时长:1h 以上
  • 更新周期:每周,但时间不固定

Self-Hosted

Self-Hosted 是一个关于如何自建各种服务,将自己的数字生活以及产生的数据自我管理的播客。

东腔西调

[[东腔西调]] 是一档由大观天下制作的文化对谈类播客。主播邀请来自不同领域的嘉宾学者聊聊他们关心的世界和生活,探寻社会文化背后的来龙去脉。

Talk Python to me (by Michael Kennedy)

这是一个和 Python 相关的播客,但是又不止于 Python,还包括 Data Science,AI,Web development 等等话题。

  • 官网:https://talkpython.fm/
  • 主题内容:Python 相关
  • 时长: 1h 左右
  • 更新周期:大致为一周

This American Life

This American Life is an American weekly hour-long radio program produced in collaboration with Chicago Public Media and hosted by Ira Glass. It is broadcast on numerous public radio stations in the United States and internationally, and is also available as a free weekly podcast.

  • 主题内容:This American Life
  • 时长: over 1 hour
  • 更新周期:大致为一周

  • 688 期,首个获得普利策奖的音频节目

Talk to me in Korean

学习基础韩国语的节目

  • 主题内容:韩语基础会话
  • 时长: less than 10min
  • 更新周期:大致为一周

牛津人文通识解读

从传统意义上这应该算是讲座了,因为《[[通往奴役之路]]》而在 YouTube 上得知这一个主题的演讲,所以下载之后转成音频在路上听了。演讲者是西北政法大学的副教授谌洪果,因为一些缘故辞职了。所以我们才能在 YouTube 上看到这一个系列的讲座,主讲的材料是基于《牛津人文通识》这样一套书籍,这一系列的书大多都是哲学类,或者哲学家相关的书籍,当然也有比如现代日本,美国大法官之类。这一个系列的讲座每一期围绕一本书进行一段分享,YouTube 上的内容不全,不过也陆陆续续听了 24 期,可以说每一期都非常值得听,现在依然记忆深刻的有,哈耶克的,美国最高法院,现代日本等等话题。

  • 主题内容:围绕牛津人文通识读本每期分享一期
  • 时长:1h-2h
  • 更新周期:不固定

Acquired

Acquired 是一个关注科技圈成功的收购和上市的节目,两位主持人 David 和 Ben 在投资界都有一定的经验 1, 能够听出来每一期节目都有非常充足的材料。有的时候他们也会分析一些国内的成功投资经验,比如 Tencent,Xiaomi,Alibaba 等等,也有一些非常热门的话题比如 Tesla,GitHub etc。

  • 官网:https://www.acquired.fm/
  • 主题内容:Exploring, Analyzing, and Grading Technology Acquisitions and IPOs
  • 时长:2h 左右
  • 更新周期:Season 季播,更新时周更

内核恐慌

听“两个中老年人程序员” 聊天。就是更新时间非常不固定。

  • 主题内容:程序,Mac,设计,字体,软件包等等
  • 时长:100 分钟以上
  • 更新周期:节目更新时间不固定

疯投圈

内核恐慌主持人 Rio 的另一档节目,年初刚开始听,果然质量还是不错的。

  • 主题内容:投资,科技,消费,各行各业
  • 时长:60 分钟以上
  • 更新周期:节目更新时间不固定,一般月更

decoder

这是一档由 The Verge 推出的播客节目,听的第一期是因为想要了解更多关于 Airbnb 的内容,于是搜到了创始人 Brain Chesky 的访谈,发现这档节目不错

  • 主题内容:访谈,创业
  • 时长:60 分钟以上
  • 更新周期:周更

故事 FM

听不同人讲自己的故事。

  • 主题内容:不定
  • 时长:小于 30 min
  • 更新周期:每 3 天左右

日本自由行

青岛刘伟品质旅游主播的一档节目,主播是一位从事多年日本旅游工作的导游,从日本文化,历史,饮食,等等方面对日本做出一些介绍。当时就是随心所听,节目不长,声音录制设备也不是很好,但是内容短小精湛非常不错,在主播的推荐下陆陆续续接触了日本文化,甚至看了《日本论》《静观日本》等等书籍。

主播本人是一位职业导游,从 90 十年代开始从事中日导游,翻译工作,给国人定制深度旅游,也接待日方游客。节目内容重在文化,历史,从历史文化中了解日本人的礼仪,美学等等方面。

  • 主题内容:日本文化,旅游,自由行
  • 时长:30 分钟左右
  • 更新周期:前期节目不固定,后面固定为每周三更新

非每期必听但值得一提

星箭广播

当时去了解 Medium 的创始人的时候搜索到的播客,后面陆陆续续还听了关于 Google Map 创始人的专题,是一档非常不错的、有一定深度的播客。后面就挑着感兴趣的继续听了。

牛油果烤面包

硅谷从业人员聊科技相关的话题,在一些不熟悉的领域上给了我开阔眼界的机会。

东亚观察局

一档中日韩三国记者的线上聊天节目,围绕着三国不同的政治、文化展开。

忽左忽右

主题不固定,主要在于媒体,投资,互联网等等

  • 主题内容:不固定
  • 时长:30 分钟到 1 小时不定
  • 更新周期:不固定

静说日本 徐静波

或许直接去看讲述日本的书,要比直接听这个要好很多。个人不是非常喜欢这个频道。

  • 主题内容:日本
  • 时长:20 分钟以下
  • 更新周期:大概每周

百靈果 NEWS

这是一档台湾人做的双语国际新闻平台,当然听到后期就会发现主播们打着双语的旗号,其实更多的是在“聊天”,或者用他们自己的话说,就是“讲干话”。当然继续收听他们的节目主要是主播二人非常有趣。

国际新闻,双语,读书,闲聊,台湾

  • 主题内容:不固定
  • 时长:30 分钟到 1 小时不定
  • 更新周期:不固定

陌声人

每一期会分享一本图书

一席

朋友推荐,听了多抓鱼创始人的一集,确实不错,可以选择自己喜欢的话题和主题来听。通常情况下一集大概半个小时左右,会邀请以为专业人士就某一个话题进行演讲,看成是国内复刻版的 TED 吧。

  • 主题内容:
  • 时长:
  • 更新周期:

Breaking into Board Games

看名字就知道是和桌游相关的内容了

  • 主题内容:
  • 时长:
  • 更新周期:

翻转电台

一档和哲学,政治,经济,平民文化相关的电台

  • 主题内容:哲学、政治、经济、平民文化
  • 时长:30min~1h 不等
  • 更新周期:每周

InSession Film Podcast

一档英语影评节目

  • 主题内容:电影,演员,导演
  • 时长:1h 以上不等
  • 更新周期:Every 2 or 3 days.

政见合集

时政

选 美

美国政治

欧罗万象

NPR Up first

综合新闻

The Daily

纽约时报的 The Daily 专题报道

The Dropout

ABC The Dropout 听深度报道

欧洲政局变化

Planet Money

这档节目来自 NPR,绝对的明星节目。每期不到 30 分钟,把抽象的经济理论和政治决议与日常生活的种种现象结合,或者通过一个普通的生活事例分析出里面蕴含的经济学道理。听这档节目会上瘾。

  • 502 集,棉花

Freakonomics Radio

Freakonomics Radio 是一个经济话题相关的播客,里面总是会提到很多经济概念。

ChooseFI

ChooseFI [[FIRE]] 运动

Mad Fintest

Mad Fintest

All in Podcast

All In Podcast 四位投资人。

曾经听过但个人不是很推荐

每个人都个人的喜好,网上很多 list 也不都是我最喜欢的博客,我觉得能够找到方法找到自己喜欢的某一类播客就是很好的,授人以鱼不如授人以渔,就是这样,但是这些我听过,并不是非常喜欢的播客我也列出来,喜不喜欢可以自行判断。

  • 代码时间
  • 电影不无聊
  • 闲侃日本
  • 新闻酸菜馆

Podcast 客户端推荐

早之前在 Android 的时候买过 Pocket Cast 可是竟然发现这家每个平台都要收费,不管是 Web ,还是 Android/iOS 客户端,每一个都是单独收费,尽管能够同步订阅列表,但后来转用 iOS 也就放弃了这个客户端,我用过的几个还不错的

  • Podcasts 苹果自身的客户端,虽然设计和使用都不是那么美观和人性化,但只要订阅了就会默默在 wifi 下帮你下载好,不用每一次上路了才想起来
  • Castbox,一款新出的客户端,客户端全免费,甚至网页版还提供博客平台的托管,虽然国内使用速度略慢,但是是我用过的里面体验比较好的了

至于其他人推荐的 Castro, Overcast 如果不缺钱的话买就行了。个人觉得重要的是内容,还有同步的功能,我希望我订阅的内容跟着账号走,而不是跟着设备走。其他国内的 app 比如 喜马拉雅,蜻蜓,荔枝 FM, 听伴 就凭着个人喜好选择即可。

至于为什么不推荐国内的这些 FM,是因为本来 Podcast 是一个公开分享的渠道,发布到 Podcast 上的内容可以被全世界的人访问到,而国内的 FM 都是闭门造车,封闭自己的用户,限制用户只能在他们的平台上传,听众也只有他们平台的账号才能收听,所有的内容就像是自家花园,外人不得入内。而放到 iTunes 的 Podcast 就像是放到了博物馆,全世界的人只要觉得有用就都能够收听到。而且封闭的环境导致审查异常严格,甚至聊电影都能碰到审查,同样也就导致了低智化内容泛滥,内容越没有思考越能得到平台的推荐,自此循环往复,推荐榜单就几乎没有能听的内容了。而在 Podcast 中反而一些很小众的 Channel 内容质量异常高,在某一个专业领域分享的知识,内容足够填满整个频道。

Podcast Hosting

其他站点

一个关于博客统计信息的站点

  • https://chartable.com/
  • https://github.com/guipdutra/awesome-geek-podcasts#in-chinese
  • https://wanqu.co/b/57/2017-03-03-podcasts.html

reference


2017-10-17 podcast , audio , castbox , pocket-cast , google-podcasts

在 Spring Boot 中使用 Swagger 生成接口文档

在使用 Spring Boot 构建一套 RESTful 接口的时候经常需要手工维护一份接口文档以提供给不同的客户端使用,有的时候手工维护成本太高,今天发现了一套自动化生成 RESTful 接口文档的工具 Swagger 。

Swagger 能根据 Spring Controller 接口自动生成一个文档页面,在代码中使用注解将接口文档注释,非常方便。 Swagger 整合到 Spring boot 项目中也非常方便。

添加依赖

io.springfox >= 3.0

<dependency>
 <groupId>io.springfox</groupId>
 <artifactId>springfox-boot-starter</artifactId>
 <version>3.0.0</version>
</dependency>

访问地址是:http://localhost:8080/swagger-ui/#/

pom.xml 中添加

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.7.0</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.7.0</version>
</dependency>

最新的版本可以在 mvnrepository 上查到,或者上官网或者 github。

添加配置类

在项目 package 根下新建如下 Class:

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket helloApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("info.einverne.springboot.demo"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                // 文档标题
                .title("API 文档")
                // 文档描述
                .description("https://github.com/einverne/thrift-swift-demo/tree/master/spring-boot-demo")
                .termsOfServiceUrl("https://github.com/einverne/thrift-swift-demo/tree/master/spring-boot-demo")
                .version("v1")
                .build();
    }
}

通过@Configuration注解,让 Spring 来加载该类配置。再通过@EnableSwagger2注解来启用 Swagger2。

  • apiInfo() 用来创建 API 的基本信息,展现在文档页面中。
  • select() 函数返回一个 ApiSelectorBuilder 实例用来控制哪些接口暴露给 Swagger ,这里使用定义扫描包路径来定义, Swagger 会扫描包下所有 Controller 的定义并产生文档内容,除了被 @ApiIgnore 注解的接口。

添加接口注释

@ApiOperation 注解来给 API 增加说明、通过 @ApiImplicitParams@ApiImplicitParam 注解来给参数增加说明。

一个简单的注释

@ApiOperation(value = "创建用户", notes = "根据 User 对象创建用户")
@ApiImplicitParam(name = "user", value = "用户详细实体 user", required = true, dataType = "User")
@RequestMapping(value = "", method = RequestMethod.POST)
public String postUser(@RequestBody User user) {
    users.put(user.getId(), user);
    return "success";
}

详细的例子可以参考源代码 https://github.com/einverne/thrift-swift-demo

再添加注释后启动 Spring boot, 访问 http://localhost:8080/swagger-ui.html 即可看到 API 文档

Api 注解

@Api 注解用于类上,说明类作用

  • value url
  • description
  • tags 设置该值,value 会被覆盖
  • basePath 基本路径不可配置
  • position
  • produces “application/json”
  • consumes “application/json”
  • protocols http, https, wss
  • authorizations 认证
  • hidden 是否在文档隐藏

ApiOperation 注解

标记在方法上,对一个操作或 HTTP 方法进行描述。具有相同路径的不同操作会被归组为同一个操作对象。不同的 HTTP 请求方法及路径组合构成一个唯一操作。此注解的属性有:

  • value 对操作的简单说明,长度为 120 个字母,60 个汉字。
  • notes 对操作的详细说明。
  • httpMethod HTTP 请求的动作名,可选值有:”GET”, “HEAD”, “POST”, “PUT”, “DELETE”, “OPTIONS” and “PATCH”。
  • code 默认为 200,有效值必须符合标准的 HTTP Status Code Definitions

ApiImplicitParams

注解 ApiImplicitParam 的容器类,以数组方式存储。

ApiImplicitParam

对 API 的单一参数进行注解。虽然注解 @ApiParam 同 JAX-RS 参数相绑定,但这个 @ApiImplicitParam 注解可以以统一的方式定义参数列表,也是在 Servelet 及非 JAX-RS 环境下,唯一的方式参数定义方式。注意这个注解 @ApiImplicitParam 必须被包含在注解 @ApiImplicitParams 之内。可以设置以下重要参数属性:

  • name 参数名称
  • value 参数的简短描述
  • required 是否为必传参数
  • dataType 参数类型,可以为类名,也可以为基本类型(String,int、boolean 等)
  • paramType 参数的传入(请求)类型,可选的值有 path, query, body, header or form。

@RequestBody 这样的场景请求参数无法使用 @ApiImplicitParam 注解进行描述

ApiParam

增加对参数的元信息说明。这个注解只能被使用在 JAX-RS 1.x/2.x 的综合环境下。其主要的属性有:

  • required 是否为必传参数
  • value 参数简短说明

ApiModel

提供对 Swagger model 额外信息的描述。在标注 @ApiOperation 注解的操作内,所有的类将自动被内省(introspected),但利用这个注解可以做一些更加详细的 model 结构说明。主要属性有:

  • value model 的别名,默认为类名
  • description model 的详细描述

ApiModelProperty

对 model 属性的注解,主要的属性值有:

  • value 属性简短描述
  • example 属性的示例值
  • required 是否为必须值
  • hidden 隐藏该属性

ApiResponse

响应配置

  • code http 状态码
  • message 描述

ApiResponses

多个 Response

验证机制

考虑到安全的问题,每次请求 API 需要对用户进行验证与授权。目前主流的验证方式采用请求头部(request header)传递 token,即用户登录之后获取一个 token,然后每次都使用这个 token 去请求 API。如果想利用 swagger-UI 进行 API 测试,必须显式为每个需要验证的 API 指定 token 参数。这时可以为每个操作添加一个注解 @ApiImplicitParams,具体代码如下:

@ApiImplicitParams({@ApiImplicitParam(name = "TOKEN", value = "Authorization token", required = true, dataType = "string", paramType = "header")})

根据环境选择开启 Swagger

Swagger 提供了 enable 方法,可以通过设置该方法来选择开启 Swagger 来在线上环境禁用 Swagger。

@Bean
public Docket customImplementation(){
    return new Docket(SWAGGER_2)
        .apiInfo(apiInfo())
        .enable(environmentSpeficicBooleanFlag) //<--- Flag to enable or disable possibly loaded using a property file
        .includePatterns(".*pet.*");
}

如果使用 Spring @Profile 也可以

@Bean
@Profile("production")
public Docket customImplementation(){
    return new Docket(SWAGGER_2)
        .apiInfo(apiInfo())
        .enable(false) //<--- Flag set to false in the production profile
        .includePatterns(".*pet.*");
}

From: https://stackoverflow.com/a/27976261/1820217

reference


2017-10-16 spring , spring-boot , swagger , java , restful-api , api-doc

每天学习一个命令: Linux 查看磁盘信息命令 di

平时在 Linux 上查看磁盘信息都使用 df -lh , -l 显示本地文件系统, -h 来表示 human readable 。虽然 df 在一定程度上能够满足查询磁盘剩余空间的需求,但是这里要介绍一款强大于 df 的命令 —- di 。

使用如下命令安装

sudo apt install di

di 命令是 disk information 的缩写,直接运行 di 会有如下结果

di
Filesystem         Mount               Size     Used    Avail %Used  fs Type
/dev/sda1          /                 901.2G   188.3G   667.1G   26%  ext4   
tmpfs              /dev/shm            7.8G     0.1G     7.6G    2%  tmpfs  
tmpfs              /run                1.6G     0.1G     1.5G    4%  tmpfs  
cgmfs              /run/cgmanager/     0.1M     0.0M     0.1M    0%  tmpfs  
tmpfs              /run/lock           5.0M     0.0M     5.0M    0%  tmpfs  
tmpfs              /run/user/0         1.6G     0.0G     1.6G    0%  tmpfs  
tmpfs              /run/user/1000      1.6G     0.0G     1.6G    0%  tmpfs  
tmpfs              /sys/fs/cgroup      7.8G     0.0G     7.8G    0%  tmpfs  
/dev/sda1          /var/lib/docker   901.2G   188.3G   667.1G   26%  ext4   

di 默认的输出就是比较人性化的了。

看 di 的使用介绍 man di 就会发现 di 是这么介绍自己的

> di Displays usage information on mounted filesystems.  Block values are reported in a human readable format.  If the user or group has  a  disk  quota,  the  values  reported  are adjusted according the quotas that apply to the user.

一些简单的使用

A 选项打印所有挂载设备

di -A
Mount                fs Type Filesystem 
	Options                                                           
	    Size     Used     Free %Used  %Free 
	    Size     Used    Avail %Used  %Free 
	    Size     Used    Avail %Used  
	   Inodes     IUsed     IFree %IUsed
/                    ext4    /dev/sda1  
	rw,relatime,errors=remount-ro,data=ordered                        
	  901.2G   188.3G   712.9G   21%    79%  
	  901.2G   234.1G   667.1G   26%    74%  
	  855.4G   188.3G   667.1G   22%  
	 60014592   1372538  58642054    2% 
/dev/shm             tmpfs   tmpfs      
	rw,nosuid,nodev                                                   
	    7.8G     0.1G     7.6G    2%    98%  
	    7.8G     0.1G     7.6G    2%    98%  
	    7.8G     0.1G     7.6G    2%  
	  2036725       741   2035984    0% 
/run                 tmpfs   tmpfs      
	rw,nosuid,noexec,relatime,size=1629380k,mode=755                  
	    1.6G     0.1G     1.5G    4%    96%  
	    1.6G     0.1G     1.5G    4%    96%  
	    1.6G     0.1G     1.5G    4%  
	  2036725       777   2035948    0% 
/run/cgmanager/fs    tmpfs   cgmfs      
	rw,relatime,size=100k,mode=755                                    
	    0.1M     0.0M     0.1M    0%   100%  
	    0.1M     0.0M     0.1M    0%   100%  
	    0.1M     0.0M     0.1M    0%  
	  2036725        14   2036711    0% 
/run/lock            tmpfs   tmpfs      
	rw,nosuid,nodev,noexec,relatime,size=5120k                        
	    5.0M     0.0M     5.0M    0%   100%  
	    5.0M     0.0M     5.0M    0%   100%  
	    5.0M     0.0M     5.0M    0%  
	  2036725         4   2036721    0% 
/run/user/0          tmpfs   tmpfs      
	rw,nosuid,nodev,relatime,size=1629380k,mode=700                   
	    1.6G     0.0G     1.6G    0%   100%  
	    1.6G     0.0G     1.6G    0%   100%  
	    1.6G     0.0G     1.6G    0%  
	  2036725         4   2036721    0% 
/run/user/1000       tmpfs   tmpfs      
	rw,nosuid,nodev,relatime,size=1629380k,mode=700,uid=1000,gid=1000 
	    1.6G     0.0G     1.6G    0%   100%  
	    1.6G     0.0G     1.6G    0%   100%  
	    1.6G     0.0G     1.6G    0%  
	  2036725        36   2036689    0% 
/sys/fs/cgroup       tmpfs   tmpfs      
	rw,mode=755                                                       
	    7.8G     0.0G     7.8G    0%   100%  
	    7.8G     0.0G     7.8G    0%   100%  
	    7.8G     0.0G     7.8G    0%  
	  2036725        18   2036707    0% 
/var/lib/docker/aufs ext4    /dev/sda1  
	rw,relatime,errors=remount-ro,data=ordered                        
	  901.2G   188.3G   712.9G   21%    79%  
	  901.2G   234.1G   667.1G   26%    74%  
	  855.4G   188.3G   667.1G   22%  
	 60014592   1372538  58642054    2% 

c 选项逗号分割

使用 -c 选项分割输出

di -c
s,m,b,u,v,p,T
"/dev/sda1","/","901.2G","188.3G","667.1G",26%,"ext4"
"tmpfs","/dev/shm","7.8G","0.1G","7.6G",2%,"tmpfs"
"tmpfs","/run","1.6G","0.1G","1.5G",4%,"tmpfs"
"cgmfs","/run/cgmanager/fs","0.1M","0.0M","0.1M",0%,"tmpfs"
"tmpfs","/run/lock","5.0M","0.0M","5.0M",0%,"tmpfs"
"tmpfs","/run/user/0","1.6G","0.0G","1.6G",0%,"tmpfs"
"tmpfs","/run/user/1000","1.6G","0.0G","1.6G",0%,"tmpfs"
"tmpfs","/sys/fs/cgroup","7.8G","0.0G","7.8G",0%,"tmpfs"
"/dev/sda1","/var/lib/docker/aufs","901.2G","188.3G","667.1G",26%,"ext4"

c 是 --csv-output 的缩写,为了便于程序解析

t 参数增加统计行

-t 参数在最后增加统计行

di -t
Filesystem         Mount               Size     Used    Avail %Used  fs Type
/dev/sda1          /                 901.2G   188.4G   667.0G   26%  ext4   
tmpfs              /dev/shm            7.8G     0.1G     7.6G    2%  tmpfs  
tmpfs              /run                1.6G     0.1G     1.5G    4%  tmpfs  
cgmfs              /run/cgmanager/     0.1M     0.0M     0.1M    0%  tmpfs  
tmpfs              /run/lock           5.0M     0.0M     5.0M    0%  tmpfs  
tmpfs              /run/user/0         1.6G     0.0G     1.6G    0%  tmpfs  
tmpfs              /run/user/1000      1.6G     0.0G     1.6G    0%  tmpfs  
tmpfs              /sys/fs/cgroup      7.8G     0.0G     7.8G    0%  tmpfs  
/dev/sda1          /var/lib/docker   901.2G   188.4G   667.0G   26%  ext4   
                   Total               1.8T     0.4T     1.3T   26%      

s 参数对结果排序

di -s 默认更具 mount point 输出

  • di -sm 默认 mount pont
  • di -sn 不排序,按照挂载表 /etc/fstab 中顺序
  • di -ss 按照特殊设备
  • di -st 根据 filesystem type
  • di -sr 逆序输出

排序方式可以组合使用,如:di –stsrm :按照类型、设备、挂载点逆序排序。 di –strsrm :按照类型、设备逆序、挂载点逆序排序。

f 选项自定义格式

di -fM
Mount               
/                   
/dev/shm            
/run                
/run/cgmanager/fs   
/run/lock           
/run/user/0         
/run/user/1000      
/sys/fs/cgroup      
/var/lib/docker/aufs

只打印 mount point

更多的 f 的选项可以直接参看 man di

总结

虽然 di 提供了比 df 更多更强大的功能,但是也有其缺点,大部分的 Linux 发行版默认是没有预装的。


2017-10-16 linux , 磁盘管理 , disk , df

SonarQube continuous code quality

关键字提取,开源,代码质量管理,多语言支持。

SonarQube is an open-source platform developed by SonarSource for continuous inspection of code quality to perform automatic reviews with static analysis of code to detect bugs, code smells, and security vulnerabilities on 20+ programming languages.

开源协议:Lesser GNU General Public License

SonarQube 可以从以下七个维度检测代码质量,而作为开发人员至少需要处理前 5 种代码质量问题

  • 不遵循代码标准 SonarQube 可以通过 PMD,CheckStyle,Findbugs 等等代码规则检测工具规范代码编写。
  • 潜在的缺陷 SonarQube 可以通过 PMD,CheckStyle,Findbugs 等等代码规则检测工具检测出潜在的缺陷。
  • 糟糕的复杂度分布 文件、类、方法等,如果复杂度过高将难以改变,这会使得开发人员难以理解它们,且如果没有自动化的单元测试,对于程序中的任何组件的改变都将可能导致需要全面的回归测试。
  • 重复 显然程序中包含大量复制粘贴的代码是质量低下的,SonarQube 可以展示源码中重复严重的地方。
  • 注释不足或者过多 没有注释将使代码可读性变差,特别是当不可避免地出现人员变动时,程序的可读性将大幅下降 而过多的注释又会使得开发人员将精力过多地花费在阅读注释上,亦违背初衷。
  • 缺乏单元测试 SonarQube 可以很方便地统计并展示单元测试覆盖率。
  • 糟糕的设计 通过 SonarQube 可以找出循环,展示包与包、类与类之间的相互依赖关系,可以检测自定义的架构规则 通过 SonarQube 可以管理第三方的 jar 包,可以利用 LCOM4 检测单个任务规则的应用情况, 检测耦合。

reference


2017-10-11

jenkins setup and introduction

Jenkins 是什么

Jenkins 是一个独立的开源自动化服务器,可用于自动化各种任务,如构建,测试和部署软件。Jenkins 可以通过本机系统包 Docker 安装,甚至可以通过安装 Java Runtime Environment 的任何机器独立运行。

Jenkins 有什么作用

用于持续、自动构建,测试项目,监控外部任务运行等。

模板类型

新建模板类型

  • Freestyle project 基础功能,执行构建任务
  • Pipeline,真实工作环境可能会包含,代码检查,编译,单元测试,部署等等任务,这个模板就是串行执行任务
  • Multi-configuration project job 跑在不同的机器上

问题

master/slave 节点配置

  • https://www.howtoforge.com/tutorial/ubuntu-jenkins-master-slave/

2017-10-10 jenkins , ci-cd , ci

mockito 使用

单元测试的目的是在不涉及依赖的情况下测试代码(隔离)。一个设计良好的系统需要遵循 SOLID 原则。

  • (S) Single responsibility principle 单一职责
  • (O) Open/closed principle 开闭原则,对修改关闭,对扩展开放
  • (L) Liskov substitution principle 里氏替换原则,子类和父类表现一致
  • (I) Interface segregation principle 接口隔离原则,不要依赖不要使用的方法,或者在设计时尽量将大接口拆开
  • (D) Dependency inversion 依赖反转,将类和其他类隔离开,尽量依赖抽象,而不依赖具体实现,比如当修改了外部外部依赖具体实现,而不需要大规模的修改原始代码

Target and challenge of unit testing

Unit test 目标是针对一个模块或者一段代码隔离测试,应该消除其他类,或者系统依赖带来的副作用。

测试替身(Test Doubles)用来消除这类副作用,test Doubles 可以分为:

  • dummy object 传入不使用
  • fake object 有基本实现,但是非常简单,比如内存数据库
  • stub class 带着特殊目的的接口或者类的部分实现
  • mock object 接口或者类的虚假实现,可以用来定义固定的输出,mock object 在测试中用来执行特定的行为

通常情况下可以通过手工代码来 mock objects 或者使用 mock framework 来模拟类的行为。Mockito 是一个非常流行的 Mock framework,使用 Mockito 的三段论:

  • Mock 外部依赖,插入 mock 代码
  • 执行 unit test
  • 验证输出

Maven

http://search.maven.org 中搜索 a:"mockito-core" 或者 g:"org.mockito"

比如说增加:

<dependency>
	<groupId>org.mockito</groupId>
	<artifactId>mockito-all</artifactId>
	<version>1.9.5</version>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<version>4.11</version>
	<scope>test</scope>
</dependency>

mockito-all

mockito-all 是一个包含了所有依赖 (hamcrest 和 objenesis) 的单一 jar 包。

mockito-core

mockito-core 不包含 hamcrest 和 objenesis 依赖,可以自己来控制依赖的版本。

启用注解

第一种方法,使用 JUnit 的 @RunWith

@RunWith(MockitoJUnitRunner.class)
public class MockitoAnnotationTest {
    ...
}

或者用代码启用

@Before
public void init() {
    MockitoAnnotations.initMocks(this);
}

@Mock 注解

最常用的注解就是 @Mock 注解,使用 @Mock 注解来创建和插入 mocked 实例,这样就省去了手动调用 Mockito.mock() 方法。

@Spy 注解

@Spy 注解可以 mock 真实对象的方法,让真实对象方法被调用时,就像 mock object 一样可以被配置。

@Captor 注解

参数捕获器,用于捕获 mock 方法参数,进行验证使用

@InjectMocks 注解

@InjectMocks 注解会自动将 mock fields 注入到被测试的 object 中。

需要注意的是,在 JUnit 4 中必须使用 @RunWith(MockitoJUnitRunner.class)MockitoAnnotations.initMocks(this) 来初始化 mocks 并注入。

在 JUnit 5 中必须使用 @ExtendWith(MockitoExtension.class)

@RunWith(MockitoJUnitRunner.class) // JUnit 4
// @ExtendWith(MockitoExtension.class) for JUnit 5
public class SomeManagerTest {

    @InjectMocks
    private SomeManager someManager;

    @Mock
    private SomeDependency someDependency; // this will be injected into someManager
 
     // tests...

}

when thenReturn

通过 when().thenReturn() 方法链可以用来指定一个方法调用预先定义好的返回值。这个方法也可以指定抛出一个异常

Properties properties = mock(Properties.class);

when(properties.get(”Anddroid”)).thenThrow(new IllegalArgumentException(...));

try {
    properties.get(”Anddroid”);
    fail(”Anddroid is misspelled”);
} catch (IllegalArgumentException ex) {
    // good!
}

thenReturn vs thenAnswer 区别

当 mock 方法是知道确定的返回值,那么可以使用 thenReturn 或者 doReturn,方法会 mock 一个确定的返回值。

thenReturn(T value) Sets a return value to be returned when the method is called.

Answer 当需要根据不同条件来 mock 方法并且返回不同返回值时需要 Answer,比如需要根据方法传入参数来返回对应的返回值的情况。

Mock 静态方法

Mockito 暂时还不支持 Mock 静态方法,所以需要借助 jmockit 完成静态方法的 Mock:

<dependency>
  <groupId>org.jmockit</groupId>
  <artifactId>jmockit</artifactId>
  <version>1.30</version>
  <scope>test</scope>
</dependency>


new MockUp<StaticClass>() {
  @mockit.Mock
  public boolean isXXX() {
    return true;
  }
};

Other

《Mockito Cookbook》参考代码:https://github.com/marcingrzejszczak/mockito-cookbook

reference


2017-09-27 mockito , unit-test , java , mock

电子书

本站提供服务

最近文章