一个适合程序员的 Markdown 文档编辑和文档管理方案

我是一个重度的 Markdown 用户。长时间使用过 Day OneUlysses、MacDown,如你所料,最终都放弃了。

坦率讲,Day One 和 Ulysses 是你一打开就会觉得非常惊艳的产品,设计上侧重写作者的主观感受和体验,在细节的打磨上非常到位。但是 Day One 的代码质量实在一般,很多闪退 bug 经常把你的写作热情消灭得荡然无存,且没有一个像样的文档管理功能。Ulysses 虽然有文档管理功能,UI 更加惊艳,尤其是沉浸模式非常吸引人,让你觉得在使用艺术品写作,当然,也会让你产生使用它写作能够提高写作质量的错觉? 不过,Ulysses 的文档管理和编辑设定更偏向于普通小白用户。这个本身是没有问题的,因为它的定位是让你享受写作,尽可能少的减少其他方面对用户的打扰。显然,作为一个经常需要从源代码级别去修改文档的用户来说,它的这种好意被我当成了一种功能的羸弱。

在很长一段时间内都没有找到满意的 Markdown 软件。于是,索性用回了只有简单编辑功能的 MacDown,同时也在摸索和完善适合自己使用习惯的 Markdown 文档管理和编辑方案。我理想中的 Makrdown 文档管理方案是这样的:

  1. 源代码级的 Markdown 文档编辑能力;不看重所见即所得的功能,且不能接受只提供所见即所得的编辑能力;
  2. 符合开发人员习惯的文档管理能力,像管理源代码一样管理我的 Markdown 文档,随时查找、编辑。
  3. 如果能够支持文档历史版本管理就更棒了。
  4. 成本不要太高,最好是免费的。

不卖关子,先说当前自己用下来比较舒服的方案:Visual Studio Code + Markdown Shortcuts + markdownlint + Bitbucket, 满足自己以上的所有需求,且成本为 0 .

选择 Visual Studio Code 作为编辑器是因为可以把自己写代码的那套文件查找和管理习惯继承过来,并且不需要重新学习和熟悉快捷键。 VS Code 本身支持 Markdown 文档编辑和预览,遗憾的是它的这两个功能都不强大,达不到自己快糙猛的要求。预览功能自己并不看重,因此选择性安装了两个插件(Markdown Shortcuts + markdownlint)来增强其编辑功能。

Markdown Shortcuts 提供了很多编辑 Makrdown 文档的风骚快捷键。我自己常用的有:

  • 快速转换成列表:

  • excel 表数据转表格:

其他命令如下:



markdownlint 则是一个语法检查 lint 工具。虽然 Markdown 语法很简单,但是因为经常编辑源代码和插入 html 代码,有一个 lint 还是能够辅助你提前发现很多 typos.

得益于 VS Code 强大而丰富的插件生态,Markdown 周边的各种功能都能通过这些插件解决,只有你想不到,没有插件没做到的。比如,如果你想导出文档为 PDF,你可以尝试 Markdown PDF; 如果你是数据公式重度用户,你可以使用 Markdown+Math
.

Bitbucket 则是用 git 来做文档的历史版本管理,免费,且支持私有仓库。如此一来,基本上编辑 Makrdown 与编写项目代码的有了相同的使用体验。

小结

习惯这东西本身就是个性化化的东西,并且一旦适应了就很难改变。以上方案只是一个我自己比较舒服的使用习惯。这个方案并不完美,比如它并没有考虑 Markdown 重度图片用户插图的效率问题。另一方面,Evernote 大陆版发布的新版本已经开始支持 Markdown 文档功能(国际版不支持哟)。如果,你是印象笔记的铁粉,不妨一试。

移除 macOS 中被企业策略强制安装的 Chrome 插件扩展的方法

公司配发的 Macbook 安装了一些监控软件,毕竟是办公设备,这点倒是无可厚非。但是,其中的 Forcepoint DLP Endpoint 会向日常使用的主力浏览器 Chrome 中安装一个 WebsenEndpoint 的扩展插件。

这个插件的作用不必多说,但是有一个副作用就是让 Chrome 无法自动填写任何网站用户名密码。每天要输入上百次的密码这种体力活是绝对不能忍受的。于是尝试卸载这个插件。但是这个插件是使用企业策略 (Installed by enterprise policy) 强制安装,无法在Chrome中下载。但是可以通过将与之关联的 Profile 删除实现卸载的作用:

但是过两天,Forcepoint 又会把这个插件装回来? 不过没关系,我们有的是办法。

方法一:通过占用 Chrome 插件文件位置,防止真正插件重新装回

原理是这样的:Chrome 的插件安装在 ~/Library/Application Support/Google/Chrome/Default/Extensions 目录下:

除去 Temp, 每个文件夹对应一个插件。我们希望移除的插件的文件夹名称是 kmofofmjgmakbbmngpgmehldlaaafnjn。由于 macOS 文件夹下,相同名称的文件和文件夹只能存在一个。因此,我们只需要在插件删除以后,在这个目录下新建一个名称为 kmofofmjgmakbbmngpgmehldlaaafnjn 的空文件:

然后,再通过文件系统的 Lock 功能锁定改文件,防止该文件被 Forcpoint 删除:

至此,大功告成,so easy!

方法二:修改 Forcepoint DLP Endpoint 插件安装脚本,禁止强奸 Chrome

法一虽然可以防止Chrome被重新安装插件,但是过几天以后,你会在你的 Profiles 配置下发现,虽然 Chrome 插件安装失败了,但是依然会成功安装这个 Profile:

虽然不会干扰系统的任何功能,但是估计很多强迫症患者看到这个还是挺难受的……从图中我们可以看到,这个插件的路径在 /Library/Application Support/Websense Endpoint/DLP:

其中 setup_chrome_ext.sh 就是强插 Chrome 的脚本:

脚本写得简单粗暴:通过 $PROFILES -I -F "/Library/Application Support/Websense Endpoint/DLP/WebsenseEndpointExtension.config" 向系统强行安装 profile. 要防止其安装插件,把这行代码删除即可。但是,这个文件是属于 admin 用户组的,我们平时使用的账号,即使集合 sudo 也只是 wheel 用户组,是干不过 admin 的,导致我们无法在正常模式下编辑该文件:

因此,我们需要通过 Command (⌘)-R 重启到恢复模式,然后在 /Volumes/mac.os/Library/Application Support/Websense Endpoint/DLP 目录下,将对应代码删除即可。

至此,自己基本满意了?

后记

稍微浏览了一下 /Library/Application Support/Websense Endpoint 文件夹,发现其实这个软件不仅强插 Chrome, 还会强插 FireFox:

艾玛……proxy…吓死宝宝了,已经不想改脚本了。直接在恢复模式下删除文件,然后用方法一的方法,建了一个 Lock 文件锁定这个位置。什么,还有 upgrade.sh? 你怎么不上天呢,也一并删除了……突然想起,自己在无所不能的恢复模式下,要不整个软件一起删除了?想到隔天IT技术小哥可能过来找麻烦,就此打住吧?

TLS 1.3 当前(2018.10)支持与部署之现状

今年8月10日,历经三年有余,TLS 1.3 最终版本终于得以发布——RFC 8446. 关于 RFC 的详细介绍可以进一步阅读 A Detailed Look at RFC 8446 (a.k.a. TLS 1.3). TLS 1.3 因为其在握手延迟以及安全性上的改进 (可以参考拙文《TLS1.3/QUIC 是怎样做到 0-RTT 的》),毫不夸张的说,这是一件将深刻而长远影响互联网发展的技术里程碑。那么将 TLS 1.3 尽快平滑应用到线上环境无疑是一件势在必行的事情了。

在我日常的工作中,对于 TLS 1.3 的支持和部署主要关注两个层面: 编程语言(Go, Java)、 API gateway (Nginx) 和浏览器. 下面分别介绍一下这三个层面 TLS 1.3 的支持部署现状。(因为 RFC 8446 已经发布,因此本文中说的支持如无特殊说明,都是指对最终版本 RFC 8446 的支持。)

编程语言对 TLS 1.3 的支持

Go 方面,官方在 TLS 1.3 draft 阶段一直没有跟进。因此,有一个关于对 TLS 1.3 支持的 issue 从 2015 年 open 至今都没有关闭:crypto/tls: add support for TLS 1.3. 其实 Go 发布版本和改进标准库的效率还是挺高的,对于 TLS 1.3 上的“不作为”更多是因为 Go 在兼容性上的承诺导致其并适合在最终版发布前实现互不兼容的 draft 方案。

