技术手记

WSL 下一些奇怪的路径依赖问题优化

在公司换用 Windows 做开发机,装了 Windows Subsystem for Linux(WSL),也就是那个 Ubuntu,用来跑开发环境

我的代码放在 Windows 的文件系统里,在 WSL 里通过 ln -s /mnt/c/foo ~/foo 的方式映射过去,不过在跑 yarn 装 node modules 的时候,会经常出现路径依赖的错误,大概就是 /mnt/c/xxxxx 这样的路径在计算父目录或子目录时会出问题

另外我跑 Docker,是使用 Docker for Windows 作为宿主,在 WSL 里装 Linux 的 Docker 客户端做控制,跑 docker-compose 总是发现挂载不上开发目录到文件系统,最后看了下是 WSL 默认的 /mnt/c/ 这样的挂载点识别有问题

最后按某些野路子方法,把 WSL 访问宿主机的入口调整为 /c/ 这样就好了

$ sudo mkdir /c
$ sudo mount --bind /mnt/c /c

不过这有个问题是重启后需要重新挂载,之前有按别的一些处理方式写到 /etc/fstab 文件表里,但是 WSL 不支持自动加载,所以按 https://nickjanetakis.com/blog/setting-up-docker-for-windows-and-wsl-to-work-flawlessly 的提示来加到 ~/.bashrc 里或我的 ~/.zshrc 里,并把 /bin/mount 改成所有用户都可启用

$ echo "sudo mount --bind /mnt/c /c" >> ~/.bashrc && source ~/.bashrc
$ sudo echo "yourname ALL=(root) NOPASSWD: /bin/mount" >> /etc/sudoers

注 1:yarn 的问题似乎现在在 /mnt/c/foo 这样的目录结构下工作正常了,不确定是不是 yarn 升级处理了这个问题

隐藏 HP 打印机在 Win10 下无法安装的驱动更新

公司使用的打印机是 HP 的 1536,很经典的一款,但是也有一个很头疼的问题,就是在 Windows 10 下,会总是提示会有驱动升级,名称是 HP driver update for HP LaserJet M1530 MFP Series PCL 6,但是从来都无法安装成功

看这个事情很不爽,但是一直也没有找到很好的解决办法,HP 官方论坛和微软的官方论坛里都没有合理的解决,有提议把驱动卸了就不会出更新提示的,但是,卸载了驱动我还怎么打印

好在今天搜到一个 Win10 系统禁止某一更新 的方法,实测解决,原文请点左边的链接进去,我这里简单重复

  1. 去微软官网下载 wushowhide.diagcab
  2. 运行,在 Hide Update 里勾选不想要的更新,解决

Windows 10 开机自动登录并锁屏

办公室的电脑有时候遇上断电重启后,希望能在通电后自动重启,且启动相关应用并保持在锁屏界面(比如启动 QQ,方便从办公室外网远程桌面)

断电自动重启这个好办,一般主板 BIOS 有设置

自动登录也能找到大把的教程,常见的操作步骤如下

Win+R 运行窗口里输入 netplwiz 并打开
用户 选项卡下去掉 要使用本计算机,用户必须输入用户名和密码 前面的勾
确定,在弹出的自动登录窗口中输入自动登录的用户名和密码
确定,保存

这样可以实现开机后自动登录有密码的账号,但是还是挺危险的,因为人不在电脑前但电脑是登录状态,万一被干点啥就呵呵了,自动屏保也有个触发时间间隔。所以还要设置登录后立即锁屏

一个方式是在启动任务里添加锁屏任务,进入 计算机管理,在 系统工具 > 系统计划任务 > 任务计划程序库 里,右侧操作面板 创建基本任务,更新如下信息

添加任意自己方便记忆的名称和描述
触发器当前用户登录时
操作启动程序
点下一步后多出来的步骤里启动程序或脚本填 %windir%\system32\rundll32.exe,添加参数里填 user32.dll, LockWorkStation
完成

另一种方式是创建一个快捷方式,快捷方式的对象位置写 %windir%\system32\rundll32.exe user32.dll, LockWorkStation,然后把这个快捷方式复制到开始菜单的 启动 里。原理类似,参数也一样,不过笨狗担心这个存在时序和可能被干掉的可能,还是选的上一种

修改 Windows 10 系统信息里制造商信息

在 Windows 的在线设备管理器里看自己账号登录了哪些机器,有一些品牌机是能正常显示型号或序列号的,但是自己装的机器会显示成 To Be Filled by O.E.M.

查了一下有几个解决办法

一是在 $Windows$/System32 目录下添加 oeminfo.iniOEMLOGO.bmp,参考 https://www.neowin.net/forum/topic/79809-how-to-change-to-be-filled-by-oem/?do=findComment&comment=871161

另一个是直接改注册表,参考 http://www.thewindowsclub.com/add-change-oem-information-windows,在注册表 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\OEMInformation 下新建一个字符串值,名字叫 Manufacturer,值填自己想要的,弄完后在本地计算机属性里就可以看到

目前还不确定是否可以在微软的账号管理里看到这个改变,根据之前经验,改了计算机名什么的都需要过一小段时间才能生效

另如果需要在 dxdiag 里也看到正确的系统制造商和系统型号,参考 https://www.reddit.com/r/techsupport/comments/3f6gyo/upgraded_to_windows_10_and_now_getting_to_be/,在注册表 HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\Bios 目录下检查 SystemManufactureSystemProductName 这两个键是否存在,以及值是否跟 BaseBoardManufacturerBaseBoardProduct 一样

wtforms 的 StringField 里设置 default 无效

在项目里使用 wtforms,设置了一个带默认值的 StringField,但提交表单如果没有这项或值是空的,则返回为空,并不会使用 default 里的值

例如定义如下表单

def ListForm(Form):
    status = StringField("status", validators=[Optional()], default="all")

提交一个空请求,返回的 status 是空而不是 all。去跟代码并看了下 GitHub 上的 issue 讨论,按 wtforms 官方的说法,这是故意设计成这样的,参考如下内容

大意是,如果用户设置了一个 Field,那么他就应该有值,不然我们就强行设置为空

但是这个逻辑狗屁不通,因为除了 StringField 其他的类型域就没这个问题,比如 IntegerField 就是可以这么用而且能返回正确的 default

def ListForm(Form):
    status = StringField("status", validators=[Optional()], default="all")
    x = IntegerField("x", validators=[Optional()], default=9)

没办法自己新增了一个 StringFieldWithDefault 的类来解决这个问题(被覆盖的代码可以看上面 GitHub Issue 里的讨论)

class StringFieldWithDefault(StringField):
    def process_formdata(self, valuelist):
        if valuelist:
            self.data = valuelist[0]
        else:
            self.data = self.object_data

而且 wtforms 的数据校验也是谜一般的逻辑,比如设置如下

def ListForm(Form):
    status = StringField("status", validators=[Optional(), AnyOf(["all", "visible"])], default="all")
    x = IntegerField("x", validators=[DataRequired()])

这里会出现两个问题

一是 Optional 如果发现没有值或空,是能通过这个校验的,这里逻辑也没错,但是特喵的 Optional 如果遇到值为空的时候,会把之前的所有错误都清空,并且停止检查后面的 validator。这个迷一般的逻辑会导致如果传的空字段,即没有按正常人预期的拿到 default,也没有去执行 AnyOf 的校验,而是直接通过了。这个官方无解,也不打算修,只说在 3.x 的时候考虑加配置参数,不过看进度上一个版本已经是 2015 年发布的 2.1,而且 GitHub 仓库并不活跃,那么只能呵呵呵呵,然后换自己的 StringFieldWithDefault 吧