而 Go 1.11 的发布时间(2018.08.24)与 RFC 8446 的发布时间比较接近,没有足够时间实现并发布该特性。从 golang-dev 小组讨论 Re: crypto/tls and TLS 1.3 看,由于 1.11 没有实现 TLS 1.3 ,那么 1.12 实现 TLS 1.3 基本是板上钉钉的事了:

The key scheduling fact is that the Go 1.11 feature freeze is just a week away, so we decided that it would be too rushed to merge the 1.3 patches for it. I definitely aim to have TLS 1.3 in Go 1.12.

根据惯例, Go 1.12 的发布时间将会是 2019.02~03. 如果期间你实在想用 Go 编程测试 TLS 1.3, 可以尝试使用 CloudFlare 的 tls-tris 库。根据 Go net/http 标准库维护者 Brad Fitzpatrick 的消息,这个库将会被合并到标准库作为 Go 官方 TLS 1.3 的实现。因此,如果你不得不用这个库干一些生成环境的活也大可放心,即使日后升级 Go 1.12, 接口兼容性还是有保证的。

Java 方面,由于 Java 11 出生时间好(2018.09.25), 因此是出生就支持 TLS 1.3 RFC 8446, 具体可以参见 JEP 332: Transport Layer Security (TLS) 1.3. Java 11 是 LTS 版本,因此,如果有条件升级到 11, 推荐使用 Java 11 实现的 TLS 1.3 以及配套的 HttpClient;如果生产环境暂无法升级 Java 版本,推荐使用 OkHttp. 关于 Java Http Client 选型可以参见Java HTTP 组件库选型看这篇就够了

Nginx 对 TLS 1.3 的支持

准确讲应该是 Nginx 所使用 SSL lib 对 TLS 1.3 的支持。在这方面,Boring SSL 跟进速度飞快,在 RFC 发布后第4天实现了对最终版本的支持。OpenSSL 虽然很早就跟进了 draft 的实现,但是对最终版本的支持需要 1.1.1-pre9 及以后的版本:

The OpenSSL git master branch (and the 1.1.1-pre9 beta version) contain our development TLSv1.3 code which is based on the final version of RFC8446 and can be used for testing purposes (i.e. it is not for production use). Earlier beta versions of OpenSSL 1.1.1 implemented draft versions of the standard. Those versions contained the macro TLS1_3_VERSION_DRAFT_TXT in the tls1.h header file which identified the specific draft version that was implemented. This macro has been removed from 1.1.1-pre9 and the current master branch.

TLSv1.3 is enabled by default in the latest development versions (there is no need to explicitly enable it). To disable it at compile time you must use the “no-tls1_3” option to “config” or “Configure”.

Although the latest 1.1.1 versions support the final standard version, other applications that support TLSv1.3 may still be using older draft versions. This is a common source of interoperability problems. If two peers supporting different TLSv1.3 draft versions attempt to communicate then they will fall back to TLSv1.2.

而第一个 OpenSSL 1.1.1 release 是在 2018.09.11, 因此如果你跟我一样是 OpenSSL 的死忠粉,当前阶段 Nginx 支持 TSL 1.3 的最佳方式是 Nginx 1.15.5 + OpenSSL 1.1.1. 而这种脏活、苦活、累活当然是交给 Docker 解决了:从源代码编译 nginx docker 镜像开启 TLS 1.3,项目地址可以参见 docker-nginx.

配置 Nginx 支持 TLS 1.3 需要注意一点:默认情况下 Nginx 因为安全原因,没有开启 TLS 1.3 0-RTT,可以通过添加 ssl_early_data on; 指令开启 0-RTT. 完整配置可以参考 nginx.conf.

浏览器对 TLS 1.3 的支持

当前阶段,Chrome 69 和 Firefox 62 都只支持到 draft 28, 而 draft 28 与最终版本是不兼容的。因此,要测试体验 TLS 1.3 final 需要使用 Chrome Beta 测试版。然后在 chrome://flags/#tls13-variant 开启 TLS 1.3 final:

扩展阅读