二是 DataRequired 不是检查 if field.name in formdata,而是检查 if field.data,这就意味着,如果传了一个 0 的整型参数给 IntegerField,是通不过 DataRequired 的验证的,同理还有空字符串。这个可以改用 InputRequired 来解决,还算是官方给了条活路

推荐一下 Vultr 及记录最近的折腾

Vultr 是一家 VPS 提供商,类似 Linode 和 DigitOcean,知道他比较晚,是在用了 DigitOcean 后。当时换他的主要考虑是有日本机房,DO 的新加坡机房在国内某些网络下访问很快,另外某些网络下就是龟速,Linode 则是因为贵,10 刀起价,虽然配置也好但是用不上

最近 Vultr 把入门款的 VPS 拉低到了 2.5 刀每月,1 CPU 512M RAM 20G SSD 500G BW 对于个人用户来说怎么看都够了,最大的优势还是有日本机房而且不像 Linode 那样有大概率被分到被墙的 IP。(感觉他家最近把入门款从 768RAM 5$ 改成 512RAM 2.5$ 应该是为了面对 Linode 把起价降低到 5 刀后的竞争)

下面是推荐链接,如果你通过这个注册并使用,我可以获得一定的奖励,你没有任何损失

http://www.vultr.com/?ref=6804130

另外,Vultr 目前还在搞充返活动,最高充一百送一百,送的金额 12 个月内有效。如果只是像我一样普通用,充 25 送 25,一年内开 2.5$ 的入门款实际只花 5$,实际上就是拿 25$ 可以用 20 个月。当然如果确定用的长也可以充 50,只不过送的 50 用不完就会过期。也可以前期选 5$ 1G RAM 的版本,不过后面迁服务器还是要折腾下,没啥必要

我自己为了省钱,把 Vultr 之前 5$/mon 的 768RAM 转成了 2.5$/mon 的 512RAM 版,之前的那个版本已经没法新建了,也不支持平滑降级,只能重新建一个 2.5$ 的实例然后人肉迁一把,还好东西不多,之前有记录的情况下半个小时不到就搞定了

另外本站之前使用的社会化评论框工具多说要停止服务了:重要通知: 多说即将关闭,还是希望能有比 WordPress 原生更友好的评论工具,所以换了 Disqus,可能会被墙,但是访问我这边的大部分人应该都是随身自带翻墙的,就无所谓了

不知道是我自己之前改 WordPress 改挂了还是多说这个版本就是有问题,从去年四月某个时间开始多说的评论就没有自动同步回我 WordPress 里,强行导入也在 wp-admin/ajax.php 调用时提示 404。没办法跑多说去导出所有数据为 json,然后在 http://urouge.github.io/migrate-to-disqus/ 提供的工具下转成 Disqus 的 xml 格式并导入到 Disqus。导入时注意 Disqus 并没有一个明确的提示说导入成功或失败,而且 Disqus 管理后台默认显示的是最近三个月的评论,很容易误判是导入没成功又导一次,然后就呵呵哒的有一堆重复评论了,最简单粗暴去除重复的方法反倒是在 Disqus 里把这个站点删除,然后新建一个并导入

至于本地缺失的再怎么导回就再说吧,刚在 Disqus 插件里试了下说 Disqus server return 500,可能回头在 Disqus 里直接导出,然后 WordPress 里直接导入是可行的

奇怪的开发需求们

这是一篇写于 2015 年 4 月的草稿,现在已经忘了当时想说什么了,又不舍得直接删除,看看发出来自己还能不能看懂当时的只言片语

大需求应该是我们自己做了一套独立系统想开个新业务

一开始面对的就是帐号体系和登陆方式, 在有自己的账号体系下如何自制授权。最后用了一些不算 OAuth 的简化版验证来实现了原型快速可用。因为双方都是自己的系统,所以可以加入一个私有的加密用的 salt,然后在已有账号体系里用账号加时间戳加一个私有字段,通过 salt 做个 MD5 或 SHA1,把用户名,时间戳和加密后内容明文发送,在接收方用明文收到的用户名和时间戳加上没有传输但是两边都加入的私有字段一起,通过私有的 salt 算 Hash,对比结果,以及确认时间戳是最近的

然后是白名单需求,因为是对一个已有系统的部分用户开放测试功能,不希望没选上的用户也进入到测试系统里来。这个似乎没有什么特别要考虑的,做一个白名单放到 DB 里持久化,就是看怎么改这个白名单,是要提供一个 Web 界面,还是裸改代码。印象中这个项目还没从裸改代码进化到 Web 管理,就无疾而终了(手动捂脸

最头疼的还是错误处理,就是文档没说的那些事,怎么做好各种防御式编程,才是工作中的精髓所在

简单粗暴的做,大概一两周后,使用 cloc 统计代码如下(用了 CDN 上的 Bootstrap,当时也不怎么会前端,大部分事情都放到后端做,所以前端代码很少)

$ perl ~/Downloads/cloc-1.62.pl . --exclude-dir=env,.git,.idea,logs
      51 text files.
      50 unique files.
       6 files ignored.

http://cloc.sourceforge.net v 1.62  T=0.33 s (137.7 files/s, 6957.6 lines/s)
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Python                          25            369             52           1078
HTML                            17             43              1            603
CSS                              1              9              0             50
Javascript                       1              9              1             47
Bourne Shell                     1              2              0             10
-------------------------------------------------------------------------------
SUM:                            45            432             54           1788
-------------------------------------------------------------------------------

成长的笔记

毕业这么久,自己做事,带人做事,带团队做事,一路过来有很多感悟,又好像说不清到底成长在哪里,最近看了知乎上一个回答,深有感悟,归纳的挺好

原文在 https://www.zhihu.com/question/50539172/answer/121771903 ,答主 Cat Chen 之前在度厂工作过,似乎有过一面之缘,最近看他的 Blog 和知乎回答,在这类思考上还是走的挺前的,跟从之

回到主题,在 Cat Chen 的知乎回答里,把工作中人的能力分成三块:Technical、Direction、People,用中文对应大概是 技术能力、方向把控能力、沟通能力

其中 技术能力 是个人可控,可以独立成长,并且很容易评估成长程度,同样一件事给一个人完成,有人一天就能完成的很好,有人花一周还弄的磕磕碰碰,前者比后者强,这个是绝大部分人都可以达到的境界

而对于 方向把控能力,我们从小到大接受的教育都比较忽略这一点,更多的时候我们都是在指定了问题的情况下怎么去解决问题,而真的把一个开放问题丢到面前,必须要自己找方向时,就挺难了。大多数人在这个阶段就被淘汰了,或者鸵鸟政策自己看现在自己可以做什么就去做什么,自我安慰只要我一直在努力那必然也是有成果的,殊不知方向错了做的再多都没用,还可能有反作用

在有 技术能力 基础和 方向把控能力 上,可以更好的与 沟通能力 做同步成长。对自己的反馈上,沟通能力好,才可以更事半功倍的提升自己的技术能力,不然有些事别人点拨一下就可以解决的问题,自己可能要花十天半月还不一定摸到门路;沟通能力好,才有机会获得足够的信息输入,且信息来源可靠或有可靠性分析,进而影响方向把控能力。对外而言,不是所有事情都可以单枪匹马搞定的,那就涉及到怎么去拉到其他人来帮自己做事情,除了上级死压下级这类没法反抗的情况,绝大部分时候涉及到怎么让人「信服」自己的事情,信服的基础是你证明你能做好这件事(技术能力),并且你做的这件事对别人有好处(方向把控能力),别人才会心甘情愿的追随或听从你,否则只会出现「唉我这个事很靠谱你们怎么都不理我」或「你们一个个明明有空为啥都不愿意听我讲跟我做事」的情况

解决 Windows 10 Insider 在 VirtualBox 里无法升级的问题

一句话解决

在 VirtualBox 的虚拟机设置里,开启 3D 加速 的设置

繁琐点的解释

因为有一些奇葩测试需要在 Windows 下进行,于是在 macOS 上装 VirtualBox 跑 Windows,又因为既不想付钱也不想用盗版,所以用的 Windows Insider,就是给微软当小白鼠跑最新的公开测试版,微软也允许免费商业使用(脑补微软的内心:你们都敢在生产环境用我的测试版我也不好意思收你钱)

从九月份开始出现了点问题,Windows 10 自动更新后在重启过程中会失败,然后会尝试回滚到上一个可用版本,最后一个可以正常运行的 Windows 10 Insider Preview 是 Fast Ring 里的 14915 版本,后面的 14926,14931,14936,14942 都有这样的问题,而且在微软的官方更新日志里也承认了这点

本来可以置之不理,但是 Win10 是不是弹提示框说当前版本授权十月一号就到期了后面会隔一段时间自动重启如何如何,还是要想办法解决才行,搜 VirtualBox 论坛中枪的人满地都是,对原因的猜测是 VirtualBox 提供的显卡驱动有问题,导致新版的 Win10 不认从而挂掉,但是都没有给出好的解决办法

今天更新 14942 还有问题,忍不住再搜,终于有见到有人说把 VirtualBox 设置里的 3D 加速 打开就可以了,试了下果然,应该还是驱动有问题吧,期待早点解决

原来被 disabled 的 input 元素是不响应鼠标事件的

前几天做客服的时候遇到个客户来说我们的页面出错了, 两边鸡同鸭讲了半天发现都没法理解对方, 让客户截图后发现果然跟我这边不一样, 远程过去一看, 对面用的是 Win10 + Edge, 在我们本该出现 tipsy 的地方鼠标移上去没任何反应, 客户看不到该有的提示, 也难怪要跑过来找客服

一开始以为是 Edge 和 IE11 又出现什么奇葩的 jQuery 事件绑定错误, 需要在 knockout 的动态渲染完成后再做事件绑定, 但是仔细查了下后发现还不是这么回事, 熊去搜了下表示似乎 “disabled 的 input 元素是不响应鼠标事件的”, 但是为啥 Chrome 又能支持? 把 Firefox 也拉进来测试, 发现似乎不响应是通用标准, Chrome 那个浓眉大眼的货又一次不按常理出牌在鼠标事件完成后另外触发了一次, 所以我们测试一直没问题

既然知道了这么个原因, 而我们又要对一个 disabled 的 input 元素做悬浮事件, 怎么办? 按 Bootstrap 的说法, 在外面包一层, 然后把悬浮事件绑定到包的那一层上就好, 但是这么搞了后还是失败, 各种姿势换了一遍后发现如果我多包一点东西进去, 然后鼠标从多包的那一边进入, 就可以正常触发事件, 这是什么鬼? 想了半天没想明白, 暴力在外面做了一个图标来绑定悬浮事件先解决问题再说

吃了个饭回来, 越想越不对, 难道是因为那个 input 元素有 1px 的 border, 然后导致 Edge 那货一看这个区域都属于 disabled input, 所以其他这个区域的其他元素也不做任何响应? 怒而把包的那一层加了个 border: 5px solid transparent; margin: -5px, 果然就好了… 就仅仅是需要让包的这层真的比那个 disabled input 更大而已

不过还有几个坑没想明白的

  1. 不知道是不是跟 Edge 的刷新率有关, 如果鼠标进出太快, 也可能不会触发包的那一层的事件
  2. 不知道是不是需要额外设置 float 或 z-index 关系, 让包的那一层的 z-index 更高, 这样才能正确响应鼠标事件, 可能也不用去 hack border

最近其他事太多, 这个坑先这么填着, 回头有空了再来细细研究, 写前端真的是折寿啊

写这篇 blog 时去搜了下相关内容, 还有建议把元素不做 disabled, 在 hover 事件里动态设置为 disabled 的乱搞流… 你这样样式都不对的好么